commit 30023847bc1cc1383584d504b4a48104d2377434 Author: MOHAN Date: Sat Jun 13 13:05:28 2026 +0530 first commit diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..4a5f072 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +DB_HOST= +DB_USER= +DB_PASS= +DB_NAME= +PORT=5000 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7e4baf4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +uploads/ +node_modules/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..0fe7f90 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# Dine360 Ads - Backend + +Node.js + Express backend for Dine360 Ads platform. + +## Setup + +1. Install dependencies: + ```bash + npm install + ``` + +2. Copy `.env.example` to `.env` and fill in your database credentials: + ``` + DB_HOST= + DB_USER= + DB_PASS= + DB_NAME= + PORT=5000 + ``` + +3. Create the `uploads/` directory: + ```bash + mkdir uploads + ``` + +4. Start the server: + ```bash + node server.js + ``` + +## Notes +- `.env` and `uploads/` are excluded from version control — configure per environment. diff --git a/config/db.js b/config/db.js new file mode 100644 index 0000000..6bc1379 --- /dev/null +++ b/config/db.js @@ -0,0 +1,24 @@ +const mysql = require('mysql2'); + +// Create MySQL Connection Pool +const pool = mysql.createPool({ + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + waitForConnections: true, + connectionLimit: 10, // Max 10 connections at a time + queueLimit: 0 +}); + +pool.getConnection((err, connection) => { + if (err) { + console.error("❌ Database connection failed: ", err.message); + } else { + console.log("✅ Database connected successfully!"); + connection.release(); + } +}); + +// Export database connection +module.exports = pool.promise(); diff --git a/middlewares/upload.js b/middlewares/upload.js new file mode 100644 index 0000000..b5a93f6 --- /dev/null +++ b/middlewares/upload.js @@ -0,0 +1,28 @@ +const multer = require("multer"); +const path = require("path"); + +const storage = multer.diskStorage({ + destination: function (req, res, cb) { + cb(null, "uploads/"); + }, + filename: function (req, file, cb) { + cb(null, Date.now() + path.extname(file.originalname)); + }, +}); + +const fileFilter = (res, file, cb) => { + const allowedTypes = ["image/jpeg", "image/png", "video/mkv", "video/mp4"]; + if (allowedTypes.includes(file.mimetype)) { + cb(null, true); + } else { + cb(new Error("Invalid File Type "), false); + } +}; + +const upload = multer({ + storage: storage, + fileFilter: fileFilter, + limits: { fileSize: 2 * 1024 * 1024 * 1024 }, +}); + +module.exports = upload; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4ad9bc3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1565 @@ +{ + "name": "backend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "backend", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "axios": "^1.8.4", + "cors": "^2.8.5", + "dotenv": "^16.4.7", + "express": "^4.21.2", + "express-fileupload": "^1.5.1", + "fluent-ffmpeg": "^2.1.3", + "fs-extra": "^11.3.0", + "multer": "^1.4.5-lts.1", + "mysql2": "^3.13.0", + "nodemon": "^3.1.9", + "uuid": "^11.1.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "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/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==" + }, + "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", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "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/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-fileupload": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.5.1.tgz", + "integrity": "sha512-LsYG1ALXEB7vlmjuSw8ABeOctMp8a31aUC5ZF55zuz7O2jLFnmJYrCv10py357ky48aEoBQ/9bVXgFynjvaPmA==", + "dependencies": { + "busboy": "^1.6.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fluent-ffmpeg": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", + "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==", + "dependencies": { + "async": "^0.2.9", + "which": "^1.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/long": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz", + "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/lru.min": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz", + "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/ms": { + "version": "2.0.0", + "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/mysql2": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.13.0.tgz", + "integrity": "sha512-M6DIQjTqKeqXH5HLbLMxwcK5XfXHw30u5ap6EZmu7QVmcF/gnh2wS/EOiQ4MTbXz/vQeoXrmycPlVRM00WSslg==", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nodemon": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", + "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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/readable-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/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "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/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/string_decoder/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/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..bf8da5e --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "backend", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "axios": "^1.8.4", + "cors": "^2.8.5", + "dotenv": "^16.4.7", + "express": "^4.21.2", + "express-fileupload": "^1.5.1", + "fluent-ffmpeg": "^2.1.3", + "fs-extra": "^11.3.0", + "multer": "^1.4.5-lts.1", + "mysql2": "^3.13.0", + "nodemon": "^3.1.9", + "uuid": "^11.1.0" + } +} diff --git a/routes/adminRoutes.js b/routes/adminRoutes.js new file mode 100644 index 0000000..2998655 --- /dev/null +++ b/routes/adminRoutes.js @@ -0,0 +1,710 @@ +const express = require("express"); +const router = express.Router(); +const pool = require("../config/db"); +const upload = require("../middlewares/upload"); +const { v4: uuidv4 } = require("uuid"); + +const generateSlug = (name) => { + return name + .toLowerCase() + .replace(/\s+/g, "-") + .replace(/[^a-z0-9-]/g, ""); +}; + +router.post("/add-partner", async (req, res) => { + try { + const { + name, + open_time, + close_time, + address, + city, + state, + pincode, + screens, + // logo_url, // Assuming logo_url is a URL string, adjust if needed. + } = req.body; + + const slug = generateSlug(name); + const uniqueurl = slug; + var id = uuidv4(); + + const [result] = await pool.query( + "INSERT INTO tbl_partners (transid,name, logo_url, open_time, close_time, address, city, state, pincode, screens) VALUES (?,?, ?, ?, ?, ?, ?, ?, ?, ?)", + [ + id, + name, + uniqueurl, + open_time, + close_time, + address, + city, + state, + pincode, + screens, + ] + ); + //hsh + var screensnumber = Number(screens); + for (let i = 0; i < screensnumber; i++) { + const [result1] = await pool.query( + "INSERT INTO tbl_screens (transid,partnerid,name) VALUES (UUID(),?, ?)", + [id, "Screen " + Number(i + 1)] + ); + } + + res.json({ + message: "Partner Added Successfully! ", + clientid: id, + uniqueurl, + id, + }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.post("/add-screen", async (req, res) => { + try { + const { + partnerid, + // logo_url, // Assuming logo_url is a URL string, adjust if needed. + } = req.body; + + console.log(partnerid); + const [countRows] = await pool.query( + `SELECT COUNT(*) AS cnt + FROM tbl_screens + WHERE partnerid = ?`, + [partnerid] + ); + const existing = countRows[0].cnt; + console.log(existing); + // 2️⃣ prepare the new screen name + const newScreenName = `Screen ${existing + 1}`; + + // 3️⃣ insert the new screen + const [result] = await pool.query( + `INSERT INTO tbl_screens + (transid, partnerid, name) + VALUES + (UUID(), ?, ?)`, + [partnerid, newScreenName] + ); + + res.json({ + message: "Screen Added Successfully! ", + }); + } catch (err) { + console.log(err); + res.status(500).json({ error: err.message }); + } +}); + +router.post("/delete-screen", async (req, res) => { + try { + const { + screenid, + } = req.body; + + + const [result] = await pool.query( + `delete from tbl_screens where transid = ? + `, + [screenid] + ); + + res.json({ + message: "Screen Deleted Successfully! ", + }); + } catch (err) { + console.log(err); + res.status(500).json({ error: err.message }); + } +}); + +router.post("/upload", upload.single("file"), async (req, res) => { + try { + const { filename, path } = req.file; + const clientid = req.body.clientid; + + await pool.query( + "insert into tbl_files (transid,client_id,file_name,file_path) values (UUID(),?,?,?)", + [clientid, filename, path] + ); + res.json({ message: "File Uploaded Successfully!", filepath: path }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.post("/upload-partner-logo", upload.single("file"), async (req, res) => { + try { + const { filename, path } = req.file; + const partnerid = req.body.partnerid; + + await pool.query("update tbl_partners set logo_url = ? where transid = ?", [ + path, + partnerid, + ]); + res.json({ + message: "Partner Logo Uploaded Successfully!", + filepath: path, + }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.get("/partners", async (req, res) => { + try { + const [partners] = await pool.query("select * from tbl_partners"); + res.json(partners); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.post("/update-partner", async (req, res) => { + try { + const { + name, + open_time, + logo_url, + close_time, + address, + city, + state, + pincode, + screens, + transid, + yt, + scrolltext, + } = req.body; + + const [result] = await pool.query( + "update tbl_partners set name =?, logo_url=?, open_time=?, close_time=?, address=?, city=?, state=?, pincode=?, screens=?,yt=?,scrolltext=? where transid = ?", + [ + name, + logo_url, + open_time, + close_time, + address, + city, + state, + pincode, + screens, + yt, + scrolltext, + transid, + ] + ); + + res.json({ + message: "Partner Updated Successfully! ", + }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.post("/update-screen-youtube", async (req, res) => { + try { + const { screenid, sts } = req.body; + + const [result] = await pool.query( + "update tbl_screens set isyoutube=? where transid = ?", + [sts, screenid] + ); + + res.json({ + message: "Partner Updated Successfully! ", + }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.post("/update-screen-type", async (req, res) => { + try { + const { screenid, sts } = req.body; + + const [result] = await pool.query( + "update tbl_screens set screentype=? where transid = ?", + [sts, screenid] + ); + + res.json({ + message: "Screen Type Updated Successfully! ", + }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.get("/clients", async (req, res) => { + try { + const [partners] = await pool.query("select * from tbl_clients"); + res.json(partners); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.post("/add-client", async (req, res) => { + try { + const { name, emailid, phoneno, address } = req.body; + + const [result] = await pool.query( + "INSERT INTO tbl_clients (transid,name,emailid,phoneno,address) VALUES (UUID(),?, ?,?,?)", + [name, emailid, phoneno, address] + ); + + res.json({ + message: "Client Added Successfully! ", + }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.post("/update-client", async (req, res) => { + try { + const { transid, name, emailid, phoneno, address } = req.body; + + const [result] = await pool.query( + "update tbl_clients set name=?,emailid=?,phoneno=?,address=? where transid = ?", + [name, emailid, phoneno, address, transid] + ); + + res.json({ + message: "Client Updated Successfully! ", + }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.delete("/delete-client/:clientid", async (req, res) => { + try { + const { clientid } = req.params; + const [response] = await pool.query( + "delete from tbl_clients where transid =? ", + [clientid] + ); + + const [response1] = await pool.query( + "delete from tbl_files where client_id =? ", + [clientid] + ); + + const [response2] = await pool.query( + "delete from tbl_client_partner_mapping where clientid =? ", + [clientid] + ); + + res.json(response); + } catch (err) { + res.status(500), json({ error: err.message }); + } +}); + +router.get("/get-partner/:partnreid", async (req, res) => { + try { + const { partnreid } = req.params; + const [partners] = await pool.query( + "select * from tbl_partners where transid=?", + [partnreid] + ); + res.json(partners); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.delete("/delete-partner/:partnerid", async (req, res) => { + try { + const { partnerid } = req.params; + const [response] = await pool.query( + "delete from tbl_partners where transid =? ", + [partnerid] + ); + + const [response1] = await pool.query( + "delete from tbl_screens where partnerid =? ", + [partnerid] + ); + + const [response2] = await pool.query( + "delete from tbl_client_partner_mapping where partnerid =? ", + [partnerid] + ); + + res.json(response); + } catch (err) { + res.status(500), json({ error: err.message }); + } +}); + +router.delete( + "/delete-client-partner-mapping/:partnerid/:clientid", + async (req, res) => { + try { + const { partnerid, clientid } = req.params; + const [response] = await pool.query( + "delete from tbl_client_partner_mapping where partnerid =? and clientid =?", + [partnerid, clientid] + ); + + res.json(response); + } catch (err) { + res.status(500), json({ error: err.message }); + } + } +); + +router.get("/get-client/:clientid", async (req, res) => { + try { + const { clientid } = req.params; + const [partners] = await pool.query( + "select * from tbl_clients where transid=?", + [clientid] + ); + res.json(partners); + } catch (err) { + res.status(500).json({ error: err.massage }); + } +}); + +router.get("/get-screen/:partnerid", async (req, res) => { + try { + const partnerid = req.params.partnerid; + + const query = "SELECT * FROM tbl_screens WHERE partnerid = ?"; + + const [screens] = await pool.query(query, [partnerid]); + res.json(screens); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +//Client - Partner Mapping + +router.get("/client-partner-mappings", async (req, res) => { + try { + const { clientid, partnerid } = req.query; // Get clientid and partnerid from query parameters + + if (!clientid || !partnerid) { + return res + .status(400) + .json({ error: "clientid and partnerid are required." }); + } + + const [mappings] = await pool.query( + "SELECT * FROM tbl_client_partner_mapping WHERE clientid = ? AND partnerid = ?", + [clientid, partnerid] + ); + + res.json(mappings); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +// router.post("/add-client-partner-mapping", async (req, res) => { +// try { +// const mappings = req.body; // Assuming req.body is an array of mapping objects +// console.log(mappings); +// if (!Array.isArray(mappings) || mappings.length === 0) { +// return res +// .status(400) +// .json({ error: "Invalid or empty mapping data provided." }); +// } +// const clientID = mappings[0].clientid; +// const partnerID = mappings[0].partnerid; +// await pool.query( +// "delete from tbl_client_partner_mapping where clientid =? and partnerid = ?", +// [clientID, partnerID] +// ); + +// for (const mapping of mappings) { +// const { +// clientid, +// partnerid, +// screenid, +// fileid, +// file_path, +// ismonday, +// istuesday, +// iswednesday, +// isthursday, +// isfriday, +// issaturday, +// issunday, +// imageduration, +// } = mapping; + +// // Basic validation (you might want to add more robust validation) +// if (!clientid || !partnerid || !screenid || !fileid || !file_path) { +// return res +// .status(400) +// .json({ error: "Missing required fields in mapping." }); +// } + +// await pool.query( +// "INSERT INTO tbl_client_partner_mapping (clientid, partnerid, screenid, fileid, file_path, ismonday, istuesday, iswednesday, isthursday, isfriday, issaturday, issunday,imageduration) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)", +// [ +// clientid, +// partnerid, +// screenid, +// fileid, +// file_path, +// ismonday || 0, // Default to 0 if not provided +// istuesday || 0, +// iswednesday || 0, +// isthursday || 0, +// isfriday || 0, +// issaturday || 0, +// issunday || 0, +// imageduration || 10, +// ] +// ); +// } + +// res.json({ message: "Client-Partner mappings added successfully!" }); +// } catch (err) { +// console.log(err); +// res.status(500).json({ error: err }); +// } +// }); + +router.post("/add-client-partner-mapping", async (req, res) => { + const mappings = req.body; + if (!Array.isArray(mappings) || mappings.length === 0) { + return res + .status(400) + .json({ error: "Invalid or empty mapping data provided." }); + } + + const clientID = mappings[0].clientid; + const partnerID = mappings[0].partnerid; + + const conn = await pool.getConnection(); + try { + await conn.beginTransaction(); + + // 1) fetch old values by fileid + const [oldRows] = await conn.query( + `SELECT fileid, imageduration, isinhousead, iscarousel, ismainad + FROM tbl_client_partner_mapping + WHERE clientid = ? AND partnerid = ?`, + [clientID, partnerID] + ); + const oldMap = oldRows.reduce((m, row) => { + m[row.fileid] = row; + return m; + }, {}); + + // 2) delete existing mappings + await conn.query( + "DELETE FROM tbl_client_partner_mapping WHERE clientid = ? AND partnerid = ?", + [clientID, partnerID] + ); + + // 3) re-insert, pulling in old flags if the incoming mapping didn’t supply them + for (const mapping of mappings) { + const { + clientid, + partnerid, + screenid, + fileid, + file_path, + ismonday = 0, + istuesday = 0, + iswednesday = 0, + isthursday = 0, + isfriday = 0, + issaturday = 0, + issunday = 0, + imageduration, + isinhousead, + iscarousel, + ismainad, + } = mapping; + + // fall back to old values (or defaults) if not supplied + const old = oldMap[fileid] || {}; + const duration = imageduration != null + ? imageduration + : old.imageduration ?? 10; + const inhouse = isinhousead != null + ? isinhousead + : old.isinhousead ?? 0; + const carousel = iscarousel != null + ? iscarousel + : old.iscarousel ?? 0; + const mainAd = ismainad != null + ? ismainad + : old.ismainad ?? 0; + + // basic field check + if (!clientid || !partnerid || !screenid || !fileid || !file_path) { + await conn.rollback(); + return res + .status(400) + .json({ error: "Missing required fields in mapping." }); + } + + await conn.query( + `INSERT INTO tbl_client_partner_mapping + (clientid, partnerid, screenid, fileid, file_path, + ismonday, istuesday, iswednesday, isthursday, + isfriday, issaturday, issunday, + imageduration, isinhousead, iscarousel, ismainad) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + [ + clientid, + partnerid, + screenid, + fileid, + file_path, + ismonday, + istuesday, + iswednesday, + isthursday, + isfriday, + issaturday, + issunday, + duration, + inhouse, + carousel, + mainAd, + ] + ); + } + + await conn.commit(); + res.json({ message: "Client-Partner mappings updated successfully!" }); + } catch (err) { + await conn.rollback(); + console.error(err); + res.status(500).json({ error: err.message || err }); + } finally { + conn.release(); + } +}); + + +router.post("/reorder-partner-ads", async (req, res) => { + try { + const mappings = req.body; // Assuming req.body is an array of mapping objects + + if (!Array.isArray(mappings) || mappings.length === 0) { + return res + .status(400) + .json({ error: "Invalid or empty mapping data provided." }); + } + + const partnerID = mappings[0].partnerid; + await pool.query( + "delete from tbl_client_partner_mapping where partnerid = ?", + [partnerID] + ); + + for (const mapping of mappings) { + const { + clientid, + partnerid, + screenid, + fileid, + file_path, + ismonday, + istuesday, + iswednesday, + isthursday, + isfriday, + issaturday, + issunday, + imageduration, + isinhousead, + iscarousel, + ismainad, + } = mapping; + + // console.log(mapping); + + // Basic validation (you might want to add more robust validation) + if (!clientid || !partnerid || !screenid || !fileid || !file_path) { + return res + .status(400) + .json({ error: "Missing required fields in mapping." }); + } + + await pool.query( + "INSERT INTO tbl_client_partner_mapping (clientid, partnerid, screenid, fileid, file_path, ismonday, istuesday, iswednesday, isthursday, isfriday, issaturday, issunday,imageduration,isinhousead,iscarousel,ismainad) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?,?,?,?)", + [ + clientid, + partnerid, + screenid, + fileid, + file_path, + ismonday || 0, // Default to 0 if not provided + istuesday || 0, + iswednesday || 0, + isthursday || 0, + isfriday || 0, + issaturday || 0, + issunday || 0, + imageduration || 10, + isinhousead || 0, + iscarousel || 0, + ismainad || 0, + ] + ); + } + + res.json({ message: "Partner Ads Re-ordered successfully!" }); + } catch (err) { + console.log(err); + res.status(500).json({ error: err.message }); + } +}); + +router.get("/partner-ads", async (req, res) => { + try { + const { partnerid } = req.query; // Get clientid and partnerid from query parameters + + if (!partnerid) { + return res.status(400).json({ error: "Partnerid is required." }); + } + + const [mappings] = await pool.query( + " SELECT *, (select name from tbl_screens where transid = screenid) as 'screenname',(select name from tbl_clients where transid = clientid) as 'clientname' FROM tbl_client_partner_mapping WHERE partnerid = ?", + [partnerid] + ); + + res.json(mappings); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.post("/update-settings", async (req, res) => { + try { + const { transid, value } = req.body; + + const [result] = await pool.query( + "update tbl_settings set value =? where transid = ?", + [value, transid] + ); + + res.json({ + message: "Settings Updated Successfully! ", + }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +module.exports = router; diff --git a/routes/clientRoutes.js b/routes/clientRoutes.js new file mode 100644 index 0000000..7044364 --- /dev/null +++ b/routes/clientRoutes.js @@ -0,0 +1,275 @@ +const express = require("express"); +const router = express.Router(); +const pool = require("../config/db"); + +router.get("/files/:clientId", async (req, res) => { + try { + const { clientId } = req.params; + const [files] = await pool.query( + "select * from tbl_files where client_id =? ", + [clientId] + ); + res.json(files); + } catch (err) { + res.status(500), json({ error: err.message }); + } +}); + +router.get("/ads/:partnerId/:screenName", async (req, res) => { + try { + const { partnerId, screenName } = req.params; + console.log(req.params); + // Find the screenId based on partnerId and screenName + const [screenResults] = await pool.query( + "SELECT transid FROM tbl_screens WHERE REPLACE(name, ' ', '') = ? AND SUBSTRING(partnerid, 1, 5) = SUBSTRING(?, 1, 5)", + [screenName, partnerId] + ); + + if (screenResults.length === 0) { + return res.status(404).json({ error: "Screen not found." }); + } + + const screenId = screenResults[0].transid; + + const [result] = await pool.query( + "update tbl_screens set isactive=1 where transid = ?", + [screenId] + ); + // Find the files associated with the screenId + const [files] = await pool.query( + "SELECT file_path,screenid,partnerid,transid,imageduration,iscarousel,isinhousead,ismainad,(select screentype from tbl_screens where transid = screenid)as screentype,(select isyoutube from tbl_screens where transid = screenid)as yt FROM tbl_client_partner_mapping WHERE screenid = ?", + [screenId] + ); + + res.json(files); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +// router.get("/ads/:partnerId/:screenName", async (req, res) => { +// try { +// const { partnerId, screenName } = req.params; +// console.log(req.params); +// // Find the screenId based on partnerId and screenName +// const [screenResults] = await pool.query( +// "SELECT file_path,screenid,partnerid,transid,imageduration,isinhousead,iscarousel,ismainad FROM tbl_client_partner_mapping WHERE screenid = ?", +// [screenId] +// ); + +// if (screenResults.length === 0) { +// return res.status(404).json({ error: "Screen not found." }); +// } + +// const screenId = screenResults[0].transid; + +// const [result] = await pool.query( +// "update tbl_screens set isactive=1 where transid = ?", +// [screenId] +// ); +// // Find the files associated with the screenId +// const [files] = await pool.query( +// "SELECT * FROM tbl_client_partner_mapping WHERE screenid = ?", +// [screenId] +// ); + +// res.json(files); +// } catch (err) { +// res.status(500).json({ error: err.message }); +// } +// }); + +router.post("/add-screen-log", async (req, res) => { + try { + const { screen_id, screen_name, partner_id } = req.body; + + // Insert data into the `screen_logs` table + + const [result1] = await pool.query( + "update tbl_screens set isactive=1 where transid = ?", + [screen_id] + ); + + // const [result] = await pool.query( + // "INSERT INTO tbl_screen_logs (screen_id, screen_name, partner_id) VALUES (?, ?, ?)", + // [screen_id, screen_name, partner_id] + // ); + + res.json({ + message: "Screen log added successfully!", + }); + } catch (err) { + console.log(err); + res.status(500).json({ error: err.message }); + } +}); + +router.post("/idle-screen-log", async (req, res) => { + try { + const { screen_id, screen_name, partner_id } = req.body; + + // Insert data into the `screen_logs` table + + const [result1] = await pool.query( + "update tbl_screens set isactive=2 where transid = ?", + [screen_id] + ); + + res.json({ + message: "IDLE log added successfully!", + }); + } catch (err) { + console.log(err); + res.status(500).json({ error: err.message }); + } +}); + +router.get("/get-settings", async (req, res) => { + try { + const [result] = await pool.query("select * from tbl_settings ", []); + + res.json(result); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.post("/validate-code", async (req, res) => { + try { + const { code } = req.body; + + if (!code || code.length !== 4) { + return res + .status(400) + .json({ error: "Invalid code format. It should be 4 digits." }); + } // Split the code into two parts + + const firstTwo = code.substring(0, 2); + const lastTwo = code.substring(3, 4); // Query to check if there is a matching record + + const [rows] = await pool.query( + "SELECT partnerid, name FROM tbl_screens WHERE LEFT(partnerid, 2) = ? AND right(name, 1) = ?", + [firstTwo, lastTwo] + ); + + if (rows.length === 0) { + return res.status(404).json({ error: "No matching record found" }); + } + + const { partnerid, name } = rows[0]; // Generate the response value: first 5 letters of transid/name (without spaces) + + const responseValue = `${partnerid.substring(0, 5)}/${name.replace( + /\s+/g, + "" + )}`; + + res.json({ success: true, value: responseValue }); + + // res.json([ + // { + // success: true, + // value: { + // screenPath: responseValue, + // raw: responseValue + // } + // } + // ]); + } catch (err) { + console.error(err); + res.status(500).json({ error: err.message }); + } +}); + +router.post("/validate-code-new", async (req, res) => { + try { + const { code } = req.body; + + if (!code || code.length !== 4) { + return res + .status(400) + .json({ error: "Invalid code format. It should be 4 digits." }); + } // Split the code into two parts + + const firstTwo = code.substring(0, 2); + const lastTwo = code.substring(3, 4); // Query to check if there is a matching record + + const [rows] = await pool.query( + "SELECT partnerid, name,transid FROM tbl_screens WHERE LEFT(partnerid, 2) = ? AND right(name, 1) = ?", + [firstTwo, lastTwo] + ); + + if (rows.length === 0) { + return res.status(404).json({ error: "No matching record found" }); + } + + const { partnerid, name, transid } = rows[0]; // Generate the response value: first 5 letters of transid/name (without spaces) + + const responseValue = `${partnerid.substring(0, 5)}/${name.replace( + /\s+/g, + "" + )}`; + + //res.json({ success: true, value: responseValue }); + + res.json([ + { + success: true, + value: { + screenPath: responseValue, + raw: responseValue, + id: transid, + }, + }, + ]); + } catch (err) { + console.error(err); + res.status(500).json({ error: err.message }); + } +}); + +router.post("/get-yt", async (req, res) => { + try { + const { code } = req.body; + + if (!code || code.length !== 4) { + return res + .status(400) + .json({ error: "Invalid code format. It should be 4 digits." }); + } // Split the code into two parts + + const firstTwo = code.substring(0, 2); + const lastTwo = code.substring(3, 4); // Query to check if there is a matching record + + const [rows] = await pool.query( + "SELECT * FROM tbl_partners WHERE LEFT(transid, 2) = ? ", + [firstTwo] + ); + + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: err.message }); + } +}); + +router.post("/add-screen-status", async (req, res) => { + try { + const { id, sts } = req.body; + + // Insert data into the `screen_logs` table + + const [result1] = await pool.query( + "update tbl_screens set status=?,stsupdat = CURRENT_TIMESTAMP where transid = ?", + [sts, id] + ); + + res.json({ + message: "Screen Status Updated successfully!", + }); + } catch (err) { + console.log(err); + res.status(500).json({ error: err.message }); + } +}); + +module.exports = router; diff --git a/routes/monitorRoutes.js b/routes/monitorRoutes.js new file mode 100644 index 0000000..f7b5a64 --- /dev/null +++ b/routes/monitorRoutes.js @@ -0,0 +1,24 @@ +const express = require('express'); +const pool = require('../config/db'); + +const router = express.Router(); + +// Endpoint to receive client status +router.post('/track-status', async (req, res) => { + try { + const { client_id, status } = req.body; + const timestamp = new Date(); + + // Log request + await pool.query("INSERT INTO client_logs (client_id, status) VALUES (?, ?)", [client_id, status]); + + // Update client last active time + await pool.query("UPDATE clients SET last_active = ? WHERE id = ?", [timestamp, client_id]); + + res.json({ message: "Status logged successfully!" }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +module.exports = router; diff --git a/routes/uploadRoutes.js b/routes/uploadRoutes.js new file mode 100644 index 0000000..892f4fe --- /dev/null +++ b/routes/uploadRoutes.js @@ -0,0 +1,88 @@ +const express = require("express"); +const multer = require("multer"); +const pool = require("../config/db"); +const path = require("path"); +const fs = require("fs-extra"); + +const router = express.Router(); + +// Ensure the uploads directory exists +const uploadDir = path.join(__dirname, "../uploads"); +fs.ensureDirSync(uploadDir); + +// Configure Multer for file uploads +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + cb(null, uploadDir); + }, + filename: (req, file, cb) => { + cb(null, Date.now() + "_" + file.originalname); // Unique filename + }, +}); + +const upload = multer({ + storage: storage, + limits: { fileSize: 2 * 1024 * 1024 * 1024 }, +}); // 2GB limit + +// Upload file API +router.post("/upload", upload.single("file"), async (req, res) => { + try { + const { client_id, file_name } = req.body; + if (!req.file) { + return res.status(400).json({ error: "No file uploaded" }); + } + + const filePath = `uploads/${req.file.filename}`; + + await pool.query( + "INSERT INTO tbl_files (transid,client_id,file_name, file_path) VALUES (UUID(),?,?, ?)", + [client_id, file_name, filePath] + ); + + res.json({ message: "File uploaded successfully!", file_path: filePath }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.post("/update-partner-logo", upload.single("file"), async (req, res) => { + try { + const { client_id, file_name } = req.body; + if (!req.file) { + return res.status(400).json({ error: "No file uploaded" }); + } + + const filePath = `uploads/${req.file.filename}`; + + await pool.query("update tbl_partners set logo_url = ? where transid = ?", [ + filePath, + client_id, + ]); + + res.json({ message: "File uploaded successfully!", file_path: filePath }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +router.delete("/del/:fileid", async (req, res) => { + try { + const { fileid } = req.params; + const [files] = await pool.query( + "delete from tbl_files where transid =? ", + [fileid] + ); + + const [filesfromads] = await pool.query( + "delete from tbl_client_partner_mapping where fileid =? ", + [fileid] + ); + + res.json(files); + } catch (err) { + res.status(500), json({ error: err.message }); + } +}); + +module.exports = router; diff --git a/routes/uploadRoutes_ffmpeg.js b/routes/uploadRoutes_ffmpeg.js new file mode 100644 index 0000000..b497806 --- /dev/null +++ b/routes/uploadRoutes_ffmpeg.js @@ -0,0 +1,94 @@ +// uploadRoute.js +const express = require("express"); +const upload = require("../middlewares/upload"); // Import the Multer middleware +const pool = require("../config/db"); +const path = require("path"); +const fs = require("fs-extra"); +const ffmpeg = require("fluent-ffmpeg"); + +const router = express.Router(); + +// Ensure the uploads and optimized directories exist +const uploadDir = path.join(__dirname, "../uploads"); +const optimizedDir = path.join(__dirname, "../optimized"); +fs.ensureDirSync(uploadDir); +fs.ensureDirSync(optimizedDir); + +async function optimizeVideo(inputPath, outputPath) { + return new Promise((resolve, reject) => { + ffmpeg(inputPath) + .videoCodec("libx264") + .audioCodec("aac") + .format("mp4") + .outputOptions([ + "-movflags frag_keyframe+empty_moov", + "-preset medium", + "-crf 23", + "-maxrate 2M", + "-bufsize 4M", + ]) + .on("end", () => { + console.log("Video optimization completed."); + resolve(); + }) + .on("error", (err) => { + console.error("Error optimizing video:", err); + reject(err); + }) + .save(outputPath); + }); +} + +// Upload file API +router.post("/upload", upload.single("file"), async (req, res) => { + try { + const { client_id, file_name } = req.body; + if (!req.file) { + return res.status(400).json({ error: "No file uploaded" }); + } + + const inputPath = path.join(uploadDir, req.file.filename); + const outputPath = path.join(optimizedDir, "optimized_" + req.file.filename); + const dbFilePath = `optimized/optimized_${req.file.filename}`; + + if (req.file.mimetype.startsWith("video/")) { + try { + await optimizeVideo(inputPath, outputPath); + await pool.query( + "INSERT INTO tbl_files (transid, client_id, file_name, file_path) VALUES (UUID(), ?, ?, ?)", + [client_id, file_name, dbFilePath] + ); + res.json({ message: "File uploaded and optimized successfully!", file_path: dbFilePath }); + } catch (optimizeError) { + console.error("Optimization error:", optimizeError); + await pool.query( + "INSERT INTO tbl_files (transid, client_id, file_name, file_path) VALUES (UUID(), ?, ?, ?)", + [client_id, file_name, `uploads/${req.file.filename}`] + ); + res.status(500).json({ message: "File uploaded, but optimization failed.", file_path: `uploads/${req.file.filename}` }); + } + } else { + await pool.query( + "INSERT INTO tbl_files (transid, client_id, file_name, file_path) VALUES (UUID(), ?, ?, ?)", + [client_id, file_name, `uploads/${req.file.filename}`] + ); + res.json({ message: "File uploaded successfully!", file_path: `uploads/${req.file.filename}` }); + } + } catch (err) { + console.error("General upload error:", err); + res.status(500).json({ error: err.message }); + } +}); + +router.delete("/del/:fileid", async (req, res) => { + try { + const { fileid } = req.params; + const [files] = await pool.query("delete from tbl_files where transid =? ", [fileid]); + const [filesfromads] = await pool.query("delete from tbl_client_partner_mapping where fileid =? ", [fileid]); + res.json(files); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..18b4808 --- /dev/null +++ b/server.js @@ -0,0 +1,48 @@ +require("dotenv").config(); +const express = require("express"); +const bodyParser = require("body-parser"); +const cors = require("cors"); +const fetch = global.fetch || require("node-fetch"); // for Node < v18 + +const path = require("path"); + +const adminRoutes = require("./routes/adminRoutes"); +const clientRoutes = require("./routes/clientRoutes"); +const uploadRoutes = require("./routes/uploadRoutes"); +const monitorRoutes = require("./routes/monitorRoutes"); + + + +const app = express(); + +// Middleware +app.use(cors()); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: true })); + + + +// Routes + + +app.use("/uploads", express.static(path.join(__dirname, "uploads"), { + setHeaders: (res) => { + res.setHeader("Cache-Control", "public, max-age=604800, immutable"); + } +})); + +app.use("/api/admin", adminRoutes); +app.use("/api/client", clientRoutes); +app.use("/api/files", uploadRoutes); +app.use("/api/monitor", monitorRoutes); + +app.get("/", (req, res) => { + res.send("Dine 360 Backend server is running..."); +}); + + +// Start server +const PORT = process.env.PORT || 5000; +app.listen(PORT, () => { + console.log("✅ Server listening on port", PORT); +});