fix(image registry): start work on image registry
This commit is contained in:
parent
482a6a101c
commit
338ed5ed75
@ -26,10 +26,10 @@
|
|||||||
"@git.zone/tstest": "^1.0.90",
|
"@git.zone/tstest": "^1.0.90",
|
||||||
"@git.zone/tswatch": "^2.0.23",
|
"@git.zone/tswatch": "^2.0.23",
|
||||||
"@push.rocks/tapbundle": "^5.0.23",
|
"@push.rocks/tapbundle": "^5.0.23",
|
||||||
"@types/node": "^20.12.13"
|
"@types/node": "^20.12.14"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@api.global/typedrequest": "3.0.28",
|
"@api.global/typedrequest": "3.0.29",
|
||||||
"@api.global/typedserver": "^3.0.50",
|
"@api.global/typedserver": "^3.0.50",
|
||||||
"@api.global/typedsocket": "^3.0.1",
|
"@api.global/typedsocket": "^3.0.1",
|
||||||
"@apiclient.xyz/cloudflare": "^6.0.1",
|
"@apiclient.xyz/cloudflare": "^6.0.1",
|
||||||
@ -47,7 +47,7 @@
|
|||||||
"@push.rocks/smartacme": "^4.0.8",
|
"@push.rocks/smartacme": "^4.0.8",
|
||||||
"@push.rocks/smartbucket": "^3.0.9",
|
"@push.rocks/smartbucket": "^3.0.9",
|
||||||
"@push.rocks/smartcli": "^4.0.11",
|
"@push.rocks/smartcli": "^4.0.11",
|
||||||
"@push.rocks/smartdata": "^5.2.1",
|
"@push.rocks/smartdata": "^5.2.4",
|
||||||
"@push.rocks/smartdelay": "^3.0.5",
|
"@push.rocks/smartdelay": "^3.0.5",
|
||||||
"@push.rocks/smartexit": "^1.0.23",
|
"@push.rocks/smartexit": "^1.0.23",
|
||||||
"@push.rocks/smartfile": "^11.0.15",
|
"@push.rocks/smartfile": "^11.0.15",
|
||||||
@ -65,7 +65,7 @@
|
|||||||
"@push.rocks/smartunique": "^3.0.9",
|
"@push.rocks/smartunique": "^3.0.9",
|
||||||
"@push.rocks/taskbuffer": "^3.0.2",
|
"@push.rocks/taskbuffer": "^3.0.2",
|
||||||
"@push.rocks/webjwt": "^1.0.9",
|
"@push.rocks/webjwt": "^1.0.9",
|
||||||
"@serve.zone/interfaces": "^1.0.56",
|
"@serve.zone/interfaces": "^1.0.61",
|
||||||
"@tsclass/tsclass": "^4.0.54"
|
"@tsclass/tsclass": "^4.0.54"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
1248
pnpm-lock.yaml
generated
1248
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/cloudly',
|
name: '@serve.zone/cloudly',
|
||||||
version: '1.1.0',
|
version: '1.1.1',
|
||||||
description: 'A cloud manager leveraging Docker Swarmkit for multi-cloud operations including DigitalOcean, Hetzner Cloud, and Cloudflare, with integration support and robust configuration management system.'
|
description: 'A cloud manager leveraging Docker Swarmkit for multi-cloud operations including DigitalOcean, Hetzner Cloud, and Cloudflare, with integration support and robust configuration management system.'
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,6 @@ export class CloudlyConfig {
|
|||||||
public cloudlyRef: Cloudly;
|
public cloudlyRef: Cloudly;
|
||||||
public appData: plugins.npmextra.AppData<plugins.servezoneInterfaces.data.ICloudlyConfig>;
|
public appData: plugins.npmextra.AppData<plugins.servezoneInterfaces.data.ICloudlyConfig>;
|
||||||
public data: plugins.servezoneInterfaces.data.ICloudlyConfig
|
public data: plugins.servezoneInterfaces.data.ICloudlyConfig
|
||||||
|
|
||||||
// authentication and settings
|
|
||||||
public smartjwtInstance: plugins.smartjwt.SmartJwt;
|
|
||||||
|
|
||||||
|
|
||||||
constructor(cloudlyRefArg: Cloudly) {
|
constructor(cloudlyRefArg: Cloudly) {
|
||||||
|
@ -34,7 +34,10 @@ export class LetsencryptConnector {
|
|||||||
},
|
},
|
||||||
mongoDescriptor: this.cloudlyRef.config.data.mongoDescriptor,
|
mongoDescriptor: this.cloudlyRef.config.data.mongoDescriptor,
|
||||||
});
|
});
|
||||||
await this.smartacme.init();
|
await this.smartacme.init().catch(err => {
|
||||||
|
console.error('error in init', err);
|
||||||
|
console.log(`trying again in a few minutes`)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
13
ts/demo/demo.data.images.ts
Normal file
13
ts/demo/demo.data.images.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import * as plugins from '../plugins.js';
|
||||||
|
|
||||||
|
export const demoImages: plugins.servezoneInterfaces.data.IImage[] = [
|
||||||
|
{
|
||||||
|
id: 'DemoImage1',
|
||||||
|
data: {
|
||||||
|
name: 'DemoImage1',
|
||||||
|
description: 'DemoImage1',
|
||||||
|
versions: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
@ -1,9 +1,12 @@
|
|||||||
export const users = [
|
import * as plugins from '../plugins.js';
|
||||||
|
|
||||||
|
export const users: plugins.servezoneInterfaces.data.IUser[] = [
|
||||||
{
|
{
|
||||||
id: 'user1',
|
id: 'user1',
|
||||||
data: {
|
data: {
|
||||||
username: 'admin',
|
username: 'admin',
|
||||||
password: 'password',
|
password: 'password',
|
||||||
|
role: 'admin',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -48,4 +48,18 @@ export const installDemoData = async (cloudlyRef: Cloudly) => {
|
|||||||
Object.assign(userInstance, user);
|
Object.assign(userInstance, user);
|
||||||
await userInstance.save();
|
await userInstance.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================================================================================
|
||||||
|
// IMAGES
|
||||||
|
const images = await cloudlyRef.imageManager.CImage.getInstances({});
|
||||||
|
for (const image of images) {
|
||||||
|
await image.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
const demoDataImages = await import('./demo.data.images.js');
|
||||||
|
for (const image of demoDataImages.demoImages) {
|
||||||
|
const imageInstance = new cloudlyRef.imageManager.CImage();
|
||||||
|
Object.assign(imageInstance, image);
|
||||||
|
await imageInstance.save();
|
||||||
|
}
|
||||||
}
|
}
|
@ -30,6 +30,7 @@ export class CloudlyAuthManager {
|
|||||||
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();
|
||||||
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('jwtKeys');
|
||||||
@ -51,7 +52,7 @@ export class CloudlyAuthManager {
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
logger.log('warn', 'login failed');
|
logger.log('warn', 'login failed');
|
||||||
} else {
|
} else {
|
||||||
jwt = await this.cloudlyRef.config.smartjwtInstance.createJWT({
|
jwt = await this.smartjwtInstance.createJWT({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
status: 'loggedIn',
|
status: 'loggedIn',
|
||||||
});
|
});
|
||||||
@ -69,8 +70,12 @@ export class CloudlyAuthManager {
|
|||||||
|
|
||||||
public adminJwtGuard = new plugins.smartguard.Guard<{jwt: string}>(async (dataArg) => {
|
public adminJwtGuard = new plugins.smartguard.Guard<{jwt: string}>(async (dataArg) => {
|
||||||
const jwt = dataArg.jwt;
|
const jwt = dataArg.jwt;
|
||||||
const jwtData: IJwtData = await this.cloudlyRef.config.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});
|
||||||
return user.data.role === 'admin';
|
const isAdminBool = user.data.role === 'admin';
|
||||||
|
console.log(`user is admin: ${isAdminBool}`);
|
||||||
|
return isAdminBool;
|
||||||
|
}, {
|
||||||
|
failedHint: 'user is not admin.'
|
||||||
})
|
})
|
||||||
}
|
}
|
@ -1,13 +1,16 @@
|
|||||||
import * as plugins from '../plugins.js';
|
import * as plugins from '../plugins.js';
|
||||||
|
|
||||||
@plugins.smartdata.managed()
|
@plugins.smartdata.managed()
|
||||||
export class User extends plugins.smartdata.SmartDataDbDoc<User, User> {
|
export class User extends plugins.smartdata.SmartDataDbDoc<
|
||||||
|
User,
|
||||||
|
plugins.servezoneInterfaces.data.IUser
|
||||||
|
> {
|
||||||
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: {
|
||||||
username: usernameArg,
|
username: usernameArg,
|
||||||
password: passwordArg,
|
password: passwordArg,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,5 +23,5 @@ export class User extends plugins.smartdata.SmartDataDbDoc<User, User> {
|
|||||||
role: 'admin' | 'user';
|
role: 'admin' | 'user';
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,12 @@ export class CloudlyCoreflowManager {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
clusterIdentifier: {
|
clusterIdentifier: {
|
||||||
|
clusterId: clusterConfig.id,
|
||||||
clusterName: clusterConfig.data.name,
|
clusterName: clusterConfig.data.name,
|
||||||
secretKey: clusterConfig.data.secretKey,
|
jwt: await this.cloudlyRef.authManager.smartjwtInstance.createJWT({
|
||||||
|
status: 'loggedIn',
|
||||||
|
userId: 'cluster:' + clusterConfig.id, // TODO: create real users for clusters
|
||||||
|
})
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@ -47,7 +51,8 @@ export class CloudlyCoreflowManager {
|
|||||||
);
|
);
|
||||||
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 clusterConfigSet.createSavableObject(),
|
||||||
|
deploymentDirectives: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
import * as plugins from '../plugins.js';
|
import * as plugins from '../plugins.js';
|
||||||
import type { ImageManager } from './classes.imagemanager.js';
|
import type { ImageManager } from './classes.imagemanager.js';
|
||||||
|
|
||||||
@plugins.smartdata.Manager()
|
@plugins.smartdata.managed()
|
||||||
export class Image extends plugins.smartdata.SmartDataDbDoc<Image, plugins.servezoneInterfaces.data.IImage, ImageManager> {
|
export class Image extends plugins.smartdata.SmartDataDbDoc<Image, plugins.servezoneInterfaces.data.IImage, ImageManager> {
|
||||||
public static async create(imageDataArg: Partial<plugins.servezoneInterfaces.data.IImage['data']>) {
|
public static async create(imageDataArg: Partial<plugins.servezoneInterfaces.data.IImage['data']>) {
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
image.id = plugins.smartunique.uni('image');
|
image.id = await this.getNewId();
|
||||||
Object.assign(image.data, imageDataArg);
|
console.log(imageDataArg);
|
||||||
|
Object.assign(image, {
|
||||||
|
data: {
|
||||||
|
name: imageDataArg.name,
|
||||||
|
description: imageDataArg.description,
|
||||||
|
versions: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log((Image as any).saveableProperties)
|
||||||
await image.save();
|
await image.save();
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
@ -5,19 +5,52 @@ import { Image } from './classes.image.js';
|
|||||||
|
|
||||||
export class ImageManager {
|
export class ImageManager {
|
||||||
cloudlyRef: Cloudly;
|
cloudlyRef: Cloudly;
|
||||||
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||||
|
public smartbucketInstance: plugins.smartbucket.SmartBucket;
|
||||||
|
|
||||||
get db() {
|
get db() {
|
||||||
return this.cloudlyRef.mongodbConnector.smartdataDb;
|
return this.cloudlyRef.mongodbConnector.smartdataDb;
|
||||||
}
|
}
|
||||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
||||||
|
|
||||||
public CImage = plugins.smartdata.setDefaultManagerForDoc(this, Image);
|
public CImage = plugins.smartdata.setDefaultManagerForDoc(this, Image);
|
||||||
|
|
||||||
smartbucketInstance: plugins.smartbucket.SmartBucket;
|
|
||||||
|
|
||||||
constructor(cloudlyRefArg: Cloudly) {
|
constructor(cloudlyRefArg: Cloudly) {
|
||||||
this.cloudlyRef = cloudlyRefArg;
|
this.cloudlyRef = cloudlyRefArg;
|
||||||
|
|
||||||
this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter);
|
this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter);
|
||||||
|
|
||||||
|
this.typedrouter.addTypedHandler(
|
||||||
|
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_CreateImage>(
|
||||||
|
'createImage',
|
||||||
|
async (reqArg, toolsArg) => {
|
||||||
|
await toolsArg.passGuards([this.cloudlyRef.authManager.adminJwtGuard], reqArg);
|
||||||
|
const image = await this.CImage.create({
|
||||||
|
name: reqArg.name,
|
||||||
|
description: reqArg.description,
|
||||||
|
versions: [],
|
||||||
|
});
|
||||||
|
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);
|
||||||
|
const image = await this.CImage.getInstance({
|
||||||
|
id: reqArg.imageId,
|
||||||
|
});
|
||||||
|
await image.delete();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
this.typedrouter.addTypedHandler(
|
this.typedrouter.addTypedHandler(
|
||||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_GetAllImages>(
|
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_GetAllImages>(
|
||||||
'getAllImages',
|
'getAllImages',
|
||||||
@ -36,22 +69,8 @@ export class ImageManager {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.typedrouter.addTypedHandler(
|
this.typedrouter.addTypedHandler(
|
||||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_CreateImage>(
|
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_PushImageVersion>(
|
||||||
'createImage',
|
'pushImageVersion',
|
||||||
async (reqArg) => {
|
|
||||||
const image = await this.CImage.create({
|
|
||||||
name: reqArg.name,
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
image: await image.createSavableObject(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.typedrouter.addTypedHandler(
|
|
||||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_PushImage>(
|
|
||||||
'pushImage',
|
|
||||||
async (reqArg) => {
|
async (reqArg) => {
|
||||||
const pushStream = reqArg.imageStream;
|
const pushStream = reqArg.imageStream;
|
||||||
return {};
|
return {};
|
||||||
@ -60,8 +79,8 @@ export class ImageManager {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.typedrouter.addTypedHandler(
|
this.typedrouter.addTypedHandler(
|
||||||
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_PullImage>(
|
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_PullImageVersion>(
|
||||||
'pullImage',
|
'pullImageVersion',
|
||||||
async (reqArg) => {
|
async (reqArg) => {
|
||||||
const image = await this.CImage.getInstance({
|
const image = await this.CImage.getInstance({
|
||||||
data: {
|
data: {
|
||||||
|
@ -38,7 +38,8 @@ export class CloudlySecretManager {
|
|||||||
this.typedrouter.addTypedHandler(
|
this.typedrouter.addTypedHandler(
|
||||||
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) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await toolsArg.passGuards([this.cloudlyRef.authManager.adminJwtGuard], dataArg);
|
||||||
dataArg.jwt
|
dataArg.jwt
|
||||||
const secretBundles = await SecretBundle.getInstances({});
|
const secretBundles = await SecretBundle.getInstances({});
|
||||||
const secretGroups = await SecretGroup.getInstances({});
|
const secretGroups = await SecretGroup.getInstances({});
|
||||||
|
@ -91,7 +91,7 @@ export class CloudlyClient {
|
|||||||
'getClusterConfig'
|
'getClusterConfig'
|
||||||
);
|
);
|
||||||
const response = await clusterConfigRequest.fire({
|
const response = await clusterConfigRequest.fire({
|
||||||
jwt: '', // TODO: do proper auth here
|
jwt: '',
|
||||||
clusterIdentifier: identityArg,
|
clusterIdentifier: identityArg,
|
||||||
});
|
});
|
||||||
return response.configData;
|
return response.configData;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import * as plugins from './plugins.js';
|
import * as plugins from './plugins.js';
|
||||||
|
|
||||||
export class Image {
|
export class Image {
|
||||||
public getImages() {}
|
public getImages() {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
import * as plugins from './plugins.js';
|
||||||
|
|
||||||
|
export class Server {
|
||||||
|
public static getServers() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/cloudly',
|
name: '@serve.zone/cloudly',
|
||||||
version: '1.1.0',
|
version: '1.1.1',
|
||||||
description: 'A cloud manager leveraging Docker Swarmkit for multi-cloud operations including DigitalOcean, Hetzner Cloud, and Cloudflare, with integration support and robust configuration management system.'
|
description: 'A cloud manager leveraging Docker Swarmkit for multi-cloud operations including DigitalOcean, Hetzner Cloud, and Cloudflare, with integration support and robust configuration management system.'
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ export const dataState = await appstate.getStatePart<IDataState>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Getting data
|
// Getting data
|
||||||
export const getDataAction = dataState.createAction(async (statePartArg) => {
|
export const getAllDataAction = dataState.createAction(async (statePartArg, partialArg?: 'secrets' | 'images') => {
|
||||||
let currentState = statePartArg.getState();
|
let currentState = statePartArg.getState();
|
||||||
// Secrets
|
// Secrets
|
||||||
const trGetSecrets =
|
const trGetSecrets =
|
||||||
@ -88,6 +88,20 @@ export const getDataAction = dataState.createAction(async (statePartArg) => {
|
|||||||
...response,
|
...response,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// images
|
||||||
|
const trGetImages =
|
||||||
|
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.image.IRequest_GetAllImages>(
|
||||||
|
'/typedrequest',
|
||||||
|
'getAllImages'
|
||||||
|
);
|
||||||
|
const responseImages = await trGetImages.fire({
|
||||||
|
jwt: loginStatePart.getState().jwt,
|
||||||
|
});
|
||||||
|
currentState = {
|
||||||
|
...currentState,
|
||||||
|
...responseImages,
|
||||||
|
};
|
||||||
|
|
||||||
// Clusters
|
// Clusters
|
||||||
const trGetClusters =
|
const trGetClusters =
|
||||||
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.cluster.IRequest_GetAllClusters>(
|
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.cluster.IRequest_GetAllClusters>(
|
||||||
@ -120,7 +134,7 @@ export const createSecretGroupAction = dataState.createAction(
|
|||||||
secretBundles: [],
|
secretBundles: [],
|
||||||
secretGroups: [payloadArg],
|
secretGroups: [payloadArg],
|
||||||
});
|
});
|
||||||
currentState = await dataState.dispatchAction(getDataAction, null);
|
currentState = await dataState.dispatchAction(getAllDataAction, null);
|
||||||
return currentState;
|
return currentState;
|
||||||
return currentState;
|
return currentState;
|
||||||
}
|
}
|
||||||
@ -139,7 +153,7 @@ export const deleteSecretGroupAction = dataState.createAction(
|
|||||||
secretBundleIds: [],
|
secretBundleIds: [],
|
||||||
secretGroupIds: [payloadArg.secretGroupId],
|
secretGroupIds: [payloadArg.secretGroupId],
|
||||||
});
|
});
|
||||||
currentState = await dataState.dispatchAction(getDataAction, null);
|
currentState = await dataState.dispatchAction(getAllDataAction, null);
|
||||||
return currentState;
|
return currentState;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -158,7 +172,53 @@ export const deleteSecretBundleAction = dataState.createAction(
|
|||||||
secretBundleIds: [payloadArg.configBundleId],
|
secretBundleIds: [payloadArg.configBundleId],
|
||||||
secretGroupIds: [],
|
secretGroupIds: [],
|
||||||
});
|
});
|
||||||
currentState = await dataState.dispatchAction(getDataAction, null);
|
currentState = await dataState.dispatchAction(getAllDataAction, null);
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// image actions
|
||||||
|
export const createImageAction = dataState.createAction(
|
||||||
|
async (statePartArg, payloadArg: { imageName: string, description: string }) => {
|
||||||
|
let currentState = statePartArg.getState();
|
||||||
|
const trCreateImage =
|
||||||
|
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.image.IRequest_CreateImage>(
|
||||||
|
'/typedrequest',
|
||||||
|
'createImage'
|
||||||
|
);
|
||||||
|
const response = await trCreateImage.fire({
|
||||||
|
jwt: loginStatePart.getState().jwt,
|
||||||
|
name: payloadArg.imageName,
|
||||||
|
description: payloadArg.description,
|
||||||
|
});
|
||||||
|
currentState = {
|
||||||
|
...currentState,
|
||||||
|
...{
|
||||||
|
images: [...currentState.images, response.image],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const deleteImageAction = dataState.createAction(
|
||||||
|
async (statePartArg, payloadArg: { imageId: string }) => {
|
||||||
|
let currentState = statePartArg.getState();
|
||||||
|
const trDeleteImage =
|
||||||
|
new domtools.plugins.typedrequest.TypedRequest<plugins.interfaces.requests.image.IRequest_DeleteImage>(
|
||||||
|
'/typedrequest',
|
||||||
|
'deleteImage'
|
||||||
|
);
|
||||||
|
const response = await trDeleteImage.fire({
|
||||||
|
jwt: loginStatePart.getState().jwt,
|
||||||
|
imageId: payloadArg.imageId,
|
||||||
|
});
|
||||||
|
currentState = {
|
||||||
|
...currentState,
|
||||||
|
...{
|
||||||
|
images: currentState.images.filter((image) => image.id !== payloadArg.imageId),
|
||||||
|
},
|
||||||
|
};
|
||||||
return currentState;
|
return currentState;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -42,6 +42,7 @@ export class CloudlyDashboard extends DeesElement {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
document.title = `cloudly v${commitinfo.version}`;
|
||||||
const subcription = appstate.dataState
|
const subcription = appstate.dataState
|
||||||
.select((stateArg) => stateArg)
|
.select((stateArg) => stateArg)
|
||||||
.subscribe((dataArg) => {
|
.subscribe((dataArg) => {
|
||||||
@ -148,7 +149,7 @@ export class CloudlyDashboard extends DeesElement {
|
|||||||
action: async () => {
|
action: async () => {
|
||||||
await plugins.deesCatalog.DeesModal.createAndShow({
|
await plugins.deesCatalog.DeesModal.createAndShow({
|
||||||
heading: 'About',
|
heading: 'About',
|
||||||
content: html`configvault ${commitinfo.version}`,
|
content: html`cloudly ${commitinfo.version}`,
|
||||||
menuOptions: [
|
menuOptions: [
|
||||||
{
|
{
|
||||||
name: 'close',
|
name: 'close',
|
||||||
@ -171,7 +172,7 @@ export class CloudlyDashboard extends DeesElement {
|
|||||||
if (loginState.jwt) {
|
if (loginState.jwt) {
|
||||||
this.jwt = loginState.jwt;
|
this.jwt = loginState.jwt;
|
||||||
await simpleLogin.switchToSlottedContent();
|
await simpleLogin.switchToSlottedContent();
|
||||||
await appstate.dataState.dispatchAction(appstate.getDataAction, null);
|
await appstate.dataState.dispatchAction(appstate.getAllDataAction, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +191,7 @@ export class CloudlyDashboard extends DeesElement {
|
|||||||
this.jwt = state.jwt;
|
this.jwt = state.jwt;
|
||||||
form.setStatus('success', 'Logged in!');
|
form.setStatus('success', 'Logged in!');
|
||||||
await simpleLogin.switchToSlottedContent();
|
await simpleLogin.switchToSlottedContent();
|
||||||
await appstate.dataState.dispatchAction(appstate.getDataAction, null);
|
await appstate.dataState.dispatchAction(appstate.getAllDataAction, null);
|
||||||
} else {
|
} else {
|
||||||
form.setStatus('error', 'Login failed!');
|
form.setStatus('error', 'Login failed!');
|
||||||
await domtools.convenience.smartdelay.delayFor(2000);
|
await domtools.convenience.smartdelay.delayFor(2000);
|
||||||
|
@ -37,35 +37,25 @@ export class CloudlyViewImages extends DeesElement {
|
|||||||
return html`
|
return html`
|
||||||
<cloudly-sectionheading>Images</cloudly-sectionheading>
|
<cloudly-sectionheading>Images</cloudly-sectionheading>
|
||||||
<dees-table
|
<dees-table
|
||||||
heading1="SecretGroups"
|
heading1="Images"
|
||||||
heading2="decoded in client"
|
heading2="an image is needed for running a service"
|
||||||
.data=${this.data.images}
|
.data=${this.data.images}
|
||||||
.displayFunction=${(secretGroup: plugins.interfaces.data.ISecretGroup) => {
|
.displayFunction=${(image: plugins.interfaces.data.IImage) => {
|
||||||
return {
|
return {
|
||||||
name: secretGroup.data.name,
|
id: image.id,
|
||||||
priority: secretGroup.data.priority,
|
name: image.data.name,
|
||||||
tags: html`<dees-chips
|
description: image.data.description,
|
||||||
.selectionMode=${'none'}
|
versions: image.data.versions.length,
|
||||||
.selectableChips=${secretGroup.data.tags}
|
|
||||||
></dees-chips>`,
|
|
||||||
key: secretGroup.data.key,
|
|
||||||
history: (() => {
|
|
||||||
const allHistory = [];
|
|
||||||
for (const environment in secretGroup.data.environments) {
|
|
||||||
allHistory.push(...secretGroup.data.environments[environment].history);
|
|
||||||
}
|
|
||||||
return allHistory.length;
|
|
||||||
})(),
|
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
.dataActions=${[
|
.dataActions=${[
|
||||||
{
|
{
|
||||||
name: 'add SecretGroup',
|
name: 'create Image',
|
||||||
type: ['header', 'footer'],
|
type: ['header', 'footer'],
|
||||||
iconName: 'plus',
|
iconName: 'plus',
|
||||||
actionFunc: async () => {
|
actionFunc: async () => {
|
||||||
plugins.deesCatalog.DeesModal.createAndShow({
|
plugins.deesCatalog.DeesModal.createAndShow({
|
||||||
heading: 'create new SecretGroup',
|
heading: 'create new Image',
|
||||||
content: html`
|
content: html`
|
||||||
<dees-form>
|
<dees-form>
|
||||||
<dees-input-text
|
<dees-input-text
|
||||||
@ -78,50 +68,6 @@ export class CloudlyViewImages extends DeesElement {
|
|||||||
.key=${'data.description'}
|
.key=${'data.description'}
|
||||||
.value=${''}
|
.value=${''}
|
||||||
></dees-input-text>
|
></dees-input-text>
|
||||||
<dees-input-text
|
|
||||||
.label=${'Secret Key (data.key)'}
|
|
||||||
.key=${'data.key'}
|
|
||||||
.value=${''}
|
|
||||||
></dees-input-text>
|
|
||||||
<dees-table
|
|
||||||
.heading1=${'Environments'}
|
|
||||||
.heading2=${'keys need to be unique'}
|
|
||||||
key="environments"
|
|
||||||
.data=${[
|
|
||||||
{
|
|
||||||
environment: 'production',
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
environment: 'staging',
|
|
||||||
value: '',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
.dataActions=${[
|
|
||||||
{
|
|
||||||
name: 'add environment',
|
|
||||||
iconName: 'plus',
|
|
||||||
type: ['footer'],
|
|
||||||
actionFunc: async (dataArg) => {
|
|
||||||
dataArg.table.data.push({
|
|
||||||
environment: 'new environment',
|
|
||||||
value: '',
|
|
||||||
});
|
|
||||||
dataArg.table.requestUpdate('data');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'delete environment',
|
|
||||||
iconName: 'trash',
|
|
||||||
type: ['inRow'],
|
|
||||||
actionFunc: async (dataArg) => {
|
|
||||||
dataArg.table.data.splice(dataArg.table.data.indexOf(dataArg.item), 1);
|
|
||||||
dataArg.table.requestUpdate('data');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
] as plugins.deesCatalog.ITableAction[]}
|
|
||||||
.editableFields=${['environment', 'value']}
|
|
||||||
></dees-table>
|
|
||||||
</dees-form>
|
</dees-form>
|
||||||
`,
|
`,
|
||||||
menuOptions: [
|
menuOptions: [
|
||||||
@ -138,24 +84,9 @@ export class CloudlyViewImages extends DeesElement {
|
|||||||
const formData = await deesForm.collectFormData();
|
const formData = await deesForm.collectFormData();
|
||||||
console.log(`Prepare saving of data:`);
|
console.log(`Prepare saving of data:`);
|
||||||
console.log(formData);
|
console.log(formData);
|
||||||
const environments: plugins.interfaces.data.ISecretGroup['data']['environments'] =
|
await appstate.dataState.dispatchAction(appstate.createImageAction, {
|
||||||
{};
|
imageName: formData['data.name'] as string,
|
||||||
for (const itemArg of formData['environments'] as any[]) {
|
description: formData['data.description'] as string,
|
||||||
environments[itemArg.environment] = {
|
|
||||||
value: itemArg.value,
|
|
||||||
history: [],
|
|
||||||
lastUpdated: Date.now(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
await appstate.dataState.dispatchAction(appstate.createSecretGroupAction, {
|
|
||||||
id: null,
|
|
||||||
data: {
|
|
||||||
name: formData['data.name'] as string,
|
|
||||||
description: formData['data.description'] as string,
|
|
||||||
key: formData['data.key'] as string,
|
|
||||||
environments,
|
|
||||||
tags: [],
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
await modalArg.destroy();
|
await modalArg.destroy();
|
||||||
},
|
},
|
||||||
@ -327,16 +258,16 @@ export class CloudlyViewImages extends DeesElement {
|
|||||||
iconName: 'trash',
|
iconName: 'trash',
|
||||||
type: ['contextmenu', 'inRow'],
|
type: ['contextmenu', 'inRow'],
|
||||||
actionFunc: async (
|
actionFunc: async (
|
||||||
itemArg: plugins.deesCatalog.ITableActionDataArg<plugins.interfaces.data.ISecretGroup>
|
itemArg: plugins.deesCatalog.ITableActionDataArg<plugins.interfaces.data.IImage>
|
||||||
) => {
|
) => {
|
||||||
plugins.deesCatalog.DeesModal.createAndShow({
|
plugins.deesCatalog.DeesModal.createAndShow({
|
||||||
heading: `Delete ${itemArg.item.data.key}`,
|
heading: `Delete Image "${itemArg.item.data.name}"`,
|
||||||
content: html`
|
content: html`
|
||||||
<div style="text-align:center">Do you really want to delete the secret?</div>
|
<div style="text-align:center">Do you really want to delete the image?</div>
|
||||||
<div
|
<div
|
||||||
style="font-size: 0.8em; color: red; text-align:center; padding: 16px; margin-top: 24px; border: 1px solid #444; font-family: Intel One Mono; font-size: 16px;"
|
style="font-size: 0.8em; color: red; text-align:center; padding: 16px; margin-top: 24px; border: 1px solid #444; font-family: Intel One Mono; font-size: 16px;"
|
||||||
>
|
>
|
||||||
${itemArg.item.data.key}
|
${itemArg.item.id}
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
menuOptions: [
|
menuOptions: [
|
||||||
@ -350,8 +281,8 @@ export class CloudlyViewImages extends DeesElement {
|
|||||||
name: 'delete',
|
name: 'delete',
|
||||||
action: async (modalArg) => {
|
action: async (modalArg) => {
|
||||||
console.log(`Delete ${itemArg.item.id}`);
|
console.log(`Delete ${itemArg.item.id}`);
|
||||||
await appstate.dataState.dispatchAction(appstate.deleteSecretGroupAction, {
|
await appstate.dataState.dispatchAction(appstate.deleteImageAction, {
|
||||||
secretGroupId: itemArg.item.id,
|
imageId: itemArg.item.id,
|
||||||
});
|
});
|
||||||
await modalArg.destroy();
|
await modalArg.destroy();
|
||||||
},
|
},
|
||||||
|
@ -21,12 +21,12 @@ export class CloudlyViewSecretBundles extends DeesElement {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
const subecription = appstate.dataState
|
const subscription = appstate.dataState
|
||||||
.select((stateArg) => stateArg)
|
.select((stateArg) => stateArg)
|
||||||
.subscribe((dataArg) => {
|
.subscribe((dataArg) => {
|
||||||
this.data = dataArg;
|
this.data = dataArg;
|
||||||
});
|
});
|
||||||
this.rxSubscriptions.push(subecription);
|
this.rxSubscriptions.push(subscription);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static styles = [
|
public static styles = [
|
||||||
@ -144,7 +144,7 @@ export class CloudlyViewSecretBundles extends DeesElement {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'edit',
|
name: 'edit',
|
||||||
iconName: 'edit',
|
iconName: 'penToSquare',
|
||||||
type: ['doubleClick', 'contextmenu', 'inRow'],
|
type: ['doubleClick', 'contextmenu', 'inRow'],
|
||||||
actionFunc: async (actionDataArg) => {
|
actionFunc: async (actionDataArg) => {
|
||||||
const modal = await plugins.deesCatalog.DeesModal.createAndShow({
|
const modal = await plugins.deesCatalog.DeesModal.createAndShow({
|
||||||
|
Loading…
Reference in New Issue
Block a user