fix(server/nodes): 连接设备上限闸读 max_devices_override(登录闸已读,连接闸漏)
EntitlementForUser 是每次连接 backstop(httpapi/nodes.go DEVICE_LIMIT_EXCEEDED) 唯一读的设备上限来源,但订阅命中分支与 free 兜底分支都从未读 users.max_devices_override (migration 000025)——运营设 override 只对登录闸(devices.ResolvePlan)生效,一连 VPN 仍按套餐默认上限被拒。两分支统一叠加 override(NULL/<=0 不生效,正值覆盖,语义与 devices/store.go:GetMaxDevicesOverride 一致),新增 sqlite 用例覆盖 free 无覆盖/ free 覆盖/pro 覆盖收紧/NULL-0 不生效四种场景。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -227,18 +227,37 @@ func (s *SQLNodeStore) EntitlementForUser(ctx context.Context, userID int64) (*E
|
||||
err := s.db.QueryRowContext(ctx, q, userID, time.Now().UTC()).Scan(
|
||||
&e.PlanCode, &e.AdGate, &e.DailyMinutes, &e.DailyMB, &e.MaxDevices, &e.ExpiresAt,
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
switch {
|
||||
case err == sql.ErrNoRows:
|
||||
// No active subscription → free plan defaults (mirrors the free plan seed).
|
||||
e.PlanCode = "free"
|
||||
e.AdGate = true
|
||||
e.DailyMinutes = sql.NullInt64{Valid: true, Int64: 10}
|
||||
e.DailyMB = sql.NullInt64{Valid: true, Int64: 500}
|
||||
e.MaxDevices = 1
|
||||
return e, nil
|
||||
}
|
||||
if err != nil {
|
||||
case err != nil:
|
||||
return nil, fmt.Errorf("nodes.SQLNodeStore.EntitlementForUser: plan: %w", err)
|
||||
}
|
||||
|
||||
// Per-user device-cap override (migration 000025, users.max_devices_override):
|
||||
// applies uniformly to BOTH branches above (subscription hit + free
|
||||
// fallback) — mirrors internal/devices/service.go's ResolvePlan funnel, so
|
||||
// the per-connection backstop (internal/httpapi/nodes.go's
|
||||
// DEVICE_LIMIT_EXCEEDED check, which reads Entitlement.MaxDevices) agrees
|
||||
// with the login gate / /v1/me / devices.CheckDeviceLimit. NULL or <=0
|
||||
// means "no override" (same semantics as devices/store.go's
|
||||
// GetMaxDevicesOverride); a positive value always wins, even if smaller
|
||||
// than the plan's own cap.
|
||||
var override sql.NullInt64
|
||||
if err := s.db.QueryRowContext(ctx,
|
||||
`SELECT max_devices_override FROM users WHERE id = ?`, userID,
|
||||
).Scan(&override); err != nil && err != sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("nodes.SQLNodeStore.EntitlementForUser: override: %w", err)
|
||||
}
|
||||
if override.Valid && override.Int64 > 0 {
|
||||
e.MaxDevices = int(override.Int64)
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user