Merged in feature/IO-3052-Skia-Canvas-Handler (pull request #2000)
Feature/IO-3052 Skia Canvas Handler
This commit is contained in:
14
Dockerfile
14
Dockerfile
@@ -7,7 +7,6 @@ RUN dnf install -y git \
|
|||||||
&& dnf install -y nodejs \
|
&& dnf install -y nodejs \
|
||||||
&& dnf clean all
|
&& dnf clean all
|
||||||
|
|
||||||
|
|
||||||
# Install dependencies required by node-canvas
|
# Install dependencies required by node-canvas
|
||||||
RUN dnf install -y \
|
RUN dnf install -y \
|
||||||
gcc \
|
gcc \
|
||||||
@@ -19,9 +18,22 @@ RUN dnf install -y \
|
|||||||
libpng-devel \
|
libpng-devel \
|
||||||
make \
|
make \
|
||||||
python3 \
|
python3 \
|
||||||
|
fontconfig \
|
||||||
|
freetype \
|
||||||
python3-pip \
|
python3-pip \
|
||||||
|
wget \
|
||||||
|
unzip \
|
||||||
&& dnf clean all
|
&& dnf clean all
|
||||||
|
|
||||||
|
# Install Montserrat fonts
|
||||||
|
RUN cd /tmp \
|
||||||
|
&& wget https://images.imex.online/fonts/montserrat.zip -O montserrat.zip \
|
||||||
|
&& unzip montserrat.zip -d montserrat \
|
||||||
|
&& mv montserrat/montserrat/*.ttf /usr/share/fonts \
|
||||||
|
&& fc-cache -fv \
|
||||||
|
&& rm -rf /tmp/montserrat /tmp/montserrat.zip \
|
||||||
|
&& echo "Montserrat fonts installed and cached successfully."
|
||||||
|
|
||||||
# Set the working directory
|
# Set the working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
98
package-lock.json
generated
98
package-lock.json
generated
@@ -53,6 +53,7 @@
|
|||||||
"recursive-diff": "^1.0.9",
|
"recursive-diff": "^1.0.9",
|
||||||
"redis": "^4.7.0",
|
"redis": "^4.7.0",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
|
"skia-canvas": "^2.0.0",
|
||||||
"soap": "^1.1.6",
|
"soap": "^1.1.6",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"socket.io-adapter": "^2.5.5",
|
"socket.io-adapter": "^2.5.5",
|
||||||
@@ -3875,6 +3876,15 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cargo-cp-artifact": {
|
||||||
|
"version": "0.1.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.9.tgz",
|
||||||
|
"integrity": "sha512-6F+UYzTaGB+awsTXg0uSJA1/b/B3DDJzpKVRu0UmyI7DmNeaAl2RFHuTGIN6fEgpadRxoXGb7gbC1xo4C3IdyA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"cargo-cp-artifact": "bin/cargo-cp-artifact.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chalk": {
|
"node_modules/chalk": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
@@ -7136,6 +7146,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
||||||
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
|
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/parenthesis": {
|
||||||
|
"version": "3.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.8.tgz",
|
||||||
|
"integrity": "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/parse5": {
|
"node_modules/parse5": {
|
||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
|
||||||
@@ -7168,6 +7184,12 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path-browserify": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/path-is-absolute": {
|
"node_modules/path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
@@ -7847,6 +7869,73 @@
|
|||||||
"is-arrayish": "^0.3.1"
|
"is-arrayish": "^0.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/skia-canvas": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/skia-canvas/-/skia-canvas-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-wpYkmr9mCxBme5HAnlm6YOEiuaN9tIm9CL+HN8e5AFD4K2FAJXCcWiWvc9+LM8jUXt+AyYXgiwUTBxdQ6P+PEg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@mapbox/node-pre-gyp": "^1.0.11",
|
||||||
|
"cargo-cp-artifact": "^0.1",
|
||||||
|
"glob": "^11.0.0",
|
||||||
|
"path-browserify": "^1.0.1",
|
||||||
|
"simple-get": "^4.0.1",
|
||||||
|
"string-split-by": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/skia-canvas/node_modules/decompress-response": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mimic-response": "^3.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/skia-canvas/node_modules/mimic-response": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/skia-canvas/node_modules/simple-get": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"decompress-response": "^6.0.0",
|
||||||
|
"once": "^1.3.1",
|
||||||
|
"simple-concat": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/slick": {
|
"node_modules/slick": {
|
||||||
"version": "1.12.2",
|
"version": "1.12.2",
|
||||||
"resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz",
|
||||||
@@ -8239,6 +8328,15 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/string-split-by": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-split-by/-/string-split-by-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"parenthesis": "^3.1.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
"recursive-diff": "^1.0.9",
|
"recursive-diff": "^1.0.9",
|
||||||
"redis": "^4.7.0",
|
"redis": "^4.7.0",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
|
"skia-canvas": "^2.0.0",
|
||||||
"soap": "^1.1.6",
|
"soap": "^1.1.6",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"socket.io-adapter": "^2.5.5",
|
"socket.io-adapter": "^2.5.5",
|
||||||
|
|||||||
32
server/middleware/validateCanvasInputMiddleware.js
Normal file
32
server/middleware/validateCanvasInputMiddleware.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
const { isObject } = require("lodash");
|
||||||
|
|
||||||
|
const validateCanvasInputMiddleware = (req, res, next) => {
|
||||||
|
const { values, keys, override, w, h } = req.body;
|
||||||
|
|
||||||
|
if (!Array.isArray(values) || !Array.isArray(keys)) {
|
||||||
|
return res.status(400).send("Invalid input: 'values' and 'keys' must be arrays.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.some((value) => typeof value !== "number")) {
|
||||||
|
return res.status(400).send("Invalid input: 'values' must be an array of numbers.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keys.some((key) => typeof key !== "string")) {
|
||||||
|
return res.status(400).send("Invalid input: 'keys' must be an array of strings.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (override && !isObject(override)) {
|
||||||
|
return res.status(400).send("Override must be an object");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w && (!Number.isFinite(w) || w <= 0)) {
|
||||||
|
return res.status(400).send("Width must be a positive number");
|
||||||
|
}
|
||||||
|
if (h && (!Number.isFinite(h) || h <= 0)) {
|
||||||
|
return res.status(400).send("Height must be a positive number");
|
||||||
|
}
|
||||||
|
|
||||||
|
next(); // Proceed to the next middleware or route handler
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = validateCanvasInputMiddleware;
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
const { isObject } = require("lodash");
|
|
||||||
|
|
||||||
const validateCanvasRequestMiddleware = (req, res, next) => {
|
|
||||||
const { w, h, values, keys, override } = req.body;
|
|
||||||
if (!values || !keys) {
|
|
||||||
return res.status(400).send("Missing required data");
|
|
||||||
}
|
|
||||||
if (override && !isObject(override)) {
|
|
||||||
return res.status(400).send("Override must be an object");
|
|
||||||
}
|
|
||||||
if (w && (!Number.isFinite(w) || w <= 0)) {
|
|
||||||
return res.status(400).send("Width must be a positive number");
|
|
||||||
}
|
|
||||||
if (h && (!Number.isFinite(h) || h <= 0)) {
|
|
||||||
return res.status(400).send("Height must be a positive number");
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = validateCanvasRequestMiddleware;
|
|
||||||
@@ -1,124 +1,149 @@
|
|||||||
const { createCanvas } = require("canvas");
|
const { createCanvas } = require("canvas");
|
||||||
|
const { Canvas, FontLibrary } = require("skia-canvas");
|
||||||
const Chart = require("chart.js/auto");
|
const Chart = require("chart.js/auto");
|
||||||
const logger = require("../utils/logger");
|
|
||||||
|
|
||||||
const { backgroundColors, borderColors } = require("./canvas-colors");
|
const { backgroundColors, borderColors } = require("./canvas-colors");
|
||||||
const { isObject, defaultsDeep, isNumber } = require("lodash");
|
const { defaultsDeep, isNumber } = require("lodash");
|
||||||
|
|
||||||
|
const CANVAS_QUEUE_LIMIT = 100;
|
||||||
|
|
||||||
let isProcessing = false;
|
let isProcessing = false;
|
||||||
const requestQueue = [];
|
const requestQueue = [];
|
||||||
|
|
||||||
const processCanvasRequest = async (req, res) => {
|
try {
|
||||||
try {
|
FontLibrary.use("Montserrat", [
|
||||||
const { w, h, values, keys, override } = req.body;
|
"/usr/share/fonts/Montserrat-Regular.ttf",
|
||||||
logger.log("inbound-canvas-creation", "debug", "jsr", null, { w, h, values, keys, override });
|
"/usr/share/fonts/Montserrat-Bold.ttf",
|
||||||
|
"/usr/share/fonts/Montserrat-Italic.ttf"
|
||||||
|
]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
"Error loading fonts Skia Canvas Fonts, please be sure to install Montserrat font package",
|
||||||
|
error.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Set dimensions with defaults
|
// Utility to create a chart configuration
|
||||||
const width = isNumber(w) ? w : 500;
|
const getChartConfiguration = (keys, values, override) => {
|
||||||
const height = isNumber(h) ? h : 275;
|
const defaultConfiguration = {
|
||||||
|
type: "doughnut",
|
||||||
const configuration = {
|
data: {
|
||||||
type: "doughnut",
|
labels: keys,
|
||||||
data: {
|
datasets: [
|
||||||
labels: keys,
|
{
|
||||||
datasets: [
|
data: values,
|
||||||
{
|
backgroundColor: backgroundColors,
|
||||||
data: values,
|
borderColor: borderColors,
|
||||||
backgroundColor: backgroundColors,
|
borderWidth: 1
|
||||||
borderColor: borderColors,
|
}
|
||||||
borderWidth: 1
|
]
|
||||||
}
|
},
|
||||||
]
|
options: {
|
||||||
},
|
devicePixelRatio: 4,
|
||||||
options: {
|
responsive: false,
|
||||||
animation: false,
|
animation: false,
|
||||||
devicePixelRatio: 4,
|
maintainAspectRatio: true,
|
||||||
responsive: false,
|
circumference: 180,
|
||||||
maintainAspectRatio: true,
|
rotation: -90,
|
||||||
circumference: 180,
|
plugins: {
|
||||||
rotation: -90,
|
legend: {
|
||||||
plugins: {
|
labels: {
|
||||||
legend: {
|
boxWidth: 20,
|
||||||
labels: {
|
font: {
|
||||||
boxWidth: 20,
|
family: "'Montserrat'",
|
||||||
font: {
|
size: 10,
|
||||||
family: "'Montserrat'",
|
style: "normal",
|
||||||
size: 10,
|
weight: "normal"
|
||||||
style: "normal",
|
}
|
||||||
weight: "normal"
|
},
|
||||||
}
|
position: "left"
|
||||||
},
|
|
||||||
position: "left"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// If we have a valid override object, merge it with the default configuration object.
|
|
||||||
// This allows for you to override the default configuration with a custom one.
|
|
||||||
const defaults = () => {
|
|
||||||
if (!override || !isObject(override)) {
|
|
||||||
return configuration;
|
|
||||||
}
|
|
||||||
return defaultsDeep(override, configuration);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generate chart
|
|
||||||
let canvas = createCanvas(width, height);
|
|
||||||
let ctx = canvas.getContext("2d");
|
|
||||||
let chart = new Chart(ctx, defaults());
|
|
||||||
const result = canvas.toDataURL();
|
|
||||||
|
|
||||||
chart.destroy();
|
|
||||||
canvas.width = 0;
|
|
||||||
canvas.height = 0;
|
|
||||||
ctx = null;
|
|
||||||
canvas = null;
|
|
||||||
chart = null;
|
|
||||||
|
|
||||||
res.status(200).send(result);
|
|
||||||
} catch (error) {
|
|
||||||
if (chart) chart.destroy();
|
|
||||||
if (canvas) {
|
|
||||||
canvas.width = 0;
|
|
||||||
canvas.height = 0;
|
|
||||||
}
|
}
|
||||||
ctx = null;
|
};
|
||||||
canvas = null;
|
|
||||||
chart = null;
|
|
||||||
|
|
||||||
logger.log("inbound-canvas-creation", "error", "jsr", null, { error: error.message, stack: error.stack });
|
return defaultsDeep(override || {}, defaultConfiguration);
|
||||||
res.status(500).send("Error generating canvas");
|
};
|
||||||
|
|
||||||
|
const processCanvasRequest = async (req, res, isSkia = false) => {
|
||||||
|
const { logger } = req;
|
||||||
|
const { w, h, values, keys, override } = req.body;
|
||||||
|
|
||||||
|
logger.log("inbound-canvas-creation", "debug", "jsr", null, { w, h, values, keys, override });
|
||||||
|
|
||||||
|
// Default width and height
|
||||||
|
const width = isNumber(w) && w > 0 ? w : 500;
|
||||||
|
const height = isNumber(h) && h > 0 ? h : 275;
|
||||||
|
|
||||||
|
const configuration = getChartConfiguration(keys, values, override);
|
||||||
|
|
||||||
|
// Placeholders to allow fine control over GAC
|
||||||
|
let canvas = null;
|
||||||
|
let ctx = null;
|
||||||
|
let chart = null;
|
||||||
|
let chartImage = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create the canvas
|
||||||
|
canvas = isSkia ? new Canvas(width, height) : createCanvas(width, height);
|
||||||
|
ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
// Render the chart
|
||||||
|
chart = new Chart(ctx, configuration);
|
||||||
|
|
||||||
|
// Generate and send the image
|
||||||
|
chartImage = isSkia ? (await canvas.toBuffer("image/png")).toString("base64") : canvas.toDataURL();
|
||||||
|
|
||||||
|
res.status(200).send(isSkia ? `data:image/png;base64,${chartImage}` : chartImage);
|
||||||
|
} catch (error) {
|
||||||
|
// Log the error and send the response
|
||||||
|
logger.log("canvas-error", "error", "jsr", null, { error: error.message });
|
||||||
|
res.status(500).send("Failed to generate canvas.");
|
||||||
|
} finally {
|
||||||
|
// Cleanup resources
|
||||||
|
if (chart) {
|
||||||
|
chart.destroy();
|
||||||
|
}
|
||||||
|
ctx = null; // Explicitly nullify for garbage collection
|
||||||
|
canvas = null; // Explicitly nullify for garbage collection
|
||||||
|
chartImage = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const processNextInQueue = async () => {
|
const enqueueRequest = (req, res, isSkia) => {
|
||||||
if (requestQueue.length === 0) {
|
if (requestQueue.length >= CANVAS_QUEUE_LIMIT) {
|
||||||
isProcessing = false;
|
res.status(503).send("Server is busy. Please try again later.");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
requestQueue.push({ req, res, isSkia });
|
||||||
|
req.logger.log("inbound-canvas-creation-queue", "debug", "jsr", null, { queue: requestQueue.length });
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
const { req, res } = requestQueue.shift();
|
const processNextInQueue = async () => {
|
||||||
await processCanvasRequest(req, res);
|
while (requestQueue.length > 0) {
|
||||||
processNextInQueue();
|
const { req, res, isSkia } = requestQueue.shift();
|
||||||
|
try {
|
||||||
|
await processCanvasRequest(req, res, isSkia);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("canvas-queue-error", "error", "jsr", null, { error: err.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isProcessing = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.canvastest = function (req, res) {
|
exports.canvastest = function (req, res) {
|
||||||
res.status(200).send("OK");
|
res.status(200).send("OK");
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.canvas = async function (req, res) {
|
exports.canvas = async (req, res) => {
|
||||||
if (isProcessing) {
|
if (isProcessing || !enqueueRequest(req, res, false)) return;
|
||||||
if (requestQueue.length >= 100) {
|
|
||||||
// Set a maximum queue size
|
|
||||||
return res.status(503).send("Server is busy. Please try again later.");
|
|
||||||
}
|
|
||||||
requestQueue.push({ req, res });
|
|
||||||
logger.log("inbound-canvas-creation-queue", "debug", "jsr", null, { queue: requestQueue.length });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isProcessing = true;
|
isProcessing = true;
|
||||||
await processCanvasRequest(req, res);
|
processNextInQueue().catch((err) => console.error("canvas-processing-error", { error: err.message }));
|
||||||
processNextInQueue();
|
};
|
||||||
|
|
||||||
|
exports.canvasSkia = async (req, res) => {
|
||||||
|
if (isProcessing || !enqueueRequest(req, res, true)) return;
|
||||||
|
isProcessing = true;
|
||||||
|
processNextInQueue().catch((err) => console.error("canvas-processing-error", { error: err.message }));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ const express = require("express");
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const { inlinecss } = require("../render/inlinecss");
|
const { inlinecss } = require("../render/inlinecss");
|
||||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||||
const validateCanvasRequestMiddleware = require("../middleware/validateCanvasRequestMiddleware");
|
const { canvas, canvasSkia } = require("../render/canvas-handler");
|
||||||
const { canvas } = require("../render/canvas-handler");
|
const validateCanvasInputMiddleware = require("../middleware/validateCanvasInputMiddleware");
|
||||||
|
|
||||||
// Define the route for inline CSS rendering
|
// Define the route for inline CSS rendering
|
||||||
router.post("/inlinecss", validateFirebaseIdTokenMiddleware, inlinecss);
|
router.post("/inlinecss", validateFirebaseIdTokenMiddleware, inlinecss);
|
||||||
router.post("/canvas", [validateFirebaseIdTokenMiddleware, validateCanvasRequestMiddleware], canvas);
|
router.post("/canvas", [validateFirebaseIdTokenMiddleware, validateCanvasInputMiddleware], canvas);
|
||||||
|
router.post("/canvas-skia", [validateFirebaseIdTokenMiddleware, validateCanvasInputMiddleware], canvasSkia);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
Reference in New Issue
Block a user