diff --git a/.gitignore b/.gitignore index 213fb8e..682f60c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ il.json test1.db report.json cache +cachefiles diff --git a/ExternalCommands/external.fly.ws.js b/ExternalCommands/external.fly.ws.js new file mode 100644 index 0000000..c526888 --- /dev/null +++ b/ExternalCommands/external.fly.ws.js @@ -0,0 +1,32 @@ +import SourceCommand from "./../renderEngine/Command/Source/BaseClasses/SourceCommand.js"; +import ICommandResult from "./../renderEngine/Models/ICommandResult.js"; +export default class FlyCommand extends SourceCommand { + /** + * @param {object} flyIl + */ + constructor(flyIl) { + super(flyIl); + } + + /** + * @param {string} sourceName + * @param {IContext} context + * @returns {Promise} + */ + async _loadDataAsync(sourceName, context) { + const [connectionName, command] = await Promise.all([ + this.connectionName.getValueAsync(context), + this.toCustomFormatHtmlAsync(context), + ]); + const inputs = { + command, + dmnid: context.domainId, + }; + const encoder = new TextEncoder(); + const byteMessage = encoder.encode(JSON.stringify(inputs)); + const parameters = { + byteMessage, + }; + return await context.loadDataAsync(sourceName, connectionName, parameters); + } +} diff --git a/Models/Connection/ConnectionInfo.js b/Models/Connection/ConnectionInfo.js index a899701..789807d 100644 --- a/Models/Connection/ConnectionInfo.js +++ b/Models/Connection/ConnectionInfo.js @@ -59,6 +59,7 @@ export default class ConnectionInfo { convertJSONToDataSet(content) { if (content?.sources && Array.isArray(content?.sources)) { let retVal = []; + content.sources.forEach((source) => { retVal.push(source.data); }); diff --git a/Models/Connection/ConnectionUtil.js b/Models/Connection/ConnectionUtil.js index 7007649..57c3b5e 100644 --- a/Models/Connection/ConnectionUtil.js +++ b/Models/Connection/ConnectionUtil.js @@ -8,6 +8,7 @@ import MongoConnectionInfo from "./MongoConnectionInfo.js"; import SqliteConnectionInfo from "./SqLiteConnectionInfo.js"; import MySqlConnectionInfo from "./MySqlConnectionInfo.js"; import SocketConnectionInfo from "./SocketConnectionInfo.js"; +import WebConnectionInfo from "./WebConnectionInfo.js"; export default class ConnectionUtil { /** * @param {NodeJS.Dict} settings @@ -54,6 +55,9 @@ export default class ConnectionUtil { connection = new SocketConnectionInfo(parts[2], settings[item]); break; } + case "web" : { + connection = new WebConnectionInfo(parts[2],settings[item]) + } default: { if (parts[1] == "ws") { continue; diff --git a/Models/Connection/IMongoSettingData.js b/Models/Connection/IMongoSettingData.js index 09cbe0f..5735d2c 100644 --- a/Models/Connection/IMongoSettingData.js +++ b/Models/Connection/IMongoSettingData.js @@ -1,12 +1,12 @@ export default class IMongoSettingData { - /** @type {string} */ - dataBase; /** @type {string}*/ - endpoint; - /** @type {string}*/ - collection; - /** @type {string}*/ - method; - /**@type {object} */ - query; + address; + /** @type {?string}*/ + databaseName; + /** @type {?string}*/ + collectionName; + /** @type {string}*/ + method; + /**@type {object} */ + query; } diff --git a/Models/Connection/MongoConnectionInfo.js b/Models/Connection/MongoConnectionInfo.js index 67b0ed2..5fc9085 100644 --- a/Models/Connection/MongoConnectionInfo.js +++ b/Models/Connection/MongoConnectionInfo.js @@ -5,11 +5,13 @@ import CancellationToken from "../../renderEngine/Cancellation/CancellationToken import Request from "./../request.js"; import IMongoSettingData from "./IMongoSettingData.js"; import BasisCoreException from "../Exceptions/BasisCoreException.js"; +import IRoutingRequest from "../IRoutingRequest.js"; +import { http } from "winston"; export default class MongoConnectionInfo extends ConnectionInfo { + /**@type {MongoClient} */ + client; /** @type {IMongoSettingData} */ settings; - /**@param {MongoClient} client */ - client; /** * @param {string} name @@ -18,8 +20,8 @@ export default class MongoConnectionInfo extends ConnectionInfo { */ constructor(name, settings) { super(name); + this.client = new MongoClient(settings.address); this.settings = settings; - this.client = new MongoClient(this.settings.endpoint); } /** @@ -30,34 +32,69 @@ export default class MongoConnectionInfo extends ConnectionInfo { async loadDataAsync(parameters, cancellationToken) { try { await this.client.connect(); - const db = this.client.db(this.settings.dataBase); - const collection = db.collection(this.settings.collection); + const { collectionName, dbname } = parameters; + const db = this.client.db(dbname); + const collection = db.collection(collectionName); if (typeof collection[this.settings.method] != "function") { throw new BasisCoreException("invalid method"); } - const result = await collection[this.settings.method](this.settings.query) + const result = await collection[this.settings.method]( + this.settings.query + ); return new DataSourceCollection([await result.toArray()]); } catch (err) { throw err; - } - finally { - await this.client.close(); + } finally { + await this.client.close(); } } + /** + * Insert a document into a MongoDB collection. + * @param {string} sourceName + * @param {string} connectionName + * @param {string} databaseName + * @param {string} collectionName + * @param {Object} data + * @returns {Promise} + */ + async insertAsync( + sourceName, + connectionName, + databaseName, + collectionName, + data + ) { + try { + if (!databaseName) { + databaseName = this.settings.databaseName; + } + await this.client.connect(); + const database = client.db(databaseName); + const collection = database.collection(collectionName); + + const result = await collection.insertOne(data); + await client.close(); + return result._id; + } catch (error) { + throw new Error( + `Error in loading data for '${sourceName}' source command from '${connectionName}' connection: ${error.message}` + ); + } + } /** * @param {Request} request * @param {BinaryContent[]} fileContents * @returns {Promise} */ async testConnectionAsync() { - try{ - await this.client.connect() - return true - }catch(err){ - return false - }finally{ - await this.client.close() + try { + await this.client.connect(); + return true; + } catch (err) { + return false; + } finally { + await this.client.close(); } } } diff --git a/Models/Connection/SqlConnectionInfo.js b/Models/Connection/SqlConnectionInfo.js index 4b47424..c80aab2 100644 --- a/Models/Connection/SqlConnectionInfo.js +++ b/Models/Connection/SqlConnectionInfo.js @@ -26,10 +26,9 @@ export default class SqlConnectionInfo extends ConnectionInfo { ? `;requestTimeout=${this.settings.requestTimeout}` : "") ); - this.connectionPool.connect().catch((err)=>{ - console.log("error in connect",this.name,err) - }) - + this.connectionPool.connect().catch((err) => { + console.log("error in connect", this.name, err); + }); } /** @@ -70,9 +69,9 @@ export default class SqlConnectionInfo extends ConnectionInfo { */ async getRoutingDataAsync(request, cancellationToken) { const params = new sql.Table(); - params.columns.add("ParamType", sql.NVarChar(50)); - params.columns.add("ParamName", sql.NVarChar(100)); - params.columns.add("ParamValue", sql.NVarChar()); + params.columns.add("ParamType", sql.NVarChar(50)); + params.columns.add("ParamName", sql.NVarChar(100)); + params.columns.add("ParamValue", sql.NVarChar()); for (const type in request) { const group = request[type]; @@ -82,9 +81,9 @@ export default class SqlConnectionInfo extends ConnectionInfo { for (const key in query) { params.rows.add(name, key, query[key]?.toString()); } - } else if(name === "json"){ + } else if (name === "json") { params.rows.add(type, name, JSON.stringify(group[name])); - }else { + } else { params.rows.add(type, name, group[name]?.toString()); } } diff --git a/Models/Connection/WebConnectionInfo.js b/Models/Connection/WebConnectionInfo.js new file mode 100644 index 0000000..38eca56 --- /dev/null +++ b/Models/Connection/WebConnectionInfo.js @@ -0,0 +1,41 @@ +import WebSettingData from "./WebSettingData.js"; +import ConnectionInfo from "./ConnectionInfo.js"; +import DataSourceCollection from "../../renderEngine/Source/DataSourceCollection.js"; +import CancellationToken from "../../renderEngine/Cancellation/CancellationToken.js"; +import BasisCoreException from "../Exceptions/BasisCoreException.js"; + +export default class WebConnectionInfo extends ConnectionInfo { + /** @type {string} */ + url; + /** + * @param {string} name + * @param {WebSettingData} settings + */ + constructor(name, settings) { + super(name); + this.url = settings.url; + } + + /** + * @param {NodeJS.Dict} parameters + * @param {CancellationToken} cancellationToken + * @returns {Promise} + */ + async loadDataAsync(parameters, cancellationToken) { + try { + let body = new URLSearchParams(parameters); + const response = await fetch(url, { + method: "POST", + body: body, + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); + return new DataSourceCollection(await response.json()); + } catch (error) { + return new BasisCoreException( + "Failed to fetch request from url " + this.url + ); + } + } +} diff --git a/Models/Connection/WebSettingData.js b/Models/Connection/WebSettingData.js new file mode 100644 index 0000000..3fc25b6 --- /dev/null +++ b/Models/Connection/WebSettingData.js @@ -0,0 +1,5 @@ +export default class WebSettingData { + /** @type {string} */ + url; + } + \ No newline at end of file diff --git a/Models/Index4Response.js b/Models/Index4Response.js index c36bf13..35d9914 100644 --- a/Models/Index4Response.js +++ b/Models/Index4Response.js @@ -1,7 +1,6 @@ import fs from "fs"; import { StatusCodes } from "http-status-codes"; import RequestBaseResponse from "./requestBaseResponse.js"; -import im from "imagemagick"; import path from "path"; import Pako from "pako"; import Util from "../Util.js"; @@ -44,6 +43,8 @@ export default class Index4Response extends RequestBaseResponse { size, setting.deform ); + const dirPath = path.dirname(destinationPath); + Index4Response._createDirectoryIfNotExist(dirPath) await fs.promises.writeFile(destinationPath, newContent); finalPath = destinationPath; if (gzip) { @@ -157,7 +158,7 @@ static _mackWebpAsync(content, quality) { static _createDirectoryIfNotExist(filePath) { const pathDirectory = path.dirname(filePath); if (!fs.existsSync(pathDirectory)) { - fs.mkdirSync(pathDirectory); + fs.mkdirSync(pathDirectory,{recursive : true}); } } diff --git a/endPoint/h2HttpHostEndPoint.js b/endPoint/h2HttpHostEndPoint.js index f58e961..006b9a2 100644 --- a/endPoint/h2HttpHostEndPoint.js +++ b/endPoint/h2HttpHostEndPoint.js @@ -259,4 +259,4 @@ export default class H2HttpHostEndPoint extends SecureHttpHostEndPoint { next(); } } -} +} \ No newline at end of file diff --git a/endPoint/httpHostEndPoint.js b/endPoint/httpHostEndPoint.js index b8dd6a1..c856db7 100644 --- a/endPoint/httpHostEndPoint.js +++ b/endPoint/httpHostEndPoint.js @@ -354,4 +354,4 @@ class HttpHostEndPoint extends HostEndPoint { console.log(`server ip ${this._ip} and port ${this._port} killed`); } } -export default HttpHostEndPoint; +export default HttpHostEndPoint; \ No newline at end of file diff --git a/endPoint/nonSecureHttpHostEndPoint.js b/endPoint/nonSecureHttpHostEndPoint.js index a4143c3..aead43d 100644 --- a/endPoint/nonSecureHttpHostEndPoint.js +++ b/endPoint/nonSecureHttpHostEndPoint.js @@ -90,4 +90,4 @@ export default class NonSecureHttpHostEndPoint extends HttpHostEndPoint { }); return this._server; } -} +} \ No newline at end of file diff --git a/endPoint/secureHttpHostEndPoint.js b/endPoint/secureHttpHostEndPoint.js index ee3b342..dfb4956 100644 --- a/endPoint/secureHttpHostEndPoint.js +++ b/endPoint/secureHttpHostEndPoint.js @@ -104,4 +104,4 @@ export default class SecureHttpHostEndPoint extends HttpHostEndPoint { .on("tlsClientError", (er) => console.error(er)); return this._server; } -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6215c26..da83a07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,11 +27,13 @@ "node-html-encoder": "^0.0.2", "pako": "^2.1.0", "promised-sqlite3": "^2.1.0", + "rate-limiter-flexible": "^5.0.4", "sharp": "^0.33.5", "socket.io": "^4.7.5", "socket.io-client": "^4.7.5", "sqlite": "^5.1.1", "sqlite3": "^5.1.6", + "validator": "^13.12.0", "winston": "^3.13.0", "winston-transport": "^4.7.0", "ws": "^8.18.0" @@ -5840,10 +5842,11 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -8937,6 +8940,12 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/rate-limiter-flexible": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-5.0.4.tgz", + "integrity": "sha512-ftYHrIfSqWYDIJZ4yPTrgOduByAp+86gUS9iklv0JoXVM8eQCAjTnydCj1hAT4MmhmkSw86NaFEJ28m/LC1pKA==", + "license": "ISC" + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -10790,6 +10799,15 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/renderEngine/Command/Source/BaseClasses/SourceCommand.js b/renderEngine/Command/Source/BaseClasses/SourceCommand.js index 530fcd8..1346120 100644 --- a/renderEngine/Command/Source/BaseClasses/SourceCommand.js +++ b/renderEngine/Command/Source/BaseClasses/SourceCommand.js @@ -82,10 +82,7 @@ export default class SourceCommand extends CommandBase { params: JSON.stringify(paramList), } context.debugContext.addDebugInformation(`Parameter(s) Send To '${sourceName}' From Command '${connectionName}' in 180 ms`,[debugParams]) - const memberNames = this.members.items.map((item)=>{ - return item.name - }) - return await context.loadDataAsync(sourceName, connectionName, params,memberNames); + return await context.loadDataAsync(sourceName, connectionName, params); } /** diff --git a/renderEngine/Command/TrackerCommand.js b/renderEngine/Command/TrackerCommand.js new file mode 100644 index 0000000..f16937b --- /dev/null +++ b/renderEngine/Command/TrackerCommand.js @@ -0,0 +1,58 @@ +import CommandBase from "./CommandBase.js"; +import TokenUtil from "../Token/TokenUtil.js"; +import VoidResult from "../Models/VoidResult.js"; +import IContext from "../Context/IContext.js"; +import { query } from "mssql"; + +export default class TrackerCommand extends CommandBase { + /*** @type {IToken}*/ + connectionName; + /*** @type {IToken}*/ + collectionname; + /*** @type {IToken}*/ + dbname; + /*** @type {IToken}*/ + addcookie + + /** + * @param {object} trackerIl + */ + constructor(trackerIl) { + super(trackerIl); + this.connectionName = TokenUtil.getFiled(trackerIl, "ConnectionName"); + this.collectionname= TokenUtil.getFiled(trackerIl, "collectionname"); + this.dbname = TokenUtil.getFiled(trackerIl, "dbname"); + this.addcookie = TokenUtil.getFiled(trackerIl, "addcookie"); + this.cookieName = TokenUtil.getFiled(trackerIl, "cookieName"); + } + + /** + * @param {IContext} context + * @returns {Promise} + */ + async _executeCommandAsync(context) { + const [connectionName, collectionname, dbname, addcookie] = await Promise.all([ + this.connectionName.getValueAsync(context), + this.collectionname.getValueAsync(context), + this.dbname.getValueAsync(context), + this.addcookie.getValueAsync(context), + ]); + let params = { + collectionname,dbname,method : "insertOne", query : context.requestObj + } + const finalCollectionName = + collectionname || context.getDefault("trackingCollectionName", "monitoring"); + const {_id} = await context.loadDataAsync("",connectionName, params) + if (addcookie) { + let cookieName; + try { + cookieName = await this.cookieName?.getValueAsync(context); + } catch (error) { + cookieName = context.getDefault("monitoringCookieName", "monitoring_id"); + } + + context.addCookie(cookieName, _id.toString(), null, null); + } + return VoidResult.result; + } +} diff --git a/renderEngine/CommandUtil.js b/renderEngine/CommandUtil.js index 0abbe43..4142a0f 100644 --- a/renderEngine/CommandUtil.js +++ b/renderEngine/CommandUtil.js @@ -12,6 +12,7 @@ import CallCommand from "./Command/Collection/CallCommand.js"; import CookieCommand from "./Command/CookieCommand.js"; import ClientComponent from "./Command/ClientComponent.js"; import RepeaterCommand from "./Command/Collection/RepeaterCommand.js"; +import TrackerCommand from "./Command/TrackerCommand.js"; export default class CommandUtil { /** * @returns {Object.} @@ -33,6 +34,7 @@ export default class CommandUtil { cookie: { default: CookieCommand }, clientcomponent: { default: ClientComponent }, repeater: { default: RepeaterCommand }, + tracker : {default : TrackerCommand} }; return retVal; } diff --git a/renderEngine/Context/ContextBase.js b/renderEngine/Context/ContextBase.js index b8e6ef0..35a715d 100644 --- a/renderEngine/Context/ContextBase.js +++ b/renderEngine/Context/ContextBase.js @@ -14,7 +14,7 @@ export default class ContextBase extends IContext { * @param {string} domainId * @param {DebugContext} debugContext */ - constructor(repository, domainId,debugContext) { + constructor(repository, domainId, debugContext) { super(domainId); this.repository = new SourceRepository(repository); this.debugContext = debugContext; @@ -23,6 +23,9 @@ export default class ContextBase extends IContext { /** @param {IDataSource} dataSource */ addSource(dataSource) { this.repository.addSource(dataSource); + this.debugContext.addDebugInformation( + dataSource.id,dataSource.data + ); } /** @@ -49,9 +52,9 @@ export default class ContextBase extends IContext { */ loadDataAsync(sourceName, connectionName, parameters) {} - /** + /** * @param {Object} commandIl * @returns {CommandBase} */ - createCommand(commandIl) {} + createCommand(commandIl) {} } diff --git a/renderEngine/Context/LocalContext.js b/renderEngine/Context/LocalContext.js index e2997b5..3c19044 100644 --- a/renderEngine/Context/LocalContext.js +++ b/renderEngine/Context/LocalContext.js @@ -27,11 +27,10 @@ export default class LocalContext extends ContextBase { * @param {string} sourceName * @param {string} connectionName * @param {NodeJS.Dict} parameters - * @param {string[]} memberNames * @returns {Promise} */ - async loadDataAsync(sourceName, connectionName, parameters,memberNames) { - return this._owner.loadDataAsync(sourceName, connectionName, parameters,memberNames); + async loadDataAsync(sourceName, connectionName, parameters) { + return this._owner.loadDataAsync(sourceName, connectionName, parameters); } /** diff --git a/renderEngine/Context/RequestContext.js b/renderEngine/Context/RequestContext.js index 3f74bc0..f1fb073 100644 --- a/renderEngine/Context/RequestContext.js +++ b/renderEngine/Context/RequestContext.js @@ -62,10 +62,9 @@ export default class RequestContext extends ContextBase { * @param {string} sourceName * @param {string} connectionName * @param {NodeJS.Dict} parameters - * @param {string[]} memberNames * @returns {Promise} */ - async loadDataAsync(sourceName, connectionName, parameters, memberNames) { + async loadDataAsync(sourceName, connectionName, parameters) { try { parameters.dmnid = this.domainId; /** @type {ConnectionInfo} */ @@ -75,11 +74,6 @@ export default class RequestContext extends ContextBase { parameters, this.cancellation ); - result.items.forEach((item, index) => { - this.debugContext.addDebugInformation( - sourceName + "." + memberNames[index], - ); - }); return result; } catch (ex) { throw new BasisCoreException(