feat: refresh accounts automatically every five minutes

This commit is contained in:
2026-08-14 15:35:27 +08:00
parent c1985852e1
commit 5f50669b98
11 changed files with 66 additions and 145 deletions
+4 -3
View File
@@ -408,9 +408,9 @@ func (a *App) logout(w http.ResponseWriter, r *http.Request) {
func (a *App) general() GeneralSettings {
g := defaults()
a.store.GetJSON("general", &g)
if g.SyncMinutes < 1 {
g.SyncMinutes = 5
}
// Keep the legacy field in the response for old clients, but automatic
// account synchronization is intentionally fixed at five minutes.
g.SyncMinutes = automaticSyncMinutes
if g.RetentionDays < 1 {
g.RetentionDays = 90
}
@@ -430,6 +430,7 @@ func (a *App) generalAPI(w http.ResponseWriter, r *http.Request) {
jsonOut(w, 400, map[string]string{"error": "无效时区"})
return
}
g.SyncMinutes = automaticSyncMinutes
_ = a.store.SetJSON("general", g)
jsonOut(w, 200, g)
}
+9 -6
View File
@@ -52,6 +52,8 @@ type accountRuntime struct {
stopped bool
}
const automaticSyncInterval = time.Duration(automaticSyncMinutes) * time.Minute
type codexClient interface {
Start(context.Context) error
Initialize(context.Context) error
@@ -267,17 +269,18 @@ func (a *App) syncAll(ctx context.Context) {
}
}
func (a *App) scheduler() {
t := time.NewTicker(time.Minute)
defer t.Stop()
maintenanceTicker := time.NewTicker(time.Minute)
syncTicker := time.NewTicker(automaticSyncInterval)
defer maintenanceTicker.Stop()
defer syncTicker.Stop()
for {
select {
case <-a.ctx.Done():
return
case <-t.C:
case <-syncTicker.C:
a.syncAll(context.Background())
case <-maintenanceTicker.C:
g := a.general()
if time.Now().Unix()%(int64(g.SyncMinutes)*60) < 60 {
a.syncAll(context.Background())
}
_, _ = a.store.Cleanup(g.RetentionDays)
go a.processReminders()
}
+13
View File
@@ -349,6 +349,19 @@ func TestPeakDailyTokensUsesOnlyTheCurrentTokenCycle(t *testing.T) {
}
}
func TestAutomaticSyncIntervalIsFixedAtFiveMinutes(t *testing.T) {
a := newReminderTestApp(t)
if err := a.store.SetJSON("general", GeneralSettings{SyncMinutes: 60, RetentionDays: 90, BeforeMinutes: 30}); err != nil {
t.Fatal(err)
}
if got := a.general().SyncMinutes; got != automaticSyncMinutes {
t.Fatalf("sync minutes = %d; want %d", got, automaticSyncMinutes)
}
if automaticSyncInterval != 5*time.Minute {
t.Fatalf("automatic sync interval = %s; want 5m", automaticSyncInterval)
}
}
func TestFlattenLimitReadsAppServerWindowDuration(t *testing.T) {
var limit rawLimit
if err := json.Unmarshal([]byte(`{
+4 -1
View File
@@ -9,6 +9,9 @@ type GeneralSettings struct {
NotifyBefore bool `json:"notifyBefore"`
NotifyAfter bool `json:"notifyAfter"`
}
const automaticSyncMinutes = 5
type SMTPSettings struct {
Host string `json:"host"`
Port int `json:"port"`
@@ -92,5 +95,5 @@ type Dashboard struct {
}
func defaults() GeneralSettings {
return GeneralSettings{Timezone: "UTC", Theme: "system", SyncMinutes: 5, RetentionDays: 90, BeforeMinutes: 30, NotifyBefore: true, NotifyAfter: true}
return GeneralSettings{Timezone: "UTC", Theme: "system", SyncMinutes: automaticSyncMinutes, RetentionDays: 90, BeforeMinutes: 30, NotifyBefore: true, NotifyAfter: true}
}