diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-12 14:21:12 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-12 16:36:54 +0200 |
| commit | 2114a54eb6335f97b0c276c4f1f224d45f46fd1a (patch) | |
| tree | dbb2aa53f1a82d652f7aeb03e29dee80b6e8e0a5 /packages/meshbay-hub/src/meshbay_hub/static/admin-page.js | |
| parent | ef4842644a207c3d1b6d6f06c1ad1055270ae283 (diff) | |
| download | meshbay-2114a54eb6335f97b0c276c4f1f224d45f46fd1a.tar.gz | |
feat(hub): the mail state is a panel section, and a ceiling falling is an event
The figure was a line beside the settings form, which is where it is changed
and not where it is watched. It sits with the other live figures under
Statistics now — four cards and, above them, a banner saying which of the two
ceilings has fallen. The two states are not the same to whoever is reading:
one means newcomers are turned away, the other means somebody locked out of
their account cannot get back in. The settings block keeps a line pointing at
it.
And an operator no longer has to be looking. When a global ceiling is reached
the administrators are notified — in `mail.py`, in its own session, never
raising, because this runs while a request is being refused and an alert that
fails must not turn a refusal into a 500. Once per hour, keyed on a row
rather than a flag in memory: a flood is what spends the budget, so one alert
per refusal would bury the message under its own cause, and a hub that is
refusing mail is a hub somebody is about to restart.
`/v1/admin/mail` gains `general_exhausted` and `all_exhausted` rather than
leaving the panel to compare two numbers.
Labels in all ten catalogues; `.warn-msg` for the middle state, on the
`--warn` token both themes already define.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4YmK41VsEURWFdop4EEeT
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/admin-page.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/admin-page.js | 50 |
1 files changed, 37 insertions, 13 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/admin-page.js b/packages/meshbay-hub/src/meshbay_hub/static/admin-page.js index 3cc42db..8800615 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/admin-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/admin-page.js @@ -105,7 +105,11 @@ export function AdminPage({ token, role }) { useEffect(() => { setError(''); if (tab === 'general') loadSettings(); - else if (tab === 'stats') loadStats(); + else if (tab === 'stats') { + loadStats(); + hubFetch('/v1/admin/mail', { token }) + .then(setMailStatus).catch(() => { /* the cards still render */ }); + } else if (tab === 'users') loadUsers(userSearch); else if (tab === 'groups') loadGroups(); else if (tab === 'nodes') { @@ -225,18 +229,7 @@ const TABS = ['general', 'stats', 'users', 'groups', 'nodes', 'logs', 'blocklist <h3 class="settings-heading">${t('admin.mail_heading')}</h3> <p class="settings-hint">${t('admin.mail_hint')}</p> - ${mailStatus && html` - <div class="settings-row"> - <span class="settings-label">${t('admin.mail_this_hour')}</span> - <span>${mailStatus.hourly_used} / ${mailStatus.hourly_budget}</span> - </div> - <p class="settings-hint"> - ${t('admin.mail_remaining', { - general: mailStatus.general_remaining, - recovery: mailStatus.recovery_remaining, - })} - </p> - `} + <p class="settings-hint">${t('admin.mail_state_is_in_stats')}</p> ${mailDraft && MAIL_FIELDS.map(key => html` <div class="settings-row" key=${key}> @@ -279,6 +272,37 @@ const TABS = ['general', 'stats', 'users', 'groups', 'nodes', 'logs', 'blocklist </div> `)} </div> + + ${mailStatus && html` + <div class="settings-section"> + <h3 class="settings-heading">${t('admin.mail_heading')}</h3> + + ${mailStatus.all_exhausted && html` + <div class="error-msg">${t('admin.mail_alert_all')}</div>`} + ${!mailStatus.all_exhausted && mailStatus.general_exhausted && html` + <div class="warn-msg">${t('admin.mail_alert_general')}</div>`} + + <div class="admin-stats"> + <div class="stat-card"> + <div class="stat-value">${mailStatus.hourly_used} / ${mailStatus.hourly_budget}</div> + <div class="stat-label">${t('admin.mail_this_hour')}</div> + </div> + <div class="stat-card"> + <div class="stat-value">${mailStatus.general_remaining}</div> + <div class="stat-label">${t('admin.mail_left_signups')}</div> + </div> + <div class="stat-card"> + <div class="stat-value">${mailStatus.recovery_remaining}</div> + <div class="stat-label">${t('admin.mail_left_recovery')}</div> + </div> + <div class="stat-card"> + <div class="stat-value">${mailStatus.recipients_tracked}</div> + <div class="stat-label">${t('admin.mail_recipients')}</div> + </div> + </div> + <p class="settings-hint">${t('admin.mail_state_hint')}</p> + </div> + `} `} ${tab === 'users' && html` |