Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
cdbab26008 | |||
1983c64b77 | |||
a6e3a7f5fe | |||
6dd687012f | |||
55b2872ffc | |||
2e6e7f6ca8 | |||
f453ce3126 |
22
package.json
22
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@serve.zone/cloudly",
|
"name": "@serve.zone/cloudly",
|
||||||
"version": "1.1.1",
|
"version": "1.1.4",
|
||||||
"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 cloud manager leveraging Docker Swarmkit for multi-cloud operations including DigitalOcean, Hetzner Cloud, and Cloudflare, with integration support and robust configuration management system.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@ -26,14 +26,14 @@
|
|||||||
"@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.2"
|
||||||
},
|
},
|
||||||
"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.0.18",
|
||||||
"@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",
|
||||||
@ -41,32 +41,34 @@
|
|||||||
"@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.17",
|
||||||
"@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": "^4.0.8",
|
||||||
"@push.rocks/smartbucket": "^3.0.9",
|
"@push.rocks/smartbucket": "^3.0.15",
|
||||||
"@push.rocks/smartcli": "^4.0.11",
|
"@push.rocks/smartcli": "^4.0.11",
|
||||||
"@push.rocks/smartdata": "^5.2.4",
|
"@push.rocks/smartdata": "^5.2.4",
|
||||||
"@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.11",
|
||||||
"@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.72",
|
||||||
"@tsclass/tsclass": "^4.0.54"
|
"@tsclass/tsclass": "^4.0.55"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"ts/**/*",
|
"ts/**/*",
|
||||||
|
1334
pnpm-lock.yaml
generated
1334
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
24
test/test.apiclient.ts
Normal file
24
test/test.apiclient.ts
Normal 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();
|
19
test/test.ts
19
test/test.ts
@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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.4',
|
||||||
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 cloud manager leveraging Docker Swarmkit for multi-cloud operations including DigitalOcean, Hetzner Cloud, and Cloudflare, with integration support and robust configuration management system.'
|
||||||
}
|
}
|
||||||
|
@ -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,
|
19
ts/00demo/demo.data.users.ts
Normal file
19
ts/00demo/demo.data.users.ts
Normal 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;
|
||||||
|
};
|
@ -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();
|
@ -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';
|
||||||
|
@ -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',
|
||||||
|
@ -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,
|
privateKey: sslCert.privateKey,
|
||||||
publicKey: sslCert.publicKey,
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
@ -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 }
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
@ -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('/', '__')
|
||||||
|
}
|
||||||
}
|
}
|
@ -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,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
84
ts/manager.server/classes.curlfresh.ts
Normal file
84
ts/manager.server/classes.curlfresh.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
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 make sure we use the correct npm registry
|
||||||
|
bash -c "npm config set registry ${this.optionsArg.npmRegistry}"
|
||||||
|
|
||||||
|
# lets install spark
|
||||||
|
bash -c "npm 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;
|
||||||
|
};
|
||||||
|
}
|
@ -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}`);
|
@ -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'];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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';
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
@ -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;
|
||||||
|
};
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
export * from './classes.cloudlyclient.js';
|
export * from './classes.cloudlyapiclient.js';
|
@ -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
|
||||||
|
@ -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.4',
|
||||||
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 cloud manager leveraging Docker Swarmkit for multi-cloud operations including DigitalOcean, Hetzner Cloud, and Cloudflare, with integration support and robust configuration management system.'
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
|
@ -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,
|
||||||
}
|
}
|
Reference in New Issue
Block a user