diff --git a/package-lock.json b/package-lock.json index 32c5ebaa9..54a33146e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3052,6 +3052,19 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -3820,6 +3833,24 @@ "react": ">=16.14.0" } }, + "node_modules/@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -3846,6 +3877,10 @@ "resolved": "client-tauri", "link": true }, + "node_modules/@stirling-pdf/server-node": { + "resolved": "server-node", + "link": true + }, "node_modules/@stirling-pdf/shared-operations": { "resolved": "shared-operations", "link": true @@ -4668,6 +4703,16 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/@wasmer/wasmfs": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@wasmer/wasmfs/-/wasmfs-0.12.0.tgz", + "integrity": "sha512-m1ftchyQ1DfSenm5XbbdGIpb6KJHH5z0gODo3IZr6lATkj4WXfX/UeBTZ0aG9YVShBp+kHLdUHvOkqjy6p/GWw==", + "dependencies": { + "memfs": "3.0.4", + "pako": "^1.0.11", + "tar-stream": "^2.1.0" + } + }, "node_modules/@xmldom/xmldom": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", @@ -4854,6 +4899,11 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -5209,7 +5259,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -5251,6 +5300,16 @@ "node": ">=8" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/blob-util": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", @@ -5370,7 +5429,6 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, "funding": [ { "type": "github", @@ -5401,8 +5459,7 @@ "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "node_modules/builtin-modules": { "version": "3.3.0", @@ -5416,6 +5473,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -5779,6 +5847,47 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/concurrently": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", @@ -6459,7 +6568,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "dependencies": { "once": "^1.4.0" } @@ -7109,6 +7217,17 @@ "node": ">= 0.10.0" } }, + "node_modules/express-fileupload": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.4.2.tgz", + "integrity": "sha512-vk+9cK595jP03T+YgoYPAebynVCZuUBtW1JkyJnitQnWzlONHdxdAIm9yo99V4viTEftq7MUfzuqmWyqWGzMIg==", + "dependencies": { + "busboy": "^1.6.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -7181,6 +7300,11 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-extend": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fast-extend/-/fast-extend-1.0.2.tgz", + "integrity": "sha512-XXA9RmlPatkFKUzqVZAFth18R4Wo+Xug/S+C7YlYA3xrXwfPlW3dqNwOb4hvQo7wZJ2cNDYhrYuPzVOfHy5/uQ==" + }, "node_modules/fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", @@ -7456,6 +7580,11 @@ "node": ">= 0.6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -7501,6 +7630,11 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "devOptional": true }, + "node_modules/fs-monkey": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-0.3.3.tgz", + "integrity": "sha512-FNUvuTAJ3CqCQb5ELn+qCbGR/Zllhf2HtwsdAtBi59s1WeCjKMT81fHcSu7dwIskqGVK+MmOrb7VOBlq3/SItw==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -8057,7 +8191,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -8980,6 +9113,18 @@ "node": ">= 10.13.0" } }, + "node_modules/joi": { + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", + "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", + "dependencies": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, "node_modules/jpeg-js": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", @@ -9170,6 +9315,11 @@ "verror": "1.10.0" } }, + "node_modules/jsqr": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsqr/-/jsqr-1.4.0.tgz", + "integrity": "sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A==" + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -9504,6 +9654,15 @@ "resolved": "https://registry.npmjs.org/median-quickselect/-/median-quickselect-1.0.1.tgz", "integrity": "sha512-/QL9ptNuLsdA68qO+2o10TKCyu621zwwTFdLvtu8rzRNKsn8zvuGoq/vDxECPyELFG8wu+BpyoMR9BnsJqfVZQ==" }, + "node_modules/memfs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.0.4.tgz", + "integrity": "sha512-OcZEzwX9E5AoY8SXjuAvw0DbIAYwUzV/I236I8Pqvrlv7sL/Y0E9aRCon05DhaV8pg1b32uxj76RgW0s5xjHBA==", + "dependencies": { + "fast-extend": "1.0.2", + "fs-monkey": "0.3.3" + } + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -9620,7 +9779,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9897,6 +10055,34 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/nan": { "version": "2.18.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", @@ -11657,10 +11843,6 @@ "node": ">= 0.8.0" } }, - "node_modules/server-node": { - "resolved": "server-node", - "link": true - }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -11984,6 +12166,14 @@ "node": ">= 0.4" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/streamx": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.1.tgz", @@ -12222,6 +12412,21 @@ "node": ">=10" } }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/tar/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -12771,6 +12976,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, "node_modules/typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", @@ -14014,7 +14224,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, "engines": { "node": ">=0.4" } @@ -14106,11 +14315,20 @@ } }, "server-node": { - "version": "1.0.0", + "name": "@stirling-pdf/server-node", + "version": "0.0.0", "license": "ISC", "dependencies": { + "@stirling-pdf/shared-operations": "*", + "@wasmer/wasmfs": "^0.12.0", + "archiver": "^6.0.1", "express": "^4.18.2", - "nodemon": "^3.0.1" + "express-fileupload": "^1.4.2", + "joi": "^17.11.0", + "jsqr": "^1.4.0", + "multer": "^1.4.5-lts.1", + "nodemon": "^3.0.1", + "pdf-lib": "^1.17.1" }, "devDependencies": { "@types/express": "^4.17.21", diff --git a/server-node/package.json b/server-node/package.json index 668c31c8b..96718f0a0 100644 --- a/server-node/package.json +++ b/server-node/package.json @@ -17,6 +17,7 @@ "archiver": "^6.0.1", "express": "^4.18.2", "express-fileupload": "^1.4.2", + "joi": "^17.11.0", "jsqr": "^1.4.0", "multer": "^1.4.5-lts.1", "nodemon": "^3.0.1", diff --git a/server-node/src/routes/api/api-controller.ts b/server-node/src/routes/api/api-controller.ts index 943de1d54..e5a820a3b 100644 --- a/server-node/src/routes/api/api-controller.ts +++ b/server-node/src/routes/api/api-controller.ts @@ -1,16 +1,16 @@ import express, { Request, Response } from 'express'; -import fileUpload from 'express-fileupload'; import workflow from './workflow-controller'; +import operations from './operations-controller'; const router = express.Router(); -router.use(fileUpload()); router.get("/", (req: Request, res: Response) => { // TODO: Implement root api endpoint res.status(501).json({"Error": "Unfinished Endpoint. This sould probably send some api docs?"}); }); +router.use("/operations", operations); router.use("/workflow", workflow); export default router; \ No newline at end of file diff --git a/server-node/src/routes/api/operations-controller.ts b/server-node/src/routes/api/operations-controller.ts new file mode 100644 index 000000000..7a71ae30d --- /dev/null +++ b/server-node/src/routes/api/operations-controller.ts @@ -0,0 +1,66 @@ + +import Operations from '../../utils/pdf-operations'; +import { respondWithBinaryPdf, response_mustHaveExactlyOneFile } from '../../utils/endpoint-utils'; + +import express, { Request, Response } from 'express'; +const router = express.Router(); +import multer from 'multer'; +const upload = multer(); +import Joi from 'joi'; + +router.post('/rotate-pdf', upload.single("pdfFile"), async function(req: Request, res: Response) { + const schema = Joi.object({ + angle: Joi.number().required() + }); + const { error, value } = schema.validate(req.body); + if (error) { + res.status(400).send(error.details); + return; + } + if (!req.file) { + response_mustHaveExactlyOneFile(res); + return; + } + + const rotated = await Operations.rotatePages(req.file.buffer, value.angle) + const newFilename = appendToFilename(req.file.originalname, '_rotated'); + respondWithBinaryPdf(res, rotated, newFilename); +}); + +router.post('/update-metadata', upload.single("pdfFile"), async function(req: Request, res: Response) { + const schema = Joi.object({ + deleteAll: Joi.string(), + author: Joi.string(), + creationDate: Joi.string(), + creator: Joi.string(), + keywords: Joi.string(), + modificationDate: Joi.string(), + producer: Joi.string(), + subject: Joi.string(), + title: Joi.string(), + trapped: Joi.string(), + allRequestParams: Joi.object().pattern(Joi.string(), Joi.string()), + }); + const { error, value } = schema.validate(req.body); + if (error) { + res.status(400).send(error.details); + return; + } + if (!req.file) { + response_mustHaveExactlyOneFile(res); + return; + } + + const processed = await Operations.updateMetadata(req.file.buffer, value.angle) + const newFilename = appendToFilename(req.file.originalname, '_edited-metadata'); + respondWithBinaryPdf(res, processed, newFilename); +}); + +/** + * appends a string before the last '.' of the given filename + */ +function appendToFilename(filename: string, str: string) { + return filename.replace(/(\.[^.]+)$/, str+'$1') +} + +export default router; \ No newline at end of file diff --git a/server-node/src/utils/endpoint-utils.ts b/server-node/src/utils/endpoint-utils.ts new file mode 100644 index 000000000..e49701c0b --- /dev/null +++ b/server-node/src/utils/endpoint-utils.ts @@ -0,0 +1,27 @@ + +import express, { Request, Response } from 'express'; + +export function respondWithBinaryPdf(res: Response, buffer: Uint8Array, filename: string) { + res.writeHead(200, { + 'Content-Type': "application/pdf", + 'Content-disposition': 'attachment;filename=' + filename, + 'Content-Length': buffer.length + }); + res.end(buffer) +} + +export function response_mustHaveExactlyOneFile(res: Response): void { + res.status(400).send([ + { + "message": "file is required", + "path": [ + "pdfFile" + ], + "type": "file", + "context": { + "label": "pdfFile", + "key": "pdfFile" + } + } + ]); +} diff --git a/shared-operations/functions/editMetadata.ts b/shared-operations/functions/updateMetadata.ts similarity index 57% rename from shared-operations/functions/editMetadata.ts rename to shared-operations/functions/updateMetadata.ts index 18ee16ac5..8a87dbab6 100644 --- a/shared-operations/functions/editMetadata.ts +++ b/shared-operations/functions/updateMetadata.ts @@ -3,14 +3,14 @@ import { PDFDocument, ParseSpeeds } from 'pdf-lib'; export type Metadata = { - Title: string | null | undefined; // The title of the document. - Author: string | null | undefined; // The author of the document. - Subject: string | null | undefined; // The subject of the document. - Keywords: string[] | null | undefined; // An array of keywords associated with the document. - Producer: string | null | undefined; // The producer of the document. - Creator: string | null | undefined; // The creator of the document. - CreationDate: Date | null | undefined; // The date when the document was created. - ModificationDate: Date | null | undefined; // The date when the document was last modified. + Title?: string; // The title of the document. + Author?: string; // The author of the document. + Subject?: string; // The subject of the document. + Keywords?: string[]; // An array of keywords associated with the document. + Producer?: string; // The producer of the document. + Creator?: string; // The creator of the document. + CreationDate?: Date; // The date when the document was created. + ModificationDate?: Date; // The date when the document was last modified. } /** * @@ -18,7 +18,7 @@ export type Metadata = { * @param {Metadata} metadata - Set property to null or "" to clear, undefined properties will be skipped. * @returns Promise */ -export async function editMetadata(snapshot: string | Uint8Array | ArrayBuffer, metadata: Metadata): Promise { +export async function updateMetadata(snapshot: string | Uint8Array | ArrayBuffer, metadata: Metadata): Promise { // Load the original PDF file const pdfDoc = await PDFDocument.load(snapshot, { parseSpeed: ParseSpeeds.Fastest, diff --git a/shared-operations/index.ts b/shared-operations/index.ts index 48dc2d1da..1ffa5cd03 100644 --- a/shared-operations/index.ts +++ b/shared-operations/index.ts @@ -1,7 +1,6 @@ // Import injected libraries here! -import { editMetadata } from "./functions/editMetadata"; import { extractPages } from "./functions/extractPages"; import { impose } from "./functions/impose"; import { mergePDFs } from './functions/mergePDFs'; @@ -12,9 +11,9 @@ import { scaleContent} from './functions/scaleContent'; import { scalePage } from './functions/scalePage'; import { splitOn } from './functions/splitOn'; import { splitPDF } from './functions/splitPDF'; +import { updateMetadata } from "./functions/updateMetadata"; export default { - editMetadata, extractPages, impose, mergePDFs, @@ -25,4 +24,5 @@ export default { scalePage, splitOn, splitPDF, + updateMetadata, } \ No newline at end of file diff --git a/shared-operations/workflow/traverseOperations.js b/shared-operations/workflow/traverseOperations.js index 81a6ae9d5..78402818f 100644 --- a/shared-operations/workflow/traverseOperations.js +++ b/shared-operations/workflow/traverseOperations.js @@ -113,10 +113,10 @@ export async function * traverseOperations(operations, input, Operations) { return splits; }); break; - case "editMetadata": + case "updateMetadata": yield* nToN(input, operation, async (input) => { input.fileName += "_metadataEdited"; - input.buffer = await Operations.editMetadata(input.buffer, operation.values["metadata"]); + input.buffer = await Operations.updateMetadata(input.buffer, operation.values["metadata"]); }); break; case "organizePages":