diff --git a/src/data/constants/subscription-plans.ts b/src/data/constants/subscription-plans.ts deleted file mode 100644 index dbe79141..00000000 --- a/src/data/constants/subscription-plans.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * VIP 订阅套餐常量 - * - * Mock 阶段硬编码,后续可改为从 `ApiPath.paymentProducts` 拉取。 - * - * 原始 Dart: lib/ui/subscription/... - * - * 当前订阅页优先使用后端 `/api/payment/plans` 返回的套餐。 - * 本常量仅保留给旧 UI / fallback 代码参考。 - */ -export type SubscriptionPlanId = "monthly" | "quarterly" | "annual"; - -export interface SubscriptionPlan { - id: SubscriptionPlanId; - /** 卡片顶部名称,例如 "Monthly" */ - name: string; - /** 当前价格 */ - price: number; - /** 划线原价 */ - originalPrice: number; - /** 每日折算价字符串,e.g. "$0.66/day" */ - perDay: string; - /** 是否高亮(默认推荐:月付) */ - highlight: boolean; - /** 该套餐每天补充的语音聊天分钟数(业务常量) */ - voiceMinutesPerDay: number; -} - -export const SUBSCRIPTION_PLANS: readonly SubscriptionPlan[] = [ - { - id: "monthly", - name: "Monthly", - price: 19.9, - originalPrice: 24.9, - perDay: "$0.66/day", - highlight: true, - voiceMinutesPerDay: 30, // 30 分钟/天 - }, - { - id: "quarterly", - name: "Quarterly", - price: 49.9, - originalPrice: 59.9, - perDay: "$0.55/day", - highlight: false, - voiceMinutesPerDay: 45, // 45 分钟/天(比月付多,性价比) - }, - { - id: "annual", - name: "Annual", - price: 169.9, - originalPrice: 199.9, - perDay: "$0.47/day", - highlight: false, - voiceMinutesPerDay: 60, // 60 分钟/天(年度最多) - }, -] as const; diff --git a/src/data/repositories/chat_repository.ts b/src/data/repositories/chat_repository.ts index ed3c98fd..5d664e94 100644 --- a/src/data/repositories/chat_repository.ts +++ b/src/data/repositories/chat_repository.ts @@ -1,20 +1,5 @@ "use client"; -/** - * ChatRepository - * - * 聊天仓库:编排远程 ChatApi + 本地 LocalChatStorage(Dexie/IndexedDB)。 - * 行为对齐原 Dart `lib/data/repositories/chat_repository_impl.dart`: - * - `saveMessagesToLocal` 保留「先清后写」语义(Dart `box.clear()` + bulk add) - * - 替代 Dart 同步 `int get localMessageCount`,改为异步 `getLocalMessageCount()` - * (Dexie 异步 API 决定) - * - * ChatMessage ↔ LocalMessage 的字段映射在仓库内 inline 完成(用户决策): - * - 写:ChatMessage 的 id/role/content/createdAt → LocalMessage(sessionId="") - * - 读:LocalMessage 的 id/role/content/createdAt → ChatMessage(丢弃 sessionId) - * - * 原始 Dart: lib/data/repositories/chat_repository_impl.dart - */ import { ChatApi, chatApi } from "@/data/services/api"; import { ChatHistoryResponse, diff --git a/src/data/repositories/interfaces/ichat_repository.ts b/src/data/repositories/interfaces/ichat_repository.ts index 8c78daa6..4a910cc6 100644 --- a/src/data/repositories/interfaces/ichat_repository.ts +++ b/src/data/repositories/interfaces/ichat_repository.ts @@ -30,12 +30,14 @@ export interface IChatRepository { /** 获取聊天历史,分页参数默认 limit=50, offset=0。 */ getHistory(limit?: number, offset?: number): Promise>; + //TODO remove /** 解锁私密消息。 */ unlockPrivateMessage(messageId: string): Promise>; /** 一键解锁历史锁定消息。 */ unlockHistory(): Promise>; + //TODO remove /** 把本地缓存中的私密消息标记为已解锁。 */ markPrivateMessageUnlockedInLocal( messageId: string, diff --git a/src/data/storage/auth/auth_storage.ts b/src/data/storage/auth/auth_storage.ts index 60f2307c..5b612dd6 100644 --- a/src/data/storage/auth/auth_storage.ts +++ b/src/data/storage/auth/auth_storage.ts @@ -75,9 +75,7 @@ export class AuthStorage implements IAuthStorage { } // ---- refresh token ---- - // TODO(security): 升级到 HttpOnly Cookie 方案 - // SECURITY: 明文存储,仅供本轮与 Dart 行为对齐 - + getRefreshToken(): Promise> { return SpAsyncUtil.getString(StorageKeys.refreshToken); } diff --git a/src/router/index.ts b/src/router/index.ts deleted file mode 100644 index 81e9c9b9..00000000 --- a/src/router/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -// / -// * Router 公共导出 -// * -// * 集中管理: -// * - 路由常量(ROUTES、ROUTE_BUILDERS、AUTH_ONLY_ROUTES、PUBLIC_ROUTES、ALL_STATIC_ROUTES) -// * - Next.js 16 Proxy(CDN 端鉴权重定向) -// * -// * 业务层导入示例: -// * ```ts -// * import { ROUTES, AUTH_ONLY_ROUTES, type Route } from "@/router"; -// * ``` -// */ -// export * from "./routes"; -// export { proxy, config as proxyConfig } from "./proxy"; diff --git a/src/router/proxy.ts b/src/router/proxy.ts deleted file mode 100644 index 45de18d0..00000000 --- a/src/router/proxy.ts +++ /dev/null @@ -1,101 +0,0 @@ -// / -// * Next.js 16 Proxy(原 `middleware.ts`) -// * -// * 行为(鉴权路由统一在此处理,业务层组件不再做 auth-driven 跳转): -// * - 已登录(NextAuth `next-auth.session-token` cookie 存在)访问 `/splash` 或 `/auth` -// * → 308 重定向到 `/chat` -// * - 未登录(cookie 缺失)访问 `/chat` 或 `/sidebar` -// * → 308 重定向到 `/splash`(首界面) -// * - 其他情况透传(`NextResponse.next()`) -// * -// * 重要约束: -// * - 不做自定义 cookie 持久化:统一用 NextAuth 内置 `next-auth.session-token` / -// * `__Secure-next-auth.session-token`(v5 默认 httpOnly + secure)作信号。 -// * - 真正的鉴权门在 Client 侧 `useSession()`(next-auth/react),用于 UI 状态。 -// * - 路由跳转(唯一鉴权跳转点)由本 proxy 集中处理。 -// * - 副作用:邮箱登录用户(不走 NextAuth)不会被本 proxy 识别为已登录。 -// * 邮箱登录在后续轮次若要恢复 proxy 行为,需引入 NextAuth EmailProvider 或独立 -// * email-session 体系。 -// * -// * Next 16 重要变更(参见 `node_modules/next/dist/docs/01-app/03-api-reference/03-file-conventions/proxy.md`): -// * - 文件从 `middleware.ts` 重命名为 `proxy.ts`;`middleware` 仍可用但已 deprecated。 -// * - 默认 Node.js runtime;不支持 `runtime` 配置(设置会抛错)。 -// * - 函数导出名建议为 `proxy`(可默认导出)。 -// * -// * 用 `src/` 时 proxy 必须放在 `src/` 内(参见 `src-folder.md`)。 -// */ - -// import { NextResponse } from "next/server"; -// import type { NextRequest } from "next/server"; - -// import { Logger } from "@/utils/logger"; - -// import { -// AUTH_ONLY_ROUTES, -// PROTECTED_ROUTES, -// ROUTES, -// } from "@/router/routes"; - -// / NextAuth 内置 session cookie 名(v5 默认 httpOnly) */ -// const SESSION_COOKIE_NAMES = [ -// "next-auth.session-token", -// "__Secure-next-auth.session-token", -// ]; - -// / proxy 入口日志(Node.js runtime 跑,pino 兼容)—— 排查 / 监控路由用 */ -// const log = new Logger("Proxy"); - -// function isAuthOnlyRoute(pathname: string): boolean { -// return (AUTH_ONLY_ROUTES as readonly string[]).includes(pathname); -// } - -// function isProtectedRoute(pathname: string): boolean { -// return (PROTECTED_ROUTES as readonly string[]).includes(pathname); -// } - -// / -// * Proxy 入口 -// */ -// export function proxy(request: NextRequest) { -// const { pathname } = request.nextUrl; - -// // 检查 NextAuth session cookie 存在性 -// const hasSession = SESSION_COOKIE_NAMES.some( -// (name) => Boolean(request.cookies.get(name)?.value), -// ); - -// // 已登录访问未登录专属页 → /chat -// if (hasSession && isAuthOnlyRoute(pathname)) { -// log.info( -// { pathname, target: ROUTES.chat, reason: "logged-in → auth-only" }, -// "[proxy] redirect", -// ); -// const url = request.nextUrl.clone(); -// url.pathname = ROUTES.chat; -// return NextResponse.redirect(url, 308); -// } - -// // 未登录访问登录态专属页 → /splash(首界面) -// if (!hasSession && isProtectedRoute(pathname)) { -// log.info( -// { pathname, target: ROUTES.splash, reason: "not-logged-in → protected" }, -// "[proxy] redirect", -// ); -// const url = request.nextUrl.clone(); -// url.pathname = ROUTES.splash; -// return NextResponse.redirect(url, 308); -// } - -// return NextResponse.next(); -// } - -// / -// * 匹配器:排除 API、Next 静态资源、图标与常见静态文件扩展名。 -// * 不排除 `_next/data`,因为 proxy 文档明确说明"即使在排除列表中 proxy 仍会跑" -// * (安全兜底)。 -// */ -// export const config = { -// matcher: [ -// "/((?!api|_next/static|_next/image|_next/data|favicon.ico|icons|.*\\.(?:png|jpg|jpeg|gif|svg|webp|ico|css|js|woff2?|ttf|otf)$).*)", -// ], -// };