178 lines
6.3 KiB
Markdown
178 lines
6.3 KiB
Markdown
# 供水管道健康状态与剩余寿命评估系统
|
||
|
||
用于上传管道数据,完成健康状态和剩余寿命预测,并导出结果表格。系统包含用户认证、邮箱二次认证、管理员用户管理、邀请注册和上传记录下载等功能。
|
||
|
||
## 主要功能
|
||
|
||
- 上传 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 密钥,也不会发送邮件。
|