From 124c4ca46fbd3a5f3811b38195ec6fea1f38162f Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Wed, 10 Sep 2025 19:06:16 +0000 Subject: [PATCH] feat: Enhance API client integration across web and CLI - Added typedRequestInterfaces import to plugins.ts for better type handling. - Updated CLI client to utilize environment variables for Cloudly API credentials and improved authentication flow. - Refactored appstate.ts to use a shared API client instance, reducing redundancy in API calls for various actions. - Simplified external registry actions in appstate.ts by leveraging the shared API client. - Updated CloudlyDashboard and CloudlyViewSettings components to utilize the shared API client for fetching settings and managing connections. - Removed redundant TypedRequest instances in favor of direct API client calls for improved performance and maintainability. - Exposed the API client in plugins.ts for easier access in UI components. --- ts_apiclient/classes.cloudlyapiclient.ts | 309 ++++++++++++++++++ ts_apiclient/plugins.ts | 6 +- ts_cliclient/index.ts | 23 +- ts_web/appstate.ts | 389 +++++++---------------- ts_web/elements/cloudly-dashboard.ts | 7 + ts_web/elements/cloudly-view-settings.ts | 27 +- ts_web/plugins.ts | 6 +- 7 files changed, 463 insertions(+), 304 deletions(-) diff --git a/ts_apiclient/classes.cloudlyapiclient.ts b/ts_apiclient/classes.cloudlyapiclient.ts index 1d35e60..e8c96f5 100644 --- a/ts_apiclient/classes.cloudlyapiclient.ts +++ b/ts_apiclient/classes.cloudlyapiclient.ts @@ -54,6 +54,21 @@ export class CloudlyApiClient { ); } + // Helper: resolve HTTP typedrequest endpoint + private get httpEndpoint() { + const base = (this.cloudlyUrl || '').replace(/\/$/, ''); + return `${base}/typedrequest`; + } + + // Helper: choose transport (WS if available, else HTTP) + private createWsRequest(operation: string) { + return this.typedsocketClient?.createTypedRequest(operation); + } + + private createHttpRequest(operation: string) { + return new plugins.typedrequest.TypedRequest(this.httpEndpoint, operation); + } + public async start() { this.typedsocketClient = await plugins.typedsocket.TypedSocket.createClient( this.typedrouter, @@ -170,9 +185,35 @@ export class CloudlyApiClient { }, createRegistry: async (optionsArg: Parameters[1]) => { return ExternalRegistry.createExternalRegistry(this, optionsArg); + }, + verifyRegistry: async (registryId: string): Promise<{ success: boolean; message: string; registry?: ExternalRegistry }> => { + const op = 'verifyExternalRegistry'; + const wsReq = this.createWsRequest(op); + const payload = { identity: this.identity, registryId } as any; + const resp = wsReq ? await wsReq.fire(payload) : await this.createHttpRequest(op).fire(payload); + let registryInstance: ExternalRegistry | undefined; + if (resp.registry) { + registryInstance = new ExternalRegistry(this); + Object.assign(registryInstance, resp.registry); + } + return { success: resp.success, message: resp.message, registry: registryInstance }; } } + // Auth helpers + public async loginWithUsernameAndPassword(username: string, password: string): Promise { + const op = 'adminLoginWithUsernameAndPassword'; + // Login endpoint is exposed via HTTP typedrequest + const httpReq = this.createHttpRequest(op); + const response = await httpReq.fire({ username, password }); + this.identity = response.identity; + // If WS connection is available, tag it with identity for server-side guards + if (this.typedsocketClient) { + try { this.typedsocketClient.addTag('identity', this.identity); } catch {} + } + return this.identity; + } + public image = { // Images getImageById: async (imageIdArg: string) => { @@ -183,6 +224,13 @@ export class CloudlyApiClient { }, createImage: async (optionsArg: Parameters[1]) => { return Image.createImage(this, optionsArg); + }, + deleteImage: async (imageId: string): Promise => { + const op = 'deleteImage'; + const payload = { identity: this.identity, imageId } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) { await wsReq.fire(payload); return; } + await this.createHttpRequest(op).fire(payload); } } @@ -196,6 +244,20 @@ export class CloudlyApiClient { }, createService: async (optionsArg: Parameters[1]) => { return Service.createService(this, optionsArg); + }, + updateService: async (serviceId: string, serviceData: plugins.servezoneInterfaces.data.IService['data']): Promise<{ service: plugins.servezoneInterfaces.data.IService }> => { + const op = 'updateService'; + const payload = { identity: this.identity, serviceId, serviceData } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + deleteService: async (serviceId: string): Promise => { + const op = 'deleteServiceById'; + const payload = { identity: this.identity, serviceId } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) { await wsReq.fire(payload); return; } + await this.createHttpRequest(op).fire(payload); } } @@ -237,4 +299,251 @@ export class CloudlyApiClient { return SecretGroup.createSecretGroup(this, optionsArg); } } + + // Settings API + public settings = { + getSettings: async (): Promise<{ + settings: plugins.servezoneInterfaces.data.ICloudlySettingsMasked + }> => { + const op = 'getSettings'; + const wsReq = this.createWsRequest(op); + if (wsReq) { + return wsReq.fire({ identity: this.identity }); + } + const httpReq = this.createHttpRequest(op); + return httpReq.fire({ identity: this.identity }); + }, + updateSettings: async (updates: Partial): Promise<{ + success: boolean; + message: string; + }> => { + const op = 'updateSettings'; + const wsReq = this.createWsRequest(op); + if (wsReq) { + return wsReq.fire({ identity: this.identity, updates }); + } + const httpReq = this.createHttpRequest(op); + return httpReq.fire({ identity: this.identity, updates }); + }, + testProviderConnection: async (provider: string): Promise<{ + connectionValid: boolean; + message: string; + }> => { + const op = 'testProviderConnection'; + const wsReq = this.createWsRequest(op); + if (wsReq) { + return wsReq.fire({ identity: this.identity, provider: provider as any }); + } + const httpReq = this.createHttpRequest(op); + return httpReq.fire({ identity: this.identity, provider: provider as any }); + } + } + + // Task API + public tasks = { + getTasks: async (): Promise<{ + tasks: Array<{ + name: string; + description: string; + category: 'maintenance' | 'deployment' | 'backup' | 'monitoring' | 'cleanup' | 'system' | 'security'; + schedule?: string; + lastRun?: number; + enabled: boolean; + }> + }> => { + const op = 'getTasks'; + const wsReq = this.createWsRequest(op); + if (wsReq) { + return wsReq.fire({ identity: this.identity }); + } + const httpReq = this.createHttpRequest(op); + return httpReq.fire({ identity: this.identity }); + }, + getTaskExecutions: async (filter?: any): Promise<{ + executions: plugins.servezoneInterfaces.data.ITaskExecution[]; + }> => { + const op = 'getTaskExecutions'; + const wsReq = this.createWsRequest(op); + if (wsReq) { + return wsReq.fire({ identity: this.identity, filter }); + } + const httpReq = this.createHttpRequest(op); + return httpReq.fire({ identity: this.identity, filter }); + }, + getTaskExecutionById: async (executionId: string): Promise<{ + execution: plugins.servezoneInterfaces.data.ITaskExecution + }> => { + const op = 'getTaskExecutionById'; + const wsReq = this.createWsRequest(op); + if (wsReq) { + return wsReq.fire({ identity: this.identity, executionId }); + } + const httpReq = this.createHttpRequest(op); + return httpReq.fire({ identity: this.identity, executionId }); + }, + triggerTask: async (taskName: string, userId?: string): Promise<{ + execution: plugins.servezoneInterfaces.data.ITaskExecution + }> => { + const op = 'triggerTask'; + const wsReq = this.createWsRequest(op); + if (wsReq) { + return wsReq.fire({ identity: this.identity, taskName, userId }); + } + const httpReq = this.createHttpRequest(op); + return httpReq.fire({ identity: this.identity, taskName, userId }); + }, + cancelTask: async (executionId: string): Promise<{ success: boolean }> => { + const op = 'cancelTask'; + const wsReq = this.createWsRequest(op); + if (wsReq) { + return wsReq.fire({ identity: this.identity, executionId }); + } + const httpReq = this.createHttpRequest(op); + return httpReq.fire({ identity: this.identity, executionId }); + } + } + + // Domain API + public domains = { + getDomains: async (): Promise<{ domains: plugins.servezoneInterfaces.data.IDomain[] }> => { + const op = 'getDomains'; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire({ identity: this.identity }); + return this.createHttpRequest(op).fire({ identity: this.identity }); + }, + getDomainById: async (domainId: string): Promise<{ domain: plugins.servezoneInterfaces.data.IDomain }> => { + const op = 'getDomainById'; + const payload = { identity: this.identity, domainId } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + createDomain: async (domainData: plugins.servezoneInterfaces.data.IDomain['data']): Promise<{ domain: plugins.servezoneInterfaces.data.IDomain }> => { + const op = 'createDomain'; + const payload = { identity: this.identity, domainData } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + updateDomain: async (domainId: string, domainData: Partial): Promise<{ domain: plugins.servezoneInterfaces.data.IDomain }> => { + const op = 'updateDomain'; + const payload = { identity: this.identity, domainId, domainData } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + deleteDomain: async (domainId: string): Promise<{ success: boolean }> => { + const op = 'deleteDomain'; + const payload = { identity: this.identity, domainId } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + verifyDomain: async (domainId: string, verificationMethod?: 'dns' | 'http' | 'email' | 'manual'): Promise<{ domain: plugins.servezoneInterfaces.data.IDomain; verificationResult: any }> => { + const op = 'verifyDomain'; + const payload = { identity: this.identity, domainId, verificationMethod } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + }; + + // DNS API + public dns = { + getDnsEntries: async (zone?: string): Promise<{ dnsEntries: plugins.servezoneInterfaces.data.IDnsEntry[] }> => { + const op = 'getDnsEntries'; + const payload = { identity: this.identity, zone } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + getDnsEntryById: async (dnsEntryId: string): Promise<{ dnsEntry: plugins.servezoneInterfaces.data.IDnsEntry }> => { + const op = 'getDnsEntryById'; + const payload = { identity: this.identity, dnsEntryId } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + createDnsEntry: async (dnsEntryData: plugins.servezoneInterfaces.data.IDnsEntry['data']): Promise<{ dnsEntry: plugins.servezoneInterfaces.data.IDnsEntry }> => { + const op = 'createDnsEntry'; + const payload = { identity: this.identity, dnsEntryData } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + updateDnsEntry: async (dnsEntryId: string, dnsEntryData: plugins.servezoneInterfaces.data.IDnsEntry['data']): Promise<{ dnsEntry: plugins.servezoneInterfaces.data.IDnsEntry }> => { + const op = 'updateDnsEntry'; + const payload = { identity: this.identity, dnsEntryId, dnsEntryData } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + deleteDnsEntry: async (dnsEntryId: string): Promise<{ success: boolean }> => { + const op = 'deleteDnsEntry'; + const payload = { identity: this.identity, dnsEntryId } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + getDnsZones: async (): Promise<{ zones: string[] }> => { + const op = 'getDnsZones'; + const payload = { identity: this.identity } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + }; + + // Deployment API + public deployments = { + getDeployments: async (): Promise<{ deployments: plugins.servezoneInterfaces.data.IDeployment[] }> => { + const op = 'getDeployments'; + const payload = { identity: this.identity } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + getDeploymentById: async (deploymentId: string): Promise<{ deployment: plugins.servezoneInterfaces.data.IDeployment }> => { + const op = 'getDeploymentById'; + const payload = { identity: this.identity, deploymentId } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + createDeployment: async (deploymentData: Partial): Promise<{ deployment: plugins.servezoneInterfaces.data.IDeployment }> => { + const op = 'createDeployment'; + const payload = { identity: this.identity, deploymentData } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + updateDeployment: async (deploymentId: string, deploymentData: Partial): Promise<{ deployment: plugins.servezoneInterfaces.data.IDeployment }> => { + const op = 'updateDeployment'; + const payload = { identity: this.identity, deploymentId, deploymentData } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + deleteDeployment: async (deploymentId: string): Promise<{ success: boolean }> => { + const op = 'deleteDeploymentById'; + const payload = { identity: this.identity, deploymentId } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + restartDeployment: async (deploymentId: string): Promise<{ success: boolean; deployment: plugins.servezoneInterfaces.data.IDeployment }> => { + const op = 'restartDeployment'; + const payload = { identity: this.identity, deploymentId } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + scaleDeployment: async (deploymentId: string, replicas: number): Promise<{ success: boolean; deployment: plugins.servezoneInterfaces.data.IDeployment }> => { + const op = 'scaleDeployment'; + const payload = { identity: this.identity, deploymentId, replicas } as any; + const wsReq = this.createWsRequest(op); + if (wsReq) return wsReq.fire(payload); + return this.createHttpRequest(op).fire(payload); + }, + }; } diff --git a/ts_apiclient/plugins.ts b/ts_apiclient/plugins.ts index a30d627..dbc9950 100644 --- a/ts_apiclient/plugins.ts +++ b/ts_apiclient/plugins.ts @@ -21,10 +21,12 @@ export { // @api.global scope import * as typedrequest from '@api.global/typedrequest'; import * as typedsocket from '@api.global/typedsocket'; +import * as typedRequestInterfaces from '@api.global/typedrequest-interfaces'; export { typedrequest, - typedsocket + typedsocket, + typedRequestInterfaces, } // @tsclass scope @@ -32,4 +34,4 @@ import * as tsclass from '@tsclass/tsclass'; export { tsclass, -} \ No newline at end of file +} diff --git a/ts_cliclient/index.ts b/ts_cliclient/index.ts index 656b566..2f2703e 100644 --- a/ts_cliclient/index.ts +++ b/ts_cliclient/index.ts @@ -1,11 +1,28 @@ import * as plugins from './plugins.js'; -import { CliClient } from "./classes.cliclient.js"; +import { CliClient } from './classes.cliclient.js'; export const runCli = async () => { const cliQenv = new plugins.qenv.Qenv(); + const cloudlyUrl = await cliQenv.getEnvVarOnDemand('CLOUDLY_URL'); + const token = process.env.CLOUDLY_TOKEN; + const username = process.env.CLOUDLY_USERNAME; + const password = process.env.CLOUDLY_PASSWORD; + const apiClient = new plugins.servezoneApi.CloudlyApiClient({ registerAs: 'cli', - cloudlyUrl: await cliQenv.getEnvVarOnDemand('CLOUDLY_URL'), + cloudlyUrl, }); + await apiClient.start(); + + if (token) { + await apiClient.getIdentityByToken(token, { tagConnection: true, statefullIdentity: true }); + } else if (username && password) { + await apiClient.loginWithUsernameAndPassword(username, password); + } else { + console.log('No credentials provided. Set CLOUDLY_TOKEN or CLOUDLY_USERNAME/CLOUDLY_PASSWORD.'); + } + const cliClient = new CliClient(apiClient); -}; \ No newline at end of file + // Default action example: list clusters when invoked without subcommands + await cliClient.getClusters(); +}; diff --git a/ts_web/appstate.ts b/ts_web/appstate.ts index bceb163..09cbe46 100644 --- a/ts_web/appstate.ts +++ b/ts_web/appstate.ts @@ -28,10 +28,21 @@ export const loginAction = loginStatePart.createAction<{ username: string; passw ...statePartArg.getState(), } }); - return { + 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; } ); @@ -84,6 +95,12 @@ export const dataState = await appstate.getStatePart( '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(); @@ -144,19 +161,13 @@ export const getAllDataAction = dataState.createAction(async (statePartArg) => { clusters: responseClusters.clusters, } - // External Registries - const trGetExternalRegistries = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'getExternalRegistries' - ); + // External Registries via shared API client try { - const responseExternalRegistries = await trGetExternalRegistries.fire({ - identity: loginStatePart.getState().identity, - }); + apiClient.identity = loginStatePart.getState().identity; + const registries = await apiClient.externalRegistry.getRegistries(); currentState = { ...currentState, - externalRegistries: responseExternalRegistries?.registries || [], + externalRegistries: registries as any, }; } catch (error) { console.error('Failed to fetch external registries:', error); @@ -210,16 +221,10 @@ export const getAllDataAction = dataState.createAction(async (statePartArg) => { }; } - // Domains - const trGetDomains = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'getDomains' - ); + // Domains via API client try { - const responseDomains = await trGetDomains.fire({ - identity: loginStatePart.getState().identity, - }); + apiClient.identity = loginStatePart.getState().identity; + const responseDomains = await apiClient.domains.getDomains(); currentState = { ...currentState, domains: responseDomains?.domains || [], @@ -232,16 +237,10 @@ export const getAllDataAction = dataState.createAction(async (statePartArg) => { }; } - // DNS Entries - const trGetDnsEntries = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'getDnsEntries' - ); + // DNS Entries via API client try { - const responseDnsEntries = await trGetDnsEntries.fire({ - identity: loginStatePart.getState().identity, - }); + apiClient.identity = loginStatePart.getState().identity; + const responseDnsEntries = await apiClient.dns.getDnsEntries(); currentState = { ...currentState, dnsEntries: responseDnsEntries?.dnsEntries || [], @@ -261,15 +260,8 @@ export const getAllDataAction = dataState.createAction(async (statePartArg) => { export const createServiceAction = dataState.createAction( async (statePartArg, payloadArg: { serviceData: plugins.interfaces.data.IService['data'] }) => { let currentState = statePartArg.getState(); - const trCreateService = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'createService' - ); - const response = await trCreateService.fire({ - identity: loginStatePart.getState().identity, - serviceData: payloadArg.serviceData, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.services.createService(payloadArg.serviceData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -278,16 +270,8 @@ export const createServiceAction = dataState.createAction( export const updateServiceAction = dataState.createAction( async (statePartArg, payloadArg: { serviceId: string; serviceData: plugins.interfaces.data.IService['data'] }) => { let currentState = statePartArg.getState(); - const trUpdateService = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'updateService' - ); - const response = await trUpdateService.fire({ - identity: loginStatePart.getState().identity, - serviceId: payloadArg.serviceId, - serviceData: payloadArg.serviceData, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.services.updateService(payloadArg.serviceId, payloadArg.serviceData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -296,15 +280,8 @@ export const updateServiceAction = dataState.createAction( export const deleteServiceAction = dataState.createAction( async (statePartArg, payloadArg: { serviceId: string }) => { let currentState = statePartArg.getState(); - const trDeleteService = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'deleteServiceById' - ); - const response = await trDeleteService.fire({ - identity: loginStatePart.getState().identity, - serviceId: payloadArg.serviceId, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.services.deleteService(payloadArg.serviceId); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -368,22 +345,9 @@ export const deleteSecretBundleAction = dataState.createAction( export const createImageAction = dataState.createAction( async (statePartArg, payloadArg: { imageName: string, description: string }) => { let currentState = statePartArg.getState(); - const trCreateImage = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'createImage' - ); - const response = await trCreateImage.fire({ - identity: loginStatePart.getState().identity, - name: payloadArg.imageName, - description: payloadArg.description, - }); - currentState = { - ...currentState, - ...{ - images: [...currentState.images, response.image], - }, - }; + 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; } ); @@ -391,21 +355,9 @@ export const createImageAction = dataState.createAction( export const deleteImageAction = dataState.createAction( async (statePartArg, payloadArg: { imageId: string }) => { let currentState = statePartArg.getState(); - const trDeleteImage = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'deleteImage' - ); - const response = await trDeleteImage.fire({ - identity: loginStatePart.getState().identity, - imageId: payloadArg.imageId, - }); - currentState = { - ...currentState, - ...{ - images: currentState.images.filter((image) => image.id !== payloadArg.imageId), - }, - }; + apiClient.identity = loginStatePart.getState().identity; + await apiClient.image.deleteImage(payloadArg.imageId); + currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } ); @@ -414,15 +366,8 @@ export const deleteImageAction = dataState.createAction( export const createDeploymentAction = dataState.createAction( async (statePartArg, payloadArg: { deploymentData: Partial }) => { let currentState = statePartArg.getState(); - const trCreateDeployment = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'createDeployment' - ); - const response = await trCreateDeployment.fire({ - identity: loginStatePart.getState().identity, - deploymentData: payloadArg.deploymentData, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.deployments.createDeployment(payloadArg.deploymentData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -431,16 +376,8 @@ export const createDeploymentAction = dataState.createAction( export const updateDeploymentAction = dataState.createAction( async (statePartArg, payloadArg: { deploymentId: string; deploymentData: Partial }) => { let currentState = statePartArg.getState(); - const trUpdateDeployment = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'updateDeployment' - ); - const response = await trUpdateDeployment.fire({ - identity: loginStatePart.getState().identity, - deploymentId: payloadArg.deploymentId, - deploymentData: payloadArg.deploymentData, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.deployments.updateDeployment(payloadArg.deploymentId, payloadArg.deploymentData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -449,15 +386,8 @@ export const updateDeploymentAction = dataState.createAction( export const deleteDeploymentAction = dataState.createAction( async (statePartArg, payloadArg: { deploymentId: string }) => { let currentState = statePartArg.getState(); - const trDeleteDeployment = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'deleteDeploymentById' - ); - const response = await trDeleteDeployment.fire({ - identity: loginStatePart.getState().identity, - deploymentId: payloadArg.deploymentId, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.deployments.deleteDeployment(payloadArg.deploymentId); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -467,15 +397,8 @@ export const deleteDeploymentAction = dataState.createAction( export const createDnsEntryAction = dataState.createAction( async (statePartArg, payloadArg: { dnsEntryData: plugins.interfaces.data.IDnsEntry['data'] }) => { let currentState = statePartArg.getState(); - const trCreateDnsEntry = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'createDnsEntry' - ); - const response = await trCreateDnsEntry.fire({ - identity: loginStatePart.getState().identity, - dnsEntryData: payloadArg.dnsEntryData, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.dns.createDnsEntry(payloadArg.dnsEntryData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -484,16 +407,8 @@ export const createDnsEntryAction = dataState.createAction( export const updateDnsEntryAction = dataState.createAction( async (statePartArg, payloadArg: { dnsEntryId: string; dnsEntryData: plugins.interfaces.data.IDnsEntry['data'] }) => { let currentState = statePartArg.getState(); - const trUpdateDnsEntry = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'updateDnsEntry' - ); - const response = await trUpdateDnsEntry.fire({ - identity: loginStatePart.getState().identity, - dnsEntryId: payloadArg.dnsEntryId, - dnsEntryData: payloadArg.dnsEntryData, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.dns.updateDnsEntry(payloadArg.dnsEntryId, payloadArg.dnsEntryData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -502,15 +417,8 @@ export const updateDnsEntryAction = dataState.createAction( export const deleteDnsEntryAction = dataState.createAction( async (statePartArg, payloadArg: { dnsEntryId: string }) => { let currentState = statePartArg.getState(); - const trDeleteDnsEntry = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'deleteDnsEntry' - ); - const response = await trDeleteDnsEntry.fire({ - identity: loginStatePart.getState().identity, - dnsEntryId: payloadArg.dnsEntryId, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.dns.deleteDnsEntry(payloadArg.dnsEntryId); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -520,15 +428,8 @@ export const deleteDnsEntryAction = dataState.createAction( export const createDomainAction = dataState.createAction( async (statePartArg, payloadArg: { domainData: plugins.interfaces.data.IDomain['data'] }) => { let currentState = statePartArg.getState(); - const trCreateDomain = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'createDomain' - ); - const response = await trCreateDomain.fire({ - identity: loginStatePart.getState().identity, - domainData: payloadArg.domainData, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.domains.createDomain(payloadArg.domainData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -537,16 +438,8 @@ export const createDomainAction = dataState.createAction( export const updateDomainAction = dataState.createAction( async (statePartArg, payloadArg: { domainId: string; domainData: plugins.interfaces.data.IDomain['data'] }) => { let currentState = statePartArg.getState(); - const trUpdateDomain = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'updateDomain' - ); - const response = await trUpdateDomain.fire({ - identity: loginStatePart.getState().identity, - domainId: payloadArg.domainId, - domainData: payloadArg.domainData, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.domains.updateDomain(payloadArg.domainId, payloadArg.domainData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -555,15 +448,8 @@ export const updateDomainAction = dataState.createAction( export const deleteDomainAction = dataState.createAction( async (statePartArg, payloadArg: { domainId: string }) => { let currentState = statePartArg.getState(); - const trDeleteDomain = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'deleteDomain' - ); - const response = await trDeleteDomain.fire({ - identity: loginStatePart.getState().identity, - domainId: payloadArg.domainId, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.domains.deleteDomain(payloadArg.domainId); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -572,16 +458,8 @@ export const deleteDomainAction = dataState.createAction( export const verifyDomainAction = dataState.createAction( async (statePartArg, payloadArg: { domainId: string; verificationMethod?: 'dns' | 'http' | 'email' | 'manual' }) => { let currentState = statePartArg.getState(); - const trVerifyDomain = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'verifyDomain' - ); - const response = await trVerifyDomain.fire({ - identity: loginStatePart.getState().identity, - domainId: payloadArg.domainId, - verificationMethod: payloadArg.verificationMethod, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.domains.verifyDomain(payloadArg.domainId, payloadArg.verificationMethod); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -591,15 +469,8 @@ export const verifyDomainAction = dataState.createAction( export const createExternalRegistryAction = dataState.createAction( async (statePartArg, payloadArg: { registryData: plugins.interfaces.data.IExternalRegistry['data'] }) => { let currentState = statePartArg.getState(); - const trCreateRegistry = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'createExternalRegistry' - ); - const response = await trCreateRegistry.fire({ - identity: loginStatePart.getState().identity, - registryData: payloadArg.registryData, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.externalRegistry.createRegistry(payloadArg.registryData); currentState = await dataState.dispatchAction(getAllDataAction, null); return currentState; } @@ -608,17 +479,17 @@ export const createExternalRegistryAction = dataState.createAction( export const updateExternalRegistryAction = dataState.createAction( async (statePartArg, payloadArg: { registryId: string; registryData: plugins.interfaces.data.IExternalRegistry['data'] }) => { let currentState = statePartArg.getState(); - const trUpdateRegistry = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'updateExternalRegistry' - ); - const response = await trUpdateRegistry.fire({ - identity: loginStatePart.getState().identity, - registryId: payloadArg.registryId, - registryData: payloadArg.registryData, - }); - currentState = await dataState.dispatchAction(getAllDataAction, null); + 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; } ); @@ -626,16 +497,16 @@ export const updateExternalRegistryAction = dataState.createAction( export const deleteExternalRegistryAction = dataState.createAction( async (statePartArg, payloadArg: { registryId: string }) => { let currentState = statePartArg.getState(); - const trDeleteRegistry = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'deleteExternalRegistryById' - ); - const response = await trDeleteRegistry.fire({ - identity: loginStatePart.getState().identity, - registryId: payloadArg.registryId, - }); - currentState = await dataState.dispatchAction(getAllDataAction, null); + 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; } ); @@ -643,26 +514,27 @@ export const deleteExternalRegistryAction = dataState.createAction( export const verifyExternalRegistryAction = dataState.createAction( async (statePartArg, payloadArg: { registryId: string }) => { let currentState = statePartArg.getState(); - const trVerifyRegistry = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'verifyExternalRegistry' - ); - const response = await trVerifyRegistry.fire({ - identity: loginStatePart.getState().identity, - registryId: payloadArg.registryId, - }); - - if (response.success && response.registry) { - // Update the registry in the state with the verified status - currentState = { - ...currentState, - externalRegistries: currentState.externalRegistries?.map(reg => - reg.id === payloadArg.registryId ? response.registry : reg - ) || [], - }; + 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; } ); @@ -672,14 +544,8 @@ export const taskActions = { getTasks: dataState.createAction( async (statePartArg, payloadArg: {}) => { const currentState = statePartArg.getState(); - const trGetTasks = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'getTasks' - ); - const response = await trGetTasks.fire({ - identity: loginStatePart.getState().identity, - }); + apiClient.identity = loginStatePart.getState().identity; + const response = await apiClient.tasks.getTasks(); return { ...currentState, tasks: response.tasks, @@ -690,15 +556,8 @@ export const taskActions = { getTaskExecutions: dataState.createAction( async (statePartArg, payloadArg: { filter?: any }) => { const currentState = statePartArg.getState(); - const trGetTaskExecutions = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'getTaskExecutions' - ); - const response = await trGetTaskExecutions.fire({ - identity: loginStatePart.getState().identity, - filter: payloadArg.filter, - }); + apiClient.identity = loginStatePart.getState().identity; + const response = await apiClient.tasks.getTaskExecutions(payloadArg.filter); return { ...currentState, taskExecutions: response.executions, @@ -709,15 +568,8 @@ export const taskActions = { getTaskExecutionById: dataState.createAction( async (statePartArg, payloadArg: { executionId: string }) => { const currentState = statePartArg.getState(); - const trGetTaskExecutionById = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'getTaskExecutionById' - ); - const response = await trGetTaskExecutionById.fire({ - identity: loginStatePart.getState().identity, - executionId: payloadArg.executionId, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.tasks.getTaskExecutionById(payloadArg.executionId); return currentState; } ), @@ -725,16 +577,8 @@ export const taskActions = { triggerTask: dataState.createAction( async (statePartArg, payloadArg: { taskName: string; userId?: string }) => { const currentState = statePartArg.getState(); - const trTriggerTask = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'triggerTask' - ); - const response = await trTriggerTask.fire({ - identity: loginStatePart.getState().identity, - taskName: payloadArg.taskName, - userId: payloadArg.userId, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.tasks.triggerTask(payloadArg.taskName, payloadArg.userId); return currentState; } ), @@ -742,15 +586,8 @@ export const taskActions = { cancelTask: dataState.createAction( async (statePartArg, payloadArg: { executionId: string }) => { const currentState = statePartArg.getState(); - const trCancelTask = - new domtools.plugins.typedrequest.TypedRequest( - '/typedrequest', - 'cancelTask' - ); - const response = await trCancelTask.fire({ - identity: loginStatePart.getState().identity, - executionId: payloadArg.executionId, - }); + apiClient.identity = loginStatePart.getState().identity; + await apiClient.tasks.cancelTask(payloadArg.executionId); return currentState; } ), diff --git a/ts_web/elements/cloudly-dashboard.ts b/ts_web/elements/cloudly-dashboard.ts index fc60194..c4e3313 100644 --- a/ts_web/elements/cloudly-dashboard.ts +++ b/ts_web/elements/cloudly-dashboard.ts @@ -217,6 +217,13 @@ export class CloudlyDashboard extends DeesElement { console.log(loginState); if (loginState.identity) { this.identity = loginState.identity; + try { + appstate.apiClient.identity = loginState.identity; + if (!appstate.apiClient['typedsocketClient']) { + await appstate.apiClient.start(); + } + try { appstate.apiClient.typedsocketClient.addTag('identity', appstate.apiClient.identity); } catch {} + } catch (e) { console.warn('Failed to initialize API client WS', e); } await simpleLogin.switchToSlottedContent(); await appstate.dataState.dispatchAction(appstate.getAllDataAction, null); } diff --git a/ts_web/elements/cloudly-view-settings.ts b/ts_web/elements/cloudly-view-settings.ts index 0b2c161..ce2308f 100644 --- a/ts_web/elements/cloudly-view-settings.ts +++ b/ts_web/elements/cloudly-view-settings.ts @@ -93,13 +93,8 @@ export class CloudlyViewSettings extends DeesElement { private async loadSettings() { this.isLoading = true; try { - const trRequest = new plugins.deesDomtools.plugins.typedrequest.TypedRequest< - plugins.interfaces.requests.settings.IRequest_GetSettings - >( - '/typedrequest', - 'getSettings' - ); - const response = await trRequest.fire({}); + // Use shared API client + const response = await appstate.apiClient.settings.getSettings(); this.settings = response.settings; } catch (error) { console.error('Failed to load settings:', error); @@ -128,13 +123,7 @@ export class CloudlyViewSettings extends DeesElement { } console.log('Updates to send:', updates); - const trRequest = new plugins.deesDomtools.plugins.typedrequest.TypedRequest< - plugins.interfaces.requests.settings.IRequest_UpdateSettings - >( - '/typedrequest', - 'updateSettings' - ); - const response = await trRequest.fire({ updates }); + const response = await appstate.apiClient.settings.updateSettings(updates); if (response.success) { plugins.deesCatalog.DeesToast.createAndShow({ @@ -159,13 +148,7 @@ export class CloudlyViewSettings extends DeesElement { private async testConnection(provider: string) { this.isLoading = true; try { - const trRequest = new plugins.deesDomtools.plugins.typedrequest.TypedRequest< - plugins.interfaces.requests.settings.IRequest_TestProviderConnection - >( - '/typedrequest', - 'testProviderConnection' - ); - const response = await trRequest.fire({ provider: provider as any }); + const response = await appstate.apiClient.settings.testProviderConnection(provider); this.testResults = { ...this.testResults, @@ -475,4 +458,4 @@ export class CloudlyViewSettings extends DeesElement { `; } -} \ No newline at end of file +} diff --git a/ts_web/plugins.ts b/ts_web/plugins.ts index d8f3d2b..4e22629 100644 --- a/ts_web/plugins.ts +++ b/ts_web/plugins.ts @@ -18,4 +18,8 @@ import * as smartstate from '@push.rocks/smartstate'; export { webjwt, smartstate, -} \ No newline at end of file +} + +// Expose API client so UI can share it with CLI +import * as servezoneApi from '@serve.zone/api'; +export { servezoneApi };