import * as plugins from '../plugins.js'; import * as interfaces from '../../ts_interfaces/index.js'; import { appState } from './shared.js'; export interface ILoginState { identity: interfaces.data.IIdentity | null; isLoggedIn: boolean; } export type IAdminBootstrapStatus = interfaces.requests.IReq_GetAdminBootstrapStatus['response']; export const loginStatePart = await appState.getStatePart( 'login', { identity: null, isLoggedIn: false, }, 'persistent' // Login state persists across browser sessions ); // Actions for state management interface IActionContext { identity: interfaces.data.IIdentity | null; } export const getActionContext = (): IActionContext => { const identity = loginStatePart.getState()!.identity; // Treat expired JWTs as no identity — prevents stale persisted sessions from firing requests if (identity && identity.expiresAt && identity.expiresAt < Date.now()) { return { identity: null }; } return { identity }; }; // Login Action export const loginAction = loginStatePart.createAction<{ username: string; password: string; authSource?: interfaces.requests.TAdminLoginAuthSource; }>(async (statePartArg, dataArg): Promise => { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_AdminLoginWithUsernameAndPassword >('/typedrequest', 'adminLoginWithUsernameAndPassword'); try { const response = await typedRequest.fire({ username: dataArg.username, password: dataArg.password, authSource: dataArg.authSource, }); if (response.identity) { return { identity: response.identity, isLoggedIn: true, }; } return statePartArg.getState()!; } catch (error: unknown) { console.error('Login failed:', error); return statePartArg.getState()!; } }); export async function getAdminBootstrapStatus(): Promise { const request = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetAdminBootstrapStatus >('/typedrequest', 'getAdminBootstrapStatus'); return request.fire({}); } export async function createInitialAdminUser(optionsArg: { email: string; name?: string; password: string; enableIdpGlobalAuth?: boolean; }) { const context = getActionContext(); if (!context.identity) { throw new Error('No identity available for admin bootstrap'); } const request = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_CreateInitialAdminUser >('/typedrequest', 'createInitialAdminUser'); const response = await request.fire({ identity: context.identity, email: optionsArg.email, name: optionsArg.name, password: optionsArg.password, enableIdpGlobalAuth: optionsArg.enableIdpGlobalAuth, }); if (response.identity) { loginStatePart.setState({ identity: response.identity, isLoggedIn: true, }); } return response; } // Logout Action — always clears state, even if identity is expired/missing export const logoutAction = loginStatePart.createAction(async (statePartArg) => { const context = getActionContext(); // Try to notify server, but don't block logout if identity is missing/expired if (context.identity) { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_AdminLogout >('/typedrequest', 'adminLogout'); try { await typedRequest.fire({ identity: context.identity }); } catch (error) { console.error('Logout error:', error); } } // Always clear login state return { identity: null, isLoggedIn: false, }; });