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; let result;
try { try {
if (!GBConfigService.get('GBVM')) { if (GBConfigService.get('GBVM') !== false) {
return await (async () => { return await (async () => {
return await new Promise((resolve) => { return await new Promise((resolve) => {
sandbox['resolve'] = resolve; sandbox['resolve'] = resolve;

View file

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

View file

@ -113,7 +113,7 @@ export class GBCoreService implements IGBCoreService {
constructor() { constructor() {
this.adminService = new GBAdminService(this); this.adminService = new GBAdminService(this);
} }
public async ensureInstances(instances: IGBInstance[], bootInstance: any, core: IGBCoreService) {} public async ensureInstances(instances: IGBInstance[], bootInstance: any, core: IGBCoreService) { }
/** /**
* Gets database config and connect to storage. Currently two databases * Gets database config and connect to storage. Currently two databases
@ -121,60 +121,60 @@ export class GBCoreService implements IGBCoreService {
*/ */
public async initStorage(): Promise<any> { public async initStorage(): Promise<any> {
this.dialect = GBConfigService.get('STORAGE_DIALECT'); this.dialect = GBConfigService.get('STORAGE_DIALECT');
let port: number | undefined; let port: number | undefined;
let host: string | undefined; let host: string | undefined;
let database: string | undefined; let database: string | undefined;
let username: string | undefined; let username: string | undefined;
let password: string | undefined; let password: string | undefined;
let storage: string | undefined; let storage: string | undefined;
if (!['mssql', 'postgres', 'sqlite'].includes(this.dialect)) { if (!['mssql', 'postgres', 'sqlite'].includes(this.dialect)) {
throw new Error(`Unknown or unsupported dialect: ${this.dialect}.`); throw new Error(`Unknown or unsupported dialect: ${this.dialect}.`);
} }
if (this.dialect === 'mssql' || this.dialect === 'postgres') { if (this.dialect === 'mssql' || this.dialect === 'postgres') {
host = GBConfigService.get('STORAGE_SERVER'); host = GBConfigService.get('STORAGE_SERVER');
database = GBConfigService.get('STORAGE_NAME'); database = GBConfigService.get('STORAGE_NAME');
username = GBConfigService.get('STORAGE_USERNAME'); username = GBConfigService.get('STORAGE_USERNAME');
password = GBConfigService.get('STORAGE_PASSWORD'); password = GBConfigService.get('STORAGE_PASSWORD');
const portStr = GBConfigService.get('STORAGE_PORT'); const portStr = GBConfigService.get('STORAGE_PORT');
port = portStr ? parseInt(portStr, 10) : undefined; port = portStr ? parseInt(portStr, 10) : undefined;
if (!host || !database || !username || !password || !port) { if (!host || !database || !username || !password || !port) {
throw new Error(`Missing required configuration for ${this.dialect}.`); throw new Error(`Missing required configuration for ${this.dialect}.`);
} }
} else if (this.dialect === 'sqlite') { } else if (this.dialect === 'sqlite') {
storage = GBConfigService.get('STORAGE_FILE'); storage = GBConfigService.get('STORAGE_FILE');
if (!storage) { if (!storage) {
throw new Error('STORAGE_FILE is required for SQLite.'); throw new Error('STORAGE_FILE is required for SQLite.');
} }
if (!(await GBUtil.exists(storage))) { if (!(await GBUtil.exists(storage))) {
process.env.STORAGE_SYNC = 'true'; process.env.STORAGE_SYNC = 'true';
} }
} }
const logging: boolean | Function = const logging: boolean | Function =
GBConfigService.get('STORAGE_LOGGING') === 'true' GBConfigService.get('STORAGE_LOGGING') === 'true'
? (str: string): void => { ? (str: string): void => {
GBLogEx.info(0, str); GBLogEx.info(0, str);
} }
: false; : false;
const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true'; const encrypt: boolean = GBConfigService.get('STORAGE_ENCRYPT') === 'true';
const acquireStr = GBConfigService.get('STORAGE_ACQUIRE_TIMEOUT'); const acquireStr = GBConfigService.get('STORAGE_ACQUIRE_TIMEOUT');
const acquire = acquireStr ? parseInt(acquireStr, 10) : 10000; // Valor padrão de 10 segundos const acquire = acquireStr ? parseInt(acquireStr, 10) : 10000; // Valor padrão de 10 segundos
const sequelizeOptions: SequelizeOptions = { const sequelizeOptions: SequelizeOptions = {
define: { define: {
freezeTableName: true, freezeTableName: true,
@ -185,7 +185,7 @@ export class GBCoreService implements IGBCoreService {
logging: logging as boolean, logging: logging as boolean,
dialect: this.dialect as Dialect, dialect: this.dialect as Dialect,
storage: storage, storage: storage,
quoteIdentifiers: this.dialect === 'postgres', quoteIdentifiers: this.dialect === 'postgres',
dialectOptions: this.dialect === 'mssql' ? { dialectOptions: this.dialect === 'mssql' ? {
options: { options: {
trustServerCertificate: true, trustServerCertificate: true,
@ -200,11 +200,11 @@ export class GBCoreService implements IGBCoreService {
acquire: acquire, acquire: acquire,
}, },
}; };
this.sequelize = new Sequelize(database, username, password, sequelizeOptions); this.sequelize = new Sequelize(database, username, password, sequelizeOptions);
} }
/** /**
* Checks wheather storage is acessible or not and opens firewall * Checks wheather storage is acessible or not and opens firewall
* in case of any connection block. * in case of any connection block.
@ -338,7 +338,7 @@ STORAGE_SYNC_ALTER=true
ENDPOINT_UPDATE=true ENDPOINT_UPDATE=true
`; `;
await fs.writeFile('.env', env); await fs.writeFile('.env', env);
} }
/** /**
@ -548,7 +548,7 @@ await fs.writeFile('.env', env);
* Verifies that an complex global password has been specified * Verifies that an complex global password has been specified
* before starting the server. * before starting the server.
*/ */
public ensureAdminIsSecured() {} public ensureAdminIsSecured() { }
public async createBootInstance( public async createBootInstance(
core: GBCoreService, core: GBCoreService,
@ -703,27 +703,27 @@ await fs.writeFile('.env', env);
if (GBConfigService.get('GB_MODE') === 'legacy') { if (GBConfigService.get('GB_MODE') === 'legacy') {
// Handles calls for BASIC persistence on sheet files. // Handles calls for BASIC persistence on sheet files.
GBLog.info(`Defining Config.xlsx variable ${name}= '${value}'...`); GBLog.info(`Defining Config.xlsx variable ${name}= '${value}'...`);
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min); let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
const maxLines = 512; const maxLines = 512;
const file = 'Config.xlsx'; const file = 'Config.xlsx';
const packagePath = GBUtil.getGBAIPath(min.botId, `gbot`); const packagePath = GBUtil.getGBAIPath(min.botId, `gbot`);
let document = await new SystemKeywords().internalGetDocument(client, baseUrl, packagePath, file); let document = await new SystemKeywords().internalGetDocument(client, baseUrl, packagePath, file);
// Creates book session that will be discarded. // Creates book session that will be discarded.
let sheets = await client.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`).get(); let sheets = await client.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`).get();
// Get the current rows in column A // Get the current rows in column A
let results = await client let results = await client
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A1:A${maxLines}')`) .api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A1:A${maxLines}')`)
.get(); .get();
const rows = results.values; const rows = results.values;
let address = ''; let address = '';
let lastEmptyRow = -1; let lastEmptyRow = -1;
let isEdit = false; let isEdit = false;
// Loop through column A to find the row where name matches, or find the next empty row // Loop through column A to find the row where name matches, or find the next empty row
for (let i = 7; i <= rows.length; i++) { for (let i = 7; i <= rows.length; i++) {
let result = rows[i - 1][0]; let result = rows[i - 1][0];
@ -732,27 +732,27 @@ await fs.writeFile('.env', env);
isEdit = true; // We are in editing mode isEdit = true; // We are in editing mode
break; break;
} else if (!result && lastEmptyRow === -1) { } else if (!result && lastEmptyRow === -1) {
lastEmptyRow = i ; // Store the first empty row if no match is found lastEmptyRow = i; // Store the first empty row if no match is found
} }
} }
// If no match was found and there's an empty row, add a new entry // If no match was found and there's an empty row, add a new entry
if (!isEdit && lastEmptyRow !== -1) { if (!isEdit && lastEmptyRow !== -1) {
address = `A${lastEmptyRow}:B${lastEmptyRow}`; // Add new entry in columns A and B address = `A${lastEmptyRow}:B${lastEmptyRow}`; // Add new entry in columns A and B
} }
// Prepare the request body based on whether it's an edit or add operation // Prepare the request body based on whether it's an edit or add operation
let body = { values: isEdit ? [[value]] : [[name, value]] }; let body = { values: isEdit ? [[value]] : [[name, value]] };
// Update or add the new value in the found address // Update or add the new value in the found address
await client await client
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${address}')`) .api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${address}')`)
.patch(body); .patch(body);
} else { } else {
let packagePath = GBUtil.getGBAIPath(min.botId, `gbot`); let packagePath = GBUtil.getGBAIPath(min.botId, `gbot`);
const config = path.join(GBConfigService.get('STORAGE_LIBRARY'), packagePath, 'config.csv'); const config = path.join(GBConfigService.get('STORAGE_LIBRARY'), packagePath, 'config.csv');
const db = await csvdb(config, ['name', 'value'], ','); const db = await csvdb(config, ['name', 'value'], ',');
if (await db.get({ name: name })) { if (await db.get({ name: name })) {
await db.edit({ name: name }, { name, value }); await db.edit({ name: name }, { name, value });
@ -761,7 +761,7 @@ await fs.writeFile('.env', env);
} }
} }
} }
/** /**
* Get a dynamic param from instance. Dynamic params are defined in Config.xlsx * Get a dynamic param from instance. Dynamic params are defined in Config.xlsx
* and loaded into the work folder from comida command. * and loaded into the work folder from comida command.
@ -863,7 +863,7 @@ await fs.writeFile('.env', env);
public async ensureFolders(instances, deployer: GBDeployer) { public async ensureFolders(instances, deployer: GBDeployer) {
const storageMode = process.env.GB_MODE; const storageMode = process.env.GB_MODE;
let libraryPath = GBConfigService.get('STORAGE_LIBRARY'); let libraryPath = GBConfigService.get('STORAGE_LIBRARY');
if (storageMode === 'gbcluster') { if (storageMode === 'gbcluster') {
const minioClient = new Client({ const minioClient = new Client({
endPoint: process.env.DRIVE_SERVER, endPoint: process.env.DRIVE_SERVER,
@ -872,54 +872,58 @@ await fs.writeFile('.env', env);
accessKey: process.env.DRIVE_ACCESSKEY, accessKey: process.env.DRIVE_ACCESSKEY,
secretKey: process.env.DRIVE_SECRET, secretKey: process.env.DRIVE_SECRET,
}); });
await this.syncBotStorage(instances, 'default', deployer, libraryPath); await this.syncBotStorage(instances, 'default', deployer, libraryPath);
const bucketStream = await minioClient.listBuckets(); const bucketStream = await minioClient.listBuckets();
for await (const bucket of bucketStream) { for await (const bucket of bucketStream) {
if (bucket.name.endsWith('.gbai') && bucket.name.startsWith(process.env.DRIVE_ORG_PREFIX)) { if (bucket.name.endsWith('.gbai') && bucket.name.startsWith(process.env.DRIVE_ORG_PREFIX)) {
const botId = bucket.name.replace('.gbai', '').replace(process.env.DRIVE_ORG_PREFIX, ''); const botId = bucket.name.replace('.gbai', '').replace(process.env.DRIVE_ORG_PREFIX, '');
await this.syncBotStorage(instances, botId, deployer, libraryPath); await this.syncBotStorage(instances, botId, deployer, libraryPath);
} }
} }
} else { } else {
if (!(await GBUtil.exists(libraryPath))) { if (!(await GBUtil.exists(libraryPath))) {
mkdirp.sync(libraryPath); mkdirp.sync(libraryPath);
} }
await this.syncBotStorage(instances, 'default', deployer, libraryPath); await this.syncBotStorage(instances, 'default', deployer, libraryPath);
const files = await fs.readdir(libraryPath); const files = await fs.readdir(libraryPath);
await CollectionUtil.asyncForEach(files, async (file) => { await CollectionUtil.asyncForEach(files, async (file) => {
if (file.trim().toLowerCase() !== 'default.gbai' && file.charAt(0) !== '_') { if (file.trim().toLowerCase() !== 'default.gbai' && file.charAt(0) !== '_') {
let botId = file.replace(/\.gbai/, ''); let botId = file.replace(/\.gbai/, '');
await this.syncBotStorage(instances, botId, deployer, libraryPath); await this.syncBotStorage(instances, botId, deployer, libraryPath);
} }
}); });
} }
} }
private async syncBotStorage(instances: any, botId: any, deployer: GBDeployer, libraryPath: string) { private async syncBotStorage(instances: any, botId: any, deployer: GBDeployer, libraryPath: string) {
let instance = instances.find(p => p.botId.toLowerCase().trim() === botId.toLowerCase().trim()); let instance = instances.find(p => p.botId.toLowerCase().trim() === botId.toLowerCase().trim());
if (!instance) { if (process.env.GB_MODE === 'local') {
GBLog.info(`Importing package ${botId}.gbai...`); if (!instance) {
GBLog.info(`Importing package ${botId}.gbai...`);
// Creates a bot. // Creates a bot.
let mobile = null, let mobile = null,
email = null; email = null;
instance = await deployer.deployBlankBot(botId, mobile, email); instance = await deployer.deployBlankBot(botId, mobile, email);
instances.push(instance); instances.push(instance);
const gbaiPath = path.join(libraryPath, `${botId}.gbai`); const gbaiPath = path.join(libraryPath, `${botId}.gbai`);
if (!(await GBUtil.exists(gbaiPath))) { if (!(await GBUtil.exists(gbaiPath))) {
fs.mkdir(gbaiPath, { recursive: true }); fs.mkdir(gbaiPath, { recursive: true });
}
} }
} }
return instance; return instance;

View file

@ -49,7 +49,7 @@ import { GBServer } from '../../../src/app.js';
import { GBVMService } from '../../basic.gblib/services/GBVMService.js'; import { GBVMService } from '../../basic.gblib/services/GBVMService.js';
import Excel from 'exceljs'; import Excel from 'exceljs';
import asyncPromise from 'async-promises'; 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 { GBAdminService } from './../../admin.gbapp/services/GBAdminService.js';
import { AzureDeployerService } from './../../azuredeployer.gbapp/services/AzureDeployerService.js'; import { AzureDeployerService } from './../../azuredeployer.gbapp/services/AzureDeployerService.js';
import { KBService } from './../../kb.gbapp/services/KBService.js'; import { KBService } from './../../kb.gbapp/services/KBService.js';
@ -234,7 +234,7 @@ export class GBDeployer implements IGBDeployer {
const service = await AzureDeployerService.createInstance(this); const service = await AzureDeployerService.createInstance(this);
const application = await service.createApplication(accessToken, botId); const application = await service.createApplication(accessToken, botId);
// Fills new instance base information and get App secret. // Fills new instance base information and get App secret.
instance.marketplaceId = (application as any).appId; instance.marketplaceId = (application as any).appId;
@ -269,11 +269,24 @@ export class GBDeployer implements IGBDeployer {
* Verifies if bot exists on bot catalog. * Verifies if bot exists on bot catalog.
*/ */
public async botExists(botId: string): Promise<boolean> { public async botExists(botId: string): Promise<boolean> {
const service = await AzureDeployerService.createInstance(this);
return await service.botExists(botId); 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);
}
} }
/** /**
* Performs all tasks of deploying a new bot on the cloud. * Performs all tasks of deploying a new bot on the cloud.
*/ */
@ -469,13 +482,13 @@ export class GBDeployer implements IGBDeployer {
} else { } else {
return []; return [];
} }
await asyncPromise.eachSeries(rows, async (line: any) => { await asyncPromise.eachSeries(rows, async (line: any) => {
if (line && line.length > 0) { if (line && line.length > 0) {
const key = line[1]; const key = line[1];
let value = line[2]; let value = line[2];
if (key && value) { if (key && value) {
if (value.text) { value = value.text }; if (value.text) { value = value.text };
obj[key] = value; obj[key] = value;
@ -490,7 +503,7 @@ export class GBDeployer implements IGBDeployer {
/** /**
*/ */
public async downloadFolder( public async downloadFolder(
min: GBMinInstance, min: GBMinInstance,
localPath: string, localPath: string,
@ -499,7 +512,7 @@ export class GBDeployer implements IGBDeployer {
client = null client = null
): Promise<any> { ): Promise<any> {
const storageMode = process.env.GB_MODE; const storageMode = process.env.GB_MODE;
if (storageMode === 'gbcluster') { if (storageMode === 'gbcluster') {
const minioClient = new Client({ const minioClient = new Client({
endPoint: process.env.DRIVE_SERVER || 'localhost', endPoint: process.env.DRIVE_SERVER || 'localhost',
@ -508,31 +521,31 @@ export class GBDeployer implements IGBDeployer {
accessKey: process.env.DRIVE_ACCESSKEY, accessKey: process.env.DRIVE_ACCESSKEY,
secretKey: process.env.DRIVE_SECRET, 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))) { if (!(await GBUtil.exists(localPath))) {
await fs.mkdir(localPath, { recursive: true }); await fs.mkdir(localPath, { recursive: true });
} }
const objectsStream = minioClient.listObjects(bucketName, remotePath, true); const objectsStream = minioClient.listObjects(bucketName, remotePath, true);
for await (const obj of objectsStream) { for await (const obj of objectsStream) {
const itemPath = path.join(localPath, obj.name); const itemPath = path.join(localPath, obj.name);
if (obj.name.endsWith('/')) { if (obj.name.endsWith('/')) {
if (!(await GBUtil.exists(itemPath))) { if (!(await GBUtil.exists(itemPath))) {
await fs.mkdir(itemPath, { recursive: true }); await fs.mkdir(itemPath, { recursive: true });
} }
} else { } else {
let download = true; let download = true;
if (await GBUtil.exists(itemPath)) { if (await GBUtil.exists(itemPath)) {
const stats = await fs.stat(itemPath); const stats = await fs.stat(itemPath);
if (stats.mtime >= new Date(obj.lastModified)) { if (stats.mtime >= new Date(obj.lastModified)) {
download = false; download = false;
} }
} }
if (download) { if (download) {
await minioClient.fGetObject(bucketName, obj.name, itemPath); await minioClient.fGetObject(bucketName, obj.name, itemPath);
await fs.utimes(itemPath, new Date(), new Date(obj.lastModified)); await fs.utimes(itemPath, new Date(), new Date(obj.lastModified));
@ -542,42 +555,42 @@ export class GBDeployer implements IGBDeployer {
} else { } else {
if (!baseUrl) { if (!baseUrl) {
const { baseUrl, client } = await GBDeployer.internalGetDriveClient(min); const { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
remotePath = remotePath.replace(/\\/gi, '/'); remotePath = remotePath.replace(/\\/gi, '/');
const parts = remotePath.split('/'); const parts = remotePath.split('/');
let pathBase = localPath; let pathBase = localPath;
if (!(await GBUtil.exists(pathBase))) { if (!(await GBUtil.exists(pathBase))) {
await fs.mkdir(pathBase, { recursive: true }); await fs.mkdir(pathBase, { recursive: true });
} }
await CollectionUtil.asyncForEach(parts, async (item) => { await CollectionUtil.asyncForEach(parts, async (item) => {
pathBase = path.join(pathBase, item); pathBase = path.join(pathBase, item);
if (!(await GBUtil.exists(pathBase))) { if (!(await GBUtil.exists(pathBase))) {
await fs.mkdir(pathBase, { recursive: true }); await fs.mkdir(pathBase, { recursive: true });
} }
}); });
let packagePath = GBUtil.getGBAIPath(min.botId); let packagePath = GBUtil.getGBAIPath(min.botId);
packagePath = urlJoin(packagePath, remotePath); packagePath = urlJoin(packagePath, remotePath);
let url = `${baseUrl}/drive/root:/${packagePath}:/children`; let url = `${baseUrl}/drive/root:/${packagePath}:/children`;
let documents; let documents;
try { try {
const res = await client.api(url).get(); const res = await client.api(url).get();
documents = res.value; documents = res.value;
} catch (error) { } catch (error) {
GBLogEx.info(min, `Error downloading: ${error.toString()}`); GBLogEx.info(min, `Error downloading: ${error.toString()}`);
} }
if (documents === undefined || documents.length === 0) { if (documents === undefined || documents.length === 0) {
return null; return null;
} }
await CollectionUtil.asyncForEach(documents, async (item) => { await CollectionUtil.asyncForEach(documents, async (item) => {
const itemPath = path.join(localPath, remotePath, item.name); const itemPath = path.join(localPath, remotePath, item.name);
if (item.folder) { if (item.folder) {
if (!(await GBUtil.exists(itemPath))) { if (!(await GBUtil.exists(itemPath))) {
await fs.mkdir(itemPath, { recursive: true }); await fs.mkdir(itemPath, { recursive: true });
@ -586,17 +599,17 @@ export class GBDeployer implements IGBDeployer {
await this.downloadFolder(min, localPath, nextFolder); await this.downloadFolder(min, localPath, nextFolder);
} else { } else {
let download = true; let download = true;
if (await GBUtil.exists(itemPath)) { if (await GBUtil.exists(itemPath)) {
const stats = await fs.stat(itemPath); const stats = await fs.stat(itemPath);
if (new Date(stats.mtime) >= new Date(item.lastModifiedDateTime)) { if (new Date(stats.mtime) >= new Date(item.lastModifiedDateTime)) {
download = false; download = false;
} }
} }
if (download) { if (download) {
const url = item['@microsoft.graph.downloadUrl']; const url = item['@microsoft.graph.downloadUrl'];
const response = await fetch(url); const response = await fetch(url);
await fs.writeFile(itemPath, new Uint8Array(await response.arrayBuffer()), { encoding: null }); await fs.writeFile(itemPath, new Uint8Array(await response.arrayBuffer()), { encoding: null });
await fs.utimes(itemPath, new Date(), new Date(item.lastModifiedDateTime)); await fs.utimes(itemPath, new Date(), new Date(item.lastModifiedDateTime));
@ -605,7 +618,7 @@ export class GBDeployer implements IGBDeployer {
}); });
} }
} }
} }
/** /**
@ -657,7 +670,7 @@ export class GBDeployer implements IGBDeployer {
await this.cleanupPackage(min.instance, packageName); 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); const filePath = path.join(GBConfigService.get('STORAGE_LIBRARY'), gbai, packageName);
await GBUtil.copyIfNewerRecursive(filePath, packageWorkFolder); await GBUtil.copyIfNewerRecursive(filePath, packageWorkFolder);
} else { } else {
@ -714,7 +727,7 @@ export class GBDeployer implements IGBDeployer {
con['storageDriver'] = min.core.getParam<string>(min.instance, `${connectionName} Driver`, null); con['storageDriver'] = min.core.getParam<string>(min.instance, `${connectionName} Driver`, null);
con['storageTables'] = min.core.getParam<string>(min.instance, `${connectionName} Tables`, null); con['storageTables'] = min.core.getParam<string>(min.instance, `${connectionName} Tables`, null);
const storageName = min.core.getParam<string>(min.instance, `${connectionName} Name`, null); const storageName = min.core.getParam<string>(min.instance, `${connectionName} Name`, null);
let file = min.core.getParam<string>(min.instance, `${connectionName} File`, null); let file = min.core.getParam<string>(min.instance, `${connectionName} File`, null);
if (storageName) { if (storageName) {

View file

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

View file

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

View file

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

View file

@ -32,26 +32,27 @@
import { GBMinInstance, GBLog } from "botlib"; import { GBMinInstance, GBLog } from "botlib";
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
const MicrosoftGraph = require("@microsoft/microsoft-graph-client"); import MicrosoftGraph from "@microsoft/microsoft-graph-client";
const Juno = require('juno-payment-node');
const sgMail = require('@sendgrid/mail'); import sgMail from '@sendgrid/mail';
const PasswordGenerator = require('strict-password-generator').default; 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 { export class GBOService {
public isValidCardNumber(ccNumber) { public isValidCardNumber(ccNumber) {
let card = new Juno.Card();
return card.validateNumber(ccNumber);
} }
public isValidSecurityCode(ccNumber, cvcNumber) { public isValidSecurityCode(ccNumber, cvcNumber) {
let card = new Juno.Card();
return card.validateCvc(ccNumber, cvcNumber);
} }
public isValidExpireDate(month, year) { public isValidExpireDate(month, year) {
let card = new Juno.Card();
return card.validateExpireDate(month, year);
} }
public async sendEmail(token: string, to: string, from: string, public async sendEmail(token: string, to: string, from: string,
@ -77,137 +78,147 @@ 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) { 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;
let templateLibraryId = process.env.SAAS_TEMPLATE_LIBRARY; let token =
let siteId = process.env.STORAGE_SITE_ID; await (min.adminService as any).acquireElevatedToken(min.instance.instanceId, true);
let token = let client = MicrosoftGraph.Client.init({
await (min.adminService as any).acquireElevatedToken(min.instance.instanceId, true); authProvider: done => {
done(null, token);
let client = MicrosoftGraph.Client.init({ }
authProvider: done => { });
done(null, token); const packagePath = `/`;
} let res = await client.api(
}); `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${templateLibraryId}/drive/root/children`)
const packagePath = `/`;
let res = await client.api(
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${templateLibraryId}/drive/root/children`)
.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(); .get();
return await client.api( return res.value;
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${templateLibraryId}/drive/items/${src.id}/copy`) }
.post(body); else {
} catch (error) { const templatesDir = path.join(process.env.PWD, 'templates');
const gbaiDirectories = [];
if (error.code === "itemNotFound") { // Read all entries in the templates directory
const entries = await fs.readdir(templatesDir, { withFileTypes: true });
} else if (error.code === "nameAlreadyExists") { 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;
let src = await client.api( if (storageMode === 'legacy') {
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${templateLibraryId}/drive/root:/${templateName}/${packageName}:/children`) // Microsoft Graph (SharePoint) Implementation
.get(); const token = await (min.adminService as any).acquireElevatedToken(min.instance.instanceId, true);
const dstName = `${botName}.gbai/${botName}.${kind}`; const siteId = process.env.STORAGE_SITE_ID;
let dst = await client.api(`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/root:/${dstName}`) const templateLibraryId = process.env.SAAS_TEMPLATE_LIBRARY;
.get(); const libraryId = process.env.STORAGE_LIBRARY;
await CollectionUtil.asyncForEach(src.value, async item => { const client = MicrosoftGraph.Client.init({
authProvider: (done) => done(null, token)
});
const body = const packageName = `${templateName.split('.')[0]}.${kind}`;
{ const destinationName = `${botName}.${kind}`;
"parentReference": { driveId: dst.parentReference.driveId, id: dst.id }
} try {
await client.api( // Try direct copy first
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${templateLibraryId}/drive/items/${item.id}/copy`) const src = await client.api(
.post(body); `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 { }
GBLog.error(error); else if (storageMode === 'gbcluster') {
throw error;
} // 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);
} }
} }
@ -351,4 +362,56 @@ export class GBOService {
return documents[0]; 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 { GBMinInstance, GBLog } from 'botlib';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
import urlJoin from 'url-join'; import urlJoin from 'url-join';
import { GBOService } from './GBOService.js';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
export class MainService { export class MainService {
async createSubscriptionMSFT(email: string, plan: string, offer: string, quantity: number, additionalData: any) { } async createSubscriptionMSFT(email: string, plan: string, offer: string, quantity: number, additionalData: any) { }
@ -104,12 +106,15 @@ export class MainService {
subscription.externalSubscriptionId = externalSubscriptionId; subscription.externalSubscriptionId = externalSubscriptionId;
await subscription.save(); 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 siteId = process.env.STORAGE_SITE_ID;
let libraryId = process.env.STORAGE_LIBRARY; let libraryId = process.env.STORAGE_LIBRARY;
let gboService = min.gbappServices['gboService']; let gboService = new GBOService();
let sleep = ms => { let sleep = ms => {
return new Promise(resolve => { return new Promise(resolve => {
setTimeout(resolve, ms); setTimeout(resolve, ms);
@ -118,15 +123,15 @@ export class MainService {
GBLog.info( 'Creating .gbai folder ...'); GBLog.info( 'Creating .gbai folder ...');
let item = await gboService.createSubFolderAtRoot(token, `${botName}.gbai`, siteId, libraryId); let item = await gboService.createRootFolder(token, `${botName}.gbai`, siteId, libraryId);
await sleep(1000);
GBLog.info( 'Copying Templates...'); GBLog.info( 'Copying Templates...');
await gboService.copyTemplates(min, item, 'Shared.gbai', 'gbkb', 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', 'gbot', botName);
await gboService.copyTemplates(min, item, 'Shared.gbai', 'gbtheme', 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', 'gbdialog', botName);
await gboService.copyTemplates(min, item, 'Shared.gbai', 'gbdata', botName); // await gboService.copyTemplates(min, item, 'shared.gbai', 'gbdata', botName);
await gboService.copyTemplates(min, item, templateName, 'gbkb', botName); await gboService.copyTemplates(min, item, templateName, 'gbkb', botName);
await gboService.copyTemplates(min, item, templateName, 'gbot', botName); await gboService.copyTemplates(min, item, templateName, 'gbot', botName);
await gboService.copyTemplates(min, item, templateName, 'gbtheme', botName); await gboService.copyTemplates(min, item, templateName, 'gbtheme', botName);