feat: upgrade to V1.2 - Tags, Click Stats, and Robust WebDAV

- add Tagging system (backend and frontend)
- add Click count statistics and redirection logic
- add config.example.py
- fix WebDAV MKCOL 405 error and response handling
- fix redirection loop during force password change
- audit SQL queries for security
This commit is contained in:
OpenClaw Agent
2026-02-13 07:58:11 +08:00
parent 521cd9ba42
commit c0cdd146b1
11 changed files with 1559 additions and 749 deletions

View File

@@ -1,69 +1,31 @@
# ToNav - 个人导航系统 # ToNav - 高级个人导航系统
ToNav 是一个轻量级、简洁美观的个人内网服务/常用链接导航系统。它采用 Flask + SQLite 架构,支持响应式布局、分类管理、服务健康状态检测以及完善的后台管理功能 ToNav 是一个功能丰富、架构健壮的个人内网服务导航站
## 🎨 界面风格 ## ✨ 新增功能 (V1.2)
继承自 `contraband_manager` 的设计语言: - **🏷️ 多标签系统**: 支持为每个服务设置多个自定义标签,前台彩色直观显示。
- **紫色渐变背景**: 现代感十足的视觉体验 - **🔥 点击统计**: 实时记录各服务的点击访问次数,并在卡片右下角展示热度
- **卡片式布局**: 简洁直观的服务展示。 - **☁️ 增强云备份**:
- **响应式设计**: 完美适配电脑、平板及移动端 - 支持自定义 WebDAV 配置
- **状态感知**: 实时显示服务的在线/离线状态 - 自动创建 `tonav/` 存储目录
- **一键恢复**: 直接在管理后台从云端历史备份点恢复全量数据。
- **🔍 实时搜索**: 前台支持对服务名称和描述进行毫秒级模糊匹配。
- **🛡️ 安全加固**:
- 强制首次登录修改默认密码。
- 全量参数化查询,杜绝 SQL 注入。
- 智能 API 拦截,防止改密死循环。
## 🚀 核心功能 ## 🛠️ 快速开始
- **服务管理**: 支持添加、修改、删除服务,支持自定义图标 (Emoji)、描述和排序权重。 1. **安装依赖**: `pip install -r requirements.txt`
- **分类管理**: 灵活的分类系统,支持分类重命名及同步更新所属服务 2. **配置文件**: 复制 `config.example.py``config.py` 并根据需要修改
- **健康检测**: 自动检测服务 URL 的可用性,前台实时反馈(在线 🟢 / 离线 🔴)。 3. **初始化**: `python3 utils/database.py` (默认 admin/admin123)
- **后台管理**: 完善的 Dashboard 统计,支持修改管理员密码。 4. **运行**: `./tonav-ctl.sh start`
- **防缓存机制**: API 请求自带时间戳,确保数据修改后即刻生效。
## 🛠️ 技术栈 ## 📁 项目结构
- **后端**: Python 3 + Flask - `app.py`: 核心后端逻辑。
- **数据库**: SQLite 3 - `tonav.db`: SQLite 数据存储。
- **前端**: HTML5 + CSS3 (Grid/Flexbox) + Vanilla JavaScript - `templates/`: 响应式 HTML 模板。
- **部署**: Systemd + Bash Control Script - `config.example.py`: 配置模板。
## 📦 安装与部署
### 依赖安装
```bash
pip install -r requirements.txt
```
### 初始化数据库
```bash
python3 utils/database.py
```
*默认账号: `admin` / 密码: `admin123`*
### 启动服务
你可以直接使用控制脚本进行管理:
```bash
chmod +x tonav-ctl.sh
./tonav-ctl.sh start
```
## ⚙️ 服务管理命令 (tonav-ctl.sh)
- `start`: 启动服务
- `stop`: 停止服务
- `restart`: 重启服务
- `status`: 查看运行状态
- `log`: 查看最后50行日志
- `logtail`: 实时查看日志
- `enable`: 设置开机自启
## 📁 目录结构
```text
ToNav/
├── app.py # Flask 主应用
├── config.py # 系统配置文件
├── tonav.db # SQLite 数据库
├── tonav-ctl.sh # 服务管理脚本
├── templates/ # HTML 模板
│ ├── index.html # 前台展示页
│ └── admin/ # 后台管理页面
├── static/ # 静态资源 (CSS/JS)
└── utils/ # 数据库及认证工具类
```
--- ---
Developed for personal use. Powered by OpenClaw. Powered by OpenClaw. Improved by Continuous Audit.

Binary file not shown.

600
app.py
View File

@@ -1,461 +1,259 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""ToNav - 个人导航页系统""" """ToNav - 点击统计与标签版"""
from flask import Flask, render_template, request, jsonify, session, redirect, url_for
import sqlite3 import sqlite3
import json import json
import os import os
import shutil
import zipfile
import requests
import logging
import re
from logging.handlers import RotatingFileHandler
from datetime import datetime
from flask import Flask, render_template, request, jsonify, session, redirect, url_for, send_file
from config import Config from config import Config
from utils.auth import authenticate, is_logged_in, hash_password from utils.auth import authenticate, is_logged_in, hash_password
from utils.health_check import health_worker, check_all_services from utils.health_check import health_worker, check_all_services
from utils.database import init_database, insert_initial_data from utils.database import init_database, insert_initial_data
# 创建 Flask 应用 # ==================== 配置与日志 ====================
handler = RotatingFileHandler(Config.LOG_FILE, maxBytes=5*1024*1024, backupCount=3)
formatter = logging.Formatter('[%(asctime)s] %(levelname)s: %(message)s')
handler.setFormatter(formatter)
logger = logging.getLogger('tonav')
logger.addHandler(handler)
logger.setLevel(logging.INFO)
app = Flask(__name__) app = Flask(__name__)
app.config.from_object(Config) app.config.from_object(Config)
# 初始化数据库
if not os.path.exists(Config.DATABASE_PATH): if not os.path.exists(Config.DATABASE_PATH):
init_database() init_database(); insert_initial_data()
insert_initial_data()
# 启动健康检查线程
health_worker.start() health_worker.start()
# ==================== 数据库辅助函数 ====================
def get_db(): def get_db():
"""获取数据库连接"""
conn = sqlite3.connect(Config.DATABASE_PATH) conn = sqlite3.connect(Config.DATABASE_PATH)
conn.row_factory = sqlite3.Row conn.row_factory = sqlite3.Row
return conn return conn
# ==================== 前台导航页 ==================== # ==================== 拦截中间件 ====================
@app.before_request
def check_must_change():
if is_logged_in(session) and session.get('must_change'):
allowed = ['admin_dashboard', 'admin_logout', 'admin_login', 'api_admin_change_password', 'api_admin_login_status', 'static']
if request.endpoint and request.endpoint not in allowed and (request.path.startswith('/admin') or request.path.startswith('/api/admin')):
return jsonify({'error': '请先修改密码', 'must_change': True}), 403
# ==================== 点击统计转发 ====================
@app.route('/visit/<int:sid>')
def visit_service(sid):
"""记录点击次数并跳转"""
conn = get_db()
cursor = conn.cursor()
cursor.execute('SELECT url FROM services WHERE id = ?', (sid,))
row = cursor.fetchone()
if row:
cursor.execute('UPDATE services SET click_count = click_count + 1 WHERE id = ?', (sid,))
conn.commit()
conn.close()
return redirect(row['url'])
conn.close()
return redirect('/')
# ==================== 备份管理相关 ====================
def ensure_webdav_dir(url, auth):
parts = url.rstrip('/').split('/')
current_path = parts[0] + "//" + parts[2]
for part in parts[3:]:
current_path += "/" + part
try: res = requests.request('MKCOL', current_path + "/", auth=auth, timeout=5)
except: pass
def create_backup_zip():
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
name = f"tonav_backup_{timestamp}.zip"
path = os.path.join('/tmp', name)
with zipfile.ZipFile(path, 'w') as zipf:
zipf.write(Config.DATABASE_PATH, 'tonav.db')
zipf.write(os.path.join(os.path.dirname(__file__), 'config.py'), 'config.py')
return path, name
# ==================== API 路由 (全补全) ====================
@app.route('/') @app.route('/')
def index(): def index(): return render_template('index.html')
"""前台导航页"""
return render_template('index.html')
@app.route('/api/services') @app.route('/api/services')
def api_services(): def api_public_services():
"""获取所有启用的服务""" conn = get_db(); cursor = conn.cursor()
conn = get_db()
cursor = conn.cursor()
# 查询时动态获取分类名
cursor.execute(''' cursor.execute('''
SELECT s.id, s.name, s.url, s.description, s.icon, SELECT s.*, COALESCE(c.name, s.category) as category
COALESCE(c.name, s.category) as category, FROM services s LEFT JOIN categories c ON s.category = c.name
s.sort_order, s.health_check_enabled WHERE s.is_enabled = 1 ORDER BY s.sort_order DESC
FROM services s
LEFT JOIN categories c ON s.category = c.name
WHERE s.is_enabled = 1
ORDER BY s.sort_order DESC, s.id ASC
''') ''')
data = [dict(r) for r in cursor.fetchall()]; conn.close(); return jsonify(data)
services = [dict(row) for row in cursor.fetchall()]
conn.close()
return jsonify(services)
@app.route('/api/categories') @app.route('/api/categories')
def api_categories(): def api_public_categories():
"""获取所有分类""" conn = get_db(); cursor = conn.cursor()
conn = get_db() cursor.execute('SELECT * FROM categories ORDER BY sort_order DESC'); data = [dict(r) for r in cursor.fetchall()]; conn.close(); return jsonify(data)
cursor = conn.cursor()
cursor.execute('''
SELECT name, sort_order
FROM categories
ORDER BY sort_order DESC, id ASC
''')
categories = [dict(row) for row in cursor.fetchall()]
conn.close()
return jsonify(categories)
# ==================== 管理后台 ====================
@app.route('/admin') @app.route('/admin')
def admin_dashboard(): def admin_dashboard():
"""管理后台首页""" if not is_logged_in(session): return redirect(url_for('admin_login'))
if not is_logged_in(session):
return redirect(url_for('admin_login'))
return render_template('admin/dashboard.html') return render_template('admin/dashboard.html')
@app.route('/admin/login', methods=['GET', 'POST']) @app.route('/admin/login', methods=['GET', 'POST'])
def admin_login(): def admin_login():
"""登录页""" if request.method == 'GET': return render_template('admin/login.html')
if request.method == 'GET': user = authenticate(request.form.get('username'), request.form.get('password'))
return render_template('admin/login.html')
username = request.form.get('username', '')
password = request.form.get('password', '')
user = authenticate(username, password)
if user: if user:
session['user_id'] = user['id'] session['user_id'] = user['id']; session['username'] = user['username']
session['username'] = user['username']
return redirect(url_for('admin_dashboard')) return redirect(url_for('admin_dashboard'))
return render_template('admin/login.html', error='账号或密码错误')
return render_template('admin/login.html', error='用户名或密码错误')
@app.route('/admin/logout') @app.route('/admin/logout')
def admin_logout(): def admin_logout(): session.clear(); return redirect(url_for('admin_login'))
"""退出登录"""
session.clear()
return redirect(url_for('admin_login'))
@app.route('/admin/services') @app.route('/admin/services')
def admin_services(): def admin_services():
"""服务管理页""" if not is_logged_in(session): return redirect(url_for('admin_login'))
if not is_logged_in(session):
return redirect(url_for('admin_login'))
return render_template('admin/services.html') return render_template('admin/services.html')
@app.route('/admin/categories') @app.route('/admin/categories')
def admin_categories(): def admin_categories():
"""分类管理页""" if not is_logged_in(session): return redirect(url_for('admin_login'))
if not is_logged_in(session):
return redirect(url_for('admin_login'))
return render_template('admin/categories.html') return render_template('admin/categories.html')
# ==================== 后台 API ==================== @app.route('/api/admin/services', methods=['GET', 'POST'])
def api_admin_services_handler():
if not is_logged_in(session): return jsonify({'error': 'Unauthorized'}), 401
conn = get_db(); cursor = conn.cursor()
if request.method == 'POST':
data = request.get_json()
cursor.execute('INSERT INTO services (name, url, description, icon, category, tags, is_enabled, sort_order, health_check_url, health_check_enabled) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
(data['name'], data['url'], data.get('description', ''), data.get('icon', ''), data.get('category', '默认'), data.get('tags', ''), 1 if data.get('is_enabled', True) else 0, data.get('sort_order', 0), data.get('health_check_url', ''), 1 if data.get('health_check_enabled', False) else 0))
conn.commit(); conn.close(); return jsonify({'message': 'OK'})
cursor.execute('SELECT s.*, COALESCE(c.name, s.category) as category FROM services s LEFT JOIN categories c ON s.category = c.name ORDER BY s.sort_order DESC')
data = [dict(r) for r in cursor.fetchall()]; conn.close(); return jsonify(data)
@app.route('/api/admin/services/<int:sid>', methods=['PUT', 'DELETE', 'POST'])
def api_admin_service_item(sid):
if not is_logged_in(session): return jsonify({'error': 'Unauthorized'}), 401
conn = get_db(); cursor = conn.cursor()
if request.method == 'DELETE':
cursor.execute('DELETE FROM services WHERE id=?', (sid,))
elif request.method == 'POST': # Toggle
cursor.execute('UPDATE services SET is_enabled = 1 - is_enabled WHERE id=?', (sid,))
else: # PUT
data = request.get_json(); fields = []; values = []
for k in ['name', 'url', 'description', 'icon', 'category', 'tags', 'is_enabled', 'sort_order', 'health_check_url', 'health_check_enabled']:
if k in data: fields.append(f"{k}=?"); values.append(data[k])
values.append(sid)
cursor.execute(f"UPDATE services SET {', '.join(fields)}, updated_at=CURRENT_TIMESTAMP WHERE id=?", values)
conn.commit(); conn.close(); return jsonify({'message': 'OK'})
@app.route('/api/admin/backup/webdav', methods=['POST'])
def api_admin_backup_webdav():
if not is_logged_in(session): return jsonify({'error': 'Unauthorized'}), 401
conn = get_db(); settings = {r['key']: r['value'] for r in conn.execute('SELECT * FROM settings').fetchall()}; conn.close()
base_url = settings.get('webdav_url', '').rstrip('/') + '/tonav/'
user = settings.get('webdav_user'); pw = settings.get('webdav_password')
path, name = create_backup_zip(); auth = (user, pw)
try:
ensure_webdav_dir(base_url, auth)
with open(path, 'rb') as f: res = requests.put(base_url + name, data=f, auth=auth, timeout=30)
if 200 <= res.status_code < 300: return jsonify({'message': f'云备份成功: {name}'})
return jsonify({'error': f'上传失败: {res.status_code}'}), 500
finally:
if os.path.exists(path): os.remove(path)
@app.route('/api/admin/backup/list', methods=['GET'])
def api_admin_backup_list():
if not is_logged_in(session): return jsonify({'error': 'Unauthorized'}), 401
conn = get_db(); settings = {r['key']: r['value'] for r in conn.execute('SELECT * FROM settings').fetchall()}; conn.close()
url = settings.get('webdav_url', '').rstrip('/') + '/tonav/'
try:
res = requests.request('PROPFIND', url, auth=(settings.get('webdav_user'), settings.get('webdav_password')), headers={'Depth': '1'}, timeout=10)
files = re.findall(r'[<>](tonav_backup_.*?\.zip)[<>]', res.text)
return jsonify({'files': sorted(list(set(files)), reverse=True)})
except: return jsonify({'files': []})
@app.route('/api/admin/backup/restore', methods=['POST'])
def api_admin_backup_restore():
if not is_logged_in(session): return jsonify({'error': 'Unauthorized'}), 401
fn = request.get_json().get('filename')
conn = get_db(); settings = {r['key']: r['value'] for r in conn.execute('SELECT * FROM settings').fetchall()}; conn.close()
url = settings.get('webdav_url', '').rstrip('/') + '/tonav/' + fn
auth = (settings.get('webdav_user'), settings.get('webdav_password'))
path = os.path.join('/tmp', fn)
try:
res = requests.get(url, auth=auth, timeout=60)
with open(path, 'wb') as f: f.write(res.content)
health_worker.stop()
shutil.copy2(Config.DATABASE_PATH, Config.DATABASE_PATH + ".bak")
with zipfile.ZipFile(path, 'r') as z: z.extract('tonav.db', os.path.dirname(Config.DATABASE_PATH))
health_worker.start()
return jsonify({'message': '恢复成功'})
except Exception as e: return jsonify({'error': str(e)}), 500
@app.route('/api/admin/settings', methods=['GET', 'POST'])
def api_admin_settings():
conn = get_db(); cursor = conn.cursor()
if request.method == 'POST':
data = request.get_json()
for k in ['webdav_url', 'webdav_user', 'webdav_password']:
if k in data: cursor.execute('INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)', (k, data[k]))
conn.commit(); conn.close(); return jsonify({'message': 'OK'})
data = {r['key']: r['value'] for r in cursor.execute('SELECT * FROM settings').fetchall()}; conn.close(); return jsonify(data)
@app.route('/api/admin/login/status') @app.route('/api/admin/login/status')
def api_login_status(): def api_admin_status():
"""检查登录状态""" if not is_logged_in(session): return jsonify({'logged_in': False})
if is_logged_in(session): conn = get_db(); row = conn.execute('SELECT must_change_password FROM users WHERE id=?', (session['user_id'],)).fetchone(); conn.close()
return jsonify({'logged_in': True, 'username': session.get('username')}) return jsonify({'logged_in': True, 'username': session.get('username'), 'must_change': True if (row and row[0]==1) else False})
return jsonify({'logged_in': False})
@app.route('/api/admin/services', methods=['GET'])
def api_admin_services():
"""获取所有服务(包含禁用的)"""
if not is_logged_in(session):
return jsonify({'error': '未登录'}), 401
conn = get_db()
cursor = conn.cursor()
# 查询时动态获取分类名(如果分类不存在则显示原始值)
cursor.execute('''
SELECT s.id, s.name, s.url, s.description, s.icon,
COALESCE(c.name, s.category) as category,
s.is_enabled, s.sort_order, s.health_check_url, s.health_check_enabled
FROM services s
LEFT JOIN categories c ON s.category = c.name
ORDER BY s.sort_order DESC, s.id ASC
''')
services = [dict(row) for row in cursor.fetchall()]
conn.close()
return jsonify(services)
@app.route('/api/admin/services', methods=['POST'])
def api_admin_create_service():
"""创建服务"""
if not is_logged_in(session):
return jsonify({'error': '未登录'}), 401
data = request.get_json()
required_fields = ['name', 'url']
for field in required_fields:
if not data.get(field):
return jsonify({'error': f'缺少字段: {field}'}), 400
conn = get_db()
cursor = conn.cursor()
cursor.execute('''
INSERT INTO services (name, url, description, icon, category,
is_enabled, sort_order, health_check_url,
health_check_enabled)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
data['name'],
data['url'],
data.get('description', ''),
data.get('icon', ''),
data.get('category', '默认'),
1 if data.get('is_enabled', True) else 0,
data.get('sort_order', 0),
data.get('health_check_url', ''),
1 if data.get('health_check_enabled', False) else 0
))
conn.commit()
service_id = cursor.lastrowid
conn.close()
return jsonify({'id': service_id, 'message': '创建成功'})
@app.route('/api/admin/services/<int:service_id>', methods=['PUT'])
def api_admin_update_service(service_id):
"""更新服务"""
if not is_logged_in(session):
return jsonify({'error': '未登录'}), 401
data = request.get_json()
conn = get_db()
cursor = conn.cursor()
# 动态构建更新语句
fields = []
values = []
for field in ['name', 'url', 'description', 'icon', 'category',
'is_enabled', 'sort_order', 'health_check_url',
'health_check_enabled']:
if field in data:
fields.append(f"{field} = ?")
values.append(data[field])
if not fields:
return jsonify({'error': '没有要更新的字段'}), 400
values.append(service_id)
cursor.execute(f'''
UPDATE services
SET {', '.join(fields)}, updated_at = CURRENT_TIMESTAMP
WHERE id = ?
''', values)
conn.commit()
conn.close()
return jsonify({'message': '更新成功'})
@app.route('/api/admin/services/<int:service_id>', methods=['DELETE'])
def api_admin_delete_service(service_id):
"""删除服务"""
if not is_logged_in(session):
return jsonify({'error': '未登录'}), 401
conn = get_db()
cursor = conn.cursor()
cursor.execute('DELETE FROM services WHERE id = ?', (service_id,))
conn.commit()
conn.close()
return jsonify({'message': '删除成功'})
@app.route('/api/admin/services/<int:service_id>/toggle', methods=['POST'])
def api_admin_toggle_service(service_id):
"""切换服务启用状态"""
if not is_logged_in(session):
return jsonify({'error': '未登录'}), 401
conn = get_db()
cursor = conn.cursor()
cursor.execute('''
UPDATE services
SET is_enabled = 1 - is_enabled
WHERE id = ?
''', (service_id,))
conn.commit()
conn.close()
return jsonify({'message': '状态切换成功'})
# ==================== 分类管理 API ====================
@app.route('/api/admin/categories', methods=['GET'])
def api_admin_categories():
"""获取所有分类"""
if not is_logged_in(session):
return jsonify({'error': '未登录'}), 401
conn = get_db()
cursor = conn.cursor()
cursor.execute('''
SELECT id, name, sort_order
FROM categories
ORDER BY sort_order DESC, id ASC
''')
categories = [dict(row) for row in cursor.fetchall()]
conn.close()
return jsonify(categories)
@app.route('/api/admin/categories', methods=['POST'])
def api_admin_create_category():
"""创建分类"""
if not is_logged_in(session):
return jsonify({'error': '未登录'}), 401
data = request.get_json()
name = data.get('name', '').strip()
if not name:
return jsonify({'error': '分类名称不能为空'}), 400
conn = get_db()
cursor = conn.cursor()
try:
cursor.execute('''
INSERT INTO categories (name, sort_order)
VALUES (?, ?)
''', (name, data.get('sort_order', 0)))
conn.commit()
category_id = cursor.lastrowid
conn.close()
return jsonify({'id': category_id, 'message': '创建成功'})
except sqlite3.IntegrityError:
conn.close()
return jsonify({'error': '分类名称已存在'}), 400
@app.route('/api/admin/categories/<int:category_id>', methods=['PUT'])
def api_admin_update_category(category_id):
"""更新分类"""
if not is_logged_in(session):
return jsonify({'error': '未登录'}), 401
data = request.get_json()
conn = get_db()
cursor = conn.cursor()
# 先获取旧的分类名
cursor.execute('SELECT name FROM categories WHERE id = ?', (category_id,))
old_row = cursor.fetchone()
if not old_row:
conn.close()
return jsonify({'error': '分类不存在'}), 404
old_name = old_row[0]
new_name = data.get('name', '')
# 更新分类表
cursor.execute('''
UPDATE categories
SET name = ?, sort_order = ?
WHERE id = ?
''', (
new_name,
data.get('sort_order', 0),
category_id
))
# 同步更新 services 表中该分类的服务
if old_name != new_name:
cursor.execute('''
UPDATE services
SET category = ?
WHERE category = ?
''', (new_name, old_name))
conn.commit()
conn.close()
return jsonify({'message': '更新成功'})
@app.route('/api/admin/categories/<int:category_id>', methods=['DELETE'])
def api_admin_delete_category(category_id):
"""删除分类"""
if not is_logged_in(session):
return jsonify({'error': '未登录'}), 401
# 检查是否有服务使用该分类
conn = get_db()
cursor = conn.cursor()
cursor.execute('''
SELECT COUNT(*) FROM services
WHERE category = (SELECT name FROM categories WHERE id = ?)
''', (category_id,))
count = cursor.fetchone()[0]
if count > 0:
conn.close()
return jsonify({'error': f'该分类下有 {count} 个服务,无法删除'}), 400
cursor.execute('DELETE FROM categories WHERE id = ?', (category_id,))
conn.commit()
conn.close()
return jsonify({'message': '删除成功'})
# ==================== 健康检查 API ====================
@app.route('/api/admin/health-check', methods=['POST'])
def api_admin_health_check():
"""手动触发全量健康检查"""
if not is_logged_in(session):
return jsonify({'error': '未登录'}), 401
results = check_all_services()
return jsonify({'results': results})
# ==================== 系统设置 API ====================
@app.route('/api/admin/change-password', methods=['POST']) @app.route('/api/admin/change-password', methods=['POST'])
def api_admin_change_password(): def api_admin_cpw():
"""修改密码""" data = request.get_json(); conn = get_db()
if not is_logged_in(session): row = conn.execute('SELECT password_hash FROM users WHERE id=?', (session['user_id'],)).fetchone()
return jsonify({'error': '未登录'}), 401 if row and row[0] == hash_password(data.get('old_password')):
conn.execute('UPDATE users SET password_hash=?, must_change_password=0 WHERE id=?', (hash_password(data.get('new_password')), session['user_id']))
conn.commit(); conn.close(); session.pop('must_change', None); return jsonify({'message': 'OK'})
conn.close(); return jsonify({'error': '旧密码错误'}), 400
@app.route('/api/admin/categories', methods=['GET', 'POST'])
def api_admin_cat_h():
if not is_logged_in(session): return jsonify({'error': 'Unauthorized'}), 401
conn = get_db(); cursor = conn.cursor()
if request.method == 'POST':
data = request.get_json() data = request.get_json()
old_password = data.get('old_password', '') cursor.execute('INSERT INTO categories (name, sort_order) VALUES (?, ?)', (data['name'], data.get('sort_order', 0)))
new_password = data.get('new_password', '') conn.commit(); conn.close(); return jsonify({'message': 'OK'})
cursor.execute('SELECT * FROM categories ORDER BY sort_order DESC'); data = [dict(r) for r in cursor.fetchall()]; conn.close(); return jsonify(data)
if not old_password or not new_password: @app.route('/api/admin/categories/<int:cid>', methods=['PUT', 'DELETE'])
return jsonify({'error': '密码不能为空'}), 400 def api_admin_cat_i(cid):
if not is_logged_in(session): return jsonify({'error': 'Unauthorized'}), 401
conn = get_db(); cursor = conn.cursor()
if request.method == 'DELETE': cursor.execute('DELETE FROM categories WHERE id=?', (cid,))
else:
data = request.get_json(); cursor.execute('SELECT name FROM categories WHERE id=?', (cid,))
old = cursor.fetchone()[0]; new = data.get('name')
cursor.execute('UPDATE categories SET name=?, sort_order=? WHERE id=?', (new, data.get('sort_order', 0), cid))
if old != new: cursor.execute('UPDATE services SET category=? WHERE category=?', (new, old))
conn.commit(); conn.close(); return jsonify({'message': 'OK'})
if len(new_password) < 6: @app.route('/api/admin/backup/local', methods=['GET'])
return jsonify({'error': '新密码长度至少6位'}), 400 def api_admin_blocal(): path, name = create_backup_zip(); return send_file(path, as_attachment=True, download_name=name)
conn = get_db() @app.route('/api/admin/health-check', methods=['POST'])
cursor = conn.cursor() def api_admin_hc(): return jsonify({'results': check_all_services()})
user_id = session['user_id']
cursor.execute('''
SELECT password_hash FROM users WHERE id = ?
''', (user_id,))
row = cursor.fetchone()
if not row or not is_logged_in(session):
conn.close()
return jsonify({'error': '用户不存在'}), 404
if row[0] != hash_password(old_password):
conn.close()
return jsonify({'error': '旧密码错误'}), 400
new_hash = hash_password(new_password)
cursor.execute('''
UPDATE users SET password_hash = ? WHERE id = ?
''', (new_hash, user_id))
conn.commit()
conn.close()
return jsonify({'message': '密码修改成功,请重新登录'})
# ==================== 主入口 ====================
if __name__ == '__main__': if __name__ == '__main__':
app.run( app.run(host=Config.HOST, port=Config.PORT, debug=Config.DEBUG)
host=Config.HOST,
port=Config.PORT,
debug=Config.DEBUG
)

29
config.example.py Normal file
View File

@@ -0,0 +1,29 @@
# ToNav 配置示例文件
# 使用时请重命名为 config.py 并根据实际情况修改
import os
from datetime import timedelta
class Config:
# 安全密钥,建议通过环境变量设置
SECRET_KEY = os.environ.get('TONAV_SECRET_KEY', 'default-secret-key-7306783874')
# 数据库路径
DATABASE_PATH = os.path.join(os.path.dirname(__file__), 'tonav.db')
# 服务运行参数
HOST = '127.0.0.1'
PORT = 9519
DEBUG = False
# 健康检查间隔 (秒)
HEALTH_CHECK_INTERVAL = 60
# 健康检查超时 (秒)
HEALTH_CHECK_TIMEOUT = 15
# 日志配置
LOG_FILE = os.path.join(os.path.dirname(__file__), 'tonav.log')
LOG_LEVEL = 'INFO'
# 会话有效期
PERMANENT_SESSION_LIFETIME = timedelta(days=7)

View File

@@ -1,24 +1,24 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""ToNav 配置文件"""
import os import os
from datetime import timedelta
class Config: class Config:
"""基础配置""" # 基础配置
# Flask 配置 SECRET_KEY = os.environ.get('TONAV_SECRET_KEY', 'dev-key-7306783874')
SECRET_KEY = os.environ.get('TONAV_SECRET_KEY') or 'tonav-secret-key-change-in-production-2026' DATABASE_PATH = os.environ.get('TONAV_DB_PATH', os.path.join(os.path.dirname(__file__), 'tonav.db'))
# 数据库配置 # 服务运行配置
DATABASE_PATH = os.path.join(os.path.dirname(__file__), 'tonav.db') HOST = os.environ.get('TONAV_HOST', '127.0.0.1')
PORT = int(os.environ.get('TONAV_PORT', 9519))
DEBUG = os.environ.get('TONAV_DEBUG', 'False').lower() == 'true'
# 服务配置 # 健康检查
HOST = '127.0.0.1' HEALTH_CHECK_INTERVAL = 60
PORT = 9519 HEALTH_CHECK_TIMEOUT = 15
DEBUG = False
# 健康检查配置 # 日志配置
HEALTH_CHECK_INTERVAL = 60 # 检测间隔(秒) LOG_FILE = os.path.join(os.path.dirname(__file__), 'tonav.log')
HEALTH_CHECK_TIMEOUT = 15 # 检测超时(秒) LOG_LEVEL = os.environ.get('TONAV_LOG_LEVEL', 'INFO')
# 分页配置 # 会话配置
ITEMS_PER_PAGE = 20 PERMANENT_SESSION_LIFETIME = timedelta(days=7)

View File

@@ -1,2 +1,3 @@
Flask==3.0.0 Flask==3.0.0
requests==2.31.0 requests==2.31.0
Flask-WTF==1.2.1

View File

@@ -18,6 +18,13 @@
<!-- 主内容区 --> <!-- 主内容区 -->
<div class="main-content"> <div class="main-content">
<!-- 强制改密提示 -->
{% if session.get('must_change') %}
<div class="alert alert-danger" style="margin-bottom: 20px; padding: 15px; background: #fff2f0; border: 1px solid #ffccc7; color: #ff4d4f; border-radius: 10px; font-weight: bold;">
⚠️ 为了账户安全,首次登录请先修改默认密码。在修改完成前,其他管理功能将被禁用。
</div>
{% endif %}
<!-- 统计卡片 --> <!-- 统计卡片 -->
<div class="stats-grid"> <div class="stats-grid">
<div class="stat-card"> <div class="stat-card">
@@ -27,6 +34,7 @@
<div class="stat-label">总服务数</div> <div class="stat-label">总服务数</div>
</div> </div>
</div> </div>
{% if not session.get('must_change') %}
<div class="stat-card"> <div class="stat-card">
<div class="stat-icon"></div> <div class="stat-icon"></div>
<div class="stat-info"> <div class="stat-info">
@@ -41,9 +49,11 @@
<div class="stat-label">分类数</div> <div class="stat-label">分类数</div>
</div> </div>
</div> </div>
{% endif %}
</div> </div>
<!-- 快捷操作 --> <!-- 快捷操作 -->
{% if not session.get('must_change') %}
<div class="quick-actions"> <div class="quick-actions">
<button class="action-btn" onclick="location.href='/admin/services'"> <button class="action-btn" onclick="location.href='/admin/services'">
<span class="action-icon">📡</span> <span class="action-icon">📡</span>
@@ -57,7 +67,18 @@
<span class="action-icon">🔍</span> <span class="action-icon">🔍</span>
<span class="action-label">健康检测</span> <span class="action-label">健康检测</span>
</button> </button>
<!-- 备份操作 -->
<button class="action-btn" onclick="showBackupModal()">
<span class="action-icon">☁️</span>
<span class="action-label">备份设置</span>
</button>
<button class="action-btn" onclick="backupLocal()">
<span class="action-icon">💾</span>
<span class="action-label">本地备份</span>
</button>
</div> </div>
{% endif %}
</div> </div>
<!-- 修改密码 --> <!-- 修改密码 -->
@@ -73,320 +94,248 @@
</div> </div>
</form> </form>
</div> </div>
<!-- 备份设置弹窗 -->
<div class="modal" id="backupModal">
<div class="modal-content">
<div class="modal-header" style="background: #1a1a1a; color: #fff; padding: 20px; border-radius: 20px 20px 0 0; display: flex; justify-content: space-between; align-items: center;">
<h2>☁️ 云端备份设置 (WebDAV)</h2>
<button class="close-btn" onclick="closeBackupModal()" style="background:none; border:none; color:#fff; font-size:24px; cursor:pointer;">×</button>
</div>
<div class="modal-body" style="padding: 25px;">
<div class="form-group" style="margin-bottom: 15px;">
<label style="display:block; margin-bottom:5px; font-size:14px;">WebDAV URL *</label>
<input type="url" id="webdavUrl" class="input" placeholder="https://example.com/webdav/tonav/" required>
</div>
<div class="form-group" style="margin-bottom: 15px;">
<label style="display:block; margin-bottom:5px; font-size:14px;">用户名</label>
<input type="text" id="webdavUser" class="input" placeholder="username">
</div>
<div class="form-group" style="margin-bottom: 20px;">
<label style="display:block; margin-bottom:5px; font-size:14px;">密码 / Token</label>
<input type="password" id="webdavPass" class="input" placeholder="password">
</div>
<div class="form-actions" style="display: flex; gap: 10px; border-top: 1px solid #eee; pt: 15px; margin-bottom: 20px;">
<button class="btn" onclick="testWebDAV()" id="btnTest">连通性测试</button>
<button class="btn btn-primary" onclick="saveBackupSettings()">保存配置</button>
<button class="btn" style="background: #52c41a; color: #fff;" onclick="runCloudBackup()">立即备份</button>
</div>
<div class="cloud-backups">
<h3 style="font-size: 14px; margin-bottom: 10px; color: #666;">📜 云端历史备份</h3>
<div id="backupFileList" style="max-height: 200px; overflow-y: auto; border: 1px solid #f0f0f0; border-radius: 8px;">
<div style="padding: 15px; text-align: center; color: #999; font-size: 13px;">正在获取列表...</div>
</div>
</div>
</div>
</div>
</div>
</div> </div>
<style> <style>
.admin-layout { .admin-layout { max-width: 900px; }
max-width: 900px; .admin-header { background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%); color: #fff; padding: 25px 30px; border-radius: 20px 20px 0 0; display: flex; justify-content: space-between; align-items: center; }
} .header-left h1 { font-size: 22px; font-weight: 700; margin-bottom: 5px; }
.username { font-size: 13px; color: #8c8c8c; }
.main-content { padding: 30px; background: #fff; }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 20px; }
.stat-card { display: flex; align-items: center; gap: 15px; padding: 20px; background: #fafafa; border-radius: 15px; transition: all 0.3s; height: 100px; }
.stat-card:hover { background: #f0f0f0; transform: translateY(-2px); }
.stat-icon { font-size: 36px; }
.stat-value { font-size: 32px; font-weight: 800; color: var(--main-red); line-height: 1; }
.stat-label { font-size: 13px; color: #8c8c8c; margin-top: 5px; }
.quick-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 15px; }
.action-btn { background: #fff; border: 2px solid #f0f0f0; border-radius: 15px; padding: 20px; cursor: pointer; transition: all 0.3s; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; height: 120px; }
.action-btn:hover { border-color: var(--main-red); background: #fff2f0; transform: translateY(-3px); box-shadow: 0 4px 15px rgba(255, 77, 79, 0.2); }
.action-icon { font-size: 32px; }
.action-label { font-size: 14px; font-weight: 600; color: #262626; }
.settings-card { background: #fff; padding: 30px; border-radius: 0 0 20px 20px; }
.settings-card h2 { font-size: 18px; font-weight: 600; margin-bottom: 20px; color: #262626; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 15px; }
.input { width: 100%; padding: 12px 15px; border: 1px solid #d9d9d9; border-radius: 10px; font-size: 14px; transition: all 0.3s; }
.input:focus { outline: none; border-color: var(--main-red); box-shadow: 0 0 0 3px rgba(255, 77, 79, 0.1); }
.header-actions { display: flex; gap: 10px; align-items: center; }
.btn-outline { background: transparent; border: 1px solid rgba(255,255,255,0.3); color: #fff; }
.btn-outline:hover { background: rgba(255,255,255,0.1); border-color: #fff; }
.btn { padding: 12px 24px; border: none; border-radius: 10px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.3s; }
.btn-primary { background: var(--main-red); color: #fff; box-shadow: 0 4px 15px rgba(255, 77, 79, 0.4); }
.admin-header { /* Modal */
background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%); .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); z-index: 1000; align-items: center; justify-content: center; }
color: #fff; .modal.active { display: flex; }
padding: 25px 30px; .modal-content { background: #fff; border-radius: 20px; width: 90%; max-width: 500px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); }
border-radius: 20px 20px 0 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.header-left h1 { @media (max-width: 768px) { .stats-grid, .quick-actions, .form-row { grid-template-columns: 1fr; } .admin-header { flex-direction: column; align-items: flex-start; gap: 15px; } .stat-card, .action-btn { height: auto; } }
font-size: 22px;
font-weight: 700;
margin-bottom: 5px;
}
.username {
font-size: 13px;
color: #8c8c8c;
}
.admin-content {
background: #fff;
padding: 30px;
}
.main-content {
padding: 30px;
background: #fff;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-bottom: 20px;
}
.stat-card {
display: flex;
align-items: center;
gap: 15px;
padding: 20px;
background: #fafafa;
border-radius: 15px;
transition: all 0.3s;
height: 100px;
}
.stat-card:hover {
background: #f0f0f0;
transform: translateY(-2px);
}
.stat-icon {
font-size: 36px;
}
.stat-info {
flex: 1;
}
.stat-value {
font-size: 32px;
font-weight: 800;
color: var(--main-red);
line-height: 1;
}
.stat-label {
font-size: 13px;
color: #8c8c8c;
margin-top: 5px;
}
.quick-actions {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
}
.action-btn {
background: #fff;
border: 2px solid #f0f0f0;
border-radius: 15px;
padding: 20px;
cursor: pointer;
transition: all 0.3s;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
height: 100px;
}
.action-btn:hover {
border-color: var(--main-red);
background: #fff2f0;
transform: translateY(-3px);
box-shadow: 0 4px 15px rgba(255, 77, 79, 0.2);
}
.action-icon {
font-size: 32px;
}
.action-label {
font-size: 14px;
font-weight: 600;
color: #262626;
}
.settings-card {
background: #fff;
padding: 30px;
border-radius: 0 0 20px 20px;
}
.settings-card h2 {
font-size: 18px;
font-weight: 600;
margin-bottom: 20px;
color: #262626;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-bottom: 15px;
}
.input {
width: 100%;
padding: 12px 15px;
border: 1px solid #d9d9d9;
border-radius: 10px;
font-size: 14px;
transition: all 0.3s;
}
.input:focus {
outline: none;
border-color: var(--main-red);
box-shadow: 0 0 0 3px rgba(255, 77, 79, 0.1);
}
.form-actions {
display: flex;
gap: 10px;
}
.header-actions {
display: flex;
gap: 10px;
align-items: center;
}
.btn-outline {
background: transparent;
border: 1px solid rgba(255,255,255,0.3);
color: #fff;
}
.btn-outline:hover {
background: rgba(255,255,255,0.1);
border-color: #fff;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 10px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
}
.btn-primary {
background: var(--main-red);
color: #fff;
box-shadow: 0 4px 15px rgba(255, 77, 79, 0.4);
}
.btn-primary:hover {
background: #ff7875;
}
@media (max-width: 768px) {
.stats-grid,
.quick-actions,
.form-row {
grid-template-columns: 1fr;
}
.admin-header {
flex-direction: column;
align-items: flex-start;
gap: 15px;
}
.stat-card,
.action-btn {
height: auto;
}
}
</style> </style>
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
<script> <script>
// 初始化
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
loadStats();
loadUsername(); loadUsername();
}); });
// 加载用户名
async function loadUsername() { async function loadUsername() {
try { try {
const response = await fetch('/api/admin/login/status'); const response = await fetch('/api/admin/login/status?t=' + new Date().getTime());
const data = await response.json(); const data = await response.json();
if (data.logged_in) { if (data.logged_in) {
document.getElementById('username').textContent = data.username; document.getElementById('username').textContent = data.username;
} else { if (!data.must_change) loadStats();
window.location.href = '/admin/login'; } else { window.location.href = '/admin/login'; }
} } catch (err) { window.location.href = '/admin/login'; }
} catch (err) {
window.location.href = '/admin/login';
}
} }
// 加载统计数据
async function loadStats() { async function loadStats() {
try { try {
const [servicesResp, categoriesResp] = await Promise.all([ const [servicesResp, categoriesResp] = await Promise.all([
fetch('/api/admin/services'), fetch('/api/admin/services?t=' + new Date().getTime()),
fetch('/api/admin/categories') fetch('/api/admin/categories?t=' + new Date().getTime())
]); ]);
const services = await servicesResp.json(); const services = await servicesResp.json();
const categories = await categoriesResp.json(); const categories = await categoriesResp.json();
document.getElementById('totalServices').textContent = services.length; document.getElementById('totalServices').textContent = services.length;
document.getElementById('enabledServices').textContent = if (document.getElementById('enabledServices')) {
services.filter(s => s.is_enabled).length; document.getElementById('enabledServices').textContent = services.filter(s => s.is_enabled).length;
document.getElementById('totalCategories').textContent = categories.length; document.getElementById('totalCategories').textContent = categories.length;
} catch (err) {
console.error('加载统计失败:', err);
} }
} catch (err) {}
} }
// 健康检测
async function runHealthCheck() { async function runHealthCheck() {
const btn = event.target.closest('.action-btn');
const icon = btn.querySelector('.action-icon');
const label = btn.querySelector('.action-label');
try { try {
icon.textContent = '⏳'; const response = await fetch('/api/admin/health-check', { method: 'POST' });
label.textContent = '检测中...';
const response = await fetch('/api/admin/health-check', {
method: 'POST'
});
const data = await response.json(); const data = await response.json();
if (data.results) { if (data.results) {
const online = data.results.filter(r => r.status === 'online').length; const online = data.results.filter(r => r.status === 'online').length;
const offline = data.results.filter(r => r.status === 'offline').length; const offline = data.results.filter(r => r.status === 'offline').length;
alert(`检测完成:\n在线: ${online}\n离线: ${offline}`); alert(`检测完成:\n在线: ${online}\n离线: ${offline}`);
} }
} catch (err) { alert('检测失败: ' + err.message); }
}
// 备份相关逻辑
function backupLocal() { window.location.href = '/api/admin/backup/local'; }
async function showBackupModal() {
try {
const response = await fetch('/api/admin/settings');
const data = await response.json();
document.getElementById('webdavUrl').value = data.webdav_url || '';
document.getElementById('webdavUser').value = data.webdav_user || '';
document.getElementById('webdavPass').value = data.webdav_password || '';
document.getElementById('backupModal').classList.add('active');
loadBackupList();
} catch (err) { alert('加载配置失败'); }
}
async function loadBackupList() {
const listDiv = document.getElementById('backupFileList');
try {
const response = await fetch('/api/admin/backup/list');
const data = await response.json();
if (data.files && data.files.length > 0) {
let html = '';
data.files.forEach(file => {
html += `
<div style="padding: 10px 15px; border-bottom: 1px solid #f9f9f9; display: flex; justify-content: space-between; align-items: center;">
<span style="font-size: 13px; font-family: monospace;">${file}</span>
<button onclick="restoreFromCloud('${file}')" style="background: #faad14; color: #fff; border: none; border-radius: 4px; padding: 4px 10px; font-size: 12px; cursor: pointer;">恢复</button>
</div>
`;
});
listDiv.innerHTML = html;
} else {
listDiv.innerHTML = '<div style="padding: 15px; text-align: center; color: #999; font-size: 13px;">云端暂无备份包</div>';
}
} catch (err) { } catch (err) {
alert('检测失败: ' + err.message); listDiv.innerHTML = '<div style="padding: 15px; text-align: center; color: #ff4d4f; font-size: 13px;">获取列表失败</div>';
} finally { }
icon.textContent = '🔍'; }
label.textContent = '健康检测';
async function restoreFromCloud(filename) {
if (!confirm(`⚠️ 警告:确定要从备份 ${filename} 恢复吗?当前所有数据将被覆盖!系统将自动备份当前版本为 tonav.db.bak`)) return;
try {
const response = await fetch('/api/admin/backup/restore', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({filename: filename})
});
const data = await response.json();
if (response.ok) { alert(data.message); location.reload(); }
else { alert('恢复失败: ' + data.error); }
} catch (err) { alert('请求异常'); }
}
function closeBackupModal() { document.getElementById('backupModal').classList.remove('active'); }
async function saveBackupSettings() {
const data = {
webdav_url: document.getElementById('webdavUrl').value,
webdav_user: document.getElementById('webdavUser').value,
webdav_password: document.getElementById('webdavPass').value
};
try {
const response = await fetch('/api/admin/settings', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
if (response.ok) alert('配置保存成功');
else alert('保存失败');
} catch (err) { alert('请求错误'); }
}
async function testWebDAV() {
const data = {
webdav_url: document.getElementById('webdavUrl').value,
webdav_user: document.getElementById('webdavUser').value,
webdav_password: document.getElementById('webdavPass').value
};
const btn = document.getElementById('btnTest');
btn.disabled = true; btn.textContent = '测试中...';
try {
const response = await fetch('/api/admin/backup/test', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
const res = await response.json();
if (response.ok) alert(res.message);
else alert(res.error);
} catch (err) { alert('测试异常'); }
finally { btn.disabled = false; btn.textContent = '连通性测试'; }
}
async function runCloudBackup() {
try {
const response = await fetch('/api/admin/backup/webdav', { method: 'POST' });
const data = await response.json();
if (response.ok) {
alert(data.message || '云端备份成功!');
loadBackupList();
} else {
alert('备份失败: ' + (data.error || '未知错误'));
}
} catch (err) {
alert('请求失败: ' + err.message);
} }
} }
// 修改密码
document.getElementById('changePasswordForm').addEventListener('submit', async function(e) { document.getElementById('changePasswordForm').addEventListener('submit', async function(e) {
e.preventDefault(); e.preventDefault();
const oldPassword = document.getElementById('oldPassword').value; const oldPassword = document.getElementById('oldPassword').value;
const newPassword = document.getElementById('newPassword').value; const newPassword = document.getElementById('newPassword').value;
try { try {
const response = await fetch('/api/admin/change-password', { const response = await fetch('/api/admin/change-password', {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: JSON.stringify({old_password: oldPassword, new_password: newPassword}) body: JSON.stringify({old_password: oldPassword, new_password: newPassword})
}); });
const data = await response.json(); const data = await response.json();
if (response.ok) { alert('密码修改成功'); location.reload(); }
if (response.ok) { else { alert(data.error || '修改失败'); }
alert('密码修改成功,请重新登录'); } catch (err) { alert('请求失败'); }
this.reset();
} else {
alert(data.error || '修改失败');
}
} catch (err) {
alert('请求失败: ' + err.message);
}
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -46,6 +46,11 @@
<input type="text" id="serviceDesc" class="input" placeholder="简短描述"> <input type="text" id="serviceDesc" class="input" placeholder="简短描述">
</div> </div>
<div class="form-group">
<label>标签 (英文逗号分隔)</label>
<input type="text" id="serviceTags" class="input" placeholder="工具, 开发, 常用">
</div>
<div class="form-row"> <div class="form-row">
<div class="form-group"> <div class="form-group">
<label>图标emoji</label> <label>图标emoji</label>
@@ -593,6 +598,7 @@
document.getElementById('serviceName').value = service.name; document.getElementById('serviceName').value = service.name;
document.getElementById('serviceUrl').value = service.url; document.getElementById('serviceUrl').value = service.url;
document.getElementById('serviceDesc').value = service.description || ''; document.getElementById('serviceDesc').value = service.description || '';
document.getElementById('serviceTags').value = service.tags || '';
document.getElementById('serviceIcon').value = service.icon || ''; document.getElementById('serviceIcon').value = service.icon || '';
document.getElementById('serviceCategory').value = service.category; document.getElementById('serviceCategory').value = service.category;
document.getElementById('serviceSort').value = service.sort_order; document.getElementById('serviceSort').value = service.sort_order;
@@ -611,6 +617,7 @@
name: document.getElementById('serviceName').value, name: document.getElementById('serviceName').value,
url: document.getElementById('serviceUrl').value, url: document.getElementById('serviceUrl').value,
description: document.getElementById('serviceDesc').value, description: document.getElementById('serviceDesc').value,
tags: document.getElementById('serviceTags').value,
icon: document.getElementById('serviceIcon').value, icon: document.getElementById('serviceIcon').value,
category: document.getElementById('serviceCategory').value, category: document.getElementById('serviceCategory').value,
sort_order: parseInt(document.getElementById('serviceSort').value) || 0, sort_order: parseInt(document.getElementById('serviceSort').value) || 0,

View File

@@ -13,6 +13,12 @@
</div> </div>
</div> </div>
<!-- 搜索栏 -->
<div class="search-bar">
<input type="text" id="searchInput" placeholder="搜索服务或描述..." oninput="handleSearch()">
<span class="search-icon">🔍</span>
</div>
<!-- 分类 Tabs --> <!-- 分类 Tabs -->
<div class="tabs" id="categoryTabs"> <div class="tabs" id="categoryTabs">
<div class="loading" style="color: #8c8c8c; font-size: 12px; padding: 10px;">加载分类...</div> <div class="loading" style="color: #8c8c8c; font-size: 12px; padding: 10px;">加载分类...</div>
@@ -55,6 +61,37 @@
color: #595959; color: #595959;
} }
.search-bar {
background: #262626;
padding: 15px 20px;
position: relative;
display: flex;
align-items: center;
}
.search-bar input {
width: 100%;
background: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.2);
border-radius: 10px;
padding: 10px 15px 10px 40px;
color: #fff;
font-size: 14px;
transition: all 0.3s;
}
.search-bar input:focus {
outline: none;
background: rgba(255,255,255,0.15);
border-color: var(--main-red);
}
.search-icon {
position: absolute;
left: 35px;
color: #8c8c8c;
}
.tabs { .tabs {
display: flex; display: flex;
background: #262626; background: #262626;
@@ -169,6 +206,22 @@
flex: 1; flex: 1;
} }
.card-tags {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin: 5px 0;
}
.mini-tag {
font-size: 10px;
background: rgba(102, 126, 226, 0.1);
color: #667eea;
padding: 1px 6px;
border-radius: 4px;
border: 1px solid rgba(102, 126, 226, 0.2);
}
.card-footer { .card-footer {
font-size: 11px; font-size: 11px;
color: #bfbfbf; color: #bfbfbf;
@@ -306,14 +359,29 @@
}); });
} }
let currentKeyword = '';
function handleSearch() {
currentKeyword = document.getElementById('searchInput').value.toLowerCase();
renderServices(window.currentTab || 'all');
}
// 渲染服务卡片 // 渲染服务卡片
function renderServices(category) { function renderServices(category) {
const container = document.getElementById('servicesGrid'); const container = document.getElementById('servicesGrid');
const filteredServices = category === 'all' let filteredServices = category === 'all'
? allServices ? allServices
: allServices.filter(s => s.category === category); : allServices.filter(s => s.category === category);
// 关键词过滤
if (currentKeyword) {
filteredServices = filteredServices.filter(s =>
s.name.toLowerCase().includes(currentKeyword) ||
(s.description && s.description.toLowerCase().includes(currentKeyword))
);
}
if (filteredServices.length === 0) { if (filteredServices.length === 0) {
container.innerHTML = '<div class="empty-state">暂无服务</div>'; container.innerHTML = '<div class="empty-state">暂无服务</div>';
return; return;
@@ -334,14 +402,20 @@
} }
html += ` html += `
<a href="${service.url}" target="_blank" class="service-card" style="animation-delay: ${index * 0.05}s"> <a href="/visit/${service.id}" target="_blank" class="service-card" style="animation-delay: ${index * 0.05}s">
<div class="card-header"> <div class="card-header">
<span class="card-icon">${service.icon || '📡'}</span> <span class="card-icon">${service.icon || '📡'}</span>
<span class="card-status ${statusClass}">${statusIcon}</span> <span class="card-status ${statusClass}">${statusIcon}</span>
</div> </div>
<div class="card-name">${service.name}</div> <div class="card-name">${service.name}</div>
<div class="card-desc">${service.description || ''}</div> <div class="card-desc">${service.description || ''}</div>
<div class="card-footer">${service.category}</div> <div class="card-tags">
${service.tags ? service.tags.split(',').map(tag => `<span class="mini-tag">${tag.trim()}</span>`).join('') : ''}
</div>
<div class="card-footer">
<span>${service.category}</span>
<span style="float: right;">🔥 ${service.click_count || 0}</span>
</div>
</a> </a>
`; `;
}); });

BIN
tonav.db

Binary file not shown.

990
tonav.log
View File

@@ -3078,3 +3078,993 @@
127.0.0.1 - - [13/Feb/2026 07:00:17] "POST /api/admin/health-check HTTP/1.1" 200 - 127.0.0.1 - - [13/Feb/2026 07:00:17] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:00:46] "GET /api/services?t=1770937245518 HTTP/1.1" 200 - 127.0.0.1 - - [13/Feb/2026 07:00:46] "GET /api/services?t=1770937245518 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:00:46] "GET /api/services?t=1770937245542 HTTP/1.1" 200 - 127.0.0.1 - - [13/Feb/2026 07:00:46] "GET /api/services?t=1770937245542 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:00:49] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:00:49] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:29] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:29] "GET /api/categories?t=1770937348821 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:29] "GET /api/services?t=1770937348822 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:31] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:32] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:32] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:32] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:32] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:34] "GET /api/services?t=1770937353253 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:37] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:38] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:38] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:38] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:02:38] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:03:09] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:03:09] "GET /api/categories?t=1770937388547 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:03:09] "GET /api/services?t=1770937388548 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:03:12] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:03:15] "GET /api/services?t=1770937394717 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:03:18] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:03:39] "GET /api/services?t=1770937418683 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:03:42] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:04:09] "GET /api/services?t=1770937448682 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:04:12] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:18] "GET /api/services?t=1770937817131 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:18] "GET /api/services?t=1770937817135 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:19] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:19] "GET /api/categories?t=1770937818789 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:19] "GET /api/services?t=1770937818789 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:21] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:21] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:22] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:24] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:24] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:24] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:24] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:29] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:29] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:29] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:29] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:31] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:31] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:31] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:31] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:33] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:33] "GET /api/categories?t=1770937832733 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:33] "GET /api/services?t=1770937832734 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:36] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:43] "GET /api/services?t=1770937841944 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:43] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:44] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:44] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:44] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:45] "GET /admin/logout HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:10:45] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:45] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:51] "POST /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:10:51] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:51] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:51] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:51] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:10:57] "GET /admin/logout HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:10:57] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:21] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:21] "GET /api/categories?t=1770937880268 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:21] "GET /api/services?t=1770937880270 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:21] "GET /favicon.ico HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:11:21] "POST /api/admin/health-check HTTP/1.1" 401 -
127.0.0.1 - - [13/Feb/2026 07:11:24] "GET /admin HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:11:24] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:29] "POST /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:11:29] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:29] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:29] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:29] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:33] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:33] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:33] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:33] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:34] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:34] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:34] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:11:34] "GET /api/admin/services HTTP/1.1" 200 -
健康检查线程已启动
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:9519
Press CTRL+C to quit
127.0.0.1 - - [13/Feb/2026 07:13:33] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:33] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:33] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:33] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:33] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:33] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:34] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:34] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:34] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:34] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:34] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:34] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:34] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:35] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:35] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:35] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:35] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:35] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:35] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:35] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:35] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:36] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:36] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:36] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:36] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:37] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:37] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:37] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:37] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:37] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:37] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:38] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:38] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:38] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:38] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:38] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:38] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:38] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:39] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:39] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:39] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:39] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:39] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:39] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:39] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:40] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:40] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:40] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:40] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:40] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:40] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:40] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:41] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:41] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:41] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:41] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:41] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:41] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:41] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:41] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:42] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:42] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:42] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:42] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:42] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:43] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:43] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:43] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:43] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:43] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:43] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:43] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:44] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:44] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:44] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:44] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:44] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:44] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:45] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:45] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:45] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:45] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:45] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:45] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:45] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:46] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:46] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:46] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:46] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:46] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:46] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:46] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:46] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:47] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:47] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:47] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:47] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:47] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:47] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:47] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:48] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:48] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:48] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:48] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:48] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:49] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:49] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:49] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:49] "GET /admin/logout HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:49] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:49] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:49] "GET /admin HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:49] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:52] "POST /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:53] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:53] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:53] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:53] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:53] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:53] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:54] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:54] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:54] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:54] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:54] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:54] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:55] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:55] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:55] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:55] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:55] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:56] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:56] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:56] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:56] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:56] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:56] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:57] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:57] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:57] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:57] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:57] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:57] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:57] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:58] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:58] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:58] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:58] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:58] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:58] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:13:59] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:59] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:59] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:13:59] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:00] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:00] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:00] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:00] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:00] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:00] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:00] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:00] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:01] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:01] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:01] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:01] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:01] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:03] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:03] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:03] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:04] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:04] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:04] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:04] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:04] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:04] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:05] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:05] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:05] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:05] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:05] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:05] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:06] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:06] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:06] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:06] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:06] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:18] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:18] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:18] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:18] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:19] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:19] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:19] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:19] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:19] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:19] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:19] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:19] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:20] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:20] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:20] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:20] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:20] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:21] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:21] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:21] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:21] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:21] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:21] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:21] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:21] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:22] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:22] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:22] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:22] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:22] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:22] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:23] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:23] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:23] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:23] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:23] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:23] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:24] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:24] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:24] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:24] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:25] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:25] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:25] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:25] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:26] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:26] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:26] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:26] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:27] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:27] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:27] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:27] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:27] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:27] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:27] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:28] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:14:28] "GET /api/admin/services HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:28] "GET /api/admin/categories HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:28] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:14:28] "GET /admin HTTP/1.1" 200 -
健康检查线程已启动
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:9519
Press CTRL+C to quit
127.0.0.1 - - [13/Feb/2026 07:15:23] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:23] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:23] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:24] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:25] "GET /admin/logout HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:26] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:30] "POST /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:30] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:30] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:31] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:31] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:31] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:32] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:32] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:32] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:32] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:33] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:33] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:33] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:34] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:34] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:34] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:35] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:35] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:35] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:36] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:36] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:36] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:36] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:37] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:37] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:37] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:38] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:38] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:38] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:39] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:39] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:39] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:40] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:40] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:40] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:40] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:41] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:41] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:41] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:42] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:42] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:43] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:43] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:44] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:44] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:44] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:45] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:45] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:45] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:45] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:46] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:46] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:46] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:47] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:53] "POST /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:54] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:54] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:54] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:54] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:54] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:55] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:55] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:55] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:55] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:56] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:56] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:56] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:56] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:56] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:57] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:57] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:57] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:57] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:58] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:58] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:58] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:58] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:58] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:59] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:59] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:15:59] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:15:59] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:00] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:00] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:00] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:00] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:00] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:01] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:01] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:01] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:01] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:02] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:02] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:02] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:02] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:03] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:03] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:04] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:05] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:05] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:05] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:05] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:06] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:06] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:06] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:07] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:07] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:07] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:18] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:18] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:18] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:18] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:19] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:19] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:19] "GET /admin/logout HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:19] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:19] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:20] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:20] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:20] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:20] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:20] "GET /admin/logout HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:21] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:39] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:39] "GET /api/services?t=1770938198575 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:39] "GET /api/categories?t=1770938198574 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:39] "POST /api/admin/health-check HTTP/1.1" 401 -
127.0.0.1 - - [13/Feb/2026 07:16:44] "GET /admin HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:44] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:51] "POST /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:52] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:52] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:52] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:52] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:53] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:53] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:53] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:53] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:54] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:54] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:54] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:54] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:55] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:55] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:55] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:55] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:56] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:56] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:56] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:56] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:56] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:57] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:57] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:57] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:58] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:58] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:59] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:16:59] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:16:59] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:17:00] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:17:00] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:17:00] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:17:00] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:17:01] "GET /api/admin/login/status HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:17:01] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:17:01] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:17:01] "GET /admin HTTP/1.1" 200 -
健康检查线程已启动
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:9519
Press CTRL+C to quit
127.0.0.1 - - [13/Feb/2026 07:18:08] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:08] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:08] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:09] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:09] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:09] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:09] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:10] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:10] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:10] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:10] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:11] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:11] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:11] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:11] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:11] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:11] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:12] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:12] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:12] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:12] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:13] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:13] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:13] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:13] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:13] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:14] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:14] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:14] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:15] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:15] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:15] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:15] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:15] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:16] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:16] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:16] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:16] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:17] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:21] "POST /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:21] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:21] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:22] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:22] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:22] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:22] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:23] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:23] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:23] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:23] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:23] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:24] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:24] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:24] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:24] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:25] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:25] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:25] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:25] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:25] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:26] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:26] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:26] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:26] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:27] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:27] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:27] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:27] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:29] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:30] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:30] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:30] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:30] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:31] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:31] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:31] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:31] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:32] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:35] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:35] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:35] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:36] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:36] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:18:36] "GET /api/admin/login/status HTTP/1.1" 403 -
127.0.0.1 - - [13/Feb/2026 07:18:36] "GET /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:18:51] "POST /api/admin/change-password HTTP/1.1" 400 -
127.0.0.1 - - [13/Feb/2026 07:19:03] "POST /api/admin/change-password HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:19:04] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:19:05] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:19:05] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:19:05] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:19:10] "GET /admin/logout HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:19:10] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:19:13] "POST /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:19:13] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:19:13] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:19:13] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:19:13] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:19:36] "GET /api/admin/backup/local HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:20:43] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:20:43] "GET /api/categories?t=1770938442822 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:20:43] "GET /api/services?t=1770938442823 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:20:46] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:20:58] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:20:58] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:20:58] "GET /favicon.ico HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:20:58] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:20:58] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:21:02] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:21:02] "GET /api/categories?t=1770938461825 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:21:02] "GET /api/services?t=1770938461826 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:21:05] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:22:45] "GET /api/services?t=1770938564541 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:22:45] "GET /api/services?t=1770938564541 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:22:46] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:22:47] "GET /api/categories?t=1770938566122 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:22:47] "GET /api/services?t=1770938566123 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:22:48] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:22:48] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:22:50] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:08] "GET /api/services?t=1770938587200 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:11] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:26] "GET /api/services?t=1770938605889 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:26] "GET /api/services?t=1770938605888 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:28] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:28] "GET /api/categories?t=1770938607428 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:28] "GET /api/services?t=1770938607429 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:29] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:29] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:30] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:30] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:30] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:30] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:31] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:40] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:41] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:41] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:41] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:46] "GET /api/services?t=1770938625331 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:46] "GET /api/services?t=1770938625331 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:48] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:48] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:48] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:48] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:49] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:50] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:23:56] "POST /api/admin/backup/webdav HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:02] "POST /api/admin/backup/webdav HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:04] "GET /admin/logout HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:24:04] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:06] "POST /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:24:06] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:06] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:07] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:07] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:14] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:14] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:14] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:14] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:15] "GET /admin/logout HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:24:15] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:43] "POST /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:24:43] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:44] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:44] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:44] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:48] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:49] "GET /api/admin/login/status HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:49] "GET /api/admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:24:49] "GET /api/admin/categories HTTP/1.1" 200 -
健康检查线程已启动
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:9519
Press CTRL+C to quit
127.0.0.1 - - [13/Feb/2026 07:25:59] "POST /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:25:59] "GET /api/admin/settings HTTP/1.1" 401 -
127.0.0.1 - - [13/Feb/2026 07:26:59] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:27:00] "GET /api/admin/login/status?t=1770938819222 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:27:00] "GET /api/admin/categories?t=1770938819411 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:27:00] "GET /api/admin/services?t=1770938819410 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:27:03] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:27:12] "POST /api/admin/backup/test HTTP/1.1" 200 -
健康检查线程已启动
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:9519
Press CTRL+C to quit
127.0.0.1 - - [13/Feb/2026 07:29:24] "GET /admin/logout HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:29:24] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:29:30] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:29:30] "GET /api/categories?t=1770938969582 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:29:30] "GET /api/services?t=1770938969585 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:29:30] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:29:30] "GET /api/categories?t=1770938969951 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:29:30] "GET /api/services?t=1770938969952 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:29:31] "POST /api/admin/health-check HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:30:01] "GET /api/services?t=1770938999952 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:30:01] "POST /api/admin/health-check HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:30:19] "GET /admin HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:30:19] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:30:22] "POST /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:30:22] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:30:22] "GET /api/admin/login/status?t=1770939021569 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:30:22] "GET /api/admin/categories?t=1770939021744 HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:30:22] "GET /api/admin/services?t=1770939021744 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:30:28] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:30:29] "GET /api/admin/login/status?t=1770939028064 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:30:29] "GET /api/admin/categories?t=1770939028240 HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:30:29] "GET /api/admin/services?t=1770939028240 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:30:32] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:31:33] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:31:33] "GET /favicon.ico HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:31:33] "GET /api/services?t=1770939092858 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:31:33] "GET /api/categories?t=1770939092857 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:31:34] "POST /api/admin/health-check HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:32:04] "GET /api/services?t=1770939123679 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:32:04] "POST /api/admin/health-check HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:32:19] "POST /api/admin/backup/test HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:32:26] "POST /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:32:28] "POST /api/admin/backup/webdav HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:32:30] "POST /api/admin/backup/webdav HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:32:33] "POST /api/admin/backup/test HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:32:36] "POST /api/admin/backup/test HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:32:39] "POST /api/admin/backup/webdav HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:32:44] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:32:50] "POST /api/admin/backup/webdav HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:32:52] "POST /api/admin/backup/webdav HTTP/1.1" 404 -
健康检查线程已启动
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:9519
Press CTRL+C to quit
127.0.0.1 - - [13/Feb/2026 07:36:29] "GET /admin/logout HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:36:31] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:32] "GET /api/admin/login/status?t=1770939391262 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:32] "GET /api/admin/categories?t=1770939391464 HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:36:32] "GET /api/admin/services?t=1770939391464 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:33] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:35] "GET /api/services?t=1770939394246 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:35] "GET /api/categories?t=1770939394246 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:35] "POST /api/admin/health-check HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:36:37] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:37] "GET /api/services?t=1770939396717 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:37] "GET /api/categories?t=1770939396717 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:37] "POST /api/admin/health-check HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:36:43] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:43] "GET /api/admin/login/status?t=1770939402460 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:43] "GET /api/admin/categories?t=1770939402645 HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:36:43] "GET /api/admin/services?t=1770939402645 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:46] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:46] "GET /api/admin/login/status?t=1770939405815 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:47] "GET /api/admin/categories?t=1770939406008 HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:36:47] "GET /api/admin/services?t=1770939406008 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:51] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:51] "GET /api/admin/login/status?t=1770939410341 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:51] "GET /api/admin/categories?t=1770939410535 HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:36:51] "GET /api/admin/services?t=1770939410535 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:52] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:52] "GET /api/categories?t=1770939411561 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:52] "GET /api/services?t=1770939411562 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:52] "POST /api/admin/health-check HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:36:54] "GET /admin/logout HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:36:59] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:59] "GET /api/admin/login/status?t=1770939418688 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:59] "GET /api/admin/services?t=1770939418881 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:36:59] "GET /api/admin/categories?t=1770939418881 HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:37:00] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:37:01] "GET /api/admin/login/status?t=1770939420074 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:37:01] "GET /api/admin/categories?t=1770939420267 HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:37:01] "GET /api/admin/services?t=1770939420267 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:37:03] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:37:04] "GET /api/admin/backup/list HTTP/1.1" 200 -
[2026-02-13 07:37:18,512] INFO: MKCOL https://chfs.ouaone.top/webdav: 405
[2026-02-13 07:37:18,872] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw: 405
[2026-02-13 07:37:19,217] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw/backup: 405
127.0.0.1 - - [13/Feb/2026 07:37:19] "POST /api/admin/backup/webdav HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:37:22] "POST /api/admin/backup/test HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:37:33] "POST /api/admin/backup/test HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:37:35] "POST /api/admin/backup/test HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:37:36] "POST /api/admin/backup/test HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:37:42] "POST /api/admin/backup/test HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:37:45] "POST /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:37:48] "POST /api/admin/backup/test HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:38:07] "POST /api/admin/backup/test HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:38:41] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:41] "GET /api/admin/login/status?t=1770939520618 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:41] "GET /api/admin/categories?t=1770939520785 HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:38:41] "GET /api/admin/services?t=1770939520785 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:43] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:43] "GET /api/admin/login/status?t=1770939522798 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:43] "GET /api/admin/categories?t=1770939522972 HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:38:43] "GET /api/admin/services?t=1770939522971 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:44] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:45] "GET /api/admin/login/status?t=1770939524210 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:45] "GET /api/admin/categories?t=1770939524385 HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:38:45] "GET /api/admin/services?t=1770939524384 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:46] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:46] "GET /api/admin/login/status?t=1770939525452 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:46] "GET /api/admin/categories?t=1770939525629 HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:38:46] "GET /api/admin/services?t=1770939525629 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:48] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:49] "GET /api/admin/backup/list HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:38:50] "POST /api/admin/backup/test HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:38:59] "POST /api/admin/backup/test HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:39:03] "POST /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:39:04] "POST /api/admin/backup/test HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:40:18] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:40:18] "GET /api/admin/backup/list HTTP/1.1" 200 -
健康检查线程已启动
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:9519
Press CTRL+C to quit
127.0.0.1 - - [13/Feb/2026 07:41:09] "POST /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:41:09] "GET /api/admin/services?t=1 HTTP/1.1" 401 -
127.0.0.1 - - [13/Feb/2026 07:42:19] "GET /admin/logout HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:43:07] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:07] "GET /api/admin/login/status?t=1770939786385 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:07] "GET /api/admin/services?t=1770939786608 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:07] "GET /api/admin/categories?t=1770939786609 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:08] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:08] "GET /api/categories?t=1770939787381 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:08] "GET /api/services?t=1770939787382 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:08] "POST /api/admin/health-check HTTP/1.1" 404 -
127.0.0.1 - - [13/Feb/2026 07:43:14] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:14] "GET /api/admin/login/status?t=1770939793949 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:15] "GET /api/admin/services?t=1770939794178 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:15] "GET /api/admin/categories?t=1770939794178 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:18] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:19] "GET /api/admin/backup/list HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:20] "POST /api/admin/backup/test HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:24] "POST /api/admin/settings HTTP/1.1" 200 -
[2026-02-13 07:43:26,125] INFO: MKCOL https://chfs.ouaone.top/webdav: 405
[2026-02-13 07:43:26,484] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw: 405
[2026-02-13 07:43:26,850] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw/backup: 405
127.0.0.1 - - [13/Feb/2026 07:43:27] "POST /api/admin/backup/webdav HTTP/1.1" 200 -
[2026-02-13 07:43:31,197] INFO: MKCOL https://chfs.ouaone.top/webdav: 405
[2026-02-13 07:43:31,555] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw: 405
[2026-02-13 07:43:31,924] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw/backup: 405
127.0.0.1 - - [13/Feb/2026 07:43:32] "POST /api/admin/backup/webdav HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:43:38] "POST /api/admin/backup/test HTTP/1.1" 200 -
[2026-02-13 07:43:40,709] INFO: MKCOL https://chfs.ouaone.top/webdav: 405
[2026-02-13 07:43:41,062] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw: 405
[2026-02-13 07:43:41,419] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw/backup: 405
127.0.0.1 - - [13/Feb/2026 07:43:42] "POST /api/admin/backup/webdav HTTP/1.1" 200 -
健康检查线程已启动
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:9519
Press CTRL+C to quit
127.0.0.1 - - [13/Feb/2026 07:45:04] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:04] "GET /api/admin/login/status?t=1770939903891 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:05] "GET /api/admin/services?t=1770939904083 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:05] "GET /api/admin/categories?t=1770939904083 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:06] "GET /admin/logout HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:45:07] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:13] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:13] "GET /api/categories?t=1770939912831 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:13] "GET /api/services?t=1770939912834 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:13] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:14] "GET /api/categories?t=1770939913206 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:14] "GET /api/services?t=1770939913206 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:14] "POST /api/admin/health-check HTTP/1.1" 401 -
127.0.0.1 - - [13/Feb/2026 07:45:16] "GET /admin HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:45:16] "GET /admin/login HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:19] "POST /admin/login HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:45:19] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:19] "GET /api/admin/login/status?t=1770939918455 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:19] "GET /api/admin/services?t=1770939918638 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:19] "GET /api/admin/categories?t=1770939918638 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:21] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:22] "GET /api/admin/backup/list HTTP/1.1" 200 -
[2026-02-13 07:45:33,281] INFO: MKCOL https://chfs.ouaone.top/webdav: 405
[2026-02-13 07:45:33,644] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw: 405
[2026-02-13 07:45:33,997] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw/backup: 405
127.0.0.1 - - [13/Feb/2026 07:45:34] "POST /api/admin/backup/webdav HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:44] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:44] "GET /api/admin/login/status?t=1770939943362 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:44] "GET /api/admin/categories?t=1770939943545 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:44] "GET /api/admin/services?t=1770939943544 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:46] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:45:47] "GET /api/admin/backup/list HTTP/1.1" 200 -
[2026-02-13 07:46:06,691] INFO: MKCOL https://chfs.ouaone.top/webdav: 405
[2026-02-13 07:46:07,072] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw: 405
[2026-02-13 07:46:07,448] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw/backup: 405
127.0.0.1 - - [13/Feb/2026 07:46:08] "POST /api/admin/backup/webdav HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:46:11] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:46:12] "GET /api/admin/backup/list HTTP/1.1" 200 -
健康检查线程已启动
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:9519
Press CTRL+C to quit
127.0.0.1 - - [13/Feb/2026 07:48:28] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:48:28] "GET /api/admin/login/status?t=1770940107816 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:48:29] "GET /api/admin/services?t=1770940108062 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:48:29] "GET /api/admin/categories?t=1770940108063 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:48:31] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:48:31] "GET /api/admin/backup/list HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:48:33] "POST /api/admin/backup/test HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:48:35] "POST /api/admin/settings HTTP/1.1" 200 -
[2026-02-13 07:48:38,326] INFO: MKCOL https://chfs.ouaone.top/webdav: 405
[2026-02-13 07:48:38,700] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw: 405
[2026-02-13 07:48:39,044] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw/backup: 405
[2026-02-13 07:48:39,425] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw/backup/tonav: 201
127.0.0.1 - - [13/Feb/2026 07:48:40] "POST /api/admin/backup/webdav HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:48:43] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:48:44] "GET /api/admin/backup/list HTTP/1.1" 200 -
健康检查线程已启动
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:9519
Press CTRL+C to quit
127.0.0.1 - - [13/Feb/2026 07:52:08] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:52:08] "GET /api/admin/login/status?t=1770940327554 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:52:08] "GET /api/admin/categories?t=1770940327792 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:52:08] "GET /api/admin/services?t=1770940327792 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:52:10] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:52:11] "GET /api/admin/backup/list HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:52:12] "POST /api/admin/backup/test HTTP/1.1" 200 -
[2026-02-13 07:52:15,643] INFO: MKCOL https://chfs.ouaone.top/webdav: 405
[2026-02-13 07:52:16,033] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw: 405
[2026-02-13 07:52:16,402] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw/backup: 405
[2026-02-13 07:52:16,786] INFO: MKCOL https://chfs.ouaone.top/webdav/openclaw/backup/tonav: 405
127.0.0.1 - - [13/Feb/2026 07:52:17] "POST /api/admin/backup/webdav HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:52:20] "GET /api/admin/backup/list HTTP/1.1" 200 -
健康检查线程已启动
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:9519
Press CTRL+C to quit
127.0.0.1 - - [13/Feb/2026 07:55:48] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:55:48] "GET /api/admin/login/status?t=1770940547329 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:55:48] "GET /api/admin/services?t=1770940547520 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:55:48] "GET /api/admin/categories?t=1770940547521 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:55:51] "GET /admin/services HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:55:51] "GET /api/admin/services?t=1770940550299 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:55:54] "GET /api/admin/categories HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:03] "GET /admin HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:03] "GET /api/admin/login/status?t=1770940562797 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:04] "GET /api/admin/categories?t=1770940563003 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:04] "GET /api/admin/services?t=1770940563002 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:06] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:06] "GET /api/admin/backup/list HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:10] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:10] "GET /api/admin/backup/list HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:16] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:16] "GET /api/admin/backup/list HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:18] "GET /api/admin/settings HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:19] "GET /api/admin/backup/list HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:34] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:34] "GET /api/categories?t=1770940593746 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:34] "GET /api/services?t=1770940593747 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:38] "POST /api/admin/health-check HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:44] "GET /visit/5 HTTP/1.1" 302 -
127.0.0.1 - - [13/Feb/2026 07:56:47] "GET /api/services?t=1770940606354 HTTP/1.1" 200 -
127.0.0.1 - - [13/Feb/2026 07:56:50] "POST /api/admin/health-check HTTP/1.1" 200 -