This commit is contained in:
seb
2026-10-29 17:32:39 +01:00
parent 867d550814
commit f555a1c9dd
6 changed files with 99 additions and 47 deletions

View File

@@ -30,6 +30,7 @@ import { auth, company, number as numberApi } from '../api';
* @typedef {Object} AuthState
* @property {AuthUser|null} user Authenticated user, or `null` for anonymous sessions.
* @property {string|null} googleClientId Google Identity Services client ID.
* @property {number|null} schemaVersion Database schema version returned by the backend.
* @property {'loading'|'anon'|'authed'} status Current authentication lifecycle state.
* @property {boolean} hasAuth True when the session has a valid authenticated user.
* @property {boolean} hasCompany True when `hasAuth` is true AND a company is selected.
@@ -47,6 +48,7 @@ export const AUTH_LOGGED_OUT = 'auth/loggedOut';
const initialState = {
user: null,
googleClientId: null,
schemaVersion: null,
status: 'loading',
hasAuth: false,
hasCompany: false,
@@ -137,22 +139,26 @@ function syncSse(state) {
function applySession(nextSession) {
const googleClientId =
nextSession.googleClientId ?? authAutomat.state.googleClientId;
const schemaVersion =
nextSession.schemaVersion !== undefined
? nextSession.schemaVersion
: (authAutomat.state.schemaVersion ?? null);
if (!nextSession.user) {
if (authAutomat.state.status !== 'authed') {
const nextState = authAutomat.setState(
buildAuthState(null, 'anon', { googleClientId, retryNotification: null })
buildAuthState(null, 'anon', { googleClientId, schemaVersion, retryNotification: null })
);
syncSse(nextState);
} else {
authAutomat.setState({ googleClientId, retryNotification: null });
authAutomat.setState({ googleClientId, schemaVersion, retryNotification: null });
}
return nextSession;
}
const user = mergeUser(authAutomat.state.user, nextSession.user);
const nextState = authAutomat.setState(
buildAuthState(user, 'authed', { googleClientId, retryNotification: null })
buildAuthState(user, 'authed', { googleClientId, schemaVersion, retryNotification: null })
);
syncSse(nextState);
emit({ type: AUTH_SESSION_ESTABLISHED });
@@ -234,6 +240,7 @@ const actions = {
const nextState = authAutomat.setState(
buildAuthState(user, 'authed', {
googleClientId: session.googleClientId ?? authAutomat.state.googleClientId,
schemaVersion: session.schemaVersion ?? authAutomat.state.schemaVersion ?? null,
})
);
syncSse(nextState);
@@ -344,6 +351,7 @@ export const selectHasCompany = () => Boolean(authAutomat.state.hasCompany);
export const selectSelectedCompanyId = () =>
authAutomat.state.hasCompany ? authAutomat.state.user?.selectedCompanyId ?? null : null;
export const selectRetryNotification = () => authAutomat.state.retryNotification;
export const selectSchemaVersion = () => authAutomat.state.schemaVersion;
/**
* Re-reads `GET /api/auth/me` from the server.
@@ -351,7 +359,7 @@ export const selectRetryNotification = () => authAutomat.state.retryNotification
* Called after login, logout, and company switches so the automat reflects
* the current server session rather than an optimistic guess.
*
* @returns {Promise<{user: AuthUser|null, googleClientId: string|null}>} Session.
* @returns {Promise<{user: AuthUser|null, googleClientId: string|null, schemaVersion?: number|null}>} Session.
*/
export function bootstrapAuthMe() {
return authAutomat.actions.bootstrapSession();