fix(run): 增强数据库初始化与安全配置
- 从环境变量读取密钥,未设置时随机生成 - 自动初始化数据库并显式使用 UTF-8 读取 schema - 移除默认管理员账号,更新页面署名与链接 - 新增 .gitignore、LICENSE 和 README
这个提交包含在:
+30
@@ -0,0 +1,30 @@
|
||||
# 数据库文件(含本地数据,不提交)
|
||||
*.db
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.egg-info/
|
||||
.Python
|
||||
venv/
|
||||
.venv/
|
||||
env/
|
||||
|
||||
# 环境变量 / 密钥
|
||||
.env
|
||||
.env.*
|
||||
|
||||
# 运行时 / 日志
|
||||
*.log
|
||||
.pyc
|
||||
|
||||
# 操作系统
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# 编辑器
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 wali-Navi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,102 @@
|
||||
# 瓦力导航 · wali-Navi
|
||||
|
||||
一个基于 Flask 的轻量级网址导航站,支持用户自定义收藏、管理员维护公共导航库、访问统计等功能。数据使用 SQLite 存储,开箱即用,可一键部署到 Docker。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 🔖 **公共导航库**:管理员可维护分类导航(搜索类 / 快捷方式类)
|
||||
- 👤 **用户系统**:支持注册、登录、退出;普通用户可自定义个人收藏
|
||||
- 🎨 **自定义收藏**:用户可增删改个人搜索与快捷导航,支持图标、颜色、标签、分组
|
||||
- 📊 **访问统计**:记录每日访问量与在线人数,提供数据分析页面
|
||||
- 🔍 **多引擎搜索**:可配置多个搜索引擎
|
||||
- 🗺️ **自动 Sitemap**:自动生成 `sitemap.xml` 便于 SEO
|
||||
- 🐳 **Docker 支持**:提供 `Dockerfile` 与 `docker-compose.yml` 一键部署
|
||||
|
||||
## 技术栈
|
||||
|
||||
- 后端:Python 3.9 + Flask 2.2
|
||||
- 缓存:Flask-Caching(SimpleCache)
|
||||
- 服务器:gevent WSGI
|
||||
- 前端:Fomantic-UI(Semantic UI)、jQuery
|
||||
- 数据库:SQLite
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
navi/
|
||||
├── run.py # 应用主入口
|
||||
├── collect.py # 导航数据采集脚本(参考)
|
||||
├── schema.sql # 数据库表结构
|
||||
├── site.txt # 初始导航数据(CSV 格式)
|
||||
├── requirements.txt # Python 依赖
|
||||
├── Dockerfile # 基于 python:3.9-slim 的镜像
|
||||
├── Dockerfile-ubuntu # 基于 Ubuntu 的镜像(备选)
|
||||
├── docker-compose.yml # 编排文件
|
||||
├── static/ # 静态资源(CSS/JS/图片)
|
||||
└── templates/ # Jinja2 模板
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 本地运行
|
||||
|
||||
1. 安装依赖:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. 配置环境变量(可选):
|
||||
|
||||
| 变量名 | 说明 | 默认值 |
|
||||
| --- | --- | --- |
|
||||
| `NAVI_SECRET_KEY` | Flask 会话签名密钥,**生产环境务必设置** | 未设置时随机生成(重启失效) |
|
||||
| `NAVI_SITE_URL` | 站点域名,用于生成 sitemap | 空(相对路径) |
|
||||
|
||||
```bash
|
||||
export NAVI_SECRET_KEY="your-strong-secret-key"
|
||||
export NAVI_SITE_URL="https://your-domain.com"
|
||||
```
|
||||
|
||||
3. 启动应用:
|
||||
|
||||
```bash
|
||||
python run.py
|
||||
```
|
||||
|
||||
首次运行会自动按 `schema.sql` 创建数据库与表结构(生成空的 `navi.db`)。
|
||||
|
||||
4. 访问 `http://localhost:18100`。
|
||||
|
||||
> 默认未内置管理员账号。如需创建管理员,可在数据库生成后执行:
|
||||
> ```sql
|
||||
> insert into user (username, password, status, type) values ('admin', 'your-strong-password', 1, 1);
|
||||
> ```
|
||||
> 其中 `type=1` 表示管理员。
|
||||
|
||||
### Docker 部署
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
容器映射端口 `18100`,数据库 `navi.db` 通过卷挂载持久化。
|
||||
|
||||
### 初始化导航数据
|
||||
|
||||
`site.txt` 为 CSV 格式的初始导航数据,可参考 `collect.py` 中的逻辑将其导入 `library` 表。
|
||||
|
||||
## 配置说明
|
||||
|
||||
- 应用默认监听 `0.0.0.0:18100`,可在 `run.py` 末尾的 `app.run(...)` 修改。
|
||||
- 缓存默认超时 300 秒,访问统计缓存 30 秒,可在 `run.py` 中调整。
|
||||
|
||||
## 安全建议
|
||||
|
||||
- **务必设置 `NAVI_SECRET_KEY` 环境变量**,否则会话可被伪造。
|
||||
- 生产环境请关闭 Flask 的 `debug` 模式,并使用 gevent 等生产级 WSGI 服务器(代码中已预留 `WSGIServer` 用法)。
|
||||
- 默认管理员密码请替换为强密码。
|
||||
|
||||
## 开源协议
|
||||
|
||||
本项目基于 [MIT License](./LICENSE) 开源。
|
||||
@@ -1,11 +1,14 @@
|
||||
import datetime,time
|
||||
import os
|
||||
import sqlite3
|
||||
from gevent.pywsgi import WSGIServer
|
||||
from flask import Flask, abort, render_template, g, escape, request, make_response, session, redirect, url_for
|
||||
from flask_caching import Cache
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = b'f98d5992f84753d9004ef6b86f8dc603e105d6f201b3a7ac0e0d6d674e3b71a3'
|
||||
# 从环境变量读取密钥,未设置时生成临时随机密钥(重启后失效,生产环境请务必设置)
|
||||
import os
|
||||
app.secret_key = os.environ.get('NAVI_SECRET_KEY') or os.urandom(24)
|
||||
# ===========db
|
||||
DATABASE = './navi.db'
|
||||
cache = Cache(config={"DEBUG": True,'CACHE_TYPE': 'SimpleCache',"CACHE_DEFAULT_TIMEOUT": 300})
|
||||
@@ -29,12 +32,22 @@ def close_connection(exception):
|
||||
def init_db():
|
||||
with app.app_context():
|
||||
db = get_db()
|
||||
with app.open_resource('./schema.sql', mode='r') as f:
|
||||
# 显式以 utf-8 读取,避免 Windows(GBK) 下因中文注释解码失败
|
||||
with app.open_resource('./schema.sql', mode='r', encoding='utf-8') as f:
|
||||
db.cursor().executescript(f.read())
|
||||
db.commit()
|
||||
|
||||
|
||||
# init_db()
|
||||
def ensure_db():
|
||||
"""确保数据库文件及表结构存在,首次运行自动按 schema.sql 初始化。"""
|
||||
if not os.path.exists(DATABASE):
|
||||
print("数据库不存在,正在初始化:%s" % DATABASE)
|
||||
init_db()
|
||||
|
||||
|
||||
# 启动时检查并初始化数据库
|
||||
ensure_db()
|
||||
# init_db() # 如需重置数据库结构可手动调用
|
||||
|
||||
def query_db(query, args=(), one=False):
|
||||
cur = get_db().execute(query, args)
|
||||
@@ -385,10 +398,11 @@ def sitemap():
|
||||
pages = []
|
||||
ten_days_ago = (datetime.datetime.now() - datetime.timedelta(days=7)).date().isoformat()
|
||||
# static pages
|
||||
site_url = os.environ.get('NAVI_SITE_URL', '').rstrip('/')
|
||||
for rule in app.url_map.iter_rules():
|
||||
if "GET" in rule.methods and len(rule.arguments) == 0:
|
||||
pages.append(
|
||||
["https://nav.iwali.top" + str(rule.rule), ten_days_ago]
|
||||
[site_url + str(rule.rule), ten_days_ago]
|
||||
)
|
||||
sitemap_xml = render_template('sitemap_template.xml', pages=pages)
|
||||
response = make_response(sitemap_xml)
|
||||
|
||||
+2
-3
@@ -8,9 +8,8 @@ CREATE TABLE user
|
||||
type INTEGER not null default 0
|
||||
);
|
||||
|
||||
insert into user (username, password,status,type)
|
||||
values ('admin', 'admin', 1, 1),
|
||||
('wangcl', 'wangcl', 1, 0);
|
||||
-- 默认管理员账号请自行通过初始化脚本创建,例如:
|
||||
-- insert into user (username, password, status, type) values ('admin', '请修改为强密码', 1, 1);
|
||||
|
||||
DROP TABLE IF EXISTS search;
|
||||
CREATE TABLE search
|
||||
|
||||
+3
-3
@@ -10,12 +10,12 @@
|
||||
|
||||
<title>瓦力导航 · wali-Navi | A Navigation Website for wali</title>
|
||||
<meta property="og:title" content="瓦力导航 · wali-Navi" />
|
||||
<meta name="author" content="iROCKBUNNY" />
|
||||
<meta name="author" content="wali-Navi" />
|
||||
<meta property="og:locale" content="zh_CN" />
|
||||
<meta name="description" content="A Navigation Website for wali" />
|
||||
<meta property="og:description" content="A Navigation Website for wali" />
|
||||
<link rel="canonical" href="https://nav.iwali.top/" />
|
||||
<meta property="og:url" content="https://nav.iwali.top/" />
|
||||
<link rel="canonical" href="/" />
|
||||
<meta property="og:url" content="/" />
|
||||
<meta property="og:site_name" content="瓦力导航 · wali-Navi" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta name="twitter:card" content="summary" />
|
||||
|
||||
@@ -2,27 +2,22 @@
|
||||
<div class="ui container">
|
||||
<div class="ui stackable inverted divided equal height stackable grid">
|
||||
<div class="six wide column">
|
||||
<h4 class="ui inverted header">站群</h4>
|
||||
<h4 class="ui inverted header">友情链接</h4>
|
||||
<div class="ui inverted link list">
|
||||
<a class="ui mini label" href="https://iwali.top/" target="_blank">iwali</a>
|
||||
<a class="ui mini label" href="https://status.iwali.top/" target="_blank">监控</a>
|
||||
<a class="ui mini label" href="https://wakapi.iwali.top/" target="_blank">wakapi</a>
|
||||
<a class="ui mini label" href="https://disk.iwali.top/" target="_blank">云盘</a>
|
||||
<a class="ui mini label" href="https://kb.iwali.top/" target="_blank">看板</a>
|
||||
<a class="ui mini label" href="/donate/" target="_blank">Donate</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">
|
||||
<h4 class="ui inverted header">About</h4>
|
||||
<div class="ui inverted link list">
|
||||
<a class="item" href="https://iwali.top" target="_blank">iwali</a>
|
||||
<a class="item" href="/donate/">Donate</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="seven wide column">
|
||||
<h4 class="ui inverted header">瓦力导航 · wali-Navi</h4>
|
||||
<div class="ui inverted link list">
|
||||
<div class="item">© 2023 iwali</div>
|
||||
<div class="item">Designed and built by iwali.</div>
|
||||
<div class="item">© 2023 wali-Navi</div>
|
||||
<div class="item">Designed and built by wali-Navi.</div>
|
||||
<a class="item" href="/analytics/">
|
||||
Visits / Page Views:
|
||||
<span id="today-visits">{{ get_today_data()['views'] }}</span> / <span id="today-actions">{{ get_today_data()['online'] }}</span> (Today)
|
||||
|
||||
在新工单中引用
屏蔽一个用户