From b7f256b01ff2cce96232579f9c9571275f1c6172 Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (pragmatismo.io)" Date: Thu, 25 Oct 2018 18:13:51 -0300 Subject: [PATCH] ARM automation done. --- README.md | 2 +- .../services/AzureDeployerService.ts | 560 ++++++++++-------- deploy/core.gbapp/models/GBModel.ts | 17 +- deploy/core.gbapp/services/GBConfigService.ts | 8 +- deploy/core.gbapp/services/GBCoreService.ts | 38 +- package.json | 3 + src/app.ts | 73 ++- 7 files changed, 408 insertions(+), 293 deletions(-) diff --git a/README.md b/README.md index fa2e3139..f9afacab 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Notes: 1. Create/Edit the .env file and add the ADDITIONAL_DEPLOY_PATH key pointing to the .gbai local parent folder of .gbapp, .gbot, .gbtheme, .gbkb package directories. 2. Specify STORAGE_SYNC to TRUE so database sync is run when the server is run. -3. In case of Microsoft SQL Server add the following keys: STORAGE_HOST, STORAGE_NAME, STORAGE_USERNAME, STORAGE_PASSWORD, STORAGE_DIALECT to `mssql`. +3. In case of Microsoft SQL Server add the following keys: STORAGE_SERVER, STORAGE_NAME, STORAGE_USERNAME, STORAGE_PASSWORD, STORAGE_DIALECT to `mssql`. Note: diff --git a/deploy/azuredeployer.gbapp/services/AzureDeployerService.ts b/deploy/azuredeployer.gbapp/services/AzureDeployerService.ts index 99cdab37..811bddaf 100644 --- a/deploy/azuredeployer.gbapp/services/AzureDeployerService.ts +++ b/deploy/azuredeployer.gbapp/services/AzureDeployerService.ts @@ -42,12 +42,12 @@ import { SqlManagementClient } from "azure-arm-sql"; import { CognitiveServicesManagementClient } from "azure-arm-cognitiveservices"; import { CognitiveServicesAccount } from "azure-arm-cognitiveservices/lib/models"; import { SearchManagementClient } from "azure-arm-search"; -import { WebResource, ServiceClient } from "ms-rest-js"; +import { WebResource, ServiceClient, HttpMethods } from "ms-rest-js"; import * as simplegit from "simple-git/promise"; import { AppServicePlan } from "azure-arm-website/lib/models"; import { GBConfigService } from "../../../deploy/core.gbapp/services/GBConfigService"; -const Spinner = require('cli-spinner').Spinner; +const Spinner = require("cli-spinner").Spinner; const scanf = require("scanf"); const msRestAzure = require("ms-rest-azure"); const git = simplegit(); @@ -56,6 +56,7 @@ const UrlJoin = require("url-join"); const PasswordGenerator = require("strict-password-generator").default; const iconUrl = "https://github.com/pragmatismo-io/BotServer/blob/master/docs/images/generalbots-logo-squared.png"; +const publicIp = require("public-ip"); export class AzureDeployerService extends GBService { instance: IGBInstance; @@ -70,9 +71,279 @@ export class AzureDeployerService extends GBService { location: string; public subscriptionId: string; static apiVersion = "2017-12-01"; + farmName: any; + + public static async getSubscriptions(credentials) { + let subscriptionClient = new SubscriptionClient.default(credentials); + return subscriptionClient.subscriptions.list(); + } + + public async deployFarm(proxyAddress: string): Promise { + let culture = "en-us"; + + // Tries do get information from .env file otherwise asks in command-line. + let instance: IGBInstance = {}; + instance = await this.ensureConfiguration(instance); + + let spinner = new Spinner("%s"); + spinner.start(); + spinner.setSpinnerString("|/-\\"); + + let keys: any; + let name = instance.botId; + + logger.info(`Deploying Deploy Group (It may take a few minutes)...`); + await this.createDeployGroup(name, instance.cloudLocation); + + logger.info(`Deploying Bot Server...`); + let serverFarm = await this.createHostingPlan( + name, + `${name}-server-plan`, + instance.cloudLocation + ); + await this.createServer( + serverFarm.id, + name, + `${name}-server`, + instance.cloudLocation + ); + + let administratorLogin = AzureDeployerService.getRndAdminAccount(); + let administratorPassword = AzureDeployerService.getRndPassword(); + + logger.info(`Deploying Bot Storage...`); + let storageServer = `${name}-storage-server`; + let storageName = `${name}-storage`; + await this.createStorageServer( + name, + storageServer, + administratorLogin, + administratorPassword, + storageServer, + instance.cloudLocation + ); + + await this.createStorage( + name, + storageServer, + storageName, + instance.cloudLocation + ); + instance.storageUsername = administratorLogin; + instance.storagePassword = administratorPassword; + instance.storageName = storageName; + instance.storageDialect = "mssql"; + instance.storageServer = storageServer; + + logger.info(`Deploying Search...`); + let searchName = `${name}-search`; + await this.createSearch(name, searchName, instance.cloudLocation); + let searchKeys = await this.searchClient.queryKeys.listBySearchService( + name, + searchName + ); + instance.searchHost = `${searchName}.search.windows.net`; + instance.searchIndex = "azuresql-index"; + instance.searchIndexer = "azuresql-indexer"; + instance.searchKey = searchKeys[0].key; + + logger.info(`Deploying Speech...`); + let speech = await this.createSpeech( + name, + `${name}-speech`, + instance.cloudLocation + ); + keys = await this.cognitiveClient.accounts.listKeys(name, speech.name); + instance.speechKeyEndpoint = speech.endpoint; + instance.speechKey = keys.key1; + + logger.info(`Deploying SpellChecker...`); + let spellChecker = await this.createSpellChecker( + name, + `${name}-spellchecker`, + instance.cloudLocation + ); + keys = await this.cognitiveClient.accounts.listKeys( + name, + spellChecker.name + ); + instance.spellCheckerKey = keys.key1; + instance.spellCheckerEndpoint = spellChecker.endpoint; + + logger.info(`Deploying Text Analytics...`); + let textAnalytics = await this.createTextAnalytics( + name, + `${name}-textanalytics`, + instance.cloudLocation + ); + keys = await this.cognitiveClient.accounts.listKeys( + name, + textAnalytics.name + ); + instance.textAnalyticsEndpoint = textAnalytics.endpoint; + instance.textAnalyticsKey = keys.key1; + + logger.info(`Deploying NLP...`); + let nlp = await this.createNLP(name, `${name}-nlp`, instance.cloudLocation); + keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name); + let nlpAppId = await this.createLUISApp( + name, + name, + instance.cloudLocation, + culture, + instance.nlpAuthoringKey + ); + let appId = msRestAzure.generateUuid(); + instance.nlpEndpoint = nlp.endpoint; + instance.nlpKey = keys.key1; + instance.nlpAppId = nlpAppId; + + logger.info(`Deploying Bot...`); + instance = await this.deployBootBot( + instance, + name, + `${proxyAddress}/api/messages/${name}`, + null, + null, + instance.cloudSubscriptionId, + appId + ); + + spinner.stop(); + return instance; + } + + public async openStorageFirewall(groupName, serverName) { + let username = GBConfigService.get("CLOUD_USERNAME"); + let password = GBConfigService.get("CLOUD_PASSWORD"); + let subscriptionId = GBConfigService.get("CLOUD_SUBSCRIPTIONID"); + + let credentials = await msRestAzure.loginWithUsernamePassword( + username, + password + ); + + let ip = await publicIp.v4(); + + let params = { + startIpAddress: ip, + endIpAddress: ip + }; + + let storageClient = new SqlManagementClient(credentials, subscriptionId); + await storageClient.firewallRules.createOrUpdate( + groupName, + serverName, + "gb", + params + ); + } + + private async ensureConfiguration(instance: IGBInstance) { + let username = GBConfigService.get("CLOUD_USERNAME"); + let password = GBConfigService.get("CLOUD_PASSWORD"); + let subscriptionId = GBConfigService.get("CLOUD_SUBSCRIPTIONID"); + let location = GBConfigService.get("CLOUD_LOCATION"); + let botId = GBConfigService.get("BOT_ID"); + + // No .env so asks for cloud credentials to start a new farm. + if (!username || !password || !subscriptionId || !location || !botId) { + process.stdout.write( + "A empty enviroment is detected. Please, enter details:" + ); + } + let retriveUsername = () => { + if (!username) { + process.stdout.write("CLOUD_USERNAME:"); + username = scanf("%s"); + } + }; + let retrivePassword = () => { + if (!password) { + process.stdout.write("CLOUD_PASSWORD:"); + password = scanf("%s"); + } + }; + let retrieveBotId = () => { + if (!botId) { + process.stdout.write( + "Bot Id must only contain lowercase letters, digits or dashes, cannot start or end with or contain consecutive dashes and is limited to 60 characters.\n" + ); + process.stdout.write("BOT_ID:"); + botId = scanf("%s"); + } + }; + let authoringKey = GBConfigService.get("NLP_AUTHORING_KEY"); + let retriveAuthoringKey = () => { + if (!authoringKey) { + process.stdout.write( + "Due to this opened issue: https://github.com/Microsoft/botbuilder-tools/issues/550\n" + ); + process.stdout.write("Please enter your LUIS Authoring Key:"); + authoringKey = scanf("%s"); + } + }; + while (!authoringKey) { + retriveAuthoringKey(); + } + while (!botId) { + retrieveBotId(); + } + while (!username) { + retriveUsername(); + } + while (!password) { + retrivePassword(); + } + + // Connects to the cloud and retrives subscriptions. + + let credentials = await msRestAzure.loginWithUsernamePassword( + username, + password + ); + if (!subscriptionId) { + let map = {}; + let index = 1; + let list = await AzureDeployerService.getSubscriptions(credentials); + list.forEach(element => { + console.log( + `${index}: ${element.displayName} (${element.subscriptionId})` + ); + map[index++] = element; + }); + let subscriptionIndex; + let retrieveSubscription = () => { + if (!subscriptionIndex) { + process.stdout.write("CLOUD_SUBSCRIPTIONID (type a number):"); + subscriptionIndex = scanf("%d"); + } + }; + while (!subscriptionIndex) { + retrieveSubscription(); + } + subscriptionId = map[subscriptionIndex].subscriptionId; + } + let retriveLocation = () => { + if (!location) { + process.stdout.write("CLOUD_LOCATION:"); + location = scanf("%s"); + } + }; + while (!location) { + retriveLocation(); + } + + // Prepares the first instance on bot farm. + + instance.botId = botId; + instance.cloudUsername = username; + instance.cloudPassword = password; + instance.cloudSubscriptionId = subscriptionId; + instance.cloudLocation = location; + instance.nlpAuthoringKey = authoringKey; + instance.adminPass = AzureDeployerService.getRndPassword(); - constructor(credentials, subscriptionId, location) { - super(); this.resourceClient = new ResourceManagementClient.default( credentials, subscriptionId @@ -86,134 +357,9 @@ export class AzureDeployerService extends GBService { credentials, subscriptionId ); + this.searchClient = new SearchManagementClient(credentials, subscriptionId); this.accessToken = credentials.tokenCache._entries[0].accessToken; - this.location = location; - this.subscriptionId = subscriptionId; - } - - public static async getSubscriptions(credentials) { - let subscriptionClient = new SubscriptionClient.default(credentials); - return subscriptionClient.subscriptions.list(); - } - - public async deployFarm( - name: string, - location: string, - proxyAddress: string - ): Promise { - let instance: any = {}; - let culture = "en-us"; - - let spinner = new Spinner('%s'); - spinner.start(); - spinner.setSpinnerString("⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈"); - - let keys: any; - - logger.info(`Deploying Deploy Group...`); - await this.createDeployGroup(name, location); - - logger.info(`Deploying Bot Server...`); - let serverFarm = await this.createHostingPlan( - name, - `${name}-server-plan`, - location - ); - await this.createServer(serverFarm.id, name, `${name}-server`, location); - - let administratorLogin = AzureDeployerService.getRndAdminAccount(); - let administratorPassword = AzureDeployerService.getRndPassword(); - - logger.info(`Deploying Bot Storage...`); - let storageServerName = `${name}-storage-server`; - await this.createStorageServer( - name, - storageServerName, - administratorLogin, - administratorPassword, - storageServerName, - location - ); - - await this.createStorage( - name, - storageServerName, - `${name}-storage`, - location - ); - instance.storageUsername = administratorLogin; - instance.storagePassword = administratorPassword; - instance.storageName = storageServerName; - instance.storageDialect = "mssql"; - instance.storageServerName = storageServerName; - - logger.info(`Deploying Search...`); - let searchName = `${name}-search`; - - await this.createSearch(name, searchName, location); - let searchKeys = await this.searchClient.queryKeys.listBySearchService( - name, - searchName - ); - instance.searchHost = `${searchName}.search.windows.net`; - instance.searchIndex = "azuresql-index"; - instance.searchIndexer = "azuresql-indexer"; - instance.searchKey = searchKeys[0].key; - - logger.info(`Deploying NLP...`); - let nlp = await this.createNLP(name, `${name}-nlp`, location); - keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name); - let nlpAppId = await this.createLUISApp(name, name, location, culture); - instance.nlpEndpoint = nlp.endpoint; - instance.nlpKey = keys.key1; - instance.nlpAppId = nlpAppId; - - logger.info(`Deploying Speech...`); - let speech = await this.createSpeech(name, `${name}-speech`, location); - keys = await this.cognitiveClient.accounts.listKeys(name, speech.name); - instance.speechKeyEndpoint = speech.endpoint; - instance.speechKey = keys.key1; - - logger.info(`Deploying SpellChecker...`); - let spellChecker = await this.createSpellChecker( - name, - `${name}-spellchecker`, - location - ); - keys = await this.cognitiveClient.accounts.listKeys( - name, - spellChecker.name - ); - instance.spellCheckerKey = keys.key1; - instance.spellCheckerEndpoint = spellChecker.endpoint; - - logger.info(`Deploying Text Analytics...`); - let textAnalytics = await this.createTextAnalytics( - name, - `${name}-textanalytics`, - location - ); - keys = await this.cognitiveClient.accounts.listKeys( - name, - textAnalytics.name - ); - instance.textAnalyticsEndpoint = textAnalytics.endpoint; - instance.textAnalyticsKey = keys.key1; - - let appId = msRestAzure.generateUuid(); - logger.info(`Deploying Bot...`); - instance = await this.deployBootBot( - instance, - name, - `${proxyAddress}/api/messages/${name}`, - nlpAppId, - keys.key1, - this.subscriptionId, - appId - ); - - spinner.stop(); return instance; } @@ -227,10 +373,7 @@ export class AzureDeployerService extends GBService { subscriptionId, appId ) { - logger.info(`Deploying Bot...`); - let botId = name + AzureDeployerService.getRndBotId(); - [ instance.marketplacePassword, instance.webchatKey @@ -378,44 +521,59 @@ export class AzureDeployerService extends GBService { name: string, description: string, location: string, - culture: string + culture: string, + authoringKey: string ) { let parameters = { name: name, description: description, culture: culture }; - let requestUrl = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/apps/`; - let req = new WebResource(); - req.method = "POST"; - req.url = requestUrl; + let body = JSON.stringify(parameters); + let apps = await this.makeNlpRequest( + location, + authoringKey, + null, + "GET", + "apps" + ); + let app = (apps.bodyAsJson as any).filter(x => x.name == name)[0]; + let id: string; + if (!app) { + let res = await this.makeNlpRequest( + location, + authoringKey, + body, + "POST", + "apps" + ); + id = res.bodyAsText; + } else { + id = app.id; + } + + return id; + } + + private async makeNlpRequest( + location: string, + authoringKey: string, + body: string, + method: HttpMethods, + resource: string + ) { + let req = new WebResource(); + req.method = method; + req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/${resource}`; req.headers = {}; req.headers["Content-Type"] = "application/json"; req.headers["accept-language"] = "*"; - - let authoringKey = GBConfigService.get("NLP_AUTHORING_KEY"); - let retriveAuthoringKey = () => { - if (!authoringKey) { - process.stdout.write( - "Due to this opened issue: https://github.com/Microsoft/botbuilder-tools/issues/550\n" - ); - process.stdout.write("Please enter your LUIS Authoring Key:"); - authoringKey = scanf("%s"); - } - }; - - while (!authoringKey) { - retriveAuthoringKey(); - } - req.headers["Ocp-Apim-Subscription-Key"] = authoringKey; - req.body = JSON.stringify(parameters); - + req.body = body; let httpClient = new ServiceClient(); let res = await httpClient.sendRequest(req); - - return res.bodyAsJson; + return res; } private async createSearch(group, name, location) { @@ -598,97 +756,13 @@ export class AzureDeployerService extends GBService { lowerCaseAlpha: true, number: true, specialCharacter: true, - minimumLength: 8, + minimumLength: 12, maximumLength: 14 }; let password = passwordGenerator.generatePassword(options); return password; } - static async ensureDeployer() { - // Tries do get information from .env file otherwise asks in command-line. - - let username = GBConfigService.get("CLOUD_USERNAME"); - let password = GBConfigService.get("CLOUD_PASSWORD"); - let subscriptionId = GBConfigService.get("CLOUD_SUBSCRIPTIONID"); - let location = GBConfigService.get("CLOUD_LOCATION"); - - // No .env so asks for cloud credentials to start a new farm. - - if (!username || !password || !subscriptionId || !location) { - process.stdout.write( - "FIRST RUN: A empty enviroment is detected. Please, enter credentials to create a new General Bots Farm." - ); - } - - let retriveUsername = () => { - if (!username) { - process.stdout.write("CLOUD_USERNAME:"); - username = scanf("%s"); - } - }; - - let retrivePassword = () => { - if (!password) { - process.stdout.write("CLOUD_PASSWORD:"); - password = scanf("%s"); - } - }; - - while (!username) { - retriveUsername(); - } - - while (!password) { - retrivePassword(); - } - - // Connects to the cloud and retrives subscriptions. - - let credentials = await msRestAzure.loginWithUsernamePassword( - username, - password - ); - - if (!subscriptionId) { - let map = {}; - let index = 1; - let list = await AzureDeployerService.getSubscriptions(credentials); - list.forEach(element => { - console.log( - `${index}: ${element.displayName} (${element.subscriptionId})` - ); - map[index++] = element; - }); - - let subscriptionIndex; - let retrieveSubscription = () => { - if (!subscriptionIndex) { - process.stdout.write("CLOUD_SUBSCRIPTIONID (type a number):"); - subscriptionIndex = scanf("%d"); - } - }; - - while (!subscriptionIndex) { - retrieveSubscription(); - } - subscriptionId = map[subscriptionIndex].subscriptionId; - } - - let retriveLocation = () => { - if (!location) { - process.stdout.write("CLOUD_LOCATION:"); - location = scanf("%s"); - } - }; - - while (!location) { - retriveLocation(); - } - - return new AzureDeployerService(credentials, subscriptionId, location); - } - static getKBSearchSchema(indexName) { return { name: indexName, diff --git a/deploy/core.gbapp/models/GBModel.ts b/deploy/core.gbapp/models/GBModel.ts index 3cd3587e..931ee272 100644 --- a/deploy/core.gbapp/models/GBModel.ts +++ b/deploy/core.gbapp/models/GBModel.ts @@ -121,9 +121,15 @@ export class GuaribasInstance extends Model @Column cloudSubscriptionId: string; + + @Column + cloudUsername: string; @Column - cloudRegion: string; + cloudPassword: string; + + @Column + cloudLocation: string; @Column whatsappBotKey: string; @@ -180,6 +186,12 @@ export class GuaribasInstance extends Model @Column({ type: DataType.STRING(512) }) nlpEndpoint: string; + @Column + nlpAuthoringKey: string; + + @Column + deploymentPaths: string; + @Column searchHost: string; @@ -210,6 +222,9 @@ export class GuaribasInstance extends Model @Column storagePath: string; + @Column + adminPass: string; + /* Settings section of bot.json */ @Column(DataType.FLOAT) diff --git a/deploy/core.gbapp/services/GBConfigService.ts b/deploy/core.gbapp/services/GBConfigService.ts index 28b359e4..146fa0d0 100644 --- a/deploy/core.gbapp/services/GBConfigService.ts +++ b/deploy/core.gbapp/services/GBConfigService.ts @@ -50,12 +50,6 @@ export class GBConfigService { } } - public writeEntry(name, value) { - if (fs.exists) { - fs.appendFileSync(".env", `${name}=${value}`); - } - } - static get(key: string): string | undefined { let value = GBConfigService.tryGet(key); @@ -77,7 +71,7 @@ export class GBConfigService { value = undefined; break; case "STORAGE_DIALECT": - value = "sqlite"; + value = undefined; break; case "STORAGE_STORAGE": value = "./guaribas.sqlite"; diff --git a/deploy/core.gbapp/services/GBCoreService.ts b/deploy/core.gbapp/services/GBCoreService.ts index ca3180a1..4c5fa3a9 100644 --- a/deploy/core.gbapp/services/GBCoreService.ts +++ b/deploy/core.gbapp/services/GBCoreService.ts @@ -34,6 +34,7 @@ const logger = require("../../../src/logger"); import { Sequelize } from "sequelize-typescript"; +import * as fs from "fs"; import { GBConfigService } from "./GBConfigService"; import { IGBInstance, IGBCoreService } from "botlib"; import { GuaribasInstance } from "../models/GBModel"; @@ -49,14 +50,6 @@ export class GBCoreService implements IGBCoreService { return GBConfigService.tryGet("STORAGE_DIALECT"); } - async ensureCloud(cloudDeployer) { - let instance = new GuaribasInstance(); - await cloudDeployer.deploy(instance, "westus"); - instance.save(); - - let content = `STORAGE_HOST = ${instance.storageServer}\n - STORAGE_NAME, STORAGE_USERNAME, STORAGE_PASSWORD, STORAGE_DIALECT`; - } /** * Data access layer instance. */ @@ -91,7 +84,6 @@ export class GBCoreService implements IGBCoreService { * Constructor retrieves default values. */ constructor() { - this.dialect = GBConfigService.get("STORAGE_DIALECT"); this.adminService = new GBAdminService(this); } @@ -101,6 +93,8 @@ export class GBCoreService implements IGBCoreService { async initDatabase() { return new Promise((resolve, reject) => { try { + this.dialect = GBConfigService.get("STORAGE_DIALECT"); + let host: string | undefined; let database: string | undefined; let username: string | undefined; @@ -108,12 +102,14 @@ export class GBCoreService implements IGBCoreService { let storage: string | undefined; if (this.dialect === "mssql") { - host = GBConfigService.get("STORAGE_HOST"); + host = GBConfigService.get("STORAGE_SERVER"); database = GBConfigService.get("STORAGE_NAME"); username = GBConfigService.get("STORAGE_USERNAME"); password = GBConfigService.get("STORAGE_PASSWORD"); } else if (this.dialect === "sqlite") { storage = GBConfigService.get("STORAGE_STORAGE"); + } else { + reject(`Unknown dialect: ${this.dialect}.`); } let logging = @@ -302,11 +298,29 @@ export class GBCoreService implements IGBCoreService { return GuaribasInstance.findOne(options); } + public async writeEnv(instance: IGBInstance) { + let env = + `ADMIN_PASS=${instance.adminPass}\n` + + `ADDITIONAL_DEPLOY_PATH=\n` + + `STORAGE_DIALECT=${instance.storageDialect}\n` + + `STORAGE_SERVER=${instance.storageServer}.database.windows.net\n` + + `STORAGE_NAME=${instance.storageName}\n` + + `STORAGE_USERNAME=${instance.storageUsername}\n` + + `STORAGE_PASSWORD=${instance.storagePassword}\n`+ + `CLOUD_USERNAME=${instance.cloudUsername}\n` + + `CLOUD_PASSWORD=${instance.cloudPassword}\n` + + `CLOUD_SUBSCRIPTIONID=${instance.cloudSubscriptionId}\n` + + `CLOUD_LOCATION=${instance.cloudLocation}\n` + + `CLOUD_GROUP=${instance.botId}\n` + + `NLP_AUTHORING_KEY=${instance.nlpAuthoringKey}`; + + fs.writeFileSync(".env", env); + } + public async ensureProxy(): Promise { let proxyAddress: string; - const ngrok = require('ngrok'); + const ngrok = require("ngrok"); return await ngrok.connect(); - // let expiresOn = new Date( // await this.adminService.getValue(0, "proxyExpiresOn") diff --git a/package.json b/package.json index 50ed5eb1..a4e006cc 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "express-promise-router": "3.0.3", "fs-extra": "7.0.0", "fs-walk": "0.0.2", + "ip": "^1.1.5", "localize": "0.4.7", "marked": "0.5.1", "mocha": "5.2.0", @@ -72,6 +73,7 @@ "ngrok": "^3.1.0", "pragmatismo-io-framework": "1.0.17", "process-exists": "^3.1.0", + "public-ip": "^2.4.0", "reflect-metadata": "0.1.12", "request-promise-native": "1.0.5", "scanf": "^1.0.2", @@ -83,6 +85,7 @@ "swagger-client": "3.8.21", "tedious": "2.6.4", "ts-node": "7.0.1", + "tslint": "^5.11.0", "typedoc": "0.13.0", "typescript": "3.1.3", "url-join": "4.0.0", diff --git a/src/app.ts b/src/app.ts index 122cfda3..e90292d9 100644 --- a/src/app.ts +++ b/src/app.ts @@ -53,7 +53,7 @@ import { GBCustomerSatisfactionPackage } from "../deploy/customer-satisfaction.g import { GBAdminService } from "../deploy/admin.gbapp/services/GBAdminService"; import { GuaribasInstance } from "../deploy/core.gbapp/models/GBModel"; import { AzureDeployerService } from "../deploy/azuredeployer.gbapp/services/AzureDeployerService"; -import { IGBPackage } from "botlib"; +import { IGBPackage, IGBInstance } from "botlib"; let appPackages = new Array(); @@ -61,13 +61,10 @@ let appPackages = new Array(); * General Bots open-core entry point. */ export class GBServer { - /** * Program entry-point. */ - static MASTERBOT_PREFIX = "generalbots-masterbot" - static run() { // Creates a basic HTTP server that will serve several URL, one for each // bot instance. This allows the same server to attend multiple Bot on @@ -85,6 +82,7 @@ export class GBServer { }) ); + let bootInstance: IGBInstance; server.listen(port, () => { (async () => { try { @@ -94,21 +92,29 @@ export class GBServer { GBConfigService.init(); let core = new GBCoreService(); - + // Ensures cloud / on-premises infrastructure is setup. logger.info(`Establishing a development local proxy...`); let proxyAddress = await core.ensureProxy(); - logger.info(`Ensuring services availability...`); - let cloudDeployer = await AzureDeployerService.ensureDeployer(); - let instance = await cloudDeployer.deployFarm('gbot', 'westus', proxyAddress); - + + try { + await core.initDatabase(); + } catch (error) { + logger.info(`Deploying cognitive infrastructure...`); + let azureDeployer = new AzureDeployerService(); + bootInstance = await azureDeployer.deployFarm(proxyAddress); + core.writeEnv(bootInstance); + logger.info(`File .env written, starting...`); + GBConfigService.init(); + + await core.initDatabase(); + } + // TODO: Get .gb* templates from GitHub and download do additional deploy folder. - await core.initDatabase(); - // Check admin password. - + let conversationalService = new GBConversationalService(core); let adminService = new GBAdminService(core); let password = GBConfigService.get("ADMIN_PASS"); @@ -138,28 +144,37 @@ export class GBServer { // Loads all bot instances from object storage, if it's formatted. - logger.info(`All instances are being now loaded...`); + logger.info(`Loading instances from storage...`); let instances: GuaribasInstance[]; try { instances = await core.loadInstances(); } catch (error) { - // Check if storage is empty and needs formatting. + if (error.parent.code === "ELOGIN") { - let isInvalidObject = - error.parent.number == 208 || error.parent.errno == 1; // MSSQL or SQLITE. + let azureDeployer = new AzureDeployerService(); + let group = GBConfigService.get("CLOUD_GROUP") + let serverName = GBConfigService.get("STORAGE_SERVER").split(".database.windows.net")[0] + await azureDeployer.openStorageFirewall(group,serverName ) - if (isInvalidObject) { - if (GBConfigService.get("STORAGE_SYNC") != "true") { - throw `Operating storage is out of sync or there is a storage connection error. Try setting STORAGE_SYNC to true in .env file. Error: ${ - error.message - }.`; - } else { - logger.info( - `Storage is empty. After collecting storage structure from all .gbapps it will get synced.` - ); - } } else { - throw `Cannot connect to operating storage: ${error.message}.`; + // Check if storage is empty and needs formatting. + + let isInvalidObject = + error.parent.number == 208 || error.parent.errno == 1; // MSSQL or SQLITE. + + if (isInvalidObject) { + if (GBConfigService.get("STORAGE_SYNC") != "true") { + throw `Operating storage is out of sync or there is a storage connection error. Try setting STORAGE_SYNC to true in .env file. Error: ${ + error.message + }.`; + } else { + logger.info( + `Storage is empty. After collecting storage structure from all .gbapps it will get synced.` + ); + } + } else { + throw `Cannot connect to operating storage: ${error.message}.`; + } } } @@ -172,11 +187,11 @@ export class GBServer { // If instances is undefined here it's because storage has been formatted. // Load all instances from .gbot found on deploy package directory. if (!instances) { + let saveInstance = new GuaribasInstance(bootInstance); + saveInstance.save(); instances = await core.loadInstances(); } - await core.ensureCloud(cloudDeployer); - // Setup server dynamic (per bot instance) resources and listeners. logger.info(`Building instances.`);