- 对象存储直链:S3 引擎 302 到限时预签名 URL(有效期钳位分享剩余时效),失败自动回落代理 - 过期回收:janitor 定时扫描 + 取件惰性回收 + 管理端手动触发(POST /admin/recycle/run), retention_days 最长存储时长;删除走引用计数(去重对象安全) - SHA512 内容去重:三条上传链路落库后计算哈希,命中即复用旧对象并删除本次副本 - 下载防盗链:Referer 白名单(同源/空 Referer/通配域名放行),挂 /share/download - 取件页图片/音频内联预览(下载地址直连,加载失败回退下载按钮) - 文件夹上传:拖拽目录明确提示"建议压缩后上传"(webkitGetAsEntry 探测) - 管理端设置卡「回收与下载安全」8 个新配置键(KVSchema + configKeys + UI + i18n) - 前端产物重建并同步 server/web/dist 与 web-embed - 文档:10-config 配置表、03-file-share 直链/防盗链/文件夹章节、07-admin 回收端点、openapi
18 KiB
管理后台 API
管理端接口:除 POST /admin/login 与初始化向导 /setup 外,一律要求
Authorization: Bearer <token>(见《认证与限流》),无效令牌 401。
初始化向导:GET /setup
未初始化时返回 HTML 配置页(站点名称、管理员密码、上传/限流/保存策略);
已初始化时 303 重定向到 /。
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) |
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"}'
{ "code": 200, "msg": "ok", "data": { "ok": true, "admin": "/#/admin" } }
错误响应(400,HTML 表单时内嵌错误提示):
{ "code": 400, "msg": "管理员密码至少 8 位" }
登录:POST /admin/login
curl -s -X POST http://localhost:8466/admin/login \
-H 'Content-Type: application/json' \
-d '{"password":"admin12345"}'
成功响应(200):
{
"code": 200, "msg": "ok",
"data": {
"id": "admin", "username": "admin",
"token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_at": 1750000000,
"expires_in": 604800
}
}
错误响应:
{ "code": 401, "msg": "密码错误" }
{ "code": 423, "msg": "请求次数过多,请稍后再试" }
校验会话:GET /admin/verify
curl -s http://localhost:8466/admin/verify -H "Authorization: Bearer $TOKEN"
{ "code": 200, "msg": "ok", "data": { "id": "admin", "username": "admin", "token": "eyJ…", "token_type": "Bearer", "expires_at": 1750000000 } }
登出:POST /admin/logout
无状态 JWT,服务端仅返回确认(客户端应丢弃令牌):
{ "code": 200, "msg": "ok", "data": { "ok": true } }
仪表盘:GET /admin/dashboard
curl -s http://localhost:8466/admin/dashboard -H "Authorization: Bearer $TOKEN"
{
"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 |
curl -s "http://localhost:8466/admin/file/list?page=1&size=10&status=active&sortBy=size&sortOrder=desc" \
-H "Authorization: Bearer $TOKEN"
{
"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(全文)。
{ "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 } }
{ "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 按时间/永久) |
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}'
{ "code": 200, "msg": "ok", "data": "更新成功" }
{ "code": 400, "msg": "code已存在" }
删除文件:DELETE /admin/file/delete
删除分享记录并连带删除存储文件(文本分享无存储文件)。DELETE 或 POST,请求体 {"id":42}。
curl -s -X DELETE http://localhost:8466/admin/file/delete \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"id":42}'
{ "code": 200, "msg": "ok", "data": null }
{ "code": 400, "msg": "请选择要删除的文件" }
批量删除:POST /admin/file/batch-delete
POST 或 DELETE,请求体 {"ids":[41,42,43]}。返回逐条统计:
{
"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(永久)。
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}'
{
"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) |
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}'
{ "code": 200, "msg": "ok", "data": { "id": 42, "action": "reset_download_limit" } }
{ "code": 400, "msg": "不支持的策略动作" }
extend_24h/extend_7d在当前过期时间(未过期时)基础上顺延;make_permanent清空过期时间并置次数 -1。
管理员下载:GET /admin/file/download?id=
下载原文件(不消耗取件次数);文件返回二进制流(支持 Range),文本分享返回 JSON(data 为文本内容)。
curl -s -OJ "http://localhost:8466/admin/file/download?id=42" -H "Authorization: Bearer $TOKEN"
文本预览:GET /admin/file/preview
仅文本分享可用(文件分享返回 400)。maxChars 截断长度(默认 4000,1~20000)。
curl -s "http://localhost:8466/admin/file/preview?id=41&maxChars=100" -H "Authorization: Bearer $TOKEN"
{
"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"
}
}
{ "code": 400, "msg": "仅文本分享支持预览" }
读取配置:GET /admin/config/get
返回运行时配置 KV(含默认值与管理端修改)。admin_token 恒返回空串(屏蔽);
jwt_secret 不下发;存储引擎为进程级单例,_engine_hint 提示引擎配置修改需重启。
26.9 新增键(需求 ①②③④⑩)一并返回:background_url、footer_text、footer_beian、
notify_enabled、max_save_count、max_file_size 等。
{
"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/switch(26.9)
运行时切换存储引擎,无需重启:
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;26.9 新增max_save_count、max_file_size、notify_enabled;opacity为浮点。 - 26.9:
site_domain(站点对外域名)可经本端点设置,非法格式 400(仅 http/https、主机+端口、不带路径)。 - 26.9 引擎键:
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)可经本端点保存——保存后对应引擎实例缓存失效, 下次切换/构建生效;敏感键空串或******表示不修改。 - 26.9 schema 校验(
settings.KVSchema,越界一律 400,中文错误信息):- 整型边界:
max_file_size≤ 10GiB(10737418240)、max_save_count≤ 100000、max_save_seconds≤ 31536000(365 天)、notify_enabled∈ {0,1}、uploadCount110000、1440 等;uploadMinute1 - 字符串长度:
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)需重启。
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}'
{ "code": 200, "msg": "ok", "data": { "ok": true } }
{ "code": 400, "msg": "adminSessionExpire 必须是 1 到 365 个整天" }
{ "code": 400, "msg": "background_url 仅支持 http(s) 地址、data:image 图片或站内相对路径" }
修改管理员密码:PATCH /admin/settings/password
PATCH 或 POST。新密码 ≥8 位;成功后哈希保存并轮换 jwt_secret,所有旧令牌失效(401),需重新登录。
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"}'
{ "code": 200, "msg": "ok", "data": { "ok": true } }
{ "code": 400, "msg": "新密码长度至少 8 位" }
{ "code": 401, "msg": "旧密码错误" }
手动回收:POST /admin/recycle/run
26.9:手动触发一轮过期分享回收(定时循环之外的管理端入口)。回收范围:
时间已过期、次数已耗尽、创建时间超过 retention_days 的分享——删除记录并
连带删除存储对象(SHA512 去重开启时做引用计数,仍有其他分享引用的对象保留)。
curl -s -X POST "http://localhost:8466/admin/recycle/run" -H "Authorization: Bearer $TOKEN"
{ "code": 200, "msg": "ok", "data": { "removed": 3 } }
相关配置键:recycle_enabled(定时开关)、recycle_interval(扫描间隔)、
retention_days(最长存储时长)、dedup_enabled(引用计数开关)。