# 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/files`、`GET/DELETE /api/admin/files/{id}`、`GET /api/admin/files/{id}/download` | Basic Auth | | 共享记事本 | `GET /wb/{id}`(公开,不存在则新建) | 公开 | | 记事本实时同步 | `WS /ws/wb/{id}`(心跳 3s,5 次失活移除) | 公开 | | 记事本管理 | `GET /wb-admin`(查看/删除) | Basic Auth | | 记事本管理 API | `GET /api/admin/wb`、`DELETE /api/admin/wb/{id}` | Basic Auth | | PDF 转换 | `GET /pdf`(上传 epub→PDF,进度轮询,下载;凭 cookie 记住任务) | 公开(cookie) | | PDF 转换 API | `POST /api/pdf/jobs`、`GET /api/pdf/jobs[/{id}]`、`GET /api/pdf/jobs/{id}/download`、`DELETE /api/pdf/jobs/{id}` | 公开(cookie) | | PDF 转换管理 | `GET /pdf-admin`(全部任务,含已软删标记,硬删) | Basic Auth | | PDF 转换管理 API | `GET /api/admin/pdf/jobs`、`DELETE /api/admin/pdf/jobs/{id}` | Basic Auth | | 主机监控 | `GET /api/system/status`(CPU/内存/磁盘,HTML+JSON 内容协商) | 公开 | | 反向隧道反代 | `ALL /api/userPort/{userName}`(经 SSH 隧道转发到 user 本地服务) | 公开 | | SFTP/SSH | 端口 2022(密码+公钥,chroot 到上传目录,承载隧道转发) | SSH | | API 文档 | `GET /docs`(Swagger)/ `GET /redoc` | Basic Auth | ## 项目结构 ``` server/ ├── app/ │ ├── main.py # FastAPI 应用工厂、路由注册、生命周期(reaper) │ ├── config.py # 从 config.yaml 加载的类型化 Settings(pydantic-settings) │ ├── database.py # SQLAlchemy 引擎/Session/Base/get_db 依赖 │ ├── security.py # Basic Auth(require_docs_auth,常量时间比较) │ ├── controllers/ # 路由层(@RestController):file/system/chunk/tunnel/whiteboard/pdf/admin │ ├── services/ # 业务层:UploadService/ChunkUploadService/SystemService/ │ │ # WhiteboardService/WhiteboardHub/TunnelService/sftp_server │ │ # PdfService(转换编排)+ pdf_converter(epub->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 # 启停 HTTP(6867)+ SFTP(2022) └── logs/ # app.log / sftp.log ``` **请求流程**:`controller → service → dao → ORM model → MySQL`。 DB Session 由 `get_db` 依赖注入。前端页面走「StaticFiles 挂载 + 具名 HTML 路由」前后端分离,JS 调同源 `/api/...`。 ## 从零安装(Ubuntu 22.04+) ### 1. 安装系统依赖 ```bash 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. 获取代码 ```bash git clone /root/zikai cd /root/zikai/server ``` ### 3. 初始化(venv + 依赖 + 建库 + SFTP 密钥) ```bash ./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_hash`:SFTP 用户(bcrypt,生成方式见下) - `tunnel.users[]`:反向隧道用户(可选) ```bash # 生成 bcrypt hash .venv/bin/python -c "import bcrypt;print(bcrypt.hashpw(b'yourpass',bcrypt.gensalt()).decode())" ``` ### 5. 启动 ```bash ./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: ```bash a2enmod ssl proxy proxy_http proxy_wstunnel rewrite headers ``` 创建 `/etc/apache2/sites-available/f.zikai.wang.conf`(关键部分): ```apache 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 ``` ```bash a2ensite f.zikai.wang systemctl reload apache2 ``` > **防火墙**:放开 443(HTTPS)与 2022(SFTP)。6867 不对外(仅 loopback)。 > **大文件上传**:Apache 全局 `Timeout 300`,慢链路建议走分片上传(`/upload`)或 SFTP。 ## 配置说明 所有运行时配置在 `config.yaml`(git-ignored)。完整 schema 见 `config.example.yaml`。 | 段 | 关键项 | 说明 | |----|--------|------| | `server` | `host`/`port` | 绑定地址,保持 `127.0.0.1:6867`(Apache 反代) | | `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/docs(Basic Auth) | | 上传页 | https://f.zikai.wang/upload | | 文件浏览 | https://f.zikai.wang/files(Basic Auth) | | 共享记事本 | https://f.zikai.wang/wb/{id}(公开,`{id}` 为 `[a-zA-Z0-9_-]{1,64}`) | | 记事本管理 | https://f.zikai.wang/wb-admin(Basic Auth) | | 系统状态 | https://f.zikai.wang/api/system/status(HTML,`?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.log`(HTTP)、`logs/sftp.log`(SFTP);pidfile:`app.pid`、`sftp.pid`。 - **临时文件清理**:分片上传完成后立即删 `.work//`;被放弃会话(`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_schema`);`sql/schema.sql` 供参考/手动初始化。