diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/admin.py')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/api/admin.py | 59 |
1 files changed, 55 insertions, 4 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/admin.py b/packages/meshbay-hub/src/meshbay_hub/api/admin.py index 219e8a9..087c221 100644 --- a/packages/meshbay-hub/src/meshbay_hub/api/admin.py +++ b/packages/meshbay-hub/src/meshbay_hub/api/admin.py @@ -38,12 +38,23 @@ class GroupPatchRequest(BaseModel): class SettingsPatchRequest(BaseModel): allow_public_groups: bool | None = None + # Every mail bound, each optional: the panel sends only what changed. + mail: dict[str, int] | None = None # ── Instance settings ──────────────────────────────────────────────────────── -def _settings_payload(allow_public_groups: bool) -> dict: - return {"allow_public_groups": allow_public_groups} +def _settings_payload(allow_public_groups: bool, mail: dict) -> dict: + return { + "allow_public_groups": allow_public_groups, + "mail": mail, + # So the panel can show what a field falls back to, and label the + # bounds it will refuse — rather than the operator finding out by + # having a value silently clamped. + "mail_defaults": {k: hub_settings.mail_default(k) + for k in hub_settings.MAIL_KEYS}, + "mail_bounds": {k: list(v) for k, v in hub_settings.MAIL_BOUNDS.items()}, + } @router.get("/settings") @@ -52,7 +63,9 @@ async def admin_get_settings( db: AsyncSession = Depends(get_db), ): """Instance-wide policy an admin controls from the panel. Moderators may read.""" - return _settings_payload(await hub_settings.public_groups_allowed(db)) + return _settings_payload( + await hub_settings.public_groups_allowed(db), + await hub_settings.mail_limits(db)) @router.patch("/settings") @@ -82,7 +95,45 @@ async def admin_patch_settings( )) await db.commit() - return _settings_payload(await hub_settings.public_groups_allowed(db)) + if body.mail: + unknown = sorted(set(body.mail) - set(hub_settings.MAIL_KEYS)) + if unknown: + raise HTTPException( + status_code=422, detail=f"Unknown mail setting(s): {unknown}") + changed = [] + for key, value in body.mail.items(): + clamped = hub_settings.clamp_mail_value(key, value) + await hub_settings.set_raw(db, f"mail.{key}", str(clamped)) + changed.append(f"{key}={clamped}") + log.info("Mail bounds changed by %s: %s", + current_user.username, ", ".join(changed)) + db.add(IPLog( + user_id=current_user.id, + event="admin_mail_limits_update", + ip_address="admin", + detail=", ".join(changed)[:255], + )) + await db.commit() + + return _settings_payload( + await hub_settings.public_groups_allowed(db), + await hub_settings.mail_limits(db)) + + +@router.get("/mail") +async def admin_mail_status( + current_user: User = Depends(require_moderator), + db: AsyncSession = Depends(get_db), +): + """Is the hub still sending, and how much of the hour is left. + + There was no way to see this at all: a refusal was a line in the journal, + so an instance that had stopped sending registration codes looked, from the + panel, exactly like one that had no sign-ups. + """ + from meshbay_hub import mail + + return await mail.status(db) # ── Stats ──────────────────────────────────────────────────────────────────── |