new(basic.gblib): API online for GB.

This commit is contained in:
Rodrigo Rodriguez 2024-01-16 23:32:04 -03:00
parent f520c69f3f
commit adac385b5a
13 changed files with 3168 additions and 164 deletions

14
api-template.json Normal file
View file

@ -0,0 +1,14 @@
{
"openapi": "3.0.0",
"info": {
"title": "General Bots API",
"description": "General Bots API description in Swagger format",
"version": "1.0"
},
"servers": [
{
"url": "https://gb.pragmatismo.com.br/api",
"description": "General Bots Online"
}
]
}

View file

@ -74,6 +74,7 @@
"@nosferatu500/textract": "3.1.2", "@nosferatu500/textract": "3.1.2",
"@push-rpc/core": "1.8.2", "@push-rpc/core": "1.8.2",
"@push-rpc/http": "1.8.2", "@push-rpc/http": "1.8.2",
"@push-rpc/openapi": "^1.9.0",
"@push-rpc/websocket": "1.8.2", "@push-rpc/websocket": "1.8.2",
"@semantic-release/changelog": "5.0.1", "@semantic-release/changelog": "5.0.1",
"@semantic-release/exec": "5.0.0", "@semantic-release/exec": "5.0.0",
@ -176,6 +177,7 @@
"ssr-for-bots": "1.0.1-c", "ssr-for-bots": "1.0.1-c",
"strict-password-generator": "1.1.2", "strict-password-generator": "1.1.2",
"swagger-client": "3.18.5", "swagger-client": "3.18.5",
"swagger-ui-dist": "^5.11.0",
"tabulator-tables": "5.4.2", "tabulator-tables": "5.4.2",
"tedious": "15.1.2", "tedious": "15.1.2",
"textract": "2.5.0", "textract": "2.5.0",

View file

@ -43,96 +43,6 @@ import { CodeServices } from '../../gpt.gblib/services/CodeServices.js';
* Web Automation services of conversation to be called by BASIC. * Web Automation services of conversation to be called by BASIC.
*/ */
export class DebuggerService { export class DebuggerService {
static systemVariables = [
'AggregateError',
'Array',
'ArrayBuffer',
'Atomics',
'BigInt',
'BigInt64Array',
'BigUint64Array',
'Boolean',
'DataView',
'Date',
'Error',
'EvalError',
'FinalizationRegistry',
'Float32Array',
'Float64Array',
'Function',
'Headers',
'Infinity',
'Int16Array',
'Int32Array',
'Int8Array',
'Intl',
'JSON',
'Map',
'Math',
'NaN',
'Number',
'Object',
'Promise',
'Proxy',
'RangeError',
'ReferenceError',
'Reflect',
'RegExp',
'Request',
'Response',
'Set',
'SharedArrayBuffer',
'String',
'Symbol',
'SyntaxError',
'TypeError',
'URIError',
'Uint16Array',
'Uint32Array',
'Uint8Array',
'Uint8ClampedArray',
'VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL',
'WeakMap',
'WeakRef',
'WeakSet',
'WebAssembly',
'__defineGetter__',
'__defineSetter__',
'__lookupGetter__',
'__lookupSetter__',
'__proto__',
'clearImmediate',
'clearInterval',
'clearTimeout',
'console',
'constructor',
'decodeURI',
'decodeURIComponent',
'dss',
'encodeURI',
'encodeURIComponent',
'escape',
'eval',
'fetch',
'global',
'globalThis',
'hasOwnProperty',
'isFinite',
'isNaN',
'isPrototypeOf',
'parseFloat',
'parseInt',
'process',
'propertyIsEnumerable',
'setImmediate',
'setInterval',
'setTimeout',
'toLocaleString',
'toString',
'undefined',
'unescape',
'valueOf'
];
public async setBreakpoint({ botId, line }) { public async setBreakpoint({ botId, line }) {
GBLog.info(`BASIC: Enabled breakpoint for ${botId} on ${line}.`); GBLog.info(`BASIC: Enabled breakpoint for ${botId} on ${line}.`);

View file

@ -994,7 +994,7 @@ export class DialogKeywords {
} }
} else if (kind === 'file') { } else if (kind === 'file') {
GBLog.info(`BASIC (${min.botId}): Upload done for ${answer.filename}.`); GBLog.info(`BASIC (${min.botId}): Upload done for ${answer.filename}.`);
const handle = WebAutomationServices.cyrb53(min.botId + answer.filename); const handle = WebAutomationServices.cyrb53({pid, str: min.botId + answer.filename});
GBServer.globals.files[handle] = answer; GBServer.globals.files[handle] = answer;
result = handle; result = handle;
} else if (kind === 'boolean') { } else if (kind === 'boolean') {
@ -1115,7 +1115,7 @@ export class DialogKeywords {
} else if (kind === 'qr-scanner') { } else if (kind === 'qr-scanner') {
//https://github.com/GeneralBots/BotServer/issues/171 //https://github.com/GeneralBots/BotServer/issues/171
GBLog.info(`BASIC (${min.botId}): Upload done for ${answer.filename}.`); GBLog.info(`BASIC (${min.botId}): Upload done for ${answer.filename}.`);
const handle = WebAutomationServices.cyrb53(min.botId + answer.filename); const handle = WebAutomationServices.cyrb53({pid, str: min.botId + answer.filename});
GBServer.globals.files[handle] = answer; GBServer.globals.files[handle] = answer;
QrScanner.scanImage(GBServer.globals.files[handle]) QrScanner.scanImage(GBServer.globals.files[handle])
.then(result => console.log(result)) .then(result => console.log(result))

View file

@ -81,6 +81,10 @@ import { GBUtil } from '../../../src/util.js';
* BASIC system class for extra manipulation of bot behaviour. * BASIC system class for extra manipulation of bot behaviour.
*/ */
export class SystemKeywords { export class SystemKeywords {
/**
* @tags System
*/
public async callVM({ pid, text }) { public async callVM({ pid, text }) {
const { min, user } = await DialogKeywords.getProcessInfo(pid); const { min, user } = await DialogKeywords.getProcessInfo(pid);
const step = null; const step = null;
@ -2104,7 +2108,7 @@ export class SystemKeywords {
} }
private cachedMerge = {}; private cachedMerge:any = {};
/** /**
* Merges a multi-value with a tabular file using BY field as key. * Merges a multi-value with a tabular file using BY field as key.

View file

@ -37,11 +37,10 @@ import Fs from 'fs';
import Path from 'path'; import Path from 'path';
import url from 'url'; import url from 'url';
import { GBLog, GBMinInstance } from 'botlib'; import { GBLog } from 'botlib';
import { GBServer } from '../../../src/app.js'; import { GBServer } from '../../../src/app.js';
import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js'; import { GBAdminService } from '../../admin.gbapp/services/GBAdminService.js';
import { GBSSR } from '../../core.gbapp/services/GBSSR.js'; import { GBSSR } from '../../core.gbapp/services/GBSSR.js';
import { GuaribasUser } from '../../security.gbapp/models/index.js';
import { DialogKeywords } from './DialogKeywords.js'; import { DialogKeywords } from './DialogKeywords.js';
import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js'; import { GBDeployer } from '../../core.gbapp/services/GBDeployer.js';
import { Mutex } from 'async-mutex'; import { Mutex } from 'async-mutex';
@ -55,10 +54,8 @@ export class WebAutomationServices {
static isSelector(name: any) { static isSelector(name: any) {
return name.startsWith('.') || name.startsWith('#') || name.startsWith('['); return name.startsWith('.') || name.startsWith('#') || name.startsWith('[');
} }
private debugWeb: boolean;
private lastDebugWeb: Date; public static cyrb53 ({pid, str, seed = 0}) {
public static cyrb53 = (str, seed = 0) => {
let h1 = 0xdeadbeef ^ seed, let h1 = 0xdeadbeef ^ seed,
h2 = 0x41c6ce57 ^ seed; h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) { for (let i = 0, ch; i < str.length; i++) {
@ -152,7 +149,7 @@ export class WebAutomationServices {
if ((!session && sessionKind === 'AS') || !sessionName) { if ((!session && sessionKind === 'AS') || !sessionName) {
// A new web session is being created. // A new web session is being created.
handle = WebAutomationServices.cyrb53(min.botId + url); handle = WebAutomationServices.cyrb53({pid, str:min.botId + url});
session = {}; session = {};
session.sessionName = sessionName; session.sessionName = sessionName;
@ -269,18 +266,21 @@ export class WebAutomationServices {
private async debugStepWeb(pid, page) { private async debugStepWeb(pid, page) {
const { min, user } = await DialogKeywords.getProcessInfo(pid); const { min, user } = await DialogKeywords.getProcessInfo(pid);
let debugWeb, lastDebugWeb; // TODO: Add this to pid bag.
let refresh = true; let refresh = true;
if (this.lastDebugWeb) { if (lastDebugWeb) {
refresh = new Date().getTime() - this.lastDebugWeb.getTime() > 5000; refresh = new Date().getTime() - lastDebugWeb.getTime() > 5000;
} }
if (this.debugWeb && refresh) { if (debugWeb && refresh) {
const mobile = min.core.getParam(min.instance, 'Bot Admin Number', null); const mobile = min.core.getParam(min.instance, 'Bot Admin Number', null);
const filename = page; const filename = page;
if (mobile) { if (mobile) {
await new DialogKeywords().sendFileTo({ pid: pid, mobile, filename, caption: 'General Bots Debugger' }); await new DialogKeywords().sendFileTo({ pid: pid, mobile, filename, caption: 'General Bots Debugger' });
} }
this.lastDebugWeb = new Date(); lastDebugWeb = new Date();
} }
} }

View file

@ -27,6 +27,98 @@ const waitUntil = condition => {
}); });
}; };
const systemVariables = [
'AggregateError',
'Array',
'ArrayBuffer',
'Atomics',
'BigInt',
'BigInt64Array',
'BigUint64Array',
'Boolean',
'DataView',
'Date',
'Error',
'EvalError',
'FinalizationRegistry',
'Float32Array',
'Float64Array',
'Function',
'Headers',
'Infinity',
'Int16Array',
'Int32Array',
'Int8Array',
'Intl',
'JSON',
'Map',
'Math',
'NaN',
'Number',
'Object',
'Promise',
'Proxy',
'RangeError',
'ReferenceError',
'Reflect',
'RegExp',
'Request',
'Response',
'Set',
'SharedArrayBuffer',
'String',
'Symbol',
'SyntaxError',
'TypeError',
'URIError',
'Uint16Array',
'Uint32Array',
'Uint8Array',
'Uint8ClampedArray',
'VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL',
'WeakMap',
'WeakRef',
'WeakSet',
'WebAssembly',
'__defineGetter__',
'__defineSetter__',
'__lookupGetter__',
'__lookupSetter__',
'__proto__',
'clearImmediate',
'clearInterval',
'clearTimeout',
'console',
'constructor',
'decodeURI',
'decodeURIComponent',
'dss',
'encodeURI',
'encodeURIComponent',
'escape',
'eval',
'fetch',
'global',
'globalThis',
'hasOwnProperty',
'isFinite',
'isNaN',
'isPrototypeOf',
'parseFloat',
'parseInt',
'process',
'propertyIsEnumerable',
'setImmediate',
'setInterval',
'setTimeout',
'toLocaleString',
'toString',
'undefined',
'unescape',
'valueOf'
];
export const createVm2Pool = ({ min, max, ...limits }) => { export const createVm2Pool = ({ min, max, ...limits }) => {
limits = Object.assign( limits = Object.assign(
{ {
@ -112,7 +204,7 @@ export const createVm2Pool = ({ min, max, ...limits }) => {
let variablesText = ''; let variablesText = '';
if (variables && variables.result) { if (variables && variables.result) {
await CollectionUtil.asyncForEach(variables.result, async v => { await CollectionUtil.asyncForEach(variables.result, async v => {
if (!DebuggerService.systemVariables.filter(x => x === v.name)[0]) { if (!systemVariables.filter(x => x === v.name)[0]) {
if (v.value.value) { if (v.value.value) {
variablesText = `${variablesText} \n ${v.name}: ${v.value.value}`; variablesText = `${variablesText} \n ${v.name}: ${v.value.value}`;
} }

View file

@ -1230,7 +1230,7 @@ export class GBMinService {
const t = new SystemKeywords(); const t = new SystemKeywords();
GBLog.info(`BASIC (${min.botId}): Upload done for ${attachmentData.fileName}.`); GBLog.info(`BASIC (${min.botId}): Upload done for ${attachmentData.fileName}.`);
const handle = WebAutomationServices.cyrb53(min.botId + attachmentData.fileName); const handle = WebAutomationServices.cyrb53({pid:0, str:min.botId + attachmentData.fileName});
let data = Fs.readFileSync(attachmentData.localPath); let data = Fs.readFileSync(attachmentData.localPath);
const gbfile = { const gbfile = {

14
src/api.ts Normal file
View file

@ -0,0 +1,14 @@
import { ImageProcessingServices } from '../packages/basic.gblib/services/ImageProcessingServices.js';
import { SystemKeywords } from '../packages/basic.gblib/services/SystemKeywords.js';
import { WebAutomationServices } from '../packages/basic.gblib/services/WebAutomationServices.js';
import { DialogKeywords } from '../packages/basic.gblib/services/DialogKeywords.js';
import { DebuggerService } from '../packages/basic.gblib/services/DebuggerService.js';
export interface GBAPI
{
systemKeywords: SystemKeywords;
dialogKeywords: DialogKeywords;
imageProcessing: ImageProcessingServices;
webAutomation: WebAutomationServices;
debuggerService: DebuggerService;
}

View file

@ -40,7 +40,9 @@ import https from 'https';
import http from 'http'; import http from 'http';
import mkdirp from 'mkdirp'; import mkdirp from 'mkdirp';
import Path from 'path'; import Path from 'path';
import * as Fs from 'fs'; import swaggerUI from 'swagger-ui-dist';
import path from 'path';
import fs from 'fs';
import { GBLog, GBMinInstance, IGBCoreService, IGBInstance, IGBPackage } from 'botlib'; import { GBLog, GBMinInstance, IGBCoreService, IGBInstance, IGBPackage } from 'botlib';
import { GBAdminService } from '../packages/admin.gbapp/services/GBAdminService.js'; import { GBAdminService } from '../packages/admin.gbapp/services/GBAdminService.js';
import { AzureDeployerService } from '../packages/azuredeployer.gbapp/services/AzureDeployerService.js'; import { AzureDeployerService } from '../packages/azuredeployer.gbapp/services/AzureDeployerService.js';
@ -84,6 +86,7 @@ export class GBServer {
} }
const server = express(); const server = express();
this.initEndpointsDocs(server);
GBServer.globals.server = server; GBServer.globals.server = server;
GBServer.globals.httpsServer = null; GBServer.globals.httpsServer = null;
@ -120,7 +123,7 @@ export class GBServer {
process.env.PWD = process.cwd(); process.env.PWD = process.cwd();
const workDir = Path.join(process.env.PWD, 'work'); const workDir = Path.join(process.env.PWD, 'work');
if (!Fs.existsSync(workDir)) { if (!fs.existsSync(workDir)) {
mkdirp.sync(workDir); mkdirp.sync(workDir);
} }
@ -299,7 +302,7 @@ export class GBServer {
if (process.env.CERTIFICATE_PFX) { if (process.env.CERTIFICATE_PFX) {
const options1 = { const options1 = {
passphrase: process.env.CERTIFICATE_PASSPHRASE, passphrase: process.env.CERTIFICATE_PASSPHRASE,
pfx: Fs.readFileSync(process.env.CERTIFICATE_PFX) pfx: fs.readFileSync(process.env.CERTIFICATE_PFX)
}; };
const httpsServer = https.createServer(options1, server).listen(port, mainCallback); const httpsServer = https.createServer(options1, server).listen(port, mainCallback);
@ -313,7 +316,7 @@ export class GBServer {
if (process.env[certPfxEnv] && process.env[certPassphraseEnv] && process.env[certDomainEnv]) { if (process.env[certPfxEnv] && process.env[certPassphraseEnv] && process.env[certDomainEnv]) {
const options = { const options = {
passphrase: process.env[certPassphraseEnv], passphrase: process.env[certPassphraseEnv],
pfx: Fs.readFileSync(process.env[certPfxEnv]) pfx: fs.readFileSync(process.env[certPfxEnv])
}; };
httpsServer.addContext(process.env[certDomainEnv], options); httpsServer.addContext(process.env[certDomainEnv], options);
} else { } else {
@ -325,4 +328,25 @@ export class GBServer {
server.listen(port, mainCallback); server.listen(port, mainCallback);
} }
} }
public static initEndpointsDocs(app: express.Application) {
const ENDPOINT = '/docs';
const SWAGGER_FILE_NAME = 'swagger.yaml';
const swaggerUiAssetPath = swaggerUI.getAbsoluteFSPath();
// A workaround for swagger-ui-dist not being able to set custom swagger URL
const indexContent = fs
.readFileSync(path.join(swaggerUiAssetPath, 'swagger-initializer.js'))
.toString()
.replace('https://petstore.swagger.io/v2/swagger.json', `/${SWAGGER_FILE_NAME}`);
app.get(`${ENDPOINT}/swagger-initializer.js`, (req, res) => res.send(indexContent));
// Serve the swagger-ui assets
app.use(ENDPOINT, express.static(swaggerUiAssetPath));
// Serve the swagger file
app.get(`/${SWAGGER_FILE_NAME}`, (req, res) => {
res.sendFile(path.join(process.env.PWD,SWAGGER_FILE_NAME));
});
}
} }

View file

@ -1,54 +0,0 @@
{
"swagger": "2.0",
"info": {
"version": "3.0.0",
"title": "General Bots API",
"description": "General Bots API definitions.",
"contact": {
"name": "Pragmatismo",
"url": "https://gb.pragmatismo.com.br",
"email": "info@pragmatismo.com.br"
},
"x-ms-api-annotation": {
"status": "Production"
}
},
"host": "f993e4828c0d50.lhr.life",
"basePath": "/",
"schemes": [
"https"
],
"consumes": [],
"produces": [],
"paths": {
"/api/v2/dev-perdomo/dialog/talk": {
"post": {
"consumes":[
"text/plain; charset=utf-8"
],
"summary": "Talk to the user.",
"description": "Talk to the user.",
"x-ms-no-generic-test": true,
"operationId": "talk",
"responses": {
"200": {
"description": "OK"
}
}
}
}
},
"definitions": {
"TalkRequest": {
"type": "object",
"properties": {
"pid": {
"type": "string"
},
"text": {
"type": "string"
}
}
}
}
}

2956
swagger.yaml Normal file

File diff suppressed because it is too large Load diff

42
tsconfig.api.json Normal file
View file

@ -0,0 +1,42 @@
{
"compilerOptions": {
"strict": false,
"allowJs": true,
"downlevelIteration": true,
"baseUrl": "./",
"declaration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"esModuleInterop": true,
"skipLibCheck": true,
"mapRoot": "./dist/",
"moduleResolution": "Node",
"module": "ESNext",
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"outDir": "./dist",
"paths": {
"*": [
"types/*"
],
"botlib/*": [
"node_modules/botlib/*"
],
"pragmatismo-io-framework/*": [
"node_modules/pragmatismo-io-framework/*"
]
},
"sourceMap": true,
"target": "ESNext",
"typeRoots": [
"node_modules/@types"
]
},
"include": [
"src/api.ts",
],
"exclude": [
"dist",
"node_modules"
]
}