Files
xdrop/README.zh.md
T

13 KiB
Raw Blame History

Xdrop

Codecov coverage GitHub Actions CI status CodeQL code scanning status Container image publish status

English | 汉语

Xdrop 是一个开源加密文件传输应用,默认适用于浏览器,也支持智能体驱动的终端工作流,并确保服务端拿不到明文文件名、文件内容和密钥。

亮点

  • 在浏览器中完成端到端加密后再上传。
  • 支持单文件和文件夹传输,接收文件夹时也可在本地重新打包为 ZIP 下载。
  • 上传中断后可依赖浏览器本地状态继续断点续传。
  • 支持到期失效链接、发送方管理,以及上传后的可选隐私模式。
  • 后端支持兼容 S3 的对象存储,并使用 PostgreSQL 和 Redis。

通过智能体使用

你也可以把 Xdrop 作为智能体技能来用,安装存储库自带的 skill:

bunx skills add https://github.com/xixu-me/xdrop/tree/main/skills/xdrop

安装后,智能体可以直接在终端中使用 Xdrop 来:

  • 上传本地文件或目录,并返回加密分享链接。
  • 接收完整的 Xdrop 分享链接(包含 #k=...)后,在本地下载并解密文件。
  • 把文件交接流程自动化,而不用每次都切换到浏览器界面操作。

适合的场景包括:

  • 在云服务器上让智能体把构建产物、日志或备份上传到你的 Xdrop 实例,并返回一个临时链接给你。
  • 在远程开发容器或类似 CI 的环境里,让智能体把某个目录打包后通过 Xdrop 传出来,而不是临时配置 SCP 或公开对象存储权限。
  • 在本地机器上直接把完整的 Xdrop 链接交给智能体,让它下载到指定目录并完成解密。

示例指令:

  • 把 ./dist 上传到 https://xdrop.example.com,并给我一个 1 小时有效的 Xdrop 链接。
  • 在这台云服务器上把 /var/log/myapp 通过 Xdrop 发出来,我要在本地排查。
  • 把这个 Xdrop 链接下载到 ~/downloads,并保留原始目录结构。

工作原理

  1. 发送方在浏览器中创建一次传输。Xdrop 会生成随机的传输根密钥和独立的链接密钥,可选地移除图片中可删除的元数据,并在上传开始前准备好可恢复的本地状态。
  2. API 创建传输记录,并返回管理令牌和上传限制。浏览器先注册加密后的文件元数据,再分批请求分块上传所需的预签名 URL。PostgreSQL 保存传输、文件和分块元数据,Redis 负责限流,兼容 S3 的对象存储只保存密文对象。
  3. 发送方分享完整链接,例如 /t/:transferId#k=...。其中 #k=... 片段只保留在浏览器端,用于在本地解开传输根密钥。
  4. 上传过程中,文件分块会在独立的 Web Worker 中加密并流式写入存储。每上传完一个分块,浏览器都会加密清单文件、上传清单,并在最后使用封装后的根密钥完成传输。
  5. 接收方打开链接后,会获取加密清单和分块 URL,并在浏览器中完成全部解密。文件夹下载时可以在本地重新打包成 ZIP。
  6. 后台清理任务会定期从存储中删除已过期或已删除的传输对象。

Xdrop 不会让服务端接触到明文文件名、路径、内容或解密密钥。服务端仍然能看到运行层面的元数据,例如传输时间戳、文件数量、分块数量、文件大小,以及限流标识符。

关键技术细节:

  • 加密模型: 浏览器会为传输根密钥和分享链接密钥各生成一个 32 字节随机密钥。随后使用 HKDF-SHA-256 派生出清单和每个文件各自的 AES-256-GCM 密钥,并在分块加密时把 transferId、fileId、chunkIndex、大小和协议版本作为认证附加数据绑定进去。
  • 分块上传: 服务端会向浏览器声明分块大小、文件数限制和传输总大小限制。当前存储库默认使用 8 MiB 分块,最多 100 个文件,加密后的传输总大小上限为 256 MiB。
  • 断点续传: 当浏览器支持 OPFS 时,Xdrop 会把源文件持久化到本地;如果不可用,则在回退存储限制范围内使用基于 IndexedDB 的 Blob 存储。恢复上传时,浏览器会先询问 API 哪些分块已存在,因此即使刷新页面或重新打开浏览器,也只会补传缺失的部分。
  • 发送方控制: 管理令牌只会在创建时返回一次,服务端保存的是它的 SHA-256 哈希。开启隐私模式后,发送方本地控制信息可在上传完成后被清除。
  • 后端职责: API 从不解密载荷。它负责校验传输状态、限制速率、签发预签名 URL、存储元数据,并清理已过期或已删除的对象。

系统架构

flowchart LR
  subgraph Sender["发送方浏览器"]
    Select["选择文件或文件夹"]
    Worker["加密 Worker<br/>AES-256-GCM + HKDF-SHA-256"]
    Local["OPFS / IndexedDB<br/>断点状态与本地控制信息"]
    Browser["浏览器应用<br/>React + 上传/下载运行时"]
    Select --> Worker
    Worker <--> Local
    Browser <--> Worker
    Browser <--> Local
  end

  subgraph Edge["默认 Xdrop 部署"]
    nginx["nginx<br/>提供 SPA 并代理 /api + /xdrop"]
    API["Go API<br/>传输生命周期、预签名、清理"]
    nginx --> API
  end

  Postgres["PostgreSQL<br/>transfers、files、chunks、管理令牌哈希"]
  Redis["Redis<br/>限流"]
  Storage["兼容 S3 的对象存储<br/>加密清单与分块对象"]
  Receiver["接收方浏览器<br/>打开 /t/:id#k=..."]

  Browser -->|create/register/finalize| nginx
  Browser -->|presigned PUT uploads| nginx
  API --> Postgres
  API --> Redis
  API -->|presigned PUT/GET URLs| Storage
  nginx -->|/xdrop proxy| Storage
  nginx -->|web app + public API| Receiver
  Receiver -->|presigned GET downloads| nginx
  Receiver -->|decrypts locally with #k fragment| Receiver

在默认的 Docker 部署中,nginx 会提供构建后的前端资源,同时代理 /api 和 /xdrop。如果 S3_PUBLIC_ENDPOINT 指向另一个公开可访问的对象存储端点,那么预签名上传和下载请求可以绕过 nginx 代理,其余架构保持不变。

部署

推荐的生产拓扑

面向公网部署时,建议把 Xdrop 放在 Caddy 或 nginx 之类的反向代理后面:

  • 反向代理为你的公网域名终止 HTTPS。
  • xdrop 容器只监听回环地址上的主机端口,例如 127.0.0.1:8080。
  • 默认不建议公开暴露 MinIO。除非你有明确理由,否则应仅将 MinIO 端口绑定到 127.0.0.1。
  • 将 S3_PUBLIC_ENDPOINT 和 ALLOWED_ORIGINS 设置为你的公开站点 URL,例如 https://xdrop.example.com。

第一步:获取文件

如果你只想运行已经发布的镜像,就不需要在服务器上克隆整个存储库。

下载部署所需文件:

mkdir -p xdrop/infra/minio
cd xdrop
curl -fsSL -o docker-compose.yml \
  https://github.com/xixu-me/xdrop/raw/refs/heads/main/docker-compose.yml
curl -fsSL -o infra/minio/init.sh \
  https://github.com/xixu-me/xdrop/raw/refs/heads/main/infra/minio/init.sh
chmod +x infra/minio/init.sh

你也可以额外下载 .env.example 作为支持配置项的参考:

curl -fsSL -o .env.example \
  https://github.com/xixu-me/xdrop/raw/refs/heads/main/.env.example

如果你打算自行构建镜像,请改为克隆存储库,以便 Docker 拿到完整的构建上下文。通常更推荐在 CI 或另一台机器上完成构建,再把最终镜像拉到服务器上部署。

第二步:检查配置

在服务器上安装 Docker 和 Docker Compose,然后检查 docker-compose.yml 中 xdrop 服务的环境变量。

至少需要把这些值替换成真实部署所用配置:

  • S3_PUBLIC_ENDPOINT
  • ALLOWED_ORIGINS

典型的生产配置如下:

services:
  minio:
    ports:
      - '127.0.0.1:9000:9000'
      - '127.0.0.1:9001:9001'

  xdrop:
    ports:
      - '127.0.0.1:8080:80'
    environment:
      S3_PUBLIC_ENDPOINT: https://xdrop.example.com
      ALLOWED_ORIGINS: https://xdrop.example.com

请把 .env.example 视为支持配置项的参考清单。仅修改 .env.example 不会影响运行中的栈,因为存储库附带的 Compose 文件使用的是内联环境变量。

第三步:使用已发布镜像

docker compose up -d

该命令会使用 ghcr.io/xixu-me/xdrop:latest。

如果已发布镜像已经符合你的前端配置需求,这一步就足够了。

需要注意:

  • VITE_SITE_URL 这类前端构建期变量会被直接烘焙进镜像。
  • 如果你的部署使用不同的公网域名,并且你在意 canonical URL、Open Graph 元数据、JSON-LD 或 sitemap 生成,请不要直接使用已发布镜像,而应自行重建镜像。

第四步:可选,使用你自己的预构建镜像

XDROP_IMAGE=ghcr.io/your-org/xdrop:latest docker compose up -d

第五步:可选,自行构建镜像

当你需要不同的前端构建期配置时,请自行构建镜像:

git clone https://github.com/xixu-me/xdrop.git
cd xdrop
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build

运行前请先修改 docker-compose.build.yml 中的构建参数。

例如:

services:
  xdrop:
    build:
      args:
        VITE_SITE_URL: https://xdrop.example.com
        VITE_API_BASE_URL: /api/v1

在低内存服务器上直接构建可能会很慢甚至失败。这种情况下,建议在其他机器完成构建,把镜像推送到存储库后,再通过 XDROP_IMAGE 部署。

第六步:把 Xdrop 放到反向代理后面

Caddyfile 示例:

xdrop.example.com {
  encode gzip zstd
  reverse_proxy 127.0.0.1:8080
}

然后重载 Caddy:

systemctl reload caddy

栈启动后,访问 https://xdrop.example.com。

生产环境说明

  • 最终容器会用 nginx 提供构建后的前端,并在同一容器中运行 Go API。
  • 整个栈包含 xdrop、postgres、redis、minio 以及初始化 bucket 的容器。
  • 在默认单机部署中,MinIO 设计为私有服务。
  • 公网流量通常只应访问反向代理暴露的 80 和 443 端口。

开发

前置要求

  • Bun
  • Go 1.26+
  • Docker / Docker Compose

第一步:安装依赖

bun install --frozen-lockfile

第二步:启动基础服务

本地开发时,用 Docker 启动 PostgreSQL、Redis 和 MinIO:

docker compose up -d postgres redis minio minio-setup

第三步:运行 API

cd apps/api
go run ./cmd/api

第四步:运行 Web 应用

在第二个终端里,从存储库根目录执行:

bun run dev:web

打开 http://localhost:5173。本地开发时,Vite 开发服务器会代理:

  • /api 到 http://localhost:8080
  • /xdrop 到 http://localhost:9000

这样既能保留前端热更新,也能连接本地 Go API 和 MinIO。

测试

Web

bun run lint:web
bun run typecheck:web
bun run test:web
bun run test:web:coverage
bun run build:web

端到端测试

如果还没安装 Playwright 浏览器,先执行一次:

bun run test:e2e:install

E2E 套件默认会访问 http://localhost:8080 上的 Xdrop,并在测试期间使用本地 Compose 启动的 postgres 和 redis 服务。请先启动完整栈:

docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build

然后运行测试:

bun run test:e2e

如果你想指向其他环境,请设置 E2E_BASE_URL 和 E2E_API_URL。

API

在 apps/api 目录下执行:

go test ./... -coverprofile=coverage.out -covermode=atomic

部分 API 集成测试会使用基于 Docker 的 testcontainers。如果 Docker 不可用,这些测试会被跳过,覆盖率也会低于 CI。

格式化

bun run format
bun run format:check

项目结构

apps/
  api/        Go API
    cmd/api/  API 入口
    internal/ 领域包
  web/        React 前端
    public/   静态资源
    src/      应用、组件、功能模块与工具代码
packages/
  shared/     共享 TypeScript 常量与辅助函数
    src/      共享源码
tests/
  e2e/        Playwright 端到端测试
infra/        部署与容器配置
scripts/      存储库自动化与辅助脚本

环境变量

完整列表请查看 .env.example。其中最重要的设置包括:

  • API_ADDR
  • DATABASE_URL
  • REDIS_ADDR
  • S3_ENDPOINT
  • S3_PUBLIC_ENDPOINT
  • S3_BUCKET
  • ALLOWED_ORIGINS
  • VITE_API_BASE_URL
  • VITE_SITE_URL

许可证

AGPL-3.0-only。详见 LICENSE。