diff --git a/Tokenization/.gitignore b/Tokenization/.gitignore index 809115f91..d2ef7c2e5 100644 --- a/Tokenization/.gitignore +++ b/Tokenization/.gitignore @@ -6,6 +6,18 @@ backend/wrapper/dist/ backend/wrapper/node_modules/ backend/wrapper/src/run_tests/ backend/node_modules -backend/certs backend/central-system/dist backend/central-system/node_modules +backend/certs +backend/central-system/ca +backend/central-system/vault/vault* +backend/central-system/crt +backend/central-system/policies +central-system.hcl +backend/central-system/.env +database/* +banner.js +.env +*.crt +*.key +*.pem \ No newline at end of file diff --git a/Tokenization/Dockerfile b/Tokenization/Dockerfile index 0f68d6f53..005bd670e 100644 --- a/Tokenization/Dockerfile +++ b/Tokenization/Dockerfile @@ -51,3 +51,9 @@ FROM nginx:1.27 AS reverse-proxy COPY ./docker/provisioning/nginx/conf.d/default.conf /etc/nginx/conf.d EXPOSE 8080 +# ---- Vault ---- +FROM hashicorp/vault:1.20 +COPY ./docker/vault/vault.crt ./docker/vault/vault.key ./docker/vault/ca.crt /vault/config/ +COPY ./docker/vault/central-system-client.crt /vault/config/central-system-client.crt +COPY ./docker/vault/vault-init.sh /usr/local/bin/vault-init.sh +RUN chmod +x /usr/local/bin/vault-init.sh \ No newline at end of file diff --git a/Tokenization/backend/central-system/package-lock.json b/Tokenization/backend/central-system/package-lock.json index ad8caf93b..27f316e73 100644 --- a/Tokenization/backend/central-system/package-lock.json +++ b/Tokenization/backend/central-system/package-lock.json @@ -13,7 +13,11 @@ "@grpc/proto-loader": "^0.7.15", "crypto": "^1.0.1", "express": "^4.19.2", - "jsonwebtoken": "^9.0.2" + "jsonwebtoken": "^9.0.2", + "mariadb": "3.0.0", + "node-fetch": "^3.3.2", + "sequelize": "6.37.0", + "umzug": "3.8.2" }, "devDependencies": { "@types/express": "^4.17.21", @@ -23,6 +27,7 @@ "concurrently": "^9.0.1", "jest": "^30.0.5", "nodemon": "^3.1.10", + "sequelize-cli": "6.6.0", "supertest": "^7.1.4", "ts-jest": "^29.4.1", "ts-node": "^10.9.2", @@ -50,6 +55,18 @@ "node": ">= 22.x" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -676,9 +693,9 @@ } }, "node_modules/@grpc/grpc-js": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.0.tgz", - "integrity": "sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.1.tgz", + "integrity": "sha512-sPxgEWtPUR3EnRJCEtbGZG2iX8LQDUls2wUS3o27jg07KqJFMq6YDeWvMo1wfpmy3rqRdS0rivpLwhqQtEyCuQ==", "license": "Apache-2.0", "dependencies": { "@grpc/proto-loader": "^0.8.0", @@ -1197,6 +1214,48 @@ "url": "https://paulmillr.com/funding/" } }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, "node_modules/@paralleldrive/cuid2": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", @@ -1295,6 +1354,107 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", "license": "BSD-3-Clause" }, + "node_modules/@rushstack/node-core-library": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.13.0.tgz", + "integrity": "sha512-IGVhy+JgUacAdCGXKUrRhwHMTzqhWwZUI+qEPcdzsb80heOw0QPbhhoVsoiMF7Klp8eYsp7hzpScMXmOa3Uhfg==", + "license": "MIT", + "dependencies": { + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~11.3.0", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/fs-extra": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", + "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@rushstack/node-core-library/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==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/@rushstack/terminal": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.15.2.tgz", + "integrity": "sha512-7Hmc0ysK5077R/IkLS9hYu0QuNafm+TbZbtYVzCMbeOdMjaRboLKrhryjwZSRJGJzu+TV1ON7qZHeqf58XfLpA==", + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.13.0", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "4.23.7", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.7.tgz", + "integrity": "sha512-Gr9cB7DGe6uz5vq2wdr89WbVDKz0UeuFEn5H2CfWDe7JvjFFaiV15gi6mqDBTbHhHCWS7w8mF1h3BnIfUndqdA==", + "license": "MIT", + "dependencies": { + "@rushstack/terminal": "0.15.2", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" + } + }, "node_modules/@sinclair/typebox": { "version": "0.34.41", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", @@ -1371,6 +1531,12 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1437,6 +1603,15 @@ "@types/node": "*" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/express": { "version": "4.17.25", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", @@ -1463,6 +1638,12 @@ "@types/send": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, "node_modules/@types/http-errors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", @@ -1530,7 +1711,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "dev": true, "license": "MIT" }, "node_modules/@types/node": { @@ -1603,6 +1783,12 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, + "node_modules/@types/validator": { + "version": "13.15.4", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.4.tgz", + "integrity": "sha512-LSFfpSnJJY9wbC0LQxgvfb+ynbHftFo0tMsFOl/J4wexLnYMmDSPaj2ZyDv3TkfL1UePxPrxOWJfbiRS8mQv7A==", + "license": "MIT" + }, "node_modules/@types/yargs": { "version": "17.0.34", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz", @@ -1896,6 +2082,16 @@ "win32" ] }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -1935,6 +2131,53 @@ "node": ">=0.4.0" } }, + "node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -2004,7 +2247,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" @@ -2036,6 +2278,16 @@ "dev": true, "license": "MIT" }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/babel-jest": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", @@ -2174,6 +2426,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -2212,7 +2471,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -2453,12 +2711,29 @@ } }, "node_modules/cjs-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.0.tgz", - "integrity": "sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz", + "integrity": "sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ==", "dev": true, "license": "MIT" }, + "node_modules/cli-color": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.4.tgz", + "integrity": "sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.64", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.15", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -2635,6 +2910,16 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/component-emitter": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", @@ -2677,6 +2962,17 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -2762,6 +3058,29 @@ "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", "license": "ISC" }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dev": true, + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2806,6 +3125,15 @@ "node": ">=0.4.0" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -2856,6 +3184,12 @@ "node": ">=0.3.1" } }, + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==", + "license": "MIT" + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2886,6 +3220,54 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/editorconfig/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2893,9 +3275,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.245", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.245.tgz", - "integrity": "sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ==", + "version": "1.5.248", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.248.tgz", + "integrity": "sha512-zsur2yunphlyAO4gIubdJEXCK6KOVvtpiuDfCIqbM9FjcnMYiyn0ICa3hWfPr0nc41zcLWobgy1iL7VvoOyA2Q==", "dev": true, "license": "ISC" }, @@ -2903,7 +3285,6 @@ "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2990,6 +3371,62 @@ "node": ">= 0.4" } }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "dev": true, + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3015,7 +3452,23 @@ "node": ">=8" } }, - "node_modules/esprima": { + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", @@ -3038,6 +3491,17 @@ "node": ">= 0.6" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -3143,6 +3607,38 @@ "url": "https://opencollective.com/express" } }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "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.8" + }, + "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", @@ -3157,6 +3653,15 @@ "dev": true, "license": "MIT" }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -3173,11 +3678,33 @@ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", "license": "MIT" }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -3258,6 +3785,18 @@ "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/formidable": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", @@ -3294,6 +3833,22 @@ "node": ">= 0.6" } }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3429,7 +3984,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -3454,7 +4008,6 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, "node_modules/handlebars": { @@ -3483,7 +4036,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3590,6 +4142,15 @@ "dev": true, "license": "ISC" }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", @@ -3620,6 +4181,15 @@ "node": ">=0.8.19" } }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ], + "license": "MIT" + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -3638,6 +4208,13 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -3667,11 +4244,25 @@ "node": ">=8" } }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "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, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3700,7 +4291,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -3713,12 +4303,18 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" } }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -4473,6 +5069,12 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "license": "MIT" + }, "node_modules/jose": { "version": "4.15.9", "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", @@ -4482,6 +5084,38 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4523,6 +5157,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -4536,6 +5176,18 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -4642,6 +5294,12 @@ "node": ">=8" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -4736,6 +5394,16 @@ "yallist": "^3.0.2" } }, + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es5-ext": "~0.10.2" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -4782,6 +5450,42 @@ "tmpl": "1.0.5" } }, + "node_modules/mariadb": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.0.0.tgz", + "integrity": "sha512-1uIqD6AWLP5ojMY67XP4+4uRLe9L92HD1ZGU8fidi8cGdYIC+Ghx1JliAtf7lc/tGjOh6J400f/1M4BXVtZFvA==", + "license": "LGPL-2.1-or-later", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@types/geojson": "^7946.0.8", + "@types/node": "^17.0.10", + "denque": "^2.0.1", + "iconv-lite": "^0.6.3", + "moment-timezone": "^0.5.34", + "please-upgrade-node": "^3.2.0" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/mariadb/node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "license": "MIT" + }, + "node_modules/mariadb/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -4800,6 +5504,26 @@ "node": ">= 0.6" } }, + "node_modules/memoizee": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", + "integrity": "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "es5-ext": "^0.10.64", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -4816,6 +5540,15 @@ "dev": true, "license": "MIT" }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -4829,7 +5562,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -4928,6 +5660,27 @@ "ospec": "ospec/bin/ospec" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -4994,6 +5747,51 @@ "dev": true, "license": "MIT" }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5122,6 +5920,22 @@ "node": ">=4" } }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -5375,6 +6189,12 @@ "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==", + "license": "MIT" + }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", @@ -5405,6 +6225,12 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5416,7 +6242,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -5448,6 +6273,24 @@ "node": ">=8" } }, + "node_modules/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "license": "MIT", + "dependencies": { + "semver-compare": "^1.0.0" + } + }, + "node_modules/pony-cause": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", + "integrity": "sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==", + "license": "0BSD", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/pretty-format": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", @@ -5482,6 +6325,13 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, "node_modules/protobufjs": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", @@ -5526,6 +6376,15 @@ "dev": true, "license": "MIT" }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/pure-rand": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", @@ -5558,6 +6417,26 @@ "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==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -5632,11 +6511,40 @@ "node": ">=0.10.0" } }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, "license": "MIT", "dependencies": { "resolve-from": "^5.0.0" @@ -5655,6 +6563,45 @@ "node": ">=8" } }, + "node_modules/retry-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", + "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==", + "license": "MIT" + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "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==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -5710,6 +6657,12 @@ "semver": "bin/semver.js" } }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "license": "MIT" + }, "node_modules/send": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", @@ -5749,6 +6702,252 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/sequelize": { + "version": "6.37.0", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.0.tgz", + "integrity": "sha512-MS6j6aXqWzB3fe9FhmfpQMgVC16bBdYroJCqIqR0l9M2ko8pZdKoi/0PiNWgMyFQDXUHxXyAOG3K07CbnOhteQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.6", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.1", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.4", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-cli": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.6.0.tgz", + "integrity": "sha512-FwTClhGRvXKanFRHMZbgfXOBV8UC2B3VkE0WOdW1n39/36PF4lWyurF95f246une/V4eaO3a7/Ywvy++3r+Jmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-color": "^2.0.3", + "fs-extra": "^9.1.0", + "js-beautify": "^1.14.5", + "lodash": "^4.17.21", + "resolve": "^1.22.1", + "umzug": "^2.3.0", + "yargs": "^16.2.0" + }, + "bin": { + "sequelize": "lib/sequelize", + "sequelize-cli": "lib/sequelize" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sequelize-cli/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sequelize-cli/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/sequelize-cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/sequelize-cli/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sequelize-cli/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, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sequelize-cli/node_modules/umzug": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", + "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.7.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/sequelize-cli/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/sequelize-cli/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sequelize-cli/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/sequelize/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==", + "license": "MIT" + }, + "node_modules/sequelize/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", @@ -5952,7 +7151,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/sqlstring": { @@ -6010,6 +7208,15 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -6261,7 +7468,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -6273,6 +7479,18 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/synckit": { "version": "0.11.11", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", @@ -6356,6 +7574,20 @@ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", "license": "MIT" }, + "node_modules/timers-ext": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", + "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", + "dev": true, + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -6367,7 +7599,6 @@ "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, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -6385,6 +7616,12 @@ "node": ">=0.6" } }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==", + "license": "MIT" + }, "node_modules/touch": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", @@ -6545,6 +7782,13 @@ "dev": true, "license": "0BSD" }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "dev": true, + "license": "ISC" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -6610,6 +7854,34 @@ "node": ">=0.8.0" } }, + "node_modules/umzug": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.2.tgz", + "integrity": "sha512-BEWEF8OJjTYVC56GjELeHl/1XjFejrD7aHzn+HldRJTx+pL1siBrKHZC8n4K/xL3bEzVA9o++qD1tK2CpZu4KA==", + "license": "MIT", + "dependencies": { + "@rushstack/ts-command-line": "^4.12.2", + "emittery": "^0.13.0", + "fast-glob": "^3.3.2", + "pony-cause": "^2.1.4", + "type-fest": "^4.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/umzug/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -6623,6 +7895,15 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -6698,6 +7979,15 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6713,6 +8003,15 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "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", @@ -6735,6 +8034,15 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.15.20", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz", + "integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -6754,6 +8062,15 @@ "makeerror": "1.0.12" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6834,6 +8151,15 @@ "node": ">= 6" } }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", diff --git a/Tokenization/backend/central-system/package.json b/Tokenization/backend/central-system/package.json index 95739597b..444b91003 100644 --- a/Tokenization/backend/central-system/package.json +++ b/Tokenization/backend/central-system/package.json @@ -11,14 +11,20 @@ "dev": "npm run build && concurrently -k \"npm:watch:*\"", "start": "node index.js", "test": "jest --runInBand", - "docker:up": "cd ../.. && docker compose -f docker-compose.yml up", - "docker:down": "cd ../.. && docker compose -f docker-compose.yml down" + "docker:build": "cd ../.. && docker compose -f docker-compose.dev.yml -f docker-compose.yml build", + "docker:up": "cd ../.. && docker compose -f docker-compose.dev.yml -f docker-compose.yml up", + "docker:up:build": "npm run docker:build && npm run docker:up", + "docker:down": "cd ../.. && docker compose -f docker-compose.dev.yml -f docker-compose.yml down" }, "dependencies": { "@aliceo2/web-ui": "^2.8.4", "crypto": "^1.0.1", "express": "^4.19.2", "jsonwebtoken": "^9.0.2", + "sequelize": "6.37.0", + "umzug": "3.8.2", + "mariadb": "3.0.0", + "node-fetch": "^3.3.2", "@grpc/grpc-js": "^1.13.4", "@grpc/proto-loader": "^0.7.15" }, @@ -33,6 +39,7 @@ "supertest": "^7.1.4", "ts-jest": "^29.4.1", "ts-node": "^10.9.2", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "sequelize-cli": "6.6.0" } } diff --git a/Tokenization/backend/central-system/src/lib/database/Database.ts b/Tokenization/backend/central-system/src/lib/database/Database.ts new file mode 100644 index 000000000..ca1f23d17 --- /dev/null +++ b/Tokenization/backend/central-system/src/lib/database/Database.ts @@ -0,0 +1,40 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { SequelizeDatabase } from './SequelizeDatabase.js'; + +class Database { + public static async createDatabase( + config: object + ): Promise { + const database = new SequelizeDatabase(config); + + await database.connect(); + await database.migrate(); + + return database; + } +} + +export const db = await Database.createDatabase({ + host: process.env.DB_HOST ?? 'database', + port: Number(process.env.DB_PORT ?? 3306), + username: process.env.DB_USER ?? 'central-system', + password: process.env.DB_PASSWORD ?? 'cern', + database: process.env.DB_NAME ?? 'tokenization', + charset: 'utf8mb4', + collate: 'utf8mb4_unicode_ci', + timezone: process.env.DB_TZ ?? '+00:00', + logging: process.env.DB_LOGGING ?? false, +}); diff --git a/Tokenization/backend/central-system/src/lib/database/SequelizeDatabase.ts b/Tokenization/backend/central-system/src/lib/database/SequelizeDatabase.ts new file mode 100644 index 000000000..d14ecb2cd --- /dev/null +++ b/Tokenization/backend/central-system/src/lib/database/SequelizeDatabase.ts @@ -0,0 +1,118 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { LogManager } from '@aliceo2/web-ui'; +import { Sequelize } from 'sequelize'; +import { getConfig } from './utils/getConfig.js'; +import { models } from './models/models.js'; +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { join } from 'path'; +import { createUmzug } from './umzug.js'; +import { SequelizeDatabaseConfig } from './utils/sequelizeDatabaseConfig'; +import { SequelizeStorage } from 'umzug'; + +export class SequelizeDatabase { + private _logger; + public sequelize: Sequelize; + private _models: object; + private _dbConfig: SequelizeDatabaseConfig; + + /** + * Initializes the Sequelize database connection. + * @param config - Database configuration object. + */ + constructor(config: object) { + this._logger = LogManager.getLogger('database/sequelize'); + + if (!config) { + this._logger.warnMessage('No database configuration provided'); + } + this._dbConfig = getConfig(config); + + const { + host, + port, + username, + password, + database, + charset, + collate, + timezone, + logging, + } = this._dbConfig; + + this.sequelize = new Sequelize(database, username, password, { + host, + port, + dialect: 'mariadb', + dialectOptions: { + charset, + collate, + timezone, + }, + logging, + define: { + underscored: true, + }, + }); + this._models = models(this.sequelize); + this._logger.infoMessage('Database connection initialized successfully.'); + } + + /** Connects to the database with retry logic. */ + async connect() { + const { retryThrottle } = this._dbConfig; + let connected = false; + + while (!connected) { + try { + await this.sequelize.authenticate(); + connected = true; + this._logger.infoMessage(`Successfully connected to database + '${this._dbConfig.database}' on '${this._dbConfig.host}:${this._dbConfig.port}'`); + } catch (error) { + this._logger.errorMessage(`Unable to connect to db: ${error}`); + this._logger.debugMessage(`Retrying in ${retryThrottle} ms...`); + await new Promise((resolve) => setTimeout(resolve, retryThrottle)); + } + } + } + + /** Executes pending database migrations. */ + async migrate() { + this._logger.debugMessage('Executing pending migrations...'); + try { + const __filename = fileURLToPath(import.meta.url); + const __dirname = dirname(__filename); + const umzug = createUmzug( + this.sequelize, + join(__dirname, 'migrations'), + new SequelizeStorage({ + sequelize: this.sequelize, + }) + ); + await umzug.up(); + this._logger.infoMessage('Migrations completed successfully.'); + } catch (error) { + this._logger.errorMessage(`Error executing migrations: ${error}`); + throw error; + } + } + + /** Access to the database models. */ + get models() { + return this._models; + } +} diff --git a/Tokenization/backend/central-system/src/lib/database/migrations/20251108-001-create-tokens.mts b/Tokenization/backend/central-system/src/lib/database/migrations/20251108-001-create-tokens.mts new file mode 100644 index 000000000..5703ba744 --- /dev/null +++ b/Tokenization/backend/central-system/src/lib/database/migrations/20251108-001-create-tokens.mts @@ -0,0 +1,62 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import type { QueryInterface } from 'sequelize'; + +/** Umzug migration: create `tokens` table */ +export async function up( + q: QueryInterface, + Sequelize: typeof import('sequelize') +) { + await q.createTable('tokens', { + id: { + type: Sequelize.INTEGER, + allowNull: false, + autoIncrement: true, + primaryKey: true, + }, + audience: { type: Sequelize.STRING(255), allowNull: false }, + subject: { type: Sequelize.STRING(255), allowNull: false }, + token_object: { type: Sequelize.JSON, allowNull: false }, + created_at: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + updated_at: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal( + 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' + ), + }, + }); + + await q.addIndex('tokens', ['audience'], { name: 'tokens_audience_idx' }); + await q.addIndex('tokens', ['subject'], { name: 'tokens_subject_idx' }); + await q.addIndex('tokens', ['created_at'], { name: 'tokens_created_at_idx' }); +} + +export async function down(q: QueryInterface) { + try { + await q.removeIndex('tokens', 'tokens_created_at_idx'); + } catch {} + try { + await q.removeIndex('tokens', 'tokens_subject_idx'); + } catch {} + try { + await q.removeIndex('tokens', 'tokens_audience_idx'); + } catch {} + await q.dropTable('tokens'); +} diff --git a/Tokenization/backend/central-system/src/lib/database/migrations/20251108-002-create-archive-tokens.mts b/Tokenization/backend/central-system/src/lib/database/migrations/20251108-002-create-archive-tokens.mts new file mode 100644 index 000000000..99b9f8926 --- /dev/null +++ b/Tokenization/backend/central-system/src/lib/database/migrations/20251108-002-create-archive-tokens.mts @@ -0,0 +1,68 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import type { QueryInterface } from 'sequelize'; + +/** Umzug migration: create `archive-tokens` table */ +export async function up( + q: QueryInterface, + Sequelize: typeof import('sequelize') +) { + await q.createTable('archive-tokens', { + id: { + type: Sequelize.INTEGER, + allowNull: false, + autoIncrement: true, + primaryKey: true, + }, + audience: { type: Sequelize.STRING(255), allowNull: false }, + subject: { type: Sequelize.STRING(255), allowNull: false }, + token_object: { type: Sequelize.JSON, allowNull: false }, + created_at: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + updated_at: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal( + 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' + ), + }, + }); + + await q.addIndex('archive-tokens', ['audience'], { + name: 'archive_tokens_audience_idx', + }); + await q.addIndex('archive-tokens', ['subject'], { + name: 'archive_tokens_subject_idx', + }); + await q.addIndex('archive-tokens', ['created_at'], { + name: 'archive_tokens_created_at_idx', + }); +} + +export async function down(q: QueryInterface) { + try { + await q.removeIndex('archive-tokens', 'archive_tokens_created_at_idx'); + } catch {} + try { + await q.removeIndex('archive-tokens', 'archive_tokens_subject_idx'); + } catch {} + try { + await q.removeIndex('archive-tokens', 'archive_tokens_audience_idx'); + } catch {} + await q.dropTable('archive-tokens'); +} diff --git a/Tokenization/backend/central-system/src/lib/database/models/ArchiveTokenModel.ts b/Tokenization/backend/central-system/src/lib/database/models/ArchiveTokenModel.ts new file mode 100644 index 000000000..ae514a699 --- /dev/null +++ b/Tokenization/backend/central-system/src/lib/database/models/ArchiveTokenModel.ts @@ -0,0 +1,77 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { Sequelize, Model, DataTypes } from 'sequelize'; + +// Define the structure of the token object +interface ArchivedTokenAttributes { + sub: string; + aud: string; + iss: string; + method: string; + jti: string; +} + +// Define the Token model +export class ArchiveToken extends Model { + declare id: number; + declare audience: string; + declare subject: string; + declare created_at: Date; + declare updated_at: Date; + declare token_object: ArchivedTokenAttributes; +} + +/* Initialize and export the Token model */ +export default (sequelize: Sequelize): typeof ArchiveToken => + ArchiveToken.init( + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + allowNull: false, + autoIncrement: true, + }, + audience: { + type: DataTypes.STRING(255), + allowNull: false, + }, + subject: { + type: DataTypes.STRING(255), + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + updated_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + token_object: { + type: DataTypes.JSON, + allowNull: false, + }, + }, + { + sequelize, + tableName: 'archive-tokens', + timestamps: true, + underscored: true, + createdAt: 'created_at', + updatedAt: 'updated_at', + } + ); diff --git a/Tokenization/backend/central-system/src/lib/database/models/TokenModel.ts b/Tokenization/backend/central-system/src/lib/database/models/TokenModel.ts new file mode 100644 index 000000000..f4a51b471 --- /dev/null +++ b/Tokenization/backend/central-system/src/lib/database/models/TokenModel.ts @@ -0,0 +1,78 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { Sequelize, Model, DataTypes } from 'sequelize'; + +// Define the structure of the token object +interface TokenAttributes { + sub: string; + aud: string; + iss: string; + iat: Record; + exp: Record; + jti: string; +} + +// Define the Token model +export class Token extends Model { + declare id: number; + declare audience: string; + declare subject: string; + declare created_at: Date; + declare updated_at: Date; + declare token_object: TokenAttributes; +} + +/* Initialize and export the Token model */ +export default (sequelize: Sequelize): typeof Token => + Token.init( + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + allowNull: false, + autoIncrement: true, + }, + audience: { + type: DataTypes.STRING(255), + allowNull: false, + }, + subject: { + type: DataTypes.STRING(255), + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + updated_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + token_object: { + type: DataTypes.JSON, + allowNull: false, + }, + }, + { + sequelize, + tableName: 'tokens', + timestamps: true, + underscored: true, + createdAt: 'created_at', + updatedAt: 'updated_at', + } + ); diff --git a/Tokenization/backend/central-system/src/lib/database/models/models.ts b/Tokenization/backend/central-system/src/lib/database/models/models.ts new file mode 100644 index 000000000..1c5d20c76 --- /dev/null +++ b/Tokenization/backend/central-system/src/lib/database/models/models.ts @@ -0,0 +1,29 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +// Import all models here and export them +import TokenModel from './TokenModel.js'; +import ArchiveTokenModel from './ArchiveTokenModel.js'; +import { Sequelize } from 'sequelize'; + +export function models(sequelize: Sequelize): { + Token: ReturnType; + ArchiveToken: ReturnType; +} { + const models = { + Token: TokenModel(sequelize), + ArchiveToken: ArchiveTokenModel(sequelize), + }; + return models; +} diff --git a/Tokenization/backend/central-system/src/lib/database/umzug.ts b/Tokenization/backend/central-system/src/lib/database/umzug.ts new file mode 100644 index 000000000..93ee822fa --- /dev/null +++ b/Tokenization/backend/central-system/src/lib/database/umzug.ts @@ -0,0 +1,67 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import pkg, { SequelizeStorage } from 'umzug'; +import { Sequelize } from 'sequelize'; +import { LogManager } from '@aliceo2/web-ui'; +import { MigrationParams } from 'umzug'; + +const { Umzug } = pkg; +const logger = LogManager.getLogger('database/umzug'); + +export const createUmzug = ( + sequelize: Sequelize, + migrationsDirectory: string, + storage: SequelizeStorage +) => + new Umzug({ + migrations: { + glob: `${migrationsDirectory}/*.mjs`, + resolve: ({ + name, + path: migrationPath, + context, + }: MigrationParams) => { + const loadMigration = async () => { + if (!migrationPath) { + throw new Error(`Missing migration path for '${name}'`); + } + const migration = await import(migrationPath); + if ( + typeof migration.up !== 'function' || + typeof migration.down !== 'function' + ) { + throw new Error( + `Migration '${name}' is missing valid up/down functions.` + ); + } + return migration; + }; + + return { + name, + up: async () => + (await loadMigration()).up(context.getQueryInterface(), Sequelize), + down: async () => + (await loadMigration()).down( + context.getQueryInterface(), + Sequelize + ), + }; + }, + }, + context: sequelize, + storage, + logger: logger, + }); diff --git a/Tokenization/backend/central-system/src/lib/database/utils/expireToken.ts b/Tokenization/backend/central-system/src/lib/database/utils/expireToken.ts new file mode 100644 index 000000000..96a6cc77a --- /dev/null +++ b/Tokenization/backend/central-system/src/lib/database/utils/expireToken.ts @@ -0,0 +1,70 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { LogManager } from '@aliceo2/web-ui'; +import { ArchiveToken } from './../models/ArchiveTokenModel.js'; +import { Token } from './../models/TokenModel.js'; +import { Sequelize } from 'sequelize'; + +type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | string; + +const logger = LogManager.getLogger('database/utils/expireToken'); + +/** + * Expires a token by archiving it and removing the specified method. + * If it's the last method, the token is deleted entirely. + * @param sequelize - Sequelize instance for database operations. + * @param id - ID of the token to expire. + * @param method - HTTP method to expire from the token. + */ +export default async ( + sequelize: Sequelize, + id: number, + method: Method +): Promise => { + const token: Token | null = await Token.findByPk(id); + if (!token) { + logger.info('No such token in database.'); + return; + } + + const { sub, aud, iss, iat, jti } = token.token_object; + const methods: string[] = Object.keys(iat); + + await sequelize.transaction(async (tx) => { + await ArchiveToken.create( + { + audience: token.audience, + subject: token.subject, + token_object: { + sub, + aud, + iss, + method, + jti, + }, + }, + { transaction: tx } + ); + + if (methods.length === 1) { + await token.destroy({ transaction: tx }); + return; + } + + const { [method]: _removed, ...newIat } = iat; + token.token_object = { ...token.token_object, iat: newIat }; + await token.save({ transaction: tx }); + }); +}; diff --git a/Tokenization/backend/central-system/src/lib/database/utils/getConfig.ts b/Tokenization/backend/central-system/src/lib/database/utils/getConfig.ts new file mode 100644 index 000000000..715bca34f --- /dev/null +++ b/Tokenization/backend/central-system/src/lib/database/utils/getConfig.ts @@ -0,0 +1,38 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { SequelizeDatabaseConfig } from './sequelizeDatabaseConfig.js'; + +/** + * Returns database configuration with default values if not provided. + * @param config - Partial database configuration object. + * @returns Complete database configuration object. + */ +export function getConfig( + config?: Partial +): SequelizeDatabaseConfig { + const c = config ?? {}; + return { + host: c.host ?? 'localhost', + port: c.port ?? 3306, + username: c.username ?? 'cern', + password: c.password ?? 'cern', + database: c.database ?? 'tkn', + charset: c.charset ?? 'utf8mb4', + collate: c.collate ?? 'utf8mb4_general_ci', + timezone: c.timezone ?? '+00:00', + logging: c.logging ?? false, + retryThrottle: c.retryThrottle ?? 5000, + }; +} diff --git a/Tokenization/backend/central-system/src/lib/database/utils/sequelizeDatabaseConfig.d.ts b/Tokenization/backend/central-system/src/lib/database/utils/sequelizeDatabaseConfig.d.ts new file mode 100644 index 000000000..33a9329e5 --- /dev/null +++ b/Tokenization/backend/central-system/src/lib/database/utils/sequelizeDatabaseConfig.d.ts @@ -0,0 +1,26 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +export interface SequelizeDatabaseConfig { + host: string; + port: number; + username: string; + password: string; + database: string; + charset: string; + collate: string; + timezone: string; + logging: boolean | ((sql: string, timing?: number) => void); + retryThrottle: number; +} diff --git a/Tokenization/backend/central-system/src/lib/utils/Scheduler.ts b/Tokenization/backend/central-system/src/lib/utils/Scheduler.ts new file mode 100644 index 000000000..39f201a4c --- /dev/null +++ b/Tokenization/backend/central-system/src/lib/utils/Scheduler.ts @@ -0,0 +1,70 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { LogManager } from '@aliceo2/web-ui'; + +const logger = LogManager.getLogger('utils/Scheduler'); + +type Key = string; + +export type ScheduledJobs = Map; + +export class ArchiveScheduler { + private jobs: ScheduledJobs = new Map(); + + private key(tokenId: number, method: string): Key { + return `${tokenId}:${method}`; + } + + /** Schedule a job to run after delayMs milliseconds. */ + schedule( + tokenId: number, + method: string, + delayMs: number, + run: () => Promise + ): () => void { + const k = this.key(tokenId, method); + + // prevent duplicates + this.cancel(tokenId, method); + + const t = setTimeout(() => { + this.jobs.delete(k); + run().catch((err) => { + logger.errorMessage( + `Scheduled job for token ${tokenId} method ${method} failed: ${err}` + ); + }); + }, delayMs); + + this.jobs.set(k, t); + return () => this.cancel(tokenId, method); + } + + /** Cancel a scheduled job. */ + cancel(tokenId: number, method: string): void { + const k = this.key(tokenId, method); + const t = this.jobs.get(k); + if (t) { + clearTimeout(t); + this.jobs.delete(k); + } + } + + /** Cancel all on shutdown. */ + cancelAll(): void { + for (const [, t] of this.jobs) clearTimeout(t); + this.jobs.clear(); + } +} diff --git a/Tokenization/backend/central-system/src/modules/CentralSystem.ts b/Tokenization/backend/central-system/src/modules/CentralSystem.ts index cb6b6f5a1..9114445f5 100644 --- a/Tokenization/backend/central-system/src/modules/CentralSystem.ts +++ b/Tokenization/backend/central-system/src/modules/CentralSystem.ts @@ -17,6 +17,8 @@ import { TokensController } from '../controllers/TokensController.js'; import path from 'path'; import { fileURLToPath } from 'url'; import { TokensGetService } from '../services/TokensGetService.js'; +import { db } from '../lib/database/Database.js'; +import { SequelizeDatabase } from '../lib/database/SequelizeDatabase.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -33,9 +35,12 @@ class CentralSystem { number, { tokenId: number; validity: string; payload: string } >; + private _db: SequelizeDatabase; + public readonly tokenController: TokensController; public constructor(wrapperPort: number) { + this._db = db; const tokensGetService = new TokensGetService(); this._centralSystemWrapper = new CentralSystemWrapper( this.PROTO_PATH, diff --git a/Tokenization/docker-compose.dev.yml b/Tokenization/docker-compose.dev.yml new file mode 100644 index 000000000..82fc287e6 --- /dev/null +++ b/Tokenization/docker-compose.dev.yml @@ -0,0 +1,108 @@ +services: + vault: + image: hashicorp/vault:latest + container_name: vault + ports: + - '127.0.0.1:8200:9300' + - '127.0.0.1:8201:9301' + environment: + VAULT_LOCAL_CONFIG: | + ui = true + disable_mlock = true + cluster_name = "dev-raft" + + api_addr = "https://vault.local:8200" + cluster_addr = "https://vault.local:8201" + + listener "tcp" { + address = "0.0.0.0:9300" + cluster_address = "0.0.0.0:9301" + tls_cert_file = "/vault/config/vault.crt" + tls_key_file = "/vault/config/vault.key" + tls_client_ca_file = "/vault/config/ca.crt" + } + + storage "raft" { + path = "/vault/data" + node_id = "node1" + } + entrypoint: + - /bin/sh + - -lc + - | + set -e + docker-entrypoint.sh >/dev/null 2>&1 || true + exec vault server -config=/vault/config/local.json + networks: + default: + aliases: + - vault.local + volumes: + - type: volume + source: vault-data + target: /vault/data + + - ./docker/vault/vault.crt:/vault/config/vault.crt:ro + - ./docker/vault/vault.key:/vault/config/vault.key:ro + - ./docker/ca/ca.crt:/vault/config/ca.crt:ro + cap_add: + - IPC_LOCK + restart: unless-stopped + vault-setup: + image: hashicorp/vault:latest + depends_on: + vault: + condition: service_started + environment: + VAULT_ADDR: https://vault.local:9300 + VAULT_TLS_SERVER_NAME: vault.local + volumes: + - vault-data:/vault/data + - ./docker/vault/vault.crt:/vault/config/vault.crt:ro + - ./docker/vault/vault.key:/vault/config/vault.key:ro + - ./docker/vault/ca.crt:/vault/config/ca.crt:ro + - ./docker/vault/vault-setup.sh:/vault-setup.sh:ro + - ./docker/vault/central-system.crt:/vault/config/central-system-client.crt:ro + entrypoint: ['/bin/sh', '-lc'] + command: ['/vault-setup.sh'] + restart: 'on-failure' + + database: + image: 'mariadb:10.5.9' + container_name: tokenization-database + environment: + MYSQL_ROOT_PASSWORD: 'cern' + ports: + - '3306:3306' + volumes: + - type: volume + source: database-data + target: /var/lib/mysql + - type: bind + read_only: true + source: ./docker/database/configuration + target: /etc/mysql/conf.d + - type: bind + read_only: true + source: ./docker/database/populate + target: /docker-entrypoint-initdb.d + healthcheck: + test: + [ + 'CMD', + 'mysqladmin', + 'ping', + '-h', + 'localhost', + '--user=root', + '--password=cern', + ] + interval: 10s + timeout: 5s + retries: 12 + start_period: 20s + restart: unless-stopped + +volumes: + database-data: + vault-data: diff --git a/Tokenization/docker-compose.test.yml b/Tokenization/docker-compose.test.yml index 695b63d6d..5f385d35c 100644 --- a/Tokenization/docker-compose.test.yml +++ b/Tokenization/docker-compose.test.yml @@ -5,7 +5,6 @@ services: volumes: - ./backend:/var/workspace command: ['npm', 'install', '--no-save', '--silent'] - backend: image: node:22-alpine working_dir: /var/workspace/central-system @@ -24,6 +23,8 @@ services: depends_on: install-backend: condition: service_completed_successfully + database: + condition: service_healthy prod-container: build: @@ -35,9 +36,13 @@ services: interval: 5s timeout: 3s retries: 5 + ports: + - 8080:80 depends_on: backend: condition: service_healthy + database: + condition: service_healthy ui-tests: build: @@ -49,3 +54,42 @@ services: depends_on: prod-container: condition: service_healthy + + database: + image: 'mariadb:10.5.9' + container_name: tokenization-database + environment: + MYSQL_ROOT_PASSWORD: 'cern' + ports: + - '3306:3306' + volumes: + - type: volume + source: database-data + target: /var/lib/mysql + - type: bind + read_only: true + source: ./docker/database/configuration + target: /etc/mysql/conf.d + - type: bind + read_only: true + source: ./docker/database/populate + target: /docker-entrypoint-initdb.d + healthcheck: + test: + [ + 'CMD', + 'mysqladmin', + 'ping', + '-h', + 'localhost', + '--user=root', + '--password=cern', + ] + interval: 10s + timeout: 5s + retries: 12 + start_period: 20s + restart: unless-stopped + +volumes: + database-data: diff --git a/Tokenization/docker-compose.yml b/Tokenization/docker-compose.yml index df50f3c06..bf3811c50 100644 --- a/Tokenization/docker-compose.yml +++ b/Tokenization/docker-compose.yml @@ -10,6 +10,8 @@ services: working_dir: /var/workspace/central-system volumes: - ./backend:/var/workspace + env_file: + - ./backend/central-system/.env command: ['npm', 'run', 'dev'] healthcheck: interval: 1s @@ -23,6 +25,10 @@ services: depends_on: install-backend: condition: service_completed_successfully + database: + condition: service_healthy + vault-setup: + condition: service_completed_successfully install-webapp: image: node:22-alpine diff --git a/Tokenization/docker/database/configuration/my.cnf b/Tokenization/docker/database/configuration/my.cnf new file mode 100644 index 000000000..d031c0009 --- /dev/null +++ b/Tokenization/docker/database/configuration/my.cnf @@ -0,0 +1,14 @@ +[client] +default-character-set = utf8mb4 + +[mysql] +default-character-set = utf8mb4 + +[mysqld] +lower_case_table_names = 0 + +# Ensure utf8mb4 everywhere +init_connect = 'SET NAMES utf8mb4; SET collation_connection = utf8mb4_unicode_ci' +character-set-server = utf8mb4 +collation-server = utf8mb4_unicode_ci +skip-character-set-client-handshake diff --git a/Tokenization/docker/database/populate/01-create-database.sql b/Tokenization/docker/database/populate/01-create-database.sql new file mode 100644 index 000000000..150b7af3b --- /dev/null +++ b/Tokenization/docker/database/populate/01-create-database.sql @@ -0,0 +1,3 @@ +CREATE DATABASE IF NOT EXISTS `tokenization` + CHARACTER SET utf8mb4 + COLLATE utf8mb4_unicode_ci; \ No newline at end of file diff --git a/Tokenization/docker/database/populate/02-create-user.sql b/Tokenization/docker/database/populate/02-create-user.sql new file mode 100644 index 000000000..9a531c438 --- /dev/null +++ b/Tokenization/docker/database/populate/02-create-user.sql @@ -0,0 +1,5 @@ +CREATE USER IF NOT EXISTS 'central-system'@'%' IDENTIFIED BY 'cern'; + +GRANT ALL PRIVILEGES ON `tokenization`.* TO 'central-system'@'%'; + +FLUSH PRIVILEGES; \ No newline at end of file diff --git a/Tokenization/docker/vault/vault-setup.sh b/Tokenization/docker/vault/vault-setup.sh new file mode 100755 index 000000000..4a5b8d39c --- /dev/null +++ b/Tokenization/docker/vault/vault-setup.sh @@ -0,0 +1,137 @@ +#!/usr/bin/env sh +set -euo pipefail + +export VAULT_ADDR="${VAULT_ADDR:-https://vault.local:9300}" +export VAULT_CACERT=${VAULT_CACERT:-/vault/config/ca.crt} +export VAULT_TLS_SERVER_NAME=${VAULT_TLS_SERVER_NAME:-vault.local} + +echo "[vault-setup] VAULT_ADDR=$VAULT_ADDR" +echo "[vault-setup] VAULT_CACERT=$VAULT_CACERT" +echo "[vault-setup] VAULT_TLS_SERVER_NAME=$VAULT_TLS_SERVER_NAME" + +echo "[vault-setup] Waiting for Vault to respond..." +for i in $(seq 1 60); do + if vault status >/dev/null 2>&1; then + break + fi + sleep 1 +done + +status_json=$(vault status -format=json || true) +initialized=$(echo "$status_json" | sed -n 's/.*"initialized":[[:space:]]*\(true\|false\).*/\1/p') +sealed=$(echo "$status_json" | sed -n 's/.*"sealed":[[:space:]]*\(true\|false\).*/\1/p') + +echo "[vault-setup] current status: initialized=$initialized, sealed=$sealed" + +UNSEAL_KEY="" +ROOT_TOKEN="" + +if [ "$initialized" = "false" ]; then + echo "[vault-setup] Vault NOT initialized. Running operator init (text output)..." + + init_output=$(vault operator init -key-shares=1 -key-threshold=1) + + echo "$init_output" | sed -n '1,50p' + + UNSEAL_KEY=$(printf '%s\n' "$init_output" | awk '/Unseal Key 1:/ {print $NF}') + ROOT_TOKEN=$(printf '%s\n' "$init_output" | awk '/Initial Root Token:/ {print $NF}') + + if [ -z "$UNSEAL_KEY" ] || [ -z "$ROOT_TOKEN" ]; then + echo "[vault-setup] ERROR: Failed to parse UNSEAL_KEY or ROOT_TOKEN from vault operator init output." + exit 1 + fi + + echo "[vault-setup] ====== COPY THESE (DO NOT COMMIT) ======" + echo "[vault-setup] UNSEAL_KEY: $UNSEAL_KEY" + echo "[vault-setup] ROOT_TOKEN: $ROOT_TOKEN" + echo "[vault-setup] ========================================" + + mkdir -p /vault/data + echo "$UNSEAL_KEY" > /vault/data/unseal_key + echo "$ROOT_TOKEN" > /vault/data/root_token + + export VAULT_TOKEN="$ROOT_TOKEN" +else + echo "[vault-setup] Vault already initialized." + + if [ -f /vault/data/root_token ]; then + ROOT_TOKEN=$(cat /vault/data/root_token) + export VAULT_TOKEN="$ROOT_TOKEN" + echo "[vault-setup] Loaded ROOT_TOKEN from /vault/data/root_token" + else + echo "[vault-setup] WARNING: /vault/data/root_token missing. Some config may fail." + fi + + if [ -f /vault/data/unseal_key ]; then + UNSEAL_KEY=$(cat /vault/data/unseal_key) + echo "[vault-setup] Loaded UNSEAL_KEY from /vault/data/unseal_key" + else + echo "[vault-setup] WARNING: /vault/data/unseal_key missing. Cannot auto-unseal if sealed." + fi +fi + + +status_json=$(vault status -format=json || true) +sealed=$(echo "$status_json" | sed -n 's/.*"sealed":[[:space:]]*\(true\|false\).*/\1/p') +echo "[vault-setup] status after init: sealed=$sealed" + +if [ "$sealed" = "true" ]; then + if [ -z "${UNSEAL_KEY:-}" ]; then + echo "[vault-setup] ERROR: Vault is sealed and UNSEAL_KEY is not available." + exit 1 + fi + + echo "[vault-setup] Unsealing Vault..." + vault operator unseal "$UNSEAL_KEY" +else + echo "[vault-setup] Vault already unsealed." +fi + +echo "[vault-setup] Ensuring Vault is reachable with token..." +if [ -n "${VAULT_TOKEN:-}" ]; then + vault token lookup >/dev/null 2>&1 || echo "[vault-setup] WARNING: VAULT_TOKEN may be invalid." +else + echo "[vault-setup] WARNING: VAULT_TOKEN not set – policy/config steps may fail." +fi + +#################################### +# VAULT CONFIGURATION STEPS +#################################### + +if [ -n "${VAULT_TOKEN:-}" ]; then + echo "[vault-setup] Enabling secrets engines..." + vault secrets enable transit 2>/dev/null || echo "[vault-setup] transit already enabled" + vault secrets enable -path=tokenization kv-v2 2>/dev/null || echo "[vault-setup] tokenization kv-v2 already enabled" + + echo "[vault-setup] Creating transit key for tokenization..." + vault write transit/keys/tokenization-signing type="rsa-2048" 2>/dev/null || echo "[vault-setup] transit key already exists" + + echo "[vault-setup] Creating central-system policy..." + vault policy write central-system - </dev/null || echo "[vault-setup] cert auth already enabled" + + vault write auth/cert/certs/central-system \ + display_name="central-system" \ + policies="central-system" \ + certificate=@/vault/config/central-system-client.crt \ + ttl=24h 2>/dev/null || echo "[vault-setup] central-system cert role already exists" +else + echo "[vault-setup] Skipping policy / auth config – no VAULT_TOKEN." +fi + +echo "[vault-setup] Initialization + unseal + config finished OK." +exit 0