Files
zTools2/README.md
zikai aa08a1cf60 feat: 新增 PDF 转换服务(epub->pdf,cookie 用户隔离,软删/硬删两级删除)
纯 Python 转换(ebooklib 解析 epub + weasyprint 渲染 HTML/CSS 为 PDF),
无需 calibre/xvfb 系统依赖。复用 UploadedFile 存储(原始文件与产物 PDF)。

- model/dao/schema:PdfJob 记录转换任务(owner_cookie/source/output/进度/状态)
- pdf_converter:epub->PDF 转换器(按 spine 顺序拼 HTML,OPF 目录解析相对资源)
- pdf_service:上传落盘 + asyncio 后台转换(独立 Session)+ 进度追踪 + 两级删除
  · 用户软删(user_deleted,管理页仍可见)· 管理员硬删(真正删磁盘+记录)
- pdf_controller:/api/pdf/*(用户,cookie)+ /api/admin/pdf/*(Basic Auth)
- /pdf、/pdf-admin 页面(原生 JS,复用 common.css/js)
- 配置 pdf 段(max_size 250MB、转换超时、cookie)
- pytest 7 项(SQLite→MySQL 隔离测试库):上传/转换/下载/软删/硬删/超限/越权
2026-07-27 11:33:43 +08:00

9.2 KiB
Raw Blame History

zikai file service

基于 FastAPI 的个人 Web 服务,提供文件上传/浏览/下载、共享记事本(实时协作)、 主机监控、SFTP 暂存、反向隧道、PDF 转换。采用 Spring 风格分层架构,自带 API 文档。

功能一览

模块 页面 / 接口 鉴权
文件上传 POST /api/files/upload(流式)/ POST /api/files/chunk-uploads/*(分片+断点续传) 公开
上传页 GET /upload(拖拽/多文件/分片/去重) 公开
文件浏览 GET /files(多选/批量下载删除/分页) Basic Auth
文件管理 API GET /api/admin/filesGET/DELETE /api/admin/files/{id}GET /api/admin/files/{id}/download Basic Auth
共享记事本 GET /wb/{id}(公开,不存在则新建) 公开
记事本实时同步 WS /ws/wb/{id}(心跳 3s5 次失活移除) 公开
记事本管理 GET /wb-admin(查看/删除) Basic Auth
记事本管理 API GET /api/admin/wbDELETE /api/admin/wb/{id} Basic Auth
PDF 转换 GET /pdf(上传 epub→PDF进度轮询下载凭 cookie 记住任务) 公开cookie
PDF 转换 API POST /api/pdf/jobsGET /api/pdf/jobs[/{id}]GET /api/pdf/jobs/{id}/downloadDELETE /api/pdf/jobs/{id} 公开cookie
PDF 转换管理 GET /pdf-admin(全部任务,含已软删标记,硬删) Basic Auth
PDF 转换管理 API GET /api/admin/pdf/jobsDELETE /api/admin/pdf/jobs/{id} Basic Auth
主机监控 GET /api/system/statusCPU/内存/磁盘HTML+JSON 内容协商) 公开
反向隧道反代 ALL /api/userPort/{userName}(经 SSH 隧道转发到 user 本地服务) 公开
SFTP/SSH 端口 2022密码+公钥chroot 到上传目录,承载隧道转发) SSH
API 文档 GET /docsSwagger/ GET /redoc Basic Auth

项目结构

server/
├── app/
│   ├── main.py              # FastAPI 应用工厂、路由注册、生命周期reaper
│   ├── config.py            # 从 config.yaml 加载的类型化 Settingspydantic-settings
│   ├── database.py          # SQLAlchemy 引擎/Session/Base/get_db 依赖
│   ├── security.py          # Basic Authrequire_docs_auth常量时间比较
│   ├── controllers/         # 路由层(@RestControllerfile/system/chunk/tunnel/whiteboard/pdf/admin
│   ├── services/            # 业务层UploadService/ChunkUploadService/SystemService/
│   │                        #   WhiteboardService/WhiteboardHub/TunnelService/sftp_server
│   │                        #   PdfService转换编排+ pdf_converterepub->pdf 纯 Python
│   ├── dao/                 # 数据访问层:唯一发 SQL 的层SQLAlchemy ORM 参数化)
│   ├── models/              # ORM 实体UploadedFile/UploadSession/Whiteboard/TunnelSession/PdfJob
│   ├── schemas/             # pydantic 请求/响应 DTO
│   ├── views/               # 服务端渲染 HTML系统状态页、上传页
│   ├── static/              # 前端静态资源common + file_browser + whiteboard + pdf + pdf_admin
│   └── scripts/init_db.py   # 数据库初始化(建库建账、随机密码写回 config.yaml
├── sql/schema.sql           # 建表 DDL参考实际由 ORM 自动建表)
├── config.example.yaml      # 配置模板(含注释)
├── config.yaml              # 实际配置git-ignored含密码
├── requirements.txt
├── setup.sh                 # 一次性初始化venv + 依赖 + 建库 + SFTP 密钥
├── start.sh / stop.sh       # 启停 HTTP6867+ SFTP2022
└── logs/                    # app.log / sftp.log

请求流程controller → service → dao → ORM model → MySQL。 DB Session 由 get_db 依赖注入。前端页面走「StaticFiles 挂载 + 具名 HTML 路由」前后端分离JS 调同源 /api/...

从零安装Ubuntu 22.04+

1. 安装系统依赖

apt update
apt install -y python3-venv python3-pip mysql-server apache2 \
    libssl-dev build-essential  # build-essential 给 bcrypt/asyncssh 编译
# PDF 转换依赖 weasyprint需 pango/cairo 系统库Ubuntu 通常已随桌面环境安装,缺则补装):
apt install -y libpango-1.0-0 libpangoft2-1.0-0 libcairo2 libgdk-pixbuf-2.0-0

2. 获取代码

git clone <repo> /root/zikai
cd /root/zikai/server

3. 初始化venv + 依赖 + 建库 + SFTP 密钥)

./setup.sh

setup.sh 会:

  • 创建 .venv 并安装 requirements.txt
  • 复制 config.example.yaml → config.yaml(若不存在)
  • 通过本机 root socket 建独立 MySQL 库 zikai_filesvc + 应用账户,随机密码写回 config.yaml
  • 生成 SFTP 主机密钥(keys/ssh_host_*

4. 配置凭据

编辑 config.yaml(见下方配置说明

  • docs.username / docs.password:管理页与 API 文档的 Basic Auth 凭据
  • sftp.users[].password_hashSFTP 用户bcrypt生成方式见下
  • tunnel.users[]:反向隧道用户(可选)
# 生成 bcrypt hash
.venv/bin/python -c "import bcrypt;print(bcrypt.hashpw(b'yourpass',bcrypt.gensalt()).decode())"

5. 启动

./start.sh    # 启动 HTTP(127.0.0.1:6867) + SFTP(0.0.0.0:2022)
./stop.sh     # 停止

6. 配置 Apache 反向代理

服务只绑 127.0.0.1:6867,通过 Apache 对外提供 HTTPS。安装模块并配置 vhost

a2enmod ssl proxy proxy_http proxy_wstunnel rewrite headers

创建 /etc/apache2/sites-available/f.zikai.wang.conf(关键部分):

<VirtualHost *:443>
    ServerName f.zikai.wang
    SSLEngine on
    SSLCertificateFile     /etc/letsencrypt/live/f.zikai.wang/fullchain.pem
    SSLCertificateKeyFile  /etc/letsencrypt/live/f.zikai.wang/privkey.pem

    ProxyPreserveHost On
    ProxyPass        /fdata !
    # WebSocket 反代:/ws/ 必须在通用 / 规则之前,用 proxy_wstunnel 透传
    ProxyPass        /ws/ ws://127.0.0.1:6867/ws/
    ProxyPassReverse /ws/ ws://127.0.0.1:6867/ws/
    ProxyPass        / http://127.0.0.1:6867/
    ProxyPassReverse / http://127.0.0.1:6867/
    ProxyTimeout 300
</VirtualHost>
a2ensite f.zikai.wang
systemctl reload apache2

防火墙:放开 443HTTPS与 2022SFTP。6867 不对外(仅 loopback大文件上传Apache 全局 Timeout 300,慢链路建议走分片上传(/upload)或 SFTP。

配置说明

所有运行时配置在 config.yamlgit-ignored。完整 schema 见 config.example.yaml

关键项 说明
server host/port 绑定地址,保持 127.0.0.1:6867Apache 反代)
database host/port/user/password/database MySQL 连接;密码由 setup.sh 自动生成写回
storage upload_dir 文件存储根目录(默认 uploads
chunk_bytes 流式上传分片大小(默认 1 MiB
chunk_session_dir 分片会话暂存目录(默认 uploads/.work
chunk_session_ttl_seconds 被放弃会话存活秒数(默认 300
docs username/password /docs/files/wb-admin/api/admin/* 的 Basic Auth明文常量时间比较
sftp enabled/host/port SFTP 服务,默认 0.0.0.0:2022
users[].username/password_hash SFTP 用户bcrypt
host_key_path/authorized_keys_path 主机密钥与公钥白名单路径
tunnel enabled/users[] 反向隧道:username/password_hash/tunnel_port/local_port
whiteboard heartbeat_interval_seconds 心跳间隔(默认 3s
heartbeat_miss_threshold 失活阈值(默认 5 次 = 15s
max_board_id_length board_id 长度上限(默认 64
max_connections_per_board 单白板并发连接上限(默认 50
list_limit 管理页单次列表上限(默认 100

访问入口

入口 URL
API 文档 https://f.zikai.wang/docsBasic Auth
上传页 https://f.zikai.wang/upload
文件浏览 https://f.zikai.wang/filesBasic Auth
共享记事本 https://f.zikai.wang/wb/{id}(公开{id}[a-zA-Z0-9_-]{1,64}
记事本管理 https://f.zikai.wang/wb-adminBasic Auth
系统状态 https://f.zikai.wang/api/system/statusHTML?format=json 切 JSON
curl 上传 curl -F file=@big.iso https://f.zikai.wang/api/files/upload
SFTP sftp -P 2022 uploader@f.zikai.wang

运维

  • 日志logs/app.logHTTPlogs/sftp.logSFTPpidfileapp.pidsftp.pid
  • 临时文件清理:分片上传完成后立即删 .work/<id>/;被放弃会话(pending 超 5 分钟)由后台 reaper 每 60s 清理;start.sh 启动时兜底清残留。
  • 重新生成 DB 密码.venv/bin/python -m app.scripts.init_db(保留现有:KEEP_DB_PASSWORD=1)。
  • 多 worker 限制:记事本 hub 是进程内存,多 uvicorn worker 下不互通,保持 workers: 1
  • 数据库表ORM 启动时自动建表(init_db_schemasql/schema.sql 供参考/手动初始化。