refactor: 清理死代码/提前失败/日志/高内聚低耦合
死代码移除: - whiteboard_hub.py: 移除未引用的 reset_hub 单例重置函数 - tunnel_service.py: 移除未引用的 is_port_allowed (逻辑已在 sftp_server 内联) - tunnel_session_dao.py: 移除未引用的 get_active_by_port - pdf_job_dao.py: 移除未用 datetime 导入 - pdf_converter.py: 移除未用 shutil 导入 - pdf_service.py: 移除未用 PdfSubmitResponse 导入 + _do_convert 内未用 hashlib 导入 - upload_html.py: 移除未用 escape 导入 (JS 侧自有 escapeHtml) - pdf_controller.py: 移除 _resolve_cookie 内未用 cfg 局部变量 提前失败/分层修复: - database.py init_db_schema: 建表后用 inspector 校验既有表列与模型一致, 缺列即抛 RuntimeError (fail-fast on schema drift), 避免运行期才暴露 - whiteboard_dao.get_or_create: 仅 IntegrityError 才回滚重读, 其他异常向上抛 (原 except Exception 会掩盖 schema/连接等真实故障) - pdf_service.admin_delete/_safe_delete_file: 改用 PdfJobDAO.delete / UploadedFileDAO.delete, 不再直接操作 job_dao.db / file_dao.db (修复分层契约: DAO 头注释声明 service 不直接操作 session) - PdfJobDAO 新增 delete(job) 方法 日志补全 (8 处 silent catch): - whiteboard_hub.py disconnect/close_board 关闭 ws: logger.debug - whiteboard_controller _safe_send/_safe_close: logger.debug - sftp_server _close_tunnel_dao/读用户名: logger.debug - sftp_server validate_public_key: logger.warning (auth 路径, 避免静默失败) 文档: - 新增 docs/routes.md, docs/configuration.md, docs/error-handling.md - README.md 精简为简介/结构/外部依赖/apache2 配置/Ubuntu 安装/docs 链接
This commit is contained in:
193
README.md
193
README.md
@@ -1,125 +1,47 @@
|
||||
# zikai file service
|
||||
# zTools2 - 个人 Web 服务后端
|
||||
|
||||
基于 FastAPI 的个人 Web 服务,提供**文件上传/浏览/下载、共享记事本(实时协作)、
|
||||
主机监控、SFTP 暂存、反向隧道、PDF 转换**。采用 Spring 风格分层架构,自带 API 文档。
|
||||
基于 FastAPI 的个人 Web 服务后端,提供文件上传/浏览/下载、共享记事本(实时协作)、主机监控、SFTP 暂存、反向隧道、PDF 转换。采用 Spring 风格分层架构(controller -> service -> dao -> ORM model -> MySQL),自带 API 文档。
|
||||
|
||||
## 功能一览
|
||||
|
||||
| 模块 | 页面 / 接口 | 鉴权 |
|
||||
|------|------------|------|
|
||||
| 文件上传 | `POST /api/files/upload`(流式)/ `POST /api/files/chunk-uploads/*`(分片+断点续传) | 公开 |
|
||||
| 上传页 | `GET /api/upload`(拖拽/多文件/分片/去重) | 公开 |
|
||||
| 文件浏览 | `GET /api/files-page`(多选/批量下载删除/分页) | Basic Auth |
|
||||
| 文件管理 API | `GET /api/admin/files`、`GET/DELETE /api/admin/files/{id}`、`GET /api/admin/files/{id}/download` | Basic Auth |
|
||||
| 共享记事本 | `GET /api/wb-page/{id}`(公开,不存在则新建) | 公开 |
|
||||
| 记事本实时同步 | `WS /api/ws/wb/{id}`(心跳 3s,5 次失活移除) | 公开 |
|
||||
| 记事本管理 | `GET /api/wb-admin`(查看/删除) | Basic Auth |
|
||||
| 记事本管理 API | `GET /api/admin/wb`、`DELETE /api/admin/wb/{id}` | Basic Auth |
|
||||
| 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 /api/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 |
|
||||
|
||||
## 路由约定
|
||||
|
||||
**所有 zTools2 托管的入口(页面 / 静态资源 / 健康探针 / WebSocket / REST API)统一挂在 `/api/` 前缀下**,只有元信息/文档例外(`/`、`/docs`、`/redoc`、`/openapi.json`)。这样反向代理与 vite dev proxy 都只需一条 `/api/` 规则即可把请求转给当前环境的 zTools2,前端用**同源相对路径**(如 `/api/pdf/jobs`、`/api/health`)即可,与运行环境(本地 / 测试 / 生产)无关,无需区分 dev/prod 指向。
|
||||
|
||||
> PDF 转换的用户侧 UI 由 zMainPage 的 zPDF_package 组件提供(构建期 import,非 iframe);zTools2 仅提供 `/api/pdf/jobs` 等 REST API 与 `/api/pdf-admin` 管理页。
|
||||
|
||||
页面类入口为避免与同名 REST API 冲突,统一加 `-page` 后缀:
|
||||
|
||||
| 类型 | 页面入口 | REST API(同名不加后缀) |
|
||||
|------|---------|------------------------|
|
||||
| 文件浏览 | `GET /api/files-page` | `GET /api/files`、`/api/files/{id}` 等 |
|
||||
| 记事本 | `GET /api/wb-page/{id}` | `GET /api/wb/{id}` |
|
||||
|
||||
其余入口:`/api/health`(探针)、`/api/static/*`(JS/CSS)、`/api/ws/wb/{id}`(WebSocket)、`/api/upload`、`/api/pdf-admin`、`/api/wb-admin`。
|
||||
所有入口统一挂在 `/api/` 前缀下,前端用同源相对路径调用,无需区分 dev/prod。前端子项目([timeTableFix](https://git.zikai.wang/zikai/timeTableFix)、[zPDF_package](https://git.zikai.wang/zikai/zPDF_package)、[zWhiteBoard](https://git.zikai.wang/zikai/zWhiteBoard))经 [zMainPage](https://git.zikai.wang/zikai/zMainPage) 构建期组件 import 集成,生产由 apache2 静态托管 + `/api` 反代到本服务。
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
server/
|
||||
zTools2/
|
||||
├── app/
|
||||
│ ├── main.py # FastAPI 应用工厂、路由注册、生命周期(reaper)
|
||||
│ ├── config.py # 从 config.yaml 加载的类型化 Settings(pydantic-settings)
|
||||
│ ├── database.py # SQLAlchemy 引擎/Session/Base/get_db 依赖
|
||||
│ ├── database.py # SQLAlchemy 引擎/Session/Base/get_db 依赖 + schema 校验
|
||||
│ ├── 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)
|
||||
│ ├── controllers/ # 路由层:file/system/chunk/tunnel/whiteboard/pdf/admin
|
||||
│ ├── services/ # 业务层:Upload/ChunkUpload/System/Whiteboard/Tunnel/Pdf/sftp
|
||||
│ ├── dao/ # 数据访问层:唯一发 SQL 的层(SQLAlchemy ORM 参数化)
|
||||
│ ├── models/ # ORM 实体:UploadedFile/UploadSession/Whiteboard/TunnelSession/PdfJob
|
||||
│ ├── schemas/ # pydantic 请求/响应 DTO
|
||||
│ ├── views/ # 服务端渲染 HTML(系统状态页、上传页)
|
||||
│ ├── static/ # 前端静态资源(common + file_browser + whiteboard + pdf + pdf_admin)
|
||||
│ ├── static/ # 前端静态资源
|
||||
│ └── 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
|
||||
├── start.sh / stop.sh # 启停 HTTP(127.0.0.1:6867)+ SFTP(2022)
|
||||
└── deploy/ # systemd 持久化部署
|
||||
```
|
||||
|
||||
**请求流程**:`controller → service → dao → ORM model → MySQL`。
|
||||
DB Session 由 `get_db` 依赖注入。前端页面走「StaticFiles 挂载 + 具名 HTML 路由」前后端分离,JS 调同源 `/api/...`。
|
||||
**请求流程**:`controller -> service -> dao -> ORM model -> MySQL`。DB Session 由 `get_db` 依赖注入。
|
||||
|
||||
## 从零安装(Ubuntu 22.04+)
|
||||
## 外部依赖
|
||||
|
||||
### 1. 安装系统依赖
|
||||
| 项 | 说明 |
|
||||
|----|------|
|
||||
| Python | ≥ 3.11(用 `.venv`) |
|
||||
| MySQL | 8.x(独立库 `zikai_filesvc`,由 `setup.sh` 建账) |
|
||||
| Apache2 | 反向代理对外提供 HTTPS;服务本身只绑 `127.0.0.1:6867` |
|
||||
| 系统库 | `libpango/cairo`(weasyprint PDF 转换)、`build-essential`(bcrypt/asyncssh 编译) |
|
||||
|
||||
```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 <repo> /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 反向代理
|
||||
### Apache2 反向代理配置
|
||||
|
||||
服务只绑 `127.0.0.1:6867`,通过 Apache 对外提供 HTTPS。安装模块并配置 vhost:
|
||||
|
||||
@@ -154,62 +76,49 @@ systemctl reload apache2
|
||||
> **防火墙**:放开 443(HTTPS)与 2022(SFTP)。6867 不对外(仅 loopback)。
|
||||
> **大文件上传**:Apache 全局 `Timeout 300`,慢链路建议走分片上传(`/api/upload`)或 SFTP。
|
||||
|
||||
## 配置说明
|
||||
## 从零安装(Ubuntu 22.04+)
|
||||
|
||||
所有运行时配置在 `config.yaml`(git-ignored)。完整 schema 见 `config.example.yaml`。
|
||||
### 1. 安装系统依赖
|
||||
|
||||
| 段 | 关键项 | 说明 |
|
||||
|----|--------|------|
|
||||
| `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`、`/api/files-page`、`/api/wb-admin`、`/api/pdf-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) |
|
||||
```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 系统库:
|
||||
apt install -y libpango-1.0-0 libpangoft2-1.0-0 libcairo2 libgdk-pixbuf-2.0-0
|
||||
```
|
||||
|
||||
## 访问入口
|
||||
### 2. 获取代码并初始化
|
||||
|
||||
| 入口 | URL |
|
||||
|------|-----|
|
||||
| API 文档 | https://f.zikai.wang/docs(Basic Auth) |
|
||||
| 上传页 | https://f.zikai.wang/api/upload |
|
||||
| 文件浏览 | https://f.zikai.wang/api/files-page(Basic Auth) |
|
||||
| 共享记事本 | https://f.zikai.wang/api/wb-page/{id}(公开,`{id}` 为 `[a-zA-Z0-9_-]{1,64}`) |
|
||||
| 记事本管理 | https://f.zikai.wang/api/wb-admin(Basic Auth) |
|
||||
| PDF 转换 | 经 zMainPage 的 PDF 页签(zPDF_package 组件)调用 `/api/pdf/jobs` 等 REST API(公开,凭 cookie) |
|
||||
| 系统状态 | 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` |
|
||||
```bash
|
||||
git clone <repo> /root/zikai
|
||||
cd /root/zikai/zTools2
|
||||
./setup.sh # 创建 .venv + 装依赖 + 复制 config.yaml + 建 MySQL 库账 + 生成 SFTP 密钥
|
||||
```
|
||||
|
||||
## 运维
|
||||
### 3. 配置凭据
|
||||
|
||||
- **日志**:`logs/app.log`(HTTP)、`logs/sftp.log`(SFTP);pidfile:`app.pid`、`sftp.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_schema`);`sql/schema.sql` 供参考/手动初始化。
|
||||
编辑 `config.yaml`(详见 [`docs/configuration.md`](./docs/configuration.md)):`docs.username/password`(管理页 Basic Auth)、`sftp.users[].password_hash`(bcrypt)、可选 `tunnel.users[]`。
|
||||
|
||||
## 持久化部署(systemd)
|
||||
### 4. 启动
|
||||
|
||||
`./start.sh` 适合手动运维;生产推荐用 systemd 管理实现开机自启 + 崩溃自动重启:
|
||||
```bash
|
||||
./start.sh # 启动 HTTP(127.0.0.1:6867) + SFTP(0.0.0.0:2022)
|
||||
./stop.sh # 停止
|
||||
```
|
||||
|
||||
### 5. 持久化部署(systemd)
|
||||
|
||||
```bash
|
||||
./deploy/install-systemd.sh # 安装并启动 ztools2 服务(开机自启)
|
||||
systemctl status ztools2 # 状态
|
||||
journalctl -u ztools2 -f # 日志
|
||||
sudo systemctl restart ztools2 # 重启(更新代码后)
|
||||
systemctl status ztools2
|
||||
journalctl -u ztools2 -f
|
||||
```
|
||||
|
||||
脚本自动检测 `.venv/bin/uvicorn` 或系统 `uvicorn`,用实际路径填充 `deploy/ztools2.service` 模板。卸载:`systemctl disable --now ztools2 && rm /etc/systemd/system/ztools2.service && systemctl daemon-reload`。
|
||||
> 与 zMainPage 整体部署配合:systemd 管后端,`zMainPage/deploy.sh --no-restart` 部署前端。
|
||||
|
||||
> 与 zMainPage 整体部署配合:systemd 管后端,`zMainPage/deploy.sh --no-restart` 部署前端。详见 zMainPage 的 [`docs/deployment.md`](https://git.zikai.wang/zikai/zMainPage/src/branch/main/docs/deployment.md)。
|
||||
## 了解更多
|
||||
|
||||
- [路由与访问入口](./docs/routes.md)
|
||||
- [配置说明](./docs/configuration.md)
|
||||
- [错误处理与日志约定](./docs/error-handling.md)
|
||||
|
||||
Reference in New Issue
Block a user