From e5b71a5cc11672e98173e8cc6500c03b1e51fe99 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Mon, 1 Dec 2025 15:29:42 +0100 Subject: [PATCH 01/10] chore(deps): Removed unused deps and updated lockfiles --- .envrc | 2 +- frontend/package.json | 3 +- frontend/pnpm-lock.yaml | 37 +- src/@shared/package.json | 2 +- src/auth/package.json | 4 +- src/chat/package.json | 4 +- src/icons/package.json | 4 +- src/package.json | 13 +- src/pnpm-lock.yaml | 1168 ++++++-------------------------------- src/pnpm-workspace.yaml | 8 +- src/user/package.json | 4 +- 11 files changed, 190 insertions(+), 1059 deletions(-) diff --git a/.envrc b/.envrc index 3550a30..24e198b 100644 --- a/.envrc +++ b/.envrc @@ -1 +1 @@ -use flake +use flake "path:$(realpath -m .)" diff --git a/frontend/package.json b/frontend/package.json index 04bd252..b0fc5a2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,13 +11,12 @@ "devDependencies": { "@types/js-cookie": "^3.0.6", "typescript": "~5.9.3", - "vite": "^7.2.6", + "vite": "^7.2.7", "vite-tsconfig-paths": "^5.1.4" }, "dependencies": { "@tailwindcss/vite": "^4.1.17", "js-cookie": "^3.0.5", - "openapi-fetch": "^0.15.0", "socket.io-client": "^4.8.1", "tailwindcss": "^4.1.17" } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 6addf30..ac3aea5 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -10,13 +10,10 @@ importers: dependencies: '@tailwindcss/vite': specifier: ^4.1.17 - version: 4.1.17(vite@7.2.6(jiti@2.6.1)(lightningcss@1.30.2)) + version: 4.1.17(vite@7.2.7(jiti@2.6.1)(lightningcss@1.30.2)) js-cookie: specifier: ^3.0.5 version: 3.0.5 - openapi-fetch: - specifier: ^0.15.0 - version: 0.15.0 socket.io-client: specifier: ^4.8.1 version: 4.8.1 @@ -31,11 +28,11 @@ importers: specifier: ~5.9.3 version: 5.9.3 vite: - specifier: ^7.2.6 - version: 7.2.6(jiti@2.6.1)(lightningcss@1.30.2) + specifier: ^7.2.7 + version: 7.2.7(jiti@2.6.1)(lightningcss@1.30.2) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.3)(vite@7.2.6(jiti@2.6.1)(lightningcss@1.30.2)) + version: 5.1.4(typescript@5.9.3)(vite@7.2.7(jiti@2.6.1)(lightningcss@1.30.2)) packages: @@ -567,12 +564,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - openapi-fetch@0.15.0: - resolution: {integrity: sha512-OjQUdi61WO4HYhr9+byCPMj0+bgste/LtSBEcV6FzDdONTs7x0fWn8/ndoYwzqCsKWIxEZwo4FN/TG1c1rI8IQ==} - - openapi-typescript-helpers@0.0.15: - resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -635,8 +626,8 @@ packages: vite: optional: true - vite@7.2.6: - resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} + vite@7.2.7: + resolution: {integrity: sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -919,12 +910,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.17 '@tailwindcss/oxide-win32-x64-msvc': 4.1.17 - '@tailwindcss/vite@4.1.17(vite@7.2.6(jiti@2.6.1)(lightningcss@1.30.2))': + '@tailwindcss/vite@4.1.17(vite@7.2.7(jiti@2.6.1)(lightningcss@1.30.2))': dependencies: '@tailwindcss/node': 4.1.17 '@tailwindcss/oxide': 4.1.17 tailwindcss: 4.1.17 - vite: 7.2.6(jiti@2.6.1)(lightningcss@1.30.2) + vite: 7.2.7(jiti@2.6.1)(lightningcss@1.30.2) '@types/estree@1.0.8': {} @@ -1060,12 +1051,6 @@ snapshots: nanoid@3.3.11: {} - openapi-fetch@0.15.0: - dependencies: - openapi-typescript-helpers: 0.0.15 - - openapi-typescript-helpers@0.0.15: {} - picocolors@1.1.1: {} picomatch@4.0.3: {} @@ -1139,18 +1124,18 @@ snapshots: typescript@5.9.3: {} - vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.6(jiti@2.6.1)(lightningcss@1.30.2)): + vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.7(jiti@2.6.1)(lightningcss@1.30.2)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) optionalDependencies: - vite: 7.2.6(jiti@2.6.1)(lightningcss@1.30.2) + vite: 7.2.7(jiti@2.6.1)(lightningcss@1.30.2) transitivePeerDependencies: - supports-color - typescript - vite@7.2.6(jiti@2.6.1)(lightningcss@1.30.2): + vite@7.2.7(jiti@2.6.1)(lightningcss@1.30.2): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) diff --git a/src/@shared/package.json b/src/@shared/package.json index d563f63..f3309d6 100644 --- a/src/@shared/package.json +++ b/src/@shared/package.json @@ -25,6 +25,6 @@ }, "devDependencies": { "@types/better-sqlite3": "^7.6.13", - "@types/node": "^22.19.1" + "@types/node": "^22.19.2" } } diff --git a/src/auth/package.json b/src/auth/package.json index 81d3afe..c95aa25 100644 --- a/src/auth/package.json +++ b/src/auth/package.json @@ -30,9 +30,9 @@ "typebox": "^1.0.61" }, "devDependencies": { - "@types/node": "^22.19.1", + "@types/node": "^22.19.2", "rollup-plugin-node-externals": "^8.1.2", - "vite": "^7.2.6", + "vite": "^7.2.7", "vite-tsconfig-paths": "^5.1.4" } } diff --git a/src/chat/package.json b/src/chat/package.json index 1e61398..12a2b6c 100644 --- a/src/chat/package.json +++ b/src/chat/package.json @@ -30,9 +30,9 @@ "typebox": "^1.0.61" }, "devDependencies": { - "@types/node": "^22.19.1", + "@types/node": "^22.19.2", "rollup-plugin-node-externals": "^8.1.2", - "vite": "^7.2.6", + "vite": "^7.2.7", "vite-tsconfig-paths": "^5.1.4" } } diff --git a/src/icons/package.json b/src/icons/package.json index 3bef3d5..bce9ed7 100644 --- a/src/icons/package.json +++ b/src/icons/package.json @@ -29,9 +29,9 @@ "sharp": "^0.34.5" }, "devDependencies": { - "@types/node": "^22.19.1", + "@types/node": "^22.19.2", "rollup-plugin-node-externals": "^8.1.2", - "vite": "^7.2.6", + "vite": "^7.2.7", "vite-tsconfig-paths": "^5.1.4" } } diff --git a/src/package.json b/src/package.json index 45b7a6d..d1b8521 100644 --- a/src/package.json +++ b/src/package.json @@ -23,20 +23,17 @@ }, "devDependencies": { "@eslint/js": "^9.39.1", - "@openapitools/openapi-generator-cli": "^2.25.2", - "@typescript-eslint/eslint-plugin": "^8.48.1", - "@typescript-eslint/parser": "^8.48.1", + "@typescript-eslint/eslint-plugin": "^8.49.0", + "@typescript-eslint/parser": "^8.49.0", "eslint": "^9.39.1", "husky": "^9.1.7", "lint-staged": "^16.2.7", - "openapi-generator-cli": "^1.0.0", - "openapi-typescript": "^7.10.1", "typescript": "^5.9.3", - "typescript-eslint": "^8.48.1", - "vite": "^7.2.6" + "typescript-eslint": "^8.49.0", + "vite": "^7.2.7" }, "dependencies": { - "@redocly/cli": "^2.12.3", + "@redocly/cli": "^2.12.5", "bindings": "^1.5.0" } } diff --git a/src/pnpm-lock.yaml b/src/pnpm-lock.yaml index a50dc94..cab5f92 100644 --- a/src/pnpm-lock.yaml +++ b/src/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@redocly/cli': - specifier: ^2.12.3 - version: 2.12.3(@opentelemetry/api@1.9.0)(ajv@8.17.1)(core-js@3.47.0) + specifier: ^2.12.5 + version: 2.12.5(@opentelemetry/api@1.9.0)(ajv@8.17.1)(core-js@3.47.0) bindings: specifier: ^1.5.0 version: 1.5.0 @@ -18,15 +18,12 @@ importers: '@eslint/js': specifier: ^9.39.1 version: 9.39.1 - '@openapitools/openapi-generator-cli': - specifier: ^2.25.2 - version: 2.25.2(@types/node@24.10.1) '@typescript-eslint/eslint-plugin': - specifier: ^8.48.1 - version: 8.48.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) + specifier: ^8.49.0 + version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) '@typescript-eslint/parser': - specifier: ^8.48.1 - version: 8.48.1(eslint@9.39.1)(typescript@5.9.3) + specifier: ^8.49.0 + version: 8.49.0(eslint@9.39.1)(typescript@5.9.3) eslint: specifier: ^9.39.1 version: 9.39.1 @@ -36,21 +33,15 @@ importers: lint-staged: specifier: ^16.2.7 version: 16.2.7 - openapi-generator-cli: - specifier: ^1.0.0 - version: 1.0.0 - openapi-typescript: - specifier: ^7.10.1 - version: 7.10.1(typescript@5.9.3) typescript: specifier: ^5.9.3 version: 5.9.3 typescript-eslint: - specifier: ^8.48.1 - version: 8.48.1(eslint@9.39.1)(typescript@5.9.3) + specifier: ^8.49.0 + version: 8.49.0(eslint@9.39.1)(typescript@5.9.3) vite: - specifier: ^7.2.6 - version: 7.2.6(@types/node@24.10.1)(yaml@2.8.2) + specifier: ^7.2.7 + version: 7.2.7(@types/node@24.10.2)(yaml@2.8.2) '@shared': dependencies: @@ -98,8 +89,8 @@ importers: specifier: ^7.6.13 version: 7.6.13 '@types/node': - specifier: ^22.19.1 - version: 22.19.1 + specifier: ^22.19.2 + version: 22.19.2 auth: dependencies: @@ -135,17 +126,17 @@ importers: version: 1.0.61 devDependencies: '@types/node': - specifier: ^22.19.1 - version: 22.19.1 + specifier: ^22.19.2 + version: 22.19.2 rollup-plugin-node-externals: specifier: ^8.1.2 version: 8.1.2(rollup@4.53.3) vite: - specifier: ^7.2.6 - version: 7.2.6(@types/node@22.19.1)(yaml@2.8.2) + specifier: ^7.2.7 + version: 7.2.7(@types/node@22.19.2)(yaml@2.8.2) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.3)(vite@7.2.6(@types/node@22.19.1)(yaml@2.8.2)) + version: 5.1.4(typescript@5.9.3)(vite@7.2.7(@types/node@22.19.2)(yaml@2.8.2)) chat: dependencies: @@ -181,17 +172,17 @@ importers: version: 1.0.61 devDependencies: '@types/node': - specifier: ^22.19.1 - version: 22.19.1 + specifier: ^22.19.2 + version: 22.19.2 rollup-plugin-node-externals: specifier: ^8.1.2 version: 8.1.2(rollup@4.53.3) vite: - specifier: ^7.2.6 - version: 7.2.6(@types/node@22.19.1)(yaml@2.8.2) + specifier: ^7.2.7 + version: 7.2.7(@types/node@22.19.2)(yaml@2.8.2) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.3)(vite@7.2.6(@types/node@22.19.1)(yaml@2.8.2)) + version: 5.1.4(typescript@5.9.3)(vite@7.2.7(@types/node@22.19.2)(yaml@2.8.2)) icons: dependencies: @@ -227,17 +218,17 @@ importers: version: 0.34.5 devDependencies: '@types/node': - specifier: ^22.19.1 - version: 22.19.1 + specifier: ^22.19.2 + version: 22.19.2 rollup-plugin-node-externals: specifier: ^8.1.2 version: 8.1.2(rollup@4.53.3) vite: - specifier: ^7.2.6 - version: 7.2.6(@types/node@22.19.1)(yaml@2.8.2) + specifier: ^7.2.7 + version: 7.2.7(@types/node@22.19.2)(yaml@2.8.2) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.3)(vite@7.2.6(@types/node@22.19.1)(yaml@2.8.2)) + version: 5.1.4(typescript@5.9.3)(vite@7.2.7(@types/node@22.19.2)(yaml@2.8.2)) user: dependencies: @@ -270,17 +261,17 @@ importers: version: 1.0.61 devDependencies: '@types/node': - specifier: ^22.19.1 - version: 22.19.1 + specifier: ^22.19.2 + version: 22.19.2 rollup-plugin-node-externals: specifier: ^8.1.2 version: 8.1.2(rollup@4.53.3) vite: - specifier: ^7.2.6 - version: 7.2.6(@types/node@22.19.1)(yaml@2.8.2) + specifier: ^7.2.7 + version: 7.2.7(@types/node@22.19.2)(yaml@2.8.2) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.3)(vite@7.2.6(@types/node@22.19.1)(yaml@2.8.2)) + version: 5.1.4(typescript@5.9.3)(vite@7.2.7(@types/node@22.19.2)(yaml@2.8.2)) packages: @@ -296,9 +287,6 @@ packages: resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} - '@borewit/text-codec@0.1.1': - resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} - '@emnapi/runtime@1.7.1': resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} @@ -749,15 +737,6 @@ packages: cpu: [x64] os: [win32] - '@inquirer/external-editor@1.0.3': - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@isaacs/balanced-match@4.0.1': resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} engines: {node: 20 || >=22} @@ -770,71 +749,14 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@lukeed/csprng@1.1.0': - resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} - engines: {node: '>=8'} - '@lukeed/ms@2.0.2': resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} engines: {node: '>=8'} - '@nestjs/axios@4.0.1': - resolution: {integrity: sha512-68pFJgu+/AZbWkGu65Z3r55bTsCPlgyKaV4BSG8yUAD72q1PPuyVRgUwFv6BxdnibTUHlyxm06FmYWNC+bjN7A==} - peerDependencies: - '@nestjs/common': ^10.0.0 || ^11.0.0 - axios: ^1.3.1 - rxjs: ^7.0.0 - - '@nestjs/common@11.1.9': - resolution: {integrity: sha512-zDntUTReRbAThIfSp3dQZ9kKqI+LjgLp5YZN5c1bgNRDuoeLySAoZg46Bg1a+uV8TMgIRziHocglKGNzr6l+bQ==} - peerDependencies: - class-transformer: '>=0.4.1' - class-validator: '>=0.13.2' - reflect-metadata: ^0.1.12 || ^0.2.0 - rxjs: ^7.1.0 - peerDependenciesMeta: - class-transformer: - optional: true - class-validator: - optional: true - - '@nestjs/core@11.1.9': - resolution: {integrity: sha512-a00B0BM4X+9z+t3UxJqIZlemIwCQdYoPKrMcM+ky4z3pkqqG1eTWexjs+YXpGObnLnjtMPVKWlcZHp3adDYvUw==} - engines: {node: '>= 20'} - peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/microservices': ^11.0.0 - '@nestjs/platform-express': ^11.0.0 - '@nestjs/websockets': ^11.0.0 - reflect-metadata: ^0.1.12 || ^0.2.0 - rxjs: ^7.1.0 - peerDependenciesMeta: - '@nestjs/microservices': - optional: true - '@nestjs/platform-express': - optional: true - '@nestjs/websockets': - optional: true - '@noble/hashes@1.8.0': resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} - '@nuxt/opencollective@0.4.1': - resolution: {integrity: sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==} - engines: {node: ^14.18.0 || >=16.10.0, npm: '>=5.10.0'} - hasBin: true - - '@nuxtjs/opencollective@0.3.2': - resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==} - engines: {node: '>=8.0.0', npm: '>=5.0.0'} - hasBin: true - - '@openapitools/openapi-generator-cli@2.25.2': - resolution: {integrity: sha512-TXElbW1NXCy0EECXiO5AD2ZzT1dmaCs41Z8t3pBUGaJf8zgF/Lm0P6GRhVEpw29iHBNjZcy8nrgQ1acUfuCdng==} - engines: {node: '>=16'} - hasBin: true - '@opentelemetry/api-logs@0.202.0': resolution: {integrity: sha512-fTBjMqKCfotFWfLzaKyhjLvyEyq5vDKTTFfBmx21btv3gvy8Lq6N5Dh2OzqeuN4DjtpSvNT1uNVfg08eD2Rfxw==} engines: {node: '>=8.0.0'} @@ -943,27 +865,27 @@ packages: '@redocly/ajv@8.17.1': resolution: {integrity: sha512-EDtsGZS964mf9zAUXAl9Ew16eYbeyAFWhsPr0fX6oaJxgd8rApYlPBf0joyhnUHz88WxrigyFtTaqqzXNzPgqw==} - '@redocly/cli@2.12.3': - resolution: {integrity: sha512-1SDW551scNdb4HmNpzyUf4gjsK89KkRUeXF91VVMRkQ5+lFEq1Nj259jN1M25uOd/cg1QjKE3kIbnN1dxPa3ng==} + '@redocly/cli@2.12.5': + resolution: {integrity: sha512-/kIIKHRpsIu/QxvLzsuxsFFbMJqQmCcbcZUSkNhbeJdYkB+3y5R6xpsLOlcaC33wJePcbDg2v3VYfKEhbU6rtA==} engines: {node: '>=22.12.0 || >=20.19.0 <21.0.0', npm: '>=10'} hasBin: true '@redocly/config@0.22.2': resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==} - '@redocly/config@0.40.0': - resolution: {integrity: sha512-MZQZs7QEGnue3rVN9Q9QvDbcGjesxbpKXUvDeckS69R1xjtgsnT9B39VA25zmwSJtgUeA9ST+sMf9GxIqixNbw==} + '@redocly/config@0.41.0': + resolution: {integrity: sha512-8yJ2e+ex8KVF25zijdpDbAEjyubk7NLfHsLI8h0MUnLEo2iEg6rTCDT9Qw71XDqd5UlXvfJb0Z0h6dd+Y6pWLw==} - '@redocly/openapi-core@1.34.5': - resolution: {integrity: sha512-0EbE8LRbkogtcCXU7liAyC00n9uNG9hJ+eMyHFdUsy9lB/WGqnEBgwjA9q2cyzAVcdTkQqTBBU1XePNnN3OijA==} + '@redocly/openapi-core@1.34.6': + resolution: {integrity: sha512-2+O+riuIUgVSuLl3Lyh5AplWZyVMNuG2F98/o6NrutKJfW4/GTZdPpZlIphS0HGgcOHgmWcCSHj+dWFlZaGSHw==} engines: {node: '>=18.17.0', npm: '>=9.5.0'} - '@redocly/openapi-core@2.12.3': - resolution: {integrity: sha512-3gdSRftIeUbzXvwDi/tBjO0uj9PzR0XzbWjNwuu3HlVXJ1ElB+K31AnzQ2iA6mjIHq9uvmLRXAs9MsP/0Hbzug==} + '@redocly/openapi-core@2.12.5': + resolution: {integrity: sha512-PlzsfOgPUWg4OmyznsIWQ6EnfZK6KSOn2MG4sZS3DY9cHgno5YywNy8zFQWHADXLqR7hTw2M7VvbiMKVJxzyiA==} engines: {node: '>=22.12.0 || >=20.19.0 <21.0.0', npm: '>=10'} - '@redocly/respect-core@2.12.3': - resolution: {integrity: sha512-ZYqrLBlRVVHwgPawOjo94sKmeuuien77xtkXluTa6+y/wkQ8c5oYY7OqWbasMv0IoxSPehwVMa0AL0OCQP3uCQ==} + '@redocly/respect-core@2.12.5': + resolution: {integrity: sha512-c0Tj+Yqd48uCD+fo0WtNeHeVIBkSWNpyT+L3ViAyMXxWD6HiMaA7JTb+IXJXhWdcUrVwd4l1gMuduSyvzFX2gg==} engines: {node: '>=22.12.0 || >=20.19.0 <21.0.0', npm: '>=10'} '@rollup/rollup-android-arm-eabi@4.53.3': @@ -1082,16 +1004,6 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - '@tokenizer/inflate@0.3.1': - resolution: {integrity: sha512-4oeoZEBQdLdt5WmP/hx1KZ6D3/Oid/0cUb2nk4F0pTDAWy+KCH3/EnAkZF/bvckWo8I33EqBm01lIPgmgc8rCA==} - engines: {node: '>=18'} - - '@tokenizer/token@0.3.0': - resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - - '@tootallnate/quickjs-emscripten@0.23.0': - resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - '@types/bcrypt@6.0.0': resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==} @@ -1107,11 +1019,11 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@22.19.1': - resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==} + '@types/node@22.19.2': + resolution: {integrity: sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==} - '@types/node@24.10.1': - resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + '@types/node@24.10.2': + resolution: {integrity: sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==} '@types/stylis@4.2.5': resolution: {integrity: sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==} @@ -1119,63 +1031,63 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - '@typescript-eslint/eslint-plugin@8.48.1': - resolution: {integrity: sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA==} + '@typescript-eslint/eslint-plugin@8.49.0': + resolution: {integrity: sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.48.1 + '@typescript-eslint/parser': ^8.49.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.48.1': - resolution: {integrity: sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==} + '@typescript-eslint/parser@8.49.0': + resolution: {integrity: sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.48.1': - resolution: {integrity: sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==} + '@typescript-eslint/project-service@8.49.0': + resolution: {integrity: sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.48.1': - resolution: {integrity: sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w==} + '@typescript-eslint/scope-manager@8.49.0': + resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.48.1': - resolution: {integrity: sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==} + '@typescript-eslint/tsconfig-utils@8.49.0': + resolution: {integrity: sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.48.1': - resolution: {integrity: sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg==} + '@typescript-eslint/type-utils@8.49.0': + resolution: {integrity: sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.48.1': - resolution: {integrity: sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==} + '@typescript-eslint/types@8.49.0': + resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.48.1': - resolution: {integrity: sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==} + '@typescript-eslint/typescript-estree@8.49.0': + resolution: {integrity: sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.48.1': - resolution: {integrity: sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA==} + '@typescript-eslint/utils@8.49.0': + resolution: {integrity: sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.48.1': - resolution: {integrity: sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==} + '@typescript-eslint/visitor-keys@8.49.0': + resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} abort-controller@3.0.0: @@ -1217,14 +1129,6 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - ansi-escapes@7.2.0: resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==} engines: {node: '>=18'} @@ -1255,10 +1159,6 @@ packages: asn1.js@5.4.1: resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} - ast-types@0.13.4: - resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} - engines: {node: '>=4'} - asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -1269,9 +1169,6 @@ packages: avvio@9.1.0: resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==} - axios@1.13.2: - resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1282,10 +1179,6 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - basic-ftp@5.0.5: - resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} - engines: {node: '>=10.0.0'} - bcrypt@6.0.0: resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==} engines: {node: '>= 18'} @@ -1347,12 +1240,6 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - change-case@5.4.4: - resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} - - chardet@2.1.1: - resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - charenc@0.0.2: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} @@ -1370,37 +1257,17 @@ packages: classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} - cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - cli-truncate@5.1.1: resolution: {integrity: sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==} engines: {node: '>=20'} - cli-width@3.0.0: - resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} - engines: {node: '>= 10'} - cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - close-with-grace@2.3.0: resolution: {integrity: sha512-38BS9BuqAml6XFIlSWQcj3eivE05yFV6cJDuYoNGiHrE+h9ud1JtMJIVKXdLWa2Uo2Xt7q/GYczOesEchvBEsw==} @@ -1429,38 +1296,15 @@ packages: resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} engines: {node: '>=20'} - commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - commist@3.2.0: resolution: {integrity: sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==} - compare-versions@6.1.1: - resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - concurrently@9.2.1: - resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} - engines: {node: '>=18'} - hasBin: true - confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} - consola@2.15.3: - resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} - - consola@3.4.2: - resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} - engines: {node: ^14.18.0 || >=16.10.0} - - console.table@0.10.0: - resolution: {integrity: sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==} - engines: {node: '> 0.10'} - content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -1501,10 +1345,6 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - data-uri-to-buffer@6.0.2: - resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} - engines: {node: '>= 14'} - dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} @@ -1540,13 +1380,6 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - - degenerator@5.0.1: - resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} - engines: {node: '>= 14'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -1563,8 +1396,8 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - dompurify@3.3.0: - resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==} + dompurify@3.3.1: + resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} dotenv@16.4.7: resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} @@ -1584,9 +1417,6 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - easy-table@1.1.0: - resolution: {integrity: sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==} - ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} @@ -1645,19 +1475,10 @@ packages: escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true - eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1684,11 +1505,6 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - esquery@1.6.0: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} @@ -1716,8 +1532,8 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} - fast-copy@4.0.0: - resolution: {integrity: sha512-/oA0gx1xyXE9R2YlV4FXwZJXngFdm9Du0zN8FhY38jnLkhp1u35h6bCyKgRhlsA6C9I+1vfXE4KISdt7xc6M9w==} + fast-copy@4.0.1: + resolution: {integrity: sha512-+uUOQlhsaswsizHFmEFAQhB3lSiQ+lisxl50N6ZP0wywlZeWsIESxSi9ftPEps8UGfiBzyYP7x27zA674WUvXw==} fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} @@ -1783,21 +1599,10 @@ packages: picomatch: optional: true - fflate@0.8.2: - resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} - - figures@3.2.0: - resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} - engines: {node: '>=8'} - file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - file-type@21.1.0: - resolution: {integrity: sha512-boU4EHmP3JXkwDo4uhyBhTt5pPstxB6eEXKJBu2yu2l7aAMMm7QQYQEzssJmKReZYrFdFOJS8koVo6bXIBGDqA==} - engines: {node: '>=20'} - file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} @@ -1824,15 +1629,6 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - foreach@2.0.6: resolution: {integrity: sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==} @@ -1851,10 +1647,6 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fs-extra@11.3.2: - resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} - engines: {node: '>=14.14'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1883,10 +1675,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-uri@6.0.5: - resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} - engines: {node: '>= 14'} - github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} @@ -1903,10 +1691,6 @@ packages: engines: {node: 20 || >=22} hasBin: true - glob@13.0.0: - resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} - engines: {node: 20 || >=22} - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -1918,12 +1702,6 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -1952,10 +1730,6 @@ packages: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} - http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} - http2-client@1.3.5: resolution: {integrity: sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==} @@ -1991,24 +1765,12 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - index-to-position@1.2.0: - resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} - engines: {node: '>=18'} - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - inquirer@8.2.7: - resolution: {integrity: sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==} - engines: {node: '>=12.0.0'} - - ip-address@10.1.0: - resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} - engines: {node: '>= 12'} - ipaddr.js@2.3.0: resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} engines: {node: '>= 10'} @@ -2038,18 +1800,10 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - isbinaryfile@4.0.10: resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} engines: {node: '>= 8.0.0'} @@ -2057,10 +1811,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - iterare@1.2.1: - resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} - engines: {node: '>=6'} - jackspeak@4.1.1: resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} engines: {node: 20 || >=22} @@ -2110,9 +1860,6 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - jsonfile@6.2.0: - resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} - jsonpath-rfc9535@1.3.0: resolution: {integrity: sha512-3jFHya7oZ45aDxIIdx+/zQARahHXxFSMWBkcBUldfXpLS9VCXDJyTKt35kQfEXLqh0K3Ixw/9xFnvcDStaxh7Q==} engines: {node: '>=20'} @@ -2144,10 +1891,6 @@ packages: resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} engines: {node: '>=20.0.0'} - load-esm@1.0.3: - resolution: {integrity: sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==} - engines: {node: '>=13.2.0'} - locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -2159,13 +1902,6 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - log-update@6.1.0: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} @@ -2181,10 +1917,6 @@ packages: resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} engines: {node: 20 || >=22} - lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} @@ -2232,10 +1964,6 @@ packages: engines: {node: '>=10.0.0'} hasBin: true - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - mimic-function@5.0.1: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} @@ -2307,9 +2035,6 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - mute-stream@0.0.8: - resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} - nano-spawn@2.0.0: resolution: {integrity: sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==} engines: {node: '>=20.17'} @@ -2332,10 +2057,6 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - netmask@2.0.2: - resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} - engines: {node: '>= 0.4.0'} - node-abi@3.85.0: resolution: {integrity: sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==} engines: {node: '>=10'} @@ -2398,38 +2119,20 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - onetime@7.0.0: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} - openapi-generator-cli@1.0.0: - resolution: {integrity: sha512-gJ6YXQAkmoTYZ6kPG3rX9lHvVTh/bnWxfr6pDxjUfx+vSoE+JsuR4LDw+MrgH/7ezsJRFaljR66k0AlOXDW/eQ==} - hasBin: true - openapi-sampler@1.6.2: resolution: {integrity: sha512-NyKGiFKfSWAZr4srD/5WDhInOWDhfml32h/FKUqLpEwKJt0kG0LGUU0MdyNkKrVGuJnw6DuPWq/sHCwAMpiRxg==} openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - openapi-typescript@7.10.1: - resolution: {integrity: sha512-rBcU8bjKGGZQT4K2ekSTY2Q5veOQbVG/lTKZ49DeCyT9z62hM2Vj/LLHjDHC9W7LJG8YMHcdXpRZDqC1ojB/lw==} - hasBin: true - peerDependencies: - typescript: ^5.x - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} - otp@1.1.2: resolution: {integrity: sha512-VoueTSCMNTCYyHMGkNfndkFSXNv+iyEJ8D1/zD5G0Rd/QUHWozySmezRWKdVAhxcvmL3e5qwhEJBH/JF9MyE+g==} @@ -2456,14 +2159,6 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - pac-proxy-agent@7.2.0: - resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} - engines: {node: '>= 14'} - - pac-resolver@7.0.1: - resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} - engines: {node: '>= 14'} - package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -2471,10 +2166,6 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-json@8.3.0: - resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} - engines: {node: '>=18'} - path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -2494,9 +2185,6 @@ packages: resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} engines: {node: 20 || >=22} - path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} - perfect-scrollbar@1.5.6: resolution: {integrity: sha512-rixgxw3SxyJbCaSpo1n35A/fwI1r2rdwMKOTCg/AcG+xOEyZcE8UHVjpZMFCVImzsFoCZeJTT+M/rdEIQYO2nw==} @@ -2582,13 +2270,6 @@ packages: resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} engines: {node: '>=12.0.0'} - proxy-agent@6.5.0: - resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} - engines: {node: '>= 14'} - - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - pump@3.0.3: resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} @@ -2656,9 +2337,6 @@ packages: react-dom: ^16.8.4 || ^17.0.0 || ^18.0.0 || ^19.0.0 styled-components: ^4.1.1 || ^5.1.1 || ^6.0.5 - reflect-metadata@0.2.2: - resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} - reftools@1.1.9: resolution: {integrity: sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==} @@ -2678,10 +2356,6 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} @@ -2708,13 +2382,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-async@2.4.1: - resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} - engines: {node: '>=0.12.0'} - - rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -2763,10 +2430,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.3: - resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} - engines: {node: '>= 0.4'} - should-equal@2.0.0: resolution: {integrity: sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==} @@ -2785,9 +2448,6 @@ packages: should@13.2.3: resolution: {integrity: sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==} - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -2809,10 +2469,6 @@ packages: resolution: {integrity: sha512-tf+h5W1IrjNm/9rKKj0JU2MDMruiopx0jjVA5zCdBtcGjfp0+c5rHw/zADLC3IeKlGHtVbHtpfzvYA0OYT+HKg==} engines: {node: '>=8.0.0'} - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - socket.io-adapter@2.5.5: resolution: {integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==} @@ -2824,14 +2480,6 @@ packages: resolution: {integrity: sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==} engines: {node: '>=10.2.0'} - socks-proxy-agent@8.0.5: - resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} - engines: {node: '>= 14'} - - socks@2.8.7: - resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - sonic-boom@4.2.0: resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} @@ -2909,10 +2557,6 @@ packages: strnum@1.1.2: resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} - strtok3@10.3.4: - resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} - engines: {node: '>=18'} - styled-components@6.1.19: resolution: {integrity: sha512-1v/e3Dl1BknC37cXMhwGomhO8AkYmN41CqyX9xhUDxry1ns3BFQy2lLDRQXJRdVVWB9OHemv/53xaStimvWyuA==} engines: {node: '>= 16'} @@ -2923,18 +2567,10 @@ packages: stylis@4.3.2: resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==} - supports-color@10.2.2: - resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} - engines: {node: '>=18'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - swagger2openapi@7.0.8: resolution: {integrity: sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==} hasBin: true @@ -2949,9 +2585,6 @@ packages: thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} - through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -2971,17 +2604,9 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - token-types@6.1.1: - resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==} - engines: {node: '>=14.16'} - tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - ts-algebra@1.2.2: resolution: {integrity: sha512-kloPhf1hq3JbCPOTYoOWDKxebWjNb2o/LKnNfkWhxVVisFFmMJPPdJeGoGmM+iRLyoXAR61e08Pb+vUXINg8aA==} @@ -3014,14 +2639,6 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} - type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} @@ -3029,8 +2646,8 @@ packages: typebox@1.0.61: resolution: {integrity: sha512-5KeeL5QoPBoYm8Z7tGR1Pw9FjWA75MLhVuiSMCRgtgTg/d2+kTvolFddhOUua9FxpIaqXznFPZcc3sl6cEpafw==} - typescript-eslint@8.48.1: - resolution: {integrity: sha512-FbOKN1fqNoXp1hIl5KYpObVrp0mCn+CLgn479nmu2IsRMrx2vyv74MmsBLVlhg8qVwNFGbXSp8fh1zp8pEoC2A==} + typescript-eslint@8.49.0: + resolution: {integrity: sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -3046,14 +2663,6 @@ packages: engines: {node: '>=0.8.0'} hasBin: true - uid@2.0.2: - resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} - engines: {node: '>=8'} - - uint8array-extras@1.5.0: - resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} - engines: {node: '>=18'} - ulid@3.0.2: resolution: {integrity: sha512-yu26mwteFYzBAot7KVMqFGCVpsF6g8wXfJzQUHvu1no3+rRRSFcSV2nKeYvNPLD2J4b08jYBDhHUjeH0ygIl9w==} hasBin: true @@ -3068,10 +2677,6 @@ packages: resolution: {integrity: sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==} engines: {node: '>=18.17'} - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -3106,8 +2711,8 @@ packages: vite: optional: true - vite@7.2.6: - resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} + vite@7.2.7: + resolution: {integrity: sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3149,9 +2754,6 @@ packages: walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -3170,10 +2772,6 @@ packages: wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -3249,10 +2847,6 @@ packages: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - yargs-parser@22.0.0: resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} @@ -3261,10 +2855,6 @@ packages: resolution: {integrity: sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==} engines: {node: '>=12'} - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -3281,8 +2871,6 @@ snapshots: '@babel/runtime@7.28.4': {} - '@borewit/text-codec@0.1.1': {} - '@emnapi/runtime@1.7.1': dependencies: tslib: 2.8.1 @@ -3384,7 +2972,7 @@ snapshots: '@eslint/config-array@0.21.1': dependencies: '@eslint/object-schema': 2.1.7 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -3400,7 +2988,7 @@ snapshots: '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -3660,13 +3248,6 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@inquirer/external-editor@1.0.3(@types/node@24.10.1)': - dependencies: - chardet: 2.1.1 - iconv-lite: 0.7.0 - optionalDependencies: - '@types/node': 24.10.1 - '@isaacs/balanced-match@4.0.1': {} '@isaacs/brace-expansion@5.0.0': @@ -3682,84 +3263,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@lukeed/csprng@1.1.0': {} - '@lukeed/ms@2.0.2': {} - '@nestjs/axios@4.0.1(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)': - dependencies: - '@nestjs/common': 11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2) - axios: 1.13.2 - rxjs: 7.8.2 - - '@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2)': - dependencies: - file-type: 21.1.0 - iterare: 1.2.1 - load-esm: 1.0.3 - reflect-metadata: 0.2.2 - rxjs: 7.8.2 - tslib: 2.8.1 - uid: 2.0.2 - transitivePeerDependencies: - - supports-color - - '@nestjs/core@11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)': - dependencies: - '@nestjs/common': 11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nuxt/opencollective': 0.4.1 - fast-safe-stringify: 2.1.1 - iterare: 1.2.1 - path-to-regexp: 8.3.0 - reflect-metadata: 0.2.2 - rxjs: 7.8.2 - tslib: 2.8.1 - uid: 2.0.2 - '@noble/hashes@1.8.0': {} - '@nuxt/opencollective@0.4.1': - dependencies: - consola: 3.4.2 - - '@nuxtjs/opencollective@0.3.2': - dependencies: - chalk: 4.1.2 - consola: 2.15.3 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - - '@openapitools/openapi-generator-cli@2.25.2(@types/node@24.10.1)': - dependencies: - '@nestjs/axios': 4.0.1(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2) - '@nestjs/common': 11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nuxtjs/opencollective': 0.3.2 - axios: 1.13.2 - chalk: 4.1.2 - commander: 8.3.0 - compare-versions: 6.1.1 - concurrently: 9.2.1 - console.table: 0.10.0 - fs-extra: 11.3.2 - glob: 13.0.0 - inquirer: 8.2.7(@types/node@24.10.1) - proxy-agent: 6.5.0 - reflect-metadata: 0.2.2 - rxjs: 7.8.2 - tslib: 2.8.1 - transitivePeerDependencies: - - '@nestjs/microservices' - - '@nestjs/platform-express' - - '@nestjs/websockets' - - '@types/node' - - class-transformer - - class-validator - - debug - - encoding - - supports-color - '@opentelemetry/api-logs@0.202.0': dependencies: '@opentelemetry/api': 1.9.0 @@ -3868,14 +3375,14 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - '@redocly/cli@2.12.3(@opentelemetry/api@1.9.0)(ajv@8.17.1)(core-js@3.47.0)': + '@redocly/cli@2.12.5(@opentelemetry/api@1.9.0)(ajv@8.17.1)(core-js@3.47.0)': dependencies: '@opentelemetry/exporter-trace-otlp-http': 0.202.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-node': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.34.0 - '@redocly/openapi-core': 2.12.3(ajv@8.17.1) - '@redocly/respect-core': 2.12.3(ajv@8.17.1) + '@redocly/openapi-core': 2.12.5(ajv@8.17.1) + '@redocly/respect-core': 2.12.5(ajv@8.17.1) abort-controller: 3.0.0 chokidar: 3.6.0 colorette: 1.4.0 @@ -3884,7 +3391,7 @@ snapshots: form-data: 4.0.5 glob: 11.1.0 handlebars: 4.7.8 - https-proxy-agent: 7.0.6(supports-color@10.2.2) + https-proxy-agent: 7.0.6 mobx: 6.15.0 pluralize: 8.0.0 react: 19.2.1 @@ -3909,16 +3416,16 @@ snapshots: '@redocly/config@0.22.2': {} - '@redocly/config@0.40.0': + '@redocly/config@0.41.0': dependencies: json-schema-to-ts: 2.7.2 - '@redocly/openapi-core@1.34.5(supports-color@10.2.2)': + '@redocly/openapi-core@1.34.6': dependencies: '@redocly/ajv': 8.17.1 '@redocly/config': 0.22.2 colorette: 1.4.0 - https-proxy-agent: 7.0.6(supports-color@10.2.2) + https-proxy-agent: 7.0.6 js-levenshtein: 1.1.6 js-yaml: 4.1.1 minimatch: 5.1.6 @@ -3927,10 +3434,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@redocly/openapi-core@2.12.3(ajv@8.17.1)': + '@redocly/openapi-core@2.12.5(ajv@8.17.1)': dependencies: '@redocly/ajv': 8.17.1 - '@redocly/config': 0.40.0 + '@redocly/config': 0.41.0 ajv-formats: 3.0.1(ajv@8.17.1) colorette: 1.4.0 js-levenshtein: 1.1.6 @@ -3941,12 +3448,12 @@ snapshots: transitivePeerDependencies: - ajv - '@redocly/respect-core@2.12.3(ajv@8.17.1)': + '@redocly/respect-core@2.12.5(ajv@8.17.1)': dependencies: '@faker-js/faker': 7.6.0 '@noble/hashes': 1.8.0 '@redocly/ajv': 8.17.1 - '@redocly/openapi-core': 2.12.3(ajv@8.17.1) + '@redocly/openapi-core': 2.12.5(ajv@8.17.1) better-ajv-errors: 1.2.0(ajv@8.17.1) colorette: 2.0.20 json-pointer: 0.6.2 @@ -4026,39 +3533,27 @@ snapshots: '@standard-schema/spec@1.0.0': {} - '@tokenizer/inflate@0.3.1': - dependencies: - debug: 4.4.3(supports-color@10.2.2) - fflate: 0.8.2 - token-types: 6.1.1 - transitivePeerDependencies: - - supports-color - - '@tokenizer/token@0.3.0': {} - - '@tootallnate/quickjs-emscripten@0.23.0': {} - '@types/bcrypt@6.0.0': dependencies: - '@types/node': 22.19.1 + '@types/node': 22.19.2 '@types/better-sqlite3@7.6.13': dependencies: - '@types/node': 22.19.1 + '@types/node': 22.19.2 '@types/cors@2.8.19': dependencies: - '@types/node': 22.19.1 + '@types/node': 22.19.2 '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} - '@types/node@22.19.1': + '@types/node@22.19.2': dependencies: undici-types: 6.21.0 - '@types/node@24.10.1': + '@types/node@24.10.2': dependencies: undici-types: 7.16.0 @@ -4067,16 +3562,15 @@ snapshots: '@types/trusted-types@2.0.7': optional: true - '@typescript-eslint/eslint-plugin@8.48.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.48.1(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.48.1 - '@typescript-eslint/type-utils': 8.48.1(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.1(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.48.1 + '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.49.0 + '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.49.0 eslint: 9.39.1 - graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.1.0(typescript@5.9.3) @@ -4084,57 +3578,57 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.48.1(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.48.1 - '@typescript-eslint/types': 8.48.1 - '@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.48.1 - debug: 4.4.3(supports-color@10.2.2) + '@typescript-eslint/scope-manager': 8.49.0 + '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.49.0 + debug: 4.4.3 eslint: 9.39.1 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.48.1(typescript@5.9.3)': + '@typescript-eslint/project-service@8.49.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.48.1(typescript@5.9.3) - '@typescript-eslint/types': 8.48.1 - debug: 4.4.3(supports-color@10.2.2) + '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3) + '@typescript-eslint/types': 8.49.0 + debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.48.1': + '@typescript-eslint/scope-manager@8.49.0': dependencies: - '@typescript-eslint/types': 8.48.1 - '@typescript-eslint/visitor-keys': 8.48.1 + '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/visitor-keys': 8.49.0 - '@typescript-eslint/tsconfig-utils@8.48.1(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.49.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.48.1(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.49.0(eslint@9.39.1)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.48.1 - '@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.1(eslint@9.39.1)(typescript@5.9.3) - debug: 4.4.3(supports-color@10.2.2) + '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) + debug: 4.4.3 eslint: 9.39.1 ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.48.1': {} + '@typescript-eslint/types@8.49.0': {} - '@typescript-eslint/typescript-estree@8.48.1(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.49.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.48.1(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.48.1(typescript@5.9.3) - '@typescript-eslint/types': 8.48.1 - '@typescript-eslint/visitor-keys': 8.48.1 - debug: 4.4.3(supports-color@10.2.2) + '@typescript-eslint/project-service': 8.49.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3) + '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/visitor-keys': 8.49.0 + debug: 4.4.3 minimatch: 9.0.5 semver: 7.7.3 tinyglobby: 0.2.15 @@ -4143,20 +3637,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.48.1(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/utils@8.49.0(eslint@9.39.1)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) - '@typescript-eslint/scope-manager': 8.48.1 - '@typescript-eslint/types': 8.48.1 - '@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.49.0 + '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) eslint: 9.39.1 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.48.1': + '@typescript-eslint/visitor-keys@8.49.0': dependencies: - '@typescript-eslint/types': 8.48.1 + '@typescript-eslint/types': 8.49.0 eslint-visitor-keys: 4.2.1 abort-controller@3.0.0: @@ -4196,12 +3690,6 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - ansi-colors@4.1.3: {} - - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - ansi-escapes@7.2.0: dependencies: environment: 1.1.0 @@ -4230,10 +3718,6 @@ snapshots: minimalistic-assert: 1.0.1 safer-buffer: 2.1.2 - ast-types@0.13.4: - dependencies: - tslib: 2.8.1 - asynckit@0.4.0: {} atomic-sleep@1.0.0: {} @@ -4243,22 +3727,12 @@ snapshots: '@fastify/error': 4.2.0 fastq: 1.19.1 - axios@1.13.2: - dependencies: - follow-redirects: 1.15.11 - form-data: 4.0.5 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - balanced-match@1.0.2: {} base64-js@1.5.1: {} base64id@2.0.0: {} - basic-ftp@5.0.5: {} - bcrypt@6.0.0: dependencies: node-addon-api: 8.5.0 @@ -4328,10 +3802,6 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - change-case@5.4.4: {} - - chardet@2.1.1: {} - charenc@0.0.2: {} chokidar@3.6.0: @@ -4354,37 +3824,21 @@ snapshots: classnames@2.5.1: {} - cli-cursor@3.1.0: - dependencies: - restore-cursor: 3.1.0 - cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 - cli-spinners@2.9.2: {} - cli-truncate@5.1.1: dependencies: slice-ansi: 7.1.2 string-width: 8.1.0 - cli-width@3.0.0: {} - cliui@7.0.4: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - clone@1.0.4: {} - close-with-grace@2.3.0: {} clsx@2.1.1: {} @@ -4405,33 +3859,12 @@ snapshots: commander@14.0.2: {} - commander@8.3.0: {} - commist@3.2.0: {} - compare-versions@6.1.1: {} - concat-map@0.0.1: {} - concurrently@9.2.1: - dependencies: - chalk: 4.1.2 - rxjs: 7.8.2 - shell-quote: 1.8.3 - supports-color: 8.1.1 - tree-kill: 1.2.2 - yargs: 17.7.2 - confbox@0.2.2: {} - consola@2.15.3: {} - - consola@3.4.2: {} - - console.table@0.10.0: - dependencies: - easy-table: 1.1.0 - content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 @@ -4467,19 +3900,15 @@ snapshots: csstype@3.1.3: {} - data-uri-to-buffer@6.0.2: {} - dateformat@4.6.3: {} debug@4.3.7: dependencies: ms: 2.1.3 - debug@4.4.3(supports-color@10.2.2): + debug@4.4.3: dependencies: ms: 2.1.3 - optionalDependencies: - supports-color: 10.2.2 decko@1.2.0: {} @@ -4491,16 +3920,6 @@ snapshots: deep-is@0.1.4: {} - defaults@1.0.4: - dependencies: - clone: 1.0.4 - - degenerator@5.0.1: - dependencies: - ast-types: 0.13.4 - escodegen: 2.1.0 - esprima: 4.0.1 - delayed-stream@1.0.0: {} depd@2.0.0: {} @@ -4509,7 +3928,7 @@ snapshots: detect-libc@2.1.2: {} - dompurify@3.3.0: + dompurify@3.3.1: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -4532,10 +3951,6 @@ snapshots: eastasianwidth@0.2.0: {} - easy-table@1.1.0: - optionalDependencies: - wcwidth: 1.0.1 - ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer: 5.2.1 @@ -4555,7 +3970,7 @@ snapshots: engine.io@6.6.4: dependencies: '@types/cors': 2.8.19 - '@types/node': 22.19.1 + '@types/node': 22.19.2 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 @@ -4620,18 +4035,8 @@ snapshots: escape-html@1.0.3: {} - escape-string-regexp@1.0.5: {} - escape-string-regexp@4.0.0: {} - escodegen@2.1.0: - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 @@ -4658,7 +4063,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -4686,8 +4091,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 4.2.1 - esprima@4.0.1: {} - esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -4706,7 +4109,7 @@ snapshots: expand-template@2.0.3: {} - fast-copy@4.0.0: {} + fast-copy@4.0.1: {} fast-decode-uri-component@1.0.1: {} @@ -4805,25 +4208,10 @@ snapshots: optionalDependencies: picomatch: 4.0.3 - fflate@0.8.2: {} - - figures@3.2.0: - dependencies: - escape-string-regexp: 1.0.5 - file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 - file-type@21.1.0: - dependencies: - '@tokenizer/inflate': 0.3.1 - strtok3: 10.3.4 - token-types: 6.1.1 - uint8array-extras: 1.5.0 - transitivePeerDependencies: - - supports-color - file-uri-to-path@1.0.0: {} fill-range@7.1.1: @@ -4852,8 +4240,6 @@ snapshots: flatted@3.3.3: {} - follow-redirects@1.15.11: {} - foreach@2.0.6: {} foreground-child@3.3.1: @@ -4873,12 +4259,6 @@ snapshots: fs-constants@1.0.0: {} - fs-extra@11.3.2: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 - fsevents@2.3.3: optional: true @@ -4913,14 +4293,6 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-uri@6.0.5: - dependencies: - basic-ftp: 5.0.5 - data-uri-to-buffer: 6.0.2 - debug: 4.4.3(supports-color@10.2.2) - transitivePeerDependencies: - - supports-color - github-from-package@0.0.0: {} glob-parent@5.1.2: @@ -4940,22 +4312,12 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 2.0.1 - glob@13.0.0: - dependencies: - minimatch: 10.1.1 - minipass: 7.1.2 - path-scurry: 2.0.1 - globals@14.0.0: {} globrex@0.1.2: {} gopd@1.2.0: {} - graceful-fs@4.2.11: {} - - graphemer@1.4.0: {} - handlebars@4.7.8: dependencies: minimist: 1.2.8 @@ -4987,19 +4349,12 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 - http-proxy-agent@7.0.2: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3(supports-color@10.2.2) - transitivePeerDependencies: - - supports-color - http2-client@1.3.5: {} - https-proxy-agent@7.0.6(supports-color@10.2.2): + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -5022,34 +4377,10 @@ snapshots: imurmurhash@0.1.4: {} - index-to-position@1.2.0: {} - inherits@2.0.4: {} ini@1.3.8: {} - inquirer@8.2.7(@types/node@24.10.1): - dependencies: - '@inquirer/external-editor': 1.0.3(@types/node@24.10.1) - ansi-escapes: 4.3.2 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-width: 3.0.0 - figures: 3.2.0 - lodash: 4.17.21 - mute-stream: 0.0.8 - ora: 5.4.1 - run-async: 2.4.1 - rxjs: 7.8.2 - string-width: 4.2.3 - strip-ansi: 6.0.1 - through: 2.3.8 - wrap-ansi: 6.2.0 - transitivePeerDependencies: - - '@types/node' - - ip-address@10.1.0: {} - ipaddr.js@2.3.0: {} is-binary-path@2.1.0: @@ -5070,18 +4401,12 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-interactive@1.0.0: {} - is-number@7.0.0: {} - is-unicode-supported@0.1.0: {} - isbinaryfile@4.0.10: {} isexe@2.0.0: {} - iterare@1.2.1: {} - jackspeak@4.1.1: dependencies: '@isaacs/cliui': 8.0.2 @@ -5118,7 +4443,7 @@ snapshots: json-schema-resolver@3.0.0: dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 fast-uri: 3.1.0 rfdc: 1.4.1 transitivePeerDependencies: @@ -5136,12 +4461,6 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} - jsonfile@6.2.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - jsonpath-rfc9535@1.3.0: {} jsonpointer@5.0.1: {} @@ -5182,8 +4501,6 @@ snapshots: rfdc: 1.4.1 wrap-ansi: 9.0.2 - load-esm@1.0.3: {} - locate-path@3.0.0: dependencies: p-locate: 3.0.0 @@ -5195,13 +4512,6 @@ snapshots: lodash.merge@4.6.2: {} - lodash@4.17.21: {} - - log-symbols@4.1.0: - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - log-update@6.1.0: dependencies: ansi-escapes: 7.2.0 @@ -5218,8 +4528,6 @@ snapshots: lru-cache@11.2.4: {} - lru-cache@7.18.3: {} - lunr@2.3.9: {} makeerror@1.0.12: @@ -5253,8 +4561,6 @@ snapshots: mime@3.0.0: {} - mimic-fn@2.1.0: {} - mimic-function@5.0.1: {} mimic-response@3.1.0: {} @@ -5307,8 +4613,6 @@ snapshots: ms@2.1.3: {} - mute-stream@0.0.8: {} - nano-spawn@2.0.0: {} nanoid@3.3.11: {} @@ -5321,8 +4625,6 @@ snapshots: neo-async@2.6.2: {} - netmask@2.0.2: {} - node-abi@3.85.0: dependencies: semver: 7.7.3 @@ -5386,16 +4688,10 @@ snapshots: dependencies: wrappy: 1.0.2 - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - onetime@7.0.0: dependencies: mimic-function: 5.0.1 - openapi-generator-cli@1.0.0: {} - openapi-sampler@1.6.2: dependencies: '@types/json-schema': 7.0.15 @@ -5404,16 +4700,6 @@ snapshots: openapi-types@12.1.3: {} - openapi-typescript@7.10.1(typescript@5.9.3): - dependencies: - '@redocly/openapi-core': 1.34.5(supports-color@10.2.2) - ansi-colors: 4.1.3 - change-case: 5.4.4 - parse-json: 8.3.0 - supports-color: 10.2.2 - typescript: 5.9.3 - yargs-parser: 21.1.1 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -5423,18 +4709,6 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - ora@5.4.1: - dependencies: - bl: 4.1.0 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-spinners: 2.9.2 - is-interactive: 1.0.0 - is-unicode-supported: 0.1.0 - log-symbols: 4.1.0 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - otp@1.1.2: dependencies: sha1: 1.1.1 @@ -5459,36 +4733,12 @@ snapshots: p-try@2.2.0: {} - pac-proxy-agent@7.2.0: - dependencies: - '@tootallnate/quickjs-emscripten': 0.23.0 - agent-base: 7.1.4 - debug: 4.4.3(supports-color@10.2.2) - get-uri: 6.0.5 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6(supports-color@10.2.2) - pac-resolver: 7.0.1 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - - pac-resolver@7.0.1: - dependencies: - degenerator: 5.0.1 - netmask: 2.0.2 - package-json-from-dist@1.0.1: {} parent-module@1.0.1: dependencies: callsites: 3.1.0 - parse-json@8.3.0: - dependencies: - '@babel/code-frame': 7.27.1 - index-to-position: 1.2.0 - type-fest: 4.41.0 - path-browserify@1.0.1: {} path-exists@3.0.0: {} @@ -5502,8 +4752,6 @@ snapshots: lru-cache: 11.2.4 minipass: 7.1.2 - path-to-regexp@8.3.0: {} - perfect-scrollbar@1.5.6: {} picocolors@1.1.1: {} @@ -5526,7 +4774,7 @@ snapshots: dependencies: colorette: 2.0.20 dateformat: 4.6.3 - fast-copy: 4.0.0 + fast-copy: 4.0.1 fast-safe-stringify: 2.1.1 help-me: 5.0.0 joycon: 3.1.1 @@ -5619,24 +4867,9 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 24.10.1 + '@types/node': 24.10.2 long: 5.3.2 - proxy-agent@6.5.0: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3(supports-color@10.2.2) - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6(supports-color@10.2.2) - lru-cache: 7.18.3 - pac-proxy-agent: 7.2.0 - proxy-from-env: 1.1.0 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - - proxy-from-env@1.1.0: {} - pump@3.0.3: dependencies: end-of-stream: 1.4.5 @@ -5697,11 +4930,11 @@ snapshots: redoc@2.5.1(core-js@3.47.0)(mobx@6.15.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(styled-components@6.1.19(react-dom@19.2.1(react@19.2.1))(react@19.2.1)): dependencies: - '@redocly/openapi-core': 1.34.5(supports-color@10.2.2) + '@redocly/openapi-core': 1.34.6 classnames: 2.5.1 core-js: 3.47.0 decko: 1.2.0 - dompurify: 3.3.0 + dompurify: 3.3.1 eventemitter3: 5.0.1 json-pointer: 0.6.2 lunr: 2.3.9 @@ -5728,8 +4961,6 @@ snapshots: - react-native - supports-color - reflect-metadata@0.2.2: {} - reftools@1.1.9: {} require-directory@2.1.1: {} @@ -5740,11 +4971,6 @@ snapshots: resolve-from@5.0.0: {} - restore-cursor@3.1.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - restore-cursor@5.1.0: dependencies: onetime: 7.0.0 @@ -5788,12 +5014,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.53.3 fsevents: 2.3.3 - run-async@2.4.1: {} - - rxjs@7.8.2: - dependencies: - tslib: 2.8.1 - safe-buffer@5.2.1: {} safe-regex2@5.0.0: @@ -5858,8 +5078,6 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.3: {} - should-equal@2.0.0: dependencies: should-type: 1.4.0 @@ -5886,8 +5104,6 @@ snapshots: should-type-adaptors: 1.1.0 should-util: 1.0.1 - signal-exit@3.0.7: {} - signal-exit@4.1.0: {} simple-concat@1.0.1: {} @@ -5900,7 +5116,7 @@ snapshots: simple-websocket@9.1.0: dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 queue-microtask: 1.2.3 randombytes: 2.1.0 readable-stream: 3.6.2 @@ -5917,8 +5133,6 @@ snapshots: slugify@1.4.7: {} - smart-buffer@4.2.0: {} - socket.io-adapter@2.5.5: dependencies: debug: 4.3.7 @@ -5949,19 +5163,6 @@ snapshots: - supports-color - utf-8-validate - socks-proxy-agent@8.0.5: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3(supports-color@10.2.2) - socks: 2.8.7 - transitivePeerDependencies: - - supports-color - - socks@2.8.7: - dependencies: - ip-address: 10.1.0 - smart-buffer: 4.2.0 - sonic-boom@4.2.0: dependencies: atomic-sleep: 1.0.0 @@ -6035,10 +5236,6 @@ snapshots: strnum@1.1.2: {} - strtok3@10.3.4: - dependencies: - '@tokenizer/token': 0.3.0 - styled-components@6.1.19(react-dom@19.2.1(react@19.2.1))(react@19.2.1): dependencies: '@emotion/is-prop-valid': 1.2.2 @@ -6055,16 +5252,10 @@ snapshots: stylis@4.3.2: {} - supports-color@10.2.2: {} - supports-color@7.2.0: dependencies: has-flag: 4.0.0 - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - swagger2openapi@7.0.8: dependencies: call-me-maybe: 1.0.2 @@ -6100,8 +5291,6 @@ snapshots: dependencies: real-require: 0.2.0 - through@2.3.8: {} - tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -6117,16 +5306,8 @@ snapshots: toidentifier@1.0.1: {} - token-types@6.1.1: - dependencies: - '@borewit/text-codec': 0.1.1 - '@tokenizer/token': 0.3.0 - ieee754: 1.2.1 - tr46@0.0.3: {} - tree-kill@1.2.2: {} - ts-algebra@1.2.2: {} ts-api-utils@2.1.0(typescript@5.9.3): @@ -6139,7 +5320,8 @@ snapshots: tslib@2.6.2: {} - tslib@2.8.1: {} + tslib@2.8.1: + optional: true tunnel-agent@0.6.0: dependencies: @@ -6149,10 +5331,6 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-fest@0.21.3: {} - - type-fest@4.41.0: {} - type-is@2.0.1: dependencies: content-type: 1.0.5 @@ -6161,12 +5339,12 @@ snapshots: typebox@1.0.61: {} - typescript-eslint@8.48.1(eslint@9.39.1)(typescript@5.9.3): + typescript-eslint@8.49.0(eslint@9.39.1)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.48.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/parser': 8.48.1(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.1(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3) eslint: 9.39.1 typescript: 5.9.3 transitivePeerDependencies: @@ -6177,12 +5355,6 @@ snapshots: uglify-js@3.19.3: optional: true - uid@2.0.2: - dependencies: - '@lukeed/csprng': 1.1.0 - - uint8array-extras@1.5.0: {} - ulid@3.0.2: {} undici-types@6.21.0: {} @@ -6191,8 +5363,6 @@ snapshots: undici@6.22.0: {} - universalify@2.0.1: {} - unpipe@1.0.0: {} uri-js@4.4.1: @@ -6211,18 +5381,18 @@ snapshots: vary@1.1.2: {} - vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.6(@types/node@22.19.1)(yaml@2.8.2)): + vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.7(@types/node@22.19.2)(yaml@2.8.2)): dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) optionalDependencies: - vite: 7.2.6(@types/node@22.19.1)(yaml@2.8.2) + vite: 7.2.7(@types/node@22.19.2)(yaml@2.8.2) transitivePeerDependencies: - supports-color - typescript - vite@7.2.6(@types/node@22.19.1)(yaml@2.8.2): + vite@7.2.7(@types/node@22.19.2)(yaml@2.8.2): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -6231,11 +5401,11 @@ snapshots: rollup: 4.53.3 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 22.19.1 + '@types/node': 22.19.2 fsevents: 2.3.3 yaml: 2.8.2 - vite@7.2.6(@types/node@24.10.1)(yaml@2.8.2): + vite@7.2.7(@types/node@24.10.2)(yaml@2.8.2): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -6244,7 +5414,7 @@ snapshots: rollup: 4.53.3 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.2 fsevents: 2.3.3 yaml: 2.8.2 @@ -6252,10 +5422,6 @@ snapshots: dependencies: makeerror: 1.0.12 - wcwidth@1.0.1: - dependencies: - defaults: 1.0.4 - webidl-conversions@3.0.1: {} whatwg-url@5.0.0: @@ -6271,12 +5437,6 @@ snapshots: wordwrap@1.0.0: {} - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -6315,8 +5475,6 @@ snapshots: yargs-parser@20.2.9: {} - yargs-parser@21.1.1: {} - yargs-parser@22.0.0: {} yargs@17.0.1: @@ -6329,14 +5487,4 @@ snapshots: y18n: 5.0.8 yargs-parser: 20.2.9 - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - yocto-queue@0.1.0: {} diff --git a/src/pnpm-workspace.yaml b/src/pnpm-workspace.yaml index 65c74c4..2840480 100644 --- a/src/pnpm-workspace.yaml +++ b/src/pnpm-workspace.yaml @@ -4,7 +4,9 @@ packages: nodeLinker: hoisted onlyBuiltDependencies: - - better-sqlite3 - - esbuild - - sharp - bcrypt + - better-sqlite3 + - core-js + - esbuild + - protobufjs + - sharp diff --git a/src/user/package.json b/src/user/package.json index 6dfb965..ce84e40 100644 --- a/src/user/package.json +++ b/src/user/package.json @@ -29,9 +29,9 @@ "typebox": "^1.0.61" }, "devDependencies": { - "@types/node": "^22.19.1", + "@types/node": "^22.19.2", "rollup-plugin-node-externals": "^8.1.2", - "vite": "^7.2.6", + "vite": "^7.2.7", "vite-tsconfig-paths": "^5.1.4" } } From c898fe8d32f9237b39d779baf715c2799e54e250 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Mon, 1 Dec 2025 19:26:22 +0100 Subject: [PATCH 02/10] wip --- .../api/generated/.openapi-generator/FILES | 5 + .../src/api/generated/apis/OpenapiOtherApi.ts | 26 ++- .../generated/models/ChatTest200Response.ts | 20 +-- .../models/ChatTest200ResponsePayload.ts | 84 +++++++++ .../generated/models/DisableOtp400Response.ts | 93 ++++++++++ .../generated/models/EnableOtp400Response.ts | 93 ++++++++++ .../models/GetUser200ResponsePayload.ts | 16 ++ .../GetUser200ResponsePayloadSelfInfo.ts | 81 +++++++++ .../generated/models/StatusOtp200Response.ts | 22 +-- .../models/StatusOtp200ResponseAnyOf.ts | 20 +-- .../StatusOtp200ResponseAnyOfPayload.ts | 66 ++++++++ .../generated/models/StatusOtp400Response.ts | 93 ++++++++++ frontend/src/api/generated/models/index.ts | 5 + frontend/src/auth/index.ts | 61 ++++--- frontend/src/pages/index.ts | 1 + frontend/src/pages/login/login.ts | 97 ++++++++++- frontend/src/pages/login/totp.html | 11 ++ frontend/src/pages/profile/profile.html | 66 ++++++++ frontend/src/pages/profile/profile.ts | 159 ++++++++++++++++++ frontend/src/routing/index.ts | 9 +- frontend/src/routing/special_routes.ts | 2 - frontend/vite.config.js | 4 + src/@shared/src/auth/index.ts | 3 +- src/auth/openapi.json | 58 ++++++- src/auth/src/routes/disableOtp.ts | 8 + src/auth/src/routes/enableOtp.ts | 8 + src/auth/src/routes/statusOtp.ts | 35 +++- src/openapi.json | 72 +++++++- src/user/openapi.json | 14 ++ src/user/src/routes/info.ts | 11 ++ 30 files changed, 1148 insertions(+), 95 deletions(-) create mode 100644 frontend/src/api/generated/models/ChatTest200ResponsePayload.ts create mode 100644 frontend/src/api/generated/models/DisableOtp400Response.ts create mode 100644 frontend/src/api/generated/models/EnableOtp400Response.ts create mode 100644 frontend/src/api/generated/models/GetUser200ResponsePayloadSelfInfo.ts create mode 100644 frontend/src/api/generated/models/StatusOtp200ResponseAnyOfPayload.ts create mode 100644 frontend/src/api/generated/models/StatusOtp400Response.ts create mode 100644 frontend/src/pages/login/totp.html create mode 100644 frontend/src/pages/profile/profile.html create mode 100644 frontend/src/pages/profile/profile.ts diff --git a/frontend/src/api/generated/.openapi-generator/FILES b/frontend/src/api/generated/.openapi-generator/FILES index af12e66..b05ee98 100644 --- a/frontend/src/api/generated/.openapi-generator/FILES +++ b/frontend/src/api/generated/.openapi-generator/FILES @@ -2,15 +2,19 @@ apis/OpenapiOtherApi.ts apis/index.ts index.ts models/ChatTest200Response.ts +models/ChatTest200ResponsePayload.ts models/DisableOtp200Response.ts +models/DisableOtp400Response.ts models/DisableOtp401Response.ts models/DisableOtp500Response.ts models/EnableOtp200Response.ts models/EnableOtp200ResponsePayload.ts +models/EnableOtp400Response.ts models/EnableOtp401Response.ts models/EnableOtp401ResponseAnyOf.ts models/GetUser200Response.ts models/GetUser200ResponsePayload.ts +models/GetUser200ResponsePayloadSelfInfo.ts models/GetUser403Response.ts models/GetUser404Response.ts models/GetUserUserParameter.ts @@ -41,6 +45,7 @@ models/Signin500Response.ts models/StatusOtp200Response.ts models/StatusOtp200ResponseAnyOf.ts models/StatusOtp200ResponseAnyOf1.ts +models/StatusOtp200ResponseAnyOfPayload.ts models/StatusOtp401Response.ts models/StatusOtp500Response.ts models/index.ts diff --git a/frontend/src/api/generated/apis/OpenapiOtherApi.ts b/frontend/src/api/generated/apis/OpenapiOtherApi.ts index fda4f60..c37580a 100644 --- a/frontend/src/api/generated/apis/OpenapiOtherApi.ts +++ b/frontend/src/api/generated/apis/OpenapiOtherApi.ts @@ -17,9 +17,11 @@ import * as runtime from '../runtime'; import type { ChatTest200Response, DisableOtp200Response, + DisableOtp400Response, DisableOtp401Response, DisableOtp500Response, EnableOtp200Response, + EnableOtp400Response, EnableOtp401Response, GetUser200Response, GetUser403Response, @@ -51,12 +53,16 @@ import { ChatTest200ResponseToJSON, DisableOtp200ResponseFromJSON, DisableOtp200ResponseToJSON, + DisableOtp400ResponseFromJSON, + DisableOtp400ResponseToJSON, DisableOtp401ResponseFromJSON, DisableOtp401ResponseToJSON, DisableOtp500ResponseFromJSON, DisableOtp500ResponseToJSON, EnableOtp200ResponseFromJSON, EnableOtp200ResponseToJSON, + EnableOtp400ResponseFromJSON, + EnableOtp400ResponseToJSON, EnableOtp401ResponseFromJSON, EnableOtp401ResponseToJSON, GetUser200ResponseFromJSON, @@ -174,7 +180,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI { /** */ - async disableOtpRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + async disableOtpRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { const queryParameters: any = {}; const headerParameters: runtime.HTTPHeaders = {}; @@ -197,6 +203,10 @@ export class OpenapiOtherApi extends runtime.BaseAPI { // Object response for status 200 return new runtime.JSONApiResponse(response, (jsonValue) => DisableOtp200ResponseFromJSON(jsonValue)); } + if (response.status === 400) { + // Object response for status 400 + return new runtime.JSONApiResponse(response, (jsonValue) => DisableOtp400ResponseFromJSON(jsonValue)); + } if (response.status === 401) { // Object response for status 401 return new runtime.JSONApiResponse(response, (jsonValue) => DisableOtp401ResponseFromJSON(jsonValue)); @@ -208,19 +218,19 @@ export class OpenapiOtherApi extends runtime.BaseAPI { // CHANGED: Throw error if status code is not handled by any of the defined responses // This ensures all code paths return a value and provides clear error messages for unexpected status codes // Only throw if responses were defined but none matched the actual status code - throw new runtime.ResponseError(response, `Unexpected status code: ${response.status}. Expected one of: 200, 401, 500`); + throw new runtime.ResponseError(response, `Unexpected status code: ${response.status}. Expected one of: 200, 400, 401, 500`); } /** */ - async disableOtp(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + async disableOtp(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { const response = await this.disableOtpRaw(initOverrides); return await response.value(); } /** */ - async enableOtpRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + async enableOtpRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { const queryParameters: any = {}; const headerParameters: runtime.HTTPHeaders = {}; @@ -243,6 +253,10 @@ export class OpenapiOtherApi extends runtime.BaseAPI { // Object response for status 200 return new runtime.JSONApiResponse(response, (jsonValue) => EnableOtp200ResponseFromJSON(jsonValue)); } + if (response.status === 400) { + // Object response for status 400 + return new runtime.JSONApiResponse(response, (jsonValue) => EnableOtp400ResponseFromJSON(jsonValue)); + } if (response.status === 401) { // Object response for status 401 return new runtime.JSONApiResponse(response, (jsonValue) => EnableOtp401ResponseFromJSON(jsonValue)); @@ -250,12 +264,12 @@ export class OpenapiOtherApi extends runtime.BaseAPI { // CHANGED: Throw error if status code is not handled by any of the defined responses // This ensures all code paths return a value and provides clear error messages for unexpected status codes // Only throw if responses were defined but none matched the actual status code - throw new runtime.ResponseError(response, `Unexpected status code: ${response.status}. Expected one of: 200, 401`); + throw new runtime.ResponseError(response, `Unexpected status code: ${response.status}. Expected one of: 200, 400, 401`); } /** */ - async enableOtp(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + async enableOtp(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { const response = await this.enableOtpRaw(initOverrides); return await response.value(); } diff --git a/frontend/src/api/generated/models/ChatTest200Response.ts b/frontend/src/api/generated/models/ChatTest200Response.ts index 6feb66e..9fa3dd7 100644 --- a/frontend/src/api/generated/models/ChatTest200Response.ts +++ b/frontend/src/api/generated/models/ChatTest200Response.ts @@ -13,13 +13,13 @@ */ import { mapValues } from '../runtime'; -import type { GetUser200ResponsePayload } from './GetUser200ResponsePayload'; +import type { ChatTest200ResponsePayload } from './ChatTest200ResponsePayload'; import { - GetUser200ResponsePayloadFromJSON, - GetUser200ResponsePayloadFromJSONTyped, - GetUser200ResponsePayloadToJSON, - GetUser200ResponsePayloadToJSONTyped, -} from './GetUser200ResponsePayload'; + ChatTest200ResponsePayloadFromJSON, + ChatTest200ResponsePayloadFromJSONTyped, + ChatTest200ResponsePayloadToJSON, + ChatTest200ResponsePayloadToJSONTyped, +} from './ChatTest200ResponsePayload'; /** * @@ -41,10 +41,10 @@ export interface ChatTest200Response { msg: ChatTest200ResponseMsgEnum; /** * - * @type {GetUser200ResponsePayload} + * @type {ChatTest200ResponsePayload} * @memberof ChatTest200Response */ - payload: GetUser200ResponsePayload; + payload: ChatTest200ResponsePayload; } @@ -87,7 +87,7 @@ export function ChatTest200ResponseFromJSONTyped(json: any, ignoreDiscriminator: 'kind': json['kind'], 'msg': json['msg'], - 'payload': GetUser200ResponsePayloadFromJSON(json['payload']), + 'payload': ChatTest200ResponsePayloadFromJSON(json['payload']), }; } @@ -104,7 +104,7 @@ export function ChatTest200ResponseToJSONTyped(value?: ChatTest200Response | nul 'kind': value['kind'], 'msg': value['msg'], - 'payload': GetUser200ResponsePayloadToJSON(value['payload']), + 'payload': ChatTest200ResponsePayloadToJSON(value['payload']), }; } diff --git a/frontend/src/api/generated/models/ChatTest200ResponsePayload.ts b/frontend/src/api/generated/models/ChatTest200ResponsePayload.ts new file mode 100644 index 0000000..2017a31 --- /dev/null +++ b/frontend/src/api/generated/models/ChatTest200ResponsePayload.ts @@ -0,0 +1,84 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ChatTest200ResponsePayload + */ +export interface ChatTest200ResponsePayload { + /** + * + * @type {string} + * @memberof ChatTest200ResponsePayload + */ + name: string; + /** + * + * @type {string} + * @memberof ChatTest200ResponsePayload + */ + id: string; + /** + * + * @type {boolean} + * @memberof ChatTest200ResponsePayload + */ + guest: boolean; +} + +/** + * Check if a given object implements the ChatTest200ResponsePayload interface. + */ +export function instanceOfChatTest200ResponsePayload(value: object): value is ChatTest200ResponsePayload { + if (!('name' in value) || value['name'] === undefined) return false; + if (!('id' in value) || value['id'] === undefined) return false; + if (!('guest' in value) || value['guest'] === undefined) return false; + return true; +} + +export function ChatTest200ResponsePayloadFromJSON(json: any): ChatTest200ResponsePayload { + return ChatTest200ResponsePayloadFromJSONTyped(json, false); +} + +export function ChatTest200ResponsePayloadFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChatTest200ResponsePayload { + if (json == null) { + return json; + } + return { + + 'name': json['name'], + 'id': json['id'], + 'guest': json['guest'], + }; +} + +export function ChatTest200ResponsePayloadToJSON(json: any): ChatTest200ResponsePayload { + return ChatTest200ResponsePayloadToJSONTyped(json, false); +} + +export function ChatTest200ResponsePayloadToJSONTyped(value?: ChatTest200ResponsePayload | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'name': value['name'], + 'id': value['id'], + 'guest': value['guest'], + }; +} + diff --git a/frontend/src/api/generated/models/DisableOtp400Response.ts b/frontend/src/api/generated/models/DisableOtp400Response.ts new file mode 100644 index 0000000..481e2be --- /dev/null +++ b/frontend/src/api/generated/models/DisableOtp400Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface DisableOtp400Response + */ +export interface DisableOtp400Response { + /** + * + * @type {string} + * @memberof DisableOtp400Response + */ + kind: DisableOtp400ResponseKindEnum; + /** + * + * @type {string} + * @memberof DisableOtp400Response + */ + msg: DisableOtp400ResponseMsgEnum; +} + + +/** + * @export + */ +export const DisableOtp400ResponseKindEnum = { + Failure: 'failure' +} as const; +export type DisableOtp400ResponseKindEnum = typeof DisableOtp400ResponseKindEnum[keyof typeof DisableOtp400ResponseKindEnum]; + +/** + * @export + */ +export const DisableOtp400ResponseMsgEnum = { + DisableOtpFailureGuest: 'disableOtp.failure.guest' +} as const; +export type DisableOtp400ResponseMsgEnum = typeof DisableOtp400ResponseMsgEnum[keyof typeof DisableOtp400ResponseMsgEnum]; + + +/** + * Check if a given object implements the DisableOtp400Response interface. + */ +export function instanceOfDisableOtp400Response(value: object): value is DisableOtp400Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function DisableOtp400ResponseFromJSON(json: any): DisableOtp400Response { + return DisableOtp400ResponseFromJSONTyped(json, false); +} + +export function DisableOtp400ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): DisableOtp400Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function DisableOtp400ResponseToJSON(json: any): DisableOtp400Response { + return DisableOtp400ResponseToJSONTyped(json, false); +} + +export function DisableOtp400ResponseToJSONTyped(value?: DisableOtp400Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/EnableOtp400Response.ts b/frontend/src/api/generated/models/EnableOtp400Response.ts new file mode 100644 index 0000000..ec0b8e7 --- /dev/null +++ b/frontend/src/api/generated/models/EnableOtp400Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface EnableOtp400Response + */ +export interface EnableOtp400Response { + /** + * + * @type {string} + * @memberof EnableOtp400Response + */ + kind: EnableOtp400ResponseKindEnum; + /** + * + * @type {string} + * @memberof EnableOtp400Response + */ + msg: EnableOtp400ResponseMsgEnum; +} + + +/** + * @export + */ +export const EnableOtp400ResponseKindEnum = { + Failure: 'failure' +} as const; +export type EnableOtp400ResponseKindEnum = typeof EnableOtp400ResponseKindEnum[keyof typeof EnableOtp400ResponseKindEnum]; + +/** + * @export + */ +export const EnableOtp400ResponseMsgEnum = { + EnableOtpFailureGuest: 'enableOtp.failure.guest' +} as const; +export type EnableOtp400ResponseMsgEnum = typeof EnableOtp400ResponseMsgEnum[keyof typeof EnableOtp400ResponseMsgEnum]; + + +/** + * Check if a given object implements the EnableOtp400Response interface. + */ +export function instanceOfEnableOtp400Response(value: object): value is EnableOtp400Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function EnableOtp400ResponseFromJSON(json: any): EnableOtp400Response { + return EnableOtp400ResponseFromJSONTyped(json, false); +} + +export function EnableOtp400ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): EnableOtp400Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function EnableOtp400ResponseToJSON(json: any): EnableOtp400Response { + return EnableOtp400ResponseToJSONTyped(json, false); +} + +export function EnableOtp400ResponseToJSONTyped(value?: EnableOtp400Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/GetUser200ResponsePayload.ts b/frontend/src/api/generated/models/GetUser200ResponsePayload.ts index f41f951..79b420a 100644 --- a/frontend/src/api/generated/models/GetUser200ResponsePayload.ts +++ b/frontend/src/api/generated/models/GetUser200ResponsePayload.ts @@ -13,6 +13,14 @@ */ import { mapValues } from '../runtime'; +import type { GetUser200ResponsePayloadSelfInfo } from './GetUser200ResponsePayloadSelfInfo'; +import { + GetUser200ResponsePayloadSelfInfoFromJSON, + GetUser200ResponsePayloadSelfInfoFromJSONTyped, + GetUser200ResponsePayloadSelfInfoToJSON, + GetUser200ResponsePayloadSelfInfoToJSONTyped, +} from './GetUser200ResponsePayloadSelfInfo'; + /** * * @export @@ -37,6 +45,12 @@ export interface GetUser200ResponsePayload { * @memberof GetUser200ResponsePayload */ guest: boolean; + /** + * + * @type {GetUser200ResponsePayloadSelfInfo} + * @memberof GetUser200ResponsePayload + */ + selfInfo?: GetUser200ResponsePayloadSelfInfo; } /** @@ -62,6 +76,7 @@ export function GetUser200ResponsePayloadFromJSONTyped(json: any, ignoreDiscrimi 'name': json['name'], 'id': json['id'], 'guest': json['guest'], + 'selfInfo': json['selfInfo'] == null ? undefined : GetUser200ResponsePayloadSelfInfoFromJSON(json['selfInfo']), }; } @@ -79,6 +94,7 @@ export function GetUser200ResponsePayloadToJSONTyped(value?: GetUser200ResponseP 'name': value['name'], 'id': value['id'], 'guest': value['guest'], + 'selfInfo': GetUser200ResponsePayloadSelfInfoToJSON(value['selfInfo']), }; } diff --git a/frontend/src/api/generated/models/GetUser200ResponsePayloadSelfInfo.ts b/frontend/src/api/generated/models/GetUser200ResponsePayloadSelfInfo.ts new file mode 100644 index 0000000..0a37bac --- /dev/null +++ b/frontend/src/api/generated/models/GetUser200ResponsePayloadSelfInfo.ts @@ -0,0 +1,81 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface GetUser200ResponsePayloadSelfInfo + */ +export interface GetUser200ResponsePayloadSelfInfo { + /** + * + * @type {string} + * @memberof GetUser200ResponsePayloadSelfInfo + */ + loginName?: string; + /** + * + * @type {string} + * @memberof GetUser200ResponsePayloadSelfInfo + */ + providerId?: string; + /** + * + * @type {string} + * @memberof GetUser200ResponsePayloadSelfInfo + */ + providerUser?: string; +} + +/** + * Check if a given object implements the GetUser200ResponsePayloadSelfInfo interface. + */ +export function instanceOfGetUser200ResponsePayloadSelfInfo(value: object): value is GetUser200ResponsePayloadSelfInfo { + return true; +} + +export function GetUser200ResponsePayloadSelfInfoFromJSON(json: any): GetUser200ResponsePayloadSelfInfo { + return GetUser200ResponsePayloadSelfInfoFromJSONTyped(json, false); +} + +export function GetUser200ResponsePayloadSelfInfoFromJSONTyped(json: any, ignoreDiscriminator: boolean): GetUser200ResponsePayloadSelfInfo { + if (json == null) { + return json; + } + return { + + 'loginName': json['login_name'] == null ? undefined : json['login_name'], + 'providerId': json['provider_id'] == null ? undefined : json['provider_id'], + 'providerUser': json['provider_user'] == null ? undefined : json['provider_user'], + }; +} + +export function GetUser200ResponsePayloadSelfInfoToJSON(json: any): GetUser200ResponsePayloadSelfInfo { + return GetUser200ResponsePayloadSelfInfoToJSONTyped(json, false); +} + +export function GetUser200ResponsePayloadSelfInfoToJSONTyped(value?: GetUser200ResponsePayloadSelfInfo | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'login_name': value['loginName'], + 'provider_id': value['providerId'], + 'provider_user': value['providerUser'], + }; +} + diff --git a/frontend/src/api/generated/models/StatusOtp200Response.ts b/frontend/src/api/generated/models/StatusOtp200Response.ts index 7dec45c..522edc1 100644 --- a/frontend/src/api/generated/models/StatusOtp200Response.ts +++ b/frontend/src/api/generated/models/StatusOtp200Response.ts @@ -13,13 +13,6 @@ */ import { mapValues } from '../runtime'; -import type { EnableOtp200ResponsePayload } from './EnableOtp200ResponsePayload'; -import { - EnableOtp200ResponsePayloadFromJSON, - EnableOtp200ResponsePayloadFromJSONTyped, - EnableOtp200ResponsePayloadToJSON, - EnableOtp200ResponsePayloadToJSONTyped, -} from './EnableOtp200ResponsePayload'; import type { StatusOtp200ResponseAnyOf } from './StatusOtp200ResponseAnyOf'; import { StatusOtp200ResponseAnyOfFromJSON, @@ -27,6 +20,13 @@ import { StatusOtp200ResponseAnyOfToJSON, StatusOtp200ResponseAnyOfToJSONTyped, } from './StatusOtp200ResponseAnyOf'; +import type { StatusOtp200ResponseAnyOfPayload } from './StatusOtp200ResponseAnyOfPayload'; +import { + StatusOtp200ResponseAnyOfPayloadFromJSON, + StatusOtp200ResponseAnyOfPayloadFromJSONTyped, + StatusOtp200ResponseAnyOfPayloadToJSON, + StatusOtp200ResponseAnyOfPayloadToJSONTyped, +} from './StatusOtp200ResponseAnyOfPayload'; import type { StatusOtp200ResponseAnyOf1 } from './StatusOtp200ResponseAnyOf1'; import { StatusOtp200ResponseAnyOf1FromJSON, @@ -55,10 +55,10 @@ export interface StatusOtp200Response { msg: StatusOtp200ResponseMsgEnum; /** * - * @type {EnableOtp200ResponsePayload} + * @type {StatusOtp200ResponseAnyOfPayload} * @memberof StatusOtp200Response */ - payload: EnableOtp200ResponsePayload; + payload: StatusOtp200ResponseAnyOfPayload; } @@ -101,7 +101,7 @@ export function StatusOtp200ResponseFromJSONTyped(json: any, ignoreDiscriminator 'kind': json['kind'], 'msg': json['msg'], - 'payload': EnableOtp200ResponsePayloadFromJSON(json['payload']), + 'payload': StatusOtp200ResponseAnyOfPayloadFromJSON(json['payload']), }; } @@ -118,7 +118,7 @@ export function StatusOtp200ResponseToJSONTyped(value?: StatusOtp200Response | n 'kind': value['kind'], 'msg': value['msg'], - 'payload': EnableOtp200ResponsePayloadToJSON(value['payload']), + 'payload': StatusOtp200ResponseAnyOfPayloadToJSON(value['payload']), }; } diff --git a/frontend/src/api/generated/models/StatusOtp200ResponseAnyOf.ts b/frontend/src/api/generated/models/StatusOtp200ResponseAnyOf.ts index 5750e63..0ea3524 100644 --- a/frontend/src/api/generated/models/StatusOtp200ResponseAnyOf.ts +++ b/frontend/src/api/generated/models/StatusOtp200ResponseAnyOf.ts @@ -13,13 +13,13 @@ */ import { mapValues } from '../runtime'; -import type { EnableOtp200ResponsePayload } from './EnableOtp200ResponsePayload'; +import type { StatusOtp200ResponseAnyOfPayload } from './StatusOtp200ResponseAnyOfPayload'; import { - EnableOtp200ResponsePayloadFromJSON, - EnableOtp200ResponsePayloadFromJSONTyped, - EnableOtp200ResponsePayloadToJSON, - EnableOtp200ResponsePayloadToJSONTyped, -} from './EnableOtp200ResponsePayload'; + StatusOtp200ResponseAnyOfPayloadFromJSON, + StatusOtp200ResponseAnyOfPayloadFromJSONTyped, + StatusOtp200ResponseAnyOfPayloadToJSON, + StatusOtp200ResponseAnyOfPayloadToJSONTyped, +} from './StatusOtp200ResponseAnyOfPayload'; /** * @@ -41,10 +41,10 @@ export interface StatusOtp200ResponseAnyOf { msg: StatusOtp200ResponseAnyOfMsgEnum; /** * - * @type {EnableOtp200ResponsePayload} + * @type {StatusOtp200ResponseAnyOfPayload} * @memberof StatusOtp200ResponseAnyOf */ - payload: EnableOtp200ResponsePayload; + payload: StatusOtp200ResponseAnyOfPayload; } @@ -87,7 +87,7 @@ export function StatusOtp200ResponseAnyOfFromJSONTyped(json: any, ignoreDiscrimi 'kind': json['kind'], 'msg': json['msg'], - 'payload': EnableOtp200ResponsePayloadFromJSON(json['payload']), + 'payload': StatusOtp200ResponseAnyOfPayloadFromJSON(json['payload']), }; } @@ -104,7 +104,7 @@ export function StatusOtp200ResponseAnyOfToJSONTyped(value?: StatusOtp200Respons 'kind': value['kind'], 'msg': value['msg'], - 'payload': EnableOtp200ResponsePayloadToJSON(value['payload']), + 'payload': StatusOtp200ResponseAnyOfPayloadToJSON(value['payload']), }; } diff --git a/frontend/src/api/generated/models/StatusOtp200ResponseAnyOfPayload.ts b/frontend/src/api/generated/models/StatusOtp200ResponseAnyOfPayload.ts new file mode 100644 index 0000000..260af1d --- /dev/null +++ b/frontend/src/api/generated/models/StatusOtp200ResponseAnyOfPayload.ts @@ -0,0 +1,66 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface StatusOtp200ResponseAnyOfPayload + */ +export interface StatusOtp200ResponseAnyOfPayload { + /** + * The otp secret + * @type {string} + * @memberof StatusOtp200ResponseAnyOfPayload + */ + secret: string; +} + +/** + * Check if a given object implements the StatusOtp200ResponseAnyOfPayload interface. + */ +export function instanceOfStatusOtp200ResponseAnyOfPayload(value: object): value is StatusOtp200ResponseAnyOfPayload { + if (!('secret' in value) || value['secret'] === undefined) return false; + return true; +} + +export function StatusOtp200ResponseAnyOfPayloadFromJSON(json: any): StatusOtp200ResponseAnyOfPayload { + return StatusOtp200ResponseAnyOfPayloadFromJSONTyped(json, false); +} + +export function StatusOtp200ResponseAnyOfPayloadFromJSONTyped(json: any, ignoreDiscriminator: boolean): StatusOtp200ResponseAnyOfPayload { + if (json == null) { + return json; + } + return { + + 'secret': json['secret'], + }; +} + +export function StatusOtp200ResponseAnyOfPayloadToJSON(json: any): StatusOtp200ResponseAnyOfPayload { + return StatusOtp200ResponseAnyOfPayloadToJSONTyped(json, false); +} + +export function StatusOtp200ResponseAnyOfPayloadToJSONTyped(value?: StatusOtp200ResponseAnyOfPayload | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'secret': value['secret'], + }; +} + diff --git a/frontend/src/api/generated/models/StatusOtp400Response.ts b/frontend/src/api/generated/models/StatusOtp400Response.ts new file mode 100644 index 0000000..a952478 --- /dev/null +++ b/frontend/src/api/generated/models/StatusOtp400Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface StatusOtp400Response + */ +export interface StatusOtp400Response { + /** + * + * @type {string} + * @memberof StatusOtp400Response + */ + kind: StatusOtp400ResponseKindEnum; + /** + * + * @type {string} + * @memberof StatusOtp400Response + */ + msg: StatusOtp400ResponseMsgEnum; +} + + +/** + * @export + */ +export const StatusOtp400ResponseKindEnum = { + Failure: 'failure' +} as const; +export type StatusOtp400ResponseKindEnum = typeof StatusOtp400ResponseKindEnum[keyof typeof StatusOtp400ResponseKindEnum]; + +/** + * @export + */ +export const StatusOtp400ResponseMsgEnum = { + StatusOtpFailureGuest: 'statusOtp.failure.guest' +} as const; +export type StatusOtp400ResponseMsgEnum = typeof StatusOtp400ResponseMsgEnum[keyof typeof StatusOtp400ResponseMsgEnum]; + + +/** + * Check if a given object implements the StatusOtp400Response interface. + */ +export function instanceOfStatusOtp400Response(value: object): value is StatusOtp400Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function StatusOtp400ResponseFromJSON(json: any): StatusOtp400Response { + return StatusOtp400ResponseFromJSONTyped(json, false); +} + +export function StatusOtp400ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): StatusOtp400Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function StatusOtp400ResponseToJSON(json: any): StatusOtp400Response { + return StatusOtp400ResponseToJSONTyped(json, false); +} + +export function StatusOtp400ResponseToJSONTyped(value?: StatusOtp400Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/index.ts b/frontend/src/api/generated/models/index.ts index a4059b0..a3cdb3a 100644 --- a/frontend/src/api/generated/models/index.ts +++ b/frontend/src/api/generated/models/index.ts @@ -1,15 +1,19 @@ /* tslint:disable */ /* eslint-disable */ export * from './ChatTest200Response'; +export * from './ChatTest200ResponsePayload'; export * from './DisableOtp200Response'; +export * from './DisableOtp400Response'; export * from './DisableOtp401Response'; export * from './DisableOtp500Response'; export * from './EnableOtp200Response'; export * from './EnableOtp200ResponsePayload'; +export * from './EnableOtp400Response'; export * from './EnableOtp401Response'; export * from './EnableOtp401ResponseAnyOf'; export * from './GetUser200Response'; export * from './GetUser200ResponsePayload'; +export * from './GetUser200ResponsePayloadSelfInfo'; export * from './GetUser403Response'; export * from './GetUser404Response'; export * from './GetUserUserParameter'; @@ -40,5 +44,6 @@ export * from './Signin500Response'; export * from './StatusOtp200Response'; export * from './StatusOtp200ResponseAnyOf'; export * from './StatusOtp200ResponseAnyOf1'; +export * from './StatusOtp200ResponseAnyOfPayload'; export * from './StatusOtp401Response'; export * from './StatusOtp500Response'; diff --git a/frontend/src/auth/index.ts b/frontend/src/auth/index.ts index 57159b7..7807bf9 100644 --- a/frontend/src/auth/index.ts +++ b/frontend/src/auth/index.ts @@ -2,49 +2,54 @@ import { showError } from "@app/toast"; import client from '@app/api'; export type User = { - id: string; - guest: boolean; - name: string; + id: string; + guest: boolean; + name: string; + selfInfo?: { + loginName?: string; + provider_id?: string; + provider_user?: string; + } }; let currentUser: User | null = null; export function getUser(): Readonly | null { - return currentUser; + return currentUser; } export function isLogged(): boolean { - return currentUser !== null; + return currentUser !== null; } export function setUser(newUser: User | null) { - currentUser = newUser; + currentUser = newUser; } export async function updateUser(): Promise | null> { - try { - let res = await client.getUser({ user: 'me' }); + try { + let res = await client.getUser({ user: 'me' }); - if (res.kind === "success") { - setUser(res.payload); - return res.payload; - } else if (res.kind === "failure") { - // well no user :D - setUser(null); - return null; - } else if (res.kind === "notLoggedIn") { - setUser(null); - return null; - } else { - setUser(null); - showError(`unknown response: ${JSON.stringify(res)}`); - return null; - } - } catch (e) { - setUser(null); - showError(`failed to get user: ${e}`); - return null; - } + if (res.kind === "success") { + setUser(res.payload); + return res.payload; + } else if (res.kind === "failure") { + // well no user :D + setUser(null); + return null; + } else if (res.kind === "notLoggedIn") { + setUser(null); + return null; + } else { + setUser(null); + showError(`unknown response: ${JSON.stringify(res)}`); + return null; + } + } catch (e) { + setUser(null); + showError(`failed to get user: ${e}`); + return null; + } } Object.assign(window as any, { getUser, setUser, updateUser, isLogged }); diff --git a/frontend/src/pages/index.ts b/frontend/src/pages/index.ts index 48e2f55..125b99d 100644 --- a/frontend/src/pages/index.ts +++ b/frontend/src/pages/index.ts @@ -3,6 +3,7 @@ import './root/root.ts' import './chat/chat.ts' import './login/login.ts' import './signin/signin.ts' +import './profile/profile.ts' // ---- Initial load ---- setTitle(""); diff --git a/frontend/src/pages/login/login.ts b/frontend/src/pages/login/login.ts index c98a84c..2cd0a14 100644 --- a/frontend/src/pages/login/login.ts +++ b/frontend/src/pages/login/login.ts @@ -5,14 +5,98 @@ import { type RouteHandlerParams, type RouteHandlerReturn, } from "@app/routing"; -import { showError, showInfo, showSuccess } from "@app/toast"; +import Cookie from "js-cookie"; import authHtml from "./login.html?raw"; import client from "@app/api"; -import { updateUser } from "@app/auth"; -import Cookie from "js-cookie"; -import loggedInHtml from "./alreadyLoggedin.html?raw"; import cuteCat from "./cuteCat.png"; +import loggedInHtml from "./alreadyLoggedin.html?raw"; +import totpHtml from "./totp.html?raw"; import { isNullish } from "@app/utils"; +import { showError, showInfo, showSuccess } from "@app/toast"; +import { updateUser } from "@app/auth"; + +const TOTP_LENGTH = 6; + +async function handleOtp(app: HTMLElement, token: string, returnTo: string | null) { + app.innerHTML = totpHtml; + + const container = app.querySelector("#totp-container")!; + container.innerHTML = ""; + + const inputs: HTMLInputElement[] = []; + + for (let i = 0; i < TOTP_LENGTH; i++) { + const input = document.createElement("input"); + input.maxLength = 1; + input.inputMode = "numeric"; + input.className = + "w-12 h-12 text-center text-xl border border-gray-300 rounded " + + "focus:outline-none focus:ring-2 focus:ring-blue-500"; + + container.appendChild(input); + inputs.push(input); + + // Handle typing a digit + input.addEventListener("input", async () => { + const value = input.value.replace(/\D/g, ""); + input.value = value; + + // Auto-advance when filled + if (value && i < TOTP_LENGTH - 1) { + inputs[i + 1].focus(); + } + await checkComplete(); + }); + + // Handle backspace + input.addEventListener("keydown", (e) => { + if (e.key === "Backspace" && !input.value && i > 0) { + inputs[i - 1].focus(); + } + }); + + // Handle pasting a full code + input.addEventListener("paste", (e: ClipboardEvent) => { + const pasted = e.clipboardData?.getData("text") ?? ""; + const digits = pasted.replace(/\D/g, "").slice(0, TOTP_LENGTH); + + if (digits.length > 1) { + e.preventDefault(); + digits.split("").forEach((d, idx) => { + if (inputs[idx]) inputs[idx].value = d; + }); + if (digits.length === TOTP_LENGTH) checkComplete(); + } + }); + } + + // Check if all digits are entered and then call totpSend + async function checkComplete() { + const code = inputs.map((i) => i.value).join(""); + if (code.length === TOTP_LENGTH && /^[0-9]+$/.test(code)) { + let res = await client.loginOtp({ + loginOtpRequest: { + code, token, + } + }) + + if (res.kind === "success") { + Cookie.set("token", res.payload.token, { + path: "/", + sameSite: "lax", + }); + if (returnTo !== null) navigateTo(returnTo); + else navigateTo("/"); + } + else if (res.kind === "failed") { + showError(`Failed to authenticate: ${res.msg}`); + } + } + } + + inputs[0].focus(); +} + async function handleLogin( _url: string, @@ -67,7 +151,7 @@ async function handleLogin( return showError( "Error while rendering the page: no form found", ); - fLogin.addEventListener("submit", async function (e: SubmitEvent) { + fLogin.addEventListener("submit", async function(e: SubmitEvent) { e.preventDefault(); let form = e.target as HTMLFormElement | null; if (form === null) return showError("Failed to send form..."); @@ -109,8 +193,7 @@ async function handleLogin( break; } case "otpRequired": { - showInfo("Got ask OTP, not yet implemented"); - break; + return await handleOtp(app!, res.payload.token, returnTo); } case "failed": { showError(`Failed to login: ${res.msg}`); diff --git a/frontend/src/pages/login/totp.html b/frontend/src/pages/login/totp.html new file mode 100644 index 0000000..48d3703 --- /dev/null +++ b/frontend/src/pages/login/totp.html @@ -0,0 +1,11 @@ +
+
+

+ Welcome to ft boules +

+
+ +
+
+
+
diff --git a/frontend/src/pages/profile/profile.html b/frontend/src/pages/profile/profile.html new file mode 100644 index 0000000..1c56014 --- /dev/null +++ b/frontend/src/pages/profile/profile.html @@ -0,0 +1,66 @@ +
+
+

Edit Profile

+ + + + +
+ +
+
+ + +
+ + + +
+ + + +
+ + + +
+ + +
+

Two-Factor Authentication (TOTP)

+ +
+ Status: Disabled + +
+ + + + + +
+
+ + +
+ +
+
diff --git a/frontend/src/pages/profile/profile.ts b/frontend/src/pages/profile/profile.ts new file mode 100644 index 0000000..b3d6e77 --- /dev/null +++ b/frontend/src/pages/profile/profile.ts @@ -0,0 +1,159 @@ +import { addRoute, navigateTo, setTitle } from "@app/routing"; +import { showError } from "@app/toast"; +import page from './profile.html?raw' +import { updateUser } from "@app/auth"; +import { isNullish } from "@app/utils"; +import client from "@app/api"; + + +async function route(url: string, _args: { [k: string]: string }) { + setTitle('Edit Profile') + return { + html: page, postInsert: async (app: HTMLElement | undefined) => { + const user = await updateUser(); + if (isNullish(user)) + return showError('No User'); + if (isNullish(app)) + return showError('Failed to render'); + let totpState = await (async () => { + let res = await client.statusOtp(); + if (res.kind === "success") + return { + enabled: (res.msg as string) === "statusOtp.success.enabled", + secret: ((res.msg as string) === "statusOtp.success.enabled") ? res.payload.secret : null, + }; + else { + showError('Failed to get OTP status') + return { + enabled: false, secret: null, + } + } + + })() + // ---- Simulated State ---- + let totpEnabled = totpState.enabled; + let totpSecret = totpState.secret; // would come from backend + + let guestBox = app.querySelector("#isGuestBox")!; + let displayNameWrapper = app.querySelector("#displayNameWrapper")!; + let displayNameBox = app.querySelector("#displayNameBox")!; + let displayNameButton = app.querySelector("#displayNameButton")!; + let loginNameWrapper = app.querySelector("#loginNameWrapper")!; + let loginNameBox = app.querySelector("#loginNameBox")!; + let passwordWrapper = app.querySelector("#passwordWrapper")!; + let passwordBox = app.querySelector("#passwordBox")!; + let passwordButton = app.querySelector("#passwordButton")!; + + + if (!isNullish(user.selfInfo?.loginName)) + loginNameBox.innerText = user.selfInfo?.loginName; + else + loginNameBox.innerHTML = 'You don\'t have a login name'; + displayNameBox.value = user.name; + + guestBox.hidden = !user.guest; + + // ---- DOM Elements ---- + const totpStatusText = app.querySelector("#totpStatusText")!; + const enableBtn = app.querySelector("#enableTotp")!; + const disableBtn = app.querySelector("#disableTotp")!; + const showSecretBtn = app.querySelector("#showSecret")!; + const secretBox = app.querySelector("#totpSecretBox")!; + + if (user.guest) { + for (let c of passwordButton.classList.values()) { + if (c.startsWith('bg-') || c.startsWith('hover:bg-')) + passwordButton.classList.remove(c); + } + passwordButton.disabled = true; + passwordButton.classList.add('bg-gray-700', 'hover:bg-gray-700'); + + passwordBox.disabled = true; + passwordBox.classList.add('color-white'); + + for (let c of displayNameButton.classList.values()) { + if (c.startsWith('bg-') || c.startsWith('hover:bg-')) + displayNameButton.classList.remove(c); + } + displayNameButton.disabled = true; + displayNameButton.classList.add('bg-gray-700'); + displayNameButton.classList.add('color-white'); + + displayNameBox.disabled = true; + displayNameBox.classList.add('color-white'); + + for (let c of enableBtn.classList.values()) { + if (c.startsWith('bg-') || c.startsWith('hover:bg-')) + enableBtn.classList.remove(c); + } + for (let c of disableBtn.classList.values()) { + if (c.startsWith('bg-') || c.startsWith('hover:bg-')) + disableBtn.classList.remove(c); + } + for (let c of showSecretBtn.classList.values()) { + if (c.startsWith('bg-') || c.startsWith('hover:bg-')) + showSecretBtn.classList.remove(c); + } + enableBtn.classList.add('bg-gray-700', 'hover:bg-gray-700'); + disableBtn.classList.add('bg-gray-700', 'hover:bg-gray-700'); + showSecretBtn.classList.add('bg-gray-700', 'hover:bg-gray-700'); + + enableBtn.disabled = true; + disableBtn.disabled = true; + showSecretBtn.disabled = true; + } + + + // ---- Update UI ---- + function refreshTotpUI() { + if (totpEnabled) { + totpStatusText.textContent = "Status: Enabled"; + + enableBtn.classList.add("hidden"); + disableBtn.classList.remove("hidden"); + showSecretBtn.classList.remove("hidden"); + } else { + totpStatusText.textContent = "Status: Disabled"; + + enableBtn.classList.remove("hidden"); + disableBtn.classList.add("hidden"); + showSecretBtn.classList.add("hidden"); + secretBox.classList.add("hidden"); + } + } + + // ---- Button Events ---- + enableBtn.onclick = async () => { + let res = await client.enableOtp(); + if (res.kind === "success") { + navigateTo(url); + } + else { + showError(`failed to activate OTP: ${res.msg}`); + } + }; + + disableBtn.onclick = async () => { + let res = await client.disableOtp(); + if (res.kind === "success") { + navigateTo(url); + } + else { + showError(`failed to deactivate OTP: ${res.msg}`); + } + }; + + showSecretBtn.onclick = () => { + secretBox.textContent = `TOTP Secret: ${totpSecret}`; + secretBox.classList.toggle("hidden"); + }; + + // Initialize UI state + refreshTotpUI(); + } + }; +} + + + +addRoute('/profile', route) diff --git a/frontend/src/routing/index.ts b/frontend/src/routing/index.ts index 0c0b9a4..02cd433 100644 --- a/frontend/src/routing/index.ts +++ b/frontend/src/routing/index.ts @@ -46,12 +46,11 @@ export class RouteHandlerData { constructor(url: string, handler: RouteHandler, special_args: Partial) { this.special_args = Object.assign({}, RouteHandlerData.SPECIAL_ARGS_DEFAULT); Object.assign(this.special_args, special_args); - console.log(url, this.special_args); let parsed = RouteHandlerData.parseUrl(url); this.handler = handler; - this.parts = parsed.parts; - this.url = parsed.parts.map((v, i) => v ?? `:${i}`).reduce((p, c) => `${p}/${c}`, ''); + this.parts = parsed.parts.filter(p => p?.length !== 0); + this.url = parsed.parts.filter(p => p?.length !== 0).map((v, i) => v ?? `:${i}`).reduce((p, c) => `${p}/${c}`, ''); this.args = parsed.args; this.orignal_url = parsed.original; } @@ -99,7 +98,7 @@ function urlToParts(url: string): string[] { let parts = trimed.split('/'); if (parts.at(0) === 'app') parts.shift(); - return parts; + return parts.filter(p => p.length !== 0); } function setupRoutes(): [ @@ -190,8 +189,6 @@ export async function handleRoute() { } let user = await updateUser(); - console.log(route_handler); - console.log(user, !route_handler.special_args.bypass_auth, user === null && !route_handler.special_args.bypass_auth); if (user === null && !route_handler.special_args.bypass_auth) return navigateTo(`/login?returnTo=${encodeURIComponent(window.location.pathname)}`) const app = document.getElementById('app')!; diff --git a/frontend/src/routing/special_routes.ts b/frontend/src/routing/special_routes.ts index b0c5ee8..5b7b7f6 100644 --- a/frontend/src/routing/special_routes.ts +++ b/frontend/src/routing/special_routes.ts @@ -2,8 +2,6 @@ import { escapeHTML } from "@app/utils"; import { getRoute, type RouteHandlerParams } from "@app/routing"; export async function route_404(url: string, _args: RouteHandlerParams): Promise { - console.log(`asked about route '${url}: not found'`) - console.log(getRoute()) return `
404 - Not Found

diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 7a6a7e3..00dab97 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -7,6 +7,10 @@ export default defineConfig({ tailwindcss(), tsconfigPaths(), ], + build: { + minify: false, + sourcemap: true, + }, server: { hmr: { protocol: 'ws', diff --git a/src/@shared/src/auth/index.ts b/src/@shared/src/auth/index.ts index 44a1312..dba527a 100644 --- a/src/@shared/src/auth/index.ts +++ b/src/@shared/src/auth/index.ts @@ -14,6 +14,7 @@ const kRouteAuthDone = Symbol('shared-route-auth-done'); type AuthedUser = { id: UserId; name: string; + guest: boolean; }; declare module 'fastify' { @@ -118,7 +119,7 @@ export const authPlugin = fp<{ onlySchema?: boolean }>(async (fastify, { onlySch .clearCookie('token', { path: '/' }) .makeResponse(401, 'notLoggedIn', 'auth.noUser'); } - req.authUser = { id: user.id, name: user.display_name }; + req.authUser = { id: user.id, name: user.name, guest: user.guest }; } catch { return res diff --git a/src/auth/openapi.json b/src/auth/openapi.json index 6522bdf..c34cda3 100644 --- a/src/auth/openapi.json +++ b/src/auth/openapi.json @@ -38,6 +38,32 @@ } } }, + "400": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "failure" + ] + }, + "msg": { + "enum": [ + "disableOtp.failure.guest" + ] + } + } + } + } + } + }, "401": { "description": "Default Response", "content": { @@ -139,6 +165,32 @@ } } }, + "400": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "failure" + ] + }, + "msg": { + "enum": [ + "enableOtp.failure.guest" + ] + } + } + } + } + } + }, "401": { "description": "Default Response", "content": { @@ -846,12 +898,12 @@ "payload": { "type": "object", "required": [ - "url" + "secret" ], "properties": { - "url": { + "secret": { "type": "string", - "description": "The otp url to feed into a 2fa app" + "description": "The otp secret" } } } diff --git a/src/auth/src/routes/disableOtp.ts b/src/auth/src/routes/disableOtp.ts index 2336362..4ef9292 100644 --- a/src/auth/src/routes/disableOtp.ts +++ b/src/auth/src/routes/disableOtp.ts @@ -7,6 +7,7 @@ import { typeResponse, isNullish } from '@shared/utils'; export const DisableOtpRes = { '200': typeResponse('success', 'disableOtp.success'), '500': typeResponse('failure', 'disableOtp.failure.generic'), + '400': typeResponse('failure', 'disableOtp.failure.guest'), }; @@ -18,6 +19,13 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise => { async function(req, res) { void res; if (isNullish(req.authUser)) { return res.makeResponse(500, 'failure', 'disableOtp.failure.generic'); } + if (req.authUser.guest) { + return res.makeResponse( + 400, + 'failure', + 'disableOtp.failure.guest', + ); + } this.db.deleteUserOtpSecret(req.authUser.id); return res.makeResponse(200, 'success', 'disableOtp.success'); }, diff --git a/src/auth/src/routes/enableOtp.ts b/src/auth/src/routes/enableOtp.ts index 0fe2d78..7732c2c 100644 --- a/src/auth/src/routes/enableOtp.ts +++ b/src/auth/src/routes/enableOtp.ts @@ -10,6 +10,7 @@ export const EnableOtpRes = { url: Type.String({ description: 'The otp url to feed into a 2fa app' }), }), '401': typeResponse('failure', ['enableOtp.failure.noUser', 'enableOtp.failure.noSecret']), + '400': typeResponse('failure', ['enableOtp.failure.guest']), }; export type EnableOtpRes = MakeStaticResponse; @@ -21,6 +22,13 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise => { { schema: { response: EnableOtpRes, operationId: 'enableOtp' }, config: { requireAuth: true } }, async function(req, res) { if (isNullish(req.authUser)) { return res.makeResponse(403, 'failure', 'enableOtp.failure.noUser'); } + if (req.authUser.guest) { + return res.makeResponse( + 400, + 'failure', + 'enableOtp.failure.guest', + ); + } const otpSecret = this.db.ensureUserOtpSecret(req.authUser!.id); if (isNullish(otpSecret)) { return res.makeResponse(403, 'failure', 'enableOtp.failure.noSecret'); } diff --git a/src/auth/src/routes/statusOtp.ts b/src/auth/src/routes/statusOtp.ts index 3236619..41d15a7 100644 --- a/src/auth/src/routes/statusOtp.ts +++ b/src/auth/src/routes/statusOtp.ts @@ -2,12 +2,12 @@ import { FastifyPluginAsync } from 'fastify'; import { Type } from 'typebox'; import { isNullish, MakeStaticResponse, typeResponse } from '@shared/utils'; -import { Otp } from '@shared/auth'; - export const StatusOtpRes = { 200: Type.Union([ - typeResponse('success', 'statusOtp.success.enabled', { url: Type.String({ description: 'The otp url to feed into a 2fa app' }) }), + typeResponse('success', 'statusOtp.success.enabled', { + secret: Type.String({ description: 'The otp secret' }), + }), typeResponse('success', 'statusOtp.success.disabled'), ]), 500: typeResponse('failure', 'statusOtp.failure.generic'), @@ -19,13 +19,32 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise => { void _opts; fastify.get( '/api/auth/statusOtp', - { schema: { response: StatusOtpRes, operationId: 'statusOtp' }, config: { requireAuth: true } }, + { + schema: { response: StatusOtpRes, operationId: 'statusOtp' }, + config: { requireAuth: true }, + }, async function(req, res) { - if (isNullish(req.authUser)) { return res.makeResponse(500, 'failure', 'statusOtp.failure.generic'); } + if (isNullish(req.authUser)) { + return res.makeResponse( + 500, + 'failure', + 'statusOtp.failure.generic', + ); + } const otpSecret = this.db.getUserOtpSecret(req.authUser.id); - if (isNullish(otpSecret)) { return res.makeResponse(200, 'success', 'statusOtp.success.disabled'); } - const otp = new Otp({ secret: otpSecret }); - return res.makeResponse(200, 'success', 'statusOtp.success.enabled', { url: otp.totpURL }); + if (isNullish(otpSecret)) { + return res.makeResponse( + 200, + 'success', + 'statusOtp.success.disabled', + ); + } + return res.makeResponse( + 200, + 'success', + 'statusOtp.success.enabled', + { secret: otpSecret }, + ); }, ); }; diff --git a/src/openapi.json b/src/openapi.json index 4327973..4ab32ff 100644 --- a/src/openapi.json +++ b/src/openapi.json @@ -51,6 +51,32 @@ } } }, + "400": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "failure" + ] + }, + "msg": { + "enum": [ + "disableOtp.failure.guest" + ] + } + } + } + } + } + }, "401": { "description": "Default Response", "content": { @@ -155,6 +181,32 @@ } } }, + "400": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "failure" + ] + }, + "msg": { + "enum": [ + "enableOtp.failure.guest" + ] + } + } + } + } + } + }, "401": { "description": "Default Response", "content": { @@ -883,12 +935,12 @@ "payload": { "type": "object", "required": [ - "url" + "secret" ], "properties": { - "url": { + "secret": { "type": "string", - "description": "The otp url to feed into a 2fa app" + "description": "The otp secret" } } } @@ -1069,6 +1121,20 @@ }, "guest": { "type": "boolean" + }, + "selfInfo": { + "type": "object", + "properties": { + "login_name": { + "type": "string" + }, + "provider_id": { + "type": "string" + }, + "provider_user": { + "type": "string" + } + } } } } diff --git a/src/user/openapi.json b/src/user/openapi.json index c785e10..87744dc 100644 --- a/src/user/openapi.json +++ b/src/user/openapi.json @@ -72,6 +72,20 @@ }, "guest": { "type": "boolean" + }, + "selfInfo": { + "type": "object", + "properties": { + "login_name": { + "type": "string" + }, + "provider_id": { + "type": "string" + }, + "provider_user": { + "type": "string" + } + } } } } diff --git a/src/user/src/routes/info.ts b/src/user/src/routes/info.ts index edf7c00..869835e 100644 --- a/src/user/src/routes/info.ts +++ b/src/user/src/routes/info.ts @@ -7,6 +7,11 @@ import { isNullish, MakeStaticResponse, typeResponse } from '@shared/utils'; export const UserInfoRes = { '200': typeResponse('success', 'userinfo.success', { name: Type.String(), id: Type.String(), guest: Type.Boolean(), + selfInfo: Type.Optional(Type.Object({ + login_name: Type.Optional(Type.String()), + provider_id: Type.Optional(Type.String()), + provider_user: Type.Optional(Type.String()), + })), }), '403': typeResponse('failure', 'userinfo.failure.notLoggedIn'), '404': typeResponse('failure', 'userinfo.failure.unknownUser'), @@ -38,6 +43,7 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise => { if (req.params.user === 'me') { req.params.user = req.authUser.id; } + const askSelf = req.params.user === req.authUser.id; const user = this.db.getUser(req.params.user); if (isNullish(user)) { @@ -57,6 +63,11 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise => { // ``` // is the same as `val = !!something` guest: !!user.guest, + selfInfo: askSelf ? { + login_name: user.login, + provider_id: user.provider_name, + provider_user: user.provider_unique, + } : null, }; return res.makeResponse(200, 'success', 'userinfo.success', payload); From 2ad9fe9859db9ee310d58daaccb88ca1d4829f61 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Thu, 4 Dec 2025 16:03:52 +0100 Subject: [PATCH 03/10] feat(nginx): Fixed going to `/` not working --- frontend/index.html | 4 +++- nginx/conf/locations/app.conf | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 47a018d..5163217 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -25,9 +25,11 @@ class="fixed top-14 left-0 w-64 h-full bg-gray-900 text-white transform -translate-x-full transition-transform duration-300 ease-in-out z-40"> diff --git a/nginx/conf/locations/app.conf b/nginx/conf/locations/app.conf index 5554a40..75bca27 100644 --- a/nginx/conf/locations/app.conf +++ b/nginx/conf/locations/app.conf @@ -3,6 +3,10 @@ location /app { try_files /index.html =404; } -location /assets { +location /assets { root /volumes/static/app/; } + +location / { + return 301 https://$http_host/app; +} From 085de411948ee1409c052e84833dd1ca5aa68786 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Sat, 6 Dec 2025 19:07:13 +0100 Subject: [PATCH 04/10] feat(frontend/otp): allow changing otp status and showing qrcode --- frontend/package.json | 2 + frontend/pnpm-lock.yaml | 255 +++++++++++++++++++++++- frontend/src/pages/profile/profile.html | 7 +- frontend/src/pages/profile/profile.ts | 154 +++++++++----- 4 files changed, 359 insertions(+), 59 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index b0fc5a2..a25c3ec 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,7 +16,9 @@ }, "dependencies": { "@tailwindcss/vite": "^4.1.17", + "@types/qrcode": "^1.5.6", "js-cookie": "^3.0.5", + "qrcode": "^1.5.4", "socket.io-client": "^4.8.1", "tailwindcss": "^4.1.17" } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index ac3aea5..4ca399c 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -10,10 +10,16 @@ importers: dependencies: '@tailwindcss/vite': specifier: ^4.1.17 - version: 4.1.17(vite@7.2.7(jiti@2.6.1)(lightningcss@1.30.2)) + version: 4.1.17(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)) + '@types/qrcode': + specifier: ^1.5.6 + version: 1.5.6 js-cookie: specifier: ^3.0.5 version: 3.0.5 + qrcode: + specifier: ^1.5.4 + version: 1.5.4 socket.io-client: specifier: ^4.8.1 version: 4.8.1 @@ -29,10 +35,10 @@ importers: version: 5.9.3 vite: specifier: ^7.2.7 - version: 7.2.7(jiti@2.6.1)(lightningcss@1.30.2) + version: 7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.3)(vite@7.2.7(jiti@2.6.1)(lightningcss@1.30.2)) + version: 5.1.4(typescript@5.9.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)) packages: @@ -417,6 +423,34 @@ packages: '@types/js-cookie@3.0.6': resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==} + '@types/node@24.10.2': + resolution: {integrity: sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==} + + '@types/qrcode@1.5.6': + resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + debug@4.3.7: resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} @@ -435,10 +469,20 @@ packages: supports-color: optional: true + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + engine.io-client@6.6.3: resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==} @@ -464,17 +508,29 @@ packages: picomatch: optional: true + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -553,6 +609,10 @@ packages: resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -564,6 +624,22 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -571,15 +647,34 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + qrcode@1.5.4: + resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} + engines: {node: '>=10.13.0'} + hasBin: true + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + rollup@4.53.3: resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + socket.io-client@4.8.1: resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} engines: {node: '>=10.0.0'} @@ -592,6 +687,14 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + tailwindcss@4.1.17: resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} @@ -618,6 +721,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + vite-tsconfig-paths@5.1.4: resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} peerDependencies: @@ -666,6 +772,13 @@ packages: yaml: optional: true + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + ws@8.17.1: resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} engines: {node: '>=10.0.0'} @@ -682,6 +795,17 @@ packages: resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} engines: {node: '>=0.4.0'} + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + snapshots: '@esbuild/aix-ppc64@0.25.12': @@ -910,17 +1034,45 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.17 '@tailwindcss/oxide-win32-x64-msvc': 4.1.17 - '@tailwindcss/vite@4.1.17(vite@7.2.7(jiti@2.6.1)(lightningcss@1.30.2))': + '@tailwindcss/vite@4.1.17(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2))': dependencies: '@tailwindcss/node': 4.1.17 '@tailwindcss/oxide': 4.1.17 tailwindcss: 4.1.17 - vite: 7.2.7(jiti@2.6.1)(lightningcss@1.30.2) + vite: 7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2) '@types/estree@1.0.8': {} '@types/js-cookie@3.0.6': {} + '@types/node@24.10.2': + dependencies: + undici-types: 7.16.0 + + '@types/qrcode@1.5.6': + dependencies: + '@types/node': 24.10.2 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + camelcase@5.3.1: {} + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + debug@4.3.7: dependencies: ms: 2.1.3 @@ -929,8 +1081,14 @@ snapshots: dependencies: ms: 2.1.3 + decamelize@1.2.0: {} + detect-libc@2.1.2: {} + dijkstrajs@1.0.3: {} + + emoji-regex@8.0.0: {} + engine.io-client@6.6.3: dependencies: '@socket.io/component-emitter': 3.1.2 @@ -983,13 +1141,22 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + fsevents@2.3.3: optional: true + get-caller-file@2.0.5: {} + globrex@0.1.2: {} graceful-fs@4.2.11: {} + is-fullwidth-code-point@3.0.0: {} + jiti@2.6.1: {} js-cookie@3.0.5: {} @@ -1043,6 +1210,10 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.2 lightningcss-win32-x64-msvc: 1.30.2 + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -1051,16 +1222,40 @@ snapshots: nanoid@3.3.11: {} + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-try@2.2.0: {} + + path-exists@4.0.0: {} + picocolors@1.1.1: {} picomatch@4.0.3: {} + pngjs@5.0.0: {} + postcss@8.5.6: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 + qrcode@1.5.4: + dependencies: + dijkstrajs: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + + require-directory@2.1.1: {} + + require-main-filename@2.0.0: {} + rollup@4.53.3: dependencies: '@types/estree': 1.0.8 @@ -1089,6 +1284,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.53.3 fsevents: 2.3.3 + set-blocking@2.0.0: {} + socket.io-client@4.8.1: dependencies: '@socket.io/component-emitter': 3.1.2 @@ -1109,6 +1306,16 @@ snapshots: source-map-js@1.2.1: {} + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + tailwindcss@4.1.17: {} tapable@2.3.0: {} @@ -1124,18 +1331,20 @@ snapshots: typescript@5.9.3: {} - vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.7(jiti@2.6.1)(lightningcss@1.30.2)): + undici-types@7.16.0: {} + + vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) optionalDependencies: - vite: 7.2.7(jiti@2.6.1)(lightningcss@1.30.2) + vite: 7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2) transitivePeerDependencies: - supports-color - typescript - vite@7.2.7(jiti@2.6.1)(lightningcss@1.30.2): + vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -1144,10 +1353,40 @@ snapshots: rollup: 4.53.3 tinyglobby: 0.2.15 optionalDependencies: + '@types/node': 24.10.2 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.30.2 + which-module@2.0.1: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + ws@8.17.1: {} xmlhttprequest-ssl@2.1.2: {} + + y18n@4.0.3: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 diff --git a/frontend/src/pages/profile/profile.html b/frontend/src/pages/profile/profile.html index 1c56014..1b2d180 100644 --- a/frontend/src/pages/profile/profile.html +++ b/frontend/src/pages/profile/profile.html @@ -59,7 +59,12 @@ - + diff --git a/frontend/src/pages/profile/profile.ts b/frontend/src/pages/profile/profile.ts index b3d6e77..c6a7bb0 100644 --- a/frontend/src/pages/profile/profile.ts +++ b/frontend/src/pages/profile/profile.ts @@ -1,109 +1,164 @@ import { addRoute, navigateTo, setTitle } from "@app/routing"; import { showError } from "@app/toast"; -import page from './profile.html?raw' +import page from "./profile.html?raw"; import { updateUser } from "@app/auth"; import { isNullish } from "@app/utils"; import client from "@app/api"; +import QRCode from "qrcode"; +type OAuthQRCodeOptions = { + label?: string; // e.g. your-app:user@example.com + issuer?: string; // e.g. "YourApp" + algorithm?: "SHA1" | "SHA256" | "SHA512"; + digits?: number; + period?: number; +}; + +/** + * Renders an OAuth2-compatible TOTP QR code into a canvas. + * + * @param canvas HTMLCanvasElement to draw into + * @param secret Base32-encoded shared secret + * @param options Meta data for QR (label, issuer, etc.) + */ +export async function renderOAuth2QRCode( + canvas: HTMLCanvasElement, + secret: string, +): Promise { + // Encode the otpauth:// URL + const otpauthUrl = new URL(`otpauth://totp/ft_boule:totp`); + + otpauthUrl.searchParams.set("secret", secret.replace(/=+$/, "")); + otpauthUrl.searchParams.set("issuer", "ft_boule"); + + // Render QR code into the canvas + await QRCode.toCanvas(canvas, otpauthUrl.toString(), { + margin: 1, + scale: 5, + }); + canvas.style.width = ""; + canvas.style.height = ""; +} async function route(url: string, _args: { [k: string]: string }) { - setTitle('Edit Profile') + setTitle("Edit Profile"); return { - html: page, postInsert: async (app: HTMLElement | undefined) => { + html: page, + postInsert: async (app: HTMLElement | undefined) => { const user = await updateUser(); - if (isNullish(user)) - return showError('No User'); - if (isNullish(app)) - return showError('Failed to render'); + if (isNullish(user)) return showError("No User"); + if (isNullish(app)) return showError("Failed to render"); let totpState = await (async () => { let res = await client.statusOtp(); if (res.kind === "success") return { - enabled: (res.msg as string) === "statusOtp.success.enabled", - secret: ((res.msg as string) === "statusOtp.success.enabled") ? res.payload.secret : null, + enabled: + (res.msg as string) === "statusOtp.success.enabled", + secret: + (res.msg as string) === "statusOtp.success.enabled" + ? res.payload.secret + : null, }; else { - showError('Failed to get OTP status') + showError("Failed to get OTP status"); return { - enabled: false, secret: null, - } + enabled: false, + secret: null, + }; } - - })() + })(); // ---- Simulated State ---- let totpEnabled = totpState.enabled; let totpSecret = totpState.secret; // would come from backend let guestBox = app.querySelector("#isGuestBox")!; - let displayNameWrapper = app.querySelector("#displayNameWrapper")!; - let displayNameBox = app.querySelector("#displayNameBox")!; - let displayNameButton = app.querySelector("#displayNameButton")!; - let loginNameWrapper = app.querySelector("#loginNameWrapper")!; - let loginNameBox = app.querySelector("#loginNameBox")!; - let passwordWrapper = app.querySelector("#passwordWrapper")!; - let passwordBox = app.querySelector("#passwordBox")!; - let passwordButton = app.querySelector("#passwordButton")!; - + let displayNameWrapper = app.querySelector( + "#displayNameWrapper", + )!; + let displayNameBox = + app.querySelector("#displayNameBox")!; + let displayNameButton = + app.querySelector("#displayNameButton")!; + let loginNameWrapper = + app.querySelector("#loginNameWrapper")!; + let loginNameBox = + app.querySelector("#loginNameBox")!; + let passwordWrapper = + app.querySelector("#passwordWrapper")!; + let passwordBox = + app.querySelector("#passwordBox")!; + let passwordButton = + app.querySelector("#passwordButton")!; if (!isNullish(user.selfInfo?.loginName)) loginNameBox.innerText = user.selfInfo?.loginName; else - loginNameBox.innerHTML = 'You don\'t have a login name'; + loginNameBox.innerHTML = + 'You don\'t have a login name'; displayNameBox.value = user.name; guestBox.hidden = !user.guest; // ---- DOM Elements ---- const totpStatusText = app.querySelector("#totpStatusText")!; - const enableBtn = app.querySelector("#enableTotp")!; - const disableBtn = app.querySelector("#disableTotp")!; - const showSecretBtn = app.querySelector("#showSecret")!; + const enableBtn = + app.querySelector("#enableTotp")!; + const disableBtn = + app.querySelector("#disableTotp")!; + const showSecretBtn = + app.querySelector("#showSecret")!; const secretBox = app.querySelector("#totpSecretBox")!; + const secretText = + app.querySelector("#totpSecretText")!; + const secretCanvas = + app.querySelector("#totpSecretCanvas")!; if (user.guest) { for (let c of passwordButton.classList.values()) { - if (c.startsWith('bg-') || c.startsWith('hover:bg-')) + if (c.startsWith("bg-") || c.startsWith("hover:bg-")) passwordButton.classList.remove(c); } passwordButton.disabled = true; - passwordButton.classList.add('bg-gray-700', 'hover:bg-gray-700'); + passwordButton.classList.add( + "bg-gray-700", + "hover:bg-gray-700", + ); passwordBox.disabled = true; - passwordBox.classList.add('color-white'); + passwordBox.classList.add("color-white"); for (let c of displayNameButton.classList.values()) { - if (c.startsWith('bg-') || c.startsWith('hover:bg-')) + if (c.startsWith("bg-") || c.startsWith("hover:bg-")) displayNameButton.classList.remove(c); } displayNameButton.disabled = true; - displayNameButton.classList.add('bg-gray-700'); - displayNameButton.classList.add('color-white'); + displayNameButton.classList.add("bg-gray-700"); + displayNameButton.classList.add("color-white"); displayNameBox.disabled = true; - displayNameBox.classList.add('color-white'); + displayNameBox.classList.add("color-white"); for (let c of enableBtn.classList.values()) { - if (c.startsWith('bg-') || c.startsWith('hover:bg-')) + if (c.startsWith("bg-") || c.startsWith("hover:bg-")) enableBtn.classList.remove(c); } for (let c of disableBtn.classList.values()) { - if (c.startsWith('bg-') || c.startsWith('hover:bg-')) + if (c.startsWith("bg-") || c.startsWith("hover:bg-")) disableBtn.classList.remove(c); } for (let c of showSecretBtn.classList.values()) { - if (c.startsWith('bg-') || c.startsWith('hover:bg-')) + if (c.startsWith("bg-") || c.startsWith("hover:bg-")) showSecretBtn.classList.remove(c); } - enableBtn.classList.add('bg-gray-700', 'hover:bg-gray-700'); - disableBtn.classList.add('bg-gray-700', 'hover:bg-gray-700'); - showSecretBtn.classList.add('bg-gray-700', 'hover:bg-gray-700'); + enableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + disableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + showSecretBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); enableBtn.disabled = true; disableBtn.disabled = true; showSecretBtn.disabled = true; } - // ---- Update UI ---- function refreshTotpUI() { if (totpEnabled) { @@ -127,8 +182,7 @@ async function route(url: string, _args: { [k: string]: string }) { let res = await client.enableOtp(); if (res.kind === "success") { navigateTo(url); - } - else { + } else { showError(`failed to activate OTP: ${res.msg}`); } }; @@ -137,23 +191,23 @@ async function route(url: string, _args: { [k: string]: string }) { let res = await client.disableOtp(); if (res.kind === "success") { navigateTo(url); - } - else { + } else { showError(`failed to deactivate OTP: ${res.msg}`); } }; showSecretBtn.onclick = () => { - secretBox.textContent = `TOTP Secret: ${totpSecret}`; + if (!isNullish(totpSecret)) { + secretText.textContent = totpSecret; + renderOAuth2QRCode(secretCanvas, totpSecret); + } secretBox.classList.toggle("hidden"); }; // Initialize UI state refreshTotpUI(); - } + }, }; } - - -addRoute('/profile', route) +addRoute("/profile", route); From eb5e4f25a1906027d6290b26c9e7c957a8d7d264 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Wed, 10 Dec 2025 16:38:26 +0100 Subject: [PATCH 05/10] yes --- frontend/Dockerfile | 16 +- .../api/generated/.openapi-generator/FILES | 5 + .../src/api/generated/apis/OpenapiOtherApi.ts | 94 ++++- .../models/ChangeDisplayName200Response.ts | 93 +++++ .../models/ChangeDisplayName400Response.ts | 94 +++++ .../models/ChangeDisplayNameRequest.ts | 66 +++ .../generated/models/GuestLogin400Response.ts | 93 +++++ .../api/generated/models/GuestLoginRequest.ts | 65 +++ frontend/src/api/generated/models/index.ts | 5 + frontend/src/auth/index.ts | 4 +- frontend/src/pages/login/login.ts | 2 +- frontend/src/pages/profile/profile.html | 27 +- frontend/src/pages/profile/profile.ts | 379 ++++++++++-------- src/@shared/src/database/init.dbml | 2 +- src/@shared/src/database/init.sql | 2 +- src/@shared/src/database/mixin/user.ts | 33 +- src/auth/openapi.json | 40 ++ src/auth/src/routes/guestLogin.ts | 80 +++- src/auth/src/routes/oauth2/callback.ts | 16 +- src/auth/src/routes/signin.ts | 14 +- src/openapi.json | 151 +++++++ src/user/openapi.json | 108 +++++ src/user/src/routes/changeDisplayName.ts | 44 ++ src/user/src/routes/info.ts | 2 +- 24 files changed, 1233 insertions(+), 202 deletions(-) create mode 100644 frontend/src/api/generated/models/ChangeDisplayName200Response.ts create mode 100644 frontend/src/api/generated/models/ChangeDisplayName400Response.ts create mode 100644 frontend/src/api/generated/models/ChangeDisplayNameRequest.ts create mode 100644 frontend/src/api/generated/models/GuestLogin400Response.ts create mode 100644 frontend/src/api/generated/models/GuestLoginRequest.ts create mode 100644 src/user/src/routes/changeDisplayName.ts diff --git a/frontend/Dockerfile b/frontend/Dockerfile index d772c9e..f44fde2 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,15 +1,23 @@ FROM node:22-alpine AS pnpm_base RUN npm install --global pnpm@10; +FROM pnpm_base AS deps + +COPY ./package.json ./pnpm-lock.yaml ./pnpm-workspace.yaml /src/ +WORKDIR /src +RUN pnpm install --frozen-lockfile; + FROM pnpm_base AS builder -COPY . /src WORKDIR /src -RUN pnpm install --frozen-lockfile && pnpm run build; +COPY --from=deps /src/node_modules /src/node_modules +COPY . /src -FROM node:22-alpine +RUN pnpm run build; -COPY --from=builder /src/dist /dist +FROM pnpm_base + +COPY --from=builder /src/dist /dist COPY ./run.sh /bin/run.sh RUN chmod +x /bin/run.sh diff --git a/frontend/src/api/generated/.openapi-generator/FILES b/frontend/src/api/generated/.openapi-generator/FILES index b05ee98..ed23b0c 100644 --- a/frontend/src/api/generated/.openapi-generator/FILES +++ b/frontend/src/api/generated/.openapi-generator/FILES @@ -1,6 +1,9 @@ apis/OpenapiOtherApi.ts apis/index.ts index.ts +models/ChangeDisplayName200Response.ts +models/ChangeDisplayName400Response.ts +models/ChangeDisplayNameRequest.ts models/ChatTest200Response.ts models/ChatTest200ResponsePayload.ts models/DisableOtp200Response.ts @@ -20,7 +23,9 @@ models/GetUser404Response.ts models/GetUserUserParameter.ts models/GuestLogin200Response.ts models/GuestLogin200ResponsePayload.ts +models/GuestLogin400Response.ts models/GuestLogin500Response.ts +models/GuestLoginRequest.ts models/Login200Response.ts models/Login202Response.ts models/Login202ResponsePayload.ts diff --git a/frontend/src/api/generated/apis/OpenapiOtherApi.ts b/frontend/src/api/generated/apis/OpenapiOtherApi.ts index c37580a..9609421 100644 --- a/frontend/src/api/generated/apis/OpenapiOtherApi.ts +++ b/frontend/src/api/generated/apis/OpenapiOtherApi.ts @@ -15,6 +15,9 @@ import * as runtime from '../runtime'; import type { + ChangeDisplayName200Response, + ChangeDisplayName400Response, + ChangeDisplayNameRequest, ChatTest200Response, DisableOtp200Response, DisableOtp400Response, @@ -28,7 +31,9 @@ import type { GetUser404Response, GetUserUserParameter, GuestLogin200Response, + GuestLogin400Response, GuestLogin500Response, + GuestLoginRequest, Login200Response, Login202Response, Login400Response, @@ -49,6 +54,12 @@ import type { StatusOtp500Response, } from '../models/index'; import { + ChangeDisplayName200ResponseFromJSON, + ChangeDisplayName200ResponseToJSON, + ChangeDisplayName400ResponseFromJSON, + ChangeDisplayName400ResponseToJSON, + ChangeDisplayNameRequestFromJSON, + ChangeDisplayNameRequestToJSON, ChatTest200ResponseFromJSON, ChatTest200ResponseToJSON, DisableOtp200ResponseFromJSON, @@ -75,8 +86,12 @@ import { GetUserUserParameterToJSON, GuestLogin200ResponseFromJSON, GuestLogin200ResponseToJSON, + GuestLogin400ResponseFromJSON, + GuestLogin400ResponseToJSON, GuestLogin500ResponseFromJSON, GuestLogin500ResponseToJSON, + GuestLoginRequestFromJSON, + GuestLoginRequestToJSON, Login200ResponseFromJSON, Login200ResponseToJSON, Login202ResponseFromJSON, @@ -115,10 +130,18 @@ import { StatusOtp500ResponseToJSON, } from '../models/index'; +export interface ChangeDisplayNameOperationRequest { + changeDisplayNameRequest: ChangeDisplayNameRequest; +} + export interface GetUserRequest { user: GetUserUserParameter; } +export interface GuestLoginOperationRequest { + guestLoginRequest?: GuestLoginRequest; +} + export interface LoginOperationRequest { loginRequest: LoginRequest; } @@ -136,6 +159,62 @@ export interface SigninRequest { */ export class OpenapiOtherApi extends runtime.BaseAPI { + /** + */ + async changeDisplayNameRaw(requestParameters: ChangeDisplayNameOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['changeDisplayNameRequest'] == null) { + throw new runtime.RequiredError( + 'changeDisplayNameRequest', + 'Required parameter "changeDisplayNameRequest" was null or undefined when calling changeDisplayName().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + + let urlPath = `/api/user/changeDisplayName`; + + const response = await this.request({ + path: urlPath, + method: 'PUT', + headers: headerParameters, + query: queryParameters, + body: ChangeDisplayNameRequestToJSON(requestParameters['changeDisplayNameRequest']), + }, initOverrides); + + // CHANGED: Handle all status codes defined in the OpenAPI spec, not just 2xx responses + // This allows typed access to error responses (4xx, 5xx) and other status codes. + // The code routes responses based on the actual HTTP status code and returns + // appropriately typed ApiResponse wrappers for each status code. + if (response.status === 200) { + // Object response for status 200 + return new runtime.JSONApiResponse(response, (jsonValue) => ChangeDisplayName200ResponseFromJSON(jsonValue)); + } + if (response.status === 400) { + // Object response for status 400 + return new runtime.JSONApiResponse(response, (jsonValue) => ChangeDisplayName400ResponseFromJSON(jsonValue)); + } + if (response.status === 401) { + // Object response for status 401 + return new runtime.JSONApiResponse(response, (jsonValue) => DisableOtp401ResponseFromJSON(jsonValue)); + } + // CHANGED: Throw error if status code is not handled by any of the defined responses + // This ensures all code paths return a value and provides clear error messages for unexpected status codes + // Only throw if responses were defined but none matched the actual status code + throw new runtime.ResponseError(response, `Unexpected status code: ${response.status}. Expected one of: 200, 400, 401`); + } + + /** + */ + async changeDisplayName(requestParameters: ChangeDisplayNameOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.changeDisplayNameRaw(requestParameters, initOverrides); + return await response.value(); + } + /** */ async chatTestRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { @@ -334,11 +413,13 @@ export class OpenapiOtherApi extends runtime.BaseAPI { /** */ - async guestLoginRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + async guestLoginRaw(requestParameters: GuestLoginOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { const queryParameters: any = {}; const headerParameters: runtime.HTTPHeaders = {}; + headerParameters['Content-Type'] = 'application/json'; + let urlPath = `/api/auth/guest`; @@ -347,6 +428,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI { method: 'POST', headers: headerParameters, query: queryParameters, + body: GuestLoginRequestToJSON(requestParameters['guestLoginRequest']), }, initOverrides); // CHANGED: Handle all status codes defined in the OpenAPI spec, not just 2xx responses @@ -357,6 +439,10 @@ export class OpenapiOtherApi extends runtime.BaseAPI { // Object response for status 200 return new runtime.JSONApiResponse(response, (jsonValue) => GuestLogin200ResponseFromJSON(jsonValue)); } + if (response.status === 400) { + // Object response for status 400 + return new runtime.JSONApiResponse(response, (jsonValue) => GuestLogin400ResponseFromJSON(jsonValue)); + } if (response.status === 500) { // Object response for status 500 return new runtime.JSONApiResponse(response, (jsonValue) => GuestLogin500ResponseFromJSON(jsonValue)); @@ -364,13 +450,13 @@ export class OpenapiOtherApi extends runtime.BaseAPI { // CHANGED: Throw error if status code is not handled by any of the defined responses // This ensures all code paths return a value and provides clear error messages for unexpected status codes // Only throw if responses were defined but none matched the actual status code - throw new runtime.ResponseError(response, `Unexpected status code: ${response.status}. Expected one of: 200, 500`); + throw new runtime.ResponseError(response, `Unexpected status code: ${response.status}. Expected one of: 200, 400, 500`); } /** */ - async guestLogin(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { - const response = await this.guestLoginRaw(initOverrides); + async guestLogin(requestParameters: GuestLoginOperationRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.guestLoginRaw(requestParameters, initOverrides); return await response.value(); } diff --git a/frontend/src/api/generated/models/ChangeDisplayName200Response.ts b/frontend/src/api/generated/models/ChangeDisplayName200Response.ts new file mode 100644 index 0000000..63168b7 --- /dev/null +++ b/frontend/src/api/generated/models/ChangeDisplayName200Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ChangeDisplayName200Response + */ +export interface ChangeDisplayName200Response { + /** + * + * @type {string} + * @memberof ChangeDisplayName200Response + */ + kind: ChangeDisplayName200ResponseKindEnum; + /** + * + * @type {string} + * @memberof ChangeDisplayName200Response + */ + msg: ChangeDisplayName200ResponseMsgEnum; +} + + +/** + * @export + */ +export const ChangeDisplayName200ResponseKindEnum = { + Success: 'success' +} as const; +export type ChangeDisplayName200ResponseKindEnum = typeof ChangeDisplayName200ResponseKindEnum[keyof typeof ChangeDisplayName200ResponseKindEnum]; + +/** + * @export + */ +export const ChangeDisplayName200ResponseMsgEnum = { + ChangeDisplayNameSuccess: 'changeDisplayName.success' +} as const; +export type ChangeDisplayName200ResponseMsgEnum = typeof ChangeDisplayName200ResponseMsgEnum[keyof typeof ChangeDisplayName200ResponseMsgEnum]; + + +/** + * Check if a given object implements the ChangeDisplayName200Response interface. + */ +export function instanceOfChangeDisplayName200Response(value: object): value is ChangeDisplayName200Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function ChangeDisplayName200ResponseFromJSON(json: any): ChangeDisplayName200Response { + return ChangeDisplayName200ResponseFromJSONTyped(json, false); +} + +export function ChangeDisplayName200ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangeDisplayName200Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function ChangeDisplayName200ResponseToJSON(json: any): ChangeDisplayName200Response { + return ChangeDisplayName200ResponseToJSONTyped(json, false); +} + +export function ChangeDisplayName200ResponseToJSONTyped(value?: ChangeDisplayName200Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/ChangeDisplayName400Response.ts b/frontend/src/api/generated/models/ChangeDisplayName400Response.ts new file mode 100644 index 0000000..1487962 --- /dev/null +++ b/frontend/src/api/generated/models/ChangeDisplayName400Response.ts @@ -0,0 +1,94 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ChangeDisplayName400Response + */ +export interface ChangeDisplayName400Response { + /** + * + * @type {string} + * @memberof ChangeDisplayName400Response + */ + kind: ChangeDisplayName400ResponseKindEnum; + /** + * + * @type {string} + * @memberof ChangeDisplayName400Response + */ + msg: ChangeDisplayName400ResponseMsgEnum; +} + + +/** + * @export + */ +export const ChangeDisplayName400ResponseKindEnum = { + Failure: 'failure' +} as const; +export type ChangeDisplayName400ResponseKindEnum = typeof ChangeDisplayName400ResponseKindEnum[keyof typeof ChangeDisplayName400ResponseKindEnum]; + +/** + * @export + */ +export const ChangeDisplayName400ResponseMsgEnum = { + ChangeDisplayNameAlreadyExist: 'changeDisplayName.alreadyExist', + ChangeDisplayNameInvalid: 'changeDisplayName.invalid' +} as const; +export type ChangeDisplayName400ResponseMsgEnum = typeof ChangeDisplayName400ResponseMsgEnum[keyof typeof ChangeDisplayName400ResponseMsgEnum]; + + +/** + * Check if a given object implements the ChangeDisplayName400Response interface. + */ +export function instanceOfChangeDisplayName400Response(value: object): value is ChangeDisplayName400Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function ChangeDisplayName400ResponseFromJSON(json: any): ChangeDisplayName400Response { + return ChangeDisplayName400ResponseFromJSONTyped(json, false); +} + +export function ChangeDisplayName400ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangeDisplayName400Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function ChangeDisplayName400ResponseToJSON(json: any): ChangeDisplayName400Response { + return ChangeDisplayName400ResponseToJSONTyped(json, false); +} + +export function ChangeDisplayName400ResponseToJSONTyped(value?: ChangeDisplayName400Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/ChangeDisplayNameRequest.ts b/frontend/src/api/generated/models/ChangeDisplayNameRequest.ts new file mode 100644 index 0000000..c72b7bb --- /dev/null +++ b/frontend/src/api/generated/models/ChangeDisplayNameRequest.ts @@ -0,0 +1,66 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ChangeDisplayNameRequest + */ +export interface ChangeDisplayNameRequest { + /** + * New Display Name + * @type {string} + * @memberof ChangeDisplayNameRequest + */ + name: string; +} + +/** + * Check if a given object implements the ChangeDisplayNameRequest interface. + */ +export function instanceOfChangeDisplayNameRequest(value: object): value is ChangeDisplayNameRequest { + if (!('name' in value) || value['name'] === undefined) return false; + return true; +} + +export function ChangeDisplayNameRequestFromJSON(json: any): ChangeDisplayNameRequest { + return ChangeDisplayNameRequestFromJSONTyped(json, false); +} + +export function ChangeDisplayNameRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangeDisplayNameRequest { + if (json == null) { + return json; + } + return { + + 'name': json['name'], + }; +} + +export function ChangeDisplayNameRequestToJSON(json: any): ChangeDisplayNameRequest { + return ChangeDisplayNameRequestToJSONTyped(json, false); +} + +export function ChangeDisplayNameRequestToJSONTyped(value?: ChangeDisplayNameRequest | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'name': value['name'], + }; +} + diff --git a/frontend/src/api/generated/models/GuestLogin400Response.ts b/frontend/src/api/generated/models/GuestLogin400Response.ts new file mode 100644 index 0000000..8f4c1ce --- /dev/null +++ b/frontend/src/api/generated/models/GuestLogin400Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface GuestLogin400Response + */ +export interface GuestLogin400Response { + /** + * + * @type {string} + * @memberof GuestLogin400Response + */ + kind: GuestLogin400ResponseKindEnum; + /** + * + * @type {string} + * @memberof GuestLogin400Response + */ + msg: GuestLogin400ResponseMsgEnum; +} + + +/** + * @export + */ +export const GuestLogin400ResponseKindEnum = { + Failed: 'failed' +} as const; +export type GuestLogin400ResponseKindEnum = typeof GuestLogin400ResponseKindEnum[keyof typeof GuestLogin400ResponseKindEnum]; + +/** + * @export + */ +export const GuestLogin400ResponseMsgEnum = { + GuestLoginFailedInvalid: 'guestLogin.failed.invalid' +} as const; +export type GuestLogin400ResponseMsgEnum = typeof GuestLogin400ResponseMsgEnum[keyof typeof GuestLogin400ResponseMsgEnum]; + + +/** + * Check if a given object implements the GuestLogin400Response interface. + */ +export function instanceOfGuestLogin400Response(value: object): value is GuestLogin400Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function GuestLogin400ResponseFromJSON(json: any): GuestLogin400Response { + return GuestLogin400ResponseFromJSONTyped(json, false); +} + +export function GuestLogin400ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): GuestLogin400Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function GuestLogin400ResponseToJSON(json: any): GuestLogin400Response { + return GuestLogin400ResponseToJSONTyped(json, false); +} + +export function GuestLogin400ResponseToJSONTyped(value?: GuestLogin400Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/GuestLoginRequest.ts b/frontend/src/api/generated/models/GuestLoginRequest.ts new file mode 100644 index 0000000..15aa94b --- /dev/null +++ b/frontend/src/api/generated/models/GuestLoginRequest.ts @@ -0,0 +1,65 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface GuestLoginRequest + */ +export interface GuestLoginRequest { + /** + * + * @type {string} + * @memberof GuestLoginRequest + */ + name?: string; +} + +/** + * Check if a given object implements the GuestLoginRequest interface. + */ +export function instanceOfGuestLoginRequest(value: object): value is GuestLoginRequest { + return true; +} + +export function GuestLoginRequestFromJSON(json: any): GuestLoginRequest { + return GuestLoginRequestFromJSONTyped(json, false); +} + +export function GuestLoginRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): GuestLoginRequest { + if (json == null) { + return json; + } + return { + + 'name': json['name'] == null ? undefined : json['name'], + }; +} + +export function GuestLoginRequestToJSON(json: any): GuestLoginRequest { + return GuestLoginRequestToJSONTyped(json, false); +} + +export function GuestLoginRequestToJSONTyped(value?: GuestLoginRequest | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'name': value['name'], + }; +} + diff --git a/frontend/src/api/generated/models/index.ts b/frontend/src/api/generated/models/index.ts index a3cdb3a..f00b24e 100644 --- a/frontend/src/api/generated/models/index.ts +++ b/frontend/src/api/generated/models/index.ts @@ -1,5 +1,8 @@ /* tslint:disable */ /* eslint-disable */ +export * from './ChangeDisplayName200Response'; +export * from './ChangeDisplayName400Response'; +export * from './ChangeDisplayNameRequest'; export * from './ChatTest200Response'; export * from './ChatTest200ResponsePayload'; export * from './DisableOtp200Response'; @@ -19,7 +22,9 @@ export * from './GetUser404Response'; export * from './GetUserUserParameter'; export * from './GuestLogin200Response'; export * from './GuestLogin200ResponsePayload'; +export * from './GuestLogin400Response'; export * from './GuestLogin500Response'; +export * from './GuestLoginRequest'; export * from './Login200Response'; export * from './Login202Response'; export * from './Login202ResponsePayload'; diff --git a/frontend/src/auth/index.ts b/frontend/src/auth/index.ts index 7807bf9..449dabc 100644 --- a/frontend/src/auth/index.ts +++ b/frontend/src/auth/index.ts @@ -7,8 +7,8 @@ export type User = { name: string; selfInfo?: { loginName?: string; - provider_id?: string; - provider_user?: string; + providerId?: string; + providerUser?: string; } }; diff --git a/frontend/src/pages/login/login.ts b/frontend/src/pages/login/login.ts index 2cd0a14..5eb5123 100644 --- a/frontend/src/pages/login/login.ts +++ b/frontend/src/pages/login/login.ts @@ -209,7 +209,7 @@ async function handleLogin( document.querySelector("#bGuestLogin"); bLoginAsGuest?.addEventListener("click", async () => { try { - const res = await client.guestLogin(); + const res = await client.guestLogin({ guestLoginRequest: { name: undefined } }); switch (res.kind) { case "success": { Cookie.set("token", res.payload.token, { diff --git a/frontend/src/pages/profile/profile.html b/frontend/src/pages/profile/profile.html index 1b2d180..6752e23 100644 --- a/frontend/src/pages/profile/profile.html +++ b/frontend/src/pages/profile/profile.html @@ -7,12 +7,33 @@ You can't change anything here +
+ + +
+ + -
+ - + +
@@ -35,7 +56,7 @@
-
+

Two-Factor Authentication (TOTP)

diff --git a/frontend/src/pages/profile/profile.ts b/frontend/src/pages/profile/profile.ts index c6a7bb0..39171ce 100644 --- a/frontend/src/pages/profile/profile.ts +++ b/frontend/src/pages/profile/profile.ts @@ -1,5 +1,5 @@ -import { addRoute, navigateTo, setTitle } from "@app/routing"; -import { showError } from "@app/toast"; +import { addRoute, handleRoute, navigateTo, setTitle } from "@app/routing"; +import { showError, showSuccess } from "@app/toast"; import page from "./profile.html?raw"; import { updateUser } from "@app/auth"; import { isNullish } from "@app/utils"; @@ -38,176 +38,219 @@ export async function renderOAuth2QRCode( }); canvas.style.width = ""; canvas.style.height = ""; -} - -async function route(url: string, _args: { [k: string]: string }) { - setTitle("Edit Profile"); - return { - html: page, - postInsert: async (app: HTMLElement | undefined) => { - const user = await updateUser(); - if (isNullish(user)) return showError("No User"); - if (isNullish(app)) return showError("Failed to render"); - let totpState = await (async () => { - let res = await client.statusOtp(); - if (res.kind === "success") - return { - enabled: - (res.msg as string) === "statusOtp.success.enabled", - secret: - (res.msg as string) === "statusOtp.success.enabled" - ? res.payload.secret - : null, - }; - else { - showError("Failed to get OTP status"); - return { - enabled: false, - secret: null, - }; - } - })(); - // ---- Simulated State ---- - let totpEnabled = totpState.enabled; - let totpSecret = totpState.secret; // would come from backend - - let guestBox = app.querySelector("#isGuestBox")!; - let displayNameWrapper = app.querySelector( - "#displayNameWrapper", - )!; - let displayNameBox = - app.querySelector("#displayNameBox")!; - let displayNameButton = - app.querySelector("#displayNameButton")!; - let loginNameWrapper = - app.querySelector("#loginNameWrapper")!; - let loginNameBox = - app.querySelector("#loginNameBox")!; - let passwordWrapper = - app.querySelector("#passwordWrapper")!; - let passwordBox = - app.querySelector("#passwordBox")!; - let passwordButton = - app.querySelector("#passwordButton")!; - - if (!isNullish(user.selfInfo?.loginName)) - loginNameBox.innerText = user.selfInfo?.loginName; - else - loginNameBox.innerHTML = - 'You don\'t have a login name'; - displayNameBox.value = user.name; - - guestBox.hidden = !user.guest; - - // ---- DOM Elements ---- - const totpStatusText = app.querySelector("#totpStatusText")!; - const enableBtn = - app.querySelector("#enableTotp")!; - const disableBtn = - app.querySelector("#disableTotp")!; - const showSecretBtn = - app.querySelector("#showSecret")!; - const secretBox = app.querySelector("#totpSecretBox")!; - const secretText = - app.querySelector("#totpSecretText")!; - const secretCanvas = - app.querySelector("#totpSecretCanvas")!; - - if (user.guest) { - for (let c of passwordButton.classList.values()) { - if (c.startsWith("bg-") || c.startsWith("hover:bg-")) - passwordButton.classList.remove(c); - } - passwordButton.disabled = true; - passwordButton.classList.add( - "bg-gray-700", - "hover:bg-gray-700", - ); - - passwordBox.disabled = true; - passwordBox.classList.add("color-white"); - - for (let c of displayNameButton.classList.values()) { - if (c.startsWith("bg-") || c.startsWith("hover:bg-")) - displayNameButton.classList.remove(c); - } - displayNameButton.disabled = true; - displayNameButton.classList.add("bg-gray-700"); - displayNameButton.classList.add("color-white"); - - displayNameBox.disabled = true; - displayNameBox.classList.add("color-white"); - - for (let c of enableBtn.classList.values()) { - if (c.startsWith("bg-") || c.startsWith("hover:bg-")) - enableBtn.classList.remove(c); - } - for (let c of disableBtn.classList.values()) { - if (c.startsWith("bg-") || c.startsWith("hover:bg-")) - disableBtn.classList.remove(c); - } - for (let c of showSecretBtn.classList.values()) { - if (c.startsWith("bg-") || c.startsWith("hover:bg-")) - showSecretBtn.classList.remove(c); - } - enableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); - disableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); - showSecretBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); - - enableBtn.disabled = true; - disableBtn.disabled = true; - showSecretBtn.disabled = true; + function removeBgColor(...elem: HTMLElement[]) { + for (let e of elem) { + for (let c of e.classList.values()) { + if (c.startsWith("bg-") || c.startsWith("hover:bg-")) + e.classList.remove(c); } + } + } - // ---- Update UI ---- - function refreshTotpUI() { - if (totpEnabled) { - totpStatusText.textContent = "Status: Enabled"; + async function route(url: string, _args: { [k: string]: string }) { + setTitle("Edit Profile"); + return { + html: page, + postInsert: async (app: HTMLElement | undefined) => { + const user = await updateUser(); + if (isNullish(user)) return showError("No User"); + if (isNullish(app)) return showError("Failed to render"); + let totpState = await (async () => { + let res = await client.statusOtp(); + if (res.kind === "success") + return { + enabled: + (res.msg as string) === "statusOtp.success.enabled", + secret: + (res.msg as string) === "statusOtp.success.enabled" + ? res.payload.secret + : null, + }; + else { + showError("Failed to get OTP status"); + return { + enabled: false, + secret: null, + }; + } + })(); + // ---- Simulated State ---- + let totpEnabled = totpState.enabled; + let totpSecret = totpState.secret; // would come from backend - enableBtn.classList.add("hidden"); - disableBtn.classList.remove("hidden"); - showSecretBtn.classList.remove("hidden"); - } else { - totpStatusText.textContent = "Status: Disabled"; + let guestBox = app.querySelector("#isGuestBox")!; + let displayNameWrapper = app.querySelector( + "#displayNameWrapper", + )!; + let displayNameBox = + app.querySelector("#displayNameBox")!; + let displayNameButton = + app.querySelector("#displayNameButton")!; + let loginNameWrapper = + app.querySelector("#loginNameWrapper")!; + let loginNameBox = + app.querySelector("#loginNameBox")!; + let passwordWrapper = + app.querySelector("#passwordWrapper")!; + let passwordBox = + app.querySelector("#passwordBox")!; + let passwordButton = + app.querySelector("#passwordButton")!; - enableBtn.classList.remove("hidden"); - disableBtn.classList.add("hidden"); - showSecretBtn.classList.add("hidden"); - secretBox.classList.add("hidden"); - } - } + let providerWrapper = + app.querySelector("#providerWrapper")!; + let providerNameBox = + app.querySelector("#providerNameBox")!; + let providerUserBox = + app.querySelector("#providerUserBox")!; - // ---- Button Events ---- - enableBtn.onclick = async () => { - let res = await client.enableOtp(); - if (res.kind === "success") { - navigateTo(url); - } else { - showError(`failed to activate OTP: ${res.msg}`); - } + let accountTypeBox = + app.querySelector("#accountType")!; + displayNameBox.value = user.name; + + guestBox.hidden = !user.guest; + + // ---- DOM Elements ---- + const totpStatusText = app.querySelector("#totpStatusText")!; + const enableBtn = + app.querySelector("#enableTotp")!; + const disableBtn = + app.querySelector("#disableTotp")!; + const showSecretBtn = + app.querySelector("#showSecret")!; + const secretBox = app.querySelector("#totpSecretBox")!; + const secretText = + app.querySelector("#totpSecretText")!; + const secretCanvas = + app.querySelector("#totpSecretCanvas")!; + + if (user.guest) { + for (let c of passwordButton.classList.values()) { + if (c.startsWith("bg-") || c.startsWith("hover:bg-")) + passwordButton.classList.remove(c); + } + let totpWrapper = app.querySelector("#totpWrapper")!; + + if (user.guest) { + removeBgColor( + passwordButton, + displayNameButton, + enableBtn, + disableBtn, + showSecretBtn, + ); + + passwordButton.classList.add( + "bg-gray-700", + "hover:bg-gray-700", + ); + + passwordBox.disabled = true; + passwordBox.classList.add("color-white"); + + displayNameButton.disabled = true; + displayNameButton.classList.add("bg-gray-700", "color-white"); + + displayNameBox.disabled = true; + displayNameBox.classList.add("color-white"); + enableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + disableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + showSecretBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + + enableBtn.disabled = true; + disableBtn.disabled = true; + showSecretBtn.disabled = true; + + accountTypeBox.innerText = "Guest"; + } else if (!isNullish(user.selfInfo?.loginName)) { + loginNameWrapper.hidden = false; + loginNameBox.innerText = user.selfInfo.loginName; + + accountTypeBox.innerText = "Normal"; + } else if ( + !isNullish(user.selfInfo?.providerId) && + !isNullish(user.selfInfo?.providerUser) + ) { + providerWrapper.hidden = false; + providerNameBox.innerText = user.selfInfo.providerId; + providerUserBox.innerText = user.selfInfo.providerUser; + + enableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + disableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + showSecretBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + + enableBtn.disabled = true; + disableBtn.disabled = true; + showSecretBtn.disabled = true; + + removeBgColor(enableBtn, disableBtn, showSecretBtn); + passwordWrapper.hidden = true; + totpWrapper.hidden = true; + + accountTypeBox.innerText = "Provider"; + } + + // ---- Update UI ---- + function refreshTotpUI() { + if (totpEnabled) { + totpStatusText.textContent = "Status: Enabled"; + + enableBtn.classList.add("hidden"); + disableBtn.classList.remove("hidden"); + showSecretBtn.classList.remove("hidden"); + } else { + totpStatusText.textContent = "Status: Disabled"; + + enableBtn.classList.remove("hidden"); + disableBtn.classList.add("hidden"); + showSecretBtn.classList.add("hidden"); + secretBox.classList.add("hidden"); + } + } + + // ---- Button Events ---- + enableBtn.onclick = async () => { + let res = await client.enableOtp(); + if (res.kind === "success") { + navigateTo(url); + } else { + showError(`failed to activate OTP: ${res.msg}`); + } + }; + + disableBtn.onclick = async () => { + let res = await client.disableOtp(); + if (res.kind === "success") { + navigateTo(url); + } else { + showError(`failed to deactivate OTP: ${res.msg}`); + } + }; + + showSecretBtn.onclick = () => { + if (!isNullish(totpSecret)) { + secretText.textContent = totpSecret; + renderOAuth2QRCode(secretCanvas, totpSecret); + } + secretBox.classList.toggle("hidden"); + }; + + displayNameButton.onclick = async () => { + let req = await client.changeDisplayName({ + changeDisplayNameRequest: { name: displayNameBox.value }, + }); + if (req.kind === "success") { + showSuccess("Successfully changed display name"); + handleRoute(); + } else { + showError(`Failed to update: ${req.msg}`); + } + }; + + // Initialize UI state + refreshTotpUI(); + }, }; + } - disableBtn.onclick = async () => { - let res = await client.disableOtp(); - if (res.kind === "success") { - navigateTo(url); - } else { - showError(`failed to deactivate OTP: ${res.msg}`); - } - }; - - showSecretBtn.onclick = () => { - if (!isNullish(totpSecret)) { - secretText.textContent = totpSecret; - renderOAuth2QRCode(secretCanvas, totpSecret); - } - secretBox.classList.toggle("hidden"); - }; - - // Initialize UI state - refreshTotpUI(); - }, - }; -} - -addRoute("/profile", route); + addRoute("/profile", route); diff --git a/src/@shared/src/database/init.dbml b/src/@shared/src/database/init.dbml index 1a663de..133e482 100644 --- a/src/@shared/src/database/init.dbml +++ b/src/@shared/src/database/init.dbml @@ -18,7 +18,7 @@ Project Transcendance { Table user { id text [PK, not null] login text [unique] - name text [not null] + name text [not null, unique] password text [null, Note: "If password is NULL, this means that the user is created through OAUTH2 or guest login"] otp text [null, Note: "If otp is NULL, then the user didn't configure 2FA"] guest integer [not null, default: 0] diff --git a/src/@shared/src/database/init.sql b/src/@shared/src/database/init.sql index b77edfe..bf363fb 100644 --- a/src/@shared/src/database/init.sql +++ b/src/@shared/src/database/init.sql @@ -1,7 +1,7 @@ CREATE TABLE IF NOT EXISTS user ( id TEXT PRIMARY KEY NOT NULL, login TEXT UNIQUE, - name TEXT NOT NULL, + name TEXT NOT NULL UNIQUE, password TEXT, otp TEXT, guest INTEGER NOT NULL DEFAULT 0, diff --git a/src/@shared/src/database/mixin/user.ts b/src/@shared/src/database/mixin/user.ts index a931aa8..fbdaba1 100644 --- a/src/@shared/src/database/mixin/user.ts +++ b/src/@shared/src/database/mixin/user.ts @@ -3,6 +3,7 @@ import { Otp } from '@shared/auth'; import { isNullish } from '@shared/utils'; import * as bcrypt from 'bcrypt'; import { UUID, newUUID } from '@shared/utils/uuid'; +import { SqliteError } from 'better-sqlite3'; // never use this directly @@ -20,6 +21,10 @@ export interface IUserDb extends Database { getAllUserFromProvider(provider: string): User[] | undefined, getAllUsers(this: IUserDb): User[] | undefined, + + updateDisplayName(id: UserId, new_name: string): boolean, + + getUserFromDisplayName(name: string): User | undefined, }; export const UserImpl: Omit = { @@ -159,6 +164,24 @@ export const UserImpl: Omit = { const req = this.prepare('SELECT * FROM user WHERE oauth2 = @oauth2').get({ oauth2: `${provider}:${unique}` }) as Partial | undefined; return userFromRow(req); }, + + updateDisplayName(this: IUserDb, id: UserId, new_name: string): boolean { + try { + this.prepare('UPDATE OR FAIL user SET name = @new_name WHERE id = @id').run({ id, new_name }); + return true; + } + catch (e) { + if (e instanceof SqliteError) { + if (e.code === 'SQLITE_CONSTRAINT_UNIQUE') return false; + } + throw e; + } + }, + + getUserFromDisplayName(this: IUserDb, name: string) { + const res = this.prepare('SELECT * FROM user WHERE name = @name LIMIT 1').get({ name }) as User | undefined; + return userFromRow(res); + }, }; export type UserId = UUID; @@ -170,7 +193,7 @@ export type User = { readonly password?: string; readonly otp?: string; readonly guest: boolean; - // will be split/merged from the `provider` column + // will be split/merged from the `oauth2` column readonly provider_name?: string; readonly provider_unique?: string; }; @@ -207,7 +230,7 @@ async function hashPassword( * * @returns The user if it exists, undefined otherwise */ -export function userFromRow(row?: Partial & { provider?: string }>): User | undefined { +export function userFromRow(row?: Partial & { oauth2?: string }>): User | undefined { if (isNullish(row)) return undefined; if (isNullish(row.id)) return undefined; if (isNullish(row.name)) return undefined; @@ -216,9 +239,9 @@ export function userFromRow(row?: Partial; +export const GuestLoginReq = Type.Object({ + name: Type.Optional(Type.String()), +}); + +export type GuestLoginReq = Static; + const getRandomFromList = (list: string[]): string => { return list[Math.floor(Math.random() * list.length)]; }; +const USERNAME_CHECK: RegExp = /^[a-zA-Z_0-9]+$/; + const route: FastifyPluginAsync = async (fastify, _opts): Promise => { void _opts; - fastify.post<{ Body: null, Reply: GuestLoginRes }>( + fastify.post<{ Body: GuestLoginReq; Reply: GuestLoginRes }>( '/api/auth/guest', - { schema: { response: GuestLoginRes, operationId: 'guestLogin' } }, + { + schema: { + body: GuestLoginReq, + response: GuestLoginRes, + operationId: 'guestLogin', + }, + }, async function(req, res) { void req; void res; try { - console.log('DEBUG ----- guest login backend'); - const adjective = getRandomFromList(fastify.words.adjectives); - const noun = getRandomFromList(fastify.words.nouns); + let user_name: string | undefined = req.body?.name; + if (isNullish(user_name)) { + const adjective = getRandomFromList( + fastify.words.adjectives, + ); + const noun = getRandomFromList(fastify.words.nouns); + user_name = `${adjective}${noun}`; + } + else { + if (user_name.length < 4 || user_name.length > 26) { + return res.makeResponse( + 400, + 'failed', + 'guestLogin.failed.invalid', + ); + } + if (!USERNAME_CHECK.test(user_name)) { + return res.makeResponse( + 400, + 'failed', + 'guestLogin.failed.invalid', + ); + } + user_name = `g_${user_name}`; + } - const user = await this.db.createGuestUser(`${adjective} ${noun}`); + const orig = user_name; + let i = 0; + while ( + this.db.getUserFromDisplayName(user_name) !== undefined && + i++ < 5 + ) { + user_name = `${orig}${Date.now() % 1000}`; + } + if (this.db.getUserFromDisplayName(user_name) !== undefined) { + user_name = `${orig}${Date.now()}`; + } + + const user = await this.db.createGuestUser(user_name); if (isNullish(user)) { - return res.makeResponse(500, 'failed', 'guestLogin.failed.generic.unknown'); + return res.makeResponse( + 500, + 'failed', + 'guestLogin.failed.generic.unknown', + ); } return res.makeResponse(200, 'success', 'guestLogin.success', { token: this.signJwt('auth', user.id.toString()), @@ -41,7 +97,11 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise => { } catch (e: unknown) { fastify.log.error(e); - return res.makeResponse(500, 'failed', 'guestLogin.failed.generic.error'); + return res.makeResponse( + 500, + 'failed', + 'guestLogin.failed.generic.error', + ); } }, ); diff --git a/src/auth/src/routes/oauth2/callback.ts b/src/auth/src/routes/oauth2/callback.ts index 10bc72a..36ec4a3 100644 --- a/src/auth/src/routes/oauth2/callback.ts +++ b/src/auth/src/routes/oauth2/callback.ts @@ -30,9 +30,23 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise => { const result = await creq.getCode(); const userinfo = await provider.getUserInfo(result); + + let u = this.db.getOauth2User(provider.display_name, userinfo.unique_id); if (isNullish(u)) { - u = await this.db.createOauth2User(userinfo.name, provider.display_name, userinfo.unique_id); + let user_name = userinfo.name; + const orig = user_name; + let i = 0; + while ( + this.db.getUserFromDisplayName(user_name) !== undefined && + i++ < 100 + ) { + user_name = `${orig}${Date.now() % 1000}`; + } + if (this.db.getUserFromDisplayName(user_name) !== undefined) { + user_name = `${orig}${Date.now()}`; + } + u = await this.db.createOauth2User(user_name, provider.display_name, userinfo.unique_id); } if (isNullish(u)) { return res.code(500).send('failed to fetch or create user...'); diff --git a/src/auth/src/routes/signin.ts b/src/auth/src/routes/signin.ts index f8b304a..fe09163 100644 --- a/src/auth/src/routes/signin.ts +++ b/src/auth/src/routes/signin.ts @@ -47,7 +47,19 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise => { // password is good too ! if (this.db.getUserFromLoginName(name) !== undefined) { return res.makeResponse(400, 'failed', 'signin.failed.username.existing'); } - const u = await this.db.createUser(name, name, password); + let user_name = name; + const orig = user_name; + let i = 0; + while ( + this.db.getUserFromDisplayName(user_name) !== undefined && + i++ < 100 + ) { + user_name = `${orig}${Date.now() % 1000}`; + } + if (this.db.getUserFromDisplayName(user_name) !== undefined) { + user_name = `${orig}${Date.now()}`; + } + const u = await this.db.createUser(name, user_name, password); if (isNullish(u)) { return res.makeResponse(500, 'failed', 'signin.failed.generic'); } // every check has been passed, they are now logged in, using this token to say who they are... diff --git a/src/openapi.json b/src/openapi.json index 4ab32ff..d9b52b6 100644 --- a/src/openapi.json +++ b/src/openapi.json @@ -352,6 +352,20 @@ "/api/auth/guest": { "post": { "operationId": "guestLogin", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + } + } + } + }, "responses": { "200": { "description": "Default Response", @@ -392,6 +406,32 @@ } } }, + "400": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "failed" + ] + }, + "msg": { + "enum": [ + "guestLogin.failed.invalid" + ] + } + } + } + } + } + }, "500": { "description": "Default Response", "content": { @@ -1057,6 +1097,117 @@ ] } }, + "/api/user/changeDisplayName": { + "put": { + "operationId": "changeDisplayName", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "New Display Name" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "success" + ] + }, + "msg": { + "enum": [ + "changeDisplayName.success" + ] + } + } + } + } + } + }, + "400": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "failure" + ] + }, + "msg": { + "enum": [ + "changeDisplayName.alreadyExist", + "changeDisplayName.invalid" + ] + } + } + } + } + } + }, + "401": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "notLoggedIn" + ] + }, + "msg": { + "enum": [ + "auth.noCookie", + "auth.invalidKind", + "auth.noUser", + "auth.invalid" + ] + } + } + } + } + } + } + }, + "tags": [ + "openapi_other" + ] + } + }, "/api/user/info/{user}": { "get": { "operationId": "getUser", diff --git a/src/user/openapi.json b/src/user/openapi.json index 87744dc..8ad61f7 100644 --- a/src/user/openapi.json +++ b/src/user/openapi.json @@ -8,6 +8,114 @@ "schemas": {} }, "paths": { + "/api/user/changeDisplayName": { + "put": { + "operationId": "changeDisplayName", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "New Display Name" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "success" + ] + }, + "msg": { + "enum": [ + "changeDisplayName.success" + ] + } + } + } + } + } + }, + "400": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "failure" + ] + }, + "msg": { + "enum": [ + "changeDisplayName.alreadyExist", + "changeDisplayName.invalid" + ] + } + } + } + } + } + }, + "401": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "notLoggedIn" + ] + }, + "msg": { + "enum": [ + "auth.noCookie", + "auth.invalidKind", + "auth.noUser", + "auth.invalid" + ] + } + } + } + } + } + } + } + } + }, "/api/user/info/{user}": { "get": { "operationId": "getUser", diff --git a/src/user/src/routes/changeDisplayName.ts b/src/user/src/routes/changeDisplayName.ts new file mode 100644 index 0000000..f089c59 --- /dev/null +++ b/src/user/src/routes/changeDisplayName.ts @@ -0,0 +1,44 @@ +import { FastifyPluginAsync } from 'fastify'; + +import { Static, Type } from 'typebox'; +import { isNullish, MakeStaticResponse, typeResponse } from '@shared/utils'; + + +export const ChangeDisplayNameRes = { + '200': typeResponse('success', 'changeDisplayName.success'), + '400': typeResponse('failure', ['changeDisplayName.alreadyExist', 'changeDisplayName.invalid']), +}; + +export type ChangeDisplayNameRes = MakeStaticResponse; + +export const ChangeDisplayNameReq = Type.Object({ name: Type.String({ description: 'New Display Name' }) }); +type ChangeDisplayNameReq = Static; + +const USERNAME_CHECK: RegExp = /^[a-zA-Z_0-9]+$/; +const route: FastifyPluginAsync = async (fastify, _opts): Promise => { + void _opts; + fastify.put<{ Body: ChangeDisplayNameReq }>( + '/api/user/changeDisplayName', + { schema: { body: ChangeDisplayNameReq, response: ChangeDisplayNameRes, operationId: 'changeDisplayName' }, config: { requireAuth: true } }, + async function(req, res) { + if (isNullish(req.authUser)) return; + if (isNullish(req.body.name)) { + return res.makeResponse(400, 'failure', 'changeDisplayName.invalid'); + } + if (req.body.name.length < 4 || req.body.name.length > 32) { + return res.makeResponse(400, 'failure', 'changeDisplayName.invalid'); + } + if (!USERNAME_CHECK.test(req.body.name)) { + return res.makeResponse(400, 'failure', 'changeDisplayName.invalid'); + } + if (this.db.updateDisplayName(req.authUser.id, req.body.name)) { + return res.makeResponse(200, 'success', 'changeDisplayName.success'); + } + else { + return res.makeResponse(400, 'failure', 'changeDisplayName.alreadyExist'); + } + }, + ); +}; + +export default route; diff --git a/src/user/src/routes/info.ts b/src/user/src/routes/info.ts index 869835e..784ea23 100644 --- a/src/user/src/routes/info.ts +++ b/src/user/src/routes/info.ts @@ -49,7 +49,7 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise => { if (isNullish(user)) { return res.makeResponse(404, 'failure', 'userinfo.failure.unknownUser'); } - + console.log(user); const payload = { name: user.name, From dbe323c77e6981db59b79f23b97fdf6974749121 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Wed, 10 Dec 2025 16:50:31 +0100 Subject: [PATCH 06/10] almost done --- frontend/src/pages/profile/profile.html | 53 ++- frontend/src/pages/profile/profile.ts | 428 ++++++++++++------------ 2 files changed, 232 insertions(+), 249 deletions(-) diff --git a/frontend/src/pages/profile/profile.html b/frontend/src/pages/profile/profile.html index 6752e23..76c7565 100644 --- a/frontend/src/pages/profile/profile.html +++ b/frontend/src/pages/profile/profile.html @@ -1,18 +1,14 @@

Edit Profile

- -
- - + + Unknown
- - @@ -39,54 +36,42 @@ - +
- - -
+ - -
+ -
diff --git a/frontend/src/pages/profile/profile.ts b/frontend/src/pages/profile/profile.ts index 39171ce..4b91b84 100644 --- a/frontend/src/pages/profile/profile.ts +++ b/frontend/src/pages/profile/profile.ts @@ -6,15 +6,7 @@ import { isNullish } from "@app/utils"; import client from "@app/api"; import QRCode from "qrcode"; -type OAuthQRCodeOptions = { - label?: string; // e.g. your-app:user@example.com - issuer?: string; // e.g. "YourApp" - algorithm?: "SHA1" | "SHA256" | "SHA512"; - digits?: number; - period?: number; -}; - -/** +/* * Renders an OAuth2-compatible TOTP QR code into a canvas. * * @param canvas HTMLCanvasElement to draw into @@ -38,219 +30,225 @@ export async function renderOAuth2QRCode( }); canvas.style.width = ""; canvas.style.height = ""; - function removeBgColor(...elem: HTMLElement[]) { - for (let e of elem) { - for (let c of e.classList.values()) { - if (c.startsWith("bg-") || c.startsWith("hover:bg-")) - e.classList.remove(c); - } +} +function removeBgColor(...elem: HTMLElement[]) { + for (let e of elem) { + for (let c of e.classList.values()) { + if (c.startsWith("bg-") || c.startsWith("hover:bg-")) + e.classList.remove(c); } } +} - async function route(url: string, _args: { [k: string]: string }) { - setTitle("Edit Profile"); - return { - html: page, - postInsert: async (app: HTMLElement | undefined) => { - const user = await updateUser(); - if (isNullish(user)) return showError("No User"); - if (isNullish(app)) return showError("Failed to render"); - let totpState = await (async () => { - let res = await client.statusOtp(); - if (res.kind === "success") - return { - enabled: - (res.msg as string) === "statusOtp.success.enabled", - secret: - (res.msg as string) === "statusOtp.success.enabled" - ? res.payload.secret - : null, - }; - else { - showError("Failed to get OTP status"); - return { - enabled: false, - secret: null, - }; - } - })(); - // ---- Simulated State ---- - let totpEnabled = totpState.enabled; - let totpSecret = totpState.secret; // would come from backend - - let guestBox = app.querySelector("#isGuestBox")!; - let displayNameWrapper = app.querySelector( - "#displayNameWrapper", - )!; - let displayNameBox = - app.querySelector("#displayNameBox")!; - let displayNameButton = - app.querySelector("#displayNameButton")!; - let loginNameWrapper = - app.querySelector("#loginNameWrapper")!; - let loginNameBox = - app.querySelector("#loginNameBox")!; - let passwordWrapper = - app.querySelector("#passwordWrapper")!; - let passwordBox = - app.querySelector("#passwordBox")!; - let passwordButton = - app.querySelector("#passwordButton")!; - - let providerWrapper = - app.querySelector("#providerWrapper")!; - let providerNameBox = - app.querySelector("#providerNameBox")!; - let providerUserBox = - app.querySelector("#providerUserBox")!; - - let accountTypeBox = - app.querySelector("#accountType")!; - displayNameBox.value = user.name; - - guestBox.hidden = !user.guest; - - // ---- DOM Elements ---- - const totpStatusText = app.querySelector("#totpStatusText")!; - const enableBtn = - app.querySelector("#enableTotp")!; - const disableBtn = - app.querySelector("#disableTotp")!; - const showSecretBtn = - app.querySelector("#showSecret")!; - const secretBox = app.querySelector("#totpSecretBox")!; - const secretText = - app.querySelector("#totpSecretText")!; - const secretCanvas = - app.querySelector("#totpSecretCanvas")!; - - if (user.guest) { - for (let c of passwordButton.classList.values()) { - if (c.startsWith("bg-") || c.startsWith("hover:bg-")) - passwordButton.classList.remove(c); - } - let totpWrapper = app.querySelector("#totpWrapper")!; - - if (user.guest) { - removeBgColor( - passwordButton, - displayNameButton, - enableBtn, - disableBtn, - showSecretBtn, - ); - - passwordButton.classList.add( - "bg-gray-700", - "hover:bg-gray-700", - ); - - passwordBox.disabled = true; - passwordBox.classList.add("color-white"); - - displayNameButton.disabled = true; - displayNameButton.classList.add("bg-gray-700", "color-white"); - - displayNameBox.disabled = true; - displayNameBox.classList.add("color-white"); - enableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); - disableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); - showSecretBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); - - enableBtn.disabled = true; - disableBtn.disabled = true; - showSecretBtn.disabled = true; - - accountTypeBox.innerText = "Guest"; - } else if (!isNullish(user.selfInfo?.loginName)) { - loginNameWrapper.hidden = false; - loginNameBox.innerText = user.selfInfo.loginName; - - accountTypeBox.innerText = "Normal"; - } else if ( - !isNullish(user.selfInfo?.providerId) && - !isNullish(user.selfInfo?.providerUser) - ) { - providerWrapper.hidden = false; - providerNameBox.innerText = user.selfInfo.providerId; - providerUserBox.innerText = user.selfInfo.providerUser; - - enableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); - disableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); - showSecretBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); - - enableBtn.disabled = true; - disableBtn.disabled = true; - showSecretBtn.disabled = true; - - removeBgColor(enableBtn, disableBtn, showSecretBtn); - passwordWrapper.hidden = true; - totpWrapper.hidden = true; - - accountTypeBox.innerText = "Provider"; - } - - // ---- Update UI ---- - function refreshTotpUI() { - if (totpEnabled) { - totpStatusText.textContent = "Status: Enabled"; - - enableBtn.classList.add("hidden"); - disableBtn.classList.remove("hidden"); - showSecretBtn.classList.remove("hidden"); - } else { - totpStatusText.textContent = "Status: Disabled"; - - enableBtn.classList.remove("hidden"); - disableBtn.classList.add("hidden"); - showSecretBtn.classList.add("hidden"); - secretBox.classList.add("hidden"); - } - } - - // ---- Button Events ---- - enableBtn.onclick = async () => { - let res = await client.enableOtp(); - if (res.kind === "success") { - navigateTo(url); - } else { - showError(`failed to activate OTP: ${res.msg}`); - } +async function route(url: string, _args: { [k: string]: string }) { + setTitle("Edit Profile"); + return { + html: page, + postInsert: async (app: HTMLElement | undefined) => { + const user = await updateUser(); + if (isNullish(user)) return showError("No User"); + if (isNullish(app)) return showError("Failed to render"); + let totpState = await (async () => { + let res = await client.statusOtp(); + if (res.kind === "success") + return { + enabled: + (res.msg as string) === "statusOtp.success.enabled", + secret: + (res.msg as string) === "statusOtp.success.enabled" + ? res.payload.secret + : null, }; - - disableBtn.onclick = async () => { - let res = await client.disableOtp(); - if (res.kind === "success") { - navigateTo(url); - } else { - showError(`failed to deactivate OTP: ${res.msg}`); - } + else { + showError("Failed to get OTP status"); + return { + enabled: false, + secret: null, }; + } + })(); + // ---- Simulated State ---- + let totpEnabled = totpState.enabled; + let totpSecret = totpState.secret; // would come from backend - showSecretBtn.onclick = () => { - if (!isNullish(totpSecret)) { - secretText.textContent = totpSecret; - renderOAuth2QRCode(secretCanvas, totpSecret); - } - secretBox.classList.toggle("hidden"); - }; + let guestBox = app.querySelector("#isGuestBox")!; + let displayNameWrapper = app.querySelector( + "#displayNameWrapper", + )!; + let displayNameBox = + app.querySelector("#displayNameBox")!; + let displayNameButton = + app.querySelector("#displayNameButton")!; + let loginNameWrapper = + app.querySelector("#loginNameWrapper")!; + let loginNameBox = + app.querySelector("#loginNameBox")!; + let passwordWrapper = + app.querySelector("#passwordWrapper")!; + let passwordBox = + app.querySelector("#passwordBox")!; + let passwordButton = + app.querySelector("#passwordButton")!; - displayNameButton.onclick = async () => { - let req = await client.changeDisplayName({ - changeDisplayNameRequest: { name: displayNameBox.value }, - }); - if (req.kind === "success") { - showSuccess("Successfully changed display name"); - handleRoute(); - } else { - showError(`Failed to update: ${req.msg}`); - } - }; + let providerWrapper = + app.querySelector("#providerWrapper")!; + let providerNameBox = + app.querySelector("#providerNameBox")!; + let providerUserBox = + app.querySelector("#providerUserBox")!; - // Initialize UI state - refreshTotpUI(); - }, + let accountTypeBox = + app.querySelector("#accountType")!; + displayNameBox.value = user.name; + + guestBox.hidden = !user.guest; + + // ---- DOM Elements ---- + const totpStatusText = app.querySelector("#totpStatusText")!; + const enableBtn = + app.querySelector("#enableTotp")!; + const disableBtn = + app.querySelector("#disableTotp")!; + const showSecretBtn = + app.querySelector("#showSecret")!; + const secretBox = app.querySelector("#totpSecretBox")!; + const secretText = + app.querySelector("#totpSecretText")!; + const secretCanvas = + app.querySelector("#totpSecretCanvas")!; + let totpWrapper = + app.querySelector("#totpWrapper")!; + + if (user.guest) { + for (let c of passwordButton.classList.values()) { + if (c.startsWith("bg-") || c.startsWith("hover:bg-")) + passwordButton.classList.remove(c); + } + } + if (user.guest) { + removeBgColor( + passwordButton, + displayNameButton, + enableBtn, + disableBtn, + showSecretBtn, + ); + + passwordButton.classList.add( + "bg-gray-700", + "hover:bg-gray-700", + ); + + passwordBox.disabled = true; + passwordBox.classList.add("color-white"); + + displayNameButton.disabled = true; + displayNameButton.classList.add("bg-gray-700", "color-white"); + + displayNameBox.disabled = true; + displayNameBox.classList.add("color-white"); + enableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + disableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + showSecretBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + + enableBtn.disabled = true; + disableBtn.disabled = true; + showSecretBtn.disabled = true; + + accountTypeBox.innerText = "Guest"; + } else if (!isNullish(user.selfInfo?.loginName)) { + loginNameWrapper.hidden = false; + loginNameBox.innerText = user.selfInfo.loginName; + totpWrapper.hidden =false; + passwordWrapper.hidden = false; + + accountTypeBox.innerText = "Normal"; + } else if ( + !isNullish(user.selfInfo?.providerId) && + !isNullish(user.selfInfo?.providerUser) + ) { + providerWrapper.hidden = false; + providerNameBox.innerText = user.selfInfo.providerId; + providerUserBox.innerText = user.selfInfo.providerUser; + + enableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + disableBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + showSecretBtn.classList.add("bg-gray-700", "hover:bg-gray-700"); + + enableBtn.disabled = true; + disableBtn.disabled = true; + showSecretBtn.disabled = true; + + removeBgColor(enableBtn, disableBtn, showSecretBtn); + passwordWrapper.hidden = true; + totpWrapper.hidden = true; + + accountTypeBox.innerText = "Provider"; + } + + // ---- Update UI ---- + function refreshTotpUI() { + if (totpEnabled) { + totpStatusText.textContent = "Status: Enabled"; + + enableBtn.classList.add("hidden"); + disableBtn.classList.remove("hidden"); + showSecretBtn.classList.remove("hidden"); + } else { + totpStatusText.textContent = "Status: Disabled"; + + enableBtn.classList.remove("hidden"); + disableBtn.classList.add("hidden"); + showSecretBtn.classList.add("hidden"); + secretBox.classList.add("hidden"); + } + } + + // ---- Button Events ---- + enableBtn.onclick = async () => { + let res = await client.enableOtp(); + if (res.kind === "success") { + navigateTo(url); + } else { + showError(`failed to activate OTP: ${res.msg}`); + } }; - } - addRoute("/profile", route); + disableBtn.onclick = async () => { + let res = await client.disableOtp(); + if (res.kind === "success") { + navigateTo(url); + } else { + showError(`failed to deactivate OTP: ${res.msg}`); + } + }; + + showSecretBtn.onclick = () => { + if (!isNullish(totpSecret)) { + secretText.textContent = totpSecret; + renderOAuth2QRCode(secretCanvas, totpSecret); + } + secretBox.classList.toggle("hidden"); + }; + + displayNameButton.onclick = async () => { + let req = await client.changeDisplayName({ + changeDisplayNameRequest: { + name: displayNameBox.value, + }, + }); + if (req.kind === "success") { + showSuccess("Successfully changed display name"); + handleRoute(); + } else { + showError(`Failed to update: ${req.msg}`); + } + }; + + // Initialize UI state + refreshTotpUI(); + }, + }; +} + +addRoute("/profile", route); From 763d0c38cc4ccf9e05974db09a553d8c110d2836 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Wed, 10 Dec 2025 17:06:53 +0100 Subject: [PATCH 07/10] removed icon server and finished profile page --- docker-compose.yml | 27 ---- .../api/generated/.openapi-generator/FILES | 6 +- .../src/api/generated/apis/OpenapiOtherApi.ts | 94 ++++++++++-- .../models/ChangePassword200Response.ts | 93 ++++++++++++ .../models/ChangePassword400Response.ts | 95 ++++++++++++ .../models/ChangePassword401Response.ts | 96 ++++++++++++ .../models/ChangePassword500Response.ts | 93 ++++++++++++ .../generated/models/ChangePasswordRequest.ts | 66 +++++++++ .../generated/models/EnableOtp401Response.ts | 14 +- .../generated/models/StatusOtp401Response.ts | 12 +- frontend/src/api/generated/models/index.ts | 6 +- frontend/src/pages/profile/profile.ts | 17 ++- src/auth/openapi.json | 134 +++++++++++++++++ src/auth/src/routes/changePassword.ts | 44 ++++++ src/icons/.dockerignore | 2 - src/icons/entrypoint.sh | 10 -- src/icons/extra/.gitkeep | 0 src/icons/package.json | 37 ----- src/icons/src/app.ts | 54 ------- src/icons/src/plugins/README.md | 16 -- src/icons/src/plugins/sensible.ts | 11 -- src/icons/src/routes/set.ts | 51 ------- src/icons/src/run.ts | 35 ----- src/icons/tsconfig.json | 5 - src/icons/vite.config.js | 51 ------- src/openapi.json | 137 ++++++++++++++++++ 26 files changed, 881 insertions(+), 325 deletions(-) create mode 100644 frontend/src/api/generated/models/ChangePassword200Response.ts create mode 100644 frontend/src/api/generated/models/ChangePassword400Response.ts create mode 100644 frontend/src/api/generated/models/ChangePassword401Response.ts create mode 100644 frontend/src/api/generated/models/ChangePassword500Response.ts create mode 100644 frontend/src/api/generated/models/ChangePasswordRequest.ts create mode 100644 src/auth/src/routes/changePassword.ts delete mode 100644 src/icons/.dockerignore delete mode 100644 src/icons/entrypoint.sh delete mode 100644 src/icons/extra/.gitkeep delete mode 100644 src/icons/package.json delete mode 100644 src/icons/src/app.ts delete mode 100644 src/icons/src/plugins/README.md delete mode 100644 src/icons/src/plugins/sensible.ts delete mode 100644 src/icons/src/routes/set.ts delete mode 100644 src/icons/src/run.ts delete mode 100644 src/icons/tsconfig.json delete mode 100644 src/icons/vite.config.js diff --git a/docker-compose.yml b/docker-compose.yml index ce19a47..fb993e0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -48,33 +48,6 @@ services: gelf-address: "udp://127.0.0.1:12201" tag: "{{.Name}}" - ############### - # ICONS # - ############### - icons: - build: - context: ./src/ - args: - - SERVICE=icons - #- EXTRA_FILES=icons/extra - container_name: icons - restart: always - networks: - - transcendance-network - volumes: - - images-volume:/volumes/store - - sqlite-volume:/volumes/database - environment: - - JWT_SECRET=KRUGKIDROVUWG2ZAMJZG653OEBTG66BANJ2W24DTEBXXMZLSEB2GQZJANRQXU6JA - - USER_ICONS_STORE=/volumes/store - - DATABASE_DIR=/volumes/database - logging: - driver: gelf - options: - gelf-address: "udp://127.0.0.1:12201" - tag: "{{.Name}}" - - ############### # AUTH # ############### diff --git a/frontend/src/api/generated/.openapi-generator/FILES b/frontend/src/api/generated/.openapi-generator/FILES index ed23b0c..dbddaec 100644 --- a/frontend/src/api/generated/.openapi-generator/FILES +++ b/frontend/src/api/generated/.openapi-generator/FILES @@ -4,11 +4,15 @@ index.ts models/ChangeDisplayName200Response.ts models/ChangeDisplayName400Response.ts models/ChangeDisplayNameRequest.ts +models/ChangePassword200Response.ts +models/ChangePassword400Response.ts +models/ChangePassword401Response.ts +models/ChangePassword500Response.ts +models/ChangePasswordRequest.ts models/ChatTest200Response.ts models/ChatTest200ResponsePayload.ts models/DisableOtp200Response.ts models/DisableOtp400Response.ts -models/DisableOtp401Response.ts models/DisableOtp500Response.ts models/EnableOtp200Response.ts models/EnableOtp200ResponsePayload.ts diff --git a/frontend/src/api/generated/apis/OpenapiOtherApi.ts b/frontend/src/api/generated/apis/OpenapiOtherApi.ts index 9609421..444695b 100644 --- a/frontend/src/api/generated/apis/OpenapiOtherApi.ts +++ b/frontend/src/api/generated/apis/OpenapiOtherApi.ts @@ -18,10 +18,14 @@ import type { ChangeDisplayName200Response, ChangeDisplayName400Response, ChangeDisplayNameRequest, + ChangePassword200Response, + ChangePassword400Response, + ChangePassword401Response, + ChangePassword500Response, + ChangePasswordRequest, ChatTest200Response, DisableOtp200Response, DisableOtp400Response, - DisableOtp401Response, DisableOtp500Response, EnableOtp200Response, EnableOtp400Response, @@ -60,14 +64,22 @@ import { ChangeDisplayName400ResponseToJSON, ChangeDisplayNameRequestFromJSON, ChangeDisplayNameRequestToJSON, + ChangePassword200ResponseFromJSON, + ChangePassword200ResponseToJSON, + ChangePassword400ResponseFromJSON, + ChangePassword400ResponseToJSON, + ChangePassword401ResponseFromJSON, + ChangePassword401ResponseToJSON, + ChangePassword500ResponseFromJSON, + ChangePassword500ResponseToJSON, + ChangePasswordRequestFromJSON, + ChangePasswordRequestToJSON, ChatTest200ResponseFromJSON, ChatTest200ResponseToJSON, DisableOtp200ResponseFromJSON, DisableOtp200ResponseToJSON, DisableOtp400ResponseFromJSON, DisableOtp400ResponseToJSON, - DisableOtp401ResponseFromJSON, - DisableOtp401ResponseToJSON, DisableOtp500ResponseFromJSON, DisableOtp500ResponseToJSON, EnableOtp200ResponseFromJSON, @@ -134,6 +146,10 @@ export interface ChangeDisplayNameOperationRequest { changeDisplayNameRequest: ChangeDisplayNameRequest; } +export interface ChangePasswordOperationRequest { + changePasswordRequest: ChangePasswordRequest; +} + export interface GetUserRequest { user: GetUserUserParameter; } @@ -161,7 +177,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI { /** */ - async changeDisplayNameRaw(requestParameters: ChangeDisplayNameOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + async changeDisplayNameRaw(requestParameters: ChangeDisplayNameOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { if (requestParameters['changeDisplayNameRequest'] == null) { throw new runtime.RequiredError( 'changeDisplayNameRequest', @@ -200,7 +216,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI { } if (response.status === 401) { // Object response for status 401 - return new runtime.JSONApiResponse(response, (jsonValue) => DisableOtp401ResponseFromJSON(jsonValue)); + return new runtime.JSONApiResponse(response, (jsonValue) => ChangePassword401ResponseFromJSON(jsonValue)); } // CHANGED: Throw error if status code is not handled by any of the defined responses // This ensures all code paths return a value and provides clear error messages for unexpected status codes @@ -210,11 +226,71 @@ export class OpenapiOtherApi extends runtime.BaseAPI { /** */ - async changeDisplayName(requestParameters: ChangeDisplayNameOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + async changeDisplayName(requestParameters: ChangeDisplayNameOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { const response = await this.changeDisplayNameRaw(requestParameters, initOverrides); return await response.value(); } + /** + */ + async changePasswordRaw(requestParameters: ChangePasswordOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['changePasswordRequest'] == null) { + throw new runtime.RequiredError( + 'changePasswordRequest', + 'Required parameter "changePasswordRequest" was null or undefined when calling changePassword().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + + let urlPath = `/api/auth/changePassword`; + + const response = await this.request({ + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: ChangePasswordRequestToJSON(requestParameters['changePasswordRequest']), + }, initOverrides); + + // CHANGED: Handle all status codes defined in the OpenAPI spec, not just 2xx responses + // This allows typed access to error responses (4xx, 5xx) and other status codes. + // The code routes responses based on the actual HTTP status code and returns + // appropriately typed ApiResponse wrappers for each status code. + if (response.status === 200) { + // Object response for status 200 + return new runtime.JSONApiResponse(response, (jsonValue) => ChangePassword200ResponseFromJSON(jsonValue)); + } + if (response.status === 400) { + // Object response for status 400 + return new runtime.JSONApiResponse(response, (jsonValue) => ChangePassword400ResponseFromJSON(jsonValue)); + } + if (response.status === 401) { + // Object response for status 401 + return new runtime.JSONApiResponse(response, (jsonValue) => ChangePassword401ResponseFromJSON(jsonValue)); + } + if (response.status === 500) { + // Object response for status 500 + return new runtime.JSONApiResponse(response, (jsonValue) => ChangePassword500ResponseFromJSON(jsonValue)); + } + // CHANGED: Throw error if status code is not handled by any of the defined responses + // This ensures all code paths return a value and provides clear error messages for unexpected status codes + // Only throw if responses were defined but none matched the actual status code + throw new runtime.ResponseError(response, `Unexpected status code: ${response.status}. Expected one of: 200, 400, 401, 500`); + } + + /** + */ + async changePassword(requestParameters: ChangePasswordOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.changePasswordRaw(requestParameters, initOverrides); + return await response.value(); + } + /** */ async chatTestRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { @@ -259,7 +335,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI { /** */ - async disableOtpRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + async disableOtpRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { const queryParameters: any = {}; const headerParameters: runtime.HTTPHeaders = {}; @@ -288,7 +364,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI { } if (response.status === 401) { // Object response for status 401 - return new runtime.JSONApiResponse(response, (jsonValue) => DisableOtp401ResponseFromJSON(jsonValue)); + return new runtime.JSONApiResponse(response, (jsonValue) => ChangePassword401ResponseFromJSON(jsonValue)); } if (response.status === 500) { // Object response for status 500 @@ -302,7 +378,7 @@ export class OpenapiOtherApi extends runtime.BaseAPI { /** */ - async disableOtp(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + async disableOtp(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { const response = await this.disableOtpRaw(initOverrides); return await response.value(); } diff --git a/frontend/src/api/generated/models/ChangePassword200Response.ts b/frontend/src/api/generated/models/ChangePassword200Response.ts new file mode 100644 index 0000000..0df6906 --- /dev/null +++ b/frontend/src/api/generated/models/ChangePassword200Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ChangePassword200Response + */ +export interface ChangePassword200Response { + /** + * + * @type {string} + * @memberof ChangePassword200Response + */ + kind: ChangePassword200ResponseKindEnum; + /** + * + * @type {string} + * @memberof ChangePassword200Response + */ + msg: ChangePassword200ResponseMsgEnum; +} + + +/** + * @export + */ +export const ChangePassword200ResponseKindEnum = { + Success: 'success' +} as const; +export type ChangePassword200ResponseKindEnum = typeof ChangePassword200ResponseKindEnum[keyof typeof ChangePassword200ResponseKindEnum]; + +/** + * @export + */ +export const ChangePassword200ResponseMsgEnum = { + ChangePasswordSuccess: 'changePassword.success' +} as const; +export type ChangePassword200ResponseMsgEnum = typeof ChangePassword200ResponseMsgEnum[keyof typeof ChangePassword200ResponseMsgEnum]; + + +/** + * Check if a given object implements the ChangePassword200Response interface. + */ +export function instanceOfChangePassword200Response(value: object): value is ChangePassword200Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function ChangePassword200ResponseFromJSON(json: any): ChangePassword200Response { + return ChangePassword200ResponseFromJSONTyped(json, false); +} + +export function ChangePassword200ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangePassword200Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function ChangePassword200ResponseToJSON(json: any): ChangePassword200Response { + return ChangePassword200ResponseToJSONTyped(json, false); +} + +export function ChangePassword200ResponseToJSONTyped(value?: ChangePassword200Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/ChangePassword400Response.ts b/frontend/src/api/generated/models/ChangePassword400Response.ts new file mode 100644 index 0000000..1606223 --- /dev/null +++ b/frontend/src/api/generated/models/ChangePassword400Response.ts @@ -0,0 +1,95 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ChangePassword400Response + */ +export interface ChangePassword400Response { + /** + * + * @type {string} + * @memberof ChangePassword400Response + */ + kind: ChangePassword400ResponseKindEnum; + /** + * + * @type {string} + * @memberof ChangePassword400Response + */ + msg: ChangePassword400ResponseMsgEnum; +} + + +/** + * @export + */ +export const ChangePassword400ResponseKindEnum = { + Failed: 'failed' +} as const; +export type ChangePassword400ResponseKindEnum = typeof ChangePassword400ResponseKindEnum[keyof typeof ChangePassword400ResponseKindEnum]; + +/** + * @export + */ +export const ChangePassword400ResponseMsgEnum = { + ChangePasswordFailedToolong: 'changePassword.failed.toolong', + ChangePasswordFailedTooshort: 'changePassword.failed.tooshort', + ChangePasswordFailedInvalid: 'changePassword.failed.invalid' +} as const; +export type ChangePassword400ResponseMsgEnum = typeof ChangePassword400ResponseMsgEnum[keyof typeof ChangePassword400ResponseMsgEnum]; + + +/** + * Check if a given object implements the ChangePassword400Response interface. + */ +export function instanceOfChangePassword400Response(value: object): value is ChangePassword400Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function ChangePassword400ResponseFromJSON(json: any): ChangePassword400Response { + return ChangePassword400ResponseFromJSONTyped(json, false); +} + +export function ChangePassword400ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangePassword400Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function ChangePassword400ResponseToJSON(json: any): ChangePassword400Response { + return ChangePassword400ResponseToJSONTyped(json, false); +} + +export function ChangePassword400ResponseToJSONTyped(value?: ChangePassword400Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/ChangePassword401Response.ts b/frontend/src/api/generated/models/ChangePassword401Response.ts new file mode 100644 index 0000000..7e460c7 --- /dev/null +++ b/frontend/src/api/generated/models/ChangePassword401Response.ts @@ -0,0 +1,96 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ChangePassword401Response + */ +export interface ChangePassword401Response { + /** + * + * @type {string} + * @memberof ChangePassword401Response + */ + kind: ChangePassword401ResponseKindEnum; + /** + * + * @type {string} + * @memberof ChangePassword401Response + */ + msg: ChangePassword401ResponseMsgEnum; +} + + +/** + * @export + */ +export const ChangePassword401ResponseKindEnum = { + NotLoggedIn: 'notLoggedIn' +} as const; +export type ChangePassword401ResponseKindEnum = typeof ChangePassword401ResponseKindEnum[keyof typeof ChangePassword401ResponseKindEnum]; + +/** + * @export + */ +export const ChangePassword401ResponseMsgEnum = { + AuthNoCookie: 'auth.noCookie', + AuthInvalidKind: 'auth.invalidKind', + AuthNoUser: 'auth.noUser', + AuthInvalid: 'auth.invalid' +} as const; +export type ChangePassword401ResponseMsgEnum = typeof ChangePassword401ResponseMsgEnum[keyof typeof ChangePassword401ResponseMsgEnum]; + + +/** + * Check if a given object implements the ChangePassword401Response interface. + */ +export function instanceOfChangePassword401Response(value: object): value is ChangePassword401Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function ChangePassword401ResponseFromJSON(json: any): ChangePassword401Response { + return ChangePassword401ResponseFromJSONTyped(json, false); +} + +export function ChangePassword401ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangePassword401Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function ChangePassword401ResponseToJSON(json: any): ChangePassword401Response { + return ChangePassword401ResponseToJSONTyped(json, false); +} + +export function ChangePassword401ResponseToJSONTyped(value?: ChangePassword401Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/ChangePassword500Response.ts b/frontend/src/api/generated/models/ChangePassword500Response.ts new file mode 100644 index 0000000..2d345f7 --- /dev/null +++ b/frontend/src/api/generated/models/ChangePassword500Response.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ChangePassword500Response + */ +export interface ChangePassword500Response { + /** + * + * @type {string} + * @memberof ChangePassword500Response + */ + kind: ChangePassword500ResponseKindEnum; + /** + * + * @type {string} + * @memberof ChangePassword500Response + */ + msg: ChangePassword500ResponseMsgEnum; +} + + +/** + * @export + */ +export const ChangePassword500ResponseKindEnum = { + Failed: 'failed' +} as const; +export type ChangePassword500ResponseKindEnum = typeof ChangePassword500ResponseKindEnum[keyof typeof ChangePassword500ResponseKindEnum]; + +/** + * @export + */ +export const ChangePassword500ResponseMsgEnum = { + ChangePasswordFailedGeneric: 'changePassword.failed.generic' +} as const; +export type ChangePassword500ResponseMsgEnum = typeof ChangePassword500ResponseMsgEnum[keyof typeof ChangePassword500ResponseMsgEnum]; + + +/** + * Check if a given object implements the ChangePassword500Response interface. + */ +export function instanceOfChangePassword500Response(value: object): value is ChangePassword500Response { + if (!('kind' in value) || value['kind'] === undefined) return false; + if (!('msg' in value) || value['msg'] === undefined) return false; + return true; +} + +export function ChangePassword500ResponseFromJSON(json: any): ChangePassword500Response { + return ChangePassword500ResponseFromJSONTyped(json, false); +} + +export function ChangePassword500ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangePassword500Response { + if (json == null) { + return json; + } + return { + + 'kind': json['kind'], + 'msg': json['msg'], + }; +} + +export function ChangePassword500ResponseToJSON(json: any): ChangePassword500Response { + return ChangePassword500ResponseToJSONTyped(json, false); +} + +export function ChangePassword500ResponseToJSONTyped(value?: ChangePassword500Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'kind': value['kind'], + 'msg': value['msg'], + }; +} + diff --git a/frontend/src/api/generated/models/ChangePasswordRequest.ts b/frontend/src/api/generated/models/ChangePasswordRequest.ts new file mode 100644 index 0000000..d54b3e1 --- /dev/null +++ b/frontend/src/api/generated/models/ChangePasswordRequest.ts @@ -0,0 +1,66 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @fastify/swagger + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 9.6.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface ChangePasswordRequest + */ +export interface ChangePasswordRequest { + /** + * + * @type {string} + * @memberof ChangePasswordRequest + */ + newPassword: string; +} + +/** + * Check if a given object implements the ChangePasswordRequest interface. + */ +export function instanceOfChangePasswordRequest(value: object): value is ChangePasswordRequest { + if (!('newPassword' in value) || value['newPassword'] === undefined) return false; + return true; +} + +export function ChangePasswordRequestFromJSON(json: any): ChangePasswordRequest { + return ChangePasswordRequestFromJSONTyped(json, false); +} + +export function ChangePasswordRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangePasswordRequest { + if (json == null) { + return json; + } + return { + + 'newPassword': json['new_password'], + }; +} + +export function ChangePasswordRequestToJSON(json: any): ChangePasswordRequest { + return ChangePasswordRequestToJSONTyped(json, false); +} + +export function ChangePasswordRequestToJSONTyped(value?: ChangePasswordRequest | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'new_password': value['newPassword'], + }; +} + diff --git a/frontend/src/api/generated/models/EnableOtp401Response.ts b/frontend/src/api/generated/models/EnableOtp401Response.ts index 4db1df0..58b0f2f 100644 --- a/frontend/src/api/generated/models/EnableOtp401Response.ts +++ b/frontend/src/api/generated/models/EnableOtp401Response.ts @@ -13,13 +13,6 @@ */ import { mapValues } from '../runtime'; -import type { DisableOtp401Response } from './DisableOtp401Response'; -import { - DisableOtp401ResponseFromJSON, - DisableOtp401ResponseFromJSONTyped, - DisableOtp401ResponseToJSON, - DisableOtp401ResponseToJSONTyped, -} from './DisableOtp401Response'; import type { EnableOtp401ResponseAnyOf } from './EnableOtp401ResponseAnyOf'; import { EnableOtp401ResponseAnyOfFromJSON, @@ -27,6 +20,13 @@ import { EnableOtp401ResponseAnyOfToJSON, EnableOtp401ResponseAnyOfToJSONTyped, } from './EnableOtp401ResponseAnyOf'; +import type { ChangePassword401Response } from './ChangePassword401Response'; +import { + ChangePassword401ResponseFromJSON, + ChangePassword401ResponseFromJSONTyped, + ChangePassword401ResponseToJSON, + ChangePassword401ResponseToJSONTyped, +} from './ChangePassword401Response'; /** * diff --git a/frontend/src/api/generated/models/StatusOtp401Response.ts b/frontend/src/api/generated/models/StatusOtp401Response.ts index 10bde87..b89208a 100644 --- a/frontend/src/api/generated/models/StatusOtp401Response.ts +++ b/frontend/src/api/generated/models/StatusOtp401Response.ts @@ -13,13 +13,13 @@ */ import { mapValues } from '../runtime'; -import type { DisableOtp401Response } from './DisableOtp401Response'; +import type { ChangePassword401Response } from './ChangePassword401Response'; import { - DisableOtp401ResponseFromJSON, - DisableOtp401ResponseFromJSONTyped, - DisableOtp401ResponseToJSON, - DisableOtp401ResponseToJSONTyped, -} from './DisableOtp401Response'; + ChangePassword401ResponseFromJSON, + ChangePassword401ResponseFromJSONTyped, + ChangePassword401ResponseToJSON, + ChangePassword401ResponseToJSONTyped, +} from './ChangePassword401Response'; /** * diff --git a/frontend/src/api/generated/models/index.ts b/frontend/src/api/generated/models/index.ts index f00b24e..af6e1a0 100644 --- a/frontend/src/api/generated/models/index.ts +++ b/frontend/src/api/generated/models/index.ts @@ -3,11 +3,15 @@ export * from './ChangeDisplayName200Response'; export * from './ChangeDisplayName400Response'; export * from './ChangeDisplayNameRequest'; +export * from './ChangePassword200Response'; +export * from './ChangePassword400Response'; +export * from './ChangePassword401Response'; +export * from './ChangePassword500Response'; +export * from './ChangePasswordRequest'; export * from './ChatTest200Response'; export * from './ChatTest200ResponsePayload'; export * from './DisableOtp200Response'; export * from './DisableOtp400Response'; -export * from './DisableOtp401Response'; export * from './DisableOtp500Response'; export * from './EnableOtp200Response'; export * from './EnableOtp200ResponsePayload'; diff --git a/frontend/src/pages/profile/profile.ts b/frontend/src/pages/profile/profile.ts index 4b91b84..7241eeb 100644 --- a/frontend/src/pages/profile/profile.ts +++ b/frontend/src/pages/profile/profile.ts @@ -1,4 +1,4 @@ -import { addRoute, handleRoute, navigateTo, setTitle } from "@app/routing"; +import { addRoute, getRoute, handleRoute, navigateTo, setTitle } from "@app/routing"; import { showError, showSuccess } from "@app/toast"; import page from "./profile.html?raw"; import { updateUser } from "@app/auth"; @@ -159,7 +159,7 @@ async function route(url: string, _args: { [k: string]: string }) { } else if (!isNullish(user.selfInfo?.loginName)) { loginNameWrapper.hidden = false; loginNameBox.innerText = user.selfInfo.loginName; - totpWrapper.hidden =false; + totpWrapper.hidden = false; passwordWrapper.hidden = false; accountTypeBox.innerText = "Normal"; @@ -244,6 +244,19 @@ async function route(url: string, _args: { [k: string]: string }) { showError(`Failed to update: ${req.msg}`); } }; + passwordButton.onclick = async () => { + let req = await client.changePassword({ + changePasswordRequest: { + newPassword: passwordBox.value, + }, + }); + if (req.kind === "success") { + showSuccess("Successfully changed password"); + handleRoute(); + } else { + showError(`Failed to update: ${req.msg}`); + } + }; // Initialize UI state refreshTotpUI(); diff --git a/src/auth/openapi.json b/src/auth/openapi.json index 7fd431f..7a08ce2 100644 --- a/src/auth/openapi.json +++ b/src/auth/openapi.json @@ -8,6 +8,140 @@ "schemas": {} }, "paths": { + "/api/auth/changePassword": { + "post": { + "operationId": "changePassword", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "new_password" + ], + "properties": { + "new_password": { + "type": "string" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "success" + ] + }, + "msg": { + "enum": [ + "changePassword.success" + ] + } + } + } + } + } + }, + "400": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "failed" + ] + }, + "msg": { + "enum": [ + "changePassword.failed.toolong", + "changePassword.failed.tooshort", + "changePassword.failed.invalid" + ] + } + } + } + } + } + }, + "401": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "notLoggedIn" + ] + }, + "msg": { + "enum": [ + "auth.noCookie", + "auth.invalidKind", + "auth.noUser", + "auth.invalid" + ] + } + } + } + } + } + }, + "500": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "failed" + ] + }, + "msg": { + "enum": [ + "changePassword.failed.generic" + ] + } + } + } + } + } + } + } + } + }, "/api/auth/disableOtp": { "put": { "operationId": "disableOtp", diff --git a/src/auth/src/routes/changePassword.ts b/src/auth/src/routes/changePassword.ts new file mode 100644 index 0000000..7d87d9c --- /dev/null +++ b/src/auth/src/routes/changePassword.ts @@ -0,0 +1,44 @@ +import { FastifyPluginAsync } from 'fastify'; + +import { Static, Type } from 'typebox'; +import { typeResponse, MakeStaticResponse } from '@shared/utils'; + +const ChangePasswordReq = Type.Object({ + new_password: Type.String(), +}); + +type ChangePasswordReq = Static; + +const ChangePasswordRes = { + '500': typeResponse('failed', + 'changePassword.failed.generic'), + '400': typeResponse('failed', [ + 'changePassword.failed.toolong', + 'changePassword.failed.tooshort', + 'changePassword.failed.invalid', + ]), + '200': typeResponse('success', 'changePassword.success'), +}; + +type ChangePasswordRes = MakeStaticResponse; + +const route: FastifyPluginAsync = async (fastify, _opts): Promise => { + void _opts; + fastify.post<{ Body: ChangePasswordReq }>( + '/api/auth/changePassword', + { schema: { body: ChangePasswordReq, response: ChangePasswordRes, operationId: 'changePassword' }, config: { requireAuth: true } }, + async function(req, res) { + const password = req.body.new_password; + + if (password.length < 8) { return res.makeResponse(400, 'failed', 'changePassword.failed.tooshort'); } + if (password.length > 64) { return res.makeResponse(400, 'failed', 'changePassword.failed.toolong'); } + // password is good too ! + + await this.db.setUserPassword(req.authUser!.id, password); + + return res.makeResponse(200, 'success', 'changePassword.success'); + }, + ); +}; + +export default route; diff --git a/src/icons/.dockerignore b/src/icons/.dockerignore deleted file mode 100644 index c925c21..0000000 --- a/src/icons/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -/dist -/node_modules diff --git a/src/icons/entrypoint.sh b/src/icons/entrypoint.sh deleted file mode 100644 index 4a3dac4..0000000 --- a/src/icons/entrypoint.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -set -e -set -x -# do anything here - -cp -r /extra /files - -# run the CMD [ ... ] from the dockerfile -exec "$@" diff --git a/src/icons/extra/.gitkeep b/src/icons/extra/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/icons/package.json b/src/icons/package.json deleted file mode 100644 index bce9ed7..0000000 --- a/src/icons/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "type": "module", - "private": false, - "name": "icons", - "version": "1.0.0", - "description": "This project was bootstrapped with Fastify-CLI.", - "main": "app.ts", - "directories": { - "test": "test" - }, - "scripts": { - "start": "npm run build && node dist/run.js", - "build": "vite build", - "build:prod": "vite build --outDir=/dist --minify=true --sourcemap=false" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "@fastify/autoload": "^6.3.1", - "@fastify/formbody": "^8.0.2", - "@fastify/multipart": "^9.3.0", - "@fastify/sensible": "^6.0.4", - "@fastify/static": "^8.3.0", - "fastify": "^5.6.2", - "fastify-cli": "^7.4.1", - "fastify-plugin": "^5.1.0", - "raw-body": "^3.0.2", - "sharp": "^0.34.5" - }, - "devDependencies": { - "@types/node": "^22.19.2", - "rollup-plugin-node-externals": "^8.1.2", - "vite": "^7.2.7", - "vite-tsconfig-paths": "^5.1.4" - } -} diff --git a/src/icons/src/app.ts b/src/icons/src/app.ts deleted file mode 100644 index 7c3b2f0..0000000 --- a/src/icons/src/app.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import fastifyFormBody from '@fastify/formbody'; -import fastifyMultipart from '@fastify/multipart'; -import { mkdir } from 'node:fs/promises'; -import fp from 'fastify-plugin'; -import * as db from '@shared/database'; -import * as utils from '@shared/utils'; -import { authPlugin, jwtPlugin } from '@shared/auth'; - -// @ts-expect-error: import.meta.glob is a vite thing. Typescript doesn't know this... -const plugins = import.meta.glob('./plugins/**/*.ts', { eager: true }); -// @ts-expect-error: import.meta.glob is a vite thing. Typescript doesn't know this... -const routes = import.meta.glob('./routes/**/*.ts', { eager: true }); - - -// When using .decorate you have to specify added properties for Typescript -declare module 'fastify' { - export interface FastifyInstance { - image_store: string; - } -} - -const app: FastifyPluginAsync = async ( - fastify, - _opts, -): Promise => { - void _opts; - // Place here your custom code! - for (const plugin of Object.values(plugins)) { - void fastify.register(plugin as FastifyPluginAsync, {}); - } - for (const route of Object.values(routes)) { - void fastify.register(route as FastifyPluginAsync, {}); - } - - await fastify.register(utils.useMonitoring); - await fastify.register(db.useDatabase as FastifyPluginAsync, {}); - await fastify.register(authPlugin as FastifyPluginAsync, {}); - await fastify.register(jwtPlugin as FastifyPluginAsync, {}); - - void fastify.register(fastifyFormBody, {}); - void fastify.register(fastifyMultipart, {}); - - // The use of fastify-plugin is required to be able - // to export the decorators to the outer scope - void fastify.register(fp(async (fastify2) => { - const image_store = process.env.USER_ICONS_STORE ?? '/tmp/icons'; - fastify2.decorate('image_store', image_store); - await mkdir(fastify2.image_store, { recursive: true }); - })); -}; - -export default app; -export { app }; diff --git a/src/icons/src/plugins/README.md b/src/icons/src/plugins/README.md deleted file mode 100644 index 1e61ee5..0000000 --- a/src/icons/src/plugins/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Plugins Folder - -Plugins define behavior that is common to all the routes in your -application. Authentication, caching, templates, and all the other cross -cutting concerns should be handled by plugins placed in this folder. - -Files in this folder are typically defined through the -[`fastify-plugin`](https://github.com/fastify/fastify-plugin) module, -making them non-encapsulated. They can define decorators and set hooks -that will then be used in the rest of your application. - -Check out: - -* [The hitchhiker's guide to plugins](https://fastify.dev/docs/latest/Guides/Plugins-Guide/) -* [Fastify decorators](https://fastify.dev/docs/latest/Reference/Decorators/). -* [Fastify lifecycle](https://fastify.dev/docs/latest/Reference/Lifecycle/). diff --git a/src/icons/src/plugins/sensible.ts b/src/icons/src/plugins/sensible.ts deleted file mode 100644 index 8c2093c..0000000 --- a/src/icons/src/plugins/sensible.ts +++ /dev/null @@ -1,11 +0,0 @@ -import fp from 'fastify-plugin'; -import sensible, { FastifySensibleOptions } from '@fastify/sensible'; - -/** - * This plugins adds some utilities to handle http errors - * - * @see https://github.com/fastify/fastify-sensible - */ -export default fp(async (fastify) => { - fastify.register(sensible); -}); diff --git a/src/icons/src/routes/set.ts b/src/icons/src/routes/set.ts deleted file mode 100644 index 8063719..0000000 --- a/src/icons/src/routes/set.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { FastifyPluginAsync } from 'fastify'; -import { join } from 'node:path'; -import { open } from 'node:fs/promises'; -import sharp from 'sharp'; -import rawBody from 'raw-body'; -import { isNullish } from '@shared/utils'; - -const route: FastifyPluginAsync = async (fastify, _opts): Promise => { - void _opts; - // await fastify.register(authMethod, {}); - // here we register plugins that will be active for the current fastify instance (aka everything in this function) - - // we register a route handler for: `/` - // it sets some configuration options, and set the actual function that will handle the request - - fastify.addContentTypeParser('*', function(request, payload, done) { - done(null); - }); - - fastify.post<{ Params: { userid: string } }>('/:userid', async function(request, reply) { - const buffer = await rawBody(request.raw); - // this is how we get the `:userid` part of things - const userid: string | undefined = (request.params)['userid']; - if (isNullish(userid)) { - return await reply.code(403); - } - const image_store: string = fastify.getDecorator('image_store'); - const image_path = join(image_store, userid); - - try { - const img = sharp(buffer); - img.resize({ - height: 128, - width: 128, - fit: 'fill', - }); - const data = await img.png({ compressionLevel: 6 }).toBuffer(); - const image_file = await open(image_path, 'w', 0o666); - await image_file.write(data); - await image_file.close(); - } - catch (e) { - fastify.log.error(`Error: ${e}`); - reply.code(400); - return { status: 'error' }; - } - }); -}; - -export default route; - diff --git a/src/icons/src/run.ts b/src/icons/src/run.ts deleted file mode 100644 index d3d410f..0000000 --- a/src/icons/src/run.ts +++ /dev/null @@ -1,35 +0,0 @@ -// this sould only be used by the docker file ! - -import fastify, { FastifyInstance } from 'fastify'; -import app from './app'; - -const start = async () => { - const envToLogger = { - development: { - transport: { - target: 'pino-pretty', - options: { - translateTime: 'HH:MM:ss Z', - ignore: 'pid,hostname', - }, - }, - }, - production: true, - test: false, - }; - - const f: FastifyInstance = fastify({ logger: envToLogger.development }); - process.on('SIGTERM', () => { - f.log.info('Requested to shutdown'); - process.exit(134); - }); - try { - await f.register(app, {}); - await f.listen({ port: 80, host: '0.0.0.0' }); - } - catch (err) { - f.log.error(err); - process.exit(1); - } -}; -start(); diff --git a/src/icons/tsconfig.json b/src/icons/tsconfig.json deleted file mode 100644 index e6d24e2..0000000 --- a/src/icons/tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "../tsconfig.base.json", - "compilerOptions": {}, - "include": ["src/**/*.ts"] -} diff --git a/src/icons/vite.config.js b/src/icons/vite.config.js deleted file mode 100644 index 2c172a5..0000000 --- a/src/icons/vite.config.js +++ /dev/null @@ -1,51 +0,0 @@ -import { defineConfig } from 'vite'; -import tsconfigPaths from 'vite-tsconfig-paths'; -import nodeExternals from 'rollup-plugin-node-externals'; -import path from 'node:path'; -import fs from 'node:fs'; - -function collectDeps(...pkgJsonPaths) { - const allDeps = new Set(); - for (const pkgPath of pkgJsonPaths) { - const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); - for (const dep of Object.keys(pkg.dependencies || {})) { - allDeps.add(dep); - } - for (const peer of Object.keys(pkg.peerDependencies || {})) { - allDeps.add(peer); - } - } - return Array.from(allDeps); -} - -const externals = collectDeps( - './package.json', - '../@shared/package.json', -); - - -export default defineConfig({ - root: __dirname, - // service root - plugins: [tsconfigPaths(), nodeExternals()], - build: { - ssr: true, - outDir: 'dist', - emptyOutDir: true, - lib: { - entry: path.resolve(__dirname, 'src/run.ts'), - // adjust main entry - formats: ['cjs'], - // CommonJS for Node.js - fileName: () => 'index.js', - }, - rollupOptions: { - external: externals, - }, - target: 'node22', - // or whatever Node version you use - sourcemap: false, - minify: true, - // for easier debugging - }, -}); diff --git a/src/openapi.json b/src/openapi.json index d9b52b6..42cbe84 100644 --- a/src/openapi.json +++ b/src/openapi.json @@ -21,6 +21,143 @@ } ], "paths": { + "/api/auth/changePassword": { + "post": { + "operationId": "changePassword", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "new_password" + ], + "properties": { + "new_password": { + "type": "string" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "success" + ] + }, + "msg": { + "enum": [ + "changePassword.success" + ] + } + } + } + } + } + }, + "400": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "failed" + ] + }, + "msg": { + "enum": [ + "changePassword.failed.toolong", + "changePassword.failed.tooshort", + "changePassword.failed.invalid" + ] + } + } + } + } + } + }, + "401": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "notLoggedIn" + ] + }, + "msg": { + "enum": [ + "auth.noCookie", + "auth.invalidKind", + "auth.noUser", + "auth.invalid" + ] + } + } + } + } + } + }, + "500": { + "description": "Default Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "kind", + "msg" + ], + "properties": { + "kind": { + "enum": [ + "failed" + ] + }, + "msg": { + "enum": [ + "changePassword.failed.generic" + ] + } + } + } + } + } + } + }, + "tags": [ + "openapi_other" + ] + } + }, "/api/auth/disableOtp": { "put": { "operationId": "disableOtp", From ec86d90a02786ddef87b46e7e5d43859b34165bf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 16:59:34 +0000 Subject: [PATCH 08/10] Initial plan From 5156e4310e7ddcea96cf272248714bef5e444c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl?= <35407363+EniumRaphael@users.noreply.github.com> Date: Wed, 10 Dec 2025 18:00:55 +0100 Subject: [PATCH 09/10] style(routes/info): removing debug print Removed console log for user information retrieval. --- src/user/src/routes/info.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/user/src/routes/info.ts b/src/user/src/routes/info.ts index 784ea23..ec46265 100644 --- a/src/user/src/routes/info.ts +++ b/src/user/src/routes/info.ts @@ -49,8 +49,6 @@ const route: FastifyPluginAsync = async (fastify, _opts): Promise => { if (isNullish(user)) { return res.makeResponse(404, 'failure', 'userinfo.failure.unknownUser'); } - console.log(user); - const payload = { name: user.name, id: user.id, From 9b70b6730e0c6e2d2d000df254c153eba02f717d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:01:30 +0000 Subject: [PATCH 10/10] Fix duplicate hidden attribute in profile.html line 43 Co-authored-by: EniumRaphael <35407363+EniumRaphael@users.noreply.github.com> --- frontend/src/pages/profile/profile.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/profile/profile.html b/frontend/src/pages/profile/profile.html index 76c7565..53492d7 100644 --- a/frontend/src/pages/profile/profile.html +++ b/frontend/src/pages/profile/profile.html @@ -40,7 +40,7 @@ class="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700">Update
-