mirror of
https://github.com/snailyp/gemini-balance.git
synced 2026-09-07 16:56:36 +08:00
refactor(scheduler): 优化定时任务配置和时间处理
- 支持CHECK_INTERVAL_HOURS设置为0以禁用密钥检查任务 - 调整日志清理任务执行时间从凌晨3点改为0点 - 移除timezone依赖,使用本地时间处理 - 优化代码格式和导入顺序 - 为配置编辑器添加CHECK_INTERVAL_HOURS输入验证 - 改进UI布局,为关键配置项添加警告提示
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||||
|
|
||||||
from app.config.config import settings
|
from app.config.config import settings
|
||||||
@@ -6,9 +5,9 @@ from app.domain.gemini_models import GeminiContent, GeminiRequest
|
|||||||
from app.log.logger import Logger
|
from app.log.logger import Logger
|
||||||
from app.service.chat.gemini_chat_service import GeminiChatService
|
from app.service.chat.gemini_chat_service import GeminiChatService
|
||||||
from app.service.error_log.error_log_service import delete_old_error_logs
|
from app.service.error_log.error_log_service import delete_old_error_logs
|
||||||
|
from app.service.files.files_service import get_files_service
|
||||||
from app.service.key.key_manager import get_key_manager_instance
|
from app.service.key.key_manager import get_key_manager_instance
|
||||||
from app.service.request_log.request_log_service import delete_old_request_logs_task
|
from app.service.request_log.request_log_service import delete_old_request_logs_task
|
||||||
from app.service.files.files_service import get_files_service
|
|
||||||
from app.utils.helpers import redact_key_for_logging
|
from app.utils.helpers import redact_key_for_logging
|
||||||
|
|
||||||
logger = Logger.setup_logger("scheduler")
|
logger = Logger.setup_logger("scheduler")
|
||||||
@@ -106,15 +105,16 @@ async def cleanup_expired_files():
|
|||||||
try:
|
try:
|
||||||
files_service = await get_files_service()
|
files_service = await get_files_service()
|
||||||
deleted_count = await files_service.cleanup_expired_files()
|
deleted_count = await files_service.cleanup_expired_files()
|
||||||
|
|
||||||
if deleted_count > 0:
|
if deleted_count > 0:
|
||||||
logger.info(f"Successfully cleaned up {deleted_count} expired files.")
|
logger.info(f"Successfully cleaned up {deleted_count} expired files.")
|
||||||
else:
|
else:
|
||||||
logger.info("No expired files to clean up.")
|
logger.info("No expired files to clean up.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"An error occurred during the scheduled file cleanup: {str(e)}", exc_info=True
|
f"An error occurred during the scheduled file cleanup: {str(e)}",
|
||||||
|
exc_info=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -122,44 +122,45 @@ def setup_scheduler():
|
|||||||
"""设置并启动 APScheduler"""
|
"""设置并启动 APScheduler"""
|
||||||
scheduler = AsyncIOScheduler(timezone=str(settings.TIMEZONE)) # 从配置读取时区
|
scheduler = AsyncIOScheduler(timezone=str(settings.TIMEZONE)) # 从配置读取时区
|
||||||
# 添加检查失败密钥的定时任务
|
# 添加检查失败密钥的定时任务
|
||||||
scheduler.add_job(
|
if settings.CHECK_INTERVAL_HOURS != 0:
|
||||||
check_failed_keys,
|
scheduler.add_job(
|
||||||
"interval",
|
check_failed_keys,
|
||||||
hours=settings.CHECK_INTERVAL_HOURS,
|
"interval",
|
||||||
id="check_failed_keys_job",
|
hours=settings.CHECK_INTERVAL_HOURS,
|
||||||
name="Check Failed API Keys",
|
id="check_failed_keys_job",
|
||||||
)
|
name="Check Failed API Keys",
|
||||||
logger.info(
|
)
|
||||||
f"Key check job scheduled to run every {settings.CHECK_INTERVAL_HOURS} hour(s)."
|
logger.info(
|
||||||
)
|
f"Key check job scheduled to run every {settings.CHECK_INTERVAL_HOURS} hour(s)."
|
||||||
|
)
|
||||||
|
|
||||||
# 新增:添加自动删除错误日志的定时任务,每天凌晨3点执行
|
# 新增:添加自动删除错误日志的定时任务,每天凌晨0点执行
|
||||||
scheduler.add_job(
|
scheduler.add_job(
|
||||||
delete_old_error_logs,
|
delete_old_error_logs,
|
||||||
"cron",
|
"cron",
|
||||||
hour=3,
|
hour=0,
|
||||||
minute=0,
|
minute=0,
|
||||||
id="delete_old_error_logs_job",
|
id="delete_old_error_logs_job",
|
||||||
name="Delete Old Error Logs",
|
name="Delete Old Error Logs",
|
||||||
)
|
)
|
||||||
logger.info("Auto-delete error logs job scheduled to run daily at 3:00 AM.")
|
logger.info("Auto-delete error logs job scheduled to run daily at 3:00 AM.")
|
||||||
|
|
||||||
# 新增:添加自动删除请求日志的定时任务,每天凌晨3点05分执行
|
# 新增:添加自动删除请求日志的定时任务,每天凌晨0点执行
|
||||||
scheduler.add_job(
|
scheduler.add_job(
|
||||||
delete_old_request_logs_task,
|
delete_old_request_logs_task,
|
||||||
"cron",
|
"cron",
|
||||||
hour=3,
|
hour=0,
|
||||||
minute=5,
|
minute=0,
|
||||||
id="delete_old_request_logs_job",
|
id="delete_old_request_logs_job",
|
||||||
name="Delete Old Request Logs",
|
name="Delete Old Request Logs",
|
||||||
)
|
)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Auto-delete request logs job scheduled to run daily at 3:05 AM, if enabled and AUTO_DELETE_REQUEST_LOGS_DAYS is set to {settings.AUTO_DELETE_REQUEST_LOGS_DAYS} days."
|
f"Auto-delete request logs job scheduled to run daily at 3:05 AM, if enabled and AUTO_DELETE_REQUEST_LOGS_DAYS is set to {settings.AUTO_DELETE_REQUEST_LOGS_DAYS} days."
|
||||||
)
|
)
|
||||||
|
|
||||||
# 新增:添加文件过期清理的定时任务,每小时执行一次
|
# 新增:添加文件过期清理的定时任务,每小时执行一次
|
||||||
if getattr(settings, 'FILES_CLEANUP_ENABLED', True):
|
if getattr(settings, "FILES_CLEANUP_ENABLED", True):
|
||||||
cleanup_interval = getattr(settings, 'FILES_CLEANUP_INTERVAL_HOURS', 1)
|
cleanup_interval = getattr(settings, "FILES_CLEANUP_INTERVAL_HOURS", 1)
|
||||||
scheduler.add_job(
|
scheduler.add_job(
|
||||||
cleanup_expired_files,
|
cleanup_expired_files,
|
||||||
"interval",
|
"interval",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from sqlalchemy import delete, func, select
|
from sqlalchemy import delete, func, select
|
||||||
@@ -28,7 +28,7 @@ async def delete_old_error_logs():
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
cutoff_date = datetime.now(timezone.utc) - timedelta(days=days_to_keep)
|
cutoff_date = datetime.now() - timedelta(days=days_to_keep)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Attempting to delete error logs older than {days_to_keep} days (before {cutoff_date.strftime('%Y-%m-%d %H:%M:%S %Z')})."
|
f"Attempting to delete error logs older than {days_to_keep} days (before {cutoff_date.strftime('%Y-%m-%d %H:%M:%S %Z')})."
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
Service for request log operations.
|
Service for request log operations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from sqlalchemy import delete
|
from sqlalchemy import delete
|
||||||
|
|
||||||
from app.database.connection import database
|
|
||||||
from app.config.config import settings
|
from app.config.config import settings
|
||||||
|
from app.database.connection import database
|
||||||
from app.database.models import RequestLog
|
from app.database.models import RequestLog
|
||||||
from app.log.logger import get_request_log_logger
|
from app.log.logger import get_request_log_logger
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ async def delete_old_request_logs_task():
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cutoff_date = datetime.now(timezone.utc) - timedelta(days=days_to_keep)
|
cutoff_date = datetime.now() - timedelta(days=days_to_keep)
|
||||||
|
|
||||||
query = delete(RequestLog).where(RequestLog.request_time < cutoff_date)
|
query = delete(RequestLog).where(RequestLog.request_time < cutoff_date)
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,24 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查间隔小时数输入控制
|
||||||
|
const checkIntervalInput = document.getElementById("CHECK_INTERVAL_HOURS");
|
||||||
|
if (checkIntervalInput) {
|
||||||
|
checkIntervalInput.addEventListener("input", function () {
|
||||||
|
let value = parseFloat(this.value);
|
||||||
|
if (isNaN(value) || value < 0) {
|
||||||
|
this.value = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
checkIntervalInput.addEventListener("change", function () {
|
||||||
|
let value = parseFloat(this.value);
|
||||||
|
if (isNaN(value) || value < 0) {
|
||||||
|
this.value = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Toggle switch events
|
// Toggle switch events
|
||||||
const toggleSwitches = document.querySelectorAll(".toggle-switch");
|
const toggleSwitches = document.querySelectorAll(".toggle-switch");
|
||||||
toggleSwitches.forEach((toggleSwitch) => {
|
toggleSwitches.forEach((toggleSwitch) => {
|
||||||
|
|||||||
@@ -1416,25 +1416,31 @@ endblock %} {% block head_extra_styles %}
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 启用代码执行工具 -->
|
<!-- 启用代码执行工具 -->
|
||||||
<div class="mb-6 flex items-center justify-between">
|
<div class="mb-6">
|
||||||
<label
|
<div class="flex items-center justify-between">
|
||||||
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
|
<label
|
||||||
for="TOOLS_CODE_EXECUTION_ENABLED"
|
for="TOOLS_CODE_EXECUTION_ENABLED"
|
||||||
class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"
|
class="font-semibold text-gray-700"
|
||||||
></label>
|
>启用代码执行工具</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="warning-text">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
<span>启用代码执行工具与大多数工具调用冲突,不建议开启</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -2007,25 +2013,31 @@ endblock %} {% block head_extra_styles %}
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<!-- 启用流式输出优化 -->
|
<!-- 启用流式输出优化 -->
|
||||||
<div class="mb-6 flex items-center justify-between">
|
<div class="mb-6">
|
||||||
<label
|
<div class="flex items-center justify-between">
|
||||||
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
|
<label
|
||||||
for="STREAM_OPTIMIZER_ENABLED"
|
for="STREAM_OPTIMIZER_ENABLED"
|
||||||
class="toggle-label block overflow-hidden h-6 rounded-full bg-gray-300 cursor-pointer"
|
class="font-semibold text-gray-700"
|
||||||
></label>
|
>启用流式输出优化</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="warning-text">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
<span>开启流式优化会在一定程度上减速返回,不建议开启</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -2185,31 +2197,18 @@ endblock %} {% block head_extra_styles %}
|
|||||||
for="CHECK_INTERVAL_HOURS"
|
for="CHECK_INTERVAL_HOURS"
|
||||||
class="block font-semibold mb-2 text-gray-700"
|
class="block font-semibold mb-2 text-gray-700"
|
||||||
>检查间隔(小时)
|
>检查间隔(小时)
|
||||||
<i class="fas fa-question-circle text-gray-400 ml-1 cursor-help" title="定时检查密钥状态的间隔时间(单位:小时)"></i>
|
<i class="fas fa-question-circle text-gray-400 ml-1 cursor-help" title="定时检查密钥状态的间隔时间(单位:小时),设置为0时不进行定时检查"></i>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
id="CHECK_INTERVAL_HOURS"
|
id="CHECK_INTERVAL_HOURS"
|
||||||
name="CHECK_INTERVAL_HOURS"
|
name="CHECK_INTERVAL_HOURS"
|
||||||
min="1"
|
min="0"
|
||||||
|
step="1"
|
||||||
class="w-full px-4 py-3 rounded-lg form-input-themed"
|
class="w-full px-4 py-3 rounded-lg form-input-themed"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 时区 -->
|
|
||||||
<div class="mb-6">
|
|
||||||
<label for="TIMEZONE" class="block font-semibold mb-2 text-gray-700"
|
|
||||||
>时区
|
|
||||||
<i class="fas fa-question-circle text-gray-400 ml-1 cursor-help" title="定时任务使用的时区,格式如 Asia/Shanghai 或 UTC"></i>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="TIMEZONE"
|
|
||||||
name="TIMEZONE"
|
|
||||||
placeholder="例如: Asia/Shanghai"
|
|
||||||
class="w-full px-4 py-3 rounded-lg form-input-themed"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 日志配置 -->
|
<!-- 日志配置 -->
|
||||||
|
|||||||
Reference in New Issue
Block a user