fix(auth): 公开登录门按来源限速并设全站发信上限,冷却表定期清理

This commit is contained in:
Lemon-miaow committed 2026-09-24 15:51:42 +08:00
1 parent 15f729ffea
commit c4e4953f3d
26 files changed
+1141 -64

No files matched your search

+38
View File
@@ -64,8 +64,44 @@ var (
Name: "auth_otp_lockouts_total",
Help: "Email-code doors locked after too many wrong codes, by purpose.",
}, []string{"purpose"})
// MailTotal counts mail the API tried to send, by kind (otp, notice) and
// result: sent, failed (the relay refused it) or throttled (the
// install-wide mail budget refused it before it reached the relay).
MailTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Name: "mail_total",
Help: "Mail the API tried to send, by kind and result (sent, failed, throttled).",
}, []string{"kind", "result"})
// RateLimitedTotal counts requests refused by a volumetric limit, by scope
// (auth_door: one client address calling the public sign-in doors too fast).
RateLimitedTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: namespace,
Name: "rate_limited_total",
Help: "Requests refused by a volumetric rate limit, by scope.",
}, []string{"scope"})
)
// OTPPurposes are the email-code doors OTPLockoutsTotal is labelled by.
var OTPPurposes = []string{"onboard_email", "login_email", "op_login", "migrate_confirm"}
// The sign-in alerts watch these counters with increase(). A labelled child
// that does not exist yet has no sample before its first event, so increase()
// would miss exactly the first lockout or throttle; every child the alerts use
// is created at zero up front.
func init() {
for _, kind := range []string{"otp", "notice"} {
for _, result := range []string{"sent", "failed", "throttled"} {
MailTotal.WithLabelValues(kind, result)
}
}
RateLimitedTotal.WithLabelValues("auth_door")
for _, p := range OTPPurposes {
OTPLockoutsTotal.WithLabelValues(p)
}
}
// SyncServerGauge republishes felis_servers_total from a full snapshot of the
// fleet's per-server states. states holds one entry per MinecraftServer the
// operator knows about (its desiredState).
@@ -97,6 +133,8 @@ func Collectors() []prometheus.Collector {
ImageBuildFailuresTotal,
ReaperWorldsDeletedTotal,
OTPLockoutsTotal,
MailTotal,
RateLimitedTotal,
}
}
+23
View File
@@ -116,3 +116,26 @@ func TestCountersRecordExpectedValues(t *testing.T) {
t.Errorf("start_duration_seconds collected %d metrics, want 1 histogram", n)
}
}
// The sign-in alerts use increase(), which needs a zero sample before the first
// event; every child they watch must be exposed before anything is counted.
func TestSignInSeriesStartAtZero(t *testing.T) {
want := map[string]int{
"felis_mail_total": 6,
"felis_rate_limited_total": 1,
"felis_auth_otp_lockouts_total": len(OTPPurposes),
}
for _, c := range []prometheus.Collector{MailTotal, RateLimitedTotal, OTPLockoutsTotal} {
reg := prometheus.NewRegistry()
reg.MustRegister(c)
mfs, err := reg.Gather()
if err != nil {
t.Fatalf("Gather: %v", err)
}
for _, mf := range mfs {
if n := len(mf.GetMetric()); n < want[mf.GetName()] {
t.Errorf("%s exposes %d children, want at least %d", mf.GetName(), n, want[mf.GetName()])
}
}
}
}