Compare commits

..

8 Commits

Author SHA1 Message Date
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
47 changed files with 3902 additions and 1867 deletions

84
changelog.md Normal file
View File

@ -0,0 +1,84 @@
# Changelog
## 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

@ -1,6 +1,6 @@
{
"name": "@serve.zone/cloudly",
"version": "1.1.6",
"version": "1.2.0",
"private": false,
"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",
@ -18,58 +18,60 @@
"start": "node cli.js",
"startTs": "node cli.ts.js",
"watch": "tswatch website",
"localPublish": "gitzone commit"
"publish": "tspublish"
},
"devDependencies": {
"@git.zone/tsbuild": "^2.1.80",
"@git.zone/tsbuild": "^2.1.84",
"@git.zone/tsbundle": "^2.0.15",
"@git.zone/tspublish": "^1.3.0",
"@git.zone/tstest": "^1.0.90",
"@git.zone/tswatch": "^2.0.23",
"@push.rocks/tapbundle": "^5.0.23",
"@types/node": "^20.14.6"
"@push.rocks/tapbundle": "^5.3.0",
"@types/node": "^22.7.7"
},
"dependencies": {
"@api.global/typedrequest": "3.0.30",
"@api.global/typedserver": "^3.0.50",
"@api.global/typedrequest": "3.1.10",
"@api.global/typedserver": "^3.0.51",
"@api.global/typedsocket": "^3.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/slack": "^3.0.9",
"@design.estate/dees-catalog": "^1.0.289",
"@design.estate/dees-domtools": "^2.0.57",
"@design.estate/dees-element": "^2.0.34",
"@git.zone/tsrun": "^1.2.37",
"@design.estate/dees-catalog": "^1.2.0",
"@design.estate/dees-domtools": "^2.0.64",
"@design.estate/dees-element": "^2.0.39",
"@git.zone/tsrun": "^1.2.49",
"@push.rocks/early": "^4.0.3",
"@push.rocks/npmextra": "^5.0.23",
"@push.rocks/projectinfo": "^5.0.1",
"@push.rocks/qenv": "^6.0.5",
"@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/smartclickhouse": "^2.0.17",
"@push.rocks/smartdata": "^5.2.6",
"@push.rocks/smartdata": "^5.2.10",
"@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartexit": "^1.0.23",
"@push.rocks/smartfile": "^11.0.20",
"@push.rocks/smartguard": "^3.0.2",
"@push.rocks/smartexpect": "^1.2.1",
"@push.rocks/smartfile": "^11.0.21",
"@push.rocks/smartguard": "^3.1.0",
"@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-destination-clickhouse": "^1.0.13",
"@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/smartrx": "^3.0.7",
"@push.rocks/smartssh": "^2.0.1",
"@push.rocks/smartstate": "^2.0.17",
"@push.rocks/smartstream": "^3.0.44",
"@push.rocks/smartstate": "^2.0.19",
"@push.rocks/smartstream": "^3.2.4",
"@push.rocks/smartstring": "^4.0.15",
"@push.rocks/smartunique": "^3.0.9",
"@push.rocks/taskbuffer": "^3.0.2",
"@push.rocks/webjwt": "^1.0.9",
"@serve.zone/interfaces": "^1.0.74",
"@tsclass/tsclass": "^4.0.60"
"@serve.zone/interfaces": "^1.1.2",
"@tsclass/tsclass": "^4.1.2"
},
"files": [
"ts/**/*",

4888
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -8,4 +8,4 @@
`code.foss.global/serve.zone/cloudly:latest`
- Note: the exports are defined in the package.json.
- For know, cloud wise only the setup with cloudron and hetzner cloud is supported.
- For now, cloud wise only the setup with cloudron and hetzner cloud is supported.

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 * as helpers from './helpers/index.js';
import * as cloudly from '../ts/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;
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 () => {
testClient = new cloudlyApiClient.CloudlyApiClient({
registerAs: 'api',
cloudlyUrl: 'http://localhost:3000',
cloudlyUrl: `http://localhost:${await helpers.getEnvVarOnDemand('SERVEZONE_PORT')}`,
});
await testClient.start();
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 testCloudly.stop();
})
export default tap.start();

View File

@ -1,28 +1,11 @@
import { expect, tap } from '@push.rocks/tapbundle';
import { Qenv } from '@push.rocks/qenv';
const testQenv = new Qenv('./', './.nogit/');
process.env.TESTING_CLOUDLY = 'true';
delete process.env.CLI_CALL;
import * as helpers from './helpers/index.js';
import * as cloudly from '../ts/index.js';
let testCloudly: cloudly.Cloudly;
tap.test('first test', 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'),
},
};
testCloudly = new cloudly.Cloudly();
testCloudly = await helpers.createCloudly();
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 = {
name: '@serve.zone/cloudly',
version: '1.1.6',
version: '1.2.0',
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({
id: 'envadmin',
data: {
type: 'human',
username: envAdminUser.split(':')[0],
password: envAdminUser.split(':')[1],
role: 'admin',

View File

@ -34,7 +34,7 @@ export class LetsencryptConnector {
},
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.log(`trying again in a few minutes`)
});

View File

@ -9,6 +9,7 @@ import { User } from './classes.user.js';
export interface IJwtData {
userId: string;
status: 'loggedIn' | 'loggedOut';
expiresAt: number;
}
export class CloudlyAuthManager {
@ -27,18 +28,22 @@ export class CloudlyAuthManager {
this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter);
}
public async createNewSecureToken() {
return plugins.smartunique.uniSimple('secureToken', 64);
}
public async start() {
// lets setup the smartjwtInstance
this.smartjwtInstance = new plugins.smartjwt.SmartJwt();
await this.smartjwtInstance.init();
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) {
await this.smartjwtInstance.createNewKeyPair();
const newJwtKeys = this.smartjwtInstance.getKeyPairAsJson();
await kvStore.writeKey('jwtKeys', newJwtKeys);
await kvStore.writeKey('jwtKeypair', newJwtKeys);
} else {
this.smartjwtInstance.setKeyPairAsJson(existingJwtKeys);
}
@ -48,18 +53,28 @@ export class CloudlyAuthManager {
'adminLoginWithUsernameAndPassword',
async (dataArg) => {
let jwt: string;
let expiresAtTimestamp: number = Date.now() + 3600 * 1000 * 24 * 7;
const user = await User.findUserByUsernameAndPassword(dataArg.username, dataArg.password);
if (!user) {
logger.log('warn', 'login failed');
throw new plugins.typedrequest.TypedResponseError('login failed');
} else {
jwt = await this.smartjwtInstance.createJWT({
userId: user.id,
status: 'loggedIn',
expiresAt: expiresAtTimestamp,
});
logger.log('success', 'login successful');
}
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 adminJwtGuard = new plugins.smartguard.Guard<{jwt: string}>(async (dataArg) => {
const jwt = dataArg.jwt;
public validIdentityGuard = new plugins.smartguard.Guard<{identity: plugins.servezoneInterfaces.data.IIdentity}>(async (dataArg) => {
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 user = await this.CUser.getInstance({id: jwtData.userId});
const isAdminBool = user.data.role === 'admin';
console.log(`user is admin: ${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,
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) {
return await User.getInstance({
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
@plugins.smartdata.unI()
public id: string;
@plugins.smartdata.svDb()
public data: {
role: 'admin' | 'user';
username: string;
password: string;
};
public data: plugins.servezoneInterfaces.data.IUser['data'];
}

View File

@ -1,5 +1,5 @@
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 { Cluster } from './classes.cluster.js';
import { data } from '@serve.zone/interfaces';
export class ClusterManager {
public ready = plugins.smartpromise.defer();
@ -22,12 +23,12 @@ export class ClusterManager {
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.cluster.IRequest_CreateCluster>(
new plugins.typedrequest.TypedHandler('createCluster', async (dataArg) => {
const cluster = await this.storeCluster({
// TODO: guards
const cluster = await this.createCluster({
id: plugins.smartunique.uniSimple('cluster'),
data: {
userId: null, // this is created by the createCluster method
name: dataArg.clusterName,
jumpCode: plugins.smartunique.uniSimple('cluster'),
jumpCodeUsedAt: null,
acmeInfo: null,
cloudlyUrl: `https://${this.cloudlyRef.config.data.publicUrl}:${this.cloudlyRef.config.data.publicPort}/`,
servers: [],
@ -57,7 +58,7 @@ 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 toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], reqDataArg);
await this.deleteCluster(reqDataArg.clusterId);
return {
success: true,
@ -80,25 +81,22 @@ export class ClusterManager {
// TODO: implement getclusterConfigByServerIp
}
public async getClusterConfigBy_JumpCode(jumpCodeArg: string) {
public async getClusterBy_UserId(userIdArg: string) {
await this.ready.promise;
return await Cluster.getInstance({
data: {
jumpCode: jumpCodeArg,
userId: userIdArg,
},
});
}
public async getClusterConfigBy_ClusterIdentifier(
clusterIdentifier: plugins.servezoneInterfaces.data.IClusterIdentifier
) {
public async getClusterBy_Identity(clusterIdentity: plugins.servezoneInterfaces.data.IIdentity) {
await this.ready.promise;
return await Cluster.getInstance({
id: clusterIdentifier.clusterId,
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 configObjectArg
*/
public async storeCluster(configObjectArg: plugins.servezoneInterfaces.data.ICluster) {
let clusterInstance = await Cluster.getInstance({ id: configObjectArg.id });
if (!clusterInstance) {
clusterInstance = await Cluster.fromConfigObject(configObjectArg);
} else {
Object.assign(clusterInstance, configObjectArg);
}
public async createCluster(configObjectArg: plugins.servezoneInterfaces.data.ICluster) {
// TODO: guards
// lets create the cluster user
const clusterUser = new this.cloudlyRef.authManager.CUser({
id: await this.cloudlyRef.authManager.CUser.getNewId(),
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();
return clusterInstance;
}

View File

@ -1,5 +1,6 @@
import * as plugins from '../plugins.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
@ -13,24 +14,42 @@ export class CloudlyCoreflowManager {
this.cloudlyRef = cloudlyRefArg;
this.cloudlyRef.typedrouter.addTypedRouter(this.typedRouter);
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByJumpCode>(
new plugins.typedrequest.TypedHandler('getIdentityByJumpCode', async (requestData) => {
const clusterConfig =
await this.cloudlyRef.clusterManager.getClusterConfigBy_JumpCode(
requestData.jumpCode
);
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByToken>(
new plugins.typedrequest.TypedHandler('getIdentityByToken', async (requestData) => {
const user = await this.cloudlyRef.authManager.CUser.getInstance({
data: {
tokens: [{
token: requestData.token,
}] // find the proper user here.
} as any
});
if (!clusterConfig) {
throw new plugins.typedrequest.TypedResponseError('The supplied jumpCode is not valid.');
if (!user) {
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 {
clusterIdentifier: {
clusterId: clusterConfig.id,
clusterName: clusterConfig.data.name,
identity: {
name: user.data.username,
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({
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>(
'getClusterConfig',
async (dataArg) => {
const clusterIdentifier = dataArg.clusterIdentifier;
const identity = dataArg.identity;
console.log('trying to get clusterConfigSet');
console.log(dataArg);
const clusterConfigSet =
await this.cloudlyRef.clusterManager.getClusterConfigBy_ClusterIdentifier(
clusterIdentifier
const cluster =
await this.cloudlyRef.clusterManager.getClusterBy_Identity(
identity
);
console.log('got cluster config and sending it back to coreflow');
return {
configData: await clusterConfigSet.createSavableObject(),
configData: await cluster.createSavableObject(),
deploymentDirectives: [],
};
}
@ -60,14 +79,14 @@ export class CloudlyCoreflowManager {
// lets enable getting of certificates
this.typedRouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.certificate.IRequest_Any_Cloudly_GetSslCertificate>(
'getSslCertificate',
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.certificate.IRequest_Any_Cloudly_GetCertificateForDomain>(
'getCertificateForDomain',
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(
dataArg.requiredCertName
dataArg.domainName
);
console.log(`got certificate ready for reponse ${dataArg.requiredCertName}`);
console.log(`got certificate ready for reponse ${dataArg.domainName}`);
return {
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) {
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 * as plugins from '../plugins.js';
import * as paths from '../paths.js';
import { Image } from './classes.image.js';
@ -9,14 +10,13 @@ export class ImageManager {
public smartbucketInstance: plugins.smartbucket.SmartBucket;
public imageDir: plugins.smartbucket.Directory;
public dockerImageStore: plugins.docker.DockerImageStore;
get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb;
}
public CImage = plugins.smartdata.setDefaultManagerForDoc(this, Image);
constructor(cloudlyRefArg: Cloudly) {
this.cloudlyRef = cloudlyRefArg;
@ -26,7 +26,7 @@ export class ImageManager {
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_CreateImage>(
'createImage',
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({
name: reqArg.name,
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(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_DeleteImage>(
'deleteImage',
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({
id: reqArg.imageId,
});
@ -57,7 +69,7 @@ export class ImageManager {
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_GetAllImages>(
'getAllImages',
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({});
return {
images: await Promise.all(
@ -74,14 +86,33 @@ export class ImageManager {
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_PushImageVersion>(
'pushImageVersion',
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,
});
if (!image) {
if (!refImage) {
throw new plugins.typedrequest.TypedResponseError('Image not found');
}
const imageVersion = reqArg.versionString;
console.log(
`got request to push image version ${imageVersion} for image ${refImage.data.name}`
);
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 {
allowed: true,
};
@ -94,12 +125,17 @@ export class ImageManager {
'pullImageVersion',
async (reqArg) => {
const image = await this.CImage.getInstance({
id: reqArg.imageId,
id: reqArg.imageId,
});
const imageVersion = image.data.versions.find((version) => version.versionString === reqArg.versionString);
const readable = this.imageDir.fastGetStream({
path: await image.getStoragePath(reqArg.versionString),
}, 'webstream');
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();
return {
imageStream: imageVirtualStream,
@ -110,6 +146,7 @@ export class ImageManager {
}
public async start() {
// lets setup s3
const s3Descriptor: plugins.tsclass.storage.IS3Descriptor =
await this.cloudlyRef.config.appData.waitForAndGetKey('s3Descriptor');
console.log(this.cloudlyRef.config.data.s3Descriptor);
@ -117,10 +154,17 @@ export class ImageManager {
this.cloudlyRef.config.data.s3Descriptor
);
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({
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>(
'adminGetConfigBundlesAndSecretGroups',
async (dataArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminJwtGuard], dataArg);
dataArg.jwt
await toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], dataArg);
dataArg.identity.jwt
const secretBundles = await SecretBundle.getInstances({});
const secretGroups = await SecretGroup.getInstances({});
return {

View File

@ -1,4 +1,5 @@
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> {

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 nogitDir = plugins.path.join(packageDir, '.nogit/');
export const dockerImageStoreDir = plugins.path.join(nogitDir, './dockerimagestore/');
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 smartdelay from '@push.rocks/smartdelay';
import * as smartexit from '@push.rocks/smartexit';
import * as smartexpect from '@push.rocks/smartexpect';
import * as smartfile from '@push.rocks/smartfile';
import * as smartguard from '@push.rocks/smartguard';
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 smartrequest from '@push.rocks/smartrequest';
import * as smartssh from '@push.rocks/smartssh';
import * as smartstream from '@push.rocks/smartstream';
import * as smartstring from '@push.rocks/smartstring';
import * as smartunique from '@push.rocks/smartunique';
import * as taskbuffer from '@push.rocks/taskbuffer';
@ -57,6 +59,7 @@ export {
smartclickhouse,
smartdata,
smartexit,
smartexpect,
smartdelay,
smartfile,
smartguard,
@ -67,6 +70,7 @@ export {
smartpromise,
smartrequest,
smartssh,
smartstream,
smartstring,
smartunique,
taskbuffer,

View File

@ -55,7 +55,7 @@ export class CloudlyApiClient {
this.cloudlyUrl
);
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();
}
public identity: plugins.servezoneInterfaces.data.IClusterIdentifier;
public async getIdentityByJumpCode(
jumpCodeArg: string,
tagConnection = false,
statefullIdentity = true
): Promise<plugins.servezoneInterfaces.data.IClusterIdentifier> {
public identity: plugins.servezoneInterfaces.data.IIdentity;
public async getIdentityByToken(
token: string,
optionsArg?: {
tagConnection?: boolean;
statefullIdentity?: boolean;
}
): Promise<plugins.servezoneInterfaces.data.IIdentity> {
optionsArg = Object.assign({}, {
tagConnection: false,
statefullIdentity: true,
}, optionsArg);
const identityRequest =
this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByJumpCode>(
'getIdentityByJumpCode'
this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByToken>(
'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({
jumpCode: jumpCodeArg,
token: token,
});
console.log('got identity response');
const identity = response.clusterIdentifier;
const identity = response.identity;
if (tagConnection) {
if (optionsArg.tagConnection) {
this.typedsocketClient.addTag('identity', identity);
}
if (statefullIdentity) {
if (optionsArg.statefullIdentity) {
this.identity = identity;
}
return identity;
}
/**
* will use statefull identity by default
*/
public async getClusterConfigFromCloudlyByIdentity(
identityArg: plugins.servezoneInterfaces.data.IClusterIdentifier
identityArg: plugins.servezoneInterfaces.data.IIdentity = this.identity
): Promise<plugins.servezoneInterfaces.data.ICluster> {
const clusterConfigRequest =
this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.config.IRequest_Any_Cloudly_GetClusterConfig>(
'getClusterConfig'
);
const response = await clusterConfigRequest.fire({
jwt: '',
clusterIdentifier: identityArg,
identity: identityArg,
});
return response.configData;
}
/**
* will use statefull identity by default
*/
public async getServerConfigFromCloudlyByIdentity(
identityArg: plugins.servezoneInterfaces.data.IClusterIdentifier
identityArg: plugins.servezoneInterfaces.data.IIdentity = this.identity
): Promise<plugins.servezoneInterfaces.data.IServer> {
const serverConfigRequest =
this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.config.IRequest_Any_Cloudly_GetServerConfig>(
'getServerConfig'
);
const response = await serverConfigRequest.fire({
jwt: '', // TODO: do proper auth here
identity: identityArg,
serverId: '', // TODO: get server id here
});
return response.configData;
@ -121,25 +133,35 @@ export class CloudlyApiClient {
/**
* gets a certificate for a domain used by a service
* @param serviceNameArg
* @param domainNameArg
*/
public async getCertificateForDomainOverHttps(
domainNameArg: string
): Promise<plugins.tsclass.network.ICert> {
public async getCertificateForDomain(optionsArg: {
domainName: string;
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 =
this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.certificate.IRequest_Any_Cloudly_GetSslCertificate>(
'getSslCertificate'
this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.certificate.IRequest_Any_Cloudly_GetCertificateForDomain>(
'getCertificateForDomain'
);
const typedResponse = await typedCertificateRequest.fire({
authToken: '', // do proper auth here
requiredCertName: domainNameArg,
identity: this.identity, // do proper auth here
domainName: optionsArg.domainName,
type: optionsArg.type,
});
return typedResponse.certificate;
}
// Images
public async getImages() {
return Image.getImages(this);
public images = {
// Images
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'
);
const response = await getAllImagesTR.fire({
jwt: cloudlyClientRef.identity.jwt,
identity: cloudlyClientRef.identity,
});
const resultImages: Image[] = [];
for (const image of response.images) {
@ -18,6 +18,23 @@ export class Image implements plugins.servezoneInterfaces.data.IImage {
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
cloudlyClientRef: CloudlyApiClient;
@ -32,11 +49,11 @@ export class Image implements plugins.servezoneInterfaces.data.IImage {
* updates the image data
*/
public async update() {
const getVersionsTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_GetImageMetadata>(
'getImageMetadata'
const getVersionsTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_GetImage>(
'getImage'
);
const response = await getVersionsTR.fire({
jwt: this.cloudlyClientRef.identity.jwt,
identity: this.cloudlyClientRef.identity,
imageId: this.id,
});
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> {
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'
);
const virtualStream = new plugins.typedrequest.VirtualStream();
const response = await pullImageTR.fire({
jwt: this.cloudlyClientRef.identity.jwt,
const response = await pushImageTR.fire({
identity: this.cloudlyClientRef.identity,
imageId: this.id,
versionString: '',
imageStream: virtualStream,
});
await virtualStream.readFromWebstream(imageReadableArg);
await done.promise;
await this.update();
};
@ -72,7 +88,7 @@ export class Image implements plugins.servezoneInterfaces.data.IImage {
'pullImageVersion'
);
const response = await pullImageTR.fire({
jwt: this.cloudlyClientRef.identity.jwt,
identity: this.cloudlyClientRef.identity,
imageId: this.id,
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,3 @@
{
"name": "@serve.zone/cli"
}

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
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';
@ -23,13 +24,9 @@ export class CloudlyViewImages extends DeesElement {
public static styles = [
cssManager.defaultStyles,
shared.viewHostCss,
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 shared from '../elements/shared/index.js';
import {
DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewLogs extends DeesElement {
public static styles = [
cssManager.defaultStyles,
shared.viewHostCss,
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 shared from '../elements/shared/index.js';
import {
DeesElement,
@ -31,14 +32,8 @@ export class CloudlyViewMails extends DeesElement {
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`,
shared.viewHostCss,
css``
];
public render() {

View File

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

View File

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

View File

@ -21,6 +21,8 @@ export class CloudlySectionheading extends DeesElement {
h1 {
font-family: 'Cal Sans';
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';