diff --git a/Dockerfile b/Dockerfile index 16e9d2159..8afe15df4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM amazonlinux:2023 # Install Git and Node.js (Amazon Linux 2023 uses the DNF package manager) RUN dnf install -y git \ - && curl -sL https://rpm.nodesource.com/setup_20.x | bash - \ + && curl -sL https://rpm.nodesource.com/setup_22.x | bash - \ && dnf install -y nodejs \ && dnf clean all diff --git a/package-lock.json b/package-lock.json index fbf55a054..ed1bd7fe6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,6 @@ "better-queue": "^3.8.12", "bluebird": "^3.7.2", "body-parser": "^1.20.3", - "canvas": "^2.11.2", "chart.js": "^4.4.6", "cloudinary": "^2.5.1", "compression": "^1.7.5", @@ -4224,21 +4223,6 @@ "node": ">=6" } }, - "node_modules/canvas": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", - "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", - "nan": "^2.17.0", - "simple-get": "^3.0.3" - }, - "engines": { - "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", @@ -4971,18 +4955,6 @@ "node": ">=0.10" } }, - "node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "license": "MIT", - "dependencies": { - "mimic-response": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/deeks": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/deeks/-/deeks-3.1.0.tgz", @@ -8232,18 +8204,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -8372,7 +8332,8 @@ "version": "2.22.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/natural-compare": { "version": "1.4.0", @@ -9818,17 +9779,6 @@ ], "license": "MIT" }, - "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "license": "MIT", - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", diff --git a/package.json b/package.json index 4904d7655..13fb42209 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "better-queue": "^3.8.12", "bluebird": "^3.7.2", "body-parser": "^1.20.3", - "canvas": "^2.11.2", "chart.js": "^4.4.6", "cloudinary": "^2.5.1", "compression": "^1.7.5", diff --git a/server/render/canvas-handler.js b/server/render/canvas-handler.js index 5b810dd73..b46760946 100644 --- a/server/render/canvas-handler.js +++ b/server/render/canvas-handler.js @@ -1,4 +1,3 @@ -const { createCanvas } = require("canvas"); const { Canvas, FontLibrary } = require("skia-canvas"); const Chart = require("chart.js/auto"); @@ -65,7 +64,7 @@ const getChartConfiguration = (keys, values, override) => { return defaultsDeep(override || {}, defaultConfiguration); }; -const processCanvasRequest = async (req, res, isSkia = false) => { +const processCanvasRequest = async (req, res) => { const { logger } = req; const { w, h, values, keys, override } = req.body; @@ -77,7 +76,6 @@ const processCanvasRequest = async (req, res, isSkia = false) => { const configuration = getChartConfiguration(keys, values, override); - // Placeholders to allow fine control over GAC let canvas = null; let ctx = null; let chart = null; @@ -85,16 +83,15 @@ const processCanvasRequest = async (req, res, isSkia = false) => { try { // Create the canvas - canvas = isSkia ? new Canvas(width, height) : createCanvas(width, height); + canvas = new Canvas(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); + chartImage = (await canvas.toBuffer("image/png")).toString("base64"); + res.status(200).send(`data:image/png;base64,${chartImage}`); } catch (error) { // Log the error and send the response logger.log("canvas-error", "error", "jsr", null, { error: error.message }); @@ -104,27 +101,27 @@ const processCanvasRequest = async (req, res, isSkia = false) => { if (chart) { chart.destroy(); } - ctx = null; // Explicitly nullify for garbage collection - canvas = null; // Explicitly nullify for garbage collection + ctx = null; + canvas = null; chartImage = null; } }; -const enqueueRequest = (req, res, isSkia) => { +const enqueueRequest = (req, res) => { if (requestQueue.length >= CANVAS_QUEUE_LIMIT) { res.status(503).send("Server is busy. Please try again later."); return false; } - requestQueue.push({ req, res, isSkia }); + requestQueue.push({ req, res }); req.logger.log("inbound-canvas-creation-queue", "debug", "jsr", null, { queue: requestQueue.length }); return true; }; const processNextInQueue = async () => { while (requestQueue.length > 0) { - const { req, res, isSkia } = requestQueue.shift(); + const { req, res } = requestQueue.shift(); try { - await processCanvasRequest(req, res, isSkia); + await processCanvasRequest(req, res); } catch (err) { console.error("canvas-queue-error", "error", "jsr", null, { error: err.message }); } @@ -137,13 +134,7 @@ exports.canvastest = function (req, res) { }; exports.canvas = async (req, res) => { - if (isProcessing || !enqueueRequest(req, res, false)) return; - isProcessing = true; - processNextInQueue().catch((err) => console.error("canvas-processing-error", { error: err.message })); -}; - -exports.canvasSkia = async (req, res) => { - if (isProcessing || !enqueueRequest(req, res, true)) return; + if (isProcessing || !enqueueRequest(req, res)) return; isProcessing = true; processNextInQueue().catch((err) => console.error("canvas-processing-error", { error: err.message })); }; diff --git a/server/routes/renderRoutes.js b/server/routes/renderRoutes.js index c1423f698..613e6b0b5 100644 --- a/server/routes/renderRoutes.js +++ b/server/routes/renderRoutes.js @@ -2,12 +2,12 @@ const express = require("express"); const router = express.Router(); const { inlinecss } = require("../render/inlinecss"); const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware"); -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 router.post("/inlinecss", validateFirebaseIdTokenMiddleware, inlinecss); -router.post("/canvas", [validateFirebaseIdTokenMiddleware, validateCanvasInputMiddleware], canvas); -router.post("/canvas-skia", [validateFirebaseIdTokenMiddleware, validateCanvasInputMiddleware], canvasSkia); +router.post("/canvas-skia", validateFirebaseIdTokenMiddleware, validateCanvasInputMiddleware, canvas); +router.post("/canvas", validateFirebaseIdTokenMiddleware, validateCanvasInputMiddleware, canvas); module.exports = router;