From a33e517a8ad6179519bdbb1b92afabe81762f91d Mon Sep 17 00:00:00 2001 From: Kerboul Date: Thu, 3 Apr 2025 09:02:34 +0200 Subject: [PATCH 1/9] =?UTF-8?q?Nettoyer=20le=20code=20en=20supprimant=20le?= =?UTF-8?q?s=20importations=20inutilis=C3=A9es=20et=20en=20d=C3=A9commenta?= =?UTF-8?q?nt=20des=20fonctions=20pour=20am=C3=A9liorer=20la=20lisibilit?= =?UTF-8?q?=C3=A9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.js | 2 - eslint.config.mjs | 11 + ffmpeg.js | 4 +- package-lock.json | 1055 ++++++++++++++++++++++++++++++++- package.json | 3 + routes/cameraRoutes.js | 11 +- routes/imageRoutes.js | 1 - routes/measurementRoutes.js | 2 +- routes/videoRoutes.js | 19 +- server.js | 1 - src/data/filewatcher.js | 7 +- src/data/storageManager.js | 1 + src/measure/measureManager.js | 10 +- src/project/projectManager.js | 6 +- src/video/videoManager.js | 12 +- test/tester.js | 2 + 16 files changed, 1091 insertions(+), 56 deletions(-) create mode 100644 eslint.config.mjs diff --git a/api.js b/api.js index 40f5076..4469765 100644 --- a/api.js +++ b/api.js @@ -7,8 +7,6 @@ const videoRoutes = require('./routes/videoRoutes'); const imageRoutes = require('./routes/imageRoutes'); const cameraRoutes = require('./routes/cameraRoutes'); const uploadRoutes = require('./routes/uploadRoutes'); -const fileWatcher = require('./src/data/filewatcher.js'); -const ffmpeg = require('./ffmpeg.js'); router.use(cors({ origin: ['http://127.0.0.1:5500', 'http://localhost:5500', 'http://localhost:3000'], diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..a358b0d --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,11 @@ +import { defineConfig } from "eslint/config"; +import globals from "globals"; +import js from "@eslint/js"; + + +export default defineConfig([ + { files: ["**/*.{js,mjs,cjs}"] }, + { files: ["**/*.js"], languageOptions: { sourceType: "commonjs" } }, + { files: ["**/*.{js,mjs,cjs}"], languageOptions: { globals: globals.browser } }, + { files: ["**/*.{js,mjs,cjs}"], plugins: { js }, extends: ["js/recommended"] }, +]); \ No newline at end of file diff --git a/ffmpeg.js b/ffmpeg.js index 4d603c5..c37217c 100644 --- a/ffmpeg.js +++ b/ffmpeg.js @@ -1,9 +1,9 @@ const { exec } = require('child_process'); -exec('ffmpeg -version', (error, stdout, stderr) => { +exec('ffmpeg -version', (error) => { if (error) { console.log('FFmpeg is not installed. Installing FFmpeg...'); - exec('apt update && apt install -y ffmpeg', (installError, installStdout, installStderr) => { + exec('apt update && apt install -y ffmpeg', (installError) => { if (installError) { console.error(`Error installing FFmpeg: ${installError}`); return; diff --git a/package-lock.json b/package-lock.json index b523072..8b0d82a 100755 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,10 @@ "swagger-ui-express": "^5.0.1" }, "devDependencies": { + "@eslint/js": "^9.23.0", "@types/cors": "^2.8.17", + "eslint": "^9.23.0", + "globals": "^16.0.0", "nodemon": "^3.1.7" } }, @@ -78,6 +81,286 @@ "tslib": "^2.4.0" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", + "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", + "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", + "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.13.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -455,6 +738,13 @@ "@types/node": "*" } }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -484,6 +774,62 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -642,6 +988,56 @@ "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", "license": "MIT" }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -760,9 +1156,9 @@ } }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -793,6 +1189,21 @@ "node": ">= 0.10" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -802,6 +1213,13 @@ "ms": "2.0.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -910,6 +1328,202 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", + "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.2", + "@eslint/config-helpers": "^0.2.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.23.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -929,9 +1543,9 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -939,7 +1553,7 @@ "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -953,7 +1567,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -968,6 +1582,44 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" } }, "node_modules/fill-range": { @@ -1001,6 +1653,44 @@ "node": ">= 0.8" } }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1111,6 +1801,19 @@ "node": ">= 6" } }, + "node_modules/globals": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", + "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -1209,6 +1912,16 @@ "node": ">=0.10.0" } }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -1216,6 +1929,33 @@ "dev": true, "license": "ISC" }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1306,6 +2046,13 @@ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "license": "MIT" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1318,6 +2065,67 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -1330,6 +2138,13 @@ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", "license": "MIT" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.mergewith": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", @@ -1527,6 +2342,13 @@ "node": ">=12.0.0" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -1649,6 +2471,69 @@ "license": "MIT", "peer": true }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1658,6 +2543,16 @@ "node": ">= 0.8" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1667,10 +2562,20 @@ "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, "node_modules/pg": { @@ -1814,6 +2719,16 @@ "node": ">=0.10.0" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -1840,6 +2755,16 @@ "dev": true, "license": "MIT" }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -1913,6 +2838,16 @@ "node": ">=8.10.0" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2072,6 +3007,29 @@ "@img/sharp-win32-x64": "0.33.5" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -2162,6 +3120,19 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -2267,6 +3238,19 @@ "license": "0BSD", "optional": true }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -2309,6 +3293,16 @@ "node": ">= 0.8" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2342,6 +3336,32 @@ "node": ">= 0.8" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2366,6 +3386,19 @@ "node": ">= 6" } }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/z-schema": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", diff --git a/package.json b/package.json index c4aef2f..1886d14 100755 --- a/package.json +++ b/package.json @@ -22,7 +22,10 @@ "swagger-ui-express": "^5.0.1" }, "devDependencies": { + "@eslint/js": "^9.23.0", "@types/cors": "^2.8.17", + "eslint": "^9.23.0", + "globals": "^16.0.0", "nodemon": "^3.1.7" } } diff --git a/routes/cameraRoutes.js b/routes/cameraRoutes.js index 51be162..a0c235c 100644 --- a/routes/cameraRoutes.js +++ b/routes/cameraRoutes.js @@ -1,8 +1,5 @@ const express = require('express'); const router = express.Router(); -const path = require('path'); -const fs = require('fs'); -const dbTester = require('../test/tester'); const db = require('../db'); const serverError = require('../utils/serverError'); @@ -176,8 +173,8 @@ async function changeProjectStatus(projectId, status) { } } -await initCamera(); -await printCameraStatus(); +initCamera(); +printCameraStatus(); /** * @swagger @@ -232,8 +229,8 @@ async function setCameraSettings(interval, maintenance) { console.error('Erreur lors de la mise à jour des paramètres de la caméra:', err); } else { console.log('Paramètres de la caméra mis à jour avec succès.'); - captureInterval = interval; - maintenance = maintenance; + //captureInterval = interval; + //maintenance = maintenance; } }); } catch (err) { diff --git a/routes/imageRoutes.js b/routes/imageRoutes.js index 08f9a70..6c6c151 100644 --- a/routes/imageRoutes.js +++ b/routes/imageRoutes.js @@ -1,7 +1,6 @@ const express = require('express'); const router = express.Router(); const sharp = require('sharp'); -const path = require('path'); const fs = require('fs'); const dbTester = require('../test/tester'); const db = require('../db'); diff --git a/routes/measurementRoutes.js b/routes/measurementRoutes.js index 06cb504..2b21911 100644 --- a/routes/measurementRoutes.js +++ b/routes/measurementRoutes.js @@ -62,7 +62,7 @@ router.delete('/measurements/:id', async (req, res) => { return res.status(400).json({ error: 'Invalid measurement ID' }); } try { - const measurement = await measureManager.deleteMeasurement(measurementId); + await measureManager.deleteMeasurement(measurementId); res.status(200).json({ message: 'Measurement deleted successfully', id: measurementId }); } catch (error) { serverError.sendError('Error deleting measurement:', res, error, 500); diff --git a/routes/videoRoutes.js b/routes/videoRoutes.js index d909650..683aa86 100644 --- a/routes/videoRoutes.js +++ b/routes/videoRoutes.js @@ -3,7 +3,6 @@ const router = express.Router(); const db = require('../db'); const fs = require('fs'); const rangeParser = require('range-parser'); -const path = require('path'); const serverError = require('../utils/serverError'); const videoManager = require('../src/video/videoManager'); const measureManager = require('../src/measure/measureManager'); @@ -228,15 +227,15 @@ router.get('/videos/progress/:video_id', async (req, res) => { } }); -function getStatusLabel(status) { - const statusMap = { - 0: 'En attente', - 1: 'Terminé', - 2: 'Échec', - 3: 'En cours' - }; - return statusMap[status] || 'Inconnu'; -} +// function getStatusLabel(status) { +// const statusMap = { +// 0: 'En attente', +// 1: 'Terminé', +// 2: 'Échec', +// 3: 'En cours' +// }; +// return statusMap[status] || 'Inconnu'; +// } router.get('/cat', (_, res) => { diff --git a/server.js b/server.js index 9eaedcd..5987951 100644 --- a/server.js +++ b/server.js @@ -1,7 +1,6 @@ // server.js const express = require('express'); const cors = require('cors'); -const sharp = require('sharp'); const app = express(); const port = 3000; diff --git a/src/data/filewatcher.js b/src/data/filewatcher.js index 28d2a5d..8e8ed4c 100644 --- a/src/data/filewatcher.js +++ b/src/data/filewatcher.js @@ -1,7 +1,6 @@ -import db from '../../db.js'; -import path from 'path'; -import storageManager from '../data/storageManager.js'; -import fs from 'fs'; +const db = require('../../db.js'); +const storageManager = require('../data/storageManager.js'); +const fs = require('fs'); let localCounter = 0; diff --git a/src/data/storageManager.js b/src/data/storageManager.js index a2e2245..9a03d52 100644 --- a/src/data/storageManager.js +++ b/src/data/storageManager.js @@ -50,6 +50,7 @@ async function scanAllImages(dir = 'storage') { } async function saveFile(filePath, content) { + let Buffer=Buffer.from(content, 'base64'); if (Buffer.isBuffer(content)) { await fs.writeFile(filePath, content); } else { diff --git a/src/measure/measureManager.js b/src/measure/measureManager.js index 1bda4b7..039d3cf 100644 --- a/src/measure/measureManager.js +++ b/src/measure/measureManager.js @@ -1,6 +1,6 @@ -import db from '../../db.js'; -import path from 'path'; -import storageManager from '../data/storageManager.js'; +const db = require('../../db.js'); +const path = require('path'); +const storageManager = require('../data/storageManager.js'); async function uploadMeasureImage(image, projectId, orderId) { try { @@ -114,7 +114,7 @@ async function getPathList(IdList, projectId) { } -export { +module.exports = { uploadMeasureImage, addMeasureToProject, getNextOrderId, @@ -127,4 +127,4 @@ export { updateMeasurementById, getPathFromIds, getPathList -} +}; diff --git a/src/project/projectManager.js b/src/project/projectManager.js index 8e845bc..ba51c86 100644 --- a/src/project/projectManager.js +++ b/src/project/projectManager.js @@ -1,5 +1,5 @@ -import storageManager from '../data/storageManager.js'; -import db from '../../db.js'; +const storageManager = require('../data/storageManager.js'); +const db = require('../../db.js'); function createProjectDirectory(projectId) { const projectPath = `${projectId}`; @@ -69,7 +69,7 @@ async function getMeasurementsByProjectId(projectId) { return res.rows; } -export { +module.exports = { createProjectDirectory, deleteProjectDirectory, getAllProjects, diff --git a/src/video/videoManager.js b/src/video/videoManager.js index b4c9dec..5980e1a 100644 --- a/src/video/videoManager.js +++ b/src/video/videoManager.js @@ -1,16 +1,10 @@ const fs = require('fs'); const path = require('path'); -const { execSync } = require('child_process'); -const { exec } = require('child_process'); -const util = require('util'); -const execPromise = util.promisify(exec); const { spawn } = require('child_process'); -let globalProgress = {}; const serverError = require('../../utils/serverError'); const db = require('../../db'); const storageManager = require('../data/storageManager'); -const measureManager = require('../measure/measureManager'); const PROJECTS_DIR = path.join('.'); @@ -31,9 +25,9 @@ async function deleteUnfinishedVideos() { } } -async function cleanVideoFiles() { - //supprimer les fichiers vidéos qui ne sont pas associés à une vidéo de la base de données -} +// async function cleanVideoFiles() { +// //supprimer les fichiers vidéos qui ne sont pas associés à une vidéo de la base de données +// } deleteUnfinishedVideos(); diff --git a/test/tester.js b/test/tester.js index fe65af7..ca2a15c 100644 --- a/test/tester.js +++ b/test/tester.js @@ -1,5 +1,7 @@ const path = require('path'); +const __dirname = path.resolve(); + function getSmileImage() { return path.join(__dirname, '../sample/smile.png'); } From 242bbcd5971080c65fbbe6f502a72f7f124cd2c3 Mon Sep 17 00:00:00 2001 From: Kerboul Date: Thu, 3 Apr 2025 09:07:45 +0200 Subject: [PATCH 2/9] =?UTF-8?q?Renommer=20la=20fonction=20de=20connexion?= =?UTF-8?q?=20=C3=A0=20la=20base=20de=20donn=C3=A9es=20et=20am=C3=A9liorer?= =?UTF-8?q?=20la=20gestion=20des=20erreurs.=20Nettoyer=20le=20code=20en=20?= =?UTF-8?q?supprimant=20les=20commentaires=20inutiles=20et=20r=C3=A9initia?= =?UTF-8?q?liser=20le=20compteur=20apr=C3=A8s=20la=20journalisation=20des?= =?UTF-8?q?=20modifications.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db.js | 6 +++--- src/data/filewatcher.js | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/db.js b/db.js index 037e842..10b2f75 100644 --- a/db.js +++ b/db.js @@ -8,16 +8,16 @@ const client = new Client({ database: 'timelapse' }); -function connectWithRetry() { +function init_database() { client.connect(err => { if (err) { console.error('Erreur de connexion à la base de données:', err); - setTimeout(connectWithRetry, 30000); + setTimeout(init_database, 30000); } else { console.log('[DB] Connecté à la base de données PostgreSQL.'); } }); } -connectWithRetry(); +init_database(); module.exports = client; \ No newline at end of file diff --git a/src/data/filewatcher.js b/src/data/filewatcher.js index 8e8ed4c..0cd653a 100644 --- a/src/data/filewatcher.js +++ b/src/data/filewatcher.js @@ -16,9 +16,7 @@ async function checkAndRemoveInvalidEntries() { } } - // Scan all images in storage const allImages = await storageManager.scanAllImages(); - //console.log('Scanned all images:', allImages); for (const imagePath of allImages) { const entryRes = await db.query('SELECT id FROM measurements WHERE path = $1', [imagePath]); if (entryRes.rows.length === 0) { @@ -29,6 +27,7 @@ async function checkAndRemoveInvalidEntries() { } if (localCounter > 0) { console.log(`[INFO] ${localCounter} entrées ont été modifiées`); + localCounter = 0; // Reset the counter after logging } } catch (err) { console.error('Error checking and removing invalid entries:', err); From 915146c1404f34dab5eefc76acb16bb7bcd4bb15 Mon Sep 17 00:00:00 2001 From: Kerboul Date: Thu, 3 Apr 2025 09:54:29 +0200 Subject: [PATCH 3/9] =?UTF-8?q?Ajouter=20la=20gestion=20de=20la=20base=20d?= =?UTF-8?q?e=20donn=C3=A9es=20avec=20des=20fonctions=20pour=20cr=C3=A9er,?= =?UTF-8?q?=20v=C3=A9rifier=20et=20supprimer=20des=20tables.=20Mettre=20?= =?UTF-8?q?=C3=A0=20jour=20la=20connexion=20=C3=A0=20la=20base=20de=20donn?= =?UTF-8?q?=C3=A9es=20pour=20un=20environnement=20de=20d=C3=A9veloppement.?= =?UTF-8?q?=20Am=C3=A9liorer=20l'initialisation=20de=20la=20cam=C3=A9ra=20?= =?UTF-8?q?et=20corriger=20l'appel=20des=20fonctions=20asynchrones.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.js | 2 + db.js | 18 +++- routes/cameraRoutes.js | 14 ++- src/data/storageManager.js | 11 +++ src/database/database_manager.js | 144 +++++++++++++++++++++++++++++++ test/tester.js | 8 +- 6 files changed, 188 insertions(+), 9 deletions(-) create mode 100644 src/database/database_manager.js diff --git a/api.js b/api.js index 4469765..1a82e09 100644 --- a/api.js +++ b/api.js @@ -7,6 +7,8 @@ const videoRoutes = require('./routes/videoRoutes'); const imageRoutes = require('./routes/imageRoutes'); const cameraRoutes = require('./routes/cameraRoutes'); const uploadRoutes = require('./routes/uploadRoutes'); +const FileWatcher = require('./src/data/filewatcher'); +const database_manager = require('./src/database/database_manager'); router.use(cors({ origin: ['http://127.0.0.1:5500', 'http://localhost:5500', 'http://localhost:3000'], diff --git a/db.js b/db.js index 10b2f75..89adcac 100644 --- a/db.js +++ b/db.js @@ -1,6 +1,8 @@ const { Client } = require('pg'); -const client = new Client({ +let dev = true; + +let client = new Client({ host: '192.168.192.3', port: 5432, user: 'timelapse', @@ -8,11 +10,23 @@ const client = new Client({ database: 'timelapse' }); +if (dev) { + client = new Client({ + host: 'mikoshi', + port: 54322, + user: 'timelapse', + password: 'timelapse', + database: 'timelapse_dev' + }); +} + + function init_database() { + console.log('[DB] Initialisation de la base de données PostgreSQL...'); client.connect(err => { if (err) { console.error('Erreur de connexion à la base de données:', err); - setTimeout(init_database, 30000); + setTimeout(init_database, 3000); } else { console.log('[DB] Connecté à la base de données PostgreSQL.'); } diff --git a/routes/cameraRoutes.js b/routes/cameraRoutes.js index a0c235c..c5d488d 100644 --- a/routes/cameraRoutes.js +++ b/routes/cameraRoutes.js @@ -2,6 +2,7 @@ const express = require('express'); const router = express.Router(); const db = require('../db'); const serverError = require('../utils/serverError'); +const { start } = require('repl'); //const minInterval = 3; // Minutes //const maxInterval = 60; // Minutes @@ -67,7 +68,7 @@ async function getCamera() { } async function printCameraStatus() { - let camera = getCamera(); + let camera = await getCamera(); console.log('Statut de la caméra:'); console.log('Intervalle de capture:', camera.captureInterval, 'minutes'); console.log('Maintenance:', camera.maintenance === 1 ? 'En cours' : 'Aucune'); @@ -173,8 +174,15 @@ async function changeProjectStatus(projectId, status) { } } -initCamera(); -printCameraStatus(); +async function startup() { + await initCamera(); + await printCameraStatus(); +} + +startup() + .catch(err => { + console.error('Erreur lors de l\'initialisation de la caméra:', err); + }); /** * @swagger diff --git a/src/data/storageManager.js b/src/data/storageManager.js index 9a03d52..1825230 100644 --- a/src/data/storageManager.js +++ b/src/data/storageManager.js @@ -32,6 +32,17 @@ async function scanAllImages(dir = 'storage') { const projectDir = path.join(PROJECTS_DIR, dir); let results = []; + // check if the directory exists and create it if not + try { + await fs.access(projectDir); + } catch (error) { + if (error.code === 'ENOENT') { + await fs.mkdir(projectDir, { recursive: true }); + } else { + throw error; + } + } + async function scanDirectory(directory) { const files = await fs.readdir(directory); for (const file of files) { diff --git a/src/database/database_manager.js b/src/database/database_manager.js new file mode 100644 index 0000000..370a9e2 --- /dev/null +++ b/src/database/database_manager.js @@ -0,0 +1,144 @@ +const db = require('../../db.js'); + +// Fonctions de gestion de la base de données interne + +async function create_database() { + const queries = [ + `CREATE TABLE IF NOT EXISTS projects ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT, + start_date DATE, + status INTEGER NOT NULL CHECK (status = ANY (ARRAY [0, 1, 2, 3])) + );`, + `ALTER TABLE projects OWNER TO timelapse;`, + `CREATE TABLE IF NOT EXISTS measurements ( + id SERIAL PRIMARY KEY, + project_id INTEGER REFERENCES projects ON DELETE CASCADE, + timestamp TIMESTAMP NOT NULL, + path VARCHAR(255), + temperature DOUBLE PRECISION, + humidity DOUBLE PRECISION, + order_id INTEGER NOT NULL, + CONSTRAINT unique_project_photo_order UNIQUE (project_id, order_id) + );`, + `ALTER TABLE measurements OWNER TO timelapse;`, + `CREATE TABLE IF NOT EXISTS videos ( + id SERIAL PRIMARY KEY, + project_id INTEGER REFERENCES projects ON DELETE CASCADE, + measurement_ids TEXT NOT NULL, + video_file VARCHAR(255), + resolution VARCHAR(255), + duration INTEGER, + status INTEGER NOT NULL CHECK (status = ANY (ARRAY [0, 1, 2, 3])), + name VARCHAR(255), + progress DOUBLE PRECISION, + started_at TIMESTAMP, + updated_at TIMESTAMP, + eta DOUBLE PRECISION + );`, + `ALTER TABLE videos OWNER TO timelapse;`, + `CREATE TABLE IF NOT EXISTS camera ( + id SERIAL PRIMARY KEY, + interval INTEGER NOT NULL, + maintenance INTEGER NOT NULL, + active INTEGER DEFAULT 0 NOT NULL + );`, + `ALTER TABLE camera OWNER TO timelapse;` + ]; + + try { + for (const query of queries) { + await db.query(query); + } + console.log('Database tables created or verified successfully.'); + } catch (err) { + console.error('Error creating database tables:', err); + throw err; + } +} + +async function check_database_existence() { + const query = ` + SELECT table_name + FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name IN ('projects', 'measurements', 'videos', 'camera'); + `; + + try { + const result = await db.query(query); + const existingTables = result.rows.map(row => row.table_name); + + const requiredTables = ['projects', 'measurements', 'videos', 'camera']; + const missingTables = requiredTables.filter(table => !existingTables.includes(table)); + + if (missingTables.length > 0) { + console.error('Missing or improperly constructed tables:', missingTables); + throw new Error(`The following tables are missing or not properly constructed: ${missingTables.join(', ')}`); + } else { + console.log('All required tables exist and are properly constructed.'); + } + } catch (err) { + console.error('Error checking database tables:', err); + throw err; + } +} + +async function delete_database() { + const queries = [ + `DROP TABLE IF EXISTS videos;`, + `DROP TABLE IF EXISTS measurements;`, + `DROP TABLE IF EXISTS projects;`, + `DROP TABLE IF EXISTS camera;` + ]; + + try { + for (const query of queries) { + await db.query(query); + } + console.log('Database tables deleted successfully.'); + } catch (err) { + console.error('Error deleting database tables:', err); + throw err; + } +} + +async function init_function() { + try { + await check_database_existence(); + } catch (err) { + console.error('Database check failed:', err); + try { + await delete_database(); + await create_database(); + console.log('Database initialized successfully.'); + } catch (err) { + console.error('Error initializing database:', err); + throw err; + } + } finally { + console.log('Database initialization process completed.'); + } +} + +init_function() + .then(() => console.log('Database initialization completed.')) + .catch(err => console.error('Error during database initialization:', err)); + + +const database_manager = { + createVideoProject: async (project_id, measurement_ids, name, resolution, duration) => { + const query = `INSERT INTO public.videos (project_id, measurement_ids, name, resolution, duration) VALUES ($1, $2, $3, $4, $5) RETURNING id`; + const values = [project_id, measurement_ids, name, resolution, duration]; + try { + const result = await db.query(query, values); + return result.rows[0].id; + } catch (err) { + console.error('Error creating video project:', err); + throw err; + } + }, +}; + +module.exports = database_manager; \ No newline at end of file diff --git a/test/tester.js b/test/tester.js index ca2a15c..61a32d4 100644 --- a/test/tester.js +++ b/test/tester.js @@ -1,6 +1,7 @@ -const path = require('path'); +import path from 'path'; +import { fileURLToPath } from 'url'; -const __dirname = path.resolve(); +const __dirname = path.dirname(fileURLToPath(import.meta.url)); function getSmileImage() { return path.join(__dirname, '../sample/smile.png'); @@ -10,5 +11,4 @@ function getCatVideo() { return path.join(__dirname, '../sample/cat.mp4'); } -exports.getSmileImage = getSmileImage; -exports.getCatVideo = getCatVideo; \ No newline at end of file +export { getSmileImage, getCatVideo }; \ No newline at end of file From 0d0c101e20237c35b26dbefedfbe26704465d9b7 Mon Sep 17 00:00:00 2001 From: Kerboul Date: Thu, 3 Apr 2025 10:29:17 +0200 Subject: [PATCH 4/9] =?UTF-8?q?Renommer=20le=20gestionnaire=20de=20stockag?= =?UTF-8?q?e=20et=20mettre=20=C3=A0=20jour=20les=20r=C3=A9f=C3=A9rences=20?= =?UTF-8?q?dans=20les=20fichiers=20concern=C3=A9s.=20Supprimer=20les=20fic?= =?UTF-8?q?hiers=20obsol=C3=A8tes=20et=20ajouter=20un=20nouveau=20fichier?= =?UTF-8?q?=20de=20gestion=20de=20stockage.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routes/projectRoutes.js | 27 ++-- src/data/filewatcher.js | 4 +- .../{storageManager.js => storage_manager.js} | 18 ++- src/database/database_manager.js | 129 ++++++++++++++++-- src/measure/measureManager.js | 10 +- src/project/projectManager.js | 82 ----------- src/video/videoManager.js | 4 +- 7 files changed, 162 insertions(+), 112 deletions(-) rename src/data/{storageManager.js => storage_manager.js} (83%) delete mode 100644 src/project/projectManager.js diff --git a/routes/projectRoutes.js b/routes/projectRoutes.js index ae1113a..973675b 100644 --- a/routes/projectRoutes.js +++ b/routes/projectRoutes.js @@ -1,11 +1,12 @@ const express = require('express'); const router = express.Router(); -const projectManager = require('../src/project/projectManager'); const serverError = require('../utils/serverError'); +const database_manager = require('../src/database/database_manager'); +const storage_manager = require('../src/data/storage_manager'); router.get('/projects', async (req, res) => { try { - const projects = await projectManager.getAllProjects(); + const projects = await database_manager.project.get_all_projects(); res.json(projects); } catch (error) { serverError.sendError('Error getting all projects:', res, error, 500); @@ -18,7 +19,7 @@ router.get('/projects/:id', async (req, res) => { return res.status(400).json({ error: 'Invalid project ID' }); } try { - const project = await projectManager.getProjectById(projectId); + const project = await database_manager.project.get_project_by_id(projectId); res.json(project); } catch (error) { serverError.sendError('Error getting project by ID:', res, error, 500); @@ -31,7 +32,10 @@ router.get('/projects/:id/videos', async (req, res) => { return res.status(400).json({ error: 'Invalid project ID' }); } try { - const videos = await projectManager.getVideosByProjectId(projectId); + const videos = await database_manager.video.get_videos_by_project_id(projectId); + if (videos.length === 0) { + return res.status(404).json({ error: 'No videos found for this project' }); + } res.json(videos); } catch (error) { serverError.sendError('Error getting videos by project ID:', res, error, 500); @@ -44,7 +48,10 @@ router.get('/projects/:id/measurements', async (req, res) => { return res.status(400).json({ error: 'Invalid project ID' }); } try { - const measurements = await projectManager.getMeasurementsByProjectId(projectId); + const measurements = await database_manager.measurement.get_measurements_by_project_id(projectId); + if (measurements.length === 0) { + return res.status(404).json({ error: 'No measurements found for this project' }); + } res.json(measurements); } catch (error) { serverError.sendError('Error getting measurements by project ID:', res, error, 500); @@ -57,8 +64,10 @@ router.post('/projects', async (req, res) => { return res.status(400).json({ error: 'Name and description are required' }); } try { - const project = await projectManager.createProject(name, description, new Date(), 0); - projectManager.createProjectDirectory(project.id); + const date = new Date(); + const default_status = 0; + const project = await database_manager.project.create_project(name, description, date, default_status); + storage_manager.createProjectDirectory(project.id); res.status(201).json({ message: 'Project added successfully', id: project.id }); } catch (error) { serverError.sendError('Error creating project:', res, error, 500); @@ -71,8 +80,8 @@ router.delete('/projects/:id', async (req, res) => { return res.status(400).json({ error: 'Invalid project ID' }); } try { - projectManager.deleteProjectDirectory(projectId); - projectManager.deleteProjectById(projectId); + storage_manager.deleteProjectDirectory(projectId); + await database_manager.project.delete_project(projectId); res.status(200).json({ message: 'Project deleted successfully', id: projectId }); } catch (error) { serverError.sendError('Error deleting project:', res, error, 500); diff --git a/src/data/filewatcher.js b/src/data/filewatcher.js index 0cd653a..97ec123 100644 --- a/src/data/filewatcher.js +++ b/src/data/filewatcher.js @@ -1,5 +1,5 @@ const db = require('../../db.js'); -const storageManager = require('../data/storageManager.js'); +const storage_manager = require('./storage_manager.js'); const fs = require('fs'); let localCounter = 0; @@ -16,7 +16,7 @@ async function checkAndRemoveInvalidEntries() { } } - const allImages = await storageManager.scanAllImages(); + const allImages = await storage_manager.scanAllImages(); for (const imagePath of allImages) { const entryRes = await db.query('SELECT id FROM measurements WHERE path = $1', [imagePath]); if (entryRes.rows.length === 0) { diff --git a/src/data/storageManager.js b/src/data/storage_manager.js similarity index 83% rename from src/data/storageManager.js rename to src/data/storage_manager.js index 1825230..8060da1 100644 --- a/src/data/storageManager.js +++ b/src/data/storage_manager.js @@ -89,11 +89,27 @@ async function deleteFile(name) { } } +function createProjectDirectory(projectId) { + const projectPath = `${projectId}`; + createFolder(projectPath); + createFolder(`${projectPath}/images`); + createFolder(`${projectPath}/videos`); + console.log("[FILE] createProjectDirectory : " + projectPath); +} + +function deleteProjectDirectory(projectId) { + const projectPath = `${projectId}`; + deleteFolder(projectPath); + console.log("[FILE] deleteProjectDirectory : " + projectPath); +} + module.exports = { createFolder, deleteFolder, scanAllImages, saveFile, getFile, - deleteFile + deleteFile, + createProjectDirectory, + deleteProjectDirectory, }; \ No newline at end of file diff --git a/src/database/database_manager.js b/src/database/database_manager.js index 370a9e2..c38e0e1 100644 --- a/src/database/database_manager.js +++ b/src/database/database_manager.js @@ -117,7 +117,7 @@ async function init_function() { console.error('Error initializing database:', err); throw err; } - } finally { + } finally { console.log('Database initialization process completed.'); } } @@ -126,19 +126,126 @@ init_function() .then(() => console.log('Database initialization completed.')) .catch(err => console.error('Error during database initialization:', err)); - -const database_manager = { - createVideoProject: async (project_id, measurement_ids, name, resolution, duration) => { - const query = `INSERT INTO public.videos (project_id, measurement_ids, name, resolution, duration) VALUES ($1, $2, $3, $4, $5) RETURNING id`; - const values = [project_id, measurement_ids, name, resolution, duration]; +// Fonctions pour les projets +function handleDatabaseOperation(operation) { + return async (...args) => { try { - const result = await db.query(query, values); - return result.rows[0].id; + return await operation(...args); } catch (err) { - console.error('Error creating video project:', err); + console.error(`Error during database operation: ${operation.name}`, err); throw err; } - }, + }; +} + +const project = { + get_all_projects: handleDatabaseOperation(async () => { + const query = `SELECT * FROM projects;`; + return (await db.query(query)).rows; + }), + get_project_by_id: handleDatabaseOperation(async (id) => { + const query = `SELECT * FROM projects WHERE id = $1;`; + return (await db.query(query, [id])).rows[0]; + }), + create_project: handleDatabaseOperation(async (name, description, start_date, status) => { + const query = `INSERT INTO projects (name, description, start_date, status) VALUES ($1, $2, $3, $4) RETURNING *;`; + return (await db.query(query, [name, description, start_date, status])).rows[0]; + }), + edit_project_by_id: handleDatabaseOperation(async (id, updates) => { + const fields = Object.keys(updates).map((key, index) => `${key} = $${index + 2}`).join(', '); + const values = [id, ...Object.values(updates)]; + const query = `UPDATE projects SET ${fields} WHERE id = $1 RETURNING *;`; + return (await db.query(query, values)).rows[0]; + }), + delete_project_by_id: handleDatabaseOperation(async (id) => { + const query = `DELETE FROM projects WHERE id = $1;`; + await db.query(query, [id]); + }) }; -module.exports = database_manager; \ No newline at end of file +const measurement = { + get_all_measurements: handleDatabaseOperation(async () => { + const query = `SELECT * FROM measurements;`; + return (await db.query(query)).rows; + }), + get_measurement_by_id: handleDatabaseOperation(async (id) => { + const query = `SELECT * FROM measurements WHERE id = $1;`; + return (await db.query(query, [id])).rows[0]; + }), + get_measurement_by_project_and_order_id: handleDatabaseOperation(async (project_id, order_id) => { + const query = `SELECT * FROM measurements WHERE project_id = $1 AND order_id = $2;`; + return (await db.query(query, [project_id, order_id])).rows[0]; + }), + get_measurements_by_project_id: handleDatabaseOperation(async (project_id) => { + const query = `SELECT * FROM measurements WHERE project_id = $1;`; + return (await db.query(query, [project_id])).rows; + }), + create_measurement: handleDatabaseOperation(async (project_id, timestamp, path, temperature, humidity, order_id) => { + const query = `INSERT INTO measurements (project_id, timestamp, path, temperature, humidity, order_id) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *;`; + return (await db.query(query, [project_id, timestamp, path, temperature, humidity, order_id])).rows[0]; + }), + edit_measurement_by_id: handleDatabaseOperation(async (id, updates) => { + const fields = Object.keys(updates).map((key, index) => `${key} = $${index + 2}`).join(', '); + const values = [id, ...Object.values(updates)]; + const query = `UPDATE measurements SET ${fields} WHERE id = $1 RETURNING *;`; + return (await db.query(query, values)).rows[0]; + }), + delete_measurement_by_id: handleDatabaseOperation(async (id) => { + const query = `DELETE FROM measurements WHERE id = $1;`; + await db.query(query, [id]); + }) +}; + +const video = { + get_all_videos: handleDatabaseOperation(async () => { + const query = `SELECT * FROM videos;`; + return (await db.query(query)).rows; + }), + get_video_by_id: handleDatabaseOperation(async (id) => { + const query = `SELECT * FROM videos WHERE id = $1;`; + return (await db.query(query, [id])).rows[0]; + }), + get_videos_by_project_id: handleDatabaseOperation(async (project_id) => { + const query = `SELECT * FROM videos WHERE project_id = $1;`; + return (await db.query(query, [project_id])).rows; + }), + create_video: handleDatabaseOperation(async (project_id, measurement_ids, video_file, resolution, duration, status, name, progress, started_at, updated_at, eta) => { + const query = `INSERT INTO videos (project_id, measurement_ids, video_file, resolution, duration, status, name, progress, started_at, updated_at, eta) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING *;`; + return (await db.query(query, [project_id, measurement_ids, video_file, resolution, duration, status, name, progress, started_at, updated_at, eta])).rows[0]; + }), + edit_video_by_id: handleDatabaseOperation(async (id, updates) => { + const fields = Object.keys(updates).map((key, index) => `${key} = $${index + 2}`).join(', '); + const values = [id, ...Object.values(updates)]; + const query = `UPDATE videos SET ${fields} WHERE id = $1 RETURNING *;`; + return (await db.query(query, values)).rows[0]; + }), + delete_video_by_id: handleDatabaseOperation(async (id) => { + const query = `DELETE FROM videos WHERE id = $1;`; + await db.query(query, [id]); + }) +}; + +const camera = { + get_camera: handleDatabaseOperation(async () => { + const query = `SELECT * FROM camera;`; + return (await db.query(query)).rows; + }), + edit_camera: handleDatabaseOperation(async (id, updates) => { + const fields = Object.keys(updates).map((key, index) => `${key} = $${index + 2}`).join(', '); + const values = [id, ...Object.values(updates)]; + const query = `UPDATE camera SET ${fields} WHERE id = $1 RETURNING *;`; + return (await db.query(query, values)).rows[0]; + }), + delete_camera: handleDatabaseOperation(async (id) => { + const query = `DELETE FROM camera WHERE id = $1;`; + await db.query(query, [id]); + }) +}; + +// Export des modules +module.exports = { + project, + measurement, + video, + camera +}; diff --git a/src/measure/measureManager.js b/src/measure/measureManager.js index 039d3cf..5e86e51 100644 --- a/src/measure/measureManager.js +++ b/src/measure/measureManager.js @@ -1,16 +1,16 @@ const db = require('../../db.js'); const path = require('path'); -const storageManager = require('../data/storageManager.js'); +const storage_manager = require('../data/storage_manager.js'); async function uploadMeasureImage(image, projectId, orderId) { try { // Ensure that folder creation and file saving are awaited - const projectDir = await storageManager.createFolder('./storage/' + projectId.toString()); - const imagesDir = await storageManager.createFolder(path.join(projectDir, 'images')); + const projectDir = await storage_manager.createFolder('./storage/' + projectId.toString()); + const imagesDir = await storage_manager.createFolder(path.join(projectDir, 'images')); const imagePath = path.join(imagesDir, `${orderId}.jpg`); // Save the file and await completion - await storageManager.saveFile(imagePath, image.buffer); + await storage_manager.saveFile(imagePath, image.buffer); console.log("[FILE] uploadMeasureImage - Image saved to: " + imagePath); return imagePath; @@ -24,7 +24,7 @@ async function getMeasureImage(projectId, orderId) { const projectPath = `${projectId}`; const imagePath = `${projectPath}/${orderId}.jpg`; console.log("[FILE] getMeasureImage - Image path: " + imagePath); - return storageManager.getFile(imagePath); + return storage_manager.getFile(imagePath); } async function getNextOrderId(projectId) { diff --git a/src/project/projectManager.js b/src/project/projectManager.js deleted file mode 100644 index ba51c86..0000000 --- a/src/project/projectManager.js +++ /dev/null @@ -1,82 +0,0 @@ -const storageManager = require('../data/storageManager.js'); -const db = require('../../db.js'); - -function createProjectDirectory(projectId) { - const projectPath = `${projectId}`; - storageManager.createFolder(projectPath); - storageManager.createFolder(`${projectPath}/images`); - storageManager.createFolder(`${projectPath}/videos`); - console.log("[FILE] createProjectDirectory : " + projectPath); -} - -function deleteProjectDirectory(projectId) { - const projectPath = `${projectId}`; - storageManager.deleteFolder(projectPath); - console.log("[FILE] deleteProjectDirectory : " + projectPath); -} - -async function getAllProjects() { - const query = 'SELECT * FROM public.projects'; - const res = await db.query(query); - //console.log("[DB] getAllProjects : ", res.rows); - return res.rows; -} - -async function getProjectById(projectId) { - const query = 'SELECT * FROM public.projects WHERE id = $1'; - const values = [projectId]; - const res = await db.query(query, values); - //console.log("[DB] getProjectById : ", res.rows[0]); - return res.rows[0]; -} - -async function createProject(name, description, start_date, status) { - const query = 'INSERT INTO public.projects (name, description, start_date, status) VALUES ($1, $2, $3, $4) RETURNING *'; - const values = [name, description, start_date, status]; - const res = await db.query(query, values); - //console.log("[DB] createProject : ", res.rows[0]); - return res.rows[0]; -} - -async function editProjectById(projectID, name, description, startDate, status) { - const query = 'UPDATE public.projects SET name = $1, description = $2, start_date = $3, status = $4 WHERE id = $5 RETURNING *'; - const values = [name, description, startDate, status, projectID]; - const res = await db.query(query, values); - //console.log("[DB] editProjectById : ", res.rows[0]); - return res.rows[0]; -} - -async function deleteProjectById(projectId) { - const query = 'DELETE FROM public.projects WHERE id = $1'; - const values = [projectId]; - //console.log("[DB] deleteProjectById : ", values); - await db.query(query, values); -} - -async function getVideosByProjectId(projectId) { - const query = 'SELECT * FROM public.videos WHERE project_id = $1'; - const values = [projectId]; - const res = await db.query(query, values); - //console.log("[DB] getVideosByProjectId : ", res.rows); - return res.rows; -} - -async function getMeasurementsByProjectId(projectId) { - const query = 'SELECT * FROM public.measurements WHERE project_id = $1'; - const values = [projectId]; - const res = await db.query(query, values); - //console.log("[DB] getMeasurementsByProjectId : ", res.rows); - return res.rows; -} - -module.exports = { - createProjectDirectory, - deleteProjectDirectory, - getAllProjects, - getProjectById, - createProject, - editProjectById, - deleteProjectById, - getVideosByProjectId, - getMeasurementsByProjectId -}; diff --git a/src/video/videoManager.js b/src/video/videoManager.js index 5980e1a..2f8a3fa 100644 --- a/src/video/videoManager.js +++ b/src/video/videoManager.js @@ -4,7 +4,7 @@ const { spawn } = require('child_process'); const serverError = require('../../utils/serverError'); const db = require('../../db'); -const storageManager = require('../data/storageManager'); +const storage_manager = require('../data/storage_manager'); const PROJECTS_DIR = path.join('.'); @@ -219,7 +219,7 @@ async function createVideo(projectId) { const workdir = path.join(PROJECTS_DIR, 'storage', `${projectId}`); const dir = path.join(PROJECTS_DIR, 'storage', `${projectId}`, 'images'); console.log('dir:', dir); - const images = storageManager.scanAllImages(dir); + const images = storage_manager.scanAllImages(dir); console.log('images:', images); // Trier les images numériquement From c3b2059428f0d950ed1925a525692f4ed1eb2542 Mon Sep 17 00:00:00 2001 From: Kerboul Date: Thu, 3 Apr 2025 10:31:29 +0200 Subject: [PATCH 5/9] =?UTF-8?q?Refactor=20le=20gestionnaire=20de=20stockag?= =?UTF-8?q?e=20pour=20encapsuler=20les=20fonctions=20de=20cr=C3=A9ation=20?= =?UTF-8?q?et=20de=20suppression=20de=20r=C3=A9pertoires=20de=20projet=20d?= =?UTF-8?q?ans=20un=20objet.=20Mettre=20=C3=A0=20jour=20les=20routes=20pou?= =?UTF-8?q?r=20utiliser=20la=20nouvelle=20structure.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routes/projectRoutes.js | 4 ++-- src/data/storage_manager.js | 29 +++++++++++++++-------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/routes/projectRoutes.js b/routes/projectRoutes.js index 973675b..acfbc3b 100644 --- a/routes/projectRoutes.js +++ b/routes/projectRoutes.js @@ -67,7 +67,7 @@ router.post('/projects', async (req, res) => { const date = new Date(); const default_status = 0; const project = await database_manager.project.create_project(name, description, date, default_status); - storage_manager.createProjectDirectory(project.id); + storage_manager.project.createProjectDirectory(project.id); res.status(201).json({ message: 'Project added successfully', id: project.id }); } catch (error) { serverError.sendError('Error creating project:', res, error, 500); @@ -80,7 +80,7 @@ router.delete('/projects/:id', async (req, res) => { return res.status(400).json({ error: 'Invalid project ID' }); } try { - storage_manager.deleteProjectDirectory(projectId); + storage_manager.project.deleteProjectDirectory(projectId); await database_manager.project.delete_project(projectId); res.status(200).json({ message: 'Project deleted successfully', id: projectId }); } catch (error) { diff --git a/src/data/storage_manager.js b/src/data/storage_manager.js index 8060da1..625ecfc 100644 --- a/src/data/storage_manager.js +++ b/src/data/storage_manager.js @@ -89,19 +89,21 @@ async function deleteFile(name) { } } -function createProjectDirectory(projectId) { - const projectPath = `${projectId}`; - createFolder(projectPath); - createFolder(`${projectPath}/images`); - createFolder(`${projectPath}/videos`); - console.log("[FILE] createProjectDirectory : " + projectPath); -} +const project = { + createProjectDirectory: async function (projectId) { + const projectPath = `${projectId}`; + await createFolder(projectPath); + await createFolder(`${projectPath}/images`); + await createFolder(`${projectPath}/videos`); + console.log("[FILE] createProjectDirectory : " + projectPath); + }, -function deleteProjectDirectory(projectId) { - const projectPath = `${projectId}`; - deleteFolder(projectPath); - console.log("[FILE] deleteProjectDirectory : " + projectPath); -} + deleteProjectDirectory: async function (projectId) { + const projectPath = `${projectId}`; + await deleteFolder(projectPath); + console.log("[FILE] deleteProjectDirectory : " + projectPath); + } +}; module.exports = { createFolder, @@ -110,6 +112,5 @@ module.exports = { saveFile, getFile, deleteFile, - createProjectDirectory, - deleteProjectDirectory, + project }; \ No newline at end of file From 6077dfd7167928d9a94a43e4c327d35c3c967d57 Mon Sep 17 00:00:00 2001 From: Kerboul Date: Thu, 3 Apr 2025 11:03:10 +0200 Subject: [PATCH 6/9] =?UTF-8?q?Refactor=20la=20gestion=20des=20mesures=20e?= =?UTF-8?q?n=20rempla=C3=A7ant=20le=20gestionnaire=20de=20mesures=20par=20?= =?UTF-8?q?le=20gestionnaire=20de=20stockage.=20Ajouter=20des=20fonctions?= =?UTF-8?q?=20pour=20g=C3=A9rer=20les=20images=20et=20les=20chemins=20des?= =?UTF-8?q?=20mesures.=20Am=C3=A9liorer=20la=20gestion=20des=20erreurs=20e?= =?UTF-8?q?t=20nettoyer=20le=20code.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routes/measurementRoutes.js | 84 +++++++------------- routes/uploadRoutes.js | 18 ++++- routes/videoRoutes.js | 7 +- src/data/storage_manager.js | 70 +++++++++++++++-- src/database/database_manager.js | 14 ++++ src/measure/measureManager.js | 130 ------------------------------- 6 files changed, 124 insertions(+), 199 deletions(-) delete mode 100644 src/measure/measureManager.js diff --git a/routes/measurementRoutes.js b/routes/measurementRoutes.js index 2b21911..eb016a6 100644 --- a/routes/measurementRoutes.js +++ b/routes/measurementRoutes.js @@ -1,86 +1,54 @@ const express = require('express'); const router = express.Router(); -const db = require('../db'); -const measureManager = require('../src/measure/measureManager'); -const serverError = require('../utils/serverError'); +const database_manager = require('../src/database/database_manager'); router.get('/measurements', (req, res) => { - const query = 'SELECT * FROM public.measurements'; - db.query(query, (err, results) => { - if (err) { - serverError.sendError('Erreur lors de la récupération des mesures:', res, err, 500); - } - res.json(results.rows); - }); + const measurements = database_manager.measurement.get_all_measurements(); + if (!measurements) { + return res.status(404).json({ error: 'No measurements found' }); + } + res.json(measurements); }); router.get('/measurements/:id', (req, res) => { - const measurementId = req.params.id; - if (!measurementId || isNaN(measurementId)) { - return res.status(400).json({ error: 'Invalid measurement ID' }); + const measurement = database_manager.measurement.get_measurement_by_id(req.params.id); + if (!measurement) { + return res.status(404).json({ error: 'Measurement not found' }); } - const query = 'SELECT * FROM public.measurements WHERE id = $1'; - db.query(query, [measurementId], (err, results) => { - if (err) { - serverError.sendError('Erreur lors de la récupération de la mesure:', res, err, 500); - } - res.json(results.rows); - }); + res.json(measurement); }); router.get('/measurements/:projectId/:orderId', async (req, res) => { - const projectId = req.params.projectId; - const orderId = req.params.orderId; - if (!projectId || isNaN(projectId) || !orderId || isNaN(orderId)) { - return res.status(400).json({ error: 'Invalid project ID or order ID' }); - } - try { - const measurement = await measureManager.getMeasurement(projectId, orderId); - res.json(measurement); - } catch (error) { - serverError.sendError('Error getting measurement:', res, error, 500); + const measurement = await database_manager.measurement.get_measurement_by_project_and_order_id(req.params.projectId, req.params.orderId); + if (!measurement) { + return res.status(404).json({ error: 'Measurement not found' }); } + res.json(measurement); }); router.post('/measurements', (req, res) => { - const { project_id, timestamp, image_path, temperature, humidity } = req.body; - if (!project_id || !timestamp || !image_path || !temperature || !humidity) { + const { projectId, timestamp, imagePath, temperature, humidity, orderId } = req.body; + if (!projectId || !timestamp || !imagePath || !temperature || !humidity || !orderId) { return res.status(400).json({ error: 'All fields are required' }); } - const query = 'INSERT INTO public.measurements (project_id, timestamp, image_path, temperature, humidity) VALUES ($1, $2, $3, $4, $5) RETURNING id'; - db.query(query, [project_id, timestamp, image_path, temperature, humidity], (err, results) => { - if (err) { - serverError.sendError('Erreur lors de l\'ajout de la mesure:', res, err, 500); - } - res.status(201).json({ message: 'Mesure ajoutée avec succès', id: results.rows[0].id }); - }); + const measurement = database_manager.measurement.add_measurement(projectId, timestamp, imagePath, temperature, humidity, orderId); + res.status(201).json(measurement); }); router.delete('/measurements/:id', async (req, res) => { - const measurementId = req.params.id; - if (!measurementId || isNaN(measurementId)) { - return res.status(400).json({ error: 'Invalid measurement ID' }); - } - try { - await measureManager.deleteMeasurement(measurementId); - res.status(200).json({ message: 'Measurement deleted successfully', id: measurementId }); - } catch (error) { - serverError.sendError('Error deleting measurement:', res, error, 500); + const measurement = await database_manager.measurement.delete_measurement_by_id(req.params.id); + if (!measurement) { + return res.status(404).json({ error: 'Measurement not found' }); } + res.json({ message: 'Measurement deleted successfully', id: measurement.id }); }); router.delete('/measurements/:projectId/:orderId', async (req, res) => { - const projectId = req.params.projectId; - const orderId = req.params.orderId; - if (!projectId || isNaN(projectId) || !orderId || isNaN(orderId)) { - return res.status(400).json({ error: 'Invalid project ID or order ID' }); - } - try { - const measurement = await measureManager.deleteMeasurementByOrderId(projectId, orderId); - res.status(200).json({ message: 'Measurement deleted successfully', id: measurement.id }); - } catch (error) { - serverError.sendError('Error deleting measurement:', res, error, 500); + const measurement = await database_manager.measurement.delete_measurement_by_project_and_order_id(req.params.projectId, req.params.orderId); + if (!measurement) { + return res.status(404).json({ error: 'Measurement not found' }); } + res.json({ message: 'Measurement deleted successfully', id: measurement.id }); }); module.exports = router; diff --git a/routes/uploadRoutes.js b/routes/uploadRoutes.js index 2f7e4b8..f39e3db 100644 --- a/routes/uploadRoutes.js +++ b/routes/uploadRoutes.js @@ -1,7 +1,8 @@ const express = require('express'); const router = express.Router(); const multer = require('multer'); -const measureManager = require('../src/measure/measureManager'); +const database_manager = require('../src/database/database_manager'); +const storage_manager = require('../src/data/storage_manager'); const serverError = require('../utils/serverError'); const upload = multer({ storage: multer.memoryStorage() }); @@ -17,7 +18,10 @@ router.post('/uploadmeasurement', upload.single('image'), async (req, res) => { } try { - const nextOrderId = await measureManager.getNextOrderId(projectId); + const nextOrderId = await database_manager.measurement.get_next_order_id(projectId); + if (nextOrderId === null) { + return res.status(404).json({ error: 'Project not found' }); + } // Log types for debugging console.log('Types:', { @@ -26,8 +30,14 @@ router.post('/uploadmeasurement', upload.single('image'), async (req, res) => { nextOrderId: typeof nextOrderId }); - const imagePath = await measureManager.uploadMeasureImage(image, projectId, nextOrderId); - const measurement = await measureManager.addMeasureToProject(projectId, timestamp, imagePath, temperature, humidity, nextOrderId); + const imagePath = await storage_manager.measurement.upload_measurement_image(image, projectId, nextOrderId); + if (!imagePath) { + return res.status(500).json({ error: 'Failed to upload image' }); + } + const measurement = await database_manager.measurement.add_measurement(projectId, timestamp, imagePath, temperature, humidity, nextOrderId); + if (!measurement) { + return res.status(500).json({ error: 'Failed to add measurement' }); + } res.json({ message: 'Measurement uploaded successfully', path: imagePath, id: measurement.id }); } catch (error) { serverError.sendError('Error uploading measurement:', res, error, 500); diff --git a/routes/videoRoutes.js b/routes/videoRoutes.js index 683aa86..bd6f3d9 100644 --- a/routes/videoRoutes.js +++ b/routes/videoRoutes.js @@ -5,7 +5,7 @@ const fs = require('fs'); const rangeParser = require('range-parser'); const serverError = require('../utils/serverError'); const videoManager = require('../src/video/videoManager'); -const measureManager = require('../src/measure/measureManager'); +const storage_manager = require('../src/data/storage_manager'); const dbTester = require('../test/tester'); router.get('/videos', (req, res) => { @@ -55,7 +55,10 @@ router.post('/videos', async (req, res) => { } const { duration: videoDuration, measurement_ids: videoMeasurementIds, project_id: videoProjectId } = result.rows[0]; - const pathList = await measureManager.getPathList(videoMeasurementIds, videoProjectId); + const pathList = await storage_manager.measurement.get_path_list(videoMeasurementIds, project_id); + if (!pathList || pathList.length === 0) { + return res.status(404).json({ error: 'Aucun chemin trouvé pour les mesures' }); + } // parser la résolution (ex: 1920x1080) const [res_width, res_height] = resolution.split('x').map(Number); diff --git a/src/data/storage_manager.js b/src/data/storage_manager.js index 625ecfc..78615a6 100644 --- a/src/data/storage_manager.js +++ b/src/data/storage_manager.js @@ -1,6 +1,7 @@ const fs = require('fs').promises; const path = require('path'); const PROJECTS_DIR = path.join('.'); +const database_manager = require('../database/database_manager.js'); async function createFolder(name) { const projectDir = path.join(PROJECTS_DIR, `${name}`); @@ -89,22 +90,79 @@ async function deleteFile(name) { } } +async function handleFileOperation(operation, ...args) { + try { + return await operation(...args); + } catch (error) { + console.error(`[FILE OPERATION ERROR] ${error.message}`); + throw error; + } +} + const project = { createProjectDirectory: async function (projectId) { const projectPath = `${projectId}`; - await createFolder(projectPath); - await createFolder(`${projectPath}/images`); - await createFolder(`${projectPath}/videos`); + await handleFileOperation(createFolder, projectPath); + await handleFileOperation(createFolder, `${projectPath}/images`); + await handleFileOperation(createFolder, `${projectPath}/videos`); console.log("[FILE] createProjectDirectory : " + projectPath); }, deleteProjectDirectory: async function (projectId) { const projectPath = `${projectId}`; - await deleteFolder(projectPath); + await handleFileOperation(deleteFolder, projectPath); console.log("[FILE] deleteProjectDirectory : " + projectPath); } }; +const measurement = { + get_measurement_image: async function (projectId, orderId) { + const projectPath = `${projectId}`; + const imagePath = `${projectPath}/images/${orderId}.jpg`; + console.log("[FILE] get_measurement_image : " + imagePath); + return await handleFileOperation(getFile, imagePath); + }, + + upload_measurement_image: async function (image, projectId, orderId) { + const projectPath = `${projectId}`; + const imagePath = `${projectPath}/images/${orderId}.jpg`; + console.log("[FILE] upload_measurement_image : " + imagePath); + await handleFileOperation(saveFile, imagePath, image.buffer); + return imagePath; + }, + + get_path_from_id: async function (projectId, orderId) { + const query = database_manager.measurement.get_measurement_by_project_and_order_id(projectId, orderId); + return query.path; + }, + + get_path_list: async function (IdList, projectId) { + let parsedIdList; + try { + parsedIdList = JSON.parse(IdList); + } catch (e) { + console.error("Error parsing IdList:", e); + return []; + } + + const pathList = []; + for (const orderId of parsedIdList) { + const path = await this.get_path_from_id(projectId, orderId); + pathList.push(path); + } + return pathList; + }, +} + +const video = { + get_video: async function (projectId, orderId) { + const projectPath = `${projectId}`; + const videoPath = `${projectPath}/videos/${orderId}.mp4`; + console.log("[FILE] get_video : " + videoPath); + return await handleFileOperation(getFile, videoPath); + } +} + module.exports = { createFolder, deleteFolder, @@ -112,5 +170,7 @@ module.exports = { saveFile, getFile, deleteFile, - project + project, + measurement, + video }; \ No newline at end of file diff --git a/src/database/database_manager.js b/src/database/database_manager.js index c38e0e1..bfc1735 100644 --- a/src/database/database_manager.js +++ b/src/database/database_manager.js @@ -190,9 +190,23 @@ const measurement = { const query = `UPDATE measurements SET ${fields} WHERE id = $1 RETURNING *;`; return (await db.query(query, values)).rows[0]; }), + + edit_measurement_by_project_and_order_id: handleDatabaseOperation(async (project_id, order_id, updates) => { + const fields = Object.keys(updates).map((key, index) => `${key} = $${index + 3}`).join(', '); + const values = [project_id, order_id, ...Object.values(updates)]; + const query = `UPDATE measurements SET ${fields} WHERE project_id = $1 AND order_id = $2 RETURNING *;`; + return (await db.query(query, values)).rows[0]; + }), + delete_measurement_by_id: handleDatabaseOperation(async (id) => { const query = `DELETE FROM measurements WHERE id = $1;`; await db.query(query, [id]); + }), + + get_next_order_id: handleDatabaseOperation(async (project_id) => { + const query = `SELECT COALESCE(MAX(order_id), 0) + 1 AS next_order_id FROM measurements WHERE project_id = $1;`; + const result = await db.query(query, [project_id]); + return result.rows[0].next_order_id; }) }; diff --git a/src/measure/measureManager.js b/src/measure/measureManager.js deleted file mode 100644 index 5e86e51..0000000 --- a/src/measure/measureManager.js +++ /dev/null @@ -1,130 +0,0 @@ -const db = require('../../db.js'); -const path = require('path'); -const storage_manager = require('../data/storage_manager.js'); - -async function uploadMeasureImage(image, projectId, orderId) { - try { - // Ensure that folder creation and file saving are awaited - const projectDir = await storage_manager.createFolder('./storage/' + projectId.toString()); - const imagesDir = await storage_manager.createFolder(path.join(projectDir, 'images')); - const imagePath = path.join(imagesDir, `${orderId}.jpg`); - - // Save the file and await completion - await storage_manager.saveFile(imagePath, image.buffer); - - console.log("[FILE] uploadMeasureImage - Image saved to: " + imagePath); - return imagePath; - } catch (error) { - console.error('Error in uploadMeasureImage:', error); - throw error; - } - } - -async function getMeasureImage(projectId, orderId) { - const projectPath = `${projectId}`; - const imagePath = `${projectPath}/${orderId}.jpg`; - console.log("[FILE] getMeasureImage - Image path: " + imagePath); - return storage_manager.getFile(imagePath); -} - -async function getNextOrderId(projectId) { - const query = 'SELECT MAX(order_id) FROM public.measurements WHERE project_id = $1'; - const values = [projectId]; - const res = await db.query(query, values); - console.log("[DB] getNextOrderId - Max order_id: " + res.rows[0].max); - return res.rows[0].max + 1; -} - -async function addMeasureToProject(projectId, orderId, timestamp, path, temperature, humidity) { - const query = 'INSERT INTO public.measurements (project_id, timestamp, path, temperature, humidity, order_id) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *'; - const values = [projectId, orderId, timestamp, path, temperature, humidity]; - const res = await db.query(query, values); - return res.rows[0]; -} - -async function getMeasurements(projectId) { - const query = 'SELECT * FROM public.measurements WHERE project_id = $1'; - const values = [projectId]; - const res = await db.query(query, values); - return res.rows; -} - -async function getMeasurement(projectId, orderId) { - const query = 'SELECT * FROM public.measurements WHERE project_id = $1 AND order_id = $2'; - const values = [projectId, orderId]; - const res = await db.query(query, values); - return res.rows[0]; -} - -async function getMeasurementById(id) { - const query = 'SELECT * FROM public.measurements WHERE id = $1'; - const values = [id]; - const res = await db.query(query, values); - return res.rows[0]; -} - -async function updateMeasurement(projectId, orderId, timestamp, path, temperature, humidity) { - const query = 'UPDATE public.measurements SET timestamp = $3, path = $4, temperature = $5, humidity = $6 WHERE project_id = $1 AND order_id = $2 RETURNING *'; - const values = [projectId, orderId, timestamp, path, temperature, humidity]; - const res = await db.query(query, values); - return res.rows[0]; -} - -async function updateMeasurementById(id, timestamp, path, temperature, humidity) { - const query = 'UPDATE public.measurements SET timestamp = $2, path = $3, temperature = $4, humidity = $5 WHERE id = $1 RETURNING *'; - const values = [id, timestamp, path, temperature, humidity]; - const res = await db.query(query, values); - return res.rows[0]; -} - -async function deleteMeasurement(id) { - const query = 'DELETE FROM public.measurements WHERE id = $1'; - const values = [id]; - const res = await db.query(query, values); - return res.rows[0]; -} - -async function getPathFromIds(projectId, orderId) { - console.log("Getting path from ids:", projectId, orderId); - const query = 'SELECT path FROM public.measurements WHERE project_id = $1 AND order_id = $2'; - const values = [projectId, orderId]; - const res = await db.query(query, values); - return res.rows[0].path; -} - -async function getPathList(IdList, projectId) { - // Convertir la chaîne de caractères en tableau - let parsedIdList; - try { - parsedIdList = JSON.parse(IdList); - } catch (e) { - console.error("Erreur lors de la conversion de la chaîne en tableau:", e); - return []; - } - - console.log(parsedIdList); - const pathList = []; - for (const orderId of parsedIdList) { - console.log(orderId); - const path = await getPathFromIds(projectId, orderId); - console.log(path); - pathList.push(path); - } - return pathList; -} - - -module.exports = { - uploadMeasureImage, - addMeasureToProject, - getNextOrderId, - getMeasurements, - getMeasurement, - updateMeasurement, - deleteMeasurement, - getMeasureImage, - getMeasurementById, - updateMeasurementById, - getPathFromIds, - getPathList -}; From 03ec179590cc894376ec5d5a13d8006b9235e538 Mon Sep 17 00:00:00 2001 From: Kerboul Date: Thu, 3 Apr 2025 11:27:11 +0200 Subject: [PATCH 7/9] =?UTF-8?q?Ajout=20de=20la=20gestion=20des=20vid=C3=A9?= =?UTF-8?q?os=20inachev=C3=A9es=20et=20mise=20=C3=A0=20jour=20des=20foncti?= =?UTF-8?q?ons=20de=20cr=C3=A9ation=20et=20de=20mise=20=C3=A0=20jour=20des?= =?UTF-8?q?=20vid=C3=A9os=20dans=20le=20gestionnaire=20de=20base=20de=20do?= =?UTF-8?q?nn=C3=A9es.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routes/videoRoutes.js | 3 +- src/data/storage_manager.js | 20 ++++ src/database/database_manager.js | 28 ++++- src/video/videoManager.js | 179 ++++++++----------------------- 4 files changed, 88 insertions(+), 142 deletions(-) diff --git a/routes/videoRoutes.js b/routes/videoRoutes.js index bd6f3d9..0ff5da4 100644 --- a/routes/videoRoutes.js +++ b/routes/videoRoutes.js @@ -5,6 +5,7 @@ const fs = require('fs'); const rangeParser = require('range-parser'); const serverError = require('../utils/serverError'); const videoManager = require('../src/video/videoManager'); +const database_manager = require('../src/database/database_manager'); const storage_manager = require('../src/data/storage_manager'); const dbTester = require('../test/tester'); @@ -70,7 +71,7 @@ router.post('/videos', async (req, res) => { videoManager.createVideoWithList(videoProjectId, pathList, videoDuration, videoId, res_width, res_height) .then(videoFile => { console.log('Rendu vidéo terminé:', videoFile); - return videoManager.updateVideoFile(videoId, videoFile); + return database_manager.video.update_video_file_path_by_id(videoId, videoFile); }) .catch(error => { console.error('Échec du rendu vidéo:', error); diff --git a/src/data/storage_manager.js b/src/data/storage_manager.js index 78615a6..e0a54db 100644 --- a/src/data/storage_manager.js +++ b/src/data/storage_manager.js @@ -160,6 +160,26 @@ const video = { const videoPath = `${projectPath}/videos/${orderId}.mp4`; console.log("[FILE] get_video : " + videoPath); return await handleFileOperation(getFile, videoPath); + }, + + delete_video: async function (videoId) { + const query = database_manager.video.get_video_by_id(videoId); + const videoPath = query.video_file; + console.log("[FILE] delete_video : " + videoPath); + return await handleFileOperation(deleteFile, videoPath); + }, + + delete_unfinished_videos: async function () { + const unfinishedVideos = await database_manager.video.get_unfinished_videos(); + + for (const video of unfinishedVideos) { + try { + await this.delete_video(video.id); + console.log(`Deleted unfinished video with id: ${video.id}`); + } catch (error) { + console.error(`Error deleting unfinished video with id: ${video.id}`, error); + } + } } } diff --git a/src/database/database_manager.js b/src/database/database_manager.js index bfc1735..2e5cf26 100644 --- a/src/database/database_manager.js +++ b/src/database/database_manager.js @@ -215,28 +215,46 @@ const video = { const query = `SELECT * FROM videos;`; return (await db.query(query)).rows; }), + get_video_by_id: handleDatabaseOperation(async (id) => { const query = `SELECT * FROM videos WHERE id = $1;`; return (await db.query(query, [id])).rows[0]; }), + get_videos_by_project_id: handleDatabaseOperation(async (project_id) => { const query = `SELECT * FROM videos WHERE project_id = $1;`; return (await db.query(query, [project_id])).rows; }), - create_video: handleDatabaseOperation(async (project_id, measurement_ids, video_file, resolution, duration, status, name, progress, started_at, updated_at, eta) => { - const query = `INSERT INTO videos (project_id, measurement_ids, video_file, resolution, duration, status, name, progress, started_at, updated_at, eta) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING *;`; - return (await db.query(query, [project_id, measurement_ids, video_file, resolution, duration, status, name, progress, started_at, updated_at, eta])).rows[0]; + + create_video: handleDatabaseOperation(async (projectId, measurementIds, name, resolution, duration, status = 0) => { + const query = `INSERT INTO public.videos (project_id, measurement_ids, name, resolution, duration, status) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id;`; + const values = [projectId, measurementIds, name, resolution, duration, status]; + return (await db.query(query, values)).rows[0]; }), + edit_video_by_id: handleDatabaseOperation(async (id, updates) => { const fields = Object.keys(updates).map((key, index) => `${key} = $${index + 2}`).join(', '); const values = [id, ...Object.values(updates)]; const query = `UPDATE videos SET ${fields} WHERE id = $1 RETURNING *;`; return (await db.query(query, values)).rows[0]; }), + + update_video_file_path_by_id: handleDatabaseOperation(async (id, video_file) => { + const query = `UPDATE videos SET video_file = $1 WHERE id = $2 RETURNING *;`; + return (await db.query(query, [video_file, id])).rows[0]; + } + ), + delete_video_by_id: handleDatabaseOperation(async (id) => { const query = `DELETE FROM videos WHERE id = $1;`; await db.query(query, [id]); - }) + }), + + get_unfinished_videos: handleDatabaseOperation(async () => { + // récupérer liste des vidéos dont le status est = 0, 2 ou 3 + const query = `SELECT * FROM videos WHERE status IN (0, 2, 3);`; + return (await db.query(query)).rows; + }), }; const camera = { @@ -244,12 +262,14 @@ const camera = { const query = `SELECT * FROM camera;`; return (await db.query(query)).rows; }), + edit_camera: handleDatabaseOperation(async (id, updates) => { const fields = Object.keys(updates).map((key, index) => `${key} = $${index + 2}`).join(', '); const values = [id, ...Object.values(updates)]; const query = `UPDATE camera SET ${fields} WHERE id = $1 RETURNING *;`; return (await db.query(query, values)).rows[0]; }), + delete_camera: handleDatabaseOperation(async (id) => { const query = `DELETE FROM camera WHERE id = $1;`; await db.query(query, [id]); diff --git a/src/video/videoManager.js b/src/video/videoManager.js index 2f8a3fa..deacba1 100644 --- a/src/video/videoManager.js +++ b/src/video/videoManager.js @@ -2,53 +2,11 @@ const fs = require('fs'); const path = require('path'); const { spawn } = require('child_process'); -const serverError = require('../../utils/serverError'); -const db = require('../../db'); +const database_manager = require('../database/database_manager'); const storage_manager = require('../data/storage_manager'); const PROJECTS_DIR = path.join('.'); -async function deleteUnfinishedVideos() { - // Au démarrage du backend, supprimer les vidéos inachevées (donc en status 1) - const unfinishedVideos = await db.query(` - SELECT id FROM public.videos - WHERE status = 0 OR status = 2 OR status = 3 - `); - - for (const video of unfinishedVideos.rows) { - try { - await deleteVideoProject(video.id); - console.log(`Deleted unfinished video with id: ${video.id}`); - } catch (error) { - console.error(`Error deleting unfinished video with id: ${video.id}`, error); - } - } -} - -// async function cleanVideoFiles() { -// //supprimer les fichiers vidéos qui ne sont pas associés à une vidéo de la base de données -// } - -deleteUnfinishedVideos(); - -async function createVideoProject(projectId, measurementIds, name, resolution, duration) { - // insérer une nouvelle vidéo dans la base de données - const status = 0; // 0 = en cours, 1 = terminé, 2 = erreur, 3 = en cours de création - const query = 'INSERT INTO public.videos (project_id, measurement_ids, name, resolution, duration, status) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id'; - const values = [projectId, measurementIds, name, resolution, duration, status]; - const res = await db.query(query, values); - console.log('New video created with id:', res.rows[0].id); - return res.rows[0].id; -} - -async function deleteVideoProject(videoId) { - const query = 'DELETE FROM public.videos WHERE id = $1'; - const values = [videoId]; - const res = await db.query(query, values); - console.log('Video deleted:', res.rows[0]); - return res.rows[0]; -} - async function createVideoWithList(projectId, pathList, duration, videoId, res_width, res_height) { const tempFile = path.join('temp.txt'); let ffmpegProcess; @@ -83,16 +41,14 @@ async function createVideoWithList(projectId, pathList, duration, videoId, res_w ); // Mise à jour initiale de la base de données - await db.query(` - UPDATE public.videos - SET - status = 3, - progress = 0, - started_at = NOW(), - updated_at = NOW(), - eta = NULL - WHERE id = $1 - `, [videoId]); + let edit_video = { + status: 3, + progress: 0, + started_at: new Date(), + updated_at: new Date(), + eta: null + } + await database_manager.video.edit_video_by_id(videoId, edit_video) const scale = res_width && res_height ? `scale=${res_width}:${res_height}` : 'scale=854:480'; // Redimensionne la vidéo en 480p par défaut // Configuration de FFmpeg @@ -132,14 +88,12 @@ async function createVideoWithList(projectId, pathList, duration, videoId, res_w // Mise à jour max toutes les 500ms if (now - lastUpdate > 500) { - db.query(` - UPDATE public.videos - SET - progress = $1, - eta = $2, - updated_at = NOW() - WHERE id = $3 - `, [progress, Math.round(eta), videoId]).catch(console.error); + let update_video = { + progress: progress, + eta: Math.round(eta), + updated_at: new Date() + } + database_manager.video.edit_video_by_id(videoId, update_video) console.log('Progress:', progress.toFixed(2), '%, ETA:', eta.toFixed(0), 's'); lastUpdate = now; @@ -153,16 +107,24 @@ async function createVideoWithList(projectId, pathList, duration, videoId, res_w if (code === 0) { try { // Mise à jour finale - await db.query(` - UPDATE public.videos - SET - status = 1, - progress = 100, - eta = 0, - video_file = $1, - updated_at = NOW() - WHERE id = $2 - `, [outputVideo, videoId]); + // await db.query(` + // UPDATE public.videos + // SET + // status = 1, + // progress = 100, + // eta = 0, + // video_file = $1, + // updated_at = NOW() + // WHERE id = $2 + // `, [outputVideo, videoId]); + let latest_update = { + status: 1, + progress: 100, + eta: 0, + video_file: outputVideo, + updated_at: new Date() + } + await database_manager.video.edit_video_by_id(videoId, latest_update) resolve(); } catch (e) { reject(e); @@ -182,15 +144,14 @@ async function createVideoWithList(projectId, pathList, duration, videoId, res_w console.error('Error in video creation:', error); try { - await db.query(` - UPDATE public.videos - SET - status = 2, - progress = 0, - eta = 0, - updated_at = NOW() - WHERE id = $1 - `, [videoId]); + // Mise à jour de la base de données en cas d'erreur + let error_video = { + status: 0, + progress: 0, + eta: null, + updated_at: new Date() + } + await database_manager.video.edit_video_by_id(videoId, error_video) } catch (dbError) { console.error('Database update error:', dbError); } @@ -211,60 +172,4 @@ async function createVideoWithList(projectId, pathList, duration, videoId, res_w } } - -async function createVideo(projectId) { - const tempFile = path.join('temp.txt'); - try { - // Trouver tous les fichiers image pour le projet donné - const workdir = path.join(PROJECTS_DIR, 'storage', `${projectId}`); - const dir = path.join(PROJECTS_DIR, 'storage', `${projectId}`, 'images'); - console.log('dir:', dir); - const images = storage_manager.scanAllImages(dir); - console.log('images:', images); - - // Trier les images numériquement - const sortedImages = images.sort((a, b) => { - const numA = parseInt(path.basename(a).match(/\d+/)[0], 10); - const numB = parseInt(path.basename(b).match(/\d+/)[0], 10); - return numA - numB; - }); - - // En déduire l'id de la première et dernière image utilisée - const firstImageId = parseInt(path.basename(sortedImages[0]).match(/\d+/)[0], 10); - const lastImageId = parseInt(path.basename(sortedImages[sortedImages.length - 1]).match(/\d+/)[0], 10); - - console.log('firstImageId:', firstImageId); - console.log('lastImageId:', lastImageId); - - // Créer un fichier temporaire pour la liste des images - fs.writeFileSync(tempFile, sortedImages.map(image => `file '${image}'`).join('\n')); - - const frameRate = 10; - const outputVideo = path.join(workdir, 'video.mp4'); - - // Commande ffmpeg pour créer la vidéo - const ffmpegCommand = `ffmpeg -r ${frameRate} -f concat -safe 0 -i ${tempFile} -vsync vfr -pix_fmt yuv420p ${outputVideo}`; - console.log('Running ffmpeg command:', ffmpegCommand); - spawn(ffmpegCommand); - console.log('Video created successfully:', outputVideo); - } catch (error) { - console.error('Error creating video:', error); - serverError(error); - } finally { - // Supprimer le fichier temporaire - if (fs.existsSync(tempFile)) { - fs.unlinkSync(tempFile); - console.log('Temporary file deleted:', tempFile); - } - } -} - -async function updateVideoFile(videoId, video_file) { - const query = 'UPDATE public.videos SET video_file = $2 WHERE id = $1 RETURNING *'; - const values = [videoId, video_file]; - const res = await db.query(query, values); - console.log('Video updated:', res.rows[0]); - return res.rows[0]; -} - -module.exports = { createVideo, createVideoWithList, createVideoProject, deleteVideoProject, updateVideoFile }; +module.exports = { createVideoWithList }; From 401deb3e69311d7100a6ef56be814eed380f3fc8 Mon Sep 17 00:00:00 2001 From: Kerboul Date: Thu, 3 Apr 2025 11:38:21 +0200 Subject: [PATCH 8/9] =?UTF-8?q?Ajout=20de=20la=20gestion=20de=20l'environn?= =?UTF-8?q?ement=20de=20d=C3=A9veloppement=20avec=20un=20fichier=20devlock?= =?UTF-8?q?,=20mise=20=C3=A0=20jour=20des=20scripts=20de=20d=C3=A9marrage?= =?UTF-8?q?=20et=20cr=C3=A9ation=20d'un=20serveur=20local=20pour=20le=20d?= =?UTF-8?q?=C3=A9veloppement.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db.js | 4 ++- devlock.js | 5 ++++ package.json | 3 ++- server_local.js | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 devlock.js create mode 100644 server_local.js diff --git a/db.js b/db.js index 89adcac..4ecb241 100644 --- a/db.js +++ b/db.js @@ -1,6 +1,8 @@ const { Client } = require('pg'); +const devlock = require('./devlock.js'); -let dev = true; +let dev = devlock.is_dev; +console.log('[INFO] Environment:', dev ? 'Development Local' : 'Development Remote'); let client = new Client({ host: '192.168.192.3', diff --git a/devlock.js b/devlock.js new file mode 100644 index 0000000..e485dac --- /dev/null +++ b/devlock.js @@ -0,0 +1,5 @@ +let is_dev = false; // Set to true for development mode + +module.exports = { + is_dev +} \ No newline at end of file diff --git a/package.json b/package.json index 1886d14..0642cfb 100755 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "main": "index.js", "scripts": { "start": "node server.js", - "dev": "nodemon server.js" + "dev": "nodemon server.js", + "local" : "nodemon server_local.js" }, "keywords": [], "author": "", diff --git a/server_local.js b/server_local.js new file mode 100644 index 0000000..ebbb55c --- /dev/null +++ b/server_local.js @@ -0,0 +1,68 @@ +const devlock = require('./devlock.js'); +devlock.is_dev = true; // Set to true for development mode + +// server.js version locale +const express = require('express'); +const cors = require('cors'); +const app = express(); +const port = 3000; + +// Middleware pour gérer les requêtes JSON +app.use(express.json()); + +// Cors accès à tout +app.use(cors({ + origin: ['http://127.0.0.1:5500', 'http://localhost:5500', 'http://localhost:3000'], + methods: ['GET', 'POST', 'PUT', 'DELETE'], + allowedHeaders: ['Content-Type'], + credentials: true, +})); + +// Importer les routes +const apiRoutes = require('./api'); +app.use('/api', apiRoutes); + +// Swagger dependencies +const swaggerUi = require('swagger-ui-express'); +const swaggerJsdoc = require('swagger-jsdoc'); + +// Configuration de Swagger +const swaggerOptions = { + definition: { + openapi: '3.0.0', + info: { + title: 'API Documentation', + version: '1.0.0', + description: 'Documentation de l\'API avec Swagger', + }, + servers: [ + { + url: 'https://timelapse.kerboul.me/api', + }, + { + url: 'http://localhost:3000/api', + } + ], + }, + apis: ['./routes/*.js'], // Prend en compte tous les fichiers de routes pour générer la documentation +}; + +// Initialisation de swagger-jsdoc +const swaggerDocs = swaggerJsdoc(swaggerOptions); + +// Route Swagger UI +app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs)); + +// Route de base pour tester le serveur +app.get('/', (req, res) => { + res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5500'); + res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); + res.send('Bienvenue sur mon API Node.js!'); +}); + +// Démarrer le serveur +app.listen(port, () => { + console.log(`[SERVER] Serveur démarré sur http://localhost:${port}`); + console.log(`[SERVER] Swagger documentation disponible sur http://localhost:${port}/api-docs`); +}); From f5fda050ed30e54f9dd3d3d5f3c264bd639a5768 Mon Sep 17 00:00:00 2001 From: Kerboul Date: Thu, 3 Apr 2025 11:40:22 +0200 Subject: [PATCH 9/9] =?UTF-8?q?Supprimer=20l'importation=20du=20gestionnai?= =?UTF-8?q?re=20de=20stockage=20dans=20le=20gestionnaire=20de=20vid=C3=A9o?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/video/videoManager.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/video/videoManager.js b/src/video/videoManager.js index deacba1..f3168e7 100644 --- a/src/video/videoManager.js +++ b/src/video/videoManager.js @@ -3,7 +3,6 @@ const path = require('path'); const { spawn } = require('child_process'); const database_manager = require('../database/database_manager'); -const storage_manager = require('../data/storage_manager'); const PROJECTS_DIR = path.join('.');