# AGENTS.md — @app/trpc ## 职责 API 层:**tRPC 路由 + service 业务逻辑**。service 可不依赖 tRPC 独立调用(脚本/测试);DB 读写经 `@app/dao`,不直接写 SQL。 域划分: - 账户域:`user`、`auth` - 媒体域:挂在 `media` 路由下的 mount / library / stream / danmaku / scrape / playback 新域按同样模式追加。 ## 结构 ``` src/ context.ts # initTRPC 单例 + TrpcContext router/ index.ts # appRouter 组装(权威定义:user / auth / media) user.router.ts # user 路由(与 index.ts 内定义重复,改路由时两边同步或收敛到一处) auth.router.ts # auth 路由 media.router.ts # 媒体域扁平 procedure(mount* / library* / scrape* / playback* / danmaku*) services/ user.service.ts # userService auth.service.ts # authService:会话 / 密码 password.ts # scrypt 哈希(与 @app/db seed 一致) procedure.ts # protectedProcedure 等 secret.ts # 媒体流密钥 mount.service.ts # mountService:WebDAV 挂载 webdav-client.ts # WebDAV 客户端(含单测) library.service.ts # libraryService:扫描 / 分组 / 剧集 stream.service.ts # 流式输出(含单测) danmaku.service.ts # danmakuService:弹幕拉取 / 导入 / 设置(含单测) scrape.service.ts # scrapeService:番剧识别 / 绑定(含单测) playback.service.ts # playbackService:进度上报 / 续播 load-env.ts server.ts # 独立 HTTP 服务(PORT,默认 4000) index.ts # 对外:appRouter、类型、services router.ts # 兼容旧入口的再导出 ``` 构建:`vite build`(`emptyOutDir: false`)→ `tsc -p tsconfig.dts.json` 产出 `.d.ts`。勿开 dts 的 incremental(vite 会先清产物,缓存会导致 tsc 跳过 emit);勿改回 vite-plugin-dts。 ## 请求流 ``` web /api/trpc ──► appRouter ──► service ──► @app/dao ──► @app/db ``` 也可 `yarn dev:api` 跑 `server.ts`(独立端口);web SSR 内嵌时通常不用。 媒体域 procedure 用 `protectedProcedure`(需登录,取 `ctx.userId`);路由层只做 input 映射 + 调 service。 ## 修改指南 1. **改入参**:先改 `@app/types` 的 Zod schema,再挂到 `t.procedure.input(...)`。 2. **加 procedure**:路由只做 input 映射 + 调 service;业务在 `services/*.service.ts`;SQL 在 `@app/dao`。 3. **加领域**:新建 `services/x.service.ts` + 在 `media.router.ts`(或新 `router/x.router.ts`)挂 procedure,接到 `appRouter`,并在 `index.ts` 导出。 4. 导出给前端的类型用 `RouterInputs` / `RouterOutputs`(来自 `router/index.ts`)。 5. 外部 API(弹幕/刮削)调用集中在对应 service,注意超时与错误包装;SSRF/路径穿越防护见 `stream.service` / `webdav-client`。 ## 约束 - `exactOptionalPropertyTypes`:service 的 update 入参写成 `name?: string | undefined`。 - `noUncheckedIndexedAccess`:判空逻辑在 DAO 内处理(参考 `userDao.create`)。 - 不要在 router/service 里写 SQL;不在 service 里依赖 HTTP/tRPC context(除非鉴权明确需要)。 - `router.ts` 仅兼容旧 import,新代码从 `index.ts` / `./router/index.js` 引入。 - service 旁的 `*.test.ts` 用 vitest/vite 跑(`vite.config.ts`),改行为时同步补测。 ## 命令 ```bash yarn workspace @app/trpc typecheck yarn workspace @app/trpc lint yarn workspace @app/trpc build yarn workspace @app/trpc dev # tsx watch src/server.ts ```