Skip to main content
K

dsh-plugin-oss-backup

kairoslll/dsh-plugin-oss-backup

DeepSeek Harness 插件:把工作区文件备份到 S3 兼容对象存储(RustFS / 阿里云 OSS / AWS S3)。AK/SK 只从环境变量读取,绝不落盘、绝不进日志。

Install

dsh plugin --profile web add github:kairoslll/dsh-plugin-oss-backup

README

dsh-plugin-oss-backup

把文件备份到对象存储的 DeepSeek Harness 插件。支持 RustFS / MinIO 等自建 S3 兼容服务、 阿里云 OSS、AWS S3 —— 一次配置,之后在设置界面点几下就能把整个文件夹传上云。


⚠️ 安全须知(先读这一段)

  1. 优先用密钥管理器把凭证注入环境变量。 1Password CLI(op run -- dsh web)、 HashiCorp Vault、AWS Secrets Manager、Azure Key Vault、Doppler、direnv 都行 —— 插件只读 process.env,所以让密钥留在它该待的地方, 远比复制粘贴到某个文件里安全。具体接法见 凭证接入指南。
  2. 不要用主账号密钥。 请新建一个只能读写指定桶的子账号,只给它 PutObject 权限。 本文「第 2 步」有现成的配置可抄。
  3. 别把密钥写进会被提交的文件 —— README、issue、脚本、截图、Dockerfile 都算。 本插件支持 .env(启动时自动读、只读不写),但它只适合本地临时使用; 用之前务必确认 .env 在 .gitignore 里(本仓库已挡 .env / .env.* / *.key / *.pem)。
  4. 密钥一旦出现在聊天记录、日志或提交历史里,就当它已经泄漏 —— 立刻去服务商后台轮换掉。
  5. 本插件自己不会把密钥写到磁盘上。 密钥只在内存里流转,界面永远只显示"已设置 / 未设置", 日志里也只会出现变量名,不会出现变量值。(.env 是你写的、插件只读 —— 落不落盘由你决定。)
  6. 想确认这一点,可以在配好之后自己检查:~/.dsh/oss-backup/config.json 里 只会有开关、目录、桶名这类非机密信息,不会有任何密钥。

一、它能做什么

四个用法

用法说明
手动备份整个文件夹在设置界面填一个目录,点「立即备份」,它就会把该目录(含所有子目录)递归传上去
定期全量备份按时间表(每 N 分钟 / 小时 / 天,或每天、每月固定时刻)自动把整个目录完整传一遍
文件改动自动备份打开开关后,每次保存文件(模型用 write / edit 改文件)都会在 2 秒后自动上传
让模型上传单个文件模型可以直接调用 oss_backup 工具,把一个本地文件传到指定的对象路径
《接数据》(趣味)点「立即备份」时先在「上传状态」框里玩一局节奏小游戏,通关才真的开始备份。不想要可以在设置里关掉

支持的对象存储

provider适用寻址方式
rustfs(默认)RustFS、MinIO 等自建 S3 兼容服务path-style
aliyun阿里云 OSS虚拟主机风格
awsAWS S3(也可指向其它 S3 兼容服务)虚拟主机风格

已实测通过:RustFS 真实上传(本机)、阿里云连通性。AWS 与阿里云的完整上传流程 依赖你自己的账号环境,仓库里的配置示例可直接照抄。

界面上有什么

  • 设置 → 对象存储备份(与主面板内容一致):
    • 顶部两张卡片:存储端点、环境状态(是否配好、密钥是否已设置);
    • 上传状态:进度条 + 最近几次上传的大小;;
    • 云端备份按钮:打开当前 provider 的网页控制台,方便核对桶里的内容;
    • 设置:自动备份开关、备份目录、「跳过依赖 / 缓存目录」开关,以及折叠的「高级设置」;
    • 凭证:在界面里临时填写密钥(见第四节);
    • 手动备份:「立即备份」按钮 + 进度条 + 上次结果;
    • 环境自检:provider / 端点 / 桶 / 区域 / 密钥是否已设置;
    • 最近上传:时间、对象路径、大小。
  • 左侧栏:一栏状态提示(已就绪 / 上传中 / 缺配置 / 未连接)。

二、安装

cd <你的插件目录>
pnpm add dsh-plugin-oss-backup          # 或把打好的 .tgz 装进 DSH profile

装完后重启 dsh web,设置页里就会出现「对象存储备份」。

本地开发时安装的坑(点开看)

不要用 link: 安装本地包。 症状是启动时报 dsh-plugin-oss-backup: failed to import: link: 会在 profile 里建一个指向源码目录的软链接,而 Node 解析模块时会先取真实路径, 于是解析起点跳出了 ~/.dsh/profiles 目录树,插件依赖的宿主模块就找不到了。

正确做法是先打包再安装:

pnpm pack                                   # 产出 dsh-plugin-oss-backup-x.y.z.tgz
pnpm add file:<绝对路径>/dsh-plugin-oss-backup-x.y.z.tgz

三、配置(照着做就行)

第 1 步:决定用哪个 provider

选一个就好:

OSS_PROVIDER=rustfs     # 默认值,自建服务(RustFS / MinIO)
OSS_PROVIDER=aliyun     # 阿里云 OSS
OSS_PROVIDER=aws        # AWS S3

不设置 OSS_PROVIDER 时按 rustfs 处理。

第 2 步:创建最小权限子账号(必做)

不要跳过这一步。 用主账号密钥意味着一旦泄漏,整个云账号都危险。

阿里云 OSS(RAM 子账号)

  1. 控制台 → RAM 访问控制 → 用户 → 创建用户,勾选"OpenAPI 调用访问" (只拿 AccessKey,不要开控制台登录);
  2. 创建自定义权限策略(不要用 AliyunOSSFullAccess 这类系统策略),把 dsh-backup 换成你自己的桶名:
{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["oss:PutObject", "oss:GetObject", "oss:HeadObject"],
      "Resource": ["acs:oss:*:*:dsh-backup/*"]
    },
    {
      "Effect": "Allow",
      "Action": ["oss:ListObjects", "oss:GetBucketInfo"],
      "Resource": ["acs:oss:*:*:dsh-backup"]
    }
  ]
}
  1. 把这个策略授权给刚建的用户;
  2. 记下它的 AccessKey ID 与 Secret,下一步要用。

AWS S3(IAM 用户)

把上面的策略换成 IAM 语法即可(Resource 形如 arn:aws:s3:::dsh-backup/*):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowBackupObjectsOnly",
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
      "Resource": ["arn:aws:s3:::dsh-backup/*"]
    },
    {
      "Sid": "AllowListOwnBucket",
      "Effect": "Allow",
      "Action": ["s3:ListBucket", "s3:GetBucketLocation"],
      "Resource": ["arn:aws:s3:::dsh-backup"]
    }
  ]
}

RustFS / MinIO

mc admin user add <alias> dsh-backup-bot '<一个强随机密码>'
mc admin policy create <alias> dsh-backup-rw ./dsh-backup-rw.json   # 内容即上面的通用策略
mc admin policy attach <alias> dsh-backup-rw --user dsh-backup-bot

第 3 步:设置环境变量

挑你自己那一档,把值换成上面拿到的密钥。

强烈建议优先用密钥管理器接入。 下面的 setx 示例只是"最不依赖任何工具"的写法; 更好的做法是让 1Password / Vault / AWS Secrets Manager 这类工具在启动 dsh web 时 把凭证注入环境变量 —— 密钥就不用出现在 shell 历史、脚本或任何磁盘文件里。 完整的接入清单(1Password / Bitwarden / Vault / AWS / Azure / Doppler / direnv / Docker / systemd)见 凭证接入指南。

先说清一件事:值从哪来都行。 插件只关心"这个键在你的进程环境里存不存在", 不关心它怎么来的。下面这些完全等价,用你顺手的那个:

来源怎么做
密钥管理器(推荐)op run -- dsh web(1Password)、doppler run -- dsh web、Vault / AWS Secrets Manager 等 —— 见接入指南
系统环境变量Windows setx、macOS / Linux export(下面就是这一种)
.env 文件复制 .env.example 改成 .env,放到 <DSH home>/oss-backup/.env 或当前工作目录 —— 插件启动时自动读(只适合本地临时用,务必确认它被 gitignore)
Dockerdocker run -e RUSTFS_ACCESS_KEY=...
CIGitHub Actions Secrets / GitLab CI Variables 之类

.env 的几条规则(只读,插件永不写入):

  • 优先级:.env > 系统环境变量。.env 是你显式写下来的那份,比机器上残留的 旧 export 更权威 —— 否则"我在 .env 里改了却毫无效果"会非常难查。 被覆盖的键名会进日志与界面(只有名字,没有值);
  • 只采用第一个能读到的文件(先看 <DSH home>/oss-backup/.env,再看当前工作目录), 不做多层合并 —— 否则出问题时没法解释某个值来自哪份文件;
  • 解析规则就那几条:KEY=value、KEY="value"、export KEY=value、# 注释。 不支持行内注释、多行值、变量展开 —— 支持它们只会让"值被悄悄改掉"成为可能的解释;
  • 改了 .env 要重启 dsh web 才生效(和系统环境变量一样)。

① RustFS / MinIO

setx OSS_PROVIDER      "rustfs"
setx RUSTFS_ENDPOINT   "http://127.0.0.1:9000"
setx RUSTFS_BUCKET     "dsh-backup"
setx RUSTFS_ACCESS_KEY "<子账号 AK>"
setx RUSTFS_SECRET_KEY "<子账号 SK>"

② 阿里云 OSS

setx OSS_PROVIDER           "aliyun"
setx OSS_BUCKET             "dsh-backup"
setx OSS_REGION             "oss-cn-hangzhou"        # 默认就是这个
# OSS_ENDPOINT 可以留空:会自动拼成 https://oss-cn-hangzhou.aliyuncs.com
setx OSS_ACCESS_KEY_ID      "<RAM 子账号 AccessKey ID>"
setx OSS_ACCESS_KEY_SECRET  "<RAM 子账号 AccessKey Secret>"

如果你显式设 OSS_ENDPOINT,一定要带上 https://。不带协议头时会被当成 http:// 处理,连接必然失败。

③ AWS S3

setx OSS_PROVIDER           "aws"
setx AWS_BUCKET             "my-backup-bucket"
setx AWS_REGION             "us-east-1"              # 必填
setx AWS_ACCESS_KEY_ID      "<IAM 用户 AccessKey ID>"
setx AWS_SECRET_ACCESS_KEY  "<IAM 用户 Secret Access Key>"
# AWS_ENDPOINT 一般留空,表示用 AWS 官方端点

macOS / Linux 把 setx X "v" 换成 export X="v",并写进你的 shell 配置文件。

第 4 步:重启 dsh web

这一步不能省。 环境变量是进程启动时读取的,正在运行的 dsh web 不会自动感知新变量。

第 5 步:确认配置成功

重启后打开 设置 → 对象存储备份,看「环境自检」:

  • 状态应显示「就绪」;
  • provider 一行会写明当前用的是哪一档(例如 RustFS / MinIO 等自建 S3 兼容端点 · path-style);
  • 密钥两行应显示「已设置(值不显示)」。

⚠️ 「就绪」不只看变量配没配齐:插件会真的对存储桶发一次 HeadBucket 去确认服务能连上 (结果缓存 30 秒)。所以还有第三种状态:

状态含义怎么办
就绪变量齐了,服务也连得上可以直接用
变量齐了,但服务连不上端点/桶/密钥有一处不对,或者服务根本没启动看下方红字里的具体原因
缺少 N 个变量环境变量没配齐那一行会列出缺哪个变量名

「服务连不上」这一条是专门为下面这种情况加的:变量都填好了、但 RustFS 没开 —— 以前会显示成"就绪",看着一切正常,实际上每次上传都在连接被拒。

小贴士:setx 设了但插件说"未配置"? setx 写入的是用户级环境变量,而已启动的进程(尤其是从快捷方式、启动器拉起来的 dsh web)不一定继承得到它。所以插件每次启动都会主动去读一遍系统里的环境变量 (先看进程已有的,再看用户级,最后看系统级),缺什么补什么 —— 不会覆盖你已经设好的值。 界面上会标注「当前来自系统环境变量(启动时自动补读:…)」,看到它就说明补读成功了。 你只需要保证设过值 + 重启过一次。


四、使用

手动备份一个文件夹

  1. 打开 设置 → 对象存储备份 → 设置;
  2. 在「备份目录」里填要备份的目录(例如 D:\projects\my-project);
  3. 点「保存」;
  4. 回到「手动备份」块,点「立即备份」。

它会递归备份整个文件夹,完成后显示:共多少个文件 → 成功几个 / 失败几个 · 总大小。

没变化的文件会自动跳过。 插件在 ~/.dsh/oss-backup/uploaded.json 里记着每个对象键对应的 (文件大小, 修改时间),下次备份时一致的直接跳过 —— 所以重复点「立即备份」只会传真正改过的 那几个,不会再把你几千个没动过的文件重传一遍。界面会显示「跳过 N 个未变化的」。

判定依据是大小 + 修改时间。改了内容一般也就会改修改时间,所以会被正确重传; 只有"内容变了但大小和修改时间都没变"这种刻意为之的情况才会漏判。 换机器或删掉那个索引文件也不影响正确性 —— 只是会重传一次,之后恢复正常。

上传后的对象路径规则是 前缀 + 被备份目录自己的名字 + 相对路径。例如备份 D:\projects\projects\yy(里面是 11、22 两个子文件夹),桶里会出现:

workspace-backup/yy/11/...
workspace-backup/yy/22/...

yy 这一层会保留,这样在桶里一眼就能看出这批文件属于哪个目录。

会自动跳过的目录:node_modules、.git、.hg、.svn、.pnpm-store、.npm-cache、 __pycache__、.venv。这些要么体积巨大,要么是可再生的产物。 想让备份"一个文件都不少",把设置里的「跳过依赖 / 缓存目录」开关关掉即可。

限额:最多 2000 个文件、单个文件最大 5 GB、总量 50 GB。触发上限时界面会提示 「已触到文件数上限」。

超过 8 MB 的文件走分片上传(@aws-sdk/lib-storage,每片 8 MB、4 片并发), 内存占用恒定、与文件大小无关 —— 所以单文件上限敢放到 5 GB(S3 单次 PUT 的上限; 分片协议本身允许到 5 TB)。这几个数只是防呆,用来拦住"误选了整个磁盘"。

误选了也没关系:备份途中可以点「强行打断」。 它会立刻停下 —— 正在传的那个文件 被掐断(在途的分片请求一并中止),已经传完的不受影响,也不会被记成"失败"。 下次备份会从没传完的地方继续(查重会跳过已成功的那部分)。

《接数据》(备份前的小游戏)

点「立即备份」时,如果游戏开关是开着的,会先在「上传状态」那一格里来一局节奏小游戏 《接数据》—— 通关了才真的开始备份。 不想玩的话,把设置里的开关关掉,之后点按钮直接开始。

怎么玩

  • 四条轨道,方块从右往左滑;键盘 D F J K 对应四条轨道(键位标在判定线左侧,一直在), 也可以直接用鼠标点那条轨道;
  • 左侧有一条判定线,方块经过它的一瞬间点掉它;
  • 判定分三级,窗口挺严的:
判定条件扣分
PERFECT±0.09 秒内点中不扣分
GOOD±0.175 秒内(不够准)扣 0.5 个
LOST过了 0.175 秒都没点扣 1 个

命中时判定线会亮起、对应轨道扫过一层光、判定处扩散一圈光环 —— PERFECT 是品牌色、GOOD 是灰色, 一眼就能看出自己准不准。

四档难度

难度一局时长特点
杂鱼约 22 秒
简单约 29 秒
中等约 36 秒
齁哦哦哦约 45 秒

难度会记住,下次打开还是这一档。

计分规则

满分 100 分,扣分按总方块数分摊:

扣分 = (GOOD × 0.5 + LOST × 1) ÷ 总方块数 × 100

也就是 PERFECT 完全不扣分,GOOD 扣半个,LOST 扣一个。参考:

  • 全部 PERFECT → 100 分;
  • 全部 GOOD → 50 分(这也会失败);
  • 全部 LOST → 0 分。

结果为:

  • 扣分 < 30 → 通过,备份自动开始;
  • 扣分 ≥ 30 → 失败,备份不会触发

用"按总数分摊"而不是"每个失败固定扣几分",是为了让四档难度的容错率一致 —— 高难度方块多得多,固定扣分会让它直接变成不可能完成。

文件改动自动备份

在设置里把「自动备份」开关打开(点一下立即生效)。

之后每次模型保存文件,该文件会在停止改动 2 秒后自动上传;上传失败会自动重试 3 次 (间隔 1 秒 / 2 秒 / 4 秒),仍失败就只记一条日志,不会影响你正常的写文件操作。 开关关闭时完全不生效,这是默认状态。

连不上时直接不提供上传

  • 确认连不上之后,所有上传入口一起停:模型调 oss_backup 会被直接拒绝、 「立即备份」按钮变灰并说明原因、自动备份与定期备份都不再排期;
  • 连接 / 凭证 / 桶名这类错误不重试 —— 它们不会因为多等 4 秒就变好,直接失败;
  • 连续失败 3 次也会熔断,暂停 60 秒,期间的文件改动直接跳过、不排队;
  • 界面上写明原因(例如 ECONNREFUSED),服务恢复后自动重新可用,不用重启。

注意:这个开关监听的是模型的文件写入操作。如果你用命令行工具(例如 git、 脚本)直接改文件,不会触发自动备份 —— 那种情况请用手动备份。

定期全量备份

在设置界面的「定期全量备份」一行选模式(改完点「保存」生效),宿主会按时间表把整个备份目录 完整传一遍 —— 与上面「文件改动自动备份」是两套独立的机制,可以只开一个,也可以都开。

模式说明例子
关闭默认,不排期—
按间隔每 N 分钟 / 小时 / 天每 6 小时
每天每天固定时刻每天 03:00
每月每月固定几号的固定时刻每月 1 日 03:00

设置那一行下面会显示下次实际触发时间,方便确认排期对不对。

几个刻意的设计,避免你踩坑:

  • 固定节拍:「按间隔」对齐到排定时刻,不是"从现在往后加一段"。每 30 分钟就是 :00 和 :30,每 6 小时就是 00/06/12/18,「每 N 天」对齐到当地零点 —— 一次备份跑多久都不会把后续时间点往后推;
  • 内部用的是"跑完再排下一次"的链式定时器(不是 setInterval),所以也不会堆叠出好几次备份;
  • 上一次还在跑就跳过这一轮(不排队),日志里会记一条"跳过";
  • 每月只支持 1-28 号:这样每个月都一定存在,不会遇到"2 月没有 30 号"那种要么跳过一个月、 要么被顺延到 3 月 1 号的情况;
  • 还没设置备份目录时不会排期,只记一条日志提醒你;
  • 时间点用的是本机时区。

备份范围、排除规则(node_modules / __pycache__ 等)和文件数 / 体积限额, 与「手动备份」完全一致 —— 它俩共用同一套逻辑。

凭证(在设置界面里临时填写密钥)

如果你不想用 setx,也可以在设置界面的「凭证」块里直接填 AK/SK,点 「应用到本次进程」:

  • 填完立刻生效,可以马上试一次手动备份;
  • 只对本次运行有效 —— 重启 dsh web 后需要重新填;
  • 提交后输入框会立即清空,界面上永远不会回显你填过的值;
  • 想让配置永久有效、以后都不用填,还是用 setx(见第三节)。

高级设置

展开「高级设置」,可以改这些非机密项(会保存下来,重启后仍在):

项说明
对象键前缀桶里的目录层级,默认 workspace-backup/
Provider覆盖 OSS_PROVIDER(留空则用环境变量)
端点覆盖端点(留空则用环境变量或按区域自动推导)
存储桶覆盖桶名(留空则用环境变量)
区域覆盖区域(留空则用环境变量或默认值)

优先级:Provider 以设置页为准(这是你在界面上明确选的),端点 / 桶 / 区域 以环境变量为准(已经设过的不会被悄悄改掉)。

让模型上传单个文件

模型可以直接调用 oss_backup 工具,传一个本地文件和目标对象路径,例如:

把 D:\backup\report.tar.gz 上传到对象存储,objectKey 用 backups/report.tar.gz

这是单文件上传;要备份整个文件夹请用上面的「手动备份」。

云端备份按钮

「云端备份」按钮会打开当前 provider 对应的网页控制台,方便你在网页里核对桶里的内容。 地址由插件按你的配置推导,换 provider 之后按钮自己就指对了:

provider打开哪里
rustfs(自建 / MinIO)本机网页控制台 —— 由端点端口 +1 推出来(http://127.0.0.1:9000 → http://127.0.0.1:9001/)
aliyun阿里云 OSS 控制台,直达当前这个桶的对象列表
awsAWS S3 控制台,直达当前这个桶(带上你配的 region)

按钮旁边会显示完整地址并做成真链接 —— 万一浏览器拦下了新窗口,点链接照样能打开。 端点或桶还没配好时,这一块会直说"地址推导不出来",而不是给一个打不开的链接。


五、常见问题

Q:环境自检显示「缺少 N 个变量」怎么办? 那一行会列出具体缺哪个变量名。按第三节补上,然后重启 dsh web。

Q:明明 setx 过了,为什么还说未配置? 两个常见原因:① 设完没有重启 dsh web;② 变量设到了别的账户下 (例如你在管理员终端里 setx,但 dsh web 跑在普通用户下)。 插件启动时会主动补读系统环境变量,所以只要"设过 + 重启过"就会生效。

Q:上传失败,怎么排查? 界面的「手动备份」块会显示失败条数,以及最多 5 条失败原因;「最近上传」里也有记录。 常见原因:密钥没有目标桶的写权限、桶名写错、端点地址不对、网络不通。

Q:备份目录里文件太多,会不会传很久? 会。手动备份是逐个文件上传的,且带上面说的限额。建议备份项目目录, 而不是整个磁盘根目录。

Q:重启后设置还在吗? 在。「开关 / 备份目录 / 前缀 / 跳过缓存 / provider / 端点 / 桶 / 区域」都保存在 ~/.dsh/oss-backup/config.json,重启后自动恢复。 只有密钥在"界面临时填写"那种方式下需要重填 —— 用 setx 就没这个问题。

Q:界面上会显示我的密钥吗? 不会,任何时候都不会。界面只显示「已设置」或「未设置」。

Q:备份上去的文件怎么恢复? 直接用你对象存储服务商的控制台或命令行下载即可(对象路径见上面的规则)。 本插件只负责上传。

Q:能同时配多个桶 / 多套账号吗? 不能。插件一次只对一个存储目标生效,切换需要改环境变量或「高级设置」里的覆盖值。


六、安全说明

你的密钥是怎么被处理的

这个插件按四条原则设计,每一条都由代码结构保证,而不是靠"记得别打印":

原则具体做法
代码里没有明文密钥整个仓库里只有变量名(如 RUSTFS_ACCESS_KEY),没有任何真实值;.env.example 里全是空占位
日志和错误里不出现密钥加载日志只报变量名;报错只说缺哪个变量;所有对外文本在出口还会再走一遍脱敏,抹掉密钥字面量以及签名形态(X-Amz-Signature、Signature=、Credential=、URL 里的用户名密码等)
密钥不写入本地文件插件不读 .env;config.json 只保存非机密项;界面上临时填写的密钥只存在于进程内存里
前端拿不到密钥界面跑在浏览器沙箱里,没有读取环境变量的能力。它唯一的数据来源是一条只读的脱敏状态接口,密钥部分只有"是 / 否"。界面填写的密钥是单向送进宿主的,宿主永远不回传

你应该注意什么

-一定一定优先用密钥管理器接入!!!本插件不自带密钥管理器功能。

  • 务必用最小权限子账号,并且只授权你真正需要的那个桶;
  • 不要在共享电脑、共享屏幕上操作「凭证」块,也不要在填完后让别人看你的浏览器;
  • 定期在服务商后台轮换密钥;
  • 如果怀疑泄漏,先吊销再排查,不要抱侥幸心理;
  • 本插件只能保护"它自己不泄露密钥",无法保护你把它贴到聊天窗口或提交进 Git。

仓库自带的检查

如果你要参与开发或二次分发,仓库里有两道自动检查:

pnpm run check:secrets   # 扫描仓库里有没有明文密钥(会与环境里真实的凭证值比对)
pnpm run check:load      # 构建产物能否正常加载

CI(GitHub Actions)里还跑 Gitleaks,会扫描提交历史,一旦发现疑似密钥就让构建失败。


七、开发者信息

pnpm install
pnpm run build          # 编译 host(tsc)+ 打包 client(esbuild)
pnpm test               # 258 个单元测试
pnpm run typecheck:client
pnpm run check:secrets
pnpm run check:load

项目结构

src/
  index.ts              host 入口:注册工具 + 四条 HTTP 路由(状态 / 设置 / 凭证 / 手动备份)
  env.ts                provider 档案(rustfs / aliyun / aws)与环境变量校验
  env-source.ts         启动时补读系统环境变量(Windows)
  dotenv.ts             启动时补读 `.env`(**只读,永不写入**;优先级高于系统环境)
  settings.ts           设置读写(只存非机密项)
  auto-backup.ts        自动备份:防抖 + 指数退避重试 + 对象键净化
  manual-backup.ts      批量备份:目录遍历、排除名单、限额、查重、逐个上传
  upload-index.ts       已上传文件索引(只存大小与修改时间,用于跳过没变化的文件)
  upload.ts             S3 客户端构造 + PutObject
  redact.ts             出口脱敏
  client/               界面(设置页 / 侧边栏 / 主面板)
assets/
  code-uploading.jpg    上传中弹出的那张图
scripts/                构建、真实上传冒烟、密钥自检、加载自检
tests/                  vitest

发布

pnpm run build && pnpm test && pnpm run check:secrets && pnpm run check:load
npm pack --dry-run      # 确认包里有 lib/、assets/、cordis.patch.yml、README.md、LICENSE
npm publish

设计上的两个注意点

  • webServer 必须是硬依赖(inject 里列上)。用软取(ctx.get)时,apply 阶段该服务 可能还没就绪,会导致路由静默不注册,症状是设置页永远"未连接"。
  • client bundle 的模块 id 必须是包名。client-modules 按最近归属的 package.json 判定浏览器模块 id,写错会报 loaded without registering 并让界面半边失效。

License

MIT —— 见仓库根目录的 LICENSE 文件。


形象来源与许可

「接数据」小游戏与界面中出现的鲸鱼娘等形象取自 @ZipZipPipe, 基于 @上善无形 原创角色二创,依 CC BY-NC-SA 4.0 协议非商业使用。

这些形象不在本项目的 MIT 许可范围内 —— MIT 只覆盖代码。

Related plugins