diff --git a/ExternalCommands/External.ws.ws.js b/ExternalCommands/External.ws.ws.js index 0413f6b..0843116 100644 --- a/ExternalCommands/External.ws.ws.js +++ b/ExternalCommands/External.ws.ws.js @@ -13,7 +13,7 @@ export default class WsCommand extends SourceCommand { * @param {IContext} context * @returns {Promise} */ - async #loadDataAsync(sourceName, context) { + async _loadDataAsync(sourceName, context) { const [connectionName, command] = await Promise.all([ this.connectionName.getValueAsync(context), this.toCustomFormatHtmlAsync(context), diff --git a/Models/CacheCommands/BaseCacheUtil.js b/Models/CacheCommands/BaseCacheUtil.js new file mode 100644 index 0000000..e64cc50 --- /dev/null +++ b/Models/CacheCommands/BaseCacheUtil.js @@ -0,0 +1,20 @@ +import ConnectionInfo from "../Connection/ConnectionInfo"; +export default class BaseCacheUtil { + /**@type {ConnectionInfo}*/ + connectionInfo; + channel; + + /**@param {ConnectionInfo} */ + constractor(connectionInfo,settings) { + this.connectionInfo = connectionInfo; + this.settings = settings + } + + /** + * @returns {Promise} + */ + connectAsync() {} + + /**@returns {Promise} */ + createDeleteChannel() {} +} diff --git a/Models/CacheCommands/RabbitMQCacheUtil.js b/Models/CacheCommands/RabbitMQCacheUtil.js new file mode 100644 index 0000000..fb3ba6b --- /dev/null +++ b/Models/CacheCommands/RabbitMQCacheUtil.js @@ -0,0 +1,51 @@ +import ConnectionInfo from "../Connection/ConnectionInfo.js"; +import BaseCacheUtil from "./BaseCacheUtil.js"; +import * as ampq from "amqplib/callback_api"; +import RabbitMQSetting from "./RabbitMqSetting.js"; +export default class RabbitMQCacheUtil extends BaseCacheUtil { + /**@type {ConnectionInfo}*/ + connectionInfo; + /**@type {RabbitMQSetting} */ + settings; + /** @type {ampq.Channel} */ + channel; + + /** + * @param {ConnectionInfo} + * @param {RabbitMQSetting} settings + */ + constractor(connectionInfo, settings) { + super(connectionInfo,settings); + } + /** + * @returns {Promise} + */ + connectAsync() { + return new Promise((resolve, reject) => { + ampq.connect(this.settings.address, function (error0, connection) { + if (error0) { + reject(error0); + } + connection.createChannel(function (error1, channel) { + if (error1) { + reject(error1); + } + this.channel = channel; + resolve(); + }); + }); + }); + } + /** + * @param {string} queueName + * @returns {Promise} + */ + createDeleteChannel(queueName) { + channel.assertQueue(queueName, { + durable: true, + }); + this.channel.consume(queueName, async function (msg) { + await this.connection.deleteAllCache(); + }); + } +} diff --git a/Models/CacheCommands/RabbitMqSetting.js b/Models/CacheCommands/RabbitMqSetting.js new file mode 100644 index 0000000..6d0569e --- /dev/null +++ b/Models/CacheCommands/RabbitMqSetting.js @@ -0,0 +1,5 @@ +export default class RabbitMQSetting { + /** @type {string} */ + address; + } + \ No newline at end of file diff --git a/Models/Connection/ConnectionInfo.js b/Models/Connection/ConnectionInfo.js index 7bbe2d7..a899701 100644 --- a/Models/Connection/ConnectionInfo.js +++ b/Models/Connection/ConnectionInfo.js @@ -3,7 +3,7 @@ import DataSourceCollection from "../../renderEngine/Source/DataSourceCollection import IRoutingRequest from "../IRoutingRequest.js"; import Request from "../request.js"; import ILoadPageResult from "./ILoadPageResult.js"; - +import CacheResult from "../options/CacheResult.js"; export default class ConnectionInfo { /** @type {string} */ name; @@ -69,4 +69,21 @@ export default class ConnectionInfo { ); } } + /** + * + * @param {string} key + * @returns {Promise} + */ + async loadContentAsync(key) {} + + /** + * @param {string} key + * @param {string} content + * @param {NodeJS.Dict} properties + * @returns {Promise} + */ + async addCacheContentAsync(key, content, properties) {} + + /** @returns {Promise} */ + async deleteAllCache() {} } diff --git a/Models/Connection/SqliteConnectionInfo.js b/Models/Connection/SqliteConnectionInfo.js index 0019f20..fa36a91 100644 --- a/Models/Connection/SqliteConnectionInfo.js +++ b/Models/Connection/SqliteConnectionInfo.js @@ -4,6 +4,8 @@ import SqliteSettingData from "./SqliteSettingData.js"; import DataSourceCollection from "../../renderEngine/Source/DataSourceCollection.js"; import CancellationToken from "../../renderEngine/Cancellation/CancellationToken.js"; import Request from "../request.js"; +import CacheResult from "./../options/CacheResult.js"; + export default class SqliteConnectionInfo extends ConnectionInfo { /** @type {SqliteSettingData} */ settings; @@ -36,9 +38,9 @@ export default class SqliteConnectionInfo extends ConnectionInfo { } }); }); - const retVal = new DataSourceCollection( - [ Array.isArray(rows) ? rows : [rows]] - ); + const retVal = new DataSourceCollection([ + Array.isArray(rows) ? rows : [rows], + ]); return retVal; } finally { @@ -47,7 +49,6 @@ export default class SqliteConnectionInfo extends ConnectionInfo { } } } - /** * @param {Request} request * @param {CancellationToken} cancellationToken @@ -59,4 +60,83 @@ export default class SqliteConnectionInfo extends ConnectionInfo { async testConnectionAsync() { throw new Error("test connection is not supported in sqlite"); } + + /** + * + * @param {string} key + * @param {CancellationToken} cancellationToken + * @returns {Promise} + */ + async loadContentAsync(key, cancellationToken) { + /**@type {sqlite3.Database} */ + let database; + try { + database = new sqlite3.Database(this.settings.dbPath); + const result = await this.#executeSqliteQuery( + database, + `SELECT * FROM ${this.settings.tableName} WHERE key = ?`, + [key] + ); + return result[0]; + } finally { + if (database) { + database.close(); + } + } + } + + /** + * @param {string} key + * @param {string} content + * @param {NodeJS.Dict} properties + * @returns {Promise} + */ + async addCacheContentAsync(key, content, properties) { + const database = new sqlite3.Database(this.settings.dbPath); + try { + const query = ` + BEGIN TRANSACTION; + WITH Selected AS ( + SELECT * FROM ${this.settings.tableName} WHERE key = ? + ) + DELETE FROM ${this.settings.tableName} WHERE key = ? AND EXISTS (SELECT 1 FROM Selected); + INSERT INTO ${this.settings.tableName} (key, content, properties) VALUES (?, ?, ?); + COMMIT TRANSACTION; + `; + const result = this.#executeSqliteQuery(database, query, [ + key, + content, + JSON.stringify(properties), + ]); + return result; + } catch (err) { + throw new Error("error in add cache : " + err); + } finally { + if (database) { + database.close(); + } + } + } + /** + * + * @param {sqlite3.Database} db + * @param {string} query + * @param {any[]} params + * @returns {Promise} + */ + #executeSqliteQuery(db, query, params = []) { + return new Promise((resolve, reject) => { + db.all(query, params, (err, rows) => { + if (err) { + reject(err); + } else { + resolve(rows); + } + }); + }); + } + /** @returns {Promise} */ + async deleteAllCache() { + return this.#executeSqliteQuery(`DELETE FROM ${this.settings.tableName}`); + } } diff --git a/Models/Connection/SqliteSettingData.js b/Models/Connection/SqliteSettingData.js index b26b560..43d9c8b 100644 --- a/Models/Connection/SqliteSettingData.js +++ b/Models/Connection/SqliteSettingData.js @@ -3,4 +3,6 @@ export default class SqlSettingData { dbPath; /** @type {string} */ query; + /** @type {string} */ + tableName } diff --git a/Models/ServiceSettings.js b/Models/ServiceSettings.js index 1ed45d1..8936662 100644 --- a/Models/ServiceSettings.js +++ b/Models/ServiceSettings.js @@ -15,6 +15,8 @@ export default class ServiceSettings { callConnection; /** @type {ConnectionInfo} */ ilUpdateConnection; + /** @type {ConnectionInfo} */ + cacheConnection; /** * @param {HostServiceOptions} options */ @@ -33,6 +35,15 @@ export default class ServiceSettings { if ("ilupdate" in this._connections) { this.ilUpdateConnection = this._connections.ilupdate; } + if ("cacheconnection" in this._connections) { + this.cacheConnection = this._connections.cacheconnection; + } + if ( + options.CacheSettings?.isEnabled && + !("cacheconnection" in this._connections) + ) { + throw new Error("must add cacheConnection if you set Cache to true"); + } } /** diff --git a/Models/options/CacheResult.js b/Models/options/CacheResult.js new file mode 100644 index 0000000..be2084c --- /dev/null +++ b/Models/options/CacheResult.js @@ -0,0 +1,8 @@ +export default class CacheResult { + /**@type {string} */ + key; + /** @type {string} */ + content; + /**@type {NodeJS.Dict}*/ + properties; +} diff --git a/Models/options/CacheSettings.js b/Models/options/CacheSettings.js new file mode 100644 index 0000000..44502e5 --- /dev/null +++ b/Models/options/CacheSettings.js @@ -0,0 +1,13 @@ +export default class CacheSettings { + /**@type {string[]} */ + responseHeaders; + /**@type {string[]} */ + requestMethods; + /** @type {boolean} */ + isEnabled; + //for now is rabbit + /** @type {"Rabbit"} */ + utilType + /**@type { NodeJS.Dict } */ + utilSetting +} diff --git a/Models/options/hostServiceOptions.js b/Models/options/hostServiceOptions.js index fcc974a..86ebe51 100644 --- a/Models/options/hostServiceOptions.js +++ b/Models/options/hostServiceOptions.js @@ -1,4 +1,5 @@ import IStreamerEngineOptions from "./IStreamerEngineOptions.js"; +import CacheSettings from "./CacheSettings.js"; export default class HostServiceOptions { /**@type {"Sql"|"Edge"|"File"} */ @@ -15,4 +16,6 @@ export default class HostServiceOptions { Streamer; /**@type {NodeJS.Dict} */ Settings; + /**@type {CacheSettings}*/ + CacheSettings; } diff --git a/endPoint/h2HttpHostEndPoint.js b/endPoint/h2HttpHostEndPoint.js index f5a12d3..c972438 100644 --- a/endPoint/h2HttpHostEndPoint.js +++ b/endPoint/h2HttpHostEndPoint.js @@ -63,92 +63,100 @@ export default class H2HttpHostEndPoint extends SecureHttpHostEndPoint { return http2 .createSecureServer(this.#options) .on("stream", async (stream, headers) => { - /** @type {Request} */ - let cms = null; - /**@type {BinaryContent[]} */ - const fileContents = []; - /**@type {NodeJS.Dict} */ - const jsonHeaders = {}; - /**@type {NodeJS.Dict} */ - const formFields = {}; - const method = headers[":method"]; - const url = headers[":path"]; - let bodyStr = ""; - const createCmsAndCreateResponseAsync = async () => { - cms = await this._createCmsObjectAsync( - url, - method, - headers, - formFields, - jsonHeaders, - stream.session.socket, - bodyStr, - true - ); - const result = await this._service.processAsync(cms, fileContents); - const [code, headerList, body] = await result.getResultAsync(); - headerList[":status"] = code; - stream.respond(headerList); - stream.end(body); - }; - stream.on("data", (chunk) => { - if (headers["content-type"] === "application/json") { - bodyStr += chunk; - } - }); - stream.on("end", () => { - if (headers["content-type"] === "application/json") { - createCmsAndCreateResponseAsync(); - } - }); - stream.on("error", (ex) => { - if (ex.code != "ERR_STREAM_WRITE_AFTER_END") { - console.error("HTTP/2 server stream error", ex); - } - stream.destroy(ex); - }); - try { - if (headers["content-length"]) { - if (headers["content-type"]?.startsWith("multipart/form-data")) { - /**@type {Array}*/ - const bb = busboy({ headers: headers }); - bb.on("file", (name, file, info) => { - const ContentParts = []; - file.on("data", (x) => ContentParts.push(x)); - file.on("end", async () => { - const content = new BinaryContent(); - content.url = `${headers["host"]}${url}`; - content.mime = info.mimeType.toLowerCase(); - content.name = info.filename; - content.payload = Buffer.concat(ContentParts); - fileContents.push(content); + this._checkCacheAsync(stream, headers, async () => { + /** @type {Request} */ + let cms = null; + /**@type {BinaryContent[]} */ + const fileContents = []; + /**@type {NodeJS.Dict} */ + const jsonHeaders = {}; + /**@type {NodeJS.Dict} */ + const formFields = {}; + const method = headers[":method"]; + const url = headers[":path"]; + let bodyStr = ""; + const createCmsAndCreateResponseAsync = async () => { + cms = await this._createCmsObjectAsync( + url, + method, + headers, + formFields, + jsonHeaders, + stream.session.socket, + bodyStr, + true + ); + const result = await this._service.processAsync(cms, fileContents); + const [code, headerList, body] = await result.getResultAsync(); + this.addCacheContentAsync( + `${headers.host}${headers[":path"]}`, + body, + headers, + headers[":method"] + ); + headerList[":status"] = code; + stream.respond(headerList); + stream.end(body); + }; + stream.on("data", (chunk) => { + if (headers["content-type"] === "application/json") { + bodyStr += chunk; + } + }); + stream.on("end", () => { + if (headers["content-type"] === "application/json") { + createCmsAndCreateResponseAsync(); + } + }); + stream.on("error", (ex) => { + if (ex.code != "ERR_STREAM_WRITE_AFTER_END") { + console.error("HTTP/2 server stream error", ex); + } + stream.destroy(ex); + }); + try { + if (headers["content-length"]) { + if (headers["content-type"]?.startsWith("multipart/form-data")) { + /**@type {Array}*/ + const bb = busboy({ headers: headers }); + bb.on("file", (name, file, info) => { + const ContentParts = []; + file.on("data", (x) => ContentParts.push(x)); + file.on("end", async () => { + const content = new BinaryContent(); + content.url = `${headers["host"]}${url}`; + content.mime = info.mimeType.toLowerCase(); + content.name = info.filename; + content.payload = Buffer.concat(ContentParts); + fileContents.push(content); + }); }); - }); - bb.on("field", (name, val, info) => { - formFields[name] = val; - if (name.startsWith("_")) { - jsonHeaders[name] = val; - } - }); - bb.on("close", createCmsAndCreateResponseAsync); - stream.pipe(bb); + bb.on("field", (name, val, info) => { + formFields[name] = val; + if (name.startsWith("_")) { + jsonHeaders[name] = val; + } + }); + bb.on("close", createCmsAndCreateResponseAsync); + stream.pipe(bb); + } + } else { + await createCmsAndCreateResponseAsync(); } - } else { - await createCmsAndCreateResponseAsync(); - } - } catch (ex) { - if (ex.code != "ERR_HTTP2_INVALID_STREAM") { - console.error("HTTP/2 server error", ex); - try { - stream.respond({ - ":status": StatusCodes.INTERNAL_SERVER_ERROR, - }); - } catch (ex) { + } catch (ex) { + if (ex.code != "ERR_HTTP2_INVALID_STREAM") { console.error("HTTP/2 server error", ex); + try { + stream.respond({ + ":status": StatusCodes.INTERNAL_SERVER_ERROR, + }); + } catch (ex) { + console.error("HTTP/2 server error", ex); + } + stream.end(ex.toString()); } - stream.end(ex.toString()); } - } + }); }) .on("clientError", (ex) => { switch (ex.code) { @@ -180,4 +188,33 @@ export default class H2HttpHostEndPoint extends SecureHttpHostEndPoint { }); }); } + + async _checkCacheAsync(stream, headers, next) { + if ( + this._service._options.CacheSettings.requestMethods.includes( + headers[":method"] + ) && + this._service.settings.cacheConnection + ) { + let connection = this._service.settings.cacheConnection; + const fullUrl = `${headers.host}${headers[":path"]}`; + const cacheOptions = await connection.loadContentAsync(fullUrl); + if (cacheOptions) { + try { + stream.respond({ + ":status": 200, + "content-type": cacheOptions.contentType, + ...this._service._options.CacheSettings.responseHeaders, + }); + stream.end(cacheOptions.file); + } catch (err) { + next(); + } + } else { + next(); + } + } else { + next(); + } + } } diff --git a/endPoint/httpHostEndPoint.js b/endPoint/httpHostEndPoint.js index a57cc56..4893b17 100644 --- a/endPoint/httpHostEndPoint.js +++ b/endPoint/httpHostEndPoint.js @@ -9,6 +9,8 @@ import { IncomingMessage, ServerResponse } from "http"; import BasisCoreException from "../models/Exceptions/BasisCoreException.js"; import BinaryContent from "../fileStreamer/Models/BinaryContent.js"; import HostService from "../services/hostService.js"; +import RabbitMQCacheUtil from "./../Models/CacheCommands/RabbitMQCacheUtil.js"; +import BaseCacheUtil from "../Models/CacheCommands/BaseCacheUtil.js"; let requestId = 0; class HttpHostEndPoint extends HostEndPoint { @@ -31,6 +33,20 @@ class HttpHostEndPoint extends HostEndPoint { /**@returns {Promise} */ async listenAsync() { + /** @type {BaseCacheUtil} */ + let cacheUtil; + switch (this._service._options?.CacheSettings.utilType) { + case "Rabbit": { + cacheUtil = new RabbitMQCacheUtil( + this._service.settings.cacheConnection, + this._service._options.CacheSettings.utilSetting + ); + } + } + if (cacheUtil) { + await cacheUtil.connectAsync(); + await cacheUtil.createDeleteChannel(); + } const server = this._createServer(); await this.initializeAsync(); server @@ -166,9 +182,71 @@ class HttpHostEndPoint extends HostEndPoint { next(); } } + /** + * @param {IncomingMessage} req + * @param {ServerResponse} res + */ + async _checkCacheAsync(req, res, next) { + if ( + this._service._options.CacheSettings?.isEnabled && + this._service._options.CacheSettings.requestMethods.includes(req.method) + ) { + let connection = this._service.settings.cacheConnection; + const fullUrl = `${req.headers.host}${req.url}`; + const cacheResults = await connection.loadContentAsync(fullUrl); + if (cacheResults) { + res.writeHead(200, { + ...cacheResults.properties, + }); + res.write(cacheResults.content ?? ""); + res.end(); + } else { + next(); + } + } else { + next(); + } + } + /** + * + * @param {string} key + * @param {string} content + * @param {NodeJS.Dict} headers + * @returns {Promise} + */ + async addCacheContentAsync(key, content, headers, method) { + if ( + this._service._options.CacheSettings?.isEnabled && + this._service._options.CacheSettings.requestMethods.includes(method) + ) { + const savedHeaders = this.findProperties( + headers, + this._service._options.CacheSettings.responseHeaders + ); + await this._service.settings.cacheConnection.addCacheContentAsync( + key, + content, + savedHeaders + ); + } + } + /** + * + * @param {NodeJS.Dict} headers + * @param {string[]} keys + * @returns + */ + findProperties(headers, keys) { + const properties = {}; + keys.forEach((key) => { + if (headers.hasOwnProperty(key)) { + properties[key] = headers[key]; + } + }); + return properties; + } initializeAsync() { return this._service.initializeAsync(); } } - export default HttpHostEndPoint; diff --git a/endPoint/nonSecureHttpHostEndPoint.js b/endPoint/nonSecureHttpHostEndPoint.js index e5f5939..ebe35cf 100644 --- a/endPoint/nonSecureHttpHostEndPoint.js +++ b/endPoint/nonSecureHttpHostEndPoint.js @@ -11,7 +11,7 @@ export default class NonSecureHttpHostEndPoint extends HttpHostEndPoint { * @param {HostService} service */ constructor(ip, port, service) { - super(ip, port, service); + super(ip, port,service); } _createServer() { @@ -19,27 +19,35 @@ export default class NonSecureHttpHostEndPoint extends HttpHostEndPoint { try { /** @type {Request} */ let cms = null; - this._handleContentTypes(req, res, async () => { - const createCmsAndCreateResponseAsync = async () => { - cms = await this._createCmsObjectAsync( - req.url, - req.method, - req.headers, - req.formFields, - req.jsonHeaders ? req.jsonHeaders : {}, - req.socket, - req.bodyStr, - false - ); - const result = await this._service.processAsync( - cms, - req.fileContents - ); - const [code, headers, body] = await result.getResultAsync(); - res.writeHead(code, headers); - res.end(body); - }; - await createCmsAndCreateResponseAsync(); + this._checkCacheAsync(req, res, async () => { + this._handleContentTypes(req, res, async () => { + const createCmsAndCreateResponseAsync = async () => { + cms = await this._createCmsObjectAsync( + req.url, + req.method, + req.headers, + req.formFields, + req.jsonHeaders ? req.jsonHeaders : {}, + req.socket, + req.bodyStr, + false + ); + const result = await this._service.processAsync( + cms, + req.fileContents + ); + const [code, headers, body] = await result.getResultAsync(); + this.addCacheContentAsync( + `${req.headers.host}${req.url}`, + body, + headers, + req.method + ); + res.writeHead(code, headers); + res.end(body); + }; + await createCmsAndCreateResponseAsync(); + }); }); } catch (ex) { console.error(ex); diff --git a/endPoint/secureHttpHostEndPoint.js b/endPoint/secureHttpHostEndPoint.js index 3e26783..5a559c3 100644 --- a/endPoint/secureHttpHostEndPoint.js +++ b/endPoint/secureHttpHostEndPoint.js @@ -4,8 +4,6 @@ import HttpHostEndPoint from "./HttpHostEndPoint.js"; import { HostService } from "../services/hostServices.js"; export default class SecureHttpHostEndPoint extends HttpHostEndPoint { - - /** @type {import("tls").SecureContextOptions} */ #options; /** @@ -15,7 +13,7 @@ export default class SecureHttpHostEndPoint extends HttpHostEndPoint { * @param {import("tls").SecureContextOptions} options */ constructor(ip, port, service, options) { - super(ip, port,service); + super(ip, port, service); this.#options = options; } @@ -24,33 +22,41 @@ export default class SecureHttpHostEndPoint extends HttpHostEndPoint { .createServer(this.#options, async (req, res) => { /** @type {Request} */ let cms = null; - this._handleContentTypes(req, res, () => { - const createCmsAndCreateResponseAsync = async () => { - cms = await this._createCmsObjectAsync( - req.url, - req.method, - req.headers, - req.formFields, - req.jsonHeaders ? req.jsonHeaders : {}, - req.socket, - req.bodyStr, - true - ); - const result = await this._service.processAsync( - cms, - req.fileContents - ); - const [code, headers, body] = await result.getResultAsync(); - res.writeHead(code, headers); - res.end(body); - }; - try { - createCmsAndCreateResponseAsync(); - } catch (ex) { - console.error(ex); - res.writeHead(StatusCodes.INTERNAL_SERVER_ERROR); - res.end(ex.toString()); - } + this._checkCacheAsync(req, res, async () => { + this._handleContentTypes(req, res, () => { + const createCmsAndCreateResponseAsync = async () => { + cms = await this._createCmsObjectAsync( + req.url, + req.method, + req.headers, + req.formFields, + req.jsonHeaders ? req.jsonHeaders : {}, + req.socket, + req.bodyStr, + true + ); + const result = await this._service.processAsync( + cms, + req.fileContents + ); + const [code, headers, body] = await result.getResultAsync(); + this.addCacheContentAsync( + `${req.headers.host}${req.url}`, + body, + headers, + req.method + ); + res.writeHead(code, headers); + res.end(body); + }; + try { + createCmsAndCreateResponseAsync(); + } catch (ex) { + console.error(ex); + res.writeHead(StatusCodes.INTERNAL_SERVER_ERROR); + res.end(ex.toString()); + } + }); }); }) .on("error", (er) => console.error(er)) diff --git a/index.js b/index.js index c69b3c7..2e7a368 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ import HostManager from "./hostManager.js"; import { HostManagerOptions } from "./models/model.js"; +import CacheSettings from "./models/options/CacheSettings.js"; /** @type {HostManagerOptions} */ const host = { diff --git a/package-lock.json b/package-lock.json index 23512d4..a240aa7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,8 @@ "http-status-codes": "^2.2.0", "imagemagick": "^0.1.3", "memorystream": "^0.3.1", + "mime": "^4.0.3", + "mime-db": "^1.52.0", "mime-types": "^2.1.35", "mongodb": "^6.3.0", "mssql": "^9.3.2", @@ -4520,15 +4522,17 @@ } }, "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.3.tgz", + "integrity": "sha512-KgUb15Oorc0NEKPbvfa0wRU+PItIEZmiv+pyAO2i0oTIVTJhlzMclU7w4RXWQrSOVH5ax/p/CkIO7KI4OyFJTQ==", + "funding": [ + "https://github.com/sponsors/broofa" + ], "bin": { - "mime": "cli.js" + "mime": "bin/cli.js" }, "engines": { - "node": ">=4.0.0" + "node": ">=16" } }, "node_modules/mime-db": { @@ -5914,6 +5918,18 @@ "node": ">=10" } }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/superagent/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -9774,10 +9790,9 @@ } }, "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.3.tgz", + "integrity": "sha512-KgUb15Oorc0NEKPbvfa0wRU+PItIEZmiv+pyAO2i0oTIVTJhlzMclU7w4RXWQrSOVH5ax/p/CkIO7KI4OyFJTQ==" }, "mime-db": { "version": "1.52.0", @@ -10756,6 +10771,12 @@ "yallist": "^4.0.0" } }, + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true + }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", diff --git a/package.json b/package.json index f891195..368721f 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,8 @@ "fileStreamerApi": "node test\\fileStreamer\\api.js", "fileStreamerWithApi": "node test\\fileStreamer\\WithApi.js", "inlinesourcexml": "node test/command/inlinesource/inlinesourcexmlCommand.js", - "bug": "node --trace-warnings --trace-deprecation test\\bug.js" + "bug": "node --trace-warnings --trace-deprecation test\\bug.js", + "cache": "node test\\connection\\sqlite\\cache.js" }, "type": "module", "dependencies": { @@ -73,6 +74,8 @@ "http-status-codes": "^2.2.0", "imagemagick": "^0.1.3", "memorystream": "^0.3.1", + "mime": "^4.0.3", + "mime-db": "^1.52.0", "mime-types": "^2.1.35", "mongodb": "^6.3.0", "mssql": "^9.3.2", diff --git a/renderEngine/Command/Source/BaseClasses/SourceCommand.js b/renderEngine/Command/Source/BaseClasses/SourceCommand.js index 6af778c..068726e 100644 --- a/renderEngine/Command/Source/BaseClasses/SourceCommand.js +++ b/renderEngine/Command/Source/BaseClasses/SourceCommand.js @@ -42,7 +42,7 @@ export default class SourceCommand extends CommandBase { async _executeCommandAsync(context) { if (this.members?.items.length > 0) { const name = await this.name.getValueAsync(context); - const dataSet = await this.#loadDataAsync(name, context); + const dataSet = await this._loadDataAsync(name, context); context.cancellation.throwIfCancellationRequested(); if (dataSet.items.length != this.members.items.length) { throw new BasisCoreException( @@ -63,7 +63,7 @@ export default class SourceCommand extends CommandBase { * @param {IContext} context * @returns {Promise} */ - async #loadDataAsync(sourceName, context) { + async _loadDataAsync(sourceName, context) { const [connectionName, command, paramList] = await Promise.all([ this.connectionName.getValueAsync(context), this.toCustomFormatHtmlAsync(context), diff --git a/renderEngine/Command/Source/ws.js b/renderEngine/Command/Source/ws.js index c870de5..4d8a39c 100644 --- a/renderEngine/Command/Source/ws.js +++ b/renderEngine/Command/Source/ws.js @@ -13,7 +13,7 @@ export default class WsCommand extends SourceCommand { * @param {IContext} context * @returns {Promise} */ - async #loadDataAsync(sourceName, context) { + async _loadDataAsync(sourceName, context) { const [connectionName, command] = await Promise.all([ this.connectionName.getValueAsync(context), this.toCustomFormatHtmlAsync(context), diff --git a/test.db b/test.db new file mode 100644 index 0000000..427b159 Binary files /dev/null and b/test.db differ diff --git a/test/connection/edge/edge-script.py b/test/connection/edge/edge-script.py index dba86e1..71743b7 100644 --- a/test/connection/edge/edge-script.py +++ b/test/connection/edge/edge-script.py @@ -1,18 +1,24 @@ from bclib import edge options = { - "endpoint": "127.0.0.1:8080", - "router": "web" + "endpoint": "127.0.0.1:8000", + "router": "restful" } app = edge.from_options(options) import asyncio -# #index 5 -# @app.web_action() -# async def process_web_remain_request(context: edge.WebContext): -# await asyncio.sleep(10) -# return "

hi from edge

" +#index 5 +@app.restful_action() +async def process_web_remain_request(context: edge.WebContext): + context.mime = edge.HttpMimeTypes.JPEG + context.response_type = edge.ResponseTypes.STATIC_FILE + cms = context.cms.cms + if "webserver" not in cms: + cms["webserver"] = dict() + cms["webserver"]["filepath"] = "C:\\Users\\Ali\\Desktop\\fingerfoofProduct1.jpg" + print(context.cms) + return context.cms @app.web_action() async def process_web_remain_request(context: edge.WebContext): diff --git a/test/connection/edge/simple.js b/test/connection/edge/simple.js index 2e29c46..e43fade 100644 --- a/test/connection/edge/simple.js +++ b/test/connection/edge/simple.js @@ -23,6 +23,10 @@ const host = { "Connections.edge.RoutingData": { endpoint: "127.0.0.1:8080", }, + "Connections.sqlite.cacheConnection" :{ + dbPath : "./../../../test.db", + table : "cache_results" + } }, }, }, diff --git a/test/connection/sqlite/cache.js b/test/connection/sqlite/cache.js new file mode 100644 index 0000000..8b118f2 --- /dev/null +++ b/test/connection/sqlite/cache.js @@ -0,0 +1,41 @@ +import HostManager from "../../../hostManager.js"; +import { HostManagerOptions } from "../../../models/model.js"; + +/** @type {HostManagerOptions} */ +const host = { + Lazy: true, + EndPoints: { + Main06: { + Type: "http", + Addresses: [ + { + EndPoint: "127.0.0.1:1563", + }, + ], + Active: true, + Routing: "mainService", + }, + }, + Services: { + mainService: { + Type: "http", + CacheSettings: { + requestMethods: "GET", + responseHeaders: ["content-type"], + isEnabled: true, + }, + Settings: { + "Connections.edge.RoutingData": { + endpoint: "127.0.0.1:8000", + }, + "Connections.sqlite.cacheConnection": { + dbPath: "test.db", + tableName: "cache_results", + }, + }, + }, + }, +}; + +const service = HostManager.fromJson(host); +service.listen(); diff --git a/test/connection/sqlite/table.sqlite b/test/connection/sqlite/table.sqlite new file mode 100644 index 0000000..93b2c0d --- /dev/null +++ b/test/connection/sqlite/table.sqlite @@ -0,0 +1,6 @@ +CREATE TABLE cache_results ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + key TEXT, + content TEXT, + properties TEXT +); \ No newline at end of file