Compare commits

...

16 Commits

Author SHA1 Message Date
320b3ed9eb 1.2.3
Some checks failed
Docker (tags) / security (push) Failing after 12s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2024-10-23 20:09:24 +02:00
7cd6bc0e33 fix(cli): Set up CLI client definition and registry configuration 2024-10-23 20:09:24 +02:00
a218805f27 1.2.2
Some checks failed
Docker (tags) / security (push) Failing after 16s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2024-10-23 20:06:16 +02:00
6f56304e07 fix(docs): Updated documentation with clearer usage instructions and examples. 2024-10-23 20:06:16 +02:00
4eb62200e8 1.2.1
Some checks failed
Docker (tags) / security (push) Failing after 17s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2024-10-23 15:59:49 +02:00
c105596455 fix(core): Fixed startup issue for the Cloudly instance 2024-10-23 15:59:49 +02:00
5aa136b8d9 1.2.0
Some checks failed
Docker (tags) / security (push) Failing after 16s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2024-10-21 16:37:46 +02:00
240516520a feat(cli): Add tspublish.json for CLI client and interfaces 2024-10-21 16:37:46 +02:00
4e1f9464fe 1.1.9
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-10-21 14:23:43 +02:00
41f9f93d1c fix(build): Update Node types and other dependencies, add tspublish.json for api client 2024-10-21 14:23:43 +02:00
c502d410b1 1.1.8
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-10-16 14:35:38 +02:00
53f96095c7 fix(big fix upgrade): upgrade multiple areas of the core functionalities 2024-10-16 14:35:38 +02:00
d212dfb9f9 1.1.7
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-08-25 14:29:26 +02:00
0ec665516d fix(deps): Update dependencies to latest versions 2024-08-25 14:29:26 +02:00
acc642adf9 1.1.6
Some checks failed
Docker (tags) / security (push) Failing after 16s
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:20:17 +02:00
a6521708f7 fix(core): update 2024-06-20 19:20:16 +02:00
50 changed files with 5622 additions and 6525 deletions

102
changelog.md Normal file
View File

@ -0,0 +1,102 @@
# Changelog
## 2024-10-23 - 1.2.3 - fix(cli)
Set up CLI client definition and registry configuration
- Added a console log message for CLI identification.
- Defined package name for CLI client.
- Configured npm registries for package distribution.
## 2024-10-23 - 1.2.2 - fix(docs)
Updated documentation with clearer usage instructions and examples.
- Refined setup guides in README.
- Added more detailed examples in code snippets.
- Clarified cloud service integration instructions.
## 2024-10-23 - 1.2.1 - fix(core)
Fixed startup issue for the Cloudly instance
## 2024-10-21 - 1.2.0 - feat(cli)
Add tspublish.json for CLI client and interfaces
- Added registration details for publishing CLI client (@serve.zone/cli)
- Specified npm and custom registries in the tspublish.json for better publish control
- Updated dependency version for @git.zone/tspublish in package.json
## 2024-10-21 - 1.1.9 - fix(build)
Update Node types and other dependencies, add tspublish.json for api client
- Updated Node types devDependency to version ^22.7.7 from ^22.7.5.
- Updated smartbucket dependency to version ^3.0.23 from ^3.0.22.
- Added tspublish configuration file to the api client.
- Fixed the npm publish script in package.json.
## 2024-10-16 - 1.1.8 - fix(big fix upgrade)
fix: update dependency versions and address type errors
- Updated all listed dependencies in the package.json to their specified ranges.
- Fixed type mismatches and added missing imports in various TypeScript files.
- Refined existing tests and added a new helper to manage Docker image streams.
## 2024-08-25 - 1.1.7 - fix(deps)
Update dependencies to latest versions
- Updated @git.zone/tsbuild from ^2.1.80 to ^2.1.84
- Updated @push.rocks/tapbundle from ^5.0.23 to ^5.0.24
- Updated @types/node from ^20.14.6 to ^22.5.0
- Updated @apiclient.xyz/docker from ^1.2.2 to ^1.2.3
- Updated @design.estate/dees-catalog from ^1.0.289 to ^1.1.6
- Updated @design.estate/dees-element from ^2.0.34 to ^2.0.36
- Updated @git.zone/tsrun from ^1.2.37 to ^1.2.49
- Updated @push.rocks/smartbucket from ^3.0.20 to ^3.0.22
- Updated @push.rocks/smartpromise from ^4.0.3 to ^4.0.4
- Updated @serve.zone/interfaces from ^1.0.74 to ^1.0.78
- Updated @tsclass/tsclass from ^4.0.60 to ^4.1.2
## 2024-06-20 - 1.1.6 - Updates
Routine updates and fixes.
- (fix) core: update
## 2024-06-13 - 1.1.4 - Service Management Preparation
Incorporated updates and service management preparations.
- (fix) core: update
- (feat) prepare service management
## 2024-06-05 - 1.1.3 - CI Integration Improvement
Structural improvements and better CI integration preparation.
- (fix) structure: improve structure, prepare better CI integration
## 2024-06-02 - 1.1.2 - Image Manager Update
Prepared proper storage and retrieval of container images.
- (fix) imagemanager: prepare proper storage and retrieval of container images
## 2024-06-01 - 1.1.0 - Image Registry Work
Initiated work on image registry.
- (fix) image registry: start work on image registry
## 2024-05-30 - 1.0.216 - Enhanced Smartguards
Enhanced smartguards to verify action authorization.
- (feat) guards: use better smartguards to verify action authorization
## 2024-05-28 - 1.0.215 - Unified Package Update
Updated package unification for cloudly + API + CLI.
- (fix) switch to unified package for cloudly + API + CLI: update
## 2024-05-05 - 1.0.214 - Core Updates
Routine core updates.
- (fix) core: update
## 2024-04-20 - 1.0.213 - Core Update
Routine core updates.
- (fix) core: update

View File

@ -16,23 +16,23 @@
"githost": "gitlab.com", "githost": "gitlab.com",
"gitscope": "servezone/private", "gitscope": "servezone/private",
"gitrepo": "cloudly", "gitrepo": "cloudly",
"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.", "description": "A comprehensive multi-cloud manager leveraging Docker Swarmkit to orchestrate containerized applications across various cloud services and provide robust configuration and API integration.",
"npmPackagename": "@serve.zone/cloudly", "npmPackagename": "@serve.zone/cloudly",
"license": "UNLICENSED", "license": "UNLICENSED",
"keywords": [ "keywords": [
"cloud management", "cloud management",
"multi-cloud", "multi-cloud management",
"Docker Swarmkit", "Docker Swarmkit",
"container orchestration",
"cloud services",
"DigitalOcean", "DigitalOcean",
"Hetzner Cloud", "Hetzner Cloud",
"Cloudflare", "Cloudflare",
"container orchestration",
"configuration management", "configuration management",
"SSL management", "SSL management",
"API integration", "API integration",
"TypeScript", "TypeScript",
"node.js", "node.js",
"cloud integration",
"infrastructure automation", "infrastructure automation",
"devOps", "devOps",
"cloud API client", "cloud API client",
@ -42,7 +42,7 @@
"task scheduling", "task scheduling",
"frontend", "frontend",
"backend", "backend",
"cli", "CLI",
"web interface", "web interface",
"cloud providers", "cloud providers",
"security", "security",

View File

@ -1,8 +1,8 @@
{ {
"name": "@serve.zone/cloudly", "name": "@serve.zone/cloudly",
"version": "1.1.5", "version": "1.2.3",
"private": false, "private": false,
"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.", "description": "A comprehensive multi-cloud manager leveraging Docker Swarmkit to orchestrate containerized applications across various cloud services and provide robust configuration and API integration.",
"type": "module", "type": "module",
"exports": { "exports": {
".": "./dist/index.js", ".": "./dist/index.js",
@ -18,58 +18,60 @@
"start": "node cli.js", "start": "node cli.js",
"startTs": "node cli.ts.js", "startTs": "node cli.ts.js",
"watch": "tswatch website", "watch": "tswatch website",
"localPublish": "gitzone commit" "publish": "tspublish"
}, },
"devDependencies": { "devDependencies": {
"@git.zone/tsbuild": "^2.1.80", "@git.zone/tsbuild": "^2.1.84",
"@git.zone/tsbundle": "^2.0.15", "@git.zone/tsbundle": "^2.0.15",
"@git.zone/tspublish": "^1.3.0",
"@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.3.0",
"@types/node": "^20.14.6" "@types/node": "^22.7.7"
}, },
"dependencies": { "dependencies": {
"@api.global/typedrequest": "3.0.30", "@api.global/typedrequest": "3.1.10",
"@api.global/typedserver": "^3.0.50", "@api.global/typedserver": "^3.0.51",
"@api.global/typedsocket": "^3.0.1", "@api.global/typedsocket": "^3.0.1",
"@apiclient.xyz/cloudflare": "^6.0.1", "@apiclient.xyz/cloudflare": "^6.0.1",
"@apiclient.xyz/docker": "^1.2.2", "@apiclient.xyz/docker": "^1.2.7",
"@apiclient.xyz/hetznercloud": "^1.2.0", "@apiclient.xyz/hetznercloud": "^1.2.0",
"@apiclient.xyz/slack": "^3.0.9", "@apiclient.xyz/slack": "^3.0.9",
"@design.estate/dees-catalog": "^1.0.289", "@design.estate/dees-catalog": "^1.2.0",
"@design.estate/dees-domtools": "^2.0.57", "@design.estate/dees-domtools": "^2.0.64",
"@design.estate/dees-element": "^2.0.34", "@design.estate/dees-element": "^2.0.39",
"@git.zone/tsrun": "^1.2.37", "@git.zone/tsrun": "^1.2.49",
"@push.rocks/early": "^4.0.3", "@push.rocks/early": "^4.0.3",
"@push.rocks/npmextra": "^5.0.23", "@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": "^5.0.0", "@push.rocks/smartacme": "^5.0.0",
"@push.rocks/smartbucket": "^3.0.20", "@push.rocks/smartbucket": "^3.0.23",
"@push.rocks/smartcli": "^4.0.11", "@push.rocks/smartcli": "^4.0.11",
"@push.rocks/smartclickhouse": "^2.0.17", "@push.rocks/smartclickhouse": "^2.0.17",
"@push.rocks/smartdata": "^5.2.6", "@push.rocks/smartdata": "^5.2.10",
"@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.20", "@push.rocks/smartexpect": "^1.2.1",
"@push.rocks/smartguard": "^3.0.2", "@push.rocks/smartfile": "^11.0.21",
"@push.rocks/smartguard": "^3.1.0",
"@push.rocks/smartjson": "^5.0.19", "@push.rocks/smartjson": "^5.0.19",
"@push.rocks/smartjwt": "^2.0.4", "@push.rocks/smartjwt": "^2.2.1",
"@push.rocks/smartlog": "^3.0.7", "@push.rocks/smartlog": "^3.0.7",
"@push.rocks/smartlog-destination-clickhouse": "^1.0.13", "@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.4",
"@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/smartstate": "^2.0.19",
"@push.rocks/smartstream": "^3.0.44", "@push.rocks/smartstream": "^3.2.4",
"@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.74", "@serve.zone/interfaces": "^1.1.2",
"@tsclass/tsclass": "^4.0.60" "@tsclass/tsclass": "^4.1.2"
}, },
"files": [ "files": [
"ts/**/*", "ts/**/*",
@ -96,18 +98,18 @@
"homepage": "https://gitlab.com/servezone/private/cloudly#readme", "homepage": "https://gitlab.com/servezone/private/cloudly#readme",
"keywords": [ "keywords": [
"cloud management", "cloud management",
"multi-cloud", "multi-cloud management",
"Docker Swarmkit", "Docker Swarmkit",
"container orchestration",
"cloud services",
"DigitalOcean", "DigitalOcean",
"Hetzner Cloud", "Hetzner Cloud",
"Cloudflare", "Cloudflare",
"container orchestration",
"configuration management", "configuration management",
"SSL management", "SSL management",
"API integration", "API integration",
"TypeScript", "TypeScript",
"node.js", "node.js",
"cloud integration",
"infrastructure automation", "infrastructure automation",
"devOps", "devOps",
"cloud API client", "cloud API client",
@ -117,7 +119,7 @@
"task scheduling", "task scheduling",
"frontend", "frontend",
"backend", "backend",
"cli", "CLI",
"web interface", "web interface",
"cloud providers", "cloud providers",
"security", "security",

11215
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -7,4 +7,5 @@
- the easiest method to spawn up a cloudly instance is to use the docker image: - the easiest method to spawn up a cloudly instance is to use the docker image:
`code.foss.global/serve.zone/cloudly:latest` `code.foss.global/serve.zone/cloudly:latest`
- Note: the exports are defined in the package.json. - Note: the exports are defined in the package.json.
- For now, cloud wise only the setup with cloudron and hetzner cloud is supported.

View File

@ -1,6 +1,6 @@
# @serve.zone/cloudly # @serve.zone/cloudly
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. A comprehensive multi-cloud manager leveraging Docker Swarmkit to orchestrate containerized applications across various cloud services and integrate robust configuration and API management capabilities.
## Install ## Install

View File

@ -0,0 +1,27 @@
import { Qenv } from '@push.rocks/qenv';
const testQenv = new Qenv('./', './.nogit/');
import * as cloudly from '../../ts/index.js';
export const createCloudly = async () => {
const cloudlyConfig: cloudly.ICloudlyConfig = {
cfToken: await testQenv.getEnvVarOnDemand('CF_TOKEN'),
environment: 'integration',
letsEncryptEmail: await testQenv.getEnvVarOnDemand('LETSENCRYPT_EMAIL'),
publicUrl: await testQenv.getEnvVarOnDemand('SERVEZONE_URL'),
publicPort: await testQenv.getEnvVarOnDemand('SERVEZONE_PORT'),
mongoDescriptor: {
mongoDbName: await testQenv.getEnvVarOnDemand('MONGODB_DATABASE'),
mongoDbUser: await testQenv.getEnvVarOnDemand('MONGODB_USER'),
mongoDbPass: await testQenv.getEnvVarOnDemand('MONGODB_PASSWORD'),
mongoDbUrl: await testQenv.getEnvVarOnDemand('MONGODB_URL'),
},
};
const cloudlyInstance = new cloudly.Cloudly();
return cloudlyInstance;
}
export const getEnvVarOnDemand = async (envVarName: string) => {
return testQenv.getEnvVarOnDemand(envVarName);
}

9
test/helpers/docker.ts Normal file
View File

@ -0,0 +1,9 @@
import * as smartstream from '@push.rocks/smartstream';
import * as smartpath from '@push.rocks/smartpath';
export const getAlpineImageReadableStream = async () => {
const currentDir = smartpath.get.dirnameFromImportMetaUrl(import.meta.url);
const imagePath = smartpath.join(currentDir, '../../.nogit/testfiles/alpine.tar');
const readableStream = smartstream.nodewebhelpers.createWebReadableStreamFromFile(imagePath);
return readableStream;
}

2
test/helpers/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './cloudlyfactory.js';
export * from './docker.js';

View File

@ -1,24 +1,80 @@
import { tap, expect } from '@push.rocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import * as helpers from './helpers/index.js';
import * as cloudly from '../ts/index.js';
import * as cloudlyApiClient from '../ts_apiclient/index.js'; import * as cloudlyApiClient from '../ts_apiclient/index.js';
import { Image } from '../ts_apiclient/classes.image.js';
let testCloudly: cloudly.Cloudly;
let testClient: cloudlyApiClient.CloudlyApiClient; let testClient: cloudlyApiClient.CloudlyApiClient;
tap.preTask('should start cloudly', async () => {
testCloudly = await helpers.createCloudly();
await testCloudly.start();
});
tap.preTask('should create a new machine user for testing', async () => {
const machineUser = new testCloudly.authManager.CUser();
machineUser.id = await testCloudly.authManager.CUser.getNewId();
machineUser.data = {
type: 'machine',
username: 'test',
password: 'test',
tokens: [{
token: 'test',
expiresAt: Date.now() + 3600 * 1000 * 24 * 365,
assignedRoles: ['admin'],
}],
role: 'admin',
};
await machineUser.save();
});
tap.test('should create a new cloudlyApiClient', async () => { tap.test('should create a new cloudlyApiClient', async () => {
testClient = new cloudlyApiClient.CloudlyApiClient({ testClient = new cloudlyApiClient.CloudlyApiClient({
registerAs: 'api', registerAs: 'api',
cloudlyUrl: 'http://localhost:3000', cloudlyUrl: `http://localhost:${await helpers.getEnvVarOnDemand('SERVEZONE_PORT')}`,
}); });
await testClient.start(); await testClient.start();
expect(testClient).toBeTruthy(); expect(testClient).toBeTruthy();
}); });
tap.test('should trigger a server action', async () => { tap.test('create a new machine user', async () => {
const machineUser = await testCloudly.authManager.CUser.createMachineUser('test', 'api');
machineUser.data.tokens.push({
token: 'test',
expiresAt: Date.now() + 3600 * 1000 * 24 * 365,
assignedRoles: ['api'],
})
await machineUser.save();
}) })
tap.test('should stop the apiclient', async () => { tap.test('should get an identity', async () => {
const identity = await testClient.getIdentityByToken('test');
expect(identity).toBeTruthy();
console.log(identity);
});
let image: Image;
tap.test('should create and upload an image', async () => {
image = await testClient.images.createImage({
name: 'test',
description: 'test'
});
console.log('created image: ', image);
expect(image).toBeInstanceOf(Image);
})
tap.test('should upload an image version', async () => {
const imageStream = await helpers.getAlpineImageReadableStream();
await image.pushImageVersion('v1.0.0', imageStream);
});
tap.test('should stop the apiclient', async (toolsArg) => {
await toolsArg.delayFor(10000);
await testClient.stop(); await testClient.stop();
await testCloudly.stop();
}) })
export default tap.start(); export default tap.start();

View File

@ -1,28 +1,11 @@
import { expect, tap } from '@push.rocks/tapbundle'; import { expect, tap } from '@push.rocks/tapbundle';
import { Qenv } from '@push.rocks/qenv'; import * as helpers from './helpers/index.js';
const testQenv = new Qenv('./', './.nogit/');
process.env.TESTING_CLOUDLY = 'true';
delete process.env.CLI_CALL;
import * as cloudly from '../ts/index.js'; 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 = { testCloudly = await helpers.createCloudly();
cfToken: await testQenv.getEnvVarOnDemand('CF_TOKEN'),
environment: 'integration',
letsEncryptEmail: await testQenv.getEnvVarOnDemand('LETSENCRYPT_EMAIL'),
publicUrl: await testQenv.getEnvVarOnDemand('SERVEZONE_URL'),
publicPort: await testQenv.getEnvVarOnDemand('SERVEZONE_PORT'),
mongoDescriptor: {
mongoDbName: await testQenv.getEnvVarOnDemand('MONGODB_DATABASE'),
mongoDbUser: await testQenv.getEnvVarOnDemand('MONGODB_USER'),
mongoDbPass: await testQenv.getEnvVarOnDemand('MONGODB_PASSWORD'),
mongoDbUrl: await testQenv.getEnvVarOnDemand('MONGODB_URL'),
},
};
testCloudly = new cloudly.Cloudly();
expect(testCloudly).toBeInstanceOf(cloudly.Cloudly); expect(testCloudly).toBeInstanceOf(cloudly.Cloudly);
}); });

View File

@ -1,8 +1,8 @@
/** /**
* autocreated commitinfo by @pushrocks/commitinfo * autocreated commitinfo by @push.rocks/commitinfo
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/cloudly', name: '@serve.zone/cloudly',
version: '1.1.5', version: '1.2.3',
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.' description: 'A comprehensive multi-cloud manager leveraging Docker Swarmkit to orchestrate containerized applications across various cloud services and provide robust configuration and API integration.'
} }

View File

@ -9,6 +9,7 @@ export const getUsers = async (cloudlyRef: Cloudly) => {
users.push({ users.push({
id: 'envadmin', id: 'envadmin',
data: { data: {
type: 'human',
username: envAdminUser.split(':')[0], username: envAdminUser.split(':')[0],
password: envAdminUser.split(':')[1], password: envAdminUser.split(':')[1],
role: 'admin', role: 'admin',

View File

@ -34,7 +34,7 @@ export class LetsencryptConnector {
}, },
mongoDescriptor: this.cloudlyRef.config.data.mongoDescriptor, mongoDescriptor: this.cloudlyRef.config.data.mongoDescriptor,
}); });
await this.smartacme.init().catch(err => { await this.smartacme.start().catch(err => {
console.error('error in init', err); console.error('error in init', err);
console.log(`trying again in a few minutes`) console.log(`trying again in a few minutes`)
}); });

View File

@ -9,6 +9,7 @@ import { User } from './classes.user.js';
export interface IJwtData { export interface IJwtData {
userId: string; userId: string;
status: 'loggedIn' | 'loggedOut'; status: 'loggedIn' | 'loggedOut';
expiresAt: number;
} }
export class CloudlyAuthManager { export class CloudlyAuthManager {
@ -27,18 +28,22 @@ export class CloudlyAuthManager {
this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter); this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter);
} }
public async createNewSecureToken() {
return plugins.smartunique.uniSimple('secureToken', 64);
}
public async start() { public async start() {
// lets setup the smartjwtInstance // lets setup the smartjwtInstance
this.smartjwtInstance = new plugins.smartjwt.SmartJwt(); this.smartjwtInstance = new plugins.smartjwt.SmartJwt();
await this.smartjwtInstance.init(); await this.smartjwtInstance.init();
const kvStore = await this.cloudlyRef.config.appData.getKvStore(); const kvStore = await this.cloudlyRef.config.appData.getKvStore();
const existingJwtKeys: plugins.tsclass.network.IJwtKeypair = await kvStore.readKey('jwtKeys'); const existingJwtKeys: plugins.tsclass.network.IJwtKeypair = (await kvStore.readKey('jwtKeypair')) as plugins.tsclass.network.IJwtKeypair;
if (!existingJwtKeys) { if (!existingJwtKeys) {
await this.smartjwtInstance.createNewKeyPair(); await this.smartjwtInstance.createNewKeyPair();
const newJwtKeys = this.smartjwtInstance.getKeyPairAsJson(); const newJwtKeys = this.smartjwtInstance.getKeyPairAsJson();
await kvStore.writeKey('jwtKeys', newJwtKeys); await kvStore.writeKey('jwtKeypair', newJwtKeys);
} else { } else {
this.smartjwtInstance.setKeyPairAsJson(existingJwtKeys); this.smartjwtInstance.setKeyPairAsJson(existingJwtKeys);
} }
@ -48,18 +53,28 @@ export class CloudlyAuthManager {
'adminLoginWithUsernameAndPassword', 'adminLoginWithUsernameAndPassword',
async (dataArg) => { async (dataArg) => {
let jwt: string; let jwt: string;
let expiresAtTimestamp: number = Date.now() + 3600 * 1000 * 24 * 7;
const user = await User.findUserByUsernameAndPassword(dataArg.username, dataArg.password); const user = await User.findUserByUsernameAndPassword(dataArg.username, dataArg.password);
if (!user) { if (!user) {
logger.log('warn', 'login failed'); logger.log('warn', 'login failed');
throw new plugins.typedrequest.TypedResponseError('login failed');
} else { } else {
jwt = await this.smartjwtInstance.createJWT({ jwt = await this.smartjwtInstance.createJWT({
userId: user.id, userId: user.id,
status: 'loggedIn', status: 'loggedIn',
expiresAt: expiresAtTimestamp,
}); });
logger.log('success', 'login successful'); logger.log('success', 'login successful');
} }
return { return {
jwt, identity: {
jwt,
userId: user.id,
name: user.data.username,
expiresAt: expiresAtTimestamp,
role: user.data.role,
type: user.data.type,
},
}; };
} }
) )
@ -68,14 +83,33 @@ export class CloudlyAuthManager {
public async stop () {} public async stop () {}
public adminJwtGuard = new plugins.smartguard.Guard<{jwt: string}>(async (dataArg) => { public validIdentityGuard = new plugins.smartguard.Guard<{identity: plugins.servezoneInterfaces.data.IIdentity}>(async (dataArg) => {
const jwt = dataArg.jwt; const jwt = dataArg.identity.jwt;
const jwtData: IJwtData = await this.smartjwtInstance.verifyJWTAndGetData(jwt);
const expired = jwtData.expiresAt < Date.now();
plugins.smartexpect.expect(jwtData.status).setFailMessage('user not logged in').toEqual('loggedIn');
plugins.smartexpect.expect(expired).setFailMessage(`jwt expired`).toBeFalse();
plugins.smartexpect.expect(dataArg.identity.expiresAt).setFailMessage(`expiresAt >>identity valid until:${dataArg.identity.expiresAt}, but jwt says: ${jwtData.expiresAt}<< has been tampered with`).toEqual(jwtData.expiresAt);
plugins.smartexpect.expect(dataArg.identity.userId).setFailMessage('userId has been tampered with').toEqual(jwtData.userId);
if (expired) {
throw new Error('identity is expired');
}
return true;
}, {
failedHint: 'identity is not valid.',
name: 'validIdentityGuard',
});
public adminIdentityGuard = new plugins.smartguard.Guard<{identity: plugins.servezoneInterfaces.data.IIdentity}>(async (dataArg) => {
await plugins.smartguard.passGuardsOrReject(dataArg, [this.validIdentityGuard]);
const jwt = dataArg.identity.jwt;
const jwtData: IJwtData = await this.smartjwtInstance.verifyJWTAndGetData(jwt); const jwtData: IJwtData = await this.smartjwtInstance.verifyJWTAndGetData(jwt);
const user = await this.CUser.getInstance({id: jwtData.userId}); const user = await this.CUser.getInstance({id: jwtData.userId});
const isAdminBool = user.data.role === 'admin'; const isAdminBool = user.data.role === 'admin';
console.log(`user is admin: ${isAdminBool}`); console.log(`user is admin: ${isAdminBool}`);
return isAdminBool; return isAdminBool;
}, { }, {
failedHint: 'user is not admin.' failedHint: 'user is not admin.',
name: 'adminIdentityGuard',
}) })
} }

View File

@ -5,6 +5,26 @@ export class User extends plugins.smartdata.SmartDataDbDoc<
User, User,
plugins.servezoneInterfaces.data.IUser plugins.servezoneInterfaces.data.IUser
> { > {
/**
* creates a machine user
*/
public static async createMachineUser(userNameArg: string, roleArg: 'api' | 'cluster') {
const user = new User();
user.id = await User.getNewId();
user.data = {
type: 'machine',
username: userNameArg,
tokens: [{
token: 'machineUser',
expiresAt: Date.now() + 3600 * 1000 * 24 * 365,
assignedRoles: ['admin'],
}],
role: 'api',
};
await user.save();
return user;
}
public static async findUserByUsernameAndPassword(usernameArg: string, passwordArg: string) { public static async findUserByUsernameAndPassword(usernameArg: string, passwordArg: string) {
return await User.getInstance({ return await User.getInstance({
data: { data: {
@ -14,14 +34,17 @@ export class User extends plugins.smartdata.SmartDataDbDoc<
}); });
} }
constructor(optionsArg?: plugins.servezoneInterfaces.data.IUser) {
super();
if (optionsArg) {
Object.assign(this, optionsArg);
}
}
// INSTANCE // INSTANCE
@plugins.smartdata.unI() @plugins.smartdata.unI()
public id: string; public id: string;
@plugins.smartdata.svDb() @plugins.smartdata.svDb()
public data: { public data: plugins.servezoneInterfaces.data.IUser['data'];
role: 'admin' | 'user';
username: string;
password: string;
};
} }

View File

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

View File

@ -4,6 +4,7 @@ import { Cloudly } from '../classes.cloudly.js';
import { logger } from '../logger.js'; import { logger } from '../logger.js';
import { Cluster } from './classes.cluster.js'; import { Cluster } from './classes.cluster.js';
import { data } from '@serve.zone/interfaces';
export class ClusterManager { export class ClusterManager {
public ready = plugins.smartpromise.defer(); public ready = plugins.smartpromise.defer();
@ -22,12 +23,12 @@ export class ClusterManager {
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.cluster.IRequest_CreateCluster>( this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.cluster.IRequest_CreateCluster>(
new plugins.typedrequest.TypedHandler('createCluster', async (dataArg) => { new plugins.typedrequest.TypedHandler('createCluster', async (dataArg) => {
const cluster = await this.storeCluster({ // TODO: guards
const cluster = await this.createCluster({
id: plugins.smartunique.uniSimple('cluster'), id: plugins.smartunique.uniSimple('cluster'),
data: { data: {
userId: null, // this is created by the createCluster method
name: dataArg.clusterName, name: dataArg.clusterName,
jumpCode: plugins.smartunique.uniSimple('cluster'),
jumpCodeUsedAt: null,
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: [],
@ -57,7 +58,7 @@ export class ClusterManager {
// delete cluster // delete cluster
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.cluster.IRequest_DeleteCluster>( this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.cluster.IRequest_DeleteCluster>(
new plugins.typedrequest.TypedHandler('deleteCluster', async (reqDataArg, toolsArg) => { new plugins.typedrequest.TypedHandler('deleteCluster', async (reqDataArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminJwtGuard], reqDataArg); await toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], reqDataArg);
await this.deleteCluster(reqDataArg.clusterId); await this.deleteCluster(reqDataArg.clusterId);
return { return {
success: true, success: true,
@ -80,25 +81,22 @@ export class ClusterManager {
// TODO: implement getclusterConfigByServerIp // TODO: implement getclusterConfigByServerIp
} }
public async getClusterConfigBy_JumpCode(jumpCodeArg: string) { public async getClusterBy_UserId(userIdArg: string) {
await this.ready.promise; await this.ready.promise;
return await Cluster.getInstance({ return await Cluster.getInstance({
data: { data: {
jumpCode: jumpCodeArg, userId: userIdArg,
}, },
}); });
} }
public async getClusterConfigBy_ClusterIdentifier( public async getClusterBy_Identity(clusterIdentity: plugins.servezoneInterfaces.data.IIdentity) {
clusterIdentifier: plugins.servezoneInterfaces.data.IClusterIdentifier
) {
await this.ready.promise; await this.ready.promise;
return await Cluster.getInstance({ return await Cluster.getInstance({
id: clusterIdentifier.clusterId,
data: { data: {
name: clusterIdentifier.clusterName, userId: clusterIdentity.userId,
}, },
}); });
} }
@ -124,17 +122,32 @@ export class ClusterManager {
} }
/** /**
* allows storage of a config * creates a cluster (and a new user for it) and saves it
* @param configName * @param configName
* @param configObjectArg * @param configObjectArg
*/ */
public async storeCluster(configObjectArg: plugins.servezoneInterfaces.data.ICluster) { public async createCluster(configObjectArg: plugins.servezoneInterfaces.data.ICluster) {
let clusterInstance = await Cluster.getInstance({ id: configObjectArg.id }); // TODO: guards
if (!clusterInstance) { // lets create the cluster user
clusterInstance = await Cluster.fromConfigObject(configObjectArg); const clusterUser = new this.cloudlyRef.authManager.CUser({
} else { id: await this.cloudlyRef.authManager.CUser.getNewId(),
Object.assign(clusterInstance, configObjectArg); data: {
} role: 'cluster',
type: 'machine',
tokens: [
{
expiresAt: Date.now() + 3600 * 1000 * 24 * 365,
assignedRoles: ['cluster'],
token: await this.cloudlyRef.authManager.createNewSecureToken(),
},
],
},
});
await clusterUser.save();
Object.assign(configObjectArg, {
userId: clusterUser.id,
});
const clusterInstance = await Cluster.fromConfigObject(configObjectArg);
await clusterInstance.save(); await clusterInstance.save();
return clusterInstance; return clusterInstance;
} }

View File

@ -1,5 +1,6 @@
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 type { Cluster } from '../manager.cluster/classes.cluster.js';
/** /**
* in charge of talking to coreflow services on clusters * in charge of talking to coreflow services on clusters
@ -13,24 +14,42 @@ export class CloudlyCoreflowManager {
this.cloudlyRef = cloudlyRefArg; this.cloudlyRef = cloudlyRefArg;
this.cloudlyRef.typedrouter.addTypedRouter(this.typedRouter); this.cloudlyRef.typedrouter.addTypedRouter(this.typedRouter);
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByJumpCode>( this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByToken>(
new plugins.typedrequest.TypedHandler('getIdentityByJumpCode', async (requestData) => { new plugins.typedrequest.TypedHandler('getIdentityByToken', async (requestData) => {
const clusterConfig = const user = await this.cloudlyRef.authManager.CUser.getInstance({
await this.cloudlyRef.clusterManager.getClusterConfigBy_JumpCode( data: {
requestData.jumpCode tokens: [{
); token: requestData.token,
}] // find the proper user here.
} as any
});
if (!clusterConfig) { if (!user) {
throw new plugins.typedrequest.TypedResponseError('The supplied jumpCode is not valid.'); throw new plugins.typedrequest.TypedResponseError('The supplied token is not valid. No matching user found.');
} }
if (user.data.type !== 'machine') {
throw new plugins.typedrequest.TypedResponseError('The supplied token is not valid. The user is not a machine.');
}
let cluster: Cluster;
if (user.data.role === 'cluster') {
cluster = await this.cloudlyRef.clusterManager.getClusterBy_UserId(user.id);
}
const expiryTimestamp = Date.now() + 3600 * 1000 * 24 * 365;
return { return {
clusterIdentifier: { identity: {
clusterId: clusterConfig.id, name: user.data.username,
clusterName: clusterConfig.data.name, role: user.data.role,
type: 'machine', // if someone authenticates by token, they are a machine, no matter what.
userId: user.id,
expiresAt: expiryTimestamp,
...(cluster ? {
clusterId: cluster.id,
clusterName: cluster.data.name,
} : {}),
jwt: await this.cloudlyRef.authManager.smartjwtInstance.createJWT({ jwt: await this.cloudlyRef.authManager.smartjwtInstance.createJWT({
status: 'loggedIn', status: 'loggedIn',
userId: 'cluster:' + clusterConfig.id, // TODO: create real users for clusters userId: user.id,
expiresAt: expiryTimestamp,
}) })
}, },
}; };
@ -42,16 +61,16 @@ export class CloudlyCoreflowManager {
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.config.IRequest_Any_Cloudly_GetClusterConfig>( new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.config.IRequest_Any_Cloudly_GetClusterConfig>(
'getClusterConfig', 'getClusterConfig',
async (dataArg) => { async (dataArg) => {
const clusterIdentifier = dataArg.clusterIdentifier; const identity = dataArg.identity;
console.log('trying to get clusterConfigSet'); console.log('trying to get clusterConfigSet');
console.log(dataArg); console.log(dataArg);
const clusterConfigSet = const cluster =
await this.cloudlyRef.clusterManager.getClusterConfigBy_ClusterIdentifier( await this.cloudlyRef.clusterManager.getClusterBy_Identity(
clusterIdentifier identity
); );
console.log('got cluster config and sending it back to coreflow'); console.log('got cluster config and sending it back to coreflow');
return { return {
configData: await clusterConfigSet.createSavableObject(), configData: await cluster.createSavableObject(),
deploymentDirectives: [], deploymentDirectives: [],
}; };
} }
@ -60,14 +79,14 @@ export class CloudlyCoreflowManager {
// lets enable getting of certificates // lets enable getting of certificates
this.typedRouter.addTypedHandler( this.typedRouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.certificate.IRequest_Any_Cloudly_GetSslCertificate>( new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.certificate.IRequest_Any_Cloudly_GetCertificateForDomain>(
'getSslCertificate', 'getCertificateForDomain',
async (dataArg) => { async (dataArg) => {
console.log(`got request for certificate ${dataArg.requiredCertName}`); console.log(`incoming API request for certificate ${dataArg.domainName}`);
const cert = await this.cloudlyRef.letsencryptConnector.getCertificateForDomain( const cert = await this.cloudlyRef.letsencryptConnector.getCertificateForDomain(
dataArg.requiredCertName dataArg.domainName
); );
console.log(`got certificate ready for reponse ${dataArg.requiredCertName}`); console.log(`got certificate ready for reponse ${dataArg.domainName}`);
return { return {
certificate: await cert.createSavableObject(), certificate: await cert.createSavableObject(),
}; };

View File

@ -34,4 +34,12 @@ export class Image extends plugins.smartdata.SmartDataDbDoc<Image, plugins.serve
public async getStoragePath(versionStringArg: string) { public async getStoragePath(versionStringArg: string) {
return `${this.data.name}:${versionStringArg}`.replace('/', '__') return `${this.data.name}:${versionStringArg}`.replace('/', '__')
} }
public async getWriteStream() {
}
public async getReadStream() {
}
} }

View File

@ -1,5 +1,6 @@
import type { Cloudly } from '../classes.cloudly.js'; import type { Cloudly } from '../classes.cloudly.js';
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as paths from '../paths.js';
import { Image } from './classes.image.js'; import { Image } from './classes.image.js';
@ -9,14 +10,13 @@ export class ImageManager {
public smartbucketInstance: plugins.smartbucket.SmartBucket; public smartbucketInstance: plugins.smartbucket.SmartBucket;
public imageDir: plugins.smartbucket.Directory; public imageDir: plugins.smartbucket.Directory;
public dockerImageStore: plugins.docker.DockerImageStore; public dockerImageStore: plugins.docker.DockerImageStore;
get db() { get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb; return this.cloudlyRef.mongodbConnector.smartdataDb;
} }
public CImage = plugins.smartdata.setDefaultManagerForDoc(this, Image); public CImage = plugins.smartdata.setDefaultManagerForDoc(this, Image);
constructor(cloudlyRefArg: Cloudly) { constructor(cloudlyRefArg: Cloudly) {
this.cloudlyRef = cloudlyRefArg; this.cloudlyRef = cloudlyRefArg;
@ -26,7 +26,7 @@ export class ImageManager {
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_CreateImage>( new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_CreateImage>(
'createImage', 'createImage',
async (reqArg, toolsArg) => { async (reqArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminJwtGuard], reqArg); await toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], reqArg);
const image = await this.CImage.create({ const image = await this.CImage.create({
name: reqArg.name, name: reqArg.name,
description: reqArg.description, description: reqArg.description,
@ -37,13 +37,25 @@ export class ImageManager {
}; };
} }
) )
) );
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_GetImage>(
new plugins.typedrequest.TypedHandler('getImage', async (reqArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], reqArg);
const image = await this.CImage.getInstance({
id: reqArg.imageId,
});
return {
image: await image.createSavableObject(),
};
})
);
this.typedrouter.addTypedHandler( this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_DeleteImage>( new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_DeleteImage>(
'deleteImage', 'deleteImage',
async (reqArg, toolsArg) => { async (reqArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminJwtGuard], reqArg); await toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], reqArg);
const image = await this.CImage.getInstance({ const image = await this.CImage.getInstance({
id: reqArg.imageId, id: reqArg.imageId,
}); });
@ -57,7 +69,7 @@ export class ImageManager {
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_GetAllImages>( new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_GetAllImages>(
'getAllImages', 'getAllImages',
async (requestArg, toolsArg) => { async (requestArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminJwtGuard], requestArg); await toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], requestArg);
const images = await this.CImage.getInstances({}); const images = await this.CImage.getInstances({});
return { return {
images: await Promise.all( images: await Promise.all(
@ -74,14 +86,33 @@ export class ImageManager {
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_PushImageVersion>( new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_PushImageVersion>(
'pushImageVersion', 'pushImageVersion',
async (reqArg, toolsArg) => { async (reqArg, toolsArg) => {
const image = await this.CImage.getInstance({ await plugins.smartguard.passGuardsOrReject(reqArg, [
this.cloudlyRef.authManager.validIdentityGuard,
]);
const refImage = await this.CImage.getInstance({
id: reqArg.imageId, id: reqArg.imageId,
}); });
if (!image) { if (!refImage) {
throw new plugins.typedrequest.TypedResponseError('Image not found'); throw new plugins.typedrequest.TypedResponseError('Image not found');
} }
const imageVersion = reqArg.versionString; const imageVersion = reqArg.versionString;
console.log(
`got request to push image version ${imageVersion} for image ${refImage.data.name}`
);
const imagePushStream = reqArg.imageStream; const imagePushStream = reqArg.imageStream;
(async () => {
const smartWebDuplex = new plugins.smartstream.webstream.WebDuplexStream<
Uint8Array,
Uint8Array
>({
writeFunction: async (chunkArg, toolsArg) => {
console.log(chunkArg);
return chunkArg;
},
});
imagePushStream.writeToWebstream(smartWebDuplex.writable);
await this.dockerImageStore.storeImage(refImage.id, plugins.smartstream.SmartDuplex.fromWebReadableStream(smartWebDuplex.readable));
})();
return { return {
allowed: true, allowed: true,
}; };
@ -94,12 +125,17 @@ export class ImageManager {
'pullImageVersion', 'pullImageVersion',
async (reqArg) => { async (reqArg) => {
const image = await this.CImage.getInstance({ const image = await this.CImage.getInstance({
id: reqArg.imageId, id: reqArg.imageId,
}); });
const imageVersion = image.data.versions.find((version) => version.versionString === reqArg.versionString); const imageVersion = image.data.versions.find(
const readable = this.imageDir.fastGetStream({ (version) => version.versionString === reqArg.versionString
path: await image.getStoragePath(reqArg.versionString), );
}, 'webstream'); 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,
@ -110,6 +146,7 @@ export class ImageManager {
} }
public async start() { public async start() {
// lets setup s3
const s3Descriptor: plugins.tsclass.storage.IS3Descriptor = const s3Descriptor: plugins.tsclass.storage.IS3Descriptor =
await this.cloudlyRef.config.appData.waitForAndGetKey('s3Descriptor'); await this.cloudlyRef.config.appData.waitForAndGetKey('s3Descriptor');
console.log(this.cloudlyRef.config.data.s3Descriptor); console.log(this.cloudlyRef.config.data.s3Descriptor);
@ -117,10 +154,17 @@ export class ImageManager {
this.cloudlyRef.config.data.s3Descriptor this.cloudlyRef.config.data.s3Descriptor
); );
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: 'images/00init', contents: 'init' });
this.imageDir = await bucket.getDirectoryFromPath({ this.imageDir = await bucket.getDirectoryFromPath({
path: 'images', path: '/images',
});
// lets setup dockerstore
await plugins.smartfile.fs.ensureDir(paths.dockerImageStoreDir);
this.dockerImageStore = new plugins.docker.DockerImageStore({
localDirPath: paths.dockerImageStoreDir,
bucketDir: this.imageDir,
}); });
} }
} }

View File

@ -39,8 +39,8 @@ export class CloudlySecretManager {
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.secret.IReq_Admin_GetConfigBundlesAndSecretGroups>( new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.secret.IReq_Admin_GetConfigBundlesAndSecretGroups>(
'adminGetConfigBundlesAndSecretGroups', 'adminGetConfigBundlesAndSecretGroups',
async (dataArg, toolsArg) => { async (dataArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminJwtGuard], dataArg); await toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], dataArg);
dataArg.jwt dataArg.identity.jwt
const secretBundles = await SecretBundle.getInstances({}); const secretBundles = await SecretBundle.getInstances({});
const secretGroups = await SecretGroup.getInstances({}); const secretGroups = await SecretGroup.getInstances({});
return { return {

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import { ServiceManager } from './classes.servicemanager.js';
export class Service extends plugins.smartdata.SmartDataDbDoc<Service, plugins.servezoneInterfaces.data.IService, ServiceManager> { export class Service extends plugins.smartdata.SmartDataDbDoc<Service, plugins.servezoneInterfaces.data.IService, ServiceManager> {

View File

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

View File

@ -33,6 +33,7 @@ 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';
import * as smartexpect from '@push.rocks/smartexpect';
import * as smartfile from '@push.rocks/smartfile'; import * as smartfile from '@push.rocks/smartfile';
import * as smartguard from '@push.rocks/smartguard'; import * as smartguard from '@push.rocks/smartguard';
import * as smartjson from '@push.rocks/smartjson'; import * as smartjson from '@push.rocks/smartjson';
@ -42,6 +43,7 @@ import * as smartpath from '@push.rocks/smartpath';
import * as smartpromise from '@push.rocks/smartpromise'; import * as smartpromise from '@push.rocks/smartpromise';
import * as smartrequest from '@push.rocks/smartrequest'; import * as smartrequest from '@push.rocks/smartrequest';
import * as smartssh from '@push.rocks/smartssh'; import * as smartssh from '@push.rocks/smartssh';
import * as smartstream from '@push.rocks/smartstream';
import * as smartstring from '@push.rocks/smartstring'; import * as smartstring from '@push.rocks/smartstring';
import * as smartunique from '@push.rocks/smartunique'; import * as smartunique from '@push.rocks/smartunique';
import * as taskbuffer from '@push.rocks/taskbuffer'; import * as taskbuffer from '@push.rocks/taskbuffer';
@ -57,6 +59,7 @@ export {
smartclickhouse, smartclickhouse,
smartdata, smartdata,
smartexit, smartexit,
smartexpect,
smartdelay, smartdelay,
smartfile, smartfile,
smartguard, smartguard,
@ -67,6 +70,7 @@ export {
smartpromise, smartpromise,
smartrequest, smartrequest,
smartssh, smartssh,
smartstream,
smartstring, smartstring,
smartunique, smartunique,
taskbuffer, taskbuffer,

View File

@ -55,7 +55,7 @@ export class CloudlyApiClient {
this.cloudlyUrl this.cloudlyUrl
); );
console.log( console.log(
`CloudlyCluent connected to cloudly at ${this.cloudlyUrl}. Remember to get an identity.` `CloudlyClient connected to cloudly at ${this.cloudlyUrl}. Remember to get an identity.`
); );
} }
@ -63,57 +63,69 @@ export class CloudlyApiClient {
await this.typedsocketClient.stop(); await this.typedsocketClient.stop();
} }
public identity: plugins.servezoneInterfaces.data.IClusterIdentifier; public identity: plugins.servezoneInterfaces.data.IIdentity;
public async getIdentityByJumpCode( public async getIdentityByToken(
jumpCodeArg: string, token: string,
tagConnection = false, optionsArg?: {
statefullIdentity = true tagConnection?: boolean;
): Promise<plugins.servezoneInterfaces.data.IClusterIdentifier> { statefullIdentity?: boolean;
}
): Promise<plugins.servezoneInterfaces.data.IIdentity> {
optionsArg = Object.assign({}, {
tagConnection: false,
statefullIdentity: true,
}, optionsArg);
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_GetIdentityByToken>(
'getIdentityByJumpCode' 'getIdentityByToken'
); );
console.log(`trying to get identity from cloudly with supplied jumpCodeArg: ${jumpCodeArg}`); console.log(`trying to get identity from cloudly with supplied jumpCodeArg: ${token}`);
const response = await identityRequest.fire({ const response = await identityRequest.fire({
jumpCode: jumpCodeArg, token: token,
}); });
console.log('got identity response'); console.log('got identity response');
const identity = response.clusterIdentifier; const identity = response.identity;
if (tagConnection) { if (optionsArg.tagConnection) {
this.typedsocketClient.addTag('identity', identity); this.typedsocketClient.addTag('identity', identity);
} }
if (statefullIdentity) { if (optionsArg.statefullIdentity) {
this.identity = identity; this.identity = identity;
} }
return identity; return identity;
} }
/**
* will use statefull identity by default
*/
public async getClusterConfigFromCloudlyByIdentity( public async getClusterConfigFromCloudlyByIdentity(
identityArg: plugins.servezoneInterfaces.data.IClusterIdentifier identityArg: plugins.servezoneInterfaces.data.IIdentity = this.identity
): Promise<plugins.servezoneInterfaces.data.ICluster> { ): Promise<plugins.servezoneInterfaces.data.ICluster> {
const clusterConfigRequest = const clusterConfigRequest =
this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.config.IRequest_Any_Cloudly_GetClusterConfig>( this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.config.IRequest_Any_Cloudly_GetClusterConfig>(
'getClusterConfig' 'getClusterConfig'
); );
const response = await clusterConfigRequest.fire({ const response = await clusterConfigRequest.fire({
jwt: '', identity: identityArg,
clusterIdentifier: identityArg,
}); });
return response.configData; return response.configData;
} }
/**
* will use statefull identity by default
*/
public async getServerConfigFromCloudlyByIdentity( public async getServerConfigFromCloudlyByIdentity(
identityArg: plugins.servezoneInterfaces.data.IClusterIdentifier identityArg: plugins.servezoneInterfaces.data.IIdentity = this.identity
): Promise<plugins.servezoneInterfaces.data.IServer> { ): Promise<plugins.servezoneInterfaces.data.IServer> {
const serverConfigRequest = const serverConfigRequest =
this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.config.IRequest_Any_Cloudly_GetServerConfig>( this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.config.IRequest_Any_Cloudly_GetServerConfig>(
'getServerConfig' 'getServerConfig'
); );
const response = await serverConfigRequest.fire({ const response = await serverConfigRequest.fire({
jwt: '', // TODO: do proper auth here identity: identityArg,
serverId: '', // TODO: get server id here serverId: '', // TODO: get server id here
}); });
return response.configData; return response.configData;
@ -121,25 +133,35 @@ export class CloudlyApiClient {
/** /**
* gets a certificate for a domain used by a service * gets a certificate for a domain used by a service
* @param serviceNameArg
* @param domainNameArg
*/ */
public async getCertificateForDomainOverHttps( public async getCertificateForDomain(optionsArg: {
domainNameArg: string domainName: string;
): Promise<plugins.tsclass.network.ICert> { type: plugins.servezoneInterfaces.requests.certificate.IRequest_Any_Cloudly_GetCertificateForDomain['request']['type'];
identity?: plugins.servezoneInterfaces.data.IIdentity;
}): Promise<plugins.tsclass.network.ICert> {
optionsArg.identity = optionsArg.identity || this.identity;
if (!optionsArg.identity) {
throw new Error('identity is required. Either provide one or login first.');
}
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_GetCertificateForDomain>(
'getSslCertificate' 'getCertificateForDomain'
); );
const typedResponse = await typedCertificateRequest.fire({ const typedResponse = await typedCertificateRequest.fire({
authToken: '', // do proper auth here identity: this.identity, // do proper auth here
requiredCertName: domainNameArg, domainName: optionsArg.domainName,
type: optionsArg.type,
}); });
return typedResponse.certificate; return typedResponse.certificate;
} }
// Images public images = {
public async getImages() { // Images
return Image.getImages(this); getImages: async () => {
return Image.getImages(this);
},
createImage: async (optionsArg: Parameters<typeof Image.createImage>[1]) => {
return Image.createImage(this, optionsArg);
}
} }
} }

View File

@ -7,7 +7,7 @@ export class Image implements plugins.servezoneInterfaces.data.IImage {
'getAllImages' 'getAllImages'
); );
const response = await getAllImagesTR.fire({ const response = await getAllImagesTR.fire({
jwt: cloudlyClientRef.identity.jwt, identity: cloudlyClientRef.identity,
}); });
const resultImages: Image[] = []; const resultImages: Image[] = [];
for (const image of response.images) { for (const image of response.images) {
@ -18,6 +18,23 @@ export class Image implements plugins.servezoneInterfaces.data.IImage {
return resultImages; return resultImages;
} }
/**
* creates a new image
*/
public static async createImage(cloudlyClientRef: CloudlyApiClient, imageDataArg: Partial<plugins.servezoneInterfaces.data.IImage['data']>) {
const createImageTR = cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_CreateImage>(
'createImage'
);
const response = await createImageTR.fire({
identity: cloudlyClientRef.identity,
name: imageDataArg.name,
description: imageDataArg.description,
});
const newImage = new Image(cloudlyClientRef);
Object.assign(newImage, response.image);
return newImage;
}
// INSTANCE // INSTANCE
cloudlyClientRef: CloudlyApiClient; cloudlyClientRef: CloudlyApiClient;
@ -32,11 +49,11 @@ export class Image implements plugins.servezoneInterfaces.data.IImage {
* updates the image data * updates the image data
*/ */
public async update() { public async update() {
const getVersionsTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_GetImageMetadata>( const getVersionsTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_GetImage>(
'getImageMetadata' 'getImage'
); );
const response = await getVersionsTR.fire({ const response = await getVersionsTR.fire({
jwt: this.cloudlyClientRef.identity.jwt, identity: this.cloudlyClientRef.identity,
imageId: this.id, imageId: this.id,
}); });
Object.assign(this, response.image); Object.assign(this, response.image);
@ -49,18 +66,17 @@ export class Image implements plugins.servezoneInterfaces.data.IImage {
*/ */
public async pushImageVersion(imageVersion: string, imageReadableArg: ReadableStream<Uint8Array>): Promise<void> { public async pushImageVersion(imageVersion: string, imageReadableArg: ReadableStream<Uint8Array>): Promise<void> {
const done = plugins.smartpromise.defer(); const done = plugins.smartpromise.defer();
const pullImageTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_PushImageVersion>( const pushImageTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_PushImageVersion>(
'pushImageVersion' 'pushImageVersion'
); );
const virtualStream = new plugins.typedrequest.VirtualStream(); const virtualStream = new plugins.typedrequest.VirtualStream();
const response = await pullImageTR.fire({ const response = await pushImageTR.fire({
jwt: this.cloudlyClientRef.identity.jwt, identity: this.cloudlyClientRef.identity,
imageId: this.id, imageId: this.id,
versionString: '', versionString: '',
imageStream: virtualStream, imageStream: virtualStream,
}); });
await virtualStream.readFromWebstream(imageReadableArg); await virtualStream.readFromWebstream(imageReadableArg);
await done.promise;
await this.update(); await this.update();
}; };
@ -72,7 +88,7 @@ export class Image implements plugins.servezoneInterfaces.data.IImage {
'pullImageVersion' 'pullImageVersion'
); );
const response = await pullImageTR.fire({ const response = await pullImageTR.fire({
jwt: this.cloudlyClientRef.identity.jwt, identity: this.cloudlyClientRef.identity,
imageId: this.id, imageId: this.id,
versionString: versionStringArg, versionString: versionStringArg,
}); });

View File

@ -0,0 +1,16 @@
{
"name": "@serve.zone/api",
"dependencies": [
"@serve.zone/interfaces",
"@push.rocks/smartpromise",
"@push.rocks/smartrx",
"@push.rocks/smartstream",
"@api.global/typedrequest",
"@api.global/typedsocket",
"@tsclass/tsclass"
],
"registries": [
"registry.npmjs.org:public",
"verdaccio.lossless.digital:public"
]
}

View File

@ -0,0 +1 @@
console.log('this is the cli client.');

View File

@ -0,0 +1,8 @@
{
"name": "@serve.zone/cli",
"dependencies": [],
"registries": [
"registry.npmjs.org:public",
"verdaccio.lossless.digital:public"
]
}

View File

@ -0,0 +1,10 @@
{
"name": "@serve.zone/interfaces",
"dependencies": [
"@tsclass/tsclass"
],
"registries": [
"registry.npmjs.org:public",
"verdaccio.lossless.digital:public"
]
}

View File

@ -1,8 +1,8 @@
/** /**
* autocreated commitinfo by @pushrocks/commitinfo * autocreated commitinfo by @push.rocks/commitinfo
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/cloudly', name: '@serve.zone/cloudly',
version: '1.1.5', version: '1.2.3',
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.' description: 'A comprehensive multi-cloud manager leveraging Docker Swarmkit to orchestrate containerized applications across various cloud services and provide robust configuration and API integration.'
} }

View File

@ -3,11 +3,11 @@ import * as domtools from '@design.estate/dees-domtools';
const appstate = new plugins.deesDomtools.plugins.smartstate.Smartstate(); const appstate = new plugins.deesDomtools.plugins.smartstate.Smartstate();
export interface ILoginState { export interface ILoginState {
jwt: string; identity: plugins.interfaces.data.IIdentity;
} }
export const loginStatePart: plugins.smartstate.StatePart<unknown, ILoginState> = await appstate.getStatePart<ILoginState>( export const loginStatePart: plugins.smartstate.StatePart<unknown, ILoginState> = await appstate.getStatePart<ILoginState>(
'login', 'login',
{ jwt: null }, { identity: null },
'persistent' 'persistent'
); );
@ -22,10 +22,15 @@ export const loginAction = loginStatePart.createAction<{ username: string; passw
const response = await trLogin.fire({ const response = await trLogin.fire({
username: payloadArg.username, username: payloadArg.username,
password: payloadArg.password, password: payloadArg.password,
}).catch(err => {
console.log(err);
return {
...statePartArg.getState(),
}
}); });
return { return {
...currentState, ...currentState,
...(response.jwt ? { jwt: response.jwt } : {}), ...(response.identity ? { identity: response.identity } : {}),
}; };
} }
); );
@ -34,7 +39,7 @@ export const logoutAction = loginStatePart.createAction(async (statePartArg) =>
const currentState = statePartArg.getState(); const currentState = statePartArg.getState();
return { return {
...currentState, ...currentState,
jwt: null, identity: null,
}; };
}); });
@ -81,7 +86,7 @@ export const getAllDataAction = dataState.createAction(async (statePartArg, part
'adminGetConfigBundlesAndSecretGroups' 'adminGetConfigBundlesAndSecretGroups'
); );
const response = await trGetSecrets.fire({ const response = await trGetSecrets.fire({
jwt: loginStatePart.getState().jwt, identity: loginStatePart.getState().identity,
}); });
currentState = { currentState = {
...currentState, ...currentState,
@ -95,7 +100,7 @@ export const getAllDataAction = dataState.createAction(async (statePartArg, part
'getAllImages' 'getAllImages'
); );
const responseImages = await trGetImages.fire({ const responseImages = await trGetImages.fire({
jwt: loginStatePart.getState().jwt, identity: loginStatePart.getState().identity,
}); });
currentState = { currentState = {
...currentState, ...currentState,
@ -109,7 +114,7 @@ export const getAllDataAction = dataState.createAction(async (statePartArg, part
'getAllClusters' 'getAllClusters'
); );
const responseClusters = await trGetClusters.fire({ const responseClusters = await trGetClusters.fire({
jwt: loginStatePart.getState().jwt, identity: loginStatePart.getState().identity,
}); });
currentState = { currentState = {
@ -130,7 +135,7 @@ export const createSecretGroupAction = dataState.createAction(
'adminCreateConfigBundlesAndSecretGroups' 'adminCreateConfigBundlesAndSecretGroups'
); );
const response = await trCreateSecretGroup.fire({ const response = await trCreateSecretGroup.fire({
jwt: loginStatePart.getState().jwt, identity: loginStatePart.getState().identity,
secretBundles: [], secretBundles: [],
secretGroups: [payloadArg], secretGroups: [payloadArg],
}); });
@ -149,7 +154,7 @@ export const deleteSecretGroupAction = dataState.createAction(
'adminDeleteConfigBundlesAndSecretGroups' 'adminDeleteConfigBundlesAndSecretGroups'
); );
const response = await trDeleteSecretGroup.fire({ const response = await trDeleteSecretGroup.fire({
jwt: loginStatePart.getState().jwt, identity: loginStatePart.getState().identity,
secretBundleIds: [], secretBundleIds: [],
secretGroupIds: [payloadArg.secretGroupId], secretGroupIds: [payloadArg.secretGroupId],
}); });
@ -168,7 +173,7 @@ export const deleteSecretBundleAction = dataState.createAction(
'adminDeleteConfigBundlesAndSecretGroups' 'adminDeleteConfigBundlesAndSecretGroups'
); );
const response = await trDeleteConfigBundle.fire({ const response = await trDeleteConfigBundle.fire({
jwt: loginStatePart.getState().jwt, identity: loginStatePart.getState().identity,
secretBundleIds: [payloadArg.configBundleId], secretBundleIds: [payloadArg.configBundleId],
secretGroupIds: [], secretGroupIds: [],
}); });
@ -187,7 +192,7 @@ export const createImageAction = dataState.createAction(
'createImage' 'createImage'
); );
const response = await trCreateImage.fire({ const response = await trCreateImage.fire({
jwt: loginStatePart.getState().jwt, identity: loginStatePart.getState().identity,
name: payloadArg.imageName, name: payloadArg.imageName,
description: payloadArg.description, description: payloadArg.description,
}); });
@ -210,7 +215,7 @@ export const deleteImageAction = dataState.createAction(
'deleteImage' 'deleteImage'
); );
const response = await trDeleteImage.fire({ const response = await trDeleteImage.fire({
jwt: loginStatePart.getState().jwt, identity: loginStatePart.getState().identity,
imageId: payloadArg.imageId, imageId: payloadArg.imageId,
}); });
currentState = { currentState = {
@ -238,7 +243,7 @@ export const addClusterAction = dataState.createAction(
'createCluster' 'createCluster'
); );
const response = await trAddCluster.fire({ const response = await trAddCluster.fire({
jwt: loginStatePart.getState().jwt, identity: loginStatePart.getState().identity,
...payloadArg, ...payloadArg,
}); });
currentState = { currentState = {

View File

@ -33,7 +33,7 @@ declare global {
@customElement('cloudly-dashboard') @customElement('cloudly-dashboard')
export class CloudlyDashboard extends DeesElement { export class CloudlyDashboard extends DeesElement {
@state() private jwt: string; @state() private identity: plugins.interfaces.data.IIdentity;
@state() private data: appstate.IDataState = { @state() private data: appstate.IDataState = {
secretGroups: [], secretGroups: [],
secretBundles: [], secretBundles: [],
@ -97,6 +97,10 @@ export class CloudlyDashboard extends DeesElement {
name: 'Services', name: 'Services',
element: CloudlyViewServices, element: CloudlyViewServices,
}, },
{
name: 'Testing & Building',
element: CloudlyViewServices,
},
{ {
name: 'Deployments', name: 'Deployments',
element: CloudlyViewDeployments, element: CloudlyViewDeployments,
@ -169,8 +173,8 @@ export class CloudlyDashboard extends DeesElement {
const domtools = await this.domtoolsPromise; const domtools = await this.domtoolsPromise;
const loginState = appstate.loginStatePart.getState(); const loginState = appstate.loginStatePart.getState();
console.log(loginState); console.log(loginState);
if (loginState.jwt) { if (loginState.identity) {
this.jwt = loginState.jwt; this.identity = loginState.identity;
await simpleLogin.switchToSlottedContent(); await simpleLogin.switchToSlottedContent();
await appstate.dataState.dispatchAction(appstate.getAllDataAction, null); await appstate.dataState.dispatchAction(appstate.getAllDataAction, null);
} }
@ -186,9 +190,9 @@ export class CloudlyDashboard extends DeesElement {
username, username,
password, password,
}); });
if (state.jwt) { if (state.identity) {
console.log('got jwt'); console.log('got jwt');
this.jwt = state.jwt; this.identity = state.identity;
form.setStatus('success', 'Logged in!'); form.setStatus('success', 'Logged in!');
await simpleLogin.switchToSlottedContent(); await simpleLogin.switchToSlottedContent();
await appstate.dataState.dispatchAction(appstate.getAllDataAction, null); await appstate.dataState.dispatchAction(appstate.getAllDataAction, null);

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewBackups extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewClusters extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewDbs extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewDeployments extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,8 @@ export class CloudlyViewDns extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
css` shared.viewHostCss,
:host { css`
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { DeesElement, customElement, html, state, css, cssManager } from '@design.estate/dees-element'; import { DeesElement, customElement, html, state, css, cssManager } from '@design.estate/dees-element';
@ -23,13 +24,9 @@ export class CloudlyViewImages extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewLogs extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,14 +32,8 @@ export class CloudlyViewMails extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
css` shared.viewHostCss,
:host { css``
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`,
]; ];
public render() { public render() {

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,8 @@ export class CloudlyViewOverview extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 8px 16px;
}
.clusterGrid { .clusterGrid {
display: grid; display: grid;
grid-template-columns: ${cssManager.cssGridColumns(3, 8)}; grid-template-columns: ${cssManager.cssGridColumns(3, 8)};

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewS3 extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewSecretBundles extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { DeesElement, customElement, html, state, css, cssManager } from '@design.estate/dees-element'; import { DeesElement, customElement, html, state, css, cssManager } from '@design.estate/dees-element';
@ -23,13 +24,9 @@ export class CloudlyViewSecretGroups extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewServices extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -21,6 +21,8 @@ export class CloudlySectionheading extends DeesElement {
h1 { h1 {
font-family: 'Cal Sans'; font-family: 'Cal Sans';
letter-spacing: 0.025em; letter-spacing: 0.025em;
margin: 0px;
margin-bottom: 16px;
} }
`, `,
] ]

View File

@ -0,0 +1,10 @@
import { css } from '@design.estate/dees-element';
export const viewHostCss = css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`;

View File

@ -1 +1,2 @@
export * from './cloudly-sectionheading.js'; export * from './cloudly-sectionheading.js';
export * from './css.js';