mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-06 13:14:11 +00:00
feat: add BackupJob and wire up routes and scheduling
This commit is contained in:
parent
3181d5805d
commit
50d3b2cd7e
4 changed files with 104 additions and 0 deletions
28
docs/Tasktracking/2026-04-26-backup-job-scheduling.md
Normal file
28
docs/Tasktracking/2026-04-26-backup-job-scheduling.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Task Record
|
||||
|
||||
Date: 2026-04-26
|
||||
Related Module: web/job, web/controller, web
|
||||
Change Type: Add
|
||||
|
||||
## Background
|
||||
Database backups were only possible manually via API. There was no scheduled backup mechanism, so users had to remember to create backups or rely on external cron.
|
||||
|
||||
## Changes
|
||||
- Created `web/job/backup_job.go`: BackupJob struct with Run() and shouldRun() methods that check backup settings and execute CreateSnapshot when the configured schedule matches the current time
|
||||
- Registered BackupController routes in `web/controller/server.go` initRouter() to expose backup API endpoints under `/panel/api/server/`
|
||||
- Scheduled BackupJob via cron in `web/web.go` startTask() at `@every 1m` interval (the job internally checks whether it should actually run)
|
||||
|
||||
## Impact
|
||||
- New file: `web/job/backup_job.go`
|
||||
- Modified: `web/controller/server.go` (3 lines added in initRouter)
|
||||
- Modified: `web/web.go` (2 lines added in startTask)
|
||||
- No database schema changes, no API breaking changes, no config changes
|
||||
|
||||
## Verification
|
||||
- `go build ./...` passed with no errors
|
||||
- `gofmt -l -w .` produced no changes (files already properly formatted)
|
||||
|
||||
## Risks And Follow-Up
|
||||
- BackupJob has zero-value service fields; methods rely on DB queries at runtime, same pattern as other jobs (CheckXrayRunningJob, LdapSyncJob)
|
||||
- The job uses `logger.Warning` (not `logger.Warn`) — verify this matches the logger API
|
||||
- Need to verify that Backups setting page in UI has the scheduling options (BackupEnabled, BackupFrequency, BackupHour) to make the job functional
|
||||
|
|
@ -60,6 +60,10 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) {
|
|||
g.POST("/xraylogs/:count", a.getXrayLogs)
|
||||
g.POST("/importDB", a.importDB)
|
||||
g.POST("/getNewEchCert", a.getNewEchCert)
|
||||
|
||||
// Backup routes
|
||||
backupCtrl := BackupController{}
|
||||
backupCtrl.initRouter(g)
|
||||
}
|
||||
|
||||
// refreshStatus updates the cached server status and collects CPU history.
|
||||
|
|
|
|||
69
web/job/backup_job.go
Normal file
69
web/job/backup_job.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package job
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v2/logger"
|
||||
"github.com/mhsanaei/3x-ui/v2/web/service"
|
||||
)
|
||||
|
||||
// BackupJob handles scheduled database backups.
|
||||
type BackupJob struct {
|
||||
settingService service.SettingService
|
||||
backupService service.BackupService
|
||||
}
|
||||
|
||||
// NewBackupJob creates a new BackupJob instance.
|
||||
func NewBackupJob() *BackupJob {
|
||||
return &BackupJob{}
|
||||
}
|
||||
|
||||
// Run executes the scheduled backup if enabled and time matches the configured frequency.
|
||||
func (j *BackupJob) Run() {
|
||||
enabled, err := j.settingService.GetBackupEnabled()
|
||||
if err != nil || !enabled {
|
||||
return
|
||||
}
|
||||
|
||||
frequency, err := j.settingService.GetBackupFrequency()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !j.shouldRun(frequency) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := j.backupService.CreateSnapshot(j.settingService); err != nil {
|
||||
logger.Warning("scheduled backup failed:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// shouldRun checks if the backup should run based on the configured frequency.
|
||||
func (j *BackupJob) shouldRun(frequency string) bool {
|
||||
now := time.Now()
|
||||
|
||||
switch frequency {
|
||||
case "hourly":
|
||||
return now.Minute() == 0
|
||||
case "every12h":
|
||||
return (now.Hour() == 0 || now.Hour() == 12) && now.Minute() == 0
|
||||
case "daily":
|
||||
hour, err := j.settingService.GetBackupHour()
|
||||
if err != nil {
|
||||
hour = 3
|
||||
}
|
||||
return now.Hour() == hour && now.Minute() == 0
|
||||
case "weekly":
|
||||
if now.Weekday() != time.Sunday {
|
||||
return false
|
||||
}
|
||||
hour, err := j.settingService.GetBackupHour()
|
||||
if err != nil {
|
||||
hour = 3
|
||||
}
|
||||
return now.Hour() == hour && now.Minute() == 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -410,6 +410,9 @@ func (s *Server) startTask() {
|
|||
} else {
|
||||
s.cron.Remove(entry)
|
||||
}
|
||||
|
||||
// Schedule database backup job (runs every minute, checks schedule internally)
|
||||
s.cron.AddJob("@every 1m", job.NewBackupJob())
|
||||
}
|
||||
|
||||
func (s *Server) startNodeLoops() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue