diff --git a/backend/package-lock.json b/backend/package-lock.json index 0bd5f891..6ea6a92c 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -14,9 +14,11 @@ "@ffmpeg-installer/ffmpeg": "^1.1.0", "@google-cloud/storage": "^7.18.0", "@nestjs/bullmq": "^11.0.4", + "@nestjs/cache-manager": "^3.1.0", "@nestjs/common": "^11.0.1", "@nestjs/config": "^4.0.2", "@nestjs/core": "^11.0.1", + "@nestjs/event-emitter": "^3.0.1", "@nestjs/jwt": "^11.0.0", "@nestjs/mapped-types": "^2.1.0", "@nestjs/passport": "^11.0.0", @@ -24,17 +26,21 @@ "@nestjs/platform-socket.io": "^11.1.12", "@nestjs/schedule": "^6.1.0", "@nestjs/typeorm": "^11.0.0", - "@stellar/stellar-sdk": "^14.4.3", + "@sendgrid/mail": "^8.1.6", + "@stellar/stellar-sdk": "^14.5.0", "@types/fluent-ffmpeg": "^2.1.28", "@types/multer": "^2.0.0", "bcrypt": "^6.0.0", "bullmq": "^5.66.6", + "cache-manager": "^7.2.8", "class-transformer": "^0.5.1", "class-validator": "^0.14.3", "crypto-js": "^4.2.0", + "express": "^5.2.1", "fluent-ffmpeg": "^2.1.3", "ioredis": "^5.9.2", "json2csv": "^6.0.0-alpha.2", + "kubo-rpc-client": "^6.1.0", "multer": "^2.0.2", "passport": "^0.7.0", "passport-jwt": "^4.0.1", @@ -45,7 +51,8 @@ "sharp": "^0.34.5", "socket.io": "^4.8.3", "typeorm": "^0.3.28", - "uuid": "^13.0.0" + "uuid": "^13.0.0", + "xss": "^1.0.15" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", @@ -55,8 +62,9 @@ "@nestjs/testing": "^11.0.1", "@types/babel__core": "^7.20.5", "@types/bcrypt": "^5.0.2", + "@types/cache-manager": "^4.0.6", "@types/crypto-js": "^4.2.2", - "@types/express": "^5.0.0", + "@types/express": "^5.0.6", "@types/jest": "^30.0.0", "@types/json2csv": "^5.0.7", "@types/node": "^22.19.11", @@ -1240,7 +1248,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1736,6 +1743,22 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/@cacheable/utils": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.3.4.tgz", + "integrity": "sha512-knwKUJEYgIfwShABS1BX6JyJJTglAFcEU7EXqzTdiGCXur4voqkiJkdgZIQtWNFhynzDWERcTYv/sETMu3uJWA==", + "license": "MIT", + "dependencies": { + "hashery": "^1.3.0", + "keyv": "^5.6.0" + } + }, + "node_modules/@chainsafe/is-ip": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chainsafe/is-ip/-/is-ip-2.1.0.tgz", + "integrity": "sha512-KIjt+6IfysQ4GCv66xihEitBjvhU/bixbbbFxdJ1sqCp4uJ0wuZiYBPhksZoy4lfaF0k9cwNzY5upEW/VWdw3w==", + "license": "MIT" + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -1771,6 +1794,19 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@dnsquery/dns-packet": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@dnsquery/dns-packet/-/dns-packet-6.1.1.tgz", + "integrity": "sha512-WXTuFvL3G+74SchFAtz3FgIYVOe196ycvGsMgvSH/8Goptb1qpIQtIuM4SOK9G9lhMWYpHxnXyy544ZhluFOew==", + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.4", + "utf8-codec": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@emnapi/core": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", @@ -3026,6 +3062,47 @@ "integrity": "sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==", "license": "MIT" }, + "node_modules/@ipld/dag-cbor": { + "version": "9.2.5", + "resolved": "https://registry.npmjs.org/@ipld/dag-cbor/-/dag-cbor-9.2.5.tgz", + "integrity": "sha512-84wSr4jv30biui7endhobYhXBQzQE4c/wdoWlFrKcfiwH+ofaPg8fwsM8okX9cOzkkrsAsNdDyH3ou+kiLquwQ==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "cborg": "^4.0.0", + "multiformats": "^13.1.0" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@ipld/dag-json": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/@ipld/dag-json/-/dag-json-10.2.6.tgz", + "integrity": "sha512-51yc5azhmkvc9mp2HV/vtJ8SlgFXADp55wAPuuAjQZ+yPurAYuTVddS3ke5vT4sjcd4DbE+DWjsMZGXjFB2cuA==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "cborg": "^4.4.0", + "multiformats": "^13.1.0" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@ipld/dag-pb": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@ipld/dag-pb/-/dag-pb-4.1.5.tgz", + "integrity": "sha512-w4PZ2yPqvNmlAir7/2hsCRMqny1EY5jj26iZcSgxREJexmbAc2FI21jp26MqiNdfgAxvkCnf2N/TJI18GaDNwA==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "multiformats": "^13.1.0" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=7.0.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3714,6 +3791,99 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "license": "MIT" + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "license": "MIT" + }, + "node_modules/@libp2p/crypto": { + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/@libp2p/crypto/-/crypto-5.1.13.tgz", + "integrity": "sha512-8NN9cQP3jDn+p9+QE9ByiEoZ2lemDFf/unTgiKmS3JF93ph240EUVdbCyyEgOMfykzb0okTM4gzvwfx9osJebQ==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "@libp2p/interface": "^3.1.0", + "@noble/curves": "^2.0.1", + "@noble/hashes": "^2.0.1", + "multiformats": "^13.4.0", + "protons-runtime": "^5.6.0", + "uint8arraylist": "^2.4.8", + "uint8arrays": "^5.1.0" + } + }, + "node_modules/@libp2p/crypto/node_modules/@noble/curves": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", + "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "2.0.1" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@libp2p/crypto/node_modules/@noble/hashes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@libp2p/interface": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@libp2p/interface/-/interface-3.1.0.tgz", + "integrity": "sha512-RE7/XyvC47fQBe1cHxhMvepYKa5bFCUyFrrpj8PuM0E7JtzxU7F+Du5j4VXbg2yLDcToe0+j8mB7jvwE2AThYw==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "@multiformats/dns": "^1.0.6", + "@multiformats/multiaddr": "^13.0.1", + "main-event": "^1.0.1", + "multiformats": "^13.4.0", + "progress-events": "^1.0.1", + "uint8arraylist": "^2.4.8" + } + }, + "node_modules/@libp2p/logger": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/@libp2p/logger/-/logger-6.2.2.tgz", + "integrity": "sha512-XtanXDT+TuMuZoCK760HGV1AmJsZbwAw5AiRUxWDbsZPwAroYq64nb41AHRu9Gyc0TK9YD+p72+5+FIxbw0hzw==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "@libp2p/interface": "^3.1.0", + "@multiformats/multiaddr": "^13.0.1", + "interface-datastore": "^9.0.1", + "multiformats": "^13.4.0", + "weald": "^1.1.0" + } + }, + "node_modules/@libp2p/peer-id": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@libp2p/peer-id/-/peer-id-6.0.4.tgz", + "integrity": "sha512-Z3xK0lwwKn4bPg3ozEpPr1HxsRi2CxZdghOL+MXoFah/8uhJJHxHFA8A/jxtKn4BB8xkk6F8R5vKNIS05yaCYw==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "@libp2p/crypto": "^5.1.13", + "@libp2p/interface": "^3.1.0", + "multiformats": "^13.4.0", + "uint8arrays": "^5.1.0" + } + }, "node_modules/@lukeed/csprng": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", @@ -3801,6 +3971,41 @@ "win32" ] }, + "node_modules/@multiformats/dns": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@multiformats/dns/-/dns-1.0.13.tgz", + "integrity": "sha512-yr4bxtA3MbvJ+2461kYIYMsiiZj/FIqKI64hE4SdvWJUdWF9EtZLar38juf20Sf5tguXKFUruluswAO6JsjS2w==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "@dnsquery/dns-packet": "^6.1.1", + "@libp2p/interface": "^3.1.0", + "hashlru": "^2.3.0", + "p-queue": "^9.0.0", + "progress-events": "^1.0.0", + "uint8arrays": "^5.0.2" + } + }, + "node_modules/@multiformats/multiaddr": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@multiformats/multiaddr/-/multiaddr-13.0.1.tgz", + "integrity": "sha512-XToN915cnfr6Lr9EdGWakGJbPT0ghpg/850HvdC+zFX8XvpLZElwa8synCiwa8TuvKNnny6m8j8NVBNCxhIO3g==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "@chainsafe/is-ip": "^2.0.1", + "multiformats": "^13.0.0", + "uint8-varint": "^2.0.1", + "uint8arrays": "^5.0.0" + } + }, + "node_modules/@multiformats/multiaddr-to-uri": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@multiformats/multiaddr-to-uri/-/multiaddr-to-uri-12.0.0.tgz", + "integrity": "sha512-3uIEBCiy8tfzxYYBl81x1tISiNBQ7mHU4pGjippbJRoQYHzy/ZdZM/7JvTldr8pc/dzpkaNJxnsuxxlhsPOJsA==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "@multiformats/multiaddr": "^13.0.0" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -3842,6 +4047,19 @@ "bullmq": "^3.0.0 || ^4.0.0 || ^5.0.0" } }, + "node_modules/@nestjs/cache-manager": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/cache-manager/-/cache-manager-3.1.0.tgz", + "integrity": "sha512-pEIqYZrBcE8UdkJmZRduurvoUfdU+3kRPeO1R2muiMbZnRuqlki5klFFNllO9LyYWzrx98bd1j0PSPKSJk1Wbw==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^9.0.0 || ^10.0.0 || ^11.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0 || ^11.0.0", + "cache-manager": ">=6", + "keyv": ">=5", + "rxjs": "^7.8.1" + } + }, "node_modules/@nestjs/cli": { "version": "11.0.16", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.16.tgz", @@ -3893,7 +4111,6 @@ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4064,7 +4281,6 @@ "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.14.tgz", "integrity": "sha512-IN/tlqd7Nl9gl6f0jsWEuOrQDaCI9vHzxv0fisHysfBQzfQIkqlv5A7w4Qge02BUQyczXT9HHPgHtWHCxhjRng==", "license": "MIT", - "peer": true, "dependencies": { "file-type": "21.3.0", "iterare": "1.2.1", @@ -4112,7 +4328,6 @@ "integrity": "sha512-7OXPPMoDr6z+5NkoQKu4hOhfjz/YYqM3bNilPqv1WVFWrzSmuNXxvhbX69YMmNmRYascPXiwESqf5jJdjKXEww==", "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "@nuxt/opencollective": "0.4.1", "fast-safe-stringify": "2.1.1", @@ -4148,6 +4363,19 @@ } } }, + "node_modules/@nestjs/event-emitter": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-3.0.1.tgz", + "integrity": "sha512-0Ln/x+7xkU6AJFOcQI9tIhUMXVF7D5itiaQGOyJbXtlAfAIt8gzDdJm+Im7cFzKoWkiW5nCXCPh6GSvdQd/3Dw==", + "license": "MIT", + "dependencies": { + "eventemitter2": "6.4.9" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "@nestjs/core": "^10.0.0 || ^11.0.0" + } + }, "node_modules/@nestjs/jwt": { "version": "11.0.2", "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-11.0.2.tgz", @@ -4196,7 +4424,6 @@ "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.14.tgz", "integrity": "sha512-Fs+/j+mBSBSXErOQJ/YdUn/HqJGSJ4pGfiJyYOyz04l42uNVnqEakvu1kXLbxMabR6vd6/h9d6Bi4tso9p7o4Q==", "license": "MIT", - "peer": true, "dependencies": { "cors": "2.8.6", "express": "5.2.1", @@ -4218,7 +4445,6 @@ "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.14.tgz", "integrity": "sha512-LLSIWkYz4FcvUhfepillYQboo9qbjq1YtQj8XC3zyex+EaqNXvxhZntx/1uJhAjc655pJts9HfZwWXei8jrRGw==", "license": "MIT", - "peer": true, "dependencies": { "socket.io": "4.8.3", "tslib": "2.8.1" @@ -4436,6 +4662,41 @@ "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/@nuxt/opencollective": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", @@ -4485,6 +4746,44 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@sendgrid/client": { + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-8.1.6.tgz", + "integrity": "sha512-/BHu0hqwXNHr2aLhcXU7RmmlVqrdfrbY9KpaNj00KZHlVOVoRxRVrpOCabIB+91ISXJ6+mLM9vpaVUhK6TwBWA==", + "license": "MIT", + "dependencies": { + "@sendgrid/helpers": "^8.0.0", + "axios": "^1.12.0" + }, + "engines": { + "node": ">=12.*" + } + }, + "node_modules/@sendgrid/helpers": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-8.0.0.tgz", + "integrity": "sha512-Ze7WuW2Xzy5GT5WRx+yEv89fsg/pgy3T1E3FS0QEx0/VvRmigMZ5qyVGhJz4SxomegDkzXv/i0aFPpHKN8qdAA==", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.2.2" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@sendgrid/mail": { + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-8.1.6.tgz", + "integrity": "sha512-/ZqxUvKeEztU9drOoPC/8opEPOk+jLlB2q4+xpx6HVLq6aFu3pMpalkTpAQz8XfRfpLp8O25bh6pGPcHDCYpqg==", + "license": "MIT", + "dependencies": { + "@sendgrid/client": "^8.1.5", + "@sendgrid/helpers": "^8.0.0" + }, + "engines": { + "node": ">=12.*" + } + }, "node_modules/@sinclair/typebox": { "version": "0.34.48", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", @@ -5463,6 +5762,13 @@ "@types/node": "*" } }, + "node_modules/@types/cache-manager": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/cache-manager/-/cache-manager-4.0.6.tgz", + "integrity": "sha512-8qL93MF05/xrzFm/LSPtzNEOE1eQF3VwGHAcQEylgp5hDSTe41jtFwbSYAPfyYcVa28y1vYSjIt0c1fLLUiC/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/caseless": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", @@ -5507,7 +5813,6 @@ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -5667,7 +5972,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -5933,7 +6237,6 @@ "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", @@ -6640,7 +6943,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "devOptional": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6699,7 +7001,6 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6822,6 +7123,16 @@ "node": ">=14" } }, + "node_modules/any-signal": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/any-signal/-/any-signal-4.2.0.tgz", + "integrity": "sha512-LndMvYuAPf4rC195lk7oSFuHOYFpOszIYrNYv0gHAvz+aEhE9qPZLhmrIz5pXP2BSsPOXvsuHDXEGaiQhIh9wA==", + "license": "Apache-2.0 OR MIT", + "engines": { + "node": ">=16.0.0", + "npm": ">=7.0.0" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -7200,6 +7511,15 @@ "ieee754": "^1.1.13" } }, + "node_modules/blob-to-it": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/blob-to-it/-/blob-to-it-2.0.10.tgz", + "integrity": "sha512-I39vO57y+LBEIcAV7fif0sn96fYOYVqrPiOD+53MxQGv4DBgt1/HHZh0BHheWx2hVe24q5LTSXxqeV1Y3Nzkgg==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "browser-readablestream-to-it": "^2.0.0" + } + }, "node_modules/body-parser": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", @@ -7245,7 +7565,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" @@ -7264,6 +7583,12 @@ "base64-js": "^1.1.2" } }, + "node_modules/browser-readablestream-to-it": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/browser-readablestream-to-it/-/browser-readablestream-to-it-2.0.10.tgz", + "integrity": "sha512-I/9hEcRtjct8CzD9sVo9Mm4ntn0D+7tOVrjbPl69XAoOfgJ8NBdOQU+WX+5SHhcELJDb14mWt7zuvyqha+MEAQ==", + "license": "Apache-2.0 OR MIT" + }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -7284,7 +7609,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -7363,7 +7687,6 @@ "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.69.3.tgz", "integrity": "sha512-P9uLsR7fDvejH/1m6uur6j7U9mqY6nNt+XvhlhStOUe7jdwbZoP/c2oWNtE+8ljOlubw4pRUKymtRqkyvloc4A==", "license": "MIT", - "peer": true, "dependencies": { "cron-parser": "4.9.0", "ioredis": "5.9.2", @@ -7431,6 +7754,16 @@ "node": ">= 0.8" } }, + "node_modules/cache-manager": { + "version": "7.2.8", + "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-7.2.8.tgz", + "integrity": "sha512-0HDaDLBBY/maa/LmUVAr70XUOwsiQD+jyzCBjmUErYZUKdMS9dT59PqW59PpVqfGM7ve6H0J6307JTpkCYefHQ==", + "license": "MIT", + "dependencies": { + "@cacheable/utils": "^2.3.3", + "keyv": "^5.5.5" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -7518,6 +7851,15 @@ ], "license": "CC-BY-4.0" }, + "node_modules/cborg": { + "version": "4.5.8", + "resolved": "https://registry.npmjs.org/cborg/-/cborg-4.5.8.tgz", + "integrity": "sha512-6/viltD51JklRhq4L7jC3zgy6gryuG5xfZ3kzpE+PravtyeQLeQmCYLREhQH7pWENg5pY4Yu/XCd6a7dKScVlw==", + "license": "Apache-2.0", + "bin": { + "cborg": "lib/bin.js" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -7558,7 +7900,6 @@ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "readdirp": "^4.0.1" }, @@ -7606,15 +7947,13 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/class-validator": { "version": "0.14.3", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz", "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==", "license": "MIT", - "peer": true, "dependencies": { "@types/validator": "^13.15.3", "libphonenumber-js": "^1.11.1", @@ -7998,6 +8337,28 @@ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", "license": "MIT" }, + "node_modules/cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==", + "license": "MIT" + }, + "node_modules/dag-jose": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/dag-jose/-/dag-jose-5.1.1.tgz", + "integrity": "sha512-9alfZ8Wh1XOOMel8bMpDqWsDT72ojFQCJPtwZSev9qh4f8GoCV9qrJW8jcOUhcstO8Kfm09FHGo//jqiZq3z9w==", + "license": "(Apache-2.0 OR MIT)", + "dependencies": { + "@ipld/dag-cbor": "^9.0.0", + "multiformats": "~13.1.3" + } + }, + "node_modules/dag-jose/node_modules/multiformats": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.1.3.tgz", + "integrity": "sha512-CZPi9lFZCM/+7oRolWYsvalsyWQGFo+GpdaTmjxXXomC+nP/W1Rnxb9sUgjvmNmRZ5bOPqRAl4nuK+Ydw/4tGw==", + "license": "Apache-2.0 OR MIT" + }, "node_modules/dayjs": { "version": "1.11.19", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", @@ -8055,7 +8416,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -8267,6 +8627,18 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/electron-fetch": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/electron-fetch/-/electron-fetch-1.9.1.tgz", + "integrity": "sha512-M9qw6oUILGVrcENMSRRefE1MbHPIz0h79EKIeJWK9v563aT9Qkh8aEHPO1H5vi970wPirNY+jO9OpFoLiMsMGA==", + "license": "MIT", + "dependencies": { + "encoding": "^0.1.13" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.302", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", @@ -8302,6 +8674,27 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/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/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -8397,6 +8790,12 @@ "node": ">=10.13.0" } }, + "node_modules/err-code": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-3.0.1.tgz", + "integrity": "sha512-GiaH0KJUewYok+eeY05IIgjtAe4Yltygk9Wqp1V5yVWLdhf0hYZchRjNIT9bb0mSwRcIusT3cx7PJUf3zEIfUA==", + "license": "MIT" + }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -8493,7 +8892,6 @@ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8554,7 +8952,6 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -8722,6 +9119,18 @@ "node": ">=6" } }, + "node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", + "license": "MIT" + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -8863,6 +9272,40 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "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-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "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", @@ -8918,6 +9361,15 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "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", @@ -8990,7 +9442,6 @@ "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" @@ -9051,6 +9502,16 @@ "node": ">=16" } }, + "node_modules/flat-cache/node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/flatted": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", @@ -9394,6 +9855,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-iterator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-iterator/-/get-iterator-1.0.2.tgz", + "integrity": "sha512-v+dm9bNVfOYsY1OrhaCrmyOcYoSeVvbt+hHZ0Au+T+p1y+0Uyj9aMaGIeUTT6xdpRbWzDeYKvfOslPhggQMcsg==", + "license": "MIT" + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -9659,6 +10126,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hashery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.0.tgz", + "integrity": "sha512-nhQ6ExaOIqti2FDWoEMWARUqIKyjr2VcZzXShrI+A3zpeiuPWzx6iPftt44LhP74E5sW36B75N6VHbvRtpvO6Q==", + "license": "MIT", + "dependencies": { + "hookified": "^1.14.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/hashlru": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hashlru/-/hashlru-2.3.0.tgz", + "integrity": "sha512-0cMsjjIC8I+D3M44pOQdsy0OHXGLVz6Z0beRuufhKa0KfaD2wGwAev6jILzXsd3/vpnNQJmWyZtIILqM1N+n5A==", + "license": "MIT" + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -9671,6 +10156,12 @@ "node": ">= 0.4" } }, + "node_modules/hookified": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "license": "MIT" + }, "node_modules/html-entities": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", @@ -9874,12 +10365,27 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/interface-datastore": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/interface-datastore/-/interface-datastore-9.0.2.tgz", + "integrity": "sha512-jebn+GV/5LTDDoyicNIB4D9O0QszpPqT09Z/MpEWvf3RekjVKpXJCDguM5Au2fwIFxFDAQMZe5bSla0jMamCNg==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "interface-store": "^7.0.0", + "uint8arrays": "^5.1.0" + } + }, + "node_modules/interface-store": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/interface-store/-/interface-store-7.0.1.tgz", + "integrity": "sha512-OPRRUO3Cs6Jr/t98BrJLQp1jUTPgrRH0PqFfuNoPAqd+J7ABN1tjFVjQdaOBiybYJTS/AyBSZnZVWLPvp3dW3w==", + "license": "Apache-2.0 OR MIT" + }, "node_modules/ioredis": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.9.3.tgz", "integrity": "sha512-VI5tMCdeoxZWU5vjHWsiE/Su76JGhBvWF1MJnV9ZtGltHk9BmD48oDq8Tj8haZ85aceXZMxLNDQZRVo5QKNgXA==", "license": "MIT", - "peer": true, "dependencies": { "@ioredis/commands": "1.5.0", "cluster-key-slot": "^1.1.0", @@ -9908,6 +10414,16 @@ "node": ">= 0.10" } }, + "node_modules/ipfs-unixfs": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/ipfs-unixfs/-/ipfs-unixfs-12.0.0.tgz", + "integrity": "sha512-6I2YSqYCxcr/u1xdWROQU+PkmG7NAMRpwNiFffU9lmSv1I4nCXRDRy6YFH7dg8v17+gM1w+ijBK31rZGrrF5Og==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "protons-runtime": "^5.5.0", + "uint8arraylist": "^2.4.8" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -9927,11 +10443,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-electron": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz", + "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==", + "license": "MIT" + }, "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" @@ -9960,7 +10481,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" @@ -9983,12 +10503,20 @@ "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-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -10059,6 +10587,15 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/iso-url": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iso-url/-/iso-url-1.2.1.tgz", + "integrity": "sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -10130,6 +10667,68 @@ "node": ">=8" } }, + "node_modules/it-all": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/it-all/-/it-all-3.0.9.tgz", + "integrity": "sha512-fz1oJJ36ciGnu2LntAlE6SA97bFZpW7Rnt0uEc1yazzR2nKokZLr8lIRtgnpex4NsmaBcvHF+Z9krljWFy/mmg==", + "license": "Apache-2.0 OR MIT" + }, + "node_modules/it-first": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/it-first/-/it-first-3.0.9.tgz", + "integrity": "sha512-ZWYun273Gbl7CwiF6kK5xBtIKR56H1NoRaiJek2QzDirgen24u8XZ0Nk+jdnJSuCTPxC2ul1TuXKxu/7eK6NuA==", + "license": "Apache-2.0 OR MIT" + }, + "node_modules/it-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/it-glob/-/it-glob-3.0.4.tgz", + "integrity": "sha512-73PbGBTK/dHp5PX4l8pkQH1ozCONP0U+PB3qMqltxPonRJQNomINE3Hn9p02m2GOu95VoeVvSZdHI2N+qub0pw==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "fast-glob": "^3.3.3" + } + }, + "node_modules/it-last": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/it-last/-/it-last-3.0.9.tgz", + "integrity": "sha512-AtfUEnGDBHBEwa1LjrpGHsJMzJAWDipD6zilvhakzJcm+BCvNX8zlX2BsHClHJLLTrsY4lY9JUjc+TQV4W7m1w==", + "license": "Apache-2.0 OR MIT" + }, + "node_modules/it-map": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/it-map/-/it-map-3.1.4.tgz", + "integrity": "sha512-QB9PYQdE9fUfpVFYfSxBIyvKynUCgblb143c+ktTK6ZuKSKkp7iH58uYFzagqcJ5HcqIfn1xbfaralHWam+3fg==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "it-peekable": "^3.0.0" + } + }, + "node_modules/it-peekable": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/it-peekable/-/it-peekable-3.0.8.tgz", + "integrity": "sha512-7IDBQKSp/dtBxXV3Fj0v3qM1jftJ9y9XrWLRIuU1X6RdKqWiN60syNwP0fiDxZD97b8SYM58dD3uklIk1TTQAw==", + "license": "Apache-2.0 OR MIT" + }, + "node_modules/it-stream-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/it-stream-types/-/it-stream-types-2.0.2.tgz", + "integrity": "sha512-Rz/DEZ6Byn/r9+/SBCuJhpPATDF9D+dz5pbgSUyBsCDtza6wtNATrz/jz1gDyNanC3XdLboriHnOC925bZRBww==", + "license": "Apache-2.0 OR MIT" + }, + "node_modules/it-to-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/it-to-stream/-/it-to-stream-1.0.0.tgz", + "integrity": "sha512-pLULMZMAB/+vbdvbZtebC0nWBTbG581lk6w8P7DfIIIKUfa8FbY7Oi0FxZcFPbxvISs7A9E+cMpLDBc1XhpAOA==", + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3", + "fast-fifo": "^1.0.0", + "get-iterator": "^1.0.2", + "p-defer": "^3.0.0", + "p-fifo": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, "node_modules/iterare": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", @@ -10160,7 +10759,6 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -11082,13 +11680,51 @@ } }, "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/kubo-rpc-client": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/kubo-rpc-client/-/kubo-rpc-client-6.1.0.tgz", + "integrity": "sha512-CH3vcqSGlEhr/HCZYQgYpXxmwIOYhNed4BQmAYmHpX7sehTC3iKK/36x7anEdRTgmV6KB66MyOk1yuhU2HqKFw==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "@ipld/dag-cbor": "^9.0.0", + "@ipld/dag-json": "^10.0.0", + "@ipld/dag-pb": "^4.0.0", + "@libp2p/crypto": "^5.0.0", + "@libp2p/interface": "^3.0.2", + "@libp2p/logger": "^6.0.5", + "@libp2p/peer-id": "^6.0.3", + "@multiformats/multiaddr": "^13.0.1", + "@multiformats/multiaddr-to-uri": "^12.0.0", + "any-signal": "^4.1.1", + "blob-to-it": "^2.0.5", + "browser-readablestream-to-it": "^2.0.5", + "dag-jose": "^5.0.0", + "electron-fetch": "^1.9.1", + "err-code": "^3.0.1", + "ipfs-unixfs": "^12.0.0", + "iso-url": "^1.2.1", + "it-all": "^3.0.4", + "it-first": "^3.0.4", + "it-glob": "^3.0.1", + "it-last": "^3.0.4", + "it-map": "^3.0.5", + "it-peekable": "^3.0.3", + "it-to-stream": "^1.0.0", + "merge-options": "^3.0.4", + "multiformats": "^13.1.0", + "nanoid": "^5.0.7", + "parse-duration": "^2.1.2", + "stream-to-it": "^1.0.1", + "uint8arrays": "^5.0.3", + "wherearewe": "^2.0.1" } }, "node_modules/leven": { @@ -11325,6 +11961,12 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/main-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/main-event/-/main-event-1.0.1.tgz", + "integrity": "sha512-NWtdGrAca/69fm6DIVd8T9rtfDII4Q8NQbIbsKQq2VzS9eqOGYs8uaNQjcuaCq/d9H/o625aOTJX2Qoxzqw0Pw==", + "license": "Apache-2.0 OR MIT" + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -11401,6 +12043,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-options": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", + "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", + "license": "MIT", + "dependencies": { + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -11408,6 +12062,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", @@ -11422,7 +12085,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", @@ -11436,7 +12098,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" @@ -11633,6 +12294,12 @@ "node": ">= 0.6" } }, + "node_modules/multiformats": { + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.4.2.tgz", + "integrity": "sha512-eh6eHCrRi1+POZ3dA+Dq1C6jhP1GNtr9CRINMb67OKzqW9I5DUuZM/3jLPlzhgpGeiNUlEGEbkCYChXMCc/8DQ==", + "license": "Apache-2.0 OR MIT" + }, "node_modules/mute-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", @@ -11643,6 +12310,24 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/nanoid": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, "node_modules/napi-postinstall": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", @@ -11814,6 +12499,7 @@ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 6" } @@ -11909,6 +12595,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-defer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-3.0.0.tgz", + "integrity": "sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-fifo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-fifo/-/p-fifo-1.0.0.tgz", + "integrity": "sha512-IjoCxXW48tqdtDFz6fqo5q1UfFVjjVZe8TC1QRflvNUJtNfCUhxOUw6MOVZhDPjqhSzc26xKdugsO17gmzd5+A==", + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.0.0", + "p-defer": "^3.0.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -11940,6 +12645,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.0.tgz", + "integrity": "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -11975,6 +12708,12 @@ "node": ">=6" } }, + "node_modules/parse-duration": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-2.1.5.tgz", + "integrity": "sha512-/IX1KRw6zHDOOJrgIz++gvFASbFl7nc8GEXaLdD7d1t1x/GnrK6hh5Fgk8G3RLpkIEi4tsGj9pupGLWNg0EiJA==", + "license": "MIT" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -12008,7 +12747,6 @@ "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", "license": "MIT", - "peer": true, "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", @@ -12139,7 +12877,6 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz", "integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==", "license": "MIT", - "peer": true, "dependencies": { "pg-connection-string": "^2.11.0", "pg-pool": "^3.11.0", @@ -12412,7 +13149,6 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -12464,6 +13200,23 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/progress-events": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/progress-events/-/progress-events-1.0.1.tgz", + "integrity": "sha512-MOzLIwhpt64KIVN64h1MwdKWiyKFNc/S6BoYKPIVUHFg0/eIEyBulhWCgn678v/4c0ri3FdGuzXymNCv02MUIw==", + "license": "Apache-2.0 OR MIT" + }, + "node_modules/protons-runtime": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-5.6.0.tgz", + "integrity": "sha512-/Kde+sB9DsMFrddJT/UZWe6XqvL7SL5dbag/DBCElFKhkwDj7XKt53S+mzLyaDP5OqS0wXjV5SA572uWDaT0Hg==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "uint8-varint": "^2.0.2", + "uint8arraylist": "^2.4.3", + "uint8arrays": "^5.0.1" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -12646,6 +13399,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/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -12850,6 +13623,16 @@ "node": ">=14" } }, + "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/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -12866,6 +13649,29 @@ "node": ">= 18" } }, + "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", @@ -13395,6 +14201,15 @@ "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", "license": "MIT" }, + "node_modules/stream-to-it": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-to-it/-/stream-to-it-1.0.1.tgz", + "integrity": "sha512-AqHYAYPHcmvMrcLNgncE/q0Aj/ajP6A4qGhxP6EVn7K3YTNs0bJpJyk57wc2Heb7MUL64jurvmnmui8D9kjZgA==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "it-stream-types": "^2.0.1" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -13803,7 +14618,6 @@ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -14010,7 +14824,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" @@ -14164,7 +14977,6 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -14325,7 +15137,6 @@ "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.28.tgz", "integrity": "sha512-6GH7wXhtfq2D33ZuRXYwIsl/qM5685WZcODZb7noOOcRMteM9KF2x2ap3H0EBjnSV0VO4gNAfJT5Ukp0PkOlvg==", "license": "MIT", - "peer": true, "dependencies": { "@sqltools/formatter": "^1.2.5", "ansis": "^4.2.0", @@ -14521,7 +15332,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14580,6 +15390,16 @@ "node": ">=8" } }, + "node_modules/uint8-varint": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/uint8-varint/-/uint8-varint-2.0.4.tgz", + "integrity": "sha512-FwpTa7ZGA/f/EssWAb5/YV6pHgVF1fViKdW8cWaEarjB8t7NyofSWBdOTyFPaGuUG4gx3v1O3PQ8etsiOs3lcw==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "uint8arraylist": "^2.0.0", + "uint8arrays": "^5.0.0" + } + }, "node_modules/uint8array-extras": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", @@ -14592,6 +15412,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/uint8arraylist": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/uint8arraylist/-/uint8arraylist-2.4.8.tgz", + "integrity": "sha512-vc1PlGOzglLF0eae1M8mLRTBivsvrGsdmJ5RbK3e+QRvRLOZfZhQROTwH/OfyF3+ZVUg9/8hE8bmKP2CvP9quQ==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "uint8arrays": "^5.0.1" + } + }, + "node_modules/uint8arrays": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-5.1.0.tgz", + "integrity": "sha512-vA6nFepEmlSKkMBnLBaUMVvAC4G3CTmO58C12y4sq6WPDOR7mOFYOi7GlrQ4djeSbP6JG9Pv9tJDM97PedRSww==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "multiformats": "^13.0.0" + } + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -14721,6 +15559,12 @@ "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", "license": "MIT" }, + "node_modules/utf8-codec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/utf8-codec/-/utf8-codec-1.0.0.tgz", + "integrity": "sha512-S/QSLezp3qvG4ld5PUfXiH7mCFxLKjSVZRFkB3DOjgwHuJPFDkInAXc/anf7BAbHt/D38ozDzL+QMZ6/7gsI6w==", + "license": "MIT" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -14823,6 +15667,37 @@ "defaults": "^1.0.3" } }, + "node_modules/weald": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/weald/-/weald-1.1.1.tgz", + "integrity": "sha512-PaEQShzMCz8J/AD2N3dJMc1hTZWkJeLKS2NMeiVkV5KDHwgZe7qXLEzyodsT/SODxWDdXJJqocuwf3kHzcXhSQ==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "ms": "^3.0.0-canary.1", + "supports-color": "^10.0.0" + } + }, + "node_modules/weald/node_modules/ms": { + "version": "3.0.0-canary.202508261828", + "resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.202508261828.tgz", + "integrity": "sha512-NotsCoUCIUkojWCzQff4ttdCfIPoA1UGZsyQbi7KmqkNRfKCrvga8JJi2PknHymHOuor0cJSn/ylj52Cbt2IrQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/weald/node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -14923,6 +15798,7 @@ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -14941,6 +15817,7 @@ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -14954,6 +15831,7 @@ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -14968,6 +15846,7 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=4.0" } @@ -14977,7 +15856,8 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/webpack/node_modules/mime-db": { "version": "1.52.0", @@ -14985,6 +15865,7 @@ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -14995,6 +15876,7 @@ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -15008,6 +15890,7 @@ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -15032,6 +15915,19 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/wherearewe": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wherearewe/-/wherearewe-2.0.1.tgz", + "integrity": "sha512-XUguZbDxCA2wBn2LoFtcEhXL6AXo+hVjGonwhSTTTU9SzbWG8Xu3onNIpzf9j/mYUcJQ0f+m37SzG77G851uFw==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "is-electron": "^2.2.0" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=7.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -15164,6 +16060,28 @@ } } }, + "node_modules/xss": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz", + "integrity": "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==", + "license": "MIT", + "dependencies": { + "commander": "^2.20.3", + "cssfilter": "0.0.10" + }, + "bin": { + "xss": "bin/xss" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/xss/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/backend/src/auth/auth.module.ts b/backend/src/auth/auth.module.ts index 817532da..dbe21364 100644 --- a/backend/src/auth/auth.module.ts +++ b/backend/src/auth/auth.module.ts @@ -20,6 +20,7 @@ import { RoleAuditLog } from './entities/role-audit-log.entity'; import { EmailServiceImpl } from './services/email.service'; import { EMAIL_SERVICE } from './interfaces/email-service.interface'; import { RolesService } from './services/roles.service'; +import { RolesController } from './controllers/roles.controller'; import { PermissionsService } from './services/permissions.service'; import { RolesPermissionsSeeder } from './seeds/roles-permissions.seed'; @@ -75,7 +76,7 @@ import { RolesPermissionsSeeder } from './seeds/roles-permissions.seed'; }), forwardRef(() => UsersModule), ], - controllers: [AuthController], + controllers: [AuthController, RolesController], providers: [ AuthService, JwtStrategy, diff --git a/backend/src/auth/constants/permission-definitions.ts b/backend/src/auth/constants/permission-definitions.ts index 7e056946..75ada120 100644 --- a/backend/src/auth/constants/permission-definitions.ts +++ b/backend/src/auth/constants/permission-definitions.ts @@ -1,53 +1,91 @@ import { Permission } from './permissions.enum'; +import { Resource, Action } from './permission-types.enum'; export interface PermissionDefinition { name: Permission; description: string; - resource: string; - action: string; + resource: Resource | '*'; + action: Action | '*'; } export const PERMISSION_DEFINITIONS: PermissionDefinition[] = [ + // PetOwner { name: Permission.READ_OWN_PETS, description: 'Read own pets', - resource: 'pets', - action: 'READ', + resource: Resource.PETS, + action: Action.READ, }, { name: Permission.UPDATE_OWN_PETS, description: 'Update own pets', - resource: 'pets', - action: 'UPDATE', + resource: Resource.PETS, + action: Action.UPDATE, }, { name: Permission.CREATE_PETS, description: 'Create pets', - resource: 'pets', - action: 'CREATE', + resource: Resource.PETS, + action: Action.CREATE, }, + { + name: Permission.SHARE_RECORDS, + description: 'Share pet medical records', + resource: Resource.MEDICAL_RECORDS, + action: Action.SHARE, + }, + + // Veterinarian { name: Permission.READ_ALL_PETS, description: 'Read all pets', - resource: 'pets', - action: 'READ', + resource: Resource.PETS, + action: Action.READ, }, { name: Permission.UPDATE_MEDICAL_RECORDS, description: 'Update medical records', - resource: 'medical_records', - action: 'UPDATE', + resource: Resource.MEDICAL_RECORDS, + action: Action.UPDATE, }, { name: Permission.CREATE_TREATMENTS, description: 'Create treatments', - resource: 'treatments', - action: 'CREATE', + resource: Resource.TREATMENTS, + action: Action.CREATE, }, + { + name: Permission.PRESCRIBE, + description: 'Prescribe medication', + resource: Resource.TREATMENTS, + action: Action.PRESCRIBE, + }, + + // VetStaff + { + name: Permission.READ_ASSIGNED_PETS, + description: 'Read assigned pets', + resource: Resource.PETS, + action: Action.READ, + }, + { + name: Permission.UPDATE_APPOINTMENTS, + description: 'Update appointments', + resource: Resource.APPOINTMENTS, + action: Action.UPDATE, + }, + { + name: Permission.CREATE_NOTES, + description: 'Create clinical notes', + resource: Resource.NOTES, + action: Action.CREATE, + }, + + // Admin { name: Permission.ALL_PERMISSIONS, - description: 'All permissions (admin only)', + description: 'Full system access', resource: '*', action: '*', }, -]; +]; \ No newline at end of file diff --git a/backend/src/auth/constants/permission-types.enum.ts b/backend/src/auth/constants/permission-types.enum.ts new file mode 100644 index 00000000..dc0a8a30 --- /dev/null +++ b/backend/src/auth/constants/permission-types.enum.ts @@ -0,0 +1,16 @@ +export enum Resource { + PETS = 'pets', + MEDICAL_RECORDS = 'medical_records', + TREATMENTS = 'treatments', + APPOINTMENTS = 'appointments', + NOTES = 'notes', +} + +export enum Action { + READ = 'READ', + CREATE = 'CREATE', + UPDATE = 'UPDATE', + DELETE = 'DELETE', + SHARE = 'SHARE', + PRESCRIBE = 'PRESCRIBE', +} \ No newline at end of file diff --git a/backend/src/auth/constants/permissions.enum.ts b/backend/src/auth/constants/permissions.enum.ts index d935b856..7167b005 100644 --- a/backend/src/auth/constants/permissions.enum.ts +++ b/backend/src/auth/constants/permissions.enum.ts @@ -1,14 +1,21 @@ export enum Permission { - // PetOwner permissions + // PetOwner READ_OWN_PETS = 'READ_OWN_PETS', UPDATE_OWN_PETS = 'UPDATE_OWN_PETS', CREATE_PETS = 'CREATE_PETS', + SHARE_RECORDS = 'SHARE_RECORDS', - // Veterinarian permissions + // Veterinarian READ_ALL_PETS = 'READ_ALL_PETS', UPDATE_MEDICAL_RECORDS = 'UPDATE_MEDICAL_RECORDS', CREATE_TREATMENTS = 'CREATE_TREATMENTS', + PRESCRIBE = 'PRESCRIBE', - // Admin permission (grants all permissions) + // VetStaff + READ_ASSIGNED_PETS = 'READ_ASSIGNED_PETS', + UPDATE_APPOINTMENTS = 'UPDATE_APPOINTMENTS', + CREATE_NOTES = 'CREATE_NOTES', + + // Admin ALL_PERMISSIONS = 'ALL_PERMISSIONS', -} +} \ No newline at end of file diff --git a/backend/src/auth/constants/roles.enum.ts b/backend/src/auth/constants/roles.enum.ts index ee30a018..1c90838c 100644 --- a/backend/src/auth/constants/roles.enum.ts +++ b/backend/src/auth/constants/roles.enum.ts @@ -1,5 +1,6 @@ export enum RoleName { PetOwner = 'PetOwner', Veterinarian = 'Veterinarian', + VetStaff = 'VetStaff', Admin = 'Admin', -} +} \ No newline at end of file diff --git a/backend/src/auth/controllers/roles.controller.ts b/backend/src/auth/controllers/roles.controller.ts new file mode 100644 index 00000000..7a92b4e2 --- /dev/null +++ b/backend/src/auth/controllers/roles.controller.ts @@ -0,0 +1,64 @@ +import { + Controller, + Post, + Body, + UseGuards, + Get, + Param, +} from '@nestjs/common'; + +import { JwtAuthGuard } from '../guards/jwt-auth.guard'; +import { RolesGuard } from '../guards/roles.guard'; +import { Roles } from '../decorators/roles.decorator'; +import { CurrentUser } from '../decorators/current-user.decorator'; + +import { RolesService } from '../services/roles.service'; +import { AssignRoleDto, RemoveRoleDto } from '../dto/role.dto'; + +import { RoleName } from '../constants/roles.enum'; +import { User } from '../../modules/users/entities/user.entity'; + +@Controller('roles') +@UseGuards(JwtAuthGuard, RolesGuard) +export class RolesController { + constructor(private readonly rolesService: RolesService) {} + + /* + |-------------------------------------------------------------------------- + | ASSIGN ROLE (ADMIN ONLY) + |-------------------------------------------------------------------------- + */ + @Post('assign') + @Roles(RoleName.Admin) + async assignRole( + @Body() dto: AssignRoleDto, + @CurrentUser() admin: User, + ) { + return this.rolesService.assignRole(dto, admin.id); + } + + /* + |-------------------------------------------------------------------------- + | REMOVE ROLE (ADMIN ONLY) + |-------------------------------------------------------------------------- + */ + @Post('remove') + @Roles(RoleName.Admin) + async removeRole( + @Body() dto: RemoveRoleDto, + @CurrentUser() admin: User, + ) { + return this.rolesService.removeRole(dto, admin.id); + } + + /* + |-------------------------------------------------------------------------- + | VIEW USER ROLES (ADMIN ONLY) + |-------------------------------------------------------------------------- + */ + @Get('user/:userId') + @Roles(RoleName.Admin) + async getUserRoles(@Param('userId') userId: string) { + return this.rolesService.getUserRoles(userId); + } +} \ No newline at end of file diff --git a/backend/src/auth/decorators/permissions.decorator.ts b/backend/src/auth/decorators/permissions.decorator.ts index ceebb85b..4abf68ea 100644 --- a/backend/src/auth/decorators/permissions.decorator.ts +++ b/backend/src/auth/decorators/permissions.decorator.ts @@ -1,4 +1,5 @@ import { SetMetadata } from '@nestjs/common'; +import { Permission } from "../constants/permissions.enum" export const PERMISSIONS_KEY = 'permissions'; @@ -7,5 +8,5 @@ export const PERMISSIONS_KEY = 'permissions'; * Allows fine-grained permission checks independent of roles * @param permissions - Array of permission names (e.g., 'READ_OWN_PETS', 'CREATE_PETS') */ -export const Permissions = (...permissions: string[]) => +export const Permissions = (...permissions: Permission[]) => SetMetadata(PERMISSIONS_KEY, permissions); diff --git a/backend/src/auth/guards/roles.guard.ts b/backend/src/auth/guards/roles.guard.ts index 9d53ce37..c1e17fcf 100644 --- a/backend/src/auth/guards/roles.guard.ts +++ b/backend/src/auth/guards/roles.guard.ts @@ -22,23 +22,23 @@ export class RolesGuard implements CanActivate { ) {} async canActivate(context: ExecutionContext): Promise { - // Get required roles and permissions from route metadata - const requiredRoles = this.reflector.getAllAndOverride( - ROLES_KEY, - [context.getHandler(), context.getClass()], - ); + const requiredPermissions = + this.reflector.getAllAndOverride( + PERMISSIONS_KEY, + [context.getHandler(), context.getClass()], + ); - const requiredPermissions = this.reflector.getAllAndOverride( - PERMISSIONS_KEY, - [context.getHandler(), context.getClass()], - ); + const requiredRoles = + this.reflector.getAllAndOverride( + ROLES_KEY, + [context.getHandler(), context.getClass()], + ); - // If no roles or permissions are required, allow access - if (!requiredRoles && !requiredPermissions) { + // If nothing required → allow + if (!requiredPermissions?.length && !requiredRoles?.length) { return true; } - // Get current user from request (set by JwtAuthGuard) const request = context.switchToHttp().getRequest(); const user: User = request.user; @@ -46,45 +46,53 @@ export class RolesGuard implements CanActivate { throw new ForbiddenException('User not authenticated'); } - // Check roles if required - if (requiredRoles && requiredRoles.length > 0) { - const userRoles = await this.rolesService.getUserRoles(user.id); - const userRoleNames = userRoles.map((role) => role.name); + /* + |-------------------------------------------------------------------------- + | PERMISSION-BASED CHECK (PRIMARY) + |-------------------------------------------------------------------------- + */ + if (requiredPermissions?.length) { + const userPermissions = + await this.rolesService.getUserPermissions(user.id); - // Check if user has at least one of the required roles - const hasRequiredRole = requiredRoles.some((requiredRole) => - userRoleNames.includes(requiredRole as RoleName), + const hasAllPermissions = requiredPermissions.every((permission) => + this.permissionsService.checkPermissionAccess( + userPermissions, + permission, + ), ); - if (!hasRequiredRole) { + if (!hasAllPermissions) { throw new ForbiddenException( - `Access denied. Required roles: ${requiredRoles.join(', ')}`, + `Access denied. Missing required permissions.`, ); } + + return true; } - // Check permissions if required - if (requiredPermissions && requiredPermissions.length > 0) { - const userPermissions = await this.rolesService.getUserPermissions( - user.id, - ); + /* + |-------------------------------------------------------------------------- + | ROLE-BASED CHECK (SECONDARY / EXPLICIT) + |-------------------------------------------------------------------------- + */ + if (requiredRoles?.length) { + const userRoles = await this.rolesService.getUserRoles(user.id); + const userRoleNames = userRoles.map((r) => r.name); - // Check if user has all required permissions - const hasAllPermissions = requiredPermissions.every( - (requiredPermission) => - this.permissionsService.checkPermissionAccess( - userPermissions, - requiredPermission, - ), + const hasRequiredRole = requiredRoles.some((role) => + userRoleNames.includes(role), ); - if (!hasAllPermissions) { + if (!hasRequiredRole) { throw new ForbiddenException( - `Access denied. Required permissions: ${requiredPermissions.join(', ')}`, + `Access denied. Required roles: ${requiredRoles.join(', ')}`, ); } + + return true; } return true; } -} +} \ No newline at end of file diff --git a/backend/src/auth/seeds/roles-permissions.seed.ts b/backend/src/auth/seeds/roles-permissions.seed.ts index d987d731..5323c6ef 100644 --- a/backend/src/auth/seeds/roles-permissions.seed.ts +++ b/backend/src/auth/seeds/roles-permissions.seed.ts @@ -6,193 +6,124 @@ import { PermissionEntity } from '../entities/permission.entity'; import { RolePermission } from '../entities/role-permission.entity'; import { RoleName } from '../constants/roles.enum'; import { Permission } from '../constants/permissions.enum'; -import { PERMISSION_DEFINITIONS } from '../constants/permission-definitions'; +import { PermissionsService } from '../services/permissions.service'; @Injectable() export class RolesPermissionsSeeder implements OnModuleInit { constructor( @InjectRepository(Role) private readonly roleRepository: Repository, - @InjectRepository(PermissionEntity) - private readonly permissionRepository: Repository, + @InjectRepository(RolePermission) private readonly rolePermissionRepository: Repository, + + @InjectRepository(PermissionEntity) + private readonly permissionRepository: Repository, + + private readonly permissionsService: PermissionsService, ) {} async onModuleInit() { - // Only seed if in development or if explicitly enabled - if ( - process.env.SEED_ROLES_PERMISSIONS === 'true' || - process.env.NODE_ENV === 'development' - ) { - await this.seed(); - } + await this.seed(); } - async seed(): Promise { - console.log('Seeding roles and permissions...'); + async seed() { + // 1️⃣ Seed permissions first + await this.permissionsService.seedPermissions(); - // Seed permissions first - await this.seedPermissions(); + // 2️⃣ Create roles if not exist + const petOwner = await this.createRoleIfNotExists( + RoleName.PetOwner, + 'Pet owner with limited access', + ); - // Seed roles - await this.seedRoles(); + const veterinarian = await this.createRoleIfNotExists( + RoleName.Veterinarian, + 'Licensed veterinarian', + ); - // Assign permissions to roles - await this.assignPermissionsToRoles(); + const vetStaff = await this.createRoleIfNotExists( + RoleName.VetStaff, + 'Veterinary support staff', + ); - console.log('Roles and permissions seeded successfully!'); - } + const admin = await this.createRoleIfNotExists( + RoleName.Admin, + 'System administrator', + true, + ); - private async seedPermissions(): Promise { - for (const definition of PERMISSION_DEFINITIONS) { - const existing = await this.permissionRepository.findOne({ - where: { name: definition.name }, - }); + // 3️⃣ Attach permissions to roles + await this.attachPermissions(petOwner, [ + Permission.READ_OWN_PETS, + Permission.UPDATE_OWN_PETS, + Permission.CREATE_PETS, + Permission.SHARE_RECORDS, + ]); - if (!existing) { - const permission = this.permissionRepository.create({ - name: definition.name, - description: definition.description, - resource: definition.resource, - action: definition.action, - }); - await this.permissionRepository.save(permission); - console.log(`Created permission: ${definition.name}`); - } else { - console.log(`Permission already exists: ${definition.name}`); - } - } - } + await this.attachPermissions(veterinarian, [ + Permission.READ_ALL_PETS, + Permission.UPDATE_MEDICAL_RECORDS, + Permission.CREATE_TREATMENTS, + Permission.PRESCRIBE, + ]); + + await this.attachPermissions(vetStaff, [ + Permission.READ_ASSIGNED_PETS, + Permission.UPDATE_APPOINTMENTS, + Permission.CREATE_NOTES, + ]); - private async seedRoles(): Promise { - // Create PetOwner role (base level, no parent) - const petOwnerRole = await this.createOrUpdateRole({ - name: RoleName.PetOwner, - description: 'Pet owner with access to own pets', - parentRoleId: null, - isSystemRole: true, - }); - - // Create Veterinarian role (parent: PetOwner - inherits PetOwner permissions) - const veterinarianRole = await this.createOrUpdateRole({ - name: RoleName.Veterinarian, - description: 'Veterinarian with access to all pets and medical records', - parentRoleId: petOwnerRole.id, - isSystemRole: true, - }); - - // Create Admin role (parent: Veterinarian - inherits Veterinarian + PetOwner permissions) - await this.createOrUpdateRole({ - name: RoleName.Admin, - description: 'Administrator with all permissions', - parentRoleId: veterinarianRole.id, - isSystemRole: true, - }); + // Admin only gets ALL_PERMISSIONS + await this.attachPermissions(admin, [Permission.ALL_PERMISSIONS]); } - private async createOrUpdateRole(data: { - name: RoleName; - description: string; - parentRoleId: string | null; - isSystemRole: boolean; - }): Promise { - let role = await this.roleRepository.findOne({ - where: { name: data.name }, - }); + private async createRoleIfNotExists( + name: RoleName, + description: string, + isSystemRole = true, + ): Promise { + let role = await this.roleRepository.findOne({ where: { name } }); if (!role) { role = this.roleRepository.create({ - name: data.name, - description: data.description, - parentRoleId: data.parentRoleId ?? undefined, - isSystemRole: data.isSystemRole, + name, + description, + isSystemRole, }); - await this.roleRepository.save(role); - console.log(`Created role: ${data.name}`); - } else { - // Update existing role - role.description = data.description; - if (data.parentRoleId !== null) { - role.parentRoleId = data.parentRoleId; - } - role.isSystemRole = data.isSystemRole; - await this.roleRepository.save(role); - console.log(`Updated role: ${data.name}`); - } - - return role; - } - private async assignPermissionsToRoles(): Promise { - // Get all roles - const adminRole = await this.roleRepository.findOne({ - where: { name: RoleName.Admin }, - }); - const veterinarianRole = await this.roleRepository.findOne({ - where: { name: RoleName.Veterinarian }, - }); - const petOwnerRole = await this.roleRepository.findOne({ - where: { name: RoleName.PetOwner }, - }); - - if (!adminRole || !veterinarianRole || !petOwnerRole) { - throw new Error('Roles not found. Please seed roles first.'); + role = await this.roleRepository.save(role); } - // Assign ALL_PERMISSIONS to Admin - await this.assignPermissionToRole(adminRole.id, Permission.ALL_PERMISSIONS); - - // Assign Veterinarian permissions - await this.assignPermissionToRole( - veterinarianRole.id, - Permission.READ_ALL_PETS, - ); - await this.assignPermissionToRole( - veterinarianRole.id, - Permission.UPDATE_MEDICAL_RECORDS, - ); - await this.assignPermissionToRole( - veterinarianRole.id, - Permission.CREATE_TREATMENTS, - ); - - // Assign PetOwner permissions - await this.assignPermissionToRole( - petOwnerRole.id, - Permission.READ_OWN_PETS, - ); - await this.assignPermissionToRole( - petOwnerRole.id, - Permission.UPDATE_OWN_PETS, - ); - await this.assignPermissionToRole(petOwnerRole.id, Permission.CREATE_PETS); + return role; } - private async assignPermissionToRole( - roleId: string, - permissionName: Permission, + private async attachPermissions( + role: Role, + permissions: Permission[], ): Promise { - const permission = await this.permissionRepository.findOne({ - where: { name: permissionName }, - }); - - if (!permission) { - throw new Error(`Permission ${permissionName} not found`); - } + for (const permissionName of permissions) { + const permission = await this.permissionRepository.findOne({ + where: { name: permissionName }, + }); - // Check if already assigned - const existing = await this.rolePermissionRepository.findOne({ - where: { roleId, permissionId: permission.id }, - }); + if (!permission) continue; - if (!existing) { - const rolePermission = this.rolePermissionRepository.create({ - roleId, - permissionId: permission.id, + const existing = await this.rolePermissionRepository.findOne({ + where: { + roleId: role.id, + permissionId: permission.id, + }, }); - await this.rolePermissionRepository.save(rolePermission); - console.log(`Assigned permission ${permissionName} to role ${roleId}`); + + if (!existing) { + const rolePermission = this.rolePermissionRepository.create({ + roleId: role.id, + permissionId: permission.id, + }); + + await this.rolePermissionRepository.save(rolePermission); + } } } -} +} \ No newline at end of file diff --git a/backend/src/auth/services/roles.service.ts b/backend/src/auth/services/roles.service.ts index a6642cd5..28483358 100644 --- a/backend/src/auth/services/roles.service.ts +++ b/backend/src/auth/services/roles.service.ts @@ -1,307 +1,183 @@ +// auth/services/roles.service.ts + import { Injectable, NotFoundException, BadRequestException, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, In } from 'typeorm'; +import { Repository } from 'typeorm'; + import { Role } from '../entities/role.entity'; -import { PermissionEntity } from '../entities/permission.entity'; import { UserRole } from '../entities/user-role.entity'; -import { - RoleAuditLog, - RoleAuditAction, -} from '../entities/role-audit-log.entity'; -import { RoleName } from '../constants/roles.enum'; +import { RolePermission } from '../entities/role-permission.entity'; +import { RoleAuditLog, RoleAuditAction } from '../entities/role-audit-log.entity'; import { Permission } from '../constants/permissions.enum'; -import { PermissionsService } from './permissions.service'; +import { AssignRoleDto, RemoveRoleDto } from '../dto/role.dto'; @Injectable() export class RolesService { constructor( @InjectRepository(Role) private readonly roleRepository: Repository, - @InjectRepository(PermissionEntity) - private readonly permissionRepository: Repository, + @InjectRepository(UserRole) private readonly userRoleRepository: Repository, - @InjectRepository(RoleAuditLog) - private readonly auditLogRepository: Repository, - private readonly permissionsService: PermissionsService, - ) {} - - /** - * Assign a role to a user with audit logging - */ - async assignRole( - userId: string, - roleId: string, - assignedBy: string, - reason?: string, - ): Promise { - // Check if role exists - const role = await this.roleRepository.findOne({ where: { id: roleId } }); - if (!role) { - throw new NotFoundException(`Role with ID ${roleId} not found`); - } - - // Check if user already has this role (active) - const existingUserRole = await this.userRoleRepository.findOne({ - where: { userId, roleId, isActive: true }, - }); - - if (existingUserRole) { - throw new BadRequestException('User already has this role'); - } - - // Create user role assignment - const userRole = this.userRoleRepository.create({ - userId, - roleId, - assignedBy, - assignedAt: new Date(), - isActive: true, - }); - - const savedUserRole = await this.userRoleRepository.save(userRole); - - // Create audit log entry - await this.createAuditLog( - userId, - roleId, - RoleAuditAction.ASSIGNED, - assignedBy, - reason, - ); - - return savedUserRole; - } - - /** - * Remove a role from a user with audit logging - */ - async removeRole( - userId: string, - roleId: string, - removedBy: string, - reason?: string, - ): Promise { - // Find active user role - const userRole = await this.userRoleRepository.findOne({ - where: { userId, roleId, isActive: true }, - }); - if (!userRole) { - throw new NotFoundException('User does not have this role'); - } + @InjectRepository(RolePermission) + private readonly rolePermissionRepository: Repository, - // Soft delete by setting isActive to false - userRole.isActive = false; - await this.userRoleRepository.save(userRole); - - // Create audit log entry - await this.createAuditLog( - userId, - roleId, - RoleAuditAction.REMOVED, - removedBy, - reason, - ); - } + @InjectRepository(RoleAuditLog) + private readonly roleAuditRepository: Repository, + ) {} - /** - * Get all active roles for a user + /* + |-------------------------------------------------------------------------- + | GET USER ROLES (ACTIVE ONLY) + |-------------------------------------------------------------------------- */ async getUserRoles(userId: string): Promise { const userRoles = await this.userRoleRepository.find({ where: { userId, isActive: true }, - relations: [ - 'role', - 'role.parentRole', - 'role.rolePermissions', - 'role.rolePermissions.permission', - ], + relations: ['role'], }); return userRoles.map((ur) => ur.role); } - /** - * Get all permissions for a user (aggregated from roles and hierarchy) + /* + |-------------------------------------------------------------------------- + | GET USER PERMISSIONS (WITH HIERARCHY SUPPORT) + |-------------------------------------------------------------------------- */ async getUserPermissions(userId: string): Promise { - const userRoles = await this.getUserRoles(userId); - const allRoleIds = new Set(); - - // Collect all role IDs including parent roles from hierarchy - for (const role of userRoles) { - allRoleIds.add(role.id); - const parentRoles = await this.getRoleHierarchy(role.id); - parentRoles.forEach((parent) => allRoleIds.add(parent.id)); - } + const roles = await this.getUserRoles(userId); - // Get all permissions from all roles - const roles = await this.roleRepository.find({ - where: { id: In(Array.from(allRoleIds)) }, - relations: ['rolePermissions', 'rolePermissions.permission'], - }); - - const permissions = new Set(); + const visitedRoles = new Set(); + const permissionSet = new Set(); for (const role of roles) { - // Check if role has ALL_PERMISSIONS - const allPermissions = role.rolePermissions?.find( - (rp) => rp.permission.name === Permission.ALL_PERMISSIONS, - ); - - if (allPermissions) { - // Early return - user has all permissions - return [Permission.ALL_PERMISSIONS]; - } - - // Collect all permissions from this role - role.rolePermissions?.forEach((rp) => { - if (rp.permission) { - permissions.add(rp.permission.name); - } - }); + await this.collectRolePermissions(role.id, permissionSet, visitedRoles); } - return Array.from(permissions); + return Array.from(permissionSet); } - /** - * Check if user has a specific role - */ - async hasRole(userId: string, roleName: RoleName): Promise { - const userRoles = await this.getUserRoles(userId); - return userRoles.some((role) => role.name === roleName); - } + private async collectRolePermissions( + roleId: string, + permissionSet: Set, + visitedRoles: Set, + ) { + if (visitedRoles.has(roleId)) return; - /** - * Check if user has a specific permission - */ - async hasPermission( - userId: string, - permissionName: Permission, - ): Promise { - const userPermissions = await this.getUserPermissions(userId); - return this.permissionsService.checkPermissionAccess( - userPermissions, - permissionName, - ); - } + visitedRoles.add(roleId); - /** - * Get all parent roles in the hierarchy for a given role - */ - async getRoleHierarchy(roleId: string): Promise { - const hierarchy: Role[] = []; - let currentRole = await this.roleRepository.findOne({ + const role = await this.roleRepository.findOne({ where: { id: roleId }, - relations: ['parentRole'], + relations: [ + 'rolePermissions', + 'rolePermissions.permission', + 'parentRole', + ], }); - while (currentRole?.parentRole) { - const parent = await this.roleRepository.findOne({ - where: { id: currentRole.parentRoleId }, - relations: ['parentRole'], - }); - if (parent) { - hierarchy.push(parent); - currentRole = parent; - } else { - break; - } + if (!role) return; + + for (const rp of role.rolePermissions) { + permissionSet.add(rp.permission.name); } - return hierarchy; + if (role.parentRole) { + await this.collectRolePermissions( + role.parentRole.id, + permissionSet, + visitedRoles, + ); + } } - /** - * Aggregate permissions from multiple roles (including hierarchy) + /* + |-------------------------------------------------------------------------- + | ASSIGN ROLE (ADMIN ONLY) + |-------------------------------------------------------------------------- */ - async aggregatePermissions(roleIds: string[]): Promise { - const allRoleIds = new Set(roleIds); - - // Add parent roles from hierarchy - for (const roleId of roleIds) { - const parents = await this.getRoleHierarchy(roleId); - parents.forEach((parent) => allRoleIds.add(parent.id)); - } + async assignRole(dto: AssignRoleDto, performedBy: string) { + const { userId, roleId, reason } = dto; - // Get all roles with their permissions - const roles = await this.roleRepository.find({ - where: { id: In(Array.from(allRoleIds)) }, - relations: ['rolePermissions', 'rolePermissions.permission'], + const role = await this.roleRepository.findOne({ + where: { id: roleId }, }); - const permissions = new Set(); + if (!role) { + throw new NotFoundException('Role not found'); + } - for (const role of roles) { - // Check for ALL_PERMISSIONS - const allPermissions = role.rolePermissions?.find( - (rp) => rp.permission.name === Permission.ALL_PERMISSIONS, - ); + const existing = await this.userRoleRepository.findOne({ + where: { userId, roleId }, + }); - if (allPermissions) { - return [Permission.ALL_PERMISSIONS]; - } + if (existing && existing.isActive) { + throw new BadRequestException('User already has this role'); + } - // Collect permissions - role.rolePermissions?.forEach((rp) => { - if (rp.permission) { - permissions.add(rp.permission.name); - } + if (existing) { + existing.isActive = true; + await this.userRoleRepository.save(existing); + } else { + const newUserRole = this.userRoleRepository.create({ + userId, + roleId, + assignedBy: performedBy, + isActive: true, }); - } - return Array.from(permissions); - } + await this.userRoleRepository.save(newUserRole); + } - /** - * Create audit log entry - */ - private async createAuditLog( - userId: string, - roleId: string, - action: RoleAuditAction, - performedBy: string, - reason?: string, - metadata?: Record, - ): Promise { - const auditLog = this.auditLogRepository.create({ + // Audit log + await this.roleAuditRepository.save({ userId, roleId, - action, + action: RoleAuditAction.ASSIGNED, performedBy, reason, - metadata, }); - return await this.auditLogRepository.save(auditLog); + return { message: 'Role assigned successfully' }; } - /** - * Get role by name + /* + |-------------------------------------------------------------------------- + | REMOVE ROLE (SOFT REMOVE) + |-------------------------------------------------------------------------- */ - async getRoleByName(name: RoleName): Promise { - return await this.roleRepository.findOne({ - where: { name }, - relations: ['rolePermissions', 'rolePermissions.permission'], + async removeRole(dto: RemoveRoleDto, performedBy: string) { + const { userId, roleId, reason } = dto; + + if (userId === performedBy) { + throw new BadRequestException('You cannot remove your own role'); + } + + const userRole = await this.userRoleRepository.findOne({ + where: { userId, roleId, isActive: true }, }); - } - /** - * Get all roles - */ - async getAllRoles(): Promise { - return await this.roleRepository.find({ - relations: [ - 'parentRole', - 'rolePermissions', - 'rolePermissions.permission', - ], + if (!userRole) { + throw new NotFoundException('Active role not found for user'); + } + + userRole.isActive = false; + await this.userRoleRepository.save(userRole); + + // Audit log + await this.roleAuditRepository.save({ + userId, + roleId, + action: RoleAuditAction.REMOVED, + performedBy, + reason, }); + + return { message: 'Role removed successfully' }; } -} +} \ No newline at end of file