Files
pipeline-lifetime/README.md
T

178 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 供水管道健康状态与剩余寿命评估系统
用于上传管道数据,完成健康状态和剩余寿命预测,并导出结果表格。系统包含用户认证、邮箱二次认证、管理员用户管理、邀请注册和上传记录下载等功能。
## 主要功能
- 上传 Excel 数据并执行预测,生成结果文件和图像。
- 用户使用用户名或邮箱登录,陌生设备需完成邮箱二次认证。
- 支持公开自助注册,也支持管理员在关闭公开注册后发送注册链接。
- 支持一次性密码重置链接、修改登录邮箱、受信设备管理和会话撤销。
- 管理员可搜索用户、启停账号、发送密码重置链接、撤销受信设备,以及下载上传记录。
- 上传记录和用户列表均支持分页。
## 运行要求
- Docker 和 Docker Compose,推荐用于部署。
- 本机开发建议使用 Python 3.12 和 Conda。
- 邮件认证、密码重置和邀请注册需要配置 Resend。
## 配置环境变量
复制模板创建实际配置文件:
```bash
cp .env.example .env
```
`.env` 用于实际运行配置,不应提交到版本库。`.env.local` 用于本机开发覆盖,空值表示继续使用 `.env` 中的值。优先级为:系统环境变量、`.env.local``.env`
生产环境至少应配置:
```dotenv
APP_ENV=production
DEBUG=false
SECRET_KEY=请填写高强度随机密钥
ADMIN_USERNAME=admin
ADMIN_PASSWORD=请填写管理员初始密码
ADMIN_EMAIL=管理员邮箱
DATABASE_URL=sqlite:////app/data/pipe_survival.db
RESEND_API_KEY=Resend_API_密钥
RESEND_FROM_EMAIL=已验证的发件人地址
SESSION_COOKIE_SECURE=true
```
可使用下列命令生成 `SECRET_KEY`
```bash
python -c "import secrets; print(secrets.token_hex(32))"
```
其余变量和默认值见 [`.env.example`](.env.example)。其中,`EMAIL_CODE_MINUTES` 控制邮箱验证码有效期,`PASSWORD_RESET_TOKEN_MINUTES` 控制密码重置和邀请注册链接的有效期。
## Docker 部署
### 直接构建
```bash
docker compose up -d --build
docker compose logs -f
```
服务默认监听 `5005` 端口。数据通过以下目录持久化:
| 本机目录 | 容器目录 | 用途 |
| --- | --- | --- |
| `./data` | `/app/data` | SQLite 数据库和运行日志 |
| `./data/uploads` | `/app/uploads` | 已上传的原始文件与预测结果 |
| `./data/images` | `/app/static/images` | 预测图像 |
不要在更新时使用 `docker compose down -v`,否则可能删除持久化卷。数据库结构会在启动时自动补齐,不会删除现有数据。
### 导入已导出的镜像
将镜像包、`docker-compose.yml``.env` 放到同一目录后执行:
```bash
sudo docker load -i pipeline-lifetime_YYYYMMDD.N.tar
docker compose up -d
docker compose logs -f
```
更新镜像时先停止旧容器即可,保留 `data` 目录:
```bash
docker compose down
sudo docker load -i pipeline-lifetime_YYYYMMDD.N.tar
docker compose up -d
```
## 本机开发
安装 Python 依赖后,使用 `.env.local` 覆盖开发配置:
```bash
conda create -n pipeline-lifetime python=3.12 -y
conda activate pipeline-lifetime
pip install -r requirements.txt
npm install
npm run build:css
python main.py
```
访问 `http://127.0.0.1:5005`。本机 HTTP 开发时请将 `SESSION_COOKIE_SECURE=false`。修改模板中的 Tailwind 类后,需要执行 `npm run build:css`;持续开发可使用:
```bash
npm run watch:css
```
也可使用 Docker 开发覆盖配置:
```bash
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
```
## 账户与邮件流程
1. 登录先校验用户名或邮箱、密码和图形验证码。
2. 未受信设备会收到 6 位邮箱验证码。验证码默认 10 分钟有效,重发间隔默认 60 秒。
3. 勾选“信任此设备”后,30 天内无需邮箱二次认证。勾选“保持登录状态”后,登录状态最长保持 7 天。
4. 找回密码会发送一次性重置链接。链接使用后失效,重置成功会撤销其他会话和受信设备。
5. 管理员可发送注册链接。受邀用户可在公开注册关闭时完成注册,注册链接同样为一次性链接。
邮件服务不可用时,邮箱验证码、密码重置和邀请注册无法完成。请确认 Resend 的 API 密钥、发件人域名和 DNS 验证状态正确。
## 反向代理建议
公开部署建议由 Nginx 提供 HTTPS,并将请求头转发给应用:
```nginx
location / {
proxy_pass http://127.0.0.1:5005;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
```
应用会基于当前请求生成密码重置和注册链接。请确保用户访问的公网域名能够正确转发到该服务。
### NPMplus 上传文件返回 500
如果小请求可以正常转发,但上传稍大的文件时 Nginx 返回 HTML 格式的 500,先检查 NPMplus 的错误日志:
```bash
sudo docker exec npmplus sh -lc \
"grep -E 'client_body_temp.*Permission denied' /data/nginx/logs/error.log | tail"
```
Nginx 会在请求体超过内存缓冲区后将内容写入 `client_body_temp`。当 NPMplus 以非 root 的 `PUID``PGID` 运行,而 `/usr/local/nginx/*_temp` 目录被 root 拥有且权限为 `0700` 时,写入会因权限不足失败并返回 500。可按实际的 `PUID``PGID` 修复全部临时目录;下面以 `1000:1000` 为例:
```bash
sudo docker exec -u 0 npmplus sh -lc '
for name in client_body_temp proxy_temp fastcgi_temp scgi_temp uwsgi_temp; do
chown -R 1000:1000 "/usr/local/nginx/$name"
chmod 700 "/usr/local/nginx/$name"
done
'
sudo docker exec -u 1000:1000 npmplus nginx -t
```
不要在 NPMplus 的 Compose 服务中直接设置 `user: 1000:1000`,其启动脚本需要先以 root 初始化,再根据 `PUID``PGID` 降权运行。检查 Nginx 配置时也应指定相同的运行账号:
```bash
sudo docker exec -u 1000:1000 npmplus nginx -t
sudo docker exec -u 1000:1000 npmplus nginx -T
```
以 root 身份执行 `nginx -t``nginx -T` 可能重新改变临时目录属主,使上传故障复发。此问题与 HTTPS、CrowdSec 拦截和 `client_max_body_size` 无关;如果响应为 413 或 403,应分别检查请求体大小限制和访问控制日志。
## 测试
```bash
python -m pytest -q
```
测试不使用真实 Resend 密钥,也不会发送邮件。