Initial commit: ToNav Personal Navigation Page

- Flask + SQLite 个人导航页系统
- 前台导航页(分类Tab、卡片展示)
- 管理后台(服务管理、分类管理、健康检测)
- 响应式设计
- Systemd 服务配置
This commit is contained in:
OpenClaw Agent
2026-02-12 21:57:15 +08:00
commit 872526505e
22 changed files with 3424 additions and 0 deletions

2
utils/__init__.py Normal file
View File

@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
"""Utils package"""

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

38
utils/auth.py Normal file
View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
"""认证工具"""
import hashlib
import sqlite3
from config import Config
def hash_password(password):
"""密码哈希SHA256简单版"""
return hashlib.sha256(password.encode()).hexdigest()
def verify_password(password, stored_hash):
"""验证密码"""
return hash_password(password) == stored_hash
def authenticate(username, password):
"""用户认证"""
conn = sqlite3.connect(Config.DATABASE_PATH)
cursor = conn.cursor()
cursor.execute('''
SELECT id, username, password_hash FROM users
WHERE username = ?
''', (username,))
user = cursor.fetchone()
conn.close()
if user and verify_password(password, user[2]):
return {
'id': user[0],
'username': user[1]
}
return None
def is_logged_in(session):
"""检查是否已登录"""
return 'user_id' in session

112
utils/database.py Normal file
View File

@@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
"""数据库初始化脚本"""
import sqlite3
import os
from config import Config
def init_database():
"""初始化数据库表"""
db_path = Config.DATABASE_PATH
# 如果数据库已存在,先删除(可选,开发环境)
if os.path.exists(db_path):
print(f"数据库已存在: {db_path}")
return
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# 创建 services 表
cursor.execute('''
CREATE TABLE IF NOT EXISTS services (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(100) NOT NULL,
url VARCHAR(500) NOT NULL,
description TEXT,
icon VARCHAR(50),
category VARCHAR(50) DEFAULT '默认',
is_enabled INTEGER DEFAULT 1,
sort_order INTEGER DEFAULT 0,
health_check_url VARCHAR(500),
health_check_enabled INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 创建 categories 表
cursor.execute('''
CREATE TABLE IF NOT EXISTS categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(50) NOT NULL UNIQUE,
sort_order INTEGER DEFAULT 0
)
''')
# 创建 users 表
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 创建索引
cursor.execute('CREATE INDEX IF NOT EXISTS idx_services_category ON services(category)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_services_enabled ON services(is_enabled)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_services_sort ON services(sort_order DESC)')
conn.commit()
conn.close()
print(f"数据库初始化完成: {db_path}")
def insert_initial_data():
"""插入初始数据"""
import hashlib
conn = sqlite3.connect(Config.DATABASE_PATH)
cursor = conn.cursor()
# 创建默认管理员账号 (admin / admin123)
# 使用 SHA256 简单哈希(生产环境建议用 bcrypt
password_hash = hashlib.sha256('admin123'.encode()).hexdigest()
cursor.execute('''
INSERT INTO users (username, password_hash)
VALUES (?, ?)
''', ('admin', password_hash))
# 创建默认分类
categories = [('内网服务', 1), ('开发工具', 2), ('测试环境', 3)]
for name, sort_order in categories:
cursor.execute('''
INSERT OR IGNORE INTO categories (name, sort_order)
VALUES (?, ?)
''', (name, sort_order))
# 创建默认服务
services = [
('违禁品查获排行榜', 'http://127.0.0.1:9517', '实时数据统计 · 自动刷新', '📊', '内网服务', 1, 100, 'http://127.0.0.1:9517/api/rankings', 1),
('短信接收端-Python', 'http://127.0.0.1:9518', 'HTTP接口 + Web管理', '📱', '内网服务', 1, 90, None, 0),
('短信接收端-Go', 'http://127.0.0.1:28001', '高性能版本 · 端口28001', '🔧', '内网服务', 1, 80, 'http://127.0.0.1:28001/', 1),
]
for service in services:
cursor.execute('''
INSERT INTO services (name, url, description, icon, category, is_enabled, sort_order, health_check_url, health_check_enabled)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
''', service)
conn.commit()
conn.close()
print("初始数据插入完成")
print("默认管理员账号: admin / admin123")
if __name__ == '__main__':
init_database()
insert_initial_data()

111
utils/health_check.py Normal file
View File

@@ -0,0 +1,111 @@
# -*- coding: utf-8 -*-
"""健康检查工具"""
import requests
import sqlite3
import threading
import time
from config import Config
def check_service_health(service_id):
"""检查单个服务健康状态"""
conn = sqlite3.connect(Config.DATABASE_PATH)
cursor = conn.cursor()
cursor.execute('''
SELECT name, url, health_check_url, health_check_enabled
FROM services
WHERE id = ?
''', (service_id,))
service = cursor.fetchone()
if not service:
conn.close()
return
name, url, check_url, check_enabled = service
check_url = check_url or url # 如果没有单独配置使用主url
status = 'offline'
status_code = None
error = None
if check_enabled:
try:
response = requests.get(check_url, timeout=Config.HEALTH_CHECK_TIMEOUT)
status_code = response.status_code
if response.status_code < 500:
status = 'online'
except requests.exceptions.Timeout:
error = 'timeout'
except requests.exceptions.ConnectionError:
error = 'connection_error'
except Exception as e:
error = str(e)
# 这里可以添加状态记录表,暂时只检查
conn.close()
return {
'id': service_id,
'name': name,
'status': status,
'status_code': status_code,
'error': error
}
def check_all_services():
"""检查所有启用健康检测的服务"""
conn = sqlite3.connect(Config.DATABASE_PATH)
cursor = conn.cursor()
cursor.execute('''
SELECT id FROM services
WHERE is_enabled = 1 AND health_check_enabled = 1
ORDER BY sort_order DESC
''')
services = cursor.fetchall()
conn.close()
results = []
for (service_id,) in services:
result = check_service_health(service_id)
if result:
results.append(result)
return results
class HealthCheckWorker:
"""后台健康检查工作线程"""
def __init__(self):
self.running = False
self.thread = None
def start(self):
"""启动后台检查"""
if not self.running:
self.running = True
self.thread = threading.Thread(target=self._run, daemon=True)
self.thread.start()
print("健康检查线程已启动")
def stop(self):
"""停止后台检查"""
self.running = False
if self.thread:
self.thread.join()
print("健康检查线程已停止")
def _run(self):
"""检查循环"""
while self.running:
results = check_all_services()
# 记录日志
for result in results:
print(f"[HealthCheck] {result['name']}: {result['status']}")
time.sleep(Config.HEALTH_CHECK_INTERVAL)
# 全局工作线程
health_worker = HealthCheckWorker()