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(); let identity: plugins.interfaces.data.IIdentity = null; try { identity = await apiClient.loginWithUsernameAndPassword(payloadArg.username, payloadArg.password) as any; } catch (err) { console.log(err); } const newState = { ...currentState, ...(identity ? { identity } : {}), }; try { // Keep shared API client in sync and establish WS for modules using sockets apiClient.identity = 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 try { apiClient.identity = loginStatePart.getState().identity; const secretGroups = await apiClient.secretgroup.getSecretGroups(); currentState = { ...currentState, secretGroups: secretGroups as any, }; } catch (err) { console.error('Failed to fetch secret groups:', err); currentState = { ...currentState, secretGroups: [], }; } // SecretBundles try { apiClient.identity = loginStatePart.getState().identity; const responseSecretBundles = await apiClient.secretbundle.getSecretBundles(); currentState = { ...currentState, secretBundles: responseSecretBundles as any, }; } catch (err) { console.error('Failed to fetch secret bundles:', err); currentState = { ...currentState, secretBundles: [], }; } // images try { apiClient.identity = loginStatePart.getState().identity; const images = await apiClient.image.getImages(); currentState = { ...currentState, images: images as any, }; } catch (err) { console.error('Failed to fetch images:', err); currentState = { ...currentState, images: [], }; } // Clusters try { apiClient.identity = loginStatePart.getState().identity; const clusters = await apiClient.cluster.getClusters(); currentState = { ...currentState, clusters: clusters as any, } } catch (err) { console.error('Failed to fetch clusters:', err); currentState = { ...currentState, 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 try { apiClient.identity = loginStatePart.getState().identity; const services = await apiClient.services.getServices(); currentState = { ...currentState, services: services as any, }; } catch (error) { console.error('Failed to fetch services:', error); currentState = { ...currentState, services: [], }; } // Deployments try { apiClient.identity = loginStatePart.getState().identity; const responseDeployments = await apiClient.deployments.getDeployments(); 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(); try { apiClient.identity = loginStatePart.getState().identity; await apiClient.secretgroup.createSecretGroup(payloadArg.data); currentState = await dataState.dispatchAction(getAllDataAction, null); } catch (err) { console.error('Failed to create secret group:', err); } return currentState; return currentState; } ); export const deleteSecretGroupAction = dataState.createAction( async (statePartArg, payloadArg: { secretGroupId: string }) => { let currentState = statePartArg.getState(); try { apiClient.identity = loginStatePart.getState().identity; const sg = (currentState.secretGroups as any[])?.find(s => s.id === payloadArg.secretGroupId); if (sg) { await sg.delete(apiClient as any, sg.id); } currentState = await dataState.dispatchAction(getAllDataAction, null); } catch (err) { console.error('Failed to delete secret group:', err); } return currentState; } ); // SecretBundle Actions export const deleteSecretBundleAction = dataState.createAction( async (statePartArg, payloadArg: { configBundleId: string }) => { let currentState = statePartArg.getState(); try { apiClient.identity = loginStatePart.getState().identity; const sb = (currentState.secretBundles as any[])?.find(b => b.id === payloadArg.configBundleId); if (sb) { await sb.delete(apiClient as any, sb.id); } currentState = await dataState.dispatchAction(getAllDataAction, null); } catch (err) { console.error('Failed to delete secret bundle:', err); } 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(); apiClient.identity = loginStatePart.getState().identity; await apiClient.cluster.createClusterAdvanced(payloadArg.clusterName, payloadArg.setupMode); return await dataState.dispatchAction(getAllDataAction, null); } );