diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..379eddd --- /dev/null +++ b/.env.example @@ -0,0 +1,15 @@ +# 复制本文件为 .env 后按需修改(.env 勿提交到 Git) +# 与 app.py 同目录;程序启动时自动加载。 + +# 必填(长期运行):随机字符串,用于会话与 CSRF。可执行: openssl rand -hex 32 +# NAV_SECRET_KEY= + +# 数据库(默认 SQLite 文件在当前工作目录) +# NAV_DATABASE_URL=sqlite:///nav_local.db + +# 仅 python app.py 直连启动时生效 +# NAV_HOST=0.0.0.0 +# NAV_PORT=5000 + +# 调试:1 开启,勿在生产长期开启 +# NAV_DEBUG=0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4d7a13e --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.env +.venv/ +venv/ +__pycache__/ +*.py[cod] +*.db +instance/ +logs/ + diff --git a/README.md b/README.md index 15059ca..65944d9 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,7 @@ Flask + SQLite 的局域网导航聚合:左侧分组与服务列表,右侧 iframe 内嵌打开 `http://内网IP:端口`,统一入口管理宝塔、面板、本地服务等。 **完整中文说明与部署文档:** [部署与使用说明.md](./部署与使用说明.md) + +配置:将 `.env.example` 复制为 `.env` 并填写变量(与 `app.py` 同目录);详见说明文档「环境变量与 `.env`」一节。 + +进程守护(可选):`pm2 start ecosystem.config.cjs`(需已安装 PM2),详见 [部署与使用说明.md](./部署与使用说明.md) 中「9.7 使用 PM2」。 diff --git a/app.py b/app.py index 27dd64b..d1e9431 100644 --- a/app.py +++ b/app.py @@ -1,7 +1,9 @@ import os import secrets +from pathlib import Path from typing import Optional +from dotenv import load_dotenv from flask import Flask, flash, redirect, render_template, request, url_for from flask_login import LoginManager, current_user, login_required, login_user, logout_user from flask_wtf.csrf import CSRFProtect @@ -9,6 +11,10 @@ from flask_wtf.csrf import CSRFProtect from forms import GroupForm, LoginForm, ServiceForm from models import Service, ServiceGroup, User, db +# 与 app.py 同目录的 .env;若文件不存在则忽略。已存在于操作系统中的同名变量不会被覆盖。 +_ROOT = Path(__file__).resolve().parent +load_dotenv(_ROOT / ".env") + login_manager = LoginManager() csrf = CSRFProtect() diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs new file mode 100644 index 0000000..5d2b78d --- /dev/null +++ b/ecosystem.config.cjs @@ -0,0 +1,55 @@ +'use strict'; + +/** + * PM2 守护进程配置(本地导航 Flask) + * + * 前置:已在项目根目录创建虚拟环境 .venv,并 pip install -r requirements.txt + * 可选:复制 .env.example 为 .env 并填写 NAV_SECRET_KEY 等 + * + * 常用命令: + * pm2 start ecosystem.config.cjs + * pm2 status + * pm2 logs nav-site + * pm2 restart nav-site + * pm2 stop nav-site + * pm2 delete nav-site + * pm2 save && pm2 startup # 开机自启(按 pm2 提示执行一次 sudo 命令) + */ + +const path = require('path'); + +const root = __dirname; +const isWin = process.platform === 'win32'; +const venvPython = isWin + ? path.join(root, '.venv', 'Scripts', 'python.exe') + : path.join(root, '.venv', 'bin', 'python'); + +module.exports = { + apps: [ + { + name: 'nav-site', + cwd: root, + script: path.join(root, 'app.py'), + interpreter: venvPython, + instances: 1, + exec_mode: 'fork', + autorestart: true, + watch: false, + max_memory_restart: '300M', + min_uptime: '5s', + max_restarts: 15, + restart_delay: 3000, + kill_timeout: 8000, + merge_logs: true, + time: true, + error_file: path.join(root, 'logs', 'pm2-error.log'), + out_file: path.join(root, 'logs', 'pm2-out.log'), + // 如需在 PM2 层覆盖环境变量,可取消注释并修改(一般使用根目录 .env 即可) + // env: { + // NAV_HOST: '0.0.0.0', + // NAV_PORT: '5000', + // NAV_DEBUG: '0', + // }, + }, + ], +}; diff --git a/requirements.txt b/requirements.txt index 3088fda..9a91225 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ Flask-Login>=0.6.3 Flask-WTF>=1.2.0 WTForms>=3.1.0 Werkzeug>=3.0.0 +python-dotenv>=1.0.0 diff --git a/部署与使用说明.md b/部署与使用说明.md index 5246af9..64fa32c 100644 --- a/部署与使用说明.md +++ b/部署与使用说明.md @@ -47,6 +47,9 @@ ├── models.py # 数据模型(用户、分组、服务) ├── forms.py # 表单定义 ├── requirements.txt # Python 依赖列表 +├── .env.example # 环境变量模板(复制为 .env 后修改) +├── .env # 本地配置(自建,勿提交 Git) +├── ecosystem.config.cjs # PM2 守护进程配置 ├── nav_local.db # SQLite 数据库(首次成功运行后生成,勿手误提交到公开仓库) ├── static/ │ └── style.css # 样式 @@ -62,7 +65,22 @@ --- -## 五、环境变量说明 +## 五、环境变量与 `.env` 文件 + +程序启动时会从 **与 `app.py` 同目录** 的 `.env` 文件加载变量(依赖 `python-dotenv`)。**若 `.env` 不存在则跳过,不影响启动。** + +**优先级**:操作系统或进程里 **已经设置** 的环境变量 **优先**;`.env` 中的键 **不会覆盖** 已有变量(与 `python-dotenv` 默认行为一致)。便于在 systemd 里写死 `NAV_SECRET_KEY`,本地开发仍用 `.env` 填其他项。 + +**初次使用:** + +```bash +cp .env.example .env +# 编辑 .env,至少设置 NAV_SECRET_KEY(长期运行强烈建议) +``` + +`.env` 含敏感信息,已列入 `.gitignore`,请勿提交到公开仓库;仓库内仅保留 **`.env.example`** 作模板。 + +### 5.1 变量一览 | 变量名 | 含义 | 默认值 | |--------|------|--------| @@ -122,8 +140,16 @@ pip install -r requirements.txt -i https://pypi.org/simple ### 6.3 直接启动(开发 / 小范围内网) +若已用 `.env` 配置 `NAV_SECRET_KEY` 等,可直接: + ```bash -export NAV_SECRET_KEY="$(openssl rand -hex 32)" # Linux,建议每次部署写进 systemd 环境文件 +python app.py +``` + +否则可临时用环境变量(与 `.env` 二选一或混用,见上文优先级): + +```bash +export NAV_SECRET_KEY="$(openssl rand -hex 32)" # Linux;长期部署也可写进 systemd 的 EnvironmentFile python app.py ``` @@ -227,6 +253,8 @@ sudo chmod 600 /etc/nav-site/secret_key 后续由 systemd 读取该文件并注入 `NAV_SECRET_KEY`(见下节)。 +**替代做法**:也可在项目根目录放置 `.env`(建议 `chmod 600 .env`),`WorkingDirectory` 指向项目根时程序会自动加载。若 systemd 的 `Environment` / `EnvironmentFile` 里已设置同名变量,**以 systemd 为准**(`.env` 不会覆盖已有环境变量)。 + ### 9.4 使用 systemd 常驻运行(推荐) 创建服务文件(仍使用内置 `python app.py` 时示例;若改用 Gunicorn,将 `ExecStart` 改为 gunicorn 命令即可): @@ -315,6 +343,56 @@ ExecStart=/opt/nav-site/.venv/bin/gunicorn -w 2 -b 0.0.0.0:5000 app:app 说明:`-w 2` 为 worker 数量,可按机器 CPU 调整;`app:app` 表示 `app.py` 中的全局变量 `app`。 +### 9.7 使用 PM2 守护进程(可选) + +适合已安装 [PM2](https://pm2.keymetrics.io/) 的环境(常见于用 Node 的服务器上顺带托管 Python 进程)。项目根目录提供 **`ecosystem.config.cjs`**,用虚拟环境里的 Python 直接运行 **`app.py`**(与手动 `python app.py` 一致),**实例数固定为 1**(Flask 内置开发服务器不宜多进程监听同一端口)。 + +**1. 安装 PM2(若尚未安装,需本机已有 Node.js / npm)** + +```bash +sudo npm install -g pm2 +``` + +**2. 准备虚拟环境与依赖(项目根目录)** + +```bash +cd /opt/nav-site # 或你的项目路径 +python3 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt -i https://pypi.org/simple +cp -n .env.example .env && nano .env # 至少配置 NAV_SECRET_KEY +``` + +**3. 启动 / 查看 / 维护** + +```bash +cd /opt/nav-site +pm2 start ecosystem.config.cjs +pm2 status +pm2 logs nav-site +``` + +日志文件默认写入项目下 **`logs/`** 目录(PM2 会自动创建;该目录已加入 `.gitignore`)。 + +**4. 开机自启(在目标机器上执行一次)** + +```bash +pm2 save +pm2 startup +# 按终端里打印的 sudo 命令整行复制执行,再执行一次 pm2 save +``` + +**5. 常用命令** + +| 命令 | 作用 | +|------|------| +| `pm2 restart nav-site` | 热重启(改代码或 .env 后) | +| `pm2 stop nav-site` | 停止 | +| `pm2 delete nav-site` | 从 PM2 列表移除 | +| `pm2 monit` | 简易监控 | + +**说明**:若 `interpreter` 指向的 `.venv/bin/python` 不存在,PM2 会启动失败;请确认虚拟环境路径与 `ecosystem.config.cjs` 中一致。Windows 下脚本会自动使用 `.venv\Scripts\python.exe`。 + --- ## 十、数据与备份