package main import ( "log" "time" "github.com/robfig/cron/v3" ) // initScheduler creates and starts the background cron jobs responsible for // peer lifecycle automation: expiry deletion, data-limit restriction, and monthly resets. func initScheduler() *cron.Cron { c := cron.New(cron.WithSeconds()) // 2:00 AM daily - Delete expired peers if _, err := c.AddFunc("0 0 2 * * *", deleteExpiredPeers); err != nil { log.Printf("scheduler: failed to schedule deleteExpiredPeers: %v", err) } // 3:00 AM daily - Disable peers that exceeded data limit if _, err := c.AddFunc("0 0 3 * * *", restrictOverLimitPeers); err != nil { log.Printf("scheduler: failed to schedule restrictOverLimitPeers: %v", err) } // 1st day of every month at 00:00 - Reset data usage counters if _, err := c.AddFunc("0 0 0 1 * *", resetMonthlyUsage); err != nil { log.Printf("scheduler: failed to schedule resetMonthlyUsage: %v", err) } c.Start() log.Println("scheduler: started background jobs (expiry, data-limit, monthly reset)") return c } // deleteExpiredPeers removes peers whose ExpiresAt is non-zero and in the past. func deleteExpiredPeers() { now := time.Now() var peers []Peer if err := db.Where("expires_at != 0 AND expires_at <= ?", now).Find(&peers).Error; err != nil { log.Printf("scheduler: error querying expired peers: %v", err) return } if len(peers) == 0 { log.Println("scheduler: no expired peers to delete") return } for _, p := range peers { if err := db.Delete(&p).Error; err != nil { log.Printf("scheduler: failed to delete peer %d: %v", p.ID, err) } else { log.Printf("scheduler: deleted expired peer %d", p.ID) } } } // restrictOverLimitPeers disables peers that have exceeded their data limit. func restrictOverLimitPeers() { var peers []Peer if err := db.Find(&peers).Error; err != nil { log.Printf("scheduler: error loading peers for restriction: %v", err) return } for _, p := range peers { if p.Enabled && p.DataLimitGB > 0 { limitBytes := p.DataLimitGB * 1_000_000_000 if p.CurrentDataUsageBytes >= limitBytes { p.Enabled = false if err := db.Save(&p).Error; err != nil { log.Printf("scheduler: failed to disable peer %d: %v", p.ID, err) } else { log.Printf("scheduler: disabled peer %d due to data limit (%d/%d bytes)", p.ID, p.CurrentDataUsageBytes, limitBytes) } } } } } // resetMonthlyUsage resets all peers' CurrentDataUsageBytes to zero at the start of each month. func resetMonthlyUsage() { var peers []Peer if err := db.Find(&peers).Error; err != nil { log.Printf("scheduler: error loading peers for monthly reset: %v", err) return } for _, p := range peers { if p.CurrentDataUsageBytes != 0 { p.CurrentDataUsageBytes = 0 if err := db.Save(&p).Error; err != nil { log.Printf("scheduler: failed to reset usage for peer %d: %v", p.ID, err) } else { log.Printf("scheduler: reset monthly usage for peer %d", p.ID) } } } }