Compare commits

..

9 Commits

Author SHA1 Message Date
206fe445bc 1.1.5
Some checks failed
Docker (tags) / security (push) Failing after 18s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2024-06-20 19:00:58 +02:00
a7ee92cde9 fix(core): update 2024-06-20 19:00:58 +02:00
cdbab26008 1.1.4
Some checks failed
Docker (tags) / security (push) Failing after 19s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2024-06-13 10:07:54 +02:00
1983c64b77 fix(core): update 2024-06-13 10:07:53 +02:00
a6e3a7f5fe prepare service management 2024-06-13 09:36:02 +02:00
6dd687012f 1.1.3
Some checks failed
Docker (tags) / security (push) Failing after 15s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2024-06-05 14:13:04 +02:00
55b2872ffc fix(structure): improve structure, prepare better CI integration 2024-06-05 14:13:03 +02:00
2e6e7f6ca8 1.1.2
Some checks failed
Docker (tags) / security (push) Failing after 14s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2024-06-02 21:39:32 +02:00
f453ce3126 fix(imagemanager): prepare proper storage and retrieval of container images 2024-06-02 21:39:31 +02:00
37 changed files with 1841 additions and 2432 deletions

View File

@ -16,26 +16,37 @@
"githost": "gitlab.com", "githost": "gitlab.com",
"gitscope": "servezone/private", "gitscope": "servezone/private",
"gitrepo": "cloudly", "gitrepo": "cloudly",
"description": "A cloud manager leveraging Docker Swarmkit for multi-cloud operations including DigitalOcean, Hetzner Cloud, and Cloudflare, with integration support and robust configuration management system.", "description": "A comprehensive multi-cloud manager leveraging Docker Swarmkit to orchestrate containerized applications across various cloud services and integrate robust configuration and API management capabilities.",
"npmPackagename": "@serve.zone/cloudly", "npmPackagename": "@serve.zone/cloudly",
"license": "UNLICENSED", "license": "UNLICENSED",
"keywords": [ "keywords": [
"cloud management", "cloud management",
"Docker Swarmkit",
"multi-cloud", "multi-cloud",
"Docker Swarmkit",
"DigitalOcean", "DigitalOcean",
"Hetzner Cloud", "Hetzner Cloud",
"Cloudflare", "Cloudflare",
"container orchestration", "container orchestration",
"TypeScript",
"node.js",
"infrastructure automation",
"Cloudron",
"configuration management", "configuration management",
"SSL management", "SSL management",
"APIs", "API integration",
"TypeScript",
"node.js",
"cloud integration",
"infrastructure automation",
"devOps", "devOps",
"cloud integration" "cloud API client",
"system logging",
"secret management",
"CI/CD integration",
"task scheduling",
"frontend",
"backend",
"cli",
"web interface",
"cloud providers",
"security",
"logging"
] ]
} }
}, },

View File

@ -1,8 +1,8 @@
{ {
"name": "@serve.zone/cloudly", "name": "@serve.zone/cloudly",
"version": "1.1.1", "version": "1.1.5",
"private": false, "private": false,
"description": "A cloud manager leveraging Docker Swarmkit for multi-cloud operations including DigitalOcean, Hetzner Cloud, and Cloudflare, with integration support and robust configuration management system.", "description": "A comprehensive multi-cloud manager leveraging Docker Swarmkit to orchestrate containerized applications across various cloud services and integrate robust configuration and API management capabilities.",
"type": "module", "type": "module",
"exports": { "exports": {
".": "./dist/index.js", ".": "./dist/index.js",
@ -26,47 +26,50 @@
"@git.zone/tstest": "^1.0.90", "@git.zone/tstest": "^1.0.90",
"@git.zone/tswatch": "^2.0.23", "@git.zone/tswatch": "^2.0.23",
"@push.rocks/tapbundle": "^5.0.23", "@push.rocks/tapbundle": "^5.0.23",
"@types/node": "^20.12.14" "@types/node": "^20.14.6"
}, },
"dependencies": { "dependencies": {
"@api.global/typedrequest": "3.0.29", "@api.global/typedrequest": "3.0.30",
"@api.global/typedserver": "^3.0.50", "@api.global/typedserver": "^3.0.50",
"@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/digitalocean": "^1.0.5", "@apiclient.xyz/docker": "^1.2.2",
"@apiclient.xyz/hetznercloud": "^1.0.18", "@apiclient.xyz/hetznercloud": "^1.2.0",
"@apiclient.xyz/slack": "^3.0.9", "@apiclient.xyz/slack": "^3.0.9",
"@design.estate/dees-catalog": "^1.0.289", "@design.estate/dees-catalog": "^1.0.289",
"@design.estate/dees-domtools": "^2.0.57", "@design.estate/dees-domtools": "^2.0.57",
"@design.estate/dees-element": "^2.0.34", "@design.estate/dees-element": "^2.0.34",
"@git.zone/tsrun": "^1.2.37", "@git.zone/tsrun": "^1.2.37",
"@push.rocks/early": "^4.0.3", "@push.rocks/early": "^4.0.3",
"@push.rocks/npmextra": "^5.0.13", "@push.rocks/npmextra": "^5.0.23",
"@push.rocks/projectinfo": "^5.0.1", "@push.rocks/projectinfo": "^5.0.1",
"@push.rocks/qenv": "^6.0.5", "@push.rocks/qenv": "^6.0.5",
"@push.rocks/smartacme": "^4.0.8", "@push.rocks/smartacme": "^5.0.0",
"@push.rocks/smartbucket": "^3.0.9", "@push.rocks/smartbucket": "^3.0.20",
"@push.rocks/smartcli": "^4.0.11", "@push.rocks/smartcli": "^4.0.11",
"@push.rocks/smartdata": "^5.2.4", "@push.rocks/smartclickhouse": "^2.0.17",
"@push.rocks/smartdata": "^5.2.6",
"@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/smartfile": "^11.0.15", "@push.rocks/smartfile": "^11.0.20",
"@push.rocks/smartguard": "^3.0.2", "@push.rocks/smartguard": "^3.0.2",
"@push.rocks/smartjson": "^5.0.19", "@push.rocks/smartjson": "^5.0.19",
"@push.rocks/smartjwt": "^2.0.4", "@push.rocks/smartjwt": "^2.0.4",
"@push.rocks/smartlog": "^3.0.6", "@push.rocks/smartlog": "^3.0.7",
"@push.rocks/smartlog-destination-clickhouse": "^1.0.11", "@push.rocks/smartlog-destination-clickhouse": "^1.0.13",
"@push.rocks/smartpath": "^5.0.18", "@push.rocks/smartpath": "^5.0.18",
"@push.rocks/smartpromise": "^4.0.3", "@push.rocks/smartpromise": "^4.0.3",
"@push.rocks/smartrequest": "^2.0.22", "@push.rocks/smartrequest": "^2.0.22",
"@push.rocks/smartrx": "^3.0.7", "@push.rocks/smartrx": "^3.0.7",
"@push.rocks/smartssh": "^2.0.1", "@push.rocks/smartssh": "^2.0.1",
"@push.rocks/smartstate": "^2.0.17",
"@push.rocks/smartstream": "^3.0.44",
"@push.rocks/smartstring": "^4.0.15", "@push.rocks/smartstring": "^4.0.15",
"@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",
"@serve.zone/interfaces": "^1.0.61", "@serve.zone/interfaces": "^1.0.74",
"@tsclass/tsclass": "^4.0.54" "@tsclass/tsclass": "^4.0.60"
}, },
"files": [ "files": [
"ts/**/*", "ts/**/*",
@ -93,20 +96,31 @@
"homepage": "https://gitlab.com/servezone/private/cloudly#readme", "homepage": "https://gitlab.com/servezone/private/cloudly#readme",
"keywords": [ "keywords": [
"cloud management", "cloud management",
"Docker Swarmkit",
"multi-cloud", "multi-cloud",
"Docker Swarmkit",
"DigitalOcean", "DigitalOcean",
"Hetzner Cloud", "Hetzner Cloud",
"Cloudflare", "Cloudflare",
"container orchestration", "container orchestration",
"TypeScript",
"node.js",
"infrastructure automation",
"Cloudron",
"configuration management", "configuration management",
"SSL management", "SSL management",
"APIs", "API integration",
"TypeScript",
"node.js",
"cloud integration",
"infrastructure automation",
"devOps", "devOps",
"cloud integration" "cloud API client",
"system logging",
"secret management",
"CI/CD integration",
"task scheduling",
"frontend",
"backend",
"cli",
"web interface",
"cloud providers",
"security",
"logging"
] ]
} }

3633
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
- This repository contains 4 projects around serve.zone
- the cloudly backend under ts/*
- the cloudly frontend under ts_web/*
- the api client under ts_apiclient
- the cli client under ts_cliclient
- the easiest method to spawn up a cloudly instance is to use the docker image:
`code.foss.global/serve.zone/cloudly:latest`
- Note: the exports are defined in the package.json.

View File

@ -1,21 +1,28 @@
# @serve.zone/cloudly # @serve.zone/cloudly
A cloud manager utilizing Docker Swarmkit, designed for operations on Cloudron, and supports various cloud platforms like DigitalOcean, Hetzner Cloud, and Cloudflare.
A comprehensive multi-cloud manager leveraging Docker Swarmkit for orchestrating containerized applications and integrating various cloud services like DigitalOcean, Hetzner Cloud, and Cloudflare, with robust configuration and API management capabilities.
## Install ## Install
To install `@serve.zone/cloudly`, run the following command in your terminal: To install `@serve.zone/cloudly`, run the following command in your terminal:
```bash ```bash
npm install @serve.zone/cloudly --save npm install @serve.zone/cloudly --save
``` ```
This will install the package and add it to your project's `package.json` dependencies. This will install the package and add it to your project's `package.json` dependencies.
## Usage ## Usage
`@serve.zone/cloudly` is designed to help you manage and configure cloud environments. This package provides a comprehensive TypeScript and ESM-based interface for interacting with various cloud services, including Docker Swarmkit cluster management, and integration with cloud providers such as DigitalOcean, Hetzner Cloud, and Cloudflare. `@serve.zone/cloudly` is designed to help you manage and configure cloud environments. This package provides a comprehensive TypeScript and ESM-based interface for interacting with various cloud services, including Docker Swarmkit cluster management, and integration with cloud providers such as DigitalOcean, Hetzner Cloud, and Cloudflare.
### Getting Started ### Getting Started
Before diving into the specifics, ensure your environment is properly set up. This includes having Node.js installed (preferably the latest LTS version), and if you are working in a TypeScript project, ensure TypeScript is configured. Before diving into the specifics, ensure your environment is properly set up. This includes having Node.js installed (preferably the latest LTS version), and if you are working in a TypeScript project, ensure TypeScript is configured.
#### Initializing Cloudly #### Initializing Cloudly
First, import `Cloudly` class from the package and initialize it as shown below:
First, import the `Cloudly` class from the package and initialize it as shown below:
```typescript ```typescript
import { Cloudly } from '@serve.zone/cloudly'; import { Cloudly } from '@serve.zone/cloudly';
@ -26,6 +33,7 @@ const myCloudlyInstance = new Cloudly();
The `Cloudly` class is the entry point to using the library features. It prepares the environment for configuring the cloud services. The `Cloudly` class is the entry point to using the library features. It prepares the environment for configuring the cloud services.
#### Configuration #### Configuration
Configuration plays a pivotal role in how `@serve.zone/cloudly` operates. The library expects certain configurations to be provided, which can include credentials for cloud services, database connections, etc. Configuration plays a pivotal role in how `@serve.zone/cloudly` operates. The library expects certain configurations to be provided, which can include credentials for cloud services, database connections, etc.
For example, to configure a connection to MongoDB, specify your MongoDB details as shown: For example, to configure a connection to MongoDB, specify your MongoDB details as shown:
@ -38,16 +46,22 @@ const myCloudlyConfig = {
mongoDbUser: 'myUser', mongoDbUser: 'myUser',
mongoDbPass: 'myPassword', mongoDbPass: 'myPassword',
}, },
// Additional configuration values... cfToken: 'your_cloudflare_api_token',
environment: 'development',
letsEncryptEmail: 'lets_encrypt_email@example.com',
publicUrl: 'example.com',
publicPort: 8443,
hetznerToken: 'your_hetzner_api_token',
}; };
const myCloudlyInstance = new Cloudly(myCloudlyConfig); const myCloudlyInstance = new Cloudly(myCloudlyConfig);
``` ```
#### Managing Docker Swarmkit Cluster ### Managing Docker Swarmkit Clusters
Cloudly allows managing Docker Swarmkit clusters through an abstracted interface, simplifying operations such as deployment and scaling. Below are examples to demonstrate these capabilities. Cloudly allows managing Docker Swarmkit clusters through an abstracted interface, simplifying operations such as deployment and scaling. Below are examples to demonstrate these capabilities.
### Example: Start a Cloudly Instance and Add a Cluster #### Example: Initializing a Cloudly Instance and Adding a Cluster
```typescript ```typescript
import { Cloudly, ClusterManager } from '@serve.zone/cloudly'; import { Cloudly, ClusterManager } from '@serve.zone/cloudly';
@ -65,7 +79,7 @@ async function main() {
letsEncryptEmail: 'lets_encrypt_email@example.com', letsEncryptEmail: 'lets_encrypt_email@example.com',
publicUrl: 'example.com', publicUrl: 'example.com',
publicPort: 8443, publicPort: 8443,
hetznerToken: 'your_hetzner_api_token' hetznerToken: 'your_hetzner_api_token',
}; };
const myCloudlyInstance = new Cloudly(myCloudlyConfig); const myCloudlyInstance = new Cloudly(myCloudlyConfig);
@ -88,10 +102,15 @@ async function main() {
console.log('Cluster added:', newCluster); console.log('Cluster added:', newCluster);
} }
main(); main();
``` ```
### Example: Manage Cloudflare DNS Records ### Additional Use Cases
#### Managing Cloudflare DNS Records
You can manage Cloudflare DNS records using the `CloudflareConnector` provided by Cloudly.
```typescript ```typescript
import { Cloudly, CloudflareConnector } from '@serve.zone/cloudly'; import { Cloudly, CloudflareConnector } from '@serve.zone/cloudly';
@ -109,7 +128,7 @@ async function manageDNSRecords() {
letsEncryptEmail: 'lets_encrypt_email@example.com', letsEncryptEmail: 'lets_encrypt_email@example.com',
publicUrl: 'example.com', publicUrl: 'example.com',
publicPort: 8443, publicPort: 8443,
hetznerToken: 'your_hetzner_api_token' hetznerToken: 'your_hetzner_api_token',
}; };
const myCloudlyInstance = new Cloudly(myCloudlyConfig); const myCloudlyInstance = new Cloudly(myCloudlyConfig);
@ -135,7 +154,9 @@ async function manageDNSRecords() {
manageDNSRecords(); manageDNSRecords();
``` ```
### Example: Integrate with DigitalOcean #### Integrating with DigitalOcean
Integrate with DigitalOcean to manage droplets and other resources.
```typescript ```typescript
import { Cloudly, DigitalOceanConnector } from '@serve.zone/cloudly'; import { Cloudly, DigitalOceanConnector } from '@serve.zone/cloudly';
@ -153,7 +174,7 @@ async function manageDroplet() {
letsEncryptEmail: 'lets_encrypt_email@example.com', letsEncryptEmail: 'lets_encrypt_email@example.com',
publicUrl: 'example.com', publicUrl: 'example.com',
publicPort: 8443, publicPort: 8443,
hetznerToken: 'your_hetzner_api_token' hetznerToken: 'your_hetzner_api_token',
}; };
const myCloudlyInstance = new Cloudly(myCloudlyConfig); const myCloudlyInstance = new Cloudly(myCloudlyConfig);
@ -180,8 +201,11 @@ manageDroplet();
``` ```
### Using Cloudly Web Interface ### Using Cloudly Web Interface
If your project includes a web interface to manage various sections like DNS, deployments, clusters, etc., you can use the provided elements and state management. Below is an example of setting up a dashboard using the components defined: If your project includes a web interface to manage various sections like DNS, deployments, clusters, etc., you can use the provided elements and state management. Below is an example of setting up a dashboard using the components defined:
#### Web Dashboard Example
```typescript ```typescript
import { commitinfo } from '../00_commitinfo_data.js'; import { commitinfo } from '../00_commitinfo_data.js';
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
@ -212,7 +236,7 @@ import { CloudlyViewServices } from './cloudly-view-services.js';
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
'cvault-dashboard': CloudlyDashboard; 'cloudly-dashboard': CloudlyDashboard;
} }
} }
@ -227,6 +251,7 @@ export class CloudlyDashboard extends DeesElement {
constructor() { constructor() {
super(); super();
document.title = `cloudly v${commitinfo.version}`;
const subcription = appstate.dataState const subcription = appstate.dataState
.select((stateArg) => stateArg) .select((stateArg) => stateArg)
.subscribe((dataArg) => { .subscribe((dataArg) => {
@ -309,6 +334,10 @@ export class CloudlyDashboard extends DeesElement {
name: 'Backups', name: 'Backups',
element: CloudlyViewBackups, element: CloudlyViewBackups,
}, },
{
name: 'Fleet',
element: CloudlyViewBackups,
}
] as plugins.deesCatalog.IView[]} ] as plugins.deesCatalog.IView[]}
></dees-simple-appdash> ></dees-simple-appdash>
</dees-simple-login> </dees-simple-login>
@ -329,7 +358,7 @@ export class CloudlyDashboard extends DeesElement {
action: async () => { action: async () => {
await plugins.deesCatalog.DeesModal.createAndShow({ await plugins.deesCatalog.DeesModal.createAndShow({
heading: 'About', heading: 'About',
content: html`configvault ${commitinfo.version}`, content: html`cloudly ${commitinfo.version}`,
menuOptions: [ menuOptions: [
{ {
name: 'close', name: 'close',
@ -352,11 +381,12 @@ export class CloudlyDashboard extends DeesElement {
if (loginState.jwt) { if (loginState.jwt) {
this.jwt = loginState.jwt; this.jwt = loginState.jwt;
await simpleLogin.switchToSlottedContent(); await simpleLogin.switchToSlottedContent();
await appstate.dataState.dispatchAction(appstate.getDataAction, null); await appstate.dataState.dispatchAction(appstate.getAllDataAction, null);
} }
} }
private async login(username: string, password: string) { private async login(username: string, password: string) {
const domtools = await this.domtoolsPromise;
console.log(`attempting to login...`); console.log(`attempting to login...`);
const simpleLogin = this.shadowRoot.querySelector('dees-simple-login'); const simpleLogin = this.shadowRoot.querySelector('dees-simple-login');
const form = simpleLogin.shadowRoot.querySelector('dees-form'); const form = simpleLogin.shadowRoot.querySelector('dees-form');
@ -370,7 +400,7 @@ export class CloudlyDashboard extends DeesElement {
this.jwt = state.jwt; this.jwt = state.jwt;
form.setStatus('success', 'Logged in!'); form.setStatus('success', 'Logged in!');
await simpleLogin.switchToSlottedContent(); await simpleLogin.switchToSlottedContent();
await appstate.dataState.dispatchAction(appstate.getDataAction, null); await appstate.dataState.dispatchAction(appstate.getAllDataAction, null);
} else { } else {
form.setStatus('error', 'Login failed!'); form.setStatus('error', 'Login failed!');
await domtools.convenience.smartdelay.delayFor(2000); await domtools.convenience.smartdelay.delayFor(2000);
@ -382,8 +412,6 @@ export class CloudlyDashboard extends DeesElement {
} }
``` ```
This script sets up a cloud management dashboard for interacting with various cloud services seamlessly. It covers creating clusters, managing DNS records, handling cloud-provider-specific resources, and much more.
With the examples provided above, you should now have a good understanding of how to use `@serve.zone/cloudly` to manage your cloud infrastructure programmatically. For deeper insights and additional features, refer to the documentation relevant to specific modules and methods used in your application. With the examples provided above, you should now have a good understanding of how to use `@serve.zone/cloudly` to manage your cloud infrastructure programmatically. For deeper insights and additional features, refer to the documentation relevant to specific modules and methods used in your application.
## License and Legal Information ## License and Legal Information

24
test/test.apiclient.ts Normal file
View File

@ -0,0 +1,24 @@
import { tap, expect } from '@push.rocks/tapbundle';
import * as cloudlyApiClient from '../ts_apiclient/index.js';
let testClient: cloudlyApiClient.CloudlyApiClient;
tap.test('should create a new cloudlyApiClient', async () => {
testClient = new cloudlyApiClient.CloudlyApiClient({
registerAs: 'api',
cloudlyUrl: 'http://localhost:3000',
});
await testClient.start();
expect(testClient).toBeTruthy();
});
tap.test('should trigger a server action', async () => {
})
tap.test('should stop the apiclient', async () => {
await testClient.stop();
})
export default tap.start();

View File

@ -10,20 +10,19 @@ import * as cloudly from '../ts/index.js';
let testCloudly: cloudly.Cloudly; let testCloudly: cloudly.Cloudly;
tap.test('first test', async () => { tap.test('first test', async () => {
const cloudlyConfig: cloudly.ICloudlyConfig = { const cloudlyConfig: cloudly.ICloudlyConfig = {
cfToken: testQenv.getEnvVarOnDemand('CF_TOKEN'), cfToken: await testQenv.getEnvVarOnDemand('CF_TOKEN'),
environment: 'integration', environment: 'integration',
letsEncryptEmail: testQenv.getEnvVarOnDemand('LETSENCRYPT_EMAIL'), letsEncryptEmail: await testQenv.getEnvVarOnDemand('LETSENCRYPT_EMAIL'),
publicUrl: testQenv.getEnvVarOnDemand('SERVEZONE_URL'), publicUrl: await testQenv.getEnvVarOnDemand('SERVEZONE_URL'),
publicPort: testQenv.getEnvVarOnDemand('SERVEZONE_PORT'), publicPort: await testQenv.getEnvVarOnDemand('SERVEZONE_PORT'),
mongoDescriptor: { mongoDescriptor: {
mongoDbName: testQenv.getEnvVarOnDemand('MONGODB_DATABASE'), mongoDbName: await testQenv.getEnvVarOnDemand('MONGODB_DATABASE'),
mongoDbUser: testQenv.getEnvVarOnDemand('MONGODB_USER'), mongoDbUser: await testQenv.getEnvVarOnDemand('MONGODB_USER'),
mongoDbPass: testQenv.getEnvVarOnDemand('MONGODB_PASSWORD'), mongoDbPass: await testQenv.getEnvVarOnDemand('MONGODB_PASSWORD'),
mongoDbUrl: testQenv.getEnvVarOnDemand('MONGODB_URL'), mongoDbUrl: await testQenv.getEnvVarOnDemand('MONGODB_URL'),
}, },
digitalOceanToken: testQenv.getEnvVarOnDemand('DIGITALOCEAN_TOKEN'),
}; };
testCloudly = new cloudly.Cloudly(cloudlyConfig); testCloudly = new cloudly.Cloudly();
expect(testCloudly).toBeInstanceOf(cloudly.Cloudly); expect(testCloudly).toBeInstanceOf(cloudly.Cloudly);
}); });

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/cloudly', name: '@serve.zone/cloudly',
version: '1.1.1', version: '1.1.5',
description: 'A cloud manager leveraging Docker Swarmkit for multi-cloud operations including DigitalOcean, Hetzner Cloud, and Cloudflare, with integration support and robust configuration management system.' description: 'A comprehensive multi-cloud manager leveraging Docker Swarmkit to orchestrate containerized applications across various cloud services and integrate robust configuration and API management capabilities.'
} }

View File

@ -63,6 +63,8 @@ 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: [],
type: 'external',
description: 'Demo Purpose', description: 'Demo Purpose',
includedSecretGroupIds: [secretGroup.id], includedSecretGroupIds: [secretGroup.id],
includedTags: secretGroup.data.tags, includedTags: secretGroup.data.tags,

View File

@ -0,0 +1,19 @@
import * as plugins from '../plugins.js';
import * as paths from '../paths.js';
import type { Cloudly } from '../classes.cloudly.js';
export const getUsers = async (cloudlyRef: Cloudly) => {
const users: plugins.servezoneInterfaces.data.IUser[] = [];
const envAdminUser = await cloudlyRef.config.appData.waitForAndGetKey('servezoneAdminaccount');
if (envAdminUser) {
users.push({
id: 'envadmin',
data: {
username: envAdminUser.split(':')[0],
password: envAdminUser.split(':')[1],
role: 'admin',
},
});
}
return users;
};

View File

@ -43,7 +43,7 @@ export const installDemoData = async (cloudlyRef: Cloudly) => {
} }
const demoDataUsers = await import('./demo.data.users.js'); const demoDataUsers = await import('./demo.data.users.js');
for (const user of demoDataUsers.users) { for (const user of await demoDataUsers.getUsers(cloudlyRef)) {
const userInstance = new cloudlyRef.authManager.CUser(); const userInstance = new cloudlyRef.authManager.CUser();
Object.assign(userInstance, user); Object.assign(userInstance, user);
await userInstance.save(); await userInstance.save();

View File

@ -15,10 +15,10 @@ import { MongodbConnector } from './connector.mongodb/connector.js';
// processes // processes
import { CloudlyCoreflowManager } from './manager.coreflow/coreflowmanager.js'; import { CloudlyCoreflowManager } from './manager.coreflow/coreflowmanager.js';
import { ClusterManager } from './manager.cluster/clustermanager.js'; import { ClusterManager } from './manager.cluster/classes.clustermanager.js';
import { CloudlyTaskmanager } from './manager.task/taskmanager.js'; 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/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 { 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';

View File

@ -40,6 +40,7 @@ export class CloudlyConfig {
useSsl: true, useSsl: true,
}, },
sslMode: 'SERVEZONE_SSLMODE' as plugins.servezoneInterfaces.data.ICloudlyConfig['sslMode'], sslMode: 'SERVEZONE_SSLMODE' as plugins.servezoneInterfaces.data.ICloudlyConfig['sslMode'],
servezoneAdminaccount: 'SERVEZONE_ADMINACCOUNT',
}, },
requiredKeys: [ requiredKeys: [
'cfToken', 'cfToken',

View File

@ -10,7 +10,8 @@ export class CloudlyServer {
/** /**
* a reference to the cloudly instance * a reference to the cloudly instance
*/ */
private cloudlyRef: Cloudly; public cloudlyRef: Cloudly;
public additionalHandlers: plugins.typedserver.servertools.Handler[] = [];
/** /**
* the smartexpress server handling the actual requests * the smartexpress server handling the actual requests
@ -37,18 +38,24 @@ export class CloudlyServer {
* init the reception instance * init the reception instance
*/ */
public async start() { public async start() {
logger.log('info', `cloudly domain is ${this.cloudlyRef.config.data.publicUrl}`) logger.log('info', `cloudly domain is ${this.cloudlyRef.config.data.publicUrl}`);
let sslCert: plugins.smartacme.Cert; let sslCert: plugins.smartacme.Cert;
if (this.cloudlyRef.config.data.sslMode === 'letsencrypt') { if (this.cloudlyRef.config.data.sslMode === 'letsencrypt') {
logger.log('info', `Using letsencrypt for ssl mode. Trying to obtain a certificate...`) logger.log('info', `Using letsencrypt for ssl mode. Trying to obtain a certificate...`);
logger.log('info', `This might take 10 minutes...`) logger.log('info', `This might take 10 minutes...`);
sslCert = await this.cloudlyRef.letsencryptConnector.getCertificateForDomain( sslCert = await this.cloudlyRef.letsencryptConnector.getCertificateForDomain(
this.cloudlyRef.config.data.publicUrl this.cloudlyRef.config.data.publicUrl
); );
logger.log('success', `Successfully obtained certificate for cloudly domain ${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') { } 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.`) logger.log(
'info',
`Using external certificate for ssl mode, meaning cloudly is not in charge of ssl termination.`
);
} }
interface IRequestGuardData { interface IRequestGuardData {
@ -72,11 +79,13 @@ export class CloudlyServer {
this.typedServer = new plugins.typedserver.TypedServer({ this.typedServer = new plugins.typedserver.TypedServer({
cors: true, cors: true,
forceSsl: false, forceSsl: false,
port: this.cloudlyRef.config.data.publicPort, port: this.cloudlyRef.config.data.publicPort,
...(sslCert ? { ...(sslCert
privateKey: sslCert.privateKey, ? {
publicKey: sslCert.publicKey, privateKey: sslCert.privateKey,
} : {}), publicKey: sslCert.publicKey,
}
: {}),
injectReload: true, injectReload: true,
serveDir: paths.distServeDir, serveDir: paths.distServeDir,
watch: true, watch: true,
@ -84,6 +93,10 @@ export class CloudlyServer {
preferredCompressionMethod: 'gzip', preferredCompressionMethod: 'gzip',
}); });
this.typedServer.typedrouter.addTypedRouter(this.typedrouter); this.typedServer.typedrouter.addTypedRouter(this.typedrouter);
this.typedServer.server.addRoute(
'/curlfresh/:scriptname',
this.cloudlyRef.serverManager.curlfreshInstance.handler
);
await this.typedServer.start(); await this.typedServer.start();
} }

View File

@ -1,12 +0,0 @@
import * as plugins from '../plugins.js';
export const users: plugins.servezoneInterfaces.data.IUser[] = [
{
id: 'user1',
data: {
username: 'admin',
password: 'password',
role: 'admin',
}
}
]

View File

@ -21,8 +21,10 @@ const runCli = async () => {
); );
await cloudlyInstance.start(); await cloudlyInstance.start();
const demoMod = await import('./demo/index.js'); const demoMod = await import('./00demo/index.js');
demoMod.installDemoData(cloudlyInstance); demoMod.installDemoData(cloudlyInstance);
}; };
export { runCli, Cloudly }; export { runCli, Cloudly };
type ICloudlyConfig = plugins.servezoneInterfaces.data.ICloudlyConfig;
export { type ICloudlyConfig }

5
ts/manager.cert/cert.ts Normal file
View File

@ -0,0 +1,5 @@
import * as plugins from '../plugins.js';
export class Cert extends plugins.smartdata.SmartDataDbDoc<> {
}

View File

@ -0,0 +1,14 @@
import type { Cloudly } from '../classes.cloudly.js';
import * as plugins from '../plugins.js';
export class CertManager {
public cloudlyRef: Cloudly;
public get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb;
}
constructor(cloudly: Cloudly) {
this.cloudlyRef = cloudly;
}
}

View File

@ -3,7 +3,7 @@ import * as paths from '../paths.js';
import { Cloudly } from '../classes.cloudly.js'; import { Cloudly } from '../classes.cloudly.js';
import { logger } from '../logger.js'; import { logger } from '../logger.js';
import { Cluster } from './cluster.js'; import { Cluster } from './classes.cluster.js';
export class ClusterManager { export class ClusterManager {
public ready = plugins.smartpromise.defer(); public ready = plugins.smartpromise.defer();
@ -28,7 +28,6 @@ export class ClusterManager {
name: dataArg.clusterName, name: dataArg.clusterName,
jumpCode: plugins.smartunique.uniSimple('cluster'), jumpCode: plugins.smartunique.uniSimple('cluster'),
jumpCodeUsedAt: null, jumpCodeUsedAt: null,
secretKey: plugins.smartunique.shortId(16),
acmeInfo: null, acmeInfo: null,
cloudlyUrl: `https://${this.cloudlyRef.config.data.publicUrl}:${this.cloudlyRef.config.data.publicPort}/`, cloudlyUrl: `https://${this.cloudlyRef.config.data.publicUrl}:${this.cloudlyRef.config.data.publicPort}/`,
servers: [], servers: [],
@ -54,6 +53,17 @@ export class ClusterManager {
}; };
}) })
); );
// delete cluster
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.cluster.IRequest_DeleteCluster>(
new plugins.typedrequest.TypedHandler('deleteCluster', async (reqDataArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminJwtGuard], reqDataArg);
await this.deleteCluster(reqDataArg.clusterId);
return {
success: true,
};
})
);
} }
public async init() { public async init() {
@ -86,9 +96,9 @@ export class ClusterManager {
await this.ready.promise; await this.ready.promise;
return await Cluster.getInstance({ return await Cluster.getInstance({
id: clusterIdentifier.clusterId,
data: { data: {
name: clusterIdentifier.clusterName, name: clusterIdentifier.clusterName,
secretKey: clusterIdentifier.secretKey,
}, },
}); });
} }
@ -128,4 +138,10 @@ export class ClusterManager {
await clusterInstance.save(); await clusterInstance.save();
return clusterInstance; return clusterInstance;
} }
public async deleteCluster(clusterId: string) {
await this.ready.promise;
const clusterInstance = await Cluster.getInstance({ id: clusterId });
await clusterInstance.delete();
}
} }

View File

@ -26,4 +26,12 @@ export class Image extends plugins.smartdata.SmartDataDbDoc<Image, plugins.serve
public data: plugins.servezoneInterfaces.data.IImage['data']; public data: plugins.servezoneInterfaces.data.IImage['data'];
public async getVersions() {} public async getVersions() {}
/**
* returns a storage path
* note: this is relative to the storage method defined by the imageManager
*/
public async getStoragePath(versionStringArg: string) {
return `${this.data.name}:${versionStringArg}`.replace('/', '__')
}
} }

View File

@ -7,6 +7,8 @@ export class ImageManager {
cloudlyRef: Cloudly; cloudlyRef: Cloudly;
public typedrouter = new plugins.typedrequest.TypedRouter(); public typedrouter = new plugins.typedrequest.TypedRouter();
public smartbucketInstance: plugins.smartbucket.SmartBucket; public smartbucketInstance: plugins.smartbucket.SmartBucket;
public imageDir: plugins.smartbucket.Directory;
public dockerImageStore: plugins.docker.DockerImageStore;
get db() { get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb; return this.cloudlyRef.mongodbConnector.smartdataDb;
@ -71,9 +73,18 @@ export class ImageManager {
this.typedrouter.addTypedHandler( this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_PushImageVersion>( new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_PushImageVersion>(
'pushImageVersion', 'pushImageVersion',
async (reqArg) => { async (reqArg, toolsArg) => {
const pushStream = reqArg.imageStream; const image = await this.CImage.getInstance({
return {}; id: reqArg.imageId,
});
if (!image) {
throw new plugins.typedrequest.TypedResponseError('Image not found');
}
const imageVersion = reqArg.versionString;
const imagePushStream = reqArg.imageStream;
return {
allowed: true,
};
} }
) )
); );
@ -83,11 +94,12 @@ export class ImageManager {
'pullImageVersion', 'pullImageVersion',
async (reqArg) => { async (reqArg) => {
const image = await this.CImage.getInstance({ const image = await this.CImage.getInstance({
data: { id: reqArg.imageId,
name: reqArg.name,
},
}); });
const imageVersion = null; const imageVersion = image.data.versions.find((version) => version.versionString === reqArg.versionString);
const readable = this.imageDir.fastGetStream({
path: await image.getStoragePath(reqArg.versionString),
}, 'webstream');
const imageVirtualStream = new plugins.typedrequest.VirtualStream(); const imageVirtualStream = new plugins.typedrequest.VirtualStream();
return { return {
imageStream: imageVirtualStream, imageStream: imageVirtualStream,
@ -106,11 +118,9 @@ export class ImageManager {
); );
const bucket = await this.smartbucketInstance.getBucketByName('cloudly-test'); const bucket = await this.smartbucketInstance.getBucketByName('cloudly-test');
await bucket.fastPut({ path: 'test/test.txt', contents: 'hello' }); await bucket.fastPut({ path: 'test/test.txt', contents: 'hello' });
}
public async createImage(nameArg: string) { this.imageDir = await bucket.getDirectoryFromPath({
const newImage = await this.CImage.create({ path: 'images',
name: nameArg,
}); });
} }
} }

View File

@ -0,0 +1,87 @@
import { logger } from '../logger.js';
import * as plugins from '../plugins.js';
import type { CloudlyServerManager } from './classes.servermanager.js';
export class CurlFresh {
public optionsArg = {
npmRegistry: 'https://registry.npmjs.org',
}
public scripts = {
'setup.sh': `#!/bin/bash
# lets update the system and install curl
# might be installed already, but entrypoint could have been wget
apt-get update
apt-get install -y --force-yes curl
# Basic updating of the software lists
echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
apt-get update
apt-get upgrade -y --force-yes
apt-get install -y --force-yes fail2ban curl git
curl -sL https://deb.nodesource.com/setup_18.x | bash
# Install docker
curl -sSL https://get.docker.com/ | sh
# Install default nodejs to run nodejs tools
apt-get install -y nodejs zsh
zsh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
npm config set unsafe-perm true
# lets install pnpm
curl -fsSL https://get.pnpm.io/install.sh | sh -
# lets make sure we use the correct npm registry
bash -c "npm config set registry ${this.optionsArg.npmRegistry}"
# lets install spark
bash -c "pnpm install -g @serve.zone/spark"
# lets install the spark daemon
bash -c "spark installdaemon"
# TODO: start spark with jump code
`,
};
public serverManagerRef: CloudlyServerManager;
public curlFreshRoute: plugins.typedserver.servertools.Route;
public handler = new plugins.typedserver.servertools.Handler('ALL', async (req, res) => {
logger.log('info', 'curlfresh handler called. a server might be coming online soon :)');
const scriptname = req.params.scriptname;
switch(scriptname) {
case 'setup.sh':
logger.log('info', 'sending setup.sh');
res.type('application/x-sh');
res.send(this.scripts['setup.sh']);
break;
default:
res.send('no script found');
break;
}
});
constructor(serverManagerRefArg: CloudlyServerManager) {
this.serverManagerRef = serverManagerRefArg;
}
public async getServerUserData(): Promise<string> {
const sslMode = await this.serverManagerRef.cloudlyRef.config.appData.waitForAndGetKey('sslMode');
let protocol: 'http' | 'https';
if (sslMode === 'none') {
protocol = 'http';
} else {
protocol = 'https';
}
const domain = await this.serverManagerRef.cloudlyRef.config.appData.waitForAndGetKey('publicUrl');
const port = await this.serverManagerRef.cloudlyRef.config.appData.waitForAndGetKey('publicPort');
const serverUserData = `#cloud-config
runcmd:
- curl -o- ${protocol}://${domain}:${port}/curlfresh/setup.sh | sh
`
console.log(serverUserData);
return serverUserData;
};
}

View File

@ -1,11 +1,13 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import { Cloudly } from '../classes.cloudly.js'; import { Cloudly } from '../classes.cloudly.js';
import { Cluster } from '../manager.cluster/cluster.js'; import { Cluster } from '../manager.cluster/classes.cluster.js';
import { Server } from './server.js'; import { Server } from './classes.server.js';
import { CurlFresh } from './classes.curlfresh.js';
export class CloudlyServerManager { export class CloudlyServerManager {
public cloudlyRef: Cloudly; public cloudlyRef: Cloudly;
public typedRouter = new plugins.typedrequest.TypedRouter(); public typedRouter = new plugins.typedrequest.TypedRouter();
public curlfreshInstance = new CurlFresh(this);
public hetznerAccount: plugins.hetznercloud.HetznerAccount; public hetznerAccount: plugins.hetznercloud.HetznerAccount;
@ -63,7 +65,8 @@ export class CloudlyServerManager {
labels: { labels: {
clusterId: cluster.id, clusterId: cluster.id,
priority: '1', priority: '1',
} },
userData: await this.curlfreshInstance.getServerUserData()
}); });
const newServer = await Server.createFromHetznerServer(server); const newServer = await Server.createFromHetznerServer(server);
console.log(`cluster created new server for cluster ${cluster.id}`); console.log(`cluster created new server for cluster ${cluster.id}`);

View File

@ -0,0 +1,12 @@
import * as plugins from '../plugins.js';
export class Service extends plugins.smartdata.SmartDataDbDoc<Service, plugins.servezoneInterfaces.data.IService, ServiceManager> {
@plugins.smartdata.svDb()
public id: string;
@plugins.smartdata.svDb()
public data: plugins.servezoneInterfaces.data.IService['data'];
}

View File

@ -0,0 +1,18 @@
import type { Cloudly } from '../classes.cloudly.js';
import * as plugins from '../plugins.js';
import { Service } from './classes.service.js';
export class ServiceManager {
public typedrouter = new plugins.typedrequest.TypedRouter();
public cloudlyRef: Cloudly;
get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb;
}
public CService = plugins.smartdata.setDefaultManagerForDoc(this, Service);
constructor(cloudlyRef: Cloudly) {
this.cloudlyRef = cloudlyRef;
}
}

View File

@ -9,13 +9,13 @@ import * as typedsocket from '@api.global/typedsocket';
export { typedrequest, typedsocket }; export { typedrequest, typedsocket };
// @mojoio scope // @apiclient.xyz scope
import * as cloudflare from '@apiclient.xyz/cloudflare'; import * as cloudflare from '@apiclient.xyz/cloudflare';
import * as digitalocean from '@apiclient.xyz/digitalocean'; import * as docker from '@apiclient.xyz/docker';
import * as hetznercloud from '@apiclient.xyz/hetznercloud'; import * as hetznercloud from '@apiclient.xyz/hetznercloud';
import * as slack from '@apiclient.xyz/slack'; import * as slack from '@apiclient.xyz/slack';
export { cloudflare, digitalocean, hetznercloud, slack }; export { cloudflare, docker, hetznercloud, slack };
// @tsclass scope // @tsclass scope
import * as tsclass from '@tsclass/tsclass'; import * as tsclass from '@tsclass/tsclass';
@ -29,6 +29,7 @@ import * as qenv from '@push.rocks/qenv';
import * as smartacme from '@push.rocks/smartacme'; import * as smartacme from '@push.rocks/smartacme';
import * as smartbucket from '@push.rocks/smartbucket'; import * as smartbucket from '@push.rocks/smartbucket';
import * as smartcli from '@push.rocks/smartcli'; import * as smartcli from '@push.rocks/smartcli';
import * as smartclickhouse from '@push.rocks/smartclickhouse';
import * as smartdata from '@push.rocks/smartdata'; import * as smartdata from '@push.rocks/smartdata';
import * as smartdelay from '@push.rocks/smartdelay'; import * as smartdelay from '@push.rocks/smartdelay';
import * as smartexit from '@push.rocks/smartexit'; import * as smartexit from '@push.rocks/smartexit';
@ -53,6 +54,7 @@ export {
smartacme, smartacme,
smartbucket, smartbucket,
smartcli, smartcli,
smartclickhouse,
smartdata, smartdata,
smartexit, smartexit,
smartdelay, smartdelay,

View File

@ -1,8 +1,10 @@
import * as plugins from './plugins.js'; import * as plugins from './plugins.js';
export type TClientType = 'coreflow' | 'cli' | 'serverconfig'; export type TClientType = 'api' | 'ci' | 'coreflow' | 'cli' | 'serverconfig';
export class CloudlyClient { import { Image } from './classes.image.js';
export class CloudlyApiClient {
private cloudlyUrl: string; private cloudlyUrl: string;
private registerAs: string; private registerAs: string;
@ -18,9 +20,13 @@ export class CloudlyClient {
plugins.servezoneInterfaces.requests.server.IRequest_TriggerServerAction['request'] plugins.servezoneInterfaces.requests.server.IRequest_TriggerServerAction['request']
>(); >();
constructor(registerAsArg: TClientType) { constructor(optionsArg?: {
this.cloudlyUrl = process.env.CLOUDLY_URL || 'https://cloudly.layer.io:443'; registerAs: TClientType;
this.registerAs = registerAsArg; cloudlyUrl?: string;
}) {
this.registerAs = optionsArg.registerAs;
this.cloudlyUrl =
optionsArg?.cloudlyUrl || process.env.CLOUDLY_URL || 'https://cloudly.layer.io:443';
console.log( console.log(
`creating LoleCloudlyClient: registering as ${this.registerAs} and target url ${this.cloudlyUrl}` `creating LoleCloudlyClient: registering as ${this.registerAs} and target url ${this.cloudlyUrl}`
@ -33,13 +39,6 @@ export class CloudlyClient {
}) })
); );
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.config.IRequest_Cloudly_Coreflow_PushClusterConfig>(
new plugins.typedrequest.TypedHandler('pushClusterConfig', async (dataArg) => {
this.configUpdateSubject.next(dataArg);
return {};
})
);
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.server.IRequest_TriggerServerAction>( this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.server.IRequest_TriggerServerAction>(
new plugins.typedrequest.TypedHandler('triggerServerAction', async (dataArg) => { new plugins.typedrequest.TypedHandler('triggerServerAction', async (dataArg) => {
this.serverActionSubject.next(dataArg); this.serverActionSubject.next(dataArg);
@ -55,15 +54,20 @@ export class CloudlyClient {
this.typedrouter, this.typedrouter,
this.cloudlyUrl this.cloudlyUrl
); );
console.log(
`CloudlyCluent connected to cloudly at ${this.cloudlyUrl}. Remember to get an identity.`
);
} }
public async stop() { public async stop() {
await this.typedsocketClient.stop(); await this.typedsocketClient.stop();
} }
public identity: plugins.servezoneInterfaces.data.IClusterIdentifier;
public async getIdentityByJumpCode( public async getIdentityByJumpCode(
jumpCodeArg: string, jumpCodeArg: string,
tagConnection = false tagConnection = false,
statefullIdentity = true
): Promise<plugins.servezoneInterfaces.data.IClusterIdentifier> { ): Promise<plugins.servezoneInterfaces.data.IClusterIdentifier> {
const identityRequest = const identityRequest =
this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByJumpCode>( this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByJumpCode>(
@ -80,6 +84,10 @@ export class CloudlyClient {
this.typedsocketClient.addTag('identity', identity); this.typedsocketClient.addTag('identity', identity);
} }
if (statefullIdentity) {
this.identity = identity;
}
return identity; return identity;
} }
@ -106,7 +114,7 @@ export class CloudlyClient {
); );
const response = await serverConfigRequest.fire({ const response = await serverConfigRequest.fire({
jwt: '', // TODO: do proper auth here jwt: '', // TODO: do proper auth here
serverId: '' // TODO: get server id here serverId: '', // TODO: get server id here
}); });
return response.configData; return response.configData;
} }
@ -116,7 +124,9 @@ export class CloudlyClient {
* @param serviceNameArg * @param serviceNameArg
* @param domainNameArg * @param domainNameArg
*/ */
public async getCertificateForDomainOverHttps(domainNameArg: string): Promise<plugins.tsclass.network.ICert> { public async getCertificateForDomainOverHttps(
domainNameArg: string
): Promise<plugins.tsclass.network.ICert> {
const typedCertificateRequest = const typedCertificateRequest =
this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.certificate.IRequest_Any_Cloudly_GetSslCertificate>( this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.certificate.IRequest_Any_Cloudly_GetSslCertificate>(
'getSslCertificate' 'getSslCertificate'
@ -127,4 +137,9 @@ export class CloudlyClient {
}); });
return typedResponse.certificate; return typedResponse.certificate;
} }
// Images
public async getImages() {
return Image.getImages(this);
}
} }

View File

@ -1,7 +1,84 @@
import type { CloudlyApiClient } from './classes.cloudlyapiclient.js';
import * as plugins from './plugins.js'; import * as plugins from './plugins.js';
export class Image { export class Image implements plugins.servezoneInterfaces.data.IImage {
public getImages() { public static async getImages(cloudlyClientRef: CloudlyApiClient) {
const getAllImagesTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_GetAllImages>(
'getAllImages'
);
const response = await getAllImagesTR.fire({
jwt: cloudlyClientRef.identity.jwt,
});
const resultImages: Image[] = [];
for (const image of response.images) {
const newImage = new Image(cloudlyClientRef);
Object.assign(newImage, image);
resultImages.push(newImage);
}
return resultImages;
} }
// INSTANCE
cloudlyClientRef: CloudlyApiClient;
id: plugins.servezoneInterfaces.data.IImage['id'];
data: plugins.servezoneInterfaces.data.IImage['data'];
constructor(cloudlyClientRef: CloudlyApiClient) {
this.cloudlyClientRef = cloudlyClientRef;
}
/**
* updates the image data
*/
public async update() {
const getVersionsTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_GetImageMetadata>(
'getImageMetadata'
);
const response = await getVersionsTR.fire({
jwt: this.cloudlyClientRef.identity.jwt,
imageId: this.id,
});
Object.assign(this, response.image);
}
/**
* pushes a new version of the image
* @param imageVersion
* @param imageReadableArg
*/
public async pushImageVersion(imageVersion: string, imageReadableArg: ReadableStream<Uint8Array>): Promise<void> {
const done = plugins.smartpromise.defer();
const pullImageTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_PushImageVersion>(
'pushImageVersion'
);
const virtualStream = new plugins.typedrequest.VirtualStream();
const response = await pullImageTR.fire({
jwt: this.cloudlyClientRef.identity.jwt,
imageId: this.id,
versionString: '',
imageStream: virtualStream,
});
await virtualStream.readFromWebstream(imageReadableArg);
await done.promise;
await this.update();
};
/**
* pulls a version of the image
*/
public async pullImageVersion(versionStringArg: string): Promise<ReadableStream<Uint8Array>> {
const pullImageTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_PullImageVersion>(
'pullImageVersion'
);
const response = await pullImageTR.fire({
jwt: this.cloudlyClientRef.identity.jwt,
imageId: this.id,
versionString: versionStringArg,
});
const imageStream = response.imageStream;
const webduplexStream = new plugins.webstream.WebDuplexStream({});
imageStream.writeToWebstream(webduplexStream.writable);
return webduplexStream.readable;
};
} }

View File

@ -1 +1 @@
export * from './classes.cloudlyclient.js'; export * from './classes.cloudlyapiclient.js';

View File

@ -6,10 +6,14 @@ export {
} }
// @push.rocks scope // @push.rocks scope
import * as smartpromise from '@push.rocks/smartpromise';
import * as smartrx from '@push.rocks/smartrx'; import * as smartrx from '@push.rocks/smartrx';
import * as webstream from '@push.rocks/smartstream/web';
export { export {
smartpromise,
smartrx, smartrx,
webstream,
} }
// @api.global scope // @api.global scope

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/cloudly', name: '@serve.zone/cloudly',
version: '1.1.1', version: '1.1.5',
description: 'A cloud manager leveraging Docker Swarmkit for multi-cloud operations including DigitalOcean, Hetzner Cloud, and Cloudflare, with integration support and robust configuration management system.' description: 'A comprehensive multi-cloud manager leveraging Docker Swarmkit to orchestrate containerized applications across various cloud services and integrate robust configuration and API management capabilities.'
} }

View File

@ -5,7 +5,7 @@ const appstate = new plugins.deesDomtools.plugins.smartstate.Smartstate();
export interface ILoginState { export interface ILoginState {
jwt: string; jwt: string;
} }
export const loginStatePart = await appstate.getStatePart<ILoginState>( export const loginStatePart: plugins.smartstate.StatePart<unknown, ILoginState> = await appstate.getStatePart<ILoginState>(
'login', 'login',
{ jwt: null }, { jwt: null },
'persistent' 'persistent'

View File

@ -13,7 +13,9 @@ export { deesDomtools, deesElement, deesCatalog };
// @push.rocks scope // @push.rocks scope
import * as webjwt from '@push.rocks/webjwt'; import * as webjwt from '@push.rocks/webjwt';
import * as smartstate from '@push.rocks/smartstate';
export { export {
webjwt, webjwt,
smartstate,
} }