Files
FileShare/docs/api/07-admin.md
T
SKYMirror 9686fe887a FileCodeBox Go 重写版 v2.5.6(安全审计修复版)
Go 1.27.1 (Gin+GORM) + Vue 3 文件快传服务:

- 安全审计全部修复(docs/security-audit-2026-09-05.md):
  bcrypt 密码哈希与自动升级、presign 直传服务端大小/内容校验、
  全局请求体上限、依赖升级(govulncheck 0 命中)、janitor 后台清理、
  管理端审计动作落库、/admin CORS 收紧、通知内容白名单净化、
  会话默认 7 天、限流缓存故障降级、robots.txt 端点等
- 前端:取件链接复制修复(不再重复拼接提取码)、markdown 净化器加固
- Redis 支持库号(FCB_REDIS_DB / redis://…/db URL)
- 文档:docs/api/* 与 openapi.yaml 同步最新行为(robots.txt、
  提码 5 位起、chunk 32MiB 上限、admin 审计动作等)

验证:gofmt/go vet/go test 全绿;二进制端到端冒烟通过
2026-09-05 04:22:41 +08:00

435 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 管理后台 API
管理端接口:除 `POST /admin/login` 与初始化向导 `/setup` 外,一律要求
`Authorization: Bearer <token>`(见《认证与限流》),无效令牌 401。
## 初始化向导:GET /setup
未初始化时返回 HTML 配置页(站点名称、管理员密码、上传/限流/保存策略);
已初始化时 `303` 重定向到 `/`
```bash
curl -i http://localhost:8466/setup
```
## 初始化提交:POST /setup
表单(浏览器向导)或 JSON 均可;成功后写入库配置 KV 并生成密码哈希与 `jwt_secret`
表单提交返回成功 HTML 页;JSON 提交返回 JSON。
**主要字段**
| 字段 | 必需 | 默认 | 说明 |
|---|---|---|---|
| `admin_password` | ✅ | - | 管理员密码(≥8 位) |
| `confirm_password` | ✅ | - | 确认密码(须一致) |
| `site_name` | ❌ | 文件快传 | 站点名称 |
| `upload_size_value` / `upload_size_unit` | ❌ | 10 / MB | 单文件大小限制(单位 KB/MB/GB) |
| `save_time_value` / `save_time_unit` | ❌ | 0 / day | 最长保存秒数(0=不限) |
| `expireStyle` | ❌ | day,hour,minute,forever,count | 过期方式(可多值/逗号分隔) |
| `code_generate_type` | ❌ | secret | 取件码类型 `number`/`secret` |
| `errorCount` / `errorMinute` | ❌ | 10 / 1 | 取件错误限流 |
| `loginCount` / `loginMinute` | ❌ | 5 / 15 | 登录失败限流 |
| `uploadCount` / `uploadMinute` | ❌ | 10 / 1 | 上传限流 |
| `allowed_file_types` | ❌ | `*` | 逗号分隔白名单 |
| `openUpload` / `enableChunk` | ❌ | 1 / 0 | 游客上传 / 分片开关(`1`/`true`/`on`/`yes` |
```bash
curl -s -X POST http://localhost:8466/setup \
-H 'Content-Type: application/json' \
-d '{"admin_password":"admin12345","confirm_password":"admin12345","site_name":"我的文件柜","upload_size_value":10,"upload_size_unit":"MB"}'
```
```json
{ "code": 200, "msg": "ok", "data": { "ok": true, "admin": "/#/admin" } }
```
**错误响应**(400,HTML 表单时内嵌错误提示):
```json
{ "code": 400, "msg": "管理员密码至少 8 位" }
```
## 登录:POST /admin/login
```bash
curl -s -X POST http://localhost:8466/admin/login \
-H 'Content-Type: application/json' \
-d '{"password":"admin12345"}'
```
**成功响应**200):
```json
{
"code": 200, "msg": "ok",
"data": {
"id": "admin", "username": "admin",
"token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_at": 1750000000,
"expires_in": 604800
}
}
```
**错误响应**
```json
{ "code": 401, "msg": "密码错误" }
```
```json
{ "code": 423, "msg": "请求次数过多,请稍后再试" }
```
## 校验会话:GET /admin/verify
```bash
curl -s http://localhost:8466/admin/verify -H "Authorization: Bearer $TOKEN"
```
```json
{ "code": 200, "msg": "ok", "data": { "id": "admin", "username": "admin", "token": "eyJ…", "token_type": "Bearer", "expires_at": 1750000000 } }
```
## 登出:POST /admin/logout
无状态 JWT,服务端仅返回确认(客户端应丢弃令牌):
```json
{ "code": 200, "msg": "ok", "data": { "ok": true } }
```
## 仪表盘:GET /admin/dashboard
```bash
curl -s http://localhost:8466/admin/dashboard -H "Authorization: Bearer $TOKEN"
```
```json
{
"code": 200, "msg": "ok",
"data": {
"totalFiles": 42,
"storageUsed": "123456789",
"sysUptime": 1750000000000,
"yesterdayCount": 5, "yesterdaySize": "1048576",
"todayCount": 12, "todaySize": "5242880",
"activeCount": 40, "expiredCount": 2,
"textCount": 10, "fileCount": 32, "chunkedCount": 3,
"usedCount": 156,
"storageBackend": "local",
"uploadSizeLimit": 10485760,
"openUpload": 1, "enableChunk": 1,
"maxSaveSeconds": 0,
"topSuffixes": [ { "suffix": ".pdf", "count": 12 }, { "suffix": "Text", "count": 10 } ],
"recentFiles": [ { "id": 42, "code": "K3P9W", "name": "report.pdf", "size": 1048576, "created_at": "2025-06-01T12:00:00+08:00", "expired_at": "2025-06-08T12:00:00+08:00", "is_expired": false, "expired_count": -1, "used_count": 3, "is_text": false, "is_chunked": false, "is_permanent": false, "has_download_limit": false, "remaining_downloads": null, "file_hash": null, "prefix": "report", "suffix": ".pdf", "text": false, "createdAt": "2025-06-01T12:00:00+08:00", "expiredAt": "2025-06-08T12:00:00+08:00", "isExpired": false, "expiredCount": -1, "usedCount": 3, "isText": false, "isChunked": false, "isPermanent": false, "hasDownloadLimit": false, "remainingDownloads": null, "fileHash": null } ],
"recentActivities": []
}
}
```
> `storageUsed`/`todaySize`/`yesterdaySize` 为字符串字节数;`sysUptime` 为服务启动时刻的 Unix 毫秒。
## 文件列表:GET /admin/file/list
**参数**
| 参数 | 默认 | 说明 |
|---|---|---|
| `page` / `size` | 1 / 10 | 分页(size 1~100 |
| `keyword` | - | 模糊匹配取件码/文件名/哈希/文本内容 |
| `status` | - | `active` / `expired` |
| `type` | - | `text` / `file` / `chunked` |
| `sortBy` | `created_at` | `created_at`/`expired_at`/`name`/`size`/`used_count`/`code` |
| `sortOrder` | `desc` | `asc` / `desc` |
```bash
curl -s "http://localhost:8466/admin/file/list?page=1&size=10&status=active&sortBy=size&sortOrder=desc" \
-H "Authorization: Bearer $TOKEN"
```
```json
{
"code": 200, "msg": "ok",
"data": {
"page": 1, "size": 10, "total": 40,
"summary": { "totalFiles": 42, "activeCount": 40, "expiredCount": 2, "textCount": 10, "fileCount": 32, "chunkedCount": 3, "storageUsed": 123456789, "usedCount": 156 },
"data": [
{ "id": 42, "code": "K3P9W", "name": "report.pdf", "prefix": "report", "suffix": ".pdf", "size": 1048576, "is_text": false, "is_chunked": false, "is_expired": false, "expired_at": "2025-06-08T12:00:00+08:00", "expired_count": -1, "used_count": 3, "created_at": "2025-06-01T12:00:00+08:00", "has_download_limit": false, "is_permanent": false, "remaining_downloads": null, "file_hash": null, "text": false, "isText": false, "isChunked": false, "isExpired": false, "expiredAt": "2025-06-08T12:00:00+08:00", "expiredCount": -1, "usedCount": 3, "createdAt": "2025-06-01T12:00:00+08:00", "hasDownloadLimit": false, "isPermanent": false, "remainingDownloads": null, "fileHash": null }
]
}
}
```
## 文件详情:GET /admin/file/detail
`GET ?id=42``POST {"id":42}`。返回列表条目字段;文本分享额外含 `content`(全文)。
```json
{ "code": 200, "msg": "ok", "data": { "id": 42, "code": "K3P9W", "name": "report.pdf", "size": 1048576, "is_text": false, "expired_at": "2025-06-08T12:00:00+08:00", "expired_count": -1, "used_count": 3, "created_at": "2025-06-01T12:00:00+08:00", "file_hash": null } }
```
```json
{ "code": 404, "msg": "文件不存在" }
```
## 更新文件:PATCH /admin/file/update
更新取件码/文件名(前后缀)/过期策略。**PATCH 为主名,POST 为兼容别名**。
**请求体**
| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | int | 必需 |
| `code` | string | 新取件码(冲突 400「code已存在」) |
| `prefix` / `suffix` | string | 文件名前后缀 |
| `expired_at` | string | 过期时间(ISO 8601,如 `2025-07-01T00:00:00+08:00` |
| `expired_count` | int | 取件次数上限(`-1` 按时间/永久) |
```bash
curl -s -X PATCH http://localhost:8466/admin/file/update \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"id":42,"expired_at":"2025-07-01T00:00:00+08:00","expired_count":10}'
```
```json
{ "code": 200, "msg": "ok", "data": "更新成功" }
```
```json
{ "code": 400, "msg": "code已存在" }
```
## 删除文件:DELETE /admin/file/delete
删除分享记录并连带删除存储文件(文本分享无存储文件)。`DELETE``POST`,请求体 `{"id":42}`
```bash
curl -s -X DELETE http://localhost:8466/admin/file/delete \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"id":42}'
```
```json
{ "code": 200, "msg": "ok", "data": null }
```
```json
{ "code": 400, "msg": "请选择要删除的文件" }
```
## 批量删除:POST /admin/file/batch-delete
`POST``DELETE`,请求体 `{"ids":[41,42,43]}`。返回逐条统计:
```json
{
"code": 200, "msg": "ok",
"data": {
"requestedCount": 3, "deletedCount": 2, "missingCount": 1, "failedCount": 0,
"deleted": [41, 42], "missing": [43], "failed": [],
"requested_count": 3, "deleted_count": 2, "missing_count": 1, "failed_count": 0
}
}
```
## 批量更新:PATCH /admin/file/batch-update
`PATCH``POST`。请求体:`ids[]` 必需;`expired_at`ISO 8601/`expired_count` 二选一;
`clearExpiredAt: true`(或 `clear_expired_at`= 清空过期时间并置 `expired_count=-1`(永久)。
```bash
curl -s -X PATCH http://localhost:8466/admin/file/batch-update \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"ids":[41,42],"clearExpiredAt":true}'
```
```json
{
"code": 200, "msg": "ok",
"data": { "requestedCount": 2, "updatedCount": 2, "missingCount": 0, "failedCount": 0, "updated": 2, "missing": [], "failed": [], "requested_count": 2, "updated_count": 2, "missing_count": 0, "failed_count": 0 }
}
```
## 过期策略动作:PATCH /admin/file/policy-action
对单个文件执行快捷策略。`PATCH``POST`。批量版为 `/admin/file/batch-policy-action``{"ids":[…]}`),响应结构同批量更新。
**请求体**
| 字段 | 说明 |
|---|---|
| `id` | 文件 ID |
| `action` | `extend_24h` / `extend_7d` / `make_permanent` / `reset_download_limit` |
| `downloadLimit` | 仅 `reset_download_limit` 用:新取件次数(默认 5,须 >0) |
```bash
curl -s -X PATCH http://localhost:8466/admin/file/policy-action \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"id":42,"action":"reset_download_limit","downloadLimit":3}'
```
```json
{ "code": 200, "msg": "ok", "data": { "id": 42, "action": "reset_download_limit" } }
```
```json
{ "code": 400, "msg": "不支持的策略动作" }
```
> `extend_24h`/`extend_7d` 在当前过期时间(未过期时)基础上顺延;`make_permanent` 清空过期时间并置次数 -1。
## 管理员下载:GET /admin/file/download?id=
下载原文件(**不消耗取件次数**);文件返回二进制流(支持 Range),文本分享返回 JSON(`data` 为文本内容)。
```bash
curl -s -OJ "http://localhost:8466/admin/file/download?id=42" -H "Authorization: Bearer $TOKEN"
```
## 文本预览:GET /admin/file/preview
仅文本分享可用(文件分享返回 400)。`maxChars` 截断长度(默认 40001~20000)。
```bash
curl -s "http://localhost:8466/admin/file/preview?id=41&maxChars=100" -H "Authorization: Bearer $TOKEN"
```
```json
{
"code": 200, "msg": "ok",
"data": {
"id": 41, "code": "8XQ2M", "name": "Text.txt", "type": "text",
"content": "你好,文件快传",
"length": 24, "previewLength": 24, "truncated": false,
"maxChars": 100, "max_chars": 100,
"created_at": "2025-06-01T12:00:00+08:00", "createdAt": "2025-06-01T12:00:00+08:00"
}
}
```
```json
{ "code": 400, "msg": "仅文本分享支持预览" }
```
## 读取配置:GET /admin/config/get
返回运行时配置 KV(含默认值与管理端修改)。`admin_token` 恒返回空串(屏蔽);
`jwt_secret` 不下发;存储引擎为进程级单例,`_engine_hint` 提示引擎配置修改需重启。
v2 新增键(需求 ①②③④⑩)一并返回:`background_url``footer_text``footer_beian`
`notify_enabled``max_save_count``max_file_size` 等。
```json
{
"code": 200, "msg": "ok",
"data": {
"site_name": "文件快传",
"name": "文件快传",
"description": "开箱即用的文件快传系统",
"page_explain": "…", "keywords": "…",
"notify_title": "系统通知", "notify_content": "…", "notify_enabled": 1,
"logo_url": "",
"favicon_url": "",
"background_url": "", "footer_text": "", "footer_beian": "",
"openUpload": 1, "uploadSize": 10485760,
"max_file_size": 0, "max_save_count": 0,
"allowed_file_types": ["*"], "expireStyle": ["day","hour","minute","forever","count"],
"code_generate_type": "secret", "enableChunk": 1,
"uploadMinute": 1, "uploadCount": 10,
"errorMinute": 1, "errorCount": 10,
"loginCount": 5, "loginMinute": 15,
"max_save_seconds": 0, "storageLimit": 0,
"opacity": 0.9, "background": "", "showAdminAddr": 0, "robotsText": "User-agent: *\nDisallow: /",
"adminSessionExpire": 604800,
"storage_path": "", "local_storage_path": "/app/data",
"file_storage": "local",
"admin_token": "",
"_engine_hint": { "storage_backend": "local", "note": "存储引擎为进程级单例,修改存储引擎相关配置后需重启服务生效" }
}
}
```
## 存储引擎热切换:POST /admin/storage/switchv3
运行时切换存储引擎,**无需重启**:
```bash
curl -s -X POST http://localhost:8466/admin/storage/switch \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"engine":"s3"}'
# 成功:{"code":200,"msg":"ok","data":{"ok":true,"engine":"s3"}}
# 失败:503 {"code":503,"msg":"存储引擎切换失败,已保持原引擎: …"}
```
- `engine` 仅接受 `local|s3|webdav`400 中文错误)。
- 切换流程:用最新 KV 参数构建新引擎 → 健康检查 → 通过才替换当前引擎并持久化 `storage_engine`
- 失败(构建/健康检查不过)返回 503,**原引擎与 KV 均保持不变**。
- 切到当前引擎为幂等操作(直接返回 200)。
- 旧文件按归属引擎(`file_codes.engine`)取回,切换后旧引擎文件仍可下载。
## 更新配置:PATCH /admin/config/update
部分更新(JSON 对象,未提供的键不变;表单亦可)。**PATCH 为主名,POST 为兼容别名**。
- 仅接受管理端可见键(见上响应键集合),未知键忽略。
- 数值型键自动转型:`openUpload``enableChunk``uploadSize``storageLimit`、限流四组、`max_save_seconds``adminSessionExpire``showAdminAddr`v2 新增 `max_save_count``max_file_size``notify_enabled``opacity` 为浮点。
- **v3.1**`site_domain`(站点对外域名)可经本端点设置,非法格式 400(仅 http/https、主机+端口、不带路径)。
- **v3 引擎键**`storage_engine` 不经本端点修改(走 `POST /admin/storage/switch`);引擎参数键
`local_storage_path``webdav_url``webdav_root_path``webdav_username``webdav_password`
`s3_endpoint_url``s3_region_name``s3_bucket_name``s3_access_key_id``s3_secret_access_key`
`aws_session_token``s3_addressing_style`)可经本端点保存——保存后对应引擎实例缓存失效,
下次切换/构建生效;敏感键空串或 `******` 表示不修改。
- **v2 schema 校验**`settings.KVSchema`,越界一律 400,中文错误信息):
- 整型边界:`max_file_size` ≤ 10GiB10737418240)、`max_save_count` ≤ 100000、`max_save_seconds` ≤ 31536000365 天)、`notify_enabled` ∈ {0,1}、`uploadCount` 1~10000、`uploadMinute` 1~1440 等;
- 字符串长度:`background_url` ≤ 2048、`footer_text` ≤ 2000、`footer_beian` ≤ 128、`notify_title` ≤ 128、`notify_content` ≤ 2000 字符;
- 列表键 `expireStyle` / `allowed_file_types`:须为字符串数组(或逗号分隔串)且至少保留一项;
- 错误示例:`{"code":400,"msg":"max_file_size 必须是整数"}``{"code":400,"msg":"max_file_size 不能大于 10737418240"}``{"code":400,"msg":"footer_beian 长度不能超过 128 字符"}``{"code":400,"msg":"notify_enabled 不能大于 1"}`
- `background_url` 协议白名单(需求 ①,防 `javascript:` 注入):仅 `http(s)://``data:image/*` 与站内相对路径(`/`开头);空串=清除背景。非法值 400:
`{"code":400,"msg":"background_url 仅支持 http(s) 地址、data:image 图片或站内相对路径"}`
- `admin_token`:明文密码自动哈希,**并轮换 `jwt_secret`(全部管理员令牌立即失效)**;空串忽略;已是哈希格式则原样保存。
- `adminSessionExpire` 须为 1~365 的整天秒数(86400 的整数倍),否则 400。
- `storageLimit` 不能小于 0。
- 修改限流/策略配置**立即生效**(无需重启:限流规则运行时同步,策略由上传链路每次实时读取);引擎相关(`file_storage`/`s3_*`/`webdav_*`/`storage_path`/`local_storage_path`)需重启。
```bash
curl -s -X PATCH http://localhost:8466/admin/config/update \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"site_name":"我的文件柜","uploadSize":52428800,"openUpload":1,"footer_beian":"京ICP备20240001号","max_file_size":10485760,"max_save_count":5}'
```
```json
{ "code": 200, "msg": "ok", "data": { "ok": true } }
```
```json
{ "code": 400, "msg": "adminSessionExpire 必须是 1 到 365 个整天" }
```
```json
{ "code": 400, "msg": "background_url 仅支持 http(s) 地址、data:image 图片或站内相对路径" }
```
## 修改管理员密码:PATCH /admin/settings/password
`PATCH``POST`。新密码 ≥8 位;成功后哈希保存并**轮换 `jwt_secret`,所有旧令牌失效(401)**,需重新登录。
```bash
curl -s -X PATCH http://localhost:8466/admin/settings/password \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"old_password":"admin12345","new_password":"new-pass-6789"}'
```
```json
{ "code": 200, "msg": "ok", "data": { "ok": true } }
```
```json
{ "code": 400, "msg": "新密码长度至少 8 位" }
```
```json
{ "code": 401, "msg": "旧密码错误" }
```