mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-14 18:05:04 +03:00
fix(settings): accept five-field crontab for report and LDAP schedules
The panel scheduler runs WithSeconds(), whose parser rejects a standard five-field crontab such as "30 8 * * *". An admin who typed one into the Telegram/Discord notify time or the LDAP sync schedule saved it without error, AddJob then failed at startup, and the job silently never ran. Admin-typed schedules now go through internal/util/cronspec, which takes an optional seconds column plus the @descriptors and hands the scheduler a parsed cron.Schedule. CheckValid rejects an unparseable schedule on save, and a stored legacy value falls back to the job's default instead of dropping the job.
This commit is contained in:
@@ -78,3 +78,28 @@ func TestCheckValidIPOrCIDRLists(t *testing.T) {
|
||||
t.Errorf("trustedProxyCIDRs error = %v, want it to name the trusted-proxy list and the bad entry", err)
|
||||
}
|
||||
}
|
||||
|
||||
// A schedule the scheduler cannot parse used to be saved anyway, and the report
|
||||
// or sync job then silently never ran.
|
||||
func TestCheckValidSchedules(t *testing.T) {
|
||||
set := map[string]func(*AllSetting, string){
|
||||
"tgRunTime": func(s *AllSetting, v string) { s.TgRunTime = v },
|
||||
"discordRunTime": func(s *AllSetting, v string) { s.DiscordRunTime = v },
|
||||
"ldapSyncCron": func(s *AllSetting, v string) { s.LdapSyncCron = v },
|
||||
}
|
||||
for name, apply := range set {
|
||||
for _, v := range []string{"", "@daily", "30 8 * * *", "0 30 8 * * *", "@every 1m"} {
|
||||
s := &AllSetting{WebPort: 2053, SubPort: 2096}
|
||||
apply(s, v)
|
||||
if err := s.CheckValid(); err != nil {
|
||||
t.Errorf("%s=%q: unexpected error %v", name, v, err)
|
||||
}
|
||||
}
|
||||
s := &AllSetting{WebPort: 2053, SubPort: 2096}
|
||||
apply(s, "every morning")
|
||||
err := s.CheckValid()
|
||||
if err == nil || !strings.Contains(err.Error(), name) {
|
||||
t.Errorf("%s=%q: want an error naming %s, got %v", name, "every morning", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/util/common"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/util/cronspec"
|
||||
)
|
||||
|
||||
type Msg struct {
|
||||
@@ -352,6 +353,19 @@ func (s *AllSetting) CheckValid() error {
|
||||
s.SubClashPath += "/"
|
||||
}
|
||||
|
||||
for _, sched := range []struct{ name, spec string }{
|
||||
{"tgRunTime", s.TgRunTime},
|
||||
{"discordRunTime", s.DiscordRunTime},
|
||||
{"ldapSyncCron", s.LdapSyncCron},
|
||||
} {
|
||||
if strings.TrimSpace(sched.spec) == "" {
|
||||
continue
|
||||
}
|
||||
if _, err := cronspec.Parse(sched.spec); err != nil {
|
||||
return common.NewError(sched.name+":", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := checkIPOrCIDRList(s.TrustedProxyCIDRs, "trusted proxy CIDR is not valid:"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user