feat(panel-go): monthly traffic reset for customers

- trafficResetDay/Period/At on the shared customer record (omitempty,
  inert for the Node panel, which preserves unknown fields)
- housekeeping applies due resets before quota evaluation: period-key
  idempotency, short-month clamping (31st fires Feb 28), missed-period
  catch-up after panel downtime, traffic suspensions auto-resume
- first sight of a schedule only arms the period marker, never zeroes;
  API create/PATCH accept trafficResetDay (0=off) and re-arm on change
- UI: reset-day input + last-reset display in the customer form
- tests: calendar math, apply pass, API lifecycle, persistence;
  full Go/Node suites green, difftest formal 50/50
This commit is contained in:
aaaamoooo
2026-10-20 15:10:13 +00:00
parent f4ef3d96a6
commit 7c21742810
7 changed files with 744 additions and 1 deletions
+9
View File
@@ -194,6 +194,15 @@ func (a *App) housekeeping() {
}
}
}
// Monthly traffic resets run before the quota evaluation below,
// so a customer whose new period just started is judged on the
// zeroed counters (and a reset can lift a traffic suspension in
// the same pass). Due-ness derives from the persisted period
// marker, so this also catches up resets missed while the panel
// was down — at the first pass after it is back (<=60s).
if err := ApplyMonthlyTrafficResets(d, time.Now()); err != nil {
return nil, err
}
for _, customer := range d.Customers {
expired := false
if customer.ExpiresAt != nil {