From 5ec22c667864e44e7236d65cc6bba14dd6e64959 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 8 Apr 2026 11:46:32 +0700 Subject: [PATCH] model: add UserQuota struct for admin-controlled per-user limits Introduce a UserQuota field on the User model to hold admin-controlled limits and flags that the user cannot modify. Only checker-related fields are defined for now (max checks per day, retention days, inactivity pause days, scheduling kill switch); future paid-plan attributes will be added here later. The user-facing API only exposes settings updates and account deletion, so Quota cannot be written through it. Updates go through the existing admin user PUT endpoint, with a new editor card in the admin UI under /users/[uid]. --- model/user.go | 4 + model/user_quota.go | 51 +++++ web-admin/src/routes/users/[uid]/+page.svelte | 4 +- .../routes/users/[uid]/UserQuotaCard.svelte | 198 ++++++++++++++++++ 4 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 model/user_quota.go create mode 100644 web-admin/src/routes/users/[uid]/UserQuotaCard.svelte diff --git a/model/user.go b/model/user.go index aa8c64fe..2c5c8f46 100644 --- a/model/user.go +++ b/model/user.go @@ -42,6 +42,10 @@ type User struct { // Settings holds the settings for an account. Settings UserSettings `json:"settings" binding:"required"` + + // Quota holds admin-controlled limits for the account. It is never + // writable through the user-facing API; only the admin API can update it. + Quota UserQuota `json:"quota"` } func (u *User) GetUserId() Identifier { diff --git a/model/user_quota.go b/model/user_quota.go new file mode 100644 index 00000000..df0f53b1 --- /dev/null +++ b/model/user_quota.go @@ -0,0 +1,51 @@ +// This file is part of the happyDomain (R) project. +// Copyright (c) 2020-2026 happyDomain +// Authors: Pierre-Olivier Mercier, et al. +// +// This program is offered under a commercial and under the AGPL license. +// For commercial licensing, contact us at . +// +// For AGPL licensing: +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package happydns + +import "time" + +// UserQuota holds admin-controlled per-user limits and flags. These fields are +// never modifiable by the user; they can only be updated through the admin API. +// +// Only checker-related fields are defined for now. Future paid-plan attributes +// (plan tier, domain caps, payment metadata, ...) will be added here later. +type UserQuota struct { + // MaxChecksPerDay caps the number of checker executions per day for this + // user. 0 means "use the system default". + MaxChecksPerDay int `json:"max_checks_per_day,omitempty"` + + // RetentionDays is the maximum age (in days) of checker executions kept in + // storage for this user. 0 means "use the system default". + RetentionDays int `json:"retention_days,omitempty"` + + // InactivityPauseDays is the number of days without login after which the + // scheduler stops running checks for this user. 0 means "use the system + // default". A negative value disables the inactivity pause for this user. + InactivityPauseDays int `json:"inactivity_pause_days,omitempty"` + + // SchedulingPaused, when true, completely disables the scheduler for this + // user (admin kill switch). + SchedulingPaused bool `json:"scheduling_paused,omitempty"` + + // UpdatedAt records the last time these quotas were modified. + UpdatedAt time.Time `json:"updated_at,omitzero" format:"date-time"` +} diff --git a/web-admin/src/routes/users/[uid]/+page.svelte b/web-admin/src/routes/users/[uid]/+page.svelte index d88278fd..2fc2ce7c 100644 --- a/web-admin/src/routes/users/[uid]/+page.svelte +++ b/web-admin/src/routes/users/[uid]/+page.svelte @@ -27,6 +27,7 @@ import { getUsersByUid, getUsersByUidDomains, getUsersByUidProviders } from "$lib/api-admin"; import UserInfoCard from "./UserInfoCard.svelte"; + import UserQuotaCard from "./UserQuotaCard.svelte"; import UserDomainsCard from "./domains/UserDomainsCard.svelte"; import UserProvidersCard from "./providers/UserProvidersCard.svelte"; @@ -55,8 +56,9 @@ {@const user = userR.data} {#if user} - + + diff --git a/web-admin/src/routes/users/[uid]/UserQuotaCard.svelte b/web-admin/src/routes/users/[uid]/UserQuotaCard.svelte new file mode 100644 index 00000000..ad58ec79 --- /dev/null +++ b/web-admin/src/routes/users/[uid]/UserQuotaCard.svelte @@ -0,0 +1,198 @@ + + + + + + +
+ + Admin Quota +
+ {#if updatedAt} + + Updated {new Date(updatedAt).toLocaleString()} + + {/if} +
+ +

+ These limits are controlled by administrators and cannot be modified + by the user. A value of 0 means "use the system default". +

+ + {#if errorMessage} + {errorMessage} + {/if} + +
+ + + + Admin kill switch — when enabled, no checks will run for this + user regardless of their plans. + + + + + + + + Maximum age of stored check executions. Older entries are + pruned by the janitor according to the tiered retention policy. + + + + + + + + Daily cap on the number of executions the scheduler may launch + for this user (enforced later). + + + + + + + + The scheduler stops running checks after this many days + without login. Use a negative value to disable. + + + + +
+
+