Compare commits

...

26 Commits

Author SHA1 Message Date
bc7a2ca5f1 5.0.4
Some checks failed
Docker (tags) / security (push) Successful in 48s
Docker (tags) / test (push) Successful in 1m56s
Docker (tags) / metadata (push) Successful in 3s
Docker (tags) / release (push) Failing after 14s
2025-04-25 18:20:18 +00:00
77d911e47a fix(platformservice/mta): Update getEmailStatus response schema: make details property optional 2025-04-25 18:20:18 +00:00
b9c9c2d0a9 5.0.3
Some checks failed
Docker (tags) / security (push) Successful in 49s
Docker (tags) / test (push) Successful in 1m55s
Docker (tags) / metadata (push) Successful in 3s
Docker (tags) / release (push) Failing after 14s
2025-04-25 17:02:48 +00:00
d5b91789d1 fix(mta): update email status response type in MTA platform service 2025-04-25 17:02:48 +00:00
eb8350f453 5.0.2
Some checks failed
Docker (tags) / security (push) Successful in 38s
Docker (tags) / test (push) Successful in 1m56s
Docker (tags) / metadata (push) Successful in 3s
Docker (tags) / release (push) Failing after 13s
2025-04-25 16:34:01 +00:00
b987ce27b8 fix(platformservice/mta): Refactor email status response in MTA service 2025-04-25 16:34:00 +00:00
630e363e53 5.0.1
Some checks failed
Docker (tags) / security (push) Successful in 48s
Docker (tags) / test (push) Successful in 1m54s
Docker (tags) / metadata (push) Successful in 3s
Docker (tags) / release (push) Failing after 14s
2025-04-25 16:27:48 +00:00
a602021952 fix(mta): Update email stats response interface in mta platform service to include totalEmailsSent, totalEmailsDelivered, totalEmailsBounced, averageDeliveryTimeMs, and lastUpdated timestamp. 2025-04-25 16:27:48 +00:00
80585437a0 5.0.0
Some checks failed
Docker (tags) / security (push) Successful in 47s
Docker (tags) / test (push) Successful in 1m54s
Docker (tags) / metadata (push) Successful in 3s
Docker (tags) / release (push) Failing after 41s
2025-04-25 15:57:35 +00:00
4674a20a2c BREAKING CHANGE(ts_interfaces/platformservice/mta): Rename mta interfaces and upgrade dependency versions 2025-04-25 15:57:35 +00:00
820cdfcd48 4.13.0
Some checks failed
Docker (tags) / security (push) Successful in 52s
Docker (tags) / test (push) Successful in 3m5s
Docker (tags) / metadata (push) Successful in 8s
Docker (tags) / release (push) Failing after 22s
2025-01-20 02:18:58 +01:00
6e5dd9b05a feat(service): Add support for service creation, update, and deletion. 2025-01-20 02:18:58 +01:00
f3d5c21fab 4.12.2
Some checks failed
Docker (tags) / security (push) Successful in 1m6s
Docker (tags) / test (push) Successful in 3m5s
Docker (tags) / metadata (push) Successful in 7s
Docker (tags) / release (push) Failing after 24s
2025-01-20 02:11:12 +01:00
04b278ee28 fix(service): Fix secret bundle and service management bugs 2025-01-20 02:11:12 +01:00
7084d76c43 4.12.1
Some checks failed
Docker (tags) / security (push) Successful in 51s
Docker (tags) / test (push) Successful in 2m53s
Docker (tags) / metadata (push) Successful in 7s
Docker (tags) / release (push) Failing after 20s
2025-01-02 04:07:43 +01:00
41d7550e89 fix(deps): Updated @git.zone/tspublish to version ^1.9.1 2025-01-02 04:07:43 +01:00
4bf361d3a6 4.12.0
Some checks failed
Docker (tags) / security (push) Successful in 1m1s
Docker (tags) / test (push) Successful in 2m57s
Docker (tags) / metadata (push) Successful in 7s
Docker (tags) / release (push) Failing after 31s
2025-01-02 03:58:09 +01:00
d70617a90c feat(cli): Add CLI support and external registries view 2025-01-02 03:58:09 +01:00
62ad1655d5 4.11.0
Some checks failed
Docker (tags) / security (push) Successful in 1m7s
Docker (tags) / test (push) Successful in 3m0s
Docker (tags) / metadata (push) Successful in 7s
Docker (tags) / release (push) Failing after 23s
2024-12-30 00:01:27 +01:00
caf3a095f2 feat(external-registry): Introduce external registry management 2024-12-30 00:01:26 +01:00
89e44b2e5f 4.10.0
Some checks failed
Docker (tags) / security (push) Successful in 1m3s
Docker (tags) / test (push) Successful in 2m59s
Docker (tags) / metadata (push) Successful in 7s
Docker (tags) / release (push) Failing after 22s
2024-12-29 13:40:51 +01:00
a617f51b19 feat(apiclient): Added support for managing external registries in the API client. 2024-12-29 13:40:51 +01:00
355e04fd1d 4.9.0
Some checks failed
Docker (tags) / security (push) Successful in 1m6s
Docker (tags) / test (push) Successful in 3m2s
Docker (tags) / metadata (push) Successful in 7s
Docker (tags) / release (push) Failing after 30s
2024-12-29 13:19:46 +01:00
89bd767bea feat(apiclient): Add external registry management capabilities to Cloudly API client. 2024-12-29 13:19:46 +01:00
e567ebbf21 4.8.1
Some checks failed
Docker (tags) / security (push) Successful in 53s
Docker (tags) / test (push) Successful in 2m58s
Docker (tags) / metadata (push) Successful in 7s
Docker (tags) / release (push) Failing after 22s
2024-12-28 21:48:57 +01:00
33311348e2 fix(interfaces): Fix image location schema in IImage interface 2024-12-28 21:48:57 +01:00
34 changed files with 3646 additions and 2314 deletions

View File

@ -1,5 +1,82 @@
# Changelog # Changelog
## 2025-04-25 - 5.0.4 - fix(platformservice/mta)
Update getEmailStatus response schema: make details property optional
- Changed details property from required with fixed message to optional with a flexible message structure in IReq_GetEMailStats response
## 2025-04-25 - 5.0.3 - fix(mta)
update email status response type in MTA platform service
- Changed the response 'status' field in IRequest_CheckEmailStatus from a literal 'unknown' to a generic string for improved flexibility
## 2025-04-25 - 5.0.2 - fix(platformservice/mta)
Refactor email status response in MTA service
- Updated IReq_CheckEmailStatus response: replaced union type ('ok' | 'not ok') with fixed status 'unknown' and added a details object with message 'Email not found'.
## 2025-04-25 - 5.0.1 - fix(mta)
Update email stats response interface in mta platform service to include totalEmailsSent, totalEmailsDelivered, totalEmailsBounced, averageDeliveryTimeMs, and lastUpdated timestamp.
- Modified IReq_GetEMailStats response in ts_interfaces/platformservice/mta.ts from an empty status object to a detailed email statistics structure.
## 2025-04-25 - 5.0.0 - BREAKING CHANGE(ts_interfaces/platformservice/mta)
Rename mta interfaces and upgrade dependency versions
- Upgraded devDependencies: @git.zone/tsbuild, tsbundle, tsdoc, tstest, tswatch, and @push.rocks/tapbundle to newer versions.
- Upgraded dependencies: @design.estate/dees-catalog, dees-domtools, dees-element, @push.rocks/smartdata, smartexpect, smartfile, smartpromise, smartrequest, smartrx, and tsclass (v4.2.0 to v9.0.0).
- Added new packageManager field in package.json and introduced pnpm-workspace.yaml for additional workspace configuration.
- Refactored mta API interfaces: renamed IRequest_SendEmail to IReq_SendEmail and IRequestRegisterRecipient to IReq_RegisterRecipient; added IReq_CheckEmailStatus and IReq_GetEMailStats.
## 2025-01-20 - 4.13.0 - feat(service)
Add support for service creation, update, and deletion.
- Implemented TypedHandlers for creating a new service.
- Added features to update existing service details.
- Enabled deletion of services by their unique ID.
## 2025-01-20 - 4.12.2 - fix(service)
Fix secret bundle and service management bugs
- Corrected the field name from 'includedImages' to 'imageClaims' in secret bundles.
- Implemented 'getFlatKeyValueObject' for secret bundles and modified related API interactions.
- Enhanced the Service class with methods for handling secret bundle data by resolving related groups and environments.
## 2025-01-02 - 4.12.1 - fix(deps)
Updated @git.zone/tspublish to version ^1.9.1
## 2025-01-02 - 4.12.0 - feat(cli)
Add CLI support and external registries view
- Adds CLI client functionality
- Introduces a new view for External Registries in the dashboard
## 2024-12-30 - 4.11.0 - feat(external-registry)
Introduce external registry management
- Added ExternalRegistryManager to handle external registry operations.
- Implemented ability to create, retrieve, and delete external registries.
- Enhanced Cloudly class to include ExternalRegistryManager.
## 2024-12-29 - 4.10.0 - feat(apiclient)
Added support for managing external registries in the API client.
- Introduced methods to get a registry by ID, get all registries, and create a new registry in the externalRegistry object.
- Updated external registry request interfaces to match new API client capabilities.
## 2024-12-29 - 4.9.0 - feat(apiclient)
Add external registry management capabilities to Cloudly API client.
- Introduce ExternalRegistry class with methods for getting, creating, and updating external registries.
- Expand requests module to handle external registry management, including creation and deletion.
## 2024-12-28 - 4.8.1 - fix(interfaces)
Fix image location schema in IImage interface
- Refactored the 'external' object within IImage data to a 'location' object.
- Added 'internal' boolean to 'location' to specify internal/external status.
## 2024-12-28 - 4.8.0 - feat(manager.registry) ## 2024-12-28 - 4.8.0 - feat(manager.registry)
Add external registry management Add external registry management

View File

@ -1,6 +1,6 @@
{ {
"name": "@serve.zone/cloudly", "name": "@serve.zone/cloudly",
"version": "4.8.0", "version": "5.0.4",
"private": false, "private": false,
"description": "A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.", "description": "A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.",
"type": "module", "type": "module",
@ -22,27 +22,27 @@
"docs": "tsdoc aidoc" "docs": "tsdoc aidoc"
}, },
"devDependencies": { "devDependencies": {
"@git.zone/tsbuild": "^2.2.0", "@git.zone/tsbuild": "^2.3.2",
"@git.zone/tsbundle": "^2.1.0", "@git.zone/tsbundle": "^2.2.5",
"@git.zone/tsdoc": "^1.4.2", "@git.zone/tsdoc": "^1.4.4",
"@git.zone/tspublish": "^1.7.7", "@git.zone/tspublish": "^1.9.1",
"@git.zone/tstest": "^1.0.90", "@git.zone/tstest": "^1.0.96",
"@git.zone/tswatch": "^2.0.37", "@git.zone/tswatch": "^2.1.0",
"@push.rocks/tapbundle": "^5.5.3", "@push.rocks/tapbundle": "^5.6.3",
"@types/node": "^22.10.2" "@types/node": "^22.15.2"
}, },
"dependencies": { "dependencies": {
"@api.global/typedrequest": "3.1.10", "@api.global/typedrequest": "3.1.10",
"@api.global/typedrequest-interfaces": "^3.0.19", "@api.global/typedrequest-interfaces": "^3.0.19",
"@api.global/typedserver": "^3.0.51", "@api.global/typedserver": "^3.0.74",
"@api.global/typedsocket": "^3.0.1", "@api.global/typedsocket": "^3.0.1",
"@apiclient.xyz/cloudflare": "^6.0.1", "@apiclient.xyz/cloudflare": "^6.0.1",
"@apiclient.xyz/docker": "^1.2.7", "@apiclient.xyz/docker": "^1.3.0",
"@apiclient.xyz/hetznercloud": "^1.2.0", "@apiclient.xyz/hetznercloud": "^1.2.0",
"@apiclient.xyz/slack": "^3.0.9", "@apiclient.xyz/slack": "^3.0.9",
"@design.estate/dees-catalog": "^1.3.2", "@design.estate/dees-catalog": "^1.8.0",
"@design.estate/dees-domtools": "^2.0.64", "@design.estate/dees-domtools": "^2.3.2",
"@design.estate/dees-element": "^2.0.39", "@design.estate/dees-element": "^2.0.42",
"@git.zone/tsrun": "^1.3.3", "@git.zone/tsrun": "^1.3.3",
"@push.rocks/early": "^4.0.3", "@push.rocks/early": "^4.0.3",
"@push.rocks/npmextra": "^5.1.2", "@push.rocks/npmextra": "^5.1.2",
@ -52,11 +52,11 @@
"@push.rocks/smartbucket": "^3.3.7", "@push.rocks/smartbucket": "^3.3.7",
"@push.rocks/smartcli": "^4.0.11", "@push.rocks/smartcli": "^4.0.11",
"@push.rocks/smartclickhouse": "^2.0.17", "@push.rocks/smartclickhouse": "^2.0.17",
"@push.rocks/smartdata": "^5.2.10", "@push.rocks/smartdata": "^5.15.1",
"@push.rocks/smartdelay": "^3.0.5", "@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartexit": "^1.0.23", "@push.rocks/smartexit": "^1.0.23",
"@push.rocks/smartexpect": "^1.2.1", "@push.rocks/smartexpect": "^1.6.1",
"@push.rocks/smartfile": "^11.0.23", "@push.rocks/smartfile": "^11.2.0",
"@push.rocks/smartguard": "^3.1.0", "@push.rocks/smartguard": "^3.1.0",
"@push.rocks/smartjson": "^5.0.19", "@push.rocks/smartjson": "^5.0.19",
"@push.rocks/smartjwt": "^2.2.1", "@push.rocks/smartjwt": "^2.2.1",
@ -64,9 +64,9 @@
"@push.rocks/smartlog-destination-clickhouse": "^1.0.13", "@push.rocks/smartlog-destination-clickhouse": "^1.0.13",
"@push.rocks/smartlog-interfaces": "^3.0.2", "@push.rocks/smartlog-interfaces": "^3.0.2",
"@push.rocks/smartpath": "^5.0.18", "@push.rocks/smartpath": "^5.0.18",
"@push.rocks/smartpromise": "^4.0.4", "@push.rocks/smartpromise": "^4.2.3",
"@push.rocks/smartrequest": "^2.0.23", "@push.rocks/smartrequest": "^2.1.0",
"@push.rocks/smartrx": "^3.0.7", "@push.rocks/smartrx": "^3.0.10",
"@push.rocks/smartssh": "^2.0.1", "@push.rocks/smartssh": "^2.0.1",
"@push.rocks/smartstate": "^2.0.19", "@push.rocks/smartstate": "^2.0.19",
"@push.rocks/smartstream": "^3.2.5", "@push.rocks/smartstream": "^3.2.5",
@ -74,7 +74,7 @@
"@push.rocks/smartunique": "^3.0.9", "@push.rocks/smartunique": "^3.0.9",
"@push.rocks/taskbuffer": "^3.0.2", "@push.rocks/taskbuffer": "^3.0.2",
"@push.rocks/webjwt": "^1.0.9", "@push.rocks/webjwt": "^1.0.9",
"@tsclass/tsclass": "^4.2.0" "@tsclass/tsclass": "^9.0.0"
}, },
"files": [ "files": [
"ts/**/*", "ts/**/*",
@ -126,5 +126,6 @@
"frontend", "frontend",
"backend", "backend",
"security" "security"
] ],
"packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6"
} }

5048
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

4
pnpm-workspace.yaml Normal file
View File

@ -0,0 +1,4 @@
onlyBuiltDependencies:
- esbuild
- mongodb-memory-server
- puppeteer

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/cloudly', name: '@serve.zone/cloudly',
version: '4.8.0', version: '5.0.4',
description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.' description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.'
} }

View File

@ -63,7 +63,7 @@ for (let i = 0; i < demoSecretGroups.length; i++) {
id: `configBundleId${i + 1}`, id: `configBundleId${i + 1}`,
data: { data: {
name: `Demo Config Bundle ${i + 1}`, name: `Demo Config Bundle ${i + 1}`,
includedImages: [], imageClaims: [],
type: 'external', type: 'external',
description: 'Demo Purpose', description: 'Demo Purpose',
includedSecretGroupIds: [secretGroup.id], includedSecretGroupIds: [secretGroup.id],

View File

@ -20,6 +20,7 @@ import { CloudlyTaskmanager } from './manager.task/taskmanager.js';
import { CloudlySecretManager } from './manager.secret/classes.secretmanager.js'; import { CloudlySecretManager } from './manager.secret/classes.secretmanager.js';
import { CloudlyServerManager } from './manager.server/classes.servermanager.js'; import { CloudlyServerManager } from './manager.server/classes.servermanager.js';
import { ExternalApiManager } from './manager.status/statusmanager.js'; import { ExternalApiManager } from './manager.status/statusmanager.js';
import { ExternalRegistryManager } from './manager.externalregistry/index.js';
import { ImageManager } from './manager.image/classes.imagemanager.js'; import { ImageManager } from './manager.image/classes.imagemanager.js';
import { logger } from './logger.js'; import { logger } from './logger.js';
import { CloudlyAuthManager } from './manager.auth/classes.authmanager.js'; import { CloudlyAuthManager } from './manager.auth/classes.authmanager.js';
@ -54,6 +55,7 @@ export class Cloudly {
public clusterManager: ClusterManager; public clusterManager: ClusterManager;
public coreflowManager: CloudlyCoreflowManager; public coreflowManager: CloudlyCoreflowManager;
public externalApiManager: ExternalApiManager; public externalApiManager: ExternalApiManager;
public externalRegistryManager: ExternalRegistryManager;
public imageManager: ImageManager; public imageManager: ImageManager;
public taskManager: CloudlyTaskmanager; public taskManager: CloudlyTaskmanager;
public serverManager: CloudlyServerManager; public serverManager: CloudlyServerManager;
@ -80,6 +82,7 @@ export class Cloudly {
this.clusterManager = new ClusterManager(this); this.clusterManager = new ClusterManager(this);
this.coreflowManager = new CloudlyCoreflowManager(this); this.coreflowManager = new CloudlyCoreflowManager(this);
this.externalApiManager = new ExternalApiManager(this); this.externalApiManager = new ExternalApiManager(this);
this.externalRegistryManager = new ExternalRegistryManager(this);
this.imageManager = new ImageManager(this); this.imageManager = new ImageManager(this);
this.taskManager = new CloudlyTaskmanager(this); this.taskManager = new CloudlyTaskmanager(this);
this.secretManager = new CloudlySecretManager(this); this.secretManager = new CloudlySecretManager(this);

View File

@ -44,7 +44,7 @@ export class ClusterManager {
); );
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.cluster.IReq_Any_Cloudly_GetClusters>( this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.cluster.IReq_Any_Cloudly_GetClusters>(
new plugins.typedrequest.TypedHandler('getAllClusters', async (dataArg) => { new plugins.typedrequest.TypedHandler('getClusters', async (dataArg) => {
// TODO: do authentication here // TODO: do authentication here
const clusters = await this.getAllClusters(); const clusters = await this.getAllClusters();
return { return {
@ -57,11 +57,11 @@ export class ClusterManager {
// delete cluster // delete cluster
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.cluster.IReq_Any_Cloudly_DeleteClusterById>( this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.cluster.IReq_Any_Cloudly_DeleteClusterById>(
new plugins.typedrequest.TypedHandler('deleteCluster', async (reqDataArg, toolsArg) => { new plugins.typedrequest.TypedHandler('deleteClusterById', async (reqDataArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], reqDataArg); await toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], reqDataArg);
await this.deleteCluster(reqDataArg.clusterId); await this.deleteCluster(reqDataArg.clusterId);
return { return {
success: true, ok: true,
}; };
}), }),
); );

View File

@ -0,0 +1,40 @@
import * as plugins from '../plugins.js';
import * as paths from '../paths.js';
import type { Cloudly } from 'ts/classes.cloudly.js';
import type { ExternalRegistryManager } from './classes.externalregistrymanager.js';
export class ExternalRegistry extends plugins.smartdata.SmartDataDbDoc<ExternalRegistry, plugins.servezoneInterfaces.data.IExternalRegistry, ExternalRegistryManager> {
// STATIC
public static async getRegistryById(registryIdArg: string) {
const externalRegistry = await this.getInstance({
id: registryIdArg,
});
return externalRegistry;
}
public static async getRegistries() {
const externalRegistries = await this.getInstances({});
return externalRegistries;
}
public static async createExternalRegistry(registryDataArg: Partial<plugins.servezoneInterfaces.data.IExternalRegistry['data']>) {
const externalRegistry = new ExternalRegistry();
externalRegistry.id = await ExternalRegistry.getNewId();
Object.assign(externalRegistry, registryDataArg);
await externalRegistry.save();
return externalRegistry;
}
// INSTANCE
@plugins.smartdata.svDb()
public id: string;
@plugins.smartdata.svDb()
public data: plugins.servezoneInterfaces.data.IExternalRegistry['data'];
constructor() {
super();
}
}

View File

@ -0,0 +1,51 @@
import * as plugins from '../plugins.js';
import { Cloudly } from '../classes.cloudly.js';
import { ExternalRegistry } from './classes.externalregistry.js';
export class ExternalRegistryManager {
public cloudlyRef: Cloudly;
public typedrouter = new plugins.typedrequest.TypedRouter();
public CExternalRegistry = plugins.smartdata.setDefaultManagerForDoc(this, ExternalRegistry);
get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb;
}
constructor(cloudlyRef: Cloudly) {
this.cloudlyRef = cloudlyRef;
}
public async start() {
// lets set up a typedrouter
this.typedrouter.addTypedRouter(this.typedrouter);
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.externalRegistry.IReq_GetRegistryById>(
new plugins.typedrequest.TypedHandler('getExternalRegistryById', async (dataArg) => {
const registry = await ExternalRegistry.getRegistryById(dataArg.id);
return {
registry: await registry.createSavableObject(),
};
})
);
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.externalRegistry.IReq_GetRegistries>(
new plugins.typedrequest.TypedHandler('getExternalRegistries', async (dataArg) => {
const registries = await ExternalRegistry.getRegistries();
return {
registries: await Promise.all(
registries.map((registry) => registry.createSavableObject())
),
};
})
);
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.externalRegistry.IReq_CreateRegistry>(
new plugins.typedrequest.TypedHandler('createExternalRegistry', async (dataArg) => {
const registry = await ExternalRegistry.createExternalRegistry(dataArg.registryData);
return {
registry: await registry.createSavableObject(),
};
})
);
}
}

View File

@ -0,0 +1,2 @@
export * from './classes.externalregistrymanager.js';
export * from './classes.externalregistry.js';

View File

@ -1,30 +0,0 @@
import * as plugins from '../plugins.js';
import * as paths from '../paths.js';
import type { Cloudly } from 'ts/classes.cloudly.js';
export class ExternalRegistry extends plugins.smartdata.SmartDataDbDoc<ExternalRegistry, plugins.servezoneInterfaces.data.IExternalRegistry> {
// STATIC
public async getRegistryById(registryNameArg: string) {
this
}
// INSTANCE
public cloudlyRef: Cloudly;
public smartdataDb: plugins.smartdata.SmartdataDb;
@plugins.smartdata.svDb()
public id: string;
@plugins.smartdata.svDb()
public data: plugins.servezoneInterfaces.data.IExternalRegistry['data'];
get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb;
}
constructor(cloudlyRef: Cloudly) {
super();
this.cloudlyRef = cloudlyRef;
}
}

View File

@ -59,4 +59,16 @@ export class SecretBundle extends plugins.smartdata.SmartDataDbDoc<
} }
return returnObject; return returnObject;
} }
public async getFlatKeyValueObject(environmentArg: string) {
if (!environmentArg) {
throw new Error('environment is required');
}
const secretGroups = await this.getSecretGroups();
const returnObject = {};
for (const secretGroup of secretGroups) {
returnObject[secretGroup.data.key] = secretGroup.data.environments[environmentArg].value;
}
return returnObject;
}
} }

View File

@ -1,3 +1,4 @@
import { SecretBundle } from 'ts/manager.secret/classes.secretbundle.js';
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import { ServiceManager } from './classes.servicemanager.js'; import { ServiceManager } from './classes.servicemanager.js';
@ -6,9 +7,51 @@ export class Service extends plugins.smartdata.SmartDataDbDoc<
plugins.servezoneInterfaces.data.IService, plugins.servezoneInterfaces.data.IService,
ServiceManager ServiceManager
> { > {
// STATIC
public static async getServiceById(serviceIdArg: string) {
const service = await this.getInstance({
id: serviceIdArg,
});
return service;
}
public static async getServices() {
const services = await this.getInstances({});
return services;
}
public static async createService(serviceDataArg: Partial<plugins.servezoneInterfaces.data.IService['data']>) {
const service = new Service();
service.id = await Service.getNewId();
Object.assign(service, serviceDataArg);
await service.save();
return service;
}
// INSTANCE
@plugins.smartdata.svDb() @plugins.smartdata.svDb()
public id: string; public id: string;
@plugins.smartdata.svDb() @plugins.smartdata.svDb()
public data: plugins.servezoneInterfaces.data.IService['data']; public data: plugins.servezoneInterfaces.data.IService['data'];
/**
* a service runs in a specific environment
* so -> this method returns the secret bundles as a flat object accordingly.
* in other words, it resolves secret groups for the relevant environment
* @param environmentArg
*/
public async getSecretBundlesAsFlatObject(environmentArg: string = 'production') {
const secreBundleIds = this.data.additionalSecretBundleIds || [];
secreBundleIds.push(this.data.secretBundleId); // put this last, so it overwrites any other secret bundles.
let finalFlatObject = {};
for (const secretBundleId of secreBundleIds) {
const secretBundle = await SecretBundle.getInstance({
id: secretBundleId,
});
const flatObject = await secretBundle.getFlatKeyValueObject(environmentArg);
Object.assign(finalFlatObject, flatObject);
}
return finalFlatObject;
}
} }

View File

@ -35,5 +35,66 @@ export class ServiceManager {
} }
) )
); );
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject>(
'getServiceSecretBundlesAsFlatObject',
async (dataArg) => {
const service = await Service.getInstance({
id: dataArg.serviceId,
});
const flatKeyValueObject = await service.getSecretBundlesAsFlatObject(dataArg.environment);
return {
flatKeyValueObject: flatKeyValueObject,
};
}
)
);
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_CreateService>(
'createService',
async (dataArg) => {
const service = await Service.createService(dataArg.serviceData);
return {
service: await service.createSavableObject(),
};
}
)
);
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_UpdateService>(
'updateService',
async (dataArg) => {
const service = await Service.getInstance({
id: dataArg.serviceId,
});
service.data = {
...service.data,
...dataArg.serviceData,
};
await service.save();
return {
service: await service.createSavableObject(),
};
}
)
);
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_DeleteServiceById>(
'deleteServiceById',
async (dataArg) => {
const service = await Service.getInstance({
id: dataArg.serviceId,
});
await service.delete();
return {
success: true,
};
}
)
);
} }
} }

View File

@ -7,6 +7,7 @@ import { Service } from './classes.service.js';
import { Cluster } from './classes.cluster.js'; import { Cluster } from './classes.cluster.js';
import { SecretBundle } from './classes.secretbundle.js'; import { SecretBundle } from './classes.secretbundle.js';
import { SecretGroup } from './classes.secretgroup.js'; import { SecretGroup } from './classes.secretgroup.js';
import { ExternalRegistry } from './classes.externalregistry.js';
export class CloudlyApiClient { export class CloudlyApiClient {
private cloudlyUrl: string; private cloudlyUrl: string;
@ -159,6 +160,19 @@ export class CloudlyApiClient {
return typedResponse.certificate; return typedResponse.certificate;
} }
public externalRegistry = {
// ExternalRegistry
getRegistryById: async (registryNameArg: string) => {
return ExternalRegistry.getExternalRegistryById(this, registryNameArg);
},
getRegistries: async () => {
return ExternalRegistry.getExternalRegistries(this);
},
createRegistry: async (optionsArg: Parameters<typeof ExternalRegistry.createExternalRegistry>[1]) => {
return ExternalRegistry.createExternalRegistry(this, optionsArg);
}
}
public image = { public image = {
// Images // Images
getImageById: async (imageIdArg: string) => { getImageById: async (imageIdArg: string) => {

View File

@ -0,0 +1,83 @@
import * as plugins from './plugins.js';
import type { CloudlyApiClient } from './classes.cloudlyapiclient.js';
export class ExternalRegistry implements plugins.servezoneInterfaces.data.IExternalRegistry {
// STATIC
public static async getExternalRegistryById(cloudlyClientRef: CloudlyApiClient, registryNameArg: string) {
const getRegistryByIdTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.externalRegistry.IReq_GetRegistryById>(
'getExternalRegistryById'
);
const response = await getRegistryByIdTR.fire({
identity: cloudlyClientRef.identity,
id: registryNameArg,
});
const newRegistry = new ExternalRegistry(cloudlyClientRef);
Object.assign(newRegistry, response.registry);
return newRegistry;
}
public static async getExternalRegistries(cloudlyClientRef: CloudlyApiClient) {
const getRegistriesTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.externalRegistry.IReq_GetRegistries>(
'getExternalRegistries'
);
const response = await getRegistriesTR.fire({
identity: cloudlyClientRef.identity,
});
const registryConfigs: ExternalRegistry[] = [];
for (const registryConfig of response.registries) {
const newRegistry = new ExternalRegistry(cloudlyClientRef);
Object.assign(newRegistry, registryConfig);
registryConfigs.push(newRegistry);
}
return registryConfigs;
}
public static async createExternalRegistry(cloudlyClientRef: CloudlyApiClient, registryDataArg: Partial<plugins.servezoneInterfaces.data.IExternalRegistry['data']>) {
const createRegistryTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.externalRegistry.IReq_CreateRegistry>(
'createExternalRegistry'
);
const response = await createRegistryTR.fire({
identity: cloudlyClientRef.identity,
registryData: registryDataArg as plugins.servezoneInterfaces.data.IExternalRegistry['data'],
});
const newRegistry = new ExternalRegistry(cloudlyClientRef);
Object.assign(newRegistry, response.registry);
return newRegistry;
}
// INSTANCE
public id: string;
public data: plugins.servezoneInterfaces.data.IExternalRegistry['data'];
public cloudlyClientRef: CloudlyApiClient;
constructor(cloudlyClientRef: CloudlyApiClient) {
this.cloudlyClientRef = cloudlyClientRef;
}
public async update() {
const updateRegistryTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.externalRegistry.IReq_UpdateRegistry>(
'updateExternalRegistry'
);
const response = await updateRegistryTR.fire({
identity: this.cloudlyClientRef.identity,
registryData: this.data,
});
const resultRegistryData = response.resultRegistry.data;
plugins.smartexpect.expect(resultRegistryData).toEqual(this.data);
return this;
}
public async delete(cloudlyClientRef: CloudlyApiClient, registryIdArg: string) {
const deleteRegistryTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.externalRegistry.IReq_DeleteRegistryById>(
'deleteExternalRegistryById'
);
const response = await deleteRegistryTR.fire({
identity: cloudlyClientRef.identity,
registryId: this.id,
});
plugins.smartexpect.expect(response.ok).toBeTrue();
return null;
}
}

View File

@ -59,7 +59,7 @@ export class SecretBundle implements plugins.servezoneInterfaces.data.ISecretBun
description: secretBundleDataArg.description, description: secretBundleDataArg.description,
type: secretBundleDataArg.type, type: secretBundleDataArg.type,
authorizations: secretBundleDataArg.authorizations, authorizations: secretBundleDataArg.authorizations,
includedImages: secretBundleDataArg.includedImages, imageClaims: secretBundleDataArg.imageClaims,
includedSecretGroupIds: secretBundleDataArg.includedSecretGroupIds, includedSecretGroupIds: secretBundleDataArg.includedSecretGroupIds,
includedTags: secretBundleDataArg.includedTags, includedTags: secretBundleDataArg.includedTags,
}, },

View File

@ -40,19 +40,7 @@ export class Service implements plugins.servezoneInterfaces.data.IService {
); );
const response = await createServiceTR.fire({ const response = await createServiceTR.fire({
identity: cloudlyClientRef.identity, identity: cloudlyClientRef.identity,
name: serviceDataArg.name, serviceData: serviceDataArg as plugins.servezoneInterfaces.data.IService['data'],
description: serviceDataArg.description,
imageId: serviceDataArg.imageId,
imageVersion: serviceDataArg.imageVersion,
environment: {},
secretBundleId: null,
scaleFactor: 1,
balancingStrategy: serviceDataArg.balancingStrategy,
ports: {
web: null,
},
resources: serviceDataArg.resources,
domains: [],
}); });
const newService = new Service(cloudlyClientRef); const newService = new Service(cloudlyClientRef);
Object.assign(newService, response.service); Object.assign(newService, response.service);
@ -75,6 +63,16 @@ export class Service implements plugins.servezoneInterfaces.data.IService {
* In other words, it resolves secret groups and * In other words, it resolves secret groups and
*/ */
public async getSecretBundleAsFlatObject(environmentArg: string = 'production') { public async getSecretBundleAsFlatObject(environmentArg: string = 'production') {
const getServiceSecretBundlesAsFlatObjectTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.service.IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject>(
'getServiceSecretBundlesAsFlatObject'
);
const response = await getServiceSecretBundlesAsFlatObjectTR.fire({
identity: this.cloudlyClientRef.identity,
serviceId: this.id,
environment: environmentArg,
});
const flatKeyValueObject: {[key: string]: string} = response.flatKeyValueObject;
return flatKeyValueObject;
} }
} }

View File

@ -0,0 +1,15 @@
import * as plugins from './plugins.js';
import { CloudlyApiClient } from '@serve.zone/api';
export class CliClient {
public cloudlyApiClient: CloudlyApiClient;
constructor(cloudlyApiClientArg: CloudlyApiClient) {
this.cloudlyApiClient = cloudlyApiClientArg;
}
public async getClusters() {
const clusters = await this.cloudlyApiClient.cluster.getClusters();
console.log(clusters);
}
}

View File

@ -1 +1,11 @@
console.log('this is the cli client.'); import * as plugins from './plugins.js';
import { CliClient } from "./classes.cliclient.js";
export const runCli = async () => {
const cliQenv = new plugins.qenv.Qenv();
const apiClient = new plugins.servezoneApi.CloudlyApiClient({
registerAs: 'cli',
cloudlyUrl: await cliQenv.getEnvVarOnDemand('CLOUDLY_URL'),
});
const cliClient = new CliClient(apiClient);
};

17
ts_cliclient/plugins.ts Normal file
View File

@ -0,0 +1,17 @@
// @serve.zone scope
import * as servezoneApi from '@serve.zone/api';
import * as servezoneInterfaces from '@serve.zone/interfaces';
export {
servezoneApi,
servezoneInterfaces
}
// @push.rocks scope
import * as projectinfo from '@push.rocks/projectinfo';
import * as qenv from '@push.rocks/qenv';
export {
projectinfo,
qenv,
}

View File

@ -1,8 +1,15 @@
{ {
"name": "@serve.zone/cli", "name": "@serve.zone/cli",
"dependencies": [], "dependencies": [
"@serve.zone/api",
"@serve.zone/interfaces",
"@push.rocks/projectinfo",
"@push.rocks/qenv",
"@push.rocks/smartcli"
],
"registries": [ "registries": [
"registry.npmjs.org:public", "registry.npmjs.org:public",
"verdaccio.lossless.digital:public" "verdaccio.lossless.digital:public"
] ],
"bin": ["servezone"]
} }

View File

@ -4,9 +4,10 @@ export interface IImage {
id: string; id: string;
data: { data: {
name: string; name: string;
external?: { location: {
internal: boolean;
externalRegistryId: string; externalRegistryId: string;
imageName: string; externalImageTag: string;
} }
description: string; description: string;
versions: Array<{ versions: Array<{

View File

@ -21,13 +21,19 @@ export interface ISecretBundle {
*/ */
type: 'service' | 'npmci' | 'gitzone' | 'external'; type: 'service' | 'npmci' | 'gitzone' | 'external';
/**
* set this if the secretBundle belongs to a service
*/
serviceId?: string;
/** /**
* You can add specific secret groups using this * You can add specific secret groups using this
*/ */
includedSecretGroupIds: string[]; includedSecretGroupIds: string[];
/** /**
* You can add specific tags using this * access to this secretBundle also grants access to resources with matching tags
*/ */
includedTags: { includedTags: {
key: string; key: string;
@ -35,12 +41,12 @@ export interface ISecretBundle {
}[]; }[];
/** /**
* add images * access to this secretBundle also grants access to the images
*/ */
includedImages: { imageClaims: {
imageId: string; imageId: string;
permissions: ('read' | 'write')[]; permissions: ('read' | 'write')[];
}[]; }[];
/** /**
* authrozations select a specific environment of a config bundle * authrozations select a specific environment of a config bundle

View File

@ -8,7 +8,15 @@ export interface IService {
imageId: string; imageId: string;
imageVersion: string; imageVersion: string;
environment: { [key: string]: string }; environment: { [key: string]: string };
/**
* the main secret bundle id, exclusive to the service
*/
secretBundleId: string; secretBundleId: string;
/**
* those secret bundle ids do not belong to the service itself
* and thus live past the service lifecycle
*/
additionalSecretBundleIds?: string[];
scaleFactor: number; scaleFactor: number;
balancingStrategy: 'round-robin' | 'least-connections'; balancingStrategy: 'round-robin' | 'least-connections';
ports: { ports: {

View File

@ -2,9 +2,9 @@ import * as plugins from '../plugins.js';
export type TTemplates = 'default' | 'linkaction' | 'notification'; export type TTemplates = 'default' | 'linkaction' | 'notification';
export interface IRequest_SendEmail extends plugins.typedrequestInterfaces.implementsTR< export interface IReq_SendEmail extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest, plugins.typedrequestInterfaces.ITypedRequest,
IRequest_SendEmail IReq_SendEmail
> { > {
method: 'sendEmail'; method: 'sendEmail';
request: { request: {
@ -25,9 +25,9 @@ export interface IRequest_SendEmail extends plugins.typedrequestInterfaces.imple
}; };
} }
export interface IRequestRegisterRecipient extends plugins.typedrequestInterfaces.implementsTR< export interface IReq_RegisterRecipient extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest, plugins.typedrequestInterfaces.ITypedRequest,
IRequestRegisterRecipient IReq_RegisterRecipient
> { > {
method: 'registerRecepient'; method: 'registerRecepient';
request: { request: {
@ -37,3 +37,34 @@ export interface IRequestRegisterRecipient extends plugins.typedrequestInterface
status: 'ok' | 'not ok'; status: 'ok' | 'not ok';
}; };
} }
export interface IReq_CheckEmailStatus extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_CheckEmailStatus
> {
method: 'checkEmailStatus';
request: {
emailId: string;
};
response: {
status: string,
details?: { message: string; }
};
}
export interface IReq_GetEMailStats extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_GetEMailStats
> {
method: 'getEmailStats';
request: {
jwt: string;
};
response: {
totalEmailsSent: number;
totalEmailsDelivered: number;
totalEmailsBounced: number;
averageDeliveryTimeMs: number;
lastUpdated: string;
};
}

View File

@ -0,0 +1,72 @@
import * as plugins from '../plugins.js';
import * as data from '../data/index.js';
import * as userInterfaces from '../data/user.js';
export interface IReq_GetRegistryById extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_GetRegistryById
> {
method: 'getExternalRegistryById';
request: {
identity: userInterfaces.IIdentity;
id: string;
};
response: {
registry: data.IExternalRegistry;
};
}
export interface IReq_GetRegistries extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_GetRegistries
> {
method: 'getExternalRegistries';
request: {
identity: userInterfaces.IIdentity;
};
response: {
registries: data.IExternalRegistry[];
};
}
export interface IReq_CreateRegistry extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_CreateRegistry
> {
method: 'createExternalRegistry';
request: {
identity: userInterfaces.IIdentity;
registryData: data.IExternalRegistry['data'];
};
response: {
registry: data.IExternalRegistry;
};
}
export interface IReq_UpdateRegistry extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_UpdateRegistry
> {
method: 'updateExternalRegistry';
request: {
identity: userInterfaces.IIdentity;
registryData: data.IExternalRegistry['data'];
};
response: {
resultRegistry: data.IExternalRegistry;
};
}
export interface IReq_DeleteRegistryById extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IReq_DeleteRegistryById
> {
method: 'deleteExternalRegistryById';
request: {
identity: userInterfaces.IIdentity;
registryId: string;
};
response: {
ok: boolean;
};
}

View File

@ -4,6 +4,7 @@ import * as adminRequests from './admin.js';
import * as certificateRequests from './certificate.js'; import * as certificateRequests from './certificate.js';
import * as clusterRequests from './cluster.js'; import * as clusterRequests from './cluster.js';
import * as configRequests from './config.js'; import * as configRequests from './config.js';
import * as externalRegistryRequests from './externalregistry.js';
import * as identityRequests from './identity.js'; import * as identityRequests from './identity.js';
import * as imageRequests from './image.js'; import * as imageRequests from './image.js';
import * as informRequests from './inform.js'; import * as informRequests from './inform.js';
@ -22,6 +23,7 @@ export {
certificateRequests as certificate, certificateRequests as certificate,
clusterRequests as cluster, clusterRequests as cluster,
configRequests as config, configRequests as config,
externalRegistryRequests as externalRegistry,
identityRequests as identity, identityRequests as identity,
imageRequests as image, imageRequests as image,
informRequests as inform, informRequests as inform,

View File

@ -40,24 +40,7 @@ extends plugins.typedrequestInterfaces.implementsTR<
method: 'createService'; method: 'createService';
request: { request: {
identity: IIdentity; identity: IIdentity;
name: string; serviceData: IService['data'];
description: string;
imageId: string;
imageVersion: string;
environment: { [key: string]: string };
secretBundleId: string;
scaleFactor: number;
balancingStrategy: 'round-robin' | 'least-connections';
ports: {
web: number;
custom?: { [domain: string]: string };
};
resources?: IServiceRessources;
domains: {
name: string;
port?: number;
protocol?: 'http' | 'https' | 'ssh';
}[];
}; };
response: { response: {
service: IService; service: IService;
@ -73,36 +56,19 @@ extends plugins.typedrequestInterfaces.implementsTR<
request: { request: {
identity: IIdentity; identity: IIdentity;
serviceId: string; serviceId: string;
name: string; serviceData: IService['data'];
description: string;
imageId: string;
imageVersion: string;
environment: { [key: string]: string };
secretBundleId: string;
scaleFactor: number;
balancingStrategy: 'round-robin' | 'least-connections';
ports: {
web: number;
custom?: { [domain: string]: string };
};
resources?: IServiceRessources;
domains: {
name: string;
port?: number;
protocol?: 'http' | 'https' | 'ssh';
}[];
}; };
response: { response: {
service: IService; service: IService;
}; };
} }
export interface IRequest_Any_Cloudly_DeleteService export interface IRequest_Any_Cloudly_DeleteServiceById
extends plugins.typedrequestInterfaces.implementsTR< extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest, plugins.typedrequestInterfaces.ITypedRequest,
IRequest_Any_Cloudly_DeleteService IRequest_Any_Cloudly_DeleteServiceById
> { > {
method: 'deleteService'; method: 'deleteServiceById';
request: { request: {
identity: IIdentity; identity: IIdentity;
serviceId: string; serviceId: string;
@ -111,3 +77,19 @@ extends plugins.typedrequestInterfaces.implementsTR<
success: boolean; success: boolean;
}; };
} }
export interface IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject
extends plugins.typedrequestInterfaces.implementsTR<
plugins.typedrequestInterfaces.ITypedRequest,
IRequest_Any_Cloudly_GetServiceSecretBundlesAsFlatObject
> {
method: 'getServiceSecretBundlesAsFlatObject';
request: {
identity: IIdentity;
serviceId: string;
environment: string;
};
response: {
flatKeyValueObject: {[key: string]: string};
};
}

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/cloudly', name: '@serve.zone/cloudly',
version: '4.8.0', version: '5.0.4',
description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.' description: 'A comprehensive tool for managing containerized applications across multiple cloud providers using Docker Swarmkit, featuring web, CLI, and API interfaces.'
} }

View File

@ -24,6 +24,7 @@ import { CloudlyViewS3 } from './cloudly-view-s3.js';
import { CloudlyViewSecretBundles } from './cloudly-view-secretbundles.js'; import { CloudlyViewSecretBundles } from './cloudly-view-secretbundles.js';
import { CloudlyViewSecretGroups } from './cloudly-view-secretgroups.js'; import { CloudlyViewSecretGroups } from './cloudly-view-secretgroups.js';
import { CloudlyViewServices } from './cloudly-view-services.js'; import { CloudlyViewServices } from './cloudly-view-services.js';
import { CloudlyViewExternalRegistries } from './cloudly-view-externalregistries.js';
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
@ -89,6 +90,10 @@ export class CloudlyDashboard extends DeesElement {
name: 'Clusters', name: 'Clusters',
element: CloudlyViewClusters, element: CloudlyViewClusters,
}, },
{
name: 'ExternalRegistries',
element: CloudlyViewExternalRegistries,
},
{ {
name: 'Images', name: 'Images',
element: CloudlyViewImages, element: CloudlyViewImages,

View File

@ -0,0 +1,129 @@
import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import {
DeesElement,
customElement,
html,
state,
css,
cssManager,
} from '@design.estate/dees-element';
import * as appstate from '../appstate.js';
@customElement('cloudly-view-externalregistries')
export class CloudlyViewExternalRegistries extends DeesElement {
@state()
private data: appstate.IDataState = {
secretGroups: [],
secretBundles: [],
};
constructor() {
super();
const subecription = appstate.dataState
.select((stateArg) => stateArg)
.subscribe((dataArg) => {
this.data = dataArg;
});
this.rxSubscriptions.push(subecription);
}
public static styles = [
cssManager.defaultStyles,
shared.viewHostCss,
css`
`,
];
public render() {
return html`
<cloudly-sectionheading>External Registries</cloudly-sectionheading>
<dees-table
.heading1=${'External Registries'}
.heading2=${'decoded in client'}
.data=${this.data.deployments}
.displayFunction=${(itemArg: plugins.interfaces.data.ICluster) => {
return {
id: itemArg.id,
serverAmount: itemArg.data.servers.length,
};
}}
.dataActions=${[
{
name: 'add configBundle',
iconName: 'plus',
type: ['header', 'footer'],
actionFunc: async (dataActionArg) => {
const modal = await plugins.deesCatalog.DeesModal.createAndShow({
heading: 'Add ConfigBundle',
content: html`
<dees-form>
<dees-input-text .key=${'id'} .label=${'ID'} .value=${''}></dees-input-text>
<dees-input-text
.key=${'data.secretGroupIds'}
.label=${'secretGroupIds'}
.value=${''}
></dees-input-text>
<dees-input-text
.key=${'data.includedTags'}
.label=${'includedTags'}
.value=${''}
></dees-input-text>
</dees-form>
`,
menuOptions: [
{ name: 'create', action: async (modalArg) => {} },
{
name: 'cancel',
action: async (modalArg) => {
modalArg.destroy();
},
},
],
});
},
},
{
name: 'delete',
iconName: 'trash',
type: ['contextmenu', 'inRow'],
actionFunc: async (actionDataArg) => {
plugins.deesCatalog.DeesModal.createAndShow({
heading: `Delete ConfigBundle ${actionDataArg.item.id}`,
content: html`
<div style="text-align:center">
Do you really want to delete the ConfigBundle?
</div>
<div
style="font-size: 0.8em; color: red; text-align:center; padding: 16px; margin-top: 24px; border: 1px solid #444; font-family: Intel One Mono; font-size: 16px;"
>
${actionDataArg.item.id}
</div>
`,
menuOptions: [
{
name: 'cancel',
action: async (modalArg) => {
await modalArg.destroy();
},
},
{
name: 'delete',
action: async (modalArg) => {
appstate.dataState.dispatchAction(appstate.deleteSecretBundleAction, {
configBundleId: actionDataArg.item.id,
});
await modalArg.destroy();
},
},
],
});
},
},
] as plugins.deesCatalog.ITableAction[]}
></dees-table>
`;
}
}

View File

@ -18,11 +18,28 @@ export class CloudlySectionheading extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
css` css`
:host {
display: grid;
grid-template-columns: min-content min-content;
}
h1 { h1 {
font-family: 'Cal Sans'; font-family: 'Cal Sans';
letter-spacing: 0.025em; letter-spacing: 0.025em;
margin: 0px; margin: 0px;
margin-bottom: 16px; margin-bottom: 16px;
white-space: nowrap;
}
.flag {
border-radius: 4px;
background: #8a0183;
height: 20px;
padding: 2px 4px;
margin-left: 16px;
font-size: 12px;
transform: translateY(12px);
white-space: nowrap;
} }
`, `,
] ]
@ -30,6 +47,7 @@ export class CloudlySectionheading extends DeesElement {
public render() { public render() {
return html` return html`
<h1><slot></slot></h1> <h1><slot></slot></h1>
<div class="flag">stability: alpha</div>
`; `;
} }
} }