feat(opsserver): introduce OpsServer (TypedRequest API) and new lightweight web UI; replace legacy Angular UI and add typed interfaces
This commit is contained in:
919
ts_web/appstate.ts
Normal file
919
ts_web/appstate.ts
Normal file
@@ -0,0 +1,919 @@
|
||||
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<ILoginState>(
|
||||
'login',
|
||||
{
|
||||
identity: null,
|
||||
isLoggedIn: false,
|
||||
},
|
||||
'persistent',
|
||||
);
|
||||
|
||||
export const systemStatePart = await appState.getStatePart<ISystemState>(
|
||||
'system',
|
||||
{
|
||||
status: null,
|
||||
},
|
||||
'soft',
|
||||
);
|
||||
|
||||
export const servicesStatePart = await appState.getStatePart<IServicesState>(
|
||||
'services',
|
||||
{
|
||||
services: [],
|
||||
currentService: null,
|
||||
currentServiceLogs: [],
|
||||
currentServiceStats: null,
|
||||
platformServices: [],
|
||||
currentPlatformService: null,
|
||||
},
|
||||
'soft',
|
||||
);
|
||||
|
||||
export const networkStatePart = await appState.getStatePart<INetworkState>(
|
||||
'network',
|
||||
{
|
||||
targets: [],
|
||||
stats: null,
|
||||
trafficStats: null,
|
||||
dnsRecords: [],
|
||||
domains: [],
|
||||
certificates: [],
|
||||
},
|
||||
'soft',
|
||||
);
|
||||
|
||||
export const registriesStatePart = await appState.getStatePart<IRegistriesState>(
|
||||
'registries',
|
||||
{
|
||||
tokens: [],
|
||||
registryStatus: null,
|
||||
},
|
||||
'soft',
|
||||
);
|
||||
|
||||
export const backupsStatePart = await appState.getStatePart<IBackupsState>(
|
||||
'backups',
|
||||
{
|
||||
backups: [],
|
||||
schedules: [],
|
||||
},
|
||||
'soft',
|
||||
);
|
||||
|
||||
export const settingsStatePart = await appState.getStatePart<ISettingsState>(
|
||||
'settings',
|
||||
{
|
||||
settings: null,
|
||||
backupPasswordConfigured: false,
|
||||
},
|
||||
'soft',
|
||||
);
|
||||
|
||||
export const uiStatePart = await appState.getStatePart<IUiState>(
|
||||
'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<interfaces.data.ISettings>;
|
||||
}>(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<typeof setInterval> | 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();
|
||||
Reference in New Issue
Block a user