diff --git a/database/model/model.go b/database/model/model.go index 856f66df..23340b24 100644 --- a/database/model/model.go +++ b/database/model/model.go @@ -27,16 +27,18 @@ type User struct { } type Inbound struct { - Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"` - UserId int `json:"-"` - Up int64 `json:"up" form:"up"` - Down int64 `json:"down" form:"down"` - Total int64 `json:"total" form:"total"` - AllTime int64 `json:"allTime" form:"allTime" gorm:"default:0"` - Remark string `json:"remark" form:"remark"` - Enable bool `json:"enable" form:"enable"` - ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` - ClientStats []xray.ClientTraffic `gorm:"foreignKey:InboundId;references:Id" json:"clientStats" form:"clientStats"` + Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"` + UserId int `json:"-"` + Up int64 `json:"up" form:"up"` + Down int64 `json:"down" form:"down"` + Total int64 `json:"total" form:"total"` + AllTime int64 `json:"allTime" form:"allTime" gorm:"default:0"` + Remark string `json:"remark" form:"remark"` + Enable bool `json:"enable" form:"enable"` + ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` + TrafficReset string `json:"trafficReset" form:"trafficReset" gorm:"default:never"` + LastTrafficResetTime int64 `json:"lastTrafficResetTime" form:"lastTrafficResetTime" gorm:"default:0"` + ClientStats []xray.ClientTraffic `gorm:"foreignKey:InboundId;references:Id" json:"clientStats" form:"clientStats"` // config part Listen string `json:"listen" form:"listen"` @@ -90,21 +92,23 @@ type Setting struct { } type Client struct { - ID string `json:"id"` - Security string `json:"security"` - Password string `json:"password"` - Flow string `json:"flow"` - Email string `json:"email"` - LimitIP int `json:"limitIp"` - TotalGB int64 `json:"totalGB" form:"totalGB"` - ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` - Enable bool `json:"enable" form:"enable"` - TgID int64 `json:"tgId" form:"tgId"` - SubID string `json:"subId" form:"subId"` - Comment string `json:"comment" form:"comment"` - Reset int `json:"reset" form:"reset"` - CreatedAt int64 `json:"created_at,omitempty"` - UpdatedAt int64 `json:"updated_at,omitempty"` + ID string `json:"id"` + Security string `json:"security"` + Password string `json:"password"` + Flow string `json:"flow"` + Email string `json:"email"` + LimitIP int `json:"limitIp"` + TotalGB int64 `json:"totalGB" form:"totalGB"` + ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` + TrafficReset string `json:"trafficReset" form:"trafficReset" gorm:"default:never"` + LastTrafficResetTime int64 `json:"lastTrafficResetTime" form:"lastTrafficResetTime" gorm:"default:0"` + Enable bool `json:"enable" form:"enable"` + TgID int64 `json:"tgId" form:"tgId"` + SubID string `json:"subId" form:"subId"` + Comment string `json:"comment" form:"comment"` + Reset int `json:"reset" form:"reset"` + CreatedAt int64 `json:"created_at,omitempty"` + UpdatedAt int64 `json:"updated_at,omitempty"` } type VLESSSettings struct { diff --git a/web/assets/js/model/dbinbound.js b/web/assets/js/model/dbinbound.js index 1added25..d7c0946b 100644 --- a/web/assets/js/model/dbinbound.js +++ b/web/assets/js/model/dbinbound.js @@ -10,6 +10,7 @@ class DBInbound { this.remark = ""; this.enable = true; this.expiryTime = 0; + this.periodicTrafficReset = "never"; this.listen = ""; this.port = 0; diff --git a/web/html/form/inbound.html b/web/html/form/inbound.html index 69f5fbb3..b173c980 100644 --- a/web/html/form/inbound.html +++ b/web/html/form/inbound.html @@ -44,6 +44,24 @@ + + + + {{ i18n "pages.inbounds.periodicTrafficReset.never" }} + {{ i18n "pages.inbounds.periodicTrafficReset.daily" }} + {{ i18n "pages.inbounds.periodicTrafficReset.weekly" }} + {{ i18n "pages.inbounds.periodicTrafficReset.monthly" }} + + + @@ -1093,6 +1099,7 @@ remark: dbInbound.remark + " - Cloned", enable: dbInbound.enable, expiryTime: dbInbound.expiryTime, + periodicTrafficReset: dbInbound.periodicTrafficReset, listen: '', port: RandomUtil.randomInteger(10000, 60000), @@ -1137,6 +1144,7 @@ remark: dbInbound.remark, enable: dbInbound.enable, expiryTime: dbInbound.expiryTime, + periodicTrafficReset: dbInbound.periodicTrafficReset, listen: inbound.listen, port: inbound.port, @@ -1160,6 +1168,7 @@ remark: dbInbound.remark, enable: dbInbound.enable, expiryTime: dbInbound.expiryTime, + periodicTrafficReset: dbInbound.periodicTrafficReset, listen: inbound.listen, port: inbound.port, diff --git a/web/html/modals/inbound_info_modal.html b/web/html/modals/inbound_info_modal.html index 7b7b0af7..d2a3a93f 100644 --- a/web/html/modals/inbound_info_modal.html +++ b/web/html/modals/inbound_info_modal.html @@ -237,6 +237,12 @@ + + {{ i18n "pages.inbounds.periodicTrafficResetTitle" }} + + [[ i18n("pages.inbounds.periodicTrafficReset." + infoModal.clientSettings.periodicTrafficReset) ]] + + {{ i18n "pages.inbounds.IPLimit" }} diff --git a/web/job/periodic_client_traffic_reset_job.go b/web/job/periodic_client_traffic_reset_job.go new file mode 100644 index 00000000..8f02f0e2 --- /dev/null +++ b/web/job/periodic_client_traffic_reset_job.go @@ -0,0 +1,42 @@ +package job + +import ( + "x-ui/logger" + "x-ui/web/service" +) + +type PeriodicClientTrafficResetJob struct { + inboundService service.InboundService + period Period +} + +func NewPeriodicClientTrafficResetJob(period Period) *PeriodicClientTrafficResetJob { + return &PeriodicClientTrafficResetJob{ + period: period, + } +} + +func (j *PeriodicClientTrafficResetJob) Run() { + clients, err := j.inboundService.GetClientsByTrafficReset(string(j.period)) + logger.Infof("Running periodic client traffic reset job for period: %s", j.period) + if err != nil { + logger.Warning("Failed to get clients for traffic reset:", err) + return + } + + resetCount := 0 + + for _, client := range clients { + if err := j.inboundService.ResetClientTrafficByEmail(client.Email); err != nil { + logger.Warning("Failed to reset traffic for client", client.Email, ":", err) + continue + } + + resetCount++ + logger.Infof("Reset traffic for client %s", client.Email) + } + + if resetCount > 0 { + logger.Infof("Periodic client traffic reset completed: %d clients reseted", resetCount) + } +} diff --git a/web/job/periodic_traffic_reset_job.go b/web/job/periodic_traffic_reset_job.go new file mode 100644 index 00000000..5d3cd178 --- /dev/null +++ b/web/job/periodic_traffic_reset_job.go @@ -0,0 +1,44 @@ +package job + +import ( + "x-ui/logger" + "x-ui/web/service" +) + +type Period string + +type PeriodicTrafficResetJob struct { + inboundService service.InboundService + period Period +} + +func NewPeriodicTrafficResetJob(period Period) *PeriodicTrafficResetJob { + return &PeriodicTrafficResetJob{ + period: period, + } +} + +func (j *PeriodicTrafficResetJob) Run() { + inbounds, err := j.inboundService.GetInboundsByTrafficReset(string(j.period)) + logger.Infof("Running periodic traffic reset job for period: %s", j.period) + if err != nil { + logger.Warning("Failed to get inbounds for traffic reset:", err) + return + } + + resetCount := 0 + + for _, inbound := range inbounds { + if err := j.inboundService.ResetAllClientTraffics(inbound.Id); err != nil { + logger.Warning("Failed to reset traffic for inbound", inbound.Id, ":", err) + continue + } + + resetCount++ + logger.Infof("Reset traffic for inbound %d (%s)", inbound.Id, inbound.Remark) + } + + if resetCount > 0 { + logger.Infof("Periodic traffic reset completed: %d inbounds reseted", resetCount) + } +} diff --git a/web/service/inbound.go b/web/service/inbound.go index 2646b1e7..13ac4cc0 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -41,6 +41,46 @@ func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error) { return inbounds, nil } +func (s *InboundService) GetInboundsByTrafficReset(period string) ([]*model.Inbound, error) { + db := database.GetDB() + var inbounds []*model.Inbound + err := db.Model(model.Inbound{}).Where("traffic_reset = ?", period).Find(&inbounds).Error + if err != nil && err != gorm.ErrRecordNotFound { + return nil, err + } + return inbounds, nil +} + +func (s *InboundService) GetClientsByTrafficReset(period string) ([]model.Client, error) { + db := database.GetDB() + var inbounds []*model.Inbound + + // Get all inbounds first + err := db.Model(model.Inbound{}).Find(&inbounds).Error + if err != nil { + return nil, err + } + + var clientsWithReset []model.Client + + // Parse each inbound's settings to find clients with matching traffic reset period + for _, inbound := range inbounds { + clients, err := s.GetClients(inbound) + if err != nil { + logger.Warning("Failed to get clients for inbound", inbound.Id, ":", err) + continue + } + + for _, client := range clients { + if client.TrafficReset == period { + clientsWithReset = append(clientsWithReset, client) + } + } + } + + return clientsWithReset, nil +} + func (s *InboundService) checkPortExist(listen string, port int, ignoreId int) (bool, error) { db := database.GetDB() if listen == "" || listen == "0.0.0.0" || listen == "::" || listen == "::0" { @@ -409,6 +449,7 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, oldInbound.Remark = inbound.Remark oldInbound.Enable = inbound.Enable oldInbound.ExpiryTime = inbound.ExpiryTime + oldInbound.TrafficReset = inbound.TrafficReset oldInbound.Listen = inbound.Listen oldInbound.Port = inbound.Port oldInbound.Protocol = inbound.Protocol @@ -698,6 +739,7 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool, } func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId string) (bool, error) { + // TODO: check if TrafficReset field are updating clients, err := s.GetClients(data) if err != nil { return false, err @@ -1684,6 +1726,7 @@ func (s *InboundService) ResetClientTrafficLimitByEmail(clientEmail string, tota func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error { db := database.GetDB() + // Reset traffic stats in ClientTraffic table result := db.Model(xray.ClientTraffic{}). Where("email = ?", clientEmail). Updates(map[string]any{"enable": true, "up": 0, "down": 0}) @@ -1692,6 +1735,48 @@ func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error { if err != nil { return err } + + // Update lastTrafficResetTime in client settings + _, inbound, err := s.GetClientInboundByEmail(clientEmail) + if err != nil { + logger.Warning("Failed to get inbound for client", clientEmail, ":", err) + return nil // Don't fail the reset if we can't update the timestamp + } + + if inbound != nil { + var settings map[string]any + err = json.Unmarshal([]byte(inbound.Settings), &settings) + if err != nil { + logger.Warning("Failed to parse inbound settings:", err) + return nil + } + + clientsSettings := settings["clients"].([]any) + now := time.Now().Unix() * 1000 + + for client_index := range clientsSettings { + c := clientsSettings[client_index].(map[string]any) + if c["email"] == clientEmail { + c["lastTrafficResetTime"] = now + c["updated_at"] = now + break + } + } + + settings["clients"] = clientsSettings + modifiedSettings, err := json.MarshalIndent(settings, "", " ") + if err != nil { + logger.Warning("Failed to marshal inbound settings:", err) + return nil + } + + inbound.Settings = string(modifiedSettings) + err = db.Save(inbound).Error + if err != nil { + logger.Warning("Failed to save inbound with updated client settings:", err) + } + } + return nil } diff --git a/web/translation/translate.ar_EG.toml b/web/translation/translate.ar_EG.toml index 9f11a30c..488da92c 100644 --- a/web/translation/translate.ar_EG.toml +++ b/web/translation/translate.ar_EG.toml @@ -248,6 +248,14 @@ "days" = "يوم/أيام" "renew" = "تجديد تلقائي" "renewDesc" = "تجديد تلقائي بعد انتهاء الصلاحية. (0 = تعطيل)(الوحدة: يوم)" +"periodicTrafficResetTitle" = "إعادة تعيين حركة المرور" +"periodicTrafficResetDesc" = "إعادة تعيين عداد حركة المرور تلقائيًا في فترات محددة" + +[pages.inbounds.periodicTrafficReset] +"never" = "أبداً" +"daily" = "يومياً" +"weekly" = "أسبوعياً" +"monthly" = "شهرياً" [pages.inbounds.toasts] "obtain" = "تم الحصول عليه" diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index f802dd6d..f7791860 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -230,6 +230,8 @@ "exportInbound" = "Export Inbound" "import" = "Import" "importInbound" = "Import an Inbound" +"periodicTrafficResetTitle" = "Traffic Reset" +"periodicTrafficResetDesc" = "Automatically reset traffic counter at specified intervals" [pages.client] "add" = "Add Client" @@ -249,6 +251,12 @@ "renew" = "Auto Renew" "renewDesc" = "Auto-renewal after expiration. (0 = disable)(unit: day)" +[pages.inbounds.periodicTrafficReset] +"never" = "Never" +"daily" = "Daily" +"weekly" = "Weekly" +"monthly" = "Monthly" + [pages.inbounds.toasts] "obtain" = "Obtain" "updateSuccess" = "The update was successful." diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index 80ddd98a..813597e4 100644 --- a/web/translation/translate.es_ES.toml +++ b/web/translation/translate.es_ES.toml @@ -248,6 +248,14 @@ "days" = "Día(s)" "renew" = "Renovación automática" "renewDesc" = "Renovación automática después de la expiración. (0 = desactivar) (unidad: día)" +"periodicTrafficResetTitle" = "Reset de Tráfico" +"periodicTrafficResetDesc" = "Reiniciar automáticamente el contador de tráfico en intervalos especificados" + +[pages.inbounds.periodicTrafficReset] +"never" = "Nunca" +"daily" = "Diariamente" +"weekly" = "Semanalmente" +"monthly" = "Mensualmente" [pages.inbounds.toasts] "obtain" = "Recibir" diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index 778dd528..b77e753c 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -247,7 +247,15 @@ "expireDays" = "مدت زمان" "days" = "(روز)" "renew" = "تمدید خودکار" -"renewDesc" = "(تمدید خودکار پس‌از ‌انقضا. (0 = غیرفعال)(واحد: روز" +"renewDesc" = "تمدید خودکار پس‌از ‌انقضا. (0 = غیرفعال)(واحد: روز)" +"periodicTrafficResetTitle" = "بازنشانی ترافیک" +"periodicTrafficResetDesc" = "بازنشانی خودکار شمارنده ترافیک در فواصل زمانی مشخص" + +[pages.inbounds.periodicTrafficReset] +"never" = "هرگز" +"daily" = "روزانه" +"weekly" = "هفتگی" +"monthly" = "ماهانه" [pages.inbounds.toasts] "obtain" = "فراهم‌سازی" diff --git a/web/translation/translate.id_ID.toml b/web/translation/translate.id_ID.toml index 2a5b568b..441debd5 100644 --- a/web/translation/translate.id_ID.toml +++ b/web/translation/translate.id_ID.toml @@ -248,6 +248,14 @@ "days" = "Hari" "renew" = "Perpanjang Otomatis" "renewDesc" = "Perpanjangan otomatis setelah kedaluwarsa. (0 = nonaktif)(unit: hari)" +"periodicTrafficResetTitle" = "Reset Trafik Berkala" +"periodicTrafficResetDesc" = "Reset otomatis penghitung trafik pada interval tertentu" + +[pages.inbounds.periodicTrafficReset] +"never" = "Tidak Pernah" +"daily" = "Harian" +"weekly" = "Mingguan" +"monthly" = "Bulanan" [pages.inbounds.toasts] "obtain" = "Dapatkan" diff --git a/web/translation/translate.ja_JP.toml b/web/translation/translate.ja_JP.toml index 7af0a339..e9d75bde 100644 --- a/web/translation/translate.ja_JP.toml +++ b/web/translation/translate.ja_JP.toml @@ -248,6 +248,14 @@ "days" = "日" "renew" = "自動更新" "renewDesc" = "期限が切れた後に自動更新。(0 = 無効)(単位:日)" +"periodicTrafficResetTitle" = "トラフィックリセット" +"periodicTrafficResetDesc" = "指定された間隔でトラフィックカウンタを自動的にリセット" + +[pages.inbounds.periodicTrafficReset] +"never" = "なし" +"daily" = "毎日" +"weekly" = "毎週" +"monthly" = "毎月" [pages.inbounds.toasts] "obtain" = "取得" diff --git a/web/translation/translate.pt_BR.toml b/web/translation/translate.pt_BR.toml index f61f8995..9cc16826 100644 --- a/web/translation/translate.pt_BR.toml +++ b/web/translation/translate.pt_BR.toml @@ -248,6 +248,14 @@ "days" = "Dia(s)" "renew" = "Renovação Automática" "renewDesc" = "Renovação automática após expiração. (0 = desativado)(unidade: dia)" +"periodicTrafficResetTitle" = "Reset de Tráfego" +"periodicTrafficResetDesc" = "Reinicia automaticamente o contador de tráfego em intervalos especificados" + +[pages.inbounds.periodicTrafficReset] +"never" = "Nunca" +"daily" = "Diariamente" +"weekly" = "Semanalmente" +"monthly" = "Mensalmente" [pages.inbounds.toasts] "obtain" = "Obter" diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index 060b7334..bbaaaf3a 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -248,6 +248,14 @@ "days" = "дней" "renew" = "Автопродление" "renewDesc" = "Автопродление после истечения срока действия. (0 = отключить)(единица: день)" +"periodicTrafficResetTitle" = "Сброс трафика" +"periodicTrafficResetDesc" = "Автоматический сброс счетчика трафика через указанные интервалы" + +[pages.inbounds.periodicTrafficReset] +"never" = "Никогда" +"daily" = "Ежедневно" +"weekly" = "Еженедельно" +"monthly" = "Ежемесячно" [pages.inbounds.toasts] "obtain" = "Получить" diff --git a/web/translation/translate.tr_TR.toml b/web/translation/translate.tr_TR.toml index ac10bf65..286ea1c2 100644 --- a/web/translation/translate.tr_TR.toml +++ b/web/translation/translate.tr_TR.toml @@ -248,6 +248,14 @@ "days" = "Gün" "renew" = "Otomatik Yenile" "renewDesc" = "Süresi dolduktan sonra otomatik yenileme. (0 = devre dışı)(birim: gün)" +"periodicTrafficResetTitle" = "Trafik Sıfırlama" +"periodicTrafficResetDesc" = "Belirtilen aralıklarla trafik sayacını otomatik olarak sıfırla" + +[pages.inbounds.periodicTrafficReset] +"never" = "Asla" +"daily" = "Günlük" +"weekly" = "Haftalık" +"monthly" = "Aylık" [pages.inbounds.toasts] "obtain" = "Elde Et" diff --git a/web/translation/translate.uk_UA.toml b/web/translation/translate.uk_UA.toml index 4005f14f..06634ef4 100644 --- a/web/translation/translate.uk_UA.toml +++ b/web/translation/translate.uk_UA.toml @@ -248,6 +248,14 @@ "days" = "Дні(в)" "renew" = "Автоматичне оновлення" "renewDesc" = "Автоматичне поновлення після закінчення терміну дії. (0 = вимкнено)(одиниця: день)" +"periodicTrafficResetTitle" = "Скидання трафіку" +"periodicTrafficResetDesc" = "Автоматично скидати лічильник трафіку через певні проміжки часу" + +[pages.inbounds.periodicTrafficReset] +"never" = "Ніколи" +"daily" = "Щодня" +"weekly" = "Щотижня" +"monthly" = "Щомісяця" [pages.inbounds.toasts] "obtain" = "Отримати" diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml index 66ed38ce..b601b263 100644 --- a/web/translation/translate.vi_VN.toml +++ b/web/translation/translate.vi_VN.toml @@ -248,6 +248,14 @@ "days" = "ngày" "renew" = "Tự động gia hạn" "renewDesc" = "Tự động gia hạn sau khi hết hạn. (0 = tắt)(đơn vị: ngày)" +"periodicTrafficResetTitle" = "Đặt lại lưu lượng" +"periodicTrafficResetDesc" = "Tự động đặt lại bộ đếm lưu lượng theo khoảng thời gian xác định" + +[pages.inbounds.periodicTrafficReset] +"never" = "Không bao giờ" +"daily" = "Hàng ngày" +"weekly" = "Hàng tuần" +"monthly" = "Hàng tháng" [pages.inbounds.toasts] "obtain" = "Nhận" diff --git a/web/translation/translate.zh_CN.toml b/web/translation/translate.zh_CN.toml index 5875add9..e0f2c222 100644 --- a/web/translation/translate.zh_CN.toml +++ b/web/translation/translate.zh_CN.toml @@ -248,6 +248,14 @@ "days" = "天" "renew" = "自动续订" "renewDesc" = "到期后自动续订。(0 = 禁用)(单位: 天)" +"periodicTrafficResetTitle" = "流量重置" +"periodicTrafficResetDesc" = "按指定间隔自动重置流量计数器" + +[pages.inbounds.periodicTrafficReset] +"never" = "从不" +"daily" = "每日" +"weekly" = "每周" +"monthly" = "每月" [pages.inbounds.toasts] "obtain" = "获取" diff --git a/web/translation/translate.zh_TW.toml b/web/translation/translate.zh_TW.toml index 6843e9ee..66b2bd33 100644 --- a/web/translation/translate.zh_TW.toml +++ b/web/translation/translate.zh_TW.toml @@ -248,6 +248,14 @@ "days" = "天" "renew" = "自動續訂" "renewDesc" = "到期後自動續訂。(0 = 禁用)(單位: 天)" +"periodicTrafficResetTitle" = "流量重置" +"periodicTrafficResetDesc" = "按指定間隔自動重置流量計數器" + +[pages.inbounds.periodicTrafficReset] +"never" = "從不" +"daily" = "每日" +"weekly" = "每週" +"monthly" = "每月" [pages.inbounds.toasts] "obtain" = "獲取" diff --git a/web/web.go b/web/web.go index 38eb24d6..81b62952 100644 --- a/web/web.go +++ b/web/web.go @@ -266,6 +266,31 @@ func (s *Server) startTask() { // check client ips from log file every day s.cron.AddJob("@daily", job.NewClearLogsJob()) + // Periodic traffic resets + logger.Info("Scheduling periodic traffic reset jobs") + { + // Inbound traffic reset jobs + // Run once a day, midnight + // TODO: for testing, run every minute, change back to daily later + // s.cron.AddJob("@daily", job.NewPeriodicTrafficResetJob("daily")) + s.cron.AddJob("* * * * *", job.NewPeriodicTrafficResetJob("daily")) + // Run once a week, midnight between Sat/Sun + s.cron.AddJob("@weekly", job.NewPeriodicTrafficResetJob("weekly")) + // Run once a month, midnight, first of month + s.cron.AddJob("@monthly", job.NewPeriodicTrafficResetJob("monthly")) + + // Client traffic reset jobs + logger.Info("Scheduling periodic client traffic reset jobs") + // Run once a day, midnight + // TODO: for testing, run every minute, change back to daily later + // s.cron.AddJob("@daily", job.NewPeriodicClientTrafficResetJob("daily")) + s.cron.AddJob("* * * * *", job.NewPeriodicClientTrafficResetJob("daily")) + // Run once a week, midnight between Sat/Sun + s.cron.AddJob("@weekly", job.NewPeriodicClientTrafficResetJob("weekly")) + // Run once a month, midnight, first of month + s.cron.AddJob("@monthly", job.NewPeriodicClientTrafficResetJob("monthly")) + } + // Make a traffic condition every day, 8:30 var entry cron.EntryID isTgbotenabled, err := s.settingService.GetTgbotEnabled()