Skip to content

API 接口设计

本文档基于以下源文件编写:

目录

  1. 设计原则
  2. 响应格式规范
  3. 路由组织结构
  4. 认证机制
  5. Handler 创建模式
  6. 三种配置接口对比
  7. Swagger 文档规范
  8. 完整路由清单

1. 设计原则

章节来源: docs/api_response.mdAGENTS.md(后端编码约定)

Songloft 后端 API 遵循以下核心原则:

  • RESTful 直返: 成功响应直接返回业务模型或集合,不使用 {code, data, message} 统一信封。HTTP 状态码即语义,code 字段与之重复属于冗余设计。
  • 错误格式统一: 所有 JSON API 端点的错误响应统一为 {"error": "...", "detail": "..."},禁止使用 messagemsgreason 等替代字段名。
  • 资源导向路径: URL 路径以名词复数表示资源集合(/songs/playlists),HTTP 方法表达操作语义(GET 查、POST 增、PUT 改、DELETE 删)。
  • 分页标准化: 列表接口通过 limit + offset 查询参数分页,响应包含 totallimitoffset 元数据。
  • 二进制流例外: 播放(/songs/{id}/play)、代理(/proxy)、静态文件等二进制流端点的错误可使用 http.Error() 纯文本,因为客户端不期望 JSON body。

2. 响应格式规范

章节来源: internal/handlers/response.godocs/api_response.md

2.1 响应辅助函数

所有 handler 通过 respondJSON(w, status, data) 输出成功响应(data 直接序列化为顶层 JSON),通过 respondError(w, status, message, err) 输出错误(自动构建 {"error","detail"})。中间件层使用独立的 respondAuthError,格式一致。

2.2 三种成功响应形态

场景格式示例
单个实体模型直接序列化{"id":1, "title":"Track", ...}
分页列表集合名 + 分页元数据{"songs":[...], "total":100, "limit":20, "offset":0}
操作结果消息字符串{"message": "歌曲已删除"}

2.3 错误响应格式

json
{
  "error": "人类可读的错误信息",
  "detail": "可选的底层技术细节"
}
  • error -- 必有,面向用户的简短描述
  • detail -- 可选,仅当 err != nil 时输出底层错误信息

对应结构体 models.ErrorResponse,禁止自定义字段名。


3. 路由组织结构

章节来源: internal/app/routers.gointernal/jsplugin/routes.go

3.1 路由树概览

路由注册分三层,由 App.setupRouter() 统一编排:

chi.Router (根)
├── 全局中间件: Compress → Logger → Recoverer → RequestID → CORS
├── 前端静态文件 / Swagger (dev 构建)
├── /api/v1 ─┬─ [公开] /auth/login, /auth/refresh, /version, /health
│            └─ [Bearer] /auth/*, /songs/*, /playlists/*, /settings/*,
│                        /configs/*, /scan/*, /cache-manage/*, /upgrade/*, /proxy
├── /api/v1/jsplugins/* [Bearer] 插件 CRUD + 注册表 + /plugins/health
├── /api/v1/jsplugin/{entryPath}[/static/*] [公开] 插件静态资源
├── /api/v1/jsplugin-assets/* [公开] 公共 CSS/JS/字体
└── /api/v1/jsplugin/{entryPath}/* [Bearer+PublicPathChecker] API 转发

3.2 中间件栈

全局中间件在 setupBaseRouter() 中注册,按执行顺序:Compress(Gzip) -> RequestLogger(slog) -> Tracely(panic 上报) -> Recoverer(500) -> RequestID -> CORS。认证中间件 AuthMiddleware 不是全局中间件,而是在各路由组内部按需添加。

3.3 路由分组策略

Chi v5 的 r.Group() 划分认证边界:同一 /api/v1 前缀下,公开端点直接注册,需认证端点通过 r.Group + r.Use(AuthMiddleware) 包裹。JS 插件路由独立注册,静态资源无需认证,API 转发支持 PublicPathChecker 接口豁免插件声明的公开路径。


4. 认证机制

章节来源: internal/middleware/auth.go

4.1 JWT 双 Token 机制

系统使用 JWT 双 Token 认证:

  • Access Token -- 短期有效,用于 API 请求认证
  • Refresh Token -- 长期有效,用于刷新 access token

4.2 Token 传递方式

认证中间件按优先级依次尝试两种方式获取 token:

  1. Authorization Header(优先): Authorization: Bearer <token>
  2. Query Parameter(回退): ?access_token=<token>

query parameter 回退主要服务于无法自定义 Header 的场景:<img> 标签加载封面、<audio> 标签加载音频、CachedNetworkImage 等。

4.3 公开端点

以下端点无需认证,直接在 AuthMiddleware 外注册:

端点用途
POST /api/v1/auth/login用户登录
POST /api/v1/auth/refresh刷新 token
GET /api/v1/version版本信息
GET /api/v1/health健康检查
GET /api/v1/jsplugin/{entryPath}插件静态页面
GET /api/v1/jsplugin/{entryPath}/static/*插件静态资源
GET /api/v1/jsplugin-assets/*插件公共资源

此外,插件通过 manifest 中 publicPaths 声明的 API 路径,由 PublicPathChecker 接口在认证中间件内部豁免。


5. Handler 创建模式

章节来源: internal/handlers/*.gointernal/app/routers.go

5.1 工厂函数模式

每个 Handler 遵循三步创建:(1) 结构体持有 service 依赖;(2) NewXxxHandler(...) 工厂函数接收 service 返回指针;(3) 可选 SetXxx(fn) Setter 解决循环依赖或延迟绑定。以 SongHandler 为例,工厂函数接收 SongServiceCacheServiceAsyncReassigner 等 6 个依赖,创建后通过 SetGetMusicPath 注入 Scanner 的路径获取函数。

5.2 Handler 清单

项目共 14 个 Handler,均位于 internal/handlers/

  • 业务核心: AuthHandler(AuthService)、SongHandler(SongService + CacheService + 4 依赖)、PlaylistHandler(PlaylistService)、BackupHandler(BackupService)
  • 配置管理: ConfigHandler(ConfigService)、ScanHandler(SongService + Scanner + ConfigService)、HLSHandler(SongService + ConfigService)、CacheHandler(CacheService + ConfigService)、LogHandler(ConfigService + LevelVar)
  • 插件/升级: JSPluginHandler(PackageManager + Repository + Manager + SourceMetrics + ConfigService)、UpgradeHandler(UpgradeService)
  • 工具类: ProxyHandler(无依赖)、VersionHandler(无依赖)、HealthHandler(无依赖)

5.3 回调注入

部分 handler 通过 Setter 绑定跨模块回调:configHandler.SetOnConfigChanged 在通用 KV 写入后触发副作用,scanHandler.SetOnMusicPathChanged / SetOnAutoScanChanged 绑定配置变更后的重建逻辑,songHandler.SetGetMusicPath 延迟注入 Scanner 的路径函数。


6. 三种配置接口对比

章节来源: AGENTS.md(配置接口规范铁律)、internal/app/routers.go

项目中存在三种配置接口风格,各有明确分工:

维度/settings/<name>/<module>/config/configs/{key}
定位业务功能开关(用户可见)模块聚合配置admin 通用 KV 编辑
路径风格/settings/<kebab-case>/<module>/config/configs/{key}
数据形态强类型 JSON强类型 JSON{key, value} 字符串
默认值handler 内部承担handler 内部承担无(key 不存在返回 404)
副作用PUT 内部直接触发PUT 内部直接触发需挂 onConfigChanged 回调
归属对应业务模块 handler模块 handlerConfigHandler
适用场景孤立配置或跨模块共享与模块动作端点强相关admin 调试/手编

6.1 业务端点 /settings/<name>

当前 17 对 GET/PUT 端点,分布在 SongHandler(remote-title-source)、HLSHandler(hls-proxy)、ScanHandler(music-path、scan-playlist-mode、scan-auto-create-playlists、scan-title-source、auto-scan 共 5 个扫描相关)、LogHandler(log-level)、JSPluginHandler(plugin-registries、http-proxy、plugin-keep-alive、plugin-auto-update)、UpgradeHandler(github-proxy)、ConfigHandler(tab-config、library-browse、user-preferences、equalizer)。数据均为强类型 JSON,如 {enabled: bool}{proxy: string} 等,handler 内部承担默认值和副作用。

6.2 模块聚合端点

典型例子是缓存管理 /cache-manage/*config(GET/PUT)与 statscleanvalidate-dir 共用前缀和 CacheService。适用于配置与模块动作端点强相关的场景。

6.3 通用 KV /configs/{key}

仅供前端通用配置编辑器(admin 手编),无强类型、无副作用(除非挂 onConfigChanged 回调)、key 不存在时 PUT 返回 404。新业务功能禁止直调。

6.4 双入口一致性

部分配置同时被业务端点和通用 KV 修改(如 music_path)。routers.go 中的 musicPathChanged 闭包确保两条入口共享同一副作用函数 -- 业务端点在 PUT handler 内直接触发,通用 KV 通过 onConfigChanged 回调触发。


7. Swagger 文档规范

章节来源: AGENTS.md(API 文档规范铁律)

7.1 铁律

凡在 routers.go 中注册的 handler 方法,必须有 swag 注释。没有豁免。

7.2 必填字段

每个 handler 至少包含以下 7 项 swag 注释:

字段说明
@Summary一行中文摘要
@Description详细描述(副作用/默认值/错误码触发条件)
@Tags业务分组(中文),复用现有 tag
@Produce响应格式(通常 json
@Success成功响应类型与说明
@SecurityBearerAuth(公开端点省略)
@Router路径与方法

有请求体的接口额外加 @Accept json@Param request body

7.3 现有业务 Tag

歌曲管理 | 歌单管理 | 电台与 HLS | 扫描管理 | 配置管理
缓存管理 | JS插件管理 | JS 插件 | 数据备份 | 设置
升级 | 认证

禁止随手创建新 tag。

7.4 多别名路由与验证

  • 多条 alias 路径(如 /songs/{id}/play/songs/{id}/play.m3u8)每条单写一行 @Router;HEAD 不单独列
  • catch-all 路由列出所有实际方法;动态路由在 @Description 注明占位性质
  • 修改注释后必须 make swagger 重新生成,产物(docs/swagger.jsondocs/swagger.yamldocs/docs.go)必须入库
  • 验证:输出含新 @Router 路径 + grep swagger.json 命中 + 启动后 /swagger/index.html 目测

8. 完整路由清单

图表来源: internal/app/routers.gointernal/handlers/jsplugin.gointernal/jsplugin/routes.go

以下路由清单涵盖 routers.goJSPluginHandler.RegisterRoutesjsplugin/routes.go 中注册的全部端点。除特别标注外,均需 Bearer 认证。

8.1 认证 (AuthHandler)

方法路径认证说明
POST/auth/login用户登录
POST/auth/refresh刷新 token
POST/auth/logoutBearer登出
GET/auth/tokensBearer列出所有 token
GET/auth/tokens/{token_id}Bearertoken 详情
DELETE/auth/tokens/{token_id}Bearer撤销 token

8.2 歌曲 (SongHandler + HLSHandler)

方法路径说明
GET/songs歌曲列表(分页+过滤)
GET/songs/ids歌曲 ID 列表
POST/songs/remote添加远程歌曲
POST/songs/radio添加电台
POST/songs/clean清理无效歌曲
POST/songs/batch-delete批量删除
POST/songs/organize整理歌曲文件
POST/songs/organize/preview预览批量整理(dry-run)
GET/songs/duplicates重复歌曲检测
GET/songs/facets标签分类聚合
POST/songs/refresh-metadata启动远程元数据刷新
GET/songs/refresh-metadata/progress元数据刷新进度
POST/songs/refresh-metadata/cancel取消元数据刷新
GET/songs/{id}获取歌曲详情
PUT/songs/{id}更新歌曲信息
DELETE/songs/{id}删除歌曲
PUT/songs/{id}/lyrics更新歌词
PUT/songs/{id}/tags写入音频 tag
POST/songs/{id}/activate激活歌曲
POST/songs/{id}/played播放事件通知(广播给插件)
GET/HEAD/songs/{id}/play播放音频流(二进制)
GET/HEAD/songs/{id}/play.m3u8HLS 电台别名(同 handler)
GET/songs/{id}/cover歌曲封面
GET/songs/{id}/lyric歌曲歌词
GET/HEAD/songs/{id}/hls/playlistHLS 播放列表代理
GET/HEAD/songs/{id}/hls/segmentHLS 切片代理

8.3 歌单 (PlaylistHandler + BackupHandler)

方法路径说明
GET/playlists/export导出歌单
POST/playlists/import导入歌单
GET/playlists歌单列表
POST/playlists创建歌单
PUT/playlists/reorder歌单排序
GET/playlists/{id}歌单详情
PUT/playlists/{id}更新歌单
DELETE/playlists/{id}删除歌单
POST/playlists/batch-delete批量删除歌单
GET/playlists/{id}/songs歌单内歌曲列表
POST/playlists/{id}/songs添加歌曲到歌单
PUT/playlists/{id}/songs/reorder歌单歌曲排序
DELETE/playlists/{id}/songs/{songId}移除歌单歌曲
POST/playlists/{id}/touch更新歌单访问时间
POST/playlists/{id}/cover上传歌单封面
GET/playlists/{id}/cover获取歌单封面

8.4 配置与设置

方法路径Handler说明
GET/PUT/settings/remote-title-sourceSongHandler网络歌曲标题来源
GET/PUT/settings/hls-proxyHLSHandlerHLS 代理开关
GET/PUT/settings/music-pathScanHandler音乐库路径
GET/PUT/settings/scan-playlist-modeScanHandler歌单归并模式
GET/PUT/settings/scan-auto-create-playlistsScanHandler自动创建歌单
GET/PUT/settings/scan-title-sourceScanHandler标题来源
GET/PUT/settings/auto-scanScanHandler自动扫描
GET/PUT/settings/log-levelLogHandler日志等级
GET/PUT/settings/plugin-registriesJSPluginHandler插件注册表
GET/PUT/settings/http-proxyJSPluginHandlerHTTP 代理
GET/PUT/settings/plugin-keep-aliveJSPluginHandler插件常驻白名单
GET/PUT/settings/plugin-auto-updateJSPluginHandler插件自动更新
GET/PUT/settings/github-proxyUpgradeHandlerGitHub 更新代理
GET/PUT/settings/tab-configConfigHandlerTab 页配置
GET/PUT/settings/library-browseConfigHandler曲库浏览视图
GET/PUT/settings/user-preferencesConfigHandler用户偏好设置
GET/PUT/settings/equalizerConfigHandler均衡器
GET/configsConfigHandler配置列表(通用 KV)
POST/configsConfigHandler创建配置
GET/configs/{key}ConfigHandler获取配置
PUT/configs/{key}ConfigHandler更新配置
DELETE/configs/{key}ConfigHandler删除配置

8.5 扫描 (ScanHandler)

方法路径说明
POST/scan扫描并导入
GET/scan/progress扫描进度
POST/scan/cancel取消扫描
GET/scan/directories目录列表
GET/scan/dir-names目录名列表
GET/scan/fingerprints/status指纹状态
POST/scan/fingerprints启动指纹计算
GET/scan/fingerprints/progress指纹计算进度

8.6 缓存 (CacheHandler)

方法路径说明
GET/cache-manage/stats缓存统计
POST/cache-manage/clean清理缓存
GET/cache-manage/config缓存配置
PUT/cache-manage/config更新缓存配置
POST/cache-manage/validate-dir验证缓存目录

8.7 升级 (UpgradeHandler)

方法路径说明
GET/upgrade/versions版本列表
GET/upgrade/check检查更新(仅 Docker)
POST/upgrade/start开始升级
POST/upgrade/reset重置到底包
GET/upgrade/progress升级进度

8.8 JS 插件管理 (JSPluginHandler)

方法路径说明
GET/jsplugins插件列表
POST/jsplugins/upload上传插件
POST/jsplugins/update-all批量更新
POST/jsplugins/storage/cleanup清理孤儿持久化存储
POST/jsplugins/registry/refresh刷新注册表
POST/jsplugins/registry/install从注册表安装
GET/jsplugins/{id}插件详情
PUT/jsplugins/{id}更新插件
DELETE/jsplugins/{id}删除插件
POST/jsplugins/{id}/enable启用插件
POST/jsplugins/{id}/disable禁用插件
GET/jsplugins/{id}/check-update检查插件更新
POST/jsplugins/{id}/update下载更新
GET/plugins/health音源健康度

8.9 JS 插件运行时 (jsplugin.Manager)

方法路径认证说明
GET/jsplugin/{entryPath}[/]插件入口 HTML
GET/jsplugin/{entryPath}/static[/*]插件静态资源
GET/jsplugin-assets/*公共 CSS/JS/字体
GET/HEAD/jsplugin/{entryPath}/files/*Bearer插件文件服务
ANY/jsplugin/{entryPath}/*Bearer*API catch-all 转发

*注: 插件 manifest 中 publicPaths 声明的路径通过 PublicPathChecker 豁免认证。

8.10 其他

方法路径认证说明
GET/version版本信息
GET/health健康检查
GET/proxyBearer外部资源 CORS 代理

以上路径均省略 /api/v1 前缀。完整 URL 为 http://<host>:58091/api/v1/<path>