usage_log 扩展:非 ext 目录源码侵入与合并标记
文档地图(usage_log 相关)
| 文档 | 读它当你需要… |
|---|---|
| 日志插件.md | 业务:扩展做什么、环境变量、附件、API、流程与限制。 |
| 插件开发.md | 机制:ext 包、目录树、新建插件、表初始化、联调、附录。 |
| 本文 | 合并:主仓库(非 ext)须保留的代码块、i18n、go.mod 要点。 |
| 与上游-Gitee-手动合并步骤.md | Git:与 Gitee 无共同祖先时的 merge 命令与自检。 |
推荐顺序: 理解业务 → 日志插件;日常开发 ext/ → 插件开发;拉上游时若遇 unrelated histories → Gitee 步骤,合并完再按本文恢复侵入点。
文档分工: 本文只解决「合并上游时要在哪些非 ext 文件里保留什么代码」。业务与配置以 日志插件.md 为准;通用扩展机制以 插件开发.md 为准。
历史上曾以基线提交 b94945e755aa1ef271cbac16e088c37538e5f6d1 为参照;合并时以本文〔合并段〕与当前仓库源码为准,不必拘泥于旧提交号。
说明:
ext/、ext/table_init/、web/src/ext/下的扩展实现不写入本文;仅记录「上游同名文件」中的侵入点。
源码标注(与仓库一致):主仓库与
web/src/ext/usage-logs/*中侵入点使用如下注释包裹,合并时建议整段保留:
- Go:
// ==============================、// 开始、// 扩展:…、侵入代码、再// 结束与// ==============================(具体见各节代码块)。- JS/JSX:同一语义,使用
// …或块注释/* … */。locales/*.json:标准 JSON 不支持注释,扩展文案以本文 〔合并段〕 内说明与 key 表为准;勿在 JSON 内写//。可
grep -E '开始|结束|扩展:'在源码中快速定位。
非 ext 侵入文件一览(合并后逐项核对)
| 文件 | 侵入内容摘要 |
|---|---|
main.go | import github.com/QuantumNous/new-api/ext;在 router.SetRouter 之前调用 ext.MustRegister(server)(见下节)。 |
router/relay-router.go | import ext;在 DecompressRequestMiddleware、BodyStorageCleanup 之后,第一个 /v1 等 relay 子路由之前调用 ext.RegisterRelayMiddleware(router);其后仍有 StatsMiddleware 等(顺序见下节)。 |
middleware/auth.go | 提供 EnforceUserAuth(c *gin.Context) bool(附件下载路由未挂中间件时使用),见专节。 |
go.mod / go.sum | 须包含 github.com/xuri/excelize/v2(表格附件),见专节。 |
web/src/components/table/usage-logs/index.jsx | 按环境变量选用 UsageLogsTableWithContent 或默认 UsageLogsTable,见专节。 |
web/src/i18n/locales/zh-CN.json 等 | 扩展 UI 所需 key(见 i18n 键表);至少维护 zh-CN 与 en,其它语种按需同步。 |
不在此列表中的文件:router/api-router.go、web/src/helpers/api.js 等无需为 usage_log 单独改路由或封装;扩展 API 由后端 ext.MustRegister → usage_log.RegisterRouter 注册 /api/ext/*,前端扩展组件内直接使用 API.get('/api/ext/...') 即可。
后端 Go
main.go
〔合并段 · 开始〕
import
与其它github.com/QuantumNous/new-api/*包放在同一import块即可,增加一行:go"github.com/QuantumNous/new-api/ext"不要求单独拆成第二个
import (...);合并冲突时只要保证ext包被导入。调用时机
在router.SetRouter(server, buildFS, indexPage)之前调用ext.MustRegister(server),并用注释块标出(与仓库一致):
// ==============================
// 开始
// 扩展:usage_log 等 Register(配置、迁移、/api/ext 路由);必须在 router.SetRouter 之前。合并上游见 docs/v2/usage_log-与上游合并-源码侵入.md
// ==============================
ext.MustRegister(server)
// ==============================
// 结束
// ==============================
// 设置路由
router.SetRouter(server, buildFS, indexPage)顺序约束(与 Gin、扩展路由的关系)
cookie.NewStore+sessions.Sessions、InjectUmamiAnalytics/InjectGoogleAnalytics等为主干原有逻辑,不是 usage_log 专用「侵入块」;但必须保证 Session 已挂载 且ext.MustRegister在router.SetRouter之前,以便扩展里UserAuth/TokenOrUserAuth能读会话。合并上游后若顺序被改,应恢复为:Session →(可选注入脚本)→ext.MustRegister→SetRouter。
〔合并段 · 结束〕
router/relay-router.go
〔合并段 · 开始〕
在 SetRelayRouter 内,relay 全局中间件须满足:
import增加"github.com/QuantumNous/new-api/ext"(与其它 import 同块即可)。- 在
DecompressRequestMiddleware→BodyStorageCleanup之后立刻调用ext.RegisterRelayMiddleware(router),再挂StatsMiddleware(),再注册/v1/models等子路由。
当前典型顺序如下(中间件名以上游为准,插入点在解压与 Body 清理之后、任何 router.Group("/v1"...) 之前):
router.Use(middleware.DecompressRequestMiddleware())
router.Use(middleware.BodyStorageCleanup()) // 清理请求体存储
// ==============================
// 开始
// 扩展:usage_log 捕获 relay 请求体;须在解压与 BodyStorageCleanup 之后、/v1 等子路由注册之前。合并上游见 docs/v2/usage_log-与上游合并-源码侵入.md
// ==============================
ext.RegisterRelayMiddleware(router)
// ==============================
// 结束
// ==============================
router.Use(middleware.StatsMiddleware())
// … 随后 modelsRouter := router.Group("/v1/models") 等为何必须卡在这一位置:Gin 在注册子路由之后再 Use 的中间件进不了已匹配子路由的请求链;捕获必须在第一个 relay 子路由注册前完成。若上游把 StatsMiddleware 挪到解压之前,须自行评估是否仍满足「解压后即可读 body」;一般保持本文插入点不变。
〔合并段 · 结束〕
middleware/auth.go
〔合并段 · 说明〕
扩展 GET /api/ext/attachment/download 在 ext/usage_log/router_ext.go 中未挂 UserAuth / TokenOrUserAuth(链接型附件需先查库再决定是否 302,本地文件再在 handler 内鉴权)。本地文件分支依赖主仓提供的 EnforceUserAuth(c *gin.Context) bool:在未走全局中间件时,用会话或 Authorization: Bearer sk- 写入 id、role 等上下文;失败则写 JSON 并返回 false。
- 须保留:
func EnforceUserAuth(c *gin.Context) bool及其完整实现(当前位于middleware/auth.go中TokenOrUserAuth与TokenAuthReadOnly之间)。合并上游后若编译报undefined: middleware.EnforceUserAuth,说明该段被覆盖,需从本分支恢复。 - 与
ext/usage_log/resolve_user.go的分工:tryResolveUserIDFromSkBearer/tryResolveUserIDFromSession仍供扩展内部仅解析 ID、不修改middleware;附件下载路径必须以EnforceUserAuth为准,与主干令牌解析习惯对齐。
〔合并段 · 结束〕
go.mod / go.sum
〔合并段 · 说明〕
ext/usage_log/attachment_xlsx.go 依赖 github.com/xuri/excelize/v2(解析 .xlsx / .xlsm 附件)。合并上游后若出现:
no required module provides package github.com/xuri/excelize/v2
须在模块根目录执行:
go get github.com/xuri/excelize/v2
go mod tidy并在 go.mod 中保留类似:
github.com/xuri/excelize/v2 v2.10.1(具体版本号以 go get / go mod tidy 结果为准;可能连带升级 golang.org/x/* 等间接依赖。)
〔合并段 · 结束〕
前端(web)
web/src/components/table/usage-logs/index.jsx
〔合并段 · 开始〕
使用日志页默认改用带「问题 / 对话详情」列的扩展表;可通过 VITE_EXT_USAGE_LOG_CONTENT=false 回退为原 UsageLogsTable。
// ==============================
// 开始
// 扩展:问题/回答列(usage_log_content),默认开启,设 VITE_EXT_USAGE_LOG_CONTENT=false 可关闭。合并上游见 docs/v2/usage_log-与上游合并-源码侵入.md
// ==============================
import UsageLogsTableWithContent from '../../../ext/usage-logs/UsageLogsTableWithContent';
// ==============================
// 结束
// ==============================在全部 import 语句之后定义:
const LogsTableComponent =
import.meta.env.VITE_EXT_USAGE_LOG_CONTENT !== 'false'
? UsageLogsTableWithContent
: LogsTable;表格处使用 <LogsTableComponent {...logsData} />(不是直接写 <LogsTable {...} />,除非关闭扩展)。
〔合并段 · 结束〕
web/src/ext/usage-logs/*.jsx(扩展组件)
文件头部为 /* … 开始 … 扩展:… */,在 export default 前为 /* … 结束 … */,与主页面 // 块语义一致。扩展内请求 /api/ext/log-content、/api/ext/attachment/download 等,无需改 helpers/api.js。
web/src/i18n/locales/zh-CN.json(及 en.json 等需同步的语种)
〔合并段 · 开始〕
使用日志扩展 UI 文案(中文 key 与项目约定一致);其它语种在对应 locales/*.json 增加同 key 的翻译。
在 translation 对象中增加或保留(示例值与中文一致;英文见 en.json 翻译):
"提示词、问题与回答": "提示词、问题与回答",
"关联附件": "关联附件",
"点击放大": "点击放大",
"内嵌 Base64 图片": "内嵌 Base64 图片",
"附件类型链接": "链接",
"附件类型文件": "文件",
"对话记录": "对话记录",
"对话详情": "对话详情",
"提示词": "提示词",
"问题": "问题",
"回答": "回答",
"无扩展内容": "无扩展内容",
"搜索无结果": "搜索无结果"扩展组件实际调用的 key 与用途(无 等主干已有 key 若已存在则勿重复定义):
| key | 用途 |
|---|---|
对话记录 | 按钮与弹窗标题(LogContentCell) |
对话详情 | 表格列标题(UsageLogsTableWithContent) |
问题 | 「问题」列表头 |
提示词、问题与回答 | 展开区标题 |
提示词 / 问题 / 回答 / 关联附件 | 对话记录区块标题 |
无扩展内容 | 无数据提示 |
无 | PromptCell 无问题摘要(上游常见已有) |
点击放大 / 内嵌 Base64 图片 | 内嵌图与缩略图 |
附件类型链接 / 附件类型文件 | 附件列表类型前缀 |
搜索无结果 | 扩展表空状态(与全局用语一致时可依赖已有翻译) |
〔合并段 · 结束〕
依赖与数据(备忘)
| 项 | 说明 |
|---|---|
github.com/xuri/excelize/v2 | ext/usage_log/attachment_xlsx.go 必需;见上文 go.mod / go.sum 节。 |
model.LOG_DB | 与扩展表读写的关系、运行前提等见 日志插件.md · 第 1 节。 |
| 环境变量 / 表结构 / 附件 | 一律见 日志插件.md 对应章节与 ext/README.md;表可由 AutoMigrate 或 ext/table_init 初始化(亦见 插件开发.md · 第 5 节)。 |
合并工作流与自检
- 与 Gitee 无共同祖先时:先按 与上游-Gitee-手动合并步骤.md 完成
merge与依赖整理;-X theirs可能覆盖主仓定制,合并后必须按本文恢复侵入点。 - 逐文件恢复:顺序建议
main.go→router/relay-router.go→middleware/auth.go→go.mod/go.sum→ 前端index.jsx→ i18n;细节以本文 〔合并段〕 为准(与 插件开发 · 第 8 节 清单一致)。 ext/:随本仓库维护;冲突以ext/目录内为准,一般不需按上游覆盖。- 合并后自检(仓库根目录):
go build .
rg 'ext\.MustRegister|ext\.RegisterRelayMiddleware' main.go router/relay-router.go
rg 'EnforceUserAuth' middleware/auth.go
rg 'excelize' go.mod