From 0d1072fea88ba047e23ee5fa0185938341548b10 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Sat, 17 Feb 2024 03:38:24 +0700 Subject: [PATCH 01/42] basic isolated media server setup --- desci-media-isolated/.dockerignore | 6 + desci-media-isolated/.eslintrc.json | 20 + desci-media-isolated/.example.env | 0 desci-media-isolated/.gitignore | 3 + desci-media-isolated/.nvmrc | 1 + desci-media-isolated/.prettierignore | 1 + desci-media-isolated/.prettierrc | 6 + desci-media-isolated/package-lock.json | 2968 +++++++++++++++++++ desci-media-isolated/package.json | 26 + desci-media-isolated/src/config/index.ts | 0 desci-media-isolated/src/index.ts | 12 + desci-media-isolated/src/routes/v1/index.ts | 0 desci-media-isolated/tsconfig.json | 109 + 13 files changed, 3152 insertions(+) create mode 100755 desci-media-isolated/.dockerignore create mode 100644 desci-media-isolated/.eslintrc.json create mode 100644 desci-media-isolated/.example.env create mode 100644 desci-media-isolated/.gitignore create mode 100644 desci-media-isolated/.nvmrc create mode 100755 desci-media-isolated/.prettierignore create mode 100755 desci-media-isolated/.prettierrc create mode 100644 desci-media-isolated/package-lock.json create mode 100644 desci-media-isolated/package.json create mode 100644 desci-media-isolated/src/config/index.ts create mode 100644 desci-media-isolated/src/index.ts create mode 100644 desci-media-isolated/src/routes/v1/index.ts create mode 100644 desci-media-isolated/tsconfig.json diff --git a/desci-media-isolated/.dockerignore b/desci-media-isolated/.dockerignore new file mode 100755 index 000000000..5e3971c4b --- /dev/null +++ b/desci-media-isolated/.dockerignore @@ -0,0 +1,6 @@ +config +database +dist +node_modules +.git +.env \ No newline at end of file diff --git a/desci-media-isolated/.eslintrc.json b/desci-media-isolated/.eslintrc.json new file mode 100644 index 000000000..6751428a9 --- /dev/null +++ b/desci-media-isolated/.eslintrc.json @@ -0,0 +1,20 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + } +} diff --git a/desci-media-isolated/.example.env b/desci-media-isolated/.example.env new file mode 100644 index 000000000..e69de29bb diff --git a/desci-media-isolated/.gitignore b/desci-media-isolated/.gitignore new file mode 100644 index 000000000..bdfa6b815 --- /dev/null +++ b/desci-media-isolated/.gitignore @@ -0,0 +1,3 @@ +.env +node_modules +dist \ No newline at end of file diff --git a/desci-media-isolated/.nvmrc b/desci-media-isolated/.nvmrc new file mode 100644 index 000000000..43bff1f8c --- /dev/null +++ b/desci-media-isolated/.nvmrc @@ -0,0 +1 @@ +20.9.0 \ No newline at end of file diff --git a/desci-media-isolated/.prettierignore b/desci-media-isolated/.prettierignore new file mode 100755 index 000000000..53c37a166 --- /dev/null +++ b/desci-media-isolated/.prettierignore @@ -0,0 +1 @@ +dist \ No newline at end of file diff --git a/desci-media-isolated/.prettierrc b/desci-media-isolated/.prettierrc new file mode 100755 index 000000000..6599520f8 --- /dev/null +++ b/desci-media-isolated/.prettierrc @@ -0,0 +1,6 @@ +{ + "printWidth": 120, + "semi": true, + "singleQuote": true, + "trailingComma": "all" +} diff --git a/desci-media-isolated/package-lock.json b/desci-media-isolated/package-lock.json new file mode 100644 index 000000000..c86eecbc4 --- /dev/null +++ b/desci-media-isolated/package-lock.json @@ -0,0 +1,2968 @@ +{ + "name": "desci-media-isolated", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "desci-media-isolated", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^4.18.2", + "filepreview-soffice": "^1.1.1" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@typescript-eslint/eslint-plugin": "^7.0.1", + "@typescript-eslint/parser": "^7.0.1", + "eslint": "^8.56.0", + "ts-node-dev": "^2.0.0", + "typescript": "^5.3.3" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.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": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@eslint/eslintrc/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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "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, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.43", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", + "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", + "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/qs": { + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz", + "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", + "dev": true + }, + "node_modules/@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.1.tgz", + "integrity": "sha512-OLvgeBv3vXlnnJGIAgCLYKjgMEU+wBGj07MQ/nxAaON+3mLzX7mJbhRYrVGiVvFiXtwFlkcBa/TtmglHy0UbzQ==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "7.0.1", + "@typescript-eslint/type-utils": "7.0.1", + "@typescript-eslint/utils": "7.0.1", + "@typescript-eslint/visitor-keys": "7.0.1", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.1.tgz", + "integrity": "sha512-8GcRRZNzaHxKzBPU3tKtFNing571/GwPBeCvmAUw0yBtfE2XVd0zFKJIMSWkHJcPQi0ekxjIts6L/rrZq5cxGQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.0.1", + "@typescript-eslint/types": "7.0.1", + "@typescript-eslint/typescript-estree": "7.0.1", + "@typescript-eslint/visitor-keys": "7.0.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.1.tgz", + "integrity": "sha512-v7/T7As10g3bcWOOPAcbnMDuvctHzCFYCG/8R4bK4iYzdFqsZTbXGln0cZNVcwQcwewsYU2BJLay8j0/4zOk4w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.0.1", + "@typescript-eslint/visitor-keys": "7.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.1.tgz", + "integrity": "sha512-YtT9UcstTG5Yqy4xtLiClm1ZpM/pWVGFnkAa90UfdkkZsR1eP2mR/1jbHeYp8Ay1l1JHPyGvoUYR6o3On5Nhmw==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.0.1", + "@typescript-eslint/utils": "7.0.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@typescript-eslint/types": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.1.tgz", + "integrity": "sha512-uJDfmirz4FHib6ENju/7cz9SdMSkeVvJDK3VcMFvf/hAShg8C74FW+06MaQPODHfDJp/z/zHfgawIJRjlu0RLg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.1.tgz", + "integrity": "sha512-SO9wHb6ph0/FN5OJxH4MiPscGah5wjOd0RRpaLvuBv9g8565Fgu0uMySFEPqwPHiQU90yzJ2FjRYKGrAhS1xig==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.0.1", + "@typescript-eslint/visitor-keys": "7.0.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.1.tgz", + "integrity": "sha512-oe4his30JgPbnv+9Vef1h48jm0S6ft4mNwi9wj7bX10joGn07QRfqIqFHoMiajrtoU88cIhXf8ahwgrcbNLgPA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.0.1", + "@typescript-eslint/types": "7.0.1", + "@typescript-eslint/typescript-estree": "7.0.1", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.1.tgz", + "integrity": "sha512-hwAgrOyk++RTXrP4KzCg7zB2U0xt7RUU0ZdMSCsqF3eKUwkdXUMyTb0qdCuji7VIbcpG62kKTU9M1J1c9UpFBw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.0.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "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, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.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, + "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-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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, + "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", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "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, + "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/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "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", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "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 + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", + "dev": true, + "dependencies": { + "xtend": "^4.0.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "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, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "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, + "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, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "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 + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "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 + }, + "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 + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filepreview-soffice": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/filepreview-soffice/-/filepreview-soffice-1.1.1.tgz", + "integrity": "sha512-DbmjeHsAgRYpgor7lwracD6NHyHJIBVTd4mk6xtc0+SIIqSG6JC2orLLvP9fSz7BIvLRgYvdCH6mtMW5y1u0HQ==" + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "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": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "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", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "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, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "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 + }, + "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 + }, + "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 + }, + "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, + "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, + "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, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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 + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "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 + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "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, + "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, + "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, + "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", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "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, + "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", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dependencies": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "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, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-api-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", + "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node-dev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", + "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.1", + "dynamic-dedupe": "^0.3.0", + "minimist": "^1.2.6", + "mkdirp": "^1.0.4", + "resolve": "^1.0.0", + "rimraf": "^2.6.1", + "source-map-support": "^0.5.12", + "tree-kill": "^1.2.2", + "ts-node": "^10.4.0", + "tsconfig": "^7.0.0" + }, + "bin": { + "ts-node-dev": "lib/bin.js", + "tsnd": "lib/bin.js" + }, + "engines": { + "node": ">=0.8.0" + }, + "peerDependencies": { + "node-notifier": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "dependencies": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + } + }, + "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, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/desci-media-isolated/package.json b/desci-media-isolated/package.json new file mode 100644 index 000000000..7883dab31 --- /dev/null +++ b/desci-media-isolated/package.json @@ -0,0 +1,26 @@ +{ + "name": "desci-media-isolated", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node dist/index.js", + "dev": "ts-node-dev --respawn --transpile-only src/index.ts", + "build": "tsc" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.18.2", + "filepreview-soffice": "^1.1.1" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@typescript-eslint/eslint-plugin": "^7.0.1", + "@typescript-eslint/parser": "^7.0.1", + "eslint": "^8.56.0", + "ts-node-dev": "^2.0.0", + "typescript": "^5.3.3" + } +} diff --git a/desci-media-isolated/src/config/index.ts b/desci-media-isolated/src/config/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/desci-media-isolated/src/index.ts b/desci-media-isolated/src/index.ts new file mode 100644 index 000000000..9e9f09de7 --- /dev/null +++ b/desci-media-isolated/src/index.ts @@ -0,0 +1,12 @@ +import express from 'express'; + +const app = express(); +const PORT = process.env.PORT || 7777; + +app.get('/health', (req, res) => { + return res.send('healthy'); +}); + +app.listen(PORT, () => { + console.log(`[Media Server Isolated]Server is listening on port: ${PORT}`); +}); diff --git a/desci-media-isolated/src/routes/v1/index.ts b/desci-media-isolated/src/routes/v1/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/desci-media-isolated/tsconfig.json b/desci-media-isolated/tsconfig.json new file mode 100644 index 000000000..e075f973c --- /dev/null +++ b/desci-media-isolated/tsconfig.json @@ -0,0 +1,109 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} From d0767132309d731e0d5b51d2c6fd07817b420113 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Tue, 20 Feb 2024 04:05:03 +0700 Subject: [PATCH 02/42] config/setup, add Dockerfile --- desci-media-isolated/.eslintrc.json | 33 ++++----- desci-media-isolated/.example.env | 1 + desci-media-isolated/.gitignore | 4 +- desci-media-isolated/Dockerfile | 65 +++++++++++++++++ desci-media-isolated/package-lock.json | 91 ++++++++++++++++++++++-- desci-media-isolated/package.json | 4 +- desci-media-isolated/src/config/index.ts | 4 ++ desci-media-isolated/tsconfig.json | 13 ++-- 8 files changed, 184 insertions(+), 31 deletions(-) create mode 100644 desci-media-isolated/Dockerfile diff --git a/desci-media-isolated/.eslintrc.json b/desci-media-isolated/.eslintrc.json index 6751428a9..e9101e7fc 100644 --- a/desci-media-isolated/.eslintrc.json +++ b/desci-media-isolated/.eslintrc.json @@ -1,20 +1,17 @@ { - "env": { - "browser": true, - "es2021": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint" - ], - "rules": { - } + "env": { + "browser": true, + "es2021": true + }, + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "rules": { + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/no-unused-vars": 0 + } } diff --git a/desci-media-isolated/.example.env b/desci-media-isolated/.example.env index e69de29bb..621fc17e8 100644 --- a/desci-media-isolated/.example.env +++ b/desci-media-isolated/.example.env @@ -0,0 +1 @@ +IPFS_GATEWAY=https://ipfs.desci.com/ipfs \ No newline at end of file diff --git a/desci-media-isolated/.gitignore b/desci-media-isolated/.gitignore index bdfa6b815..ec3b56bda 100644 --- a/desci-media-isolated/.gitignore +++ b/desci-media-isolated/.gitignore @@ -1,3 +1,5 @@ .env node_modules -dist \ No newline at end of file +dist +.npm +.temp \ No newline at end of file diff --git a/desci-media-isolated/Dockerfile b/desci-media-isolated/Dockerfile new file mode 100644 index 000000000..5a46e9c03 --- /dev/null +++ b/desci-media-isolated/Dockerfile @@ -0,0 +1,65 @@ + +FROM docker.io/node:20.9.0-alpine as base + +# Install dumb-init so we can use it as PID 1 +RUN apk update && apk add --no-cache dumb-init + +# No longer using libre office file preview remove soon +# ARG APP_ROOT=/opt/app-root/src +# ENV NO_UPDATE_NOTIFIER=true \ +# PATH="/usr/lib/libreoffice/program:${PATH}" \ +# PYTHONUNBUFFERED=1 +# WORKDIR ${APP_ROOT} + +# # Install LibreOffice & Common Fonts +# RUN apk --no-cache add bash libreoffice util-linux \ +# font-droid-nonlatin font-droid ttf-dejavu ttf-freefont ttf-liberation && \ +# rm -rf /var/cache/apk/* + +# # Install Microsoft Core Fonts +# RUN apk --no-cache add msttcorefonts-installer fontconfig && \ +# update-ms-fonts && \ +# fc-cache -f && \ +# rm -rf /var/cache/apk/* + +# # Fix Python/LibreOffice Integration +# COPY container/libreOffice ${APP_ROOT}/container/libreOffice +# RUN chmod a+rx ${APP_ROOT}/container/libreOffice/bindPython.sh \ +# && ${APP_ROOT}/container/libreOffice/bindPython.sh + +# NPM Permission Fix +RUN mkdir -p /.npm +RUN chown -R 1001:0 /.npm + +# App Setup +WORKDIR /usr/src/app + +COPY package*.json ./ + +FROM base as dev + +RUN --mount=type=cache,target=/usr/src/app/.npm \ + npm set cache /usr/src/app/.npm && \ + npm install + +COPY . . + +CMD ["dumb-init", "npx", "ts-node-dev", "--respawn", "--transpile-only", "src/index.ts"] + +FROM base as production + +ENV NODE_ENV production + +# Cache mounts for faster builds, prod env for better express perf +RUN --mount=type=cache,target=/usr/src/app/.npm \ + npm set cache /usr/src/app/.npm && \ + npm ci --only=production + + +# 'node' user is created by the node image, prevent perm issues, run with reduced privs +USER node +COPY --chown=node:node ./src/ . +RUN npm run build + + +CMD ["dumb-init", "node", "dist/index.js"] diff --git a/desci-media-isolated/package-lock.json b/desci-media-isolated/package-lock.json index c86eecbc4..77e762c38 100644 --- a/desci-media-isolated/package-lock.json +++ b/desci-media-isolated/package-lock.json @@ -9,8 +9,10 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "axios": "^1.6.7", "express": "^4.18.2", - "filepreview-soffice": "^1.1.1" + "filepreview_ts": "^1.0.0", + "helmet": "^7.1.0" }, "devDependencies": { "@types/express": "^4.17.21", @@ -827,6 +829,21 @@ "node": ">=8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "dependencies": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -986,6 +1003,17 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1074,6 +1102,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -1463,10 +1499,10 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/filepreview-soffice": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/filepreview-soffice/-/filepreview-soffice-1.1.1.tgz", - "integrity": "sha512-DbmjeHsAgRYpgor7lwracD6NHyHJIBVTd4mk6xtc0+SIIqSG6JC2orLLvP9fSz7BIvLRgYvdCH6mtMW5y1u0HQ==" + "node_modules/filepreview_ts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/filepreview_ts/-/filepreview_ts-1.0.0.tgz", + "integrity": "sha512-BhdiTLhW0Uzg672Zg9HhcD/YvLWJMNSKzzi2zsQAaA6SqVZfH+pefUaRdXKW5aVeZetUQHw0mkHm2LBfvTFf/g==" }, "node_modules/fill-range": { "version": "7.0.1", @@ -1548,6 +1584,38 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1747,6 +1815,14 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", + "integrity": "sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -2299,6 +2375,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/desci-media-isolated/package.json b/desci-media-isolated/package.json index 7883dab31..6dd984c5e 100644 --- a/desci-media-isolated/package.json +++ b/desci-media-isolated/package.json @@ -12,8 +12,10 @@ "author": "", "license": "ISC", "dependencies": { + "axios": "^1.6.7", "express": "^4.18.2", - "filepreview-soffice": "^1.1.1" + "filepreview_ts": "^1.0.0", + "helmet": "^7.1.0" }, "devDependencies": { "@types/express": "^4.17.21", diff --git a/desci-media-isolated/src/config/index.ts b/desci-media-isolated/src/config/index.ts index e69de29bb..94ff1ff43 100644 --- a/desci-media-isolated/src/config/index.ts +++ b/desci-media-isolated/src/config/index.ts @@ -0,0 +1,4 @@ +export const IPFS_GATEWAY = process.env.IPFS_GATEWAY; +export const TEMP_DIR = '.temp'; +export const THUMBNAIL_FILES_DIR = `${TEMP_DIR}/files`; +export const THUMBNAIL_OUTPUT_DIR = `${TEMP_DIR}/thumbnails`; diff --git a/desci-media-isolated/tsconfig.json b/desci-media-isolated/tsconfig.json index e075f973c..1fbd165a5 100644 --- a/desci-media-isolated/tsconfig.json +++ b/desci-media-isolated/tsconfig.json @@ -11,7 +11,8 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "outDir": "./dist", + "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ @@ -25,7 +26,7 @@ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ + "module": "commonjs" /* Specify what module code is generated. */, // "rootDir": "./", /* Specify the root folder within your source files. */ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ @@ -77,12 +78,12 @@ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true /* Enable all strict type-checking options. */, // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ @@ -104,6 +105,6 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ } } From 99b9dd6570c0a62b52c09eb669bf37f430c73653 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Tue, 20 Feb 2024 04:05:33 +0700 Subject: [PATCH 03/42] create thumbnail service/routes, create ipfs service, add err handling --- .../src/controllers/thumbnails/create.ts | 41 ++++++++++++++++ desci-media-isolated/src/index.ts | 13 ++++- .../src/middleware/errorHandler.ts | 16 ++++++ desci-media-isolated/src/routes/index.ts | 8 +++ desci-media-isolated/src/routes/v1/index.ts | 8 +++ .../src/routes/v1/thumbnails.ts | 8 +++ desci-media-isolated/src/services/ipfs.ts | 30 ++++++++++++ .../src/services/thumbnails.ts | 46 +++++++++++++++++ .../src/utils/customErrors.ts | 49 +++++++++++++++++++ 9 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 desci-media-isolated/src/controllers/thumbnails/create.ts create mode 100644 desci-media-isolated/src/middleware/errorHandler.ts create mode 100644 desci-media-isolated/src/routes/index.ts create mode 100644 desci-media-isolated/src/routes/v1/thumbnails.ts create mode 100644 desci-media-isolated/src/services/ipfs.ts create mode 100644 desci-media-isolated/src/services/thumbnails.ts create mode 100644 desci-media-isolated/src/utils/customErrors.ts diff --git a/desci-media-isolated/src/controllers/thumbnails/create.ts b/desci-media-isolated/src/controllers/thumbnails/create.ts new file mode 100644 index 000000000..ab9ecf2f2 --- /dev/null +++ b/desci-media-isolated/src/controllers/thumbnails/create.ts @@ -0,0 +1,41 @@ +import { Request, Response } from 'express'; +import { ThumbnailsService } from '../../services/thumbnails'; +import path from 'path'; +import fs from 'fs'; +import { TEMP_DIR } from '../../config'; +import { BadRequestError, NotFoundError } from '../../utils/customErrors'; + +export type GenerateThumbnailRequestBody = { + cid: string; + fileName: string; +}; + +const BASE_TEMP_DIR = path.resolve(__dirname, '../../..', TEMP_DIR); + +export const generateThumbnail = async (req: Request<any, any, GenerateThumbnailRequestBody>, res: Response) => { + const { cid, fileName } = req.body; + if (!cid) throw new BadRequestError('Missing cid in request body'); + if (!fileName) throw new BadRequestError('Missing fileName in request body'); + + try { + const thumbnailPath = await ThumbnailsService.generateThumbnail(cid); + const fullThumbnailPath = path.join(BASE_TEMP_DIR, thumbnailPath); + + // Check if the file exists before attempting to stream it + fs.access(fullThumbnailPath, fs.constants.F_OK, (err) => { + if (err) { + throw new NotFoundError(`Thumbnail not found for file with cid: ${cid}`); + } + + res.setHeader('Content-Type', 'image/png'); + const readStream = fs.createReadStream(fullThumbnailPath); + + readStream.pipe(res); + }); + + // Send the thumbnail as a response + res.status(200); + } catch (err: any) { + res.status(500).json({ message: err.message }); + } +}; diff --git a/desci-media-isolated/src/index.ts b/desci-media-isolated/src/index.ts index 9e9f09de7..c68b0d7e1 100644 --- a/desci-media-isolated/src/index.ts +++ b/desci-media-isolated/src/index.ts @@ -1,12 +1,23 @@ import express from 'express'; +import helmet from 'helmet'; +import { errorHandler } from './middleware/errorHandler'; +import routes from './routes'; const app = express(); -const PORT = process.env.PORT || 7777; +const PORT = process.env.PORT || 7771; + +app.use(helmet()); +app.use(express.json()); app.get('/health', (req, res) => { return res.send('healthy'); }); +app.use('/', routes); + +// Keep after all routes +app.use(errorHandler); + app.listen(PORT, () => { console.log(`[Media Server Isolated]Server is listening on port: ${PORT}`); }); diff --git a/desci-media-isolated/src/middleware/errorHandler.ts b/desci-media-isolated/src/middleware/errorHandler.ts new file mode 100644 index 000000000..91135f88a --- /dev/null +++ b/desci-media-isolated/src/middleware/errorHandler.ts @@ -0,0 +1,16 @@ +import { Request, Response, NextFunction } from 'express'; +import { BaseError } from '../utils/customErrors'; + +export const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => { + console.error(err.stack); + + let statusCode = 500; + if (err instanceof BaseError && typeof err.statusCode === 'number') { + statusCode = err.statusCode; + } + + res.status(statusCode).json({ + message: err.message || 'Something went wrong', + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined, + }); +}; diff --git a/desci-media-isolated/src/routes/index.ts b/desci-media-isolated/src/routes/index.ts new file mode 100644 index 000000000..d7c2b6204 --- /dev/null +++ b/desci-media-isolated/src/routes/index.ts @@ -0,0 +1,8 @@ +import { Router } from 'express'; +import v1 from './v1/index'; + +const router = Router(); + +router.use(`/v1`, v1); + +export default router; diff --git a/desci-media-isolated/src/routes/v1/index.ts b/desci-media-isolated/src/routes/v1/index.ts index e69de29bb..e631244d4 100644 --- a/desci-media-isolated/src/routes/v1/index.ts +++ b/desci-media-isolated/src/routes/v1/index.ts @@ -0,0 +1,8 @@ +import { Router } from 'express'; +import thumbnails from './thumbnails'; + +const router = Router(); + +router.use('/thumbnails', thumbnails); + +export default router; diff --git a/desci-media-isolated/src/routes/v1/thumbnails.ts b/desci-media-isolated/src/routes/v1/thumbnails.ts new file mode 100644 index 000000000..df4a339d1 --- /dev/null +++ b/desci-media-isolated/src/routes/v1/thumbnails.ts @@ -0,0 +1,8 @@ +import { Router } from 'express'; +import { generateThumbnail } from '../../controllers/thumbnails/create'; + +const router = Router(); + +router.post('/', generateThumbnail); + +export default router; diff --git a/desci-media-isolated/src/services/ipfs.ts b/desci-media-isolated/src/services/ipfs.ts new file mode 100644 index 000000000..7e6abd92f --- /dev/null +++ b/desci-media-isolated/src/services/ipfs.ts @@ -0,0 +1,30 @@ +import axios from 'axios'; +import fs from 'fs'; +import { pipeline } from 'stream/promises'; +import { IPFS_GATEWAY } from '../config'; +import { IpfsConfigurationError } from '../utils/customErrors'; + +export class IpfsService { + static async saveFile(cid: string, outputPath: string) { + if (!IPFS_GATEWAY) { + throw new IpfsConfigurationError('process.env.IPFS_GATEWAY is not defined in environment variables'); + } + + const url = `${IPFS_GATEWAY}/${cid}`; + + try { + const response = await axios({ + method: 'get', + url: url, + responseType: 'stream', + }); + + await pipeline(response.data, fs.createWriteStream(outputPath)); + + console.log(`File downloaded and saved to ${outputPath}`); + } catch (error) { + console.error('Error downloading or saving the file:', error); + throw error; + } + } +} diff --git a/desci-media-isolated/src/services/thumbnails.ts b/desci-media-isolated/src/services/thumbnails.ts new file mode 100644 index 000000000..95e49770c --- /dev/null +++ b/desci-media-isolated/src/services/thumbnails.ts @@ -0,0 +1,46 @@ +import { generateAsync } from 'filepreview_ts'; +import { TEMP_DIR, THUMBNAIL_FILES_DIR, THUMBNAIL_OUTPUT_DIR } from '../config'; +import { IpfsService } from './ipfs'; +import { UnhandledError } from '../utils/customErrors'; +import path from 'path'; +import fs from 'fs'; + +const THUMBNAIL_DIMENSIONS = { + width: 220, + height: 300, +}; +const BASE_TEMP_DIR = path.resolve(__dirname, '..', TEMP_DIR); + +export class ThumbnailsService { + static async generateThumbnail(cid: string) { + const tempFilePath = path.join(BASE_TEMP_DIR, THUMBNAIL_FILES_DIR, `${cid}`); + const thumbnailPath = this.getThumbnailPath(cid); + const exportPath = path.join(BASE_TEMP_DIR, THUMBNAIL_OUTPUT_DIR, thumbnailPath); + + await IpfsService.saveFile(cid, tempFilePath); + try { + await generateAsync(tempFilePath, exportPath, THUMBNAIL_DIMENSIONS); + return thumbnailPath; + } catch (e) { + console.error(e); + throw new UnhandledError(`Failed generating thumbnail for file with cid: ${cid}`); + } finally { + // The initially saved file is removed, however the thumbnail remains. Further cleanup can be done for the thumbnail. + try { + await fs.unlink(tempFilePath, (err) => { + if (err) { + console.error(err, `Failed to cleanup temporary file: ${tempFilePath}`); + return; + } + console.log(`Temporary file ${tempFilePath} deleted successfully.`); + }); + } catch (cleanupError) { + console.error(`Failed to delete temporary file ${tempFilePath}:`, cleanupError); + } + } + } + + static getThumbnailPath(cid: string) { + return `${THUMBNAIL_DIMENSIONS.width}x${THUMBNAIL_DIMENSIONS.height}_${cid}`; + } +} diff --git a/desci-media-isolated/src/utils/customErrors.ts b/desci-media-isolated/src/utils/customErrors.ts new file mode 100644 index 000000000..ec577effe --- /dev/null +++ b/desci-media-isolated/src/utils/customErrors.ts @@ -0,0 +1,49 @@ +import crypto from 'crypto'; + +export class BaseError extends Error { + statusCode: number; + + constructor(message: string, statusCode: number) { + super(message); + this.statusCode = statusCode; + + // Restores the prototype chain + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export class NotFoundError extends BaseError { + constructor(message = 'Resource not found') { + super(message, 404); + } +} + +export class BadRequestError extends BaseError { + constructor(message = 'Bad request') { + super(message, 400); + } +} + +export class UnhandledError extends BaseError { + constructor(message = `Unhandled error occured, error reference: ${crypto.randomUUID()}`) { + super(message, 500); + } +} + +export class InternalServerError extends BaseError { + constructor(message = 'Internal server error') { + super(message, 500); + } +} + +export class IpfsConfigurationError extends BaseError { + constructor(message = 'IPFS Misconfigured') { + super(message, 500); + } +} + +export class IpfsFetchError extends BaseError { + constructor(message = 'Failed to retrieve file from IPFS') { + super(message, 502); + } +} From ca483826ddc2395e58219b242ef444931d27741c Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Tue, 20 Feb 2024 23:41:20 +0700 Subject: [PATCH 04/42] switch to a debian based image for package support --- desci-media-isolated/Dockerfile | 40 +++++++++------------------------ 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/desci-media-isolated/Dockerfile b/desci-media-isolated/Dockerfile index 5a46e9c03..1704d2786 100644 --- a/desci-media-isolated/Dockerfile +++ b/desci-media-isolated/Dockerfile @@ -1,31 +1,12 @@ -FROM docker.io/node:20.9.0-alpine as base +FROM docker.io/node:20.9.0 as base # Install dumb-init so we can use it as PID 1 -RUN apk update && apk add --no-cache dumb-init - -# No longer using libre office file preview remove soon -# ARG APP_ROOT=/opt/app-root/src -# ENV NO_UPDATE_NOTIFIER=true \ -# PATH="/usr/lib/libreoffice/program:${PATH}" \ -# PYTHONUNBUFFERED=1 -# WORKDIR ${APP_ROOT} - -# # Install LibreOffice & Common Fonts -# RUN apk --no-cache add bash libreoffice util-linux \ -# font-droid-nonlatin font-droid ttf-dejavu ttf-freefont ttf-liberation && \ -# rm -rf /var/cache/apk/* - -# # Install Microsoft Core Fonts -# RUN apk --no-cache add msttcorefonts-installer fontconfig && \ -# update-ms-fonts && \ -# fc-cache -f && \ -# rm -rf /var/cache/apk/* - -# # Fix Python/LibreOffice Integration -# COPY container/libreOffice ${APP_ROOT}/container/libreOffice -# RUN chmod a+rx ${APP_ROOT}/container/libreOffice/bindPython.sh \ -# && ${APP_ROOT}/container/libreOffice/bindPython.sh +# RUN apk update && apk add --no-cache dumb-init +# RUN apk add --no-cache unoconv ffmpeg imagemagick curl + +RUN apt-get update && apt-get install -y dumb-init unoconv ffmpeg imagemagick curl && \ + rm -rf /var/lib/apt/lists/* # NPM Permission Fix RUN mkdir -p /.npm @@ -44,13 +25,14 @@ RUN --mount=type=cache,target=/usr/src/app/.npm \ COPY . . -CMD ["dumb-init", "npx", "ts-node-dev", "--respawn", "--transpile-only", "src/index.ts"] - -FROM base as production +# Expose debugger port +EXPOSE 9277 -ENV NODE_ENV production +CMD ["dumb-init", "npx", "tsx","watch", "--inspect=0.0.0.0:9277", "src/index.ts"] +FROM base as production # Cache mounts for faster builds, prod env for better express perf +ENV NODE_ENV production RUN --mount=type=cache,target=/usr/src/app/.npm \ npm set cache /usr/src/app/.npm && \ npm ci --only=production From a3965649e562aa2d33c5371b8ffa751d8b8dd1ed Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Tue, 20 Feb 2024 23:42:40 +0700 Subject: [PATCH 05/42] configure support for es modules, add debugging --- .vscode/launch.json | 16 + desci-media-isolated/.dockerignore | 2 +- desci-media-isolated/package-lock.json | 435 ++++++++++++++++++++++++- desci-media-isolated/package.json | 7 +- desci-media-isolated/tsconfig.json | 18 +- 5 files changed, 466 insertions(+), 12 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 069982987..e0d58f439 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -43,6 +43,22 @@ "address": "localhost", "localRoot": "${workspaceFolder}", "remoteRoot": "/app" + }, + { + "name": "media-isolated tsx", + "type": "node", + "request": "attach", + "restart": true, + "localRoot": "${workspaceFolder}/desci-media-isolated", + "remoteRoot": "/usr/src/app", + "port": 9277, + "skipFiles": [ + // Node.js internal core modules + "<node_internals>/**", + + // Ignore all dependencies (optional) + "${workspaceFolder}/node_modules/**" + ] } ] } diff --git a/desci-media-isolated/.dockerignore b/desci-media-isolated/.dockerignore index 5e3971c4b..0af6aafe9 100755 --- a/desci-media-isolated/.dockerignore +++ b/desci-media-isolated/.dockerignore @@ -3,4 +3,4 @@ database dist node_modules .git -.env \ No newline at end of file +# .env \ No newline at end of file diff --git a/desci-media-isolated/package-lock.json b/desci-media-isolated/package-lock.json index 77e762c38..32d011c98 100644 --- a/desci-media-isolated/package-lock.json +++ b/desci-media-isolated/package-lock.json @@ -10,9 +10,11 @@ "license": "ISC", "dependencies": { "axios": "^1.6.7", + "dotenv": "^16.4.5", "express": "^4.18.2", "filepreview_ts": "^1.0.0", - "helmet": "^7.1.0" + "helmet": "^7.1.0", + "tsx": "^4.7.1" }, "devDependencies": { "@types/express": "^4.17.21", @@ -44,6 +46,351 @@ "node": ">=12" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1160,6 +1507,17 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dynamic-dedupe": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", @@ -1201,6 +1559,43 @@ "node": ">= 0.4" } }, + "node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1642,7 +2037,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -1678,6 +2072,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-tsconfig": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", + "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2483,6 +2888,14 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -2894,6 +3307,24 @@ "strip-json-comments": "^2.0.0" } }, + "node_modules/tsx": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.1.tgz", + "integrity": "sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==", + "dependencies": { + "esbuild": "~0.19.10", + "get-tsconfig": "^4.7.2" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/desci-media-isolated/package.json b/desci-media-isolated/package.json index 6dd984c5e..8104c1fc7 100644 --- a/desci-media-isolated/package.json +++ b/desci-media-isolated/package.json @@ -3,9 +3,10 @@ "version": "1.0.0", "description": "", "main": "index.js", + "type": "module", "scripts": { "start": "node dist/index.js", - "dev": "ts-node-dev --respawn --transpile-only src/index.ts", + "dev": "tsx watch src/index.ts", "build": "tsc" }, "keywords": [], @@ -13,9 +14,11 @@ "license": "ISC", "dependencies": { "axios": "^1.6.7", + "dotenv": "^16.4.5", "express": "^4.18.2", "filepreview_ts": "^1.0.0", - "helmet": "^7.1.0" + "helmet": "^7.1.0", + "tsx": "^4.7.1" }, "devDependencies": { "@types/express": "^4.17.21", diff --git a/desci-media-isolated/tsconfig.json b/desci-media-isolated/tsconfig.json index 1fbd165a5..b663cb032 100644 --- a/desci-media-isolated/tsconfig.json +++ b/desci-media-isolated/tsconfig.json @@ -12,7 +12,7 @@ /* Language and Environment */ "outDir": "./dist", - "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + "target": "ES2020" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ @@ -26,7 +26,7 @@ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - "module": "commonjs" /* Specify what module code is generated. */, + "module": "NodeNext" /* Specify what module code is generated. */, // "rootDir": "./", /* Specify the root folder within your source files. */ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ @@ -53,8 +53,8 @@ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ // "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "sourceMap": true /* Create source map files for emitted JavaScript files. */, + "inlineSourceMap": true /* Include sourcemap files inside the emitted JavaScript. */, // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ // "outDir": "./", /* Specify an output folder for all emitted files. */ // "removeComments": true, /* Disable emitting comments. */ @@ -64,7 +64,7 @@ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + "inlineSources": true /* Include source code in the sourcemaps inside the emitted JavaScript. */, // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ // "newLine": "crlf", /* Set the newline character for emitting files. */ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ @@ -76,9 +76,9 @@ /* Interop Constraints */ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + "verbatimModuleSyntax": true /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */, // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + // "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, @@ -106,5 +106,9 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "ts-node": { + // Tell ts-node CLI to install the --loader automatically, explained below + "esm": true } } From 8c2b8a7fd89e75935c211612a1a3ae6ab323049b Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Tue, 20 Feb 2024 23:43:41 +0700 Subject: [PATCH 06/42] get thumbnail generation working --- desci-media-isolated/src/config/index.ts | 4 ++-- .../src/controllers/thumbnails/create.ts | 20 ++++++++++-------- desci-media-isolated/src/index.ts | 6 ++++-- .../src/middleware/errorHandler.ts | 6 +++--- desci-media-isolated/src/routes/index.ts | 2 +- desci-media-isolated/src/routes/v1/index.ts | 2 +- .../src/routes/v1/thumbnails.ts | 2 +- desci-media-isolated/src/services/ipfs.ts | 4 ++-- .../src/services/thumbnails.ts | 21 ++++++++++++------- 9 files changed, 39 insertions(+), 28 deletions(-) diff --git a/desci-media-isolated/src/config/index.ts b/desci-media-isolated/src/config/index.ts index 94ff1ff43..46ac0834c 100644 --- a/desci-media-isolated/src/config/index.ts +++ b/desci-media-isolated/src/config/index.ts @@ -1,4 +1,4 @@ export const IPFS_GATEWAY = process.env.IPFS_GATEWAY; export const TEMP_DIR = '.temp'; -export const THUMBNAIL_FILES_DIR = `${TEMP_DIR}/files`; -export const THUMBNAIL_OUTPUT_DIR = `${TEMP_DIR}/thumbnails`; +export const THUMBNAIL_FILES_DIR = `/files`; +export const THUMBNAIL_OUTPUT_DIR = `/thumbnails`; diff --git a/desci-media-isolated/src/controllers/thumbnails/create.ts b/desci-media-isolated/src/controllers/thumbnails/create.ts index ab9ecf2f2..0e23350b1 100644 --- a/desci-media-isolated/src/controllers/thumbnails/create.ts +++ b/desci-media-isolated/src/controllers/thumbnails/create.ts @@ -1,15 +1,18 @@ -import { Request, Response } from 'express'; -import { ThumbnailsService } from '../../services/thumbnails'; +import type { Request, Response } from 'express'; +import { ThumbnailsService } from '../../services/thumbnails.js'; import path from 'path'; +import { fileURLToPath } from 'url'; import fs from 'fs'; -import { TEMP_DIR } from '../../config'; -import { BadRequestError, NotFoundError } from '../../utils/customErrors'; +import { TEMP_DIR, THUMBNAIL_OUTPUT_DIR } from '../../config/index.js'; +import { BadRequestError, NotFoundError } from '../../utils/customErrors.js'; export type GenerateThumbnailRequestBody = { cid: string; fileName: string; }; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); const BASE_TEMP_DIR = path.resolve(__dirname, '../../..', TEMP_DIR); export const generateThumbnail = async (req: Request<any, any, GenerateThumbnailRequestBody>, res: Response) => { @@ -18,8 +21,8 @@ export const generateThumbnail = async (req: Request<any, any, GenerateThumbnail if (!fileName) throw new BadRequestError('Missing fileName in request body'); try { - const thumbnailPath = await ThumbnailsService.generateThumbnail(cid); - const fullThumbnailPath = path.join(BASE_TEMP_DIR, thumbnailPath); + const thumbnailPath = await ThumbnailsService.generateThumbnail(cid, fileName); + const fullThumbnailPath = path.join(BASE_TEMP_DIR, THUMBNAIL_OUTPUT_DIR, thumbnailPath); // Check if the file exists before attempting to stream it fs.access(fullThumbnailPath, fs.constants.F_OK, (err) => { @@ -33,9 +36,8 @@ export const generateThumbnail = async (req: Request<any, any, GenerateThumbnail readStream.pipe(res); }); - // Send the thumbnail as a response - res.status(200); + return res.status(200); } catch (err: any) { - res.status(500).json({ message: err.message }); + return res.status(500).json({ message: err.message }); } }; diff --git a/desci-media-isolated/src/index.ts b/desci-media-isolated/src/index.ts index c68b0d7e1..abbed5727 100644 --- a/desci-media-isolated/src/index.ts +++ b/desci-media-isolated/src/index.ts @@ -1,9 +1,11 @@ +import 'dotenv/config'; import express from 'express'; import helmet from 'helmet'; -import { errorHandler } from './middleware/errorHandler'; -import routes from './routes'; +import { errorHandler } from './middleware/errorHandler.js'; +import routes from './routes/index.js'; const app = express(); +console.log('process.env.PORT:', process.env.PORT); const PORT = process.env.PORT || 7771; app.use(helmet()); diff --git a/desci-media-isolated/src/middleware/errorHandler.ts b/desci-media-isolated/src/middleware/errorHandler.ts index 91135f88a..e755da4f7 100644 --- a/desci-media-isolated/src/middleware/errorHandler.ts +++ b/desci-media-isolated/src/middleware/errorHandler.ts @@ -1,5 +1,5 @@ -import { Request, Response, NextFunction } from 'express'; -import { BaseError } from '../utils/customErrors'; +import type { Request, Response, NextFunction } from 'express'; +import { BaseError } from '../utils/customErrors.js'; export const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => { console.error(err.stack); @@ -9,7 +9,7 @@ export const errorHandler = (err: Error, req: Request, res: Response, next: Next statusCode = err.statusCode; } - res.status(statusCode).json({ + return res.status(statusCode).json({ message: err.message || 'Something went wrong', stack: process.env.NODE_ENV === 'development' ? err.stack : undefined, }); diff --git a/desci-media-isolated/src/routes/index.ts b/desci-media-isolated/src/routes/index.ts index d7c2b6204..bafe8f18a 100644 --- a/desci-media-isolated/src/routes/index.ts +++ b/desci-media-isolated/src/routes/index.ts @@ -1,5 +1,5 @@ import { Router } from 'express'; -import v1 from './v1/index'; +import v1 from './v1/index.js'; const router = Router(); diff --git a/desci-media-isolated/src/routes/v1/index.ts b/desci-media-isolated/src/routes/v1/index.ts index e631244d4..f07481a44 100644 --- a/desci-media-isolated/src/routes/v1/index.ts +++ b/desci-media-isolated/src/routes/v1/index.ts @@ -1,5 +1,5 @@ import { Router } from 'express'; -import thumbnails from './thumbnails'; +import thumbnails from './thumbnails.js'; const router = Router(); diff --git a/desci-media-isolated/src/routes/v1/thumbnails.ts b/desci-media-isolated/src/routes/v1/thumbnails.ts index df4a339d1..0ca747e60 100644 --- a/desci-media-isolated/src/routes/v1/thumbnails.ts +++ b/desci-media-isolated/src/routes/v1/thumbnails.ts @@ -1,5 +1,5 @@ import { Router } from 'express'; -import { generateThumbnail } from '../../controllers/thumbnails/create'; +import { generateThumbnail } from '../../controllers/thumbnails/create.js'; const router = Router(); diff --git a/desci-media-isolated/src/services/ipfs.ts b/desci-media-isolated/src/services/ipfs.ts index 7e6abd92f..cd4a96c11 100644 --- a/desci-media-isolated/src/services/ipfs.ts +++ b/desci-media-isolated/src/services/ipfs.ts @@ -1,8 +1,8 @@ import axios from 'axios'; import fs from 'fs'; import { pipeline } from 'stream/promises'; -import { IPFS_GATEWAY } from '../config'; -import { IpfsConfigurationError } from '../utils/customErrors'; +import { IPFS_GATEWAY } from '../config/index.js'; +import { IpfsConfigurationError } from '../utils/customErrors.js'; export class IpfsService { static async saveFile(cid: string, outputPath: string) { diff --git a/desci-media-isolated/src/services/thumbnails.ts b/desci-media-isolated/src/services/thumbnails.ts index 95e49770c..b3084fbd3 100644 --- a/desci-media-isolated/src/services/thumbnails.ts +++ b/desci-media-isolated/src/services/thumbnails.ts @@ -1,25 +1,32 @@ import { generateAsync } from 'filepreview_ts'; -import { TEMP_DIR, THUMBNAIL_FILES_DIR, THUMBNAIL_OUTPUT_DIR } from '../config'; -import { IpfsService } from './ipfs'; -import { UnhandledError } from '../utils/customErrors'; +import { TEMP_DIR, THUMBNAIL_FILES_DIR, THUMBNAIL_OUTPUT_DIR } from '../config/index.js'; +import { IpfsService } from './ipfs.js'; +import { BadRequestError, UnhandledError } from '../utils/customErrors.js'; import path from 'path'; import fs from 'fs'; +import { fileURLToPath } from 'url'; const THUMBNAIL_DIMENSIONS = { width: 220, height: 300, }; -const BASE_TEMP_DIR = path.resolve(__dirname, '..', TEMP_DIR); + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const BASE_TEMP_DIR = path.resolve(__dirname, '../..', TEMP_DIR); export class ThumbnailsService { - static async generateThumbnail(cid: string) { - const tempFilePath = path.join(BASE_TEMP_DIR, THUMBNAIL_FILES_DIR, `${cid}`); + static async generateThumbnail(cid: string, fileName: string) { + const extension = '.' + fileName.split('.').pop(); + if (!extension) throw new BadRequestError('Invalid file name, requires extension'); + const tempFilePath = path.join(BASE_TEMP_DIR, THUMBNAIL_FILES_DIR, `${cid + extension}`); const thumbnailPath = this.getThumbnailPath(cid); const exportPath = path.join(BASE_TEMP_DIR, THUMBNAIL_OUTPUT_DIR, thumbnailPath); await IpfsService.saveFile(cid, tempFilePath); try { await generateAsync(tempFilePath, exportPath, THUMBNAIL_DIMENSIONS); + console.log('Thumbnail generated successfully:', exportPath); return thumbnailPath; } catch (e) { console.error(e); @@ -41,6 +48,6 @@ export class ThumbnailsService { } static getThumbnailPath(cid: string) { - return `${THUMBNAIL_DIMENSIONS.width}x${THUMBNAIL_DIMENSIONS.height}_${cid}`; + return `${THUMBNAIL_DIMENSIONS.width}x${THUMBNAIL_DIMENSIONS.height}_${cid}.jpg`; } } From c5548361e05fc4ab7757ee2f82757f1330e7b8ad Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Wed, 21 Feb 2024 02:43:43 +0700 Subject: [PATCH 07/42] fix thumbnail gen for pdfs --- desci-media-isolated/Dockerfile | 5 ++++- desci-media-isolated/src/services/thumbnails.ts | 8 +++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/desci-media-isolated/Dockerfile b/desci-media-isolated/Dockerfile index 1704d2786..80102bf10 100644 --- a/desci-media-isolated/Dockerfile +++ b/desci-media-isolated/Dockerfile @@ -5,9 +5,12 @@ FROM docker.io/node:20.9.0 as base # RUN apk update && apk add --no-cache dumb-init # RUN apk add --no-cache unoconv ffmpeg imagemagick curl -RUN apt-get update && apt-get install -y dumb-init unoconv ffmpeg imagemagick curl && \ +RUN apt-get update && apt-get install -y dumb-init ghostscript unoconv ffmpeg imagemagick curl && \ rm -rf /var/lib/apt/lists/* +# Modify ImageMagick policy to allow PDF processing +RUN sed -i '/<policy domain="coder" rights="none" pattern="PDF" \/>/c\<policy domain="Undefined" rights="read|write" pattern="PDF" \/>' /etc/ImageMagick-6/policy.xml + # NPM Permission Fix RUN mkdir -p /.npm RUN chown -R 1001:0 /.npm diff --git a/desci-media-isolated/src/services/thumbnails.ts b/desci-media-isolated/src/services/thumbnails.ts index b3084fbd3..b9adbc352 100644 --- a/desci-media-isolated/src/services/thumbnails.ts +++ b/desci-media-isolated/src/services/thumbnails.ts @@ -7,8 +7,9 @@ import fs from 'fs'; import { fileURLToPath } from 'url'; const THUMBNAIL_DIMENSIONS = { - width: 220, + // width: 220, height: 300, + keepAspect: true, }; const __filename = fileURLToPath(import.meta.url); @@ -25,7 +26,7 @@ export class ThumbnailsService { await IpfsService.saveFile(cid, tempFilePath); try { - await generateAsync(tempFilePath, exportPath, THUMBNAIL_DIMENSIONS); + await generateAsync(tempFilePath, exportPath, { ...THUMBNAIL_DIMENSIONS }); console.log('Thumbnail generated successfully:', exportPath); return thumbnailPath; } catch (e) { @@ -48,6 +49,7 @@ export class ThumbnailsService { } static getThumbnailPath(cid: string) { - return `${THUMBNAIL_DIMENSIONS.width}x${THUMBNAIL_DIMENSIONS.height}_${cid}.jpg`; + return `h-${THUMBNAIL_DIMENSIONS.height}px_${cid}.jpg`; + // return `${THUMBNAIL_DIMENSIONS.width}x${THUMBNAIL_DIMENSIONS.height}_${cid}.jpg`; } } From ad5b88d3f358f224b6776c7daeb3f7326a8c952e Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Wed, 21 Feb 2024 03:56:53 +0700 Subject: [PATCH 08/42] add media-isolated service to docker-compose-dev, isolate communcations --- .vscode/launch.json | 2 +- desci-media-isolated/Dockerfile | 4 +-- docker-compose.dev.yml | 54 +++++++++++++++++++-------------- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index e0d58f439..99647a2ac 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -51,7 +51,7 @@ "restart": true, "localRoot": "${workspaceFolder}/desci-media-isolated", "remoteRoot": "/usr/src/app", - "port": 9277, + "port": 9777, "skipFiles": [ // Node.js internal core modules "<node_internals>/**", diff --git a/desci-media-isolated/Dockerfile b/desci-media-isolated/Dockerfile index 80102bf10..9a07b49a8 100644 --- a/desci-media-isolated/Dockerfile +++ b/desci-media-isolated/Dockerfile @@ -29,9 +29,9 @@ RUN --mount=type=cache,target=/usr/src/app/.npm \ COPY . . # Expose debugger port -EXPOSE 9277 +EXPOSE 9777 -CMD ["dumb-init", "npx", "tsx","watch", "--inspect=0.0.0.0:9277", "src/index.ts"] +CMD ["dumb-init", "npx", "tsx","watch", "--inspect=0.0.0.0:9777", "src/index.ts"] FROM base as production # Cache mounts for faster builds, prod env for better express perf diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index f30ce58c7..da2433ba8 100755 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -18,8 +18,8 @@ services: volumes: - ./local-data/database/boilerplate:/var/lib/postgresql/data/ environment: - POSTGRES_INITDB_ARGS: '--encoding=UTF-8 --lc-collate=C --lc-ctype=C' - POSTGRES_PASSWORD: 'white' + POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C" + POSTGRES_PASSWORD: "white" desci_blockchain_ganache: container_name: "desci_blockchain_ganache" @@ -41,11 +41,9 @@ services: - ./desci-contracts/.openzeppelin:/app/.openzeppelin depends_on: graph_node: - condition: - service_started + condition: service_started db_postgres: - condition: - service_healthy + condition: service_healthy desci_nodes_backend: container_name: "desci_nodes_backend" @@ -61,23 +59,22 @@ services: - host.docker.internal:host-gateway depends_on: db_postgres: - condition: - service_healthy + condition: service_healthy desci_blockchain_ganache: - condition: - service_healthy + condition: service_healthy graph_node: - condition: - service_started + condition: service_started redis: - condition: - service_started + condition: service_started # - nodes_media # UNCOMMENT FOR LOCAL DEV OF nodes-media links: - db_postgres volumes: - ./local-data/yarn_cache:/root/.yarn # mem_limit: 2g #uncomment to test large data with limited memory + networks: + - default + - isolated block_explorer_dev: image: sinaiman/expedition-dev:latest @@ -104,11 +101,9 @@ services: - host.docker.internal:host-gateway depends_on: ipfs: - condition: - service_healthy + condition: service_healthy db_postgres: - condition: - service_healthy + condition: service_healthy environment: # https://github.com/graphprotocol/graph-node/blob/master/docs/environment-variables.md postgres_host: db_postgres @@ -178,11 +173,22 @@ services: - ./.ceramicDev.config.json:/root/.ceramic/daemon.config.json depends_on: ipfs: - condition: - service_healthy + condition: service_healthy db_postgres: - condition: - service_healthy + condition: service_healthy + + desci-media-isolated: + build: + context: ./desci-media-isolated + target: dev + container_name: "media_isolated" + volumes: + - ./desci-media-isolated:/usr/src/app + ports: + - "9777:9777" # debugger + # - "7771:7771" # Uncomment if you want to test the media server from the host machine + networks: + - isolated # desci_nodes_backend_test: # container_name: 'be_test_boilerplate' @@ -199,3 +205,7 @@ services: # volumes: # - .:/app/ # - /app/node_modules + +networks: + isolated: + driver: bridge From 0b6e6dfd84687e61e022517a73c7f050ebc76f4a Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Wed, 21 Feb 2024 04:15:26 +0700 Subject: [PATCH 09/42] update example envs --- desci-media-isolated/.example.env | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/desci-media-isolated/.example.env b/desci-media-isolated/.example.env index 621fc17e8..f2fe1d153 100644 --- a/desci-media-isolated/.example.env +++ b/desci-media-isolated/.example.env @@ -1 +1,5 @@ +NODE_ENV=development + +PORT=7771 + IPFS_GATEWAY=https://ipfs.desci.com/ipfs \ No newline at end of file From 2ba70b4d0f73ca77d5a455a1a2d4db911aa489b8 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Wed, 21 Feb 2024 04:33:15 +0700 Subject: [PATCH 10/42] add k8s deployment configs and network policies --- .../kubernetes/deployment_dev.yaml | 36 +++++++++++++++++++ .../kubernetes/network_policies.yaml | 28 +++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 desci-media-isolated/kubernetes/deployment_dev.yaml create mode 100644 desci-media-isolated/kubernetes/network_policies.yaml diff --git a/desci-media-isolated/kubernetes/deployment_dev.yaml b/desci-media-isolated/kubernetes/deployment_dev.yaml new file mode 100644 index 000000000..7887ea5b0 --- /dev/null +++ b/desci-media-isolated/kubernetes/deployment_dev.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nodes-isolated-media-server-dev + labels: + App: NodesIsolatedMediaServerDev +spec: + replicas: 1 + revisionHistoryLimit: 2 + selector: + matchLabels: + App: NodesIsolatedMediaServerDev + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + App: NodesIsolatedMediaServerDev + spec: + containers: + - image: {CHANGE}.dkr.ecr.us-east-2.amazonaws.com/nodes-isolated-media-server-dev:production + name: nodes-isolated-media-server-dev + ports: + - containerPort: 7771 + name: media_isolated + resources: + limits: + cpu: '0.5' + memory: 2Gi + requests: + cpu: 250m + memory: 1Gi + serviceAccountName: 'default' diff --git a/desci-media-isolated/kubernetes/network_policies.yaml b/desci-media-isolated/kubernetes/network_policies.yaml new file mode 100644 index 000000000..8b2819127 --- /dev/null +++ b/desci-media-isolated/kubernetes/network_policies.yaml @@ -0,0 +1,28 @@ +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: allow-backend-pod-to-isolated-media-server-comms +spec: + podSelector: + matchLabels: + App: NodesIsolatedMediaServerDev + ingress: + - from: + - podSelector: + matchLabels: + allow-from: 'DesciServerDev' + policyTypes: + - Ingress + + +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: deny-egress-from-isolated-media-server +spec: + podSelector: + matchLabels: + App: NodesIsolatedMediaServerDev + egress: [] + policyTypes: + - Egress From 46abfb6ed90740acab17633204e328bef5560058 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Thu, 22 Feb 2024 02:44:56 +0700 Subject: [PATCH 11/42] fix attachUser middleware --- desci-server/src/middleware/attachUser.ts | 38 +++++++++++++++++++++++ desci-server/src/middleware/ensureUser.ts | 24 ++++++++++++-- 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100755 desci-server/src/middleware/attachUser.ts diff --git a/desci-server/src/middleware/attachUser.ts b/desci-server/src/middleware/attachUser.ts new file mode 100755 index 000000000..c9fb62a9d --- /dev/null +++ b/desci-server/src/middleware/attachUser.ts @@ -0,0 +1,38 @@ +import { User } from '@prisma/client'; +import { Request, Response, NextFunction } from 'express'; + +import { + AuthMethods, + extractApiKey, + extractAuthToken, + extractUserFromApiKey, + extractUserFromToken, +} from './permissions.js'; + +/** + * Attaches the user to the request (req.user), the difference between this middleware and ensureUser is that this is optional + * and won't reject with a 401 if not logged in. + */ +export const attachUser = async (req: Request, res: Response, next: NextFunction) => { + const authHeader = req.headers['authorization']; + const apiKeyHeader = req.headers['api-key']; + + const token = authHeader ? await extractAuthToken(req) : undefined; + const apiKey = apiKeyHeader ? await await extractApiKey(req) : undefined; + const authTokenRetrieval = authHeader ? await extractUserFromToken(token) : undefined; + const apiKeyRetrieval = apiKeyHeader ? await extractUserFromApiKey(apiKey, req.ip) : undefined; + + const retrievedUser = authTokenRetrieval || apiKeyRetrieval; + + if (retrievedUser) { + (req as any).user = retrievedUser; + (req as any).authMethod = authTokenRetrieval ? AuthMethods.AUTH_TOKEN : AuthMethods.API_KEY; + } + next(); +}; + +export const retrieveUser = async (req: Request): Promise<User | null> => { + const token = await extractAuthToken(req); + const retrievedUser = await extractUserFromToken(token); + return retrievedUser; +}; diff --git a/desci-server/src/middleware/ensureUser.ts b/desci-server/src/middleware/ensureUser.ts index 77ebde032..6da4f3fcb 100755 --- a/desci-server/src/middleware/ensureUser.ts +++ b/desci-server/src/middleware/ensureUser.ts @@ -1,7 +1,13 @@ import { User } from '@prisma/client'; import { Request, Response, NextFunction } from 'express'; -import { extractAuthToken, extractUserFromToken } from './permissions.js'; +import { + AuthMethods, + extractApiKey, + extractAuthToken, + extractUserFromApiKey, + extractUserFromToken, +} from './permissions.js'; // export const ensureUser = async (req: Request, res: Response, next: NextFunction) => { // const retrievedUser = await retrieveUser(req); @@ -18,8 +24,20 @@ import { extractAuthToken, extractUserFromToken } from './permissions.js'; * and won't reject with a 401 if not logged in. */ export const attachUser = async (req: Request, res: Response, next: NextFunction) => { - const retrievedUser = await retrieveUser(req); - (req as any).user = retrievedUser; + const authHeader = req.headers['authorization']; + const apiKeyHeader = req.headers['api-key']; + + const token = authHeader ? await extractAuthToken(req) : undefined; + const apiKey = apiKeyHeader ? await await extractApiKey(req) : undefined; + const authTokenRetrieval = authHeader ? await extractUserFromToken(token) : undefined; + const apiKeyRetrieval = apiKeyHeader ? await extractUserFromApiKey(apiKey, req.ip) : undefined; + + const retrievedUser = authTokenRetrieval || apiKeyRetrieval; + + if (retrievedUser) { + (req as any).user = retrievedUser; + (req as any).authMethod = authTokenRetrieval ? AuthMethods.AUTH_TOKEN : AuthMethods.API_KEY; + } next(); }; From d20f3c7cf6325e145ba4d04065978e4e6920b4cb Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Thu, 22 Feb 2024 03:08:57 +0700 Subject: [PATCH 12/42] add thumbnails table/migration --- .../migration.sql | 15 +++++++++++++++ desci-server/prisma/schema.prisma | 11 +++++++++++ 2 files changed, 26 insertions(+) create mode 100644 desci-server/prisma/migrations/20240221145731_add_thumbnails_table/migration.sql diff --git a/desci-server/prisma/migrations/20240221145731_add_thumbnails_table/migration.sql b/desci-server/prisma/migrations/20240221145731_add_thumbnails_table/migration.sql new file mode 100644 index 000000000..ba2e3be50 --- /dev/null +++ b/desci-server/prisma/migrations/20240221145731_add_thumbnails_table/migration.sql @@ -0,0 +1,15 @@ +-- CreateTable +CREATE TABLE "NodeThumbnails" ( + "id" SERIAL NOT NULL, + "componentCid" TEXT NOT NULL, + "nodeUuid" TEXT NOT NULL, + "thumbnails" JSONB NOT NULL, + + CONSTRAINT "NodeThumbnails_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "NodeThumbnails_nodeUuid_componentCid_key" ON "NodeThumbnails"("nodeUuid", "componentCid"); + +-- AddForeignKey +ALTER TABLE "NodeThumbnails" ADD CONSTRAINT "NodeThumbnails_nodeUuid_fkey" FOREIGN KEY ("nodeUuid") REFERENCES "Node"("uuid") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/desci-server/prisma/schema.prisma b/desci-server/prisma/schema.prisma index 0c3c67e5b..2a882b36c 100755 --- a/desci-server/prisma/schema.prisma +++ b/desci-server/prisma/schema.prisma @@ -39,6 +39,7 @@ model Node { DraftNodeTree DraftNodeTree[] ceramicStream String? NodeAttestation NodeAttestation[] + NodeThumbnails NodeThumbnails[] @@index([ownerId]) @@index([uuid]) @@ -457,6 +458,16 @@ model NodeCover { @@unique([nodeUuid, version]) } +model NodeThumbnails { + id Int @id @default(autoincrement()) + componentCid String + nodeUuid String + thumbnails Json + node Node @relation(fields: [nodeUuid], references: [uuid]) + + @@unique([nodeUuid, componentCid]) +} + model FriendReferral { id Int @id @default(autoincrement()) uuid String @unique @default(uuid()) From 351e9ebe84718634d5f4ad45db497a966d05c02a Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Thu, 22 Feb 2024 03:09:55 +0700 Subject: [PATCH 13/42] add desci-server routes/services/controllers for communicating with isolated media server --- .../src/controllers/nodes/thumbnails.ts | 59 ++++++++ desci-server/src/middleware/ensureUser.ts | 48 ------- .../src/middleware/ensureUserIfPresent.ts | 2 +- desci-server/src/middleware/index.ts | 2 +- desci-server/src/middleware/permissions.ts | 11 +- desci-server/src/routes/v1/data.ts | 2 +- desci-server/src/routes/v1/nodes.ts | 3 + desci-server/src/services/Thumbnails.ts | 131 ++++++++++++++++++ desci-server/src/services/data/processing.ts | 10 ++ desci-server/src/services/ipfs.ts | 8 ++ desci-server/src/services/repoService.ts | 2 + 11 files changed, 223 insertions(+), 55 deletions(-) create mode 100644 desci-server/src/controllers/nodes/thumbnails.ts delete mode 100755 desci-server/src/middleware/ensureUser.ts create mode 100644 desci-server/src/services/Thumbnails.ts diff --git a/desci-server/src/controllers/nodes/thumbnails.ts b/desci-server/src/controllers/nodes/thumbnails.ts new file mode 100644 index 000000000..6ae6384b8 --- /dev/null +++ b/desci-server/src/controllers/nodes/thumbnails.ts @@ -0,0 +1,59 @@ +import type { Request, Response, NextFunction } from 'express'; + +import { prisma } from '../../client.js'; +import { NodeUuid } from '../../internal.js'; +import { type ThumbnailMap, thumbnailsService } from '../../services/Thumbnails.js'; +import { ensureUuidEndsWithDot } from '../../utils.js'; + +type ThumbnailsReqBodyParams = { + uuid: string; + manifestCid?: string; +}; + +type ThumbnailsResponse = { + ok: true; + thumbnailMap: ThumbnailMap; +}; + +type ThumbnailsErrorResponse = { + ok: false; + error: string; + status?: number; +}; + +/** + * Generates and retrieves preview thumbnails of pinned components for a node. + * @param req.params.uuid required for both drafts and published nodes + * @param req.params.manifestCid only required for published nodes (to get a specific version), without the latest draft will be used. + * @return {ThumbnailMap} ThumbnailMap = Record<ComponentCidString, Record<HeightPx, ThumbnailCidString>> + */ +export const thumbnails = async ( + req: Request<any, any, ThumbnailsReqBodyParams>, + res: Response<ThumbnailsResponse | ThumbnailsErrorResponse>, +) => { + const user = (req as any).user; + const { uuid, manifestCid } = req.params; + + if (!uuid) return res.status(400).json({ ok: false, error: 'UUID is required.' }); + + if (!user && !manifestCid) { + // If there's no manifestCid passed in, we're looking at a draft node, and it requires auth. + return res.status(401).json({ ok: false, error: 'Unauthorized' }); + } + + if (user && !manifestCid) { + // Check if user owns node, if requesting draft thumbnails + const node = await prisma.node.findFirst({ + where: { + ownerId: user.id, + uuid: ensureUuidEndsWithDot(uuid), + }, + }); + + if (!node) return res.status(401).json({ ok: false, error: 'Unauthorized' }); + } + + const thumbnailMap = await thumbnailsService.getThumbnailsForNode({ uuid: uuid as NodeUuid, manifestCid }); + + return res.status(200).json({ ok: true, thumbnailMap }); +}; diff --git a/desci-server/src/middleware/ensureUser.ts b/desci-server/src/middleware/ensureUser.ts deleted file mode 100755 index 6da4f3fcb..000000000 --- a/desci-server/src/middleware/ensureUser.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { User } from '@prisma/client'; -import { Request, Response, NextFunction } from 'express'; - -import { - AuthMethods, - extractApiKey, - extractAuthToken, - extractUserFromApiKey, - extractUserFromToken, -} from './permissions.js'; - -// export const ensureUser = async (req: Request, res: Response, next: NextFunction) => { -// const retrievedUser = await retrieveUser(req); -// if (!retrievedUser) { -// res.status(401).send({ ok: false, message: 'Unauthorized' }); -// return; -// } -// (req as any).user = retrievedUser; -// next(); -// }; - -/** - * Attaches the user to the request (req.user), the difference between this middleware and ensureUser is that this is optional - * and won't reject with a 401 if not logged in. - */ -export const attachUser = async (req: Request, res: Response, next: NextFunction) => { - const authHeader = req.headers['authorization']; - const apiKeyHeader = req.headers['api-key']; - - const token = authHeader ? await extractAuthToken(req) : undefined; - const apiKey = apiKeyHeader ? await await extractApiKey(req) : undefined; - const authTokenRetrieval = authHeader ? await extractUserFromToken(token) : undefined; - const apiKeyRetrieval = apiKeyHeader ? await extractUserFromApiKey(apiKey, req.ip) : undefined; - - const retrievedUser = authTokenRetrieval || apiKeyRetrieval; - - if (retrievedUser) { - (req as any).user = retrievedUser; - (req as any).authMethod = authTokenRetrieval ? AuthMethods.AUTH_TOKEN : AuthMethods.API_KEY; - } - next(); -}; - -export const retrieveUser = async (req: Request): Promise<User | null> => { - const token = await extractAuthToken(req); - const retrievedUser = await extractUserFromToken(token); - return retrievedUser; -}; diff --git a/desci-server/src/middleware/ensureUserIfPresent.ts b/desci-server/src/middleware/ensureUserIfPresent.ts index c772924c3..d18198f76 100644 --- a/desci-server/src/middleware/ensureUserIfPresent.ts +++ b/desci-server/src/middleware/ensureUserIfPresent.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from 'express'; -import { retrieveUser } from './ensureUser.js'; +import { retrieveUser } from './attachUser.js'; export const ensureUserIfPresent = async (req: Request, res: Response, next: NextFunction) => { const retrievedUser = await retrieveUser(req); diff --git a/desci-server/src/middleware/index.ts b/desci-server/src/middleware/index.ts index a2af0b418..4ca90616f 100644 --- a/desci-server/src/middleware/index.ts +++ b/desci-server/src/middleware/index.ts @@ -1,7 +1,7 @@ export * from './authorisation.js'; export * from './checkJwt.js'; export * from './ensureAdmin.js'; -export * from './ensureUser.js'; +export * from './attachUser.js'; export * from './ensureUserIfPresent.js'; export * from './errorHandler.js'; export * from './permissions.js'; diff --git a/desci-server/src/middleware/permissions.ts b/desci-server/src/middleware/permissions.ts index 8f006516a..4ad0c7608 100644 --- a/desci-server/src/middleware/permissions.ts +++ b/desci-server/src/middleware/permissions.ts @@ -13,10 +13,13 @@ export enum AuthMethods { } export const ensureUser = async (req: ExpressRequest, res: Response, next: NextFunction) => { - const token = await extractAuthToken(req); - const apiKey = await extractApiKey(req); - const authTokenRetrieval = await extractUserFromToken(token); - const apiKeyRetrieval = await extractUserFromApiKey(apiKey, req.ip); + const authHeader = req.headers['authorization']; + const apiKeyHeader = req.headers['api-key']; + + const token = authHeader ? await extractAuthToken(req) : undefined; + const apiKey = apiKeyHeader ? await await extractApiKey(req) : undefined; + const authTokenRetrieval = authHeader ? await extractUserFromToken(token) : undefined; + const apiKeyRetrieval = apiKeyHeader ? await extractUserFromApiKey(apiKey, req.ip) : undefined; const retrievedUser = authTokenRetrieval || apiKeyRetrieval; diff --git a/desci-server/src/routes/v1/data.ts b/desci-server/src/routes/v1/data.ts index 54ff342ee..c4057ed32 100644 --- a/desci-server/src/routes/v1/data.ts +++ b/desci-server/src/routes/v1/data.ts @@ -9,7 +9,7 @@ import { moveData } from '../../controllers/data/move.js'; import { updateExternalCid } from '../../controllers/data/updateExternalCid.js'; import { logger } from '../../logger.js'; import { ensureNodeAccess, ensureWriteAccessCheck } from '../../middleware/authorisation.js'; -import { attachUser } from '../../middleware/ensureUser.js'; +import { attachUser } from '../../middleware/attachUser.js'; import { ensureUser } from '../../middleware/permissions.js'; import { isS3Configured, s3Client } from '../../services/s3.js'; diff --git a/desci-server/src/routes/v1/nodes.ts b/desci-server/src/routes/v1/nodes.ts index ec420cfe0..555f8f3c7 100755 --- a/desci-server/src/routes/v1/nodes.ts +++ b/desci-server/src/routes/v1/nodes.ts @@ -22,7 +22,9 @@ import { } from '../../controllers/nodes/index.js'; import { retrieveTitle } from '../../controllers/nodes/legacyManifestApi.js'; import { prepublish } from '../../controllers/nodes/prepublish.js'; +import { thumbnails } from '../../controllers/nodes/thumbnails.js'; import { versionDetails } from '../../controllers/nodes/versionDetails.js'; +import { attachUser } from '../../internal.js'; import { ensureNodeAccess } from '../../middleware/authorisation.js'; import { ensureUser } from '../../middleware/permissions.js'; @@ -48,6 +50,7 @@ router.get('/cover/:uuid', [], getCoverImage); router.get('/cover/:uuid/:version', [], getCoverImage); router.get('/documents/:uuid', [ensureUser, ensureNodeAccess], getNodeDocument); router.post('/documents/:uuid/actions', [ensureUser, ensureNodeAccess], dispatchDocumentChange); +router.get('/thumbnails/:uuid/:manifestCid?', [attachUser], thumbnails); router.delete('/:uuid', [ensureUser], deleteNode); diff --git a/desci-server/src/services/Thumbnails.ts b/desci-server/src/services/Thumbnails.ts new file mode 100644 index 000000000..8668ce2f0 --- /dev/null +++ b/desci-server/src/services/Thumbnails.ts @@ -0,0 +1,131 @@ +import axios from 'axios'; + +import { prisma } from '../client.js'; +import { logger as parentLogger } from '../logger.js'; + +import { getManifestByCid, getManifestFromNode, pinNewFiles } from './data/processing.js'; +import { pinFile } from './ipfs.js'; +import { NodeUuid } from './manifestRepo.js'; +import repoService from './repoService.js'; + +const logger = parentLogger.child({ + module: 'Services::Thumbnails', +}); + +export type HeightPx = number; +export type CidString = string; +export type FileName = string; + +export type Thumbnail = Record<HeightPx, CidString>; +export type ThumbnailMap = Record<CidString, Thumbnail>; + +type GenerateThumbnailResult = { + componentCid: CidString; + height: HeightPx; + thumbnailCid: CidString; +} | null; + +// Hardcoded for the time being, we can modify this logic if the need more size variants +const HEIGHT_PX = 300; + +export class ThumbnailsService { + async getThumbnailsForNode({ + uuid, + manifestCid, + // heightPx, + }: { + uuid: NodeUuid; + manifestCid?: string; + // heightPx: HeightPx; + }): Promise<ThumbnailMap> { + debugger; + const manifest = manifestCid + ? await getManifestByCid(manifestCid) + : await repoService.getDraftManifest(uuid as NodeUuid); + + const pinnedComponents = manifest?.components?.filter((c) => c.starred); + + // Determined by the file extension (can't generate thumbnails for files without extensions) + const fileComponents = pinnedComponents?.filter((c) => c.payload.path.split('/').pop().includes('.')); + const fileComponentCids = fileComponents?.map((c) => c.payload.cid || c.payload.url); + // const fileComponentCidMap = fileComponents.reduce((map, comp) => { + // const key = comp.payload.cid || comp.payload.url; + // map[key] = comp; + // return map; + // }, {}); + + const thumbnailsToGenerate: Record<CidString, FileName> = fileComponents?.reduce((map, comp) => { + const fileName = comp.payload.path.split('/').pop(); + const cid = comp.payload.cid || comp.payload.url; + map[cid] = fileName; + return map; + }, {}); + + const thumbnailMap: ThumbnailMap = {}; + // Check which thumbnails already exist + const existingThumbnailsFound = await prisma.nodeThumbnails.findMany({ + where: { componentCid: { in: fileComponentCids } }, + }); + // Check if the desired sizes exist, otherwise add to the generation array + for (const thumbnail of existingThumbnailsFound) { + const desiredSizeThumbnail = thumbnail.thumbnails[HEIGHT_PX]; + if (desiredSizeThumbnail) { + // If exists, add it to the returned thumbnail map. + thumbnailMap[thumbnail.componentCid] = thumbnail.thumbnails; + // Remove it from the generation map + delete thumbnailsToGenerate[thumbnail.componentCid]; + } + } + + // Generate thumbnails for the ones that don't exist + const generatedThumbnails = await Promise.all( + Object.entries(thumbnailsToGenerate).map(([cid, fileName]) => this.generateThumbnail(cid, fileName, HEIGHT_PX)), + ); + + // Add the newly generated ones to the thumbnail map + generatedThumbnails.forEach((newThumb) => { + thumbnailMap[newThumb.componentCid] = { [HEIGHT_PX]: newThumb.thumbnailCid }; + }); + + return thumbnailMap; + } + + private async generateThumbnail( + cid: CidString, + componentFileName: string, + heightPx: HeightPx, + ): Promise<GenerateThumbnailResult> { + if (process.env.ISOLATED_MEDIA_SERVER_URL === undefined) { + logger.error('process.env.ISOLATED_MEDIA_SERVER_URL is not defined'); + return null; + } + // Generate the thumbnail + const thumbnailStream = await axios.post( + `${process.env.ISOLATED_MEDIA_SERVER_URL}/v1/thumbnails?height${heightPx}`, + { cid: cid, fileName: componentFileName }, + { + responseType: 'stream', + }, + ); + + // Save it on IPFS + const pinned = await pinFile(thumbnailStream.data); + // Save it to the database + const existingThumbnail = await prisma.nodeThumbnails.findFirst({ + where: { componentCid: cid }, + }); + (await existingThumbnail) + ? prisma.nodeThumbnails.update({ + where: { componentCid: cid }, + data: { thumbnails: { ...existingThumbnail.thumbnails, [heightPx]: pinned.cid } }, + }) + : prisma.nodeThumbnails.create({ data: { componentCid: cid, thumbnails: { [heightPx]: pinned.cid } } }); + + // LATER: Add data ref + + // Return the CID + return { componentCid: cid, height: heightPx, thumbnailCid: pinned.cid }; + } +} + +export const thumbnailsService = new ThumbnailsService(); diff --git a/desci-server/src/services/data/processing.ts b/desci-server/src/services/data/processing.ts index d1f8dcddb..bcb7c3b44 100644 --- a/desci-server/src/services/data/processing.ts +++ b/desci-server/src/services/data/processing.ts @@ -341,6 +341,16 @@ export async function getManifestFromNode( } } +export async function getManifestByCid(manifestCid: string, queryString?: string): Promise<ResearchObjectV1> { + const manifestUrlEntry = manifestCid ? cleanupManifestUrl(manifestCid, queryString as string) : null; + try { + const fetchedManifest = manifestUrlEntry ? await (await axios.get(manifestUrlEntry)).data : null; + return fetchedManifest; + } catch (e) { + throw createIpfsUnresolvableError(`Error fetching manifest from IPFS, manifestCid: ${manifestCid}`); + } +} + export function pathContainsExternalCids(flatTreeMap: Record<DrivePath, RecursiveLsResult>, contextPath: string) { // Check if update path contains externals, disable adding to external DAGs const pathMatch = flatTreeMap[contextPath]; diff --git a/desci-server/src/services/ipfs.ts b/desci-server/src/services/ipfs.ts index c25478bf3..9f6df529d 100644 --- a/desci-server/src/services/ipfs.ts +++ b/desci-server/src/services/ipfs.ts @@ -298,6 +298,14 @@ export async function pinExternalDags(cids: string[]): Promise<string[]> { return result; } +export const pinFile = async (file: Buffer | Readable | ReadableStream): Promise<IpfsPinnedResult> => { + const isOnline = await client.isOnline(); + logger.debug({ fn: 'pinFile' }, `isOnline: ${isOnline}`); + + const uploaded = await client.add(file, { cidVersion: 1, pin: true }); + return { ...uploaded, cid: uploaded.cid.toString() }; +}; + export interface RecursiveLsResult extends IpfsPinnedResult { name: string; contains?: RecursiveLsResult[]; diff --git a/desci-server/src/services/repoService.ts b/desci-server/src/services/repoService.ts index 0cec60b0f..6fc3cddd7 100644 --- a/desci-server/src/services/repoService.ts +++ b/desci-server/src/services/repoService.ts @@ -4,6 +4,7 @@ import axios, { AxiosInstance } from 'axios'; import { logger as parentLogger } from '../logger.js'; import { ResearchObjectDocument } from '../types/documents.js'; +import { ensureUuidEndsWithDot } from '../utils.js'; import { ManifestActions, NodeUuid } from './manifestRepo.js'; @@ -103,6 +104,7 @@ class RepoService { async getDraftManifest(uuid: NodeUuid) { logger.info({ uuid }, 'Retrieve Draft Document'); // try {} catch (err) {} + // uuid = ensureUuidEndsWithDot(uuid) as NodeUuid; try { const response = await this.getDraftDocument({ uuid }); return response ? response.manifest : null; From 575aadccc17b4e932dd62b561774f13bfed6f5b3 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Thu, 22 Feb 2024 03:10:16 +0700 Subject: [PATCH 14/42] slight fixes, update .env.example --- .env.example | 3 +++ desci-media-isolated/src/controllers/thumbnails/create.ts | 8 ++++++-- desci-media-isolated/src/services/thumbnails.ts | 4 ++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index d6b37f9be..2a0b0c88e 100755 --- a/.env.example +++ b/.env.example @@ -5,6 +5,7 @@ PORT=5420 IPFS_NODE_URL=http://host.docker.internal:5001 PUBLIC_IPFS_RESOLVER=https://ipfs.io + # IPFS_RESOLVER_OVERRIDE=http://host.docker.internal:8089/ipfs ### Database - Postgres @@ -96,3 +97,5 @@ REPO_SERVICE_SECRET_KEY=secretrepo TOGGLE_CERAMIC= # If above is set, clone `@desci-labs/desci-codex` and put the path to it here CODEX_REPO_PATH= + +ISOLATED_MEDIA_SERVER_URL=http://host.docker.internal:7771 diff --git a/desci-media-isolated/src/controllers/thumbnails/create.ts b/desci-media-isolated/src/controllers/thumbnails/create.ts index 0e23350b1..912bdd257 100644 --- a/desci-media-isolated/src/controllers/thumbnails/create.ts +++ b/desci-media-isolated/src/controllers/thumbnails/create.ts @@ -15,13 +15,17 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const BASE_TEMP_DIR = path.resolve(__dirname, '../../..', TEMP_DIR); -export const generateThumbnail = async (req: Request<any, any, GenerateThumbnailRequestBody>, res: Response) => { +export const generateThumbnail = async ( + req: Request<any, any, GenerateThumbnailRequestBody, { height: number }>, + res: Response, +) => { const { cid, fileName } = req.body; + const { height = 300 } = req.query; if (!cid) throw new BadRequestError('Missing cid in request body'); if (!fileName) throw new BadRequestError('Missing fileName in request body'); try { - const thumbnailPath = await ThumbnailsService.generateThumbnail(cid, fileName); + const thumbnailPath = await ThumbnailsService.generateThumbnail(cid, fileName, height); const fullThumbnailPath = path.join(BASE_TEMP_DIR, THUMBNAIL_OUTPUT_DIR, thumbnailPath); // Check if the file exists before attempting to stream it diff --git a/desci-media-isolated/src/services/thumbnails.ts b/desci-media-isolated/src/services/thumbnails.ts index b9adbc352..a3b9fff07 100644 --- a/desci-media-isolated/src/services/thumbnails.ts +++ b/desci-media-isolated/src/services/thumbnails.ts @@ -17,7 +17,7 @@ const __dirname = path.dirname(__filename); const BASE_TEMP_DIR = path.resolve(__dirname, '../..', TEMP_DIR); export class ThumbnailsService { - static async generateThumbnail(cid: string, fileName: string) { + static async generateThumbnail(cid: string, fileName: string, heightPx: number) { const extension = '.' + fileName.split('.').pop(); if (!extension) throw new BadRequestError('Invalid file name, requires extension'); const tempFilePath = path.join(BASE_TEMP_DIR, THUMBNAIL_FILES_DIR, `${cid + extension}`); @@ -26,7 +26,7 @@ export class ThumbnailsService { await IpfsService.saveFile(cid, tempFilePath); try { - await generateAsync(tempFilePath, exportPath, { ...THUMBNAIL_DIMENSIONS }); + await generateAsync(tempFilePath, exportPath, { ...THUMBNAIL_DIMENSIONS, height: heightPx }); console.log('Thumbnail generated successfully:', exportPath); return thumbnailPath; } catch (e) { From e7245debef37177950be78220962b59faeab966e Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Thu, 22 Feb 2024 04:56:57 +0700 Subject: [PATCH 15/42] fix --- desci-server/src/middleware/permissions.ts | 12 +++++------- desci-server/src/services/Thumbnails.ts | 3 ++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/desci-server/src/middleware/permissions.ts b/desci-server/src/middleware/permissions.ts index 4ad0c7608..db26115f1 100644 --- a/desci-server/src/middleware/permissions.ts +++ b/desci-server/src/middleware/permissions.ts @@ -13,13 +13,11 @@ export enum AuthMethods { } export const ensureUser = async (req: ExpressRequest, res: Response, next: NextFunction) => { - const authHeader = req.headers['authorization']; - const apiKeyHeader = req.headers['api-key']; - - const token = authHeader ? await extractAuthToken(req) : undefined; - const apiKey = apiKeyHeader ? await await extractApiKey(req) : undefined; - const authTokenRetrieval = authHeader ? await extractUserFromToken(token) : undefined; - const apiKeyRetrieval = apiKeyHeader ? await extractUserFromApiKey(apiKey, req.ip) : undefined; + debugger; + const token = await extractAuthToken(req); + const apiKey = await extractApiKey(req); + const authTokenRetrieval = await extractUserFromToken(token); + const apiKeyRetrieval = await extractUserFromApiKey(apiKey, req.ip); const retrievedUser = authTokenRetrieval || apiKeyRetrieval; diff --git a/desci-server/src/services/Thumbnails.ts b/desci-server/src/services/Thumbnails.ts index 1916c287d..d1244314e 100644 --- a/desci-server/src/services/Thumbnails.ts +++ b/desci-server/src/services/Thumbnails.ts @@ -54,6 +54,7 @@ export class ThumbnailsService { // map[key] = comp; // return map; // }, {}); + if (!fileComponents) return {}; const thumbnailsToGenerate: Record<CidString, FileName> = fileComponents?.reduce((map, comp) => { const fileName = comp.payload.path.split('/').pop(); @@ -65,7 +66,7 @@ export class ThumbnailsService { const thumbnailMap: ThumbnailMap = {}; // Check which thumbnails already exist const existingThumbnailsFound = await prisma.nodeThumbnails.findMany({ - where: { componentCid: { in: fileComponentCids } }, + where: { componentCid: { in: fileComponentCids }, nodeUuid: ensureUuidEndsWithDot(uuid) }, }); // Check if the desired sizes exist, otherwise add to the generation array for (const thumbnail of existingThumbnailsFound) { From 955e089d4e46b49a97ec98d2879fcfda7991338b Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Thu, 22 Feb 2024 05:08:52 +0700 Subject: [PATCH 16/42] update example env --- .env.example | 2 +- desci-server/src/services/Thumbnails.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index 7f880a5d6..a8156e369 100755 --- a/.env.example +++ b/.env.example @@ -98,6 +98,6 @@ TOGGLE_CERAMIC= # If above is set, clone `@desci-labs/desci-codex` and put the path to it here CODEX_REPO_PATH= -ISOLATED_MEDIA_SERVER_URL=http://host.docker.internal:7771 +ISOLATED_MEDIA_SERVER_URL=http://media_isolated:7771 # SET TO 1 to run communities seed script RUN=1 diff --git a/desci-server/src/services/Thumbnails.ts b/desci-server/src/services/Thumbnails.ts index d1244314e..a9bcc445b 100644 --- a/desci-server/src/services/Thumbnails.ts +++ b/desci-server/src/services/Thumbnails.ts @@ -6,7 +6,7 @@ import { ensureUuidEndsWithDot } from '../utils.js'; import { getManifestByCid, getManifestFromNode, pinNewFiles } from './data/processing.js'; import { pinFile } from './ipfs.js'; -import { NodeUuid } from './manifestRepo.js'; +import { NodeUuid, getLatestManifestFromNode } from './manifestRepo.js'; import repoService from './repoService.js'; const logger = parentLogger.child({ @@ -40,9 +40,9 @@ export class ThumbnailsService { // heightPx: HeightPx; }): Promise<ThumbnailMap> { debugger; - const manifest = manifestCid - ? await getManifestByCid(manifestCid) - : await repoService.getDraftManifest(uuid as NodeUuid); + const node = await prisma.node.findFirst({ where: { uuid: ensureUuidEndsWithDot(uuid) } }); + + const manifest = manifestCid ? await getManifestByCid(manifestCid) : await getLatestManifestFromNode(node); const pinnedComponents = manifest?.components?.filter((c) => c.starred); From 744f9658656ce45ffd4cb8c37988484ff4456f52 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Thu, 22 Feb 2024 05:16:43 +0700 Subject: [PATCH 17/42] remove debuggers --- desci-server/src/middleware/permissions.ts | 2 +- desci-server/src/services/Thumbnails.ts | 2 +- desci-server/src/services/user.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/desci-server/src/middleware/permissions.ts b/desci-server/src/middleware/permissions.ts index db26115f1..0872a52a4 100644 --- a/desci-server/src/middleware/permissions.ts +++ b/desci-server/src/middleware/permissions.ts @@ -13,7 +13,7 @@ export enum AuthMethods { } export const ensureUser = async (req: ExpressRequest, res: Response, next: NextFunction) => { - debugger; + // debugger; const token = await extractAuthToken(req); const apiKey = await extractApiKey(req); const authTokenRetrieval = await extractUserFromToken(token); diff --git a/desci-server/src/services/Thumbnails.ts b/desci-server/src/services/Thumbnails.ts index a9bcc445b..d1f3314a9 100644 --- a/desci-server/src/services/Thumbnails.ts +++ b/desci-server/src/services/Thumbnails.ts @@ -39,7 +39,7 @@ export class ThumbnailsService { manifestCid?: string; // heightPx: HeightPx; }): Promise<ThumbnailMap> { - debugger; + // debugger; const node = await prisma.node.findFirst({ where: { uuid: ensureUuidEndsWithDot(uuid) } }); const manifest = manifestCid ? await getManifestByCid(manifestCid) : await getLatestManifestFromNode(node); diff --git a/desci-server/src/services/user.ts b/desci-server/src/services/user.ts index c94405b3c..1f651ac08 100644 --- a/desci-server/src/services/user.ts +++ b/desci-server/src/services/user.ts @@ -98,7 +98,7 @@ export async function writeExternalIdToOrcidProfile(userId: number, didAddress: console.log('External ID already added'); return; } - debugger; + // debugger; } catch (error) { console.error('Error getting external IDs:', error.response?.data || error.message); } From 2d0c4ba639544ee7c68b4e9b47a3798ed2357f96 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Fri, 23 Feb 2024 04:05:01 +0700 Subject: [PATCH 18/42] fixes for local dev --- .env.example | 5 +++ desci-media-isolated/.env.example | 7 ++++ desci-media-isolated/.example.env | 5 --- desci-media-isolated/src/services/ipfs.ts | 4 ++- .../src/services/thumbnails.ts | 2 ++ .../src/controllers/proxy/ipfsReadGateway.ts | 36 +++++++++++++++++++ desci-server/src/routes/v1/data.ts | 2 +- desci-server/src/routes/v1/index.ts | 2 ++ 8 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 desci-media-isolated/.env.example delete mode 100644 desci-media-isolated/.example.env create mode 100644 desci-server/src/controllers/proxy/ipfsReadGateway.ts diff --git a/.env.example b/.env.example index a8156e369..5c03f0e8d 100755 --- a/.env.example +++ b/.env.example @@ -98,6 +98,11 @@ TOGGLE_CERAMIC= # If above is set, clone `@desci-labs/desci-codex` and put the path to it here CODEX_REPO_PATH= + + +# ISOLATED MEDIA SERVER ISOLATED_MEDIA_SERVER_URL=http://media_isolated:7771 +IPFS_READ_ONLY_GATEWAY_SERVER=http://host.docker.internal:8089/ipfs # Used to proxy ipfs requests for ISOLATED_MEDIA_SERVER + # SET TO 1 to run communities seed script RUN=1 diff --git a/desci-media-isolated/.env.example b/desci-media-isolated/.env.example new file mode 100644 index 000000000..01693de22 --- /dev/null +++ b/desci-media-isolated/.env.example @@ -0,0 +1,7 @@ +NODE_ENV=development + +PORT=7771 + +# IPFS_GATEWAY=https://ipfs.desci.com/ipfs + +IPFS_GATEWAY=http://host.docker.internal:5420/v1/ipfs \ No newline at end of file diff --git a/desci-media-isolated/.example.env b/desci-media-isolated/.example.env deleted file mode 100644 index f2fe1d153..000000000 --- a/desci-media-isolated/.example.env +++ /dev/null @@ -1,5 +0,0 @@ -NODE_ENV=development - -PORT=7771 - -IPFS_GATEWAY=https://ipfs.desci.com/ipfs \ No newline at end of file diff --git a/desci-media-isolated/src/services/ipfs.ts b/desci-media-isolated/src/services/ipfs.ts index cd4a96c11..bbf4413ce 100644 --- a/desci-media-isolated/src/services/ipfs.ts +++ b/desci-media-isolated/src/services/ipfs.ts @@ -7,9 +7,10 @@ import { IpfsConfigurationError } from '../utils/customErrors.js'; export class IpfsService { static async saveFile(cid: string, outputPath: string) { if (!IPFS_GATEWAY) { + console.log('IPFS_GATEWAY:', IPFS_GATEWAY); throw new IpfsConfigurationError('process.env.IPFS_GATEWAY is not defined in environment variables'); } - + debugger; const url = `${IPFS_GATEWAY}/${cid}`; try { @@ -17,6 +18,7 @@ export class IpfsService { method: 'get', url: url, responseType: 'stream', + timeout: 60000, }); await pipeline(response.data, fs.createWriteStream(outputPath)); diff --git a/desci-media-isolated/src/services/thumbnails.ts b/desci-media-isolated/src/services/thumbnails.ts index a3b9fff07..91f5bc92f 100644 --- a/desci-media-isolated/src/services/thumbnails.ts +++ b/desci-media-isolated/src/services/thumbnails.ts @@ -10,6 +10,8 @@ const THUMBNAIL_DIMENSIONS = { // width: 220, height: 300, keepAspect: true, + quality: '100', + background: 'white', }; const __filename = fileURLToPath(import.meta.url); diff --git a/desci-server/src/controllers/proxy/ipfsReadGateway.ts b/desci-server/src/controllers/proxy/ipfsReadGateway.ts new file mode 100644 index 000000000..a0766c2cc --- /dev/null +++ b/desci-server/src/controllers/proxy/ipfsReadGateway.ts @@ -0,0 +1,36 @@ +import axios from 'axios'; +import { Request, Response } from 'express'; + +import { logger as parentLogger } from '../../logger.js'; + +/** + * Proxy for the read only IPFS gateway, to allow the isolated media server to access IPFS content, without writability. + */ +export const ipfsReadGatewayProxy = async (req: Request, res: Response) => { + debugger; + try { + const logger = parentLogger.child({ + module: 'PROXY::ipfsReadGatewayProxyController', + cid: req.params.cid, + }); + const { cid } = req.params; + if (!process.env.IPFS_READ_ONLY_GATEWAY_SERVER) { + logger.error('IPFS_READ_ONLY_GATEWAY_SERVER is not defined in environment variables'); + return res.status(500).send('Unable to connect to IPFS gateway'); + } + const url = `${process.env.IPFS_READ_ONLY_GATEWAY_SERVER}/${cid}`; + + // Forward the request to the IPFS gateway + const response = await axios.get(url, { responseType: 'stream' }); + + // Forward headers + res.set('Content-Type', response.headers['content-type']); + + // Stream the response back to the client + response.data.pipe(res); + } catch (error) { + console.error('Error forwarding IPFS request:', error); + return res.status(500).send('Error forwarding IPFS request'); + } + return res.status(200); +}; diff --git a/desci-server/src/routes/v1/data.ts b/desci-server/src/routes/v1/data.ts index c4057ed32..5ab9f4a68 100644 --- a/desci-server/src/routes/v1/data.ts +++ b/desci-server/src/routes/v1/data.ts @@ -8,8 +8,8 @@ import { pubTree, retrieveTree, deleteData, update, renameData } from '../../con import { moveData } from '../../controllers/data/move.js'; import { updateExternalCid } from '../../controllers/data/updateExternalCid.js'; import { logger } from '../../logger.js'; -import { ensureNodeAccess, ensureWriteAccessCheck } from '../../middleware/authorisation.js'; import { attachUser } from '../../middleware/attachUser.js'; +import { ensureNodeAccess, ensureWriteAccessCheck } from '../../middleware/authorisation.js'; import { ensureUser } from '../../middleware/permissions.js'; import { isS3Configured, s3Client } from '../../services/s3.js'; diff --git a/desci-server/src/routes/v1/index.ts b/desci-server/src/routes/v1/index.ts index 57caba27d..89fc2c568 100755 --- a/desci-server/src/routes/v1/index.ts +++ b/desci-server/src/routes/v1/index.ts @@ -4,6 +4,7 @@ import { generateNonce } from 'siwe'; import { prisma } from '../../client.js'; import { queryResearchFields } from '../../controllers/data/index.js'; import { queryRor } from '../../controllers/proxy/index.js'; +import { ipfsReadGatewayProxy } from '../../controllers/proxy/ipfsReadGateway.js'; import { nft } from '../../controllers/raw/nft.js'; import { ensureUser } from '../../middleware/permissions.js'; @@ -53,5 +54,6 @@ router.get('/nft/:id', nft); router.use('/referral', referral); router.get('/researchFields', [ensureUser], queryResearchFields); router.get('/ror', [ensureUser], queryRor); +router.get('/ipfs/:cid', ipfsReadGatewayProxy); export default router; From ff689c8719727fdb119c8d11d01b24838247d233 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Sat, 24 Feb 2024 04:02:32 +0700 Subject: [PATCH 19/42] remove links from thumbnail gen list --- desci-server/src/controllers/data/delete.ts | 1 - desci-server/src/controllers/nodes/thumbnails.ts | 11 ++++++++++- desci-server/src/controllers/proxy/ipfsReadGateway.ts | 1 - desci-server/src/services/Thumbnails.ts | 11 +++++------ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/desci-server/src/controllers/data/delete.ts b/desci-server/src/controllers/data/delete.ts index e65adadad..0bfc8a1d1 100644 --- a/desci-server/src/controllers/data/delete.ts +++ b/desci-server/src/controllers/data/delete.ts @@ -50,7 +50,6 @@ export const deleteData = async (req: Request, res: Response<DeleteResponse | Er /** * Remove draft node tree entries, add them to the cid prune list */ - // debugger; const entriesToDelete = await prisma.draftNodeTree.findMany({ where: { nodeId: node.id, diff --git a/desci-server/src/controllers/nodes/thumbnails.ts b/desci-server/src/controllers/nodes/thumbnails.ts index 6ae6384b8..09180e36c 100644 --- a/desci-server/src/controllers/nodes/thumbnails.ts +++ b/desci-server/src/controllers/nodes/thumbnails.ts @@ -2,6 +2,7 @@ import type { Request, Response, NextFunction } from 'express'; import { prisma } from '../../client.js'; import { NodeUuid } from '../../internal.js'; +import { logger as parentLogger } from '../../logger.js'; import { type ThumbnailMap, thumbnailsService } from '../../services/Thumbnails.js'; import { ensureUuidEndsWithDot } from '../../utils.js'; @@ -34,6 +35,14 @@ export const thumbnails = async ( const user = (req as any).user; const { uuid, manifestCid } = req.params; + const logger = parentLogger.child({ + module: 'NODES::Thumbnails', + uuid, + manifestCid, + userId: user.id, + }); + logger.trace({ fn: 'Retrieving thumbnails' }); + if (!uuid) return res.status(400).json({ ok: false, error: 'UUID is required.' }); if (!user && !manifestCid) { @@ -52,7 +61,7 @@ export const thumbnails = async ( if (!node) return res.status(401).json({ ok: false, error: 'Unauthorized' }); } - + // debugger; const thumbnailMap = await thumbnailsService.getThumbnailsForNode({ uuid: uuid as NodeUuid, manifestCid }); return res.status(200).json({ ok: true, thumbnailMap }); diff --git a/desci-server/src/controllers/proxy/ipfsReadGateway.ts b/desci-server/src/controllers/proxy/ipfsReadGateway.ts index a0766c2cc..350fc8610 100644 --- a/desci-server/src/controllers/proxy/ipfsReadGateway.ts +++ b/desci-server/src/controllers/proxy/ipfsReadGateway.ts @@ -7,7 +7,6 @@ import { logger as parentLogger } from '../../logger.js'; * Proxy for the read only IPFS gateway, to allow the isolated media server to access IPFS content, without writability. */ export const ipfsReadGatewayProxy = async (req: Request, res: Response) => { - debugger; try { const logger = parentLogger.child({ module: 'PROXY::ipfsReadGatewayProxyController', diff --git a/desci-server/src/services/Thumbnails.ts b/desci-server/src/services/Thumbnails.ts index d1f3314a9..18042cb42 100644 --- a/desci-server/src/services/Thumbnails.ts +++ b/desci-server/src/services/Thumbnails.ts @@ -8,6 +8,7 @@ import { getManifestByCid, getManifestFromNode, pinNewFiles } from './data/proce import { pinFile } from './ipfs.js'; import { NodeUuid, getLatestManifestFromNode } from './manifestRepo.js'; import repoService from './repoService.js'; +import { ResearchObjectComponentType } from '@desci-labs/desci-models'; const logger = parentLogger.child({ module: 'Services::Thumbnails', @@ -47,13 +48,11 @@ export class ThumbnailsService { const pinnedComponents = manifest?.components?.filter((c) => c.starred); // Determined by the file extension (can't generate thumbnails for files without extensions) - const fileComponents = pinnedComponents?.filter((c) => c.payload.path.split('/').pop().includes('.')); + const fileComponents = pinnedComponents?.filter( + (c) => c.payload.path.split('/').pop().includes('.') && c.type !== ResearchObjectComponentType.LINK, + ); const fileComponentCids = fileComponents?.map((c) => c.payload.cid || c.payload.url); - // const fileComponentCidMap = fileComponents.reduce((map, comp) => { - // const key = comp.payload.cid || comp.payload.url; - // map[key] = comp; - // return map; - // }, {}); + if (!fileComponents) return {}; const thumbnailsToGenerate: Record<CidString, FileName> = fileComponents?.reduce((map, comp) => { From 740e31e497084c96cec65efcc364664a9df07314 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 03:53:05 +0700 Subject: [PATCH 20/42] improve thumbnail gen logic to not fail on a single odd extension --- desci-server/src/services/Thumbnails.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/desci-server/src/services/Thumbnails.ts b/desci-server/src/services/Thumbnails.ts index 18042cb42..f868e2229 100644 --- a/desci-server/src/services/Thumbnails.ts +++ b/desci-server/src/services/Thumbnails.ts @@ -1,3 +1,4 @@ +import { ResearchObjectComponentType } from '@desci-labs/desci-models'; import axios from 'axios'; import { prisma } from '../client.js'; @@ -7,8 +8,6 @@ import { ensureUuidEndsWithDot } from '../utils.js'; import { getManifestByCid, getManifestFromNode, pinNewFiles } from './data/processing.js'; import { pinFile } from './ipfs.js'; import { NodeUuid, getLatestManifestFromNode } from './manifestRepo.js'; -import repoService from './repoService.js'; -import { ResearchObjectComponentType } from '@desci-labs/desci-models'; const logger = parentLogger.child({ module: 'Services::Thumbnails', @@ -79,7 +78,7 @@ export class ThumbnailsService { } // Generate thumbnails for the ones that don't exist - const generatedThumbnails = await Promise.all( + const generatedThumbnails = await Promise.allSettled( Object.entries(thumbnailsToGenerate).map(([cid, fileName]) => this.generateThumbnail(uuid, cid, fileName, HEIGHT_PX), ), @@ -87,7 +86,9 @@ export class ThumbnailsService { // Add the newly generated ones to the thumbnail map generatedThumbnails.forEach((newThumb) => { - thumbnailMap[newThumb.componentCid] = { [HEIGHT_PX]: newThumb.thumbnailCid }; + if (newThumb.status === 'fulfilled' && 'componentCid' in newThumb.value) { + thumbnailMap[newThumb.value.componentCid] = { [HEIGHT_PX]: newThumb.value.thumbnailCid }; + } }); return thumbnailMap; From f11140ba2d664536c867d16da1a5304fc4528827 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 04:31:52 +0700 Subject: [PATCH 21/42] add gh actions image building config, adjust k8s configs for isolated media server --- .../build-isolated-media-server.yaml | 119 ++++++++++++++++++ .../kubernetes/deployment.yaml | 36 ++++++ .../kubernetes/deployment_dev.yaml | 14 +-- 3 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/build-isolated-media-server.yaml create mode 100644 desci-media-isolated/kubernetes/deployment.yaml diff --git a/.github/workflows/build-isolated-media-server.yaml b/.github/workflows/build-isolated-media-server.yaml new file mode 100644 index 000000000..76e49f398 --- /dev/null +++ b/.github/workflows/build-isolated-media-server.yaml @@ -0,0 +1,119 @@ +# build.yml +on: + push: + paths: + - .github/workflows/** + - desci-media-isolated/** + - Dockerfile + branches: # array of glob patterns matching against refs/heads. Optional; defaults to all + - main # triggers on pushes that contain changes + - develop + + # TODO: add demo env + +name: Build desci-media-isolated + +# https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html +env: + AWS_DEFAULT_REGION: us-east-2 + AWS_DEFAULT_OUTPUT: json + AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + CONTAINER_IMAGE: desci-media-isolated + DOCKER_BUILDKIT: 1 + +jobs: + build-and-push: + name: Build and deploy + runs-on: ubuntu-latest + steps: + - uses: hashicorp/setup-terraform@v1 + - name: Checkout + uses: actions/checkout@master + + # Add steps here like linting, testing, minification, etc. + - id: install-aws-cli + uses: unfor19/install-aws-cli-action@v1 + with: + version: 1 + + - uses: prepor/action-aws-iam-authenticator@master + - run: aws-iam-authenticator version + + - name: Install Kubectl + run: | + #$(curl -Ls https://dl.k8s.io/release/stable.txt) + version=v1.23.6 + echo "using kubectl@$version" + curl -sLO "https://dl.k8s.io/release/$version/bin/linux/amd64/kubectl" -o kubectl + chmod +x kubectl + mv kubectl /usr/local/bin + mkdir $HOME/.kube + sudo apt-get update + sudo apt-get install less + echo ${{ secrets.KUBE_CONFIG_DATA }} | base64 --decode > $HOME/.kube/config + aws sts get-caller-identity + kubectl describe deployments + + - name: Build and tag the image (DEV) + if: github.ref == 'refs/heads/develop' + run: | + # Build and tag the image + docker build \ + --target production -t $CONTAINER_IMAGE-dev:production \ + --target production -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE-dev \ + ./desci-media-isolated + + - name: Build and tag the image (PROD) + if: github.ref == 'refs/heads/main' + run: | + # Build and tag the image + docker build \ + --target production -t $CONTAINER_IMAGE:production \ + --target production -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE \ + ./desci-media-isolated + + # Add additional steps here like scanning of image + + # Only push to registry on master + - name: Push (DEV) + if: github.ref == 'refs/heads/develop' + run: | + # Push image to AWS ECR + aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com + docker tag $CONTAINER_IMAGE-dev:production $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE-dev:${{ github.sha }} + docker tag $CONTAINER_IMAGE-dev:production $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE-dev:production + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE-dev:${{ github.sha }} + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE-dev:production + + - name: Push (PROD) + if: github.ref == 'refs/heads/main' + run: | + # Push image to AWS ECR + aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com + docker tag $CONTAINER_IMAGE:production $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:${{ github.sha }} + docker tag $CONTAINER_IMAGE:production $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:production + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:${{ github.sha }} + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:production + + - name: Deploy to EKS (DEV) + # uses: steebchen/kubectl@v2.0.0 + if: github.ref == 'refs/heads/develop' + run: | # defaults to latest kubectl binary version + kubectl set image deployment/desci-media-isolated-dev desci-media-isolated-dev=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE-dev:${{ github.sha }} --record + + - name: Deploy to EKS (PROD) + if: github.ref == 'refs/heads/main' + run: | # defaults to latest kubectl binary version + kubectl set image deployment/desci-media-isolated desci-media-isolated=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:${{ github.sha }} --record + + - name: Verify EKS Deployment (DEV) + if: github.ref == 'refs/heads/develop' + run: | + kubectl rollout status deployment/desci-media-isolated-dev + + - name: Verify EKS Deployment (PROD) + if: github.ref == 'refs/heads/main' + run: | + kubectl rollout status deployment/desci-media-isolated diff --git a/desci-media-isolated/kubernetes/deployment.yaml b/desci-media-isolated/kubernetes/deployment.yaml new file mode 100644 index 000000000..7190a322d --- /dev/null +++ b/desci-media-isolated/kubernetes/deployment.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: desci-media-isolated + labels: + App: DesciMediaIsolated +spec: + replicas: 1 + revisionHistoryLimit: 2 + selector: + matchLabels: + App: DesciMediaIsolated + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + App: DesciMediaIsolated + spec: + containers: + - image: 523044037273.dkr.ecr.us-east-2.amazonaws.com/desci-media-isolated:production + name: desci-media-isolated + ports: + - containerPort: 7771 + name: media_isolated + resources: + limits: + cpu: '0.5' + memory: 2Gi + requests: + cpu: 250m + memory: 1Gi + serviceAccountName: 'default' diff --git a/desci-media-isolated/kubernetes/deployment_dev.yaml b/desci-media-isolated/kubernetes/deployment_dev.yaml index 7887ea5b0..b946ef574 100644 --- a/desci-media-isolated/kubernetes/deployment_dev.yaml +++ b/desci-media-isolated/kubernetes/deployment_dev.yaml @@ -1,15 +1,15 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: nodes-isolated-media-server-dev + name: desci-media-isolated-dev labels: - App: NodesIsolatedMediaServerDev + App: DesciMediaIsolatedDev spec: replicas: 1 revisionHistoryLimit: 2 selector: matchLabels: - App: NodesIsolatedMediaServerDev + App: DesciMediaIsolatedDev strategy: rollingUpdate: maxSurge: 25% @@ -18,14 +18,14 @@ spec: template: metadata: labels: - App: NodesIsolatedMediaServerDev + App: DesciMediaIsolatedDev spec: containers: - - image: {CHANGE}.dkr.ecr.us-east-2.amazonaws.com/nodes-isolated-media-server-dev:production - name: nodes-isolated-media-server-dev + - image: 523044037273.dkr.ecr.us-east-2.amazonaws.com/desci-media-isolated-dev:production + name: desci-media-isolated-dev ports: - containerPort: 7771 - name: media_isolated + name: media_isolated_dev resources: limits: cpu: '0.5' From a6c3f4977593bbbae031689097da3ae635090cc4 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 04:40:14 +0700 Subject: [PATCH 22/42] adjust k8s network policy configs for isolated media server --- .../kubernetes/network_policies-dev.yaml | 34 +++++++++++++++++++ .../kubernetes/network_policies.yaml | 16 ++++++--- 2 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 desci-media-isolated/kubernetes/network_policies-dev.yaml diff --git a/desci-media-isolated/kubernetes/network_policies-dev.yaml b/desci-media-isolated/kubernetes/network_policies-dev.yaml new file mode 100644 index 000000000..a23e838c7 --- /dev/null +++ b/desci-media-isolated/kubernetes/network_policies-dev.yaml @@ -0,0 +1,34 @@ +# Enable ingress from DesciServerDev only +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: allow-backend-pod-to-isolated-media-server-dev-comms +spec: + podSelector: + matchLabels: + App: DesciMediaIsolatedDev + ingress: + - from: + - podSelector: + matchLabels: + allow-from: 'DesciServerDev' + policyTypes: + - Ingress + + +# Enable egress to DesciServerDev only +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: allow-egress-to-desciserverdev-only +spec: + podSelector: + matchLabels: + App: DesciMediaIsolatedDev + egress: + - to: + - podSelector: + matchLabels: + allow-from: 'DesciServerDev' + policyTypes: + - Egress diff --git a/desci-media-isolated/kubernetes/network_policies.yaml b/desci-media-isolated/kubernetes/network_policies.yaml index 8b2819127..7bc77dff0 100644 --- a/desci-media-isolated/kubernetes/network_policies.yaml +++ b/desci-media-isolated/kubernetes/network_policies.yaml @@ -1,3 +1,4 @@ +# Enable ingress from DesciServer only kind: NetworkPolicy apiVersion: networking.k8s.io/v1 metadata: @@ -5,24 +6,29 @@ metadata: spec: podSelector: matchLabels: - App: NodesIsolatedMediaServerDev + App: DesciMediaIsolated ingress: - from: - podSelector: matchLabels: - allow-from: 'DesciServerDev' + allow-from: 'DesciServer' policyTypes: - Ingress +# Enable egress to DesciServer only kind: NetworkPolicy apiVersion: networking.k8s.io/v1 metadata: - name: deny-egress-from-isolated-media-server + name: allow-egress-to-desciserver-only spec: podSelector: matchLabels: - App: NodesIsolatedMediaServerDev - egress: [] + App: DesciMediaIsolated + egress: + - to: + - podSelector: + matchLabels: + allow-from: 'DesciServer' policyTypes: - Egress From cbdda118cc7b2f257a6d7c88011db9de35f45923 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 04:41:55 +0700 Subject: [PATCH 23/42] clean --- desci-media-isolated/.env.example | 2 -- 1 file changed, 2 deletions(-) diff --git a/desci-media-isolated/.env.example b/desci-media-isolated/.env.example index 01693de22..d61e65e5f 100644 --- a/desci-media-isolated/.env.example +++ b/desci-media-isolated/.env.example @@ -2,6 +2,4 @@ NODE_ENV=development PORT=7771 -# IPFS_GATEWAY=https://ipfs.desci.com/ipfs - IPFS_GATEWAY=http://host.docker.internal:5420/v1/ipfs \ No newline at end of file From 4c96c5e8b0878e9e86a029956fa9f4c08d73ef08 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 14:25:26 +0700 Subject: [PATCH 24/42] auto gen .env in dev run if not present --- desci-media-isolated/Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/desci-media-isolated/Dockerfile b/desci-media-isolated/Dockerfile index 9a07b49a8..2256172d2 100644 --- a/desci-media-isolated/Dockerfile +++ b/desci-media-isolated/Dockerfile @@ -26,8 +26,12 @@ RUN --mount=type=cache,target=/usr/src/app/.npm \ npm set cache /usr/src/app/.npm && \ npm install + COPY . . +# Auto create .env from .env.example if it doesn't exist +RUN if [ ! -f .env ]; then cp .env.example .env; fi + # Expose debugger port EXPOSE 9777 From ff60247ea0f4668425cc5652c580e3e68c4b102f Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 14:58:08 +0700 Subject: [PATCH 25/42] prevent dotenv import issues --- desci-media-isolated/package.json | 3 ++- desci-media-isolated/src/index.ts | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/desci-media-isolated/package.json b/desci-media-isolated/package.json index 8104c1fc7..f888fb47c 100644 --- a/desci-media-isolated/package.json +++ b/desci-media-isolated/package.json @@ -26,6 +26,7 @@ "@typescript-eslint/parser": "^7.0.1", "eslint": "^8.56.0", "ts-node-dev": "^2.0.0", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "dotenv": "^16.4.5" } } diff --git a/desci-media-isolated/src/index.ts b/desci-media-isolated/src/index.ts index abbed5727..dfec27a1a 100644 --- a/desci-media-isolated/src/index.ts +++ b/desci-media-isolated/src/index.ts @@ -1,9 +1,11 @@ -import 'dotenv/config'; +import dotenv from 'dotenv'; import express from 'express'; import helmet from 'helmet'; import { errorHandler } from './middleware/errorHandler.js'; import routes from './routes/index.js'; +dotenv.config(); + const app = express(); console.log('process.env.PORT:', process.env.PORT); const PORT = process.env.PORT || 7771; From 40ad6c642e66b9eb3389dd71fd6d87fc468e0454 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:41:02 +0700 Subject: [PATCH 26/42] adjust image to fix prod issues --- desci-media-isolated/.dockerignore | 2 +- desci-media-isolated/Dockerfile | 14 +- desci-media-isolated/package-lock.json | 348 +------------------------ desci-media-isolated/package.json | 1 - 4 files changed, 15 insertions(+), 350 deletions(-) diff --git a/desci-media-isolated/.dockerignore b/desci-media-isolated/.dockerignore index 0af6aafe9..4e6088d61 100755 --- a/desci-media-isolated/.dockerignore +++ b/desci-media-isolated/.dockerignore @@ -3,4 +3,4 @@ database dist node_modules .git -# .env \ No newline at end of file +# .env diff --git a/desci-media-isolated/Dockerfile b/desci-media-isolated/Dockerfile index 2256172d2..83a38259e 100644 --- a/desci-media-isolated/Dockerfile +++ b/desci-media-isolated/Dockerfile @@ -18,6 +18,7 @@ RUN chown -R 1001:0 /.npm # App Setup WORKDIR /usr/src/app +COPY tsconfig.json . COPY package*.json ./ FROM base as dev @@ -39,16 +40,21 @@ CMD ["dumb-init", "npx", "tsx","watch", "--inspect=0.0.0.0:9777", "src/index.ts" FROM base as production # Cache mounts for faster builds, prod env for better express perf -ENV NODE_ENV production RUN --mount=type=cache,target=/usr/src/app/.npm \ npm set cache /usr/src/app/.npm && \ - npm ci --only=production - + npm install +ENV NODE_ENV production # 'node' user is created by the node image, prevent perm issues, run with reduced privs +RUN mkdir -p /usr/src/app/dist && chown node:node /usr/src/app/dist USER node COPY --chown=node:node ./src/ . -RUN npm run build +USER root +RUN chown -R node:node /usr/src/app +USER node +RUN npm run build && \ + npm prune --production + CMD ["dumb-init", "node", "dist/index.js"] diff --git a/desci-media-isolated/package-lock.json b/desci-media-isolated/package-lock.json index 32d011c98..fd21ae5a2 100644 --- a/desci-media-isolated/package-lock.json +++ b/desci-media-isolated/package-lock.json @@ -14,12 +14,14 @@ "express": "^4.18.2", "filepreview_ts": "^1.0.0", "helmet": "^7.1.0", - "tsx": "^4.7.1" + "tsx": "^4.7.1", + "typescript": "^5.3.3" }, "devDependencies": { "@types/express": "^4.17.21", "@typescript-eslint/eslint-plugin": "^7.0.1", "@typescript-eslint/parser": "^7.0.1", + "dotenv": "^16.4.5", "eslint": "^8.56.0", "ts-node-dev": "^2.0.0", "typescript": "^5.3.3" @@ -46,246 +48,6 @@ "node": ">=12" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", - "cpu": [ - "loong64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", - "cpu": [ - "mips64el" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/linux-x64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", @@ -301,96 +63,6 @@ "node": ">=12" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1511,6 +1183,7 @@ "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true, "engines": { "node": ">=12" }, @@ -2033,19 +1706,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", diff --git a/desci-media-isolated/package.json b/desci-media-isolated/package.json index f888fb47c..d94ece43d 100644 --- a/desci-media-isolated/package.json +++ b/desci-media-isolated/package.json @@ -14,7 +14,6 @@ "license": "ISC", "dependencies": { "axios": "^1.6.7", - "dotenv": "^16.4.5", "express": "^4.18.2", "filepreview_ts": "^1.0.0", "helmet": "^7.1.0", From cbc846dce5fa4ca4fcc0558c4c4fefc157e875e0 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:51:04 +0700 Subject: [PATCH 27/42] hardcode envs in for k8s deployment --- desci-media-isolated/kubernetes/deployment.yaml | 5 +++++ desci-media-isolated/kubernetes/deployment_dev.yaml | 5 +++++ desci-media-isolated/package-lock.json | 5 +---- desci-media-isolated/package.json | 6 +++--- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/desci-media-isolated/kubernetes/deployment.yaml b/desci-media-isolated/kubernetes/deployment.yaml index 7190a322d..32c08e2b9 100644 --- a/desci-media-isolated/kubernetes/deployment.yaml +++ b/desci-media-isolated/kubernetes/deployment.yaml @@ -26,6 +26,11 @@ spec: ports: - containerPort: 7771 name: media_isolated + env: + - name: PORT + value: '7771' + - name: IPFS_GATEWAY + value: 'http://host.docker.internal:5420/v1/ipfs' resources: limits: cpu: '0.5' diff --git a/desci-media-isolated/kubernetes/deployment_dev.yaml b/desci-media-isolated/kubernetes/deployment_dev.yaml index b946ef574..519e2f5c3 100644 --- a/desci-media-isolated/kubernetes/deployment_dev.yaml +++ b/desci-media-isolated/kubernetes/deployment_dev.yaml @@ -26,6 +26,11 @@ spec: ports: - containerPort: 7771 name: media_isolated_dev + env: + - name: PORT + value: '7771' + - name: IPFS_GATEWAY + value: 'http://host.docker.internal:5420/v1/ipfs' resources: limits: cpu: '0.5' diff --git a/desci-media-isolated/package-lock.json b/desci-media-isolated/package-lock.json index fd21ae5a2..1a195e658 100644 --- a/desci-media-isolated/package-lock.json +++ b/desci-media-isolated/package-lock.json @@ -14,14 +14,12 @@ "express": "^4.18.2", "filepreview_ts": "^1.0.0", "helmet": "^7.1.0", - "tsx": "^4.7.1", - "typescript": "^5.3.3" + "tsx": "^4.7.1" }, "devDependencies": { "@types/express": "^4.17.21", "@typescript-eslint/eslint-plugin": "^7.0.1", "@typescript-eslint/parser": "^7.0.1", - "dotenv": "^16.4.5", "eslint": "^8.56.0", "ts-node-dev": "^2.0.0", "typescript": "^5.3.3" @@ -1183,7 +1181,6 @@ "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "dev": true, "engines": { "node": ">=12" }, diff --git a/desci-media-isolated/package.json b/desci-media-isolated/package.json index d94ece43d..2648360dd 100644 --- a/desci-media-isolated/package.json +++ b/desci-media-isolated/package.json @@ -17,7 +17,8 @@ "express": "^4.18.2", "filepreview_ts": "^1.0.0", "helmet": "^7.1.0", - "tsx": "^4.7.1" + "tsx": "^4.7.1", + "dotenv": "^16.4.5" }, "devDependencies": { "@types/express": "^4.17.21", @@ -25,7 +26,6 @@ "@typescript-eslint/parser": "^7.0.1", "eslint": "^8.56.0", "ts-node-dev": "^2.0.0", - "typescript": "^5.3.3", - "dotenv": "^16.4.5" + "typescript": "^5.3.3" } } From a0b7b9fb226c9b885e1149c35091717603866c75 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 16:35:42 +0700 Subject: [PATCH 28/42] generate .env in makefile for isolated-media --- desci-media-isolated/src/services/ipfs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desci-media-isolated/src/services/ipfs.ts b/desci-media-isolated/src/services/ipfs.ts index bbf4413ce..e1adc1457 100644 --- a/desci-media-isolated/src/services/ipfs.ts +++ b/desci-media-isolated/src/services/ipfs.ts @@ -10,7 +10,7 @@ export class IpfsService { console.log('IPFS_GATEWAY:', IPFS_GATEWAY); throw new IpfsConfigurationError('process.env.IPFS_GATEWAY is not defined in environment variables'); } - debugger; + // debugger; const url = `${IPFS_GATEWAY}/${cid}`; try { From a51c896efd75ce6c11b27411096513abc7e85d36 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 16:39:33 +0700 Subject: [PATCH 29/42] push --- Makefile | 3 +++ desci-server/src/controllers/nodes/thumbnails.ts | 2 +- desci-server/src/services/Thumbnails.ts | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9de709eb6..958c0f944 100644 --- a/Makefile +++ b/Makefile @@ -42,3 +42,6 @@ nodes-media/.env: desci-repo/.env: if [ ! -f desci-repo/.env ]; then cp desci-repo/.env.example desci-repo/.env; fi +desci-media-isolated/.env: + if [ ! -f desci-media-isolated/.env ]; then cp desci-media-isolated/.env.example desci-media-isolated/.env; fi + diff --git a/desci-server/src/controllers/nodes/thumbnails.ts b/desci-server/src/controllers/nodes/thumbnails.ts index 09180e36c..b3969d385 100644 --- a/desci-server/src/controllers/nodes/thumbnails.ts +++ b/desci-server/src/controllers/nodes/thumbnails.ts @@ -34,7 +34,7 @@ export const thumbnails = async ( ) => { const user = (req as any).user; const { uuid, manifestCid } = req.params; - + // debugger; const logger = parentLogger.child({ module: 'NODES::Thumbnails', uuid, diff --git a/desci-server/src/services/Thumbnails.ts b/desci-server/src/services/Thumbnails.ts index f868e2229..5c3b4f4c8 100644 --- a/desci-server/src/services/Thumbnails.ts +++ b/desci-server/src/services/Thumbnails.ts @@ -105,6 +105,7 @@ export class ThumbnailsService { return null; } // Generate the thumbnail + // debugger; const thumbnailStream = await axios.post( `${process.env.ISOLATED_MEDIA_SERVER_URL}/v1/thumbnails?height${heightPx}`, { cid: cid, fileName: componentFileName }, @@ -112,7 +113,6 @@ export class ThumbnailsService { responseType: 'stream', }, ); - // Save it on IPFS const pinned = await pinFile(thumbnailStream.data); // Save it to the database From 17c4e57145a0cf00ea173720f98ca0dbce95b2a3 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 19:09:58 +0700 Subject: [PATCH 30/42] fixes --- desci-media-isolated/Dockerfile | 4 ++-- desci-media-isolated/src/services/ipfs.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/desci-media-isolated/Dockerfile b/desci-media-isolated/Dockerfile index 83a38259e..b6c2f853f 100644 --- a/desci-media-isolated/Dockerfile +++ b/desci-media-isolated/Dockerfile @@ -12,8 +12,8 @@ RUN apt-get update && apt-get install -y dumb-init ghostscript unoconv ffmpeg im RUN sed -i '/<policy domain="coder" rights="none" pattern="PDF" \/>/c\<policy domain="Undefined" rights="read|write" pattern="PDF" \/>' /etc/ImageMagick-6/policy.xml # NPM Permission Fix -RUN mkdir -p /.npm -RUN chown -R 1001:0 /.npm +# RUN mkdir -p /.npm +# RUN chown -R 1001:0 /.npm # App Setup WORKDIR /usr/src/app diff --git a/desci-media-isolated/src/services/ipfs.ts b/desci-media-isolated/src/services/ipfs.ts index e1adc1457..e8ee8a968 100644 --- a/desci-media-isolated/src/services/ipfs.ts +++ b/desci-media-isolated/src/services/ipfs.ts @@ -1,13 +1,13 @@ import axios from 'axios'; import fs from 'fs'; import { pipeline } from 'stream/promises'; -import { IPFS_GATEWAY } from '../config/index.js'; import { IpfsConfigurationError } from '../utils/customErrors.js'; +const IPFS_GATEWAY = process.env.IPFS_GATEWAY; export class IpfsService { static async saveFile(cid: string, outputPath: string) { if (!IPFS_GATEWAY) { - console.log('IPFS_GATEWAY:', IPFS_GATEWAY); + console.log('IPFS_GATEWAY:', process.env.IPFS_GATEWAY); throw new IpfsConfigurationError('process.env.IPFS_GATEWAY is not defined in environment variables'); } // debugger; From a13dd3af20842536928cd35b4743b3b6dad8506c Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 23:11:07 +0700 Subject: [PATCH 31/42] fix dev --- .../scripts/containerInitDev.sh | 20 +++++++++++++++++++ .../scripts/containerInitProd.sh | 7 +++++++ 2 files changed, 27 insertions(+) create mode 100755 desci-media-isolated/scripts/containerInitDev.sh create mode 100755 desci-media-isolated/scripts/containerInitProd.sh diff --git a/desci-media-isolated/scripts/containerInitDev.sh b/desci-media-isolated/scripts/containerInitDev.sh new file mode 100755 index 000000000..904d86628 --- /dev/null +++ b/desci-media-isolated/scripts/containerInitDev.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Check if .env file does not exist and auto generate it from .example.env +if [ ! -f /usr/src/app/.env ]; then + cp /usr/src/app/.env.example /usr/src/app/.env +fi + +# Ensure temp directories exist +mkdir -p /usr/src/app/.temp/files /usr/src/app/.temp/thumbnails + + +# Check if node_modules directory doesn't exist and run npm install if necessary +if [ ! -d "/usr/src/app/node_modules" ]; then + echo "node_modules not found, running npm install..." + cd /usr/src/app + npm install +fi + +# Execute the main container command +exec "$@" diff --git a/desci-media-isolated/scripts/containerInitProd.sh b/desci-media-isolated/scripts/containerInitProd.sh new file mode 100755 index 000000000..d5711244a --- /dev/null +++ b/desci-media-isolated/scripts/containerInitProd.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Ensure temp directories exist +mkdir -p /usr/src/app/.temp/files /usr/src/app/.temp/thumbnails + +# Execute the main container command +exec "$@" From 03d539a2715b9778cc555b39d0f273812344a7c7 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 23:22:50 +0700 Subject: [PATCH 32/42] readd script to dockerfile --- desci-media-isolated/Dockerfile | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/desci-media-isolated/Dockerfile b/desci-media-isolated/Dockerfile index b6c2f853f..98f4c3878 100644 --- a/desci-media-isolated/Dockerfile +++ b/desci-media-isolated/Dockerfile @@ -2,8 +2,6 @@ FROM docker.io/node:20.9.0 as base # Install dumb-init so we can use it as PID 1 -# RUN apk update && apk add --no-cache dumb-init -# RUN apk add --no-cache unoconv ffmpeg imagemagick curl RUN apt-get update && apt-get install -y dumb-init ghostscript unoconv ffmpeg imagemagick curl && \ rm -rf /var/lib/apt/lists/* @@ -11,9 +9,6 @@ RUN apt-get update && apt-get install -y dumb-init ghostscript unoconv ffmpeg im # Modify ImageMagick policy to allow PDF processing RUN sed -i '/<policy domain="coder" rights="none" pattern="PDF" \/>/c\<policy domain="Undefined" rights="read|write" pattern="PDF" \/>' /etc/ImageMagick-6/policy.xml -# NPM Permission Fix -# RUN mkdir -p /.npm -# RUN chown -R 1001:0 /.npm # App Setup WORKDIR /usr/src/app @@ -30,12 +25,10 @@ RUN --mount=type=cache,target=/usr/src/app/.npm \ COPY . . -# Auto create .env from .env.example if it doesn't exist -RUN if [ ! -f .env ]; then cp .env.example .env; fi - # Expose debugger port EXPOSE 9777 +ENTRYPOINT ["/usr/src/app/scripts/containerInitDev.sh"] CMD ["dumb-init", "npx", "tsx","watch", "--inspect=0.0.0.0:9777", "src/index.ts"] FROM base as production @@ -48,7 +41,7 @@ ENV NODE_ENV production # 'node' user is created by the node image, prevent perm issues, run with reduced privs RUN mkdir -p /usr/src/app/dist && chown node:node /usr/src/app/dist USER node -COPY --chown=node:node ./src/ . +COPY --chown=node:node ./src/ ./src/ USER root RUN chown -R node:node /usr/src/app USER node @@ -56,5 +49,5 @@ RUN npm run build && \ npm prune --production - +ENTRYPOINT ["/usr/src/app/scripts/containerInitProd.sh"] CMD ["dumb-init", "node", "dist/index.js"] From 286b517094ce160fef9c8ed776551bafd8bd1ffe Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Mon, 26 Feb 2024 23:35:00 +0700 Subject: [PATCH 33/42] readd scripts --- desci-media-isolated/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/desci-media-isolated/Dockerfile b/desci-media-isolated/Dockerfile index 98f4c3878..b3e598c5e 100644 --- a/desci-media-isolated/Dockerfile +++ b/desci-media-isolated/Dockerfile @@ -49,5 +49,6 @@ RUN npm run build && \ npm prune --production +COPY --chown=node:node ./scripts/containerInitProd.sh /usr/src/app/scripts/containerInitProd.sh ENTRYPOINT ["/usr/src/app/scripts/containerInitProd.sh"] CMD ["dumb-init", "node", "dist/index.js"] From b80336a6eb2ceb523e1aeb874ce690eb6423083a Mon Sep 17 00:00:00 2001 From: shadrach-tayo <shadrachtemitayo@gmail.com> Date: Mon, 26 Feb 2024 18:53:53 +0100 Subject: [PATCH 34/42] resolve node cover from radar api --- desci-server/src/controllers/communities/util.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/desci-server/src/controllers/communities/util.ts b/desci-server/src/controllers/communities/util.ts index 5e416eea2..657e6637e 100644 --- a/desci-server/src/controllers/communities/util.ts +++ b/desci-server/src/controllers/communities/util.ts @@ -15,13 +15,21 @@ export const resolveLatestNode = async (radar: Partial<NodeRadar>) => { uuid, isDeleted: false, }, + select: { + id: true, + manifestUrl: true, + ownerId: true, + uuid: true, + title: true, + NodeCover: true, + }, }); if (!discovery) { logger.warn({ uuid }, 'uuid not found'); } - const selectAttributes = ['manifestUrl', 'ownerId', 'title']; + const selectAttributes = ['manifestUrl', 'ownerId', 'title', 'NodeCover']; const node: Partial<Node & { versions: number }> = _.pick(discovery, selectAttributes); const publisedVersions = (await prisma.$queryRaw`SELECT * from "NodeVersion" where "nodeId" = ${discovery.id} AND "transactionId" IS NOT NULL ORDER BY "createdAt" DESC`) as NodeVersion[]; From dc4b56fb4524817d49acb3d5910e22deba4dd3f1 Mon Sep 17 00:00:00 2001 From: kadami <kadamidev@users.noreply.github.com> Date: Tue, 27 Feb 2024 01:25:33 +0700 Subject: [PATCH 35/42] fix envs --- desci-media-isolated/Dockerfile | 2 +- desci-media-isolated/package-lock.json | 12 ------------ desci-media-isolated/package.json | 5 ++--- desci-media-isolated/src/index.ts | 4 ++-- desci-media-isolated/src/services/thumbnails.ts | 2 +- 5 files changed, 6 insertions(+), 19 deletions(-) diff --git a/desci-media-isolated/Dockerfile b/desci-media-isolated/Dockerfile index b3e598c5e..49d31968c 100644 --- a/desci-media-isolated/Dockerfile +++ b/desci-media-isolated/Dockerfile @@ -29,7 +29,7 @@ COPY . . EXPOSE 9777 ENTRYPOINT ["/usr/src/app/scripts/containerInitDev.sh"] -CMD ["dumb-init", "npx", "tsx","watch", "--inspect=0.0.0.0:9777", "src/index.ts"] +CMD ["dumb-init", "npx", "tsx","watch", "--env-file=.env", "--inspect=0.0.0.0:9777", "src/index.ts"] FROM base as production # Cache mounts for faster builds, prod env for better express perf diff --git a/desci-media-isolated/package-lock.json b/desci-media-isolated/package-lock.json index 1a195e658..551d9f750 100644 --- a/desci-media-isolated/package-lock.json +++ b/desci-media-isolated/package-lock.json @@ -10,7 +10,6 @@ "license": "ISC", "dependencies": { "axios": "^1.6.7", - "dotenv": "^16.4.5", "express": "^4.18.2", "filepreview_ts": "^1.0.0", "helmet": "^7.1.0", @@ -1177,17 +1176,6 @@ "node": ">=6.0.0" } }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/dynamic-dedupe": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", diff --git a/desci-media-isolated/package.json b/desci-media-isolated/package.json index 2648360dd..75d956db8 100644 --- a/desci-media-isolated/package.json +++ b/desci-media-isolated/package.json @@ -6,7 +6,7 @@ "type": "module", "scripts": { "start": "node dist/index.js", - "dev": "tsx watch src/index.ts", + "dev": "tsx watch --env-file=.env src/index.ts", "build": "tsc" }, "keywords": [], @@ -17,8 +17,7 @@ "express": "^4.18.2", "filepreview_ts": "^1.0.0", "helmet": "^7.1.0", - "tsx": "^4.7.1", - "dotenv": "^16.4.5" + "tsx": "^4.7.1" }, "devDependencies": { "@types/express": "^4.17.21", diff --git a/desci-media-isolated/src/index.ts b/desci-media-isolated/src/index.ts index dfec27a1a..280ad2184 100644 --- a/desci-media-isolated/src/index.ts +++ b/desci-media-isolated/src/index.ts @@ -1,10 +1,10 @@ -import dotenv from 'dotenv'; +// import dotenv from 'dotenv'; import express from 'express'; import helmet from 'helmet'; import { errorHandler } from './middleware/errorHandler.js'; import routes from './routes/index.js'; -dotenv.config(); +// dotenv.config(); const app = express(); console.log('process.env.PORT:', process.env.PORT); diff --git a/desci-media-isolated/src/services/thumbnails.ts b/desci-media-isolated/src/services/thumbnails.ts index 91f5bc92f..2dae30b69 100644 --- a/desci-media-isolated/src/services/thumbnails.ts +++ b/desci-media-isolated/src/services/thumbnails.ts @@ -25,7 +25,7 @@ export class ThumbnailsService { const tempFilePath = path.join(BASE_TEMP_DIR, THUMBNAIL_FILES_DIR, `${cid + extension}`); const thumbnailPath = this.getThumbnailPath(cid); const exportPath = path.join(BASE_TEMP_DIR, THUMBNAIL_OUTPUT_DIR, thumbnailPath); - + // debugger; await IpfsService.saveFile(cid, tempFilePath); try { await generateAsync(tempFilePath, exportPath, { ...THUMBNAIL_DIMENSIONS, height: heightPx }); From 9a96d5cb855f5801cf0c56df0d84b1085069c8d6 Mon Sep 17 00:00:00 2001 From: Sina Iman <sina.iman@gmail.com> Date: Mon, 26 Feb 2024 11:38:26 -0700 Subject: [PATCH 36/42] kubeconfig edit --- .../kubernetes/deployment_dev.yaml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/desci-media-isolated/kubernetes/deployment_dev.yaml b/desci-media-isolated/kubernetes/deployment_dev.yaml index 519e2f5c3..fcf2b0ff8 100644 --- a/desci-media-isolated/kubernetes/deployment_dev.yaml +++ b/desci-media-isolated/kubernetes/deployment_dev.yaml @@ -1,3 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: desci-media-isolated-dev-service + labels: + App: DesciMediaIsolatedDev +spec: + type: ClusterIP + selector: + App: DesciMediaIsolatedDev + ports: + - name: api + port: 7771 + targetPort: 7771 +--- apiVersion: apps/v1 kind: Deployment metadata: @@ -30,7 +45,7 @@ spec: - name: PORT value: '7771' - name: IPFS_GATEWAY - value: 'http://host.docker.internal:5420/v1/ipfs' + value: 'https://ipfs.desci.com/ipfs' resources: limits: cpu: '0.5' From de8e92d09820b4d7d824a5829b2b99febc9f8189 Mon Sep 17 00:00:00 2001 From: Sina Iman <sina.iman@gmail.com> Date: Mon, 26 Feb 2024 11:39:22 -0700 Subject: [PATCH 37/42] Add env for desci-server --- desci-server/kubernetes/deployment_dev.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/desci-server/kubernetes/deployment_dev.yaml b/desci-server/kubernetes/deployment_dev.yaml index ee25fc863..d937ed4b2 100644 --- a/desci-server/kubernetes/deployment_dev.yaml +++ b/desci-server/kubernetes/deployment_dev.yaml @@ -74,6 +74,8 @@ spec: export ESTUARY_API_URL={{ .Data.ESTUARY_API_URL }} export REPO_SERVER_URL={{ .Data.REPO_SERVER_URL }} export REPO_SERVICE_SECRET_KEY={{ .Data.REPO_SERVICE_SECRET_KEY }} + export ISOLATED_MEDIA_SERVER_URL={{ .Data.ISOLATED_MEDIA_SERVER_URL }} + export IPFS_READ_ONLY_GATEWAY_SERVER_URL={{ .Data.IPFS_READ_ONLY_GATEWAY_SERVER_URL }} export DEBUG_TEST=0; echo "appfinish"; {{- end -}} From aee80c536807f95019b87e0247f9a3a1950a3cf6 Mon Sep 17 00:00:00 2001 From: Sina Iman <sina.iman@gmail.com> Date: Mon, 26 Feb 2024 11:51:42 -0700 Subject: [PATCH 38/42] trigger GH action --- .github/workflows/build-isolated-media-server.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-isolated-media-server.yaml b/.github/workflows/build-isolated-media-server.yaml index 76e49f398..94a40df05 100644 --- a/.github/workflows/build-isolated-media-server.yaml +++ b/.github/workflows/build-isolated-media-server.yaml @@ -11,7 +11,7 @@ on: # TODO: add demo env -name: Build desci-media-isolated +name: Build desci-isolated-media-server # https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html env: From 966b7a2c713fc720add386e8ec1f33971f8cc09a Mon Sep 17 00:00:00 2001 From: Sina Iman <sina.iman@gmail.com> Date: Mon, 26 Feb 2024 11:54:36 -0700 Subject: [PATCH 39/42] Enable manual trigger --- .github/workflows/build-isolated-media-server.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-isolated-media-server.yaml b/.github/workflows/build-isolated-media-server.yaml index 94a40df05..37c1b871e 100644 --- a/.github/workflows/build-isolated-media-server.yaml +++ b/.github/workflows/build-isolated-media-server.yaml @@ -1,5 +1,6 @@ # build.yml on: + workflow_dispatch: # This line enables manual triggers push: paths: - .github/workflows/** From a6ca005c7fbfc2673ace27edb975665f3cc235c5 Mon Sep 17 00:00:00 2001 From: Sina Iman <sina.iman@gmail.com> Date: Mon, 26 Feb 2024 13:05:04 -0700 Subject: [PATCH 40/42] fix port name --- desci-media-isolated/kubernetes/deployment_dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desci-media-isolated/kubernetes/deployment_dev.yaml b/desci-media-isolated/kubernetes/deployment_dev.yaml index fcf2b0ff8..812ea016b 100644 --- a/desci-media-isolated/kubernetes/deployment_dev.yaml +++ b/desci-media-isolated/kubernetes/deployment_dev.yaml @@ -40,7 +40,7 @@ spec: name: desci-media-isolated-dev ports: - containerPort: 7771 - name: media_isolated_dev + name: api env: - name: PORT value: '7771' From efba3f89655ed30ec7359885f1993766f35b2979 Mon Sep 17 00:00:00 2001 From: Sina Iman <sina.iman@gmail.com> Date: Mon, 26 Feb 2024 13:28:48 -0700 Subject: [PATCH 41/42] dont kill server --- desci-server/src/controllers/nodes/thumbnails.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desci-server/src/controllers/nodes/thumbnails.ts b/desci-server/src/controllers/nodes/thumbnails.ts index b3969d385..b9f2afe52 100644 --- a/desci-server/src/controllers/nodes/thumbnails.ts +++ b/desci-server/src/controllers/nodes/thumbnails.ts @@ -39,7 +39,7 @@ export const thumbnails = async ( module: 'NODES::Thumbnails', uuid, manifestCid, - userId: user.id, + userId: user?.id, }); logger.trace({ fn: 'Retrieving thumbnails' }); From e0bd39b997bde00d7f2120b6340e8defc3538db1 Mon Sep 17 00:00:00 2001 From: Sina Iman <sina.iman@gmail.com> Date: Mon, 26 Feb 2024 14:18:12 -0700 Subject: [PATCH 42/42] media config for prod --- .../build-isolated-media-server.yaml | 14 +++++----- .../kubernetes/deployment.yaml | 27 ++++++++++++++----- desci-server/kubernetes/deployment.yaml | 2 ++ 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-isolated-media-server.yaml b/.github/workflows/build-isolated-media-server.yaml index 37c1b871e..86669ae83 100644 --- a/.github/workflows/build-isolated-media-server.yaml +++ b/.github/workflows/build-isolated-media-server.yaml @@ -71,7 +71,7 @@ jobs: run: | # Build and tag the image docker build \ - --target production -t $CONTAINER_IMAGE:production \ + --target production -t $CONTAINER_IMAGE-prod:production \ --target production -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE \ ./desci-media-isolated @@ -93,10 +93,10 @@ jobs: run: | # Push image to AWS ECR aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com - docker tag $CONTAINER_IMAGE:production $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:${{ github.sha }} - docker tag $CONTAINER_IMAGE:production $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:production - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:${{ github.sha }} - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:production + docker tag $CONTAINER_IMAGE:production $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE-prod:${{ github.sha }} + docker tag $CONTAINER_IMAGE:production $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE-prod:production + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE-prod:${{ github.sha }} + docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE-prod:production - name: Deploy to EKS (DEV) # uses: steebchen/kubectl@v2.0.0 @@ -107,7 +107,7 @@ jobs: - name: Deploy to EKS (PROD) if: github.ref == 'refs/heads/main' run: | # defaults to latest kubectl binary version - kubectl set image deployment/desci-media-isolated desci-media-isolated=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:${{ github.sha }} --record + kubectl set image deployment/desci-media-isolated-prod desci-media-isolated-prod=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE-prod:${{ github.sha }} --record - name: Verify EKS Deployment (DEV) if: github.ref == 'refs/heads/develop' @@ -117,4 +117,4 @@ jobs: - name: Verify EKS Deployment (PROD) if: github.ref == 'refs/heads/main' run: | - kubectl rollout status deployment/desci-media-isolated + kubectl rollout status deployment/desci-media-isolated-prod diff --git a/desci-media-isolated/kubernetes/deployment.yaml b/desci-media-isolated/kubernetes/deployment.yaml index 32c08e2b9..9d14c8e8c 100644 --- a/desci-media-isolated/kubernetes/deployment.yaml +++ b/desci-media-isolated/kubernetes/deployment.yaml @@ -1,15 +1,30 @@ +apiVersion: v1 +kind: Service +metadata: + name: desci-media-isolated-prod-service + labels: + App: DesciMediaIsolatedProd +spec: + type: ClusterIP + selector: + App: DesciMediaIsolatedProd + ports: + - name: api + port: 7771 + targetPort: 7771 +--- apiVersion: apps/v1 kind: Deployment metadata: - name: desci-media-isolated + name: desci-media-isolated-prod labels: - App: DesciMediaIsolated + App: DesciMediaIsolatedProd spec: replicas: 1 revisionHistoryLimit: 2 selector: matchLabels: - App: DesciMediaIsolated + App: DesciMediaIsolatedProd strategy: rollingUpdate: maxSurge: 25% @@ -21,11 +36,11 @@ spec: App: DesciMediaIsolated spec: containers: - - image: 523044037273.dkr.ecr.us-east-2.amazonaws.com/desci-media-isolated:production - name: desci-media-isolated + - image: 523044037273.dkr.ecr.us-east-2.amazonaws.com/desci-media-isolated-prod:production + name: desci-media-isolated-prod ports: - containerPort: 7771 - name: media_isolated + name: api env: - name: PORT value: '7771' diff --git a/desci-server/kubernetes/deployment.yaml b/desci-server/kubernetes/deployment.yaml index e4a9cdddf..7fbc7a293 100755 --- a/desci-server/kubernetes/deployment.yaml +++ b/desci-server/kubernetes/deployment.yaml @@ -74,6 +74,8 @@ spec: export ESTUARY_API_URL={{ .Data.ESTUARY_API_URL }} export REPO_SERVER_URL={{ .Data.REPO_SERVER_URL }} export REPO_SERVICE_SECRET_KEY={{ .Data.REPO_SERVICE_SECRET_KEY }} + export ISOLATED_MEDIA_SERVER_URL={{ .Data.ISOLATED_MEDIA_SERVER_URL }} + export IPFS_READ_ONLY_GATEWAY_SERVER_URL={{ .Data.IPFS_READ_ONLY_GATEWAY_SERVER_URL }} export DEBUG_TEST=0; echo "appfinish"; {{- end -}}