diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-15 02:16:39 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-15 02:21:01 +0200 |
| commit | 73ad8e4eb566fe682107fa7e50ef624591199e99 (patch) | |
| tree | ff0017d014d46d8835487c080dca55c6def7fd6b /packages/meshbay-hub/src/meshbay_hub/static/admin-page.js | |
| parent | bdefcd025604f2c3009fe5e0cc01213c2ba62a6a (diff) | |
| download | meshbay-73ad8e4eb566fe682107fa7e50ef624591199e99.tar.gz | |
feat(hub): session lifetime is an admin setting, and a browser signs out when idle
Browser idle sign-out (media playback counts as activity; not the desktop app),
refresh idle window and maximum session length, in hours. Sign-out now revokes
on the hub, and the profile has "sign out everywhere".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XuNrwLf5EFWCMHzfoEvnpm
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 | 34 |
1 files changed, 34 insertions, 0 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 c40d240..d5cc7a8 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/admin-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/admin-page.js @@ -17,6 +17,7 @@ export function AdminPage({ token, role }) { // and a rejected one never looks applied. const [mailDraft, setMailDraft] = useState(null); const [loginDraft, setLoginDraft] = useState(null); + const [sessionDraft, setSessionDraft] = useState(null); const [users, setUsers] = useState([]); const [usersTotal, setUsersTotal] = useState(0); const [userSearch, setUserSearch] = useState(''); @@ -45,6 +46,7 @@ export function AdminPage({ token, role }) { setSettings(data); setMailDraft({ ...data.mail }); setLoginDraft({ ...data.login }); + setSessionDraft({ ...data.session }); } catch (e) { setError(e.message); } try { setMailStatus(await hubFetch('/v1/admin/mail', { token })); @@ -64,6 +66,7 @@ export function AdminPage({ token, role }) { // answer rather than left showing a number that was not stored. setMailDraft({ ...data.mail }); setLoginDraft({ ...data.login }); + setSessionDraft({ ...data.session }); if (patch.mail) { try { setMailStatus(await hubFetch('/v1/admin/mail', { token })); @@ -198,6 +201,8 @@ const MAIL_FIELDS = [ const LOGIN_FIELDS = ['max_failures', 'lockout_minutes']; +const SESSION_FIELDS = ['browser_idle_hours', 'refresh_idle_hours', 'max_hours']; + // Only what changed, and only what is a number: an empty field is someone // mid-edit, not a request to set zero. const changedNumbers = (fields, draft, stored) => Object.fromEntries(fields @@ -296,6 +301,35 @@ const TABS = ['general', 'stats', 'users', 'groups', 'nodes', 'logs', 'blocklist </div> `} </div> + + <div class="settings-section"> + <h3 class="settings-heading">${t('admin.session_heading')}</h3> + <p class="settings-hint">${t('admin.session_hint')}</p> + + ${sessionDraft && SESSION_FIELDS.map(key => html` + <div class="settings-row" key=${key}> + <span class="settings-label">${t('admin.session_' + key)}</span> + <input type="number" class="settings-number" + min=${(settings.session_bounds?.[key] || [0])[0]} + max=${(settings.session_bounds?.[key] || [0, 0])[1]} + value=${sessionDraft[key]} + disabled=${!canEditSettings || settingsSaving} + onInput=${e => setSessionDraft(d => ({ ...d, [key]: e.target.value }))} /> + </div> + `)} + + ${canEditSettings && sessionDraft && html` + <div class="settings-row"> + <button class="btn" disabled=${settingsSaving} + onClick=${() => saveSettings({ + session: changedNumbers(SESSION_FIELDS, sessionDraft, settings.session), + })}>${t('admin.session_save')}</button> + <button class="btn btn-secondary" disabled=${settingsSaving} + onClick=${() => setSessionDraft({ ...settings.session_defaults })} + >${t('admin.mail_reset_defaults')}</button> + </div> + `} + </div> `} `} |