feat: add log settings page
This commit is contained in:
@@ -49,7 +49,15 @@ const (
|
|||||||
|
|
||||||
// System Settings Key 常量
|
// System Settings Key 常量
|
||||||
KeyInitialized = "initialized"
|
KeyInitialized = "initialized"
|
||||||
KeyLogRetention = "log_retention"
|
// KeyLogRetention = "log_retention" // Deprecated
|
||||||
|
|
||||||
|
// Log Retention Keys
|
||||||
|
KeySystemNoticeDays = "system_notice_days"
|
||||||
|
KeySystemNoticeMaxCount = "system_notice_max_count"
|
||||||
|
KeyPushLogDays = "push_log_days"
|
||||||
|
KeyPushLogMaxCount = "push_log_max_count"
|
||||||
|
KeyLoginLogDays = "login_log_days"
|
||||||
|
KeyLoginLogMaxCount = "login_log_max_count"
|
||||||
|
|
||||||
// Scheduler Settings Key 常量
|
// Scheduler Settings Key 常量
|
||||||
KeyWorkerCount = "worker_count"
|
KeyWorkerCount = "worker_count"
|
||||||
@@ -160,6 +168,3 @@ var DefaultSettings = map[string]map[string]string{
|
|||||||
KeyRateInterval: "200",
|
KeyRateInterval: "200",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultLogRetention 默认日志清理配置
|
|
||||||
var DefaultLogRetention = `{"system_notice":{"days":30,"max_count":500},"push_log":{"days":15,"max_count":5000}}`
|
|
||||||
|
|||||||
@@ -108,23 +108,12 @@ func (sc *SettingsController) GetSiteSettings(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取日志清理配置
|
// 获取日志清理配置
|
||||||
logRetentionJson := sc.settingsService.Get(constant.SectionSystem, constant.KeyLogRetention)
|
settings["system_notice_days"] = sc.settingsService.Get(constant.SectionSystem, constant.KeySystemNoticeDays)
|
||||||
if logRetentionJson != "" {
|
settings["system_notice_max_count"] = sc.settingsService.Get(constant.SectionSystem, constant.KeySystemNoticeMaxCount)
|
||||||
var configs map[string]struct {
|
settings["push_log_days"] = sc.settingsService.Get(constant.SectionSystem, constant.KeyPushLogDays)
|
||||||
Days int `json:"days"`
|
settings["push_log_max_count"] = sc.settingsService.Get(constant.SectionSystem, constant.KeyPushLogMaxCount)
|
||||||
MaxCount int `json:"max_count"`
|
settings["login_log_days"] = sc.settingsService.Get(constant.SectionSystem, constant.KeyLoginLogDays)
|
||||||
}
|
settings["login_log_max_count"] = sc.settingsService.Get(constant.SectionSystem, constant.KeyLoginLogMaxCount)
|
||||||
if err := json.Unmarshal([]byte(logRetentionJson), &configs); err == nil {
|
|
||||||
if cfg, ok := configs[constant.LogCategorySystemNotice]; ok {
|
|
||||||
settings["system_notice_days"] = fmt.Sprintf("%d", cfg.Days)
|
|
||||||
settings["system_notice_max_count"] = fmt.Sprintf("%d", cfg.MaxCount)
|
|
||||||
}
|
|
||||||
if cfg, ok := configs[constant.LogCategoryPushLog]; ok {
|
|
||||||
settings["push_log_days"] = fmt.Sprintf("%d", cfg.Days)
|
|
||||||
settings["push_log_max_count"] = fmt.Sprintf("%d", cfg.MaxCount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.Success(c, settings)
|
utils.Success(c, settings)
|
||||||
}
|
}
|
||||||
@@ -156,6 +145,8 @@ func (sc *SettingsController) UpdateSiteSettings(c *gin.Context) {
|
|||||||
SystemNoticeMaxCount int `json:"system_notice_max_count"`
|
SystemNoticeMaxCount int `json:"system_notice_max_count"`
|
||||||
PushLogDays int `json:"push_log_days"`
|
PushLogDays int `json:"push_log_days"`
|
||||||
PushLogMaxCount int `json:"push_log_max_count"`
|
PushLogMaxCount int `json:"push_log_max_count"`
|
||||||
|
LoginLogDays int `json:"login_log_days"`
|
||||||
|
LoginLogMaxCount int `json:"login_log_max_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
@@ -190,19 +181,12 @@ func (sc *SettingsController) UpdateSiteSettings(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 保存日志清理配置
|
// 保存日志清理配置
|
||||||
logRetentionConfig := map[string]interface{}{
|
sc.settingsService.Set(constant.SectionSystem, constant.KeySystemNoticeDays, fmt.Sprintf("%d", req.SystemNoticeDays))
|
||||||
constant.LogCategorySystemNotice: map[string]int{
|
sc.settingsService.Set(constant.SectionSystem, constant.KeySystemNoticeMaxCount, fmt.Sprintf("%d", req.SystemNoticeMaxCount))
|
||||||
"days": req.SystemNoticeDays,
|
sc.settingsService.Set(constant.SectionSystem, constant.KeyPushLogDays, fmt.Sprintf("%d", req.PushLogDays))
|
||||||
"max_count": req.SystemNoticeMaxCount,
|
sc.settingsService.Set(constant.SectionSystem, constant.KeyPushLogMaxCount, fmt.Sprintf("%d", req.PushLogMaxCount))
|
||||||
},
|
sc.settingsService.Set(constant.SectionSystem, constant.KeyLoginLogDays, fmt.Sprintf("%d", req.LoginLogDays))
|
||||||
constant.LogCategoryPushLog: map[string]int{
|
sc.settingsService.Set(constant.SectionSystem, constant.KeyLoginLogMaxCount, fmt.Sprintf("%d", req.LoginLogMaxCount))
|
||||||
"days": req.PushLogDays,
|
|
||||||
"max_count": req.PushLogMaxCount,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if logRetentionJson, err := json.Marshal(logRetentionConfig); err == nil {
|
|
||||||
sc.settingsService.Set(constant.SectionSystem, constant.KeyLogRetention, string(logRetentionJson))
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.SuccessMsg(c, "保存成功")
|
utils.SuccessMsg(c, "保存成功")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -85,24 +84,30 @@ func (s *AppLogService) Clear(category string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *AppLogService) GetRetentionConfigs() map[string]LogRetentionConfig {
|
func (s *AppLogService) GetRetentionConfigs() map[string]LogRetentionConfig {
|
||||||
val := s.settingsService.Get(constant.SectionSystem, "log_retention")
|
configs := map[string]LogRetentionConfig{
|
||||||
var configs map[string]LogRetentionConfig
|
constant.LogCategorySystemNotice: {
|
||||||
if val != "" {
|
Days: utils.ToInt(s.settingsService.Get(constant.SectionSystem, constant.KeySystemNoticeDays), 30),
|
||||||
_ = json.Unmarshal([]byte(val), &configs)
|
MaxCount: utils.ToInt(s.settingsService.Get(constant.SectionSystem, constant.KeySystemNoticeMaxCount), 500),
|
||||||
}
|
},
|
||||||
if configs == nil {
|
constant.LogCategoryPushLog: {
|
||||||
configs = map[string]LogRetentionConfig{
|
Days: utils.ToInt(s.settingsService.Get(constant.SectionSystem, constant.KeyPushLogDays), 15),
|
||||||
constant.LogCategorySystemNotice: {Days: 30, MaxCount: 500},
|
MaxCount: utils.ToInt(s.settingsService.Get(constant.SectionSystem, constant.KeyPushLogMaxCount), 5000),
|
||||||
constant.LogCategoryPushLog: {Days: 15, MaxCount: 5000},
|
},
|
||||||
constant.LogCategoryDefault: {Days: 30, MaxCount: 10000},
|
constant.LogCategoryLoginLog: {
|
||||||
}
|
Days: utils.ToInt(s.settingsService.Get(constant.SectionSystem, constant.KeyLoginLogDays), 30),
|
||||||
|
MaxCount: utils.ToInt(s.settingsService.Get(constant.SectionSystem, constant.KeyLoginLogMaxCount), 1000),
|
||||||
|
},
|
||||||
|
constant.LogCategoryDefault: {
|
||||||
|
Days: 30,
|
||||||
|
MaxCount: 10000,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return configs
|
return configs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AppLogService) CleanUp() {
|
func (s *AppLogService) CleanUp() {
|
||||||
configs := s.GetRetentionConfigs()
|
configs := s.GetRetentionConfigs()
|
||||||
categories := []string{constant.LogCategorySystemNotice, constant.LogCategoryPushLog}
|
categories := []string{constant.LogCategorySystemNotice, constant.LogCategoryPushLog, constant.LogCategoryLoginLog}
|
||||||
|
|
||||||
for _, cat := range categories {
|
for _, cat := range categories {
|
||||||
cfg, ok := configs[cat]
|
cfg, ok := configs[cat]
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/engigu/baihu-panel/internal/cache"
|
"github.com/engigu/baihu-panel/internal/cache"
|
||||||
"github.com/engigu/baihu-panel/internal/constant"
|
"github.com/engigu/baihu-panel/internal/constant"
|
||||||
"github.com/engigu/baihu-panel/internal/database"
|
"github.com/engigu/baihu-panel/internal/database"
|
||||||
@@ -34,16 +37,53 @@ func (s *SettingsService) InitSettings() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化日志清理配置
|
// 初始化日志清理配置
|
||||||
var logRetentionCount int64
|
// 检查是否需要从旧的 JSON 迁移
|
||||||
database.DB.Model(&models.Setting{}).Where("section = ? AND `key` = ?", constant.SectionSystem, constant.KeyLogRetention).Count(&logRetentionCount)
|
oldVal := s.Get(constant.SectionSystem, "log_retention")
|
||||||
if logRetentionCount == 0 {
|
if oldVal != "" {
|
||||||
if err := database.DB.Create(&models.Setting{
|
var oldConfigs map[string]struct {
|
||||||
ID: utils.GenerateID(),
|
Days int `json:"days"`
|
||||||
Section: constant.SectionSystem,
|
MaxCount int `json:"max_count"`
|
||||||
Key: constant.KeyLogRetention,
|
}
|
||||||
Value: models.BigText(constant.DefaultLogRetention),
|
if err := json.Unmarshal([]byte(oldVal), &oldConfigs); err == nil {
|
||||||
}).Error; err != nil {
|
migrationMap := map[string]string{}
|
||||||
return err
|
if cfg, ok := oldConfigs[constant.LogCategorySystemNotice]; ok {
|
||||||
|
migrationMap[constant.KeySystemNoticeDays] = fmt.Sprintf("%d", cfg.Days)
|
||||||
|
migrationMap[constant.KeySystemNoticeMaxCount] = fmt.Sprintf("%d", cfg.MaxCount)
|
||||||
|
}
|
||||||
|
if cfg, ok := oldConfigs[constant.LogCategoryPushLog]; ok {
|
||||||
|
migrationMap[constant.KeyPushLogDays] = fmt.Sprintf("%d", cfg.Days)
|
||||||
|
migrationMap[constant.KeyPushLogMaxCount] = fmt.Sprintf("%d", cfg.MaxCount)
|
||||||
|
}
|
||||||
|
if cfg, ok := oldConfigs[constant.LogCategoryLoginLog]; ok {
|
||||||
|
migrationMap[constant.KeyLoginLogDays] = fmt.Sprintf("%d", cfg.Days)
|
||||||
|
migrationMap[constant.KeyLoginLogMaxCount] = fmt.Sprintf("%d", cfg.MaxCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(migrationMap) > 0 {
|
||||||
|
for k, v := range migrationMap {
|
||||||
|
s.Set(constant.SectionSystem, k, v)
|
||||||
|
}
|
||||||
|
// 迁移完成后删除旧键
|
||||||
|
s.Delete(constant.SectionSystem, "log_retention")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认值初始化
|
||||||
|
defaultRetention := map[string]string{
|
||||||
|
constant.KeySystemNoticeDays: "30",
|
||||||
|
constant.KeySystemNoticeMaxCount: "500",
|
||||||
|
constant.KeyPushLogDays: "15",
|
||||||
|
constant.KeyPushLogMaxCount: "5000",
|
||||||
|
constant.KeyLoginLogDays: "30",
|
||||||
|
constant.KeyLoginLogMaxCount: "1000",
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range defaultRetention {
|
||||||
|
var count int64
|
||||||
|
database.DB.Model(&models.Setting{}).Where("section = ? AND `key` = ?", constant.SectionSystem, k).Count(&count)
|
||||||
|
if count == 0 {
|
||||||
|
s.Set(constant.SectionSystem, k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,15 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ToInt 解析字符串为整数,如果解析失败则返回默认值
|
||||||
|
func ToInt(s string, defaultVal int) int {
|
||||||
|
val, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return defaultVal
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
// ParseInt 解析字符串为整数
|
// ParseInt 解析字符串为整数
|
||||||
func ParseInt(s string) (int, error) {
|
func ParseInt(s string) (int, error) {
|
||||||
return strconv.Atoi(s)
|
return strconv.Atoi(s)
|
||||||
|
|||||||
@@ -454,6 +454,8 @@ export interface SiteSettings {
|
|||||||
system_notice_max_count?: string
|
system_notice_max_count?: string
|
||||||
push_log_days?: string
|
push_log_days?: string
|
||||||
push_log_max_count?: string
|
push_log_max_count?: string
|
||||||
|
login_log_days?: string
|
||||||
|
login_log_max_count?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SchedulerSettings {
|
export interface SchedulerSettings {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { toast } from 'vue-sonner'
|
|||||||
import { useSiteSettings } from '@/composables/useSiteSettings'
|
import { useSiteSettings } from '@/composables/useSiteSettings'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Switch } from '@/components/ui/switch'
|
import { Switch } from '@/components/ui/switch'
|
||||||
import { RefreshCw, Copy, AlertTriangle, ExternalLink } from 'lucide-vue-next'
|
import { RefreshCw, Copy, AlertTriangle, ExternalLink, Info, Clock } from 'lucide-vue-next'
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
@@ -36,7 +36,9 @@ const form = ref<SiteSettings>({
|
|||||||
system_notice_days: '30',
|
system_notice_days: '30',
|
||||||
system_notice_max_count: '500',
|
system_notice_max_count: '500',
|
||||||
push_log_days: '15',
|
push_log_days: '15',
|
||||||
push_log_max_count: '5000'
|
push_log_max_count: '5000',
|
||||||
|
login_log_days: '30',
|
||||||
|
login_log_max_count: '1000'
|
||||||
})
|
})
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const showOpenapiConfirmDialog = ref(false)
|
const showOpenapiConfirmDialog = ref(false)
|
||||||
@@ -70,7 +72,9 @@ async function saveSettings() {
|
|||||||
system_notice_days: String(form.value.system_notice_days || '30'),
|
system_notice_days: String(form.value.system_notice_days || '30'),
|
||||||
system_notice_max_count: String(form.value.system_notice_max_count || '500'),
|
system_notice_max_count: String(form.value.system_notice_max_count || '500'),
|
||||||
push_log_days: String(form.value.push_log_days || '15'),
|
push_log_days: String(form.value.push_log_days || '15'),
|
||||||
push_log_max_count: String(form.value.push_log_max_count || '5000')
|
push_log_max_count: String(form.value.push_log_max_count || '5000'),
|
||||||
|
login_log_days: String(form.value.login_log_days || '30'),
|
||||||
|
login_log_max_count: String(form.value.login_log_max_count || '1000')
|
||||||
})
|
})
|
||||||
await refreshSettings()
|
await refreshSettings()
|
||||||
toast.success('保存成功')
|
toast.success('保存成功')
|
||||||
@@ -149,36 +153,82 @@ onMounted(loadSettings)
|
|||||||
<h3 class="text-lg font-medium text-foreground mb-4">日志清理策略</h3>
|
<h3 class="text-lg font-medium text-foreground mb-4">日志清理策略</h3>
|
||||||
<p class="text-sm text-muted-foreground mb-4">自动清理超过指定天数或数量的日志记录,保持系统性能。</p>
|
<p class="text-sm text-muted-foreground mb-4">自动清理超过指定天数或数量的日志记录,保持系统性能。</p>
|
||||||
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4 sm:space-y-4">
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-4 items-center gap-2 sm:gap-4">
|
<div class="grid grid-cols-1 sm:grid-cols-4 items-center gap-2 sm:gap-4">
|
||||||
<Label class="sm:text-right text-muted-foreground">系统通知</Label>
|
<Label class="sm:text-right text-muted-foreground whitespace-nowrap">系统通知</Label>
|
||||||
<div class="sm:col-span-3 flex flex-wrap items-center gap-4">
|
<div class="sm:col-span-3 flex flex-wrap items-center gap-x-3 gap-y-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-1.5">
|
||||||
<Input v-model="form.system_notice_days" type="number" class="w-20" min="0" />
|
<Input v-model="form.system_notice_days" type="number" class="w-16 h-8 text-xs sm:w-20 sm:h-9 sm:text-sm"
|
||||||
<span class="text-sm text-muted-foreground">天后清理</span>
|
min="0" />
|
||||||
|
<span class="text-xs sm:text-sm text-muted-foreground whitespace-nowrap">天清理</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-1.5 border-l pl-3 border-border/50">
|
||||||
<span class="text-sm text-muted-foreground">或保留最新</span>
|
<span class="text-xs sm:text-sm text-muted-foreground whitespace-nowrap">保留</span>
|
||||||
<Input v-model="form.system_notice_max_count" type="number" class="w-24" min="0" />
|
<Input v-model="form.system_notice_max_count" type="number"
|
||||||
<span class="text-sm text-muted-foreground">条</span>
|
class="w-20 h-8 text-xs sm:w-24 sm:h-9 sm:text-sm" min="0" />
|
||||||
|
<span class="text-xs sm:text-sm text-muted-foreground whitespace-nowrap">条</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-4 items-center gap-2 sm:gap-4">
|
<div class="grid grid-cols-1 sm:grid-cols-4 items-center gap-2 sm:gap-4">
|
||||||
<Label class="sm:text-right text-muted-foreground">推送日志</Label>
|
<Label class="sm:text-right text-muted-foreground whitespace-nowrap">推送日志</Label>
|
||||||
<div class="sm:col-span-3 flex flex-wrap items-center gap-4">
|
<div class="sm:col-span-3 flex flex-wrap items-center gap-x-3 gap-y-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-1.5">
|
||||||
<Input v-model="form.push_log_days" type="number" class="w-20" min="0" />
|
<Input v-model="form.push_log_days" type="number" class="w-16 h-8 text-xs sm:w-20 sm:h-9 sm:text-sm"
|
||||||
<span class="text-sm text-muted-foreground">天后清理</span>
|
min="0" />
|
||||||
|
<span class="text-xs sm:text-sm text-muted-foreground whitespace-nowrap">天清理</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-1.5 border-l pl-3 border-border/50">
|
||||||
<span class="text-sm text-muted-foreground">或保留最新</span>
|
<span class="text-xs sm:text-sm text-muted-foreground whitespace-nowrap">保留</span>
|
||||||
<Input v-model="form.push_log_max_count" type="number" class="w-24" min="0" />
|
<Input v-model="form.push_log_max_count" type="number" class="w-20 h-8 text-xs sm:w-24 sm:h-9 sm:text-sm"
|
||||||
<span class="text-sm text-muted-foreground">条</span>
|
min="0" />
|
||||||
|
<span class="text-xs sm:text-sm text-muted-foreground whitespace-nowrap">条</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 sm:grid-cols-4 items-center gap-2 sm:gap-4">
|
||||||
|
<Label class="sm:text-right text-muted-foreground whitespace-nowrap">登录日志</Label>
|
||||||
|
<div class="sm:col-span-3 flex flex-wrap items-center gap-x-3 gap-y-2">
|
||||||
|
<div class="flex items-center gap-1.5">
|
||||||
|
<Input v-model="form.login_log_days" type="number" class="w-16 h-8 text-xs sm:w-20 sm:h-9 sm:text-sm"
|
||||||
|
min="0" />
|
||||||
|
<span class="text-xs sm:text-sm text-muted-foreground whitespace-nowrap">天清理</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1.5 border-l pl-3 border-border/50">
|
||||||
|
<span class="text-xs sm:text-sm text-muted-foreground whitespace-nowrap">保留</span>
|
||||||
|
<Input v-model="form.login_log_max_count" type="number" class="w-20 h-8 text-xs sm:w-24 sm:h-9 sm:text-sm"
|
||||||
|
min="0" />
|
||||||
|
<span class="text-xs sm:text-sm text-muted-foreground whitespace-nowrap">条</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 p-4 bg-muted/30 rounded-lg border border-dashed border-border flex flex-col gap-3">
|
||||||
|
<div class="flex items-start gap-3">
|
||||||
|
<div class="p-1.5 bg-blue-500/10 rounded-full">
|
||||||
|
<Info class="w-4 h-4 text-blue-600 dark:text-blue-400" />
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<p class="text-sm font-medium">双重维度限制</p>
|
||||||
|
<p class="text-xs text-muted-foreground leading-relaxed">
|
||||||
|
系统将根据天数和数量同时进行监测。满足任一条件即执行清理:超过天数的旧数据将被物理删除;若日志总数超过限制条数,则自动剔除最早产生的记录。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-start gap-3">
|
||||||
|
<div class="p-1.5 bg-amber-500/10 rounded-full">
|
||||||
|
<Clock class="w-4 h-4 text-amber-600 dark:text-amber-400" />
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<p class="text-sm font-medium">执行周期说明</p>
|
||||||
|
<p class="text-xs text-muted-foreground leading-relaxed">
|
||||||
|
清理任务在白虎面板后端服务启动时立即执行一次。在运行期间,系统将自动开启后台巡检计数器,每隔 24 小时进行周期性自动清理。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user