Initial commit: DSH Web Manager
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
# DSH Web 面板 API(MVP)
|
||||
|
||||
默认监听 127.0.0.1:8088。JSON 错误统一为 {error:string}。登录使用 HttpOnly session/access/refresh cookie;写请求需 X-CSRF-Token 和精确同源 Origin(未配置 PANEL_PUBLIC_URL 时也校验)。access JWT 有效期5分钟,refresh轮换且受24小时session限制。
|
||||
|
||||
## 配置
|
||||
- UPLOAD_ALLOWED_EXTENSIONS:可选逗号分隔扩展名白名单(例如 .txt,.md);空表示不限制。大小写不敏感,非内容/恶意软件检测。
|
||||
|
||||
## 管理员孤儿回收
|
||||
|
||||
GET /api/orphans 返回可回收的完整容器ID数组。DELETE /api/orphans?id=<id>&confirm=<相同完整id> 二次核验后非强制删除,保留bind数据及网络。仅接受本项目身份/挂载匹配、已停止、无实例或升级引用的容器;普通用户403,确认不匹配400,状态不安全409。
|
||||
|
||||
## 其他配置
|
||||
- LISTEN_ADDR(默认 127.0.0.1:8088)
|
||||
- DATA_ROOT(默认 ./data)
|
||||
- HOST_DATA_ROOT(默认等于 DATA_ROOT 的绝对路径,用于 Docker bind source;容器部署必须显式设置宿主绝对路径)
|
||||
- CONTAINER_DATA_ROOT(默认 DATA_ROOT)
|
||||
- DOCKER_HOST(默认 unix:///var/run/docker.sock)
|
||||
- PANEL_PUBLIC_URL(可选)
|
||||
- ADMIN_USERNAME / ADMIN_PASSWORD(首次启动创建管理员;密码必须 12–72 字节,不设置则拒绝启动,不打印密码)
|
||||
- WEB_DIR(可选静态前端目录)
|
||||
- PANEL_CONTAINER_ID(面板容器名/ID,用于加入实例网络;宿主进程运行时留空)
|
||||
- DEFAULT_QUOTA_BYTES(默认10GiB)
|
||||
- DSH_DOCKER_ROOT_VISIBLE(与DockerRootDir相同路径的明确映射,用于构建前容量检查;未知容量拒绝新构建)
|
||||
|
||||
## 认证
|
||||
- POST /api/login {username,password} → {user,csrf_token};user 字段为 id,username,admin,quota_bytes,created_at
|
||||
- POST /api/logout → {ok:true}
|
||||
- POST /api/refresh → {ok:true}(刷新HttpOnly cookie,需Origin和CSRF)
|
||||
- GET /api/me → {user,csrf_token}
|
||||
- POST /api/me/password {current_password,password} → {ok:true,reauthenticate:true}(普通用户及管理员仅修改本人;不接收目标 ID,未知 JSON 字段返回 400)。需有效 access/session、同源 Origin 和 X-CSRF-Token;新密码为 UTF-8 12–72 字节,非法 JSON、缺少当前密码或长度不符返回 400。错误当前密码返回 400 而非 401,客户端不应因此退出登录。未登录 401,Origin/CSRF 不符 403(失效 session 在 guard 中也可能返回 403),已认证的非 POST 请求返回 405。
|
||||
- 与登录、管理员重设共用 authLoginMu,等待后复验会话与当前密码;沿用账户登录失败计数,累计 5 次错误锁定 15 分钟,锁定期间返回 429(即使当前密码正确),已登录不能绕过。认证存储故障返回 503。
|
||||
- 同一事务保存 bcrypt hash、撤销本人全部 session/refresh、清除失败计数,并记录 user.change_password 审计(操作者和目标均为本人 ID,detail 为空,无密码/hash/token);事务失败返回 500 并完整回滚,其他用户不受影响。成功后客户端应清理登录状态并重新登录;旧密码、旧 access/session/refresh 不再可用。已建立的 runtime WS/SSE 不主动断开,下一次请求或重连重新鉴权。
|
||||
|
||||
## 管理
|
||||
- GET /api/users(管理员)
|
||||
- POST /api/users {username,password,admin?,quota_bytes?}(管理员)
|
||||
- PATCH /api/users {id,quota_bytes}(管理员修改正数配额)
|
||||
- POST /api/users/reset-password {id,password} → {ok:true,reauthenticate:boolean}(仅管理员;需有效 access/session、同源 Origin 和 X-CSRF-Token)。密码按 UTF-8 字节计为 12–72 字节,以 bcrypt 保存;缺少 ID、非法 JSON 或长度不符返回 400,目标不存在 404,未认证 401,非管理员或 Origin/CSRF 不符 403(失效 session 在 guard 中也可能返回 403),已认证的非 POST 请求返回 405。
|
||||
- 同一事务更新密码、删除目标全部 session/refresh、清除目标登录失败锁定,并记录 user.reset_password 审计(操作者 ID、目标 ID、时间,detail 为空,不含密码/hash/token)。任何数据库失败返回 500 并回滚,不影响其他用户会话。与登录串行,等待后重新检查管理员会话和权限。
|
||||
- 允许重设自身,此时 reauthenticate:true,当前会话也立即失效,客户端应清理登录状态并回到登录页;重设他人返回 false。旧密码不能再登录,旧 access/refresh 不能发起新的认证请求。已建立的 runtime WS/SSE 不会被主动立即断开:代理仅在请求/握手时鉴权,下一次请求或重连会被拒绝;现有 runtime 页面刷新认证脚本不是即时断连保证。
|
||||
- GET /api/audit(管理员)
|
||||
|
||||
## 镜像与构建
|
||||
- GET /api/images
|
||||
- DELETE /api/images?image_id=sha256:...(管理员;保护实例、活动任务、升级及全部Docker容器引用,不force)
|
||||
- GET /api/latest(查询 npm dist-tags.latest)
|
||||
- GET /api/builds
|
||||
- POST /api/builds {version}(固定版本,异步;版本/模板/平台去重,新任务每用户每小时最多3个,共享复用不扣次数)
|
||||
|
||||
## 实例
|
||||
- GET /api/instances → Instance[];字段 id,uid,name,version,image_id,status,container_id,created_at
|
||||
- POST /api/instances {name,image_id,version} → Instance(每用户最多 2 个)
|
||||
- POST /api/instances/{id}/start|stop|restart
|
||||
- DELETE /api/instances/{id} → {ok:true}
|
||||
- POST /api/intents {name,version}(固定精确版本,持久预留构建后创建名额)
|
||||
- GET /api/intents(当前用户意图)
|
||||
- DELETE /api/intents/{id}(仅取消等待,不取消共享构建)
|
||||
|
||||
所有成功 JSON 列表均为顶层数组(不是 data 包装);错误均为 {error:string}。实现与验收仍在进行,不应仅凭接口列在此处认定已验收。
|
||||
- GET /api/instances/{id}/logs
|
||||
- /dsh/{id}/...(登录和 owner 校验;需同 Docker 网络)
|
||||
|
||||
## 工作区
|
||||
- GET /api/ws/tree?path=.
|
||||
- GET /api/ws/usage
|
||||
- POST /api/ws/mkdir {path}
|
||||
- POST /api/ws/rename {path,new_path}
|
||||
- DELETE /api/ws/delete?path=...
|
||||
- POST /api/ws/upload multipart file,path
|
||||
- GET /api/ws/download?path=...:普通文件保持原始流与 Range 支持;目录返回 `application/zip` 附件(目录名.zip),空 path 或 `.` 下载整个工作区(workspace.zip)。ZIP 条目相对所选目录,保留空目录,不额外包裹顶层目录,使用 Store 模式(不压缩),不支持 ZIP Range。
|
||||
- ZIP 先校验有界文件清单,再逐文件流式输出,不生成临时归档,不持 workspace/global 锁跨网络传输。每次最多 10,000 个文件/目录条目、64 层、10 GiB 文件总大小;进程最多同时 4 个目录下载,超出分别返回 413 / 429;扫描和传输合计限时 2 分钟。
|
||||
- 路径必须位于工作区内;下载拒绝任意路径分量的符号链接、跨挂载点、特殊文件和多硬链接普通文件。目录含上述条目或含反斜杠/冒号的不安全 ZIP 文件名时整体拒绝(400),不静默漏文件。依赖 Linux openat2,内核不支持时安全失败。
|
||||
- 下载不是原子快照:流式读取前复核文件身份、大小、类型及修改时间;并发修改、取消或传输错误可能导致连接中断/不完整 ZIP,请停止写入后重试。单文件下载不受 ZIP 数量、大小、并发和时限约束。
|
||||
|
||||
## profile / 备份
|
||||
- GET /api/instances/{id}/profile
|
||||
- PUT /api/instances/{id}/profile {content,restart?}(停机原子写;restart=true启动并检查就绪)
|
||||
- GET /api/instances/{id}/profile-preview(bundle清单和脱敏dump-config,字符串值隐藏;运行中才能dump)
|
||||
- POST /api/instances/{id}/backups {password}(当前登录密码加密;自动恢复原运行状态)
|
||||
- POST /api/instances/{id}/backups multipart file,password(导入外部包,password为原包密码;不立即覆盖home)
|
||||
- GET /api/instances/{id}/backups
|
||||
- GET /api/instances/{id}/backups?download={backup_id}
|
||||
- POST /api/instances/{id}/restore {backup_id,password}(原备份密码;旧平台key包可留空)
|
||||
- POST /api/instances/{id}/upgrade {image_id,password}(当前登录密码,加密pre-upgrade备份)
|
||||
|
||||
Profile写入要求实例先停止。备份/还原/升级会受控停启;恢复保留原运行状态,升级成功运行新版,失败恢复旧状态。模板2入口先准备插件再启动应用。删除实例仅删除容器与网络并移除索引,保留数据目录。升级失败恢复实例home,不撤销共享workspace或外部系统副作用。
|
||||
|
||||
已实现并通过核心集成检查:镜像复用验证、Docker 生命周期、token/cookie 桥接、加密备份恢复、同镜像替换升级。WS/SSE 代理有单元测试,真实完整会话待验证。硬配额与跨公网租户域名隔离未实现;当前 dsh 发行版不支持 --public-url。默认仅 loopback 监听,不应直接暴露公网。完整限制见 README.md。
|
||||
Reference in New Issue
Block a user