fix(core): update

This commit is contained in:
2024-04-20 12:21:41 +02:00
commit c24262f765
84 changed files with 14340 additions and 0 deletions

8
ts/00_commitinfo_data.ts Normal file
View File

@@ -0,0 +1,8 @@
/**
* autocreated commitinfo by @pushrocks/commitinfo
*/
export const commitinfo = {
name: '@serve.zone/cloudly',
version: '1.0.214',
description: 'A cloud manager utilizing Docker Swarmkit, designed for operations on Cloudron, and supports various cloud platforms like DigitalOcean, Hetzner Cloud, and Cloudflare.'
}

View File

@@ -0,0 +1,120 @@
import * as plugins from './cloudly.plugins.js';
import { CloudlyConfig } from './cloudly.classes.config.js';
// interfaces
import {} from '@tsclass/tsclass';
// Cloudly mods
import { CloudlyInfo } from './cloudly.classes.cloudlyinfo.js';
import { CloudlyServer } from './cloudly.classes.server.js';
// connectors
import { CloudflareConnector } from './connector.cloudflare/connector.js';
import { LetsencryptConnector } from './connector.letsencrypt/connector.js';
import { MongodbConnector } from './connector.mongodb/connector.js';
// processes
import { CloudlyCoreflowManager } from './manager.coreflow/coreflowmanager.js';
import { ClusterManager } from './manager.cluster/clustermanager.js';
import { CloudlyTaskmanager } from './manager.task/taskmanager.js';
import { CloudlyVersionManager } from './manager.version/versionmanager.js';
import { CloudlySecretManager } from './manager.secret/classes.secretmanager.js'
import { CloudlyServerManager } from './manager.server/servermanager.js';
import { ExternalApiManager } from './manager.status/statusmanager.js';
import { ImageManager } from './manager.image/classes.imagemanager.js';
import { logger } from './cloudly.logging.js';
/**
* Cloudly class can be used to instantiate a cloudly server.
* It is implemented as class in order to make it easier ro write node services that are more adjusted to invidual services
*
* ```ts
* const mycloudly = new Cloudly ({...})
* ```
*/
export class Cloudly {
public typedrouter = new plugins.typedrequest.TypedRouter();
public config: CloudlyConfig;
public logger: plugins.smartlog.Smartlog;
public ready: Promise<any>;
// mods
public cloudlyInfo: CloudlyInfo;
public server: CloudlyServer;
// connectors
public cloudflareConnector: CloudflareConnector;
public letsencryptConnector: LetsencryptConnector;
public mongodbConnector: MongodbConnector;
// managers
public secretManager: CloudlySecretManager;
public clusterManager: ClusterManager;
public coreflowManager: CloudlyCoreflowManager;
public externalApiManager: ExternalApiManager;
public imageManager: ImageManager;
public taskManager: CloudlyTaskmanager;
public versionManager: CloudlyVersionManager;
public serverManager: CloudlyServerManager;
private readyDeferred = new plugins.smartpromise.Deferred();
constructor() {
this.cloudlyInfo = new CloudlyInfo(this);
this.config = new CloudlyConfig(this);
this.logger = logger;
this.server = new CloudlyServer(this);
this.ready = this.readyDeferred.promise;
// connectors
this.mongodbConnector = new MongodbConnector(this); // database needs to go first
this.cloudflareConnector = new CloudflareConnector(this);
this.letsencryptConnector = new LetsencryptConnector(this);
// processes
this.clusterManager = new ClusterManager(this);
this.coreflowManager = new CloudlyCoreflowManager(this);
this.externalApiManager = new ExternalApiManager(this);
this.imageManager = new ImageManager(this);
this.taskManager = new CloudlyTaskmanager(this);
this.versionManager = new CloudlyVersionManager(this);
this.secretManager = new CloudlySecretManager(this);
this.serverManager = new CloudlyServerManager(this);
}
/**
* starts the cloudly instance and
* @param configArg
*/
public async start() {
// config
await this.config.init();
// manageers
await this.secretManager.start();
await this.serverManager.start();
// connectors
await this.mongodbConnector.init();
await this.cloudflareConnector.init();
await this.letsencryptConnector.init();
await this.clusterManager.init();
await this.server.start();
this.readyDeferred.resolve();
// start the managers
this.imageManager.start();
}
/**
* stop the reception instance
*/
public async stop() {
await this.server.stop();
await this.letsencryptConnector.stop();
await this.mongodbConnector.stop();
await this.secretManager.stop();
}
}

View File

@@ -0,0 +1,13 @@
import * as plugins from './cloudly.plugins.js';
import * as paths from './cloudly.paths.js';
import { Cloudly } from './index.js';
export class CloudlyInfo {
public cloudlyRef: Cloudly;
constructor(cloudlyRefArg: Cloudly) {
this.cloudlyRef = cloudlyRefArg;
}
public projectInfo = new plugins.projectinfo.ProjectInfo(paths.packageDir);
}

View File

@@ -0,0 +1,79 @@
import * as plugins from './cloudly.plugins.js';
import * as paths from './cloudly.paths.js';
import { logger } from './cloudly.logging.js';
import type { Cloudly } from './cloudly.classes.cloudly.js';
/**
* the main cloudly config
*/
export class CloudlyConfig {
public cloudlyRef: Cloudly;
public appData: plugins.npmextra.AppData<plugins.servezoneInterfaces.data.ICloudlyConfig>;
public data: plugins.servezoneInterfaces.data.ICloudlyConfig
// authentication and settings
public smartjwtInstance: plugins.smartjwt.SmartJwt;
constructor(cloudlyRefArg: Cloudly) {
this.cloudlyRef = cloudlyRefArg;
}
public async init() {
this.appData = await plugins.npmextra.AppData.createAndInit<plugins.servezoneInterfaces.data.ICloudlyConfig>({
envMapping: {
cfToken: 'CF_TOKEN',
environment: 'SERVEZONE_ENVIRONMENT' as 'production' | 'integration',
letsEncryptEmail: 'hard:domains@lossless.org',
hetznerToken: 'HETZNER_API_TOKEN',
letsEncryptPrivateKey: null,
publicUrl: 'SERVEZONE_URL',
publicPort: 'SERVEZONE_PORT',
mongoDescriptor: {
mongoDbUrl: 'MONGODB_URL',
mongoDbName: 'MONGODB_DATABASE',
mongoDbUser: 'MONGODB_USER',
mongoDbPass: 'MONGODB_PASSWORD',
},
s3Descriptor: {
endpoint: 'S3_ENDPOINT',
accessKey: 'S3_ACCESSKEY',
accessSecret: 'S3_SECRETKEY',
port: 'S3_PORT', // Note: This will remain as a string. Ensure to parse it to an integer where it's used.
useSsl: true,
},
sslMode: 'SERVEZONE_SSLMODE' as plugins.servezoneInterfaces.data.ICloudlyConfig['sslMode'],
},
requiredKeys: [
'cfToken',
'hetznerToken',
'letsEncryptEmail',
'publicUrl',
'publicPort',
'sslMode',
'environment',
'mongoDescriptor',
],
});
this.smartjwtInstance = new plugins.smartjwt.SmartJwt();
const kvStore = await this.appData.getKvStore();
const existingJwtKeys: plugins.tsclass.network.IJwtKeypair = await kvStore.readKey('jwtKeys');
if (!existingJwtKeys) {
await this.smartjwtInstance.createNewKeyPair();
const newJwtKeys = this.smartjwtInstance.getKeyPairAsJson();
await kvStore.writeKey('jwtKeys', newJwtKeys);
} else {
this.smartjwtInstance.setKeyPairAsJson(existingJwtKeys);
}
this.data = await kvStore.readAll();
const missingKeys = await this.appData.logMissingKeys();
if (missingKeys.length > 0) {
logger.log('error', `missing keys: ${missingKeys.join(', ')}`);
throw new Error('missing keys');
}
}
}

View File

@@ -0,0 +1,96 @@
import * as plugins from './cloudly.plugins.js';
import * as paths from './cloudly.paths.js';
import { Cloudly } from './cloudly.classes.cloudly.js';
import { logger } from './cloudly.logging.js';
/**
* handles incoming requests from CI to deploy new versions of apps
*/
export class CloudlyServer {
/**
* a reference to the cloudly instance
*/
private cloudlyRef: Cloudly;
/**
* 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;
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.`)
}
interface IRequestGuardData {
req: plugins.typedserver.Request;
res: plugins.typedserver.Response;
}
// guards
const guardIp = new plugins.smartguard.Guard<IRequestGuardData>(async (dataArg) => {
if (true) {
return true;
} else {
dataArg.res.status(500);
dataArg.res.send(`Not allowed to perform this operation!`);
dataArg.res.end();
return false;
}
});
// 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,
enableCompression: true,
preferredCompressionMethod: 'gzip',
});
this.typedServer.typedrouter.addTypedRouter(this.typedrouter);
await this.typedServer.start();
}
/**
* stop the reception instance
*/
public async stop() {
await this.typedServer.stop();
}
}

16
ts/cloudly.logging.ts Normal file
View File

@@ -0,0 +1,16 @@
import * as plugins from './cloudly.plugins.js';
import * as paths from './cloudly.paths.js';
export const logger = new plugins.smartlog.Smartlog({
logContext: {
company: null,
environment: null,
runtime: null,
zone: null,
companyunit: null,
containerName: null,
}
});
logger.enableConsole({
captureAll: false
});

5
ts/cloudly.paths.ts Normal file
View File

@@ -0,0 +1,5 @@
import * as plugins from './cloudly.plugins.js';
export const packageDir = plugins.path.join(plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), '../');
export const nogitDir = plugins.path.join(packageDir, '.nogit/');
export const distServeDir = plugins.path.join(packageDir, './dist_serve');

77
ts/cloudly.plugins.ts Normal file
View File

@@ -0,0 +1,77 @@
// node native
import * as path from 'path';
export { path };
// @apiglobal scope
import * as typedrequest from '@api.global/typedrequest';
import * as typedsocket from '@api.global/typedsocket';
export { typedrequest, typedsocket };
// @mojoio scope
import * as cloudflare from '@apiclient.xyz/cloudflare';
import * as digitalocean from '@apiclient.xyz/digitalocean';
import * as hetznercloud from '@apiclient.xyz/hetznercloud';
import * as slack from '@apiclient.xyz/slack';
export { cloudflare, digitalocean, hetznercloud, slack };
// @tsclass scope
import * as tsclass from '@tsclass/tsclass';
export { tsclass };
// @push.rocks scope
import * as npmextra from '@push.rocks/npmextra';
import * as projectinfo from '@push.rocks/projectinfo';
import * as qenv from '@push.rocks/qenv';
import * as smartacme from '@push.rocks/smartacme';
import * as smartbucket from '@push.rocks/smartbucket';
import * as smartcli from '@push.rocks/smartcli';
import * as smartdata from '@push.rocks/smartdata';
import * as smartdelay from '@push.rocks/smartdelay';
import * as smartexit from '@push.rocks/smartexit';
import * as typedserver from '@api.global/typedserver';
import * as smartfile from '@push.rocks/smartfile';
import * as smartguard from '@push.rocks/smartguard';
import * as smartjson from '@push.rocks/smartjson';
import * as smartjwt from '@push.rocks/smartjwt';
import * as smartlog from '@push.rocks/smartlog';
import * as smartpath from '@push.rocks/smartpath';
import * as smartpromise from '@push.rocks/smartpromise';
import * as smartrequest from '@push.rocks/smartrequest';
import * as smartssh from '@push.rocks/smartssh';
import * as smartstring from '@push.rocks/smartstring';
import * as smartunique from '@push.rocks/smartunique';
import * as taskbuffer from '@push.rocks/taskbuffer';
export {
npmextra,
projectinfo,
qenv,
smartacme,
smartbucket,
smartcli,
smartdata,
smartexit,
typedserver,
smartdelay,
smartfile,
smartguard,
smartjson,
smartjwt,
smartlog,
smartpath,
smartpromise,
smartrequest,
smartssh,
smartstring,
smartunique,
taskbuffer,
};
// @servezone scope
import * as servezoneInterfaces from '@serve.zone/interfaces';
export { servezoneInterfaces };

View File

@@ -0,0 +1,19 @@
import * as plugins from '../cloudly.plugins.js';
import { Cloudly } from '../cloudly.classes.cloudly.js';
/**
* the portion of Cloudflare responsible
*/
export class CloudflareConnector {
private cloudlyRef: Cloudly;
public cloudflare: plugins.cloudflare.CloudflareAccount;
constructor(cloudlyArg: Cloudly) {
this.cloudlyRef = cloudlyArg;
}
// init the instance
public async init() {
this.cloudflare = new plugins.cloudflare.CloudflareAccount(this.cloudlyRef.config.data.cfToken);
}
}

View File

@@ -0,0 +1,46 @@
import * as plugins from '../cloudly.plugins.js';
import { Cloudly } from '../cloudly.classes.cloudly.js';
export class LetsencryptConnector {
private cloudlyRef: Cloudly;
private smartacme: plugins.smartacme.SmartAcme;
constructor(cloudlyArg: Cloudly) {
this.cloudlyRef = cloudlyArg;
}
public async getCertificateForDomain(domainName: string) {
const cert = await this.smartacme.getCertificateForDomain(domainName);
return cert;
}
/**
* inits letsencrypt
*/
public async init() {
this.smartacme = new plugins.smartacme.SmartAcme({
accountEmail: this.cloudlyRef.config.data.letsEncryptEmail,
accountPrivateKey: this.cloudlyRef.config.data.letsEncryptPrivateKey,
environment: this.cloudlyRef.config.data.environment,
setChallenge: async (dnsChallenge) => {
await this.cloudlyRef.cloudflareConnector.cloudflare.convenience.acmeSetDnsChallenge(
dnsChallenge
);
},
removeChallenge: async (dnsChallenge) => {
await this.cloudlyRef.cloudflareConnector.cloudflare.convenience.acmeRemoveDnsChallenge(
dnsChallenge
);
},
mongoDescriptor: this.cloudlyRef.config.data.mongoDescriptor,
});
await this.smartacme.init();
}
/**
* stops the instance
*/
public async stop() {
await this.smartacme.stop();
}
}

View File

@@ -0,0 +1,21 @@
import * as plugins from '../cloudly.plugins.js';
import { Cloudly } from '../cloudly.classes.cloudly.js';
export class MongodbConnector {
// INSTANCE
private cloudlyRef: Cloudly;
public smartdataDb: plugins.smartdata.SmartdataDb;
constructor(cloudlyRefArg: Cloudly) {
this.cloudlyRef = cloudlyRefArg;
}
public async init() {
this.smartdataDb = new plugins.smartdata.SmartdataDb(this.cloudlyRef.config.data.mongoDescriptor);
await this.smartdataDb.init();
}
public async stop() {
await this.smartdataDb.close();
}
}

View File

View File

@@ -0,0 +1,83 @@
import * as plugins from '../cloudly.plugins.js';
// Create an array to hold 10 ISecretGroup objects
const demoSecretGroups: plugins.servezoneInterfaces.data.ISecretGroup[] = [];
// Generate 10 ISecretGroup objects
for (let i = 1; i <= 10; i++) {
const secretGroup: plugins.servezoneInterfaces.data.ISecretGroup = {
id: `${plugins.smartunique.shortId(8)}`,
data: {
name: `Demo Secret Group ${i}`,
description: `This is a demo secret group for testing purposes ${i}`,
key: `CI_RUNNER_TOKEN_${i}`,
priority: i,
tags: [
{
key: 'project',
value: `my_project_${i}`,
},
{
key: 'environment',
value: i % 2 === 0 ? 'staging' : 'production',
},
],
environments: {
production: {
value: `prod_secret_value_${i}`,
lastUpdated: 1630522000 + i,
history: [
{
timestamp: String(1630521000 + i),
value: `old_prod_value_${i}`,
},
],
},
staging: {
value: `stag_secret_value_${i}`,
updateToken: `updateToken${i}`,
lastUpdated: 1630522500 + i,
history: [
{
timestamp: String(1630521500 + i),
value: `old_stag_value_${i}`,
},
],
},
},
},
};
// Push each ISecretGroup object into the array
demoSecretGroups.push(secretGroup);
}
// Create an array to hold 10 IConfigBundle objects
const demoConfigBundles: plugins.servezoneInterfaces.data.ISecretBundle[] = [];
// Generate 10 IConfigBundle objects that match demoSecretGroups
for (let i = 0; i < demoSecretGroups.length; i++) {
const secretGroup = demoSecretGroups[i];
const configBundle: plugins.servezoneInterfaces.data.ISecretBundle = {
id: `configBundleId${i + 1}`,
data: {
name: `Demo Config Bundle ${i + 1}`,
description: 'Demo Purpose',
includedSecretGroupIds: [secretGroup.id],
includedTags: secretGroup.data.tags,
authorizations: Object.keys(secretGroup.data.environments).map((env) => {
return {
secretAccessKey: `mockSecretAccessKeyFor${env}`,
environment: env,
};
}),
},
};
// Push each IConfigBundle object into the array
demoConfigBundles.push(configBundle);
}
// Exporting the array of demo IConfigBundle objects
export { demoSecretGroups, demoConfigBundles };

38
ts/demo/index.ts Normal file
View File

@@ -0,0 +1,38 @@
import type { Cloudly } from '../cloudly.classes.cloudly.js';
export const installDemoData = async (cloudlyRef: Cloudly) => {
// ================================================================================
// SECRETS
const demoDataSecrets = await import('./demo.data.secrets.js');
const secretGroups = await cloudlyRef.secretManager.CSecretGroup.getInstances({});
for (const secretGroup of secretGroups) {
await secretGroup.delete();
}
const secretBundles = await cloudlyRef.secretManager.CSecretBundle.getInstances({});
for (const secretBundle of secretBundles) {
await secretBundle.delete();
}
for (const secretData of demoDataSecrets.demoSecretGroups) {
const secretGroup = new cloudlyRef.secretManager.CSecretGroup();
Object.assign(secretGroup, secretData);
await secretGroup.save();
}
for (const secretBundleData of demoDataSecrets.demoConfigBundles) {
const secretBundle = new cloudlyRef.secretManager.CSecretBundle();
Object.assign(secretBundle, secretBundleData);
await secretBundle.save();
}
// ================================================================================
// CLUSTERS
const clusters = await cloudlyRef.clusterManager.CCluster.getInstances({});
for (const cluster of clusters) {
await cluster.delete();
}
}

28
ts/index.ts Normal file
View File

@@ -0,0 +1,28 @@
import * as early from '@push.rocks/early';
early.start('cloudly');
import * as plugins from './cloudly.plugins.js';
import * as paths from './cloudly.paths.js';
import { Cloudly } from './cloudly.classes.cloudly.js';
import { logger } from './cloudly.logging.js';
const cloudlyQenv = new plugins.qenv.Qenv(paths.packageDir, paths.nogitDir, true);
early.stop();
/**
* starts the cloudly instance
*/
const runCli = async () => {
logger.log('info', process.env.SERVEZONE_ENVIRONMENT);
const cloudlyInstance = new Cloudly();
logger.log(
'info',
`running in environment ${await cloudlyQenv.getEnvVarOnDemand('SERVEZONE_ENVIRONMENT')}`
);
await cloudlyInstance.start();
const demoMod = await import('./demo/index.js');
demoMod.installDemoData(cloudlyInstance);
};
export { runCli, Cloudly };

View File

@@ -0,0 +1,31 @@
import * as plugins from '../cloudly.plugins.js';
/*
* cluster defines a swarmkit cluster
*/
@plugins.smartdata.Manager()
export class Cluster extends plugins.smartdata.SmartDataDbDoc<Cluster, plugins.servezoneInterfaces.data.ICluster> {
// STATIC
public static async fromConfigObject(
configObjectArg: plugins.servezoneInterfaces.data.ICluster
) {
const newCluster = new Cluster();
Object.assign(newCluster, configObjectArg);
return newCluster;
}
// INSTANCE
@plugins.smartdata.unI()
public id: string;
@plugins.smartdata.svDb()
public data: plugins.servezoneInterfaces.data.ICluster['data'];
constructor() {
super();
}
public async getServices(): Promise<plugins.servezoneInterfaces.data.IService[]> {
return [];
}
}

View File

@@ -0,0 +1,131 @@
import * as plugins from '../cloudly.plugins.js';
import * as paths from '../cloudly.paths.js';
import { Cloudly } from '../cloudly.classes.cloudly.js';
import { logger } from '../cloudly.logging.js';
import { Cluster } from './cluster.js';
export class ClusterManager {
public ready = plugins.smartpromise.defer();
public cloudlyRef: Cloudly;
public get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb;
}
public typedrouter = new plugins.typedrequest.TypedRouter();
public CCluster = plugins.smartdata.setDefaultManagerForDoc(this, Cluster);
constructor(cloudlyRef: Cloudly) {
this.cloudlyRef = cloudlyRef;
this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter);
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.cluster.IRequest_CreateCluster>(
new plugins.typedrequest.TypedHandler('createCluster', async (dataArg) => {
const cluster = await this.storeCluster({
id: plugins.smartunique.uniSimple('cluster'),
data: {
name: dataArg.clusterName,
jumpCode: plugins.smartunique.uniSimple('cluster'),
jumpCodeUsedAt: null,
secretKey: plugins.smartunique.shortId(16),
acmeInfo: null,
cloudlyUrl: `https://${this.cloudlyRef.config.data.publicUrl}:${this.cloudlyRef.config.data.publicPort}/`,
servers: [],
sshKeys: [],
},
});
console.log(await cluster.createSavableObject());
this.cloudlyRef.serverManager.ensureServerInfrastructure();
return {
clusterConfig: await cluster.createSavableObject(),
};
})
);
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.cluster.IRequest_GetAllClusters>(
new plugins.typedrequest.TypedHandler('getAllClusters', async (dataArg) => {
// TODO: do authentication here
const clusters = await this.getAllClusters();
return {
clusters: await Promise.all(
clusters.map((clusterArg) => clusterArg.createSavableObject())
),
};
})
);
}
public async init() {
// lets read the config folder
logger.log('info', 'config manager is now initializing');
this.ready.resolve();
}
/**
* gets a cluster config by Name
*/
public async getClusterConfigBy_ServerIP() {
await this.ready.promise;
// TODO: implement getclusterConfigByServerIp
}
public async getClusterConfigBy_JumpCode(jumpCodeArg: string) {
await this.ready.promise;
return await Cluster.getInstance({
data: {
jumpCode: jumpCodeArg,
},
});
}
public async getClusterConfigBy_ClusterIdentifier(
clusterIdentifier: plugins.servezoneInterfaces.data.IClusterIdentifier
) {
await this.ready.promise;
return await Cluster.getInstance({
data: {
name: clusterIdentifier.clusterName,
secretKey: clusterIdentifier.secretKey,
},
});
}
/**
* get config by id
*/
public async getConfigBy_ConfigID(configId: string) {
await this.ready.promise;
return await Cluster.getInstance({
id: configId,
});
}
/**
* gets all cluster configs
*/
public async getAllClusters() {
await this.ready.promise;
return await Cluster.getInstances({});
}
/**
* allows storage of a config
* @param configName
* @param configObjectArg
*/
public async storeCluster(configObjectArg: plugins.servezoneInterfaces.data.ICluster) {
let clusterInstance = await Cluster.getInstance({ id: configObjectArg.id });
if (!clusterInstance) {
clusterInstance = await Cluster.fromConfigObject(configObjectArg);
} else {
Object.assign(clusterInstance, configObjectArg);
}
await clusterInstance.save();
return clusterInstance;
}
}

View File

@@ -0,0 +1,73 @@
import * as plugins from '../cloudly.plugins.js';
import { Cloudly } from '../cloudly.classes.cloudly.js';
/**
* in charge of talking to coreflow services on clusters
* coreflow runs on a server when ServerManager is done.
*/
export class CloudlyCoreflowManager {
public cloudlyRef: Cloudly;
public typedRouter = new plugins.typedrequest.TypedRouter();
constructor(cloudlyRefArg: Cloudly) {
this.cloudlyRef = cloudlyRefArg;
this.cloudlyRef.typedrouter.addTypedRouter(this.typedRouter);
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByJumpCode>(
new plugins.typedrequest.TypedHandler('getIdentityByJumpCode', async (requestData) => {
const clusterConfig =
await this.cloudlyRef.clusterManager.getClusterConfigBy_JumpCode(
requestData.jumpCode
);
if (!clusterConfig) {
throw new plugins.typedrequest.TypedResponseError('The supplied jumpCode is not valid.');
}
return {
clusterIdentifier: {
clusterName: clusterConfig.data.name,
secretKey: clusterConfig.data.secretKey,
},
};
})
);
// lets enable the getting of cluster configs
this.typedRouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.config.IRequest_Any_Cloudly_GetClusterConfig>(
'getClusterConfig',
async (dataArg) => {
const clusterIdentifier = dataArg.clusterIdentifier;
console.log('trying to get clusterConfigSet');
console.log(dataArg);
const clusterConfigSet =
await this.cloudlyRef.clusterManager.getClusterConfigBy_ClusterIdentifier(
clusterIdentifier
);
console.log('got cluster config and sending it back to coreflow');
return {
configData: await clusterConfigSet.createSavableObject()
};
}
)
);
// lets enable getting of certificates
this.typedRouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.certificate.IRequest_Any_Cloudly_GetSslCertificate>(
'getSslCertificate',
async (dataArg) => {
console.log(`got request for certificate ${dataArg.requiredCertName}`);
const cert = await this.cloudlyRef.letsencryptConnector.getCertificateForDomain(
dataArg.requiredCertName
);
console.log(`got certificate ready for reponse ${dataArg.requiredCertName}`);
return {
certificate: await cert.createSavableObject(),
};
}
)
);
}
}

View File

@@ -0,0 +1,21 @@
import * as plugins from '../cloudly.plugins.js';
import type { ImageManager } from './classes.imagemanager.js';
@plugins.smartdata.Manager()
export class Image extends plugins.smartdata.SmartDataDbDoc<Image, plugins.servezoneInterfaces.data.IImage, ImageManager> {
public static async create(imageDataArg: Partial<plugins.servezoneInterfaces.data.IImage['data']>) {
const image = new Image();
image.id = plugins.smartunique.uni('image');
Object.assign(image.data, imageDataArg);
await image.save();
return image;
}
@plugins.smartdata.unI()
public id: string;
@plugins.smartdata.svDb()
public data: plugins.servezoneInterfaces.data.IImage['data'];
public async getVersions() {}
}

View File

@@ -0,0 +1,82 @@
import type { Cloudly } from '../cloudly.classes.cloudly.js';
import * as plugins from '../cloudly.plugins.js';
import { Image } from './classes.image.js';
export class ImageManager {
cloudlyRef: Cloudly;
get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb;
}
public typedrouter = new plugins.typedrequest.TypedRouter();
public CImage = plugins.smartdata.setDefaultManagerForDoc(this, Image);
smartbucketInstance: plugins.smartbucket.SmartBucket;
constructor(cloudlyRefArg: Cloudly) {
this.cloudlyRef = cloudlyRefArg;
this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter);
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_GetAllImages>(
'getAllImages',
async (requestArg) => {
const images = await this.CImage.getInstances({});
return {
images: await Promise.all(
images.map((image) => {
return image.createSavableObject();
})
),
};
}
)
);
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_CreateImage>(
'createImage',
async (reqArg) => {
const image = await this.CImage.create({
name: reqArg.name,
});
return {
image: await image.createSavableObject(),
};
}
)
);
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_DownloadImage>(
'pullImage',
async (reqArg) => {
const image = await this.CImage.getInstance({
data: {
name: reqArg.name,
}
});
const imageVersion =
}
)
);
}
public async start() {
const s3Descriptor: plugins.tsclass.storage.IS3Descriptor =
await this.cloudlyRef.config.appData.waitForAndGetKey('s3Descriptor');
console.log(this.cloudlyRef.config.data.s3Descriptor);
this.smartbucketInstance = new plugins.smartbucket.SmartBucket(
this.cloudlyRef.config.data.s3Descriptor
);
const bucket = await this.smartbucketInstance.getBucketByName('cloudly-test');
await bucket.fastStore('test/test.txt', 'hello');
}
public async createImage(nameArg: string) {
const newImage = await this.CImage.create({
name: nameArg,
});
}
}

View File

@@ -0,0 +1,15 @@
import * as plugins from '../cloudly.plugins.js';
import { Cloudly } from '../cloudly.classes.cloudly.js';
/**
* takes care of receiving and providing logs
*/
export class LogManager {
public cloudlyRef: Cloudly;
public typedRouter = new plugins.typedrequest.TypedRouter();
constructor(cloudlyRefArg: Cloudly) {
this.cloudlyRef = cloudlyRefArg;
this.cloudlyRef.typedrouter.addTypedRouter(this.typedRouter);
}
}

View File

@@ -0,0 +1,62 @@
// a secret bundle is a set of secrets ready to be used in a project.
// it bundles secretgroups
import { SecretGroup } from './classes.secretgroup.js';
import * as plugins from '../cloudly.plugins.js';
@plugins.smartdata.Manager()
export class SecretBundle extends plugins.smartdata.SmartDataDbDoc<
SecretBundle,
plugins.servezoneInterfaces.data.ISecretBundle
> {
// STATIC
// INSTANCE
@plugins.smartdata.unI()
public id: string;
@plugins.smartdata.svDb()
public data: plugins.servezoneInterfaces.data.ISecretBundle['data'];
public async getSecretGroups() {
const secretGroups: SecretGroup[] = [];
for (const secretGroupId of this.data.includedSecretGroupIds) {
secretGroups.push(
await SecretGroup.getInstance({
id: secretGroupId,
})
);
}
return secretGroups;
}
/**
* searches the secretGroups for environments and returns them
*/
public async getEnvironments() {
const environments = new Set();
const secretGroups = await this.getSecretGroups();
for (const secretGroup of secretGroups) {
environments.add(secretGroup.data.environments);
}
return Array.from(environments);
}
public async getAuthorizationFromAuthKey(authKeyArg: string) {
const authorization = this.data.authorizations.find((authArg) => {
return authArg.secretAccessKey === authKeyArg;
});
return authorization;
}
public async getKeyValueObjectForEnvironment(environmentArg: string) {
const secretGroups = await this.getSecretGroups();
const returnObject = {};
for (const secretGroup of secretGroups) {
if (!secretGroup.data.environments[environmentArg]) {
continue;
}
returnObject[secretGroup.data.key] = secretGroup.data.environments[environmentArg].value;
}
return returnObject;
}
}

View File

@@ -0,0 +1,21 @@
/**
* a secretgroup is a set of secrets for different environments.
*/
import * as plugins from '../cloudly.plugins.js';
@plugins.smartdata.Manager()
export class SecretGroup extends plugins.smartdata.SmartDataDbDoc<
SecretGroup,
plugins.servezoneInterfaces.data.ISecretGroup
> {
// INSTANCE
/**
* the insatnce id. This should be a random id, except for default
*/
@plugins.smartdata.unI()
id: string;
@plugins.smartdata.svDb()
data: plugins.servezoneInterfaces.data.ISecretGroup['data'];
}

View File

@@ -0,0 +1,156 @@
import * as plugins from '../cloudly.plugins.js';
import * as paths from '../cloudly.paths.js';
import { SecretBundle } from './classes.secretbundle.js';
import { SecretGroup } from './classes.secretgroup.js';
import { logger } from '../cloudly.logging.js';
import type { Cloudly } from '../cloudly.classes.cloudly.js';
/**
* The `ConfigVault` class provides methods for reading and writing configuration data to a file.
* It uses the `TypedServer` and `TypedRouter` classes from the `configvault.plugins.js` module to handle HTTP requests and route them to the appropriate handlers.
*
* @class
*/
export class CloudlySecretManager {
// attached classes
public CSecretBundle = plugins.smartdata.setDefaultManagerForDoc(this, SecretBundle);
public CSecretGroup = plugins.smartdata.setDefaultManagerForDoc(this, SecretGroup);
// INSTANCE
public cloudlyRef: Cloudly;
public projectinfo = new plugins.projectinfo.ProjectinfoNpm(paths.packageDir);
public serviceQenv = new plugins.qenv.Qenv(paths.packageDir, paths.nogitDir);
public typedrouter: plugins.typedrequest.TypedRouter;
get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb;
}
constructor(cloudlyRefArg: Cloudly) {
this.cloudlyRef = cloudlyRefArg;
}
public async start() {
// lets set up a typedrouter
this.typedrouter = new plugins.typedrequest.TypedRouter();
this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter);
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.secret.IReq_Admin_LoginWithUsernameAndPassword>(
'adminLoginWithUsernameAndPassword',
async (dataArg) => {
let jwt: string;
// console.log(dataArg);
if (dataArg.username !== 'admin' || dataArg.password !== 'password') {
logger.log('warn', 'login failed');
} else {
jwt = await this.cloudlyRef.config.smartjwtInstance.createJWT({
status: 'loggedIn',
});
logger.log('success', 'login successful');
}
return {
jwt,
};
}
)
);
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.secret.IReq_Admin_GetConfigBundlesAndSecretGroups>(
'adminGetConfigBundlesAndSecretGroups',
async (dataArg) => {
dataArg.jwt
const secretBundles = await SecretBundle.getInstances({});
const secretGroups = await SecretGroup.getInstances({});
return {
secretBundles: [
...(await Promise.all(
secretBundles.map((configBundle) => configBundle.createSavableObject())
)),
],
secretGroups: [
...(await Promise.all(
secretGroups.map((secretGroup) => secretGroup.createSavableObject())
)),
],
};
}
)
);
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.secret.IReq_Admin_CreateConfigBundlesAndSecretGroups>(
new plugins.typedrequest.TypedHandler(
'adminCreateConfigBundlesAndSecretGroups',
async (dataArg) => {
for (const secretGroupObject of dataArg.secretGroups) {
const secretGroup = new SecretGroup();
secretGroup.id = plugins.smartunique.shortId(8);
secretGroup.data = secretGroupObject.data;
await secretGroup.save();
}
return {
ok: true,
};
}
)
);
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.secret.IReq_Admin_DeleteConfigBundlesAndSecretGroups>(
'adminDeleteConfigBundlesAndSecretGroups',
async (dataArg) => {
for (const secretGroupId of dataArg.secretGroupIds) {
const secretGroup = await SecretGroup.getInstance({
id: secretGroupId,
});
await secretGroup.delete();
}
for (const secretBundleId of dataArg.secretBundleIds) {
const configBundle = await SecretBundle.getInstance({
id: secretBundleId,
});
await configBundle.delete();
console.log(`deleted configbundle ${secretBundleId}`);
}
return {
ok: true,
};
}
)
);
// lets add typedrouter routes for accessing the configvailt from apps
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.secret.IReq_GetEnvBundle>(
'getEnvBundle',
async (dataArg) => {
const wantedBundle = await SecretBundle.getInstance({
data: {
authorizations: {
// @ts-ignore
$elemMatch: {
secretAccessKey: dataArg.authorization,
},
},
},
});
const authorization = await wantedBundle.getAuthorizationFromAuthKey(
dataArg.authorization
);
return {
envBundle: {
configKeyValueObject: await wantedBundle.getKeyValueObjectForEnvironment(
authorization.environment
),
environment: authorization.environment,
timeSensitive: false,
},
};
}
)
);
}
public async stop() {}
}

View File

@@ -0,0 +1,39 @@
import * as plugins from '../cloudly.plugins.js';
/*
* cluster defines a swarmkit cluster
*/
@plugins.smartdata.Manager()
export class Server extends plugins.smartdata.SmartDataDbDoc<Server, plugins.servezoneInterfaces.data.IServer> {
// STATIC
public static async createFromHetznerServer(
hetznerServerArg: plugins.hetznercloud.HetznerServer
) {
const newServer = new Server();
newServer.id = plugins.smartunique.shortId(8);
const data: plugins.servezoneInterfaces.data.IServer['data'] = {
assignedClusterId: hetznerServerArg.data.labels.clusterId,
requiredDebianPackages: [],
sshKeys: [],
type: 'hetzner',
}
Object.assign(newServer, { data });
await newServer.save();
return newServer;
}
// INSTANCE
@plugins.smartdata.unI()
public id: string;
@plugins.smartdata.svDb()
public data: plugins.servezoneInterfaces.data.IServer['data'];
constructor() {
super();
}
public async getServices(): Promise<plugins.servezoneInterfaces.data.IService[]> {
return [];
}
}

View File

@@ -0,0 +1,103 @@
import * as plugins from '../cloudly.plugins.js';
import { Cloudly } from '../cloudly.classes.cloudly.js';
import { Cluster } from '../manager.cluster/cluster.js';
import { Server } from './server.js';
export class CloudlyServerManager {
public cloudlyRef: Cloudly;
public typedRouter = new plugins.typedrequest.TypedRouter();
public hetznerAccount: plugins.hetznercloud.HetznerAccount;
public get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb;
}
public CServer = plugins.smartdata.setDefaultManagerForDoc(this, Server);
constructor(cloudlyRefArg: Cloudly) {
this.cloudlyRef = cloudlyRefArg;
/**
* is used be serverconfig module on the server to get the actual server config
*/
this.typedRouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.config.IRequest_Any_Cloudly_GetServerConfig>(
'getServerConfig',
async (requestData) => {
const serverId = requestData.serverId;
const server = await this.CServer.getInstance({
id: serverId,
})
return {
configData: await server.createSavableObject(),
};
}
)
);
}
public async start() {
this.hetznerAccount = new plugins.hetznercloud.HetznerAccount(this.cloudlyRef.config.data.hetznerToken);
}
public async stop() {}
/**
* creates the server infrastructure on hetzner
* ensures that there are exactly the reources that are needed
* no more, no less
*/
public async ensureServerInfrastructure() {
// get all clusters
const allClusters = await this.cloudlyRef.clusterManager.getAllClusters();
for (const cluster of allClusters) {
// get existing servers
const servers = await this.getServersByCluster(cluster);
// if there is no server, create one
if (servers.length === 0) {
const server = await this.hetznerAccount.createServer({
name: plugins.smartunique.uniSimple('server'),
location: 'nbg1',
type: 'cpx41',
labels: {
clusterId: cluster.id,
priority: '1',
}
});
const newServer = await Server.createFromHetznerServer(server);
console.log(`cluster created new server for cluster ${cluster.id}`);
} else {
console.log(`cluster ${cluster.id} already has servers. Making sure that they actually exist in the real world...`);
// if there is a server, make sure that it exists
for (const server of servers) {
const hetznerServer = await this.hetznerAccount.getServersByLabel({
'clusterId': cluster.id
});
if (!hetznerServer) {
console.log(`server ${server.id} does not exist in the real world. Creating it now...`);
const hetznerServer = await this.hetznerAccount.createServer({
name: plugins.smartunique.uniSimple('server'),
location: 'nbg1',
type: 'cpx41',
labels: {
clusterId: cluster.id,
priority: '1',
}
});
const newServer = await Server.createFromHetznerServer(hetznerServer);
}
}
}
}
}
public async getServersByCluster(clusterArg: Cluster) {
const results = await this.CServer.getInstances({
data: {
assignedClusterId: clusterArg.id,
}
});
return results;
}
}

View File

View File

@@ -0,0 +1,22 @@
import * as plugins from '../cloudly.plugins.js';
import { Cloudly } from '../index.js';
/**
* external api manager manages external api requests
*/
export class ExternalApiManager {
public cloudlyRef: Cloudly;
public typedRouter = new plugins.typedrequest.TypedRouter();
constructor(cloudlyRef: Cloudly) {
this.cloudlyRef = cloudlyRef;
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.requests.network.IRequest_Any_Cloudly_GetNetworkNodes>(
new plugins.typedrequest.TypedHandler('getNetworkNodes', async (requestData) => {
const networkNodes = [];
return {
networkNodes,
};
})
);
}
}

View File

@@ -0,0 +1,35 @@
import * as plugins from '../cloudly.plugins.js';
import { Cloudly } from '../cloudly.classes.cloudly.js';
import { logger } from '../cloudly.logging.js';
export class CloudlyTaskmanager {
public cloudlyRef: Cloudly;
constructor(cloudlyRefArg: Cloudly) {
this.cloudlyRef = cloudlyRefArg;
}
public everyMinuteTask = new plugins.taskbuffer.Task({
taskFunction: async () => {},
});
public everyHourTask = new plugins.taskbuffer.Task({
taskFunction: async () => {
logger.log('info', `Performing hourly maintenance check.`);
const configs = await this.cloudlyRef.clusterManager.getAllClusters();
logger.log('info', `Got ${configs.length} configs`);
configs.map((configArg) => {
console.log(configArg.name);
});
},
});
public everyDayTask = new plugins.taskbuffer.Task({
taskFunction: async () => {},
});
public everyWeekTask = new plugins.taskbuffer.Task({
taskFunction: async () => {},
});
}

View File

@@ -0,0 +1,32 @@
import * as plugins from '../cloudly.plugins.js';
/*
A container version is managed by the versionmanager
*/
@plugins.smartdata.Manager()
export class ContainerVersion
extends plugins.smartdata.SmartDataDbDoc<ContainerVersion, unknown>
implements plugins.servezoneInterfaces.data.IContainerVersionData
{
public static async fromIVersionData(
dataArg: plugins.servezoneInterfaces.data.IContainerVersionData
) {
const containerVersionInstance = new ContainerVersion();
containerVersionInstance.id = plugins.smartunique.shortId();
Object.assign(containerVersionInstance, dataArg);
return containerVersionInstance;
}
@plugins.smartdata.unI()
public id: string;
@plugins.smartdata.svDb()
public dockerImageUrl: string;
@plugins.smartdata.svDb()
public dockerImageVersion: string;
constructor() {
super();
}
}

View File

@@ -0,0 +1,149 @@
import * as plugins from '../cloudly.plugins.js';
import { ContainerVersion } from './containerversion.js';
import { Cloudly } from '../cloudly.classes.cloudly.js';
export class CloudlyVersionManager {
// INSTANCE
public cloudlyRef: Cloudly;
public get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb;
}
public typedRouter = new plugins.typedrequest.TypedRouter();
// connected classes
public CContainerVersion = plugins.smartdata.setDefaultManagerForDoc(this, ContainerVersion);
constructor(cloudlyRefArg: Cloudly) {
this.cloudlyRef = cloudlyRefArg;
this.cloudlyRef.typedrouter.addTypedRouter(this.typedRouter);
// get version
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.requests.version.IRequest_Any_Cloudly_VersionManager_GetLatestContainerVersion>(
new plugins.typedrequest.TypedHandler(
'getLatestContainerVersion',
async (typedRequestData) => {
const containerVersionGet: ContainerVersion =
await ContainerVersion.getInstance<ContainerVersion>({
dockerImageUrl: typedRequestData.dockerImageUrl,
});
return {
dockerImageUrl: containerVersionGet.dockerImageUrl,
dockerImageVersion: containerVersionGet.dockerImageVersion,
};
}
)
);
// update version
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.requests.version.IRequest_Any_Cloudly_VersionManager_InformCloudlyAboutNewContainerVersion>(
new plugins.typedrequest.TypedHandler(
'informCloudlyAboutNewContainerVersion',
async (dataArg) => {
console.log(`Got a container version announcement! "${dataArg.dockerImageUrl}"`);
let containerVersion: ContainerVersion =
await ContainerVersion.getInstance<ContainerVersion>({
dockerImageUrl: dataArg.dockerImageUrl,
});
if (containerVersion) {
containerVersion.dockerImageVersion = dataArg.dockerImageVersion;
await containerVersion.save();
} else {
containerVersion = await ContainerVersion.fromIVersionData(dataArg);
await containerVersion.save();
}
// lets push this info to the relevant clusters
const clusters = await this.cloudlyRef.clusterManager.getAllClusters();
let foundServices: plugins.servezoneInterfaces.data.IService;
let relevantClusterIdentifier: plugins.servezoneInterfaces.data.IClusterIdentifier;
for (const clusterArg of clusters) {
console.log(clusterArg);
for (const serviceArg of await clusterArg.getServices()) {
if (serviceArg.image === containerVersion.dockerImageUrl) {
foundServices = serviceArg;
break;
}
}
if (foundServices) {
relevantClusterIdentifier = {
clusterName: clusterArg.data.name,
secretKey: clusterArg.data.secretKey,
};
break;
}
}
if (!relevantClusterIdentifier) {
console.log('no cluster found that needs to update');
return {};
} else {
console.log('found relevant cluster identifier:');
console.log(relevantClusterIdentifier);
}
const targetConnection =
await this.cloudlyRef.server.typedsocketServer.findTargetConnection(
async (connectionArg) => {
const identityTag = await connectionArg.getTagById('identity');
if (!identityTag) {
return false;
}
const result =
plugins.smartjson.stringify(identityTag.payload) ===
plugins.smartjson.stringify(relevantClusterIdentifier);
return result;
}
);
if (targetConnection) {
console.log(`the relevant cluster is connected and is now being informed.`);
const informCoreflowTR =
this.cloudlyRef.server.typedsocketServer.createTypedRequest<plugins.servezoneInterfaces.requests.version.IRequest_Cloudly_Coreflow_VersionManager_InformCoreflowAboutNewContainerVersion>(
'informCoreflowAboutNewContainerVersion',
targetConnection
);
informCoreflowTR.fire({
dockerImageUrl: containerVersion.dockerImageUrl,
dockerImageVersion: containerVersion.dockerImageVersion,
});
} else {
console.log('the relevant cluster is not connected at this time.');
}
return {};
}
)
);
// lets support the servezone standard
this.typedRouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.IRequest_InformAboutNewContainerImage>(
'servezonestandard_InformAboutNewContainerVersion',
async (dataArg) => {
const result =
await this.typedRouter.routeAndAddResponse<plugins.servezoneInterfaces.requests.version.IRequest_Any_Cloudly_VersionManager_InformCloudlyAboutNewContainerVersion>(
{
method: 'informCloudlyAboutNewContainerVersion',
request: {
dockerImageUrl: dataArg.containerImageInfo.registryUrl,
dockerImageVersion: dataArg.containerImageInfo.version,
},
response: null
},
true
);
return result.response;
}
)
);
}
/**
* gets all versions
*/
public async getAllVersions() {
const result = await ContainerVersion.getInstances<ContainerVersion>({});
return result;
}
}