feat: add a service for periodic cleanup of error accounts based on configuration.
This commit is contained in:
@@ -104,8 +104,9 @@ func startServer(cfg *config.Config) {
|
|||||||
// 日志 API
|
// 日志 API
|
||||||
mux.HandleFunc("/api/logs", api.CORS(handleGetLogs))
|
mux.HandleFunc("/api/logs", api.CORS(handleGetLogs))
|
||||||
mux.HandleFunc("/api/logs/clear", api.CORS(handleClearLogs))
|
mux.HandleFunc("/api/logs/clear", api.CORS(handleClearLogs))
|
||||||
mux.HandleFunc("/api/logs/query", api.CORS(handleQueryLogs)) // 按模块查询日志
|
mux.HandleFunc("/api/logs/clear-module", api.CORS(handleClearLogsByModule)) // 按模块清除日志
|
||||||
mux.HandleFunc("/api/logs/stream", handleLogStream) // SSE 实时日志
|
mux.HandleFunc("/api/logs/query", api.CORS(handleQueryLogs)) // 按模块查询日志
|
||||||
|
mux.HandleFunc("/api/logs/stream", handleLogStream) // SSE 实时日志
|
||||||
|
|
||||||
// S2A 代理 API
|
// S2A 代理 API
|
||||||
mux.HandleFunc("/api/s2a/test", api.CORS(handleS2ATest))
|
mux.HandleFunc("/api/s2a/test", api.CORS(handleS2ATest))
|
||||||
@@ -296,6 +297,26 @@ func handleClearLogs(w http.ResponseWriter, r *http.Request) {
|
|||||||
api.Success(w, map[string]string{"message": "日志已清空"})
|
api.Success(w, map[string]string{"message": "日志已清空"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleClearLogsByModule POST /api/logs/clear-module?module=cleaner
|
||||||
|
func handleClearLogsByModule(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
api.Error(w, http.StatusMethodNotAllowed, "仅支持 POST")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
module := r.URL.Query().Get("module")
|
||||||
|
if module == "" {
|
||||||
|
api.Error(w, http.StatusBadRequest, "缺少 module 参数")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cleared := logger.ClearLogsByModule(module)
|
||||||
|
api.Success(w, map[string]interface{}{
|
||||||
|
"message": fmt.Sprintf("已清除 %d 条 %s 日志", cleared, module),
|
||||||
|
"cleared": cleared,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// handleQueryLogs GET /api/logs/query?module=cleaner&page=1&page_size=5
|
// handleQueryLogs GET /api/logs/query?module=cleaner&page=1&page_size=5
|
||||||
func handleQueryLogs(w http.ResponseWriter, r *http.Request) {
|
func handleQueryLogs(w http.ResponseWriter, r *http.Request) {
|
||||||
module := r.URL.Query().Get("module")
|
module := r.URL.Query().Get("module")
|
||||||
|
|||||||
@@ -89,13 +89,10 @@ func checkAndCleanErrors() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(errorAccounts) == 0 {
|
if len(errorAccounts) == 0 {
|
||||||
logger.Info("没有错误账号需要清理", "", "cleaner")
|
|
||||||
lastCleanTime = time.Now()
|
lastCleanTime = time.Now()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info(fmt.Sprintf("找到 %d 个错误账号,开始删除...", len(errorAccounts)), "", "cleaner")
|
|
||||||
|
|
||||||
success := 0
|
success := 0
|
||||||
failed := 0
|
failed := 0
|
||||||
|
|
||||||
@@ -106,7 +103,6 @@ func checkAndCleanErrors() {
|
|||||||
logger.Warning(fmt.Sprintf("删除账号失败: ID=%d, Email=%s, Error=%v", account.ID, account.Email, err), account.Email, "cleaner")
|
logger.Warning(fmt.Sprintf("删除账号失败: ID=%d, Email=%s, Error=%v", account.ID, account.Email, err), account.Email, "cleaner")
|
||||||
} else {
|
} else {
|
||||||
success++
|
success++
|
||||||
logger.Success(fmt.Sprintf("删除账号成功: ID=%d, Email=%s", account.ID, account.Email), account.Email, "cleaner")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -242,3 +242,22 @@ func ClearLogs() {
|
|||||||
defer logsMu.Unlock()
|
defer logsMu.Unlock()
|
||||||
logs = make([]LogEntry, 0, 1000)
|
logs = make([]LogEntry, 0, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearLogsByModule 按模块清除日志
|
||||||
|
func ClearLogsByModule(module string) int {
|
||||||
|
logsMu.Lock()
|
||||||
|
defer logsMu.Unlock()
|
||||||
|
|
||||||
|
// 过滤掉指定模块的日志
|
||||||
|
var newLogs []LogEntry
|
||||||
|
cleared := 0
|
||||||
|
for _, log := range logs {
|
||||||
|
if log.Module != module {
|
||||||
|
newLogs = append(newLogs, log)
|
||||||
|
} else {
|
||||||
|
cleared++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logs = newLogs
|
||||||
|
return cleared
|
||||||
|
}
|
||||||
|
|||||||
@@ -131,8 +131,22 @@ export default function LiveLogViewer({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClear = () => {
|
const handleClear = async () => {
|
||||||
setLogs([])
|
if (!confirm('确定要清空所有日志吗?此操作不可撤销。')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/logs/clear', { method: 'POST' })
|
||||||
|
const data = await res.json()
|
||||||
|
if (data.code === 0) {
|
||||||
|
setLogs([])
|
||||||
|
pausedLogsRef.current = []
|
||||||
|
} else {
|
||||||
|
alert('清空日志失败: ' + (data.message || '未知错误'))
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert('清空日志失败: ' + (e instanceof Error ? e.message : '网络错误'))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -161,8 +175,8 @@ export default function LiveLogViewer({
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleClear}
|
onClick={handleClear}
|
||||||
className="p-1.5 rounded-lg bg-slate-700 text-slate-400 hover:bg-slate-600 transition-colors"
|
className="p-1.5 rounded-lg bg-red-500/20 text-red-400 hover:bg-red-500/30 transition-colors"
|
||||||
title="清空"
|
title="清空所有日志"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-3.5 w-3.5" />
|
<Trash2 className="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -396,6 +396,31 @@ export default function Cleaner() {
|
|||||||
<span className="text-sm text-slate-500 dark:text-slate-400">
|
<span className="text-sm text-slate-500 dark:text-slate-400">
|
||||||
共 {logTotal} 条
|
共 {logTotal} 条
|
||||||
</span>
|
</span>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={async () => {
|
||||||
|
if (!confirm('确定要清空所有清理日志吗?此操作不可撤销。')) return
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/logs/clear-module?module=cleaner', { method: 'POST' })
|
||||||
|
const data = await res.json()
|
||||||
|
if (data.code === 0) {
|
||||||
|
setLogEntries([])
|
||||||
|
setLogTotal(0)
|
||||||
|
setLogTotalPages(0)
|
||||||
|
setMessage({ type: 'success', text: data.data.message || '日志已清空' })
|
||||||
|
} else {
|
||||||
|
setMessage({ type: 'error', text: data.message || '清空失败' })
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setMessage({ type: 'error', text: '网络错误' })
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
icon={<Trash2 className="h-3 w-3" />}
|
||||||
|
className="text-red-500 hover:text-red-600 border-red-300 hover:border-red-400 dark:border-red-800"
|
||||||
|
>
|
||||||
|
清空日志
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
Reference in New Issue
Block a user