diff --git a/api/main_endpoints/routes/Printer.js b/api/main_endpoints/routes/Printer.js index 73a5c97f7..ea10004f5 100644 --- a/api/main_endpoints/routes/Printer.js +++ b/api/main_endpoints/routes/Printer.js @@ -7,6 +7,8 @@ const fs = require('fs'); const path = require('path'); const { MetricsHandler, register } = require('../../util/metrics.js'); const { cleanUpChunks, cleanUpExpiredChunks, recordPrintingFolderSize } = require('../util/Printer.js'); +const { PDFDocument, StandardFonts } = require('pdf-lib'); +const {subtractUserPages} = require('../util/userHelpers'); const { decodeToken, @@ -17,6 +19,7 @@ const { UNAUTHORIZED, NOT_FOUND, SERVER_ERROR, + BAD_REQUEST } = require('../../util/constants').STATUS_CODES; const { PRINTING = {} @@ -113,16 +116,25 @@ router.post('/sendPrintRequest', upload.single('chunk'), async (req, res) => { return res.sendStatus(SERVER_ERROR); } } - - const stream = await fs.createReadStream(assembledPdfFromChunks); + const stream = await fs.promises.readFile(assembledPdfFromChunks); // Buffer const data = new FormData(); data.append('file', stream, {filename: id, type: 'application/pdf'}); data.append('copies', copies); data.append('sides', sides); try { + const pdfDoc = await PDFDocument.load(stream); // load PDF + const numpages = pdfDoc.getPages().length; // get number of pages + const copiesInt = parseInt(copies || 1, 10); + const totalPages = numpages * copiesInt; // updates users printcount + const success = await subtractUserPages(user.id, totalPages); + if (!success) { + await cleanUpChunks(dir, id); + return res.status(BAD_REQUEST).json({ error: 'Page limit exceeded or user not found' }); + } // full pdf can be sent to quasar no problem const printRes = await axios.post(PRINTER_URL + '/print', data, { + headers: { ...data.getHeaders(), }, diff --git a/api/main_endpoints/util/userHelpers.js b/api/main_endpoints/util/userHelpers.js index 8ac327872..951596020 100644 --- a/api/main_endpoints/util/userHelpers.js +++ b/api/main_endpoints/util/userHelpers.js @@ -202,6 +202,26 @@ function checkIfPageCountResets(lastLogin) { return lastLoginWasOverOneWeekAgo || aSundayHasPassedSinceLastLogin; } +// updates users available pages +async function subtractUserPages(userId, pagesToPrint){ + const user = await User.findById(userId); + if(!user){ + logger.error(`User not found ID: ${userId}`); + return false; // checks user + } + if(!Number.isInteger(pagesToPrint) || pagesToPrint <= 0){ + logger.error(`Invalid pagesToPrint value: ${pagesToPrint}.`); + return false; + } + if (user.pagesPrinted < pagesToPrint) { + logger.error(`User has insufficient pages remaining. Requested: ${pagesToPrint}. Available:${user.pagesPrinted}`); + return false; + } + + user.pagesPrinted -= pagesToPrint; + await user.save(); + return true; +} module.exports = { registerUser, @@ -211,4 +231,5 @@ module.exports = { userWithEmailExists, checkIfPageCountResets, findPasswordReset, + subtractUserPages, }; diff --git a/api/package-lock.json b/api/package-lock.json index 048d7d385..794e6c666 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -22,6 +22,7 @@ "nodemon": "^2.0.4", "passport": "^0.4.1", "passport-jwt": "^4.0.0", + "pdf-lib": "^1.17.1", "prom-client": "^15.1.3" }, "devDependencies": { @@ -631,6 +632,24 @@ "node": ">=8.0.0" } }, + "node_modules/@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "license": "MIT", + "dependencies": { + "pako": "^1.0.6" + } + }, + "node_modules/@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "license": "MIT", + "dependencies": { + "pako": "^1.0.10" + } + }, "node_modules/@types/bson": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz", @@ -3253,6 +3272,12 @@ "node": ">= 0.8.0" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3336,6 +3361,18 @@ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, + "node_modules/pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "license": "MIT", + "dependencies": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3977,6 +4014,12 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4626,6 +4669,22 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==" }, + "@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "requires": { + "pako": "^1.0.6" + } + }, + "@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "requires": { + "pako": "^1.0.10" + } + }, "@types/bson": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz", @@ -6420,6 +6479,11 @@ "word-wrap": "^1.2.5" } }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6479,6 +6543,17 @@ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, + "pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "requires": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } + }, "picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -6926,6 +7001,11 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/api/package.json b/api/package.json index f680fd1ad..99caa727f 100644 --- a/api/package.json +++ b/api/package.json @@ -41,6 +41,7 @@ "nodemon": "^2.0.4", "passport": "^0.4.1", "passport-jwt": "^4.0.0", + "pdf-lib": "^1.17.1", "prom-client": "^15.1.3" }, "devDependencies": {