fix(big fix upgrade): upgrade multiple areas of the core functionalities

This commit is contained in:
2024-10-16 14:35:38 +02:00
parent d212dfb9f9
commit 53f96095c7
37 changed files with 2141 additions and 618 deletions

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/cloudly',
version: '1.1.7',
version: '1.1.8',
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

@ -28,6 +28,10 @@ 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();
@ -53,6 +57,7 @@ export class CloudlyAuthManager {
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,
@ -96,7 +101,7 @@ export class CloudlyAuthManager {
});
public adminIdentityGuard = new plugins.smartguard.Guard<{identity: plugins.servezoneInterfaces.data.IIdentity}>(async (dataArg) => {
await plugins.smartexpect.expectAsync(this.validIdentityGuard.exec(dataArg)).toBeTrue();
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});

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,6 +34,13 @@ 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;

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();
@ -26,10 +27,8 @@ export class ClusterManager {
const cluster = await this.createCluster({
id: plugins.smartunique.uniSimple('cluster'),
data: {
userId: null,
userId: null, // this is created by the createCluster method
name: dataArg.clusterName,
initialJumpToken: plugins.smartunique.uniSimple('initialJumpToken'),
initialJumpTokenUsedAt: null,
acmeInfo: null,
cloudlyUrl: `https://${this.cloudlyRef.config.data.publicUrl}:${this.cloudlyRef.config.data.publicPort}/`,
servers: [],
@ -82,19 +81,17 @@ export class ClusterManager {
// TODO: implement getclusterConfigByServerIp
}
public async getClusterBy_JumpCode(initialJumpTokenArg: string) {
public async getClusterBy_UserId(userIdArg: string) {
await this.ready.promise;
return await Cluster.getInstance({
data: {
initialJumpToken: initialJumpTokenArg,
userId: userIdArg,
},
});
}
public async getClusterBy_Identity(
clusterIdentity: plugins.servezoneInterfaces.data.IIdentity
) {
public async getClusterBy_Identity(clusterIdentity: plugins.servezoneInterfaces.data.IIdentity) {
await this.ready.promise;
return await Cluster.getInstance({
@ -125,19 +122,27 @@ 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 createCluster(configObjectArg: plugins.servezoneInterfaces.data.ICluster) {
// TODO: guards
// lets create the cluster user
const clusterUser = new this.cloudlyRef.authManager.CUser();
clusterUser.id = await this.cloudlyRef.authManager.CUser.getNewId();
clusterUser.data = {
role: 'cluster',
type: 'machine',
}
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,

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,38 +14,41 @@ 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 cluster =
await this.cloudlyRef.clusterManager.getClusterBy_JumpCode(
requestData.jumpCode
);
if (!cluster) {
throw new plugins.typedrequest.TypedResponseError('The supplied jumpCode is not valid. No cluster found.');
}
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({
id: cluster.data.userId,
data: {
tokens: [{
token: requestData.token,
}] // find the proper user here.
} as any
});
if (!user) {
throw new plugins.typedrequest.TypedResponseError('The supplied jumpCode is not valid. No user found.');
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 {
identity: {
name: cluster.data.name,
role: 'cluster',
type: 'machine',
userId: cluster.data.userId,
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,
clusterId: cluster.id,
clusterName: cluster.data.name,
...(cluster ? {
clusterId: cluster.id,
clusterName: cluster.data.name,
} : {}),
jwt: await this.cloudlyRef.authManager.smartjwtInstance.createJWT({
status: 'loggedIn',
userId: cluster.data.userId,
userId: user.id,
expiresAt: expiryTimestamp,
})
},

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;
@ -37,7 +37,19 @@ 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>(
@ -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

@ -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

@ -43,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';
@ -69,6 +70,7 @@ export {
smartpromise,
smartrequest,
smartssh,
smartstream,
smartstring,
smartunique,
taskbuffer,