Compare commits

...

2 commits

Author SHA1 Message Date
Rodrigo Rodriguez (Pragmatismo)
ba750a289f fix(services): refactor GBOService instantiation and update template listing logic
Some checks failed
GBCI / build (push) Has been cancelled
2025-03-29 20:27:22 -03:00
Rodrigo Rodriguez (Pragmatismo)
0e74502cc1 fix(services): refactor GBOService instantiation and update template listing logic 2025-03-29 11:03:46 -03:00
9 changed files with 322 additions and 234 deletions

View file

@ -1028,7 +1028,7 @@ export class GBVMService extends GBService {
let result;
try {
if (!GBConfigService.get('GBVM')) {
if (GBConfigService.get('GBVM') !== false) {
return await (async () => {
return await new Promise((resolve) => {
sandbox['resolve'] = resolve;

View file

@ -86,7 +86,7 @@ export class GBConfigService {
value = this.getServerPort();
break;
case 'GBVM':
value = false;
value = true;
break;
case 'STORAGE_NAME':
value = null;

View file

@ -904,8 +904,11 @@ await fs.writeFile('.env', env);
}
private async syncBotStorage(instances: any, botId: any, deployer: GBDeployer, libraryPath: string) {
let instance = instances.find(p => p.botId.toLowerCase().trim() === botId.toLowerCase().trim());
if (process.env.GB_MODE === 'local') {
if (!instance) {
GBLog.info(`Importing package ${botId}.gbai...`);
@ -922,6 +925,7 @@ await fs.writeFile('.env', env);
fs.mkdir(gbaiPath, { recursive: true });
}
}
}
return instance;
}

View file

@ -49,7 +49,7 @@ import { GBServer } from '../../../src/app.js';
import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
import Excel from 'exceljs';
import asyncPromise from 'async-promises';
import { GuaribasPackage } from '../models/GBModel.js';
import { GuaribasInstance, GuaribasPackage } from '../models/GBModel.js';
import { GBAdminService } from './../../admin.gbapp/services/GBAdminService.js';
import { AzureDeployerService } from './../../azuredeployer.gbapp/services/AzureDeployerService.js';
import { KBService } from './../../kb.gbapp/services/KBService.js';
@ -269,9 +269,22 @@ export class GBDeployer implements IGBDeployer {
* Verifies if bot exists on bot catalog.
*/
public async botExists(botId: string): Promise<boolean> {
if (GBConfigService.get('GB_MODE') !== 'legacy') {
const where = { botId: botId };
return await GuaribasInstance.findOne({
where: where
}) !== null;
}
else {
const service = await AzureDeployerService.createInstance(this);
return await service.botExists(botId);
}
}
/**
@ -509,7 +522,7 @@ export class GBDeployer implements IGBDeployer {
secretKey: process.env.DRIVE_SECRET,
});
const bucketName = process.env.DRIVE_BUCKETPREFIX + min.botId + '.gbai';
const bucketName = process.env.DRIVE_ORG_PREFIX + min.botId + '.gbai';
if (!(await GBUtil.exists(localPath))) {
await fs.mkdir(localPath, { recursive: true });
@ -657,7 +670,7 @@ export class GBDeployer implements IGBDeployer {
await this.cleanupPackage(min.instance, packageName);
}
if (GBConfigService.get('GB_MODE') !== 'legacy') {
if (GBConfigService.get('GB_MODE') === 'local') {
const filePath = path.join(GBConfigService.get('STORAGE_LIBRARY'), gbai, packageName);
await GBUtil.copyIfNewerRecursive(filePath, packageWorkFolder);
} else {

View file

@ -1269,7 +1269,7 @@ export class GBMinService {
if (GBConfigService.get('GB_MODE') !== 'legacy') {
const context = adapter['createContext'](req);
context['_activity'] = context.activity.body;
await handler(context);
await adapter['processActivity'](req, res, handler);
// Return status
res.status(200);

View file

@ -35,6 +35,7 @@ import { Messages } from '../strings.js';
import { MainService } from '../service/MainService.js';
import { SaaSPackage } from '../index.js';
import { CollectionUtil } from 'pragmatismo-io-framework';
import { GBOService } from '../service/GBOService.js';
export class NewUserDialog extends IGBDialog {
static getBotNameDialog(min: GBMinInstance) {
@ -83,7 +84,7 @@ export class NewUserDialog extends IGBDialog {
async step => {
const locale = 'en-US';
await step.context.sendActivity('Aqui estão alguns modelos para você escolher:');
let gboService = min.gbappServices['gboService'];
let gboService = new GBOService();
const list = await gboService.listTemplates(min);
let templateMessage = undefined;
@ -101,8 +102,9 @@ export class NewUserDialog extends IGBDialog {
async step => {
const list = step.activeDialog.state.options.templateList;
let template = null;
let gboService = new GBOService();
await CollectionUtil.asyncForEach(list, async item => {
let gboService = min.gbappServices['gboService'];
if (gboService.kmpSearch(step.context.activity.originalText, item.name) != -1) {
template = item.name;
}
@ -114,7 +116,7 @@ export class NewUserDialog extends IGBDialog {
return await step.replaceDialog('/welcome_saas_bottemplate', step.activeDialog.state.options);
} else {
step.activeDialog.state.options.templateName = template;
debugger;
await NewUserDialog.createBot(step, min, true);
return await step.replaceDialog('/ask', { isReturning: true });
@ -250,7 +252,7 @@ export class NewUserDialog extends IGBDialog {
'12',
'99',
'1234',
'crawlergbot.gbai',
'ai-search.gbai',
true
);

View file

@ -37,6 +37,7 @@ import { GBOnlineSubscription } from './model/MainModel.js'
import { MSSubscriptionService } from './service/MSSubscription.js'
import { CollectionUtil } from 'pragmatismo-io-framework';
import { NewUserDialog } from './dialog/NewUserDialog.js'
import { GBOService } from './service/GBOService.js'
export class SaaSPackage implements IGBPackage {
sysPackages: IGBPackage[]
@ -109,7 +110,7 @@ export class SaaSPackage implements IGBPackage {
async loadBot(min: GBMinInstance): Promise<void> {
let gboService = min.gbappServices['gboService'];
let gboService = new GBOService();
// Gets the sendToDevice method of whatsapp.gblib and setups scheduler.

View file

@ -32,26 +32,27 @@
import { GBMinInstance, GBLog } from "botlib";
import { CollectionUtil } from 'pragmatismo-io-framework';
const MicrosoftGraph = require("@microsoft/microsoft-graph-client");
const Juno = require('juno-payment-node');
const sgMail = require('@sendgrid/mail');
const PasswordGenerator = require('strict-password-generator').default;
import MicrosoftGraph from "@microsoft/microsoft-graph-client";
import sgMail from '@sendgrid/mail';
import { default as PasswordGenerator } from 'strict-password-generator';
import { promises as fs } from 'fs';
import { existsSync } from 'fs';
import { GBConfigService } from "../../core.gbapp/services/GBConfigService.js";
import path from "path";
import { Client } from "minio";
export class GBOService {
public isValidCardNumber(ccNumber) {
let card = new Juno.Card();
return card.validateNumber(ccNumber);
}
public isValidSecurityCode(ccNumber, cvcNumber) {
let card = new Juno.Card();
return card.validateCvc(ccNumber, cvcNumber);
}
public isValidExpireDate(month, year) {
let card = new Juno.Card();
return card.validateExpireDate(month, year);
}
public async sendEmail(token: string, to: string, from: string,
@ -77,57 +78,9 @@ export class GBOService {
});
}
public async createSubFolderAtRoot(token: string, name: string,
siteId: string, libraryId: string) {
return new Promise<any>((resolve, reject) => {
let client = MicrosoftGraph.Client.init({
authProvider: done => {
done(null, token);
}
});
const body = {
"name": name,
"folder": {},
"@microsoft.graph.conflictBehavior": "rename"
}
client.api(`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/root/children`)
.post(body, (err, res) => {
if (err) {
reject(err)
}
else {
resolve(res);
}
});
});
}
public async createSubFolderAt(token: string, parentPath: string, name: string,
siteId: string, libraryId: string) {
return new Promise<any>((resolve, reject) => {
let client = MicrosoftGraph.Client.init({
authProvider: done => {
done(null, token);
}
});
const body = {
"name": name,
"folder": {},
"@microsoft.graph.conflictBehavior": "rename"
}
client.api(`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/root:/${parentPath}:/children`)
.post(body, (err, res) => {
if (err) {
reject(err)
}
else {
resolve(res);
}
});
});
}
public async listTemplates(min: GBMinInstance) {
if (GBConfigService.get('GB_MODE') === 'legacy') {
let templateLibraryId = process.env.SAAS_TEMPLATE_LIBRARY;
let siteId = process.env.STORAGE_SITE_ID;
@ -145,71 +98,129 @@ export class GBOService {
.get();
return res.value;
}
public async copyTemplates(min: GBMinInstance, gbaiDest, templateName: string, kind: string, botName: string) {
let token =
await (min.adminService as any).acquireElevatedToken(min.instance.instanceId, true);
let siteId = process.env.STORAGE_SITE_ID;
let templateLibraryId = process.env.SAAS_TEMPLATE_LIBRARY;
let libraryId = process.env.STORAGE_LIBRARY;
let client = MicrosoftGraph.Client.init({
authProvider: done => {
done(null, token);
}
});
const body =
{
"parentReference": { driveId: gbaiDest.parentReference.driveId, id: gbaiDest.id },
"name": `${botName}.${kind}`
}
const packageName = `${templateName.split('.')[0]}.${kind}`;
try {
const src = await client.api(
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${templateLibraryId}/drive/root:/${templateName}/${packageName}`)
.get();
return await client.api(
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${templateLibraryId}/drive/items/${src.id}/copy`)
.post(body);
} catch (error) {
if (error.code === "itemNotFound") {
} else if (error.code === "nameAlreadyExists") {
let src = await client.api(
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${templateLibraryId}/drive/root:/${templateName}/${packageName}:/children`)
.get();
const dstName = `${botName}.gbai/${botName}.${kind}`;
let dst = await client.api(`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/root:/${dstName}`)
.get();
await CollectionUtil.asyncForEach(src.value, async item => {
const body =
{
"parentReference": { driveId: dst.parentReference.driveId, id: dst.id }
}
await client.api(
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${templateLibraryId}/drive/items/${item.id}/copy`)
.post(body);
});
}
else {
GBLog.error(error);
const templatesDir = path.join(process.env.PWD, 'templates');
const gbaiDirectories = [];
// Read all entries in the templates directory
const entries = await fs.readdir(templatesDir, { withFileTypes: true });
for (const entry of entries) {
// Check if it's a directory and ends with .gbai
if (entry.isDirectory() && entry.name.endsWith('.gbai')) {
gbaiDirectories.push({ name: entry.name });
}
}
return gbaiDirectories;
}
}
public async copyTemplates(min: GBMinInstance, gbaiDest: any, templateName: string, kind: string, botName: string): Promise<void> {
const storageMode = process.env.GB_MODE;
if (storageMode === 'legacy') {
// Microsoft Graph (SharePoint) Implementation
const token = await (min.adminService as any).acquireElevatedToken(min.instance.instanceId, true);
const siteId = process.env.STORAGE_SITE_ID;
const templateLibraryId = process.env.SAAS_TEMPLATE_LIBRARY;
const libraryId = process.env.STORAGE_LIBRARY;
const client = MicrosoftGraph.Client.init({
authProvider: (done) => done(null, token)
});
const packageName = `${templateName.split('.')[0]}.${kind}`;
const destinationName = `${botName}.${kind}`;
try {
// Try direct copy first
const src = await client.api(
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${templateLibraryId}/drive/root:/${templateName}/${packageName}`
).get();
await client.api(
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${templateLibraryId}/drive/items/${src.id}/copy`
).post({
parentReference: {
driveId: gbaiDest.parentReference.driveId,
id: gbaiDest.id
},
name: destinationName
});
} catch (error) {
if (error.code === "nameAlreadyExists") {
// Handle existing destination by copying contents individually
const srcItems = await client.api(
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${templateLibraryId}/drive/root:/${templateName}/${packageName}:/children`
).get();
const dstPath = `${botName}.gbai/${destinationName}`;
const dst = await client.api(
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/root:/${dstPath}`
).get();
await CollectionUtil.asyncForEach(srcItems.value, async (item) => {
await client.api(
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${templateLibraryId}/drive/items/${item.id}/copy`
).post({
parentReference: {
driveId: dst.parentReference.driveId,
id: dst.id
}
});
});
} else {
GBLog.error(`Failed to copy templates: ${error.message}`);
throw error;
}
}
}
else if (storageMode === 'gbcluster') {
// MinIO Implementation
const minioClient = new Client({
endPoint: process.env.DRIVE_SERVER,
port: parseInt(process.env.DRIVE_PORT),
useSSL: process.env.DRIVE_USE_SSL === 'true',
accessKey: process.env.DRIVE_ACCESSKEY,
secretKey: process.env.DRIVE_SECRET,
});
const bucketName = `${process.env.DRIVE_ORG_PREFIX}${botName}.gbai`;
const packageName = `${templateName.split('.')[0]}.${kind}`;
const localTemplatePath = path.join(process.env.PWD, 'templates', templateName, packageName);
const minioDestinationPath = `${botName}.${kind}`;
const uploadDirectory = async (localPath: string, minioPath: string = '') => {
// Ensure the bucket exists in local file system
if (existsSync(localPath)) {
const entries = await fs.readdir(localPath, { withFileTypes: true });
for (const entry of entries) {
const fullLocalPath = path.join(localPath, entry.name);
const objectName = path.posix.join(minioPath, entry.name);
if (entry.isDirectory()) {
await uploadDirectory(fullLocalPath, objectName);
} else {
const fileContent = await fs.readFile(fullLocalPath);
await minioClient.putObject(bucketName, objectName, fileContent);
GBLog.info(`Uploaded ${objectName} to MinIO bucket ${bucketName}`);
}
}
}
else {
GBLog.verbose(`Package ${localPath} does not exist on templates.`);
}
};
await uploadDirectory(localTemplatePath, minioDestinationPath);
}
}
public async createExcelFile(min: GBMinInstance, destinationFolder: any, name: string) {
@ -351,4 +362,56 @@ export class GBOService {
return documents[0];
}
public async createRootFolder(token: string, name: string,
siteId: string, libraryId: string) {
const storageMode = process.env.GB_MODE;
if (storageMode === 'gbcluster') {
// Minio implementation
const minioClient = new Client({
endPoint: process.env.DRIVE_SERVER,
port: parseInt(process.env.DRIVE_PORT),
useSSL: process.env.DRIVE_USE_SSL === 'true',
accessKey: process.env.DRIVE_ACCESSKEY,
secretKey: process.env.DRIVE_SECRET,
});
// Ensure bucket exists
name = `${process.env.DRIVE_ORG_PREFIX}${name}`;
const bucketExists = await minioClient.bucketExists(name);
if (!bucketExists) {
await minioClient.makeBucket(name);
}
return { name: name, folder: {} }; // Return similar structure to MS Graph
} else {
// Original MS Graph implementation
return new Promise<any>((resolve, reject) => {
let client = MicrosoftGraph.Client.init({
authProvider: done => {
done(null, token);
}
});
const body = {
"name": name,
"folder": {},
"@microsoft.graph.conflictBehavior": "rename"
}
client.api(`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/root/children`)
.post(body, (err, res) => {
if (err) {
reject(err)
}
else {
resolve(res);
}
});
});
}
}
}

View file

@ -34,6 +34,8 @@ import { GBOnlineSubscription } from '../model/MainModel.js';
import { GBMinInstance, GBLog } from 'botlib';
import { CollectionUtil } from 'pragmatismo-io-framework';
import urlJoin from 'url-join';
import { GBOService } from './GBOService.js';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
export class MainService {
async createSubscriptionMSFT(email: string, plan: string, offer: string, quantity: number, additionalData: any) { }
@ -104,11 +106,14 @@ export class MainService {
subscription.externalSubscriptionId = externalSubscriptionId;
await subscription.save();
let token = await (min.adminService.acquireElevatedToken as any)(min.instance.instanceId, true);
let token =
GBConfigService.get('GB_MODE') === 'legacy'?
await (min.adminService.acquireElevatedToken as any)(min.instance.instanceId, true) :
null;
let siteId = process.env.STORAGE_SITE_ID;
let libraryId = process.env.STORAGE_LIBRARY;
let gboService = min.gbappServices['gboService'];
let gboService = new GBOService();
let sleep = ms => {
return new Promise(resolve => {
@ -118,15 +123,15 @@ export class MainService {
GBLog.info( 'Creating .gbai folder ...');
let item = await gboService.createSubFolderAtRoot(token, `${botName}.gbai`, siteId, libraryId);
await sleep(1000);
let item = await gboService.createRootFolder(token, `${botName}.gbai`, siteId, libraryId);
GBLog.info( 'Copying Templates...');
await gboService.copyTemplates(min, item, 'Shared.gbai', 'gbkb', botName);
await gboService.copyTemplates(min, item, 'Shared.gbai', 'gbot', botName);
await gboService.copyTemplates(min, item, 'Shared.gbai', 'gbtheme', botName);
await gboService.copyTemplates(min, item, 'Shared.gbai', 'gbdialog', botName);
await gboService.copyTemplates(min, item, 'Shared.gbai', 'gbdata', botName);
// await gboService.copyTemplates(min, item, 'shared.gbai', 'gbkb', botName);
// await gboService.copyTemplates(min, item, 'shared.gbai', 'gbot', botName);
// await gboService.copyTemplates(min, item, 'shared.gbai', 'gbtheme', botName);
// await gboService.copyTemplates(min, item, 'shared.gbai', 'gbdialog', botName);
// await gboService.copyTemplates(min, item, 'shared.gbai', 'gbdata', botName);
await gboService.copyTemplates(min, item, templateName, 'gbkb', botName);
await gboService.copyTemplates(min, item, templateName, 'gbot', botName);
await gboService.copyTemplates(min, item, templateName, 'gbtheme', botName);