@stringzhao/feedback-sdk
v0.1.0
Published
通用用户反馈接入 SDK(hook 核心 + 可选预制 UI),用于接入 stringzhao 名下 base-account 反馈系统(`user.stringzhao.life`)。
Readme
@stringzhao/feedback-sdk
通用用户反馈接入 SDK(hook 核心 + 可选预制 UI),用于接入 stringzhao 名下 base-account 反馈系统(user.stringzhao.life)。
鉴权解耦:SDK 只调可配置 baseUrl(默认相对 "" 同源),鉴权完全由宿主 proxy route 注入。SDK 不内化 token / cookie,避免 CORS 与跨域 cookie 麻烦。
安装
npm install @stringzhao/feedback-sdk
# peer: react >= 19, next >= 15(可选)1. 配置 Proxy(宿主项目,同源注入鉴权)
SDK 默认走相对路径 /api/feedback/*,宿主需提供一个同源 proxy 转发到 base-account 并注入 access_token。Next.js App Router 示例:
// app/api/feedback/[...path]/route.ts
import { cookies } from "next/headers";
const BASE_ACCOUNT_URL = process.env.BASE_ACCOUNT_URL!; // https://user.stringzhao.life
export async function POST(req: Request, { params }: { params: Promise<{ path: string[] }> }) {
return forward(req, await params);
}
export async function GET(req: Request, { params }: { params: Promise<{ path: string[] }> }) {
return forward(req, await params);
}
async function forward(req: Request, params: { path: string[] }) {
const store = await cookies();
const accessToken = store.get("access_token")?.value;
const devToken = process.env.DEV_FEEDBACK_ACCESS_TOKEN;
const token = accessToken ?? devToken;
if (!token) {
return Response.json({ error: "unauthorized" }, { status: 401 });
}
const url = `${BASE_ACCOUNT_URL}/api/feedback/${params.path.join("/")}${new URL(req.url).search}`;
const headers = new Headers(req.headers);
headers.set("Cookie", `access_token=${token}`);
return fetch(url, { method: req.method, headers, body: req.body });
}生产环境:
access_tokencookie 需跨子域(.stringzhao.life),依赖宿主 silent_reauth 持续刷新。 本地开发:cookie 不可跨域,用DEV_FEEDBACK_ACCESS_TOKEN兜底(从 base-accountcurl /dev/admin-login取)。
2. 挂 Provider
// app/layout.tsx
import { FeedbackProvider } from "@stringzhao/feedback-sdk";
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<FeedbackProvider serviceKey="my-app" baseUrl="" />
</body>
</html>
);
}baseUrl:默认""(相对同源)。SDK 不内化鉴权,由 proxy 控制。serviceKey:Provider 级默认值,调用时可覆盖。
3a. 用 hook 自建 UI
"use client";
import { useFeedback } from "@stringzhao/feedback-sdk";
export function MyFeedbackButton() {
const { submit, loading, error } = useFeedback();
async function onClick() {
const result = await submit({
type: "BUG",
description: "某按钮点击无响应",
});
if (result) alert("已提交,感谢反馈!");
}
return (
<>
<button onClick={onClick} disabled={loading}>反馈</button>
{error && <p role="alert">{error.message}</p>}
</>
);
}useFeedback() 返回:
| 字段 | 说明 |
| --- | --- |
| submit({ type, description, attachments?, serviceKey? }) | 提交反馈,成功返回 { id, status },失败返回 null(错误进 error) |
| listMine() | 拉取我的反馈,填充 feedbacks |
| uploadAttachment(file) | 上传单张截图(image/* ≤ 2MB),返回 { url, size } |
| addMessage(feedbackId, content) | 追加消息 |
| loading / error / feedbacks | 状态 |
| clearError() | 清除错误态 |
容错契约:所有操作 catch 错误转入 error 态,不向调用方抛未捕获异常(网络失败 / 5xx 同样安全)。
3b. 用预制 UI(FeedbackFab)
// app/layout.tsx
import { FeedbackProvider, FeedbackFab } from "@stringzhao/feedback-sdk";
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<FeedbackProvider serviceKey="my-app">
<FeedbackFab />
</FeedbackProvider>
</body>
</html>
);
}<FeedbackFab/> 右下悬浮 → 点击展开 <FeedbackDialog/>(类型选择 + 描述 + 截图上传 ≤4 + 提交)。
配色遵循 stringzhao.life/colors(苔绿主色 + 暖白基底 + 语义色)。
也可单独用 <FeedbackDialog open={...} onClose={...} serviceKey="..." onSuccess={(id)=>...} />。
错误码
FeedbackError.code(对齐 base-account 服务端):
| code | HTTP | 含义 |
| --- | --- | --- |
| invalid_input | 400 | 参数不合法 |
| too_many_attachments | 400 | 附件超过 4 张 |
| invalid_service_key | 400 | 未注册的 serviceKey |
| invalid_file_type | 400 | 非 image/* |
| unauthorized | 401 | 未登录 / 登录过期 |
| forbidden | 403 | 无权操作他人反馈 |
| feedback_not_found | 404 | 反馈不存在 |
| file_too_large | 413 | 单张 > 2MB |
| rate_limited | 429 | 60s 内超 3 条 |
| network_error | — | 网络失败(SDK 归类) |
API 导出
| 导出 | 说明 |
| --- | --- |
| useFeedback() | 核心 hook |
| FeedbackProvider | Context Provider("use client") |
| FeedbackFab | 右下悬浮触发 + 弹窗 |
| FeedbackDialog | 反馈表单弹窗 |
| createFeedbackApi(baseUrl?) | 底层 API client(进阶,绕过 React) |
| FeedbackType / FeedbackStatus | 枚举 |
| FeedbackView / FeedbackMessage / SubmitFeedbackInput / FeedbackError 等 | 类型 |
