feat(app): add WGRplane web UI and backend features

- Add Vue 3 frontend with glassmorphism design (Tailwind CSS)
- Add Go backend handlers: auth, webhooks, stats, scheduler, validation
- Add i18n support (EN, ID, ZH)
- Add Swagger docs and API handlers
- Add nftables integration and plugins support
- Remove deprecated go.mod (migrated to wgrplane)
This commit is contained in:
datadunia
2026-05-03 23:20:34 +07:00
parent 3eae539a56
commit 6d8899a078
54 changed files with 7395 additions and 434 deletions
+95
View File
@@ -0,0 +1,95 @@
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)
}
}
}
}