Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7fede0212b | |||
| 801dd1e29b | |||
| e5ba13980f |
106
admin.py
106
admin.py
@@ -662,3 +662,109 @@ def api_user_add_package(user_id):
|
||||
'name': user_package.package_name,
|
||||
'remaining': user_package.remaining_count
|
||||
}})
|
||||
|
||||
|
||||
# ==================== LLM大模型配置 ====================
|
||||
@admin_bp.route('/llm_config')
|
||||
@admin_required
|
||||
def llm_config():
|
||||
"""LLM配置页面"""
|
||||
from config import LLM_CONFIG
|
||||
|
||||
# 从数据库获取配置,如果没有则使用默认值
|
||||
config = {
|
||||
'api_base': DynamicConfig.get('llm_api_base', LLM_CONFIG.get('api_base')),
|
||||
'api_key': DynamicConfig.get('llm_api_key', LLM_CONFIG.get('api_key')),
|
||||
'model': DynamicConfig.get('llm_model', LLM_CONFIG.get('model')),
|
||||
'max_tokens': DynamicConfig.get('llm_max_tokens', LLM_CONFIG.get('max_tokens')),
|
||||
'chunk_size': DynamicConfig.get('llm_chunk_size', LLM_CONFIG.get('chunk_size')),
|
||||
'timeout': DynamicConfig.get('llm_timeout', LLM_CONFIG.get('timeout')),
|
||||
}
|
||||
|
||||
return render_template('admin/llm_config.html', config=config)
|
||||
|
||||
|
||||
@admin_bp.route('/llm_config/save', methods=['POST'])
|
||||
@admin_required
|
||||
def save_llm_config():
|
||||
"""保存LLM配置"""
|
||||
data = request.json
|
||||
|
||||
DynamicConfig.set('llm_api_base', data.get('api_base'), category='llm', user_id=session.get('user_id'))
|
||||
DynamicConfig.set('llm_api_key', data.get('api_key'), category='llm', user_id=session.get('user_id'))
|
||||
DynamicConfig.set('llm_model', data.get('model'), category='llm', user_id=session.get('user_id'))
|
||||
DynamicConfig.set('llm_max_tokens', data.get('max_tokens'), category='llm', value_type='int', user_id=session.get('user_id'))
|
||||
DynamicConfig.set('llm_chunk_size', data.get('chunk_size'), category='llm', value_type='int', user_id=session.get('user_id'))
|
||||
DynamicConfig.set('llm_timeout', data.get('timeout'), category='llm', value_type='int', user_id=session.get('user_id'))
|
||||
|
||||
# 记录日志
|
||||
log = OperationLog(
|
||||
user_id=session.get('user_id'),
|
||||
username='admin',
|
||||
action='update_llm_config',
|
||||
detail='更新大模型配置'
|
||||
)
|
||||
db.session.add(log)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({'success': True})
|
||||
|
||||
|
||||
@admin_bp.route('/llm_config/test', methods=['POST'])
|
||||
@admin_required
|
||||
def test_llm_connection():
|
||||
"""测试LLM连接"""
|
||||
data = request.json
|
||||
|
||||
try:
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key=data.get('api_key', 'sk-test'),
|
||||
base_url=data.get('api_base'),
|
||||
)
|
||||
|
||||
# 发送简单测试请求
|
||||
response = client.chat.completions.create(
|
||||
model=data.get('model'),
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
max_tokens=10,
|
||||
timeout=10,
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'model': data.get('model'),
|
||||
'response': response.choices[0].message.content[:50] if response.choices else 'OK'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'error': str(e)})
|
||||
|
||||
|
||||
@admin_bp.route('/llm_config/reset', methods=['POST'])
|
||||
@admin_required
|
||||
def reset_llm_config():
|
||||
"""恢复默认LLM配置"""
|
||||
from config import LLM_CONFIG
|
||||
|
||||
# 删除数据库中的LLM配置
|
||||
DynamicConfig.query.filter_by(category='llm').delete()
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({'success': True})
|
||||
|
||||
|
||||
# ==================== 获取当前LLM配置(供其他模块使用) ====================
|
||||
def get_llm_config():
|
||||
"""获取当前LLM配置"""
|
||||
from config import LLM_CONFIG
|
||||
|
||||
return {
|
||||
'api_base': DynamicConfig.get('llm_api_base', LLM_CONFIG.get('api_base')),
|
||||
'api_key': DynamicConfig.get('llm_api_key', LLM_CONFIG.get('api_key')),
|
||||
'model': DynamicConfig.get('llm_model', LLM_CONFIG.get('model')),
|
||||
'max_tokens': DynamicConfig.get('llm_max_tokens', LLM_CONFIG.get('max_tokens')),
|
||||
'chunk_size': DynamicConfig.get('llm_chunk_size', LLM_CONFIG.get('chunk_size')),
|
||||
'timeout': DynamicConfig.get('llm_timeout', LLM_CONFIG.get('timeout')),
|
||||
}
|
||||
39
app.py
39
app.py
@@ -93,11 +93,29 @@ def index():
|
||||
"""首页"""
|
||||
user = get_current_user()
|
||||
if user:
|
||||
# 检查日期并重置计数(使用上海时区)
|
||||
from datetime import timezone, timedelta
|
||||
shanghai_tz = timezone(timedelta(hours=8))
|
||||
today = datetime.now(shanghai_tz).date()
|
||||
if user.last_translate_date != today:
|
||||
user.daily_count = 0
|
||||
user.last_translate_date = today
|
||||
db.session.commit()
|
||||
|
||||
limits = USER_LIMITS.get(user.user_type, USER_LIMITS['free'])
|
||||
daily_remaining = limits['daily_translations'] - user.daily_count if limits['daily_translations'] > 0 else '无限'
|
||||
max_pages = limits['max_pages'] if limits['max_pages'] > 0 else '无限'
|
||||
else:
|
||||
guest = get_or_create_guest()
|
||||
# 检查日期并重置访客计数
|
||||
from datetime import timezone, timedelta
|
||||
shanghai_tz = timezone(timedelta(hours=8))
|
||||
today = datetime.now(shanghai_tz).date()
|
||||
if guest.last_translate_date != today:
|
||||
guest.daily_count = 0
|
||||
guest.last_translate_date = today
|
||||
db.session.commit()
|
||||
|
||||
limits = USER_LIMITS['guest']
|
||||
daily_remaining = limits['daily_translations'] - guest.daily_count
|
||||
max_pages = limits['max_pages']
|
||||
@@ -229,13 +247,8 @@ def upload_pdf():
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
output_path = os.path.join(output_dir, f"{filename}_translated.md")
|
||||
|
||||
# 创建异步翻译任务
|
||||
# 创建异步翻译任务(先不创建,等translation_id生成后)
|
||||
task_id = str(uuid.uuid4())
|
||||
TranslationTask.create_task(
|
||||
task_id, upload_path, output_path,
|
||||
{'LLM_CONFIG': LLM_CONFIG},
|
||||
instruction
|
||||
)
|
||||
|
||||
# 创建翻译记录
|
||||
translation = Translation(
|
||||
@@ -252,6 +265,20 @@ def upload_pdf():
|
||||
)
|
||||
db.session.add(translation)
|
||||
|
||||
# 预先提交获取 translation_id
|
||||
if not from_cache:
|
||||
db.session.flush() # 获取 ID 但不提交完整事务
|
||||
|
||||
# 创建异步翻译任务(需要翻译时)
|
||||
if not from_cache:
|
||||
TranslationTask.create_task(
|
||||
task_id, upload_path, output_path,
|
||||
{'LLM_CONFIG': LLM_CONFIG},
|
||||
instruction,
|
||||
translation_id=translation.id,
|
||||
app=app
|
||||
)
|
||||
|
||||
# 更新用户/访客计数
|
||||
if user:
|
||||
user.increment_count()
|
||||
|
||||
10
models.py
10
models.py
@@ -67,8 +67,10 @@ class User(db.Model):
|
||||
if max_pages > 0 and pages > max_pages:
|
||||
return False, f"PDF页数超出限制(最大{max_pages}页)"
|
||||
|
||||
# 检查每日次数限制
|
||||
today = datetime.utcnow().date()
|
||||
# 检查每日次数限制 - 使用上海时间(UTC+8)
|
||||
from datetime import timezone, timedelta
|
||||
shanghai_tz = timezone(timedelta(hours=8))
|
||||
today = datetime.now(shanghai_tz).date()
|
||||
if self.last_translate_date != today:
|
||||
self.daily_count = 0
|
||||
self.last_translate_date = today
|
||||
@@ -81,7 +83,9 @@ class User(db.Model):
|
||||
|
||||
def increment_count(self):
|
||||
"""增加翻译计数"""
|
||||
today = datetime.utcnow().date()
|
||||
from datetime import timezone, timedelta
|
||||
shanghai_tz = timezone(timedelta(hours=8))
|
||||
today = datetime.now(shanghai_tz).date()
|
||||
if self.last_translate_date != today:
|
||||
self.daily_count = 0
|
||||
self.last_translate_date = today
|
||||
|
||||
49
services.py
49
services.py
@@ -255,7 +255,7 @@ class TranslationTask:
|
||||
lock = threading.Lock()
|
||||
|
||||
@classmethod
|
||||
def create_task(cls, task_id, pdf_path, output_path, config, instruction=None):
|
||||
def create_task(cls, task_id, pdf_path, output_path, config, instruction=None, translation_id=None, app=None):
|
||||
"""创建翻译任务"""
|
||||
task = {
|
||||
'id': task_id,
|
||||
@@ -266,6 +266,7 @@ class TranslationTask:
|
||||
'error': None,
|
||||
'started_at': None,
|
||||
'completed_at': None,
|
||||
'translation_id': translation_id,
|
||||
}
|
||||
|
||||
with cls.lock:
|
||||
@@ -273,15 +274,40 @@ class TranslationTask:
|
||||
|
||||
# 启动翻译线程
|
||||
def run_translation():
|
||||
# 动态获取LLM配置
|
||||
if app:
|
||||
with app.app_context():
|
||||
from admin import get_llm_config
|
||||
llm_config = get_llm_config()
|
||||
config['LLM_CONFIG'] = llm_config
|
||||
|
||||
service = TranslationService(config)
|
||||
task['status'] = 'processing'
|
||||
task['started_at'] = datetime.now().isoformat()
|
||||
|
||||
# 更新数据库状态为 processing
|
||||
if app and translation_id:
|
||||
with app.app_context():
|
||||
from models import db, Translation
|
||||
trans = Translation.query.get(translation_id)
|
||||
if trans:
|
||||
trans.status = 'processing'
|
||||
db.session.commit()
|
||||
|
||||
def progress_callback(progress, total, message):
|
||||
with cls.lock:
|
||||
task['progress'] = progress
|
||||
task['message'] = message
|
||||
|
||||
# 更新数据库进度
|
||||
if app and translation_id:
|
||||
with app.app_context():
|
||||
from models import db, Translation
|
||||
trans = Translation.query.get(translation_id)
|
||||
if trans:
|
||||
trans.progress = progress
|
||||
db.session.commit()
|
||||
|
||||
try:
|
||||
result = service.translate_pdf(
|
||||
pdf_path, output_path, instruction, progress_callback
|
||||
@@ -292,11 +318,32 @@ class TranslationTask:
|
||||
task['completed_at'] = datetime.now().isoformat()
|
||||
task['result'] = result
|
||||
|
||||
# 更新数据库状态为 completed
|
||||
if app and translation_id:
|
||||
with app.app_context():
|
||||
from models import db, Translation
|
||||
trans = Translation.query.get(translation_id)
|
||||
if trans:
|
||||
trans.status = 'completed'
|
||||
trans.progress = 100
|
||||
trans.completed_at = datetime.now()
|
||||
db.session.commit()
|
||||
|
||||
except Exception as e:
|
||||
task['status'] = 'failed'
|
||||
task['error'] = str(e)
|
||||
task['message'] = f'翻译失败: {e}'
|
||||
|
||||
# 更新数据库状态为 failed
|
||||
if app and translation_id:
|
||||
with app.app_context():
|
||||
from models import db, Translation
|
||||
trans = Translation.query.get(translation_id)
|
||||
if trans:
|
||||
trans.status = 'failed'
|
||||
trans.error_message = str(e)
|
||||
db.session.commit()
|
||||
|
||||
thread = threading.Thread(target=run_translation)
|
||||
thread.start()
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<li class="nav-item"><a class="nav-link active" href="{{ url_for('admin.cache_list') }}"><i class="bi bi-database"></i> 缓存管理</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
|
||||
</ul>
|
||||
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
|
||||
|
||||
@@ -93,6 +93,11 @@
|
||||
<i class="bi bi-list-check"></i> 操作日志
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('admin.llm_config') }}">
|
||||
<i class="bi bi-cpu"></i> 大模型配置
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('admin.settings') }}">
|
||||
<i class="bi bi-sliders"></i> 系统配置
|
||||
|
||||
187
templates/admin/llm_config.html
Normal file
187
templates/admin/llm_config.html
Normal file
@@ -0,0 +1,187 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>大模型配置 - 后台管理</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f5f5f5; }
|
||||
.sidebar { position: fixed; top: 0; left: 0; height: 100vh; width: 250px; background: #343a40; padding-top: 60px; }
|
||||
.sidebar .nav-link { color: #adb5bd; padding: 12px 20px; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: #fff; background: rgba(255,255,255,0.1); }
|
||||
.main-content { margin-left: 250px; padding: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="sidebar">
|
||||
<div class="position-absolute top-0 w-100 p-3 border-bottom border-secondary">
|
||||
<h5 class="text-white mb-0"><i class="bi bi-gear-fill"></i> 后台管理</h5>
|
||||
</div>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.dashboard') }}"><i class="bi bi-speedometer2"></i> 数据概览</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.users') }}"><i class="bi bi-people"></i> 用户管理</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.translations') }}"><i class="bi bi-file-text"></i> 翻译记录</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.cache_list') }}"><i class="bi bi-database"></i> 缓存管理</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
|
||||
</ul>
|
||||
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
|
||||
<a href="/" class="btn btn-outline-light btn-sm w-100"><i class="bi bi-house"></i> 返回前台</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="main-content">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0"><i class="bi bi-cpu"></i> 大模型接口配置</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="llmConfigForm">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">API 地址</label>
|
||||
<input type="text" class="form-control" name="api_base" value="{{ config.api_base }}" placeholder="http://localhost:1234/v1">
|
||||
<small class="text-muted">LLM服务的API endpoint</small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">API Key</label>
|
||||
<input type="text" class="form-control" name="api_key" value="{{ config.api_key }}" placeholder="sk-xxx">
|
||||
<small class="text-muted">如果不需要可留空</small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">模型名称</label>
|
||||
<input type="text" class="form-control" name="model" value="{{ config.model }}" placeholder="qwen/qwen3.5-35b">
|
||||
<small class="text-muted">使用的模型ID</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">最大输出Token</label>
|
||||
<input type="number" class="form-control" name="max_tokens" value="{{ config.max_tokens }}" placeholder="8000">
|
||||
<small class="text-muted">每次翻译最大输出长度</small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">分块大小</label>
|
||||
<input type="number" class="form-control" name="chunk_size" value="{{ config.chunk_size }}" placeholder="2000">
|
||||
<small class="text-muted">PDF文本分块大小</small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">超时时间(秒)</label>
|
||||
<input type="number" class="form-control" name="timeout" value="{{ config.timeout }}" placeholder="180">
|
||||
<small class="text-muted">API请求超时时间</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-check-lg"></i> 保存配置</button>
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="testConnection()"><i class="bi bi-plug"></i> 测试连接</button>
|
||||
<button type="button" class="btn btn-outline-warning" onclick="resetToDefault()"><i class="bi bi-arrow-counterclockwise"></i> 恢复默认</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="testResult" class="mt-3" style="display:none;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><h6 class="mb-0"><i class="bi bi-info-circle"></i> 常用模型配置参考</h6></div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm">
|
||||
<thead class="table-light">
|
||||
<tr><th>服务商</th><th>API地址</th><th>模型示例</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>本地LM Studio</td><td>http://localhost:1234/v1</td><td>根据加载的模型</td></tr>
|
||||
<tr><td>OpenAI</td><td>https://api.openai.com/v1</td><td>gpt-4, gpt-3.5-turbo</td></tr>
|
||||
<tr><td>DeepSeek</td><td>https://api.deepseek.com/v1</td><td>deepseek-chat</td></tr>
|
||||
<tr><td>阿里百炼</td><td>https://dashscope.aliyuncs.com/compatible-mode/v1</td><td>qwen-turbo, qwen-plus</td></tr>
|
||||
<tr><td>SiliconFlow</td><td>https://api.siliconflow.cn/v1</td><td>Qwen/Qwen2.5-72B-Instruct</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
document.getElementById('llmConfigForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(this);
|
||||
const data = {};
|
||||
formData.forEach((value, key) => {
|
||||
if (key === 'max_tokens' || key === 'chunk_size' || key === 'timeout') {
|
||||
data[key] = parseInt(value) || 0;
|
||||
} else {
|
||||
data[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
fetch('/admin/llm_config/save', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
alert('配置已保存!');
|
||||
} else {
|
||||
alert('保存失败: ' + res.error);
|
||||
}
|
||||
})
|
||||
.catch(err => alert('请求失败: ' + err));
|
||||
});
|
||||
|
||||
function testConnection() {
|
||||
const formData = new FormData(document.getElementById('llmConfigForm'));
|
||||
const data = {};
|
||||
formData.forEach((value, key) => {
|
||||
if (key === 'max_tokens' || key === 'chunk_size' || key === 'timeout') {
|
||||
data[key] = parseInt(value) || 0;
|
||||
} else {
|
||||
data[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
const resultDiv = document.getElementById('testResult');
|
||||
resultDiv.style.display = 'block';
|
||||
resultDiv.innerHTML = '<div class="alert alert-info">正在测试连接...</div>';
|
||||
|
||||
fetch('/admin/llm_config/test', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
resultDiv.innerHTML = '<div class="alert alert-success"><i class="bi bi-check-circle"></i> 连接成功!模型: ' + res.model + '</div>';
|
||||
} else {
|
||||
resultDiv.innerHTML = '<div class="alert alert-danger"><i class="bi bi-x-circle"></i> 连接失败: ' + res.error + '</div>';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
resultDiv.innerHTML = '<div class="alert alert-danger"><i class="bi bi-x-circle"></i> 请求失败: ' + err + '</div>';
|
||||
});
|
||||
}
|
||||
|
||||
function resetToDefault() {
|
||||
if (confirm('确定恢复默认配置吗?')) {
|
||||
fetch('/admin/llm_config/reset', {method: 'POST'})
|
||||
.then(r => r.json())
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('恢复失败: ' + res.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -25,6 +25,7 @@
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.cache_list') }}"><i class="bi bi-database"></i> 缓存管理</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
|
||||
</ul>
|
||||
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.packages') }}"><i class="bi bi-box-seam"></i> 数据包套餐</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
|
||||
</ul>
|
||||
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<li class="nav-item"><a class="nav-link active" href="{{ url_for('admin.packages') }}"><i class="bi bi-box-seam"></i> 数据包套餐</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
|
||||
</ul>
|
||||
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
<li class="nav-item"><a class="nav-link active" href="{{ url_for('admin.packages') }}"><i class="bi bi-box-seam"></i> 数据包套餐</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
|
||||
</ul>
|
||||
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.cache_list') }}"><i class="bi bi-database"></i> 缓存管理</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
|
||||
</ul>
|
||||
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.cache_list') }}"><i class="bi bi-database"></i> 缓存管理</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
|
||||
</ul>
|
||||
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.cache_list') }}"><i class="bi bi-database"></i> 缓存管理</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
|
||||
</ul>
|
||||
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.cache_list') }}"><i class="bi bi-database"></i> 缓存管理</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
|
||||
</ul>
|
||||
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.cache_list') }}"><i class="bi bi-database"></i> 缓存管理</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
|
||||
</ul>
|
||||
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.packages') }}"><i class="bi bi-box-seam"></i> 数据包套餐</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
|
||||
</ul>
|
||||
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.cache_list') }}"><i class="bi bi-database"></i> 缓存管理</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.stats') }}"><i class="bi bi-bar-chart"></i> 统计报表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.logs') }}"><i class="bi bi-list-check"></i> 操作日志</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.llm_config') }}"><i class="bi bi-cpu"></i> 大模型配置</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin.settings') }}"><i class="bi bi-sliders"></i> 系统配置</a></li>
|
||||
</ul>
|
||||
<div class="position-absolute bottom-0 w-100 p-3 border-top border-secondary">
|
||||
|
||||
Reference in New Issue
Block a user