import * as plugins from './plugins.js'; import * as domtools from '@design.estate/dees-domtools'; const appstate = new plugins.deesDomtools.plugins.smartstate.Smartstate(); export interface ILoginState { identity: plugins.interfaces.data.IIdentity; } export const loginStatePart: plugins.smartstate.StatePart = await appstate.getStatePart( 'login', { identity: null }, 'persistent' ); export const loginAction = loginStatePart.createAction<{ username: string; password: string }>( async (statePartArg, payloadArg) => { const currentState = statePartArg.getState(); const trLogin = new domtools.plugins.typedrequest.TypedRequest( '/typedrequest', 'adminLoginWithUsernameAndPassword' ); const response = await trLogin.fire({ username: payloadArg.username, password: payloadArg.password, }).catch(err => { console.log(err); return { ...statePartArg.getState(), } }); const newState = { ...currentState, ...(response.identity ? { identity: response.identity } : {}), }; try { // Keep shared API client in sync and establish WS for modules using sockets apiClient.identity = (response as any)?.identity || null; if (apiClient.identity) { if (!apiClient['typedsocketClient']) { await apiClient.start(); } try { apiClient.typedsocketClient.addTag('identity', apiClient.identity); } catch {} } } catch {} return newState; } ); export const logoutAction = loginStatePart.createAction(async (statePartArg) => { const currentState = statePartArg.getState(); return { ...currentState, identity: null, }; }); export interface IDataState { secretGroups?: plugins.interfaces.data.ISecretGroup[]; secretBundles?: plugins.interfaces.data.ISecretBundle[]; clusters?: plugins.interfaces.data.ICluster[]; externalRegistries?: plugins.interfaces.data.IExternalRegistry[]; images?: any[]; services?: plugins.interfaces.data.IService[]; deployments?: plugins.interfaces.data.IDeployment[]; domains?: plugins.interfaces.data.IDomain[]; dnsEntries?: plugins.interfaces.data.IDnsEntry[]; tasks?: any[]; taskExecutions?: plugins.interfaces.data.ITaskExecution[]; mails?: any[]; logs?: any[]; s3?: any[]; dbs?: any[]; backups?: any[]; } export const dataState = await appstate.getStatePart( 'data', { secretGroups: [], secretBundles: [], clusters: [], externalRegistries: [], images: [], services: [], deployments: [], domains: [], dnsEntries: [], tasks: [], taskExecutions: [], mails: [], logs: [], s3: [], dbs: [], backups: [], }, 'soft' ); // Shared API client instance (used by UI actions) export const apiClient = new plugins.servezoneApi.CloudlyApiClient({ registerAs: 'api', cloudlyUrl: (typeof window !== 'undefined' && window.location?.origin) ? window.location.origin : undefined, }); // Getting data export const getAllDataAction = dataState.createAction(async (statePartArg) => { let currentState = statePartArg.getState(); // SecretsGroups const trGetSecretGroups = new domtools.plugins.typedrequest.TypedRequest( '/typedrequest', 'getSecretGroups' ); const response = await trGetSecretGroups.fire({ identity: loginStatePart.getState().identity, }); currentState = { ...currentState, secretGroups: response.secretGroups, }; // SecretBundles const trGetSecretBundles = new domtools.plugins.typedrequest.TypedRequest( '/typedrequest', 'getSecretBundles' ); const responseSecretBundles = await trGetSecretBundles.fire({ identity: loginStatePart.getState().identity, }); currentState = { ...currentState, secretBundles: responseSecretBundles.secretBundles, }; // images const trGetImages = new domtools.plugins.typedrequest.TypedRequest( '/typedrequest', 'getAllImages' ); const responseImages = await trGetImages.fire({ identity: loginStatePart.getState().identity, }); currentState = { ...currentState, images: responseImages.images, }; // Clusters const trGetClusters = new domtools.plugins.typedrequest.TypedRequest( '/typedrequest', 'getClusters' ); const responseClusters = await trGetClusters.fire({ identity: loginStatePart.getState().identity, }); currentState = { ...currentState, clusters: responseClusters.clusters, } // External Registries via shared API client try { apiClient.identity = loginStatePart.getState().identity; const registries = await apiClient.externalRegistry.getRegistries(); currentState = { ...currentState, externalRegistries: registries as any, }; } catch (error) { console.error('Failed to fetch external registries:', error); currentState = { ...currentState, externalRegistries: [], }; } // Services const trGetServices = new domtools.plugins.typedrequest.TypedRequest( '/typedrequest', 'getServices' ); try { const responseServices = await trGetServices.fire({ identity: loginStatePart.getState().identity, }); currentState = { ...currentState, services: responseServices?.services || [], }; } catch (error) { console.error('Failed to fetch services:', error); currentState = { ...currentState, services: [], }; } // Deployments const trGetDeployments = new domtools.plugins.typedrequest.TypedRequest( '/typedrequest', 'getDeployments' ); try { const responseDeployments = await trGetDeployments.fire({ identity: loginStatePart.getState().identity, }); currentState = { ...currentState, deployments: responseDeployments?.deployments || [], }; } catch (error) { console.error('Failed to fetch deployments:', error); currentState = { ...currentState, deployments: [], }; } // Domains via API client try { apiClient.identity = loginStatePart.getState().identity; const responseDomains = await apiClient.domains.getDomains(); currentState = { ...currentState, domains: responseDomains?.domains || [], }; } catch (error) { console.error('Failed to fetch domains:', error); currentState = { ...currentState, domains: [], }; } // DNS Entries via API client try { apiClient.identity = loginStatePart.getState().identity; const responseDnsEntries = await apiClient.dns.getDnsEntries(); currentState = { ...currentState, dnsEntries: responseDnsEntries?.dnsEntries || [], }; } catch (error) { console.error('Failed to fetch DNS entries:', error); currentState = { ...currentState, dnsEntries: [], }; } return currentState; }); // Service Actions export const createServiceAction = dataState.createAction( async (statePartArg, payloadArg: { serviceData: plugins.interfaces.data.IService['data'] }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.services.createService(payloadArg.serviceData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); export const updateServiceAction = dataState.createAction( async (statePartArg, payloadArg: { serviceId: string; serviceData: plugins.interfaces.data.IService['data'] }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.services.updateService(payloadArg.serviceId, payloadArg.serviceData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); export const deleteServiceAction = dataState.createAction( async (statePartArg, payloadArg: { serviceId: string }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.services.deleteService(payloadArg.serviceId); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); // SecretGroup Actions export const createSecretGroupAction = dataState.createAction( async (statePartArg, payloadArg: plugins.interfaces.data.ISecretGroup) => { let currentState = statePartArg.getState(); const trCreateSecretGroup = new domtools.plugins.typedrequest.TypedRequest( '/typedrequest', 'createSecretGroup' ); const response = await trCreateSecretGroup.fire({ identity: loginStatePart.getState().identity, secretGroup: payloadArg, }); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; return currentState; } ); export const deleteSecretGroupAction = dataState.createAction( async (statePartArg, payloadArg: { secretGroupId: string }) => { let currentState = statePartArg.getState(); const trDeleteSecretGroup = new domtools.plugins.typedrequest.TypedRequest( '/typedrequest', 'deleteSecretGroupById' ); const response = await trDeleteSecretGroup.fire({ identity: loginStatePart.getState().identity, secretGroupId: payloadArg.secretGroupId, }); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); // SecretBundle Actions export const deleteSecretBundleAction = dataState.createAction( async (statePartArg, payloadArg: { configBundleId: string }) => { let currentState = statePartArg.getState(); const trDeleteConfigBundle = new domtools.plugins.typedrequest.TypedRequest( '/typedrequest', 'deleteSecretBundleById' ); const response = await trDeleteConfigBundle.fire({ identity: loginStatePart.getState().identity, secretBundleId: payloadArg.configBundleId, }); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); // image actions export const createImageAction = dataState.createAction( async (statePartArg, payloadArg: { imageName: string, description: string }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.image.createImage({ name: payloadArg.imageName, description: payloadArg.description } as any); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); export const deleteImageAction = dataState.createAction( async (statePartArg, payloadArg: { imageId: string }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.image.deleteImage(payloadArg.imageId); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); // Deployment Actions export const createDeploymentAction = dataState.createAction( async (statePartArg, payloadArg: { deploymentData: Partial }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.deployments.createDeployment(payloadArg.deploymentData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); export const updateDeploymentAction = dataState.createAction( async (statePartArg, payloadArg: { deploymentId: string; deploymentData: Partial }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.deployments.updateDeployment(payloadArg.deploymentId, payloadArg.deploymentData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); export const deleteDeploymentAction = dataState.createAction( async (statePartArg, payloadArg: { deploymentId: string }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.deployments.deleteDeployment(payloadArg.deploymentId); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); // DNS Actions export const createDnsEntryAction = dataState.createAction( async (statePartArg, payloadArg: { dnsEntryData: plugins.interfaces.data.IDnsEntry['data'] }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.dns.createDnsEntry(payloadArg.dnsEntryData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); export const updateDnsEntryAction = dataState.createAction( async (statePartArg, payloadArg: { dnsEntryId: string; dnsEntryData: plugins.interfaces.data.IDnsEntry['data'] }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.dns.updateDnsEntry(payloadArg.dnsEntryId, payloadArg.dnsEntryData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); export const deleteDnsEntryAction = dataState.createAction( async (statePartArg, payloadArg: { dnsEntryId: string }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.dns.deleteDnsEntry(payloadArg.dnsEntryId); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); // Domain Actions export const createDomainAction = dataState.createAction( async (statePartArg, payloadArg: { domainData: plugins.interfaces.data.IDomain['data'] }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.domains.createDomain(payloadArg.domainData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); export const updateDomainAction = dataState.createAction( async (statePartArg, payloadArg: { domainId: string; domainData: plugins.interfaces.data.IDomain['data'] }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.domains.updateDomain(payloadArg.domainId, payloadArg.domainData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); export const deleteDomainAction = dataState.createAction( async (statePartArg, payloadArg: { domainId: string }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.domains.deleteDomain(payloadArg.domainId); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); export const verifyDomainAction = dataState.createAction( async (statePartArg, payloadArg: { domainId: string; verificationMethod?: 'dns' | 'http' | 'email' | 'manual' }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.domains.verifyDomain(payloadArg.domainId, payloadArg.verificationMethod); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); // External Registry Actions export const createExternalRegistryAction = dataState.createAction( async (statePartArg, payloadArg: { registryData: plugins.interfaces.data.IExternalRegistry['data'] }) => { let currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.externalRegistry.createRegistry(payloadArg.registryData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); export const updateExternalRegistryAction = dataState.createAction( async (statePartArg, payloadArg: { registryId: string; registryData: plugins.interfaces.data.IExternalRegistry['data'] }) => { let currentState = statePartArg.getState(); try { apiClient.identity = loginStatePart.getState().identity; const reg = (currentState.externalRegistries as any[])?.find(r => r.id === payloadArg.registryId); if (reg) { reg.data = payloadArg.registryData; await reg.update(); } currentState = await dataState.dispatchAction(getAllDataAction, null); } catch (err) { console.error('Failed to update external registry:', err); } return currentState; } ); export const deleteExternalRegistryAction = dataState.createAction( async (statePartArg, payloadArg: { registryId: string }) => { let currentState = statePartArg.getState(); try { apiClient.identity = loginStatePart.getState().identity; const reg = (currentState.externalRegistries as any[])?.find(r => r.id === payloadArg.registryId); if (reg) { await reg.delete(apiClient as any, reg.id); } currentState = await dataState.dispatchAction(getAllDataAction, null); } catch (err) { console.error('Failed to delete external registry:', err); } return currentState; } ); export const verifyExternalRegistryAction = dataState.createAction( async (statePartArg, payloadArg: { registryId: string }) => { let currentState = statePartArg.getState(); try { apiClient.identity = loginStatePart.getState().identity; const result = await apiClient.externalRegistry.verifyRegistry(payloadArg.registryId); if (result.success && result.registry) { const regs = (currentState.externalRegistries || []).slice(); const idx = regs.findIndex(r => r.id === payloadArg.registryId); if (idx >= 0) { // Preserve instance; update its data + shallow props const instance: any = regs[idx]; instance.data = result.registry.data; instance.id = result.registry.id; regs[idx] = instance; } currentState = { ...currentState, externalRegistries: regs, }; } } catch (err) { console.error('Failed to verify external registry:', err); } return currentState; } ); // Task Actions export const taskActions = { getTasks: dataState.createAction( async (statePartArg, payloadArg: {}) => { const currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; const response = await apiClient.tasks.getTasks(); return { ...currentState, tasks: response.tasks, }; } ), getTaskExecutions: dataState.createAction( async (statePartArg, payloadArg: { filter?: any }) => { const currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; const response = await apiClient.tasks.getTaskExecutions(payloadArg.filter); return { ...currentState, taskExecutions: response.executions, }; } ), getTaskExecutionById: dataState.createAction( async (statePartArg, payloadArg: { executionId: string }) => { const currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.tasks.getTaskExecutionById(payloadArg.executionId); return currentState; } ), triggerTask: dataState.createAction( async (statePartArg, payloadArg: { taskName: string; userId?: string }) => { const currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.tasks.triggerTask(payloadArg.taskName, payloadArg.userId); return currentState; } ), cancelTask: dataState.createAction( async (statePartArg, payloadArg: { executionId: string }) => { const currentState = statePartArg.getState(); apiClient.identity = loginStatePart.getState().identity; await apiClient.tasks.cancelTask(payloadArg.executionId); return currentState; } ), }; // cluster export const addClusterAction = dataState.createAction( async ( statePartArg, payloadArg: { clusterName: string; setupMode?: 'manual' | 'hetzner' | 'aws' | 'digitalocean'; } ) => { let currentState = statePartArg.getState(); const trAddCluster = new domtools.plugins.typedrequest.TypedRequest( '/typedrequest', 'createCluster' ); const response = await trAddCluster.fire({ identity: loginStatePart.getState().identity, ...payloadArg, }); currentState = { ...currentState, ...{ clusters: [...currentState.clusters, response.cluster], }, } return currentState; } );