import * as plugins from './plugins.js'; import * as paths from './paths.js'; import { Cloudly } from './classes.cloudly.js'; import { logger } from './logger.js'; /** * handles incoming requests from CI to deploy new versions of apps */ export class CloudlyServer { /** * a reference to the cloudly instance */ public cloudlyRef: Cloudly; public additionalHandlers: plugins.typedserver.IRouteHandler[] = []; /** * the smartexpress server handling the actual requests */ public typedServer!: plugins.typedserver.TypedServer; public typedsocketServer!: plugins.typedsocket.TypedSocket; /** * typedrouter * @param cloudlyArg */ public typedrouter = new plugins.typedrequest.TypedRouter(); constructor(cloudlyArg: Cloudly) { this.cloudlyRef = cloudlyArg; this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter); } // ========= // LIFECYCLE // ========= /** * init the reception instance */ public async start() { logger.log('info', `cloudly domain is ${this.cloudlyRef.config.data.publicUrl}`); let sslCert: plugins.smartacme.Cert | undefined; if (this.cloudlyRef.config.data.sslMode === 'letsencrypt') { logger.log('info', `Using letsencrypt for ssl mode. Trying to obtain a certificate...`); logger.log('info', `This might take 10 minutes...`); sslCert = await this.cloudlyRef.letsencryptConnector.getCertificateForDomain( this.cloudlyRef.config.data.publicUrl!, ); logger.log( 'success', `Successfully obtained certificate for cloudly domain ${this.cloudlyRef.config.data.publicUrl}`, ); } else if (this.cloudlyRef.config.data.sslMode === 'external') { logger.log( 'info', `Using external certificate for ssl mode, meaning cloudly is not in charge of ssl termination.`, ); } // server this.typedServer = new plugins.typedserver.TypedServer({ cors: true, forceSsl: false, port: this.cloudlyRef.config.data.publicPort, ...(sslCert ? { privateKey: sslCert.privateKey, publicKey: sslCert.publicKey, } : {}), injectReload: true, serveDir: paths.distServeDir, watch: true, compression: { enabled: true, algorithms: ['gzip'], }, }); this.typedsocketServer = this.typedServer.typedsocket; this.typedServer.typedrouter.addTypedRouter(this.typedrouter); this.typedServer.addRoute( '/v2', 'ALL', async (ctx) => this.cloudlyRef.registryManager.handleHttpRequest(ctx), ); this.typedServer.addRoute( '/v2/*', 'ALL', async (ctx) => this.cloudlyRef.registryManager.handleHttpRequest(ctx), ); this.typedServer.addRoute( '/curlfresh/:scriptname', 'ALL', async (ctx) => this.cloudlyRef.nodeManager.curlfreshInstance.handleRequest(ctx), ); this.typedServer.addRoute( '/baseos/v1/nodes/register', 'POST', async (ctx) => this.cloudlyRef.baseOsManager.handleRegisterHttpRequest(ctx), ); this.typedServer.addRoute( '/baseos/v1/nodes/heartbeat', 'POST', async (ctx) => this.cloudlyRef.baseOsManager.handleHeartbeatHttpRequest(ctx), ); this.typedServer.addRoute( '/baseos/v1/images/:buildId/download', 'GET', async (ctx) => this.cloudlyRef.baseOsManager.handleImageDownloadHttpRequest(ctx), ); await this.typedServer.start(); } /** * stop the reception instance */ public async stop() { await this.typedServer?.stop(); } }