new(all): TRUE multicloud.

This commit is contained in:
Rodrigo Rodriguez 2024-08-19 17:09:23 -03:00
parent 5880355349
commit 3ebf79c7b5
4 changed files with 89 additions and 127 deletions

View file

@ -1,5 +1,3 @@
/*****************************************************************************\ /*****************************************************************************\
| ® | | ® |
| | | |
@ -49,7 +47,7 @@ import { GBSharePointService } from '../../sharepoint.gblib/services/SharePointS
import { GuaribasAdmin } from '../models/AdminModel.js'; import { GuaribasAdmin } from '../models/AdminModel.js';
import msRestAzure from 'ms-rest-azure'; import msRestAzure from 'ms-rest-azure';
import Path from 'path'; import Path from 'path';
import { caseSensitive_Numbs_SpecialCharacters_PW, lowercase_PW } from 'super-strong-password-generator' import { caseSensitive_Numbs_SpecialCharacters_PW, lowercase_PW } from 'super-strong-password-generator';
import crypto from 'crypto'; import crypto from 'crypto';
import Fs from 'fs'; import Fs from 'fs';
import { GBServer } from '../../../src/app.js'; import { GBServer } from '../../../src/app.js';
@ -89,45 +87,40 @@ export class GBAdminService implements IGBAdminService {
} }
public static async getADALCredentialsFromUsername(username: string, password: string) { public static async getADALCredentialsFromUsername(username: string, password: string) {
return await msRestAzure.loginWithUsernamePassword(username, password); return await msRestAzure.loginWithUsernamePassword(username, password);
} }
public static getMobileCode() { public static getMobileCode() {
return this.getNumberIdentifier(6); return this.getNumberIdentifier(6);
} }
public static getRndPassword(): string { public static getRndPassword(): string {
let password = caseSensitive_Numbs_SpecialCharacters_PW(15); let password = caseSensitive_Numbs_SpecialCharacters_PW(15);
password = password.replace(/[\@\[\=\:\;\?\"\'\#]/gi, '*'); password = password.replace(/[\@\[\=\:\;\?\"\'\#]/gi, '*');
const removeRepeatedChars = (s, r) => { const removeRepeatedChars = (s, r) => {
let res = '', last = null, counter = 0; let res = '',
last = null,
counter = 0;
s.split('').forEach(char => { s.split('').forEach(char => {
if (char == last) if (char == last) counter++;
counter++;
else { else {
counter = 0; counter = 0;
last = char; last = char;
} }
if (counter < r) if (counter < r) res += char;
res += char;
}); });
return res; return res;
} };
return removeRepeatedChars(password, 1); return removeRepeatedChars(password, 1);
} }
public static getRndReadableIdentifier(): string { public static getRndReadableIdentifier(): string {
return lowercase_PW(14); return lowercase_PW(14);
} }
public static getNumberIdentifier(digits: number = 14): string { public static getNumberIdentifier(digits: number = 14): string {
if (digits <= 0) { if (digits <= 0) {
throw new Error('Number of digits should be greater than 0.'); throw new Error('Number of digits should be greater than 0.');
} }
@ -155,7 +148,6 @@ export class GBAdminService implements IGBAdminService {
} }
public static async undeployPackageCommand(text: string, min: GBMinInstance) { public static async undeployPackageCommand(text: string, min: GBMinInstance) {
const packageName = text.split(' ')[1]; const packageName = text.split(' ')[1];
const importer = new GBImporter(min.core); const importer = new GBImporter(min.core);
const deployer = new GBDeployer(min.core, importer); const deployer = new GBDeployer(min.core, importer);
@ -167,7 +159,12 @@ export class GBAdminService implements IGBAdminService {
public static isSharePointPath(path: string) { public static isSharePointPath(path: string) {
return path.indexOf('sharepoint.com') !== -1; return path.indexOf('sharepoint.com') !== -1;
} }
public static async deployPackageCommand(min: GBMinInstance, user: GuaribasUser, text: string, deployer: IGBDeployer) { public static async deployPackageCommand(
min: GBMinInstance,
user: GuaribasUser,
text: string,
deployer: IGBDeployer
) {
const packageName = text.split(' ')[1]; const packageName = text.split(' ')[1];
if (!this.isSharePointPath(packageName)) { if (!this.isSharePointPath(packageName)) {
@ -190,19 +187,21 @@ export class GBAdminService implements IGBAdminService {
await deployer['cleanupPackage'](min.instance, packageName); await deployer['cleanupPackage'](min.instance, packageName);
} }
await deployer['downloadFolder'](min, if (process.env.STORAGE_FILE) {
Path.join('work', `${gbai}`), const path = Path.join(process.env.STORAGE_LIBRARY, gbaiPath);
Path.basename(localFolder)); Fs.cpSync(path, localFolder, { errorOnExist: false, force: true, recursive: true});
} else {
await deployer['downloadFolder'](min, Path.join('work', `${gbai}`), Path.basename(localFolder));
}
await deployer['deployPackage2'](min, user, localFolder); await deployer['deployPackage2'](min, user, localFolder);
} }
} }
public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: GBDeployer) { public static async rebuildIndexPackageCommand(min: GBMinInstance, deployer: GBDeployer) {
const service = await AzureDeployerService.createInstance(deployer); const service = await AzureDeployerService.createInstance(deployer);
const searchIndex = min.instance.searchIndex ? min.instance.searchIndex : GBServer.globals.minBoot.instance.searchIndex; const searchIndex = min.instance.searchIndex
await deployer.rebuildIndex( ? min.instance.searchIndex
min.instance, : GBServer.globals.minBoot.instance.searchIndex;
service.getKBSearchSchema(searchIndex) await deployer.rebuildIndex(min.instance, service.getKBSearchSchema(searchIndex));
);
} }
public static async syncBotServerCommand(min: GBMinInstance, deployer: GBDeployer) { public static async syncBotServerCommand(min: GBMinInstance, deployer: GBDeployer) {
@ -245,15 +244,15 @@ export class GBAdminService implements IGBAdminService {
return obj.value; return obj.value;
} }
public async acquireElevatedToken(instanceId: number, root: boolean = false, public async acquireElevatedToken(
instanceId: number,
root: boolean = false,
tokenName: string = '', tokenName: string = '',
clientId: string = null, clientId: string = null,
clientSecret: string = null, clientSecret: string = null,
host: string = null, host: string = null,
tenant: string = null tenant: string = null
): Promise<string> { ): Promise<string> {
if (root) { if (root) {
const minBoot = GBServer.globals.minBoot; const minBoot = GBServer.globals.minBoot;
instanceId = minBoot.instance.instanceId; instanceId = minBoot.instance.instanceId;
@ -267,7 +266,6 @@ export class GBAdminService implements IGBAdminService {
throw new Error(`/setupSecurity is required before running /publish.`); throw new Error(`/setupSecurity is required before running /publish.`);
} }
return new Promise<string>(async (resolve, reject) => { return new Promise<string>(async (resolve, reject) => {
const instance = await this.core.loadInstanceById(instanceId); const instance = await this.core.loadInstanceById(instanceId);
@ -276,14 +274,10 @@ export class GBAdminService implements IGBAdminService {
const accessToken = await this.getValue(instanceId, `${tokenName}accessToken`); const accessToken = await this.getValue(instanceId, `${tokenName}accessToken`);
resolve(accessToken); resolve(accessToken);
} else { } else {
if (tokenName && !root) { if (tokenName && !root) {
const refreshToken = await this.getValue(instanceId, `${tokenName}refreshToken`); const refreshToken = await this.getValue(instanceId, `${tokenName}refreshToken`);
let url = urlJoin( let url = urlJoin(host, tenant, 'oauth/token');
host,
tenant, 'oauth/token');
let buff = new Buffer(`${clientId}:${clientSecret}`); let buff = new Buffer(`${clientId}:${clientSecret}`);
const base64 = buff.toString('base64'); const base64 = buff.toString('base64');
@ -295,8 +289,8 @@ export class GBAdminService implements IGBAdminService {
'Content-Type': 'application/x-www-form-urlencoded' 'Content-Type': 'application/x-www-form-urlencoded'
}, },
body: new URLSearchParams({ body: new URLSearchParams({
'grant_type': 'refresh_token', grant_type: 'refresh_token',
'refresh_token': refreshToken refresh_token: refreshToken
}) })
}; };
const result = await fetch(url, options); const result = await fetch(url, options);
@ -313,15 +307,15 @@ export class GBAdminService implements IGBAdminService {
await this.setValue(instanceId, `${tokenName}accessToken`, token['access_token']); await this.setValue(instanceId, `${tokenName}accessToken`, token['access_token']);
await this.setValue(instanceId, `${tokenName}refreshToken`, token['refresh_token']); await this.setValue(instanceId, `${tokenName}refreshToken`, token['refresh_token']);
await this.setValue(instanceId, `${tokenName}expiresOn`, await this.setValue(
new Date(Date.now() + (token['expires_in'] * 1000)).toString()); instanceId,
`${tokenName}expiresOn`,
new Date(Date.now() + token['expires_in'] * 1000).toString()
);
await this.setValue(instanceId, `${tokenName}AntiCSRFAttackState`, null); await this.setValue(instanceId, `${tokenName}AntiCSRFAttackState`, null);
resolve(token['access_token']); resolve(token['access_token']);
} else {
}
else {
const oauth2 = tokenName ? 'oauth' : 'oauth2'; const oauth2 = tokenName ? 'oauth' : 'oauth2';
const authorizationUrl = urlJoin( const authorizationUrl = urlJoin(
tokenName ? host : instance.authenticatorAuthorityHostUrl, tokenName ? host : instance.authenticatorAuthorityHostUrl,

View file

@ -45,6 +45,8 @@ import { AzureSearch } from 'pragmatismo-io-framework';
import { CollectionUtil } from 'pragmatismo-io-framework'; import { CollectionUtil } from 'pragmatismo-io-framework';
import { GBServer } from '../../../src/app.js'; 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 asyncPromise from 'async-promises';
import { GuaribasPackage } from '../models/GBModel.js'; import { 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';
@ -286,7 +288,6 @@ export class GBDeployer implements IGBDeployer {
`${publicAddress}/api/messages/${instance.botId}` `${publicAddress}/api/messages/${instance.botId}`
); );
} else { } else {
// Internally create resources on cloud provider. // Internally create resources on cloud provider.
instance = await service.internalDeployBot( instance = await service.internalDeployBot(
@ -326,7 +327,6 @@ export class GBDeployer implements IGBDeployer {
let embedding; let embedding;
if (!azureOpenAIEmbeddingModel) { if (!azureOpenAIEmbeddingModel) {
return; return;
} }
@ -344,7 +344,6 @@ export class GBDeployer implements IGBDeployer {
vectorStore = new HNSWLib(embedding, { vectorStore = new HNSWLib(embedding, {
space: 'cosine' space: 'cosine'
}); });
} }
return vectorStore; return vectorStore;
} }
@ -418,64 +417,37 @@ export class GBDeployer implements IGBDeployer {
/** /**
* Loads all para from tabular file Config.xlsx. * Loads all para from tabular file Config.xlsx.
*/ */
public async loadParamsFromTabular(min: GBMinInstance): Promise<any> { public async loadParamsFromTabular(min: GBMinInstance, path): Promise<any> {
const siteId = process.env.STORAGE_SITE_ID; const workbook = new Excel.Workbook();
const libraryId = process.env.STORAGE_LIBRARY; const data = await workbook.xlsx.readFile(Path.join(path, 'Config.xlsx'));
GBLogEx.info(min, `Connecting to Config.xslx (siteId: ${siteId}, libraryId: ${libraryId})...`); let worksheet: any;
for (let t = 0; t < data.worksheets.length; t++) {
worksheet = data.worksheets[t];
if (worksheet) {
break;
}
}
const rows = worksheet._rows;
GBLogEx.info(min, `Processing ${rows.length} rows from Config file ${path}...`);
let list = [];
// Connects to MSFT storage. await asyncPromise.eachSeries(rows, async line => {
// Skips the first line.
const token = await (min.adminService as any)['acquireElevatedToken'](min.instance.instanceId, true); if (line != undefined && line._cells[0] !== undefined && line._cells[1] !== undefined) {
// Extracts values from columns in the current line.
const client = MicrosoftGraph.Client.init({ let obj = {};
authProvider: done => { obj[line._cells[0].text] = line._cells[1].text;
done(null, token); list.push(obj);
} }
}); });
// Retrieves all files in .bot folder. return list;
const botId = min.instance.botId;
const path = DialogKeywords.getGBAIPath(botId, 'gbot');
let url = `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/root:/${path}:/children`;
GBLogEx.info(min, `Loading .gbot from Excel: ${url}`);
const res = await client.api(url).get();
// Finds Config.xlsx.
const document = res.value.filter(m => {
return m.name === 'Config.xlsx';
});
if (document === undefined || document.length === 0) {
GBLogEx.info(min, `Config.xlsx not found on .bot folder, check the package.`);
return null;
}
// Reads all rows in Config.xlsx that contains a pair of name/value
// and fills an object that is returned to be saved in params instance field.
const results = await client
.api(
`https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${libraryId}/drive/items/${document[0].id}/workbook/worksheets('General')/range(address='A7:B100')`
)
.get();
let index = 0,
obj = {};
for (; index < results.text.length; index++) {
if (results.text[index][0] === '') {
return obj;
}
obj[results.text[index][0]] = results.text[index][1];
}
return obj;
} }
/** /**
* Loads all para from tabular file Config.xlsx.
*/ */
public async downloadFolder( public async downloadFolder(
min: GBMinInstance, min: GBMinInstance,
@ -632,14 +604,7 @@ export class GBDeployer implements IGBDeployer {
case '.gbot': case '.gbot':
// Extracts configuration information from .gbot files. // Extracts configuration information from .gbot files.
if (process.env.ENABLE_PARAMS_ONLINE === 'false') { min.instance.params = await this.loadParamsFromTabular(min, localPath);
if (Fs.existsSync(localPath)) {
GBLogEx.info(min, `Loading .gbot from ${localPath}.`);
await this.deployBotFromLocalPath(localPath, GBServer.globals.publicAddress);
}
} else {
min.instance.params = await this.loadParamsFromTabular(min);
}
let connections = []; let connections = [];

View file

@ -317,14 +317,14 @@ export class GBMinService {
mkdirp.sync(dir); mkdirp.sync(dir);
} }
dir = Path.join(process.env.PWD, 'work', gbai); if (process.env.STORAGE_FILE) {
dir = Path.join(process.env.STORAGE_LIBRARY, 'work', gbai);
const server = new webdav.WebDAVServer(); const server = new webdav.WebDAVServer();
server.setFileSystem(`/${botId}`, server.setFileSystem(`/${botId}`, new webdav.PhysicalFileSystem(dir), success => {
new webdav.PhysicalFileSystem(dir), (success) => {
server.start(() => console.log('WEBDAV READY')); server.start(() => console.log('WEBDAV READY'));
}) });
}
// Loads Named Entity data for this bot. // Loads Named Entity data for this bot.
// TODO: await KBService.RefreshNER(min); // TODO: await KBService.RefreshNER(min);
@ -665,7 +665,6 @@ export class GBMinService {
if (instance !== null) { if (instance !== null) {
// Gets the webchat token, speech token and theme. // Gets the webchat token, speech token and theme.
const webchatTokenContainer = await this.getWebchatToken(instance);
const speechToken = instance.speechKey != undefined ? await this.getSTSToken(instance) : null; const speechToken = instance.speechKey != undefined ? await this.getSTSToken(instance) : null;
let theme = instance.theme; let theme = instance.theme;
@ -680,7 +679,6 @@ export class GBMinService {
botId: botId, botId: botId,
theme: theme, theme: theme,
speechToken: speechToken, speechToken: speechToken,
conversationId: webchatTokenContainer.conversationId,
authenticatorTenant: instance.authenticatorTenant, authenticatorTenant: instance.authenticatorTenant,
authenticatorClientId: instance.marketplaceId, authenticatorClientId: instance.marketplaceId,
paramLogoImageUrl: this.core.getParam(instance, 'Logo Image Url', null), paramLogoImageUrl: this.core.getParam(instance, 'Logo Image Url', null),
@ -696,6 +694,8 @@ export class GBMinService {
if (process.env.STORAGE_FILE) { if (process.env.STORAGE_FILE) {
config['domain'] = `http://localhost:${process.env.PORT}/directline`; config['domain'] = `http://localhost:${process.env.PORT}/directline`;
} else { } else {
const webchatTokenContainer = await this.getWebchatToken(instance);
config['conversationId']= webchatTokenContainer.conversationId,
config['webchatToken'] = webchatTokenContainer.token; config['webchatToken'] = webchatTokenContainer.token;
} }
@ -711,7 +711,7 @@ export class GBMinService {
* Gets Webchat token from Bot Service. * Gets Webchat token from Bot Service.
*/ */
private async getWebchatToken(instance: any) { private async getWebchatToken(instance: any) {
const url = 'https://directline.botframework.com/v3/directline/tokens/generate'; const url = `http://localhost:${process.env.PORT}/v3/directline/tokens/generate`;
const options = { const options = {
method: 'POST', method: 'POST',
headers: { headers: {
@ -1397,7 +1397,8 @@ export class GBMinService {
context.activity.text = context.activity.text.trim(); context.activity.text = context.activity.text.trim();
const member = context.activity.from; const member = context.activity.from;
let memberId = null, email = null; let memberId = null,
email = null;
// Processes e-mail from id in case of Teams messages. // Processes e-mail from id in case of Teams messages.

View file

@ -1369,10 +1369,12 @@ export class KBService implements IGBKBService {
const p = await deployer.deployPackageToStorage(instance.instanceId, packageName); const p = await deployer.deployPackageToStorage(instance.instanceId, packageName);
await this.importKbPackage(min, localPath, p, instance); await this.importKbPackage(min, localPath, p, instance);
GBDeployer.mountGBKBAssets(packageName, min.botId, localPath); GBDeployer.mountGBKBAssets(packageName, min.botId, localPath);
if (!process.env.STORAGE_FILE) {
const service = await AzureDeployerService.createInstance(deployer); const service = await AzureDeployerService.createInstance(deployer);
const searchIndex = instance.searchIndex ? instance.searchIndex : GBServer.globals.minBoot.instance.searchIndex; const searchIndex = instance.searchIndex ? instance.searchIndex : GBServer.globals.minBoot.instance.searchIndex;
await deployer.rebuildIndex(instance, service.getKBSearchSchema(searchIndex)); await deployer.rebuildIndex(instance, service.getKBSearchSchema(searchIndex));
}
min['groupCache'] = await KBService.getGroupReplies(instance.instanceId); min['groupCache'] = await KBService.getGroupReplies(instance.instanceId);
await KBService.RefreshNER(min); await KBService.RefreshNER(min);