app: use auth model for init, sidebar, login
This commit is contained in:
+22
-4
@@ -1,4 +1,4 @@
|
|||||||
import { h, render, Link, hComp, ToastContainer, connect, apiFetch, modelRegister, modelFetch, reactive, initAuth, getAuthToken, checkSession } from '/static/hoover/index.js';
|
import { h, render, Link, hComp, ToastContainer, connect, disconnect, apiFetch, modelRegister, modelFetch, reactive, createAuthModel, isAuthenticated, getAuthData } from '/static/hoover/index.js';
|
||||||
|
|
||||||
import DashboardPage from '/static/pages/dashboard.js';
|
import DashboardPage from '/static/pages/dashboard.js';
|
||||||
import InterfacesPage from '/static/pages/interfaces.js';
|
import InterfacesPage from '/static/pages/interfaces.js';
|
||||||
@@ -32,14 +32,17 @@ const _NavBase = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
function getNav() {
|
function getNav() {
|
||||||
|
const perms = getAuthData()?.permissions;
|
||||||
const nav = [..._NavBase];
|
const nav = [..._NavBase];
|
||||||
const perms = JSON.parse(sessionStorage.getItem('vw:permissions') || 'null');
|
|
||||||
if (perms && perms.auth === 'rw') {
|
if (perms && perms.auth === 'rw') {
|
||||||
nav.push({ path: '/users', label: 'Users' });
|
nav.push({ path: '/users', label: 'Users' });
|
||||||
}
|
}
|
||||||
return nav;
|
return nav;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Auth model (silent topic — the daemon never broadcasts 'auth') ── */
|
||||||
|
modelRegister('auth', createAuthModel());
|
||||||
|
|
||||||
modelRegister('firewall', {
|
modelRegister('firewall', {
|
||||||
subsystem: 'firewall',
|
subsystem: 'firewall',
|
||||||
fetch: async () => {
|
fetch: async () => {
|
||||||
@@ -266,14 +269,29 @@ export async function initApp() {
|
|||||||
// Listen for login events to update router state after auth
|
// Listen for login events to update router state after auth
|
||||||
window.addEventListener('auth:login', () => {
|
window.addEventListener('auth:login', () => {
|
||||||
router.isAuthenticated = true;
|
router.isAuthenticated = true;
|
||||||
|
// Defer to a macrotask: at dispatch time (microtask) the login form's
|
||||||
|
// hash change has not run yet — router.state.path is still '/login'.
|
||||||
|
// The deferred check runs after the hashchange task, so a fresh login
|
||||||
|
// fetches all models. connect() is idempotent (_wsConnect no-ops with
|
||||||
|
// a live connection) and gives the post-login session its WS — today
|
||||||
|
// WS only connects on an authenticated page load (pre-existing gap).
|
||||||
|
setTimeout(() => {
|
||||||
|
connect();
|
||||||
if (!router.state.path.startsWith('/login')) {
|
if (!router.state.path.startsWith('/login')) {
|
||||||
fetchInitialData();
|
fetchInitialData();
|
||||||
}
|
}
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Terminal auth transition — close the WS socket so a same-tab relogin
|
||||||
|
// establishes a fresh connection with the new user's token.
|
||||||
|
window.addEventListener('auth:logout', () => {
|
||||||
|
disconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check auth state before connecting WS
|
// Check auth state before connecting WS
|
||||||
const ok = await initAuth();
|
await modelFetch('auth', { action: 'check' });
|
||||||
if (ok) {
|
if (isAuthenticated()) {
|
||||||
router.isAuthenticated = true;
|
router.isAuthenticated = true;
|
||||||
fetchInitialData();
|
fetchInitialData();
|
||||||
setTimeout(connect, 0);
|
setTimeout(connect, 0);
|
||||||
|
|||||||
@@ -23,13 +23,19 @@ export { definePage, hComp } from './component.js';
|
|||||||
export { createRouter, Link } from './router.js';
|
export { createRouter, Link } from './router.js';
|
||||||
|
|
||||||
/* ── WebSocket ───────────────────────────────────────────────── */
|
/* ── WebSocket ───────────────────────────────────────────────── */
|
||||||
export { connect, onMessage } from './websocket.js';
|
export { connect, onMessage, disconnect } from './websocket.js';
|
||||||
|
|
||||||
/* ── API & Toast ─────────────────────────────────────────────── */
|
/* ── API & Toast ─────────────────────────────────────────────── */
|
||||||
export { apiFetch, toast, dismissToast, apiSubmit, checkAbort, poll, refactorLoad, formAction, setAuthToken, clearAuthTokens, getAuthToken } from './api.js';
|
export { apiFetch, toast, dismissToast, apiSubmit, checkAbort, poll, refactorLoad, formAction }
|
||||||
|
from './api.js';
|
||||||
|
|
||||||
/* ── UI Components: Auth ──────────────────────────────────────── */
|
/* ── UI Components: Auth ──────────────────────────────────────── */
|
||||||
export { scheduleTokenRefresh, cancelTokenRefresh, checkSession, logout, initAuth, handleLoginSuccess, webauthnSupported, startRegistration, startAuthentication } from './components/auth.js';
|
export { logout, doLogin, webauthnSupported, checkWebAuthnCapable,
|
||||||
|
startRegistration, startAuthentication } from './components/auth.js';
|
||||||
|
|
||||||
|
/* ── Auth model ───────────────────────────────────────────────── */
|
||||||
|
export { createAuthModel, getAuthToken, isAuthenticated, refreshAuth, getAuthData }
|
||||||
|
from './auth_model.js';
|
||||||
|
|
||||||
/* ── Model ───────────────────────────────────────────────────── */
|
/* ── Model ───────────────────────────────────────────────────── */
|
||||||
export { modelRegister, getModel, modelFetch, collectLoadingModels } from './model.js';
|
export { modelRegister, getModel, modelFetch, collectLoadingModels } from './model.js';
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ import {
|
|||||||
html,
|
html,
|
||||||
apiFetch,
|
apiFetch,
|
||||||
toast,
|
toast,
|
||||||
setAuthToken,
|
doLogin,
|
||||||
getAuthToken,
|
|
||||||
handleLoginSuccess,
|
|
||||||
webauthnSupported,
|
webauthnSupported,
|
||||||
startAuthentication,
|
startAuthentication,
|
||||||
esc,
|
esc,
|
||||||
@@ -89,7 +87,7 @@ async function doPasswordLogin() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
handleLoginSuccess(res.data);
|
doLogin(res.data);
|
||||||
toast('Welcome, ' + esc(username), 'success');
|
toast('Welcome, ' + esc(username), 'success');
|
||||||
} else {
|
} else {
|
||||||
errEl.textContent = res.error || 'Login failed';
|
errEl.textContent = res.error || 'Login failed';
|
||||||
@@ -173,7 +171,7 @@ const passkeyClickHandler = async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (finishRes.ok) {
|
if (finishRes.ok) {
|
||||||
handleLoginSuccess(finishRes.data);
|
doLogin(finishRes.data);
|
||||||
toast('Welcome, ' + esc(username), 'success');
|
toast('Welcome, ' + esc(username), 'success');
|
||||||
} else {
|
} else {
|
||||||
errEl.textContent = finishRes.error || 'Passkey authentication failed';
|
errEl.textContent = finishRes.error || 'Passkey authentication failed';
|
||||||
|
|||||||
Reference in New Issue
Block a user