import * as plugins from './plugins.js'; import * as interfaces from '../ts_interfaces/index.js'; // ============================================================================ // Smartstate instance // ============================================================================ export const appState = new plugins.domtools.plugins.smartstate.Smartstate(); // ============================================================================ // State Part Interfaces // ============================================================================ export interface ILoginState { identity: interfaces.data.IIdentity | null; isLoggedIn: boolean; } export interface ISystemState { status: interfaces.data.ISystemStatus | null; } export interface IServicesState { services: interfaces.data.IService[]; currentService: interfaces.data.IService | null; currentServiceLogs: interfaces.data.ILogEntry[]; currentServiceStats: interfaces.data.IContainerStats | null; platformServices: interfaces.data.IPlatformService[]; currentPlatformService: interfaces.data.IPlatformService | null; } export interface INetworkState { targets: interfaces.data.INetworkTarget[]; stats: interfaces.data.INetworkStats | null; trafficStats: interfaces.data.ITrafficStats | null; dnsRecords: interfaces.data.IDnsRecord[]; domains: interfaces.data.IDomainDetail[]; certificates: interfaces.data.ICertificate[]; } export interface IRegistriesState { tokens: interfaces.data.IRegistryToken[]; registryStatus: { running: boolean; port: number } | null; } export interface IBackupsState { backups: interfaces.data.IBackup[]; schedules: interfaces.data.IBackupSchedule[]; } export interface ISettingsState { settings: interfaces.data.ISettings | null; backupPasswordConfigured: boolean; } export interface IUiState { activeView: string; autoRefresh: boolean; refreshInterval: number; } // ============================================================================ // State Parts // ============================================================================ export const loginStatePart = await appState.getStatePart( 'login', { identity: null, isLoggedIn: false, }, 'persistent', ); export const systemStatePart = await appState.getStatePart( 'system', { status: null, }, 'soft', ); export const servicesStatePart = await appState.getStatePart( 'services', { services: [], currentService: null, currentServiceLogs: [], currentServiceStats: null, platformServices: [], currentPlatformService: null, }, 'soft', ); export const networkStatePart = await appState.getStatePart( 'network', { targets: [], stats: null, trafficStats: null, dnsRecords: [], domains: [], certificates: [], }, 'soft', ); export const registriesStatePart = await appState.getStatePart( 'registries', { tokens: [], registryStatus: null, }, 'soft', ); export const backupsStatePart = await appState.getStatePart( 'backups', { backups: [], schedules: [], }, 'soft', ); export const settingsStatePart = await appState.getStatePart( 'settings', { settings: null, backupPasswordConfigured: false, }, 'soft', ); export const uiStatePart = await appState.getStatePart( 'ui', { activeView: 'dashboard', autoRefresh: true, refreshInterval: 30000, }, ); // ============================================================================ // Helpers // ============================================================================ interface IActionContext { identity: interfaces.data.IIdentity | null; } const getActionContext = (): IActionContext => { return { identity: loginStatePart.getState().identity }; }; // ============================================================================ // Login Actions // ============================================================================ export const loginAction = loginStatePart.createAction<{ username: string; password: string; }>(async (statePartArg, dataArg) => { try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_AdminLoginWithUsernameAndPassword >('/typedrequest', 'adminLoginWithUsernameAndPassword'); const response = await typedRequest.fire({ username: dataArg.username, password: dataArg.password, }); return { identity: response.identity, isLoggedIn: true, }; } catch (err) { console.error('Login failed:', err); return { identity: null, isLoggedIn: false }; } }); export const logoutAction = loginStatePart.createAction(async (statePartArg) => { const context = getActionContext(); try { if (context.identity) { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_AdminLogout >('/typedrequest', 'adminLogout'); await typedRequest.fire({ identity: context.identity }); } } catch (err) { console.error('Logout error:', err); } return { identity: null, isLoggedIn: false }; }); // ============================================================================ // System Status Actions // ============================================================================ export const fetchSystemStatusAction = systemStatePart.createAction(async (statePartArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetSystemStatus >('/typedrequest', 'getSystemStatus'); const response = await typedRequest.fire({ identity: context.identity! }); return { status: response.status }; } catch (err) { console.error('Failed to fetch system status:', err); return statePartArg.getState(); } }); // ============================================================================ // Services Actions // ============================================================================ export const fetchServicesAction = servicesStatePart.createAction(async (statePartArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetServices >('/typedrequest', 'getServices'); const response = await typedRequest.fire({ identity: context.identity! }); return { ...statePartArg.getState(), services: response.services }; } catch (err) { console.error('Failed to fetch services:', err); return statePartArg.getState(); } }); export const fetchServiceAction = servicesStatePart.createAction<{ name: string }>( async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetService >('/typedrequest', 'getService'); const response = await typedRequest.fire({ identity: context.identity!, serviceName: dataArg.name, }); return { ...statePartArg.getState(), currentService: response.service }; } catch (err) { console.error('Failed to fetch service:', err); return statePartArg.getState(); } }, ); export const createServiceAction = servicesStatePart.createAction<{ config: interfaces.data.IServiceCreate; }>(async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_CreateService >('/typedrequest', 'createService'); await typedRequest.fire({ identity: context.identity!, serviceConfig: dataArg.config, }); // Re-fetch services list const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetServices >('/typedrequest', 'getServices'); const listResp = await listReq.fire({ identity: context.identity! }); return { ...statePartArg.getState(), services: listResp.services }; } catch (err) { console.error('Failed to create service:', err); return statePartArg.getState(); } }); export const deleteServiceAction = servicesStatePart.createAction<{ name: string }>( async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_DeleteService >('/typedrequest', 'deleteService'); await typedRequest.fire({ identity: context.identity!, serviceName: dataArg.name, }); const state = statePartArg.getState(); return { ...state, services: state.services.filter((s) => s.name !== dataArg.name), currentService: null, }; } catch (err) { console.error('Failed to delete service:', err); return statePartArg.getState(); } }, ); export const startServiceAction = servicesStatePart.createAction<{ name: string }>( async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_StartService >('/typedrequest', 'startService'); await typedRequest.fire({ identity: context.identity!, serviceName: dataArg.name, }); // Re-fetch services const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetServices >('/typedrequest', 'getServices'); const listResp = await listReq.fire({ identity: context.identity! }); return { ...statePartArg.getState(), services: listResp.services }; } catch (err) { console.error('Failed to start service:', err); return statePartArg.getState(); } }, ); export const stopServiceAction = servicesStatePart.createAction<{ name: string }>( async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_StopService >('/typedrequest', 'stopService'); await typedRequest.fire({ identity: context.identity!, serviceName: dataArg.name, }); const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetServices >('/typedrequest', 'getServices'); const listResp = await listReq.fire({ identity: context.identity! }); return { ...statePartArg.getState(), services: listResp.services }; } catch (err) { console.error('Failed to stop service:', err); return statePartArg.getState(); } }, ); export const restartServiceAction = servicesStatePart.createAction<{ name: string }>( async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_RestartService >('/typedrequest', 'restartService'); await typedRequest.fire({ identity: context.identity!, serviceName: dataArg.name, }); const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetServices >('/typedrequest', 'getServices'); const listResp = await listReq.fire({ identity: context.identity! }); return { ...statePartArg.getState(), services: listResp.services }; } catch (err) { console.error('Failed to restart service:', err); return statePartArg.getState(); } }, ); export const fetchServiceLogsAction = servicesStatePart.createAction<{ name: string; lines?: number; }>(async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetServiceLogs >('/typedrequest', 'getServiceLogs'); const response = await typedRequest.fire({ identity: context.identity!, serviceName: dataArg.name, lines: dataArg.lines || 200, }); return { ...statePartArg.getState(), currentServiceLogs: response.logs }; } catch (err) { console.error('Failed to fetch service logs:', err); return statePartArg.getState(); } }); export const fetchServiceStatsAction = servicesStatePart.createAction<{ name: string }>( async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetServiceStats >('/typedrequest', 'getServiceStats'); const response = await typedRequest.fire({ identity: context.identity!, serviceName: dataArg.name, }); return { ...statePartArg.getState(), currentServiceStats: response.stats }; } catch (err) { console.error('Failed to fetch service stats:', err); return statePartArg.getState(); } }, ); // ============================================================================ // Platform Services Actions // ============================================================================ export const fetchPlatformServicesAction = servicesStatePart.createAction( async (statePartArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetPlatformServices >('/typedrequest', 'getPlatformServices'); const response = await typedRequest.fire({ identity: context.identity! }); return { ...statePartArg.getState(), platformServices: response.platformServices }; } catch (err) { console.error('Failed to fetch platform services:', err); return statePartArg.getState(); } }, ); export const startPlatformServiceAction = servicesStatePart.createAction<{ serviceType: interfaces.data.TPlatformServiceType; }>(async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_StartPlatformService >('/typedrequest', 'startPlatformService'); await typedRequest.fire({ identity: context.identity!, serviceType: dataArg.serviceType, }); // Re-fetch platform services const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetPlatformServices >('/typedrequest', 'getPlatformServices'); const listResp = await listReq.fire({ identity: context.identity! }); return { ...statePartArg.getState(), platformServices: listResp.platformServices }; } catch (err) { console.error('Failed to start platform service:', err); return statePartArg.getState(); } }); export const stopPlatformServiceAction = servicesStatePart.createAction<{ serviceType: interfaces.data.TPlatformServiceType; }>(async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_StopPlatformService >('/typedrequest', 'stopPlatformService'); await typedRequest.fire({ identity: context.identity!, serviceType: dataArg.serviceType, }); const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetPlatformServices >('/typedrequest', 'getPlatformServices'); const listResp = await listReq.fire({ identity: context.identity! }); return { ...statePartArg.getState(), platformServices: listResp.platformServices }; } catch (err) { console.error('Failed to stop platform service:', err); return statePartArg.getState(); } }); // ============================================================================ // Network Actions // ============================================================================ export const fetchNetworkTargetsAction = networkStatePart.createAction(async (statePartArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetNetworkTargets >('/typedrequest', 'getNetworkTargets'); const response = await typedRequest.fire({ identity: context.identity! }); return { ...statePartArg.getState(), targets: response.targets }; } catch (err) { console.error('Failed to fetch network targets:', err); return statePartArg.getState(); } }); export const fetchNetworkStatsAction = networkStatePart.createAction(async (statePartArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetNetworkStats >('/typedrequest', 'getNetworkStats'); const response = await typedRequest.fire({ identity: context.identity! }); return { ...statePartArg.getState(), stats: response.stats }; } catch (err) { console.error('Failed to fetch network stats:', err); return statePartArg.getState(); } }); export const fetchTrafficStatsAction = networkStatePart.createAction(async (statePartArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetTrafficStats >('/typedrequest', 'getTrafficStats'); const response = await typedRequest.fire({ identity: context.identity! }); return { ...statePartArg.getState(), trafficStats: response.stats }; } catch (err) { console.error('Failed to fetch traffic stats:', err); return statePartArg.getState(); } }); export const fetchDnsRecordsAction = networkStatePart.createAction(async (statePartArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetDnsRecords >('/typedrequest', 'getDnsRecords'); const response = await typedRequest.fire({ identity: context.identity! }); return { ...statePartArg.getState(), dnsRecords: response.records }; } catch (err) { console.error('Failed to fetch DNS records:', err); return statePartArg.getState(); } }); export const syncDnsAction = networkStatePart.createAction(async (statePartArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_SyncDns >('/typedrequest', 'syncDns'); await typedRequest.fire({ identity: context.identity! }); // Re-fetch DNS records const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetDnsRecords >('/typedrequest', 'getDnsRecords'); const listResp = await listReq.fire({ identity: context.identity! }); return { ...statePartArg.getState(), dnsRecords: listResp.records }; } catch (err) { console.error('Failed to sync DNS:', err); return statePartArg.getState(); } }); export const fetchDomainsAction = networkStatePart.createAction(async (statePartArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetDomains >('/typedrequest', 'getDomains'); const response = await typedRequest.fire({ identity: context.identity! }); return { ...statePartArg.getState(), domains: response.domains }; } catch (err) { console.error('Failed to fetch domains:', err); return statePartArg.getState(); } }); export const fetchCertificatesAction = networkStatePart.createAction(async (statePartArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_ListCertificates >('/typedrequest', 'listCertificates'); const response = await typedRequest.fire({ identity: context.identity! }); return { ...statePartArg.getState(), certificates: response.certificates }; } catch (err) { console.error('Failed to fetch certificates:', err); return statePartArg.getState(); } }); export const renewCertificateAction = networkStatePart.createAction<{ domain: string }>( async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_RenewCertificate >('/typedrequest', 'renewCertificate'); await typedRequest.fire({ identity: context.identity!, domain: dataArg.domain, }); // Re-fetch certificates const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_ListCertificates >('/typedrequest', 'listCertificates'); const listResp = await listReq.fire({ identity: context.identity! }); return { ...statePartArg.getState(), certificates: listResp.certificates }; } catch (err) { console.error('Failed to renew certificate:', err); return statePartArg.getState(); } }, ); // ============================================================================ // Registry Actions // ============================================================================ export const fetchRegistryTokensAction = registriesStatePart.createAction( async (statePartArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetRegistryTokens >('/typedrequest', 'getRegistryTokens'); const response = await typedRequest.fire({ identity: context.identity! }); return { ...statePartArg.getState(), tokens: response.tokens }; } catch (err) { console.error('Failed to fetch registry tokens:', err); return statePartArg.getState(); } }, ); export const createRegistryTokenAction = registriesStatePart.createAction<{ token: interfaces.data.ICreateTokenRequest; }>(async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_CreateRegistryToken >('/typedrequest', 'createRegistryToken'); await typedRequest.fire({ identity: context.identity!, token: dataArg.token, }); // Re-fetch tokens const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetRegistryTokens >('/typedrequest', 'getRegistryTokens'); const listResp = await listReq.fire({ identity: context.identity! }); return { ...statePartArg.getState(), tokens: listResp.tokens }; } catch (err) { console.error('Failed to create registry token:', err); return statePartArg.getState(); } }); export const deleteRegistryTokenAction = registriesStatePart.createAction<{ tokenId: string; }>(async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_DeleteRegistryToken >('/typedrequest', 'deleteRegistryToken'); await typedRequest.fire({ identity: context.identity!, tokenId: dataArg.tokenId, }); const state = statePartArg.getState(); return { ...state, tokens: state.tokens.filter((t) => t.id !== dataArg.tokenId), }; } catch (err) { console.error('Failed to delete registry token:', err); return statePartArg.getState(); } }); // ============================================================================ // Backups Actions // ============================================================================ export const fetchBackupsAction = backupsStatePart.createAction(async (statePartArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetBackups >('/typedrequest', 'getBackups'); const response = await typedRequest.fire({ identity: context.identity! }); return { ...statePartArg.getState(), backups: response.backups }; } catch (err) { console.error('Failed to fetch backups:', err); return statePartArg.getState(); } }); export const deleteBackupAction = backupsStatePart.createAction<{ backupId: number }>( async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_DeleteBackup >('/typedrequest', 'deleteBackup'); await typedRequest.fire({ identity: context.identity!, backupId: dataArg.backupId, }); const state = statePartArg.getState(); return { ...state, backups: state.backups.filter((b) => b.id !== dataArg.backupId), }; } catch (err) { console.error('Failed to delete backup:', err); return statePartArg.getState(); } }, ); export const fetchSchedulesAction = backupsStatePart.createAction(async (statePartArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetBackupSchedules >('/typedrequest', 'getBackupSchedules'); const response = await typedRequest.fire({ identity: context.identity! }); return { ...statePartArg.getState(), schedules: response.schedules }; } catch (err) { console.error('Failed to fetch schedules:', err); return statePartArg.getState(); } }); export const createScheduleAction = backupsStatePart.createAction<{ config: interfaces.data.IBackupScheduleCreate; }>(async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_CreateBackupSchedule >('/typedrequest', 'createBackupSchedule'); await typedRequest.fire({ identity: context.identity!, scheduleConfig: dataArg.config, }); // Re-fetch schedules const listReq = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetBackupSchedules >('/typedrequest', 'getBackupSchedules'); const listResp = await listReq.fire({ identity: context.identity! }); return { ...statePartArg.getState(), schedules: listResp.schedules }; } catch (err) { console.error('Failed to create schedule:', err); return statePartArg.getState(); } }); export const deleteScheduleAction = backupsStatePart.createAction<{ scheduleId: number }>( async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_DeleteBackupSchedule >('/typedrequest', 'deleteBackupSchedule'); await typedRequest.fire({ identity: context.identity!, scheduleId: dataArg.scheduleId, }); const state = statePartArg.getState(); return { ...state, schedules: state.schedules.filter((s) => s.id !== dataArg.scheduleId), }; } catch (err) { console.error('Failed to delete schedule:', err); return statePartArg.getState(); } }, ); export const triggerScheduleAction = backupsStatePart.createAction<{ scheduleId: number }>( async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_TriggerBackupSchedule >('/typedrequest', 'triggerBackupSchedule'); await typedRequest.fire({ identity: context.identity!, scheduleId: dataArg.scheduleId, }); // Re-fetch backups const backupsReq = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetBackups >('/typedrequest', 'getBackups'); const backupsResp = await backupsReq.fire({ identity: context.identity! }); return { ...statePartArg.getState(), backups: backupsResp.backups }; } catch (err) { console.error('Failed to trigger schedule:', err); return statePartArg.getState(); } }, ); // ============================================================================ // Settings Actions // ============================================================================ export const fetchSettingsAction = settingsStatePart.createAction(async (statePartArg) => { const context = getActionContext(); try { const [settingsResp, passwordResp] = await Promise.all([ new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetSettings >('/typedrequest', 'getSettings').fire({ identity: context.identity! }), new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_GetBackupPasswordStatus >('/typedrequest', 'getBackupPasswordStatus').fire({ identity: context.identity! }), ]); return { settings: settingsResp.settings, backupPasswordConfigured: passwordResp.status.isConfigured, }; } catch (err) { console.error('Failed to fetch settings:', err); return statePartArg.getState(); } }); export const updateSettingsAction = settingsStatePart.createAction<{ settings: Partial; }>(async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_UpdateSettings >('/typedrequest', 'updateSettings'); const response = await typedRequest.fire({ identity: context.identity!, settings: dataArg.settings, }); return { ...statePartArg.getState(), settings: response.settings }; } catch (err) { console.error('Failed to update settings:', err); return statePartArg.getState(); } }); export const setBackupPasswordAction = settingsStatePart.createAction<{ password: string }>( async (statePartArg, dataArg) => { const context = getActionContext(); try { const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest< interfaces.requests.IReq_SetBackupPassword >('/typedrequest', 'setBackupPassword'); await typedRequest.fire({ identity: context.identity!, password: dataArg.password, }); return { ...statePartArg.getState(), backupPasswordConfigured: true }; } catch (err) { console.error('Failed to set backup password:', err); return statePartArg.getState(); } }, ); // ============================================================================ // UI Actions // ============================================================================ export const setActiveViewAction = uiStatePart.createAction<{ view: string }>( async (statePartArg, dataArg) => { return { ...statePartArg.getState(), activeView: dataArg.view }; }, ); export const toggleAutoRefreshAction = uiStatePart.createAction(async (statePartArg) => { const state = statePartArg.getState(); return { ...state, autoRefresh: !state.autoRefresh }; }); // ============================================================================ // Auto-refresh system // ============================================================================ let refreshIntervalHandle: ReturnType | null = null; const dispatchCombinedRefreshAction = async () => { const loginState = loginStatePart.getState(); if (!loginState.isLoggedIn) return; try { await systemStatePart.dispatchAction(fetchSystemStatusAction, null); } catch (err) { // Silently fail on auto-refresh } }; const startAutoRefresh = () => { const uiState = uiStatePart.getState(); const loginState = loginStatePart.getState(); if (uiState.autoRefresh && loginState.isLoggedIn) { if (refreshIntervalHandle) { clearInterval(refreshIntervalHandle); } refreshIntervalHandle = setInterval(() => { dispatchCombinedRefreshAction(); }, uiState.refreshInterval); } else { if (refreshIntervalHandle) { clearInterval(refreshIntervalHandle); refreshIntervalHandle = null; } } }; uiStatePart.select((s) => s).subscribe(() => startAutoRefresh()); loginStatePart.select((s) => s).subscribe(() => startAutoRefresh()); startAutoRefresh();