Files
gemini-balance/app/templates/config_editor.html
snaily e1c068ed9e feat: 实现日志自动删除功能并更新配置管理
本次提交主要包含以下内容:

1.  **日志自动删除功能**:
    *   新增环境变量 (`AUTO_DELETE_ERROR_LOGS_ENABLED`, `AUTO_DELETE_ERROR_LOGS_DAYS`, `AUTO_DELETE_REQUEST_LOGS_ENABLED`, `AUTO_DELETE_REQUEST_LOGS_DAYS`) 用于控制错误日志和请求日志的自动删除策略。
    *   在 `app/config/config.py` 中添加了对这些新配置项的支持和验证逻辑 (Pydantic `validator` 更新为 `field_validator`)。
    *   修改了 `app/log/logger.py` 以适应新的日志配置。
    *   新增 `app/scheduler/scheduled_tasks.py` 用于执行定期的日志清理任务。
    *   新增 `app/service/error_log/error_log_service.py` 和 `app/service/request_log/request_log_service.py` 来处理具体的日志删除逻辑。
    *   更新了 `app/router/error_log_routes.py` 和 `app/router/scheduler_routes.py` 以集成新功能。

2.  **前端配置页面更新**:
    *   在 `app/templates/config_editor.html` 和 `app/static/js/config_editor.js` 中添加了用于配置日志自动删除选项的用户界面元素。

3.  **代码和文件结构调整**:
    *   删除了不再使用的 `app/scheduler/key_checker.py` 文件。
    *   在 `.gitignore` 文件中添加了 `default_db` 以忽略该目录。

4.  **其他**:
    *   对 `app/core/application.py` 进行了相应调整。

该更新旨在增强应用的日志管理能力,提供更灵活的日志保留策略,并优化了配置界面的用户体验。
2025-05-08 00:31:17 +08:00

1293 lines
48 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %} {% block title %}配置编辑器 - Gemini Balance{%
endblock %} {% block head_extra_styles %}
<style>
/* config_editor.html specific styles */
/* Animations (already in base.html, but keep fade-in class usage) */
.fade-in {
animation: fadeIn 0.3s ease forwards;
}
/* Modal specific styles (already in base.html) */
.array-container {
max-height: 300px;
overflow-y: auto;
padding-right: 5px; /* Keep specific padding if needed */
}
#API_KEYS_container {
/* Keep specific ID styling if needed */
max-height: 300px;
overflow-y: auto;
}
.config-section {
display: none;
}
.config-section.active {
display: block;
animation: fadeIn 0.3s ease forwards; /* Use base animation */
}
.provider-config {
display: none;
}
.provider-config.active {
display: block;
}
/* Tailwind Toggle Switch Helper CSS */
.toggle-checkbox:checked {
@apply: right-0 border-primary-600;
right: 0;
border-color: #4f46e5;
}
.toggle-checkbox:checked + .toggle-label {
@apply: bg-primary-600;
background-color: #4f46e5;
}
/* 统一通知样式为黑色半透明,确保与 keys_status 一致 */
.notification {
background: rgba(0, 0, 0, 0.8) !important;
color: #fff !important;
}
</style>
{% endblock %} {% block content %}
<div class="container max-w-4xl mx-auto px-4">
<div class="glass-card rounded-2xl shadow-xl p-6 md:p-8">
<button
class="absolute top-6 right-6 bg-white bg-opacity-20 hover:bg-opacity-30 rounded-full w-8 h-8 flex items-center justify-center text-primary-600 transition-all duration-300"
onclick="refreshPage(this)"
>
<i class="fas fa-sync-alt"></i>
</button>
<h1
class="text-3xl font-extrabold text-center text-transparent bg-clip-text bg-gradient-to-r from-primary-600 to-primary-700 mb-4"
>
<img
src="/static/icons/logo.png"
alt="Gemini Balance Logo"
class="h-9 inline-block align-middle mr-2"
/>
Gemini Balance - 配置编辑
</h1>
<!-- Navigation Tabs -->
<div class="flex justify-center mb-8 overflow-x-auto pb-2 gap-2">
<a
href="/config"
class="whitespace-nowrap flex items-center justify-center gap-2 px-6 py-3 font-medium rounded-lg bg-primary-600 text-white shadow-md"
>
<i class="fas fa-cog"></i> 配置编辑
</a>
<a
href="/keys"
class="whitespace-nowrap flex items-center justify-center gap-2 px-6 py-3 font-medium rounded-lg bg-white bg-opacity-50 hover:bg-opacity-70 text-gray-700 transition-all duration-200"
>
<i class="fas fa-tachometer-alt"></i> 监控面板
</a>
<a
href="/logs"
class="whitespace-nowrap flex items-center justify-center gap-2 px-6 py-3 font-medium rounded-lg bg-white bg-opacity-50 hover:bg-opacity-70 text-gray-700 transition-all duration-200"
>
<i class="fas fa-exclamation-triangle"></i> 错误日志
</a>
</div>
<!-- Config Tabs -->
<div class="flex justify-center mb-6 flex-wrap gap-2">
<button
class="tab-btn bg-primary-600 text-white px-5 py-2 rounded-full shadow-md font-medium text-sm"
data-tab="api"
>
API配置
</button>
<button
class="tab-btn bg-white bg-opacity-50 text-gray-700 px-5 py-2 rounded-full font-medium text-sm hover:bg-opacity-70 transition-all duration-200"
data-tab="model"
>
模型配置
</button>
<button
class="tab-btn bg-white bg-opacity-50 text-gray-700 px-5 py-2 rounded-full font-medium text-sm hover:bg-opacity-70 transition-all duration-200"
data-tab="image"
>
图像生成
</button>
<button
class="tab-btn bg-white bg-opacity-50 text-gray-700 px-5 py-2 rounded-full font-medium text-sm hover:bg-opacity-70 transition-all duration-200"
data-tab="stream"
>
流式输出
</button>
<button
class="tab-btn bg-white bg-opacity-50 text-gray-700 px-5 py-2 rounded-full font-medium text-sm hover:bg-opacity-70 transition-all duration-200"
data-tab="scheduler"
>
定时任务
</button>
<button
class="tab-btn bg-white bg-opacity-50 text-gray-700 px-5 py-2 rounded-full font-medium text-sm hover:bg-opacity-70 transition-all duration-200"
data-tab="logging"
>
日志配置
</button>
</div>
<!-- Save Status Banner (Removed - using notification component now) -->
<!-- Configuration Form -->
<form id="configForm" class="mt-6">
<!-- API 相关配置 -->
<div
class="config-section active bg-white bg-opacity-70 rounded-xl p-6 mb-6 shadow-lg"
id="api-section"
>
<h2
class="text-xl font-bold mb-6 pb-3 border-b border-gray-200 flex items-center gap-2"
>
<i class="fas fa-key text-primary-600"></i> API相关配置
</h2>
<!-- API密钥列表 -->
<div class="mb-6">
<label for="API_KEYS" class="block font-semibold mb-2 text-gray-700"
>API密钥列表</label
>
<div class="mb-2">
<input
type="search"
id="apiKeySearchInput"
placeholder="搜索密钥..."
class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
</div>
<div
class="array-container bg-white rounded-lg border border-gray-200 p-4 mb-2"
id="API_KEYS_container"
>
<!-- 数组项将在这里动态添加 -->
</div>
<div class="flex justify-end gap-2">
<button
type="button"
class="bg-danger-600 hover:bg-danger-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center gap-2"
id="bulkDeleteApiKeyBtn"
>
<i class="fas fa-trash-alt"></i> 删除密钥
</button>
<button
type="button"
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center gap-2"
id="addApiKeyBtn"
>
<i class="fas fa-plus"></i> 添加密钥
</button>
</div>
<small class="text-gray-500 mt-1 block"
>Gemini API密钥列表每行一个</small
>
</div>
<!-- 允许的令牌列表 -->
<div class="mb-6">
<label
for="ALLOWED_TOKENS"
class="block font-semibold mb-2 text-gray-700"
>允许的令牌列表</label
>
<div
class="array-container bg-white rounded-lg border border-gray-200 p-4 mb-2"
id="ALLOWED_TOKENS_container"
>
<!-- 数组项将在这里动态添加 -->
</div>
<div class="flex justify-end">
<button
type="button"
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center gap-2"
onclick="addArrayItem('ALLOWED_TOKENS')"
>
<i class="fas fa-plus"></i> 添加令牌
</button>
</div>
<small class="text-gray-500 mt-1 block">允许访问API的令牌列表</small>
</div>
<!-- 认证令牌 -->
<div class="mb-6">
<label for="AUTH_TOKEN" class="block font-semibold mb-2 text-gray-700"
>认证令牌</label
>
<div class="flex items-center">
<div
class="flex items-center flex-grow border border-gray-300 rounded-md focus-within:border-primary-500 focus-within:ring focus-within:ring-primary-200 focus-within:ring-opacity-50"
>
<input
type="text"
id="AUTH_TOKEN"
name="AUTH_TOKEN"
placeholder="默认使用ALLOWED_TOKENS中的第一个"
class="array-input flex-grow px-3 py-2 border-none rounded-l-md focus:outline-none sensitive-input"
/>
<button
type="button"
id="generateAuthTokenBtn"
class="generate-btn px-2 py-2 text-gray-500 hover:text-primary-600 focus:outline-none rounded-r-md bg-gray-100 hover:bg-gray-200 transition-colors"
title="生成随机令牌"
>
<i class="fas fa-dice"></i>
</button>
</div>
</div>
<small class="text-gray-500 mt-1 block">用于API认证的令牌</small>
</div>
<!-- API基础URL -->
<div class="mb-6">
<label for="BASE_URL" class="block font-semibold mb-2 text-gray-700"
>API基础URL</label
>
<input
type="text"
id="BASE_URL"
name="BASE_URL"
placeholder="https://generativelanguage.googleapis.com/v1beta"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block">Gemini API的基础URL</small>
</div>
<!-- 最大失败次数 -->
<div class="mb-6">
<label
for="MAX_FAILURES"
class="block font-semibold mb-2 text-gray-700"
>最大失败次数</label
>
<input
type="number"
id="MAX_FAILURES"
name="MAX_FAILURES"
min="1"
max="100"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block"
>API密钥失败后标记为无效的次数</small
>
</div>
<!-- 请求超时时间 -->
<div class="mb-6">
<label for="TIME_OUT" class="block font-semibold mb-2 text-gray-700"
>请求超时时间(秒)</label
>
<input
type="number"
id="TIME_OUT"
name="TIME_OUT"
min="1"
max="600"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block">API请求的超时时间</small>
</div>
<!-- 最大重试次数 -->
<div class="mb-6">
<label
for="MAX_RETRIES"
class="block font-semibold mb-2 text-gray-700"
>最大重试次数</label
>
<input
type="number"
id="MAX_RETRIES"
name="MAX_RETRIES"
min="0"
max="10"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block"
>API请求失败后的最大重试次数</small
>
</div>
<!-- 代理服务器列表 -->
<div class="mb-6">
<label for="PROXIES" class="block font-semibold mb-2 text-gray-700"
>代理服务器列表</label
>
<div
class="array-container bg-white rounded-lg border border-gray-200 p-4 mb-2"
id="PROXIES_container"
>
<!-- 代理项将在这里动态添加 -->
</div>
<div class="flex justify-end gap-2">
<button
type="button"
class="bg-danger-600 hover:bg-danger-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center gap-2"
id="bulkDeleteProxyBtn"
>
<i class="fas fa-trash-alt"></i> 删除代理
</button>
<button
type="button"
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center gap-2"
id="addProxyBtn"
>
<i class="fas fa-plus"></i> 添加代理
</button>
</div>
<small class="text-gray-500 mt-1 block"
>代理服务器列表,支持 http 和 socks5 格式,例如:
http://user:pass@host:port 或
socks5://host:port。点击按钮可批量添加或删除。</small
>
</div>
</div>
<!-- 模型相关配置 -->
<div
class="config-section bg-white bg-opacity-70 rounded-xl p-6 mb-6 shadow-lg"
id="model-section"
>
<h2
class="text-xl font-bold mb-6 pb-3 border-b border-gray-200 flex items-center gap-2"
>
<i class="fas fa-robot text-primary-600"></i> 模型相关配置
</h2>
<!-- 测试模型 -->
<div class="mb-6">
<label for="TEST_MODEL" class="block font-semibold mb-2 text-gray-700"
>测试模型</label
>
<input
type="text"
id="TEST_MODEL"
name="TEST_MODEL"
placeholder="gemini-1.5-flash"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block">用于测试API密钥的模型</small>
</div>
<!-- 图像模型列表 -->
<div class="mb-6">
<label
for="IMAGE_MODELS"
class="block font-semibold mb-2 text-gray-700"
>图像模型列表</label
>
<div
class="array-container bg-white rounded-lg border border-gray-200 p-4 mb-2"
id="IMAGE_MODELS_container"
>
<!-- 数组项将在这里动态添加 -->
</div>
<div class="flex justify-end">
<button
type="button"
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center gap-2"
onclick="addArrayItem('IMAGE_MODELS')"
>
<i class="fas fa-plus"></i> 添加模型
</button>
</div>
<small class="text-gray-500 mt-1 block">支持图像处理的模型列表</small>
</div>
<!-- 搜索模型列表 -->
<div class="mb-6">
<label
for="SEARCH_MODELS"
class="block font-semibold mb-2 text-gray-700"
>搜索模型列表</label
>
<div
class="array-container bg-white rounded-lg border border-gray-200 p-4 mb-2"
id="SEARCH_MODELS_container"
>
<!-- 数组项将在这里动态添加 -->
</div>
<div class="flex justify-end">
<button
type="button"
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center gap-2"
onclick="addArrayItem('SEARCH_MODELS')"
>
<i class="fas fa-plus"></i> 添加模型
</button>
</div>
<small class="text-gray-500 mt-1 block">支持搜索功能的模型列表</small>
</div>
<!-- 过滤模型列表 -->
<div class="mb-6">
<label
for="FILTERED_MODELS"
class="block font-semibold mb-2 text-gray-700"
>过滤模型列表</label
>
<div
class="array-container bg-white rounded-lg border border-gray-200 p-4 mb-2"
id="FILTERED_MODELS_container"
>
<!-- 数组项将在这里动态添加 -->
</div>
<div class="flex justify-end">
<button
type="button"
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center gap-2"
onclick="addArrayItem('FILTERED_MODELS')"
>
<i class="fas fa-plus"></i> 添加模型
</button>
</div>
<small class="text-gray-500 mt-1 block">需要过滤的模型列表</small>
</div>
<!-- 启用代码执行工具 -->
<div class="mb-6 flex items-center justify-between">
<label
for="TOOLS_CODE_EXECUTION_ENABLED"
class="font-semibold text-gray-700"
>启用代码执行工具</label
>
<div
class="relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in"
>
<input
type="checkbox"
name="TOOLS_CODE_EXECUTION_ENABLED"
id="TOOLS_CODE_EXECUTION_ENABLED"
class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer"
/>
<label
for="TOOLS_CODE_EXECUTION_ENABLED"
class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"
></label>
</div>
</div>
<!-- 显示搜索链接 -->
<div class="mb-6 flex items-center justify-between">
<label for="SHOW_SEARCH_LINK" class="font-semibold text-gray-700"
>显示搜索链接</label
>
<div
class="relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in"
>
<input
type="checkbox"
name="SHOW_SEARCH_LINK"
id="SHOW_SEARCH_LINK"
class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer"
/>
<label
for="SHOW_SEARCH_LINK"
class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"
></label>
</div>
</div>
<!-- 显示思考过程 -->
<div class="mb-6 flex items-center justify-between">
<label for="SHOW_THINKING_PROCESS" class="font-semibold text-gray-700"
>显示思考过程</label
>
<div
class="relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in"
>
<input
type="checkbox"
name="SHOW_THINKING_PROCESS"
id="SHOW_THINKING_PROCESS"
class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer"
/>
<label
for="SHOW_THINKING_PROCESS"
class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"
></label>
</div>
</div>
<!-- 思考模型列表 -->
<div class="mb-6">
<label
for="THINKING_MODELS"
class="block font-semibold mb-2 text-gray-700"
>思考模型列表</label
>
<div
class="array-container bg-white rounded-lg border border-gray-200 p-4 mb-2"
id="THINKING_MODELS_container"
>
<!-- 数组项将在这里动态添加 -->
</div>
<div class="flex justify-end">
<button
type="button"
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center gap-2"
onclick="addArrayItem('THINKING_MODELS')"
>
<i class="fas fa-plus"></i> 添加模型
</button>
</div>
<small class="text-gray-500 mt-1 block"
>用于"思考过程"的模型列表</small
>
</div>
<!-- 思考模型预算映射 -->
<div class="mb-6">
<label
for="THINKING_BUDGET_MAP"
class="block font-semibold mb-2 text-gray-700"
>思考模型预算映射</label
>
<div
class="bg-white rounded-lg border border-gray-200 p-4 mb-2 space-y-3"
id="THINKING_BUDGET_MAP_container"
>
<!-- 键值对将在这里动态添加 -->
<div class="text-gray-500 text-sm italic">
请先在上方添加思考模型,然后在此处配置预算。
</div>
</div>
<!-- 移除添加预算映射按钮 -->
<!-- <div class="flex justify-end">
<button type="button" class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center gap-2" id="addBudgetMapItemBtn">
<i class="fas fa-plus"></i> 添加预算映射
</button>
</div> -->
<small class="text-gray-500 mt-1 block"
>为每个思考模型设置预算(整数,最大值
24576此项与上方模型列表自动关联。</small
>
</div>
<!-- 安全设置 -->
<div class="mb-6">
<label
for="SAFETY_SETTINGS"
class="block font-semibold mb-2 text-gray-700"
>安全设置 (Safety Settings)</label
>
<div
class="bg-white rounded-lg border border-gray-200 p-4 mb-2 space-y-3"
id="SAFETY_SETTINGS_container"
>
<!-- 安全设置项将在这里动态添加 -->
<div class="text-gray-500 text-sm italic">
定义模型的安全过滤阈值。
</div>
</div>
<div class="flex justify-end">
<button
type="button"
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center gap-2"
id="addSafetySettingBtn"
>
<i class="fas fa-plus"></i> 添加安全设置
</button>
</div>
<small class="text-gray-500 mt-1 block"
>配置模型的安全过滤级别,例如 HARM_CATEGORY_HARASSMENT:
BLOCK_NONE。</small
>
<small class="text-red-500 mt-1 block"
>建议设置成OFF其他值会影响输出速度非必要不要随便改动。</small
>
</div>
</div>
<!-- 图像生成相关配置 -->
<div
class="config-section bg-white bg-opacity-70 rounded-xl p-6 mb-6 shadow-lg"
id="image-section"
>
<h2
class="text-xl font-bold mb-6 pb-3 border-b border-gray-200 flex items-center gap-2"
>
<i class="fas fa-image text-primary-600"></i> 图像生成配置
</h2>
<!-- 付费API密钥 -->
<div class="mb-6">
<label for="PAID_KEY" class="block font-semibold mb-2 text-gray-700"
>付费API密钥</label
>
<input
type="text"
id="PAID_KEY"
name="PAID_KEY"
placeholder="AIzaSyxxxxxxxxxxxxxxxxxxx"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50 sensitive-input"
/>
<small class="text-gray-500 mt-1 block"
>用于图像生成的付费API密钥</small
>
</div>
<!-- 图像生成模型 -->
<div class="mb-6">
<label
for="CREATE_IMAGE_MODEL"
class="block font-semibold mb-2 text-gray-700"
>图像生成模型</label
>
<input
type="text"
id="CREATE_IMAGE_MODEL"
name="CREATE_IMAGE_MODEL"
placeholder="imagen-3.0-generate-002"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block">用于图像生成的模型</small>
</div>
<!-- 上传提供商 -->
<div class="mb-6">
<label
for="UPLOAD_PROVIDER"
class="block font-semibold mb-2 text-gray-700"
>上传提供商</label
>
<select
id="UPLOAD_PROVIDER"
name="UPLOAD_PROVIDER"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50 bg-white"
>
<option value="smms" selected>SM.MS</option>
<option value="picgo">PicGo</option>
<option value="cloudflare_imgbed">Cloudflare</option>
</select>
<small class="text-gray-500 mt-1 block">图片上传服务提供商</small>
</div>
<!-- SM.MS密钥 -->
<div class="mb-6 provider-config active" data-provider="smms">
<label
for="SMMS_SECRET_TOKEN"
class="block font-semibold mb-2 text-gray-700"
>SM.MS密钥</label
>
<input
type="text"
id="SMMS_SECRET_TOKEN"
name="SMMS_SECRET_TOKEN"
placeholder="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50 sensitive-input"
/>
<small class="text-gray-500 mt-1 block">SM.MS图床的密钥</small>
</div>
<!-- PicGo API密钥 -->
<div class="mb-6 provider-config" data-provider="picgo">
<label
for="PICGO_API_KEY"
class="block font-semibold mb-2 text-gray-700"
>PicGo API密钥</label
>
<input
type="text"
id="PICGO_API_KEY"
name="PICGO_API_KEY"
placeholder="xxxx"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50 sensitive-input"
/>
<small class="text-gray-500 mt-1 block">PicGo的API密钥</small>
</div>
<!-- Cloudflare图床URL -->
<div class="mb-6 provider-config" data-provider="cloudflare_imgbed">
<label
for="CLOUDFLARE_IMGBED_URL"
class="block font-semibold mb-2 text-gray-700"
>Cloudflare图床URL</label
>
<input
type="text"
id="CLOUDFLARE_IMGBED_URL"
name="CLOUDFLARE_IMGBED_URL"
placeholder="https://xxxxxxx.pages.dev/upload"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block">Cloudflare图床的URL</small>
</div>
<!-- Cloudflare认证码 -->
<div class="mb-6 provider-config" data-provider="cloudflare_imgbed">
<label
for="CLOUDFLARE_IMGBED_AUTH_CODE"
class="block font-semibold mb-2 text-gray-700"
>Cloudflare认证码</label
>
<input
type="text"
id="CLOUDFLARE_IMGBED_AUTH_CODE"
name="CLOUDFLARE_IMGBED_AUTH_CODE"
placeholder="xxxxxxxxx"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50 sensitive-input"
/>
<small class="text-gray-500 mt-1 block">Cloudflare图床的认证码</small>
</div>
</div>
<!-- 流式输出优化器配置 -->
<div
class="config-section bg-white bg-opacity-70 rounded-xl p-6 mb-6 shadow-lg"
id="stream-section"
>
<h2
class="text-xl font-bold mb-6 pb-3 border-b border-gray-200 flex items-center gap-2"
>
<i class="fas fa-stream text-primary-600"></i> 流式输出优化器
</h2>
<!-- 启用流式输出优化 -->
<div class="mb-6 flex items-center justify-between">
<label
for="STREAM_OPTIMIZER_ENABLED"
class="font-semibold text-gray-700"
>启用流式输出优化</label
>
<div
class="relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in"
>
<input
type="checkbox"
name="STREAM_OPTIMIZER_ENABLED"
id="STREAM_OPTIMIZER_ENABLED"
class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer"
/>
<label
for="STREAM_OPTIMIZER_ENABLED"
class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"
></label>
</div>
</div>
<!-- 最小延迟 -->
<div class="mb-6">
<label
for="STREAM_MIN_DELAY"
class="block font-semibold mb-2 text-gray-700"
>最小延迟(秒)</label
>
<input
type="number"
id="STREAM_MIN_DELAY"
name="STREAM_MIN_DELAY"
min="0"
max="1"
step="0.001"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block">流式输出的最小延迟时间</small>
</div>
<!-- 最大延迟 -->
<div class="mb-6">
<label
for="STREAM_MAX_DELAY"
class="block font-semibold mb-2 text-gray-700"
>最大延迟(秒)</label
>
<input
type="number"
id="STREAM_MAX_DELAY"
name="STREAM_MAX_DELAY"
min="0"
max="1"
step="0.001"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block">流式输出的最大延迟时间</small>
</div>
<!-- 短文本阈值 -->
<div class="mb-6">
<label
for="STREAM_SHORT_TEXT_THRESHOLD"
class="block font-semibold mb-2 text-gray-700"
>短文本阈值</label
>
<input
type="number"
id="STREAM_SHORT_TEXT_THRESHOLD"
name="STREAM_SHORT_TEXT_THRESHOLD"
min="1"
max="100"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block">短文本的字符阈值</small>
</div>
<!-- 长文本阈值 -->
<div class="mb-6">
<label
for="STREAM_LONG_TEXT_THRESHOLD"
class="block font-semibold mb-2 text-gray-700"
>长文本阈值</label
>
<input
type="number"
id="STREAM_LONG_TEXT_THRESHOLD"
name="STREAM_LONG_TEXT_THRESHOLD"
min="1"
max="1000"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block">长文本的字符阈值</small>
</div>
<!-- 分块大小 -->
<div class="mb-6">
<label
for="STREAM_CHUNK_SIZE"
class="block font-semibold mb-2 text-gray-700"
>分块大小</label
>
<input
type="number"
id="STREAM_CHUNK_SIZE"
name="STREAM_CHUNK_SIZE"
min="1"
max="100"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block">流式输出的分块大小</small>
</div>
</div>
<!-- 定时任务配置 -->
<div
class="config-section bg-white bg-opacity-70 rounded-xl p-6 mb-6 shadow-lg"
id="scheduler-section"
>
<h2
class="text-xl font-bold mb-6 pb-3 border-b border-gray-200 flex items-center gap-2"
>
<i class="fas fa-clock text-primary-600"></i> 定时任务配置
</h2>
<!-- 检查间隔 -->
<div class="mb-6">
<label
for="CHECK_INTERVAL_HOURS"
class="block font-semibold mb-2 text-gray-700"
>检查间隔(小时)</label
>
<input
type="number"
id="CHECK_INTERVAL_HOURS"
name="CHECK_INTERVAL_HOURS"
min="1"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block"
>定时检查密钥状态的间隔时间(单位:小时)</small
>
</div>
<!-- 时区 -->
<div class="mb-6">
<label for="TIMEZONE" class="block font-semibold mb-2 text-gray-700"
>时区</label
>
<input
type="text"
id="TIMEZONE"
name="TIMEZONE"
placeholder="例如: Asia/Shanghai"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<small class="text-gray-500 mt-1 block"
>定时任务使用的时区,格式如 "Asia/Shanghai" 或 "UTC"</small
>
</div>
</div>
<!-- 日志配置 -->
<div
class="config-section bg-white bg-opacity-70 rounded-xl p-6 mb-6 shadow-lg"
id="logging-section"
>
<h2
class="text-xl font-bold mb-6 pb-3 border-b border-gray-200 flex items-center gap-2"
>
<i class="fas fa-file-alt text-primary-600"></i> 日志配置
</h2>
<!-- 日志级别 -->
<div class="mb-6">
<label for="LOG_LEVEL" class="block font-semibold mb-2 text-gray-700"
>日志级别</label
>
<select
id="LOG_LEVEL"
name="LOG_LEVEL"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50 bg-white"
>
<option value="DEBUG">DEBUG</option>
<option value="INFO">INFO</option>
<option value="WARNING">WARNING</option>
<option value="ERROR">ERROR</option>
<option value="CRITICAL">CRITICAL</option>
</select>
<small class="text-gray-500 mt-1 block"
>设置应用程序的日志记录详细程度</small
>
</div>
<!-- 自动删除错误日志 -->
<div class="mb-6">
<div class="flex items-center justify-between">
<label
for="AUTO_DELETE_ERROR_LOGS_ENABLED"
class="font-semibold text-gray-700"
>是否开启自动删除错误日志</label
>
<div
class="relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in"
>
<input
type="checkbox"
name="AUTO_DELETE_ERROR_LOGS_ENABLED"
id="AUTO_DELETE_ERROR_LOGS_ENABLED"
class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer"
/>
<label
for="AUTO_DELETE_ERROR_LOGS_ENABLED"
class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"
></label>
</div>
</div>
<small class="text-gray-500 mt-1 block"
>开启后,将自动删除指定天数前的错误日志。</small
>
</div>
<!-- 自动删除日志天数 -->
<div class="mb-6">
<label
for="AUTO_DELETE_ERROR_LOGS_DAYS"
class="block font-semibold mb-2 text-gray-700"
>自动删除多少天前的错误日志</label
>
<select
id="AUTO_DELETE_ERROR_LOGS_DAYS"
name="AUTO_DELETE_ERROR_LOGS_DAYS"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50 bg-white"
>
<option value="1">1 天</option>
<option value="7">7 天</option>
<option value="30">30 天</option>
</select>
<small class="text-gray-500 mt-1 block"
>选择自动删除错误日志的天数。</small
>
</div>
<!-- 自动删除请求日志 -->
<div class="mb-6">
<div class="flex items-center justify-between">
<label
for="AUTO_DELETE_REQUEST_LOGS_ENABLED"
class="font-semibold text-gray-700"
>是否开启自动删除请求日志</label
>
<div
class="relative inline-block w-10 mr-2 align-middle select-none transition duration-200 ease-in"
>
<input
type="checkbox"
name="AUTO_DELETE_REQUEST_LOGS_ENABLED"
id="AUTO_DELETE_REQUEST_LOGS_ENABLED"
class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer"
/>
<label
for="AUTO_DELETE_REQUEST_LOGS_ENABLED"
class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"
></label>
</div>
</div>
<small class="text-gray-500 mt-1 block"
>开启后,将自动删除指定天数前的请求日志。</small
>
</div>
<!-- 自动删除请求日志天数 -->
<div class="mb-6">
<label
for="AUTO_DELETE_REQUEST_LOGS_DAYS"
class="block font-semibold mb-2 text-gray-700"
>自动删除多少天前的请求日志</label
>
<select
id="AUTO_DELETE_REQUEST_LOGS_DAYS"
name="AUTO_DELETE_REQUEST_LOGS_DAYS"
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50 bg-white"
>
<option value="1">1 天</option>
<option value="7">7 天</option>
<option value="30">30 天</option>
</select>
<small class="text-gray-500 mt-1 block"
>选择自动删除请求日志的天数。</small
>
</div>
</div>
<!-- Action Buttons -->
<div class="flex flex-col md:flex-row justify-center gap-4 mt-8">
<button
type="button"
id="saveBtn"
class="bg-gradient-to-r from-primary-600 to-primary-700 text-white px-8 py-3 rounded-xl font-semibold transition-all duration-300 transform hover:-translate-y-1 hover:shadow-lg flex items-center justify-center gap-2"
>
<i class="fas fa-save"></i> 保存配置
</button>
<button
type="button"
id="resetBtn"
class="bg-gradient-to-r from-gray-400 to-gray-500 text-white px-8 py-3 rounded-xl font-semibold transition-all duration-300 transform hover:-translate-y-1 hover:shadow-lg flex items-center justify-center gap-2"
>
<i class="fas fa-undo"></i> 重置配置
</button>
</div>
</form>
</div>
</div>
<!-- Scroll buttons are now in base.html -->
<div class="scroll-buttons">
<button class="scroll-button" onclick="scrollToTop()" title="回到顶部">
<i class="fas fa-chevron-up"></i>
</button>
<button class="scroll-button" onclick="scrollToBottom()" title="滚动到底部">
<i class="fas fa-chevron-down"></i>
</button>
</div>
<!-- Notification component is now in base.html -->
<div id="notification" class="notification"></div>
<!-- Footer is now in base.html -->
<!-- API Key Add Modal -->
<div id="apiKeyModal" class="modal">
<div
class="w-full max-w-lg mx-auto bg-white rounded-2xl shadow-2xl overflow-hidden animate-fade-in"
>
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-gray-800">批量添加 API 密钥</h2>
<button
id="closeApiKeyModalBtn"
class="text-gray-400 hover:text-gray-600 text-xl"
>
&times;
</button>
</div>
<p class="text-gray-600 mb-4">
每行粘贴一个或多个密钥,将自动提取有效密钥并去重。
</p>
<textarea
id="apiKeyBulkInput"
rows="10"
placeholder="在此处粘贴 API 密钥..."
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50 font-mono text-sm"
></textarea>
<div class="flex justify-end gap-3 mt-6">
<button
type="button"
id="confirmAddApiKeyBtn"
class="bg-primary-600 hover:bg-primary-700 text-white px-6 py-2 rounded-lg font-medium transition"
>
确认添加
</button>
<button
type="button"
id="cancelAddApiKeyBtn"
class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-6 py-2 rounded-lg font-medium transition"
>
取消
</button>
</div>
</div>
</div>
</div>
<!-- Bulk Delete API Key Modal -->
<div id="bulkDeleteApiKeyModal" class="modal">
<div
class="w-full max-w-lg mx-auto bg-white rounded-2xl shadow-2xl overflow-hidden animate-fade-in"
>
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-gray-800">批量删除 API 密钥</h2>
<button
id="closeBulkDeleteModalBtn"
class="text-gray-400 hover:text-gray-600 text-xl"
>
&times;
</button>
</div>
<p class="text-gray-600 mb-4">
每行粘贴一个或多个密钥,将自动提取有效密钥并从列表中删除。
</p>
<textarea
id="bulkDeleteApiKeyInput"
rows="10"
placeholder="在此处粘贴要删除的 API 密钥..."
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-danger-500 focus:ring focus:ring-danger-200 focus:ring-opacity-50 font-mono text-sm"
></textarea>
<div class="flex justify-end gap-3 mt-6">
<button
type="button"
id="confirmBulkDeleteApiKeyBtn"
class="bg-danger-600 hover:bg-danger-700 text-white px-6 py-2 rounded-lg font-medium transition"
>
确认删除
</button>
<button
type="button"
id="cancelBulkDeleteApiKeyBtn"
class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-6 py-2 rounded-lg font-medium transition"
>
取消
</button>
</div>
</div>
</div>
</div>
<!-- Proxy Add Modal -->
<div id="proxyModal" class="modal">
<div
class="w-full max-w-lg mx-auto bg-white rounded-2xl shadow-2xl overflow-hidden animate-fade-in"
>
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-gray-800">批量添加代理服务器</h2>
<button
id="closeProxyModalBtn"
class="text-gray-400 hover:text-gray-600 text-xl"
>
&times;
</button>
</div>
<p class="text-gray-600 mb-4">
每行粘贴一个或多个代理地址,将自动提取有效地址并去重。
</p>
<textarea
id="proxyBulkInput"
rows="10"
placeholder="在此处粘贴代理地址 (例如 http://user:pass@host:port 或 socks5://host:port)..."
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50 font-mono text-sm"
></textarea>
<div class="flex justify-end gap-3 mt-6">
<button
type="button"
id="confirmAddProxyBtn"
class="bg-primary-600 hover:bg-primary-700 text-white px-6 py-2 rounded-lg font-medium transition"
>
确认添加
</button>
<button
type="button"
id="cancelAddProxyBtn"
class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-6 py-2 rounded-lg font-medium transition"
>
取消
</button>
</div>
</div>
</div>
</div>
<!-- Bulk Delete Proxy Modal -->
<div id="bulkDeleteProxyModal" class="modal">
<div
class="w-full max-w-lg mx-auto bg-white rounded-2xl shadow-2xl overflow-hidden animate-fade-in"
>
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-gray-800">批量删除代理服务器</h2>
<button
id="closeBulkDeleteProxyModalBtn"
class="text-gray-400 hover:text-gray-600 text-xl"
>
&times;
</button>
</div>
<p class="text-gray-600 mb-4">
每行粘贴一个或多个代理地址,将自动提取有效地址并从列表中删除。
</p>
<textarea
id="bulkDeleteProxyInput"
rows="10"
placeholder="在此处粘贴要删除的代理地址..."
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:border-danger-500 focus:ring focus:ring-danger-200 focus:ring-opacity-50 font-mono text-sm"
></textarea>
<div class="flex justify-end gap-3 mt-6">
<button
type="button"
id="confirmBulkDeleteProxyBtn"
class="bg-danger-600 hover:bg-danger-700 text-white px-6 py-2 rounded-lg font-medium transition"
>
确认删除
</button>
<button
type="button"
id="cancelBulkDeleteProxyBtn"
class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-6 py-2 rounded-lg font-medium transition"
>
取消
</button>
</div>
</div>
</div>
</div>
<!-- Reset Confirmation Modal -->
<div id="resetConfirmModal" class="modal">
<div
class="w-full max-w-md mx-auto bg-white rounded-2xl shadow-2xl overflow-hidden animate-fade-in"
>
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold text-gray-800">确认重置配置</h2>
<button
id="closeResetModalBtn"
class="text-gray-400 hover:text-gray-600 text-xl"
>
&times;
</button>
</div>
<p class="text-gray-600 mb-6">
确定要重置所有配置吗?<br />这将恢复到默认值,此操作不可撤销。
</p>
<div class="flex justify-end gap-3">
<button
type="button"
id="confirmResetBtn"
class="bg-red-500 hover:bg-red-600 text-white px-6 py-2 rounded-lg font-medium transition"
>
确认重置
</button>
<button
type="button"
id="cancelResetBtn"
class="bg-primary-600 hover:bg-primary-700 text-white px-6 py-2 rounded-lg font-medium transition"
>
取消
</button>
</div>
</div>
</div>
</div>
{% endblock %} {% block body_scripts %}
<script src="/static/js/config_editor.js"></script>
<!-- Add any other page-specific JS initialization here if needed -->
{% endblock %}