Files
zTools2/README.md
zikai 80b96d236f init: 从 /root/zikai 根目录迁入
把原本散落在 /root/zikai 的 FastAPI 服务整理到 server/ 子目录,
开启独立 git 与 venv。运行时数据(config.yaml / keys / uploads)
按 .gitignore 留在工作目录但不入仓。
2026-06-23 16:17:31 +00:00

136 lines
5.5 KiB
Markdown

# zikai file service
A Python web service (FastAPI) for **f.zikai.wang** that provides host
monitoring and large-file upload over **both HTTP and SFTP**. It follows a
Spring-style layered architecture (`controllers``services``dao`, plus
`models` and `schemas`), ships with auto-generated API docs, and runs entirely
from a self-contained `.venv`.
## Features
- `GET /api/system/status` — CPU, memory, and per-disk usage (via `psutil`).
- `POST /api/files/upload`**streamed** multipart upload (flat memory, multi-GB friendly), with SHA-256.
- `GET /api/files`, `GET /api/files/{id}`, `GET /api/files/{id}/download`.
- **Embedded SFTP server** (asyncssh) supporting **password + public-key** auth, sharing the same storage as HTTP.
- `/docs` (Swagger UI) and `/redoc` — interactive, auto-lists all APIs.
- Metadata persisted in an **independent MySQL database** (`zikai_filesvc`).
- `start.sh` / `stop.sh` lifecycle; `setup.sh` for one-time provisioning.
## Architecture (Spring-style layers)
```
app/
├── controllers/ # FastAPI routers — HTTP boundary (like @RestController)
├── services/ # business logic (SystemService, UploadService, SFTP server)
├── dao/ # data access objects — the only layer that issues SQL/ORM
├── models/ # SQLAlchemy ORM entities
├── schemas/ # pydantic DTOs (request/response validation)
├── database.py # engine, session, Base, get_db() dependency
├── config.py # typed Settings loaded from config.yaml
└── scripts/ # init_db.py — DB provisioning
```
Request flow: **controller****service****dao****ORM model** → MySQL.
The DB session is injected by FastAPI's `get_db` dependency and passed down.
## Quick start
```bash
cd /root/zikai
./setup.sh # one-time: venv, deps, provision DB + user, SFTP host key
./start.sh # start HTTP (127.0.0.1:6867) + SFTP (0.0.0.0:2022)
./stop.sh # stop both
```
`setup.sh` is re-runnable. It creates the `.venv`, installs `requirements.txt`,
copies `config.example.yaml``config.yaml` (if absent), provisions a **new
independent MySQL database and app user** via the local root socket, and
generates the SFTP host key.
## Access
| Where | URL |
|------|-----|
| Status page (HTML) | https://f.zikai.wang/api/system/status |
| Status page (JSON) | https://f.zikai.wang/api/system/status?format=json (or `Accept: application/json`) |
| API docs (Swagger) | https://f.zikai.wang/docs **(HTTP Basic Auth — see `docs:` in config.yaml)** |
| API docs (ReDoc) | https://f.zikai.wang/redoc (same auth) |
| Upload | `curl -F file=@big.iso https://f.zikai.wang/api/files/upload` |
| SFTP | `sftp -P 2022 uploader@f.zikai.wang` |
`/api/system/status` content-negotiates: browsers (`Accept: text/html`) get a
human-readable page with progress bars; API clients get JSON. Force one with
`?format=html` or `?format=json`.
`/docs`, `/redoc`, and `/openapi.json` require HTTP Basic Auth — the browser
will prompt you. Username + plaintext password live in `config.yaml` under
`docs:`. `/health` and `/` remain public.
Apache (`/etc/apache2/sites-available/f.zikai.wang-le-ssl.conf`) proxies
`f.zikai.wang``127.0.0.1:6867` with `ProxyPreserveHost On`, so the service
only binds the loopback.
> **Large/slow HTTP uploads:** Apache's proxy leg inherits the global `Timeout 300`.
> For multi-GB transfers over a slow link, prefer **SFTP** (it bypasses the HTTP
> proxy entirely). To raise the HTTP ceiling you can add `ProxyTimeout`/`Timeout`
> in the Apache vhost.
## Configuration
All runtime config lives in **`config.yaml`** (git-ignored). See
`config.example.yaml` for the full schema. Notable keys:
- `server` — bind host/port (keep `127.0.0.1:6867` to match Apache).
- `database` — host/port/user/password/database. The password is auto-generated
and written here by `setup.sh`/`init_db.py`.
- `storage.upload_dir`, `storage.chunk_bytes` (default 1 MiB streaming chunk).
- `sftp` — enabled, host/port, host key + authorized_keys paths, and `users`.
### Setting the /docs admin password
Edit `config.yaml` directly — no hashing required:
```yaml
docs:
enabled: true
username: admin
password: "your-plaintext-password"
realm: "zikai docs"
```
Then `./stop.sh && ./start.sh`. The file is root-owned and stays on this
host; comparison is constant-time (`secrets.compare_digest`).
### Setting SFTP credentials
**Password auth** — generate a bcrypt hash and put it in `config.yaml`:
```bash
.venv/bin/python -c "import bcrypt;print(bcrypt.hashpw(b'yourpass',bcrypt.gensalt()).decode())"
# paste the output into sftp.users[].password_hash, then ./stop.sh && ./start.sh
```
**Public-key auth** — append each client's public key (OpenSSH format) to
`keys/authorized_keys` (one per line). Clients in `sftp.users[]` may then log
in with either method.
### Regenerating the DB password
```bash
.venv/bin/python -m app.scripts.init_db # new random password
KEEP_DB_PASSWORD=1 .venv/bin/python -m app.scripts.init_db # keep current
```
## SFTP notes
- The SFTP server can't traverse Apache's HTTP proxy, so it binds `0.0.0.0:2022`
directly. **Open port 2022** in your firewall for external clients
(FileZilla/WinSCP/scp).
- Sessions are chrooted to the upload root (`uploads/`), shared with HTTP.
- Only users listed in `sftp.users` may connect; only SFTP (no shell/exec) is allowed.
## Logs & pidfiles
- HTTP logs → `logs/app.log`; SFTP logs → `logs/sftp.log`.
- Pidfiles: `app.pid`, `sftp.pid` (used by `stop.sh`).