fix(core.gbapp): Fixes in a blank environment creation.

This commit is contained in:
Rodrigo Rodriguez 2021-04-17 17:20:44 -03:00
parent 8de9777423
commit b0d45c3212
9 changed files with 85 additions and 50 deletions

View file

@ -113,7 +113,7 @@ export class GBAdminService implements IGBAdminService {
maximumLength: 14 maximumLength: 14
}; };
let password = passwordGenerator.generatePassword(options); let password = passwordGenerator.generatePassword(options);
password = password.replace(/[\@\[\=\:\;\?]/gi, '#'); password = password.replace(/[\@\[\=\:\;\?\"\'\#]/gi, '1');
return password; return password;
} }

View file

@ -146,6 +146,11 @@ cannot start or end with or contain consecutive dashes and having 4 to 42 charac
return botId; return botId;
} }
/**
*
* Update Manifest in Azure: "signInAudience": "AzureADandPersonalMicrosoftAccount" and "accessTokenAcceptedVersion": 2.
*/
private static retrieveAppId() { private static retrieveAppId() {
let appId = GBConfigService.get('MARKETPLACE_ID'); let appId = GBConfigService.get('MARKETPLACE_ID');
if (appId === undefined) { if (appId === undefined) {
@ -154,6 +159,7 @@ cannot start or end with or contain consecutive dashes and having 4 to 42 charac
please go to https://apps.dev.microsoft.com/portal/register-app to please go to https://apps.dev.microsoft.com/portal/register-app to
generate manually an App ID and App Secret.\n` generate manually an App ID and App Secret.\n`
); );
process.stdout.write('Generated Application Id (MARKETPLACE_ID):'); process.stdout.write('Generated Application Id (MARKETPLACE_ID):');
appId = scanf('%s').replace(/(\n|\r)+$/, ''); appId = scanf('%s').replace(/(\n|\r)+$/, '');
} }

View file

@ -44,7 +44,7 @@ import { SqlManagementClient } from 'azure-arm-sql';
import { WebSiteManagementClient } from 'azure-arm-website'; import { WebSiteManagementClient } from 'azure-arm-website';
//tslint:disable-next-line:no-submodule-imports //tslint:disable-next-line:no-submodule-imports
import { AppServicePlan, Site, SiteConfigResource, SiteLogsConfig, SiteSourceControl } from 'azure-arm-website/lib/models'; import { AppServicePlan, Site, SiteConfigResource, SiteLogsConfig, SiteSourceControl } from 'azure-arm-website/lib/models';
import { GBLog, IGBInstallationDeployer, IGBInstance, IGBDeployer } from 'botlib'; import { GBLog, IGBInstallationDeployer, IGBInstance, IGBDeployer, IGBCoreService } from 'botlib';
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService'; import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService';
import { GBCorePackage } from '../../../packages/core.gbapp'; import { GBCorePackage } from '../../../packages/core.gbapp';
import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService'; import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService';
@ -82,13 +82,18 @@ export class AzureDeployerService implements IGBInstallationDeployer {
public subscriptionId: string; public subscriptionId: string;
public farmName: any; public farmName: any;
public deployer: IGBDeployer; public deployer: IGBDeployer;
public core: IGBCoreService;
private freeTier: boolean; private freeTier: boolean;
constructor(deployer: IGBDeployer, freeTier: boolean = true) { constructor(deployer: IGBDeployer, freeTier: boolean = false) {
this.deployer = deployer; this.deployer = deployer;
this.freeTier = freeTier; this.freeTier = freeTier;
} }
public async runSearch(instance: IGBInstance) {
await this.deployer.rebuildIndex(instance, this.getKBSearchSchema(instance.searchIndex));
}
public static async createInstance(deployer: GBDeployer): Promise<AzureDeployerService> { public static async createInstance(deployer: GBDeployer): Promise<AzureDeployerService> {
const username = GBConfigService.get('CLOUD_USERNAME'); const username = GBConfigService.get('CLOUD_USERNAME');
@ -97,6 +102,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID'); const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
const service = new AzureDeployerService(deployer); const service = new AzureDeployerService(deployer);
service.core = deployer.core;
service.initServices(credentials, subscriptionId); service.initServices(credentials, subscriptionId);
return service; return service;
@ -350,15 +356,6 @@ export class AzureDeployerService implements IGBInstallationDeployer {
}; };
await storageClient.firewallRules.createOrUpdate(groupName, serverName, 'gb', params); await storageClient.firewallRules.createOrUpdate(groupName, serverName, 'gb', params);
// AllowAllWindowsAzureIps must be created that way, so the Azure Search can
// access SQL Database to index its contents.
params = {
startIpAddress: '0.0.0.0',
endIpAddress: '0.0.0.0'
};
await storageClient.firewallRules.createOrUpdate(groupName, serverName, 'AllowAllWindowsAzureIps', params);
} }
public async deployFarm( public async deployFarm(
@ -396,10 +393,19 @@ export class AzureDeployerService implements IGBInstallationDeployer {
await this.createStorage(name, storageServer, storageName, instance.cloudLocation); await this.createStorage(name, storageServer, storageName, instance.cloudLocation);
instance.storageUsername = administratorLogin; instance.storageUsername = administratorLogin;
instance.storagePassword = administratorPassword; instance.storagePassword = administratorPassword;
instance.storageName = storageServer; instance.storageName = storageName;
instance.storageDialect = 'mssql'; instance.storageDialect = 'mssql';
instance.storageServer = `${storageServer}.database.windows.net`; instance.storageServer = `${storageServer}.database.windows.net`;
GBLog.info(`Deploying Search...`);
const searchName = `${name}-search`.toLowerCase();
await this.createSearch(name, searchName, instance.cloudLocation);
const searchKeys = await this.searchClient.adminKeys.get(name, searchName);
instance.searchHost = `${searchName}.search.windows.net`;
instance.searchIndex = 'azuresql-index';
instance.searchIndexer = 'azuresql-indexer';
instance.searchKey = searchKeys.primaryKey;
GBLog.info(`Deploying Speech...`); GBLog.info(`Deploying Speech...`);
const speech = await this.createSpeech(name, `${name}-speech`, instance.cloudLocation); const speech = await this.createSpeech(name, `${name}-speech`, instance.cloudLocation);
keys = await this.cognitiveClient.accounts.listKeys(name, speech.name); keys = await this.cognitiveClient.accounts.listKeys(name, speech.name);
@ -409,7 +415,6 @@ export class AzureDeployerService implements IGBInstallationDeployer {
GBLog.info(`Deploying Text Analytics...`); GBLog.info(`Deploying Text Analytics...`);
const textAnalytics = await this.createTextAnalytics(name, `${name}-textanalytics`, instance.cloudLocation); const textAnalytics = await this.createTextAnalytics(name, `${name}-textanalytics`, instance.cloudLocation);
keys = await this.cognitiveClient.accounts.listKeys(name, textAnalytics.name); keys = await this.cognitiveClient.accounts.listKeys(name, textAnalytics.name);
instance.textAnalyticsEndpoint = textAnalytics.endpoint.replace(`/text/analytics/v2.0`, ''); instance.textAnalyticsEndpoint = textAnalytics.endpoint.replace(`/text/analytics/v2.0`, '');
instance.textAnalyticsKey = keys.key1; instance.textAnalyticsKey = keys.key1;
@ -419,17 +424,6 @@ export class AzureDeployerService implements IGBInstallationDeployer {
instance.spellcheckerKey = keys.key1; instance.spellcheckerKey = keys.key1;
instance.spellcheckerEndpoint = spellChecker.endpoint; instance.spellcheckerEndpoint = spellChecker.endpoint;
GBLog.info(`Deploying Search...`);
const searchName = `${name}-search`.toLowerCase();
await this.createSearch(name, searchName, instance.cloudLocation);
const searchKeys = await this.searchClient.adminKeys.get(name, searchName);
instance.searchHost = `${searchName}.search.windows.net`;
instance.searchIndex = 'azuresql-index';
instance.searchIndexer = 'azuresql-indexer';
instance.searchKey = searchKeys.primaryKey;
await this.deployer.rebuildIndex(instance, this.getKBSearchSchema(instance.searchIndex));
GBLog.info(`Deploying NLP...`); GBLog.info(`Deploying NLP...`);
const nlp = await this.createNLP(name, `${name}-nlp`, instance.cloudLocation); const nlp = await this.createNLP(name, `${name}-nlp`, instance.cloudLocation);
const nlpa = await this.createNLPAuthoring(name, `${name}-nlpa`, instance.cloudLocation); const nlpa = await this.createNLPAuthoring(name, `${name}-nlpa`, instance.cloudLocation);
@ -459,7 +453,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
GBLog.info('Opening your browser with default.gbui...'); GBLog.info('Opening your browser with default.gbui...');
const opn = require('opn'); const opn = require('opn');
opn(`https://${serverName}.azurewebsites.net`); opn(`http://localhost:4242`);
return instance; return instance;
} }
@ -591,7 +585,18 @@ export class AzureDeployerService implements IGBInstallationDeployer {
fullyQualifiedDomainName: serverName fullyQualifiedDomainName: serverName
}; };
return await this.storageClient.servers.createOrUpdate(group, name, params); const database = await this.storageClient.servers.createOrUpdate(group, name, params);
// AllowAllWindowsAzureIps must be created that way, so the Azure Search can
// access SQL Database to index its contents.
const paramsFirewall = {
startIpAddress: '0.0.0.0',
endIpAddress: '0.0.0.0'
};
await this.storageClient.firewallRules.createOrUpdate(group, name, 'AllowAllWindowsAzureIps', paramsFirewall);
return database;
} }
public async createApplication(token: string, name: string) { public async createApplication(token: string, name: string) {
@ -668,7 +673,19 @@ export class AzureDeployerService implements IGBInstallationDeployer {
const body = JSON.stringify(parameters); const body = JSON.stringify(parameters);
const apps = await this.makeNlpRequest(location, authoringKey, undefined, 'GET', 'apps'); const apps = await this.makeNlpRequest(location, authoringKey, undefined, 'GET', 'apps');
const app = JSON.parse(apps.bodyAsText).filter(x => x.name === name)[0];
let app = null;
if (apps.bodyAsText && apps.bodyAsText !== '[]') {
const result = JSON.parse(apps.bodyAsText)
if (result.error) {
if (result.error.code !== "401") {
throw new Error(result.error);
}
}
else {
app = result.filter(x => x.name === name)[0];
}
}
let id: string; let id: string;
if (!app) { if (!app) {
const res = await this.makeNlpRequest(location, authoringKey, body, 'POST', 'apps'); const res = await this.makeNlpRequest(location, authoringKey, body, 'POST', 'apps');

View file

@ -116,7 +116,7 @@ export class GBConfigService {
value = undefined; value = undefined;
break; break;
case 'STORAGE_SYNC': case 'STORAGE_SYNC':
value = 'false'; value = 'true';
break; break;
case 'STORAGE_SYNC_ALTER': case 'STORAGE_SYNC_ALTER':
value = 'false'; value = 'false';

View file

@ -105,7 +105,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
@ -201,6 +201,7 @@ export class GBCoreService implements IGBCoreService {
} }
} }
/** /**
* Syncronizes structure between model and tables in storage. * Syncronizes structure between model and tables in storage.
*/ */
@ -209,7 +210,7 @@ export class GBCoreService implements IGBCoreService {
const alter = GBConfigService.get('STORAGE_SYNC_ALTER') === 'true'; const alter = GBConfigService.get('STORAGE_SYNC_ALTER') === 'true';
GBLog.info('Syncing database...'); GBLog.info('Syncing database...');
return this.sequelize.sync({ return await this.sequelize.sync({
alter: alter, alter: alter,
force: false // Keep it false this due to data loss danger. force: false // Keep it false this due to data loss danger.
}); });
@ -274,7 +275,8 @@ export class GBCoreService implements IGBCoreService {
* full base environment. * full base environment.
*/ */
public async writeEnv(instance: IGBInstance) { public async writeEnv(instance: IGBInstance) {
const env = `ADDITIONAL_DEPLOY_PATH= const env = `
ADDITIONAL_DEPLOY_PATH=
ADMIN_PASS=${instance.adminPass} ADMIN_PASS=${instance.adminPass}
BOT_ID=${instance.botId} BOT_ID=${instance.botId}
CLOUD_SUBSCRIPTIONID=${instance.cloudSubscriptionId} CLOUD_SUBSCRIPTIONID=${instance.cloudSubscriptionId}
@ -290,6 +292,7 @@ STORAGE_NAME=${instance.storageName}
STORAGE_USERNAME=${instance.storageUsername} STORAGE_USERNAME=${instance.storageUsername}
STORAGE_PASSWORD=${instance.storagePassword} STORAGE_PASSWORD=${instance.storagePassword}
STORAGE_SYNC=true STORAGE_SYNC=true
ENDPOINT_UPDATE=true
`; `;
fs.writeFileSync('.env', env); fs.writeFileSync('.env', env);
@ -499,16 +502,20 @@ STORAGE_SYNC=true
GBLog.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`); GBLog.info(`Deploying cognitive infrastructure (on the cloud / on premises)...`);
try { try {
const { instance, credentials, subscriptionId } = await StartDialog.createBaseInstance(installationDeployer); const { instance, credentials, subscriptionId } = await StartDialog.createBaseInstance(installationDeployer);
installationDeployer['core'] = this;
const changedInstance = await installationDeployer.deployFarm( const changedInstance = await installationDeployer.deployFarm(
proxyAddress, proxyAddress,
instance, instance,
credentials, credentials,
subscriptionId subscriptionId
); );
core.writeEnv(changedInstance); await this.writeEnv(changedInstance);
GBLog.info(`File .env written, starting General Bots...`);
GBConfigService.init(); GBConfigService.init();
GBLog.info(`File .env written. Preparing storage and search for the first time...`);
await this.openStorageFrontier(installationDeployer);
await this.initStorage();
return changedInstance; return changedInstance;
} catch (error) { } catch (error) {
GBLog.warn( GBLog.warn(
@ -639,7 +646,7 @@ STORAGE_SYNC=true
value = instance['dataValues'][name]; value = instance['dataValues'][name];
if (value === null) { if (value === null) {
const minBoot = GBServer.globals.minBoot as any; const minBoot = GBServer.globals.minBoot as any;
if (minBoot.instance && minBoot.instance.datavalues){ if (minBoot.instance && minBoot.instance.datavalues) {
value = minBoot.instance.datavalues[name]; value = minBoot.instance.datavalues[name];
} }
} }
@ -678,7 +685,7 @@ STORAGE_SYNC=true
options options
); );
GBLog.info(`Running .gbdialog word ${item.name} on:${item.schedule}...`); GBLog.info(`Running .gbdialog word ${item.name} on:${item.schedule}...`);
} catch (error) {} } catch (error) { }
}); });
} }
} catch (error) { } catch (error) {

View file

@ -738,7 +738,7 @@ export class GBDeployer implements IGBDeployer {
// If it is a 404 there is nothing to delete as it is the first creation. // If it is a 404 there is nothing to delete as it is the first creation.
if (err.code !== 404 || err.code !== "OperationNotAllowed") { if (err.code !== 404 && err.code !== "OperationNotAllowed") {
throw err; throw err;
} }
} }

View file

@ -635,7 +635,7 @@ export class GBMinService {
// If there is WhatsApp configuration specified, initialize // If there is WhatsApp configuration specified, initialize
// infrastructure objects. // infrastructure objects.
if (min.instance.whatsappServiceKey !== null) { if (min.instance.whatsappServiceUrl !== null) {
min.whatsAppDirectLine = new WhatsappDirectLine( min.whatsAppDirectLine = new WhatsappDirectLine(
min, min,
min.botId, min.botId,
@ -647,7 +647,7 @@ export class GBMinService {
await min.whatsAppDirectLine.setup(true); await min.whatsAppDirectLine.setup(true);
} else { } else {
const minBoot = GBServer.globals.minBoot as any; const minBoot = GBServer.globals.minBoot as any;
if (minBoot.instance.whatsappServiceKey) { if (minBoot.instance.whatsappServiceUrl) {
min.whatsAppDirectLine = new WhatsappDirectLine( min.whatsAppDirectLine = new WhatsappDirectLine(
min, min,
min.botId, min.botId,

View file

@ -87,6 +87,7 @@ export class WhatsappDirectLine extends GBService {
} }
public async setup(setUrl) { public async setup(setUrl) {
this.directLineClient = this.directLineClient =
new Swagger({ new Swagger({
spec: JSON.parse(fs.readFileSync('directline-3.0.json', 'utf8')), spec: JSON.parse(fs.readFileSync('directline-3.0.json', 'utf8')),

View file

@ -140,7 +140,7 @@ export class GBServer {
GBLog.verbose(`Error initializing storage: ${error}`); GBLog.verbose(`Error initializing storage: ${error}`);
GBServer.globals.bootInstance = GBServer.globals.bootInstance =
await core.createBootInstance(core, azureDeployer, GBServer.globals.publicAddress); await core.createBootInstance(core, azureDeployer, GBServer.globals.publicAddress);
await core.initStorage();
} }
core.ensureAdminIsSecured(); core.ensureAdminIsSecured();
@ -150,6 +150,7 @@ export class GBServer {
GBLog.info(`Deploying packages...`); GBLog.info(`Deploying packages...`);
GBServer.globals.sysPackages = await core.loadSysPackages(core); GBServer.globals.sysPackages = await core.loadSysPackages(core);
await core.checkStorage(azureDeployer); await core.checkStorage(azureDeployer);
await core.syncDatabaseStructure();
await deployer.deployPackages(core, server, GBServer.globals.appPackages); await deployer.deployPackages(core, server, GBServer.globals.appPackages);
GBLog.info(`Publishing instances...`); GBLog.info(`Publishing instances...`);
@ -160,6 +161,7 @@ export class GBServer {
); );
if (instances.length === 0) { if (instances.length === 0) {
const instance = await importer.importIfNotExistsBotPackage( const instance = await importer.importIfNotExistsBotPackage(
GBConfigService.get('BOT_ID'), GBConfigService.get('BOT_ID'),
'boot.gbot', 'boot.gbot',
@ -168,6 +170,8 @@ export class GBServer {
); );
await deployer.deployBotFull(instance, GBServer.globals.publicAddress); await deployer.deployBotFull(instance, GBServer.globals.publicAddress);
instances.push(instance); instances.push(instance);
await azureDeployer['runSearch'](instance);
} }
GBServer.globals.bootInstance = instances[0]; GBServer.globals.bootInstance = instances[0];