|
|
@@ -24,6 +24,7 @@ import (
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/mtproto"
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/tuic"
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/util/common"
|
|
|
+ "github.com/mhsanaei/3x-ui/v3/internal/util/cronspec"
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/util/sys"
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/controller"
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/job"
|
|
|
@@ -385,13 +386,9 @@ func (s *Server) startTask(restartXray bool, loc *time.Location) {
|
|
|
|
|
|
// LDAP sync scheduling
|
|
|
if ldapEnabled, _ := s.settingService.GetLdapEnable(); ldapEnabled {
|
|
|
- runtime, err := s.settingService.GetLdapSyncCron()
|
|
|
- if err != nil || runtime == "" {
|
|
|
- runtime = "@every 1m"
|
|
|
- }
|
|
|
- j := job.NewLdapSyncJob()
|
|
|
+ runtime, _ := s.settingService.GetLdapSyncCron()
|
|
|
// job has zero-value services with method receivers that read settings on demand
|
|
|
- _, _ = s.cron.AddJob(runtime, j)
|
|
|
+ scheduleSettingJob(s.cron, "LDAP sync", runtime, "@every 1m", job.NewLdapSyncJob())
|
|
|
}
|
|
|
|
|
|
// Telegram-bot–dependent jobs: periodic stats report + callback-hash cleanup.
|
|
|
@@ -399,16 +396,9 @@ func (s *Server) startTask(restartXray bool, loc *time.Location) {
|
|
|
if (err == nil) && isTgbotenabled {
|
|
|
runtime, err := s.settingService.GetTgbotRuntime()
|
|
|
if err != nil {
|
|
|
- logger.Warningf("Add NewStatsNotifyJob: failed to load runtime: %v; using default @daily", err)
|
|
|
- runtime = "@daily"
|
|
|
- } else if strings.TrimSpace(runtime) == "" {
|
|
|
- logger.Warning("Add NewStatsNotifyJob runtime is empty, using default @daily")
|
|
|
- runtime = "@daily"
|
|
|
- }
|
|
|
- logger.Infof("Tg notify enabled,run at %s", runtime)
|
|
|
- if _, err = s.cron.AddJob(runtime, job.NewStatsNotifyJob()); err != nil {
|
|
|
- logger.Warningf("Add NewStatsNotifyJob: failed to schedule runtime %q: %v", runtime, err)
|
|
|
+ logger.Warningf("Telegram notify: failed to load runtime: %v", err)
|
|
|
}
|
|
|
+ scheduleSettingJob(s.cron, "Telegram notify", runtime, "@daily", job.NewStatsNotifyJob())
|
|
|
|
|
|
// check for Telegram bot callback query hash storage reset
|
|
|
_, _ = s.cron.AddJob(cadenceCheckHash, job.NewCheckHashStorageJob())
|
|
|
@@ -419,18 +409,9 @@ func (s *Server) startTask(restartXray bool, loc *time.Location) {
|
|
|
if (err == nil) && isDiscordEnabled {
|
|
|
runtime, err := s.settingService.GetDiscordRunTime()
|
|
|
if err != nil {
|
|
|
- logger.Warningf("Add NewDiscordNotifyJob: failed to load runtime: %v; using default @daily", err)
|
|
|
- runtime = "@daily"
|
|
|
- } else if strings.TrimSpace(runtime) == "" {
|
|
|
- logger.Warning("Add NewDiscordNotifyJob runtime is empty, using default @daily")
|
|
|
- runtime = "@daily"
|
|
|
- }
|
|
|
- logger.Infof("Discord notify enabled, run at %s", runtime)
|
|
|
- if entryID, err := s.cron.AddJob(runtime, job.NewDiscordNotifyJob(s.discordService)); err != nil {
|
|
|
- logger.Warningf("Add NewDiscordNotifyJob: failed to schedule runtime %q: %v", runtime, err)
|
|
|
- } else {
|
|
|
- s.discordNotifyEntryID = entryID
|
|
|
+ logger.Warningf("Discord notify: failed to load runtime: %v", err)
|
|
|
}
|
|
|
+ s.discordNotifyEntryID = scheduleSettingJob(s.cron, "Discord notify", runtime, "@daily", job.NewDiscordNotifyJob(s.discordService))
|
|
|
}
|
|
|
|
|
|
// CPU monitor publishes cpu.high events; register it whenever any notifier
|
|
|
@@ -710,16 +691,10 @@ func (s *Server) start(restartXray bool, startTgBot bool) (err error) {
|
|
|
return
|
|
|
}
|
|
|
runtime, err := s.settingService.GetDiscordRunTime()
|
|
|
- if err != nil || strings.TrimSpace(runtime) == "" {
|
|
|
- runtime = "@daily"
|
|
|
- }
|
|
|
- entryID, err := s.cron.AddJob(runtime, job.NewDiscordNotifyJob(s.discordService))
|
|
|
if err != nil {
|
|
|
- logger.Warningf("Reload Discord notify: failed to schedule runtime %q: %v", runtime, err)
|
|
|
- } else {
|
|
|
- s.discordNotifyEntryID = entryID
|
|
|
- logger.Infof("Discord notify rescheduled, run at %s", runtime)
|
|
|
+ logger.Warningf("Discord notify: failed to load runtime: %v", err)
|
|
|
}
|
|
|
+ s.discordNotifyEntryID = scheduleSettingJob(s.cron, "Discord notify", runtime, "@daily", job.NewDiscordNotifyJob(s.discordService))
|
|
|
|
|
|
if s.discordGateway != nil && !s.discordGateway.IsRunning() {
|
|
|
_ = s.discordGateway.Start(s.ctx)
|
|
|
@@ -859,3 +834,23 @@ func (s *Server) GetWSHub() any {
|
|
|
func (s *Server) RestartXray() error {
|
|
|
return s.xrayService.RestartXray(true)
|
|
|
}
|
|
|
+
|
|
|
+// scheduleSettingJob schedules j on an admin-typed spec, running it on fallback
|
|
|
+// when the spec is empty or a legacy value that no longer parses.
|
|
|
+func scheduleSettingJob(c *cron.Cron, name, spec, fallback string, j cron.Job) cron.EntryID {
|
|
|
+ if strings.TrimSpace(spec) == "" {
|
|
|
+ logger.Warningf("%s: schedule is empty, using default %s", name, fallback)
|
|
|
+ spec = fallback
|
|
|
+ }
|
|
|
+ schedule, err := cronspec.Parse(spec)
|
|
|
+ if err != nil {
|
|
|
+ logger.Warningf("%s: %v; using default %s", name, err, fallback)
|
|
|
+ spec = fallback
|
|
|
+ if schedule, err = cronspec.Parse(fallback); err != nil {
|
|
|
+ logger.Errorf("%s: default schedule %q: %v", name, fallback, err)
|
|
|
+ return 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+ logger.Infof("%s scheduled, run at %s", name, spec)
|
|
|
+ return c.Schedule(schedule, j)
|
|
|
+}
|