mirror of
https://github.com/snailyp/gemini-balance.git
synced 2026-05-22 08:49:32 +08:00
feat(error_log): 添加清空所有错误日志的功能
主要变更: - 在数据库服务层 ([`app/database/services.py:364`](app/database/services.py:364)) 添加了 `delete_all_error_logs` 函数。 - 在错误日志路由 ([`app/router/error_log_routes.py:186`](app/router/error_log_routes.py:186)) 中添加了新的 `DELETE /api/logs/errors/all` API 端点。 - 在前端 ([`app/static/js/error_logs.js`](app/static/js/error_logs.js)) 添加了“清空全部”按钮和相应的处理逻辑,并重构了删除确认模态框以支持此新功能。 - 将 [`app/core/application.py:42`](app/core/application.py:42) 中的 `initialize_database()` 调用从异步更改为同步。
This commit is contained in:
@@ -39,7 +39,7 @@ def update_template_globals(app: FastAPI, update_info: dict):
|
||||
# --- Helper functions for lifespan ---
|
||||
async def _setup_database_and_config(app_settings):
|
||||
"""Initializes database, syncs settings, and initializes KeyManager."""
|
||||
await initialize_database()
|
||||
initialize_database()
|
||||
logger.info("Database initialized successfully")
|
||||
await connect_to_db()
|
||||
await sync_initial_settings()
|
||||
|
||||
@@ -360,7 +360,38 @@ async def delete_error_log_by_id(log_id: int) -> bool:
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting error log with ID {log_id}: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
|
||||
async def delete_all_error_logs() -> int:
|
||||
"""
|
||||
删除所有错误日志条目。
|
||||
|
||||
Returns:
|
||||
int: 被删除的错误日志数量。
|
||||
"""
|
||||
try:
|
||||
# 1. 获取删除前的总数
|
||||
count_query = select(func.count()).select_from(ErrorLog)
|
||||
# fetch_val() is suitable here as we expect a single scalar value
|
||||
total_to_delete = await database.fetch_val(count_query)
|
||||
|
||||
if total_to_delete == 0:
|
||||
logger.info("No error logs found to delete.")
|
||||
return 0
|
||||
|
||||
# 2. 执行删除操作
|
||||
# This creates a query like "DELETE FROM error_log"
|
||||
delete_query = delete(ErrorLog)
|
||||
await database.execute(delete_query)
|
||||
|
||||
logger.info(f"Successfully deleted all {total_to_delete} error logs.")
|
||||
return total_to_delete
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete all error logs: {str(e)}", exc_info=True)
|
||||
# Re-raise the exception so it can be caught by the service layer or route handler
|
||||
raise
|
||||
|
||||
|
||||
# 新增函数:添加请求日志
|
||||
async def add_request_log(
|
||||
model_name: Optional[str],
|
||||
|
||||
@@ -183,6 +183,28 @@ async def delete_error_logs_bulk_api(
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/errors/all", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_all_error_logs_api(request: Request):
|
||||
"""
|
||||
删除所有错误日志 (异步)
|
||||
"""
|
||||
auth_token = request.cookies.get("auth_token")
|
||||
if not auth_token or not verify_auth_token(auth_token):
|
||||
logger.warning("Unauthorized access attempt to delete all error logs")
|
||||
raise HTTPException(status_code=401, detail="Not authenticated")
|
||||
|
||||
try:
|
||||
deleted_count = await error_log_service.process_delete_all_error_logs()
|
||||
logger.info(f"Successfully deleted all {deleted_count} error logs.")
|
||||
# No body needed for 204 response
|
||||
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||
except Exception as e:
|
||||
logger.exception(f"Error deleting all error logs: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=500, detail="Internal server error during deletion of all logs"
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/errors/{log_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_error_log_api(request: Request, log_id: int = Path(..., ge=1)):
|
||||
"""
|
||||
@@ -192,7 +214,7 @@ async def delete_error_log_api(request: Request, log_id: int = Path(..., ge=1)):
|
||||
if not auth_token or not verify_auth_token(auth_token):
|
||||
logger.warning(f"Unauthorized access attempt to delete error log ID: {log_id}")
|
||||
raise HTTPException(status_code=401, detail="Not authenticated")
|
||||
|
||||
|
||||
try:
|
||||
success = await error_log_service.process_delete_error_log_by_id(log_id)
|
||||
if not success:
|
||||
@@ -209,25 +231,3 @@ async def delete_error_log_api(request: Request, log_id: int = Path(..., ge=1)):
|
||||
raise HTTPException(
|
||||
status_code=500, detail="Internal server error during deletion"
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/errors/all", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_all_error_logs_api(request: Request):
|
||||
"""
|
||||
删除所有错误日志 (异步)
|
||||
"""
|
||||
auth_token = request.cookies.get("auth_token")
|
||||
if not auth_token or not verify_auth_token(auth_token):
|
||||
logger.warning("Unauthorized access attempt to delete all error logs")
|
||||
raise HTTPException(status_code=401, detail="Not authenticated")
|
||||
|
||||
try:
|
||||
deleted_count = await error_log_service.process_delete_all_error_logs()
|
||||
logger.info(f"Successfully deleted all {deleted_count} error logs.")
|
||||
# No body needed for 204 response
|
||||
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||
except Exception as e:
|
||||
logger.exception(f"Error deleting all error logs: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=500, detail="Internal server error during deletion of all logs"
|
||||
)
|
||||
|
||||
@@ -111,6 +111,8 @@ let cancelDeleteBtn; // 新增:取消删除按钮
|
||||
let confirmDeleteBtn; // 新增:确认删除按钮
|
||||
let deleteConfirmMessage; // 新增:删除确认消息元素
|
||||
let idsToDeleteGlobally = []; // 新增:存储待删除的ID
|
||||
let currentConfirmCallback = null; // 新增:存储当前的确认回调
|
||||
let deleteAllLogsBtn; // 新增:清空全部按钮
|
||||
|
||||
// Helper functions for initialization
|
||||
function cacheDOMElements() {
|
||||
@@ -147,9 +149,10 @@ function cacheDOMElements() {
|
||||
cancelDeleteBtn = document.getElementById("cancelDeleteBtn");
|
||||
confirmDeleteBtn = document.getElementById("confirmDeleteBtn");
|
||||
deleteConfirmMessage = document.getElementById("deleteConfirmMessage");
|
||||
}
|
||||
|
||||
function initializePageSizeControls() {
|
||||
deleteAllLogsBtn = document.getElementById("deleteAllLogsBtn"); // 缓存清空全部按钮
|
||||
}
|
||||
|
||||
function initializePageSizeControls() {
|
||||
if (pageSizeSelector) {
|
||||
pageSizeSelector.value = errorLogState.pageSize;
|
||||
pageSizeSelector.addEventListener("change", function () {
|
||||
@@ -247,9 +250,35 @@ function initializeActionControls() {
|
||||
}
|
||||
// Bulk selection listeners are closely related to actions
|
||||
setupBulkSelectionListeners();
|
||||
}
|
||||
|
||||
// 页面加载完成后执行
|
||||
|
||||
// 为 "清空全部" 按钮添加事件监听器
|
||||
if (deleteAllLogsBtn) {
|
||||
deleteAllLogsBtn.addEventListener("click", function() {
|
||||
const message = "您确定要清空所有错误日志吗?此操作不可恢复!";
|
||||
showDeleteConfirmModal(message, handleDeleteAllLogs); // 传入回调
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:处理 "清空全部" 逻辑的函数
|
||||
async function handleDeleteAllLogs() {
|
||||
const url = "/api/logs/errors/all";
|
||||
const options = {
|
||||
method: "DELETE",
|
||||
};
|
||||
|
||||
try {
|
||||
await fetchAPI(url, options);
|
||||
showNotification("所有错误日志已成功清空", "success");
|
||||
if (selectAllCheckbox) selectAllCheckbox.checked = false; // 取消全选
|
||||
loadErrorLogs(); // 重新加载日志
|
||||
} catch (error) {
|
||||
console.error("清空所有错误日志失败:", error);
|
||||
showNotification(`清空失败: ${error.message}`, "error", 5000);
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后执行
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
cacheDOMElements();
|
||||
initializePageSizeControls();
|
||||
@@ -267,31 +296,33 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
});
|
||||
|
||||
// 新增:显示删除确认模态框
|
||||
function showDeleteConfirmModal(message) {
|
||||
function showDeleteConfirmModal(message, confirmCallback) {
|
||||
if (deleteConfirmModal && deleteConfirmMessage) {
|
||||
deleteConfirmMessage.textContent = message;
|
||||
currentConfirmCallback = confirmCallback; // 存储回调
|
||||
deleteConfirmModal.classList.add("show");
|
||||
document.body.style.overflow = "hidden"; // Prevent body scrolling
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 新增:隐藏删除确认模态框
|
||||
function hideDeleteConfirmModal() {
|
||||
if (deleteConfirmModal) {
|
||||
deleteConfirmModal.classList.remove("show");
|
||||
document.body.style.overflow = ""; // Restore body scrolling
|
||||
idsToDeleteGlobally = []; // 清空待删除ID
|
||||
currentConfirmCallback = null; // 清除回调
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 新增:处理确认删除按钮点击
|
||||
function handleConfirmDelete() {
|
||||
if (idsToDeleteGlobally.length > 0) {
|
||||
performActualDelete(idsToDeleteGlobally);
|
||||
if (typeof currentConfirmCallback === 'function') {
|
||||
currentConfirmCallback(); // 调用存储的回调
|
||||
}
|
||||
hideDeleteConfirmModal(); // 关闭模态框
|
||||
}
|
||||
|
||||
|
||||
// Fallback copy function using document.execCommand
|
||||
function fallbackCopyTextToClipboard(text) {
|
||||
const textArea = document.createElement("textarea");
|
||||
@@ -553,11 +584,13 @@ function handleDeleteSelected() {
|
||||
}
|
||||
|
||||
// 存储待删除ID并显示模态框
|
||||
idsToDeleteGlobally = logIdsToDelete;
|
||||
idsToDeleteGlobally = logIdsToDelete; // 仍然需要设置,因为 performActualDelete 会用到
|
||||
const message = `确定要删除选中的 ${logIdsToDelete.length} 条日志吗?此操作不可恢复!`;
|
||||
showDeleteConfirmModal(message);
|
||||
showDeleteConfirmModal(message, function() { // 传入匿名回调
|
||||
performActualDelete(idsToDeleteGlobally);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 新增:执行实际的删除操作(提取自原 handleDeleteSelected 和 handleDeleteLogRow)
|
||||
async function performActualDelete(logIds) {
|
||||
if (!logIds || logIds.length === 0) return;
|
||||
@@ -599,12 +632,14 @@ function handleDeleteLogRow(logId) {
|
||||
if (!logId) return;
|
||||
|
||||
// 存储待删除ID并显示模态框
|
||||
idsToDeleteGlobally = [parseInt(logId)]; // 存储为数组
|
||||
idsToDeleteGlobally = [parseInt(logId)]; // 存储为数组 // 仍然需要设置,因为 performActualDelete 会用到
|
||||
// 使用通用确认消息,不显示具体ID
|
||||
const message = `确定要删除这条日志吗?此操作不可恢复!`;
|
||||
showDeleteConfirmModal(message);
|
||||
showDeleteConfirmModal(message, function() { // 传入匿名回调
|
||||
performActualDelete([parseInt(logId)]); // 确保传递的是数组
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 新增:处理 ID 排序点击的函数
|
||||
function handleSortById() {
|
||||
if (errorLogState.sort.field === "id") {
|
||||
|
||||
Reference in New Issue
Block a user