docs(gateway): gateway 剩余文件注释改中文(逐行 1:1,代码零字节改动)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

主人 2026-10-03 拍板「注释一律中文」;gateway 至此整包注释中文化完成(本会话共 3 批:
已推库 16 个文件 + 本批剩余文件)。
校验:机械校验(非注释改动行)= 空;剥注释后代码文本逐行比对全等(不一致文件数 0);
全树 go test ./... 58 包 ok / 0 FAIL;交叉编译绿。
This commit is contained in:
AskaEth committed 2026-10-03 22:28:25 +08:00
1 parent 91e8d7a029
commit 074ffc4ebf
73 files changed
+2529 -2529

No files matched your search

+76 -76
View File
@@ -6,22 +6,22 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/auth"
)
// This file owns the HTTP surface: the route table (REST /api/v1, the realtime
// WebSocket hub, /api/v1/admin/* and externally-mounted webhook callbacks) and
// the outer middleware chain. Route registration is split by audience so the
// RBAC gate guarding each group stays visible: public, authenticated user,
// private history, admin reads and owner-only writes.
// 本文件拥有 HTTP 表面:路由表(REST /api/v1、实时
// WebSocket hub、/api/v1/admin/* 与外部挂载的 webhook 回调)以及
// 外层中间件链。路由注册按受众切分,使守护每组
// 的 RBAC 门保持可见:公开、已认证用户、
// 私有历史、管理读与仅 owner 写。
// Handler returns the fully assembled http.Handler: the ServeMux routing
// REST + WS + admin, wrapped in recovery, CORS and request logging.
// Handler 返回完整组装的 http.Handler:路由 REST + WS + 管理的
// ServeMux,外面包上 recover、CORS 与请求日志。
func (s *Server) Handler() http.Handler {
mux := http.NewServeMux()
// The RBAC middlewares are built once per role set and shared by every route
// that requires it (owner>admin>viewer, DevDocs/02 §九). viewerAuth is "every
// authenticated role": it names the read-only monitoring surface the viewer
// role exists for (module inventories, runtime metrics/traces, the log tail
// and the notification feed) — see registerAdminRoutes.
// RBAC 中间件按角色集各构建一次,并被每条需要它的路由
// 共用(owner>admin>viewer,DevDocs/02 §九)。viewerAuth 是「每个
// 已认证角色」:它指 viewer 角色存在的那个只读监控
// 表面(模块清单、运行时指标/trace、日志尾部
// 与通知 feed)—— 见 registerAdminRoutes。
anyAuth := s.auth.Authorize()
ownerAuth := s.auth.Authorize(auth.RoleOwner)
writeAuth := s.auth.Authorize(auth.RoleOwner, auth.RoleAdmin)
@@ -36,11 +36,11 @@ func (s *Server) Handler() http.Handler {
return s.withMiddleware(mux)
}
// registerPublicRoutes mounts the unauthenticated surface: liveness plus the
// credential endpoints. Refresh/logout are public by design — the opaque refresh
// token is itself the credential, so a client with an expired access token can
// still rotate or revoke it. Both fail closed with 401 invalid_grant on a bad
// token.
// registerPublicRoutes 挂载未认证表面:存活探针加上
// 凭据端点。refresh/logout 按设计是公开的 —— 非透明 refresh
// token 本身就是凭据,因此访问令牌已过期的客户端仍能
// 轮换或吊销它。二者在 token 非法时都以 401 invalid_grant
// 失败关闭。
func (s *Server) registerPublicRoutes(mux *http.ServeMux) {
mux.HandleFunc("GET /api/v1/health", s.handleHealth)
mux.HandleFunc("POST /api/v1/auth/login", s.handleLogin)
@@ -48,11 +48,11 @@ func (s *Server) registerPublicRoutes(mux *http.ServeMux) {
mux.HandleFunc("POST /api/v1/auth/logout", s.handleLogout)
}
// registerUserRoutes mounts the authenticated user-facing surface. Chat is a
// write (owner/admin); reads accept any authenticated role so a viewer can still
// list personas/sessions. Audio STT/TTS and image upload are owner/admin-only (the
// owner's private assistant, audited) and are the REST surface native clients use
// to bypass the 4KB WebSocket frame limit.
// registerUserRoutes 挂载已认证的面向用户表面。Chat 是
// 写(owner/admin);读接受任何已认证角色,因此 viewer 仍能
// 列出人格/会话。音频 STT/TTS 与图片上传仅 owner/admin(主人的
// 私密助手,带审计),它们是原生客户端用来
// 绕过 4KB WebSocket 帧上限的 REST 表面。
func (s *Server) registerUserRoutes(
mux *http.ServeMux,
anyAuth func(http.Handler) http.Handler,
@@ -62,74 +62,74 @@ func (s *Server) registerUserRoutes(
mux.Handle("POST /api/v1/chat", writeAuth(http.HandlerFunc(s.handleChat)))
mux.Handle("GET /api/v1/sessions", anyAuth(http.HandlerFunc(s.handleListSessions)))
mux.Handle("GET /api/v1/personas", anyAuth(http.HandlerFunc(s.handleListPersonas)))
// Shared client enum tables (Android collaboration B4, design/07 §6): the
// authority every client maps ids to labels from, so a source badge or a mood
// label cannot drift per client. A read-only projection of
// internal/contract/enums with nothing sensitive in it, so any authenticated
// role may read it (same gate as /sessions above).
// 共享的客户端枚举表(Android 协作 B4,design/07 §6):
// 每个客户端据此把 id 映射为标签的权威,因此来源徽标或
// 情绪标签不会逐客户端漂移。它是
// internal/contract/enums 的只读投影,不含任何敏感内容,因此任何已认证
// 角色都可读它(上面的 /sessions 同一道门)。
mux.Handle("GET /api/v1/meta/enums", anyAuth(http.HandlerFunc(s.handleListEnums)))
mux.Handle("POST /api/v1/audio/transcribe", ownerAuth(http.HandlerFunc(s.handleTranscribe)))
mux.Handle("POST /api/v1/audio/speech", ownerAuth(http.HandlerFunc(s.handleSpeech)))
mux.Handle("POST /api/v1/audio/voice-clone", ownerAuth(http.HandlerFunc(s.handleVoiceClone)))
// Image upload (B1): a multipart POST that stores a raster image under the
// resolved data dir and returns a same-origin URL usable directly as
// chat.image. Upload is owner/admin + audited; the static route is a
// capability URL (unguessable name) because chat.image / <img> cannot carry a
// bearer token. Both ride the one gateway port (single-binary red line).
// 图片上传(B1):一个 multipart POST,把光栅图片存到
// 解析后的数据目录下,返回可直接用作
// chat.image 的同源 URL。上传是 owner/admin + 带审计;静态路由是
// 能力 URL(名字不可猜)因为 chat.image / <img> 无法携带
// bearer token。二者都走同一个网关端口(单二进制红线)。
mux.Handle("POST /api/v1/upload", writeAuth(http.HandlerFunc(s.handleUpload)))
mux.HandleFunc("GET /api/v1/files/{name}", s.handleUploadFile)
}
// registerHistoryRoutes mounts the persistent chat-history surface (design/16)
// plus the realtime endpoint. Session/model reads and writes are the owner's
// private history, so owner/admin only (chat uses the same roles); the message
// content (and thinking) is owner/admin-only per design/16 §6, while the session
// metadata in GET /sessions above stays readable by any authenticated role.
// Creating and deleting a sub session mutate persistent state.
// registerHistoryRoutes 挂载持久化聊天历史表面(design/16)
// 加上实时端点。会话/模型读与写是主人的
// 私有历史,因此仅 owner/admin(chat 用同样的角色);消息
// 内容(与 thinking)按 design/16 §6 仅 owner/admin,而上面
// GET /sessions 中的会话元数据仍可被任何已认证角色读取。
// 创建与删除子会话会改变持久化状态。
func (s *Server) registerHistoryRoutes(mux *http.ServeMux, writeAuth func(http.Handler) http.Handler) {
mux.Handle("GET /api/v1/sessions/{id}/messages", writeAuth(http.HandlerFunc(s.handleListMessages)))
mux.Handle("POST /api/v1/sessions", writeAuth(http.HandlerFunc(s.handleCreateSubSession)))
mux.Handle("DELETE /api/v1/sessions/{id}", writeAuth(http.HandlerFunc(s.handleDeleteSession)))
mux.Handle("PATCH /api/v1/sessions/{id}", writeAuth(http.HandlerFunc(s.handlePatchSession)))
// Read cursor (D4 / Android collaboration B3): advancing it clears the session's
// unread badge on every end of the account, so it is an owner/admin history write
// like the other session mutations. It broadcasts a `session_read` frame.
// 读游标(D4 / Android 协作 B3):推进它会为该账号
// 每一端清除该会话的未读角标,因此它和其他会话变更一样
// 是 owner/admin 历史写。它会广播一个 `session_read` 帧。
mux.Handle("POST /api/v1/sessions/{id}/read", writeAuth(http.HandlerFunc(s.handleMarkSessionRead)))
// Single-message edit/delete (Android collaboration batch ③a, D3). A message is
// addressed by its own opaque id; both are owner/admin-gated history writes.
// 单条消息编辑/删除(Android 协作批次 ③a,D3)。消息由
// 自身非透明 id 定位;二者都是受 owner/admin 门控的历史写。
mux.Handle("PATCH /api/v1/messages/{id}", writeAuth(http.HandlerFunc(s.handlePatchMessage)))
mux.Handle("DELETE /api/v1/messages/{id}", writeAuth(http.HandlerFunc(s.handleDeleteMessage)))
// Realtime.
// 实时。
mux.HandleFunc("/ws/chat", s.handleWS)
}
// registerAdminRoutes mounts the admin API. Three read tiers plus two write
// tiers, mirroring the role hierarchy (RBAC red line):
// registerAdminRoutes 挂载管理 API。三个读层加两个写
// 层,镜像角色层级(RBAC 红线):
//
// - viewerAuth (owner/admin/viewer): the read-only monitoring surface — module
// inventories, runtime metrics/traces, the backend log tail and the
// notification feed. Every payload here is a non-sensitive projection with no
// credential, no other account's data and no message body, which is what
// makes the viewer role a usable dashboard/logs/monitoring account.
// - writeAuth reads (owner/admin): everything a viewer must not see — memories,
// the LLM provider stack, accounts/API keys, the backthink monitor, the audit
// trail, channel message logs/transcripts, full session export.
// - writeAuth writes (owner/admin): the audited knowledge search.
// - ownerAuth (owner): the audited policy write, every credential-acquiring
// channel endpoint (login-qr/login-status) and every other mutation.
// - viewerAuth(owner/admin/viewer):只读监控表面 —— 模块
// 清单、运行时指标/trace、后端日志尾部与
// 通知 feed。这里每个载荷都是非敏感投影,不含
// 凭据、不含其他账号的数据、不含消息体,这正是
// 让 viewer 角色成为可用的看板/日志/监控账号的原因。
// - writeAuth 读(owner/admin):viewer 不得看到的一切 —— 记忆、
// LLM provider 栈、账号/API key、后台思考监控、审计
// 链、渠道消息日志/转录、完整会话导出。
// - writeAuth 写(owner/admin):带审计的知识搜索。
// - ownerAuth(owner):带审计的策略写、每个获取凭据的
// 渠道端点(login-qr/login-status)以及每一个其他变更。
//
// Each write handler persists to the real backing store or answers honestly with
// a clear "M10 / not-wired" error rather than fabricating a success.
// 每个写 handler 都会持久化到真实后端存储,或用明确的
// "M10 / not-wired" 错误诚实作答,而不是编造成功。
func (s *Server) registerAdminRoutes(
mux *http.ServeMux,
ownerAuth func(http.Handler) http.Handler,
writeAuth func(http.Handler) http.Handler,
viewerAuth func(http.Handler) http.Handler,
) {
// Admin reads open to viewer: the read-only monitoring/inventory surface.
// 对 viewer 开放的管理读:只读监控/清单表面。
mux.Handle("GET /api/v1/admin/personas", viewerAuth(http.HandlerFunc(s.adminListPersonas)))
mux.Handle("GET /api/v1/admin/sessions", viewerAuth(http.HandlerFunc(s.adminListSessions)))
mux.Handle("GET /api/v1/admin/plugins", viewerAuth(http.HandlerFunc(s.adminListPlugins)))
@@ -137,21 +137,21 @@ func (s *Server) registerAdminRoutes(
mux.Handle("GET /api/v1/admin/channels", viewerAuth(http.HandlerFunc(s.adminListChannels)))
mux.Handle("GET /api/v1/admin/iot", viewerAuth(http.HandlerFunc(s.adminListIoT)))
mux.Handle("GET /api/v1/admin/automations", viewerAuth(http.HandlerFunc(s.adminListAutomations)))
// Knowledge-base document list: names/descriptions plus chunk/embedding
// status. The `?query=` search rides along here and stays read-only.
// 知识库文档列表:名字/描述加上切分/嵌入
// 状态。`?query=` 搜索在旁一并提供,且保持只读。
mux.Handle("GET /api/v1/admin/rag", viewerAuth(http.HandlerFunc(s.adminListRag)))
mux.Handle("GET /api/v1/admin/observability/metrics", viewerAuth(http.HandlerFunc(s.adminObservabilityMetrics)))
mux.Handle("GET /api/v1/admin/observability/trace", viewerAuth(http.HandlerFunc(s.adminObservabilityTrace)))
// Backend log tail: the logger's own bounded ring buffer, injected by the
// composition root (501 until then). Newest first, `?level=`/`?limit=` filtered.
// 后端日志尾部:logger 自带有界环形缓冲,由组合根注入
// (在那之前 501)。最新优先,按 `?level=`/`?limit=` 过滤。
mux.Handle("GET /api/v1/admin/observability/logs", viewerAuth(http.HandlerFunc(s.adminObservabilityLogs)))
// Unified notification feed (design/10 §3 #16): a read-only projection of the
// audit trail + backthink pushes, so the panel's notification page needs no
// new persisted model.
// 统一通知 feed(design/10 §3 #16):审计链 + 后台思考推送的
// 只读投影,因此面板的通知页不需要
// 任何新的持久化模型。
mux.Handle("GET /api/v1/admin/notifications", viewerAuth(http.HandlerFunc(s.adminListNotifications)))
// Admin reads reserved for owner/admin: private content, other accounts'
// data and the audit trail. Not open to viewer.
// 保留给 owner/admin 的管理读:私有内容、其他账号的
// 数据与审计链。不对 viewer 开放。
mux.Handle("GET /api/v1/admin/memories", writeAuth(http.HandlerFunc(s.adminListMemories)))
mux.Handle("GET /api/v1/admin/channels/messages", writeAuth(http.HandlerFunc(s.handleChannelMessages)))
mux.Handle("GET /api/v1/admin/channels/{platform}/messages", writeAuth(http.HandlerFunc(s.handleChannelPlatformLog)))
@@ -163,7 +163,7 @@ func (s *Server) registerAdminRoutes(
mux.Handle("POST /api/v1/admin/rag/query", writeAuth(http.HandlerFunc(s.adminSearchRag)))
mux.Handle("POST /api/v1/admin/plugins/policy", ownerAuth(http.HandlerFunc(s.adminSetPolicy)))
// Admin writes: owner-only, audited.
// 管理写:仅 owner,带审计。
mux.Handle("POST /api/v1/admin/personas", ownerAuth(http.HandlerFunc(s.adminCreatePersona)))
mux.Handle("PUT /api/v1/admin/personas/{id}", ownerAuth(http.HandlerFunc(s.adminUpdatePersona)))
mux.Handle("POST /api/v1/admin/users", ownerAuth(http.HandlerFunc(s.adminCreateUser)))
@@ -200,9 +200,9 @@ func (s *Server) registerAdminRoutes(
mux.Handle("POST /api/v1/admin/system/restore", ownerAuth(http.HandlerFunc(s.adminRestore)))
}
// mountWebhooks mounts externally-supplied inbound callbacks (e.g. the webhook
// channel) without gateway auth; each handler enforces its own
// signature/authorization.
// mountWebhooks 挂载外部提供的入站回调(例如 webhook
// 渠道)且不经网关认证;每个 handler 强制自己的
// 签名/授权。
func (s *Server) mountWebhooks(mux *http.ServeMux) {
for path, hdl := range s.webhooks {
if path == "" {
@@ -212,9 +212,9 @@ func (s *Server) mountWebhooks(mux *http.ServeMux) {
}
}
// withMiddleware wraps the routed handler in the outer layers, outermost first:
// the upload capability-URL guard, the SPA fallback (when a frontend is
// configured), panic recovery, CORS and request logging.
// withMiddleware 把已路由的 handler 包进外层,最外层在前:
// 上传能力 URL 防护、SPA 回退(当配置了前端时)、
// panic recover、CORS 与请求日志。
func (s *Server) withMiddleware(mux http.Handler) http.Handler {
var h http.Handler = mux
h = guardUploadPaths(h)