import type { Cloudly } from '../classes.cloudly.js'; import * as plugins from '../plugins.js'; import { PlatformBinding } from './classes.platformbinding.js'; import { PlatformProviderConfig } from './classes.platformproviderconfig.js'; export class CloudlyPlatformManager { public typedrouter = new plugins.typedrequest.TypedRouter(); public cloudlyRef: Cloudly; public capabilities: plugins.servezoneInterfaces.platform.IPlatformCapability[] = [ { id: 'email', title: 'Email', accessMode: 'rpc', defaultProviderType: 'cloudly' }, { id: 'sms', title: 'SMS', accessMode: 'rpc', defaultProviderType: 'cloudly' }, { id: 'pushnotification', title: 'Push Notifications', accessMode: 'rpc', defaultProviderType: 'cloudly' }, { id: 'letter', title: 'Letters', accessMode: 'rpc', defaultProviderType: 'cloudly' }, { id: 'ai', title: 'AI', accessMode: 'rpc', defaultProviderType: 'cloudly' }, { id: 'database', title: 'Database', accessMode: 'binding', defaultProviderType: 'docker' }, { id: 'objectstorage', title: 'Object Storage', accessMode: 'binding', defaultProviderType: 's3' }, { id: 'logging', title: 'Logging', accessMode: 'sidecar', defaultProviderType: 'corelog' }, { id: 'backup', title: 'Backup', accessMode: 'internal', defaultProviderType: 'corebackup' }, { id: 'sip', title: 'SIP', accessMode: 'rpc', defaultProviderType: 'cloudly' }, ]; get db() { return this.cloudlyRef.mongodbConnector.smartdataDb; } public CPlatformProviderConfig = plugins.smartdata.setDefaultManagerForDoc( this, PlatformProviderConfig, ); public CPlatformBinding = plugins.smartdata.setDefaultManagerForDoc(this, PlatformBinding); constructor(cloudlyRefArg: Cloudly) { this.cloudlyRef = cloudlyRefArg; this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getPlatformDesiredState', async (requestData) => { await this.passValidIdentity(requestData); return await this.getPlatformDesiredState(); }, ), ); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getPlatformCapabilities', async (requestData) => { await this.passValidIdentity(requestData); return { capabilities: this.capabilities, }; }, ), ); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getPlatformProviderConfigs', async (requestData) => { await this.passValidIdentity(requestData); const query = requestData.capability ? { capability: requestData.capability } : {}; return { providerConfigs: await this.getProviderConfigs(query), }; }, ), ); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'upsertPlatformProviderConfig', async (requestData) => { await this.passAdminIdentity(requestData); const providerConfig = await PlatformProviderConfig.upsertProviderConfig( requestData.providerConfig, ); return { providerConfig: await providerConfig.createSavableObject(), }; }, ), ); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'deletePlatformProviderConfigById', async (requestData) => { await this.passAdminIdentity(requestData); const providerConfig = await PlatformProviderConfig.getInstance({ id: requestData.providerConfigId, }); if (providerConfig) { await providerConfig.delete(); } return { success: true, }; }, ), ); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'getPlatformBindings', async (requestData) => { await this.passValidIdentity(requestData); return { bindings: await this.getBindings({ ...(requestData.serviceId ? { serviceId: requestData.serviceId } : {}), ...(requestData.capability ? { capability: requestData.capability } : {}), }), }; }, ), ); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'upsertPlatformBinding', async (requestData) => { await this.passAdminIdentity(requestData); const binding = await PlatformBinding.upsertBinding(requestData.binding); return { binding: await binding.createSavableObject(), }; }, ), ); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'updatePlatformBindingStatus', async (requestData) => { await this.passAdminOrClusterIdentity(requestData); const binding = await PlatformBinding.getInstance({ id: requestData.bindingId, }); if (!binding) { throw new plugins.typedrequest.TypedResponseError( `Platform binding ${requestData.bindingId} not found`, ); } binding.status = requestData.status; binding.updatedAt = Date.now(); if (requestData.endpoints) { binding.endpoints = requestData.endpoints; } if (requestData.credentials) { binding.credentials = requestData.credentials; } if (requestData.errorText !== undefined) { binding.errorText = requestData.errorText; } await binding.save(); return { binding: await binding.createSavableObject(), }; }, ), ); this.typedrouter.addTypedHandler( new plugins.typedrequest.TypedHandler( 'deletePlatformBindingById', async (requestData) => { await this.passAdminIdentity(requestData); const binding = await PlatformBinding.getInstance({ id: requestData.bindingId, }); if (binding) { await binding.delete(); } return { success: true, }; }, ), ); } public async start() {} public async stop() {} public async getPlatformDesiredState() { return { capabilities: this.capabilities, providerConfigs: await this.getProviderConfigs(), bindings: await this.getBindings(), }; } public async getProviderConfigs(queryArg: Record = {}) { const providerConfigs = await this.CPlatformProviderConfig.getInstances(queryArg); return await Promise.all( providerConfigs.map((providerConfig) => providerConfig.createSavableObject()), ); } public async getBindings(queryArg: Record = {}) { const bindings = await this.CPlatformBinding.getInstances(queryArg); return await Promise.all(bindings.map((binding) => binding.createSavableObject())); } private async passValidIdentity(requestData: { identity: plugins.servezoneInterfaces.data.IIdentity }) { await plugins.smartguard.passGuardsOrReject(requestData, [ this.cloudlyRef.authManager.validIdentityGuard, ]); } private async passAdminIdentity(requestData: { identity: plugins.servezoneInterfaces.data.IIdentity }) { await plugins.smartguard.passGuardsOrReject(requestData, [ this.cloudlyRef.authManager.adminIdentityGuard, ]); } private async passAdminOrClusterIdentity(requestData: { identity: plugins.servezoneInterfaces.data.IIdentity; }) { await this.passValidIdentity(requestData); if (requestData.identity.role !== 'admin' && requestData.identity.role !== 'cluster') { throw new plugins.typedrequest.TypedResponseError('identity must be admin or cluster'); } } }