From 7ce0de4e919d0ea7bc4617f0bb4bf13c61674bb1 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Sun, 2 Aug 2026 00:25:36 +0700 Subject: [PATCH 01/34] feat(identity): redesign identity module and introduce RBAC foundation - Redesign the Identity module with a richer domain model. - Extend the User entity to support username, Authentik integration, activity tracking, and storage information. - Add Role and Permission domain models with many-to-many relationships. - Implement RBAC foundation using UserRole, RolePermission, and UserPermission mappings. - Add user storage quota and usage fields with default values. - Introduce Authentik identifiers and synchronization metadata. - Refactor user domain logic for role and permission management. - Update Prisma schema to support the new identity architecture. - Improve JWT authentication and permission guard integration. - Update repositories, handlers, controllers, mappers, DTOs, and Swagger configuration. - Refresh environment configuration and project dependencies. --- .env.example | 60 +- .vscode/launch.json | 20 + Target V1.txt | 25 + changes.patch | Bin 0 -> 830508 bytes docs/How To Dev.txt | 46 + my-project.bundle | Bin 0 -> 122257 bytes package-lock.json | 5455 ++++++++++++----- package.json | 7 +- playwright.config.ts | 17 + postman/RayLab.postman_collection.json | 58 + .../20260730165554_permission/migration.sql | 93 + .../20260801084138_change/migration.sql | 16 + .../migration.sql | 2 + .../migration.sql | 11 + .../migration.sql | 10 + .../migration.sql | 15 + .../migration.sql | 17 + .../20260803_add_storage/migration.sql | 7 + prisma/migrations/migration_lock.toml | 3 + prisma/schema.prisma | 104 +- src/app.module.ts | 2 + src/common/constants/permission.constants.ts | 16 + src/core/auth/guards/jwt-auth.guard.ts | 60 +- .../authenticated-request.interface.ts | 16 +- src/core/auth/interfaces/identity-data.ts | 8 + src/main.ts | 7 +- src/modules/auth/auth.controller.ts | 65 + src/modules/auth/auth.module.ts | 46 + src/modules/auth/auth.service.ts | 193 + src/modules/auth/dto/login.dto.ts | 2 + src/modules/auth/pkce/redis-pkce.store.ts | 42 + .../auth/refresh/inmemory-refresh.store.ts | 38 + .../presentation/guards/permission.guard.ts | 20 +- src/modules/identity/application/README.md | 13 +- .../application/config/env-auth-config.ts | 15 + .../application/config/i-auth-config.ts | 5 + .../identity/application/handlers/README.MD | 1 + .../handlers/create-user.handler.ts | 37 - .../handlers/delete-user.handler.ts | 28 - .../application/handlers/find-user.handler.ts | 24 - .../handlers/find-users.handler.ts | 15 - .../permission/create-permission.handler.ts | 21 + .../permission/delete-permission.handler.ts | 14 + .../permission/get-permission.handler.ts | 11 + .../permission/get-permissions.handler.ts | 11 + .../permission/update-permission.handler.ts | 18 + .../role/assign-permission.handler.ts | 23 + .../handlers/role/create-role.handler.ts | 30 + .../handlers/role/delete-role.handler.ts | 14 + .../handlers/role/get-role.handler.ts | 11 + .../handlers/role/get-roles.handler.ts | 11 + .../role/remove-permission.handler.ts | 16 + .../handlers/role/update-role.handler.ts | 18 + .../handlers/update-user.handler.ts | 51 - .../user/assign-permission.handler.ts | 26 + .../handlers/user/assign-role.handler.ts | 23 + .../handlers/user/create-user.handler.ts | 31 + .../handlers/user/delete-user.handler.ts | 14 + .../handlers/user/disable-user.handler.ts | 14 + .../handlers/user/enable-user.handler.ts | 14 + .../handlers/user/get-current-user.handler.ts | 10 + .../handlers/user/get-user.handler.ts | 11 + .../handlers/user/get-users.handler.ts | 27 + .../user/remove-permission.handler.ts | 16 + .../handlers/user/remove-role.handler.ts | 16 + .../handlers/user/restore-user.handler.ts | 14 + .../handlers/user/sync-identity.handler.ts | 86 + .../identity/application/services/README.md | 12 - .../application/services/user.service.ts | 20 + .../identity/domain/entities/README.md | 12 +- .../domain/entities/permission.entity.ts | 40 + .../identity/domain/entities/role.entity.ts | 64 + .../identity/domain/entities/user.entity.ts | 210 +- .../domain/events/user-created.event.ts | 3 +- .../domain/events/user-deleted.event.ts | 6 - .../domain/events/user-disabled.event.ts | 7 + .../domain/events/user-email-changed.event.ts | 7 + .../domain/events/user-enabled.event.ts | 7 + .../events/user-username-changed.event.ts | 7 + .../identity/domain/repositories/README.MD | 1 + .../repositories/permission.interface.ts | 9 + .../domain/repositories/role.interface.ts | 12 + .../domain/repositories/user.interface.ts | 29 + .../repositories/user.repository.interface.ts | 17 - src/modules/identity/identity.module.ts | 119 +- src/modules/identity/infrastructure/README.md | 13 +- .../identity/infrastructure/mappers/README.MD | 1 + .../mappers/prisma-permission.mapper.ts | 23 + .../mappers/prisma-role.mapper.ts | 27 + .../mappers/prisma-user.mapper.ts | 80 +- .../infrastructure/persistence/README.md | 1 + .../infrastructure/repositories/README.md | 12 +- .../prisma-permission.repository.ts | 53 + .../repositories/prisma-role.repository.ts | 109 + .../repositories/prisma-user.repository.ts | 263 +- .../controllers/permissions.controller.ts | 67 + .../controllers/roles.controller.ts | 88 + .../controllers/users.controller.ts | 177 +- .../presentation/dto/create-permission.dto.ts | 19 + .../presentation/dto/create-role.dto.ts | 23 + .../presentation/dto/create-user.dto.ts | 19 +- .../presentation/dto/update-permission.dto.ts | 21 + .../presentation/dto/update-role.dto.ts | 20 + .../presentation/dto/update-user.dto.ts | 14 +- .../presentation/guards/current-user.guard.ts | 27 + test-results/.last-run.json | 7 + .../error-context.md | 193 + .../error-context.md | 136 + tests/README.md | 12 + .../domain => tests/api/permissions}/.gitkeep | 0 .../events => tests/api/roles}/.gitkeep | 0 tests/api/users/enable-user.spec.ts | 11 + tests/api/users/get-current-user.spec.ts | 20 + tests/api/users/get-user.spec.ts | 11 + tests/api/users/get-users.spec.ts | 23 + tests/fixtures/user.fixture.ts | 11 + tests/helpers/auth.ts | 91 + tests/helpers/env.ts | 7 + tests/helpers/index.ts | 3 + tests/helpers/request.ts | 73 + tests/integration/user.integration.spec.ts | 188 + .../mappers => tests/output}/.gitkeep | 0 tests/output/latest-result.txt | 1 + tests/reporter/custom-reporter.js | 72 + tests/reporter/custom-reporter.ts | 78 + .../failed-login-1785604157244-cookies.json | 1 + .../failed-login-1785604157244-page.html | 1 + .../failed-login-1785604157244.png | Bin 0 -> 6969 bytes .../failed-login-2-1785604169592-cookies.json | 1 + .../failed-login-2-1785604169592-page.html | 1 + .../failed-login-2-1785604169592.png | Bin 0 -> 6969 bytes tests/unit/user.service.spec.ts | 41 + 132 files changed, 7754 insertions(+), 2037 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 Target V1.txt create mode 100644 changes.patch create mode 100644 docs/How To Dev.txt create mode 100644 my-project.bundle create mode 100644 playwright.config.ts create mode 100644 postman/RayLab.postman_collection.json create mode 100644 prisma/migrations/20260730165554_permission/migration.sql create mode 100644 prisma/migrations/20260801084138_change/migration.sql create mode 100644 prisma/migrations/20260801101438_add_role_is_default/migration.sql create mode 100644 prisma/migrations/20260801101841_add_authentik_id/migration.sql create mode 100644 prisma/migrations/20260801104311_add_deleted_at/migration.sql create mode 100644 prisma/migrations/20260801132311_add_authentik_fields/migration.sql create mode 100644 prisma/migrations/20260802_add_authentik_fields/migration.sql create mode 100644 prisma/migrations/20260803_add_storage/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 src/common/constants/permission.constants.ts create mode 100644 src/core/auth/interfaces/identity-data.ts create mode 100644 src/modules/auth/auth.controller.ts create mode 100644 src/modules/auth/auth.module.ts create mode 100644 src/modules/auth/auth.service.ts create mode 100644 src/modules/auth/dto/login.dto.ts create mode 100644 src/modules/auth/pkce/redis-pkce.store.ts create mode 100644 src/modules/auth/refresh/inmemory-refresh.store.ts create mode 100644 src/modules/identity/application/config/env-auth-config.ts create mode 100644 src/modules/identity/application/config/i-auth-config.ts create mode 100644 src/modules/identity/application/handlers/README.MD delete mode 100644 src/modules/identity/application/handlers/create-user.handler.ts delete mode 100644 src/modules/identity/application/handlers/delete-user.handler.ts delete mode 100644 src/modules/identity/application/handlers/find-user.handler.ts delete mode 100644 src/modules/identity/application/handlers/find-users.handler.ts create mode 100644 src/modules/identity/application/handlers/permission/create-permission.handler.ts create mode 100644 src/modules/identity/application/handlers/permission/delete-permission.handler.ts create mode 100644 src/modules/identity/application/handlers/permission/get-permission.handler.ts create mode 100644 src/modules/identity/application/handlers/permission/get-permissions.handler.ts create mode 100644 src/modules/identity/application/handlers/permission/update-permission.handler.ts create mode 100644 src/modules/identity/application/handlers/role/assign-permission.handler.ts create mode 100644 src/modules/identity/application/handlers/role/create-role.handler.ts create mode 100644 src/modules/identity/application/handlers/role/delete-role.handler.ts create mode 100644 src/modules/identity/application/handlers/role/get-role.handler.ts create mode 100644 src/modules/identity/application/handlers/role/get-roles.handler.ts create mode 100644 src/modules/identity/application/handlers/role/remove-permission.handler.ts create mode 100644 src/modules/identity/application/handlers/role/update-role.handler.ts delete mode 100644 src/modules/identity/application/handlers/update-user.handler.ts create mode 100644 src/modules/identity/application/handlers/user/assign-permission.handler.ts create mode 100644 src/modules/identity/application/handlers/user/assign-role.handler.ts create mode 100644 src/modules/identity/application/handlers/user/create-user.handler.ts create mode 100644 src/modules/identity/application/handlers/user/delete-user.handler.ts create mode 100644 src/modules/identity/application/handlers/user/disable-user.handler.ts create mode 100644 src/modules/identity/application/handlers/user/enable-user.handler.ts create mode 100644 src/modules/identity/application/handlers/user/get-current-user.handler.ts create mode 100644 src/modules/identity/application/handlers/user/get-user.handler.ts create mode 100644 src/modules/identity/application/handlers/user/get-users.handler.ts create mode 100644 src/modules/identity/application/handlers/user/remove-permission.handler.ts create mode 100644 src/modules/identity/application/handlers/user/remove-role.handler.ts create mode 100644 src/modules/identity/application/handlers/user/restore-user.handler.ts create mode 100644 src/modules/identity/application/handlers/user/sync-identity.handler.ts delete mode 100644 src/modules/identity/application/services/README.md create mode 100644 src/modules/identity/application/services/user.service.ts create mode 100644 src/modules/identity/domain/entities/permission.entity.ts create mode 100644 src/modules/identity/domain/entities/role.entity.ts delete mode 100644 src/modules/identity/domain/events/user-deleted.event.ts create mode 100644 src/modules/identity/domain/events/user-disabled.event.ts create mode 100644 src/modules/identity/domain/events/user-email-changed.event.ts create mode 100644 src/modules/identity/domain/events/user-enabled.event.ts create mode 100644 src/modules/identity/domain/events/user-username-changed.event.ts create mode 100644 src/modules/identity/domain/repositories/README.MD create mode 100644 src/modules/identity/domain/repositories/permission.interface.ts create mode 100644 src/modules/identity/domain/repositories/role.interface.ts create mode 100644 src/modules/identity/domain/repositories/user.interface.ts delete mode 100644 src/modules/identity/domain/repositories/user.repository.interface.ts create mode 100644 src/modules/identity/infrastructure/mappers/README.MD create mode 100644 src/modules/identity/infrastructure/mappers/prisma-permission.mapper.ts create mode 100644 src/modules/identity/infrastructure/mappers/prisma-role.mapper.ts create mode 100644 src/modules/identity/infrastructure/persistence/README.md create mode 100644 src/modules/identity/infrastructure/repositories/prisma-permission.repository.ts create mode 100644 src/modules/identity/infrastructure/repositories/prisma-role.repository.ts create mode 100644 src/modules/identity/presentation/controllers/permissions.controller.ts create mode 100644 src/modules/identity/presentation/controllers/roles.controller.ts create mode 100644 src/modules/identity/presentation/dto/create-permission.dto.ts create mode 100644 src/modules/identity/presentation/dto/create-role.dto.ts create mode 100644 src/modules/identity/presentation/dto/update-permission.dto.ts create mode 100644 src/modules/identity/presentation/dto/update-role.dto.ts create mode 100644 src/modules/identity/presentation/guards/current-user.guard.ts create mode 100644 test-results/.last-run.json create mode 100644 test-results/user.integration-Integrati-096a0-ser-and-issues-internal-JWT/error-context.md create mode 100644 test-results/user.integration-Integrati-8202b-ed-user-issues-internal-JWT/error-context.md create mode 100644 tests/README.md rename {src/modules/identity/domain => tests/api/permissions}/.gitkeep (100%) rename {src/modules/identity/events => tests/api/roles}/.gitkeep (100%) create mode 100644 tests/api/users/enable-user.spec.ts create mode 100644 tests/api/users/get-current-user.spec.ts create mode 100644 tests/api/users/get-user.spec.ts create mode 100644 tests/api/users/get-users.spec.ts create mode 100644 tests/fixtures/user.fixture.ts create mode 100644 tests/helpers/auth.ts create mode 100644 tests/helpers/env.ts create mode 100644 tests/helpers/index.ts create mode 100644 tests/helpers/request.ts create mode 100644 tests/integration/user.integration.spec.ts rename {src/modules/identity/infrastructure/mappers => tests/output}/.gitkeep (100%) create mode 100644 tests/output/latest-result.txt create mode 100644 tests/reporter/custom-reporter.js create mode 100644 tests/reporter/custom-reporter.ts create mode 100644 tests/test-artifacts/failed-login-1785604157244-cookies.json create mode 100644 tests/test-artifacts/failed-login-1785604157244-page.html create mode 100644 tests/test-artifacts/failed-login-1785604157244.png create mode 100644 tests/test-artifacts/failed-login-2-1785604169592-cookies.json create mode 100644 tests/test-artifacts/failed-login-2-1785604169592-page.html create mode 100644 tests/test-artifacts/failed-login-2-1785604169592.png create mode 100644 tests/unit/user.service.spec.ts diff --git a/.env.example b/.env.example index c2b7a5c..f74dde0 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,56 @@ -APP_ENV=development #development / test / prod -PORT=3069 -DATABASE_URL=postgresql://USERNAME:PASSWORD@HOST:5432/DATABASE?schema=public +# Application +BASE_URL=http://localhost:3069 PORT=3000 NODE_ENV=development -JWT_SECRET=GANTI_DENGAN_SECRET -JWT_REFRESH_SECRET=GANTI_DENGAN_REFRESH_SECRET +# Database (PostgreSQL) +DATABASE_URL=postgresql://user:password@localhost:5432/Database?shema=public -SWAGGER_ENABLED=true +# Authentik (OIDC discovery base) +# Used by OIDC discovery and token exchange (AuthService) +AUTHENTIK_URL=https://auth.raylab.site -LOG_LEVEL=debug \ No newline at end of file +# Authentik Admin API (provisioning identities & groups) +# Example: https://auth.raylab.site/api +AUTHENTIK_API_BASE=https://auth.raylab.site/api + +# Bearer token for Authentik Admin API (admin service account) +AUTHENTIK_API_TOKEN=changeme_admin_api_token + +# Default group name format: RL-{object} +# RayLab will use this group to gate access (default RL-RayLab-Users) +AUTHENTIK_DEFAULT_GROUP=RL-RayLab-Users + +# OIDC / JWKS / Issuer settings for verifying Authentik tokens (if needed) +AUTHENTIK_JWKS_URI=https://auth.raylab.site/.well-known/jwks.json AUTHENTIK_ISSUER=https://auth.raylab.site/application/o/ray-lab-core/ AUTHENTIK_AUDIENCE=ray-lab-core + +# OIDC client credentials (used when RayLab needs to call token endpoint) +AUTHENTIK_CLIENT_ID=raylab-client AUTHENTIK_CLIENT_SECRET=raylab-client-secret + +# RayLab internal JWT (used to sign internal tokens) +RAYLAB_JWT_SECRET=replace_with_a_long_random_secret + +# Token lifetime in seconds +RAYLAB_JWT_EXPIRES_IN=3600 + +# Sync / reconciliation +# Interval in seconds for reconciler to retry PENDING/FAILED syncs +AUTH_RECONCILE_INTERVAL_SECONDS=3600 + +# Auth / sync behavior +# Disable auto-creation of users on login (must remain false per design) +AUTH_AUTO_CREATE_USER=false AUTH_SYNC_EMAIL=true AUTH_SYNC_USERNAME=false + +# Test accounts for integration tests (must exist in Authentik and RayLab DB before tests, +# or be provisioned via RayLab Admin endpoints during test run) +ADMIN_USERNAME=admin@example.com ADMIN_PASSWORD=changeme_admin_password + +OWNER_USERNAME=owner@example.com OWNER_PASSWORD=changeme_owner_password + +EMPLOYEE_USERNAME=employee@example.com EMPLOYEE_PASSWORD=changeme_employee_password + +# Optional: integration test user (overrides defaults used by tests) +TEST_USER_EMAIL=test-integration@example.com TEST_USER_USERNAME=test-integration TEST_USER_PASSWORD=StrongP@ssw0rd! + +# Optional: adjust logging or other runtime flags +LOG_LEVEL=info + +SWAGGER_ENABLED=true \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5cab610 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug NestJS", + "type": "node", + "request": "launch", + "runtimeExecutable": "npm", + "runtimeArgs": [ + "run", + "start:debug" + ], + "console": "integratedTerminal", + "restart": true, + "skipFiles": [ + "/**" + ] + } + ] +} \ No newline at end of file diff --git a/Target V1.txt b/Target V1.txt new file mode 100644 index 0000000..602ac97 --- /dev/null +++ b/Target V1.txt @@ -0,0 +1,25 @@ +RayLab Core +│ +├── Identity +│ ├── User +│ ├── Group +│ ├── Role +│ └── Permission +│ +├── Authentication +│ +├── Authorization +│ +├── Audit +│ +├── Scheduler +│ +├── Media +│ +├── Storage +│ +├── Configuration +│ +├── Workflow / Events +│ +└── Registry \ No newline at end of file diff --git a/changes.patch b/changes.patch new file mode 100644 index 0000000000000000000000000000000000000000..bcd6328dfffcdee4463535bf28d14421cd246995 GIT binary patch literal 830508 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
oo-a|Oyll2@&FP1ccT#zM0WBRf%PQ~x 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

#Tf^_D;^DYdYD&HUKZz9m*d>qU=2|1#yj}U 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%0A}v0rz6oRJ3e^UmW;{#@GcD}ClQo#I^U;)@l! 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

+!G>J4SUC=ihF1zPWF_ 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 literal 0 HcmV?d00001 diff --git a/docs/How To Dev.txt b/docs/How To Dev.txt new file mode 100644 index 0000000..68eccff --- /dev/null +++ b/docs/How To Dev.txt @@ -0,0 +1,46 @@ +Note : +- pastikan berada di branch yang benar. +- + +Requirement : +- Node.js 22 LTS + +Step : +1. jalankan "NPM Install". +2. generate prisma client "npx prisma generate". +3. jalankan "cp env.example env" atau buat file .env baru. +4. sesuaikan isi dari .env + +Run Server : +- npm run start:dev + +Command Dev : +- npm test +- npm run lint +- npm run format +- npx prisma studio {untuk cek db} + +Test API : +- menggunakan postman : + arahkan ke file /postman/RayLab.postman_collection.json +- menggunakan swagger : + - masuk ke http://localhost:port/apilist + - klik authorize dan masukkan jwt token + +Controller Requirement : +- diatas deklarasi class controller pastikan ada ini : + - @ApiTags('object') + - @Controller('object') + - @UseGuards(JwtAuthGuard, PermissionGuard) + +- diatas deklarasi method controller pastikan ada ini : + - tipe api : @Post(), @Get(':id'), @Patch(':id'), @Delete(':id') + - @Permissions(PermissionType.SOMETHING) + - @ApiOperation({ summary: 'penjelasan singkat api' }) + +Table : +- untuk table akan dibuat otomatis oleh schema.prisma, disana hanya perlu ditambahkan model atau diubah, jalankan commandnya, setelah itu otomatis tablenya dibuat +- command : npx prisma migrate dev --name {Nama Perubahan} + +Use case: +- penentuan use case ditentukan siapa pemiliknya dahulu, dan harus konsisten. \ No newline at end of file diff --git a/my-project.bundle b/my-project.bundle new file mode 100644 index 0000000000000000000000000000000000000000..0d4559c98b12eb74bc2d5aa03c0ad65bd2cff3b1 GIT binary patch literal 122257 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

6ke-ov7p6v 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 literal 0 HcmV?d00001 diff --git a/package-lock.json b/package-lock.json index 1056868..53a6213 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,32 +1,95 @@ { "name": "raylab-core", "version": "0.1.0", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@babel/code-frame": { + "packages": { + "": { + "name": "raylab-core", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@nestjs/common": "^10.4.20", + "@nestjs/config": "^3.3.0", + "@nestjs/core": "^10.4.20", + "@nestjs/jwt": "^10.2.0", + "@nestjs/mapped-types": "^2.1.0", + "@nestjs/passport": "^10.0.3", + "@nestjs/platform-express": "^10.4.20", + "@nestjs/swagger": "^7.4.2", + "@prisma/client": "^5.22.0", + "bcrypt": "^5.1.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.2", + "dotenv": "^16.6.1", + "ioredis": "^5.3.2", + "jose": "^6.2.6", + "jsonwebtoken": "^9.0.2", + "openid-client": "^6.5.0", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", + "pino": "^9.7.0", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.2", + "swagger-ui-express": "^5.0.1", + "uuid": "^11.1.0" + }, + "devDependencies": { + "@eslint/js": "^9.21.0", + "@nestjs/testing": "^10.4.22", + "@playwright/test": "^1.62.1", + "@types/bcrypt": "^5.0.2", + "@types/jest": "^29.5.14", + "@types/node": "^22.13.10", + "@types/passport-jwt": "^4.0.1", + "@types/supertest": "^6.0.3", + "@types/swagger-ui-express": "^4.1.8", + "@typescript-eslint/eslint-plugin": "^8.25.0", + "@typescript-eslint/parser": "^8.25.0", + "eslint": "^9.21.0", + "globals": "^16.0.0", + "jest": "^29.7.0", + "prettier": "^3.5.3", + "prisma": "^5.22.0", + "supertest": "^7.0.0", + "ts-jest": "^29.2.6", + "ts-node": "^10.9.2", + "ts-node-dev": "^2.0.0", + "typescript": "^5.8.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/compat-data": { + "node_modules/@babel/compat-data": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/core": { + "node_modules/@babel/core": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", @@ -43,289 +106,409 @@ "json5": "^2.2.3", "semver": "^6.3.1" }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "@babel/generator": { + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, - "requires": { + "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-compilation-targets": { + "node_modules/@babel/helper-compilation-targets": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, - "requires": { + "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-globals": { + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/helper-module-imports": { + "node_modules/@babel/helper-module-imports": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, - "requires": { + "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-module-transforms": { + "node_modules/@babel/helper-module-transforms": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "@babel/helper-plugin-utils": { + "node_modules/@babel/helper-plugin-utils": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/helper-string-parser": { + "node_modules/@babel/helper-string-parser": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/helper-validator-identifier": { + "node_modules/@babel/helper-validator-identifier": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/helper-validator-option": { + "node_modules/@babel/helper-validator-option": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/helpers": { + "node_modules/@babel/helpers": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, - "requires": { + "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/parser": { + "node_modules/@babel/parser": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "@babel/plugin-syntax-async-generators": { + "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-bigint": { + "node_modules/@babel/plugin-syntax-bigint": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-class-properties": { + "node_modules/@babel/plugin-syntax-class-properties": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-class-static-block": { + "node_modules/@babel/plugin-syntax-class-static-block": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-import-attributes": { + "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-import-meta": { + "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-json-strings": { + "node_modules/@babel/plugin-syntax-json-strings": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-jsx": { + "node_modules/@babel/plugin-syntax-jsx": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-logical-assignment-operators": { + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-nullish-coalescing-operator": { + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-numeric-separator": { + "node_modules/@babel/plugin-syntax-numeric-separator": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-object-rest-spread": { + "node_modules/@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-optional-catch-binding": { + "node_modules/@babel/plugin-syntax-optional-catch-binding": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-optional-chaining": { + "node_modules/@babel/plugin-syntax-optional-chaining": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-private-property-in-object": { + "node_modules/@babel/plugin-syntax-private-property-in-object": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-top-level-await": { + "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-typescript": { + "node_modules/@babel/plugin-syntax-typescript": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/template": { + "node_modules/@babel/template": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/traverse": { + "node_modules/@babel/traverse": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", @@ -333,108 +516,144 @@ "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/types": { + "node_modules/@babel/types": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "@bcoe/v8-coverage": { + "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "@borewit/text-codec": { + "node_modules/@borewit/text-codec": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", - "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==" + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } }, - "@cspotcode/source-map-support": { + "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } + "engines": { + "node": ">=12" } }, - "@eslint-community/eslint-utils": { + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@eslint-community/eslint-utils": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, - "requires": { + "dependencies": { "eslint-visitor-keys": "^3.4.3" }, - "dependencies": { - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true - } + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "@eslint-community/regexpp": { + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } }, - "@eslint/config-array": { + "node_modules/@eslint/config-array": { "version": "0.21.2", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, - "requires": { + "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "@eslint/config-helpers": { + "node_modules/@eslint/config-helpers": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, - "requires": { + "dependencies": { "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "@eslint/core": { + "node_modules/@eslint/core": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, - "requires": { + "dependencies": { "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "@eslint/eslintrc": { + "node_modules/@eslint/eslintrc": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", "dev": true, - "requires": { + "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", @@ -445,193 +664,288 @@ "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, - "dependencies": { - "globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true - }, - "ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true - }, - "js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - } + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "@eslint/js": { + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/js": { "version": "9.39.5", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", - "dev": true + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } }, - "@eslint/object-schema": { + "node_modules/@eslint/object-schema": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } }, - "@eslint/plugin-kit": { + "node_modules/@eslint/plugin-kit": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, - "requires": { + "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "@humanfs/core": { + "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, - "requires": { + "dependencies": { "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" } }, - "@humanfs/node": { + "node_modules/@humanfs/node": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, - "requires": { + "dependencies": { "@humanfs/core": "^0.19.2", "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" } }, - "@humanfs/types": { + "node_modules/@humanfs/types": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=18.18.0" + } }, - "@humanwhocodes/module-importer": { + "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, - "@humanwhocodes/retry": { + "node_modules/@humanwhocodes/retry": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, - "@istanbuljs/load-nyc-config": { + "node_modules/@ioredis/commands": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz", + "integrity": "sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==", + "license": "MIT" + }, + "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, - "requires": { + "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "js-yaml": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", - "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } + "engines": { + "node": ">=8" } }, - "@istanbuljs/schema": { + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", + "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "@jest/console": { + "node_modules/@jest/console": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0", "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/core": { + "node_modules/@jest/core": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, - "requires": { + "dependencies": { "@jest/console": "^29.7.0", "@jest/reporters": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -660,71 +974,97 @@ "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "@jest/environment": { + "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, - "requires": { + "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/expect": { + "node_modules/@jest/expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, - "requires": { + "dependencies": { "expect": "^29.7.0", "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/expect-utils": { + "node_modules/@jest/expect-utils": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, - "requires": { + "dependencies": { "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/fake-timers": { + "node_modules/@jest/fake-timers": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", "jest-message-util": "^29.7.0", "jest-mock": "^29.7.0", "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/globals": { + "node_modules/@jest/globals": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, - "requires": { + "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", "@jest/types": "^29.6.3", "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/reporters": { + "node_modules/@jest/reporters": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, - "requires": { + "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -749,58 +1089,81 @@ "string-length": "^4.0.1", "strip-ansi": "^6.0.0", "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "@jest/schemas": { + "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, - "requires": { + "dependencies": { "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/source-map": { + "node_modules/@jest/source-map": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/test-result": { + "node_modules/@jest/test-result": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, - "requires": { + "dependencies": { "@jest/console": "^29.7.0", "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/test-sequencer": { + "node_modules/@jest/test-sequencer": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, - "requires": { + "dependencies": { "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/transform": { + "node_modules/@jest/transform": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, - "requires": { + "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", @@ -816,74 +1179,86 @@ "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jest/types": { + "node_modules/@jest/types": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, - "requires": { + "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "@jridgewell/gen-mapping": { + "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, - "@jridgewell/remapping": { + "node_modules/@jridgewell/remapping": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, - "@jridgewell/resolve-uri": { + "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.0.0" + } }, - "@jridgewell/sourcemap-codec": { + "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true }, - "@jridgewell/trace-mapping": { + "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "@lukeed/csprng": { + "node_modules/@lukeed/csprng": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", - "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==" + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "engines": { + "node": ">=8" + } }, - "@mapbox/node-pre-gyp": { + "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "requires": { + "dependencies": { "detect-libc": "^2.0.0", "https-proxy-agent": "^5.0.0", "make-dir": "^3.1.0", @@ -893,109 +1268,193 @@ "rimraf": "^3.0.2", "semver": "^7.3.5", "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" } }, - "@microsoft/tsdoc": { + "node_modules/@microsoft/tsdoc": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==" }, - "@nestjs/common": { + "node_modules/@nestjs/common": { "version": "10.4.22", "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.22.tgz", "integrity": "sha512-fxJ4v85nDHaqT1PmfNCQ37b/jcv2OojtXTaK1P2uAXhzLf9qq6WNUOFvxBrV4fhQek1EQoT1o9oj5xAZmv3NRw==", - "requires": { + "dependencies": { "file-type": "20.4.1", "iterare": "1.2.1", "tslib": "2.8.1", "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } } }, - "@nestjs/config": { + "node_modules/@nestjs/config": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.3.0.tgz", "integrity": "sha512-pdGTp8m9d0ZCrjTpjkUbZx6gyf2IKf+7zlkrPNMsJzYZ4bFRRTpXrnj+556/5uiI6AfL5mMrJc2u7dB6bvM+VA==", - "requires": { + "dependencies": { "dotenv": "16.4.5", "dotenv-expand": "10.0.0", "lodash": "4.17.21" }, - "dependencies": { - "dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" - } + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "rxjs": "^7.1.0" } }, - "@nestjs/core": { + "node_modules/@nestjs/config/node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@nestjs/core": { "version": "10.4.22", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.22.tgz", "integrity": "sha512-6IX9+VwjiKtCjx+mXVPncpkQ5ZjKfmssOZPFexmT+6T9H9wZ3svpYACAo7+9e7Nr9DZSoRZw3pffkJP7Z0UjaA==", - "requires": { + "hasInstallScript": true, + "dependencies": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", "iterare": "1.2.1", "path-to-regexp": "3.3.0", "tslib": "2.8.1", "uid": "2.0.2" - } - }, - "@nestjs/jwt": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", - "integrity": "sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==", - "requires": { - "@types/jsonwebtoken": "9.0.5", - "jsonwebtoken": "9.0.2" }, - "dependencies": { - "jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "requires": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/microservices": "^10.0.0", + "@nestjs/platform-express": "^10.0.0", + "@nestjs/websockets": "^10.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 } } }, - "@nestjs/mapped-types": { + "node_modules/@nestjs/jwt": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", + "integrity": "sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==", + "dependencies": { + "@types/jsonwebtoken": "9.0.5", + "jsonwebtoken": "9.0.2" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/@nestjs/jwt/node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/@nestjs/mapped-types": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.1.tgz", - "integrity": "sha512-SCCoMEJ6jdeI5h/N+KCVF1+pmg/hmEkNA5nHTS8Gvww7T/LCl4o1gFLinw2iQ60w7slFkszHcGLKGdazVI4F8A==" + "integrity": "sha512-SCCoMEJ6jdeI5h/N+KCVF1+pmg/hmEkNA5nHTS8Gvww7T/LCl4o1gFLinw2iQ60w7slFkszHcGLKGdazVI4F8A==", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0 || ^0.15.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } }, - "@nestjs/passport": { + "node_modules/@nestjs/passport": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", - "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==" + "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "passport": "^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0" + } }, - "@nestjs/platform-express": { + "node_modules/@nestjs/platform-express": { "version": "10.4.22", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.22.tgz", "integrity": "sha512-ySSq7Py/DFozzZdNDH67m/vHoeVdphDniWBnl6q5QVoXldDdrZIHLXLRMPayTDh5A95nt7jjJzmD4qpTbNQ6tA==", - "requires": { + "dependencies": { "body-parser": "1.20.4", "cors": "2.8.5", "express": "4.22.1", "multer": "2.0.2", "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0" } }, - "@nestjs/swagger": { + "node_modules/@nestjs/swagger": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.4.2.tgz", "integrity": "sha512-Mu6TEn1M/owIvAx2B4DUQObQXqo2028R2s9rSZ/hJEgBK95+doTwS0DjmVA2wTeZTyVtXOoN7CsoM5pONBzvKQ==", - "requires": { + "dependencies": { "@microsoft/tsdoc": "^0.15.0", "@nestjs/mapped-types": "2.0.5", "js-yaml": "4.1.0", @@ -1003,162 +1462,270 @@ "path-to-regexp": "3.3.0", "swagger-ui-dist": "5.17.14" }, - "dependencies": { - "@nestjs/mapped-types": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", - "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==" + "peerDependencies": { + "@fastify/static": "^6.0.0 || ^7.0.0", + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true } } }, - "@noble/hashes": { + "node_modules/@nestjs/swagger/node_modules/@nestjs/mapped-types": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", + "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/testing": { + "version": "10.4.22", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.22.tgz", + "integrity": "sha512-HO9aPus3bAedAC+jKVAA8jTdaj4fs5M9fing4giHrcYV2txe9CvC1l1WAjwQ9RDhEHdugjY4y+FZA/U/YqPZrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0", + "@nestjs/microservices": "^10.0.0", + "@nestjs/platform-express": "^10.0.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + } + } + }, + "node_modules/@noble/hashes": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "dev": true + "dev": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, - "@nuxtjs/opencollective": { + "node_modules/@nuxtjs/opencollective": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", - "requires": { + "dependencies": { "chalk": "^4.1.0", "consola": "^2.15.0", "node-fetch": "^2.6.1" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" } }, - "@paralleldrive/cuid2": { + "node_modules/@paralleldrive/cuid2": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", "dev": true, - "requires": { + "dependencies": { "@noble/hashes": "^1.1.5" } }, - "@pinojs/redact": { + "node_modules/@pinojs/redact": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==" }, - "@prisma/client": { + "node_modules/@playwright/test": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz", + "integrity": "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@prisma/client": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", - "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==" + "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", + "hasInstallScript": true, + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } }, - "@prisma/debug": { + "node_modules/@prisma/debug": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==", - "dev": true + "devOptional": true }, - "@prisma/engines": { + "node_modules/@prisma/engines": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", - "dev": true, - "requires": { + "devOptional": true, + "hasInstallScript": true, + "dependencies": { "@prisma/debug": "5.22.0", "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", "@prisma/fetch-engine": "5.22.0", "@prisma/get-platform": "5.22.0" } }, - "@prisma/engines-version": { + "node_modules/@prisma/engines-version": { "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==", - "dev": true + "devOptional": true }, - "@prisma/fetch-engine": { + "node_modules/@prisma/fetch-engine": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", - "dev": true, - "requires": { + "devOptional": true, + "dependencies": { "@prisma/debug": "5.22.0", "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", "@prisma/get-platform": "5.22.0" } }, - "@prisma/get-platform": { + "node_modules/@prisma/get-platform": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", - "dev": true, - "requires": { + "devOptional": true, + "dependencies": { "@prisma/debug": "5.22.0" } }, - "@sinclair/typebox": { + "node_modules/@sinclair/typebox": { "version": "0.27.12", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.12.tgz", "integrity": "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==", "dev": true }, - "@sinonjs/commons": { + "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, - "requires": { + "dependencies": { "type-detect": "4.0.8" } }, - "@sinonjs/fake-timers": { + "node_modules/@sinonjs/fake-timers": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, - "requires": { + "dependencies": { "@sinonjs/commons": "^3.0.0" } }, - "@tokenizer/inflate": { + "node_modules/@tokenizer/inflate": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", - "requires": { + "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "@tokenizer/token": { + "node_modules/@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" }, - "@tsconfig/node10": { + "node_modules/@tsconfig/node10": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", "dev": true }, - "@tsconfig/node12": { + "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true }, - "@tsconfig/node14": { + "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true }, - "@tsconfig/node16": { + "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, - "@types/babel__core": { + "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, - "requires": { + "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", @@ -1166,329 +1733,325 @@ "@types/babel__traverse": "*" } }, - "@types/babel__generator": { + "node_modules/@types/babel__generator": { "version": "7.27.0", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.0.0" } }, - "@types/babel__template": { + "node_modules/@types/babel__template": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, - "requires": { + "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, - "@types/babel__traverse": { + "node_modules/@types/babel__traverse": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.28.2" } }, - "@types/bcrypt": { + "node_modules/@types/bcrypt": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "@types/body-parser": { + "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, - "requires": { + "dependencies": { "@types/connect": "*", "@types/node": "*" } }, - "@types/connect": { + "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "@types/cookiejar": { + "node_modules/@types/cookiejar": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", "dev": true }, - "@types/estree": { + "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true }, - "@types/express": { + "node_modules/@types/express": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", "dev": true, - "requires": { + "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", "@types/serve-static": "^2" } }, - "@types/express-serve-static-core": { + "node_modules/@types/express-serve-static-core": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.2.tgz", "integrity": "sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, - "@types/graceful-fs": { + "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "@types/http-errors": { + "node_modules/@types/http-errors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "dev": true }, - "@types/istanbul-lib-coverage": { + "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, - "@types/istanbul-lib-report": { + "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, - "requires": { + "dependencies": { "@types/istanbul-lib-coverage": "*" } }, - "@types/istanbul-reports": { + "node_modules/@types/istanbul-reports": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, - "requires": { + "dependencies": { "@types/istanbul-lib-report": "*" } }, - "@types/jest": { + "node_modules/@types/jest": { "version": "29.5.14", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, - "requires": { + "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" } }, - "@types/json-schema": { + "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, - "@types/jsonwebtoken": { + "node_modules/@types/jsonwebtoken": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", - "requires": { - "@types/node": "*" - }, "dependencies": { - "@types/node": { - "version": "26.1.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", - "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", - "requires": { - "undici-types": "~8.3.0" - } - } + "@types/node": "*" } }, - "@types/methods": { + "node_modules/@types/jsonwebtoken/node_modules/@types/node": { + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", "dev": true }, - "@types/node": { + "node_modules/@types/node": { "version": "22.20.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", "dev": true, - "requires": { - "undici-types": "~6.21.0" - }, "dependencies": { - "undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true - } + "undici-types": "~6.21.0" } }, - "@types/passport": { + "node_modules/@types/node/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true + }, + "node_modules/@types/passport": { "version": "1.0.17", "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", "dev": true, - "requires": { + "dependencies": { "@types/express": "*" } }, - "@types/passport-jwt": { + "node_modules/@types/passport-jwt": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-4.0.1.tgz", "integrity": "sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==", "dev": true, - "requires": { + "dependencies": { "@types/jsonwebtoken": "*", "@types/passport-strategy": "*" } }, - "@types/passport-strategy": { + "node_modules/@types/passport-strategy": { "version": "0.2.38", "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", "dev": true, - "requires": { + "dependencies": { "@types/express": "*", "@types/passport": "*" } }, - "@types/qs": { + "node_modules/@types/qs": { "version": "6.15.1", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", "dev": true }, - "@types/range-parser": { + "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, - "@types/send": { + "node_modules/@types/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "@types/serve-static": { + "node_modules/@types/serve-static": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", "dev": true, - "requires": { + "dependencies": { "@types/http-errors": "*", "@types/node": "*" } }, - "@types/stack-utils": { + "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, - "@types/strip-bom": { + "node_modules/@types/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", "dev": true }, - "@types/strip-json-comments": { + "node_modules/@types/strip-json-comments": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", "dev": true }, - "@types/superagent": { + "node_modules/@types/superagent": { "version": "8.1.11", "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.11.tgz", "integrity": "sha512-KA7srSW/HENDtOw9DOqaFLgWuMqN9WgjEw62lh9dpvRaZDkhdOkazASd7X7i2eMUYLHa1U37ZttnePsH5zTDHw==", "dev": true, - "requires": { + "dependencies": { "@types/cookiejar": "^2.1.5", "@types/methods": "^1.1.4", "@types/node": "*", "form-data": "^4.0.0" } }, - "@types/supertest": { + "node_modules/@types/supertest": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz", "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==", "dev": true, - "requires": { + "dependencies": { "@types/methods": "^1.1.4", "@types/superagent": "^8.1.0" } }, - "@types/swagger-ui-express": { + "node_modules/@types/swagger-ui-express": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz", "integrity": "sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==", "dev": true, - "requires": { + "dependencies": { "@types/express": "*", "@types/serve-static": "*" } }, - "@types/validator": { + "node_modules/@types/validator": { "version": "13.15.10", "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==" }, - "@types/yargs": { + "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "dev": true, - "requires": { + "dependencies": { "@types/yargs-parser": "*" } }, - "@types/yargs-parser": { + "node_modules/@types/yargs-parser": { "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, - "@typescript-eslint/eslint-plugin": { + "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", "dev": true, - "requires": { + "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.65.0", "@typescript-eslint/type-utils": "8.65.0", @@ -1497,73 +2060,141 @@ "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.65.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "@typescript-eslint/parser": { + "node_modules/@typescript-eslint/parser": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", "dev": true, - "requires": { + "dependencies": { "@typescript-eslint/scope-manager": "8.65.0", "@typescript-eslint/types": "8.65.0", "@typescript-eslint/typescript-estree": "8.65.0", "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "@typescript-eslint/project-service": { + "node_modules/@typescript-eslint/project-service": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", "dev": true, - "requires": { + "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.65.0", "@typescript-eslint/types": "^8.65.0", "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "@typescript-eslint/scope-manager": { + "node_modules/@typescript-eslint/scope-manager": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, - "requires": { + "dependencies": { "@typescript-eslint/types": "8.65.0", "@typescript-eslint/visitor-keys": "8.65.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "@typescript-eslint/tsconfig-utils": { + "node_modules/@typescript-eslint/tsconfig-utils": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", - "dev": true + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } }, - "@typescript-eslint/type-utils": { + "node_modules/@typescript-eslint/type-utils": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", "dev": true, - "requires": { + "dependencies": { "@typescript-eslint/types": "8.65.0", "@typescript-eslint/typescript-estree": "8.65.0", "@typescript-eslint/utils": "8.65.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "@typescript-eslint/types": { + "node_modules/@typescript-eslint/types": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", - "dev": true + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "@typescript-eslint/typescript-estree": { + "node_modules/@typescript-eslint/typescript-estree": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, - "requires": { + "dependencies": { "@typescript-eslint/project-service": "8.65.0", "@typescript-eslint/tsconfig-utils": "8.65.0", "@typescript-eslint/types": "8.65.0", @@ -1574,200 +2205,298 @@ "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, - "dependencies": { - "balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true - }, - "brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", - "dev": true, - "requires": { - "balanced-match": "^4.0.2" - } - }, - "minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "requires": { - "brace-expansion": "^5.0.8" - } - } + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "@typescript-eslint/utils": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", "dev": true, - "requires": { + "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.65.0", "@typescript-eslint/types": "8.65.0", "@typescript-eslint/typescript-estree": "8.65.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "@typescript-eslint/visitor-keys": { + "node_modules/@typescript-eslint/visitor-keys": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, - "requires": { + "dependencies": { "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "abbrev": { + "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, - "accepts": { + "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { + "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" } }, - "acorn": { + "node_modules/acorn": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "dev": true + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } }, - "acorn-jsx": { + "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } }, - "acorn-walk": { + "node_modules/acorn-walk": { "version": "8.3.5", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", "dev": true, - "requires": { + "dependencies": { "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" } }, - "agent-base": { + "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { + "dependencies": { "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" } }, - "ajv": { + "node_modules/ajv": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, - "requires": { + "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "ansi-escapes": { + "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, - "requires": { + "dependencies": { "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "ansi-regex": { + "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } }, - "ansi-styles": { + "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { + "dependencies": { "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "anymatch": { + "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, - "requires": { + "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "append-field": { + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" }, - "aproba": { + "node_modules/aproba": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==" }, - "are-we-there-yet": { + "node_modules/are-we-there-yet": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "requires": { + "deprecated": "This package is no longer supported.", + "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" } }, - "arg": { + "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, - "argparse": { + "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "array-flatten": { + "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, - "asap": { + "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, - "asynckit": { + "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, - "atomic-sleep": { + "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "engines": { + "node": ">=8.0.0" + } }, - "babel-jest": { + "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, - "requires": { + "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", @@ -1775,60 +2504,76 @@ "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" } }, - "babel-plugin-istanbul": { + "node_modules/babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" }, - "dependencies": { - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } + "engines": { + "node": ">=8" } }, - "babel-plugin-jest-hoist": { + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, - "requires": { + "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "babel-preset-current-node-syntax": { + "node_modules/babel-preset-current-node-syntax": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", "dev": true, - "requires": { + "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -1844,49 +2589,74 @@ "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" } }, - "babel-preset-jest": { + "node_modules/babel-preset-jest": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, - "requires": { + "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "baseline-browser-mapping": { + "node_modules/baseline-browser-mapping": { "version": "2.11.7", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.7.tgz", "integrity": "sha512-APw5YuIQAg6L9w4sHDI6j26DGFJI6RpYOhnkMPdC9lWbkKvsyPHzDsve1yd73lk21yz7Y09Kci8B2Pp9FonzWA==", - "dev": true + "dev": true, + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } }, - "bcrypt": { + "node_modules/bcrypt": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", - "requires": { + "hasInstallScript": true, + "dependencies": { "@mapbox/node-pre-gyp": "^1.0.11", "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "binary-extensions": { + "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "body-parser": { + "node_modules/body-parser": { "version": "1.20.4", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", - "requires": { + "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", @@ -1900,336 +2670,472 @@ "type-is": "~1.6.18", "unpipe": "~1.0.0" }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "brace-expansion": { + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/brace-expansion": { "version": "1.1.18", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { + "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "requires": { + "dependencies": { "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, - "browserslist": { + "node_modules/browserslist": { "version": "4.28.7", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", "dev": true, - "requires": { + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { "baseline-browser-mapping": "^2.10.44", "caniuse-lite": "^1.0.30001806", "electron-to-chromium": "^1.5.393", "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "bs-logger": { + "node_modules/bs-logger": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, - "requires": { + "dependencies": { "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" } }, - "bser": { + "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, - "requires": { + "dependencies": { "node-int64": "^0.4.0" } }, - "buffer-equal-constant-time": { + "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, - "buffer-from": { + "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, - "busboy": { + "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "requires": { + "dependencies": { "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" } }, - "bytes": { + "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } }, - "call-bind-apply-helpers": { + "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "requires": { + "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "call-bound": { + "node_modules/call-bound": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "requires": { + "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "callsites": { + "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "camelcase": { + "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "caniuse-lite": { + "node_modules/caniuse-lite": { "version": "1.0.30001806", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", - "dev": true + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] }, - "chalk": { + "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { + "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "char-regex": { + "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "chokidar": { + "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "requires": { + "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "chownr": { + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } }, - "ci-info": { + "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } }, - "cjs-module-lexer": { + "node_modules/cjs-module-lexer": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true }, - "class-transformer": { + "node_modules/class-transformer": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" }, - "class-validator": { + "node_modules/class-validator": { "version": "0.14.4", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.4.tgz", "integrity": "sha512-AwNusCCam51q703dW82x95tOqQp6oC9HNUl724KxJJOfnKscI8dOloXFgyez7LbTTKWuRBA37FScqVbJEoq8Yw==", - "requires": { + "dependencies": { "@types/validator": "^13.15.3", "libphonenumber-js": "^1.11.1", "validator": "^13.15.22" } }, - "cliui": { + "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "requires": { + "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "co": { + "node_modules/cluster-key-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", + "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } }, - "collect-v8-coverage": { + "node_modules/collect-v8-coverage": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", "dev": true }, - "color-convert": { + "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { + "dependencies": { "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { + "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "color-support": { + "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } }, - "combined-stream": { + "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, - "requires": { + "dependencies": { "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "component-emitter": { + "node_modules/component-emitter": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "dev": true + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "concat-stream": { + "node_modules/concat-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "requires": { + "engines": [ + "node >= 6.0" + ], + "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, - "consola": { + "node_modules/consola": { "version": "2.15.3", "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" }, - "console-control-strings": { + "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, - "content-disposition": { + "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { + "dependencies": { "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" } }, - "content-type": { + "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } }, - "convert-source-map": { + "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "cookie": { + "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==" + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } }, - "cookie-signature": { + "node_modules/cookie-signature": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" }, - "cookiejar": { + "node_modules/cookiejar": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, - "cors": { + "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { + "dependencies": { "object-assign": "^4", "vary": "^1" + }, + "engines": { + "node": ">= 0.10" } }, - "create-jest": { + "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "exit": "^0.1.2", @@ -2237,231 +3143,329 @@ "jest-config": "^29.7.0", "jest-util": "^29.7.0", "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "create-require": { + "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, - "cross-spawn": { + "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "requires": { + "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "debug": { + "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "requires": { + "dependencies": { "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "dedent": { + "node_modules/dedent": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", - "dev": true + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } }, - "deep-is": { + "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "deepmerge": { + "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "delayed-stream": { + "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.4.0" + } }, - "delegates": { + "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, - "depd": { + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } }, - "destroy": { + "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, - "detect-libc": { + "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "engines": { + "node": ">=8" + } }, - "detect-newline": { + "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "dezalgo": { + "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, - "requires": { + "dependencies": { "asap": "^2.0.0", "wrappy": "1" } }, - "diff": { + "node_modules/diff": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.3.1" + } }, - "diff-sequences": { + "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "dotenv": { + "node_modules/dotenv": { "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==" + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } }, - "dotenv-expand": { + "node_modules/dotenv-expand": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", - "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==" + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "engines": { + "node": ">=12" + } }, - "dunder-proto": { + "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "requires": { + "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" } }, - "dynamic-dedupe": { + "node_modules/dynamic-dedupe": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", "dev": true, - "requires": { + "dependencies": { "xtend": "^4.0.0" } }, - "ecdsa-sig-formatter": { + "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { + "dependencies": { "safe-buffer": "^5.0.1" } }, - "ee-first": { + "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, - "electron-to-chromium": { + "node_modules/electron-to-chromium": { "version": "1.5.398", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.398.tgz", "integrity": "sha512-AsvhAxopJGh6museTDMIjn6JpDYOfgu4RLlygomt87MUwBUqTfd/1EiPtx10/LZE8xpTvkP2E9Gafq7lkLtodQ==", "dev": true }, - "emittery": { + "node_modules/emittery": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } }, - "emoji-regex": { + "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "encodeurl": { + "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } }, - "error-ex": { + "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, - "requires": { + "dependencies": { "is-arrayish": "^0.2.1" } }, - "es-define-property": { + "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } }, - "es-errors": { + "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } }, - "es-object-atoms": { + "node_modules/es-object-atoms": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "requires": { + "dependencies": { "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" } }, - "es-set-tostringtag": { + "node_modules/es-set-tostringtag": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, - "requires": { + "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "escalade": { + "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "escape-html": { + "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, - "escape-string-regexp": { + "node_modules/escape-string-regexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "eslint": { + "node_modules/eslint": { "version": "9.39.5", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", "dev": true, - "requires": { + "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", @@ -2497,109 +3501,183 @@ "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true - }, - "ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true } } }, - "eslint-scope": { + "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, - "requires": { + "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "eslint-visitor-keys": { + "node_modules/eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true + "dev": true, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "espree": { + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "requires": { + "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" }, - "dependencies": { - "eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true - } + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "esprima": { + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } }, - "esquery": { + "node_modules/esquery": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, - "requires": { + "dependencies": { "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" } }, - "esrecurse": { + "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "requires": { + "dependencies": { "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" } }, - "estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "dev": true, + "engines": { + "node": ">=4.0" + } }, - "esutils": { + "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "etag": { + "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } }, - "execa": { + "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, - "requires": { + "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", @@ -2609,32 +3687,44 @@ "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "exit": { + "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8.0" + } }, - "expect": { + "node_modules/expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, - "requires": { + "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "express": { + "node_modules/express": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", - "requires": { + "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", @@ -2667,104 +3757,132 @@ "utils-merge": "1.0.1", "vary": "~1.1.2" }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "path-to-regexp": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", - "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==" - } + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "fast-deep-equal": { + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==" + }, + "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "fast-json-stable-stringify": { + "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "fast-levenshtein": { + "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "fast-safe-stringify": { + "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, - "fb-watchman": { + "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, - "requires": { + "dependencies": { "bser": "2.1.1" } }, - "fdir": { + "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } }, - "fflate": { + "node_modules/fflate": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==" }, - "file-entry-cache": { + "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, - "requires": { + "dependencies": { "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" } }, - "file-type": { + "node_modules/file-type": { "version": "20.4.1", "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.4.1.tgz", "integrity": "sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==", - "requires": { + "dependencies": { "@tokenizer/inflate": "^0.2.6", "strtok3": "^10.2.0", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" } }, - "fill-range": { + "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "requires": { + "dependencies": { "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "finalhandler": { + "node_modules/finalhandler": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "requires": { + "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", @@ -2773,122 +3891,162 @@ "statuses": "~2.0.2", "unpipe": "~1.0.0" }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } + "engines": { + "node": ">= 0.8" } }, - "find-up": { + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "requires": { + "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "flat-cache": { + "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, - "requires": { + "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" } }, - "flatted": { + "node_modules/flatted": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true }, - "form-data": { + "node_modules/form-data": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "dev": true, - "requires": { + "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.4", "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" } }, - "formidable": { + "node_modules/formidable": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, - "requires": { + "dependencies": { "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", "once": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" } }, - "forwarded": { + "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } }, - "fresh": { + "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } }, - "fs-minipass": { + "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "requires": { + "dependencies": { "minipass": "^3.0.0" }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - } + "engines": { + "node": ">= 8" } }, - "fs.realpath": { + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, - "fsevents": { + "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "optional": true + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, - "function-bind": { + "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "gauge": { + "node_modules/gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "requires": { + "deprecated": "This package is no longer supported.", + "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.2", "console-control-strings": "^1.0.0", @@ -2898,25 +4056,34 @@ "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" } }, - "gensync": { + "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "get-caller-file": { + "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } }, - "get-intrinsic": { + "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "requires": { + "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", @@ -2927,395 +4094,586 @@ "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "get-package-type": { + "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8.0.0" + } }, - "get-proto": { + "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "requires": { + "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" } }, - "get-stream": { + "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "glob": { + "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "glob-parent": { + "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "requires": { + "dependencies": { "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" } }, - "globals": { + "node_modules/globals": { "version": "16.5.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "gopd": { + "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "graceful-fs": { + "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "handlebars": { + "node_modules/handlebars": { "version": "4.7.9", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, - "requires": { + "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", - "uglify-js": "^3.1.4", "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "has-flag": { + "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } }, - "has-symbols": { + "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "has-tostringtag": { + "node_modules/has-tostringtag": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, - "requires": { + "dependencies": { "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "has-unicode": { + "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, - "hasown": { + "node_modules/hasown": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "requires": { + "dependencies": { "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "html-escaper": { + "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "http-errors": { + "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "requires": { + "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "https-proxy-agent": { + "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "requires": { + "dependencies": { "agent-base": "6", "debug": "4" + }, + "engines": { + "node": ">= 6" } }, - "human-signals": { + "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true + "dev": true, + "engines": { + "node": ">=10.17.0" + } }, - "iconv-lite": { + "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { + "dependencies": { "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" } }, - "ieee754": { + "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "ignore": { + "node_modules/ignore": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", - "dev": true + "dev": true, + "engines": { + "node": ">= 4" + } }, - "import-fresh": { + "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "requires": { + "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "import-local": { + "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, - "requires": { + "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "imurmurhash": { + "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8.19" + } }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ipaddr.js": { + "node_modules/ioredis": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.11.1.tgz", + "integrity": "sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "1.10.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "redis-parser": "3.0.0", + "standard-as-callback": "2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } }, - "is-arrayish": { + "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, - "is-binary-path": { + "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "requires": { + "dependencies": { "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "is-core-module": { + "node_modules/is-core-module": { "version": "2.16.2", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dev": true, - "requires": { + "dependencies": { "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-extglob": { + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-fullwidth-code-point": { + "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } }, - "is-generator-fn": { + "node_modules/is-generator-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "is-glob": { + "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "requires": { + "dependencies": { "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-number": { + "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.12.0" + } }, - "is-stream": { + "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "isexe": { + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "istanbul-lib-coverage": { + "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "istanbul-lib-instrument": { + "node_modules/istanbul-lib-instrument": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, - "requires": { + "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" } }, - "istanbul-lib-report": { + "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "requires": { + "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, - "dependencies": { - "make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "requires": { - "semver": "^7.5.3" - } - } + "engines": { + "node": ">=10" } }, - "istanbul-lib-source-maps": { + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, - "requires": { + "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" } }, - "istanbul-reports": { + "node_modules/istanbul-reports": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, - "requires": { + "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "iterare": { + "node_modules/iterare": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", - "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==" + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "engines": { + "node": ">=6" + } }, - "jest": { + "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, - "requires": { + "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", "import-local": "^3.0.2", "jest-cli": "^29.7.0" }, - "dependencies": { - "jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - } + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "jest-changed-files": { + "node_modules/jest-changed-files": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, - "requires": { + "dependencies": { "execa": "^5.0.0", "jest-util": "^29.7.0", "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-circus": { + "node_modules/jest-circus": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, - "requires": { + "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -3336,14 +4694,17 @@ "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-config": { + "node_modules/jest-config": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, - "requires": { + "dependencies": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^29.7.0", "@jest/types": "^29.6.3", @@ -3366,110 +4727,151 @@ "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "jest-diff": { + "node_modules/jest-diff": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-docblock": { + "node_modules/jest-docblock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, - "requires": { + "dependencies": { "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-each": { + "node_modules/jest-each": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "jest-get-type": "^29.6.3", "jest-util": "^29.7.0", "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-environment-node": { + "node_modules/jest-environment-node": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, - "requires": { + "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "jest-mock": "^29.7.0", "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-get-type": { + "node_modules/jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "jest-haste-map": { + "node_modules/jest-haste-map": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "jest-leak-detector": { + "node_modules/jest-leak-detector": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, - "requires": { + "dependencies": { "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-matcher-utils": { + "node_modules/jest-matcher-utils": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-message-util": { + "node_modules/jest-message-util": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", @@ -3479,37 +4881,57 @@ "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-mock": { + "node_modules/jest-mock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-pnp-resolver": { + "node_modules/jest-pnp-resolver": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } }, - "jest-regex-util": { + "node_modules/jest-regex-util": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "jest-resolve": { + "node_modules/jest-resolve": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", @@ -3519,24 +4941,30 @@ "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-resolve-dependencies": { + "node_modules/jest-resolve-dependencies": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, - "requires": { + "dependencies": { "jest-regex-util": "^29.6.3", "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-runner": { + "node_modules/jest-runner": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, - "requires": { + "dependencies": { "@jest/console": "^29.7.0", "@jest/environment": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -3558,14 +4986,17 @@ "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-runtime": { + "node_modules/jest-runtime": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, - "requires": { + "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", "@jest/globals": "^29.7.0", @@ -3588,14 +5019,17 @@ "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-snapshot": { + "node_modules/jest-snapshot": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, - "requires": { + "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", "@babel/plugin-syntax-jsx": "^7.7.2", @@ -3616,28 +5050,47 @@ "natural-compare": "^1.4.0", "pretty-format": "^29.7.0", "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-util": { + "node_modules/jest-util": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-validate": { + "node_modules/jest-util/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, - "requires": { + "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", @@ -3645,21 +5098,28 @@ "leven": "^3.1.0", "pretty-format": "^29.7.0" }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - } + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-watcher": { + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, - "requires": { + "dependencies": { "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", @@ -3668,86 +5128,153 @@ "emittery": "^0.13.1", "jest-util": "^29.7.0", "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "jest-worker": { + "node_modules/jest-worker": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest/node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "js-tokens": { + "node_modules/jose": { + "version": "6.2.6", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.6.tgz", + "integrity": "sha512-HwMtbJjMw8rC8dUTwCNilHJD+fxTeKM3JV1eprSmTjS41qwXSSt6exJXgyPK1QOu0jB9eDYLESRDkB3qaT3jnw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, - "js-yaml": { + "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { + "dependencies": { "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "jsesc": { + "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } }, - "json-buffer": { + "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "json-parse-even-better-errors": { + "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "json-schema-traverse": { + "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "json-stable-stringify-without-jsonify": { + "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "json5": { + "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } }, - "jsonwebtoken": { + "node_modules/jsonwebtoken": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", - "requires": { + "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", @@ -3759,312 +5286,393 @@ "ms": "^2.1.1", "semver": "^7.5.4" }, - "dependencies": { - "jwa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", - "requires": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", - "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", - "requires": { - "jwa": "^2.0.1", - "safe-buffer": "^5.0.1" - } - } + "engines": { + "node": ">=12", + "npm": ">=6" } }, - "jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", - "requires": { + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, - "jws": { + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", - "requires": { + "dependencies": { "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, - "keyv": { + "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "requires": { + "dependencies": { "json-buffer": "3.0.1" } }, - "kleur": { + "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "leven": { + "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "levn": { + "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "requires": { + "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "libphonenumber-js": { + "node_modules/libphonenumber-js": { "version": "1.13.10", "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.13.10.tgz", "integrity": "sha512-xJxrdqvbl2rtn2MaUJrUejz8J7/uZNC0V77oks2LxYrO/+ZtVpRmz+fEQMuu6VusnEB1fmpByiLS1WXecOnAnw==" }, - "lines-and-columns": { + "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "locate-path": { + "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "requires": { + "dependencies": { "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "lodash": { + "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.includes": { + "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, - "lodash.isboolean": { + "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, - "lodash.isinteger": { + "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" }, - "lodash.isnumber": { + "node_modules/lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" }, - "lodash.isplainobject": { + "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" }, - "lodash.isstring": { + "node_modules/lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" }, - "lodash.memoize": { + "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, - "lodash.merge": { + "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "lodash.once": { + "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, - "lru-cache": { + "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "requires": { - "yallist": "^3.0.2" - }, "dependencies": { - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } + "yallist": "^3.0.2" } }, - "make-dir": { + "node_modules/lru-cache/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { + "dependencies": { "semver": "^6.0.0" }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "make-error": { + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "makeerror": { + "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, - "requires": { + "dependencies": { "tmpl": "1.0.5" } }, - "math-intrinsics": { + "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } }, - "media-typer": { + "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } }, - "merge-descriptors": { + "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "merge-stream": { + "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "methods": { + "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } }, - "micromatch": { + "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "requires": { + "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "mime": { + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } }, - "mime-db": { + "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { + "node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { + "dependencies": { "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "mimic-fn": { + "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "minimatch": { + "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "requires": { + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { + "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "minipass": { + "node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } }, - "minizlib": { + "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "requires": { + "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - } + "engines": { + "node": ">= 8" } }, - "mkdirp": { + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "requires": { + "dependencies": { "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "ms": { + "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "multer": { + "node_modules/multer": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", - "requires": { + "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", @@ -4072,262 +5680,394 @@ "object-assign": "^4.1.1", "type-is": "^1.6.18", "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" } }, - "natural-compare": { + "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "negotiator": { + "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } }, - "neo-async": { + "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node-addon-api": { + "node_modules/node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" }, - "node-fetch": { + "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "requires": { + "dependencies": { "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node-int64": { + "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, - "node-releases": { + "node_modules/node-releases": { "version": "2.0.51", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=18" + } }, - "nopt": { + "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "requires": { + "dependencies": { "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" } }, - "normalize-path": { + "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "npm-run-path": { + "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "requires": { + "dependencies": { "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "npmlog": { + "node_modules/npmlog": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "requires": { + "deprecated": "This package is no longer supported.", + "dependencies": { "are-we-there-yet": "^2.0.0", "console-control-strings": "^1.1.0", "gauge": "^3.0.0", "set-blocking": "^2.0.0" } }, - "object-assign": { + "node_modules/oauth4webapi": { + "version": "3.8.6", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.6.tgz", + "integrity": "sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } }, - "object-inspect": { + "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "on-exit-leak-free": { + "node_modules/on-exit-leak-free": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", - "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==" + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "engines": { + "node": ">=14.0.0" + } }, - "on-finished": { + "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { + "dependencies": { "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { + "dependencies": { "wrappy": "1" } }, - "onetime": { + "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "requires": { + "dependencies": { "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "optionator": { + "node_modules/openid-client": { + "version": "6.8.4", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.4.tgz", + "integrity": "sha512-QSw0BA08piujetEwfZsHoTrDpMEha7GDZDicQqVwX4u0ChCjefvjDB++TZ8BTg76UpwhzIQgdvvfgfl3HpCSAw==", + "license": "MIT", + "dependencies": { + "jose": "^6.2.2", + "oauth4webapi": "^3.8.5" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "requires": { + "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" } }, - "p-limit": { + "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "requires": { + "dependencies": { "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-locate": { + "node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "requires": { + "dependencies": { "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-try": { + "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "parent-module": { + "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "requires": { + "dependencies": { "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "parse-json": { + "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "parseurl": { + "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } }, - "passport": { + "node_modules/passport": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", - "requires": { + "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" } }, - "passport-jwt": { + "node_modules/passport-jwt": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", - "requires": { + "dependencies": { "jsonwebtoken": "^9.0.0", "passport-strategy": "^1.0.0" } }, - "passport-strategy": { + "node_modules/passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==" + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } }, - "path-exists": { + "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } }, - "path-key": { + "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "path-parse": { + "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "path-to-regexp": { + "node_modules/path-to-regexp": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==" }, - "pause": { + "node_modules/pause": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" }, - "picocolors": { + "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, - "picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "pino": { + "node_modules/pino": { "version": "9.14.0", "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", - "requires": { + "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", @@ -4339,304 +6079,537 @@ "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" } }, - "pino-abstract-transport": { + "node_modules/pino-abstract-transport": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", - "requires": { + "dependencies": { "split2": "^4.0.0" } }, - "pino-std-serializers": { + "node_modules/pino-std-serializers": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==" }, - "pirates": { + "node_modules/pirates": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true + "dev": true, + "engines": { + "node": ">= 6" + } }, - "pkg-dir": { + "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, - "requires": { + "dependencies": { "find-up": "^4.0.0" }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } + "engines": { + "node": ">=8" } }, - "prelude-ls": { + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/playwright": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz", + "integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz", + "integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8.0" + } }, - "prettier": { + "node_modules/prettier": { "version": "3.9.6", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", - "dev": true + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } }, - "pretty-format": { + "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "requires": { + "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "prisma": { + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prisma": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", - "dev": true, - "requires": { - "@prisma/engines": "5.22.0", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/engines": "5.22.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + }, + "optionalDependencies": { "fsevents": "2.3.3" } }, - "process-warning": { + "node_modules/process-warning": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.1.0.tgz", - "integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==" + "integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] }, - "prompts": { + "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, - "requires": { + "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" } }, - "proxy-addr": { + "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { + "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" } }, - "punycode": { + "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "pure-rand": { + "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] }, - "qs": { + "node_modules/qs": { "version": "6.14.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", - "requires": { + "dependencies": { "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "quick-format-unescaped": { + "node_modules/quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" }, - "range-parser": { + "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } }, - "raw-body": { + "node_modules/raw-body": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "requires": { + "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "react-is": { + "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, - "readable-stream": { + "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "requires": { + "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "readdirp": { + "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "requires": { + "dependencies": { "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "real-require": { + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/real-require": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==" + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "engines": { + "node": ">= 12.13.0" + } }, - "reflect-metadata": { + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "resolve": { + "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "dev": true, - "requires": { + "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "resolve-cwd": { + "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, - "requires": { + "dependencies": { "resolve-from": "^5.0.0" }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } + "engines": { + "node": ">=8" } }, - "resolve-from": { + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "resolve.exports": { + "node_modules/resolve.exports": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "rimraf": { + "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dependencies": { "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "rxjs": { + "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "requires": { + "dependencies": { "tslib": "^2.1.0" } }, - "safe-buffer": { + "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "safe-stable-stringify": { + "node_modules/safe-stable-stringify": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==" + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "engines": { + "node": ">=10" + } }, - "safer-buffer": { + "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "semver": { + "node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==" + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "send": { + "node_modules/send": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "requires": { + "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -4651,243 +6624,327 @@ "range-parser": "~1.2.1", "statuses": "~2.0.2" }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - } + "engines": { + "node": ">= 0.8.0" } }, - "serve-static": { + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/serve-static": { "version": "1.16.3", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "requires": { + "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "set-blocking": { + "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, - "setprototypeof": { + "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, - "shebang-command": { + "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { + "dependencies": { "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "shebang-regex": { + "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "side-channel": { + "node_modules/side-channel": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", - "requires": { + "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "side-channel-list": { + "node_modules/side-channel-list": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "requires": { + "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "side-channel-map": { + "node_modules/side-channel-map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "requires": { + "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "side-channel-weakmap": { + "node_modules/side-channel-weakmap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "requires": { + "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "signal-exit": { + "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "sisteransi": { + "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, - "slash": { + "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "sonic-boom": { + "node_modules/sonic-boom": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", - "requires": { + "dependencies": { "atomic-sleep": "^1.0.0" } }, - "source-map": { + "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "source-map-support": { + "node_modules/source-map-support": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, - "requires": { + "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, - "split2": { + "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } }, - "sprintf-js": { + "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "stack-utils": { + "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, - "requires": { + "dependencies": { "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" } }, - "statuses": { + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, + "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==" + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "engines": { + "node": ">= 0.8" + } }, - "streamsearch": { + "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } }, - "string-length": { + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, - "requires": { + "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" } }, - "string-width": { + "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { + "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { + "dependencies": { "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-bom": { + "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "strip-final-newline": { + "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "strip-json-comments": { + "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "strtok3": { + "node_modules/strtok3": { "version": "10.3.5", "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", - "requires": { + "dependencies": { "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "superagent": { + "node_modules/superagent": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==", "dev": true, - "requires": { + "dependencies": { "component-emitter": "^1.3.1", "cookiejar": "^2.1.4", "debug": "^4.3.7", @@ -4898,66 +6955,93 @@ "mime": "2.6.0", "qs": "^6.14.1" }, - "dependencies": { - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true - } + "engines": { + "node": ">=14.18.0" } }, - "supertest": { + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/supertest": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", "dev": true, - "requires": { + "dependencies": { "cookie-signature": "^1.2.2", "methods": "^1.1.2", "superagent": "^10.3.0" }, - "dependencies": { - "cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "dev": true - } + "engines": { + "node": ">=14.18.0" } }, - "supports-color": { + "node_modules/supertest/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { + "dependencies": { "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "supports-preserve-symlinks-flag": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "swagger-ui-dist": { + "node_modules/swagger-ui-dist": { "version": "5.17.14", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz", "integrity": "sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==" }, - "swagger-ui-express": { + "node_modules/swagger-ui-express": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", - "requires": { + "dependencies": { "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" } }, - "tar": { + "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "requires": { + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", @@ -4965,104 +7049,134 @@ "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - } + "engines": { + "node": ">=10" } }, - "test-exclude": { + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, - "requires": { + "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" } }, - "thread-stream": { + "node_modules/thread-stream": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.2.0.tgz", "integrity": "sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==", - "requires": { + "dependencies": { "real-require": "^0.2.0" } }, - "tinyglobby": { + "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, - "requires": { + "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" }, - "dependencies": { - "picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", - "dev": true - } + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "tmpl": { + "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "to-regex-range": { + "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "requires": { + "dependencies": { "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "toidentifier": { + "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } }, - "token-types": { + "node_modules/token-types": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", - "requires": { + "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" } }, - "tr46": { + "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, - "tree-kill": { + "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true + "dev": true, + "bin": { + "tree-kill": "cli.js" + } }, - "ts-api-utils": { + "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "dev": true + "dev": true, + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } }, - "ts-jest": { + "node_modules/ts-jest": { "version": "29.4.12", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.12.tgz", "integrity": "sha512-Ov6ClY53Fflh6BGAnY2DlTq1hYDrTycz2PVTXBWFW2CU+9zrEqAp9fWdGXl42EXO5RLSFAcAZ2JFKbP+zBTFfw==", "dev": true, - "requires": { + "dependencies": { "bs-logger": "^0.2.6", "fast-json-stable-stringify": "^2.1.0", "handlebars": "^4.7.9", @@ -5073,21 +7187,60 @@ "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, - "dependencies": { - "type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true } } }, - "ts-node": { + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, - "requires": { + "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", @@ -5101,14 +7254,36 @@ "make-error": "^1.1.1", "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, - "ts-node-dev": { + "node_modules/ts-node-dev": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", "dev": true, - "requires": { + "dependencies": { "chokidar": "^3.5.1", "dynamic-dedupe": "^0.3.0", "minimist": "^1.2.6", @@ -5120,287 +7295,418 @@ "ts-node": "^10.4.0", "tsconfig": "^7.0.0" }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } + "bin": { + "ts-node-dev": "lib/bin.js", + "tsnd": "lib/bin.js" + }, + "engines": { + "node": ">=0.8.0" + }, + "peerDependencies": { + "node-notifier": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "tsconfig": { + "node_modules/ts-node-dev/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-node-dev/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/tsconfig": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", "dev": true, - "requires": { + "dependencies": { "@types/strip-bom": "^3.0.0", "@types/strip-json-comments": "0.0.30", "strip-bom": "^3.0.0", "strip-json-comments": "^2.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true - } } }, - "tslib": { + "node_modules/tsconfig/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tsconfig/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, - "type-check": { + "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "requires": { + "dependencies": { "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "type-detect": { + "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "type-fest": { + "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "type-is": { + "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { + "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" } }, - "typedarray": { + "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, - "typescript": { + "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } }, - "uglify-js": { + "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, - "optional": true + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } }, - "uid": { + "node_modules/uid": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", - "requires": { + "dependencies": { "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" } }, - "uint8array-extras": { + "node_modules/uint8array-extras": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", - "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==" + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "undici-types": { + "node_modules/undici-types": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==" }, - "unpipe": { + "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } }, - "update-browserslist-db": { + "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, - "requires": { + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "uri-js": { + "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { + "dependencies": { "punycode": "^2.1.0" } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "utils-merge": { + "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } }, - "uuid": { + "node_modules/uuid": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", - "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==" + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/esm/bin/uuid" + } }, - "v8-compile-cache-lib": { + "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, - "v8-to-istanbul": { + "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" } }, - "validator": { + "node_modules/validator": { "version": "13.15.35", "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", - "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==" + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "engines": { + "node": ">= 0.10" + } }, - "vary": { + "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } }, - "walker": { + "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, - "requires": { + "dependencies": { "makeerror": "1.0.12" } }, - "webidl-conversions": { + "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, - "whatwg-url": { + "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { + "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, - "which": { + "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "wide-align": { + "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "requires": { + "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "word-wrap": { + "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "wordwrap": { + "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, - "wrap-ansi": { + "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "write-file-atomic": { + "node_modules/write-file-atomic": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, - "requires": { + "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "xtend": { + "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "yallist": { + "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "yargs": { + "node_modules/yargs": { "version": "17.7.3", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", "dev": true, - "requires": { + "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", @@ -5408,25 +7714,40 @@ "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true + "dev": true, + "engines": { + "node": ">=12" + } }, - "yn": { + "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "yocto-queue": { + "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 48b8469..2cc6cd2 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "scripts": { "start": "node dist/main.js", "start:dev": "ts-node-dev --respawn --pretty --transpile-only src/main.ts", + "start:debug": "nest start --debug --watch", "build": "tsc -p tsconfig.json", "lint": "eslint \"src/**/*.ts\"", "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"", @@ -37,6 +38,8 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.2", "dotenv": "^16.6.1", + "jose": "^6.2.6", + "openid-client": "^6.5.0", "jsonwebtoken": "^9.0.2", "passport": "^0.7.0", "passport-jwt": "^4.0.1", @@ -47,6 +50,9 @@ "uuid": "^11.1.0" }, "devDependencies": { + "@eslint/js": "^9.21.0", + "@nestjs/testing": "^10.4.22", + "@playwright/test": "^1.62.1", "@types/bcrypt": "^5.0.2", "@types/jest": "^29.5.14", "@types/node": "^22.13.10", @@ -56,7 +62,6 @@ "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "eslint": "^9.21.0", - "@eslint/js": "^9.21.0", "globals": "^16.0.0", "jest": "^29.7.0", "prettier": "^3.5.3", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..5b753de --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,17 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + // only run Playwright tests in the integration folder to avoid running Jest unit tests + testDir: './tests/integration', + timeout: 30 * 1000, + expect: { + timeout: 5000, + }, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: [["list"], ["./tests/reporter/custom-reporter.js"]], + use: { + baseURL: process.env.BASE_URL || 'http://localhost:3000', + }, +}); diff --git a/postman/RayLab.postman_collection.json b/postman/RayLab.postman_collection.json new file mode 100644 index 0000000..f94df2a --- /dev/null +++ b/postman/RayLab.postman_collection.json @@ -0,0 +1,58 @@ +{ + "info": { + "_postman_id": "e1e605cc-a2c6-4410-a7aa-4692b594d42d", + "name": "RayLab", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "45313893" + }, + "item": [ + { + "name": "User", + "item": [ + { + "name": "Create User", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Authorization", + "value": "Bearer ", + "description": "Bearer ", + "type": "text", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"username\": \"rayyan\",\r\n \"email\": \"rayyan@example.com\",\r\n \"password\": \"Password123!\",\r\n \"fullName\": \"Rayyan\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:3000/api/v1/users", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "3000", + "path": [ + "api", + "v1", + "users" + ] + } + }, + "response": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/prisma/migrations/20260730165554_permission/migration.sql b/prisma/migrations/20260730165554_permission/migration.sql new file mode 100644 index 0000000..23e8e28 --- /dev/null +++ b/prisma/migrations/20260730165554_permission/migration.sql @@ -0,0 +1,93 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL, + "username" TEXT NOT NULL, + "email" TEXT NOT NULL, + "password" TEXT NOT NULL, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Role" ( + "id" TEXT NOT NULL, + "code" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Role_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Permission" ( + "id" TEXT NOT NULL, + "code" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Permission_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "UserRole" ( + "userId" TEXT NOT NULL, + "roleId" TEXT NOT NULL, + "assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "UserRole_pkey" PRIMARY KEY ("userId","roleId") +); + +-- CreateTable +CREATE TABLE "RolePermission" ( + "roleId" TEXT NOT NULL, + "permissionId" TEXT NOT NULL, + "assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "RolePermission_pkey" PRIMARY KEY ("roleId","permissionId") +); + +-- CreateTable +CREATE TABLE "UserPermission" ( + "userId" TEXT NOT NULL, + "permissionId" TEXT NOT NULL, + "assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "UserPermission_pkey" PRIMARY KEY ("userId","permissionId") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Role_code_key" ON "Role"("code"); + +-- CreateIndex +CREATE UNIQUE INDEX "Permission_code_key" ON "Permission"("code"); + +-- AddForeignKey +ALTER TABLE "UserRole" ADD CONSTRAINT "UserRole_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UserRole" ADD CONSTRAINT "UserRole_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "Role"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RolePermission" ADD CONSTRAINT "RolePermission_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "Role"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RolePermission" ADD CONSTRAINT "RolePermission_permissionId_fkey" FOREIGN KEY ("permissionId") REFERENCES "Permission"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UserPermission" ADD CONSTRAINT "UserPermission_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UserPermission" ADD CONSTRAINT "UserPermission_permissionId_fkey" FOREIGN KEY ("permissionId") REFERENCES "Permission"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260801084138_change/migration.sql b/prisma/migrations/20260801084138_change/migration.sql new file mode 100644 index 0000000..f73805a --- /dev/null +++ b/prisma/migrations/20260801084138_change/migration.sql @@ -0,0 +1,16 @@ +/* + Warnings: + + - Added the required column `assignedBy` to the `RolePermission` table without a default value. This is not possible if the table is not empty. + - Added the required column `assignedBy` to the `UserPermission` table without a default value. This is not possible if the table is not empty. + - Added the required column `assignedBy` to the `UserRole` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "RolePermission" ADD COLUMN "assignedBy" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "UserPermission" ADD COLUMN "assignedBy" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "UserRole" ADD COLUMN "assignedBy" TEXT NOT NULL; diff --git a/prisma/migrations/20260801101438_add_role_is_default/migration.sql b/prisma/migrations/20260801101438_add_role_is_default/migration.sql new file mode 100644 index 0000000..dabc294 --- /dev/null +++ b/prisma/migrations/20260801101438_add_role_is_default/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Role" ADD COLUMN "isDefault" BOOLEAN NOT NULL DEFAULT false; diff --git a/prisma/migrations/20260801101841_add_authentik_id/migration.sql b/prisma/migrations/20260801101841_add_authentik_id/migration.sql new file mode 100644 index 0000000..aa49398 --- /dev/null +++ b/prisma/migrations/20260801101841_add_authentik_id/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - A unique constraint covering the columns `[authentikId]` on the table `User` will be added. If there are existing duplicate values, this will fail. + +*/ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "authentikId" TEXT; + +-- CreateIndex +CREATE UNIQUE INDEX "User_authentikId_key" ON "User"("authentikId"); diff --git a/prisma/migrations/20260801104311_add_deleted_at/migration.sql b/prisma/migrations/20260801104311_add_deleted_at/migration.sql new file mode 100644 index 0000000..a9d12c0 --- /dev/null +++ b/prisma/migrations/20260801104311_add_deleted_at/migration.sql @@ -0,0 +1,10 @@ +/* + Warnings: + + - You are about to drop the column `password` on the `User` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "User" DROP COLUMN "password", +ADD COLUMN "deletedAt" TIMESTAMP(3), +ADD COLUMN "lastSeenAt" TIMESTAMP(3); diff --git a/prisma/migrations/20260801132311_add_authentik_fields/migration.sql b/prisma/migrations/20260801132311_add_authentik_fields/migration.sql new file mode 100644 index 0000000..32ee861 --- /dev/null +++ b/prisma/migrations/20260801132311_add_authentik_fields/migration.sql @@ -0,0 +1,15 @@ +/* + Warnings: + + - A unique constraint covering the columns `[authentikUserId]` on the table `User` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[authentikSubject]` on the table `User` will be added. If there are existing duplicate values, this will fail. + +*/ +-- AlterTable +ALTER TABLE "User" ALTER COLUMN "lastSyncedAt" SET DATA TYPE TIMESTAMP(3); + +-- CreateIndex +CREATE UNIQUE INDEX "User_authentikUserId_key" ON "User"("authentikUserId"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_authentikSubject_key" ON "User"("authentikSubject"); diff --git a/prisma/migrations/20260802_add_authentik_fields/migration.sql b/prisma/migrations/20260802_add_authentik_fields/migration.sql new file mode 100644 index 0000000..fc9ffb9 --- /dev/null +++ b/prisma/migrations/20260802_add_authentik_fields/migration.sql @@ -0,0 +1,17 @@ +-- Migration: add_authentik_fields + +ALTER TABLE "User" +ADD COLUMN IF NOT EXISTS "authentikUserId" TEXT; + +ALTER TABLE "User" +ADD COLUMN IF NOT EXISTS "authentikSubject" TEXT; + +ALTER TABLE "User" +ADD COLUMN IF NOT EXISTS "lastSyncedAt" TIMESTAMP; + +ALTER TABLE "User" +ADD COLUMN IF NOT EXISTS "syncStatus" TEXT; + +-- Optional: add unique constraints if desired +-- DO NOT add unique constraints without verifying existing data +-- ALTER TABLE "User" ADD CONSTRAINT "User_authentikUserId_key" UNIQUE ("authentikUserId"); diff --git a/prisma/migrations/20260803_add_storage/migration.sql b/prisma/migrations/20260803_add_storage/migration.sql new file mode 100644 index 0000000..d7df7a3 --- /dev/null +++ b/prisma/migrations/20260803_add_storage/migration.sql @@ -0,0 +1,7 @@ +-- Migration: add_storage + +ALTER TABLE "User" + ADD COLUMN IF NOT EXISTS "storageQuota" bigint NOT NULL DEFAULT 10737418240; + +ALTER TABLE "User" + ADD COLUMN IF NOT EXISTS "storageUsed" bigint NOT NULL DEFAULT 0; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..fbffa92 --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 19f49c7..a6c5140 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -11,13 +11,99 @@ datasource db { } model User { - id String @id @default(uuid()) - name String - email String @unique - password String - status String @default("ACTIVE") - metadata Json? - created_at DateTime @default(now()) - updated_at DateTime @updatedAt - deleted_at DateTime? + id String @id @default(uuid()) + + authentikId String? @unique + authentikUserId String? @unique + authentikSubject String? @unique + + username String @unique + email String @unique + + + isActive Boolean @default(true) + + // storage (in bytes) + storageQuota BigInt @default(10737418240) + storageUsed BigInt @default(0) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime? + lastSeenAt DateTime? + lastSyncedAt DateTime? + syncStatus String? + + roles UserRole[] + permissions UserPermission[] + } + +model Role { + id String @id @default(uuid()) + + code String @unique + name String + description String? + + isDefault Boolean @default(false) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + users UserRole[] + permissions RolePermission[] +} + +model Permission { + id String @id @default(uuid()) + + code String @unique + name String + description String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + roles RolePermission[] + users UserPermission[] +} + +model UserRole { + userId String + roleId String + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) + + assignedAt DateTime @default(now()) + assignedBy String + + @@id([userId, roleId]) +} + +model RolePermission { + roleId String + permissionId String + + role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) + permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) + + assignedAt DateTime @default(now()) + assignedBy String + + @@id([roleId, permissionId]) +} + +model UserPermission { + userId String + permissionId String + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) + + assignedAt DateTime @default(now()) + assignedBy String + + @@id([userId, permissionId]) +} \ No newline at end of file diff --git a/src/app.module.ts b/src/app.module.ts index 01cb319..e6e9b1a 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { IdentityModule } from './modules/identity/identity.module'; +import { AuthModule } from './modules/auth/auth.module'; @Module({ imports: [ @@ -10,6 +11,7 @@ import { IdentityModule } from './modules/identity/identity.module'; }), IdentityModule, + AuthModule, ], }) export class AppModule {} \ No newline at end of file diff --git a/src/common/constants/permission.constants.ts b/src/common/constants/permission.constants.ts new file mode 100644 index 0000000..29c7547 --- /dev/null +++ b/src/common/constants/permission.constants.ts @@ -0,0 +1,16 @@ +export const PermissionType = { + USER_CREATE: 'USER_CREATE', + USER_READ: 'USER_READ', + USER_UPDATE: 'USER_UPDATE', + USER_DELETE: 'USER_DELETE', + + ROLE_CREATE: 'ROLE_CREATE', + ROLE_READ: 'ROLE_READ', + ROLE_UPDATE: 'ROLE_UPDATE', + ROLE_DELETE: 'ROLE_DELETE', + + PERMISSION_CREATE: 'PERMISSION_CREATE', + PERMISSION_READ: 'PERMISSION_READ', + PERMISSION_UPDATE: 'PERMISSION_UPDATE', + PERMISSION_DELETE: 'PERMISSION_DELETE', +} as const; \ No newline at end of file diff --git a/src/core/auth/guards/jwt-auth.guard.ts b/src/core/auth/guards/jwt-auth.guard.ts index d29b625..7b657bb 100644 --- a/src/core/auth/guards/jwt-auth.guard.ts +++ b/src/core/auth/guards/jwt-auth.guard.ts @@ -4,17 +4,23 @@ import { ExecutionContext, UnauthorizedException, } from '@nestjs/common'; -import { JwtService } from '@nestjs/jwt'; import { Request } from 'express'; +import { createRemoteJWKSet, jwtVerify } from 'jose'; +import { IdentityData } from '../interfaces/identity-data'; +import * as jwt from 'jsonwebtoken'; +/** + * JwtAuthGuard verifies JWTs issued by the external Identity Provider (Authentik) + * using JWKS (RS256). It also accepts internal RayLab JWTs signed with a local secret. + */ @Injectable() export class JwtAuthGuard implements CanActivate { - constructor( - private readonly jwtService: JwtService, - ) {} + private jwks: ReturnType | null = null; + + constructor() {} async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); + const request = context.switchToHttp().getRequest(); const authHeader = request.headers.authorization; @@ -28,13 +34,51 @@ export class JwtAuthGuard implements CanActivate { throw new UnauthorizedException('Invalid authorization header.'); } + const jwksUri = process.env.AUTHENTIK_JWKS_URI; + + // First try verifying with external JWKS (Authentik) + if (jwksUri) { + try { + if (!this.jwks) this.jwks = createRemoteJWKSet(new URL(jwksUri)); + + const { payload } = await jwtVerify(token, this.jwks, { + issuer: process.env.AUTHENTIK_ISSUER, + audience: process.env.AUTHENTIK_AUDIENCE, + }); + + const identity = new IdentityData( + payload.sub as string, + (payload as any).preferred_username as string | undefined, + (payload as any).email as string | undefined, + payload as Record, + ); + + request.identity = identity; + return true; + } catch (err) { + // ignore and try internal verification + } + } + + // Fallback: verify with internal symmetric secret + const secret = process.env.RAYLAB_JWT_SECRET; + if (!secret) { + throw new UnauthorizedException('Invalid or expired token.'); + } + try { - const payload = await this.jwtService.verifyAsync(token); + const payload = jwt.verify(token, secret) as any; - request['user'] = payload; + const identity = new IdentityData( + payload.sub as string, + payload.preferred_username as string | undefined, + payload.email as string | undefined, + payload as Record, + ); + request.identity = identity; return true; - } catch { + } catch (err: any) { throw new UnauthorizedException('Invalid or expired token.'); } } diff --git a/src/core/auth/interfaces/authenticated-request.interface.ts b/src/core/auth/interfaces/authenticated-request.interface.ts index 440f68b..03019a1 100644 --- a/src/core/auth/interfaces/authenticated-request.interface.ts +++ b/src/core/auth/interfaces/authenticated-request.interface.ts @@ -1,12 +1,12 @@ import { Request } from 'express'; - -export interface JwtPayload { - sub: string; - email: string; - role: string; - permissions: string[]; -} +import { UserData } from '../../../modules/identity/domain/entities/user.entity'; +import { IdentityData } from './identity-data'; export interface AuthenticatedRequest extends Request { - user: JwtPayload; + // Identity comes from the external Identity Provider (Authentik) + // JwtAuthGuard must set request.identity = payload + identity?: IdentityData; + + // After CurrentUserGuard resolves the user from repository, it must set request.currentUser = User Domain + currentUser?: UserData; } \ No newline at end of file diff --git a/src/core/auth/interfaces/identity-data.ts b/src/core/auth/interfaces/identity-data.ts new file mode 100644 index 0000000..640aca7 --- /dev/null +++ b/src/core/auth/interfaces/identity-data.ts @@ -0,0 +1,8 @@ +export class IdentityData { + constructor( + public readonly sub: string, + public readonly preferred_username?: string, + public readonly email?: string, + public readonly claims?: Record, + ) {} +} diff --git a/src/main.ts b/src/main.ts index 6449bee..f6e22bd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,7 +10,7 @@ async function bootstrap() { const config = app.get(ConfigService); - app.setGlobalPrefix('api'); + app.setGlobalPrefix(''); app.useGlobalPipes( new ValidationPipe({ @@ -22,8 +22,7 @@ async function bootstrap() { app.enableCors(); - const swaggerEnabled = - config.get('SWAGGER_ENABLED') === 'true'; + const swaggerEnabled = config.get('SWAGGER_ENABLED') === 'true'; if (swaggerEnabled) { const swaggerConfig = new DocumentBuilder() @@ -35,7 +34,7 @@ async function bootstrap() { const document = SwaggerModule.createDocument(app, swaggerConfig); - SwaggerModule.setup('docs', app, document); + SwaggerModule.setup('ApiList', app, document); } const port = config.get('PORT') || 3000; diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts new file mode 100644 index 0000000..5ef7553 --- /dev/null +++ b/src/modules/auth/auth.controller.ts @@ -0,0 +1,65 @@ + +import { Controller, Get, Post, Query, Res, Req, Body, HttpCode, HttpStatus } from '@nestjs/common'; +import { ApiTags, ApiOperation } from '@nestjs/swagger'; +import { AuthService } from './auth.service'; +import { Response, Request } from 'express'; + +@ApiTags('Auth') +@Controller('auth') +export class AuthController { + constructor(private readonly authService: AuthService) {} + + @Get('login') + @ApiOperation({ summary: 'Start Authorization Code + PKCE login (redirect to Identity Provider)' }) + async login(@Query('returnTo') returnTo: string | undefined, @Res() res: Response) { + const redirect = await this.authService.createAuthorizationRedirect(returnTo); + return res.redirect(302, redirect); + } + + @Get('callback') + @ApiOperation({ summary: 'OIDC callback endpoint' }) + async callback(@Query('code') code: string, @Query('state') state: string, @Res() res: Response) { + const result = await this.authService.handleCallback(code, state); + + // set cookies + const cookieOptions: any = { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + path: '/', + }; + + // access token cookie (internal JWT) + res.cookie('raylab_jwt', result.accessToken, { ...cookieOptions, maxAge: result.expiresIn * 1000 }); + + // refresh token cookie + res.cookie('raylab_refresh', result.refreshToken, { ...cookieOptions, maxAge: result.refreshTtl * 1000 }); + + return res.redirect(302, result.returnTo || '/'); + } + + @Post('logout') + @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: 'Logout (invalidate internal session and redirect to identity provider logout)' }) + async logout(@Req() req: Request, @Res() res: Response) { + const refreshToken = req.cookies?.raylab_refresh || req.body?.refreshToken; + const redirect = await this.authService.logout(refreshToken); + return res.redirect(302, redirect); + } + + @Post('refresh') + @ApiOperation({ summary: 'Refresh internal JWT using internal refresh token' }) + async refresh(@Req() req: Request) { + const refreshToken = req.cookies?.raylab_refresh || req.body?.refreshToken; + const result = await this.authService.refresh(refreshToken); + return { success: true, data: result }; + } + + @Get('me') + @ApiOperation({ summary: 'Get current user from internal JWT (cookie or Authorization header)' }) + async me(@Req() req: Request) { + const token = (req.cookies?.raylab_jwt) || (req.headers.authorization && (req.headers.authorization as string).replace(/^Bearer\s+/i, '')); + const user = await this.authService.me(token); + return { success: true, data: user }; + } +} diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts new file mode 100644 index 0000000..820594c --- /dev/null +++ b/src/modules/auth/auth.module.ts @@ -0,0 +1,46 @@ +import { Module } from '@nestjs/common'; +import { AuthController } from './auth.controller'; +import { AuthService } from './auth.service'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { JwtModule } from '@nestjs/jwt'; +import { PrismaService } from '../../shared/prisma.service'; +import { IUser } from '../identity/domain/repositories/user.interface'; +import { PrismaUserRepository } from '../identity/infrastructure/repositories/prisma-user.repository'; +import { SyncIdentityHandler } from '../identity/application/handlers/user/sync-identity.handler'; +import { IRole } from '../identity/domain/repositories/role.interface'; +import { PrismaRoleRepository } from '../identity/infrastructure/repositories/prisma-role.repository'; +import { IAuthConfig } from '../identity/application/config/i-auth-config'; +import { EnvAuthConfig } from '../identity/application/config/env-auth-config'; +import { RedisPkceStore } from './pkce/redis-pkce.store'; +import { InMemoryRefreshStore } from './refresh/inmemory-refresh.store'; + +@Module({ + imports: [ + ConfigModule, + JwtModule.registerAsync({ + imports: [ConfigModule], + useFactory: async (config: ConfigService) => ({ + secret: config.get('RAYLAB_JWT_SECRET') || 'raylab-secret', + signOptions: { algorithm: 'HS256' }, + }), + inject: [ConfigService], + }), + ], + controllers: [AuthController], + providers: [ + AuthService, + PrismaService, + PrismaUserRepository, + PrismaRoleRepository, + SyncIdentityHandler, + { provide: IUser, useClass: PrismaUserRepository }, + { provide: IRole, useClass: PrismaRoleRepository }, + { provide: IAuthConfig, useClass: EnvAuthConfig }, + + // In-memory PKCE and Refresh stores (Redis removed) + RedisPkceStore, + InMemoryRefreshStore, + ], + exports: [AuthService], +}) +export class AuthModule {} diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts new file mode 100644 index 0000000..f488309 --- /dev/null +++ b/src/modules/auth/auth.service.ts @@ -0,0 +1,193 @@ +import { Injectable, UnauthorizedException, Inject } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { JwtService } from '@nestjs/jwt'; +import { SyncIdentityHandler } from '../identity/application/handlers/user/sync-identity.handler'; +import { IUser } from '../identity/domain/repositories/user.interface'; +import { PrismaService } from '../../shared/prisma.service'; +import { RedisPkceStore } from './pkce/redis-pkce.store'; +import { InMemoryRefreshStore } from './refresh/inmemory-refresh.store'; + +import { Issuer, generators, TokenSet } from 'openid-client'; + +@Injectable() +export class AuthService { + constructor( + private readonly config: ConfigService, + private readonly jwtService: JwtService, + private readonly prisma: PrismaService, + @Inject(IUser) private readonly userRepository: IUser, + private readonly syncIdentityHandler: SyncIdentityHandler, + private readonly pkceStore: RedisPkceStore, + private readonly refreshStore: InMemoryRefreshStore, + ) {} + + private issuer: any = null; + private client: any = null; + + private async getIssuer() { + if (this.issuer) return this.issuer; + const issuerUrl = this.config.get('AUTHENTIK_ISSUER'); + if (!issuerUrl) throw new Error('AUTHENTIK_ISSUER not configured'); + this.issuer = await Issuer.discover(issuerUrl); + return this.issuer; + } + + private async getClient() { + if (this.client) return this.client; + const issuer = await this.getIssuer(); + const clientId = this.config.get('AUTHENTIK_CLIENT_ID'); + const clientSecret = this.config.get('AUTHENTIK_CLIENT_SECRET'); + if (!clientId) throw new Error('AUTHENTIK_CLIENT_ID not configured'); + this.client = new issuer.Client({ client_id: clientId, client_secret: clientSecret }); + return this.client; + } + + async createAuthorizationRedirect(returnTo?: string) { + const client = await this.getClient(); + const redirectUri = this.config.get('AUTH_CALLBACK_URL') || `${this.config.get('APP_URL') || ''}/auth/callback`; + + const state = require('crypto').randomUUID(); + const code_verifier = generators.codeVerifier(); + const code_challenge = await generators.codeChallenge(code_verifier); + const nonce = generators.nonce(); + + await this.pkceStore.save(state, { code_verifier, nonce, returnTo }, 300); + + const url = client.authorizationUrl({ + redirect_uri: redirectUri, + scope: this.config.get('AUTHENTIK_DEFAULT_SCOPE') || 'openid email profile', + response_type: 'code', + code_challenge, + code_challenge_method: 'S256', + state, + nonce, + }); + + return url; + } + + async handleCallback(code: string, state: string) { + const client = await this.getClient(); + const pkce = await this.pkceStore.get(state); + if (!pkce) throw new UnauthorizedException('Invalid or expired state'); + + // remove one-time state + await this.pkceStore.remove(state); + + const redirectUri = this.config.get('AUTH_CALLBACK_URL') || `${this.config.get('APP_URL') || ''}/auth/callback`; + + // exchange code + const tokenSet: TokenSet = await client.callback(redirectUri, { code, state }, { code_verifier: pkce.code_verifier, nonce: pkce.nonce }); + + // verify id_token and get claims + const claims = tokenSet.claims(); + + // fetch userinfo if available + let userInfo = null; + try { + if (tokenSet.access_token && client.userinfo) { + userInfo = await client.userinfo(tokenSet.access_token); + } + } catch (e) { + // ignore + } + + const identity = { + sub: userInfo?.sub || claims.sub || null, + preferred_username: userInfo?.preferred_username || userInfo?.username || userInfo?.email || claims.preferred_username || claims.email, + email: userInfo?.email || claims.email, + raw: { tokenSet, userInfo, claims }, + } as any; + + // Sync identity to local user (create if needed) + const domainUser = await this.syncIdentityHandler.execute(identity as any); + + // ensure active/not deleted + if (!domainUser.isActive) throw new UnauthorizedException('User is not active'); + if (domainUser.deletedAt) throw new UnauthorizedException('User is deleted'); + + // create internal JWT + const jwtPayload = { + sub: domainUser.id, + preferred_username: domainUser.username, + email: domainUser.email, + } as any; + + const expiresIn = Number(this.config.get('RAYLAB_JWT_EXPIRES_IN') || 3600); + const access = this.jwtService.sign(jwtPayload, { expiresIn }); + + // create internal refresh token + const refreshToken = require('crypto').randomUUID(); + const refreshTtl = Number(this.config.get('RAYLAB_REFRESH_EXPIRES_IN') || 30 * 24 * 3600); // default 30 days + + await this.refreshStore.set(refreshToken, { userId: domainUser.id }, refreshTtl); + + return { + accessToken: access, + refreshToken, + expiresIn, + refreshTtl, + user: { + id: domainUser.id, + username: domainUser.username, + email: domainUser.email, + roles: domainUser.roles || [], + }, + returnTo: pkce.returnTo, + }; + } + + async refresh(refreshToken?: string) { + if (!refreshToken) throw new UnauthorizedException('Missing refresh token'); + const data = await this.refreshStore.get(refreshToken); + if (!data) throw new UnauthorizedException('Invalid refresh token'); + + const userId = data.userId; + // load user + const domainUser = await this.userRepository.findById(userId); + if (!domainUser) throw new UnauthorizedException('User not found'); + if (!domainUser.isActive) throw new UnauthorizedException('User is not active'); + + // rotate refresh token + await this.refreshStore.del(refreshToken); + const newRefresh = require('crypto').randomUUID(); + const refreshTtl = Number(this.config.get('RAYLAB_REFRESH_EXPIRES_IN') || 30 * 24 * 3600); + await this.refreshStore.set(newRefresh, { userId }, refreshTtl); + + const expiresIn = Number(this.config.get('RAYLAB_JWT_EXPIRES_IN') || 3600); + const access = this.jwtService.sign({ sub: domainUser.id, preferred_username: domainUser.username, email: domainUser.email }, { expiresIn }); + + return { accessToken: access, refreshToken: newRefresh, expiresIn, refreshTtl }; + } + + async logout(refreshToken?: string) { + if (refreshToken) { + await this.refreshStore.del(refreshToken); + } + + const issuer = await this.getIssuer(); + const endSession = issuer.metadata.end_session_endpoint; + const postLogout = this.config.get('APP_URL') || '/'; + + if (endSession) { + // Redirect to identity provider logout + const url = new URL(endSession); + if (postLogout) url.searchParams.set('post_logout_redirect_uri', postLogout); + return url.toString(); + } + + return postLogout; + } + + async me(token?: string) { + if (!token) throw new UnauthorizedException('Missing token'); + try { + const payload: any = this.jwtService.verify(token); + const user = await this.userRepository.findById(payload.sub); + if (!user) throw new UnauthorizedException('User not found'); + return { id: user.id, username: user.username, email: user.email, roles: user.roles || [] }; + } catch (e) { + throw new UnauthorizedException('Invalid token'); + } + } +} diff --git a/src/modules/auth/dto/login.dto.ts b/src/modules/auth/dto/login.dto.ts new file mode 100644 index 0000000..d3c2d25 --- /dev/null +++ b/src/modules/auth/dto/login.dto.ts @@ -0,0 +1,2 @@ +// Login DTO removed. Password grant has been removed in favor of Authorization Code + PKCE flow. +// Formerly contained username/password properties. diff --git a/src/modules/auth/pkce/redis-pkce.store.ts b/src/modules/auth/pkce/redis-pkce.store.ts new file mode 100644 index 0000000..2124c59 --- /dev/null +++ b/src/modules/auth/pkce/redis-pkce.store.ts @@ -0,0 +1,42 @@ +import { Injectable, OnModuleDestroy } from '@nestjs/common'; + +type PkceEntry = { code_verifier: string; nonce: string; returnTo?: string; expiresAt: number }; + +@Injectable() +export class RedisPkceStore implements OnModuleDestroy { + // In-memory PKCE store replacing Redis-backed implementation + private map = new Map(); + private cleanupInterval?: NodeJS.Timeout; + + constructor() { + // periodic cleanup + this.cleanupInterval = setInterval(() => { + const now = Date.now(); + for (const [k, v] of this.map.entries()) { + if (v.expiresAt <= now) this.map.delete(k); + } + }, 60 * 1000); + } + + private key(state: string) { return state; } + + async save(state: string, data: { code_verifier: string; nonce: string; returnTo?: string }, ttlSeconds = 300) { + const expiresAt = Date.now() + ttlSeconds * 1000; + this.map.set(this.key(state), { ...data, expiresAt }); + } + + async get(state: string) { + const v = this.map.get(this.key(state)); + if (!v) return null; + if (v.expiresAt <= Date.now()) { this.map.delete(this.key(state)); return null; } + return { code_verifier: v.code_verifier, nonce: v.nonce, returnTo: v.returnTo }; + } + + async remove(state: string) { + this.map.delete(this.key(state)); + } + + onModuleDestroy() { + if (this.cleanupInterval) clearInterval(this.cleanupInterval); + } +} diff --git a/src/modules/auth/refresh/inmemory-refresh.store.ts b/src/modules/auth/refresh/inmemory-refresh.store.ts new file mode 100644 index 0000000..5917283 --- /dev/null +++ b/src/modules/auth/refresh/inmemory-refresh.store.ts @@ -0,0 +1,38 @@ +import { Injectable, OnModuleDestroy } from '@nestjs/common'; + +type RefreshEntry = { userId: string; expiresAt: number }; + +@Injectable() +export class InMemoryRefreshStore implements OnModuleDestroy { + private map = new Map(); + private cleanupInterval?: NodeJS.Timeout; + + constructor() { + this.cleanupInterval = setInterval(() => { + const now = Date.now(); + for (const [k, v] of this.map.entries()) { + if (v.expiresAt <= now) this.map.delete(k); + } + }, 60 * 1000); + } + + async set(token: string, data: { userId: string }, ttlSeconds: number) { + const expiresAt = Date.now() + ttlSeconds * 1000; + this.map.set(token, { userId: data.userId, expiresAt }); + } + + async get(token: string) { + const v = this.map.get(token); + if (!v) return null; + if (v.expiresAt <= Date.now()) { this.map.delete(token); return null; } + return { userId: v.userId }; + } + + async del(token: string) { + this.map.delete(token); + } + + onModuleDestroy() { + if (this.cleanupInterval) clearInterval(this.cleanupInterval); + } +} diff --git a/src/modules/authorization/presentation/guards/permission.guard.ts b/src/modules/authorization/presentation/guards/permission.guard.ts index acca065..bee1027 100644 --- a/src/modules/authorization/presentation/guards/permission.guard.ts +++ b/src/modules/authorization/presentation/guards/permission.guard.ts @@ -2,6 +2,8 @@ import { Injectable, CanActivate, ExecutionContext, + UnauthorizedException, + ForbiddenException, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; @@ -25,12 +27,20 @@ export class PermissionGuard implements CanActivate { return true; } - const request = context.switchToHttp().getRequest(); + const request = context.switchToHttp().getRequest() as any; - const user = request.user; + const user = request.currentUser; - return permissions.every(permission => - user.permissions.includes(permission), - ); + if (!user) { + throw new UnauthorizedException('Current user is missing.'); + } + + const hasAll = permissions.every((permission) => { + return user.hasPermission(permission); + }); + + if (!hasAll) throw new ForbiddenException('Insufficient permissions.'); + + return true; } } \ No newline at end of file diff --git a/src/modules/identity/application/README.md b/src/modules/identity/application/README.md index e01622b..68f05f1 100644 --- a/src/modules/identity/application/README.md +++ b/src/modules/identity/application/README.md @@ -1,12 +1 @@ -modules/identity/application/ - -Penjelasan: -Layer application berisi use-case (services/commands/queries) yang mengorkestrasi domain dan infrastruktur. - -Contoh file: -- services/get-user.service.ts -- commands/create-user.command.ts - -Aturan: -- Application service boleh memanggil repository interface, domain services, dan event publisher. -- Application menangani transaksi jika diperlukan. +ini adalah manager, semua kegiatan diarahkan dari sini, disini bukan mengakses db, menghitung, dll, dari controller akan masuk ke dalam sini dan dijalankan prosesnya, tapi tidak tau http dan databasenya. \ No newline at end of file diff --git a/src/modules/identity/application/config/env-auth-config.ts b/src/modules/identity/application/config/env-auth-config.ts new file mode 100644 index 0000000..74b24ed --- /dev/null +++ b/src/modules/identity/application/config/env-auth-config.ts @@ -0,0 +1,15 @@ +import { IAuthConfig } from './i-auth-config'; + +export class EnvAuthConfig implements IAuthConfig { + autoCreateUser(): boolean { + return (process.env.AUTH_AUTO_CREATE_USER ?? 'true') === 'true'; + } + + syncEmail(): boolean { + return (process.env.AUTH_SYNC_EMAIL ?? 'true') === 'true'; + } + + syncUsername(): boolean { + return (process.env.AUTH_SYNC_USERNAME ?? 'false') === 'true'; + } +} diff --git a/src/modules/identity/application/config/i-auth-config.ts b/src/modules/identity/application/config/i-auth-config.ts new file mode 100644 index 0000000..8e1d6a5 --- /dev/null +++ b/src/modules/identity/application/config/i-auth-config.ts @@ -0,0 +1,5 @@ +export abstract class IAuthConfig { + abstract autoCreateUser(): boolean; + abstract syncEmail(): boolean; + abstract syncUsername(): boolean; +} diff --git a/src/modules/identity/application/handlers/README.MD b/src/modules/identity/application/handlers/README.MD new file mode 100644 index 0000000..a0639ca --- /dev/null +++ b/src/modules/identity/application/handlers/README.MD @@ -0,0 +1 @@ +semua method disini didapat dari interface, itu ada di domain/repositories. \ No newline at end of file diff --git a/src/modules/identity/application/handlers/create-user.handler.ts b/src/modules/identity/application/handlers/create-user.handler.ts deleted file mode 100644 index 58a314b..0000000 --- a/src/modules/identity/application/handlers/create-user.handler.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - Injectable, - ConflictException, -} from '@nestjs/common'; - -import { CreateUserDto } from '../../presentation/dto/create-user.dto'; -import { User } from '../../domain/entities/user.entity'; -import { UserRepository } from '../../domain/repositories/user.repository.interface'; - -@Injectable() -export class CreateUserHandler { - constructor( - private readonly userRepository: UserRepository, - ) {} - - async execute(dto: CreateUserDto): Promise { - const exists = await this.userRepository.existsByEmail(dto.email); - - if (exists) { - throw new ConflictException('Email already exists.'); - } - - const user = User.create({ - name: dto.name, - email: dto.email, - password: dto.password, - metadata: dto.metadata, - }); - - await this.userRepository.create(user); - - // TODO: - // this.eventDispatcher.publish(new UserCreatedEvent(user)); - - return user; - } -} \ No newline at end of file diff --git a/src/modules/identity/application/handlers/delete-user.handler.ts b/src/modules/identity/application/handlers/delete-user.handler.ts deleted file mode 100644 index be70def..0000000 --- a/src/modules/identity/application/handlers/delete-user.handler.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - Injectable, - NotFoundException, -} from '@nestjs/common'; - -import { UserRepository } from '../../domain/repositories/user.repository.interface'; - -@Injectable() -export class DeleteUserHandler { - constructor( - private readonly userRepository: UserRepository, - ) {} - - async execute(id: string): Promise { - const user = await this.userRepository.findById(id); - - if (!user) { - throw new NotFoundException('User not found.'); - } - - user.delete(); - - await this.userRepository.update(user); - - // TODO: - // Publish UserDeletedEvent - } -} \ No newline at end of file diff --git a/src/modules/identity/application/handlers/find-user.handler.ts b/src/modules/identity/application/handlers/find-user.handler.ts deleted file mode 100644 index 5942fb4..0000000 --- a/src/modules/identity/application/handlers/find-user.handler.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - Injectable, - NotFoundException, -} from '@nestjs/common'; - -import { UserRepository } from '../../domain/repositories/user.repository.interface'; -import { User } from '../../domain/entities/user.entity'; - -@Injectable() -export class FindUserHandler { - constructor( - private readonly userRepository: UserRepository, - ) {} - - async execute(id: string): Promise { - const user = await this.userRepository.findById(id); - - if (!user) { - throw new NotFoundException('User not found.'); - } - - return user; - } -} \ No newline at end of file diff --git a/src/modules/identity/application/handlers/find-users.handler.ts b/src/modules/identity/application/handlers/find-users.handler.ts deleted file mode 100644 index c7b3af1..0000000 --- a/src/modules/identity/application/handlers/find-users.handler.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -import { UserRepository } from '../../domain/repositories/user.repository.interface'; -import { User } from '../../domain/entities/user.entity'; - -@Injectable() -export class FindUsersHandler { - constructor( - private readonly userRepository: UserRepository, - ) {} - - async execute(): Promise { - return await this.userRepository.findAll(); - } -} \ No newline at end of file diff --git a/src/modules/identity/application/handlers/permission/create-permission.handler.ts b/src/modules/identity/application/handlers/permission/create-permission.handler.ts new file mode 100644 index 0000000..ebcd193 --- /dev/null +++ b/src/modules/identity/application/handlers/permission/create-permission.handler.ts @@ -0,0 +1,21 @@ +import { Injectable, ConflictException } from '@nestjs/common'; +import { IPermission } from '../../../domain/repositories/permission.interface'; +import { PermissionData } from '../../../domain/entities/permission.entity'; + +@Injectable() +export class CreatePermissionHandler { + constructor(private readonly permissionRepository: IPermission) {} + + async execute(dto: any) { + const perm = PermissionData.restore({ + id: crypto.randomUUID(), + code: dto.code || dto.name, + name: dto.name, + description: dto.description || '', + createdAt: new Date(), + updatedAt: new Date(), + } as any); + + return this.permissionRepository.create(perm); + } +} diff --git a/src/modules/identity/application/handlers/permission/delete-permission.handler.ts b/src/modules/identity/application/handlers/permission/delete-permission.handler.ts new file mode 100644 index 0000000..cde347e --- /dev/null +++ b/src/modules/identity/application/handlers/permission/delete-permission.handler.ts @@ -0,0 +1,14 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IPermission } from '../../../domain/repositories/permission.interface'; + +@Injectable() +export class DeletePermissionHandler { + constructor(private readonly permissionRepository: IPermission) {} + + async execute(id: string) { + const p = await this.permissionRepository.getById(id); + if (!p) throw new NotFoundException('Permission not found.'); + + await this.permissionRepository.delete(id); + } +} diff --git a/src/modules/identity/application/handlers/permission/get-permission.handler.ts b/src/modules/identity/application/handlers/permission/get-permission.handler.ts new file mode 100644 index 0000000..c7a1c8c --- /dev/null +++ b/src/modules/identity/application/handlers/permission/get-permission.handler.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { IPermission } from '../../../domain/repositories/permission.interface'; + +@Injectable() +export class GetPermissionHandler { + constructor(private readonly permissionRepository: IPermission) {} + + async execute(id: string) { + return this.permissionRepository.getById(id); + } +} diff --git a/src/modules/identity/application/handlers/permission/get-permissions.handler.ts b/src/modules/identity/application/handlers/permission/get-permissions.handler.ts new file mode 100644 index 0000000..309301b --- /dev/null +++ b/src/modules/identity/application/handlers/permission/get-permissions.handler.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { IPermission } from '../../../domain/repositories/permission.interface'; + +@Injectable() +export class GetPermissionsHandler { + constructor(private readonly permissionRepository: IPermission) {} + + async execute(query: { page?: number; limit?: number; search?: string }) { + return this.permissionRepository.find({ page: query.page, limit: query.limit, search: query.search || null }); + } +} diff --git a/src/modules/identity/application/handlers/permission/update-permission.handler.ts b/src/modules/identity/application/handlers/permission/update-permission.handler.ts new file mode 100644 index 0000000..7e642cf --- /dev/null +++ b/src/modules/identity/application/handlers/permission/update-permission.handler.ts @@ -0,0 +1,18 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IPermission } from '../../../domain/repositories/permission.interface'; + +@Injectable() +export class UpdatePermissionHandler { + constructor(private readonly permissionRepository: IPermission) {} + + async execute(id: string, dto: any) { + const perm = await this.permissionRepository.getById(id); + if (!perm) throw new NotFoundException('Permission not found.'); + + if (dto.name) perm.changeName(dto.name); + if (dto.description !== undefined) perm.changeDescription(dto.description); + if (dto.code) perm.changeCode(dto.code); + + return this.permissionRepository.update(perm); + } +} diff --git a/src/modules/identity/application/handlers/role/assign-permission.handler.ts b/src/modules/identity/application/handlers/role/assign-permission.handler.ts new file mode 100644 index 0000000..95f892f --- /dev/null +++ b/src/modules/identity/application/handlers/role/assign-permission.handler.ts @@ -0,0 +1,23 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; +import { IPermission } from '../../../domain/repositories/permission.interface'; + +@Injectable() +export class RoleAssignPermissionHandler { + constructor( + private readonly roleRepository: IRole, + private readonly permissionRepository: IPermission, + ) {} + + async execute(roleId: string, permissionId: string) { + const role = await this.roleRepository.findById(roleId); + if (!role) throw new NotFoundException('Role not found.'); + + const perm = await this.permissionRepository.getById(permissionId); + if (!perm) throw new NotFoundException('Permission not found.'); + + role.assignPermission(perm); + + return this.roleRepository.update(role); + } +} diff --git a/src/modules/identity/application/handlers/role/create-role.handler.ts b/src/modules/identity/application/handlers/role/create-role.handler.ts new file mode 100644 index 0000000..6cabff4 --- /dev/null +++ b/src/modules/identity/application/handlers/role/create-role.handler.ts @@ -0,0 +1,30 @@ +import { Injectable, ConflictException } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; +import { RoleData } from '../../../domain/entities/role.entity'; + +@Injectable() +export class CreateRoleHandler { + constructor(private readonly roleRepository: IRole) {} + + async execute(dto: any) { + // check uniqueness by code + // simple check + try { + // attempt to create; repository may enforce uniqueness + const role = RoleData.restore({ + id: crypto.randomUUID(), + code: dto.code, + name: dto.name, + description: dto.description || '', + permissions: [], + isDefault: dto.isDefault || false, + createdAt: new Date(), + updatedAt: new Date(), + } as any); + + return this.roleRepository.create(role); + } catch (e) { + throw new ConflictException('Role creation failed.'); + } + } +} diff --git a/src/modules/identity/application/handlers/role/delete-role.handler.ts b/src/modules/identity/application/handlers/role/delete-role.handler.ts new file mode 100644 index 0000000..fa694ca --- /dev/null +++ b/src/modules/identity/application/handlers/role/delete-role.handler.ts @@ -0,0 +1,14 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; + +@Injectable() +export class DeleteRoleHandler { + constructor(private readonly roleRepository: IRole) {} + + async execute(id: string) { + const role = await this.roleRepository.findById(id); + if (!role) throw new NotFoundException('Role not found.'); + + await this.roleRepository.delete(id); + } +} diff --git a/src/modules/identity/application/handlers/role/get-role.handler.ts b/src/modules/identity/application/handlers/role/get-role.handler.ts new file mode 100644 index 0000000..ea0df1c --- /dev/null +++ b/src/modules/identity/application/handlers/role/get-role.handler.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; + +@Injectable() +export class GetRoleHandler { + constructor(private readonly roleRepository: IRole) {} + + async execute(id: string) { + return this.roleRepository.findById(id); + } +} diff --git a/src/modules/identity/application/handlers/role/get-roles.handler.ts b/src/modules/identity/application/handlers/role/get-roles.handler.ts new file mode 100644 index 0000000..a931691 --- /dev/null +++ b/src/modules/identity/application/handlers/role/get-roles.handler.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; + +@Injectable() +export class GetRolesHandler { + constructor(private readonly roleRepository: IRole) {} + + async execute(query: { page?: number; limit?: number; search?: string }) { + return this.roleRepository.find({ page: query.page, limit: query.limit, search: query.search || null }); + } +} diff --git a/src/modules/identity/application/handlers/role/remove-permission.handler.ts b/src/modules/identity/application/handlers/role/remove-permission.handler.ts new file mode 100644 index 0000000..61a03c0 --- /dev/null +++ b/src/modules/identity/application/handlers/role/remove-permission.handler.ts @@ -0,0 +1,16 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; + +@Injectable() +export class RoleRemovePermissionHandler { + constructor(private readonly roleRepository: IRole) {} + + async execute(roleId: string, permissionId: string) { + const role = await this.roleRepository.findById(roleId); + if (!role) throw new NotFoundException('Role not found.'); + + role.removePermission(permissionId); + + return this.roleRepository.update(role); + } +} diff --git a/src/modules/identity/application/handlers/role/update-role.handler.ts b/src/modules/identity/application/handlers/role/update-role.handler.ts new file mode 100644 index 0000000..0b23bfc --- /dev/null +++ b/src/modules/identity/application/handlers/role/update-role.handler.ts @@ -0,0 +1,18 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IRole } from '../../../domain/repositories/role.interface'; + +@Injectable() +export class UpdateRoleHandler { + constructor(private readonly roleRepository: IRole) {} + + async execute(id: string, dto: any) { + const role = await this.roleRepository.findById(id); + if (!role) throw new NotFoundException('Role not found.'); + + if (dto.name) role.changeName(dto.name); + if (dto.description !== undefined) role.changeDescription(dto.description); + if (dto.isDefault !== undefined) role.setDefault(!!dto.isDefault); + + return this.roleRepository.update(role); + } +} diff --git a/src/modules/identity/application/handlers/update-user.handler.ts b/src/modules/identity/application/handlers/update-user.handler.ts deleted file mode 100644 index d4e04d6..0000000 --- a/src/modules/identity/application/handlers/update-user.handler.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { - Injectable, - ConflictException, - NotFoundException, -} from '@nestjs/common'; - -import { UpdateUserDto } from '../../presentation/dto/update-user.dto'; -import { UserRepository } from '../../domain/repositories/user.repository.interface'; -import { User } from '../../domain/entities/user.entity'; - -@Injectable() -export class UpdateUserHandler { - constructor( - private readonly userRepository: UserRepository, - ) {} - - async execute( - id: string, - dto: UpdateUserDto, - ): Promise { - const user = await this.userRepository.findById(id); - - if (!user) { - throw new NotFoundException('User not found.'); - } - - if ( - dto.email && - dto.email !== user.email - ) { - const exists = - await this.userRepository.existsByEmail(dto.email); - - if (exists) { - throw new ConflictException( - 'Email already exists.', - ); - } - - user.changeEmail(dto.email); - } - - if (dto.name) { - user.changeName(dto.name); - } - - await this.userRepository.update(user); - - return user; - } -} \ No newline at end of file diff --git a/src/modules/identity/application/handlers/user/assign-permission.handler.ts b/src/modules/identity/application/handlers/user/assign-permission.handler.ts new file mode 100644 index 0000000..28b3b86 --- /dev/null +++ b/src/modules/identity/application/handlers/user/assign-permission.handler.ts @@ -0,0 +1,26 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; +import { PermissionData } from '../../../domain/entities/permission.entity'; + +@Injectable() +export class AssignPermissionHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(userId: string, permissionId: string) { + const user = await this.userRepository.getById(userId); + if (!user) throw new NotFoundException('User not found.'); + + // Permission repository is not available; create a minimal PermissionData + const perm = PermissionData.restore({ + id: permissionId, + name: permissionId, + description: '', + createdAt: new Date(), + updatedAt: new Date(), + }); + + user.assignPermission(perm); + + return this.userRepository.update(user); + } +} diff --git a/src/modules/identity/application/handlers/user/assign-role.handler.ts b/src/modules/identity/application/handlers/user/assign-role.handler.ts new file mode 100644 index 0000000..2b07179 --- /dev/null +++ b/src/modules/identity/application/handlers/user/assign-role.handler.ts @@ -0,0 +1,23 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; +import { IRole } from '../../../domain/repositories/role.interface'; + +@Injectable() +export class AssignRoleHandler { + constructor( + private readonly userRepository: IUser, + private readonly roleRepository: IRole, + ) {} + + async execute(userId: string, roleId: string) { + const user = await this.userRepository.getById(userId); + if (!user) throw new NotFoundException('User not found.'); + + const role = await this.roleRepository.findById(roleId); + if (!role) throw new NotFoundException('Role not found.'); + + user.assignRole(role); + + return this.userRepository.update(user); + } +} diff --git a/src/modules/identity/application/handlers/user/create-user.handler.ts b/src/modules/identity/application/handlers/user/create-user.handler.ts new file mode 100644 index 0000000..5bee566 --- /dev/null +++ b/src/modules/identity/application/handlers/user/create-user.handler.ts @@ -0,0 +1,31 @@ +import { + Injectable, + ConflictException, + BadRequestException, +} from '@nestjs/common'; + +import { CreateUserDto } from '../../../presentation/dto/create-user.dto'; +import { IUser } from '../../../domain/repositories/user.interface'; +import { UserData } from '../../../domain/entities/user.entity'; +import { UserService } from '../../services/user.service'; + +@Injectable() +export class CreateUserHandler { + constructor( + private readonly userRepository: IUser, + private readonly userService: UserService, + ) {} + + async execute(dto: CreateUserDto): Promise { + + const exists = await this.userRepository.existByEmail(dto.email); + + if (exists) { + throw new ConflictException('Email already exists.'); + } + + // Provisioning disabled: Do not create users in external IdP from RayLab. + // Reject attempts to create users via API to enforce creation-at-Authentik policy. + throw new BadRequestException('User creation via RayLab API is disabled. Create users in Authentik and then login to sync.'); + } +} \ No newline at end of file diff --git a/src/modules/identity/application/handlers/user/delete-user.handler.ts b/src/modules/identity/application/handlers/user/delete-user.handler.ts new file mode 100644 index 0000000..17c98a4 --- /dev/null +++ b/src/modules/identity/application/handlers/user/delete-user.handler.ts @@ -0,0 +1,14 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class DeleteUserHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(id: string) { + const exists = await this.userRepository.existsById(id); + if (!exists) throw new NotFoundException('User not found.'); + + await this.userRepository.softDelete(id); + } +} diff --git a/src/modules/identity/application/handlers/user/disable-user.handler.ts b/src/modules/identity/application/handlers/user/disable-user.handler.ts new file mode 100644 index 0000000..87c627a --- /dev/null +++ b/src/modules/identity/application/handlers/user/disable-user.handler.ts @@ -0,0 +1,14 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class DisableUserHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(id: string) { + const exists = await this.userRepository.existsById(id); + if (!exists) throw new NotFoundException('User not found.'); + + return this.userRepository.disable(id); + } +} diff --git a/src/modules/identity/application/handlers/user/enable-user.handler.ts b/src/modules/identity/application/handlers/user/enable-user.handler.ts new file mode 100644 index 0000000..dd73fbd --- /dev/null +++ b/src/modules/identity/application/handlers/user/enable-user.handler.ts @@ -0,0 +1,14 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class EnableUserHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(id: string) { + const exists = await this.userRepository.existsById(id); + if (!exists) throw new NotFoundException('User not found.'); + + return this.userRepository.enable(id); + } +} diff --git a/src/modules/identity/application/handlers/user/get-current-user.handler.ts b/src/modules/identity/application/handlers/user/get-current-user.handler.ts new file mode 100644 index 0000000..a9e2184 --- /dev/null +++ b/src/modules/identity/application/handlers/user/get-current-user.handler.ts @@ -0,0 +1,10 @@ +import { Injectable } from '@nestjs/common'; +import { UserData } from '../../../domain/entities/user.entity'; + +@Injectable() +export class GetCurrentUserHandler { + async execute(currentUser: UserData) { + // currentUser is already domain user attached by CurrentUserGuard + return currentUser; + } +} diff --git a/src/modules/identity/application/handlers/user/get-user.handler.ts b/src/modules/identity/application/handlers/user/get-user.handler.ts new file mode 100644 index 0000000..d78d600 --- /dev/null +++ b/src/modules/identity/application/handlers/user/get-user.handler.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class GetUserHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(id: string) { + return this.userRepository.getById(id); + } +} diff --git a/src/modules/identity/application/handlers/user/get-users.handler.ts b/src/modules/identity/application/handlers/user/get-users.handler.ts new file mode 100644 index 0000000..fe3af46 --- /dev/null +++ b/src/modules/identity/application/handlers/user/get-users.handler.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class GetUsersHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(query: { + page?: number; + limit?: number; + search?: string; + isActive?: boolean; + deleted?: boolean; + roleId?: string; + }) { + const res = await this.userRepository.find({ + page: query.page, + limit: query.limit, + search: query.search || null, + isActive: query.isActive !== undefined ? query.isActive : null, + deleted: query.deleted !== undefined ? query.deleted : null, + roleId: query.roleId || null, + }); + + return res; + } +} diff --git a/src/modules/identity/application/handlers/user/remove-permission.handler.ts b/src/modules/identity/application/handlers/user/remove-permission.handler.ts new file mode 100644 index 0000000..c8cb40d --- /dev/null +++ b/src/modules/identity/application/handlers/user/remove-permission.handler.ts @@ -0,0 +1,16 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class RemovePermissionHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(userId: string, permissionId: string) { + const user = await this.userRepository.getById(userId); + if (!user) throw new NotFoundException('User not found.'); + + user.removePermission(permissionId); + + return this.userRepository.update(user); + } +} diff --git a/src/modules/identity/application/handlers/user/remove-role.handler.ts b/src/modules/identity/application/handlers/user/remove-role.handler.ts new file mode 100644 index 0000000..b6c855a --- /dev/null +++ b/src/modules/identity/application/handlers/user/remove-role.handler.ts @@ -0,0 +1,16 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class RemoveRoleHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(userId: string, roleId: string) { + const user = await this.userRepository.getById(userId); + if (!user) throw new NotFoundException('User not found.'); + + user.removeRole(roleId); + + return this.userRepository.update(user); + } +} diff --git a/src/modules/identity/application/handlers/user/restore-user.handler.ts b/src/modules/identity/application/handlers/user/restore-user.handler.ts new file mode 100644 index 0000000..657ee47 --- /dev/null +++ b/src/modules/identity/application/handlers/user/restore-user.handler.ts @@ -0,0 +1,14 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; + +@Injectable() +export class RestoreUserHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(id: string) { + const exists = await this.userRepository.existsById(id); + if (!exists) throw new NotFoundException('User not found.'); + + return this.userRepository.restore(id); + } +} diff --git a/src/modules/identity/application/handlers/user/sync-identity.handler.ts b/src/modules/identity/application/handlers/user/sync-identity.handler.ts new file mode 100644 index 0000000..aeb114b --- /dev/null +++ b/src/modules/identity/application/handlers/user/sync-identity.handler.ts @@ -0,0 +1,86 @@ +import { Injectable, UnauthorizedException, ForbiddenException, Inject } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; +import { IRole } from '../../../domain/repositories/role.interface'; +import { IAuthConfig } from '../../config/i-auth-config'; +import { UserData } from '../../../domain/entities/user.entity'; +import { IdentityData } from '../../../../../core/auth/interfaces/identity-data'; + +@Injectable() +export class SyncIdentityHandler { + constructor( + private readonly userRepository: IUser, + private readonly roleRepository: IRole, + private readonly authConfig: IAuthConfig, + ) {} + + async execute(identity: IdentityData): Promise { + const { sub, preferred_username, email } = identity as any; + + if (!sub) throw new UnauthorizedException('Invalid identity payload.'); + + // Try find by authentikId + let user = await this.userRepository.findByAuthentikId(sub); + + const syncEmail = this.authConfig.syncEmail(); + const syncUsername = this.authConfig.syncUsername(); + + if (!user) { + // User does not exist locally: create minimal local record per new architecture + const username = preferred_username || email || sub; + const userEntity = UserData.create({ username, email: email || '', authentikId: sub }); + + // assign default role if available + try { + const defaultRole = await this.roleRepository.getDefaultRole(); + if (defaultRole) { + userEntity.assignRole(defaultRole); + } + } catch (e) { + // ignore if role repo not available or no default role + } + + // persist local user + user = await this.userRepository.create(userEntity); + + // return freshly created user + return user; + } else { + let changed = false; + + if (syncEmail && email && user.email !== email) { + user.changeEmail(email); + changed = true; + + // prepare event class instance if needed (no dispatch) + } + + if (syncUsername && preferred_username && user.username !== preferred_username) { + user.changeUsername(preferred_username); + changed = true; + + // prepare event class instance if needed (no dispatch) + } + + // update last seen + user.touchLastSeen(); + changed = true; + + if (changed) { + user = await this.userRepository.update(user); + } + } + + // Validations + if (!user.isActive) { + // Authentication succeeded but user is disabled + throw new ForbiddenException('User is not active.'); + } + + if (user.deletedAt) { + throw new ForbiddenException('User is deleted.'); + } + + // Ensure roles/permissions loaded (repo should return includes) + return user; + } +} diff --git a/src/modules/identity/application/services/README.md b/src/modules/identity/application/services/README.md deleted file mode 100644 index 3ac5650..0000000 --- a/src/modules/identity/application/services/README.md +++ /dev/null @@ -1,12 +0,0 @@ -services/ - -Penjelasan: -Application services (use-cases) untuk module identity. - -Contoh file: -- get-user.service.ts -- create-user.service.ts - -Aturan: -- Application service mengorkestrasi domain services dan repository. -- Menangani transaction boundary jika perlu. diff --git a/src/modules/identity/application/services/user.service.ts b/src/modules/identity/application/services/user.service.ts new file mode 100644 index 0000000..bd921df --- /dev/null +++ b/src/modules/identity/application/services/user.service.ts @@ -0,0 +1,20 @@ +import { Injectable, BadRequestException, Logger } from '@nestjs/common'; +import { PrismaService } from '../../../../shared/prisma.service'; +import { IUser } from '../../domain/repositories/user.interface'; +import { UserData } from '../../domain/entities/user.entity'; + +@Injectable() +export class UserService { + private readonly logger = new Logger(UserService.name); + + constructor( + private readonly prisma: PrismaService, + private readonly userRepository: IUser, + ) {} + + async createUser(input: { username: string; email: string; roles?: string[]; isActive?: boolean }) { + // Provisioning to external Identity Provider (Authentik) has been disabled. + // All users must be created in Authentik first. RayLab will create local record on first successful login. + throw new BadRequestException('Provisioning disabled: create users in Authentik and login to sync to RayLab'); + } +} diff --git a/src/modules/identity/domain/entities/README.md b/src/modules/identity/domain/entities/README.md index bf9110b..b394cb0 100644 --- a/src/modules/identity/domain/entities/README.md +++ b/src/modules/identity/domain/entities/README.md @@ -1,11 +1 @@ -entities/ - -Penjelasan: -Entity domain untuk identity, mis. User, Profile. - -Contoh file: -- user.entity.ts -- profile.entity.ts - -Aturan: -- Entity berisi atribut dan mungkin method domain kecil (invariants), bukan orchestration. +ini adalah tempat class objek dibuat, objek user, role, dll. disini juga ada method dan harus diingat yang boleh mengubah objek ini hanya objek ini sendiri. \ No newline at end of file diff --git a/src/modules/identity/domain/entities/permission.entity.ts b/src/modules/identity/domain/entities/permission.entity.ts new file mode 100644 index 0000000..f27dce1 --- /dev/null +++ b/src/modules/identity/domain/entities/permission.entity.ts @@ -0,0 +1,40 @@ +export class PermissionData { + private constructor( + public readonly id: string, + public code: string, + public name: string, + public description: string, + public createdAt: Date, + public updatedAt: Date, + ) {} + + changeName(name: string) { + this.name = name; + } + + changeDescription(description: string) { + this.description = description; + } + + changeCode(code: string) { + this.code = code; + } + + static restore(props: { + id: string; + code?: string; + name: string; + description: string; + createdAt: Date; + updatedAt: Date; + }) { + return new PermissionData( + props.id, + props.code || props.name, + props.name, + props.description, + props.createdAt, + props.updatedAt, + ); + } +} \ No newline at end of file diff --git a/src/modules/identity/domain/entities/role.entity.ts b/src/modules/identity/domain/entities/role.entity.ts new file mode 100644 index 0000000..96a0d0f --- /dev/null +++ b/src/modules/identity/domain/entities/role.entity.ts @@ -0,0 +1,64 @@ +import { PermissionData } from "./permission.entity"; + +export class RoleData { + private constructor( + public readonly id: string, + public code: string, + public name: string, + public description: string, + public permissions: PermissionData[], + public isDefault: boolean, + public createdAt: Date, + public updatedAt: Date, + ) {} + + hasPermissions(permissionId: string): boolean { + return this.permissions.some(x => x.id.toLowerCase() === permissionId.toLowerCase()); + } + + assignPermission(permissionData: PermissionData): void { + if (this.hasPermissions(permissionData.id)) throw new Error("Permission sudah dimiliki."); + + this.permissions.push(permissionData); + } + + removePermission(permissionId: string): void { + const idx = this.permissions.findIndex(p => p.id.toLowerCase() === permissionId.toLowerCase()); + if (idx === -1) throw new Error('Permission tidak ditemukan pada role.'); + this.permissions.splice(idx, 1); + } + + changeName(name: string) { + this.name = name; + } + + changeDescription(description: string) { + this.description = description; + } + + setDefault(isDefault: boolean) { + this.isDefault = isDefault; + } + + static restore(props: { + id: string; + code: string; + name: string; + description: string; + permissions?: PermissionData[]; + isDefault?: boolean; + createdAt: Date; + updatedAt: Date; + }) { + return new RoleData( + props.id, + props.code, + props.name, + props.description, + props.permissions || [], + props.isDefault ?? false, + props.createdAt, + props.updatedAt, + ); + } +} \ No newline at end of file diff --git a/src/modules/identity/domain/entities/user.entity.ts b/src/modules/identity/domain/entities/user.entity.ts index e1a4538..335462e 100644 --- a/src/modules/identity/domain/entities/user.entity.ts +++ b/src/modules/identity/domain/entities/user.entity.ts @@ -1,52 +1,196 @@ -export class User { - private constructor( +import { RoleData } from "./role.entity"; +import { PermissionData } from "./permission.entity"; + +export class UserData { + private constructor( public readonly id: string, - public name: string, + public authentikId: string | null, + public username: string, public email: string, - public password: string, - public metadata?: Record, + public password: string | null, + public roles: RoleData[], + public permissions: PermissionData[], + public isActive: boolean, + public deletedAt: Date | null, + public lastSeenAt: Date | null, + public storageQuota: number, + public storageUsed: number, ) {} - static create(data: { - name: string; + //#region Create + + static create(data: { + username: string; email: string; - password: string; - metadata?: Record; - }): User { - return new User( + password?: string | null; + authentikId?: string | null; + storageQuota?: number; + storageUsed?: number; + }): UserData { + const quota = data.storageQuota !== undefined ? data.storageQuota : 10737418240; // 10 GB + const used = data.storageUsed !== undefined ? data.storageUsed : 0; + return new UserData( crypto.randomUUID(), - data.name, + data.authentikId || null, + data.username, data.email, - data.password, - data.metadata, + data.password ?? null, + [], + [], + true, + null, + null, + quota, + used, ); } - delete() { - // Business Rule + //#endregion + + //#region Read + + hasRole(roleId: string) : boolean { + return this.roles.some(x => x.id.toLowerCase() === roleId.toLowerCase()) } - changeEmail(email: string) { + assignRole(roleData : RoleData) : void { + if (this.hasRole(roleData.id)) + throw new Error("Role Sudah dimiliki."); + + this.roles.push(roleData); + } + + hasPermissions(permissionId : string) : boolean { + return this.permissions.some(x => x.id.toLowerCase() === permissionId.toLowerCase()) + } + + hasPermission(permission: string) : boolean { + const byPerm = this.permissions.some(x => x.id.toLowerCase() === permission.toLowerCase() || x.name.toLowerCase() === permission.toLowerCase()); + if (byPerm) return true; + + // check roles + return this.roles.some(r => r.permissions.some(p => p.id.toLowerCase() === permission.toLowerCase() || p.name.toLowerCase() === permission.toLowerCase())); + } + + assignPermission(permissionData : PermissionData) : void { + if (this.hasPermissions(permissionData.id)) + throw new Error("Permission sudah dimiliki.") + + this.permissions.push(permissionData); + } + + //#endregion + + //#region Update + changeEmail(email: string) { this.email = email; } - changeName(name: string) { - this.name = name; + changeUsername(username: string) { + this.username = username; } - static restore(data: { - id: string; - name: string; - email: string; - password: string; - metadata?: Record; - }): User { - return new User( - data.id, - data.name, - data.email, - data.password, - data.metadata, - ); + touchLastSeen() { + this.lastSeenAt = new Date(); + } + + enable() { + this.isActive = true; + } + + disable() { + this.isActive = false; + } + //#endregion + + //#region Delete + softDelete() { + this.deletedAt = new Date(); + this.isActive = false; + } + + restoreInstance() { + this.deletedAt = null; + } + //#endregion + + public static restore(props: { + id: string; + authentikId?: string | null; + username: string; + email: string; + password: string | null; + roles?: RoleData[]; + permissions?: PermissionData[]; + isActive?: boolean; + deletedAt?: Date | null; + lastSeenAt?: Date | null; + storageQuota?: number | null; + storageUsed?: number | null; + }): UserData { + + return new UserData( + props.id, + props.authentikId || null, + props.username, + props.email, + props.password, + props.roles || [], + props.permissions || [], + props.isActive !== undefined ? props.isActive : true, + props.deletedAt || null, + props.lastSeenAt || null, + props.storageQuota !== undefined && props.storageQuota !== null ? props.storageQuota : 10737418240, + props.storageUsed !== undefined && props.storageUsed !== null ? props.storageUsed : 0, + ); + } + + removeRole(roleId: string) { + const idx = this.roles.findIndex(r => r.id.toLowerCase() === roleId.toLowerCase()); + if (idx === -1) throw new Error('Role tidak ditemukan pada user.'); + this.roles.splice(idx, 1); + } + + removePermission(permissionId: string) { + const idx = this.permissions.findIndex(p => p.id.toLowerCase() === permissionId.toLowerCase()); + if (idx === -1) throw new Error('Permission tidak ditemukan pada user.'); + this.permissions.splice(idx, 1); + } + + // Storage helpers + setStorageQuota(bytes: number) { + if (bytes < 0) throw new Error('storageQuota must be >= 0'); + if (this.storageUsed > bytes) throw new Error('storageQuota cannot be less than storageUsed'); + this.storageQuota = bytes; + } + + setStorageUsed(bytes: number) { + if (bytes < 0) throw new Error('storageUsed must be >= 0'); + if (bytes > this.storageQuota) throw new Error('storageUsed cannot exceed storageQuota'); + this.storageUsed = bytes; + } + + // Response helper for API + toResponse() { + const remaining = this.storageQuota - this.storageUsed; + const usagePercentage = this.storageQuota > 0 ? Math.round((this.storageUsed / this.storageQuota) * 100) : 0; + + return { + id: this.id, + authentikId: this.authentikId, + username: this.username, + email: this.email, + roles: this.roles, + permissions: this.permissions, + isActive: this.isActive, + deletedAt: this.deletedAt, + lastSeenAt: this.lastSeenAt, + storage: { + quota: this.storageQuota, + used: this.storageUsed, + remaining, + usagePercentage, + }, + }; } } \ No newline at end of file diff --git a/src/modules/identity/domain/events/user-created.event.ts b/src/modules/identity/domain/events/user-created.event.ts index 07fea7e..997431e 100644 --- a/src/modules/identity/domain/events/user-created.event.ts +++ b/src/modules/identity/domain/events/user-created.event.ts @@ -2,5 +2,6 @@ import { DomainEvent } from '../../../../core/events/event.interface'; export class UserCreatedEvent implements DomainEvent { readonly name = 'UserCreated'; - constructor(public readonly payload: any, public readonly occurredAt: Date = new Date()) {} + readonly occurredAt: Date = new Date(); + constructor(public readonly payload: { userId: string; authentikId: string; email: string; username: string }) {} } diff --git a/src/modules/identity/domain/events/user-deleted.event.ts b/src/modules/identity/domain/events/user-deleted.event.ts deleted file mode 100644 index 07cc1d4..0000000 --- a/src/modules/identity/domain/events/user-deleted.event.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { DomainEvent } from '../../../../core/events/event.interface'; - -export class UserDeletedEvent implements DomainEvent { - readonly name = 'UserDeleted'; - constructor(public readonly payload: any, public readonly occurredAt: Date = new Date()) {} -} diff --git a/src/modules/identity/domain/events/user-disabled.event.ts b/src/modules/identity/domain/events/user-disabled.event.ts new file mode 100644 index 0000000..2f243fc --- /dev/null +++ b/src/modules/identity/domain/events/user-disabled.event.ts @@ -0,0 +1,7 @@ +import { DomainEvent } from '../../../../core/events/event.interface'; + +export class UserDisabledEvent implements DomainEvent { + readonly name = 'UserDisabled'; + readonly occurredAt: Date = new Date(); + constructor(public readonly payload: { userId: string }) {} +} diff --git a/src/modules/identity/domain/events/user-email-changed.event.ts b/src/modules/identity/domain/events/user-email-changed.event.ts new file mode 100644 index 0000000..db50cbc --- /dev/null +++ b/src/modules/identity/domain/events/user-email-changed.event.ts @@ -0,0 +1,7 @@ +import { DomainEvent } from '../../../../core/events/event.interface'; + +export class UserEmailChangedEvent implements DomainEvent { + readonly name = 'UserEmailChanged'; + readonly occurredAt: Date = new Date(); + constructor(public readonly payload: { userId: string; oldEmail: string; newEmail: string }) {} +} diff --git a/src/modules/identity/domain/events/user-enabled.event.ts b/src/modules/identity/domain/events/user-enabled.event.ts new file mode 100644 index 0000000..9ed1c60 --- /dev/null +++ b/src/modules/identity/domain/events/user-enabled.event.ts @@ -0,0 +1,7 @@ +import { DomainEvent } from '../../../../core/events/event.interface'; + +export class UserEnabledEvent implements DomainEvent { + readonly name = 'UserEnabled'; + readonly occurredAt: Date = new Date(); + constructor(public readonly payload: { userId: string }) {} +} diff --git a/src/modules/identity/domain/events/user-username-changed.event.ts b/src/modules/identity/domain/events/user-username-changed.event.ts new file mode 100644 index 0000000..7023331 --- /dev/null +++ b/src/modules/identity/domain/events/user-username-changed.event.ts @@ -0,0 +1,7 @@ +import { DomainEvent } from '../../../../core/events/event.interface'; + +export class UserUsernameChangedEvent implements DomainEvent { + readonly name = 'UserUsernameChanged'; + readonly occurredAt: Date = new Date(); + constructor(public readonly payload: { userId: string; oldUsername: string; newUsername: string }) {} +} diff --git a/src/modules/identity/domain/repositories/README.MD b/src/modules/identity/domain/repositories/README.MD new file mode 100644 index 0000000..1d0ee81 --- /dev/null +++ b/src/modules/identity/domain/repositories/README.MD @@ -0,0 +1 @@ +ini adalah interface, semua yang akan dibuat oleh application/handler harus dibuat disini dulu. \ No newline at end of file diff --git a/src/modules/identity/domain/repositories/permission.interface.ts b/src/modules/identity/domain/repositories/permission.interface.ts new file mode 100644 index 0000000..a603ee2 --- /dev/null +++ b/src/modules/identity/domain/repositories/permission.interface.ts @@ -0,0 +1,9 @@ +import { PermissionData } from '../entities/permission.entity'; + +export abstract class IPermission { + abstract find(params: { page?: number; limit?: number; search?: string | null }): Promise<{ data: PermissionData[]; total: number }>; + abstract getById(id: string): Promise; + abstract create(permission: PermissionData): Promise; + abstract update(permission: PermissionData): Promise; + abstract delete(id: string): Promise; +} diff --git a/src/modules/identity/domain/repositories/role.interface.ts b/src/modules/identity/domain/repositories/role.interface.ts new file mode 100644 index 0000000..0405e8b --- /dev/null +++ b/src/modules/identity/domain/repositories/role.interface.ts @@ -0,0 +1,12 @@ +import { RoleData } from '../entities/role.entity'; + +export abstract class IRole { + abstract getDefaultRole(): Promise; + abstract findById(roleId: string): Promise; + + abstract find(params: { page?: number; limit?: number; search?: string | null }): Promise<{ data: RoleData[]; total: number }>; + + abstract create(role: RoleData): Promise; + abstract update(role: RoleData): Promise; + abstract delete(roleId: string): Promise; +} diff --git a/src/modules/identity/domain/repositories/user.interface.ts b/src/modules/identity/domain/repositories/user.interface.ts new file mode 100644 index 0000000..7a34862 --- /dev/null +++ b/src/modules/identity/domain/repositories/user.interface.ts @@ -0,0 +1,29 @@ +import { UserData } from '../entities/user.entity'; + +export abstract class IUser { + abstract create(user: UserData): Promise; + + abstract existsById(userId: string): Promise; + abstract existByEmail(userEmail: string): Promise; + abstract getById(userId: string): Promise; + abstract getByEmail(userEmail: string): Promise; + + abstract findByAuthentikId(authentikId: string): Promise; + + abstract update(user: UserData): Promise; + + abstract find(params: { + page?: number; + limit?: number; + search?: string | null; + isActive?: boolean | null; + deleted?: boolean | null; + roleId?: string | null; + }): Promise<{ data: UserData[]; total: number }>; + + abstract softDelete(userId: string): Promise; + abstract restore(userId: string): Promise; + + abstract enable(userId: string): Promise; + abstract disable(userId: string): Promise; +} \ No newline at end of file diff --git a/src/modules/identity/domain/repositories/user.repository.interface.ts b/src/modules/identity/domain/repositories/user.repository.interface.ts deleted file mode 100644 index f76c4a8..0000000 --- a/src/modules/identity/domain/repositories/user.repository.interface.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { User } from '../entities/user.entity'; - -export abstract class UserRepository { - abstract create(user: User): Promise; - - abstract update(user: User): Promise; - - abstract findById( - id: string, - ): Promise; - - abstract findAll(): Promise; - - abstract existsByEmail( - email: string, - ): Promise; -} \ No newline at end of file diff --git a/src/modules/identity/identity.module.ts b/src/modules/identity/identity.module.ts index 9074380..91ccfe3 100644 --- a/src/modules/identity/identity.module.ts +++ b/src/modules/identity/identity.module.ts @@ -1,52 +1,115 @@ import { Module } from '@nestjs/common'; import { UsersController } from './presentation/controllers/users.controller'; +import { RolesController } from './presentation/controllers/roles.controller'; +import { PermissionsController } from './presentation/controllers/permissions.controller'; import { PrismaService } from '../../shared/prisma.service'; +import { UserService } from './application/services/user.service'; import { PrismaUserRepository } from './infrastructure/repositories/prisma-user.repository'; -import { UserRepository } from './domain/repositories/user.repository.interface'; -import { EventDispatcher } from '../../core/events/event-dispatcher'; +import { IUser } from './domain/repositories/user.interface'; + + import { JwtAuthGuard } from '../../core/auth/guards/jwt-auth.guard'; -import { JwtModule } from '@nestjs/jwt'; -import { CreateUserHandler } from './application/handlers/create-user.handler'; -import { FindUserHandler } from './application/handlers/find-user.handler'; -import { DeleteUserHandler } from './application/handlers/delete-user.handler'; -import { FindUsersHandler } from './application/handlers/find-users.handler'; -import { UpdateUserHandler } from './application/handlers/update-user.handler'; +import { CreateUserHandler } from './application/handlers/user/create-user.handler'; import { PermissionGuard } from '../authorization/presentation/guards/permission.guard'; import { Reflector } from '@nestjs/core'; +import { SyncIdentityHandler } from './application/handlers/user/sync-identity.handler'; +import { CurrentUserGuard } from './presentation/guards/current-user.guard'; +import { PrismaRoleRepository } from './infrastructure/repositories/prisma-role.repository'; +import { PrismaPermissionRepository } from './infrastructure/repositories/prisma-permission.repository'; +import { IRole } from './domain/repositories/role.interface'; +import { IPermission } from './domain/repositories/permission.interface'; +import { IAuthConfig } from './application/config/i-auth-config'; +import { EnvAuthConfig } from './application/config/env-auth-config'; +import { GetUsersHandler } from './application/handlers/user/get-users.handler'; +import { GetUserHandler } from './application/handlers/user/get-user.handler'; +import { GetCurrentUserHandler } from './application/handlers/user/get-current-user.handler'; +import { EnableUserHandler } from './application/handlers/user/enable-user.handler'; +import { DisableUserHandler } from './application/handlers/user/disable-user.handler'; +import { DeleteUserHandler } from './application/handlers/user/delete-user.handler'; +import { RestoreUserHandler } from './application/handlers/user/restore-user.handler'; +import { AssignRoleHandler } from './application/handlers/user/assign-role.handler'; +import { RemoveRoleHandler } from './application/handlers/user/remove-role.handler'; +import { AssignPermissionHandler } from './application/handlers/user/assign-permission.handler'; +import { RemovePermissionHandler } from './application/handlers/user/remove-permission.handler'; + +import { GetRolesHandler } from './application/handlers/role/get-roles.handler'; +import { GetRoleHandler } from './application/handlers/role/get-role.handler'; +import { CreateRoleHandler } from './application/handlers/role/create-role.handler'; +import { UpdateRoleHandler } from './application/handlers/role/update-role.handler'; +import { DeleteRoleHandler } from './application/handlers/role/delete-role.handler'; +import { RoleAssignPermissionHandler } from './application/handlers/role/assign-permission.handler'; +import { RoleRemovePermissionHandler } from './application/handlers/role/remove-permission.handler'; + +import { GetPermissionsHandler } from './application/handlers/permission/get-permissions.handler'; +import { GetPermissionHandler } from './application/handlers/permission/get-permission.handler'; +import { CreatePermissionHandler } from './application/handlers/permission/create-permission.handler'; +import { UpdatePermissionHandler } from './application/handlers/permission/update-permission.handler'; +import { DeletePermissionHandler } from './application/handlers/permission/delete-permission.handler'; @Module({ - imports: [ - JwtModule.register({ - secret: process.env.JWT_SECRET, - signOptions: { - expiresIn: '1d', - }, - }), - ], - controllers: [UsersController], providers: [ - //#region User - CreateUserHandler, - FindUserHandler, - FindUsersHandler, - UpdateUserHandler, + //#region User + // CreateUserHandler has been removed: provisioning disabled; users must be created in Authentik. + SyncIdentityHandler, + GetUsersHandler, + + GetUserHandler, + GetCurrentUserHandler, + EnableUserHandler, + DisableUserHandler, DeleteUserHandler, + RestoreUserHandler, + AssignRoleHandler, + RemoveRoleHandler, + AssignPermissionHandler, + RemovePermissionHandler, //#endregion + // role & permission handlers + GetRolesHandler, + GetRoleHandler, + CreateRoleHandler, + UpdateRoleHandler, + DeleteRoleHandler, + RoleAssignPermissionHandler, + RoleRemovePermissionHandler, + + GetPermissionsHandler, + GetPermissionHandler, + CreatePermissionHandler, + UpdatePermissionHandler, + DeletePermissionHandler, + JwtAuthGuard, + CurrentUserGuard, PermissionGuard, Reflector, - PrismaService, + PrismaService, PrismaUserRepository, + PrismaRoleRepository, + PrismaPermissionRepository, + UserService, { - provide: UserRepository, + provide: IUser, useClass: PrismaUserRepository, }, - { - provide: 'EVENT_DISPATCHER', - useValue: new EventDispatcher(), + { + provide: IRole, + useClass: PrismaRoleRepository, }, - ], + { + provide: IPermission, + useClass: PrismaPermissionRepository, + }, + { + provide: IAuthConfig, + useClass: EnvAuthConfig, + }, + ], + imports: [], + controllers: [UsersController, RolesController, PermissionsController], }) export class IdentityModule {} + + diff --git a/src/modules/identity/infrastructure/README.md b/src/modules/identity/infrastructure/README.md index 06bd408..006593d 100644 --- a/src/modules/identity/infrastructure/README.md +++ b/src/modules/identity/infrastructure/README.md @@ -1,12 +1 @@ -modules/identity/infrastructure/ - -Penjelasan: -Implementasi teknis untuk module identity, seperti Prisma repository, adapter implementations, dan data mappers. - -Contoh file: -- prisma/user.repository.ts (mengimplementasikan domain repository interface) -- adapter/identity-adapter.ts - -Aturan: -- Infrastruktur hanya mengimplementasikan interface domain; jangan memuat business rules. -- Import dari infrastructure ke domain harus satu arah: infrastructure -> domain (implementasi). +infrastrucute adalah tempat yang langsung berhubungan dengan dunia luar, misal db, server lain, JWT, dsb. \ No newline at end of file diff --git a/src/modules/identity/infrastructure/mappers/README.MD b/src/modules/identity/infrastructure/mappers/README.MD new file mode 100644 index 0000000..7a42625 --- /dev/null +++ b/src/modules/identity/infrastructure/mappers/README.MD @@ -0,0 +1 @@ +disini emngubah dari domain/entity menjadi format di database dan sebaliknya. \ No newline at end of file diff --git a/src/modules/identity/infrastructure/mappers/prisma-permission.mapper.ts b/src/modules/identity/infrastructure/mappers/prisma-permission.mapper.ts new file mode 100644 index 0000000..05abad0 --- /dev/null +++ b/src/modules/identity/infrastructure/mappers/prisma-permission.mapper.ts @@ -0,0 +1,23 @@ +import { PermissionData } from '../../domain/entities/permission.entity'; + +export class PrismaPermissionMapper { + static toDomain(model: any): PermissionData { + return PermissionData.restore({ + id: model.id, + code: model.code || model.name, + name: model.name, + description: model.description, + createdAt: model.createdAt, + updatedAt: model.updatedAt, + }); + } + + static toPersistence(permission: PermissionData) { + return { + id: permission.id, + code: permission.code, + name: permission.name, + description: permission.description, + }; + } +} diff --git a/src/modules/identity/infrastructure/mappers/prisma-role.mapper.ts b/src/modules/identity/infrastructure/mappers/prisma-role.mapper.ts new file mode 100644 index 0000000..133431d --- /dev/null +++ b/src/modules/identity/infrastructure/mappers/prisma-role.mapper.ts @@ -0,0 +1,27 @@ +import { RoleData } from '../../domain/entities/role.entity'; +import { PermissionData } from '../../domain/entities/permission.entity'; + +export class PrismaRoleMapper { + static toDomain(model: any): RoleData { + const permissions: PermissionData[] = (model.permissions || []).map((p: any) => + PermissionData.restore({ + id: p.id, + name: p.name, + description: p.description, + createdAt: p.createdAt, + updatedAt: p.updatedAt, + }), + ); + + return RoleData.restore({ + id: model.id, + code: model.code, + name: model.name, + description: model.description, + permissions, + isDefault: model.isDefault ?? false, + createdAt: model.createdAt, + updatedAt: model.updatedAt, + }); + } +} \ No newline at end of file diff --git a/src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts b/src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts index 1ce360d..2a8dcad 100644 --- a/src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts +++ b/src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts @@ -1,27 +1,73 @@ -import { User } from '../../domain/entities/user.entity'; +import { UserData } from '../../domain/entities/user.entity'; +import { RoleData } from '../../domain/entities/role.entity'; +import { PermissionData } from '../../domain/entities/permission.entity'; export class PrismaUserMapper { - static toDomain(model: any): User | null { - if (!model) { - return null; - } + static toDomain(model: any): UserData { + const roles: RoleData[] = (model.roles || []).map((ur: any) => { + const r = ur.role; + const permissions: PermissionData[] = (r?.permissions || []).map((rp: any) => + PermissionData.restore({ + id: rp.permission.id, + name: rp.permission.name, + description: rp.permission.description, + createdAt: rp.permission.createdAt, + updatedAt: rp.permission.updatedAt, + }), + ); - return User.restore({ + return RoleData.restore({ + id: r.id, + code: r.code, + name: r.name, + description: r.description, + permissions, + createdAt: r.createdAt, + updatedAt: r.updatedAt, + }); + }); + + const permissions: PermissionData[] = (model.permissions || []).map((up: any) => + PermissionData.restore({ + id: up.permission.id, + name: up.permission.name, + description: up.permission.description, + createdAt: up.permission.createdAt, + updatedAt: up.permission.updatedAt, + }), + ); + + return UserData.restore({ id: model.id, - name: model.name, + authentikId: model.authentikUserId || model.authentikId || null, + username: model.username || model.name || model.username, email: model.email, - password: model.password, - metadata: model.metadata, + password: model.password ?? null, + roles, + permissions, + isActive: model.isActive ?? true, + deletedAt: model.deletedAt || null, + lastSeenAt: model.lastSeenAt || null, + storageQuota: model.storageQuota !== undefined && model.storageQuota !== null ? Number(model.storageQuota) : undefined, + storageUsed: model.storageUsed !== undefined && model.storageUsed !== null ? Number(model.storageUsed) : undefined, }); } - static toPersistence(user: User) { - return { - id: user.id, - name: user.name, - email: user.email, - password: user.password, - metadata: user.metadata ?? {}, - }; + static toPersistence(userData: UserData) { + return { + id: userData.id, + authentikUserId: (userData as any).authentikUserId || userData.authentikId, + authentikId: userData.authentikId, + username: userData.username, + email: userData.email, + password: userData.password ?? null, + isActive: userData.isActive, + deletedAt: userData.deletedAt, + lastSeenAt: userData.lastSeenAt, + lastSyncedAt: (userData as any).lastSyncedAt, + syncStatus: (userData as any).syncStatus, + storageQuota: (userData as any).storageQuota, + storageUsed: (userData as any).storageUsed, + }; } } \ No newline at end of file diff --git a/src/modules/identity/infrastructure/persistence/README.md b/src/modules/identity/infrastructure/persistence/README.md new file mode 100644 index 0000000..07d7361 --- /dev/null +++ b/src/modules/identity/infrastructure/persistence/README.md @@ -0,0 +1 @@ +ini adalah tempat konfigurasi database. \ No newline at end of file diff --git a/src/modules/identity/infrastructure/repositories/README.md b/src/modules/identity/infrastructure/repositories/README.md index b7e780b..455ca20 100644 --- a/src/modules/identity/infrastructure/repositories/README.md +++ b/src/modules/identity/infrastructure/repositories/README.md @@ -1,11 +1 @@ -repositories/ - -Penjelasan: -Implementasi repository di layer infrastructure. Biasanya berisi Prisma queries dan mapping antara DB model dan domain entity. - -Contoh file: -- prisma/user.repository.ts - -Aturan: -- Repository mengimplementasikan interface di domain layer. -- Hindari business logic di repository. +disini yang menjalankan logika bisnisnya. \ No newline at end of file diff --git a/src/modules/identity/infrastructure/repositories/prisma-permission.repository.ts b/src/modules/identity/infrastructure/repositories/prisma-permission.repository.ts new file mode 100644 index 0000000..9459cff --- /dev/null +++ b/src/modules/identity/infrastructure/repositories/prisma-permission.repository.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../../../../shared/prisma.service'; +import { IPermission } from '../../domain/repositories/permission.interface'; +import { PrismaPermissionMapper } from '../mappers/prisma-permission.mapper'; +import { PermissionData } from '../../domain/entities/permission.entity'; + +@Injectable() +export class PrismaPermissionRepository implements IPermission { + constructor(private readonly prisma: PrismaService) {} + + async find(params: { page?: number; limit?: number; search?: string | null }) { + const page = params.page && params.page > 0 ? params.page : 1; + const limit = params.limit && params.limit > 0 ? params.limit : 10; + + const where: any = {}; + if (params.search) { + where.OR = [ + { name: { contains: params.search, mode: 'insensitive' } }, + { code: { contains: params.search, mode: 'insensitive' } }, + { description: { contains: params.search, mode: 'insensitive' } }, + ]; + } + + const [total, items] = await Promise.all([ + this.prisma.permission.count({ where }), + this.prisma.permission.findMany({ where, skip: (page - 1) * limit, take: limit, orderBy: { createdAt: 'desc' } }), + ]); + + return { data: items.map(i => PrismaPermissionMapper.toDomain(i)!), total }; + } + + async getById(id: string) { + const p = await this.prisma.permission.findUnique({ where: { id } }); + if (!p) throw new Error('Permission not found.'); + return PrismaPermissionMapper.toDomain(p); + } + + async create(permission: PermissionData) { + const base = PrismaPermissionMapper.toPersistence(permission); + const created = await this.prisma.permission.create({ data: base }); + return PrismaPermissionMapper.toDomain(created)!; + } + + async update(permission: PermissionData) { + const base = PrismaPermissionMapper.toPersistence(permission); + const updated = await this.prisma.permission.update({ where: { id: permission.id }, data: base }); + return PrismaPermissionMapper.toDomain(updated)!; + } + + async delete(id: string) { + await this.prisma.permission.delete({ where: { id } }); + } +} diff --git a/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts b/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts new file mode 100644 index 0000000..e2bbbee --- /dev/null +++ b/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts @@ -0,0 +1,109 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../../../../shared/prisma.service'; +import { IRole } from '../../domain/repositories/role.interface'; +import { PrismaRoleMapper } from '../mappers/prisma-role.mapper'; +import { RoleData } from '../../domain/entities/role.entity'; + +@Injectable() +export class PrismaRoleRepository implements IRole { + constructor(private readonly prisma: PrismaService) {} + + async getDefaultRole() { + const role = await this.prisma.role.findFirst({ + where: { isDefault: true }, + include: { permissions: true }, + }); + + if (!role) return null; + + return PrismaRoleMapper.toDomain(role); + } + + async findById(roleId: string) { + const role = await this.prisma.role.findUnique({ + where: { id: roleId }, + include: { permissions: true }, + }); + + if (!role) throw new Error('Role not found.'); + + return PrismaRoleMapper.toDomain(role); + } + + async find(params: { page?: number; limit?: number; search?: string | null }) { + const page = params.page && params.page > 0 ? params.page : 1; + const limit = params.limit && params.limit > 0 ? params.limit : 10; + + const where: any = {}; + if (params.search) { + where.OR = [ + { name: { contains: params.search, mode: 'insensitive' } }, + { code: { contains: params.search, mode: 'insensitive' } }, + { description: { contains: params.search, mode: 'insensitive' } }, + ]; + } + + const [total, items] = await Promise.all([ + this.prisma.role.count({ where }), + this.prisma.role.findMany({ where, skip: (page - 1) * limit, take: limit, orderBy: { createdAt: 'desc' }, include: { permissions: true } }), + ]); + + return { data: items.map(i => PrismaRoleMapper.toDomain(i)!), total }; + } + + async create(role: RoleData) { + const base: any = { + id: role.id, + code: role.code, + name: role.name, + description: role.description, + isDefault: role.isDefault, + }; + + if (role.permissions && role.permissions.length > 0) { + base.permissions = { + create: role.permissions.map(p => ({ permission: { connect: { id: p.id } }, assignedBy: 'system' })), + }; + } + + const created = await this.prisma.role.create({ data: base, include: { permissions: true } }); + return PrismaRoleMapper.toDomain(created)!; + } + + async update(role: RoleData) { + const base: any = { + code: role.code, + name: role.name, + description: role.description, + isDefault: role.isDefault, + }; + + // sync permissions via transaction + const permIds = role.permissions ? role.permissions.map(p => p.id) : []; + + await this.prisma.$transaction(async (prisma) => { + const currentPerms = await prisma.rolePermission.findMany({ where: { roleId: role.id } }); + const currentIds = currentPerms.map(p => p.permissionId); + + const toAdd = permIds.filter(id => !currentIds.includes(id)); + const toRemove = currentIds.filter(id => !permIds.includes(id)); + + if (toRemove.length > 0) { + await prisma.rolePermission.deleteMany({ where: { roleId: role.id, permissionId: { in: toRemove } } }); + } + + if (toAdd.length > 0) { + await prisma.rolePermission.createMany({ data: toAdd.map(pid => ({ roleId: role.id, permissionId: pid, assignedBy: 'system' })) as any, skipDuplicates: true }); + } + + await prisma.role.update({ where: { id: role.id }, data: base }); + }); + + const updated = await this.prisma.role.findUnique({ where: { id: role.id }, include: { permissions: true } }); + return PrismaRoleMapper.toDomain(updated)!; + } + + async delete(roleId: string) { + await this.prisma.role.delete({ where: { id: roleId } }); + } +} diff --git a/src/modules/identity/infrastructure/repositories/prisma-user.repository.ts b/src/modules/identity/infrastructure/repositories/prisma-user.repository.ts index 74bc603..e575bc2 100644 --- a/src/modules/identity/infrastructure/repositories/prisma-user.repository.ts +++ b/src/modules/identity/infrastructure/repositories/prisma-user.repository.ts @@ -1,64 +1,257 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../../../../shared/prisma.service'; -import { UserRepository } from '../../domain/repositories/user.repository.interface'; -import { User } from '../../domain/entities/user.entity'; +import { IUser } from '../../domain/repositories/user.interface'; +import { UserData } from '../../domain/entities/user.entity'; import { PrismaUserMapper } from '../mappers/prisma-user.mapper'; @Injectable() -export class PrismaUserRepository implements UserRepository { +export class PrismaUserRepository implements IUser { constructor(private readonly prisma: PrismaService) {} + + //#region Create - async findById(id: string) { - const user = await this.prisma.user.findUnique({ - where: { id }, - }); + async create(userData: UserData): Promise { + const base = PrismaUserMapper.toPersistence(userData); - return PrismaUserMapper.toDomain(user); - } + const data: any = { ...base }; - async findAll(): Promise { - const users = await this.prisma.user.findMany({ - where: { deleted_at: null }, - orderBy: { created_at: 'desc' }, - }); + if (userData.roles && userData.roles.length > 0) { + data.roles = { + create: userData.roles.map(r => ({ role: { connect: { id: r.id } }, assignedBy: 'system' })), + }; + } - return users.map(PrismaUserMapper.toDomain); - } - - async create(user: User): Promise { const created = await this.prisma.user.create({ - data: PrismaUserMapper.toPersistence(user), + data, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, }); return PrismaUserMapper.toDomain(created)!; } - async update(user: User): Promise { + //#endregion + + //#region Read + + async existsById(userId: string): Promise { + return ( + (await this.prisma.user.findUnique({ + where: { id: userId }, + select: { id: true }, + })) !== null + ); + } + + async existByEmail(userEmail: string): Promise { + return ( + (await this.prisma.user.findUnique({ + where: { email: userEmail }, + select: { email: true }, + })) !== null + ); + } + + async getById(userId: string): Promise { + const userData = await this.prisma.user.findUnique({ + where: { id: userId }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + + if (userData === null) throw new Error('User tidak ditemukan.'); + + return PrismaUserMapper.toDomain(userData); + } + + async getByEmail(userEmail: string): Promise { + const userData = await this.prisma.user.findUnique({ + where: { email: userEmail }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + + if (userData === null) throw new Error('User tidak ditemukan.'); + + return PrismaUserMapper.toDomain(userData); + } + + async findByAuthentikId(authentikId: string): Promise { + const userData = await this.prisma.user.findUnique({ + where: { authentikId }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + + if (!userData) return null; + + return PrismaUserMapper.toDomain(userData); + } + + async find(params: { + page?: number; + limit?: number; + search?: string | null; + isActive?: boolean | null; + deleted?: boolean | null; + roleId?: string | null; + }): Promise<{ data: UserData[]; total: number }> { + const page = params.page && params.page > 0 ? params.page : 1; + const limit = params.limit && params.limit > 0 ? params.limit : 10; + + const where: any = {}; + + if (params.search) { + where.OR = [ + { username: { contains: params.search, mode: 'insensitive' } }, + { email: { contains: params.search, mode: 'insensitive' } }, + ]; + } + + if (params.isActive !== undefined && params.isActive !== null) { + where.isActive = params.isActive; + } + + if (params.deleted !== undefined && params.deleted !== null) { + if (params.deleted) where.deletedAt = { not: null }; + else where.deletedAt = null; + } + + if (params.roleId) { + where.roles = { some: { roleId: params.roleId } }; + } + + const [total, items] = await Promise.all([ + this.prisma.user.count({ where }), + this.prisma.user.findMany({ + where, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + skip: (page - 1) * limit, + take: limit, + orderBy: { createdAt: 'desc' }, + }), + ]); + + return { data: items.map(i => PrismaUserMapper.toDomain(i)!), total }; + } + + async softDelete(userId: string): Promise { + await this.prisma.user.update({ where: { id: userId }, data: { deletedAt: new Date(), isActive: false } }); + } + + async restore(userId: string): Promise { const updated = await this.prisma.user.update({ - where: { - id: user.id, + where: { id: userId }, + data: { deletedAt: null }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, }, - data: PrismaUserMapper.toPersistence(user), }); return PrismaUserMapper.toDomain(updated)!; } - async delete(id: string) { - const user = await this.prisma.user.update({ - where: { id }, - data: { deleted_at: new Date() }, + async enable(userId: string): Promise { + const updated = await this.prisma.user.update({ + where: { id: userId }, + data: { isActive: true }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, }); - return PrismaUserMapper.toDomain(user); + + return PrismaUserMapper.toDomain(updated)!; } - async existsByEmail(email: string, excludeId?: string | null) { - const where: any = { email }; - if (excludeId) { - where.id = { not: excludeId }; - } + async disable(userId: string): Promise { + const updated = await this.prisma.user.update({ + where: { id: userId }, + data: { isActive: false }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); - const user = await this.prisma.user.findFirst({ where }); - return !!user; + return PrismaUserMapper.toDomain(updated)!; } + + async update(userData: UserData): Promise { + const base = PrismaUserMapper.toPersistence(userData); + + const data: any = { ...base }; + + // sync roles and permissions using transaction + const roleIds = userData.roles ? userData.roles.map(r => r.id) : []; + const permissionIds = userData.permissions ? userData.permissions.map(p => p.id) : []; + + // perform transaction: delete removed relations, create missing ones, update user + await this.prisma.$transaction(async (prisma) => { + // current roles + const currentRoles = await prisma.userRole.findMany({ where: { userId: userData.id } }); + const currentRoleIds = currentRoles.map(r => r.roleId); + + const toAddRoles = roleIds.filter(id => !currentRoleIds.includes(id)); + const toRemoveRoles = currentRoleIds.filter(id => !roleIds.includes(id)); + + if (toRemoveRoles.length > 0) { + await prisma.userRole.deleteMany({ where: { userId: userData.id, roleId: { in: toRemoveRoles } } }); + } + + if (toAddRoles.length > 0) { + await prisma.userRole.createMany({ + data: toAddRoles.map(rid => ({ userId: userData.id, roleId: rid, assignedBy: 'system' })) as any, + skipDuplicates: true, + }); + } + + // permissions + const currentPerms = await prisma.userPermission.findMany({ where: { userId: userData.id } }); + const currentPermIds = currentPerms.map(p => p.permissionId); + + const toAddPerms = permissionIds.filter(id => !currentPermIds.includes(id)); + const toRemovePerms = currentPermIds.filter(id => !permissionIds.includes(id)); + + if (toRemovePerms.length > 0) { + await prisma.userPermission.deleteMany({ where: { userId: userData.id, permissionId: { in: toRemovePerms } } }); + } + + if (toAddPerms.length > 0) { + await prisma.userPermission.createMany({ + data: toAddPerms.map(pid => ({ userId: userData.id, permissionId: pid, assignedBy: 'system' })) as any, + skipDuplicates: true, + }); + } + + // update base user fields + await prisma.user.update({ where: { id: userData.id }, data }); + }); + + const updated = await this.prisma.user.findUnique({ + where: { id: userData.id }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + + return PrismaUserMapper.toDomain(updated)!; + } + + //#endregion } + + diff --git a/src/modules/identity/presentation/controllers/permissions.controller.ts b/src/modules/identity/presentation/controllers/permissions.controller.ts new file mode 100644 index 0000000..73e3fe9 --- /dev/null +++ b/src/modules/identity/presentation/controllers/permissions.controller.ts @@ -0,0 +1,67 @@ +import { Controller, Get, Post, Patch, Delete, Param, UseGuards, Body, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { JwtAuthGuard } from '../../../../core/auth/guards/jwt-auth.guard'; +import { PermissionGuard } from '../../../authorization/presentation/guards/permission.guard'; +import { CurrentUserGuard } from '../guards/current-user.guard'; +import { Permissions } from '../../../authorization/presentation/decorators/permission.decorator'; +import { PermissionType } from '../../../../common/constants/permission.constants'; + +import { GetPermissionsHandler } from '../../application/handlers/permission/get-permissions.handler'; +import { GetPermissionHandler } from '../../application/handlers/permission/get-permission.handler'; +import { CreatePermissionHandler } from '../../application/handlers/permission/create-permission.handler'; +import { UpdatePermissionHandler } from '../../application/handlers/permission/update-permission.handler'; +import { DeletePermissionHandler } from '../../application/handlers/permission/delete-permission.handler'; + +@ApiTags('Permissions') +@Controller('permissions') +@UseGuards(JwtAuthGuard, CurrentUserGuard, PermissionGuard) +export class PermissionsController { + constructor( + private readonly getPermissionsHandler: GetPermissionsHandler, + private readonly getPermissionHandler: GetPermissionHandler, + private readonly createPermissionHandler: CreatePermissionHandler, + private readonly updatePermissionHandler: UpdatePermissionHandler, + private readonly deletePermissionHandler: DeletePermissionHandler, + ) {} + + @Get() + @Permissions(PermissionType.PERMISSION_READ) + @ApiOperation({ summary: 'List permissions' }) + async findAll(@Query() query: any) { + const res = await this.getPermissionsHandler.execute(query); + return { success: true, data: res.data, meta: { total: res.total } }; + } + + @Get(':id') + @Permissions(PermissionType.PERMISSION_READ) + @ApiOperation({ summary: 'Get permission' }) + async findOne(@Param('id') id: string) { + const p = await this.getPermissionHandler.execute(id); + return { success: true, data: p, meta: {} }; + } + + @Post() + @Permissions(PermissionType.PERMISSION_CREATE) + @ApiOperation({ summary: 'Create permission' }) + async create(@Body() body: any) { + const created = await this.createPermissionHandler.execute(body); + return { success: true, data: created, meta: {} }; + } + + @Patch(':id') + @Permissions(PermissionType.PERMISSION_UPDATE) + @ApiOperation({ summary: 'Update permission' }) + async update(@Param('id') id: string, @Body() body: any) { + const updated = await this.updatePermissionHandler.execute(id, body); + return { success: true, data: updated, meta: {} }; + } + + @Delete(':id') + @Permissions(PermissionType.PERMISSION_DELETE) + @ApiOperation({ summary: 'Delete permission' }) + async remove(@Param('id') id: string) { + await this.deletePermissionHandler.execute(id); + return { success: true, data: null, meta: {} }; + } +} diff --git a/src/modules/identity/presentation/controllers/roles.controller.ts b/src/modules/identity/presentation/controllers/roles.controller.ts new file mode 100644 index 0000000..6a8d23e --- /dev/null +++ b/src/modules/identity/presentation/controllers/roles.controller.ts @@ -0,0 +1,88 @@ +import { Controller, Get, Post, Patch, Delete, Param, UseGuards, Body, Query } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; + +import { JwtAuthGuard } from '../../../../core/auth/guards/jwt-auth.guard'; +import { PermissionGuard } from '../../../authorization/presentation/guards/permission.guard'; +import { CurrentUserGuard } from '../guards/current-user.guard'; +import { Permissions } from '../../../authorization/presentation/decorators/permission.decorator'; +import { PermissionType } from '../../../../common/constants/permission.constants'; + +import { GetRolesHandler } from '../../application/handlers/role/get-roles.handler'; +import { GetRoleHandler } from '../../application/handlers/role/get-role.handler'; +import { CreateRoleHandler } from '../../application/handlers/role/create-role.handler'; +import { UpdateRoleHandler } from '../../application/handlers/role/update-role.handler'; +import { DeleteRoleHandler } from '../../application/handlers/role/delete-role.handler'; +import { RoleAssignPermissionHandler } from '../../application/handlers/role/assign-permission.handler'; +import { RoleRemovePermissionHandler } from '../../application/handlers/role/remove-permission.handler'; + +@ApiTags('Roles') +@Controller('roles') +@UseGuards(JwtAuthGuard, CurrentUserGuard, PermissionGuard) +export class RolesController { + constructor( + private readonly getRolesHandler: GetRolesHandler, + private readonly getRoleHandler: GetRoleHandler, + private readonly createRoleHandler: CreateRoleHandler, + private readonly updateRoleHandler: UpdateRoleHandler, + private readonly deleteRoleHandler: DeleteRoleHandler, + private readonly roleAssignPermissionHandler: RoleAssignPermissionHandler, + private readonly roleRemovePermissionHandler: RoleRemovePermissionHandler, + ) {} + + @Get() + @Permissions(PermissionType.ROLE_READ) + @ApiOperation({ summary: 'List roles' }) + async findAll(@Query() query: any) { + const res = await this.getRolesHandler.execute(query); + return { success: true, data: res.data, meta: { total: res.total } }; + } + + @Get(':id') + @Permissions(PermissionType.ROLE_READ) + @ApiOperation({ summary: 'Get role' }) + async findOne(@Param('id') id: string) { + const role = await this.getRoleHandler.execute(id); + return { success: true, data: role, meta: {} }; + } + + @Post() + @Permissions(PermissionType.ROLE_CREATE) + @ApiOperation({ summary: 'Create role' }) + async create(@Body() body: any) { + const created = await this.createRoleHandler.execute(body); + return { success: true, data: created, meta: {} }; + } + + @Patch(':id') + @Permissions(PermissionType.ROLE_UPDATE) + @ApiOperation({ summary: 'Update role' }) + async update(@Param('id') id: string, @Body() body: any) { + const updated = await this.updateRoleHandler.execute(id, body); + return { success: true, data: updated, meta: {} }; + } + + @Delete(':id') + @Permissions(PermissionType.ROLE_DELETE) + @ApiOperation({ summary: 'Delete role' }) + async remove(@Param('id') id: string) { + await this.deleteRoleHandler.execute(id); + return { success: true, data: null, meta: {} }; + } + + @Post(':id/permissions') + @Permissions(PermissionType.ROLE_UPDATE) + @ApiOperation({ summary: 'Assign permission to role' }) + async assignPermission(@Param('id') id: string, @Body() body: any) { + const permissionId = body.permissionId; + const updated = await this.roleAssignPermissionHandler.execute(id, permissionId); + return { success: true, data: updated, meta: {} }; + } + + @Delete(':id/permissions/:permissionId') + @Permissions(PermissionType.ROLE_UPDATE) + @ApiOperation({ summary: 'Remove permission from role' }) + async removePermission(@Param('id') id: string, @Param('permissionId') permissionId: string) { + const updated = await this.roleRemovePermissionHandler.execute(id, permissionId); + return { success: true, data: updated, meta: {} }; + } +} diff --git a/src/modules/identity/presentation/controllers/users.controller.ts b/src/modules/identity/presentation/controllers/users.controller.ts index edd1057..73e766f 100644 --- a/src/modules/identity/presentation/controllers/users.controller.ts +++ b/src/modules/identity/presentation/controllers/users.controller.ts @@ -1,101 +1,134 @@ -import { - Controller, - Get, - Post, - Delete, - Param, - UseGuards, - Body, - Patch -} from '@nestjs/common'; +import { Controller, Get, Param, UseGuards, Query, Patch, Delete, Post, Body, Req } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { JwtAuthGuard } from '../../../../core/auth/guards/jwt-auth.guard'; import { PermissionGuard } from '../../../authorization/presentation/guards/permission.guard'; +import { CurrentUserGuard } from '../guards/current-user.guard'; import { Permissions } from '../../../authorization/presentation/decorators/permission.decorator'; +import { PermissionType } from '../../../../common/constants/permission.constants'; -import { FindUserHandler } from '../../application/handlers/find-user.handler'; -import { FindUsersHandler } from '../../application/handlers/find-users.handler'; -import { CreateUserHandler } from '../../application/handlers/create-user.handler'; -import { UpdateUserHandler } from '../../application/handlers/update-user.handler'; -import { DeleteUserHandler } from '../../application/handlers/delete-user.handler'; -import { CreateUserDto } from '../dto/create-user.dto'; -import { UpdateUserDto } from '../dto/update-user.dto'; +import { GetUsersHandler } from '../../application/handlers/user/get-users.handler'; +import { GetUserHandler } from '../../application/handlers/user/get-user.handler'; +import { GetCurrentUserHandler } from '../../application/handlers/user/get-current-user.handler'; +import { EnableUserHandler } from '../../application/handlers/user/enable-user.handler'; +import { DisableUserHandler } from '../../application/handlers/user/disable-user.handler'; +import { DeleteUserHandler } from '../../application/handlers/user/delete-user.handler'; +import { RestoreUserHandler } from '../../application/handlers/user/restore-user.handler'; +import { AssignRoleHandler } from '../../application/handlers/user/assign-role.handler'; +import { RemoveRoleHandler } from '../../application/handlers/user/remove-role.handler'; +import { AssignPermissionHandler } from '../../application/handlers/user/assign-permission.handler'; +import { RemovePermissionHandler } from '../../application/handlers/user/remove-permission.handler'; @ApiTags('Users') -@Controller('api/v1/users') -@UseGuards(JwtAuthGuard, PermissionGuard) +@Controller('users') +@UseGuards(JwtAuthGuard, CurrentUserGuard, PermissionGuard) export class UsersController { constructor( - private readonly createUserHandler: CreateUserHandler, - private readonly findUserHandler: FindUserHandler, - private readonly findUsersHandler: FindUsersHandler, - private readonly updateUserHandler: UpdateUserHandler, + private readonly getUsersHandler: GetUsersHandler, + private readonly getUserHandler: GetUserHandler, + private readonly getCurrentUserHandler: GetCurrentUserHandler, + private readonly enableUserHandler: EnableUserHandler, + private readonly disableUserHandler: DisableUserHandler, private readonly deleteUserHandler: DeleteUserHandler, + private readonly restoreUserHandler: RestoreUserHandler, + private readonly assignRoleHandler: AssignRoleHandler, + private readonly removeRoleHandler: RemoveRoleHandler, + private readonly assignPermissionHandler: AssignPermissionHandler, + private readonly removePermissionHandler: RemovePermissionHandler, ) {} - @Post() - @Permissions('USER_CREATE') - @ApiOperation({ summary: 'Create user' }) - async create(@Body() dto: CreateUserDto) { - const data = await this.createUserHandler.execute(dto); + @Get() + @Permissions(PermissionType.USER_READ) + @ApiOperation({ summary: 'List users' }) + async findAll(@Query() query: any) { + const res = await this.getUsersHandler.execute(query); return { success: true, - data, - meta: {}, - } + data: res.data.map(u => (u as any).toResponse ? (u as any).toResponse() : u), + meta: { total: res.total }, + }; + } + + @Get('me') + @ApiOperation({ summary: 'Get current user' }) + async me(@Req() req: any) { + const user = await this.getCurrentUserHandler.execute(req.currentUser); + + return { success: true, data: (user as any).toResponse ? (user as any).toResponse() : user, meta: {} }; } @Get(':id') - @Permissions('USER_READ') + @Permissions(PermissionType.USER_READ) @ApiOperation({ summary: 'Get user by id' }) - async getById(@Param('id') id: string) { - const data = await this.findUserHandler.execute(id); - - return { - success: true, - data, - meta: {}, - }; + async findOne(@Param('id') id: string) { + const user = await this.getUserHandler.execute(id); + return { success: true, data: (user as any).toResponse ? (user as any).toResponse() : user, meta: {} }; } - @Get() - @Permissions('USER_READ_ADMIN') - @ApiOperation({ summary: 'Get All User' }) - async getAll() { - const data = await this.findUsersHandler.execute(); - - return { - success: true, - data, - meta: {}, - }; + @Patch(':id/enable') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Enable user' }) + async enable(@Param('id') id: string) { + const updated = await this.enableUserHandler.execute(id); + return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; } - @Patch(':id') - @Permissions('USER_UPDATE') - @ApiOperation({ summary: 'Update User' }) - async update(@Param('id') id:string, @Body() dto:UpdateUserDto) { - const data = await this.updateUserHandler.execute(id, dto); - - return { - success: true, - data, - meta: {}, - } + @Patch(':id/disable') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Disable user' }) + async disable(@Param('id') id: string) { + const updated = await this.disableUserHandler.execute(id); + return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; } @Delete(':id') - @Permissions('USER_DELETE') - @ApiOperation({ summary: 'Delete user by id' }) - async delete(@Param('id') id: string) { + @Permissions(PermissionType.USER_DELETE) + @ApiOperation({ summary: 'Soft delete user' }) + async remove(@Param('id') id: string) { await this.deleteUserHandler.execute(id); - - return { - success: true, - data: null, - meta: {}, - }; + return { success: true, data: null, meta: {} }; } -} \ No newline at end of file + + @Post(':id/restore') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Restore user' }) + async restore(@Param('id') id: string) { + const restored = await this.restoreUserHandler.execute(id); + return { success: true, data: (restored as any).toResponse ? (restored as any).toResponse() : restored, meta: {} }; + } + + @Post(':id/roles') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Assign role to user' }) + async assignRole(@Param('id') id: string, @Body() body: any) { + const roleId = body.roleId; + const updated = await this.assignRoleHandler.execute(id, roleId); + return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; + } + + @Delete(':id/roles/:roleId') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Remove role from user' }) + async removeRole(@Param('id') id: string, @Param('roleId') roleId: string) { + const updated = await this.removeRoleHandler.execute(id, roleId); + return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; + } + + @Post(':id/permissions') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Assign permission to user' }) + async assignPermission(@Param('id') id: string, @Body() body: any) { + const permissionId = body.permissionId; + const updated = await this.assignPermissionHandler.execute(id, permissionId); + return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; + } + + @Delete(':id/permissions/:permissionId') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Remove permission from user' }) + async removePermission(@Param('id') id: string, @Param('permissionId') permissionId: string) { + const updated = await this.removePermissionHandler.execute(id, permissionId); + return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; + } +} diff --git a/src/modules/identity/presentation/dto/create-permission.dto.ts b/src/modules/identity/presentation/dto/create-permission.dto.ts new file mode 100644 index 0000000..c030a48 --- /dev/null +++ b/src/modules/identity/presentation/dto/create-permission.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, IsNotEmpty, IsOptional } from 'class-validator'; + +export class CreatePermissionDto { + @ApiProperty() + @IsString() + @IsNotEmpty() + code!: string; + + @ApiProperty() + @IsString() + @IsNotEmpty() + name!: string; + + @ApiProperty({ required: false }) + @IsOptional() + @IsString() + description?: string; +} diff --git a/src/modules/identity/presentation/dto/create-role.dto.ts b/src/modules/identity/presentation/dto/create-role.dto.ts new file mode 100644 index 0000000..6104e72 --- /dev/null +++ b/src/modules/identity/presentation/dto/create-role.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, IsNotEmpty, IsOptional } from 'class-validator'; + +export class CreateRoleDto { + @ApiProperty() + @IsString() + @IsNotEmpty() + code!: string; + + @ApiProperty() + @IsString() + @IsNotEmpty() + name!: string; + + @ApiProperty({ required: false }) + @IsOptional() + @IsString() + description?: string; + + @ApiProperty({ required: false }) + @IsOptional() + isDefault?: boolean; +} diff --git a/src/modules/identity/presentation/dto/create-user.dto.ts b/src/modules/identity/presentation/dto/create-user.dto.ts index adcfdea..ee203a6 100644 --- a/src/modules/identity/presentation/dto/create-user.dto.ts +++ b/src/modules/identity/presentation/dto/create-user.dto.ts @@ -1,11 +1,11 @@ -import { IsEmail, IsNotEmpty, IsString, Length } from 'class-validator'; -import { ApiProperty } from '@nestjs/swagger'; +import { IsEmail, IsNotEmpty, IsString, Length, IsOptional, IsInt, Min } from 'class-validator'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; export class CreateUserDto { @ApiProperty({ example: 'John Doe' }) @IsString() @IsNotEmpty() - name!: string; + username!: string; @ApiProperty({ example: 'user@example.com' }) @IsEmail() @@ -16,6 +16,15 @@ export class CreateUserDto { @Length(8, 128) password!: string; - @ApiProperty({ required: false }) - metadata?: Record; + @ApiPropertyOptional({ example: 10737418240 }) + @IsOptional() + @IsInt() + @Min(0) + storageQuota?: number; + + @ApiPropertyOptional({ example: 0 }) + @IsOptional() + @IsInt() + @Min(0) + storageUsed?: number; } \ No newline at end of file diff --git a/src/modules/identity/presentation/dto/update-permission.dto.ts b/src/modules/identity/presentation/dto/update-permission.dto.ts new file mode 100644 index 0000000..a942d25 --- /dev/null +++ b/src/modules/identity/presentation/dto/update-permission.dto.ts @@ -0,0 +1,21 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreatePermissionDto } from './create-permission.dto'; +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class UpdatePermissionDto extends PartialType(CreatePermissionDto) { + @ApiPropertyOptional() + @IsOptional() + @IsString() + name?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + description?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + code?: string; +} diff --git a/src/modules/identity/presentation/dto/update-role.dto.ts b/src/modules/identity/presentation/dto/update-role.dto.ts new file mode 100644 index 0000000..2a72472 --- /dev/null +++ b/src/modules/identity/presentation/dto/update-role.dto.ts @@ -0,0 +1,20 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateRoleDto } from './create-role.dto'; +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class UpdateRoleDto extends PartialType(CreateRoleDto) { + @ApiPropertyOptional() + @IsOptional() + @IsString() + name?: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + description?: string; + + @ApiPropertyOptional() + @IsOptional() + isDefault?: boolean; +} diff --git a/src/modules/identity/presentation/dto/update-user.dto.ts b/src/modules/identity/presentation/dto/update-user.dto.ts index 1dbe93f..968d85a 100644 --- a/src/modules/identity/presentation/dto/update-user.dto.ts +++ b/src/modules/identity/presentation/dto/update-user.dto.ts @@ -1,7 +1,7 @@ import { PartialType } from '@nestjs/mapped-types'; import { CreateUserDto } from './create-user.dto'; import { ApiPropertyOptional } from '@nestjs/swagger'; -import { IsOptional, IsString, IsEmail, Length } from 'class-validator'; +import { IsOptional, IsString, IsEmail, Length, IsInt, Min } from 'class-validator'; export class UpdateUserDto extends PartialType(CreateUserDto) { @ApiPropertyOptional() @@ -22,4 +22,16 @@ export class UpdateUserDto extends PartialType(CreateUserDto) { @ApiPropertyOptional() @IsOptional() metadata?: Record; + + @ApiPropertyOptional({ example: 10737418240 }) + @IsOptional() + @IsInt() + @Min(0) + storageQuota?: number; + + @ApiPropertyOptional({ example: 0 }) + @IsOptional() + @IsInt() + @Min(0) + storageUsed?: number; } diff --git a/src/modules/identity/presentation/guards/current-user.guard.ts b/src/modules/identity/presentation/guards/current-user.guard.ts new file mode 100644 index 0000000..f26fcd6 --- /dev/null +++ b/src/modules/identity/presentation/guards/current-user.guard.ts @@ -0,0 +1,27 @@ +import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common'; +import { SyncIdentityHandler } from '../../application/handlers/user/sync-identity.handler'; +import { IdentityData } from '../../../../core/auth/interfaces/identity-data'; + +@Injectable() +export class CurrentUserGuard implements CanActivate { + constructor( + private readonly syncIdentityHandler: SyncIdentityHandler, + ) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest() as any; + + const identity = request.identity as IdentityData | undefined; + + if (!identity) { + throw new UnauthorizedException('Identity is missing.'); + } + + const user = await this.syncIdentityHandler.execute(identity); + + // attach domain user as currentUser + request.currentUser = user; + + return true; + } +} diff --git a/test-results/.last-run.json b/test-results/.last-run.json new file mode 100644 index 0000000..d8b6315 --- /dev/null +++ b/test-results/.last-run.json @@ -0,0 +1,7 @@ +{ + "status": "failed", + "failedTests": [ + "c6e079ee66bcf8f961dc-c23b04c7b0e0ed54fd6c", + "c6e079ee66bcf8f961dc-eb7b68d84e422e1710d8" + ] +} \ No newline at end of file diff --git a/test-results/user.integration-Integrati-096a0-ser-and-issues-internal-JWT/error-context.md b/test-results/user.integration-Integrati-096a0-ser-and-issues-internal-JWT/error-context.md new file mode 100644 index 0000000..9978184 --- /dev/null +++ b/test-results/user.integration-Integrati-096a0-ser-and-issues-internal-JWT/error-context.md @@ -0,0 +1,193 @@ +# Instructions + +- Following Playwright test failed. +- Explain why, be concise, respect Playwright best practices. +- Provide a snippet of code with the fix, if possible. + +# Test info + +- Name: user.integration.spec.ts >> Integration - User lifecycle >> login via Authentik creates local user and issues internal JWT +- Location: tests\integration\user.integration.spec.ts:82:7 + +# Error details + +``` +Error: No jwt cookie set after login. Saved screenshot/cookies/page to tests/test-artifacts +``` + +# Test source + +```ts + 19 | // increase default timeout for slow integration flows + 20 | test.setTimeout(60000); + 21 | + 22 | async function adminLogin() { + 23 | const adminUser = process.env.ADMIN_USERNAME; + 24 | const adminPass = process.env.ADMIN_PASSWORD; + 25 | if (!adminUser || !adminPass) throw new Error('ADMIN_USERNAME/ADMIN_PASSWORD must be set in env for integration tests'); + 26 | + 27 | const browser = await chromium.launch({ headless: true }); + 28 | const context = await browser.newContext(); + 29 | const page = await context.newPage(); + 30 | await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + 31 | + 32 | try { + 33 | await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + 34 | const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + 35 | if (usernameInput) await usernameInput.fill(adminUser); + 36 | const passwordInput = await page.$('input[type="password"], input[name="password"]'); + 37 | if (passwordInput) await passwordInput.fill(adminPass); + 38 | const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + 39 | if (submitButton) { + 40 | await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + 41 | } + 42 | } catch (e) { + 43 | // ignore if login form not present + 44 | } + 45 | + 46 | await page.waitForTimeout(1000); + 47 | const cookies = await context.cookies(); + 48 | const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + 49 | await context.close(); + 50 | await browser.close(); + 51 | + 52 | if (!jwtCookie) throw new Error('Admin login failed: no session cookie'); + 53 | return jwtCookie.value; + 54 | } + 55 | + 56 | async function apiRequest(path, token, opts = {}) { + 57 | const headers = Object.assign({ 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, opts.headers || {}); + 58 | const method = opts.method || 'GET'; + 59 | const body = opts.body ? JSON.stringify(opts.body) : undefined; + 60 | let url; + 61 | try { + 62 | url = new URL(path, BASE_URL).toString(); + 63 | } catch (e) { + 64 | throw new Error(`Invalid BASE_URL for integration tests: ${BASE_URL}`); + 65 | } + 66 | const res = await fetch(url, { method, headers, body }); + 67 | let json = null; + 68 | try { json = await res.json(); } catch (e) { json = null; } + 69 | return { status: res.status, body: json }; + 70 | } + 71 | + 72 | + 73 | test.describe('Integration - User lifecycle', () => { + 74 | let adminToken; + 75 | let createdUser = null; + 76 | + 77 | test.beforeAll(async () => { + 78 | // Integration tests assume test users are created in Authentik prior to running. + 79 | // RayLab will create local user record upon first successful login via Authentik. + 80 | }); + 81 | + 82 | test('login via Authentik creates local user and issues internal JWT', async () => { + 83 | // use browser flow to login as the test user + 84 | const browser = await chromium.launch({ headless: true }); + 85 | const context = await browser.newContext(); + 86 | const page = await context.newPage(); + 87 | await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + 88 | + 89 | try { + 90 | await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + 91 | const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + 92 | if (usernameInput) await usernameInput.fill(TEST_USER_USERNAME); + 93 | const passwordInput = await page.$('input[type="password"], input[name="password"]'); + 94 | if (passwordInput) await passwordInput.fill(TEST_USER_PASSWORD); + 95 | const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + 96 | if (submitButton) { + 97 | await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + 98 | } + 99 | } catch (e) { + 100 | // ignore if login form not present + 101 | } + 102 | + 103 | await page.waitForTimeout(1000); + 104 | const cookies = await context.cookies(); + 105 | const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + 106 | const refreshCookie = cookies.find(c => c.name === 'raylab_refresh'); + 107 | + 108 | if (!jwtCookie) { + 109 | const fs = require('fs'); + 110 | const dir = 'tests/test-artifacts'; + 111 | if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + 112 | const ts = Date.now(); + 113 | await page.screenshot({ path: `${dir}/failed-login-${ts}.png`, fullPage: true }); + 114 | fs.writeFileSync(`${dir}/failed-login-${ts}-cookies.json`, JSON.stringify(cookies, null, 2), 'utf-8'); + 115 | const content = await page.content(); + 116 | fs.writeFileSync(`${dir}/failed-login-${ts}-page.html`, content, 'utf-8'); + 117 | await context.close(); + 118 | await browser.close(); +> 119 | throw new Error(`No jwt cookie set after login. Saved screenshot/cookies/page to ${dir}`); + | ^ Error: No jwt cookie set after login. Saved screenshot/cookies/page to tests/test-artifacts + 120 | } + 121 | + 122 | // ensure we have an internal jwt cookie + 123 | expect(jwtCookie).toBeDefined(); + 124 | expect(jwtCookie.value).toBeTruthy(); + 125 | + 126 | // validate /auth/me using the internal jwt + 127 | const token = jwtCookie.value; + 128 | const meUrl = new URL('/auth/me', BASE_URL).toString(); + 129 | const meResp = await fetch(meUrl, { headers: { Authorization: `Bearer ${token}` } }); + 130 | const meJson = await meResp.json(); + 131 | expect(meResp.status).toBeLessThan(300); + 132 | expect(meJson.success).toBe(true); + 133 | expect(meJson.data).toBeDefined(); + 134 | expect(meJson.data.email).toBe(TEST_USER_EMAIL); + 135 | + 136 | await context.close(); + 137 | await browser.close(); + 138 | }); + 139 | + 140 | test('login via Authentik (created user) issues internal JWT', async () => { + 141 | // use browser flow to login as the created user + 142 | const browser = await chromium.launch({ headless: true }); + 143 | const context = await browser.newContext(); + 144 | const page = await context.newPage(); + 145 | await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + 146 | + 147 | try { + 148 | await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + 149 | const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + 150 | if (usernameInput) await usernameInput.fill(TEST_USER_USERNAME); + 151 | const passwordInput = await page.$('input[type="password"], input[name="password"]'); + 152 | if (passwordInput) await passwordInput.fill(TEST_USER_PASSWORD); + 153 | const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + 154 | if (submitButton) { + 155 | await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + 156 | } + 157 | } catch (e) { + 158 | // ignore if login form not present + 159 | } + 160 | + 161 | await page.waitForTimeout(1000); + 162 | const cookies = await context.cookies(); + 163 | const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + 164 | + 165 | if (!jwtCookie) { + 166 | const fs = require('fs'); + 167 | const dir = 'tests/test-artifacts'; + 168 | if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + 169 | const ts = Date.now(); + 170 | await page.screenshot({ path: `${dir}/failed-login-2-${ts}.png`, fullPage: true }); + 171 | fs.writeFileSync(`${dir}/failed-login-2-${ts}-cookies.json`, JSON.stringify(cookies, null, 2), 'utf-8'); + 172 | const content = await page.content(); + 173 | fs.writeFileSync(`${dir}/failed-login-2-${ts}-page.html`, content, 'utf-8'); + 174 | await context.close(); + 175 | await browser.close(); + 176 | throw new Error(`No jwt cookie set after login (second test). Saved screenshot/cookies/page to ${dir}`); + 177 | } + 178 | + 179 | await context.close(); + 180 | await browser.close(); + 181 | + 182 | expect(jwtCookie).toBeDefined(); + 183 | expect(jwtCookie.value).toBeTruthy(); + 184 | }); + 185 | + 186 | // Cleanup via API provisioning has been removed. If test environment requires cleanup, perform manually in Authentik. + 187 | + 188 | }); + 189 | +``` \ No newline at end of file diff --git a/test-results/user.integration-Integrati-8202b-ed-user-issues-internal-JWT/error-context.md b/test-results/user.integration-Integrati-8202b-ed-user-issues-internal-JWT/error-context.md new file mode 100644 index 0000000..18988bc --- /dev/null +++ b/test-results/user.integration-Integrati-8202b-ed-user-issues-internal-JWT/error-context.md @@ -0,0 +1,136 @@ +# Instructions + +- Following Playwright test failed. +- Explain why, be concise, respect Playwright best practices. +- Provide a snippet of code with the fix, if possible. + +# Test info + +- Name: user.integration.spec.ts >> Integration - User lifecycle >> login via Authentik (created user) issues internal JWT +- Location: tests\integration\user.integration.spec.ts:140:7 + +# Error details + +``` +Error: No jwt cookie set after login (second test). Saved screenshot/cookies/page to tests/test-artifacts +``` + +# Test source + +```ts + 76 | + 77 | test.beforeAll(async () => { + 78 | // Integration tests assume test users are created in Authentik prior to running. + 79 | // RayLab will create local user record upon first successful login via Authentik. + 80 | }); + 81 | + 82 | test('login via Authentik creates local user and issues internal JWT', async () => { + 83 | // use browser flow to login as the test user + 84 | const browser = await chromium.launch({ headless: true }); + 85 | const context = await browser.newContext(); + 86 | const page = await context.newPage(); + 87 | await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + 88 | + 89 | try { + 90 | await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + 91 | const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + 92 | if (usernameInput) await usernameInput.fill(TEST_USER_USERNAME); + 93 | const passwordInput = await page.$('input[type="password"], input[name="password"]'); + 94 | if (passwordInput) await passwordInput.fill(TEST_USER_PASSWORD); + 95 | const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + 96 | if (submitButton) { + 97 | await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + 98 | } + 99 | } catch (e) { + 100 | // ignore if login form not present + 101 | } + 102 | + 103 | await page.waitForTimeout(1000); + 104 | const cookies = await context.cookies(); + 105 | const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + 106 | const refreshCookie = cookies.find(c => c.name === 'raylab_refresh'); + 107 | + 108 | if (!jwtCookie) { + 109 | const fs = require('fs'); + 110 | const dir = 'tests/test-artifacts'; + 111 | if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + 112 | const ts = Date.now(); + 113 | await page.screenshot({ path: `${dir}/failed-login-${ts}.png`, fullPage: true }); + 114 | fs.writeFileSync(`${dir}/failed-login-${ts}-cookies.json`, JSON.stringify(cookies, null, 2), 'utf-8'); + 115 | const content = await page.content(); + 116 | fs.writeFileSync(`${dir}/failed-login-${ts}-page.html`, content, 'utf-8'); + 117 | await context.close(); + 118 | await browser.close(); + 119 | throw new Error(`No jwt cookie set after login. Saved screenshot/cookies/page to ${dir}`); + 120 | } + 121 | + 122 | // ensure we have an internal jwt cookie + 123 | expect(jwtCookie).toBeDefined(); + 124 | expect(jwtCookie.value).toBeTruthy(); + 125 | + 126 | // validate /auth/me using the internal jwt + 127 | const token = jwtCookie.value; + 128 | const meUrl = new URL('/auth/me', BASE_URL).toString(); + 129 | const meResp = await fetch(meUrl, { headers: { Authorization: `Bearer ${token}` } }); + 130 | const meJson = await meResp.json(); + 131 | expect(meResp.status).toBeLessThan(300); + 132 | expect(meJson.success).toBe(true); + 133 | expect(meJson.data).toBeDefined(); + 134 | expect(meJson.data.email).toBe(TEST_USER_EMAIL); + 135 | + 136 | await context.close(); + 137 | await browser.close(); + 138 | }); + 139 | + 140 | test('login via Authentik (created user) issues internal JWT', async () => { + 141 | // use browser flow to login as the created user + 142 | const browser = await chromium.launch({ headless: true }); + 143 | const context = await browser.newContext(); + 144 | const page = await context.newPage(); + 145 | await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + 146 | + 147 | try { + 148 | await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + 149 | const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + 150 | if (usernameInput) await usernameInput.fill(TEST_USER_USERNAME); + 151 | const passwordInput = await page.$('input[type="password"], input[name="password"]'); + 152 | if (passwordInput) await passwordInput.fill(TEST_USER_PASSWORD); + 153 | const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + 154 | if (submitButton) { + 155 | await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + 156 | } + 157 | } catch (e) { + 158 | // ignore if login form not present + 159 | } + 160 | + 161 | await page.waitForTimeout(1000); + 162 | const cookies = await context.cookies(); + 163 | const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + 164 | + 165 | if (!jwtCookie) { + 166 | const fs = require('fs'); + 167 | const dir = 'tests/test-artifacts'; + 168 | if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + 169 | const ts = Date.now(); + 170 | await page.screenshot({ path: `${dir}/failed-login-2-${ts}.png`, fullPage: true }); + 171 | fs.writeFileSync(`${dir}/failed-login-2-${ts}-cookies.json`, JSON.stringify(cookies, null, 2), 'utf-8'); + 172 | const content = await page.content(); + 173 | fs.writeFileSync(`${dir}/failed-login-2-${ts}-page.html`, content, 'utf-8'); + 174 | await context.close(); + 175 | await browser.close(); +> 176 | throw new Error(`No jwt cookie set after login (second test). Saved screenshot/cookies/page to ${dir}`); + | ^ Error: No jwt cookie set after login (second test). Saved screenshot/cookies/page to tests/test-artifacts + 177 | } + 178 | + 179 | await context.close(); + 180 | await browser.close(); + 181 | + 182 | expect(jwtCookie).toBeDefined(); + 183 | expect(jwtCookie.value).toBeTruthy(); + 184 | }); + 185 | + 186 | // Cleanup via API provisioning has been removed. If test environment requires cleanup, perform manually in Authentik. + 187 | + 188 | }); + 189 | +``` \ No newline at end of file diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..8f482be --- /dev/null +++ b/tests/README.md @@ -0,0 +1,12 @@ +Playwright API Testing for RayLab Core + +Structure: +- helpers: auth/request helpers +- fixtures: reusable data helpers +- api: per-controller tests +- reporter: custom reporter writing tests/output/latest-result.txt + +Run: + npx playwright test + +Ensure .env is configured with BASE_URL and identity provider (Authentik or compatible) details. diff --git a/src/modules/identity/domain/.gitkeep b/tests/api/permissions/.gitkeep similarity index 100% rename from src/modules/identity/domain/.gitkeep rename to tests/api/permissions/.gitkeep diff --git a/src/modules/identity/events/.gitkeep b/tests/api/roles/.gitkeep similarity index 100% rename from src/modules/identity/events/.gitkeep rename to tests/api/roles/.gitkeep diff --git a/tests/api/users/enable-user.spec.ts b/tests/api/users/enable-user.spec.ts new file mode 100644 index 0000000..623dc56 --- /dev/null +++ b/tests/api/users/enable-user.spec.ts @@ -0,0 +1,11 @@ +import test from '@playwright/test'; +import { requestPatch } from '../../helpers/request'; + +const { expect } = test; + +test.describe('Users - PATCH /users/:id/enable', () => { + test('should return 404 for non-existing user', async () => { + const res = await requestPatch('/users/non-existing-id/enable'); + expect([404, 400, 500]).toContain(res.status); + }); +}); diff --git a/tests/api/users/get-current-user.spec.ts b/tests/api/users/get-current-user.spec.ts new file mode 100644 index 0000000..e562eba --- /dev/null +++ b/tests/api/users/get-current-user.spec.ts @@ -0,0 +1,20 @@ +import test from '@playwright/test'; +import { requestGet } from '../../helpers/request'; + +const { expect } = test; + +test.describe('Users - GET /users/me', () => { + test('should return current user when token provided', async () => { + const res = await requestGet('/users/me'); + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + expect(res.body.success).toBe(true); + expect(res.body.data).toBeDefined(); + expect(res.body.data.email).toBeTruthy(); + }); + + test('should be unauthorized without token', async () => { + const res = await requestGet('/users/me', null); + expect(res.status).toBe(401); + }); +}); diff --git a/tests/api/users/get-user.spec.ts b/tests/api/users/get-user.spec.ts new file mode 100644 index 0000000..8333624 --- /dev/null +++ b/tests/api/users/get-user.spec.ts @@ -0,0 +1,11 @@ +import test from '@playwright/test'; +import { requestGet } from '../../helpers/request'; + +const { expect } = test; + +test.describe('Users - GET /users/:id', () => { + test('should return 404 for non-existing user', async () => { + const res = await requestGet('/users/non-existing-id'); + expect([404, 400, 500]).toContain(res.status); + }); +}); diff --git a/tests/api/users/get-users.spec.ts b/tests/api/users/get-users.spec.ts new file mode 100644 index 0000000..4855d82 --- /dev/null +++ b/tests/api/users/get-users.spec.ts @@ -0,0 +1,23 @@ +import test from '@playwright/test'; +import { requestGet } from '../../helpers/request'; + + +const { expect } = test; + +test.describe('Users - GET /users', () => { + test('should return list of users (authorized)', async () => { + const res = await requestGet('/users'); + + expect(res.status).toBeGreaterThanOrEqual(200); + expect(res.status).toBeLessThan(300); + expect(res.body).toBeTruthy(); + expect(res.body.success).toBe(true); + expect(res.body.data).toBeInstanceOf(Array); + expect(res.body.meta).toBeDefined(); + }); + + test('should return unauthorized when missing token', async () => { + const res = await requestGet('/users', null); + expect(res.status).toBe(401); + }); +}); diff --git a/tests/fixtures/user.fixture.ts b/tests/fixtures/user.fixture.ts new file mode 100644 index 0000000..74d47ae --- /dev/null +++ b/tests/fixtures/user.fixture.ts @@ -0,0 +1,11 @@ +import { requestPost, requestDelete } from '../helpers/request'; + +export async function createDummyUser(payload: any) { + const res = await requestPost('/user', 'admin', payload); + return res; +} + +export async function deleteDummyUser(userId: string) { + // soft delete + await requestDelete(`/users/${userId}`, 'admin'); +} diff --git a/tests/helpers/auth.ts b/tests/helpers/auth.ts new file mode 100644 index 0000000..d9590eb --- /dev/null +++ b/tests/helpers/auth.ts @@ -0,0 +1,91 @@ +import { chromium } from 'playwright'; +import { env } from './env'; + +const store: Map = new Map(); + +function setToken(key: string, token: string, expiresIn?: number) { + const expiresAt = expiresIn ? Date.now() + expiresIn * 1000 - 5000 : undefined; + store.set(key, { token, expiresAt }); +} + +function getTokenFromStore(key: string) { + const entry = store.get(key); + if (!entry) return null; + if (entry.expiresAt && Date.now() > entry.expiresAt) { + store.delete(key); + return null; + } + return entry.token; +} + +async function loginViaRaylab(username: string, password: string) { + const base = env.BASE_URL || 'http://localhost:3000'; + const browser = await chromium.launch({ headless: Boolean(process.env.PW_HEADLESS || '1') }); + const context = await browser.newContext(); + const page = await context.newPage(); + + // Navigate to RayLab - expects redirect to identity provider + await page.goto(base, { waitUntil: 'networkidle' }); + + // Wait for a login form to appear on identity provider + try { + await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + // fill username + const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + if (usernameInput) await usernameInput.fill(username); + + // fill password + const passwordInput = await page.$('input[type="password"], input[name="password"]'); + if (passwordInput) await passwordInput.fill(password); + + // try to submit + const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + if (submitButton) { + await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + } + } catch (e) { + // If no login form found, we may already be at callback + } + + // Wait for callback to be done and cookie to be set + await page.waitForTimeout(1000); + const cookies = await context.cookies(); + const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + const refreshCookie = cookies.find(c => c.name === 'raylab_refresh'); + + const result: any = {}; + if (jwtCookie) result.accessToken = jwtCookie.value; + if (refreshCookie) result.refreshToken = refreshCookie.value; + + await context.close(); + await browser.close(); + + if (!result.accessToken) throw new Error('Login failed: no session cookie set'); + + return result; +} + +export async function getToken(role: 'owner' | 'admin' | 'employee' = 'admin') { + const cacheKey = `role:${role}`; + const cached = getTokenFromStore(cacheKey); + if (cached) return cached; + + const username = role === 'owner' ? env.OWNER_USERNAME : role === 'admin' ? env.ADMIN_USERNAME : env.EMPLOYEE_USERNAME; + const password = role === 'owner' ? env.OWNER_PASSWORD : role === 'admin' ? env.ADMIN_PASSWORD : env.EMPLOYEE_PASSWORD; + + if (!username || !password) throw new Error('Missing credentials in .env for role: ' + role); + + const data = await loginViaRaylab(username, password); + const token = data.accessToken; + // expiresIn not available from cookie - default to 1 hour + const expiresIn = Number(process.env.TEST_TOKEN_EXPIRES_IN || 3600); + setToken(cacheKey, token, expiresIn); + return token; +} + +export async function getRawToken(username: string, password: string) { + const data = await loginViaRaylab(username, password); + return data.accessToken; +} + +export default { getToken, getRawToken }; diff --git a/tests/helpers/env.ts b/tests/helpers/env.ts new file mode 100644 index 0000000..eab59dd --- /dev/null +++ b/tests/helpers/env.ts @@ -0,0 +1,7 @@ +import dotenv from 'dotenv'; +import path from 'path'; + +const envPath = path.resolve(process.cwd(), '.env'); +dotenv.config({ path: envPath }); + +export const env = process.env; diff --git a/tests/helpers/index.ts b/tests/helpers/index.ts new file mode 100644 index 0000000..63ad0e0 --- /dev/null +++ b/tests/helpers/index.ts @@ -0,0 +1,3 @@ +export * from './auth'; +export * from './request'; +export * from './env'; diff --git a/tests/helpers/request.ts b/tests/helpers/request.ts new file mode 100644 index 0000000..eb92b0d --- /dev/null +++ b/tests/helpers/request.ts @@ -0,0 +1,73 @@ +import { request } from '@playwright/test'; +import { env } from './env'; +import { getToken } from './auth'; + +async function buildContext(token?: string) { + const headers: any = { 'Content-Type': 'application/json' }; + if (token) headers['Authorization'] = `Bearer ${token}`; + + const api = await request.newContext({ baseURL: env.BASE_URL || 'http://localhost:3000', extraHTTPHeaders: headers }); + return api; +} + +export async function requestGet(path: string, role: 'owner' | 'admin' | 'employee' | null = 'admin', opts: any = {}) { + const token = role ? await getToken(role) : undefined; + const api = await buildContext(token); + try { + const res = await api.get(path, opts); + const body = await parseResponseSafe(res); + return { status: res.status(), body, headers: res.headers() }; + } finally { + await api.dispose(); + } +} + +export async function requestPost(path: string, role: 'owner' | 'admin' | 'employee' | null = 'admin', data?: any, opts: any = {}) { + const token = role ? await getToken(role) : undefined; + const api = await buildContext(token); + try { + const res = await api.post(path, { data, ...opts }); + const body = await parseResponseSafe(res); + return { status: res.status(), body, headers: res.headers() }; + } finally { + await api.dispose(); + } +} + +export async function requestPatch(path: string, role: 'owner' | 'admin' | 'employee' | null = 'admin', data?: any, opts: any = {}) { + const token = role ? await getToken(role) : undefined; + const api = await buildContext(token); + try { + const res = await api.patch(path, { data, ...opts }); + const body = await parseResponseSafe(res); + return { status: res.status(), body, headers: res.headers() }; + } finally { + await api.dispose(); + } +} + +export async function requestDelete(path: string, role: 'owner' | 'admin' | 'employee' | null = 'admin', opts: any = {}) { + const token = role ? await getToken(role) : undefined; + const api = await buildContext(token); + try { + const res = await api.delete(path, opts); + const body = await parseResponseSafe(res); + return { status: res.status(), body, headers: res.headers() }; + } finally { + await api.dispose(); + } +} + +async function parseResponseSafe(res: any) { + const ct = res.headers()['content-type'] || ''; + try { + if (ct.includes('application/json')) return await res.json(); + return await res.text(); + } catch (e) { + try { + return await res.text(); + } catch (e2) { + return null; + } + } +} diff --git a/tests/integration/user.integration.spec.ts b/tests/integration/user.integration.spec.ts new file mode 100644 index 0000000..62bf886 --- /dev/null +++ b/tests/integration/user.integration.spec.ts @@ -0,0 +1,188 @@ +import dotenv from 'dotenv'; +import fetch from 'node-fetch'; +import { test, expect } from '@playwright/test'; +import { chromium } from 'playwright'; + +dotenv.config(); + +const TEST_USER_EMAIL = process.env.TEST_USER_EMAIL || 'test-integration@example.com'; +const TEST_USER_USERNAME = process.env.TEST_USER_USERNAME || 'test-integration'; +const TEST_USER_PASSWORD = process.env.TEST_USER_PASSWORD || 'StrongP@ssw0rd!'; + +let BASE_URL = (process.env.BASE_URL || 'http://localhost:3000').trim(); +// sometimes env can contain accidental concatenated vars; take first token +BASE_URL = BASE_URL.split(/\s+/)[0]; +if (!BASE_URL.startsWith('http://') && !BASE_URL.startsWith('https://')) { + BASE_URL = 'http://' + BASE_URL; +} + +// increase default timeout for slow integration flows +test.setTimeout(60000); + +async function adminLogin() { + const adminUser = process.env.ADMIN_USERNAME; + const adminPass = process.env.ADMIN_PASSWORD; + if (!adminUser || !adminPass) throw new Error('ADMIN_USERNAME/ADMIN_PASSWORD must be set in env for integration tests'); + + const browser = await chromium.launch({ headless: true }); + const context = await browser.newContext(); + const page = await context.newPage(); + await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + + try { + await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + if (usernameInput) await usernameInput.fill(adminUser); + const passwordInput = await page.$('input[type="password"], input[name="password"]'); + if (passwordInput) await passwordInput.fill(adminPass); + const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + if (submitButton) { + await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + } + } catch (e) { + // ignore if login form not present + } + + await page.waitForTimeout(1000); + const cookies = await context.cookies(); + const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + await context.close(); + await browser.close(); + + if (!jwtCookie) throw new Error('Admin login failed: no session cookie'); + return jwtCookie.value; +} + +async function apiRequest(path, token, opts = {}) { + const headers = Object.assign({ 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, opts.headers || {}); + const method = opts.method || 'GET'; + const body = opts.body ? JSON.stringify(opts.body) : undefined; + let url; + try { + url = new URL(path, BASE_URL).toString(); + } catch (e) { + throw new Error(`Invalid BASE_URL for integration tests: ${BASE_URL}`); + } + const res = await fetch(url, { method, headers, body }); + let json = null; + try { json = await res.json(); } catch (e) { json = null; } + return { status: res.status, body: json }; +} + + +test.describe('Integration - User lifecycle', () => { + let adminToken; + let createdUser = null; + + test.beforeAll(async () => { + // Integration tests assume test users are created in Authentik prior to running. + // RayLab will create local user record upon first successful login via Authentik. + }); + + test('login via Authentik creates local user and issues internal JWT', async () => { + // use browser flow to login as the test user + const browser = await chromium.launch({ headless: true }); + const context = await browser.newContext(); + const page = await context.newPage(); + await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + + try { + await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + if (usernameInput) await usernameInput.fill(TEST_USER_USERNAME); + const passwordInput = await page.$('input[type="password"], input[name="password"]'); + if (passwordInput) await passwordInput.fill(TEST_USER_PASSWORD); + const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + if (submitButton) { + await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + } + } catch (e) { + // ignore if login form not present + } + + await page.waitForTimeout(1000); + const cookies = await context.cookies(); + const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + const refreshCookie = cookies.find(c => c.name === 'raylab_refresh'); + + if (!jwtCookie) { + const fs = require('fs'); + const dir = 'tests/test-artifacts'; + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + const ts = Date.now(); + await page.screenshot({ path: `${dir}/failed-login-${ts}.png`, fullPage: true }); + fs.writeFileSync(`${dir}/failed-login-${ts}-cookies.json`, JSON.stringify(cookies, null, 2), 'utf-8'); + const content = await page.content(); + fs.writeFileSync(`${dir}/failed-login-${ts}-page.html`, content, 'utf-8'); + await context.close(); + await browser.close(); + throw new Error(`No jwt cookie set after login. Saved screenshot/cookies/page to ${dir}`); + } + + // ensure we have an internal jwt cookie + expect(jwtCookie).toBeDefined(); + expect(jwtCookie.value).toBeTruthy(); + + // validate /auth/me using the internal jwt + const token = jwtCookie.value; + const meUrl = new URL('/auth/me', BASE_URL).toString(); + const meResp = await fetch(meUrl, { headers: { Authorization: `Bearer ${token}` } }); + const meJson = await meResp.json(); + expect(meResp.status).toBeLessThan(300); + expect(meJson.success).toBe(true); + expect(meJson.data).toBeDefined(); + expect(meJson.data.email).toBe(TEST_USER_EMAIL); + + await context.close(); + await browser.close(); + }); + + test('login via Authentik (created user) issues internal JWT', async () => { + // use browser flow to login as the created user + const browser = await chromium.launch({ headless: true }); + const context = await browser.newContext(); + const page = await context.newPage(); + await page.goto(BASE_URL, { waitUntil: 'networkidle' }); + + try { + await page.waitForSelector('input[type="text"], input[type="email"], input[name="username"]', { timeout: 10000 }); + const usernameInput = await page.$('input[name="username"]') || await page.$('input[type="email"]') || await page.$('input[type="text"]'); + if (usernameInput) await usernameInput.fill(TEST_USER_USERNAME); + const passwordInput = await page.$('input[type="password"], input[name="password"]'); + if (passwordInput) await passwordInput.fill(TEST_USER_PASSWORD); + const submitButton = await page.$('button[type="submit"], input[type="submit"]'); + if (submitButton) { + await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle', timeout: 20000 }), submitButton.click()]); + } + } catch (e) { + // ignore if login form not present + } + + await page.waitForTimeout(1000); + const cookies = await context.cookies(); + const jwtCookie = cookies.find(c => c.name === 'raylab_jwt'); + + if (!jwtCookie) { + const fs = require('fs'); + const dir = 'tests/test-artifacts'; + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + const ts = Date.now(); + await page.screenshot({ path: `${dir}/failed-login-2-${ts}.png`, fullPage: true }); + fs.writeFileSync(`${dir}/failed-login-2-${ts}-cookies.json`, JSON.stringify(cookies, null, 2), 'utf-8'); + const content = await page.content(); + fs.writeFileSync(`${dir}/failed-login-2-${ts}-page.html`, content, 'utf-8'); + await context.close(); + await browser.close(); + throw new Error(`No jwt cookie set after login (second test). Saved screenshot/cookies/page to ${dir}`); + } + + await context.close(); + await browser.close(); + + expect(jwtCookie).toBeDefined(); + expect(jwtCookie.value).toBeTruthy(); + }); + + // Cleanup via API provisioning has been removed. If test environment requires cleanup, perform manually in Authentik. + +}); diff --git a/src/modules/identity/infrastructure/mappers/.gitkeep b/tests/output/.gitkeep similarity index 100% rename from src/modules/identity/infrastructure/mappers/.gitkeep rename to tests/output/.gitkeep diff --git a/tests/output/latest-result.txt b/tests/output/latest-result.txt new file mode 100644 index 0000000..32528bb --- /dev/null +++ b/tests/output/latest-result.txt @@ -0,0 +1 @@ +TEST RUN FAILED OR ABORTED \ No newline at end of file diff --git a/tests/reporter/custom-reporter.js b/tests/reporter/custom-reporter.js new file mode 100644 index 0000000..3e3569d --- /dev/null +++ b/tests/reporter/custom-reporter.js @@ -0,0 +1,72 @@ +const fs = require('fs'); + +class CustomReporter { + onEnd(config, result) { + const outputPath = 'tests/output/latest-result.txt'; + + if (!result) { + // test run aborted or failed to initialize + if (!fs.existsSync('tests/output')) fs.mkdirSync('tests/output', { recursive: true }); + fs.writeFileSync(outputPath, 'TEST RUN FAILED OR ABORTED', 'utf-8'); + return; + } + + const failed = result.status === 'failed' || result.numFailedTests > 0; + + const date = new Date().toISOString(); + const duration = (result.duration || 0) + 'ms'; + + if (!fs.existsSync('tests/output')) fs.mkdirSync('tests/output', { recursive: true }); + + if (!failed) { + const content = [ + '==================================', + 'TEST RESULT', + 'PASSED', + `Date: ${date}`, + `Total Test: ${result.total || 0}`, + `Passed: ${result.passed || 0}`, + `Failed: ${result.failed || 0}`, + `Duration: ${duration}`, + '==================================', + ].join('\n'); + + fs.writeFileSync(outputPath, content, 'utf-8'); + return; + } + + const lines = []; + lines.push('=================================='); + lines.push('FAILED'); + lines.push(`Date: ${date}`); + lines.push(`Duration: ${duration}`); + lines.push('=================================='); + + const buildFailureDetails = (suites) => { + for (const s of suites) { + if (s.suites && s.suites.length) buildFailureDetails(s.suites); + if (s.tests && s.tests.length) { + for (const t of s.tests) { + if (t.status === 'failed') { + lines.push('Test Name: ' + (t.title || t.titleText || '')); + for (const r of t.results || []) { + if (r.status === 'failed') { + const error = r.error || {}; + lines.push('Error Message: ' + (error.message || '')); + if (error.stack) lines.push('Stack Trace: ' + error.stack); + } + } + lines.push('----------------------------------'); + } + } + } + } + }; + + if (result.suites) buildFailureDetails(result.suites); + + fs.writeFileSync(outputPath, lines.join('\n'), 'utf-8'); + } +} + +module.exports = CustomReporter; diff --git a/tests/reporter/custom-reporter.ts b/tests/reporter/custom-reporter.ts new file mode 100644 index 0000000..fff0d6a --- /dev/null +++ b/tests/reporter/custom-reporter.ts @@ -0,0 +1,78 @@ +import fs from 'fs'; +import { FullConfig, Reporter, Suite, TestCase, TestError } from '@playwright/test/reporter'; + +class CustomReporter implements Reporter { + onEnd(config: FullConfig, result: any) { + const outputPath = 'tests/output/latest-result.txt'; + + const allTests = result.suites || []; + + const failed = result.status === 'failed' || result.numFailedTests > 0; + + const date = new Date().toISOString(); + const duration = (result.duration || 0) + 'ms'; + + if (!fs.existsSync('tests/output')) fs.mkdirSync('tests/output', { recursive: true }); + + if (!failed) { + const content = [ + '==================================', + 'TEST RESULT', + 'PASSED', + `Date: ${date}`, + `Total Test: ${result.total || 0}`, + `Passed: ${result.passed || 0}`, + `Failed: ${result.failed || 0}`, + `Duration: ${duration}`, + '==================================', + ].join('\n'); + + fs.writeFileSync(outputPath, content, 'utf-8'); + return; + } + + // Failed: build detailed report + const lines: string[] = []; + lines.push('=================================='); + lines.push('FAILED'); + lines.push(`Date: ${date}`); + lines.push(`Duration: ${duration}`); + lines.push('=================================='); + + for (const res of result.report || []) { + // older Playwright may not provide report; fallback to result.annotations + } + + // Walk tests + const buildFailureDetails = (suites: any[]) => { + for (const s of suites) { + if (s.suites && s.suites.length) buildFailureDetails(s.suites); + if (s.tests && s.tests.length) { + for (const t of s.tests) { + if (t.status === 'failed') { + lines.push('Test Name: ' + t.title.join(' > ')); + // try extract location + const location = t.location ? `${t.location.file}:${t.location.line}` : ''; + if (location) lines.push('Location: ' + location); + for (const r of t.results || []) { + if (r.status === 'failed') { + const error = r.error as TestError | undefined; + lines.push('Error Message: ' + (error?.message || '')); + if (r.stdout && r.stdout.length) lines.push('Stdout: ' + r.stdout.join('\n')); + if (error?.stack) lines.push('Stack Trace: ' + error.stack); + } + } + lines.push('----------------------------------'); + } + } + } + } + }; + + if (result.suites) buildFailureDetails(result.suites); + + fs.writeFileSync(outputPath, lines.join('\n'), 'utf-8'); + } +} + +export default CustomReporter; diff --git a/tests/test-artifacts/failed-login-1785604157244-cookies.json b/tests/test-artifacts/failed-login-1785604157244-cookies.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/tests/test-artifacts/failed-login-1785604157244-cookies.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/test-artifacts/failed-login-1785604157244-page.html b/tests/test-artifacts/failed-login-1785604157244-page.html new file mode 100644 index 0000000..fd764ba --- /dev/null +++ b/tests/test-artifacts/failed-login-1785604157244-page.html @@ -0,0 +1 @@ +

{"message":"Cannot GET /","error":"Not Found","statusCode":404}
\ No newline at end of file diff --git a/tests/test-artifacts/failed-login-1785604157244.png b/tests/test-artifacts/failed-login-1785604157244.png new file mode 100644 index 0000000000000000000000000000000000000000..2a575385ef70d9bf701f0792fa510d524ec5fbbf GIT binary patch literal 6969 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 literal 0 HcmV?d00001 diff --git a/tests/test-artifacts/failed-login-2-1785604169592-cookies.json b/tests/test-artifacts/failed-login-2-1785604169592-cookies.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/tests/test-artifacts/failed-login-2-1785604169592-cookies.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/test-artifacts/failed-login-2-1785604169592-page.html b/tests/test-artifacts/failed-login-2-1785604169592-page.html new file mode 100644 index 0000000..fd764ba --- /dev/null +++ b/tests/test-artifacts/failed-login-2-1785604169592-page.html @@ -0,0 +1 @@ +
{"message":"Cannot GET /","error":"Not Found","statusCode":404}
\ No newline at end of file diff --git a/tests/test-artifacts/failed-login-2-1785604169592.png b/tests/test-artifacts/failed-login-2-1785604169592.png new file mode 100644 index 0000000000000000000000000000000000000000..2a575385ef70d9bf701f0792fa510d524ec5fbbf GIT binary patch literal 6969 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 literal 0 HcmV?d00001 diff --git a/tests/unit/user.service.spec.ts b/tests/unit/user.service.spec.ts new file mode 100644 index 0000000..d8ad0a1 --- /dev/null +++ b/tests/unit/user.service.spec.ts @@ -0,0 +1,41 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UserService } from '../../src/modules/identity/application/services/user.service'; +import { PrismaService } from '../../src/shared/prisma.service'; +import { IUser } from '../../src/modules/identity/domain/repositories/user.interface'; + +describe('UserService', () => { + let service: UserService; + let prisma: Partial; + let userRepo: Partial; + + beforeEach(async () => { + prisma = { + user: { + update: jest.fn().mockResolvedValue(null), + findMany: jest.fn().mockResolvedValue([]), + }, + } as any; + + userRepo = { + create: jest.fn().mockImplementation(async (user) => { + return { ...user, id: 'user-1' }; + }), + getById: jest.fn().mockResolvedValue({ id: 'user-1', username: 'u', email: 'e' }), + existByEmail: jest.fn().mockResolvedValue(false), + } as any; + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + UserService, + { provide: PrismaService, useValue: prisma }, + { provide: IUser, useValue: userRepo }, + ], + }).compile(); + + service = module.get(UserService); + }); + + it('createUser - provisioning disabled', async () => { + await expect(service.createUser({ username: 'u', email: 'e' })).rejects.toThrow(); + }); +}); From ffc5ecd2596c06d15ac2c080feaef49dc7ead76c Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Sun, 2 Aug 2026 17:44:50 +0700 Subject: [PATCH 02/34] Phase 3 Completed --- PHASE1_README.md | 165 +++++ api_spec.txt | 203 ++++++ jest.config.cjs | 12 + package-lock.json | 687 +++++++++++++++++- package.json | 9 +- prisma/migrations/0001_init/migration.sql | 112 +++ .../migrations/0002_phase4_init/migration.sql | 37 + .../20260802_add_application/migration.sql | 18 + prisma/schema.prisma | 227 ++++-- prisma/seed.ts | 172 ++++- src/app.module.ts | 15 +- src/core/event-bus/event-bus.service.ts | 28 + src/core/event-bus/event.interface.ts | 6 + src/modules/application/application.module.ts | 37 + .../handlers/create-application.handler.ts | 26 + .../handlers/delete-application.handler.ts | 13 + .../handlers/get-application.handler.ts | 12 + .../handlers/get-applications.handler.ts | 12 + .../handlers/get-me-applications.handler.ts | 17 + .../handlers/update-application.handler.ts | 19 + .../validators/application.validator.ts | 64 ++ .../domain/entities/application.entity.ts | 100 +++ .../repositories/application.interface.ts | 12 + .../mappers/prisma-application.mapper.ts | 19 + .../prisma-application.repository.ts | 92 +++ .../controllers/applications.controller.ts | 76 ++ src/modules/audit/audit.event-handler.ts | 46 ++ src/modules/audit/audit.module.ts | 11 + src/modules/audit/audit.service.ts | 28 + src/modules/auth/auth.module.ts | 21 +- src/modules/auth/authentication.service.ts | 55 ++ src/modules/auth/group-hash.service.ts | 11 + src/modules/auth/guards/oidc.guard.ts | 26 + src/modules/auth/oidc.service.ts | 37 + src/modules/auth/role-sync.service.ts | 74 ++ .../authorization/authorization.module.ts | 16 + .../authorization/authorization.service.ts | 51 ++ .../cache/permission-cache.interface.ts | 5 + .../cache/redis-permission-cache.service.ts | 33 + .../authorization/guards/permission.guard.ts | 28 + src/modules/health/health.controller.ts | 12 + src/modules/health/health.module.ts | 10 + src/modules/health/health.service.ts | 12 + .../permission/create-permission.handler.ts | 12 +- .../permission/delete-permission.handler.ts | 22 +- .../permission/update-permission.handler.ts | 22 +- .../role/assign-permission.handler.ts | 19 +- .../handlers/role/create-role.handler.ts | 17 +- .../handlers/role/delete-role.handler.ts | 14 +- .../role/remove-permission.handler.ts | 15 +- .../handlers/role/update-role.handler.ts | 19 +- .../handlers/user/update-user.handler.ts | 25 + .../repositories/permission.interface.ts | 3 + .../domain/repositories/role.interface.ts | 3 + src/modules/identity/identity.module.ts | 32 +- .../prisma-permission.repository.ts | 6 + .../repositories/prisma-role.repository.ts | 6 + .../controllers/users.controller.ts | 51 +- .../presentation/guards/current-user.guard.ts | 8 + src/shared/cache-keys.ts | 3 + src/shared/redis.service.ts | 109 +++ src/shared/types/request-context.ts | 6 + tests/authentication.service.spec.ts | 38 + tests/authorization.service.spec.ts | 34 + tests/role-sync.rollback.spec.ts | 30 + tests/role-sync.service.spec.ts | 65 ++ tests/role.handlers.spec.ts | 41 ++ tests/update-user.handler.spec.ts | 29 + 68 files changed, 3132 insertions(+), 163 deletions(-) create mode 100644 PHASE1_README.md create mode 100644 api_spec.txt create mode 100644 jest.config.cjs create mode 100644 prisma/migrations/0001_init/migration.sql create mode 100644 prisma/migrations/0002_phase4_init/migration.sql create mode 100644 prisma/migrations/20260802_add_application/migration.sql create mode 100644 src/core/event-bus/event-bus.service.ts create mode 100644 src/core/event-bus/event.interface.ts create mode 100644 src/modules/application/application.module.ts create mode 100644 src/modules/application/application/handlers/create-application.handler.ts create mode 100644 src/modules/application/application/handlers/delete-application.handler.ts create mode 100644 src/modules/application/application/handlers/get-application.handler.ts create mode 100644 src/modules/application/application/handlers/get-applications.handler.ts create mode 100644 src/modules/application/application/handlers/get-me-applications.handler.ts create mode 100644 src/modules/application/application/handlers/update-application.handler.ts create mode 100644 src/modules/application/application/validators/application.validator.ts create mode 100644 src/modules/application/domain/entities/application.entity.ts create mode 100644 src/modules/application/domain/repositories/application.interface.ts create mode 100644 src/modules/application/infrastructure/mappers/prisma-application.mapper.ts create mode 100644 src/modules/application/infrastructure/repositories/prisma-application.repository.ts create mode 100644 src/modules/application/presentation/controllers/applications.controller.ts create mode 100644 src/modules/audit/audit.event-handler.ts create mode 100644 src/modules/audit/audit.module.ts create mode 100644 src/modules/audit/audit.service.ts create mode 100644 src/modules/auth/authentication.service.ts create mode 100644 src/modules/auth/group-hash.service.ts create mode 100644 src/modules/auth/guards/oidc.guard.ts create mode 100644 src/modules/auth/oidc.service.ts create mode 100644 src/modules/auth/role-sync.service.ts create mode 100644 src/modules/authorization/authorization.module.ts create mode 100644 src/modules/authorization/authorization.service.ts create mode 100644 src/modules/authorization/cache/permission-cache.interface.ts create mode 100644 src/modules/authorization/cache/redis-permission-cache.service.ts create mode 100644 src/modules/authorization/guards/permission.guard.ts create mode 100644 src/modules/health/health.controller.ts create mode 100644 src/modules/health/health.module.ts create mode 100644 src/modules/health/health.service.ts create mode 100644 src/modules/identity/application/handlers/user/update-user.handler.ts create mode 100644 src/shared/cache-keys.ts create mode 100644 src/shared/redis.service.ts create mode 100644 src/shared/types/request-context.ts create mode 100644 tests/authentication.service.spec.ts create mode 100644 tests/authorization.service.spec.ts create mode 100644 tests/role-sync.rollback.spec.ts create mode 100644 tests/role-sync.service.spec.ts create mode 100644 tests/role.handlers.spec.ts create mode 100644 tests/update-user.handler.spec.ts diff --git a/PHASE1_README.md b/PHASE1_README.md new file mode 100644 index 0000000..4a6aefd --- /dev/null +++ b/PHASE1_README.md @@ -0,0 +1,165 @@ +Phase 1 Deliverables - RayLab Core +================================= + +Contents +-------- +1. Architecture explanation +2. Folder structure +3. Source code references (what was added/changed) +4. Database schema (Prisma) +5. Migration (SQL) +6. Seed data (script) +7. API design (initial endpoints) +8. Design decisions +9. Advantages +10. Possible future extensions +11. How to run (migrate & seed) + +1) Architecture explanation +-------------------------- +Phase 1 establishes the foundational data model and project layout for RayLab Core. It prepares the system for secure integration with Authentik (OIDC) and supports role/permission based authorization owned by RayLab Core. The design follows Clean Architecture, SOLID principles, modular NestJS structure, and Prisma for type-safe DB access. + +Key principles applied: +- RayLab Core does NOT implement authentication; it only validates external OIDC tokens (Phase 2). +- Roles, permissions, role mappings, users, and audit logs are owned by RayLab Core. Media Manager and Scheduler are intentionally excluded from Phase 1 and will be implemented in Phase 3. +- No Group entity is created in RayLab Core; only mapping configuration (auth group name string -> role) is stored. +- Critical operations (synchronization) will be transactional (Phase 2 implementation). + +2) Folder structure +------------------- +(Only top-level relevant directories shown) + +- prisma/ + - schema.prisma // Prisma schema (source of truth for DB) + - migrations/0001_init/ // Initial SQL migration + - seed.ts // Seed script (idempotent upserts) + +- src/ + - main.ts + - app.module.ts + - modules/ + - identity/ // DDD-based users/roles/permissions implementation + - auth/ // (Phase 2) OIDC integration + - authorization/ // (Phase 2) permission guards & services + - health/ // basic health endpoint (added) + - media/ // media manager module (phase 3) + - audit/ // audit module (phase 3) + - ... other domain modules + - shared/ + - prisma.service.ts + - ... + +3) Source code (what was added/changed) +--------------------------------------- +- prisma/schema.prisma (UPDATED): replaced with production-ready schema containing: + User, Role, Permission, RolePermission, UserRole, AuthGroupRoleMapping, AuditLog + +- prisma/migrations/0001_init/migration.sql (ADDED): initial SQL migration DDL + +- prisma/seed.ts (UPDATED): seed script that creates default roles, permissions, role-permission assignments, and Authentik group -> role mappings + +- src/modules/health/* (ADDED): HealthModule, Controller, Service (thin controller) + +- src/shared/prisma.service.ts (EXISTING): PrismaService (already present) + +4) Database schema +------------------ +The Prisma schema (prisma/schema.prisma) is the authoritative schema. High-level model summary: +- User: id (uuid), externalId (unique), email, name, picture, metadata, createdAt, updatedAt +- Role: id, name (unique), displayName, description +- Permission: id, name (unique), description +- RolePermission: join table role <-> permission (unique constraint roleId+permissionId) +- UserRole: join table user <-> role with source (e.g., 'authentik') and syncedAt timestamp (unique userId+roleId) +- AuthGroupRoleMapping: mapping table authGroup (string) -> roleId. One authGroup may map to multiple roles (no uniqueness enforced on authGroup). +- AuditLog: append-only audit logs with JSON details + + +Indexes and constraints are included for common lookup paths. The migration SQL contains explicit DDL statements. + +5) Migration +------------ +- prisma/migrations/0001_init/migration.sql contains the SQL to create the schema. +- The migration file includes CREATE EXTENSION IF NOT EXISTS "pgcrypto" to provide gen_random_uuid(). +- Recommended migration procedure in production: + 1. Ensure backups and schedule maintenance window for initial deployment. + 2. Run migrations with Prisma or psql: "psql < migration.sql" or use "prisma migrate deploy" after generating migrations from schema.prisma. + +6) Seed data +------------ +- prisma/seed.ts seeds the following: + - Default roles: Owner, Employee, Family + - Default permissions following resource.action naming convention (users.create, users.read, ..., scheduler.manage) + - RolePermission assignments: + - Owner receives all permissions + - Employee and Family receive reasonable subsets (editable later) + - Authentik group mappings: + - RL-Owner -> Owner + - RL-Employee -> Employee + - RL-Family -> Family + +The seed script is idempotent using upsert operations. + +7) API design (initial) +----------------------- +Phase 1 provides a minimal, safe public surface to verify system health and readiness. +- GET /api/health + - Returns: { status: 'ok', timestamp, service } + +Identity module controllers (users, roles, permissions) are present under src/modules/identity/presentation/controllers and follow RESTful conventions, but full auth/guards are implemented in Phase 2. + +8) Design decisions +------------------- +- Prisma selected for type-safe DB access and migrations. (User requested Prisma) +- UUID primary keys (Postgres gen_random_uuid()) for scalability and horizontal distribution. +- JSONB (Prisma Json) used for flexible metadata and audit details. +- AuthGroupRoleMapping stores external group string only; RayLab Core does NOT model Groups as domain entities. +- Seed sets conservative permission assignments; owners get full permissions. +- Minimal initial API surface (health) avoids unintentionally exposing functionality before auth & guards are in place. + +9) Advantages +------------- +- Production-ready DB schema with indices and constraints for performance and data integrity. +- Modular NestJS structure aligned with Clean Architecture / DDD; easy to extend in subsequent phases. +- Prisma provides type-safety and reduces runtime errors. +- Seed script allows reproducible environments and CI setup. +- Audit and media tables are present to support required domain features. + +10) Possible future extensions +------------------------------ +Planned for Phase 2 and 3 (once Phase 1 is approved): +- OIDC validation using openid-client and configuration from env vars +- Role synchronization flow (validate token -> read groups -> map -> compute hash -> replace UserRole in transaction -> invalidate permission cache -> write audit) +- Permission cache using Redis, invalidation hooks +- Auth guards and AuthorizationService for permission checks +- Media Manager: presigned URLs, storage backend (S3/MinIO) +- Scheduler: BullMQ integration with Redis +- Monitoring: Prometheus metrics, structured logging, Sentry + +11) How to run (migrate & seed) +------------------------------- +Prerequisites: +- Node 20+ +- PostgreSQL reachable via DATABASE_URL env var +- Install dependencies: npm ci + +Generate Prisma client and run migration: + +1) Generate Prisma client: + npm run prisma:generate + +2) Apply migration (development): + npm run prisma:migrate + + Or run SQL directly against the database: + psql "$DATABASE_URL" -f prisma/migrations/0001_init/migration.sql + +3) Run seed script: + npm run prisma:seed + +4) Start the app (development): + npm run start:dev + +Next steps +---------- +Please review Phase 1 deliverables. After your approval I will implement Phase 2 (OIDC module, Authentik integration, Group->Role Sync, Authorization Guard, Permission Cache, Audit logging) following the same level of production-quality implementation. + diff --git a/api_spec.txt b/api_spec.txt new file mode 100644 index 0000000..8682759 --- /dev/null +++ b/api_spec.txt @@ -0,0 +1,203 @@ +API Spec - Contoh REST API (Bahasa Indonesia) + +Ringkasan +-------- +API ini adalah contoh RESTful API yang bisa dipakai untuk manajemen pengguna, item, dan pesanan. Menggunakan JSON untuk request/response. Autentikasi memakai Bearer Token (JWT). + +Base URL +-------- +- https://api.example.com/v1 + +Header Umum +----------- +- Authorization: Bearer (kecuali endpoint login/register) +- Content-Type: application/json +- Accept: application/json + +Autentikasi +----------- +- POST /auth/login + - Body (application/json): + { + "email": "user@example.com", + "password": "string" + } + - Response 200: + { + "access_token": "", + "token_type": "Bearer", + "expires_in": 3600 + } + - Kesalahan: 400 (invalid input), 401 (invalid credentials) + +- POST /auth/register + - Body: + { + "name": "Nama User", + "email": "user@example.com", + "password": "password123" + } + - Response 201: user created (id, name, email) + +Endpoint Pengguna (Users) +------------------------- +1) GET /users +- Deskripsi: Mendapatkan daftar pengguna (admin) +- Query params: + - page (int, optional, default=1) + - per_page (int, optional, default=20, max=100) + - sort (string, optional, contoh: "created_at:desc") + - q (string, optional) - pencarian nama/email +- Response 200: + { + "data": [ {"id":1, "name":"...", "email":"..."} , ...], + "meta": {"page":1, "per_page":20, "total":123} + } + +2) GET /users/{id} +- Path params: + - id (integer, required) +- Response 200: user object +- Errors: 404 jika tidak ditemukan + +3) PUT /users/{id} +- Path params: id +- Body (application/json): + { + "name": "Nama Baru", + "email": "email@baru.com" + } +- Validasi: email harus format valid; name min 2 karakter +- Response 200: updated user + +4) DELETE /users/{id} +- Path params: id +- Response 204: no content +- Permissions: hanya admin + +Endpoint Item (Items) +--------------------- +1) GET /items +- Query params: + - page, per_page, sort (lihat Users) + - category (string, optional) + - min_price, max_price (decimal, optional) +- Response: list items dengan fields id, name, description, price, stock + +2) POST /items +- Body: + { + "name": "Nama Item", + "description": "Deskripsi...", + "price": 125000.50, + "stock": 10, + "category": "Elektronik" + } +- Validasi: + - name (required, max 255) + - price (required, >=0) + - stock (integer, >=0) +- Response 201: created item +- Permissions: admin atau vendor + +3) GET /items/{id} +- Response 200: item object +- 404 jika tidak ditemukan + +4) PUT /items/{id} +- Body: fields yang boleh diupdate (name, description, price, stock, category) +- Response 200 + +5) DELETE /items/{id} +- Response 204 + +Endpoint Pesanan (Orders) +------------------------- +1) POST /orders +- Body: + { + "user_id": 12, // optional jika token sudah mewakili user + "items": [ + {"item_id": 5, "quantity": 2}, + {"item_id": 7, "quantity": 1} + ], + "shipping_address": "Alamat lengkap", + "note": "Catatan opsional" + } +- Validasi: setiap item quantity >= 1 dan tersedia di stock +- Response 201: + { + "order_id": 987, + "status": "pending", + "total": 375000.00 + } + +2) GET /orders/{id} +- Path param: id +- Response 200: full order detail (items, prices, shipping, status) +- Permissions: hanya pemilik order atau admin + +3) PATCH /orders/{id}/status +- Body: {"status": "shipped"} +- Allowed status: pending, confirmed, shipped, delivered, cancelled +- Permissions: hanya admin atau staff + +Format Tanggal dan Numerik +-------------------------- +- Tanggal/waktu: ISO 8601 (UTC), contoh: "2024-08-01T12:34:56Z" +- Desimal: titik sebagai pemisah desimal, misal 125000.50 + +Pagination +---------- +- Gunakan page & per_page +- Meta object harus mengandung total, page, per_page, total_pages + +Response Error Umum +------------------- +- 400 Bad Request - payload tidak valid + { + "error": "invalid_request", + "message": "Deskripsi kesalahan", + "details": { "field": ["pesan validasi"] } + } +- 401 Unauthorized - token tidak ada/invalid/expired +- 403 Forbidden - tidak cukup izin +- 404 Not Found +- 429 Too Many Requests - rate limit +- 500 Internal Server Error + +Rate Limiting +------------- +- Contoh: 1000 requests per 1 jam per API key +- Header terkait: + - X-RateLimit-Limit: 1000 + - X-RateLimit-Remaining: 750 + - X-RateLimit-Reset: 1650000000 (epoch seconds) + +Keamanan dan Persyaratan +------------------------ +- Semua permintaan harus lewat HTTPS (TLS 1.2+) +- Gunakan header Authorization: Bearer +- CORS: domain yang diijinkan harus didaftarkan +- Validasi input di server (length, tipe, range) +- Sanitasi data untuk mencegah injection + +Versioning +---------- +- Versi di URL: /v1/ +- Buat v2 jika ada breaking change + +Contoh Request/Response (cURL) +----------------------------- +Login: +curl -X POST "https://api.example.com/v1/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"email":"user@example.com","password":"password123"}' + +Ambil daftar item: +curl "https://api.example.com/v1/items?page=1&per_page=20" \ + -H "Authorization: Bearer " + +Footer +------ +Spesifikasi ini adalah contoh umum. Sesuaikan endpoint, nama field, aturan autentikasi, dan kebijakan rate limit sesuai kebutuhan aplikasi Anda. \ No newline at end of file diff --git a/jest.config.cjs b/jest.config.cjs new file mode 100644 index 0000000..58bacb7 --- /dev/null +++ b/jest.config.cjs @@ -0,0 +1,12 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/tests/**/*.spec.ts', '**/?(*.)+(spec|test).ts'], + testPathIgnorePatterns: ['/tests/api/', '/tests/integration/'], + moduleFileExtensions: ['ts', 'js', 'json'], + globals: { + 'ts-jest': { + tsconfig: 'tsconfig.json', + }, + }, +}; diff --git a/package-lock.json b/package-lock.json index 53a6213..42e9515 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { + "@aws-sdk/client-s3": "^3.379.0", "@nestjs/common": "^10.4.20", "@nestjs/config": "^3.3.0", "@nestjs/core": "^10.4.20", @@ -18,13 +19,16 @@ "@nestjs/platform-express": "^10.4.20", "@nestjs/swagger": "^7.4.2", "@prisma/client": "^5.22.0", + "axios": "^1.4.0", "bcrypt": "^5.1.1", + "bullmq": "^1.73.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.2", "dotenv": "^16.6.1", "ioredis": "^5.3.2", "jose": "^6.2.6", "jsonwebtoken": "^9.0.2", + "nodemailer": "^6.9.4", "openid-client": "^6.5.0", "passport": "^0.7.0", "passport-jwt": "^4.0.1", @@ -61,6 +65,314 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/checksums": { + "version": "3.1000.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/checksums/-/checksums-3.1000.24.tgz", + "integrity": "sha512-7TWLjypP8kk3savsDBRuhZJx7mBuFFA2136BQhwwLllsAnO4Tmq/p+SXZaNxbuulkzUFz3BZzj0bb4YzexZcNQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.4", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.1101.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1101.0.tgz", + "integrity": "sha512-16EFb1aTEBgPcfUAWAjjlB57IZCyn7B3rlfT+xqE7M6WoH8AMMU3vFZO0UOitwh/xvvzVx73YED1/n0PU4qBMw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/checksums": "^3.1000.24", + "@aws-sdk/core": "^3.977.4", + "@aws-sdk/credential-provider-node": "^3.972.76", + "@aws-sdk/middleware-sdk-s3": "^3.972.70", + "@aws-sdk/signature-v4-multi-region": "^3.996.43", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/fetch-http-handler": "^5.6.13", + "@smithy/node-http-handler": "^4.9.13", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.977.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.977.4.tgz", + "integrity": "sha512-CEkcQlMOQJCvul60U7wdAOACjtdgFWDsfJI+6wUOGdhGNV2lGbuJpi/R50QLpFG3Tp+sQxa/RmzC3X7KHbhuTA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.2", + "@aws-sdk/xml-builder": "^3.972.37", + "@aws/lambda-invoke-store": "^0.3.0", + "@smithy/core": "^3.31.1", + "@smithy/signature-v4": "^5.6.12", + "@smithy/types": "^4.16.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.65", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.65.tgz", + "integrity": "sha512-lJT2aRw9wCV8jPHyFJjdZLD4HTydL6/22AnCSOB8e/LqOc55nEJGLHkJQeSxhn8QiqyjFwPKQFtMw0ovjRUY/g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.4", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.67", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.67.tgz", + "integrity": "sha512-N7fw/15hSwI/CPxe5ohOyb7O4ge9f5me1gVIn8OIkBRB0squ8OJqQyDyH/HoL+Sb1W5xdC88jVC+bHkw73iu+Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.4", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/fetch-http-handler": "^5.6.13", + "@smithy/node-http-handler": "^4.9.13", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.973.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.10.tgz", + "integrity": "sha512-Zh9XRaPnDN9buO7GfWBubS22R6Nq5D6hbyYEMN05LiOnXugm/8WDjUx6y756bSPbdn3aJB2qG4zFW3bN82QhoQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.4", + "@aws-sdk/credential-provider-env": "^3.972.65", + "@aws-sdk/credential-provider-http": "^3.972.67", + "@aws-sdk/credential-provider-login": "^3.972.72", + "@aws-sdk/credential-provider-process": "^3.972.65", + "@aws-sdk/credential-provider-sso": "^3.973.9", + "@aws-sdk/credential-provider-web-identity": "^3.972.71", + "@aws-sdk/nested-clients": "^3.997.39", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/credential-provider-imds": "^4.4.16", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.72", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.72.tgz", + "integrity": "sha512-zZapIKwaHp7TdTf9hbH1I3CVUdEupmt7FXO/BoTQGC+4h6NkXKWpqF2p5WyfpjurDLHCpSyh+BzMlAg8arqWLA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.4", + "@aws-sdk/nested-clients": "^3.997.39", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.76", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.76.tgz", + "integrity": "sha512-1yzLmRiYSgGC25v7ZZEwJn/auhHHTIHgFOmzL2f36hf1+7jSLcX+1QrAz4760WEzPiiQl8xmlpFhHfl2OoyVzA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.65", + "@aws-sdk/credential-provider-http": "^3.972.67", + "@aws-sdk/credential-provider-ini": "^3.973.10", + "@aws-sdk/credential-provider-process": "^3.972.65", + "@aws-sdk/credential-provider-sso": "^3.973.9", + "@aws-sdk/credential-provider-web-identity": "^3.972.71", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/credential-provider-imds": "^4.4.16", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.65", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.65.tgz", + "integrity": "sha512-e5DbbNteOSalN58U83G6kFa4ECLEuGbGqNBHIXE7zYXA/m4GHblIGjFbSH7wYv6gBV8iNSDcRZBKfQZF5vF9nw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.4", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.973.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.9.tgz", + "integrity": "sha512-0V0u4t+KBku9fbh5CPCaC5hUWwSzDafp8nCuDy817zWbp2gz80jO44rMQkiwnZ+k54B+tjAtzRy00DJRGTKGBg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.4", + "@aws-sdk/nested-clients": "^3.997.39", + "@aws-sdk/token-providers": "3.1100.0", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.71", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.71.tgz", + "integrity": "sha512-e4dwiRltGAaQ+2yxw57Hj0l/BF3BHiG14+QpYE7bGYBlpAq/fkIri2BDhjWon8c0mhhtd2txQBAkQb9BcTStFg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.4", + "@aws-sdk/nested-clients": "^3.997.39", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.972.70", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.70.tgz", + "integrity": "sha512-APdP0iODt39AkjCjzTFIoFrxDH/Cz3CpWRDKLcsJg7eOnfE1htkxL9BhDoe/xL7cXdoMwh2HBYv3DiT1uf64NQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.4", + "@aws-sdk/signature-v4-multi-region": "^3.996.43", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.997.39", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.39.tgz", + "integrity": "sha512-wU5NPnj62Sb7A8xn/Zb+xThe05P3otNtDl37iOIi5DDMeCesNeCckaG+eXWGUs12Z9R34I8CD05TaTe6SIa61g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.4", + "@aws-sdk/signature-v4-multi-region": "^3.996.43", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/fetch-http-handler": "^5.6.13", + "@smithy/node-http-handler": "^4.9.13", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.43", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.43.tgz", + "integrity": "sha512-lKekx8bLBXSv4O+cslk9Zfnw2XKSkWBs3uWL5QGhH2ZAQfNS7FE0vcSSN2vD/AhxX54ZTywWxR4STThoeOXlBA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.2", + "@smithy/signature-v4": "^5.6.12", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1100.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1100.0.tgz", + "integrity": "sha512-THf3MkgY3fNJZ3zdgSenLqR7gSE68KccCj1RCKretlG73Ppszvues02VpCUO9NlB/tZDC483FvGCld+AiPCkvg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.4", + "@aws-sdk/nested-clients": "^3.997.39", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.974.2", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.2.tgz", + "integrity": "sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.37", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.37.tgz", + "integrity": "sha512-zKq4HQum8JwDyEuyfuI4bbiAcU0KxP6qy+9PR/IsR92IyE/DaBAikzAS50tjxip4bqIIANpCcG+Yyj6CVhXupg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz", + "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -1278,6 +1590,84 @@ "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==" }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", + "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz", + "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz", + "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz", + "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz", + "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", + "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@nestjs/common": { "version": "10.4.22", "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.22.tgz", @@ -1674,6 +2064,87 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@smithy/core": { + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.31.1.tgz", + "integrity": "sha512-CyogUINxvi7C7LDsh8Syo6hVJOT9ckz4rG8dRZfTJ8r91HkMY59PnNooaj7WcHyxEkxPfBAmbgztZU+xTo76lg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.4.16", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.16.tgz", + "integrity": "sha512-QfuLWAkLzptffFW980AFeHZFdqds2B64rpEd3uJ6lgs3xVn9QegGMUgUcj+4d7dRrAsya3r58ZKpku97WcFb4w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.6.13", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.6.13.tgz", + "integrity": "sha512-4fW86pEUOMbrD5nkbyl/tTvPHHWJFbuB2odl6ps9lWfHoXf9HWh3Q/Smh59qH1g7+c/BSZghX6bbUk4gsiMs8A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.9.13", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.9.13.tgz", + "integrity": "sha512-Nmd/Nl35zfYrd+a6OO2cDJb3GPh9bgTjIUhcM+JFfjpp8/osCgboDV5nCT1I01Pv6R13eSKDKLSoVa5ZB6Zsfw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.6.12", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.6.12.tgz", + "integrity": "sha512-I6KLtq3H0qqSuV9vLglfi8puHqzygzWHOnI4z/Rdoo+q50vvo18vBRdPAvvEtcaKROz7Zn6qnPa14kRfPH6PcQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.16.1.tgz", + "integrity": "sha512-0JFs3V2y2M9tKW5na/qxe69Zv+uxLMO7QBbhxF/FHu/Gp2NFZAAL9tWl9PU02xxo07pb3G9FTyjNc6D5uZrJIg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@tokenizer/inflate": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", @@ -2480,8 +2951,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/atomic-sleep": { "version": "1.0.0", @@ -2491,6 +2961,18 @@ "node": ">=8.0.0" } }, + "node_modules/axios": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", + "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -2688,6 +3170,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.18", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", @@ -2773,6 +3261,78 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/bullmq": { + "version": "1.91.1", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-1.91.1.tgz", + "integrity": "sha512-u7dat9I8ZwouZ651AMZkBSvB6NVUPpnAjd4iokd9DM41whqIBnDjuL11h7+kEjcpiDKj6E+wxZiER00FqirZQg==", + "license": "MIT", + "dependencies": { + "cron-parser": "^4.6.0", + "get-port": "6.1.2", + "glob": "^8.0.3", + "ioredis": "^5.2.2", + "lodash": "^4.17.21", + "msgpackr": "^1.6.2", + "semver": "^7.3.7", + "tslib": "^2.0.0", + "uuid": "^9.0.0" + } + }, + "node_modules/bullmq/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/bullmq/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/bullmq/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/bullmq/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -3028,7 +3588,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -3157,6 +3716,19 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "deprecated": "v4 is no longer maintained, upgrade to v5", + "license": "MIT", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3220,7 +3792,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -3426,7 +3997,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -3943,11 +4513,30 @@ "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/form-data": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -4111,6 +4700,18 @@ "node": ">=8.0.0" } }, + "node_modules/get-port": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-6.1.2.tgz", + "integrity": "sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -4240,7 +4841,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -5462,6 +6062,15 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/luxon": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -5668,6 +6277,37 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/msgpackr": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.12.1.tgz", + "integrity": "sha512-4EUH9tQHnMmEgzW/MdAP0KIfa1T9AF+htl0ffe2n5vb2EKn9y2co8ccpgWko6S52Jy1PQZKwRnx5/KkYjtd9MQ==", + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", + "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" + } + }, "node_modules/multer": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", @@ -5729,6 +6369,21 @@ } } }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5744,6 +6399,15 @@ "node": ">=18" } }, + "node_modules/nodemailer": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", + "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -6326,6 +6990,15 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/package.json b/package.json index 2cc6cd2..59403a7 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "class-validator": "^0.14.2", "dotenv": "^16.6.1", "jose": "^6.2.6", - "openid-client": "^6.5.0", + "openid-client": "^6.5.0", "jsonwebtoken": "^9.0.2", "passport": "^0.7.0", "passport-jwt": "^4.0.1", @@ -47,7 +47,12 @@ "reflect-metadata": "^0.2.2", "rxjs": "^7.8.2", "swagger-ui-express": "^5.0.1", - "uuid": "^11.1.0" + "uuid": "^11.1.0", + "ioredis": "^5.3.2", + "bullmq": "^1.73.0", + "nodemailer": "^6.9.4", + "axios": "^1.4.0", + "@aws-sdk/client-s3": "^3.379.0" }, "devDependencies": { "@eslint/js": "^9.21.0", diff --git a/prisma/migrations/0001_init/migration.sql b/prisma/migrations/0001_init/migration.sql new file mode 100644 index 0000000..54f181d --- /dev/null +++ b/prisma/migrations/0001_init/migration.sql @@ -0,0 +1,112 @@ +-- Initial migration for RayLab Core (PostgreSQL) +-- Requires: CREATE EXTENSION IF NOT EXISTS pgcrypto; (for gen_random_uuid()) + +BEGIN; + +-- Extension for UUID generation +CREATE EXTENSION IF NOT EXISTS "pgcrypto"; + +-- Enum types +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'userrolesource') THEN + CREATE TYPE "UserRoleSource" AS ENUM ('AUTHENTIK', 'SYSTEM'); + END IF; +END $$; + +-- Users +CREATE TABLE IF NOT EXISTS "User" ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + authentik_id text UNIQUE, + authentik_user_id text UNIQUE, + authentik_subject text UNIQUE, + username varchar(255) UNIQUE, + email varchar(320) UNIQUE, + name varchar(255), + picture text, + is_active boolean NOT NULL DEFAULT true, + deleted_at timestamptz, + last_seen_at timestamptz, + last_synced_at timestamptz, + sync_status text, + storage_quota bigint DEFAULT 10737418240, + storage_used bigint DEFAULT 0, + metadata jsonb, + last_group_hash text, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_user_authentik_id ON "User" (authentik_id); +CREATE INDEX IF NOT EXISTS idx_user_username ON "User" (username); +CREATE INDEX IF NOT EXISTS idx_user_email ON "User" (email); + +-- Roles +CREATE TABLE IF NOT EXISTS "Role" ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + code varchar(100) NOT NULL UNIQUE, + name varchar(255) NOT NULL, + display_name varchar(255), + description text, + is_default boolean NOT NULL DEFAULT false, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); + +-- Permissions +CREATE TABLE IF NOT EXISTS "Permission" ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + code varchar(150) NOT NULL UNIQUE, + name varchar(255) NOT NULL, + display_name varchar(255), + description text, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); + +-- RolePermission +CREATE TABLE IF NOT EXISTS "RolePermission" ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + role_id uuid NOT NULL REFERENCES "Role"(id) ON DELETE CASCADE, + permission_id uuid NOT NULL REFERENCES "Permission"(id) ON DELETE CASCADE, + created_at timestamptz NOT NULL DEFAULT now(), + CONSTRAINT uq_role_permission UNIQUE (role_id, permission_id) +); +CREATE INDEX IF NOT EXISTS idx_rolepermission_role_id ON "RolePermission" (role_id); +CREATE INDEX IF NOT EXISTS idx_rolepermission_permission_id ON "RolePermission" (permission_id); + +-- UserRole +CREATE TABLE IF NOT EXISTS "UserRole" ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + user_id uuid NOT NULL REFERENCES "User"(id) ON DELETE CASCADE, + role_id uuid NOT NULL REFERENCES "Role"(id) ON DELETE CASCADE, + source "UserRoleSource" NOT NULL DEFAULT 'AUTHENTIK', + synced_at timestamptz NOT NULL DEFAULT now(), + created_at timestamptz NOT NULL DEFAULT now(), + CONSTRAINT uq_user_role UNIQUE (user_id, role_id) +); +CREATE INDEX IF NOT EXISTS idx_userrole_user_id ON "UserRole" (user_id); +CREATE INDEX IF NOT EXISTS idx_userrole_role_id ON "UserRole" (role_id); + +-- AuthGroupRoleMapping +CREATE TABLE IF NOT EXISTS "AuthGroupRoleMapping" ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + auth_group varchar(255) NOT NULL, + role_id uuid NOT NULL REFERENCES "Role"(id) ON DELETE CASCADE, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_authgroup_name ON "AuthGroupRoleMapping" (auth_group); + +-- AuditLog +CREATE TABLE IF NOT EXISTS "AuditLog" ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + user_id uuid REFERENCES "User"(id) ON DELETE SET NULL, + action varchar(200) NOT NULL, + resource varchar(200), + resource_id text, + details jsonb, + created_at timestamptz NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_auditlog_user_id ON "AuditLog" (user_id); +CREATE INDEX IF NOT EXISTS idx_auditlog_action ON "AuditLog" (action); + +COMMIT; diff --git a/prisma/migrations/0002_phase4_init/migration.sql b/prisma/migrations/0002_phase4_init/migration.sql new file mode 100644 index 0000000..7c13bc6 --- /dev/null +++ b/prisma/migrations/0002_phase4_init/migration.sql @@ -0,0 +1,37 @@ +-- Phase 4 migration: add ScheduledJob and MediaObject + +BEGIN; + +-- ScheduledJob +CREATE TABLE IF NOT EXISTS "ScheduledJob" ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + name varchar(255) NOT NULL, + payload jsonb, + cron varchar(255), + run_at timestamptz, + status varchar(50) NOT NULL DEFAULT 'pending', + attempts integer NOT NULL DEFAULT 0, + last_run_at timestamptz, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_scheduledjob_name ON "ScheduledJob" (name); +CREATE INDEX IF NOT EXISTS idx_scheduledjob_status ON "ScheduledJob" (status); + +-- MediaObject +CREATE TABLE IF NOT EXISTS "MediaObject" ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + owner_user_id uuid REFERENCES "User"(id) ON DELETE SET NULL, + storage_key varchar(1024) NOT NULL, + filename varchar(1024), + mime_type varchar(255), + size bigint, + is_public boolean NOT NULL DEFAULT false, + metadata jsonb, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_media_owner_user_id ON "MediaObject" (owner_user_id); +CREATE INDEX IF NOT EXISTS idx_media_storage_key ON "MediaObject" (storage_key); + +COMMIT; diff --git a/prisma/migrations/20260802_add_application/migration.sql b/prisma/migrations/20260802_add_application/migration.sql new file mode 100644 index 0000000..ba75c12 --- /dev/null +++ b/prisma/migrations/20260802_add_application/migration.sql @@ -0,0 +1,18 @@ +-- Migration: add Application table + +CREATE TABLE IF NOT EXISTS "Application" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "code" varchar(100) NOT NULL UNIQUE, + "name" varchar(255) NOT NULL, + "description" text, + "icon" text, + "url" text, + "applicationsClaim" varchar(255) NOT NULL UNIQUE, + "isActive" boolean NOT NULL DEFAULT true, + "displayOrder" integer NOT NULL DEFAULT 0, + "createdAt" timestamptz NOT NULL DEFAULT now(), + "updatedAt" timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS "Application_code_idx" ON "Application" ("code"); +CREATE INDEX IF NOT EXISTS "Application_applicationsClaim_idx" ON "Application" ("applicationsClaim"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a6c5140..ec3e0e6 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,5 +1,5 @@ -// Prisma schema -// Basic User model for RayLab Core +// Prisma schema for RayLab Core - Phase 1 +// PostgreSQL datasource. DATABASE_URL must be set in environment. generator client { provider = "prisma-client-js" @@ -11,99 +11,170 @@ datasource db { } model User { - id String @id @default(uuid()) - - authentikId String? @unique + id String @id @default(uuid()) @db.Uuid + authentikId String? @unique authentikUserId String? @unique authentikSubject String? @unique + username String? @db.VarChar(255) @unique + email String? @db.VarChar(320) @unique + name String? @db.VarChar(255) + picture String? + isActive Boolean @default(true) + deletedAt DateTime? + lastSeenAt DateTime? + lastSyncedAt DateTime? + syncStatus String? + storageQuota BigInt? @default(10737418240) + storageUsed BigInt? @default(0) + metadata Json? + userRoles UserRole[] + auditLogs AuditLog[] + lastGroupHash String? @db.Text + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt - username String @unique - email String @unique - - - isActive Boolean @default(true) - - // storage (in bytes) - storageQuota BigInt @default(10737418240) - storageUsed BigInt @default(0) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - deletedAt DateTime? - lastSeenAt DateTime? - lastSyncedAt DateTime? - syncStatus String? - - roles UserRole[] - permissions UserPermission[] - + @@index([authentikId]) + @@index([username]) + @@index([email]) } model Role { - id String @id @default(uuid()) - - code String @unique - name String + id String @id @default(uuid()) @db.Uuid + code String @unique @db.VarChar(100) + name String @db.VarChar(255) + displayName String? @db.VarChar(255) description String? - - isDefault Boolean @default(false) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - users UserRole[] - permissions RolePermission[] + isDefault Boolean @default(false) + rolePermissions RolePermission[] + userRoles UserRole[] + mappings AuthGroupRoleMapping[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model Permission { - id String @id @default(uuid()) - - code String @unique - name String + id String @id @default(uuid()) @db.Uuid + code String @unique @db.VarChar(150) + name String @db.VarChar(255) + displayName String? @db.VarChar(255) description String? - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - roles RolePermission[] - users UserPermission[] -} - -model UserRole { - userId String - roleId String - - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) - - assignedAt DateTime @default(now()) - assignedBy String - - @@id([userId, roleId]) + rolePermissions RolePermission[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model RolePermission { - roleId String - permissionId String + id String @id @default(uuid()) @db.Uuid + role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) + roleId String @db.Uuid + permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) + permissionId String @db.Uuid + createdAt DateTime @default(now()) - role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) - permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) - - assignedAt DateTime @default(now()) - assignedBy String - - @@id([roleId, permissionId]) + @@unique([roleId, permissionId]) + @@index([roleId]) + @@index([permissionId]) } -model UserPermission { - userId String - permissionId String +enum UserRoleSource { + AUTHENTIK + SYSTEM +} + +model UserRole { + id String @id @default(uuid()) @db.Uuid + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String @db.Uuid + role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) + roleId String @db.Uuid + source UserRoleSource @default(AUTHENTIK) + syncedAt DateTime @default(now()) + createdAt DateTime @default(now()) + + @@unique([userId, roleId]) + @@index([userId]) + @@index([roleId]) +} + +model AuthGroupRoleMapping { + id String @id @default(uuid()) @db.Uuid + authGroup String @db.VarChar(255) + role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) + roleId String @db.Uuid + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([authGroup]) +} + +model AuditLog { + id String @id @default(uuid()) @db.Uuid + user User? @relation(fields: [userId], references: [id], onDelete: SetNull) + userId String? @db.Uuid + action String @db.VarChar(200) + resource String? @db.VarChar(200) + resourceId String? + details Json? + createdAt DateTime @default(now()) + + @@index([userId]) + @@index([action]) +} + +model ScheduledJob { + id String @id @default(uuid()) @db.Uuid + name String @db.VarChar(255) + payload Json? + cron String? + runAt DateTime? + status String @db.VarChar(50) @default("pending") + attempts Int @default(0) + lastRunAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([name]) + @@index([status]) +} + +model MediaObject { + id String @id @default(uuid()) @db.Uuid + ownerUserId String? @db.Uuid + owner User? @relation(fields: [ownerUserId], references: [id], onDelete: SetNull) + storageKey String @db.VarChar(1024) + filename String? @db.VarChar(1024) + mimeType String? @db.VarChar(255) + size BigInt? + isPublic Boolean @default(false) + metadata Json? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([ownerUserId]) + @@index([storageKey]) +} + + + +model Application { + id String @id @default(uuid()) @db.Uuid + code String @unique @db.VarChar(100) + name String @db.VarChar(255) + description String? + icon String? + url String? + applicationsClaim String @unique @db.VarChar(255) + isActive Boolean @default(true) + displayOrder Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([code]) + @@index([applicationsClaim]) +} + + + - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) - assignedAt DateTime @default(now()) - assignedBy String - @@id([userId, permissionId]) -} \ No newline at end of file diff --git a/prisma/seed.ts b/prisma/seed.ts index 6107de9..4f4e997 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1 +1,171 @@ -// prisma seed placeholder +/** + * Prisma seed script for RayLab Core - Phase 1 + * Run with: npx ts-node prisma/seed.ts + * Requires DATABASE_URL env var. + */ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +async function main() { + console.log('Seeding default roles, permissions, and mappings...'); + + // Default roles + const roles = [ + { name: 'Owner', displayName: 'Owner', description: 'Platform owner with full access' }, + { name: 'Employee', displayName: 'Employee', description: 'Employee role with internal permissions' }, + { name: 'Family', displayName: 'Family', description: 'Family role with limited access' }, + ]; + + for (const r of roles) { + const code = r.name.toLowerCase(); + await prisma.role.upsert({ + where: { code }, + update: { name: r.name, displayName: r.displayName, description: r.description }, + create: { code, name: r.name, displayName: r.displayName, description: r.description }, + }); + } + + // Default permissions (resource.action lowercase as requested) + const permissions = [ + 'users.create', + 'users.read', + 'users.update', + 'users.delete', + 'roles.create', + 'roles.read', + 'roles.update', + 'roles.delete', + 'permissions.create', + 'permissions.read', + 'permissions.update', + 'permissions.delete', + 'audit.read', + 'storage.read', + 'storage.write', + 'storage.delete', + 'media.read', + 'media.write', + 'media.delete', + 'scheduler.read', + 'scheduler.manage', + ]; + + for (const p of permissions) { + // Use code as canonical identifier (resource.action). Use name == code for now. + const code = p; + await prisma.permission.upsert({ + where: { code }, + update: { name: p, description: null }, + create: { code, name: p, description: null }, + }); + } + + // Assign permissions to roles (example mapping) + const owner = await prisma.role.findUnique({ where: { code: 'owner' } }); + const employee = await prisma.role.findUnique({ where: { code: 'employee' } }); + const family = await prisma.role.findUnique({ where: { code: 'family' } }); + + if (!owner || !employee || !family) { + throw new Error('Default roles missing after upsert'); + } + + // Owner -> all permissions + const allPerms = await prisma.permission.findMany(); + for (const perm of allPerms) { + await prisma.rolePermission.upsert({ + where: { roleId_permissionId: { roleId: owner.id, permissionId: perm.id } }, + update: {}, + create: { roleId: owner.id, permissionId: perm.id }, + }); + } + + // Employee -> a subset + const employeePerms = [ + 'users.read', + 'permissions.read', + 'scheduler.read', + 'scheduler.manage', + 'media.read', + 'media.write', + ]; + for (const p of employeePerms) { + const perm = await prisma.permission.findUnique({ where: { code: p } }); + if (perm) { + await prisma.rolePermission.upsert({ + where: { roleId_permissionId: { roleId: employee.id, permissionId: perm.id } }, + update: {}, + create: { roleId: employee.id, permissionId: perm.id }, + }); + } + } + + // Family -> limited + const familyPerms = [ + 'media.read', + 'media.write', + 'media.delete', + ]; + for (const p of familyPerms) { + const perm = await prisma.permission.findUnique({ where: { code: p } }); + if (perm) { + await prisma.rolePermission.upsert({ + where: { roleId_permissionId: { roleId: family.id, permissionId: perm.id } }, + update: {}, + create: { roleId: family.id, permissionId: perm.id }, + }); + } + } + + // Authentik group mappings + const mappings = [ + { authGroup: 'RL-Owner', roleName: 'Owner' }, + { authGroup: 'RL-Employee', roleName: 'Employee' }, + { authGroup: 'RL-Family', roleName: 'Family' }, + ]; + + for (const m of mappings) { + const role = await prisma.role.findUnique({ where: { name: m.roleName } }); + if (!role) continue; + // Ensure mapping exists (authGroup + roleId). AuthGroup can map to multiple roles. + const existing = await prisma.authGroupRoleMapping.findFirst({ where: { authGroup: m.authGroup, roleId: role.id } }); + if (!existing) { + await prisma.authGroupRoleMapping.create({ data: { authGroup: m.authGroup, roleId: role.id } }); + } + } + + // Seed application catalog + const apps = [ + { code: 'core', name: 'Core', description: 'RayLab Core Dashboard', icon: 'mdi-apps', url: '/', applicationsClaim: 'core', displayOrder: 0 }, + { code: 'warung', name: 'Warung', description: 'Warung app', icon: 'mdi-store', url: '/warung', applicationsClaim: 'warung', displayOrder: 10 }, + { code: 'nextcloud', name: 'Nextcloud', description: 'Nextcloud', icon: 'mdi-cloud', url: 'https://nextcloud.example', applicationsClaim: 'nextcloud', displayOrder: 20 }, + { code: 'jellyfin', name: 'Jellyfin', description: 'Jellyfin media server', icon: 'mdi-movie', url: 'https://jellyfin.example', applicationsClaim: 'jellyfin', displayOrder: 30 }, + { code: 'forgejo', name: 'Forgejo', description: 'Forgejo git server', icon: 'mdi-source-repository', url: 'https://forgejo.example', applicationsClaim: 'forgejo', displayOrder: 40 }, + { code: 'immich', name: 'Immich', description: 'Immich photo server', icon: 'mdi-image', url: 'https://immich.example', applicationsClaim: 'immich', displayOrder: 50 }, + { code: 'grafana', name: 'Grafana', description: 'Grafana monitoring', icon: 'mdi-chart-line', url: 'https://grafana.example', applicationsClaim: 'grafana', displayOrder: 60 }, + { code: 'vaultwarden', name: 'Vaultwarden', description: 'Vaultwarden password manager', icon: 'mdi-lock', url: 'https://vaultwarden.example', applicationsClaim: 'vaultwarden', displayOrder: 70 }, + { code: 'homepage', name: 'Homepage', description: 'Homepage', icon: 'mdi-home', url: 'https://home.example', applicationsClaim: 'homepage', displayOrder: 80 }, + ]; + + for (const a of apps) { + await prisma.application.upsert({ + where: { code: a.code }, + update: { name: a.name, description: a.description, icon: a.icon, url: a.url, applicationsClaim: a.applicationsClaim, isActive: true, displayOrder: a.displayOrder }, + create: { code: a.code, name: a.name, description: a.description, icon: a.icon, url: a.url, applicationsClaim: a.applicationsClaim, isActive: true, displayOrder: a.displayOrder }, + }); + } + + console.log('Seeding completed.'); +} + +main() + .catch(async (e) => { + console.error(e); + await prisma.$disconnect(); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); + + diff --git a/src/app.module.ts b/src/app.module.ts index e6e9b1a..b2459ba 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -3,6 +3,11 @@ import { ConfigModule } from '@nestjs/config'; import { IdentityModule } from './modules/identity/identity.module'; import { AuthModule } from './modules/auth/auth.module'; +import { HealthModule } from './modules/health/health.module'; +import { AuthorizationModule } from './modules/authorization/authorization.module'; +import { AuditModule } from './modules/audit/audit.module'; +import { ApplicationModule } from './modules/application/application.module'; + @Module({ imports: [ @@ -10,8 +15,14 @@ import { AuthModule } from './modules/auth/auth.module'; isGlobal: true, }), - IdentityModule, - AuthModule, + IdentityModule, + AuthModule, + HealthModule, + // Authorization module provides permission checks and cache + AuthorizationModule, + AuditModule, + ApplicationModule, + ], }) export class AppModule {} \ No newline at end of file diff --git a/src/core/event-bus/event-bus.service.ts b/src/core/event-bus/event-bus.service.ts new file mode 100644 index 0000000..b8b0336 --- /dev/null +++ b/src/core/event-bus/event-bus.service.ts @@ -0,0 +1,28 @@ +import { Injectable, Logger } from '@nestjs/common'; + +import { EventEnvelope } from './event.interface'; + +type Handler = (event: EventEnvelope) => Promise | void; + +@Injectable() +export class EventBus { + private handlers: Map = new Map(); + private readonly logger = new Logger(EventBus.name); + + publish(event: EventEnvelope) { + const handlers = this.handlers.get(event.type) || []; + for (const h of handlers) { + try { + Promise.resolve(h(event)).catch((err) => this.logger.error('Event handler error', err)); + } catch (e) { + this.logger.error('Event handler threw', e as any); + } + } + } + + subscribe(eventType: string, handler: Handler) { + const list = this.handlers.get(eventType) || []; + list.push(handler); + this.handlers.set(eventType, list); + } +} diff --git a/src/core/event-bus/event.interface.ts b/src/core/event-bus/event.interface.ts new file mode 100644 index 0000000..d1fb4ab --- /dev/null +++ b/src/core/event-bus/event.interface.ts @@ -0,0 +1,6 @@ +export interface EventEnvelope { + id: string; + timestamp: string; // ISO + type: string; // PascalCase event type + payload: T; +} diff --git a/src/modules/application/application.module.ts b/src/modules/application/application.module.ts new file mode 100644 index 0000000..0b9ce52 --- /dev/null +++ b/src/modules/application/application.module.ts @@ -0,0 +1,37 @@ +import { Module } from '@nestjs/common'; +import { PrismaService } from '../../shared/prisma.service'; +import { ApplicationsController } from './presentation/controllers/applications.controller'; +import { PrismaApplicationRepository } from './infrastructure/repositories/prisma-application.repository'; +import { IApplication } from './domain/repositories/application.interface'; +import { GetApplicationsHandler } from './application/handlers/get-applications.handler'; +import { GetApplicationHandler } from './application/handlers/get-application.handler'; +import { CreateApplicationHandler } from './application/handlers/create-application.handler'; +import { UpdateApplicationHandler } from './application/handlers/update-application.handler'; +import { DeleteApplicationHandler } from './application/handlers/delete-application.handler'; +import { GetMeApplicationsHandler } from './application/handlers/get-me-applications.handler'; +import { ApplicationValidator } from './application/validators/application.validator'; +import { JwtAuthGuard } from '../../core/auth/guards/jwt-auth.guard'; +import { CurrentUserGuard } from '../identity/presentation/guards/current-user.guard'; + +@Module({ + imports: [], + providers: [ + PrismaService, + PrismaApplicationRepository, + GetApplicationsHandler, + GetApplicationHandler, + CreateApplicationHandler, + UpdateApplicationHandler, + DeleteApplicationHandler, + GetMeApplicationsHandler, + ApplicationValidator, + JwtAuthGuard, + CurrentUserGuard, + { + provide: IApplication, + useClass: PrismaApplicationRepository, + }, + ], + controllers: [ApplicationsController], +}) +export class ApplicationModule {} diff --git a/src/modules/application/application/handlers/create-application.handler.ts b/src/modules/application/application/handlers/create-application.handler.ts new file mode 100644 index 0000000..b04d1fd --- /dev/null +++ b/src/modules/application/application/handlers/create-application.handler.ts @@ -0,0 +1,26 @@ +import { Injectable, BadRequestException } from '@nestjs/common'; +import { IApplication } from '../../domain/repositories/application.interface'; +import { ApplicationValidator } from '../../application/validators/application.validator'; +import { ApplicationData } from '../../domain/entities/application.entity'; + +@Injectable() +export class CreateApplicationHandler { + constructor(private readonly appRepo: IApplication, private readonly validator: ApplicationValidator) {} + + async execute(payload: any) { + await this.validator.validateCreate(payload); + + const app = ApplicationData.create({ + code: payload.code, + name: payload.name, + description: payload.description, + icon: payload.icon, + url: payload.url, + applicationsClaim: payload.applicationsClaim, + displayOrder: payload.displayOrder, + }); + + const created = await this.appRepo.create(app); + return created; + } +} diff --git a/src/modules/application/application/handlers/delete-application.handler.ts b/src/modules/application/application/handlers/delete-application.handler.ts new file mode 100644 index 0000000..383353c --- /dev/null +++ b/src/modules/application/application/handlers/delete-application.handler.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@nestjs/common'; +import { IApplication } from '../../domain/repositories/application.interface'; + +@Injectable() +export class DeleteApplicationHandler { + constructor(private readonly appRepo: IApplication) {} + + async execute(id: string) { + // ensure exists + await this.appRepo.findById(id); + await this.appRepo.delete(id); + } +} diff --git a/src/modules/application/application/handlers/get-application.handler.ts b/src/modules/application/application/handlers/get-application.handler.ts new file mode 100644 index 0000000..dc1eeb8 --- /dev/null +++ b/src/modules/application/application/handlers/get-application.handler.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@nestjs/common'; +import { IApplication } from '../../domain/repositories/application.interface'; + +@Injectable() +export class GetApplicationHandler { + constructor(private readonly appRepo: IApplication) {} + + async execute(id: string) { + const app = await this.appRepo.findById(id); + return app; + } +} diff --git a/src/modules/application/application/handlers/get-applications.handler.ts b/src/modules/application/application/handlers/get-applications.handler.ts new file mode 100644 index 0000000..8af3a91 --- /dev/null +++ b/src/modules/application/application/handlers/get-applications.handler.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@nestjs/common'; +import { IApplication } from '../../domain/repositories/application.interface'; + +@Injectable() +export class GetApplicationsHandler { + constructor(private readonly appRepo: IApplication) {} + + async execute(query: { page?: number; limit?: number; search?: string }) { + const res = await this.appRepo.find({ page: query.page, limit: query.limit, search: query.search || null }); + return res; + } +} diff --git a/src/modules/application/application/handlers/get-me-applications.handler.ts b/src/modules/application/application/handlers/get-me-applications.handler.ts new file mode 100644 index 0000000..c35274d --- /dev/null +++ b/src/modules/application/application/handlers/get-me-applications.handler.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { IApplication } from '../../domain/repositories/application.interface'; + +@Injectable() +export class GetMeApplicationsHandler { + constructor(private readonly appRepo: IApplication) {} + + async execute(applicationsClaimList: string[] | null | undefined) { + if (!applicationsClaimList || applicationsClaimList.length === 0) return { data: [], total: 0 }; + + // Only return active applications whose applicationsClaim exists in provided list + const res = await this.appRepo.find({ page: 1, limit: 1000, isActive: true, applicationsClaimIn: applicationsClaimList }); + // sort by displayOrder asc + res.data.sort((a, b) => (a.displayOrder ?? 0) - (b.displayOrder ?? 0)); + return res; + } +} diff --git a/src/modules/application/application/handlers/update-application.handler.ts b/src/modules/application/application/handlers/update-application.handler.ts new file mode 100644 index 0000000..e9b6fe1 --- /dev/null +++ b/src/modules/application/application/handlers/update-application.handler.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@nestjs/common'; +import { IApplication } from '../../domain/repositories/application.interface'; +import { ApplicationValidator } from '../../application/validators/application.validator'; + +@Injectable() +export class UpdateApplicationHandler { + constructor(private readonly appRepo: IApplication, private readonly validator: ApplicationValidator) {} + + async execute(id: string, payload: any) { + const existing = await this.appRepo.findById(id); + if (!existing) throw new Error('Application not found'); + + await this.validator.validateUpdate(id, payload); + + existing.update(payload); + const updated = await this.appRepo.update(existing); + return updated; + } +} diff --git a/src/modules/application/application/validators/application.validator.ts b/src/modules/application/application/validators/application.validator.ts new file mode 100644 index 0000000..7d0833c --- /dev/null +++ b/src/modules/application/application/validators/application.validator.ts @@ -0,0 +1,64 @@ +import { Injectable, BadRequestException } from '@nestjs/common'; +import { IApplication } from '../../domain/repositories/application.interface'; + +@Injectable() +export class ApplicationValidator { + constructor(private readonly appRepo: IApplication) {} + + async validateCreate(payload: any) { + if (!payload || !payload.code) throw new BadRequestException('code is required'); + if (!payload.name) throw new BadRequestException('name is required'); + if (!payload.applicationsClaim) throw new BadRequestException('applicationsClaim is required'); + + // url validation if provided + if (payload.url) { + try { + // allow relative urls + if (!payload.url.startsWith('/') && !payload.url.startsWith('http')) { + throw new Error('invalid'); + } + // new URL(payload.url) // avoid throwing for relative + } catch (e) { + throw new BadRequestException('invalid url'); + } + } + + // displayOrder + if (payload.displayOrder !== undefined && typeof payload.displayOrder !== 'number') { + throw new BadRequestException('invalid displayOrder'); + } + + // unique code + const byCode = await this.appRepo.findByCode(payload.code); + if (byCode) throw new BadRequestException('duplicate code'); + + const byClaim = await this.appRepo.findByApplicationsClaim(payload.applicationsClaim); + if (byClaim) throw new BadRequestException('duplicate applicationsClaim'); + } + + async validateUpdate(id: string, payload: any) { + if (!payload) return; + + if (payload.url) { + try { + if (!payload.url.startsWith('/') && !payload.url.startsWith('http')) throw new Error('invalid'); + } catch (e) { + throw new BadRequestException('invalid url'); + } + } + + if (payload.displayOrder !== undefined && typeof payload.displayOrder !== 'number') { + throw new BadRequestException('invalid displayOrder'); + } + + if (payload.code) { + const existing = await this.appRepo.findByCode(payload.code); + if (existing && existing.id !== id) throw new BadRequestException('duplicate code'); + } + + if (payload.applicationsClaim) { + const existing = await this.appRepo.findByApplicationsClaim(payload.applicationsClaim); + if (existing && existing.id !== id) throw new BadRequestException('duplicate applicationsClaim'); + } + } +} diff --git a/src/modules/application/domain/entities/application.entity.ts b/src/modules/application/domain/entities/application.entity.ts new file mode 100644 index 0000000..0601738 --- /dev/null +++ b/src/modules/application/domain/entities/application.entity.ts @@ -0,0 +1,100 @@ +export class ApplicationData { + private constructor( + public readonly id: string, + public code: string, + public name: string, + public description: string | null, + public icon: string | null, + public url: string | null, + public applicationsClaim: string, + public isActive: boolean, + public displayOrder: number, + public createdAt: Date | null, + public updatedAt: Date | null, + ) {} + + static create(data: { + code: string; + name: string; + description?: string | null; + icon?: string | null; + url?: string | null; + applicationsClaim: string; + displayOrder?: number; + }) { + return new ApplicationData( + crypto.randomUUID(), + data.code, + data.name, + data.description ?? null, + data.icon ?? null, + data.url ?? null, + data.applicationsClaim, + true, + data.displayOrder ?? 0, + new Date(), + new Date(), + ); + } + + static restore(props: { + id: string; + code: string; + name: string; + description?: string | null; + icon?: string | null; + url?: string | null; + applicationsClaim: string; + isActive?: boolean; + displayOrder?: number; + createdAt?: Date | null; + updatedAt?: Date | null; + }) { + return new ApplicationData( + props.id, + props.code, + props.name, + props.description ?? null, + props.icon ?? null, + props.url ?? null, + props.applicationsClaim, + props.isActive !== undefined ? props.isActive : true, + props.displayOrder ?? 0, + props.createdAt ?? null, + props.updatedAt ?? null, + ); + } + + update(data: { + code?: string; + name?: string; + description?: string | null; + icon?: string | null; + url?: string | null; + applicationsClaim?: string; + isActive?: boolean; + displayOrder?: number; + }) { + if (data.code !== undefined) this.code = data.code; + if (data.name !== undefined) this.name = data.name; + if (data.description !== undefined) this.description = data.description; + if (data.icon !== undefined) this.icon = data.icon; + if (data.url !== undefined) this.url = data.url; + if (data.applicationsClaim !== undefined) this.applicationsClaim = data.applicationsClaim; + if (data.isActive !== undefined) this.isActive = data.isActive; + if (data.displayOrder !== undefined) this.displayOrder = data.displayOrder; + this.updatedAt = new Date(); + } + + toResponse() { + return { + id: this.id, + code: this.code, + name: this.name, + description: this.description, + icon: this.icon, + url: this.url, + displayOrder: this.displayOrder, + }; + } +} diff --git a/src/modules/application/domain/repositories/application.interface.ts b/src/modules/application/domain/repositories/application.interface.ts new file mode 100644 index 0000000..55e855b --- /dev/null +++ b/src/modules/application/domain/repositories/application.interface.ts @@ -0,0 +1,12 @@ +import { ApplicationData } from '../entities/application.entity'; + +export abstract class IApplication { + abstract find(params: { page?: number; limit?: number; search?: string | null; isActive?: boolean | null; applicationsClaimIn?: string[] | null }): Promise<{ data: ApplicationData[]; total: number }>; + abstract findById(id: string): Promise; + abstract findByCode(code: string): Promise; + abstract findByApplicationsClaim(claim: string): Promise; + + abstract create(app: ApplicationData): Promise; + abstract update(app: ApplicationData): Promise; + abstract delete(appId: string): Promise; +} diff --git a/src/modules/application/infrastructure/mappers/prisma-application.mapper.ts b/src/modules/application/infrastructure/mappers/prisma-application.mapper.ts new file mode 100644 index 0000000..5f9ab4c --- /dev/null +++ b/src/modules/application/infrastructure/mappers/prisma-application.mapper.ts @@ -0,0 +1,19 @@ +import { ApplicationData } from '../../domain/entities/application.entity'; + +export class PrismaApplicationMapper { + static toDomain(model: any): ApplicationData { + return ApplicationData.restore({ + id: model.id, + code: model.code, + name: model.name, + description: model.description, + icon: model.icon, + url: model.url, + applicationsClaim: model.applicationsClaim, + isActive: model.isActive, + displayOrder: model.displayOrder, + createdAt: model.createdAt, + updatedAt: model.updatedAt, + }); + } +} diff --git a/src/modules/application/infrastructure/repositories/prisma-application.repository.ts b/src/modules/application/infrastructure/repositories/prisma-application.repository.ts new file mode 100644 index 0000000..d7be1b1 --- /dev/null +++ b/src/modules/application/infrastructure/repositories/prisma-application.repository.ts @@ -0,0 +1,92 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../../../../shared/prisma.service'; +import { IApplication } from '../../domain/repositories/application.interface'; +import { PrismaApplicationMapper } from '../mappers/prisma-application.mapper'; +import { ApplicationData } from '../../domain/entities/application.entity'; + +@Injectable() +export class PrismaApplicationRepository implements IApplication { + constructor(private readonly prisma: PrismaService) {} + + async find(params: { page?: number; limit?: number; search?: string | null; isActive?: boolean | null; applicationsClaimIn?: string[] | null }) { + const page = params.page && params.page > 0 ? params.page : 1; + const limit = params.limit && params.limit > 0 ? params.limit : 25; + + const where: any = {}; + if (params.search) { + where.OR = [ + { name: { contains: params.search, mode: 'insensitive' } }, + { code: { contains: params.search, mode: 'insensitive' } }, + { description: { contains: params.search, mode: 'insensitive' } }, + ]; + } + + if (params.isActive !== undefined && params.isActive !== null) { + where.isActive = params.isActive; + } + + if (params.applicationsClaimIn && params.applicationsClaimIn.length > 0) { + where.applicationsClaim = { in: params.applicationsClaimIn }; + } + + const [total, items] = await Promise.all([ + this.prisma.application.count({ where }), + this.prisma.application.findMany({ where, skip: (page - 1) * limit, take: limit, orderBy: { displayOrder: 'asc', createdAt: 'asc' } }), + ]); + + return { data: items.map(i => PrismaApplicationMapper.toDomain(i)), total }; + } + + async findById(id: string) { + const row = await this.prisma.application.findUnique({ where: { id } }); + if (!row) throw new Error('Application not found'); + return PrismaApplicationMapper.toDomain(row); + } + + async findByCode(code: string) { + const row = await this.prisma.application.findUnique({ where: { code } }); + if (!row) return null; + return PrismaApplicationMapper.toDomain(row); + } + + async findByApplicationsClaim(claim: string) { + const row = await this.prisma.application.findUnique({ where: { applicationsClaim: claim } }); + if (!row) return null; + return PrismaApplicationMapper.toDomain(row); + } + + async create(app: ApplicationData) { + const created = await this.prisma.application.create({ data: { + id: app.id, + code: app.code, + name: app.name, + description: app.description, + icon: app.icon, + url: app.url, + applicationsClaim: app.applicationsClaim, + isActive: app.isActive, + displayOrder: app.displayOrder, + } }); + + return PrismaApplicationMapper.toDomain(created); + } + + async update(app: ApplicationData) { + const updated = await this.prisma.application.update({ where: { id: app.id }, data: { + code: app.code, + name: app.name, + description: app.description, + icon: app.icon, + url: app.url, + applicationsClaim: app.applicationsClaim, + isActive: app.isActive, + displayOrder: app.displayOrder, + } }); + + return PrismaApplicationMapper.toDomain(updated); + } + + async delete(appId: string) { + await this.prisma.application.delete({ where: { id: appId } }); + } +} diff --git a/src/modules/application/presentation/controllers/applications.controller.ts b/src/modules/application/presentation/controllers/applications.controller.ts new file mode 100644 index 0000000..2063864 --- /dev/null +++ b/src/modules/application/presentation/controllers/applications.controller.ts @@ -0,0 +1,76 @@ +import { Controller, Get, Param, UseGuards, Query, Patch, Delete, Post, Body, Req } from '@nestjs/common'; +import { ApiTags, ApiOperation } from '@nestjs/swagger'; +import { JwtAuthGuard } from '../../../../core/auth/guards/jwt-auth.guard'; +import { CurrentUserGuard } from '../../../identity/presentation/guards/current-user.guard'; +import { GetApplicationsHandler } from '../../application/handlers/get-applications.handler'; +import { GetApplicationHandler } from '../../application/handlers/get-application.handler'; +import { CreateApplicationHandler } from '../../application/handlers/create-application.handler'; +import { UpdateApplicationHandler } from '../../application/handlers/update-application.handler'; +import { DeleteApplicationHandler } from '../../application/handlers/delete-application.handler'; +import { GetMeApplicationsHandler } from '../../application/handlers/get-me-applications.handler'; + +@ApiTags('Applications') +@Controller() +export class ApplicationsController { + constructor( + private readonly getApplicationsHandler: GetApplicationsHandler, + private readonly getApplicationHandler: GetApplicationHandler, + private readonly createApplicationHandler: CreateApplicationHandler, + private readonly updateApplicationHandler: UpdateApplicationHandler, + private readonly deleteApplicationHandler: DeleteApplicationHandler, + private readonly getMeApplicationsHandler: GetMeApplicationsHandler, + ) {} + + @Get('applications') + @UseGuards(JwtAuthGuard, CurrentUserGuard) + @ApiOperation({ summary: 'List applications' }) + async findAll(@Query() query: any) { + const res = await this.getApplicationsHandler.execute({ page: query.page, limit: query.limit, search: query.search }); + return { success: true, data: res.data.map(a => a.toResponse ? a.toResponse() : a), meta: { total: res.total } }; + } + + @Get('applications/:id') + @UseGuards(JwtAuthGuard, CurrentUserGuard) + @ApiOperation({ summary: 'Get application by id' }) + async findOne(@Param('id') id: string) { + const app = await this.getApplicationHandler.execute(id); + return { success: true, data: app.toResponse(), meta: {} }; + } + + @Post('applications') + @UseGuards(JwtAuthGuard, CurrentUserGuard) + @ApiOperation({ summary: 'Create application' }) + async create(@Body() body: any) { + const created = await this.createApplicationHandler.execute(body); + return { success: true, data: created.toResponse(), meta: {} }; + } + + @Patch('applications/:id') + @UseGuards(JwtAuthGuard, CurrentUserGuard) + @ApiOperation({ summary: 'Update application' }) + async update(@Param('id') id: string, @Body() body: any) { + const updated = await this.updateApplicationHandler.execute(id, body); + return { success: true, data: updated.toResponse(), meta: {} }; + } + + @Delete('applications/:id') + @UseGuards(JwtAuthGuard, CurrentUserGuard) + @ApiOperation({ summary: 'Delete application' }) + async remove(@Param('id') id: string) { + await this.deleteApplicationHandler.execute(id); + return { success: true, data: null, meta: {} }; + } + + // Dashboard endpoint + @Get('me/applications') + @UseGuards(JwtAuthGuard, CurrentUserGuard) + @ApiOperation({ summary: "Get current user's applications (filtered by Authentik claims)" }) + async me(@Req() req: any) { + const ctx = req.raylabContext; + const identity = ctx && ctx.identity ? ctx.identity : {}; + const applicationsClaimList = identity.applications || []; + + const res = await this.getMeApplicationsHandler.execute(applicationsClaimList); + return { success: true, data: res.data.map(a => a.toResponse ? a.toResponse() : a), meta: { total: res.total } }; + } +} diff --git a/src/modules/audit/audit.event-handler.ts b/src/modules/audit/audit.event-handler.ts new file mode 100644 index 0000000..d4e1842 --- /dev/null +++ b/src/modules/audit/audit.event-handler.ts @@ -0,0 +1,46 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { EventBus } from '../../core/event-bus/event-bus.service'; +import { EventEnvelope } from '../../core/event-bus/event.interface'; +import { AuditService } from './audit.service'; + +@Injectable() +export class AuditEventHandler { + private readonly logger = new Logger(AuditEventHandler.name); + + constructor(private readonly events: EventBus, private readonly auditService: AuditService) { + this.events.subscribe('RolesSynchronized', (e) => this.handleRolesSynchronized(e)); + this.events.subscribe('UserAuthenticated', (e) => this.handleUserAuthenticated(e)); + this.events.subscribe('RoleCreated', (e) => this.handleGeneric(e)); + this.events.subscribe('RoleUpdated', (e) => this.handleGeneric(e)); + this.events.subscribe('RoleDeleted', (e) => this.handleGeneric(e)); + this.events.subscribe('PermissionCreated', (e) => this.handleGeneric(e)); + this.events.subscribe('PermissionUpdated', (e) => this.handleGeneric(e)); + this.events.subscribe('PermissionDeleted', (e) => this.handleGeneric(e)); + this.events.subscribe('UserActivated', (e) => this.handleGeneric(e)); + this.events.subscribe('UserDeactivated', (e) => this.handleGeneric(e)); + } + + async handleRolesSynchronized(event: EventEnvelope) { + try { + await this.auditService.createFromEvent(event); + } catch (e) { + this.logger.error('Audit handler failed', e as any); + } + } + + async handleUserAuthenticated(event: EventEnvelope) { + try { + await this.auditService.createFromEvent(event); + } catch (e) { + this.logger.error('Audit handler failed', e as any); + } + } + + async handleGeneric(event: EventEnvelope) { + try { + await this.auditService.createFromEvent(event); + } catch (e) { + this.logger.error('Audit handler failed', e as any); + } + } +} diff --git a/src/modules/audit/audit.module.ts b/src/modules/audit/audit.module.ts new file mode 100644 index 0000000..6f7b286 --- /dev/null +++ b/src/modules/audit/audit.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { AuditService } from './audit.service'; +import { AuditEventHandler } from './audit.event-handler'; +import { PrismaService } from '../../shared/prisma.service'; +import { EventBus } from '../../core/event-bus/event-bus.service'; + +@Module({ + providers: [AuditService, AuditEventHandler, PrismaService, EventBus], + exports: [AuditService], +}) +export class AuditModule {} diff --git a/src/modules/audit/audit.service.ts b/src/modules/audit/audit.service.ts new file mode 100644 index 0000000..5526b94 --- /dev/null +++ b/src/modules/audit/audit.service.ts @@ -0,0 +1,28 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { PrismaService } from '../../shared/prisma.service'; +import { EventEnvelope } from '../../core/event-bus/event.interface'; + +@Injectable() +export class AuditService { + private readonly logger = new Logger(AuditService.name); + + constructor(private readonly prisma: PrismaService) {} + + async createFromEvent(event: EventEnvelope) { + try { + // Map standard events to AuditLog entries + await this.prisma.auditLog.create({ + data: { + userId: event.payload?.userId || null, + action: event.type, + resource: event.payload?.resource || null, + resourceId: event.payload?.resourceId || null, + details: event.payload, + createdAt: new Date(event.timestamp), + }, + }); + } catch (e) { + this.logger.error('Failed to write audit log', e as any); + } + } +} diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts index 820594c..0e02aff 100644 --- a/src/modules/auth/auth.module.ts +++ b/src/modules/auth/auth.module.ts @@ -13,10 +13,20 @@ import { IAuthConfig } from '../identity/application/config/i-auth-config'; import { EnvAuthConfig } from '../identity/application/config/env-auth-config'; import { RedisPkceStore } from './pkce/redis-pkce.store'; import { InMemoryRefreshStore } from './refresh/inmemory-refresh.store'; +import { OidcService } from './oidc.service'; +import { RoleSyncService } from './role-sync.service'; +import { RedisService } from '../../shared/redis.service'; +import { GroupHashService } from './group-hash.service'; +import { AuthenticationService } from './authentication.service'; +import { EventBus } from '../../core/event-bus/event-bus.service'; +import { AuditService } from '../audit/audit.service'; +import { AuthorizationModule } from '../authorization/authorization.module'; + @Module({ imports: [ ConfigModule, + AuthorizationModule, JwtModule.registerAsync({ imports: [ConfigModule], useFactory: async (config: ConfigService) => ({ @@ -40,7 +50,16 @@ import { InMemoryRefreshStore } from './refresh/inmemory-refresh.store'; // In-memory PKCE and Refresh stores (Redis removed) RedisPkceStore, InMemoryRefreshStore, + + // OIDC & Role Sync + OidcService, + RoleSyncService, + GroupHashService, + AuthenticationService, + EventBus, + AuditService, + RedisService, ], - exports: [AuthService], + exports: [AuthService, OidcService, RoleSyncService, AuthenticationService, EventBus], }) export class AuthModule {} diff --git a/src/modules/auth/authentication.service.ts b/src/modules/auth/authentication.service.ts new file mode 100644 index 0000000..7f22740 --- /dev/null +++ b/src/modules/auth/authentication.service.ts @@ -0,0 +1,55 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { OidcService } from './oidc.service'; +import { PrismaService } from '../../shared/prisma.service'; +import { RoleSyncService } from './role-sync.service'; +import { AuthorizationService } from '../authorization/authorization.service'; +import { EventBus } from '../../core/event-bus/event-bus.service'; +import { RequestContext } from '../../shared/types/request-context'; + +@Injectable() +export class AuthenticationService { + private readonly logger = new Logger(AuthenticationService.name); + + constructor( + private readonly oidc: OidcService, + private readonly prisma: PrismaService, + private readonly roleSync: RoleSyncService, + private readonly authorization: AuthorizationService, + private readonly events: EventBus, + ) {} + + async authenticate(bearerToken: string): Promise { + // Validate token (signature/iss/aud/exp) + const claims = await this.oidc.verifyToken(bearerToken); + + const sub = claims.sub; + if (!sub) throw new Error('Invalid token: missing sub'); + + // Resolve identity + const identity = { sub, email: claims.email, preferred_username: claims.preferred_username, raw: claims }; + + // Find or create user (materialize) + let user = await (this.prisma as any).user.findUnique({ where: { authentikId: sub } }); + if (!user) { + user = await (this.prisma as any).user.create({ data: { authentikId: sub, username: identity.preferred_username || identity.email || sub, email: identity.email || null } }); + } + + // Synchronize roles + const groups: string[] = Array.isArray(claims.groups) ? claims.groups : []; + await this.roleSync.syncUserRolesFromAuthentik(user.id, groups); + + // Load permissions + const permissions = await this.authorization.getUserPermissions(user.id); + + // Build request context + const rolesRows = await (this.prisma as any).userRole.findMany({ where: { userId: user.id } }); + const roles = rolesRows.map((r: any) => r.roleId); + + const ctx: RequestContext = { user, identity, roles, permissions }; + + // Publish domain event + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'UserAuthenticated', payload: { userId: user.id, identity } }); + + return ctx; + } +} diff --git a/src/modules/auth/group-hash.service.ts b/src/modules/auth/group-hash.service.ts new file mode 100644 index 0000000..32eb60d --- /dev/null +++ b/src/modules/auth/group-hash.service.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import * as crypto from 'crypto'; + +@Injectable() +export class GroupHashService { + compute(groups: string[]): string { + const sorted = (groups || []).slice().sort(); + const data = sorted.join(','); + return crypto.createHash('sha256').update(data, 'utf8').digest('hex'); + } +} diff --git a/src/modules/auth/guards/oidc.guard.ts b/src/modules/auth/guards/oidc.guard.ts new file mode 100644 index 0000000..1bd0d27 --- /dev/null +++ b/src/modules/auth/guards/oidc.guard.ts @@ -0,0 +1,26 @@ +import { CanActivate, ExecutionContext, Injectable, UnauthorizedException, Logger, Inject } from '@nestjs/common'; +import { OidcService } from '../oidc.service'; +import { AuthenticationService } from '../authentication.service'; + +@Injectable() +export class OidcGuard implements CanActivate { + private readonly logger = new Logger(OidcGuard.name); + constructor(private readonly oidc: OidcService, private readonly authn: AuthenticationService) {} + + async canActivate(context: ExecutionContext): Promise { + const req = context.switchToHttp().getRequest(); + const auth = req.headers['authorization'] || req.headers['Authorization']; + if (!auth || typeof auth !== 'string' || !auth.startsWith('Bearer ')) throw new UnauthorizedException('Missing bearer token'); + const token = auth.substring(7).trim(); + + try { + const ctx = await this.authn.authenticate(token); + // attach context to request under a structured key + req.raylabContext = ctx; + return true; + } catch (e) { + this.logger.debug('Authentication failed', (e as any).message); + throw new UnauthorizedException('Invalid token or authentication failed'); + } + } +} diff --git a/src/modules/auth/oidc.service.ts b/src/modules/auth/oidc.service.ts new file mode 100644 index 0000000..42205b7 --- /dev/null +++ b/src/modules/auth/oidc.service.ts @@ -0,0 +1,37 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; + +@Injectable() +export class OidcService { + private jwksUri: string | null = null; + private issuer: string; + private audience: string | string[] | undefined; + private logger = new Logger(OidcService.name); + + constructor(private readonly config: ConfigService) { + this.issuer = this.config.get('AUTHENTIK_ISSUER') || ''; + this.audience = this.config.get('AUTHENTIK_AUDIENCE') || undefined; + const jwksUri = this.config.get('AUTHENTIK_JWKS_URI'); + if (jwksUri) this.jwksUri = jwksUri; + else if (this.issuer) this.jwksUri = `${this.issuer.replace(/\/+$/, '')}/.well-known/jwks.json`; + } + + async verifyToken(token: string) { + if (!this.jwksUri) throw new Error('JWKS not configured'); + + try { + // dynamic import to avoid ESM loading issues in test environment + const jose = await import('jose'); + const jwks = jose.createRemoteJWKSet(new URL(this.jwksUri)); + const { payload } = await jose.jwtVerify(token, jwks, { + issuer: this.issuer || undefined, + audience: this.audience, + } as any); + + return payload as Record; + } catch (e) { + this.logger.debug('Token verification failed', (e as Error).message); + throw e; + } + } +} diff --git a/src/modules/auth/role-sync.service.ts b/src/modules/auth/role-sync.service.ts new file mode 100644 index 0000000..2abc796 --- /dev/null +++ b/src/modules/auth/role-sync.service.ts @@ -0,0 +1,74 @@ +import { Injectable, Logger, Inject } from '@nestjs/common'; +import { PrismaService } from '../../shared/prisma.service'; +import { PermissionCache } from '../authorization/cache/permission-cache.interface'; +import { EventBus } from '../../core/event-bus/event-bus.service'; +import { GroupHashService } from './group-hash.service'; +import { PERMISSION_CACHE } from '../authorization/authorization.service'; + +@Injectable() +export class RoleSyncService { + private readonly logger = new Logger(RoleSyncService.name); + + constructor( + private readonly prisma: PrismaService, + private readonly groupHash: GroupHashService, + @Inject(PERMISSION_CACHE) private readonly permissionCache: PermissionCache, + private readonly events: EventBus, + ) {} + + computeGroupHash(groups: string[]): string { + return this.groupHash.compute(groups || []); + } + + async mapGroupsToRoleIds(groups: string[]): Promise { + if (!groups || groups.length === 0) return []; + const mappings = await (this.prisma as any).authGroupRoleMapping.findMany({ where: { authGroup: { in: groups } } }); + const roleIds = mappings.map((m: any) => m.roleId); + return Array.from(new Set(roleIds)); + } + + async syncUserRolesFromAuthentik(userId: string, groups: string[]) { + const groupHash = this.computeGroupHash(groups || []); + + const user = await (this.prisma as any).user.findUnique({ where: { id: userId } }); + if (!user) throw new Error('User not found'); + + if (user.lastGroupHash === groupHash) { + this.logger.debug('Group hash unchanged, skipping sync'); + return { skipped: true }; + } + + const roleIds = await this.mapGroupsToRoleIds(groups || []); + + const previousRoleRows = await (this.prisma as any).userRole.findMany({ where: { userId, source: 'AUTHENTIK' }, select: { roleId: true } }); + const previousRoles = previousRoleRows.map((r: any) => r.roleId); + + await (this.prisma as any).$transaction(async (tx: any) => { + await tx.userRole.deleteMany({ where: { userId: userId, source: 'AUTHENTIK' } }); + + if (roleIds.length > 0) { + const createData = roleIds.map((rid: string) => ({ userId, roleId: rid, source: 'AUTHENTIK' })); + await tx.userRole.createMany({ data: createData, skipDuplicates: true } as any); + } + + await tx.user.update({ where: { id: userId }, data: { lastGroupHash: groupHash } }); + }); + + // Invalidate permission cache through abstraction + try { + await this.permissionCache.invalidate(userId); + } catch (e) { + this.logger.error('Failed to invalidate permission cache', e as any); + } + + // Publish event for audit and other subscribers + this.events.publish({ + id: require('crypto').randomUUID(), + timestamp: new Date().toISOString(), + type: 'RolesSynchronized', + payload: { userId, groups, assignedRoleIds: roleIds, previousRoles }, + }); + + return { skipped: false, assignedRoleIds: roleIds }; + } +} diff --git a/src/modules/authorization/authorization.module.ts b/src/modules/authorization/authorization.module.ts new file mode 100644 index 0000000..ac85e4e --- /dev/null +++ b/src/modules/authorization/authorization.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { AuthorizationService, PERMISSION_CACHE } from './authorization.service'; +import { PrismaService } from '../../shared/prisma.service'; +import { RedisService } from '../../shared/redis.service'; +import { RedisPermissionCache } from './cache/redis-permission-cache.service'; + +@Module({ + providers: [ + AuthorizationService, + PrismaService, + RedisService, + { provide: PERMISSION_CACHE, useClass: RedisPermissionCache }, + ], + exports: [AuthorizationService, PERMISSION_CACHE], +}) +export class AuthorizationModule {} diff --git a/src/modules/authorization/authorization.service.ts b/src/modules/authorization/authorization.service.ts new file mode 100644 index 0000000..09f0f19 --- /dev/null +++ b/src/modules/authorization/authorization.service.ts @@ -0,0 +1,51 @@ +import { Injectable, Logger, Inject } from '@nestjs/common'; +import { PrismaService } from '../../shared/prisma.service'; +import { PermissionCache } from './cache/permission-cache.interface'; + +export const PERMISSION_CACHE = 'PERMISSION_CACHE'; + +@Injectable() +export class AuthorizationService { + private readonly logger = new Logger(AuthorizationService.name); + + constructor(private readonly prisma: PrismaService, @Inject(PERMISSION_CACHE) private readonly cache: PermissionCache) {} + + async getUserPermissions(userId: string): Promise { + try { + const cached = await this.cache.get(userId); + if (cached) return cached; + } catch (e) { + this.logger.debug('PermissionCache get failed', e as any); + } + + const rows = await this.prisma.$queryRaw` + SELECT p.code as code + FROM "UserRole" ur + JOIN "RolePermission" rp ON rp.role_id = ur.role_id + JOIN "Permission" p ON p.id = rp.permission_id + WHERE ur.user_id = ${userId}`; + + const perms = Array.isArray(rows) ? rows.map((r: any) => r.code) : []; + + try { + await this.cache.set(userId, perms); + } catch (e) { + this.logger.debug('PermissionCache set failed', e as any); + } + + return perms; + } + + async hasPermission(userId: string, permissionCode: string): Promise { + const perms = await this.getUserPermissions(userId); + return perms.includes(permissionCode); + } + + async invalidateUserPermissions(userId: string) { + try { + await this.cache.invalidate(userId); + } catch (e) { + this.logger.debug('PermissionCache invalidate failed', e as any); + } + } +} diff --git a/src/modules/authorization/cache/permission-cache.interface.ts b/src/modules/authorization/cache/permission-cache.interface.ts new file mode 100644 index 0000000..a8e8eab --- /dev/null +++ b/src/modules/authorization/cache/permission-cache.interface.ts @@ -0,0 +1,5 @@ +export interface PermissionCache { + get(userId: string): Promise; + set(userId: string, permissions: string[], ttlSeconds?: number): Promise; + invalidate(userId: string): Promise; +} diff --git a/src/modules/authorization/cache/redis-permission-cache.service.ts b/src/modules/authorization/cache/redis-permission-cache.service.ts new file mode 100644 index 0000000..1b290f6 --- /dev/null +++ b/src/modules/authorization/cache/redis-permission-cache.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { PermissionCache } from './permission-cache.interface'; +import { RedisService } from '../../../shared/redis.service'; + +@Injectable() +export class RedisPermissionCache implements PermissionCache { + private readonly TTL = 60 * 5; + + constructor(private readonly redis: RedisService) {} + + private key(userId: string) { + const { CacheKeys } = require('../../../shared/cache-keys'); + return CacheKeys.permission(userId); + } + + async get(userId: string): Promise { + const data = await this.redis.get(this.key(userId)); + if (!data) return null; + try { + return JSON.parse(data) as string[]; + } catch { + return null; + } + } + + async set(userId: string, permissions: string[], ttlSeconds?: number): Promise { + await this.redis.set(this.key(userId), JSON.stringify(permissions), ttlSeconds ?? this.TTL); + } + + async invalidate(userId: string): Promise { + await this.redis.del(this.key(userId)); + } +} diff --git a/src/modules/authorization/guards/permission.guard.ts b/src/modules/authorization/guards/permission.guard.ts new file mode 100644 index 0000000..0aa3e89 --- /dev/null +++ b/src/modules/authorization/guards/permission.guard.ts @@ -0,0 +1,28 @@ +import { CanActivate, ExecutionContext, Injectable, ForbiddenException } from '@nestjs/common'; +import { AuthorizationService } from '../authorization.service'; + +@Injectable() +export class PermissionGuard implements CanActivate { + constructor(private readonly authz: AuthorizationService, private readonly permission: string) {} + + async canActivate(context: ExecutionContext): Promise { + const req = context.switchToHttp().getRequest(); + const user = req.raylab?.user; + if (!user) throw new ForbiddenException('Missing user'); + + const allowed = await this.authz.hasPermission(user.id, this.permission); + if (!allowed) throw new ForbiddenException('Forbidden'); + return true; + } +} + +// Factory to create guard instances with permission string (used in decorators) +export const createPermissionGuard = (permission: string) => { + @Injectable() + class _Guard extends PermissionGuard { + constructor(authz: AuthorizationService) { + super(authz, permission); + } + } + return _Guard; +}; diff --git a/src/modules/health/health.controller.ts b/src/modules/health/health.controller.ts new file mode 100644 index 0000000..239c869 --- /dev/null +++ b/src/modules/health/health.controller.ts @@ -0,0 +1,12 @@ +import { Controller, Get } from '@nestjs/common'; +import { HealthService } from './health.service'; + +@Controller('api/health') +export class HealthController { + constructor(private readonly healthService: HealthService) {} + + @Get() + async get() { + return this.healthService.getHealth(); + } +} diff --git a/src/modules/health/health.module.ts b/src/modules/health/health.module.ts new file mode 100644 index 0000000..a38cb2c --- /dev/null +++ b/src/modules/health/health.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { HealthController } from './health.controller'; +import { HealthService } from './health.service'; + +@Module({ + controllers: [HealthController], + providers: [HealthService], + exports: [HealthService], +}) +export class HealthModule {} diff --git a/src/modules/health/health.service.ts b/src/modules/health/health.service.ts new file mode 100644 index 0000000..aca7ef4 --- /dev/null +++ b/src/modules/health/health.service.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class HealthService { + async getHealth() { + return { + status: 'ok', + timestamp: new Date().toISOString(), + service: 'raylab-core', + }; + } +} diff --git a/src/modules/identity/application/handlers/permission/create-permission.handler.ts b/src/modules/identity/application/handlers/permission/create-permission.handler.ts index ebcd193..07f1452 100644 --- a/src/modules/identity/application/handlers/permission/create-permission.handler.ts +++ b/src/modules/identity/application/handlers/permission/create-permission.handler.ts @@ -1,21 +1,29 @@ import { Injectable, ConflictException } from '@nestjs/common'; import { IPermission } from '../../../domain/repositories/permission.interface'; import { PermissionData } from '../../../domain/entities/permission.entity'; +import { EventBus } from '../../../../../core/event-bus/event-bus.service'; + +const crypto = require('crypto'); @Injectable() export class CreatePermissionHandler { - constructor(private readonly permissionRepository: IPermission) {} + constructor(private readonly permissionRepository: IPermission, private readonly events: EventBus) {} async execute(dto: any) { + if (!dto.name || !dto.code) throw new ConflictException('Missing required fields'); + const perm = PermissionData.restore({ id: crypto.randomUUID(), code: dto.code || dto.name, name: dto.name, + displayName: dto.displayName || dto.name, description: dto.description || '', createdAt: new Date(), updatedAt: new Date(), } as any); - return this.permissionRepository.create(perm); + const created = await this.permissionRepository.create(perm); + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'PermissionCreated', payload: { permissionId: created.id } }); + return created; } } diff --git a/src/modules/identity/application/handlers/permission/delete-permission.handler.ts b/src/modules/identity/application/handlers/permission/delete-permission.handler.ts index cde347e..ad9b8db 100644 --- a/src/modules/identity/application/handlers/permission/delete-permission.handler.ts +++ b/src/modules/identity/application/handlers/permission/delete-permission.handler.ts @@ -1,14 +1,34 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { IPermission } from '../../../domain/repositories/permission.interface'; +import { IRole } from '../../../domain/repositories/role.interface'; +import { AuthorizationService } from '../../../../authorization/authorization.service'; +import { EventBus } from '../../../../../core/event-bus/event-bus.service'; @Injectable() export class DeletePermissionHandler { - constructor(private readonly permissionRepository: IPermission) {} + constructor(private readonly permissionRepository: IPermission, private readonly roleRepository: IRole, private readonly authorizationService: AuthorizationService, private readonly events: EventBus) {} async execute(id: string) { const p = await this.permissionRepository.getById(id); if (!p) throw new NotFoundException('Permission not found.'); + // find roles that reference this permission + const rolesWithPerm = await (this.permissionRepository as any).findRoleIdsByPermission(id); + + // delete permission await this.permissionRepository.delete(id); + + // invalidate caches for users who have affected roles + const userSet = new Set(); + for (const rid of rolesWithPerm) { + const uids = await this.roleRepository.getAssignedUserIds(rid); + uids.forEach(u => userSet.add(u)); + } + + for (const uid of Array.from(userSet)) await this.authorizationService.invalidateUserPermissions(uid); + + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'PermissionDeleted', payload: { permissionId: id, affectedRoles: rolesWithPerm } }); + + return; } } diff --git a/src/modules/identity/application/handlers/permission/update-permission.handler.ts b/src/modules/identity/application/handlers/permission/update-permission.handler.ts index 7e642cf..f5390c6 100644 --- a/src/modules/identity/application/handlers/permission/update-permission.handler.ts +++ b/src/modules/identity/application/handlers/permission/update-permission.handler.ts @@ -1,9 +1,12 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { IPermission } from '../../../domain/repositories/permission.interface'; +import { IRole } from '../../../domain/repositories/role.interface'; +import { AuthorizationService } from '../../../../authorization/authorization.service'; +import { EventBus } from '../../../../../core/event-bus/event-bus.service'; @Injectable() export class UpdatePermissionHandler { - constructor(private readonly permissionRepository: IPermission) {} + constructor(private readonly permissionRepository: IPermission, private readonly roleRepository: IRole, private readonly authorizationService: AuthorizationService, private readonly events: EventBus) {} async execute(id: string, dto: any) { const perm = await this.permissionRepository.getById(id); @@ -13,6 +16,21 @@ export class UpdatePermissionHandler { if (dto.description !== undefined) perm.changeDescription(dto.description); if (dto.code) perm.changeCode(dto.code); - return this.permissionRepository.update(perm); + const updated = await this.permissionRepository.update(perm); + + // invalidate caches for users who belong to roles that reference this permission + const roleIds = await (this.permissionRepository as any).findRoleIdsByPermission(id); + const userSet = new Set(); + + for (const rid of roleIds) { + const uids = await this.roleRepository.getAssignedUserIds(rid); + uids.forEach(u => userSet.add(u)); + } + + for (const uid of Array.from(userSet)) await this.authorizationService.invalidateUserPermissions(uid); + + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'PermissionUpdated', payload: { permissionId: id } }); + + return updated; } } diff --git a/src/modules/identity/application/handlers/role/assign-permission.handler.ts b/src/modules/identity/application/handlers/role/assign-permission.handler.ts index 95f892f..f5f005d 100644 --- a/src/modules/identity/application/handlers/role/assign-permission.handler.ts +++ b/src/modules/identity/application/handlers/role/assign-permission.handler.ts @@ -1,12 +1,16 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable, NotFoundException, Inject } from '@nestjs/common'; import { IRole } from '../../../domain/repositories/role.interface'; import { IPermission } from '../../../domain/repositories/permission.interface'; +import { AuthorizationService } from '../../../../authorization/authorization.service'; +import { EventBus } from '../../../../../core/event-bus/event-bus.service'; @Injectable() export class RoleAssignPermissionHandler { constructor( private readonly roleRepository: IRole, private readonly permissionRepository: IPermission, + private readonly authorizationService: AuthorizationService, + private readonly events: EventBus, ) {} async execute(roleId: string, permissionId: string) { @@ -18,6 +22,17 @@ export class RoleAssignPermissionHandler { role.assignPermission(perm); - return this.roleRepository.update(role); + const updated = await this.roleRepository.update(role); + + // Invalidate permissions cache for users who have this role + const userIds = await this.roleRepository.getAssignedUserIds(roleId); + for (const uid of userIds) { + await this.authorizationService.invalidateUserPermissions(uid); + } + + // Publish role.updated event + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleUpdated', payload: { roleId, permissionId } }); + + return updated; } } diff --git a/src/modules/identity/application/handlers/role/create-role.handler.ts b/src/modules/identity/application/handlers/role/create-role.handler.ts index 6cabff4..842186f 100644 --- a/src/modules/identity/application/handlers/role/create-role.handler.ts +++ b/src/modules/identity/application/handlers/role/create-role.handler.ts @@ -1,16 +1,19 @@ import { Injectable, ConflictException } from '@nestjs/common'; import { IRole } from '../../../domain/repositories/role.interface'; import { RoleData } from '../../../domain/entities/role.entity'; +import { EventBus } from '../../../../../core/event-bus/event-bus.service'; + +const crypto = require('crypto'); @Injectable() export class CreateRoleHandler { - constructor(private readonly roleRepository: IRole) {} + constructor(private readonly roleRepository: IRole, private readonly events: EventBus) {} async execute(dto: any) { - // check uniqueness by code - // simple check + // basic validation + if (!dto.code || !dto.name) throw new ConflictException('Missing required fields'); + try { - // attempt to create; repository may enforce uniqueness const role = RoleData.restore({ id: crypto.randomUUID(), code: dto.code, @@ -22,7 +25,11 @@ export class CreateRoleHandler { updatedAt: new Date(), } as any); - return this.roleRepository.create(role); + const created = await this.roleRepository.create(role); + + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleCreated', payload: { roleId: created.id } }); + + return created; } catch (e) { throw new ConflictException('Role creation failed.'); } diff --git a/src/modules/identity/application/handlers/role/delete-role.handler.ts b/src/modules/identity/application/handlers/role/delete-role.handler.ts index fa694ca..770ba07 100644 --- a/src/modules/identity/application/handlers/role/delete-role.handler.ts +++ b/src/modules/identity/application/handlers/role/delete-role.handler.ts @@ -1,14 +1,26 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { IRole } from '../../../domain/repositories/role.interface'; +import { AuthorizationService } from '../../../../authorization/authorization.service'; +import { EventBus } from '../../../../../core/event-bus/event-bus.service'; @Injectable() export class DeleteRoleHandler { - constructor(private readonly roleRepository: IRole) {} + constructor(private readonly roleRepository: IRole, private readonly authorizationService: AuthorizationService, private readonly events: EventBus) {} async execute(id: string) { const role = await this.roleRepository.findById(id); if (!role) throw new NotFoundException('Role not found.'); + // get affected users before delete + const userIds = await this.roleRepository.getAssignedUserIds(id); + await this.roleRepository.delete(id); + + // invalidate caches + for (const uid of userIds) await this.authorizationService.invalidateUserPermissions(uid); + + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleDeleted', payload: { roleId: id } }); + + return; } } diff --git a/src/modules/identity/application/handlers/role/remove-permission.handler.ts b/src/modules/identity/application/handlers/role/remove-permission.handler.ts index 61a03c0..6793a68 100644 --- a/src/modules/identity/application/handlers/role/remove-permission.handler.ts +++ b/src/modules/identity/application/handlers/role/remove-permission.handler.ts @@ -1,9 +1,11 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { IRole } from '../../../domain/repositories/role.interface'; +import { AuthorizationService } from '../../../../authorization/authorization.service'; +import { EventBus } from '../../../../../core/event-bus/event-bus.service'; @Injectable() export class RoleRemovePermissionHandler { - constructor(private readonly roleRepository: IRole) {} + constructor(private readonly roleRepository: IRole, private readonly authorizationService: AuthorizationService, private readonly events: EventBus) {} async execute(roleId: string, permissionId: string) { const role = await this.roleRepository.findById(roleId); @@ -11,6 +13,15 @@ export class RoleRemovePermissionHandler { role.removePermission(permissionId); - return this.roleRepository.update(role); + const updated = await this.roleRepository.update(role); + + // Invalidate caches for users with the role + const userIds = await this.roleRepository.getAssignedUserIds(roleId); + for (const uid of userIds) await this.authorizationService.invalidateUserPermissions(uid); + + // Publish event + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleUpdated', payload: { roleId, removedPermissionId: permissionId } }); + + return updated; } } diff --git a/src/modules/identity/application/handlers/role/update-role.handler.ts b/src/modules/identity/application/handlers/role/update-role.handler.ts index 0b23bfc..b060cd7 100644 --- a/src/modules/identity/application/handlers/role/update-role.handler.ts +++ b/src/modules/identity/application/handlers/role/update-role.handler.ts @@ -1,18 +1,31 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common'; import { IRole } from '../../../domain/repositories/role.interface'; +import { AuthorizationService } from '../../../../authorization/authorization.service'; +import { EventBus } from '../../../../../core/event-bus/event-bus.service'; @Injectable() export class UpdateRoleHandler { - constructor(private readonly roleRepository: IRole) {} + constructor(private readonly roleRepository: IRole, private readonly authorizationService: AuthorizationService, private readonly events: EventBus) {} async execute(id: string, dto: any) { const role = await this.roleRepository.findById(id); if (!role) throw new NotFoundException('Role not found.'); + // validation + if (dto.name && dto.name.length < 2) throw new BadRequestException('Name too short'); + if (dto.name) role.changeName(dto.name); if (dto.description !== undefined) role.changeDescription(dto.description); if (dto.isDefault !== undefined) role.setDefault(!!dto.isDefault); - return this.roleRepository.update(role); + const updated = await this.roleRepository.update(role); + + // invalidate caches for users with this role + const userIds = await this.roleRepository.getAssignedUserIds(id); + for (const uid of userIds) await this.authorizationService.invalidateUserPermissions(uid); + + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleUpdated', payload: { roleId: id } }); + + return updated; } } diff --git a/src/modules/identity/application/handlers/user/update-user.handler.ts b/src/modules/identity/application/handlers/user/update-user.handler.ts new file mode 100644 index 0000000..21c8156 --- /dev/null +++ b/src/modules/identity/application/handlers/user/update-user.handler.ts @@ -0,0 +1,25 @@ +import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common'; +import { IUser } from '../../../domain/repositories/user.interface'; +import { UpdateUserDto } from '../../../presentation/dto/update-user.dto'; + +@Injectable() +export class UpdateUserHandler { + constructor(private readonly userRepository: IUser) {} + + async execute(id: string, dto: UpdateUserDto) { + // Password management is not allowed in RayLab Core + if ((dto as any).password) throw new BadRequestException('Password management is not allowed.'); + + const user = await this.userRepository.getById(id); + if (!user) throw new NotFoundException('User not found.'); + + // Only allow updating profile fields: name, email, metadata, storage settings + if (dto.name !== undefined) user.changeUsername(dto.name); + if (dto.email !== undefined) user.changeEmail(dto.email); + // metadata field not handled at domain level yet + if ((dto as any).storageQuota !== undefined) user.setStorageQuota((dto as any).storageQuota); + if ((dto as any).storageUsed !== undefined) user.setStorageUsed((dto as any).storageUsed); + + return this.userRepository.update(user); + } +} diff --git a/src/modules/identity/domain/repositories/permission.interface.ts b/src/modules/identity/domain/repositories/permission.interface.ts index a603ee2..d17f078 100644 --- a/src/modules/identity/domain/repositories/permission.interface.ts +++ b/src/modules/identity/domain/repositories/permission.interface.ts @@ -6,4 +6,7 @@ export abstract class IPermission { abstract create(permission: PermissionData): Promise; abstract update(permission: PermissionData): Promise; abstract delete(id: string): Promise; + + // Returns role ids that reference this permission + abstract findRoleIdsByPermission(permissionId: string): Promise; } diff --git a/src/modules/identity/domain/repositories/role.interface.ts b/src/modules/identity/domain/repositories/role.interface.ts index 0405e8b..9031700 100644 --- a/src/modules/identity/domain/repositories/role.interface.ts +++ b/src/modules/identity/domain/repositories/role.interface.ts @@ -9,4 +9,7 @@ export abstract class IRole { abstract create(role: RoleData): Promise; abstract update(role: RoleData): Promise; abstract delete(roleId: string): Promise; + + // Returns user ids assigned to a role (preserves layering) + abstract getAssignedUserIds(roleId: string): Promise; } diff --git a/src/modules/identity/identity.module.ts b/src/modules/identity/identity.module.ts index 91ccfe3..5aae086 100644 --- a/src/modules/identity/identity.module.ts +++ b/src/modules/identity/identity.module.ts @@ -20,6 +20,9 @@ import { IRole } from './domain/repositories/role.interface'; import { IPermission } from './domain/repositories/permission.interface'; import { IAuthConfig } from './application/config/i-auth-config'; import { EnvAuthConfig } from './application/config/env-auth-config'; +import { AuthModule } from '../auth/auth.module'; +import { AuthorizationModule } from '../authorization/authorization.module'; +import { EventBus } from '../../core/event-bus/event-bus.service'; import { GetUsersHandler } from './application/handlers/user/get-users.handler'; import { GetUserHandler } from './application/handlers/user/get-user.handler'; @@ -28,10 +31,9 @@ import { EnableUserHandler } from './application/handlers/user/enable-user.handl import { DisableUserHandler } from './application/handlers/user/disable-user.handler'; import { DeleteUserHandler } from './application/handlers/user/delete-user.handler'; import { RestoreUserHandler } from './application/handlers/user/restore-user.handler'; -import { AssignRoleHandler } from './application/handlers/user/assign-role.handler'; -import { RemoveRoleHandler } from './application/handlers/user/remove-role.handler'; -import { AssignPermissionHandler } from './application/handlers/user/assign-permission.handler'; -import { RemovePermissionHandler } from './application/handlers/user/remove-permission.handler'; +import { UpdateUserHandler } from './application/handlers/user/update-user.handler'; + + import { GetRolesHandler } from './application/handlers/role/get-roles.handler'; import { GetRoleHandler } from './application/handlers/role/get-role.handler'; @@ -48,7 +50,9 @@ import { UpdatePermissionHandler } from './application/handlers/permission/updat import { DeletePermissionHandler } from './application/handlers/permission/delete-permission.handler'; @Module({ + imports: [AuthModule, AuthorizationModule], providers: [ + //#region User // CreateUserHandler has been removed: provisioning disabled; users must be created in Authentik. SyncIdentityHandler, @@ -59,11 +63,9 @@ import { DeletePermissionHandler } from './application/handlers/permission/delet EnableUserHandler, DisableUserHandler, DeleteUserHandler, - RestoreUserHandler, - AssignRoleHandler, - RemoveRoleHandler, - AssignPermissionHandler, - RemovePermissionHandler, + RestoreUserHandler, + UpdateUserHandler, + //#endregion // role & permission handlers @@ -75,21 +77,24 @@ import { DeletePermissionHandler } from './application/handlers/permission/delet RoleAssignPermissionHandler, RoleRemovePermissionHandler, - GetPermissionsHandler, + GetPermissionsHandler, GetPermissionHandler, CreatePermissionHandler, UpdatePermissionHandler, DeletePermissionHandler, JwtAuthGuard, + CurrentUserGuard, PermissionGuard, Reflector, - PrismaService, + PrismaService, PrismaUserRepository, PrismaRoleRepository, PrismaPermissionRepository, - UserService, + UserService, + UpdateUserHandler, + { provide: IUser, useClass: PrismaUserRepository, @@ -106,8 +111,7 @@ import { DeletePermissionHandler } from './application/handlers/permission/delet provide: IAuthConfig, useClass: EnvAuthConfig, }, - ], - imports: [], + ], controllers: [UsersController, RolesController, PermissionsController], }) export class IdentityModule {} diff --git a/src/modules/identity/infrastructure/repositories/prisma-permission.repository.ts b/src/modules/identity/infrastructure/repositories/prisma-permission.repository.ts index 9459cff..a1a3f4b 100644 --- a/src/modules/identity/infrastructure/repositories/prisma-permission.repository.ts +++ b/src/modules/identity/infrastructure/repositories/prisma-permission.repository.ts @@ -50,4 +50,10 @@ export class PrismaPermissionRepository implements IPermission { async delete(id: string) { await this.prisma.permission.delete({ where: { id } }); } + + async findRoleIdsByPermission(permissionId: string): Promise { + const rows = await this.prisma.rolePermission.findMany({ where: { permissionId }, select: { roleId: true } }); + return rows.map(r => r.roleId); + } } + diff --git a/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts b/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts index e2bbbee..038fa3f 100644 --- a/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts +++ b/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts @@ -106,4 +106,10 @@ export class PrismaRoleRepository implements IRole { async delete(roleId: string) { await this.prisma.role.delete({ where: { id: roleId } }); } + + async getAssignedUserIds(roleId: string): Promise { + const rows = await this.prisma.userRole.findMany({ where: { roleId }, select: { userId: true } }); + return rows.map(r => r.userId); + } } + diff --git a/src/modules/identity/presentation/controllers/users.controller.ts b/src/modules/identity/presentation/controllers/users.controller.ts index 73e766f..8c6b1b2 100644 --- a/src/modules/identity/presentation/controllers/users.controller.ts +++ b/src/modules/identity/presentation/controllers/users.controller.ts @@ -14,10 +14,8 @@ import { EnableUserHandler } from '../../application/handlers/user/enable-user.h import { DisableUserHandler } from '../../application/handlers/user/disable-user.handler'; import { DeleteUserHandler } from '../../application/handlers/user/delete-user.handler'; import { RestoreUserHandler } from '../../application/handlers/user/restore-user.handler'; -import { AssignRoleHandler } from '../../application/handlers/user/assign-role.handler'; -import { RemoveRoleHandler } from '../../application/handlers/user/remove-role.handler'; -import { AssignPermissionHandler } from '../../application/handlers/user/assign-permission.handler'; -import { RemovePermissionHandler } from '../../application/handlers/user/remove-permission.handler'; +import { UpdateUserHandler } from '../../application/handlers/user/update-user.handler'; + @ApiTags('Users') @Controller('users') @@ -31,10 +29,7 @@ export class UsersController { private readonly disableUserHandler: DisableUserHandler, private readonly deleteUserHandler: DeleteUserHandler, private readonly restoreUserHandler: RestoreUserHandler, - private readonly assignRoleHandler: AssignRoleHandler, - private readonly removeRoleHandler: RemoveRoleHandler, - private readonly assignPermissionHandler: AssignPermissionHandler, - private readonly removePermissionHandler: RemovePermissionHandler, + private readonly updateUserHandler: UpdateUserHandler, ) {} @Get() @@ -82,6 +77,14 @@ export class UsersController { return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; } + @Patch(':id') + @Permissions(PermissionType.USER_UPDATE) + @ApiOperation({ summary: 'Update user profile' }) + async update(@Param('id') id: string, @Body() body: any) { + const updated = await this.updateUserHandler.execute(id, body); + return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; + } + @Delete(':id') @Permissions(PermissionType.USER_DELETE) @ApiOperation({ summary: 'Soft delete user' }) @@ -98,37 +101,5 @@ export class UsersController { return { success: true, data: (restored as any).toResponse ? (restored as any).toResponse() : restored, meta: {} }; } - @Post(':id/roles') - @Permissions(PermissionType.USER_UPDATE) - @ApiOperation({ summary: 'Assign role to user' }) - async assignRole(@Param('id') id: string, @Body() body: any) { - const roleId = body.roleId; - const updated = await this.assignRoleHandler.execute(id, roleId); - return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; - } - @Delete(':id/roles/:roleId') - @Permissions(PermissionType.USER_UPDATE) - @ApiOperation({ summary: 'Remove role from user' }) - async removeRole(@Param('id') id: string, @Param('roleId') roleId: string) { - const updated = await this.removeRoleHandler.execute(id, roleId); - return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; - } - - @Post(':id/permissions') - @Permissions(PermissionType.USER_UPDATE) - @ApiOperation({ summary: 'Assign permission to user' }) - async assignPermission(@Param('id') id: string, @Body() body: any) { - const permissionId = body.permissionId; - const updated = await this.assignPermissionHandler.execute(id, permissionId); - return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; - } - - @Delete(':id/permissions/:permissionId') - @Permissions(PermissionType.USER_UPDATE) - @ApiOperation({ summary: 'Remove permission from user' }) - async removePermission(@Param('id') id: string, @Param('permissionId') permissionId: string) { - const updated = await this.removePermissionHandler.execute(id, permissionId); - return { success: true, data: (updated as any).toResponse ? (updated as any).toResponse() : updated, meta: {} }; - } } diff --git a/src/modules/identity/presentation/guards/current-user.guard.ts b/src/modules/identity/presentation/guards/current-user.guard.ts index f26fcd6..2b1743a 100644 --- a/src/modules/identity/presentation/guards/current-user.guard.ts +++ b/src/modules/identity/presentation/guards/current-user.guard.ts @@ -11,6 +11,14 @@ export class CurrentUserGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest() as any; + // Prefer RequestContext produced by AuthenticationService + const ctx = request.raylabContext as any; + if (ctx && ctx.user) { + request.currentUser = ctx.user; + return true; + } + + // Fallback to legacy identity if present const identity = request.identity as IdentityData | undefined; if (!identity) { diff --git a/src/shared/cache-keys.ts b/src/shared/cache-keys.ts new file mode 100644 index 0000000..714d63a --- /dev/null +++ b/src/shared/cache-keys.ts @@ -0,0 +1,3 @@ +export const CacheKeys = { + permission: (userId: string) => `permissions:${userId}`, +}; diff --git a/src/shared/redis.service.ts b/src/shared/redis.service.ts new file mode 100644 index 0000000..ef5a3d5 --- /dev/null +++ b/src/shared/redis.service.ts @@ -0,0 +1,109 @@ +import { Injectable, OnModuleDestroy, OnModuleInit, Logger } from '@nestjs/common'; +import IORedis from 'ioredis'; + +@Injectable() +export class RedisService implements OnModuleInit, OnModuleDestroy { + // Keep client typed as any to avoid tight coupling to ioredis types in tests + private client: any = null; + private readonly logger = new Logger(RedisService.name); + private lastLogAt = 0; + private readonly LOG_THROTTLE_MS = 5000; // throttle repeated error logs + + onModuleInit() { + const enabled = (process.env.REDIS_ENABLED || 'true').toLowerCase() === 'true'; + if (!enabled) { + this.logger.log('Redis disabled via REDIS_ENABLED=false'); + return; + } + + const url = process.env.REDIS_URL || 'redis://localhost:6379'; + + // Use lazyConnect so application can start even if Redis is unavailable temporarily + this.client = new IORedis(url, { + lazyConnect: true, + // limit retries to avoid infinite reconnect storms + maxRetriesPerRequest: 5, + // automatic reconnection strategy + reconnectOnError: (err: any) => { + return true; + }, + enableOfflineQueue: true, + // optional reconnect strategy + retryStrategy: (times: number) => { + // exponential backoff capped at 5s + const delay = Math.min(50 * Math.pow(2, times), 5000); + return delay; + }, + }); + + this.client.on('connect', () => this.logger.log('Connected to Redis')); + this.client.on('ready', () => this.logger.log('Redis ready')); + this.client.on('error', (err: any) => this.handleError(err)); + this.client.on('close', () => this.logger.warn('Redis connection closed')); + this.client.on('reconnecting', () => this.logger.log('Redis reconnecting')); + + // attempt to connect but do not throw if it fails + this.client.connect().catch((err: any) => { + this.handleError(err); + }); + } + + private handleError(err: any) { + const now = Date.now(); + if (now - this.lastLogAt > this.LOG_THROTTLE_MS) { + this.logger.error('Redis error', err instanceof Error ? err.message : err); + this.lastLogAt = now; + } + } + + onModuleDestroy() { + if (this.client) { + try { + this.client.disconnect(); + } catch (e) { + // ignore + } + } + } + + getClient(): any { + return this.client; + } + + private ensureClient() { + // returns true if client is connected/usable + return this.client && this.client.status && this.client.status !== 'end' && this.client.status !== 'close'; + } + + async get(key: string): Promise { + if (!this.ensureClient()) return null; + try { + return await this.client.get(key); + } catch (e) { + this.handleError(e); + return null; + } + } + + async set(key: string, value: string, ttlSeconds?: number) { + if (!this.ensureClient()) return; + try { + if (ttlSeconds) { + await this.client.set(key, value, 'EX', ttlSeconds); + } else { + await this.client.set(key, value); + } + } catch (e) { + this.handleError(e); + } + } + + async del(key: string) { + if (!this.ensureClient()) return; + try { + await this.client.del(key); + } catch (e) { + this.handleError(e); + } + } +} diff --git a/src/shared/types/request-context.ts b/src/shared/types/request-context.ts new file mode 100644 index 0000000..8c15493 --- /dev/null +++ b/src/shared/types/request-context.ts @@ -0,0 +1,6 @@ +export interface RequestContext { + user: any; + identity: Record; + roles: string[]; // role ids + permissions: string[]; // permission codes +} diff --git a/tests/authentication.service.spec.ts b/tests/authentication.service.spec.ts new file mode 100644 index 0000000..a402bb3 --- /dev/null +++ b/tests/authentication.service.spec.ts @@ -0,0 +1,38 @@ +import { AuthenticationService } from '../src/modules/auth/authentication.service'; + +describe('AuthenticationService', () => { + let service: AuthenticationService; + const mockOidc: any = { verifyToken: jest.fn() }; + const mockPrisma: any = {}; + const mockRoleSync: any = { syncUserRolesFromAuthentik: jest.fn() }; + const mockAuthz: any = { getUserPermissions: jest.fn() }; + const mockEvents: any = { publish: jest.fn() }; + + beforeEach(() => { + mockPrisma.user = { findUnique: jest.fn(), create: jest.fn() }; + mockPrisma.userRole = { findMany: jest.fn() }; + + service = new AuthenticationService(mockOidc as any, mockPrisma as any, mockRoleSync as any, mockAuthz as any, mockEvents as any); + }); + + test('authenticate creates context on valid token', async () => { + const token = 'valid'; + mockOidc.verifyToken.mockResolvedValue({ sub: 'sub1', email: 'u@example.com', groups: ['RL-Owner'] }); + mockPrisma.user.findUnique.mockResolvedValue(null); + mockPrisma.user.create.mockResolvedValue({ id: 'uid', authentikId: 'sub1', email: 'u@example.com' }); + mockRoleSync.syncUserRolesFromAuthentik.mockResolvedValue({ skipped: false, assignedRoleIds: ['r1'] }); + mockAuthz.getUserPermissions.mockResolvedValue(['users.read']); + mockPrisma.userRole.findMany.mockResolvedValue([{ roleId: 'r1' }]); + + const ctx = await service.authenticate(token); + + expect(ctx.user.id).toBe('uid'); + expect(ctx.permissions).toContain('users.read'); + expect(mockEvents.publish).toHaveBeenCalledWith('user.authenticated', expect.any(Object)); + }); + + test('authenticate throws for missing sub', async () => { + mockOidc.verifyToken.mockResolvedValue({ email: 'u@example.com' }); + await expect(service.authenticate('bad')).rejects.toThrow('Invalid token: missing sub'); + }); +}); diff --git a/tests/authorization.service.spec.ts b/tests/authorization.service.spec.ts new file mode 100644 index 0000000..a6d9736 --- /dev/null +++ b/tests/authorization.service.spec.ts @@ -0,0 +1,34 @@ +import { AuthorizationService, PERMISSION_CACHE } from '../src/modules/authorization/authorization.service'; + +describe('AuthorizationService', () => { + let service: AuthorizationService; + const mockPrisma: any = { $queryRaw: jest.fn(), $queryRawUnsafe: jest.fn() }; + const mockCache: any = { get: jest.fn(), set: jest.fn(), invalidate: jest.fn() }; + + beforeEach(() => { + service = new AuthorizationService(mockPrisma as any, mockCache as any); + }); + + test('getUserPermissions uses cache when available', async () => { + mockCache.get.mockResolvedValue(['users.read']); + const perms = await service.getUserPermissions('uid'); + expect(perms).toEqual(['users.read']); + expect(mockCache.get).toHaveBeenCalledWith('uid'); + }); + + test('getUserPermissions queries DB and sets cache when missing', async () => { + mockCache.get.mockResolvedValue(null); + mockPrisma.$queryRaw = jest.fn().mockResolvedValue([{ code: 'users.read' }]); + const perms = await service.getUserPermissions('uid'); + expect(perms).toEqual(['users.read']); + expect(mockCache.set).toHaveBeenCalledWith('uid', ['users.read']); + }); + + test('hasPermission returns correct boolean', async () => { + jest.spyOn(service, 'getUserPermissions' as any).mockResolvedValue(['users.read']); + const ok = await service.hasPermission('uid', 'users.read'); + expect(ok).toBe(true); + const nok = await service.hasPermission('uid', 'users.delete'); + expect(nok).toBe(false); + }); +}); diff --git a/tests/role-sync.rollback.spec.ts b/tests/role-sync.rollback.spec.ts new file mode 100644 index 0000000..e829b42 --- /dev/null +++ b/tests/role-sync.rollback.spec.ts @@ -0,0 +1,30 @@ +import { RoleSyncService } from '../src/modules/auth/role-sync.service'; + +describe('RoleSyncService - transaction rollback', () => { + let service: RoleSyncService; + const mockPrisma: any = {}; + const mockPermissionCache: any = { invalidate: jest.fn() }; + const mockEvents: any = { publish: jest.fn() }; + const mockGroupHash: any = { compute: (g: any) => require('crypto').createHash('sha256').update((g||[]).slice().sort().join(','), 'utf8').digest('hex') }; + + beforeEach(() => { + mockPrisma.user = { findUnique: jest.fn() }; + mockPrisma.authGroupRoleMapping = { findMany: jest.fn() }; + mockPrisma.userRole = { findMany: jest.fn().mockResolvedValue([]) }; + mockPrisma.user = mockPrisma.user; + // simulate transaction throwing + mockPrisma.$transaction = jest.fn(async (cb: any) => { throw new Error('tx failed'); }); + + service = new RoleSyncService(mockPrisma as any, mockGroupHash as any, mockPermissionCache as any, mockEvents as any); + }); + + test('does not invalidate cache or publish event when transaction fails', async () => { + mockPrisma.user.findUnique.mockResolvedValue({ id: 'uid', lastGroupHash: 'old' }); + mockPrisma.authGroupRoleMapping.findMany.mockResolvedValue([{ roleId: 'r1' }]); + + await expect(service.syncUserRolesFromAuthentik('uid', ['RL-Owner'])).rejects.toThrow('tx failed'); + + expect(mockPermissionCache.invalidate).not.toHaveBeenCalled(); + expect(mockEvents.publish).not.toHaveBeenCalled(); + }); +}); diff --git a/tests/role-sync.service.spec.ts b/tests/role-sync.service.spec.ts new file mode 100644 index 0000000..d08ab89 --- /dev/null +++ b/tests/role-sync.service.spec.ts @@ -0,0 +1,65 @@ +import { RoleSyncService } from '../src/modules/auth/role-sync.service'; + +describe('RoleSyncService', () => { + const mockPrisma: any = {}; + const mockPermissionCache: any = { invalidate: jest.fn() }; + const mockEvents: any = { publish: jest.fn() }; + const mockGroupHash: any = { compute: (g: any) => require('crypto').createHash('sha256').update((g||[]).slice().sort().join(','), 'utf8').digest('hex') }; + let service: RoleSyncService; + + beforeEach(() => { + mockPrisma.user = { findUnique: jest.fn() }; + mockPrisma.authGroupRoleMapping = { findMany: jest.fn() }; + mockPrisma.userRole = { findMany: jest.fn() }; + mockPrisma.auditLog = { create: jest.fn() }; + mockPrisma.user = mockPrisma.user; + mockPrisma.$transaction = jest.fn(async (cb: any) => { + // simulate transaction by calling provided callback with tx = mockPrisma + await cb(mockPrisma); + }); + + service = new RoleSyncService(mockPrisma as any, mockGroupHash as any, mockPermissionCache as any, mockEvents as any); + }); + + test('computeGroupHash consistent and order independent', () => { + const a = ['b', 'a', 'c']; + const h1 = service.computeGroupHash(a); + const h2 = service.computeGroupHash(['a', 'b', 'c']); + expect(h1).toBe(h2); + }); + + test('skips sync when group hash unchanged', async () => { + const groups = ['RL-Owner']; + const hash = service.computeGroupHash(groups); + mockPrisma.user.findUnique.mockResolvedValue({ id: 'uid', lastGroupHash: hash }); + + const res = await service.syncUserRolesFromAuthentik('uid', groups); + expect(res.skipped).toBe(true); + expect(mockPrisma.$transaction).not.toHaveBeenCalled(); + }); + + test('performs transaction and writes audit when changed', async () => { + const groups = ['RL-Owner']; + mockPrisma.user.findUnique.mockResolvedValue({ id: 'uid', lastGroupHash: 'old' }); + mockPrisma.authGroupRoleMapping.findMany.mockResolvedValue([{ roleId: 'r1' }]); + mockPrisma.userRole.findMany.mockResolvedValue([{ roleId: 'r_old' }]); + + // spy on tx ops + mockPrisma.userRole.deleteMany = jest.fn(); + mockPrisma.userRole.createMany = jest.fn(); + mockPrisma.user.update = jest.fn(); + + mockPrisma.auditLog.create = jest.fn(); + + const res = await service.syncUserRolesFromAuthentik('uid', groups); + + expect(mockPrisma.$transaction).toHaveBeenCalled(); + expect(mockPrisma.userRole.deleteMany).toHaveBeenCalledWith({ where: { userId: 'uid', source: 'AUTHENTIK' } }); + expect(mockPrisma.userRole.createMany).toHaveBeenCalled(); + expect(mockPrisma.user.update).toHaveBeenCalled(); + expect(mockPermissionCache.invalidate).toHaveBeenCalledWith('uid'); + expect(mockEvents.publish).toHaveBeenCalledWith(expect.objectContaining({ type: 'RolesSynchronized' })); + expect(res.skipped).toBe(false); + expect(res.assignedRoleIds).toEqual(['r1']); + }); +}); diff --git a/tests/role.handlers.spec.ts b/tests/role.handlers.spec.ts new file mode 100644 index 0000000..2e0a0e8 --- /dev/null +++ b/tests/role.handlers.spec.ts @@ -0,0 +1,41 @@ +import { RoleAssignPermissionHandler } from '../src/modules/identity/application/handlers/role/assign-permission.handler'; +import { RoleRemovePermissionHandler } from '../src/modules/identity/application/handlers/role/remove-permission.handler'; + +describe('Role permission handlers', () => { + test('assign permission invalidates cache and publishes event', async () => { + const mockRoleRepo: any = { findById: jest.fn(), update: jest.fn(), getAssignedUserIds: jest.fn().mockResolvedValue(['u1','u2']) }; + const mockPermRepo: any = { getById: jest.fn() }; + const mockAuthz: any = { invalidateUserPermissions: jest.fn() }; + const mockEvents: any = { publish: jest.fn() }; + + mockRoleRepo.findById.mockResolvedValue({ id: 'r1', assignPermission: jest.fn() }); + mockPermRepo.getById.mockResolvedValue({ id: 'p1' }); + mockRoleRepo.update.mockResolvedValue({ id: 'r1' }); + + const handler = new RoleAssignPermissionHandler(mockRoleRepo, mockPermRepo, mockAuthz as any, mockEvents as any); + + const res = await handler.execute('r1', 'p1'); + + expect(mockRoleRepo.update).toHaveBeenCalled(); + expect(mockAuthz.invalidateUserPermissions).toHaveBeenCalledWith('u1'); + expect(mockAuthz.invalidateUserPermissions).toHaveBeenCalledWith('u2'); + expect(mockEvents.publish).toHaveBeenCalledWith(expect.objectContaining({ type: 'RoleUpdated' })); + }); + + test('remove permission invalidates cache and publishes event', async () => { + const mockRoleRepo: any = { findById: jest.fn(), update: jest.fn(), getAssignedUserIds: jest.fn().mockResolvedValue(['u1']) }; + const mockAuthz: any = { invalidateUserPermissions: jest.fn() }; + const mockEvents: any = { publish: jest.fn() }; + + mockRoleRepo.findById.mockResolvedValue({ id: 'r1', removePermission: jest.fn() }); + mockRoleRepo.update.mockResolvedValue({ id: 'r1' }); + + const handler = new RoleRemovePermissionHandler(mockRoleRepo as any, mockAuthz as any, mockEvents as any); + + const res = await handler.execute('r1', 'p1'); + + expect(mockRoleRepo.update).toHaveBeenCalled(); + expect(mockAuthz.invalidateUserPermissions).toHaveBeenCalledWith('u1'); + expect(mockEvents.publish).toHaveBeenCalledWith(expect.objectContaining({ type: 'RoleUpdated' })); + }); +}); diff --git a/tests/update-user.handler.spec.ts b/tests/update-user.handler.spec.ts new file mode 100644 index 0000000..c1dfb39 --- /dev/null +++ b/tests/update-user.handler.spec.ts @@ -0,0 +1,29 @@ +import { UpdateUserHandler } from '../src/modules/identity/application/handlers/user/update-user.handler'; + +describe('UpdateUserHandler', () => { + test('rejects password in DTO', async () => { + const mockRepo: any = { getById: jest.fn(), update: jest.fn() }; + mockRepo.getById.mockResolvedValue({ id: 'u1', changeUsername: jest.fn(), changeEmail: jest.fn(), setStorageQuota: jest.fn(), setStorageUsed: jest.fn() }); + + const handler = new UpdateUserHandler(mockRepo as any); + + await expect(handler.execute('u1', { password: 'secret' } as any)).rejects.toThrow('Password management is not allowed.'); + }); + + test('updates allowed fields', async () => { + const userObj: any = { id: 'u1', changeUsername: jest.fn(), changeEmail: jest.fn(), setStorageQuota: jest.fn(), setStorageUsed: jest.fn() }; + const mockRepo: any = { getById: jest.fn().mockResolvedValue(userObj), update: jest.fn().mockResolvedValue(userObj) }; + + const handler = new UpdateUserHandler(mockRepo as any); + + const dto = { name: 'New Name', email: 'a@b.com', storageQuota: 1000, storageUsed: 10 } as any; + + const res = await handler.execute('u1', dto); + + expect(userObj.changeUsername).toHaveBeenCalledWith('New Name'); + expect(userObj.changeEmail).toHaveBeenCalledWith('a@b.com'); + expect(userObj.setStorageQuota).toHaveBeenCalledWith(1000); + expect(userObj.setStorageUsed).toHaveBeenCalledWith(10); + expect(mockRepo.update).toHaveBeenCalledWith(userObj); + }); +}); From 680100c52975ce26b0d335d00bed7cba98260566 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Sun, 2 Aug 2026 17:51:36 +0700 Subject: [PATCH 03/34] Update schema.prisma --- prisma/schema.prisma | 165 +++++++++++++++++++++---------------------- 1 file changed, 79 insertions(+), 86 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ec3e0e6..0672dc1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -11,27 +11,28 @@ datasource db { } model User { - id String @id @default(uuid()) @db.Uuid - authentikId String? @unique - authentikUserId String? @unique - authentikSubject String? @unique - username String? @db.VarChar(255) @unique - email String? @db.VarChar(320) @unique - name String? @db.VarChar(255) - picture String? - isActive Boolean @default(true) - deletedAt DateTime? - lastSeenAt DateTime? - lastSyncedAt DateTime? - syncStatus String? - storageQuota BigInt? @default(10737418240) - storageUsed BigInt? @default(0) - metadata Json? - userRoles UserRole[] - auditLogs AuditLog[] - lastGroupHash String? @db.Text - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(uuid()) @db.Uuid + authentikId String? @unique + authentikUserId String? @unique + authentikSubject String? @unique + username String? @unique @db.VarChar(255) + email String? @unique @db.VarChar(320) + name String? @db.VarChar(255) + picture String? + isActive Boolean @default(true) + deletedAt DateTime? + lastSeenAt DateTime? + lastSyncedAt DateTime? + syncStatus String? + storageQuota BigInt? @default(10737418240) + storageUsed BigInt? @default(0) + metadata Json? + userRoles UserRole[] + auditLogs AuditLog[] + mediaObjects MediaObject[] + lastGroupHash String? @db.Text + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt @@index([authentikId]) @@index([username]) @@ -39,37 +40,37 @@ model User { } model Role { - id String @id @default(uuid()) @db.Uuid - code String @unique @db.VarChar(100) - name String @db.VarChar(255) - displayName String? @db.VarChar(255) - description String? - isDefault Boolean @default(false) + id String @id @default(uuid()) @db.Uuid + code String @unique @db.VarChar(100) + name String @db.VarChar(255) + displayName String? @db.VarChar(255) + description String? + isDefault Boolean @default(false) rolePermissions RolePermission[] - userRoles UserRole[] - mappings AuthGroupRoleMapping[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + userRoles UserRole[] + mappings AuthGroupRoleMapping[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model Permission { - id String @id @default(uuid()) @db.Uuid - code String @unique @db.VarChar(150) - name String @db.VarChar(255) - displayName String? @db.VarChar(255) - description String? + id String @id @default(uuid()) @db.Uuid + code String @unique @db.VarChar(150) + name String @db.VarChar(255) + displayName String? @db.VarChar(255) + description String? rolePermissions RolePermission[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model RolePermission { - id String @id @default(uuid()) @db.Uuid - role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) - roleId String @db.Uuid + id String @id @default(uuid()) @db.Uuid + role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) + roleId String @db.Uuid permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) - permissionId String @db.Uuid - createdAt DateTime @default(now()) + permissionId String @db.Uuid + createdAt DateTime @default(now()) @@unique([roleId, permissionId]) @@index([roleId]) @@ -82,14 +83,14 @@ enum UserRoleSource { } model UserRole { - id String @id @default(uuid()) @db.Uuid - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - userId String @db.Uuid - role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) - roleId String @db.Uuid - source UserRoleSource @default(AUTHENTIK) - syncedAt DateTime @default(now()) - createdAt DateTime @default(now()) + id String @id @default(uuid()) @db.Uuid + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String @db.Uuid + role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) + roleId String @db.Uuid + source UserRoleSource @default(AUTHENTIK) + syncedAt DateTime @default(now()) + createdAt DateTime @default(now()) @@unique([userId, roleId]) @@index([userId]) @@ -97,12 +98,12 @@ model UserRole { } model AuthGroupRoleMapping { - id String @id @default(uuid()) @db.Uuid - authGroup String @db.VarChar(255) - role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) - roleId String @db.Uuid - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(uuid()) @db.Uuid + authGroup String @db.VarChar(255) + role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) + roleId String @db.Uuid + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt @@index([authGroup]) } @@ -122,40 +123,38 @@ model AuditLog { } model ScheduledJob { - id String @id @default(uuid()) @db.Uuid - name String @db.VarChar(255) - payload Json? - cron String? - runAt DateTime? - status String @db.VarChar(50) @default("pending") - attempts Int @default(0) - lastRunAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(uuid()) @db.Uuid + name String @db.VarChar(255) + payload Json? + cron String? + runAt DateTime? + status String @default("pending") @db.VarChar(50) + attempts Int @default(0) + lastRunAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt @@index([name]) @@index([status]) } model MediaObject { - id String @id @default(uuid()) @db.Uuid - ownerUserId String? @db.Uuid - owner User? @relation(fields: [ownerUserId], references: [id], onDelete: SetNull) - storageKey String @db.VarChar(1024) - filename String? @db.VarChar(1024) - mimeType String? @db.VarChar(255) - size BigInt? - isPublic Boolean @default(false) - metadata Json? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(uuid()) @db.Uuid + ownerUserId String? @db.Uuid + owner User? @relation(fields: [ownerUserId], references: [id], onDelete: SetNull) + storageKey String @db.VarChar(1024) + filename String? @db.VarChar(1024) + mimeType String? @db.VarChar(255) + size BigInt? + isPublic Boolean @default(false) + metadata Json? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt @@index([ownerUserId]) @@index([storageKey]) } - - model Application { id String @id @default(uuid()) @db.Uuid code String @unique @db.VarChar(100) @@ -172,9 +171,3 @@ model Application { @@index([code]) @@index([applicationsClaim]) } - - - - - - From 306c145f9101ab4e63bd33192ef876049fb29d35 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Sun, 2 Aug 2026 17:57:29 +0700 Subject: [PATCH 04/34] Update .env.example --- .env.example | 114 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 39 deletions(-) diff --git a/.env.example b/.env.example index f74dde0..8b10dbd 100644 --- a/.env.example +++ b/.env.example @@ -1,56 +1,92 @@ -# Application -BASE_URL=http://localhost:3069 PORT=3000 NODE_ENV=development +# RayLab Core - contoh konfigurasi environment +# Salin file ini menjadi .env dan isi nilai-nilai sensitif sesuai environment Anda. +# Jangan commit .env yang berisi secrets ke VCS. -# Database (PostgreSQL) -DATABASE_URL=postgresql://user:password@localhost:5432/Database?shema=public +######################## +# Database (Postgres) +######################## +# URL koneksi Postgres untuk Prisma. +# Contoh: postgresql://user:password@localhost:5432/raylab +DATABASE_URL="postgresql://raylab:password@localhost:5432/raylab" -# Authentik (OIDC discovery base) -# Used by OIDC discovery and token exchange (AuthService) -AUTHENTIK_URL=https://auth.raylab.site +# Catatan penting: migration menggunakan fungsi UUID di migration SQL. +# Pastikan database memiliki ekstensi yang sesuai: +# - Jika migration menggunakan gen_random_uuid(): pasang pgcrypto +# SQL: CREATE EXTENSION IF NOT EXISTS pgcrypto; +# - Jika ingin gunakan uuid_generate_v4(): pasang uuid-ossp +# SQL: CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"; -# Authentik Admin API (provisioning identities & groups) -# Example: https://auth.raylab.site/api -AUTHENTIK_API_BASE=https://auth.raylab.site/api +######################## +# Internal JWT (RayLab) +######################## +# Secret untuk menandatangani JWT internal (hingga JwtModule digunakan). +# Ganti dengan secret yang kuat di produksi. +RAYLAB_JWT_SECRET="replace-with-a-strong-secret" -# Bearer token for Authentik Admin API (admin service account) -AUTHENTIK_API_TOKEN=changeme_admin_api_token +######################## +# Authentik / OIDC (Identity Provider) +######################## +# Issuer base URL dari Authentik / OIDC provider (wajib untuk verifikasi token) +# Contoh: https://auth.example.com +AUTHENTIK_ISSUER="https://auth.example.com" -# Default group name format: RL-{object} -# RayLab will use this group to gate access (default RL-RayLab-Users) -AUTHENTIK_DEFAULT_GROUP=RL-RayLab-Users +# Audience (aud) yang diharapkan pada token OIDC (opsional) +AUTHENTIK_AUDIENCE="raylab" -# OIDC / JWKS / Issuer settings for verifying Authentik tokens (if needed) -AUTHENTIK_JWKS_URI=https://auth.raylab.site/.well-known/jwks.json AUTHENTIK_ISSUER=https://auth.raylab.site/application/o/ray-lab-core/ AUTHENTIK_AUDIENCE=ray-lab-core +# JWKS URI jika ingin override. Jika kosong, akan dibentuk dari AUTHENTIK_ISSUER + "/.well-known/jwks.json" +AUTHENTIK_JWKS_URI="" -# OIDC client credentials (used when RayLab needs to call token endpoint) -AUTHENTIK_CLIENT_ID=raylab-client AUTHENTIK_CLIENT_SECRET=raylab-client-secret +######################## +# Redis (opsional, beberapa fitur) +######################## +# Aktifkan/Nonaktifkan Redis (default true). +REDIS_ENABLED="true" -# RayLab internal JWT (used to sign internal tokens) -RAYLAB_JWT_SECRET=replace_with_a_long_random_secret +# URL Redis (contoh: redis://localhost:6379). Hanya dipakai bila REDIS_ENABLED=true +REDIS_URL="redis://localhost:6379" -# Token lifetime in seconds -RAYLAB_JWT_EXPIRES_IN=3600 +######################## +# Server / Aplikasi +######################## +# Base URL aplikasi (dipakai pada integration tests / helper) +BASE_URL="http://localhost:3000" -# Sync / reconciliation -# Interval in seconds for reconciler to retry PENDING/FAILED syncs -AUTH_RECONCILE_INTERVAL_SECONDS=3600 +# Port aplikasi +PORT="3000" -# Auth / sync behavior -# Disable auto-creation of users on login (must remain false per design) -AUTH_AUTO_CREATE_USER=false AUTH_SYNC_EMAIL=true AUTH_SYNC_USERNAME=false +# Node environment +NODE_ENV="development" -# Test accounts for integration tests (must exist in Authentik and RayLab DB before tests, -# or be provisioned via RayLab Admin endpoints during test run) -ADMIN_USERNAME=admin@example.com ADMIN_PASSWORD=changeme_admin_password +# Log level (debug/info/warn/error) +LOG_LEVEL="debug" -OWNER_USERNAME=owner@example.com OWNER_PASSWORD=changeme_owner_password +######################## +# Admin / Test accounts (opsional, dipakai tests/integration jika diperlukan) +######################## +# Akun admin untuk keperluan pengujian/integrasi (hanya contoh) +ADMIN_USERNAME="admin" +ADMIN_PASSWORD="changeme" -EMPLOYEE_USERNAME=employee@example.com EMPLOYEE_PASSWORD=changeme_employee_password +# Informasi akun uji (opsional) +TEST_USER_EMAIL="test-integration@example.com" +TEST_USER_USERNAME="test-integration" +TEST_USER_PASSWORD="StrongP@ssw0rd!" -# Optional: integration test user (overrides defaults used by tests) -TEST_USER_EMAIL=test-integration@example.com TEST_USER_USERNAME=test-integration TEST_USER_PASSWORD=StrongP@ssw0rd! +# Expire time for test tokens (detik) +TEST_TOKEN_EXPIRES_IN="3600" -# Optional: adjust logging or other runtime flags -LOG_LEVEL=info +######################## +# Optional / Integration hints +######################## +# Jika Anda menggunakan penyedia lain atau menambahkan variabel tambahan, +# tambahkan di sini. Contoh: +# SSO_CALLBACK_URL="http://localhost:3000/auth/callback" +# MAILER_* variables, STORAGE_*, dsb. -SWAGGER_ENABLED=true \ No newline at end of file +######################## +# Security reminders +######################## +# - Jangan commit file .env dengan secrets. +# - Gunakan secret manager di production (Vault, AWS Secrets Manager, ..). +# - Pastikan DATABASE_URL menunjuk ke database yang benar dan aman. +# - Pastikan AUTHENTIK_ISSUER dan JWKS dapat diakses dari RayLab instance. \ No newline at end of file From eb6031282a3ee50df2a2aa8403448a3e35941d33 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Sun, 2 Aug 2026 20:01:29 +0700 Subject: [PATCH 05/34] fix npm run build --- dist/app.module.js | 37 +++ dist/app.module.js.map | 1 + dist/common/constants/permission.constants.js | 18 ++ .../constants/permission.constants.js.map | 1 + dist/core/auth/guards/jwt-auth.guard.js | 106 +++++++++ dist/core/auth/guards/jwt-auth.guard.js.map | 1 + .../authenticated-request.interface.js | 3 + .../authenticated-request.interface.js.map | 1 + dist/core/auth/interfaces/identity-data.js | 17 ++ .../core/auth/interfaces/identity-data.js.map | 1 + dist/core/config/database.config.js | 13 ++ dist/core/config/database.config.js.map | 1 + dist/core/event-bus/event-bus.service.js | 36 +++ dist/core/event-bus/event-bus.service.js.map | 1 + dist/core/event-bus/event.interface.js | 3 + dist/core/event-bus/event.interface.js.map | 1 + dist/core/events/event-dispatcher.js | 14 ++ dist/core/events/event-dispatcher.js.map | 1 + dist/core/events/event-handler.js | 3 + dist/core/events/event-handler.js.map | 1 + dist/core/events/event-publisher.js | 14 ++ dist/core/events/event-publisher.js.map | 1 + dist/core/events/event.interface.js | 3 + dist/core/events/event.interface.js.map | 1 + dist/core/exceptions/http-exception.filter.js | 29 +++ .../exceptions/http-exception.filter.js.map | 1 + dist/core/logger/logger.service.js | 11 + dist/core/logger/logger.service.js.map | 1 + dist/core/middleware/request-id.middleware.js | 23 ++ .../middleware/request-id.middleware.js.map | 1 + dist/main.js | 34 +++ dist/main.js.map | 1 + .../modules/application/application.module.js | 50 ++++ .../application/application.module.js.map | 1 + .../handlers/create-application.handler.js | 44 ++++ .../create-application.handler.js.map | 1 + .../handlers/delete-application.handler.js | 31 +++ .../delete-application.handler.js.map | 1 + .../handlers/get-application.handler.js | 30 +++ .../handlers/get-application.handler.js.map | 1 + .../handlers/get-applications.handler.js | 30 +++ .../handlers/get-applications.handler.js.map | 1 + .../handlers/get-me-applications.handler.js | 35 +++ .../get-me-applications.handler.js.map | 1 + .../handlers/update-application.handler.js | 38 +++ .../update-application.handler.js.map | 1 + .../validators/application.validator.js | 84 +++++++ .../validators/application.validator.js.map | 1 + .../domain/entities/application.entity.js | 67 ++++++ .../domain/entities/application.entity.js.map | 1 + .../repositories/application.interface.js | 7 + .../repositories/application.interface.js.map | 1 + .../mappers/prisma-application.mapper.js | 23 ++ .../mappers/prisma-application.mapper.js.map | 1 + .../prisma-application.repository.js | 98 ++++++++ .../prisma-application.repository.js.map | 1 + .../controllers/applications.controller.js | 136 +++++++++++ .../applications.controller.js.map | 1 + dist/modules/audit/audit.event-handler.js | 65 ++++++ dist/modules/audit/audit.event-handler.js.map | 1 + dist/modules/audit/audit.module.js | 24 ++ dist/modules/audit/audit.module.js.map | 1 + dist/modules/audit/audit.service.js | 46 ++++ dist/modules/audit/audit.service.js.map | 1 + dist/modules/auth/auth.controller.js | 110 +++++++++ dist/modules/auth/auth.controller.js.map | 1 + dist/modules/auth/auth.module.js | 75 ++++++ dist/modules/auth/auth.module.js.map | 1 + dist/modules/auth/auth.service.js | 209 +++++++++++++++++ dist/modules/auth/auth.service.js.map | 1 + dist/modules/auth/authentication.service.js | 70 ++++++ .../auth/authentication.service.js.map | 1 + dist/modules/auth/dto/login.dto.js | 4 + dist/modules/auth/dto/login.dto.js.map | 1 + dist/modules/auth/group-hash.service.js | 56 +++++ dist/modules/auth/group-hash.service.js.map | 1 + dist/modules/auth/guards/oidc.guard.js | 48 ++++ dist/modules/auth/guards/oidc.guard.js.map | 1 + dist/modules/auth/oidc.service.js | 89 ++++++++ dist/modules/auth/oidc.service.js.map | 1 + dist/modules/auth/pkce/redis-pkce.store.js | 56 +++++ .../modules/auth/pkce/redis-pkce.store.js.map | 1 + .../auth/refresh/inmemory-refresh.store.js | 53 +++++ .../refresh/inmemory-refresh.store.js.map | 1 + dist/modules/auth/role-sync.service.js | 88 +++++++ dist/modules/auth/role-sync.service.js.map | 1 + .../application/authorization.service.js | 2 + .../application/authorization.service.js.map | 1 + .../authorization/authorization.module.js | 29 +++ .../authorization/authorization.module.js.map | 1 + .../authorization/authorization.service.js | 71 ++++++ .../authorization.service.js.map | 1 + .../cache/permission-cache.interface.js | 3 + .../cache/permission-cache.interface.js.map | 1 + .../cache/redis-permission-cache.service.js | 48 ++++ .../redis-permission-cache.service.js.map | 1 + .../authorization/guards/permission.guard.js | 52 +++++ .../guards/permission.guard.js.map | 1 + .../decorators/permission.decorator.js | 7 + .../decorators/permission.decorator.js.map | 1 + .../presentation/guards/permission.guard.js | 46 ++++ .../guards/permission.guard.js.map | 1 + dist/modules/health/health.controller.js | 35 +++ dist/modules/health/health.controller.js.map | 1 + dist/modules/health/health.module.js | 23 ++ dist/modules/health/health.module.js.map | 1 + dist/modules/health/health.service.js | 24 ++ dist/modules/health/health.service.js.map | 1 + .../application/config/env-auth-config.js | 16 ++ .../application/config/env-auth-config.js.map | 1 + .../application/config/i-auth-config.js | 7 + .../application/config/i-auth-config.js.map | 1 + .../permission/create-permission.handler.js | 47 ++++ .../create-permission.handler.js.map | 1 + .../permission/delete-permission.handler.js | 54 +++++ .../delete-permission.handler.js.map | 1 + .../permission/get-permission.handler.js | 29 +++ .../permission/get-permission.handler.js.map | 1 + .../permission/get-permissions.handler.js | 29 +++ .../permission/get-permissions.handler.js.map | 1 + .../permission/update-permission.handler.js | 58 +++++ .../update-permission.handler.js.map | 1 + .../role/assign-permission.handler.js | 56 +++++ .../role/assign-permission.handler.js.map | 1 + .../handlers/role/create-role.handler.js | 54 +++++ .../handlers/role/create-role.handler.js.map | 1 + .../handlers/role/delete-role.handler.js | 45 ++++ .../handlers/role/delete-role.handler.js.map | 1 + .../handlers/role/get-role.handler.js | 29 +++ .../handlers/role/get-role.handler.js.map | 1 + .../handlers/role/get-roles.handler.js | 29 +++ .../handlers/role/get-roles.handler.js.map | 1 + .../role/remove-permission.handler.js | 46 ++++ .../role/remove-permission.handler.js.map | 1 + .../handlers/role/update-role.handler.js | 53 +++++ .../handlers/role/update-role.handler.js.map | 1 + .../user/assign-permission.handler.js | 42 ++++ .../user/assign-permission.handler.js.map | 1 + .../handlers/user/assign-role.handler.js | 40 ++++ .../handlers/user/assign-role.handler.js.map | 1 + .../handlers/user/create-user.handler.js | 39 ++++ .../handlers/user/create-user.handler.js.map | 1 + .../handlers/user/delete-user.handler.js | 32 +++ .../handlers/user/delete-user.handler.js.map | 1 + .../handlers/user/disable-user.handler.js | 32 +++ .../handlers/user/disable-user.handler.js.map | 1 + .../handlers/user/enable-user.handler.js | 32 +++ .../handlers/user/enable-user.handler.js.map | 1 + .../handlers/user/get-current-user.handler.js | 21 ++ .../user/get-current-user.handler.js.map | 1 + .../handlers/user/get-user.handler.js | 29 +++ .../handlers/user/get-user.handler.js.map | 1 + .../handlers/user/get-users.handler.js | 37 +++ .../handlers/user/get-users.handler.js.map | 1 + .../user/remove-permission.handler.js | 33 +++ .../user/remove-permission.handler.js.map | 1 + .../handlers/user/remove-role.handler.js | 33 +++ .../handlers/user/remove-role.handler.js.map | 1 + .../handlers/user/restore-user.handler.js | 32 +++ .../handlers/user/restore-user.handler.js.map | 1 + .../handlers/user/sync-identity.handler.js | 92 ++++++++ .../user/sync-identity.handler.js.map | 1 + .../handlers/user/update-user.handler.js | 45 ++++ .../handlers/user/update-user.handler.js.map | 1 + .../application/services/user.service.js | 37 +++ .../application/services/user.service.js.map | 1 + .../domain/entities/permission.entity.js | 33 +++ .../domain/entities/permission.entity.js.map | 1 + .../identity/domain/entities/role.entity.js | 51 +++++ .../domain/entities/role.entity.js.map | 1 + .../identity/domain/entities/user.entity.js | 143 ++++++++++++ .../domain/entities/user.entity.js.map | 1 + .../domain/events/user-created.event.js | 13 ++ .../domain/events/user-created.event.js.map | 1 + .../domain/events/user-disabled.event.js | 13 ++ .../domain/events/user-disabled.event.js.map | 1 + .../domain/events/user-email-changed.event.js | 13 ++ .../events/user-email-changed.event.js.map | 1 + .../domain/events/user-enabled.event.js | 13 ++ .../domain/events/user-enabled.event.js.map | 1 + .../events/user-username-changed.event.js | 13 ++ .../events/user-username-changed.event.js.map | 1 + .../repositories/permission.interface.js | 7 + .../repositories/permission.interface.js.map | 1 + .../domain/repositories/role.interface.js | 7 + .../domain/repositories/role.interface.js.map | 1 + .../domain/repositories/user.interface.js | 7 + .../domain/repositories/user.interface.js.map | 1 + dist/modules/identity/identity.module.js | 113 +++++++++ dist/modules/identity/identity.module.js.map | 1 + .../mappers/prisma-permission.mapper.js | 26 +++ .../mappers/prisma-permission.mapper.js.map | 1 + .../mappers/prisma-role.mapper.js | 31 +++ .../mappers/prisma-role.mapper.js.map | 1 + .../mappers/prisma-user.mapper.js | 69 ++++++ .../mappers/prisma-user.mapper.js.map | 1 + .../prisma-permission.repository.js | 67 ++++++ .../prisma-permission.repository.js.map | 1 + .../repositories/prisma-role.repository.js | 110 +++++++++ .../prisma-role.repository.js.map | 1 + .../repositories/prisma-user.repository.js | 216 ++++++++++++++++++ .../prisma-user.repository.js.map | 1 + .../controllers/permissions.controller.js | 119 ++++++++++ .../controllers/permissions.controller.js.map | 1 + .../controllers/roles.controller.js | 156 +++++++++++++ .../controllers/roles.controller.js.map | 1 + .../controllers/users.controller.js | 173 ++++++++++++++ .../controllers/users.controller.js.map | 1 + .../presentation/dto/create-permission.dto.js | 39 ++++ .../dto/create-permission.dto.js.map | 1 + .../presentation/dto/create-role.dto.js | 45 ++++ .../presentation/dto/create-role.dto.js.map | 1 + .../presentation/dto/create-user.dto.js | 54 +++++ .../presentation/dto/create-user.dto.js.map | 1 + .../presentation/dto/update-permission.dto.js | 41 ++++ .../dto/update-permission.dto.js.map | 1 + .../presentation/dto/update-role.dto.js | 40 ++++ .../presentation/dto/update-role.dto.js.map | 1 + .../presentation/dto/update-user.dto.js | 63 +++++ .../presentation/dto/update-user.dto.js.map | 1 + .../presentation/guards/current-user.guard.js | 44 ++++ .../guards/current-user.guard.js.map | 1 + dist/shared/cache-keys.js | 7 + dist/shared/cache-keys.js.map | 1 + dist/shared/prisma.service.js | 24 ++ dist/shared/prisma.service.js.map | 1 + dist/shared/redis.service.js | 121 ++++++++++ dist/shared/redis.service.js.map | 1 + dist/shared/types/request-context.js | 3 + dist/shared/types/request-context.js.map | 1 + dist/shared/utils/index.js | 5 + dist/shared/utils/index.js.map | 1 + prisma/schema.prisma | 66 ++++-- src/modules/auth/auth.service.ts | 16 +- .../mappers/prisma-role.mapper.ts | 19 +- .../repositories/prisma-role.repository.ts | 10 +- 236 files changed, 5557 insertions(+), 47 deletions(-) create mode 100644 dist/app.module.js create mode 100644 dist/app.module.js.map create mode 100644 dist/common/constants/permission.constants.js create mode 100644 dist/common/constants/permission.constants.js.map create mode 100644 dist/core/auth/guards/jwt-auth.guard.js create mode 100644 dist/core/auth/guards/jwt-auth.guard.js.map create mode 100644 dist/core/auth/interfaces/authenticated-request.interface.js create mode 100644 dist/core/auth/interfaces/authenticated-request.interface.js.map create mode 100644 dist/core/auth/interfaces/identity-data.js create mode 100644 dist/core/auth/interfaces/identity-data.js.map create mode 100644 dist/core/config/database.config.js create mode 100644 dist/core/config/database.config.js.map create mode 100644 dist/core/event-bus/event-bus.service.js create mode 100644 dist/core/event-bus/event-bus.service.js.map create mode 100644 dist/core/event-bus/event.interface.js create mode 100644 dist/core/event-bus/event.interface.js.map create mode 100644 dist/core/events/event-dispatcher.js create mode 100644 dist/core/events/event-dispatcher.js.map create mode 100644 dist/core/events/event-handler.js create mode 100644 dist/core/events/event-handler.js.map create mode 100644 dist/core/events/event-publisher.js create mode 100644 dist/core/events/event-publisher.js.map create mode 100644 dist/core/events/event.interface.js create mode 100644 dist/core/events/event.interface.js.map create mode 100644 dist/core/exceptions/http-exception.filter.js create mode 100644 dist/core/exceptions/http-exception.filter.js.map create mode 100644 dist/core/logger/logger.service.js create mode 100644 dist/core/logger/logger.service.js.map create mode 100644 dist/core/middleware/request-id.middleware.js create mode 100644 dist/core/middleware/request-id.middleware.js.map create mode 100644 dist/main.js create mode 100644 dist/main.js.map create mode 100644 dist/modules/application/application.module.js create mode 100644 dist/modules/application/application.module.js.map create mode 100644 dist/modules/application/application/handlers/create-application.handler.js create mode 100644 dist/modules/application/application/handlers/create-application.handler.js.map create mode 100644 dist/modules/application/application/handlers/delete-application.handler.js create mode 100644 dist/modules/application/application/handlers/delete-application.handler.js.map create mode 100644 dist/modules/application/application/handlers/get-application.handler.js create mode 100644 dist/modules/application/application/handlers/get-application.handler.js.map create mode 100644 dist/modules/application/application/handlers/get-applications.handler.js create mode 100644 dist/modules/application/application/handlers/get-applications.handler.js.map create mode 100644 dist/modules/application/application/handlers/get-me-applications.handler.js create mode 100644 dist/modules/application/application/handlers/get-me-applications.handler.js.map create mode 100644 dist/modules/application/application/handlers/update-application.handler.js create mode 100644 dist/modules/application/application/handlers/update-application.handler.js.map create mode 100644 dist/modules/application/application/validators/application.validator.js create mode 100644 dist/modules/application/application/validators/application.validator.js.map create mode 100644 dist/modules/application/domain/entities/application.entity.js create mode 100644 dist/modules/application/domain/entities/application.entity.js.map create mode 100644 dist/modules/application/domain/repositories/application.interface.js create mode 100644 dist/modules/application/domain/repositories/application.interface.js.map create mode 100644 dist/modules/application/infrastructure/mappers/prisma-application.mapper.js create mode 100644 dist/modules/application/infrastructure/mappers/prisma-application.mapper.js.map create mode 100644 dist/modules/application/infrastructure/repositories/prisma-application.repository.js create mode 100644 dist/modules/application/infrastructure/repositories/prisma-application.repository.js.map create mode 100644 dist/modules/application/presentation/controllers/applications.controller.js create mode 100644 dist/modules/application/presentation/controllers/applications.controller.js.map create mode 100644 dist/modules/audit/audit.event-handler.js create mode 100644 dist/modules/audit/audit.event-handler.js.map create mode 100644 dist/modules/audit/audit.module.js create mode 100644 dist/modules/audit/audit.module.js.map create mode 100644 dist/modules/audit/audit.service.js create mode 100644 dist/modules/audit/audit.service.js.map create mode 100644 dist/modules/auth/auth.controller.js create mode 100644 dist/modules/auth/auth.controller.js.map create mode 100644 dist/modules/auth/auth.module.js create mode 100644 dist/modules/auth/auth.module.js.map create mode 100644 dist/modules/auth/auth.service.js create mode 100644 dist/modules/auth/auth.service.js.map create mode 100644 dist/modules/auth/authentication.service.js create mode 100644 dist/modules/auth/authentication.service.js.map create mode 100644 dist/modules/auth/dto/login.dto.js create mode 100644 dist/modules/auth/dto/login.dto.js.map create mode 100644 dist/modules/auth/group-hash.service.js create mode 100644 dist/modules/auth/group-hash.service.js.map create mode 100644 dist/modules/auth/guards/oidc.guard.js create mode 100644 dist/modules/auth/guards/oidc.guard.js.map create mode 100644 dist/modules/auth/oidc.service.js create mode 100644 dist/modules/auth/oidc.service.js.map create mode 100644 dist/modules/auth/pkce/redis-pkce.store.js create mode 100644 dist/modules/auth/pkce/redis-pkce.store.js.map create mode 100644 dist/modules/auth/refresh/inmemory-refresh.store.js create mode 100644 dist/modules/auth/refresh/inmemory-refresh.store.js.map create mode 100644 dist/modules/auth/role-sync.service.js create mode 100644 dist/modules/auth/role-sync.service.js.map create mode 100644 dist/modules/authorization/application/authorization.service.js create mode 100644 dist/modules/authorization/application/authorization.service.js.map create mode 100644 dist/modules/authorization/authorization.module.js create mode 100644 dist/modules/authorization/authorization.module.js.map create mode 100644 dist/modules/authorization/authorization.service.js create mode 100644 dist/modules/authorization/authorization.service.js.map create mode 100644 dist/modules/authorization/cache/permission-cache.interface.js create mode 100644 dist/modules/authorization/cache/permission-cache.interface.js.map create mode 100644 dist/modules/authorization/cache/redis-permission-cache.service.js create mode 100644 dist/modules/authorization/cache/redis-permission-cache.service.js.map create mode 100644 dist/modules/authorization/guards/permission.guard.js create mode 100644 dist/modules/authorization/guards/permission.guard.js.map create mode 100644 dist/modules/authorization/presentation/decorators/permission.decorator.js create mode 100644 dist/modules/authorization/presentation/decorators/permission.decorator.js.map create mode 100644 dist/modules/authorization/presentation/guards/permission.guard.js create mode 100644 dist/modules/authorization/presentation/guards/permission.guard.js.map create mode 100644 dist/modules/health/health.controller.js create mode 100644 dist/modules/health/health.controller.js.map create mode 100644 dist/modules/health/health.module.js create mode 100644 dist/modules/health/health.module.js.map create mode 100644 dist/modules/health/health.service.js create mode 100644 dist/modules/health/health.service.js.map create mode 100644 dist/modules/identity/application/config/env-auth-config.js create mode 100644 dist/modules/identity/application/config/env-auth-config.js.map create mode 100644 dist/modules/identity/application/config/i-auth-config.js create mode 100644 dist/modules/identity/application/config/i-auth-config.js.map create mode 100644 dist/modules/identity/application/handlers/permission/create-permission.handler.js create mode 100644 dist/modules/identity/application/handlers/permission/create-permission.handler.js.map create mode 100644 dist/modules/identity/application/handlers/permission/delete-permission.handler.js create mode 100644 dist/modules/identity/application/handlers/permission/delete-permission.handler.js.map create mode 100644 dist/modules/identity/application/handlers/permission/get-permission.handler.js create mode 100644 dist/modules/identity/application/handlers/permission/get-permission.handler.js.map create mode 100644 dist/modules/identity/application/handlers/permission/get-permissions.handler.js create mode 100644 dist/modules/identity/application/handlers/permission/get-permissions.handler.js.map create mode 100644 dist/modules/identity/application/handlers/permission/update-permission.handler.js create mode 100644 dist/modules/identity/application/handlers/permission/update-permission.handler.js.map create mode 100644 dist/modules/identity/application/handlers/role/assign-permission.handler.js create mode 100644 dist/modules/identity/application/handlers/role/assign-permission.handler.js.map create mode 100644 dist/modules/identity/application/handlers/role/create-role.handler.js create mode 100644 dist/modules/identity/application/handlers/role/create-role.handler.js.map create mode 100644 dist/modules/identity/application/handlers/role/delete-role.handler.js create mode 100644 dist/modules/identity/application/handlers/role/delete-role.handler.js.map create mode 100644 dist/modules/identity/application/handlers/role/get-role.handler.js create mode 100644 dist/modules/identity/application/handlers/role/get-role.handler.js.map create mode 100644 dist/modules/identity/application/handlers/role/get-roles.handler.js create mode 100644 dist/modules/identity/application/handlers/role/get-roles.handler.js.map create mode 100644 dist/modules/identity/application/handlers/role/remove-permission.handler.js create mode 100644 dist/modules/identity/application/handlers/role/remove-permission.handler.js.map create mode 100644 dist/modules/identity/application/handlers/role/update-role.handler.js create mode 100644 dist/modules/identity/application/handlers/role/update-role.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/assign-permission.handler.js create mode 100644 dist/modules/identity/application/handlers/user/assign-permission.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/assign-role.handler.js create mode 100644 dist/modules/identity/application/handlers/user/assign-role.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/create-user.handler.js create mode 100644 dist/modules/identity/application/handlers/user/create-user.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/delete-user.handler.js create mode 100644 dist/modules/identity/application/handlers/user/delete-user.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/disable-user.handler.js create mode 100644 dist/modules/identity/application/handlers/user/disable-user.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/enable-user.handler.js create mode 100644 dist/modules/identity/application/handlers/user/enable-user.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/get-current-user.handler.js create mode 100644 dist/modules/identity/application/handlers/user/get-current-user.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/get-user.handler.js create mode 100644 dist/modules/identity/application/handlers/user/get-user.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/get-users.handler.js create mode 100644 dist/modules/identity/application/handlers/user/get-users.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/remove-permission.handler.js create mode 100644 dist/modules/identity/application/handlers/user/remove-permission.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/remove-role.handler.js create mode 100644 dist/modules/identity/application/handlers/user/remove-role.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/restore-user.handler.js create mode 100644 dist/modules/identity/application/handlers/user/restore-user.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/sync-identity.handler.js create mode 100644 dist/modules/identity/application/handlers/user/sync-identity.handler.js.map create mode 100644 dist/modules/identity/application/handlers/user/update-user.handler.js create mode 100644 dist/modules/identity/application/handlers/user/update-user.handler.js.map create mode 100644 dist/modules/identity/application/services/user.service.js create mode 100644 dist/modules/identity/application/services/user.service.js.map create mode 100644 dist/modules/identity/domain/entities/permission.entity.js create mode 100644 dist/modules/identity/domain/entities/permission.entity.js.map create mode 100644 dist/modules/identity/domain/entities/role.entity.js create mode 100644 dist/modules/identity/domain/entities/role.entity.js.map create mode 100644 dist/modules/identity/domain/entities/user.entity.js create mode 100644 dist/modules/identity/domain/entities/user.entity.js.map create mode 100644 dist/modules/identity/domain/events/user-created.event.js create mode 100644 dist/modules/identity/domain/events/user-created.event.js.map create mode 100644 dist/modules/identity/domain/events/user-disabled.event.js create mode 100644 dist/modules/identity/domain/events/user-disabled.event.js.map create mode 100644 dist/modules/identity/domain/events/user-email-changed.event.js create mode 100644 dist/modules/identity/domain/events/user-email-changed.event.js.map create mode 100644 dist/modules/identity/domain/events/user-enabled.event.js create mode 100644 dist/modules/identity/domain/events/user-enabled.event.js.map create mode 100644 dist/modules/identity/domain/events/user-username-changed.event.js create mode 100644 dist/modules/identity/domain/events/user-username-changed.event.js.map create mode 100644 dist/modules/identity/domain/repositories/permission.interface.js create mode 100644 dist/modules/identity/domain/repositories/permission.interface.js.map create mode 100644 dist/modules/identity/domain/repositories/role.interface.js create mode 100644 dist/modules/identity/domain/repositories/role.interface.js.map create mode 100644 dist/modules/identity/domain/repositories/user.interface.js create mode 100644 dist/modules/identity/domain/repositories/user.interface.js.map create mode 100644 dist/modules/identity/identity.module.js create mode 100644 dist/modules/identity/identity.module.js.map create mode 100644 dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js create mode 100644 dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js.map create mode 100644 dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js create mode 100644 dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js.map create mode 100644 dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js create mode 100644 dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js.map create mode 100644 dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js create mode 100644 dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js.map create mode 100644 dist/modules/identity/infrastructure/repositories/prisma-role.repository.js create mode 100644 dist/modules/identity/infrastructure/repositories/prisma-role.repository.js.map create mode 100644 dist/modules/identity/infrastructure/repositories/prisma-user.repository.js create mode 100644 dist/modules/identity/infrastructure/repositories/prisma-user.repository.js.map create mode 100644 dist/modules/identity/presentation/controllers/permissions.controller.js create mode 100644 dist/modules/identity/presentation/controllers/permissions.controller.js.map create mode 100644 dist/modules/identity/presentation/controllers/roles.controller.js create mode 100644 dist/modules/identity/presentation/controllers/roles.controller.js.map create mode 100644 dist/modules/identity/presentation/controllers/users.controller.js create mode 100644 dist/modules/identity/presentation/controllers/users.controller.js.map create mode 100644 dist/modules/identity/presentation/dto/create-permission.dto.js create mode 100644 dist/modules/identity/presentation/dto/create-permission.dto.js.map create mode 100644 dist/modules/identity/presentation/dto/create-role.dto.js create mode 100644 dist/modules/identity/presentation/dto/create-role.dto.js.map create mode 100644 dist/modules/identity/presentation/dto/create-user.dto.js create mode 100644 dist/modules/identity/presentation/dto/create-user.dto.js.map create mode 100644 dist/modules/identity/presentation/dto/update-permission.dto.js create mode 100644 dist/modules/identity/presentation/dto/update-permission.dto.js.map create mode 100644 dist/modules/identity/presentation/dto/update-role.dto.js create mode 100644 dist/modules/identity/presentation/dto/update-role.dto.js.map create mode 100644 dist/modules/identity/presentation/dto/update-user.dto.js create mode 100644 dist/modules/identity/presentation/dto/update-user.dto.js.map create mode 100644 dist/modules/identity/presentation/guards/current-user.guard.js create mode 100644 dist/modules/identity/presentation/guards/current-user.guard.js.map create mode 100644 dist/shared/cache-keys.js create mode 100644 dist/shared/cache-keys.js.map create mode 100644 dist/shared/prisma.service.js create mode 100644 dist/shared/prisma.service.js.map create mode 100644 dist/shared/redis.service.js create mode 100644 dist/shared/redis.service.js.map create mode 100644 dist/shared/types/request-context.js create mode 100644 dist/shared/types/request-context.js.map create mode 100644 dist/shared/utils/index.js create mode 100644 dist/shared/utils/index.js.map diff --git a/dist/app.module.js b/dist/app.module.js new file mode 100644 index 0000000..ba0f573 --- /dev/null +++ b/dist/app.module.js @@ -0,0 +1,37 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AppModule = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const identity_module_1 = require("./modules/identity/identity.module"); +const auth_module_1 = require("./modules/auth/auth.module"); +const health_module_1 = require("./modules/health/health.module"); +const authorization_module_1 = require("./modules/authorization/authorization.module"); +const audit_module_1 = require("./modules/audit/audit.module"); +const application_module_1 = require("./modules/application/application.module"); +let AppModule = class AppModule { +}; +exports.AppModule = AppModule; +exports.AppModule = AppModule = __decorate([ + (0, common_1.Module)({ + imports: [ + config_1.ConfigModule.forRoot({ + isGlobal: true, + }), + identity_module_1.IdentityModule, + auth_module_1.AuthModule, + health_module_1.HealthModule, + // Authorization module provides permission checks and cache + authorization_module_1.AuthorizationModule, + audit_module_1.AuditModule, + application_module_1.ApplicationModule, + ], + }) +], AppModule); +//# sourceMappingURL=app.module.js.map \ No newline at end of file diff --git a/dist/app.module.js.map b/dist/app.module.js.map new file mode 100644 index 0000000..4fef502 --- /dev/null +++ b/dist/app.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2CAA8C;AAE9C,wEAAoE;AACpE,4DAAwD;AACxD,kEAA8D;AAC9D,uFAAmF;AACnF,+DAA2D;AAC3D,iFAA6E;AAmBtE,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IAhBrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,CAAC;gBACnB,QAAQ,EAAE,IAAI;aACf,CAAC;YAEE,gCAAc;YACd,wBAAU;YACV,4BAAY;YACZ,4DAA4D;YACpD,0CAAmB;YAC3B,0BAAW;YACX,sCAAiB;SAEtB;KACF,CAAC;GACW,SAAS,CAAG"} \ No newline at end of file diff --git a/dist/common/constants/permission.constants.js b/dist/common/constants/permission.constants.js new file mode 100644 index 0000000..ffc60e9 --- /dev/null +++ b/dist/common/constants/permission.constants.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PermissionType = void 0; +exports.PermissionType = { + USER_CREATE: 'USER_CREATE', + USER_READ: 'USER_READ', + USER_UPDATE: 'USER_UPDATE', + USER_DELETE: 'USER_DELETE', + ROLE_CREATE: 'ROLE_CREATE', + ROLE_READ: 'ROLE_READ', + ROLE_UPDATE: 'ROLE_UPDATE', + ROLE_DELETE: 'ROLE_DELETE', + PERMISSION_CREATE: 'PERMISSION_CREATE', + PERMISSION_READ: 'PERMISSION_READ', + PERMISSION_UPDATE: 'PERMISSION_UPDATE', + PERMISSION_DELETE: 'PERMISSION_DELETE', +}; +//# sourceMappingURL=permission.constants.js.map \ No newline at end of file diff --git a/dist/common/constants/permission.constants.js.map b/dist/common/constants/permission.constants.js.map new file mode 100644 index 0000000..2749d2f --- /dev/null +++ b/dist/common/constants/permission.constants.js.map @@ -0,0 +1 @@ +{"version":3,"file":"permission.constants.js","sourceRoot":"","sources":["../../../src/common/constants/permission.constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,cAAc,GAAG;IAC5B,WAAW,EAAE,aAAa;IAC1B,SAAS,EAAE,WAAW;IACtB,WAAW,EAAE,aAAa;IAC1B,WAAW,EAAE,aAAa;IAE1B,WAAW,EAAE,aAAa;IAC1B,SAAS,EAAE,WAAW;IACtB,WAAW,EAAE,aAAa;IAC1B,WAAW,EAAE,aAAa;IAE1B,iBAAiB,EAAE,mBAAmB;IACtC,eAAe,EAAE,iBAAiB;IAClC,iBAAiB,EAAE,mBAAmB;IACtC,iBAAiB,EAAE,mBAAmB;CAC9B,CAAC"} \ No newline at end of file diff --git a/dist/core/auth/guards/jwt-auth.guard.js b/dist/core/auth/guards/jwt-auth.guard.js new file mode 100644 index 0000000..5007d31 --- /dev/null +++ b/dist/core/auth/guards/jwt-auth.guard.js @@ -0,0 +1,106 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.JwtAuthGuard = void 0; +const common_1 = require("@nestjs/common"); +const jose_1 = require("jose"); +const identity_data_1 = require("../interfaces/identity-data"); +const jwt = __importStar(require("jsonwebtoken")); +/** + * JwtAuthGuard verifies JWTs issued by the external Identity Provider (Authentik) + * using JWKS (RS256). It also accepts internal RayLab JWTs signed with a local secret. + */ +let JwtAuthGuard = class JwtAuthGuard { + jwks = null; + constructor() { } + async canActivate(context) { + const request = context.switchToHttp().getRequest(); + const authHeader = request.headers.authorization; + if (!authHeader) { + throw new common_1.UnauthorizedException('Authorization header is missing.'); + } + const [type, token] = authHeader.split(' '); + if (type !== 'Bearer' || !token) { + throw new common_1.UnauthorizedException('Invalid authorization header.'); + } + const jwksUri = process.env.AUTHENTIK_JWKS_URI; + // First try verifying with external JWKS (Authentik) + if (jwksUri) { + try { + if (!this.jwks) + this.jwks = (0, jose_1.createRemoteJWKSet)(new URL(jwksUri)); + const { payload } = await (0, jose_1.jwtVerify)(token, this.jwks, { + issuer: process.env.AUTHENTIK_ISSUER, + audience: process.env.AUTHENTIK_AUDIENCE, + }); + const identity = new identity_data_1.IdentityData(payload.sub, payload.preferred_username, payload.email, payload); + request.identity = identity; + return true; + } + catch (err) { + // ignore and try internal verification + } + } + // Fallback: verify with internal symmetric secret + const secret = process.env.RAYLAB_JWT_SECRET; + if (!secret) { + throw new common_1.UnauthorizedException('Invalid or expired token.'); + } + try { + const payload = jwt.verify(token, secret); + const identity = new identity_data_1.IdentityData(payload.sub, payload.preferred_username, payload.email, payload); + request.identity = identity; + return true; + } + catch (err) { + throw new common_1.UnauthorizedException('Invalid or expired token.'); + } + } +}; +exports.JwtAuthGuard = JwtAuthGuard; +exports.JwtAuthGuard = JwtAuthGuard = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", []) +], JwtAuthGuard); +//# sourceMappingURL=jwt-auth.guard.js.map \ No newline at end of file diff --git a/dist/core/auth/guards/jwt-auth.guard.js.map b/dist/core/auth/guards/jwt-auth.guard.js.map new file mode 100644 index 0000000..fa4c38d --- /dev/null +++ b/dist/core/auth/guards/jwt-auth.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"jwt-auth.guard.js","sourceRoot":"","sources":["../../../../src/core/auth/guards/jwt-auth.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAKwB;AAExB,+BAAqD;AACrD,+DAA2D;AAC3D,kDAAoC;AAEpC;;;GAGG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAY;IACf,IAAI,GAAiD,IAAI,CAAC;IAElE,gBAAe,CAAC;IAEhB,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAyC,CAAC;QAE3F,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QAEjD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,8BAAqB,CAAC,kCAAkC,CAAC,CAAC;QACtE,CAAC;QAED,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE5C,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;YAChC,MAAM,IAAI,8BAAqB,CAAC,+BAA+B,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;QAE/C,qDAAqD;QACrD,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,IAAI,CAAC,IAAI,CAAC,IAAI;oBAAE,IAAI,CAAC,IAAI,GAAG,IAAA,yBAAkB,EAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;gBAEjE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAA,gBAAS,EAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE;oBACpD,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB;oBACpC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB;iBACzC,CAAC,CAAC;gBAEH,MAAM,QAAQ,GAAG,IAAI,4BAAY,CAC/B,OAAO,CAAC,GAAa,EACpB,OAAe,CAAC,kBAAwC,EACxD,OAAe,CAAC,KAA2B,EAC5C,OAA8B,CAC/B,CAAC;gBAEF,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;gBAC5B,OAAO,IAAI,CAAC;YACd,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,uCAAuC;YACzC,CAAC;QACH,CAAC;QAED,kDAAkD;QAClD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,8BAAqB,CAAC,2BAA2B,CAAC,CAAC;QAC/D,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAQ,CAAC;YAEjD,MAAM,QAAQ,GAAG,IAAI,4BAAY,CAC/B,OAAO,CAAC,GAAa,EACrB,OAAO,CAAC,kBAAwC,EAChD,OAAO,CAAC,KAA2B,EACnC,OAA8B,CAC/B,CAAC;YAEF,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,MAAM,IAAI,8BAAqB,CAAC,2BAA2B,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;CACF,CAAA;AApEY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;;GACA,YAAY,CAoExB"} \ No newline at end of file diff --git a/dist/core/auth/interfaces/authenticated-request.interface.js b/dist/core/auth/interfaces/authenticated-request.interface.js new file mode 100644 index 0000000..c514541 --- /dev/null +++ b/dist/core/auth/interfaces/authenticated-request.interface.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=authenticated-request.interface.js.map \ No newline at end of file diff --git a/dist/core/auth/interfaces/authenticated-request.interface.js.map b/dist/core/auth/interfaces/authenticated-request.interface.js.map new file mode 100644 index 0000000..220894c --- /dev/null +++ b/dist/core/auth/interfaces/authenticated-request.interface.js.map @@ -0,0 +1 @@ +{"version":3,"file":"authenticated-request.interface.js","sourceRoot":"","sources":["../../../../src/core/auth/interfaces/authenticated-request.interface.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/core/auth/interfaces/identity-data.js b/dist/core/auth/interfaces/identity-data.js new file mode 100644 index 0000000..e130fd7 --- /dev/null +++ b/dist/core/auth/interfaces/identity-data.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.IdentityData = void 0; +class IdentityData { + sub; + preferred_username; + email; + claims; + constructor(sub, preferred_username, email, claims) { + this.sub = sub; + this.preferred_username = preferred_username; + this.email = email; + this.claims = claims; + } +} +exports.IdentityData = IdentityData; +//# sourceMappingURL=identity-data.js.map \ No newline at end of file diff --git a/dist/core/auth/interfaces/identity-data.js.map b/dist/core/auth/interfaces/identity-data.js.map new file mode 100644 index 0000000..292e6dc --- /dev/null +++ b/dist/core/auth/interfaces/identity-data.js.map @@ -0,0 +1 @@ +{"version":3,"file":"identity-data.js","sourceRoot":"","sources":["../../../../src/core/auth/interfaces/identity-data.ts"],"names":[],"mappings":";;;AAAA,MAAa,YAAY;IAEL;IACA;IACA;IACA;IAJlB,YACkB,GAAW,EACX,kBAA2B,EAC3B,KAAc,EACd,MAA4B;QAH5B,QAAG,GAAH,GAAG,CAAQ;QACX,uBAAkB,GAAlB,kBAAkB,CAAS;QAC3B,UAAK,GAAL,KAAK,CAAS;QACd,WAAM,GAAN,MAAM,CAAsB;IAC3C,CAAC;CACL;AAPD,oCAOC"} \ No newline at end of file diff --git a/dist/core/config/database.config.js b/dist/core/config/database.config.js new file mode 100644 index 0000000..c4bf0dd --- /dev/null +++ b/dist/core/config/database.config.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = () => ({ + database: { + url: `postgresql://` + + `${process.env.DB_USER}:` + + `${process.env.DB_PASSWORD}@` + + `${process.env.SERVER_HOST}:` + + `${process.env.DB_PORT}/` + + `${process.env.DB_NAME}?schema=public` + } +}); +//# sourceMappingURL=database.config.js.map \ No newline at end of file diff --git a/dist/core/config/database.config.js.map b/dist/core/config/database.config.js.map new file mode 100644 index 0000000..4f98463 --- /dev/null +++ b/dist/core/config/database.config.js.map @@ -0,0 +1 @@ +{"version":3,"file":"database.config.js","sourceRoot":"","sources":["../../../src/core/config/database.config.ts"],"names":[],"mappings":";;AAAA,kBAAe,GAAG,EAAE,CAAC,CAAC;IAClB,QAAQ,EAAE;QACN,GAAG,EACC,eAAe;YACf,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG;YACzB,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG;YAC7B,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG;YAC7B,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG;YACzB,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,gBAAgB;KAC7C;CACJ,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/core/event-bus/event-bus.service.js b/dist/core/event-bus/event-bus.service.js new file mode 100644 index 0000000..2421563 --- /dev/null +++ b/dist/core/event-bus/event-bus.service.js @@ -0,0 +1,36 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var EventBus_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EventBus = void 0; +const common_1 = require("@nestjs/common"); +let EventBus = EventBus_1 = class EventBus { + handlers = new Map(); + logger = new common_1.Logger(EventBus_1.name); + publish(event) { + const handlers = this.handlers.get(event.type) || []; + for (const h of handlers) { + try { + Promise.resolve(h(event)).catch((err) => this.logger.error('Event handler error', err)); + } + catch (e) { + this.logger.error('Event handler threw', e); + } + } + } + subscribe(eventType, handler) { + const list = this.handlers.get(eventType) || []; + list.push(handler); + this.handlers.set(eventType, list); + } +}; +exports.EventBus = EventBus; +exports.EventBus = EventBus = EventBus_1 = __decorate([ + (0, common_1.Injectable)() +], EventBus); +//# sourceMappingURL=event-bus.service.js.map \ No newline at end of file diff --git a/dist/core/event-bus/event-bus.service.js.map b/dist/core/event-bus/event-bus.service.js.map new file mode 100644 index 0000000..3aa167f --- /dev/null +++ b/dist/core/event-bus/event-bus.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"event-bus.service.js","sourceRoot":"","sources":["../../../src/core/event-bus/event-bus.service.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAoD;AAO7C,IAAM,QAAQ,gBAAd,MAAM,QAAQ;IACX,QAAQ,GAA2B,IAAI,GAAG,EAAE,CAAC;IACpC,MAAM,GAAG,IAAI,eAAM,CAAC,UAAQ,CAAC,IAAI,CAAC,CAAC;IAEpD,OAAO,CAAC,KAAyB;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACrD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC,CAAC;YAC1F,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAQ,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;IACH,CAAC;IAED,SAAS,CAAC,SAAiB,EAAE,OAAgB;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;CACF,CAAA;AApBY,4BAAQ;mBAAR,QAAQ;IADpB,IAAA,mBAAU,GAAE;GACA,QAAQ,CAoBpB"} \ No newline at end of file diff --git a/dist/core/event-bus/event.interface.js b/dist/core/event-bus/event.interface.js new file mode 100644 index 0000000..051ab18 --- /dev/null +++ b/dist/core/event-bus/event.interface.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=event.interface.js.map \ No newline at end of file diff --git a/dist/core/event-bus/event.interface.js.map b/dist/core/event-bus/event.interface.js.map new file mode 100644 index 0000000..db1995e --- /dev/null +++ b/dist/core/event-bus/event.interface.js.map @@ -0,0 +1 @@ +{"version":3,"file":"event.interface.js","sourceRoot":"","sources":["../../../src/core/event-bus/event.interface.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/core/events/event-dispatcher.js b/dist/core/events/event-dispatcher.js new file mode 100644 index 0000000..ad25771 --- /dev/null +++ b/dist/core/events/event-dispatcher.js @@ -0,0 +1,14 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EventDispatcher = void 0; +class EventDispatcher { + publisher = new (require('./event-publisher').InMemoryEventPublisher)(); + register(handler) { + this.publisher.register(handler); + } + async dispatch(event) { + await this.publisher.publish(event); + } +} +exports.EventDispatcher = EventDispatcher; +//# sourceMappingURL=event-dispatcher.js.map \ No newline at end of file diff --git a/dist/core/events/event-dispatcher.js.map b/dist/core/events/event-dispatcher.js.map new file mode 100644 index 0000000..065bcf8 --- /dev/null +++ b/dist/core/events/event-dispatcher.js.map @@ -0,0 +1 @@ +{"version":3,"file":"event-dispatcher.js","sourceRoot":"","sources":["../../../src/core/events/event-dispatcher.ts"],"names":[],"mappings":";;;AAEA,MAAa,eAAe;IAClB,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,sBAAsB,CAAC,EAAE,CAAC;IAEhF,QAAQ,CAAC,OAA0C;QACjD,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAkB;QAC/B,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;CACF;AAVD,0CAUC"} \ No newline at end of file diff --git a/dist/core/events/event-handler.js b/dist/core/events/event-handler.js new file mode 100644 index 0000000..6950f78 --- /dev/null +++ b/dist/core/events/event-handler.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=event-handler.js.map \ No newline at end of file diff --git a/dist/core/events/event-handler.js.map b/dist/core/events/event-handler.js.map new file mode 100644 index 0000000..efbfd3f --- /dev/null +++ b/dist/core/events/event-handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"event-handler.js","sourceRoot":"","sources":["../../../src/core/events/event-handler.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/core/events/event-publisher.js b/dist/core/events/event-publisher.js new file mode 100644 index 0000000..7429352 --- /dev/null +++ b/dist/core/events/event-publisher.js @@ -0,0 +1,14 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.InMemoryEventPublisher = void 0; +class InMemoryEventPublisher { + handlers = []; + register(handler) { + this.handlers.push(handler); + } + async publish(event) { + await Promise.all(this.handlers.map((h) => h(event))); + } +} +exports.InMemoryEventPublisher = InMemoryEventPublisher; +//# sourceMappingURL=event-publisher.js.map \ No newline at end of file diff --git a/dist/core/events/event-publisher.js.map b/dist/core/events/event-publisher.js.map new file mode 100644 index 0000000..c2c673c --- /dev/null +++ b/dist/core/events/event-publisher.js.map @@ -0,0 +1 @@ +{"version":3,"file":"event-publisher.js","sourceRoot":"","sources":["../../../src/core/events/event-publisher.ts"],"names":[],"mappings":";;;AAMA,MAAa,sBAAsB;IACzB,QAAQ,GAA6C,EAAE,CAAC;IAEhE,QAAQ,CAAC,OAA0C;QACjD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAkB;QAC9B,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;CACF;AAVD,wDAUC"} \ No newline at end of file diff --git a/dist/core/events/event.interface.js b/dist/core/events/event.interface.js new file mode 100644 index 0000000..051ab18 --- /dev/null +++ b/dist/core/events/event.interface.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=event.interface.js.map \ No newline at end of file diff --git a/dist/core/events/event.interface.js.map b/dist/core/events/event.interface.js.map new file mode 100644 index 0000000..2fa0bd2 --- /dev/null +++ b/dist/core/events/event.interface.js.map @@ -0,0 +1 @@ +{"version":3,"file":"event.interface.js","sourceRoot":"","sources":["../../../src/core/events/event.interface.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/core/exceptions/http-exception.filter.js b/dist/core/exceptions/http-exception.filter.js new file mode 100644 index 0000000..36ced11 --- /dev/null +++ b/dist/core/exceptions/http-exception.filter.js @@ -0,0 +1,29 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HttpExceptionFilter = void 0; +const common_1 = require("@nestjs/common"); +let HttpExceptionFilter = class HttpExceptionFilter { + catch(exception, host) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const status = exception.getStatus ? exception.getStatus() : 500; + response.status(status).json({ + success: false, + error: { + code: exception.code || 'INTERNAL_ERROR', + message: exception.message || 'Internal server error', + }, + }); + } +}; +exports.HttpExceptionFilter = HttpExceptionFilter; +exports.HttpExceptionFilter = HttpExceptionFilter = __decorate([ + (0, common_1.Catch)() +], HttpExceptionFilter); +//# sourceMappingURL=http-exception.filter.js.map \ No newline at end of file diff --git a/dist/core/exceptions/http-exception.filter.js.map b/dist/core/exceptions/http-exception.filter.js.map new file mode 100644 index 0000000..9774136 --- /dev/null +++ b/dist/core/exceptions/http-exception.filter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"http-exception.filter.js","sourceRoot":"","sources":["../../../src/core/exceptions/http-exception.filter.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAuE;AAGhE,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC9B,KAAK,CAAC,SAAc,EAAE,IAAmB;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;QAEjE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;YAC3B,OAAO,EAAE,KAAK;YACd,KAAK,EAAE;gBACL,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,gBAAgB;gBACxC,OAAO,EAAE,SAAS,CAAC,OAAO,IAAI,uBAAuB;aACtD;SACF,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AAdY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,cAAK,GAAE;GACK,mBAAmB,CAc/B"} \ No newline at end of file diff --git a/dist/core/logger/logger.service.js b/dist/core/logger/logger.service.js new file mode 100644 index 0000000..8de8f09 --- /dev/null +++ b/dist/core/logger/logger.service.js @@ -0,0 +1,11 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.logger = void 0; +const pino_1 = __importDefault(require("pino")); +exports.logger = (0, pino_1.default)({ + level: process.env.LOG_LEVEL || 'info', +}); +//# sourceMappingURL=logger.service.js.map \ No newline at end of file diff --git a/dist/core/logger/logger.service.js.map b/dist/core/logger/logger.service.js.map new file mode 100644 index 0000000..459e62c --- /dev/null +++ b/dist/core/logger/logger.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"logger.service.js","sourceRoot":"","sources":["../../../src/core/logger/logger.service.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AAEX,QAAA,MAAM,GAAG,IAAA,cAAI,EAAC;IACzB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;CACvC,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/core/middleware/request-id.middleware.js b/dist/core/middleware/request-id.middleware.js new file mode 100644 index 0000000..4fa05fb --- /dev/null +++ b/dist/core/middleware/request-id.middleware.js @@ -0,0 +1,23 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RequestIdMiddleware = void 0; +const common_1 = require("@nestjs/common"); +const uuid_1 = require("uuid"); +let RequestIdMiddleware = class RequestIdMiddleware { + use(req, res, next) { + req.requestId = req.headers['x-request-id'] || (0, uuid_1.v4)(); + res.setHeader('X-Request-ID', req.requestId); + next(); + } +}; +exports.RequestIdMiddleware = RequestIdMiddleware; +exports.RequestIdMiddleware = RequestIdMiddleware = __decorate([ + (0, common_1.Injectable)() +], RequestIdMiddleware); +//# sourceMappingURL=request-id.middleware.js.map \ No newline at end of file diff --git a/dist/core/middleware/request-id.middleware.js.map b/dist/core/middleware/request-id.middleware.js.map new file mode 100644 index 0000000..393bde4 --- /dev/null +++ b/dist/core/middleware/request-id.middleware.js.map @@ -0,0 +1 @@ +{"version":3,"file":"request-id.middleware.js","sourceRoot":"","sources":["../../../src/core/middleware/request-id.middleware.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4D;AAC5D,+BAAoC;AAG7B,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC9B,GAAG,CAAC,GAAQ,EAAE,GAAQ,EAAE,IAAgB;QACtC,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,IAAA,SAAM,GAAE,CAAC;QACxD,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,EAAE,CAAC;IACT,CAAC;CACF,CAAA;AANY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,mBAAU,GAAE;GACA,mBAAmB,CAM/B"} \ No newline at end of file diff --git a/dist/main.js b/dist/main.js new file mode 100644 index 0000000..7a2a568 --- /dev/null +++ b/dist/main.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const common_1 = require("@nestjs/common"); +const core_1 = require("@nestjs/core"); +const config_1 = require("@nestjs/config"); +const swagger_1 = require("@nestjs/swagger"); +const app_module_1 = require("./app.module"); +async function bootstrap() { + const app = await core_1.NestFactory.create(app_module_1.AppModule); + const config = app.get(config_1.ConfigService); + app.setGlobalPrefix(''); + app.useGlobalPipes(new common_1.ValidationPipe({ + whitelist: true, + transform: true, + forbidNonWhitelisted: true, + })); + app.enableCors(); + const swaggerEnabled = config.get('SWAGGER_ENABLED') === 'true'; + if (swaggerEnabled) { + const swaggerConfig = new swagger_1.DocumentBuilder() + .setTitle('RayLab Core API') + .setDescription('RayLab Core REST API') + .setVersion('1.0.0') + .addBearerAuth() + .build(); + const document = swagger_1.SwaggerModule.createDocument(app, swaggerConfig); + swagger_1.SwaggerModule.setup('ApiList', app, document); + } + const port = config.get('PORT') || 3000; + await app.listen(port); + console.log(`Server running on http://localhost:${port}`); +} +bootstrap(); +//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/dist/main.js.map b/dist/main.js.map new file mode 100644 index 0000000..8c9087b --- /dev/null +++ b/dist/main.js.map @@ -0,0 +1 @@ +{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;AAAA,2CAAgD;AAChD,uCAA2C;AAC3C,2CAA+C;AAC/C,6CAAiE;AAEjE,6CAAyC;AAEzC,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,CAAC;IAEhD,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,sBAAa,CAAC,CAAC;IAEtC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IAExB,GAAG,CAAC,cAAc,CAChB,IAAI,uBAAc,CAAC;QACjB,SAAS,EAAE,IAAI;QACf,SAAS,EAAE,IAAI;QACf,oBAAoB,EAAE,IAAI;KAC3B,CAAC,CACH,CAAC;IAEF,GAAG,CAAC,UAAU,EAAE,CAAC;IAEjB,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAS,iBAAiB,CAAC,KAAK,MAAM,CAAC;IAExE,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,aAAa,GAAG,IAAI,yBAAe,EAAE;aACxC,QAAQ,CAAC,iBAAiB,CAAC;aAC3B,cAAc,CAAC,sBAAsB,CAAC;aACtC,UAAU,CAAC,OAAO,CAAC;aACnB,aAAa,EAAE;aACf,KAAK,EAAE,CAAC;QAEX,MAAM,QAAQ,GAAG,uBAAa,CAAC,cAAc,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAElE,uBAAa,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAS,MAAM,CAAC,IAAI,IAAI,CAAC;IAEhD,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAEvB,OAAO,CAAC,GAAG,CAAC,sCAAsC,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/modules/application/application.module.js b/dist/modules/application/application.module.js new file mode 100644 index 0000000..93b38ff --- /dev/null +++ b/dist/modules/application/application.module.js @@ -0,0 +1,50 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ApplicationModule = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../shared/prisma.service"); +const applications_controller_1 = require("./presentation/controllers/applications.controller"); +const prisma_application_repository_1 = require("./infrastructure/repositories/prisma-application.repository"); +const application_interface_1 = require("./domain/repositories/application.interface"); +const get_applications_handler_1 = require("./application/handlers/get-applications.handler"); +const get_application_handler_1 = require("./application/handlers/get-application.handler"); +const create_application_handler_1 = require("./application/handlers/create-application.handler"); +const update_application_handler_1 = require("./application/handlers/update-application.handler"); +const delete_application_handler_1 = require("./application/handlers/delete-application.handler"); +const get_me_applications_handler_1 = require("./application/handlers/get-me-applications.handler"); +const application_validator_1 = require("./application/validators/application.validator"); +const jwt_auth_guard_1 = require("../../core/auth/guards/jwt-auth.guard"); +const current_user_guard_1 = require("../identity/presentation/guards/current-user.guard"); +let ApplicationModule = class ApplicationModule { +}; +exports.ApplicationModule = ApplicationModule; +exports.ApplicationModule = ApplicationModule = __decorate([ + (0, common_1.Module)({ + imports: [], + providers: [ + prisma_service_1.PrismaService, + prisma_application_repository_1.PrismaApplicationRepository, + get_applications_handler_1.GetApplicationsHandler, + get_application_handler_1.GetApplicationHandler, + create_application_handler_1.CreateApplicationHandler, + update_application_handler_1.UpdateApplicationHandler, + delete_application_handler_1.DeleteApplicationHandler, + get_me_applications_handler_1.GetMeApplicationsHandler, + application_validator_1.ApplicationValidator, + jwt_auth_guard_1.JwtAuthGuard, + current_user_guard_1.CurrentUserGuard, + { + provide: application_interface_1.IApplication, + useClass: prisma_application_repository_1.PrismaApplicationRepository, + }, + ], + controllers: [applications_controller_1.ApplicationsController], + }) +], ApplicationModule); +//# sourceMappingURL=application.module.js.map \ No newline at end of file diff --git a/dist/modules/application/application.module.js.map b/dist/modules/application/application.module.js.map new file mode 100644 index 0000000..d908fa3 --- /dev/null +++ b/dist/modules/application/application.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"application.module.js","sourceRoot":"","sources":["../../../src/modules/application/application.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,gEAA4D;AAC5D,gGAA4F;AAC5F,+GAA0G;AAC1G,uFAA2E;AAC3E,8FAAyF;AACzF,4FAAuF;AACvF,kGAA6F;AAC7F,kGAA6F;AAC7F,kGAA6F;AAC7F,oGAA8F;AAC9F,0FAAsF;AACtF,0EAAqE;AACrE,2FAAsF;AAuB/E,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;CAAG,CAAA;AAApB,8CAAiB;4BAAjB,iBAAiB;IArB7B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,EAAE;QACX,SAAS,EAAE;YACT,8BAAa;YACb,2DAA2B;YAC3B,iDAAsB;YACtB,+CAAqB;YACrB,qDAAwB;YACxB,qDAAwB;YACxB,qDAAwB;YACxB,sDAAwB;YACxB,4CAAoB;YACpB,6BAAY;YACZ,qCAAgB;YAChB;gBACE,OAAO,EAAE,oCAAY;gBACrB,QAAQ,EAAE,2DAA2B;aACtC;SACF;QACD,WAAW,EAAE,CAAC,gDAAsB,CAAC;KACtC,CAAC;GACW,iBAAiB,CAAG"} \ No newline at end of file diff --git a/dist/modules/application/application/handlers/create-application.handler.js b/dist/modules/application/application/handlers/create-application.handler.js new file mode 100644 index 0000000..ada011b --- /dev/null +++ b/dist/modules/application/application/handlers/create-application.handler.js @@ -0,0 +1,44 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateApplicationHandler = void 0; +const common_1 = require("@nestjs/common"); +const application_interface_1 = require("../../domain/repositories/application.interface"); +const application_validator_1 = require("../../application/validators/application.validator"); +const application_entity_1 = require("../../domain/entities/application.entity"); +let CreateApplicationHandler = class CreateApplicationHandler { + appRepo; + validator; + constructor(appRepo, validator) { + this.appRepo = appRepo; + this.validator = validator; + } + async execute(payload) { + await this.validator.validateCreate(payload); + const app = application_entity_1.ApplicationData.create({ + code: payload.code, + name: payload.name, + description: payload.description, + icon: payload.icon, + url: payload.url, + applicationsClaim: payload.applicationsClaim, + displayOrder: payload.displayOrder, + }); + const created = await this.appRepo.create(app); + return created; + } +}; +exports.CreateApplicationHandler = CreateApplicationHandler; +exports.CreateApplicationHandler = CreateApplicationHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [application_interface_1.IApplication, application_validator_1.ApplicationValidator]) +], CreateApplicationHandler); +//# sourceMappingURL=create-application.handler.js.map \ No newline at end of file diff --git a/dist/modules/application/application/handlers/create-application.handler.js.map b/dist/modules/application/application/handlers/create-application.handler.js.map new file mode 100644 index 0000000..cb30776 --- /dev/null +++ b/dist/modules/application/application/handlers/create-application.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"create-application.handler.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/handlers/create-application.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiE;AACjE,2FAA+E;AAC/E,8FAA0F;AAC1F,iFAA2E;AAGpE,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IACN;IAAwC;IAArE,YAA6B,OAAqB,EAAmB,SAA+B;QAAvE,YAAO,GAAP,OAAO,CAAc;QAAmB,cAAS,GAAT,SAAS,CAAsB;IAAG,CAAC;IAExG,KAAK,CAAC,OAAO,CAAC,OAAY;QACxB,MAAM,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAE7C,MAAM,GAAG,GAAG,oCAAe,CAAC,MAAM,CAAC;YACjC,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;YAC5C,YAAY,EAAE,OAAO,CAAC,YAAY;SACnC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC/C,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AAnBY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;qCAE2B,oCAAY,EAA8B,4CAAoB;GADzF,wBAAwB,CAmBpC"} \ No newline at end of file diff --git a/dist/modules/application/application/handlers/delete-application.handler.js b/dist/modules/application/application/handlers/delete-application.handler.js new file mode 100644 index 0000000..97a35cf --- /dev/null +++ b/dist/modules/application/application/handlers/delete-application.handler.js @@ -0,0 +1,31 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeleteApplicationHandler = void 0; +const common_1 = require("@nestjs/common"); +const application_interface_1 = require("../../domain/repositories/application.interface"); +let DeleteApplicationHandler = class DeleteApplicationHandler { + appRepo; + constructor(appRepo) { + this.appRepo = appRepo; + } + async execute(id) { + // ensure exists + await this.appRepo.findById(id); + await this.appRepo.delete(id); + } +}; +exports.DeleteApplicationHandler = DeleteApplicationHandler; +exports.DeleteApplicationHandler = DeleteApplicationHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [application_interface_1.IApplication]) +], DeleteApplicationHandler); +//# sourceMappingURL=delete-application.handler.js.map \ No newline at end of file diff --git a/dist/modules/application/application/handlers/delete-application.handler.js.map b/dist/modules/application/application/handlers/delete-application.handler.js.map new file mode 100644 index 0000000..7bb6d4d --- /dev/null +++ b/dist/modules/application/application/handlers/delete-application.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"delete-application.handler.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/handlers/delete-application.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2FAA+E;AAGxE,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IACN;IAA7B,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,gBAAgB;QAChB,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;CACF,CAAA;AARY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;qCAE2B,oCAAY;GADvC,wBAAwB,CAQpC"} \ No newline at end of file diff --git a/dist/modules/application/application/handlers/get-application.handler.js b/dist/modules/application/application/handlers/get-application.handler.js new file mode 100644 index 0000000..0021280 --- /dev/null +++ b/dist/modules/application/application/handlers/get-application.handler.js @@ -0,0 +1,30 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetApplicationHandler = void 0; +const common_1 = require("@nestjs/common"); +const application_interface_1 = require("../../domain/repositories/application.interface"); +let GetApplicationHandler = class GetApplicationHandler { + appRepo; + constructor(appRepo) { + this.appRepo = appRepo; + } + async execute(id) { + const app = await this.appRepo.findById(id); + return app; + } +}; +exports.GetApplicationHandler = GetApplicationHandler; +exports.GetApplicationHandler = GetApplicationHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [application_interface_1.IApplication]) +], GetApplicationHandler); +//# sourceMappingURL=get-application.handler.js.map \ No newline at end of file diff --git a/dist/modules/application/application/handlers/get-application.handler.js.map b/dist/modules/application/application/handlers/get-application.handler.js.map new file mode 100644 index 0000000..4c93749 --- /dev/null +++ b/dist/modules/application/application/handlers/get-application.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get-application.handler.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/handlers/get-application.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2FAA+E;AAGxE,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IACH;IAA7B,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC5C,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAA;AAPY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;qCAE2B,oCAAY;GADvC,qBAAqB,CAOjC"} \ No newline at end of file diff --git a/dist/modules/application/application/handlers/get-applications.handler.js b/dist/modules/application/application/handlers/get-applications.handler.js new file mode 100644 index 0000000..2fb1069 --- /dev/null +++ b/dist/modules/application/application/handlers/get-applications.handler.js @@ -0,0 +1,30 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetApplicationsHandler = void 0; +const common_1 = require("@nestjs/common"); +const application_interface_1 = require("../../domain/repositories/application.interface"); +let GetApplicationsHandler = class GetApplicationsHandler { + appRepo; + constructor(appRepo) { + this.appRepo = appRepo; + } + async execute(query) { + const res = await this.appRepo.find({ page: query.page, limit: query.limit, search: query.search || null }); + return res; + } +}; +exports.GetApplicationsHandler = GetApplicationsHandler; +exports.GetApplicationsHandler = GetApplicationsHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [application_interface_1.IApplication]) +], GetApplicationsHandler); +//# sourceMappingURL=get-applications.handler.js.map \ No newline at end of file diff --git a/dist/modules/application/application/handlers/get-applications.handler.js.map b/dist/modules/application/application/handlers/get-applications.handler.js.map new file mode 100644 index 0000000..5d78953 --- /dev/null +++ b/dist/modules/application/application/handlers/get-applications.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get-applications.handler.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/handlers/get-applications.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2FAA+E;AAGxE,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IACJ;IAA7B,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,KAAyD;QACrE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QAC5G,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAA;AAPY,wDAAsB;iCAAtB,sBAAsB;IADlC,IAAA,mBAAU,GAAE;qCAE2B,oCAAY;GADvC,sBAAsB,CAOlC"} \ No newline at end of file diff --git a/dist/modules/application/application/handlers/get-me-applications.handler.js b/dist/modules/application/application/handlers/get-me-applications.handler.js new file mode 100644 index 0000000..db284f1 --- /dev/null +++ b/dist/modules/application/application/handlers/get-me-applications.handler.js @@ -0,0 +1,35 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetMeApplicationsHandler = void 0; +const common_1 = require("@nestjs/common"); +const application_interface_1 = require("../../domain/repositories/application.interface"); +let GetMeApplicationsHandler = class GetMeApplicationsHandler { + appRepo; + constructor(appRepo) { + this.appRepo = appRepo; + } + async execute(applicationsClaimList) { + if (!applicationsClaimList || applicationsClaimList.length === 0) + return { data: [], total: 0 }; + // Only return active applications whose applicationsClaim exists in provided list + const res = await this.appRepo.find({ page: 1, limit: 1000, isActive: true, applicationsClaimIn: applicationsClaimList }); + // sort by displayOrder asc + res.data.sort((a, b) => (a.displayOrder ?? 0) - (b.displayOrder ?? 0)); + return res; + } +}; +exports.GetMeApplicationsHandler = GetMeApplicationsHandler; +exports.GetMeApplicationsHandler = GetMeApplicationsHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [application_interface_1.IApplication]) +], GetMeApplicationsHandler); +//# sourceMappingURL=get-me-applications.handler.js.map \ No newline at end of file diff --git a/dist/modules/application/application/handlers/get-me-applications.handler.js.map b/dist/modules/application/application/handlers/get-me-applications.handler.js.map new file mode 100644 index 0000000..7983311 --- /dev/null +++ b/dist/modules/application/application/handlers/get-me-applications.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get-me-applications.handler.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/handlers/get-me-applications.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2FAA+E;AAGxE,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IACN;IAA7B,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,qBAAkD;QAC9D,IAAI,CAAC,qBAAqB,IAAI,qBAAqB,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAEhG,kFAAkF;QAClF,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,CAAC,CAAC;QAC1H,2BAA2B;QAC3B,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC;QACvE,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAA;AAZY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;qCAE2B,oCAAY;GADvC,wBAAwB,CAYpC"} \ No newline at end of file diff --git a/dist/modules/application/application/handlers/update-application.handler.js b/dist/modules/application/application/handlers/update-application.handler.js new file mode 100644 index 0000000..242bd8e --- /dev/null +++ b/dist/modules/application/application/handlers/update-application.handler.js @@ -0,0 +1,38 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UpdateApplicationHandler = void 0; +const common_1 = require("@nestjs/common"); +const application_interface_1 = require("../../domain/repositories/application.interface"); +const application_validator_1 = require("../../application/validators/application.validator"); +let UpdateApplicationHandler = class UpdateApplicationHandler { + appRepo; + validator; + constructor(appRepo, validator) { + this.appRepo = appRepo; + this.validator = validator; + } + async execute(id, payload) { + const existing = await this.appRepo.findById(id); + if (!existing) + throw new Error('Application not found'); + await this.validator.validateUpdate(id, payload); + existing.update(payload); + const updated = await this.appRepo.update(existing); + return updated; + } +}; +exports.UpdateApplicationHandler = UpdateApplicationHandler; +exports.UpdateApplicationHandler = UpdateApplicationHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [application_interface_1.IApplication, application_validator_1.ApplicationValidator]) +], UpdateApplicationHandler); +//# sourceMappingURL=update-application.handler.js.map \ No newline at end of file diff --git a/dist/modules/application/application/handlers/update-application.handler.js.map b/dist/modules/application/application/handlers/update-application.handler.js.map new file mode 100644 index 0000000..83a74d2 --- /dev/null +++ b/dist/modules/application/application/handlers/update-application.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"update-application.handler.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/handlers/update-application.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2FAA+E;AAC/E,8FAA0F;AAGnF,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IACN;IAAwC;IAArE,YAA6B,OAAqB,EAAmB,SAA+B;QAAvE,YAAO,GAAP,OAAO,CAAc;QAAmB,cAAS,GAAT,SAAS,CAAsB;IAAG,CAAC;IAExG,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,OAAY;QACpC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAExD,MAAM,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAEjD,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACpD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AAbY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;qCAE2B,oCAAY,EAA8B,4CAAoB;GADzF,wBAAwB,CAapC"} \ No newline at end of file diff --git a/dist/modules/application/application/validators/application.validator.js b/dist/modules/application/application/validators/application.validator.js new file mode 100644 index 0000000..dd91f08 --- /dev/null +++ b/dist/modules/application/application/validators/application.validator.js @@ -0,0 +1,84 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ApplicationValidator = void 0; +const common_1 = require("@nestjs/common"); +const application_interface_1 = require("../../domain/repositories/application.interface"); +let ApplicationValidator = class ApplicationValidator { + appRepo; + constructor(appRepo) { + this.appRepo = appRepo; + } + async validateCreate(payload) { + if (!payload || !payload.code) + throw new common_1.BadRequestException('code is required'); + if (!payload.name) + throw new common_1.BadRequestException('name is required'); + if (!payload.applicationsClaim) + throw new common_1.BadRequestException('applicationsClaim is required'); + // url validation if provided + if (payload.url) { + try { + // allow relative urls + if (!payload.url.startsWith('/') && !payload.url.startsWith('http')) { + throw new Error('invalid'); + } + // new URL(payload.url) // avoid throwing for relative + } + catch (e) { + throw new common_1.BadRequestException('invalid url'); + } + } + // displayOrder + if (payload.displayOrder !== undefined && typeof payload.displayOrder !== 'number') { + throw new common_1.BadRequestException('invalid displayOrder'); + } + // unique code + const byCode = await this.appRepo.findByCode(payload.code); + if (byCode) + throw new common_1.BadRequestException('duplicate code'); + const byClaim = await this.appRepo.findByApplicationsClaim(payload.applicationsClaim); + if (byClaim) + throw new common_1.BadRequestException('duplicate applicationsClaim'); + } + async validateUpdate(id, payload) { + if (!payload) + return; + if (payload.url) { + try { + if (!payload.url.startsWith('/') && !payload.url.startsWith('http')) + throw new Error('invalid'); + } + catch (e) { + throw new common_1.BadRequestException('invalid url'); + } + } + if (payload.displayOrder !== undefined && typeof payload.displayOrder !== 'number') { + throw new common_1.BadRequestException('invalid displayOrder'); + } + if (payload.code) { + const existing = await this.appRepo.findByCode(payload.code); + if (existing && existing.id !== id) + throw new common_1.BadRequestException('duplicate code'); + } + if (payload.applicationsClaim) { + const existing = await this.appRepo.findByApplicationsClaim(payload.applicationsClaim); + if (existing && existing.id !== id) + throw new common_1.BadRequestException('duplicate applicationsClaim'); + } + } +}; +exports.ApplicationValidator = ApplicationValidator; +exports.ApplicationValidator = ApplicationValidator = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [application_interface_1.IApplication]) +], ApplicationValidator); +//# sourceMappingURL=application.validator.js.map \ No newline at end of file diff --git a/dist/modules/application/application/validators/application.validator.js.map b/dist/modules/application/application/validators/application.validator.js.map new file mode 100644 index 0000000..7f4f045 --- /dev/null +++ b/dist/modules/application/application/validators/application.validator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"application.validator.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/validators/application.validator.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/application/domain/entities/application.entity.js b/dist/modules/application/domain/entities/application.entity.js new file mode 100644 index 0000000..6319c92 --- /dev/null +++ b/dist/modules/application/domain/entities/application.entity.js @@ -0,0 +1,67 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ApplicationData = void 0; +class ApplicationData { + id; + code; + name; + description; + icon; + url; + applicationsClaim; + isActive; + displayOrder; + createdAt; + updatedAt; + constructor(id, code, name, description, icon, url, applicationsClaim, isActive, displayOrder, createdAt, updatedAt) { + this.id = id; + this.code = code; + this.name = name; + this.description = description; + this.icon = icon; + this.url = url; + this.applicationsClaim = applicationsClaim; + this.isActive = isActive; + this.displayOrder = displayOrder; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } + static create(data) { + return new ApplicationData(crypto.randomUUID(), data.code, data.name, data.description ?? null, data.icon ?? null, data.url ?? null, data.applicationsClaim, true, data.displayOrder ?? 0, new Date(), new Date()); + } + static restore(props) { + return new ApplicationData(props.id, props.code, props.name, props.description ?? null, props.icon ?? null, props.url ?? null, props.applicationsClaim, props.isActive !== undefined ? props.isActive : true, props.displayOrder ?? 0, props.createdAt ?? null, props.updatedAt ?? null); + } + update(data) { + if (data.code !== undefined) + this.code = data.code; + if (data.name !== undefined) + this.name = data.name; + if (data.description !== undefined) + this.description = data.description; + if (data.icon !== undefined) + this.icon = data.icon; + if (data.url !== undefined) + this.url = data.url; + if (data.applicationsClaim !== undefined) + this.applicationsClaim = data.applicationsClaim; + if (data.isActive !== undefined) + this.isActive = data.isActive; + if (data.displayOrder !== undefined) + this.displayOrder = data.displayOrder; + this.updatedAt = new Date(); + } + toResponse() { + return { + id: this.id, + code: this.code, + name: this.name, + description: this.description, + icon: this.icon, + url: this.url, + displayOrder: this.displayOrder, + }; + } +} +exports.ApplicationData = ApplicationData; +//# sourceMappingURL=application.entity.js.map \ No newline at end of file diff --git a/dist/modules/application/domain/entities/application.entity.js.map b/dist/modules/application/domain/entities/application.entity.js.map new file mode 100644 index 0000000..fb200f8 --- /dev/null +++ b/dist/modules/application/domain/entities/application.entity.js.map @@ -0,0 +1 @@ +{"version":3,"file":"application.entity.js","sourceRoot":"","sources":["../../../../../src/modules/application/domain/entities/application.entity.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/application/domain/repositories/application.interface.js b/dist/modules/application/domain/repositories/application.interface.js new file mode 100644 index 0000000..a6bf53a --- /dev/null +++ b/dist/modules/application/domain/repositories/application.interface.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.IApplication = void 0; +class IApplication { +} +exports.IApplication = IApplication; +//# sourceMappingURL=application.interface.js.map \ No newline at end of file diff --git a/dist/modules/application/domain/repositories/application.interface.js.map b/dist/modules/application/domain/repositories/application.interface.js.map new file mode 100644 index 0000000..906e160 --- /dev/null +++ b/dist/modules/application/domain/repositories/application.interface.js.map @@ -0,0 +1 @@ +{"version":3,"file":"application.interface.js","sourceRoot":"","sources":["../../../../../src/modules/application/domain/repositories/application.interface.ts"],"names":[],"mappings":";;;AAEA,MAAsB,YAAY;CASjC;AATD,oCASC"} \ No newline at end of file diff --git a/dist/modules/application/infrastructure/mappers/prisma-application.mapper.js b/dist/modules/application/infrastructure/mappers/prisma-application.mapper.js new file mode 100644 index 0000000..dd8942e --- /dev/null +++ b/dist/modules/application/infrastructure/mappers/prisma-application.mapper.js @@ -0,0 +1,23 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrismaApplicationMapper = void 0; +const application_entity_1 = require("../../domain/entities/application.entity"); +class PrismaApplicationMapper { + static toDomain(model) { + return application_entity_1.ApplicationData.restore({ + id: model.id, + code: model.code, + name: model.name, + description: model.description, + icon: model.icon, + url: model.url, + applicationsClaim: model.applicationsClaim, + isActive: model.isActive, + displayOrder: model.displayOrder, + createdAt: model.createdAt, + updatedAt: model.updatedAt, + }); + } +} +exports.PrismaApplicationMapper = PrismaApplicationMapper; +//# sourceMappingURL=prisma-application.mapper.js.map \ No newline at end of file diff --git a/dist/modules/application/infrastructure/mappers/prisma-application.mapper.js.map b/dist/modules/application/infrastructure/mappers/prisma-application.mapper.js.map new file mode 100644 index 0000000..4c74586 --- /dev/null +++ b/dist/modules/application/infrastructure/mappers/prisma-application.mapper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prisma-application.mapper.js","sourceRoot":"","sources":["../../../../../src/modules/application/infrastructure/mappers/prisma-application.mapper.ts"],"names":[],"mappings":";;;AAAA,iFAA2E;AAE3E,MAAa,uBAAuB;IAClC,MAAM,CAAC,QAAQ,CAAC,KAAU;QACxB,OAAO,oCAAe,CAAC,OAAO,CAAC;YAC7B,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;YAC1C,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B,CAAC,CAAC;IACL,CAAC;CACF;AAhBD,0DAgBC"} \ No newline at end of file diff --git a/dist/modules/application/infrastructure/repositories/prisma-application.repository.js b/dist/modules/application/infrastructure/repositories/prisma-application.repository.js new file mode 100644 index 0000000..50a273b --- /dev/null +++ b/dist/modules/application/infrastructure/repositories/prisma-application.repository.js @@ -0,0 +1,98 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrismaApplicationRepository = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../../../shared/prisma.service"); +const prisma_application_mapper_1 = require("../mappers/prisma-application.mapper"); +let PrismaApplicationRepository = class PrismaApplicationRepository { + prisma; + constructor(prisma) { + this.prisma = prisma; + } + async find(params) { + const page = params.page && params.page > 0 ? params.page : 1; + const limit = params.limit && params.limit > 0 ? params.limit : 25; + const where = {}; + if (params.search) { + where.OR = [ + { name: { contains: params.search, mode: 'insensitive' } }, + { code: { contains: params.search, mode: 'insensitive' } }, + { description: { contains: params.search, mode: 'insensitive' } }, + ]; + } + if (params.isActive !== undefined && params.isActive !== null) { + where.isActive = params.isActive; + } + if (params.applicationsClaimIn && params.applicationsClaimIn.length > 0) { + where.applicationsClaim = { in: params.applicationsClaimIn }; + } + const [total, items] = await Promise.all([ + this.prisma.application.count({ where }), + this.prisma.application.findMany({ where, skip: (page - 1) * limit, take: limit, orderBy: { displayOrder: 'asc', createdAt: 'asc' } }), + ]); + return { data: items.map(i => prisma_application_mapper_1.PrismaApplicationMapper.toDomain(i)), total }; + } + async findById(id) { + const row = await this.prisma.application.findUnique({ where: { id } }); + if (!row) + throw new Error('Application not found'); + return prisma_application_mapper_1.PrismaApplicationMapper.toDomain(row); + } + async findByCode(code) { + const row = await this.prisma.application.findUnique({ where: { code } }); + if (!row) + return null; + return prisma_application_mapper_1.PrismaApplicationMapper.toDomain(row); + } + async findByApplicationsClaim(claim) { + const row = await this.prisma.application.findUnique({ where: { applicationsClaim: claim } }); + if (!row) + return null; + return prisma_application_mapper_1.PrismaApplicationMapper.toDomain(row); + } + async create(app) { + const created = await this.prisma.application.create({ data: { + id: app.id, + code: app.code, + name: app.name, + description: app.description, + icon: app.icon, + url: app.url, + applicationsClaim: app.applicationsClaim, + isActive: app.isActive, + displayOrder: app.displayOrder, + } }); + return prisma_application_mapper_1.PrismaApplicationMapper.toDomain(created); + } + async update(app) { + const updated = await this.prisma.application.update({ where: { id: app.id }, data: { + code: app.code, + name: app.name, + description: app.description, + icon: app.icon, + url: app.url, + applicationsClaim: app.applicationsClaim, + isActive: app.isActive, + displayOrder: app.displayOrder, + } }); + return prisma_application_mapper_1.PrismaApplicationMapper.toDomain(updated); + } + async delete(appId) { + await this.prisma.application.delete({ where: { id: appId } }); + } +}; +exports.PrismaApplicationRepository = PrismaApplicationRepository; +exports.PrismaApplicationRepository = PrismaApplicationRepository = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], PrismaApplicationRepository); +//# sourceMappingURL=prisma-application.repository.js.map \ No newline at end of file diff --git a/dist/modules/application/infrastructure/repositories/prisma-application.repository.js.map b/dist/modules/application/infrastructure/repositories/prisma-application.repository.js.map new file mode 100644 index 0000000..87825ed --- /dev/null +++ b/dist/modules/application/infrastructure/repositories/prisma-application.repository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prisma-application.repository.js","sourceRoot":"","sources":["../../../../../src/modules/application/infrastructure/repositories/prisma-application.repository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/application/presentation/controllers/applications.controller.js b/dist/modules/application/presentation/controllers/applications.controller.js new file mode 100644 index 0000000..92d3eb3 --- /dev/null +++ b/dist/modules/application/presentation/controllers/applications.controller.js @@ -0,0 +1,136 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ApplicationsController = void 0; +const common_1 = require("@nestjs/common"); +const swagger_1 = require("@nestjs/swagger"); +const jwt_auth_guard_1 = require("../../../../core/auth/guards/jwt-auth.guard"); +const current_user_guard_1 = require("../../../identity/presentation/guards/current-user.guard"); +const get_applications_handler_1 = require("../../application/handlers/get-applications.handler"); +const get_application_handler_1 = require("../../application/handlers/get-application.handler"); +const create_application_handler_1 = require("../../application/handlers/create-application.handler"); +const update_application_handler_1 = require("../../application/handlers/update-application.handler"); +const delete_application_handler_1 = require("../../application/handlers/delete-application.handler"); +const get_me_applications_handler_1 = require("../../application/handlers/get-me-applications.handler"); +let ApplicationsController = class ApplicationsController { + getApplicationsHandler; + getApplicationHandler; + createApplicationHandler; + updateApplicationHandler; + deleteApplicationHandler; + getMeApplicationsHandler; + constructor(getApplicationsHandler, getApplicationHandler, createApplicationHandler, updateApplicationHandler, deleteApplicationHandler, getMeApplicationsHandler) { + this.getApplicationsHandler = getApplicationsHandler; + this.getApplicationHandler = getApplicationHandler; + this.createApplicationHandler = createApplicationHandler; + this.updateApplicationHandler = updateApplicationHandler; + this.deleteApplicationHandler = deleteApplicationHandler; + this.getMeApplicationsHandler = getMeApplicationsHandler; + } + async findAll(query) { + const res = await this.getApplicationsHandler.execute({ page: query.page, limit: query.limit, search: query.search }); + return { success: true, data: res.data.map(a => a.toResponse ? a.toResponse() : a), meta: { total: res.total } }; + } + async findOne(id) { + const app = await this.getApplicationHandler.execute(id); + return { success: true, data: app.toResponse(), meta: {} }; + } + async create(body) { + const created = await this.createApplicationHandler.execute(body); + return { success: true, data: created.toResponse(), meta: {} }; + } + async update(id, body) { + const updated = await this.updateApplicationHandler.execute(id, body); + return { success: true, data: updated.toResponse(), meta: {} }; + } + async remove(id) { + await this.deleteApplicationHandler.execute(id); + return { success: true, data: null, meta: {} }; + } + // Dashboard endpoint + async me(req) { + const ctx = req.raylabContext; + const identity = ctx && ctx.identity ? ctx.identity : {}; + const applicationsClaimList = identity.applications || []; + const res = await this.getMeApplicationsHandler.execute(applicationsClaimList); + return { success: true, data: res.data.map(a => a.toResponse ? a.toResponse() : a), meta: { total: res.total } }; + } +}; +exports.ApplicationsController = ApplicationsController; +__decorate([ + (0, common_1.Get)('applications'), + (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard), + (0, swagger_1.ApiOperation)({ summary: 'List applications' }), + __param(0, (0, common_1.Query)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], ApplicationsController.prototype, "findAll", null); +__decorate([ + (0, common_1.Get)('applications/:id'), + (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard), + (0, swagger_1.ApiOperation)({ summary: 'Get application by id' }), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], ApplicationsController.prototype, "findOne", null); +__decorate([ + (0, common_1.Post)('applications'), + (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard), + (0, swagger_1.ApiOperation)({ summary: 'Create application' }), + __param(0, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], ApplicationsController.prototype, "create", null); +__decorate([ + (0, common_1.Patch)('applications/:id'), + (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard), + (0, swagger_1.ApiOperation)({ summary: 'Update application' }), + __param(0, (0, common_1.Param)('id')), + __param(1, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object]), + __metadata("design:returntype", Promise) +], ApplicationsController.prototype, "update", null); +__decorate([ + (0, common_1.Delete)('applications/:id'), + (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard), + (0, swagger_1.ApiOperation)({ summary: 'Delete application' }), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], ApplicationsController.prototype, "remove", null); +__decorate([ + (0, common_1.Get)('me/applications'), + (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard), + (0, swagger_1.ApiOperation)({ summary: "Get current user's applications (filtered by Authentik claims)" }), + __param(0, (0, common_1.Req)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], ApplicationsController.prototype, "me", null); +exports.ApplicationsController = ApplicationsController = __decorate([ + (0, swagger_1.ApiTags)('Applications'), + (0, common_1.Controller)(), + __metadata("design:paramtypes", [get_applications_handler_1.GetApplicationsHandler, + get_application_handler_1.GetApplicationHandler, + create_application_handler_1.CreateApplicationHandler, + update_application_handler_1.UpdateApplicationHandler, + delete_application_handler_1.DeleteApplicationHandler, + get_me_applications_handler_1.GetMeApplicationsHandler]) +], ApplicationsController); +//# sourceMappingURL=applications.controller.js.map \ No newline at end of file diff --git a/dist/modules/application/presentation/controllers/applications.controller.js.map b/dist/modules/application/presentation/controllers/applications.controller.js.map new file mode 100644 index 0000000..29a4e4a --- /dev/null +++ b/dist/modules/application/presentation/controllers/applications.controller.js.map @@ -0,0 +1 @@ +{"version":3,"file":"applications.controller.js","sourceRoot":"","sources":["../../../../../src/modules/application/presentation/controllers/applications.controller.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/audit/audit.event-handler.js b/dist/modules/audit/audit.event-handler.js new file mode 100644 index 0000000..ad7e864 --- /dev/null +++ b/dist/modules/audit/audit.event-handler.js @@ -0,0 +1,65 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AuditEventHandler_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuditEventHandler = void 0; +const common_1 = require("@nestjs/common"); +const event_bus_service_1 = require("../../core/event-bus/event-bus.service"); +const audit_service_1 = require("./audit.service"); +let AuditEventHandler = AuditEventHandler_1 = class AuditEventHandler { + events; + auditService; + logger = new common_1.Logger(AuditEventHandler_1.name); + constructor(events, auditService) { + this.events = events; + this.auditService = auditService; + this.events.subscribe('RolesSynchronized', (e) => this.handleRolesSynchronized(e)); + this.events.subscribe('UserAuthenticated', (e) => this.handleUserAuthenticated(e)); + this.events.subscribe('RoleCreated', (e) => this.handleGeneric(e)); + this.events.subscribe('RoleUpdated', (e) => this.handleGeneric(e)); + this.events.subscribe('RoleDeleted', (e) => this.handleGeneric(e)); + this.events.subscribe('PermissionCreated', (e) => this.handleGeneric(e)); + this.events.subscribe('PermissionUpdated', (e) => this.handleGeneric(e)); + this.events.subscribe('PermissionDeleted', (e) => this.handleGeneric(e)); + this.events.subscribe('UserActivated', (e) => this.handleGeneric(e)); + this.events.subscribe('UserDeactivated', (e) => this.handleGeneric(e)); + } + async handleRolesSynchronized(event) { + try { + await this.auditService.createFromEvent(event); + } + catch (e) { + this.logger.error('Audit handler failed', e); + } + } + async handleUserAuthenticated(event) { + try { + await this.auditService.createFromEvent(event); + } + catch (e) { + this.logger.error('Audit handler failed', e); + } + } + async handleGeneric(event) { + try { + await this.auditService.createFromEvent(event); + } + catch (e) { + this.logger.error('Audit handler failed', e); + } + } +}; +exports.AuditEventHandler = AuditEventHandler; +exports.AuditEventHandler = AuditEventHandler = AuditEventHandler_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [event_bus_service_1.EventBus, audit_service_1.AuditService]) +], AuditEventHandler); +//# sourceMappingURL=audit.event-handler.js.map \ No newline at end of file diff --git a/dist/modules/audit/audit.event-handler.js.map b/dist/modules/audit/audit.event-handler.js.map new file mode 100644 index 0000000..e0434a4 --- /dev/null +++ b/dist/modules/audit/audit.event-handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"audit.event-handler.js","sourceRoot":"","sources":["../../../src/modules/audit/audit.event-handler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/audit/audit.module.js b/dist/modules/audit/audit.module.js new file mode 100644 index 0000000..efa72d9 --- /dev/null +++ b/dist/modules/audit/audit.module.js @@ -0,0 +1,24 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuditModule = void 0; +const common_1 = require("@nestjs/common"); +const audit_service_1 = require("./audit.service"); +const audit_event_handler_1 = require("./audit.event-handler"); +const prisma_service_1 = require("../../shared/prisma.service"); +const event_bus_service_1 = require("../../core/event-bus/event-bus.service"); +let AuditModule = class AuditModule { +}; +exports.AuditModule = AuditModule; +exports.AuditModule = AuditModule = __decorate([ + (0, common_1.Module)({ + providers: [audit_service_1.AuditService, audit_event_handler_1.AuditEventHandler, prisma_service_1.PrismaService, event_bus_service_1.EventBus], + exports: [audit_service_1.AuditService], + }) +], AuditModule); +//# sourceMappingURL=audit.module.js.map \ No newline at end of file diff --git a/dist/modules/audit/audit.module.js.map b/dist/modules/audit/audit.module.js.map new file mode 100644 index 0000000..e4509ad --- /dev/null +++ b/dist/modules/audit/audit.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"audit.module.js","sourceRoot":"","sources":["../../../src/modules/audit/audit.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,mDAA+C;AAC/C,+DAA0D;AAC1D,gEAA4D;AAC5D,8EAAkE;AAM3D,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAG,CAAA;AAAd,kCAAW;sBAAX,WAAW;IAJvB,IAAA,eAAM,EAAC;QACN,SAAS,EAAE,CAAC,4BAAY,EAAE,uCAAiB,EAAE,8BAAa,EAAE,4BAAQ,CAAC;QACrE,OAAO,EAAE,CAAC,4BAAY,CAAC;KACxB,CAAC;GACW,WAAW,CAAG"} \ No newline at end of file diff --git a/dist/modules/audit/audit.service.js b/dist/modules/audit/audit.service.js new file mode 100644 index 0000000..fbe5659 --- /dev/null +++ b/dist/modules/audit/audit.service.js @@ -0,0 +1,46 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AuditService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuditService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../shared/prisma.service"); +let AuditService = AuditService_1 = class AuditService { + prisma; + logger = new common_1.Logger(AuditService_1.name); + constructor(prisma) { + this.prisma = prisma; + } + async createFromEvent(event) { + try { + // Map standard events to AuditLog entries + await this.prisma.auditLog.create({ + data: { + userId: event.payload?.userId || null, + action: event.type, + resource: event.payload?.resource || null, + resourceId: event.payload?.resourceId || null, + details: event.payload, + createdAt: new Date(event.timestamp), + }, + }); + } + catch (e) { + this.logger.error('Failed to write audit log', e); + } + } +}; +exports.AuditService = AuditService; +exports.AuditService = AuditService = AuditService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], AuditService); +//# sourceMappingURL=audit.service.js.map \ No newline at end of file diff --git a/dist/modules/audit/audit.service.js.map b/dist/modules/audit/audit.service.js.map new file mode 100644 index 0000000..3ee3df5 --- /dev/null +++ b/dist/modules/audit/audit.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"audit.service.js","sourceRoot":"","sources":["../../../src/modules/audit/audit.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAoD;AACpD,gEAA4D;AAIrD,IAAM,YAAY,oBAAlB,MAAM,YAAY;IAGM;IAFZ,MAAM,GAAG,IAAI,eAAM,CAAC,cAAY,CAAC,IAAI,CAAC,CAAC;IAExD,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,KAAK,CAAC,eAAe,CAAC,KAAyB;QAC7C,IAAI,CAAC;YACH,0CAA0C;YAC1C,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAChC,IAAI,EAAE;oBACJ,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,IAAI,IAAI;oBACrC,MAAM,EAAE,KAAK,CAAC,IAAI;oBAClB,QAAQ,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,IAAI,IAAI;oBACzC,UAAU,EAAE,KAAK,CAAC,OAAO,EAAE,UAAU,IAAI,IAAI;oBAC7C,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,SAAS,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;iBACrC;aACF,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,CAAQ,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;CACF,CAAA;AAtBY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;qCAI0B,8BAAa;GAHvC,YAAY,CAsBxB"} \ No newline at end of file diff --git a/dist/modules/auth/auth.controller.js b/dist/modules/auth/auth.controller.js new file mode 100644 index 0000000..3052f6a --- /dev/null +++ b/dist/modules/auth/auth.controller.js @@ -0,0 +1,110 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthController = void 0; +const common_1 = require("@nestjs/common"); +const swagger_1 = require("@nestjs/swagger"); +const auth_service_1 = require("./auth.service"); +let AuthController = class AuthController { + authService; + constructor(authService) { + this.authService = authService; + } + async login(returnTo, res) { + const redirect = await this.authService.createAuthorizationRedirect(returnTo); + return res.redirect(302, redirect); + } + async callback(code, state, res) { + const result = await this.authService.handleCallback(code, state); + // set cookies + const cookieOptions = { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + path: '/', + }; + // access token cookie (internal JWT) + res.cookie('raylab_jwt', result.accessToken, { ...cookieOptions, maxAge: result.expiresIn * 1000 }); + // refresh token cookie + res.cookie('raylab_refresh', result.refreshToken, { ...cookieOptions, maxAge: result.refreshTtl * 1000 }); + return res.redirect(302, result.returnTo || '/'); + } + async logout(req, res) { + const refreshToken = req.cookies?.raylab_refresh || req.body?.refreshToken; + const redirect = await this.authService.logout(refreshToken); + return res.redirect(302, redirect); + } + async refresh(req) { + const refreshToken = req.cookies?.raylab_refresh || req.body?.refreshToken; + const result = await this.authService.refresh(refreshToken); + return { success: true, data: result }; + } + async me(req) { + const token = (req.cookies?.raylab_jwt) || (req.headers.authorization && req.headers.authorization.replace(/^Bearer\s+/i, '')); + const user = await this.authService.me(token); + return { success: true, data: user }; + } +}; +exports.AuthController = AuthController; +__decorate([ + (0, common_1.Get)('login'), + (0, swagger_1.ApiOperation)({ summary: 'Start Authorization Code + PKCE login (redirect to Identity Provider)' }), + __param(0, (0, common_1.Query)('returnTo')), + __param(1, (0, common_1.Res)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object, Object]), + __metadata("design:returntype", Promise) +], AuthController.prototype, "login", null); +__decorate([ + (0, common_1.Get)('callback'), + (0, swagger_1.ApiOperation)({ summary: 'OIDC callback endpoint' }), + __param(0, (0, common_1.Query)('code')), + __param(1, (0, common_1.Query)('state')), + __param(2, (0, common_1.Res)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, Object]), + __metadata("design:returntype", Promise) +], AuthController.prototype, "callback", null); +__decorate([ + (0, common_1.Post)('logout'), + (0, common_1.HttpCode)(common_1.HttpStatus.OK), + (0, swagger_1.ApiOperation)({ summary: 'Logout (invalidate internal session and redirect to identity provider logout)' }), + __param(0, (0, common_1.Req)()), + __param(1, (0, common_1.Res)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object, Object]), + __metadata("design:returntype", Promise) +], AuthController.prototype, "logout", null); +__decorate([ + (0, common_1.Post)('refresh'), + (0, swagger_1.ApiOperation)({ summary: 'Refresh internal JWT using internal refresh token' }), + __param(0, (0, common_1.Req)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], AuthController.prototype, "refresh", null); +__decorate([ + (0, common_1.Get)('me'), + (0, swagger_1.ApiOperation)({ summary: 'Get current user from internal JWT (cookie or Authorization header)' }), + __param(0, (0, common_1.Req)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], AuthController.prototype, "me", null); +exports.AuthController = AuthController = __decorate([ + (0, swagger_1.ApiTags)('Auth'), + (0, common_1.Controller)('auth'), + __metadata("design:paramtypes", [auth_service_1.AuthService]) +], AuthController); +//# sourceMappingURL=auth.controller.js.map \ No newline at end of file diff --git a/dist/modules/auth/auth.controller.js.map b/dist/modules/auth/auth.controller.js.map new file mode 100644 index 0000000..33fa0bb --- /dev/null +++ b/dist/modules/auth/auth.controller.js.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.controller.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.controller.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/auth/auth.module.js b/dist/modules/auth/auth.module.js new file mode 100644 index 0000000..a07a073 --- /dev/null +++ b/dist/modules/auth/auth.module.js @@ -0,0 +1,75 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthModule = void 0; +const common_1 = require("@nestjs/common"); +const auth_controller_1 = require("./auth.controller"); +const auth_service_1 = require("./auth.service"); +const config_1 = require("@nestjs/config"); +const jwt_1 = require("@nestjs/jwt"); +const prisma_service_1 = require("../../shared/prisma.service"); +const user_interface_1 = require("../identity/domain/repositories/user.interface"); +const prisma_user_repository_1 = require("../identity/infrastructure/repositories/prisma-user.repository"); +const sync_identity_handler_1 = require("../identity/application/handlers/user/sync-identity.handler"); +const role_interface_1 = require("../identity/domain/repositories/role.interface"); +const prisma_role_repository_1 = require("../identity/infrastructure/repositories/prisma-role.repository"); +const i_auth_config_1 = require("../identity/application/config/i-auth-config"); +const env_auth_config_1 = require("../identity/application/config/env-auth-config"); +const redis_pkce_store_1 = require("./pkce/redis-pkce.store"); +const inmemory_refresh_store_1 = require("./refresh/inmemory-refresh.store"); +const oidc_service_1 = require("./oidc.service"); +const role_sync_service_1 = require("./role-sync.service"); +const redis_service_1 = require("../../shared/redis.service"); +const group_hash_service_1 = require("./group-hash.service"); +const authentication_service_1 = require("./authentication.service"); +const event_bus_service_1 = require("../../core/event-bus/event-bus.service"); +const audit_service_1 = require("../audit/audit.service"); +const authorization_module_1 = require("../authorization/authorization.module"); +let AuthModule = class AuthModule { +}; +exports.AuthModule = AuthModule; +exports.AuthModule = AuthModule = __decorate([ + (0, common_1.Module)({ + imports: [ + config_1.ConfigModule, + authorization_module_1.AuthorizationModule, + jwt_1.JwtModule.registerAsync({ + imports: [config_1.ConfigModule], + useFactory: async (config) => ({ + secret: config.get('RAYLAB_JWT_SECRET') || 'raylab-secret', + signOptions: { algorithm: 'HS256' }, + }), + inject: [config_1.ConfigService], + }), + ], + controllers: [auth_controller_1.AuthController], + providers: [ + auth_service_1.AuthService, + prisma_service_1.PrismaService, + prisma_user_repository_1.PrismaUserRepository, + prisma_role_repository_1.PrismaRoleRepository, + sync_identity_handler_1.SyncIdentityHandler, + { provide: user_interface_1.IUser, useClass: prisma_user_repository_1.PrismaUserRepository }, + { provide: role_interface_1.IRole, useClass: prisma_role_repository_1.PrismaRoleRepository }, + { provide: i_auth_config_1.IAuthConfig, useClass: env_auth_config_1.EnvAuthConfig }, + // In-memory PKCE and Refresh stores (Redis removed) + redis_pkce_store_1.RedisPkceStore, + inmemory_refresh_store_1.InMemoryRefreshStore, + // OIDC & Role Sync + oidc_service_1.OidcService, + role_sync_service_1.RoleSyncService, + group_hash_service_1.GroupHashService, + authentication_service_1.AuthenticationService, + event_bus_service_1.EventBus, + audit_service_1.AuditService, + redis_service_1.RedisService, + ], + exports: [auth_service_1.AuthService, oidc_service_1.OidcService, role_sync_service_1.RoleSyncService, authentication_service_1.AuthenticationService, event_bus_service_1.EventBus], + }) +], AuthModule); +//# sourceMappingURL=auth.module.js.map \ No newline at end of file diff --git a/dist/modules/auth/auth.module.js.map b/dist/modules/auth/auth.module.js.map new file mode 100644 index 0000000..4cdac68 --- /dev/null +++ b/dist/modules/auth/auth.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.module.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,uDAAmD;AACnD,iDAA6C;AAC7C,2CAA6D;AAC7D,qCAAwC;AACxC,gEAA4D;AAC5D,mFAAuE;AACvE,2GAAsG;AACtG,uGAAkG;AAClG,mFAAuE;AACvE,2GAAsG;AACtG,gFAA2E;AAC3E,oFAA+E;AAC/E,8DAAyD;AACzD,6EAAwE;AACxE,iDAA6C;AAC7C,2DAAsD;AACtD,8DAA0D;AAC1D,6DAAwD;AACxD,qEAAiE;AACjE,8EAAkE;AAClE,0DAAsD;AACtD,gFAA4E;AA0CrE,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IAvCtB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY;YACZ,0CAAmB;YACnB,eAAS,CAAC,aAAa,CAAC;gBACtB,OAAO,EAAE,CAAC,qBAAY,CAAC;gBACvB,UAAU,EAAE,KAAK,EAAE,MAAqB,EAAE,EAAE,CAAC,CAAC;oBAC5C,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,eAAe;oBAC1D,WAAW,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE;iBACpC,CAAC;gBACF,MAAM,EAAE,CAAC,sBAAa,CAAC;aACxB,CAAC;SACH;QACD,WAAW,EAAE,CAAC,gCAAc,CAAC;QAC7B,SAAS,EAAE;YACT,0BAAW;YACX,8BAAa;YACb,6CAAoB;YACpB,6CAAoB;YACpB,2CAAmB;YACnB,EAAE,OAAO,EAAE,sBAAK,EAAE,QAAQ,EAAE,6CAAoB,EAAE;YAClD,EAAE,OAAO,EAAE,sBAAK,EAAE,QAAQ,EAAE,6CAAoB,EAAE;YAClD,EAAE,OAAO,EAAE,2BAAW,EAAE,QAAQ,EAAE,+BAAa,EAAE;YAEjD,oDAAoD;YACpD,iCAAc;YACd,6CAAoB;YAEpB,mBAAmB;YACnB,0BAAW;YACX,mCAAe;YACf,qCAAgB;YAChB,8CAAqB;YACrB,4BAAQ;YACR,4BAAY;YACZ,4BAAY;SACb;QACD,OAAO,EAAE,CAAC,0BAAW,EAAE,0BAAW,EAAE,mCAAe,EAAE,8CAAqB,EAAE,4BAAQ,CAAC;KACtF,CAAC;GACW,UAAU,CAAG"} \ No newline at end of file diff --git a/dist/modules/auth/auth.service.js b/dist/modules/auth/auth.service.js new file mode 100644 index 0000000..754d15e --- /dev/null +++ b/dist/modules/auth/auth.service.js @@ -0,0 +1,209 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthService = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const jwt_1 = require("@nestjs/jwt"); +const sync_identity_handler_1 = require("../identity/application/handlers/user/sync-identity.handler"); +const user_interface_1 = require("../identity/domain/repositories/user.interface"); +const prisma_service_1 = require("../../shared/prisma.service"); +const redis_pkce_store_1 = require("./pkce/redis-pkce.store"); +const inmemory_refresh_store_1 = require("./refresh/inmemory-refresh.store"); +const openidClient = require('openid-client'); +let AuthService = class AuthService { + config; + jwtService; + prisma; + userRepository; + syncIdentityHandler; + pkceStore; + refreshStore; + constructor(config, jwtService, prisma, userRepository, syncIdentityHandler, pkceStore, refreshStore) { + this.config = config; + this.jwtService = jwtService; + this.prisma = prisma; + this.userRepository = userRepository; + this.syncIdentityHandler = syncIdentityHandler; + this.pkceStore = pkceStore; + this.refreshStore = refreshStore; + } + issuer = null; + client = null; + async getIssuer() { + if (this.issuer) + return this.issuer; + const issuerUrl = this.config.get('AUTHENTIK_ISSUER'); + if (!issuerUrl) + throw new Error('AUTHENTIK_ISSUER not configured'); + this.issuer = await openidClient.Issuer.discover(issuerUrl); + return this.issuer; + } + async getClient() { + if (this.client) + return this.client; + const issuer = await this.getIssuer(); + const clientId = this.config.get('AUTHENTIK_CLIENT_ID'); + const clientSecret = this.config.get('AUTHENTIK_CLIENT_SECRET'); + if (!clientId) + throw new Error('AUTHENTIK_CLIENT_ID not configured'); + this.client = new issuer.Client({ client_id: clientId, client_secret: clientSecret }); + return this.client; + } + async createAuthorizationRedirect(returnTo) { + const client = await this.getClient(); + const redirectUri = this.config.get('AUTH_CALLBACK_URL') || `${this.config.get('APP_URL') || ''}/auth/callback`; + const state = require('crypto').randomUUID(); + const code_verifier = openidClient.generators.codeVerifier(); + const code_challenge = await openidClient.generators.codeChallenge(code_verifier); + const nonce = openidClient.generators.nonce(); + await this.pkceStore.save(state, { code_verifier, nonce, returnTo }, 300); + const url = client.authorizationUrl({ + redirect_uri: redirectUri, + scope: this.config.get('AUTHENTIK_DEFAULT_SCOPE') || 'openid email profile', + response_type: 'code', + code_challenge, + code_challenge_method: 'S256', + state, + nonce, + }); + return url; + } + async handleCallback(code, state) { + const client = await this.getClient(); + const pkce = await this.pkceStore.get(state); + if (!pkce) + throw new common_1.UnauthorizedException('Invalid or expired state'); + // remove one-time state + await this.pkceStore.remove(state); + const redirectUri = this.config.get('AUTH_CALLBACK_URL') || `${this.config.get('APP_URL') || ''}/auth/callback`; + // exchange code + const tokenSet = await client.callback(redirectUri, { code, state }, { code_verifier: pkce.code_verifier, nonce: pkce.nonce }); + // verify id_token and get claims + const claims = tokenSet.claims(); + // fetch userinfo if available + let userInfo = null; + try { + if (tokenSet.access_token && client.userinfo) { + userInfo = await client.userinfo(tokenSet.access_token); + } + } + catch (e) { + // ignore + } + const identity = { + sub: userInfo?.sub || claims.sub || null, + preferred_username: userInfo?.preferred_username || userInfo?.username || userInfo?.email || claims.preferred_username || claims.email, + email: userInfo?.email || claims.email, + raw: { tokenSet, userInfo, claims }, + }; + // Sync identity to local user (create if needed) + const domainUser = await this.syncIdentityHandler.execute(identity); + // ensure active/not deleted + if (!domainUser.isActive) + throw new common_1.UnauthorizedException('User is not active'); + if (domainUser.deletedAt) + throw new common_1.UnauthorizedException('User is deleted'); + // create internal JWT + const jwtPayload = { + sub: domainUser.id, + preferred_username: domainUser.username, + email: domainUser.email, + }; + const expiresIn = Number(this.config.get('RAYLAB_JWT_EXPIRES_IN') || 3600); + const access = this.jwtService.sign(jwtPayload, { expiresIn }); + // create internal refresh token + const refreshToken = require('crypto').randomUUID(); + const refreshTtl = Number(this.config.get('RAYLAB_REFRESH_EXPIRES_IN') || 30 * 24 * 3600); // default 30 days + await this.refreshStore.set(refreshToken, { userId: domainUser.id }, refreshTtl); + return { + accessToken: access, + refreshToken, + expiresIn, + refreshTtl, + user: { + id: domainUser.id, + username: domainUser.username, + email: domainUser.email, + roles: domainUser.roles || [], + }, + returnTo: pkce.returnTo, + }; + } + async refresh(refreshToken) { + if (!refreshToken) + throw new common_1.UnauthorizedException('Missing refresh token'); + const data = await this.refreshStore.get(refreshToken); + if (!data) + throw new common_1.UnauthorizedException('Invalid refresh token'); + const userId = data.userId; + // load user + const domainUser = await this.userRepository.getById(userId); + if (!domainUser) + throw new common_1.UnauthorizedException('User not found'); + if (!domainUser.isActive) + throw new common_1.UnauthorizedException('User is not active'); + // rotate refresh token + await this.refreshStore.del(refreshToken); + const newRefresh = require('crypto').randomUUID(); + const refreshTtl = Number(this.config.get('RAYLAB_REFRESH_EXPIRES_IN') || 30 * 24 * 3600); + await this.refreshStore.set(newRefresh, { userId }, refreshTtl); + const expiresIn = Number(this.config.get('RAYLAB_JWT_EXPIRES_IN') || 3600); + const access = this.jwtService.sign({ sub: domainUser.id, preferred_username: domainUser.username, email: domainUser.email }, { expiresIn }); + return { accessToken: access, refreshToken: newRefresh, expiresIn, refreshTtl }; + } + async logout(refreshToken) { + if (refreshToken) { + await this.refreshStore.del(refreshToken); + } + const issuer = await this.getIssuer(); + const endSession = issuer.metadata.end_session_endpoint; + const postLogout = this.config.get('APP_URL') || '/'; + if (endSession) { + // Redirect to identity provider logout + const url = new URL(endSession); + if (postLogout) + url.searchParams.set('post_logout_redirect_uri', postLogout); + return url.toString(); + } + return postLogout; + } + async me(token) { + if (!token) + throw new common_1.UnauthorizedException('Missing token'); + try { + const payload = this.jwtService.verify(token); + const user = await this.userRepository.getById(payload.sub); + if (!user) + throw new common_1.UnauthorizedException('User not found'); + return { id: user.id, username: user.username, email: user.email, roles: user.roles || [] }; + } + catch (e) { + throw new common_1.UnauthorizedException('Invalid token'); + } + } +}; +exports.AuthService = AuthService; +exports.AuthService = AuthService = __decorate([ + (0, common_1.Injectable)(), + __param(3, (0, common_1.Inject)(user_interface_1.IUser)), + __metadata("design:paramtypes", [config_1.ConfigService, + jwt_1.JwtService, + prisma_service_1.PrismaService, + user_interface_1.IUser, + sync_identity_handler_1.SyncIdentityHandler, + redis_pkce_store_1.RedisPkceStore, + inmemory_refresh_store_1.InMemoryRefreshStore]) +], AuthService); +//# sourceMappingURL=auth.service.js.map \ No newline at end of file diff --git a/dist/modules/auth/auth.service.js.map b/dist/modules/auth/auth.service.js.map new file mode 100644 index 0000000..9581060 --- /dev/null +++ b/dist/modules/auth/auth.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/auth/authentication.service.js b/dist/modules/auth/authentication.service.js new file mode 100644 index 0000000..d95cea1 --- /dev/null +++ b/dist/modules/auth/authentication.service.js @@ -0,0 +1,70 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AuthenticationService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthenticationService = void 0; +const common_1 = require("@nestjs/common"); +const oidc_service_1 = require("./oidc.service"); +const prisma_service_1 = require("../../shared/prisma.service"); +const role_sync_service_1 = require("./role-sync.service"); +const authorization_service_1 = require("../authorization/authorization.service"); +const event_bus_service_1 = require("../../core/event-bus/event-bus.service"); +let AuthenticationService = AuthenticationService_1 = class AuthenticationService { + oidc; + prisma; + roleSync; + authorization; + events; + logger = new common_1.Logger(AuthenticationService_1.name); + constructor(oidc, prisma, roleSync, authorization, events) { + this.oidc = oidc; + this.prisma = prisma; + this.roleSync = roleSync; + this.authorization = authorization; + this.events = events; + } + async authenticate(bearerToken) { + // Validate token (signature/iss/aud/exp) + const claims = await this.oidc.verifyToken(bearerToken); + const sub = claims.sub; + if (!sub) + throw new Error('Invalid token: missing sub'); + // Resolve identity + const identity = { sub, email: claims.email, preferred_username: claims.preferred_username, raw: claims }; + // Find or create user (materialize) + let user = await this.prisma.user.findUnique({ where: { authentikId: sub } }); + if (!user) { + user = await this.prisma.user.create({ data: { authentikId: sub, username: identity.preferred_username || identity.email || sub, email: identity.email || null } }); + } + // Synchronize roles + const groups = Array.isArray(claims.groups) ? claims.groups : []; + await this.roleSync.syncUserRolesFromAuthentik(user.id, groups); + // Load permissions + const permissions = await this.authorization.getUserPermissions(user.id); + // Build request context + const rolesRows = await this.prisma.userRole.findMany({ where: { userId: user.id } }); + const roles = rolesRows.map((r) => r.roleId); + const ctx = { user, identity, roles, permissions }; + // Publish domain event + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'UserAuthenticated', payload: { userId: user.id, identity } }); + return ctx; + } +}; +exports.AuthenticationService = AuthenticationService; +exports.AuthenticationService = AuthenticationService = AuthenticationService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [oidc_service_1.OidcService, + prisma_service_1.PrismaService, + role_sync_service_1.RoleSyncService, + authorization_service_1.AuthorizationService, + event_bus_service_1.EventBus]) +], AuthenticationService); +//# sourceMappingURL=authentication.service.js.map \ No newline at end of file diff --git a/dist/modules/auth/authentication.service.js.map b/dist/modules/auth/authentication.service.js.map new file mode 100644 index 0000000..dd36d16 --- /dev/null +++ b/dist/modules/auth/authentication.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"authentication.service.js","sourceRoot":"","sources":["../../../src/modules/auth/authentication.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/auth/dto/login.dto.js b/dist/modules/auth/dto/login.dto.js new file mode 100644 index 0000000..72ae07d --- /dev/null +++ b/dist/modules/auth/dto/login.dto.js @@ -0,0 +1,4 @@ +"use strict"; +// Login DTO removed. Password grant has been removed in favor of Authorization Code + PKCE flow. +// Formerly contained username/password properties. +//# sourceMappingURL=login.dto.js.map \ No newline at end of file diff --git a/dist/modules/auth/dto/login.dto.js.map b/dist/modules/auth/dto/login.dto.js.map new file mode 100644 index 0000000..1cf14bb --- /dev/null +++ b/dist/modules/auth/dto/login.dto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"login.dto.js","sourceRoot":"","sources":["../../../../src/modules/auth/dto/login.dto.ts"],"names":[],"mappings":";AAAA,iGAAiG;AACjG,mDAAmD"} \ No newline at end of file diff --git a/dist/modules/auth/group-hash.service.js b/dist/modules/auth/group-hash.service.js new file mode 100644 index 0000000..8b2219c --- /dev/null +++ b/dist/modules/auth/group-hash.service.js @@ -0,0 +1,56 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GroupHashService = void 0; +const common_1 = require("@nestjs/common"); +const crypto = __importStar(require("crypto")); +let GroupHashService = class GroupHashService { + compute(groups) { + const sorted = (groups || []).slice().sort(); + const data = sorted.join(','); + return crypto.createHash('sha256').update(data, 'utf8').digest('hex'); + } +}; +exports.GroupHashService = GroupHashService; +exports.GroupHashService = GroupHashService = __decorate([ + (0, common_1.Injectable)() +], GroupHashService); +//# sourceMappingURL=group-hash.service.js.map \ No newline at end of file diff --git a/dist/modules/auth/group-hash.service.js.map b/dist/modules/auth/group-hash.service.js.map new file mode 100644 index 0000000..c5f2db7 --- /dev/null +++ b/dist/modules/auth/group-hash.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"group-hash.service.js","sourceRoot":"","sources":["../../../src/modules/auth/group-hash.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAA4C;AAC5C,+CAAiC;AAG1B,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAC3B,OAAO,CAAC,MAAgB;QACtB,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxE,CAAC;CACF,CAAA;AANY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;GACA,gBAAgB,CAM5B"} \ No newline at end of file diff --git a/dist/modules/auth/guards/oidc.guard.js b/dist/modules/auth/guards/oidc.guard.js new file mode 100644 index 0000000..06308ac --- /dev/null +++ b/dist/modules/auth/guards/oidc.guard.js @@ -0,0 +1,48 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var OidcGuard_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.OidcGuard = void 0; +const common_1 = require("@nestjs/common"); +const oidc_service_1 = require("../oidc.service"); +const authentication_service_1 = require("../authentication.service"); +let OidcGuard = OidcGuard_1 = class OidcGuard { + oidc; + authn; + logger = new common_1.Logger(OidcGuard_1.name); + constructor(oidc, authn) { + this.oidc = oidc; + this.authn = authn; + } + async canActivate(context) { + const req = context.switchToHttp().getRequest(); + const auth = req.headers['authorization'] || req.headers['Authorization']; + if (!auth || typeof auth !== 'string' || !auth.startsWith('Bearer ')) + throw new common_1.UnauthorizedException('Missing bearer token'); + const token = auth.substring(7).trim(); + try { + const ctx = await this.authn.authenticate(token); + // attach context to request under a structured key + req.raylabContext = ctx; + return true; + } + catch (e) { + this.logger.debug('Authentication failed', e.message); + throw new common_1.UnauthorizedException('Invalid token or authentication failed'); + } + } +}; +exports.OidcGuard = OidcGuard; +exports.OidcGuard = OidcGuard = OidcGuard_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [oidc_service_1.OidcService, authentication_service_1.AuthenticationService]) +], OidcGuard); +//# sourceMappingURL=oidc.guard.js.map \ No newline at end of file diff --git a/dist/modules/auth/guards/oidc.guard.js.map b/dist/modules/auth/guards/oidc.guard.js.map new file mode 100644 index 0000000..c66917b --- /dev/null +++ b/dist/modules/auth/guards/oidc.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"oidc.guard.js","sourceRoot":"","sources":["../../../../src/modules/auth/guards/oidc.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAkH;AAClH,kDAA8C;AAC9C,sEAAkE;AAG3D,IAAM,SAAS,iBAAf,MAAM,SAAS;IAES;IAAoC;IADhD,MAAM,GAAG,IAAI,eAAM,CAAC,WAAS,CAAC,IAAI,CAAC,CAAC;IACrD,YAA6B,IAAiB,EAAmB,KAA4B;QAAhE,SAAI,GAAJ,IAAI,CAAa;QAAmB,UAAK,GAAL,KAAK,CAAuB;IAAG,CAAC;IAEjG,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,MAAM,IAAI,8BAAqB,CAAC,sBAAsB,CAAC,CAAC;QAC9H,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAEvC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YACjD,mDAAmD;YACnD,GAAG,CAAC,aAAa,GAAG,GAAG,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAG,CAAS,CAAC,OAAO,CAAC,CAAC;YAC/D,MAAM,IAAI,8BAAqB,CAAC,wCAAwC,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;CACF,CAAA;AApBY,8BAAS;oBAAT,SAAS;IADrB,IAAA,mBAAU,GAAE;qCAGwB,0BAAW,EAA0B,8CAAqB;GAFlF,SAAS,CAoBrB"} \ No newline at end of file diff --git a/dist/modules/auth/oidc.service.js b/dist/modules/auth/oidc.service.js new file mode 100644 index 0000000..cf2ba55 --- /dev/null +++ b/dist/modules/auth/oidc.service.js @@ -0,0 +1,89 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var OidcService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.OidcService = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +let OidcService = OidcService_1 = class OidcService { + config; + jwksUri = null; + issuer; + audience; + logger = new common_1.Logger(OidcService_1.name); + constructor(config) { + this.config = config; + this.issuer = this.config.get('AUTHENTIK_ISSUER') || ''; + this.audience = this.config.get('AUTHENTIK_AUDIENCE') || undefined; + const jwksUri = this.config.get('AUTHENTIK_JWKS_URI'); + if (jwksUri) + this.jwksUri = jwksUri; + else if (this.issuer) + this.jwksUri = `${this.issuer.replace(/\/+$/, '')}/.well-known/jwks.json`; + } + async verifyToken(token) { + if (!this.jwksUri) + throw new Error('JWKS not configured'); + try { + // dynamic import to avoid ESM loading issues in test environment + const jose = await Promise.resolve().then(() => __importStar(require('jose'))); + const jwks = jose.createRemoteJWKSet(new URL(this.jwksUri)); + const { payload } = await jose.jwtVerify(token, jwks, { + issuer: this.issuer || undefined, + audience: this.audience, + }); + return payload; + } + catch (e) { + this.logger.debug('Token verification failed', e.message); + throw e; + } + } +}; +exports.OidcService = OidcService; +exports.OidcService = OidcService = OidcService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [config_1.ConfigService]) +], OidcService); +//# sourceMappingURL=oidc.service.js.map \ No newline at end of file diff --git a/dist/modules/auth/oidc.service.js.map b/dist/modules/auth/oidc.service.js.map new file mode 100644 index 0000000..d8e5d3b --- /dev/null +++ b/dist/modules/auth/oidc.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"oidc.service.js","sourceRoot":"","sources":["../../../src/modules/auth/oidc.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAoD;AACpD,2CAA+C;AAGxC,IAAM,WAAW,mBAAjB,MAAM,WAAW;IAMO;IALrB,OAAO,GAAkB,IAAI,CAAC;IAC9B,MAAM,CAAS;IACf,QAAQ,CAAgC;IACxC,MAAM,GAAG,IAAI,eAAM,CAAC,aAAW,CAAC,IAAI,CAAC,CAAC;IAE9C,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;QAChD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAS,kBAAkB,CAAC,IAAI,EAAE,CAAC;QAChE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAS,oBAAoB,CAAC,IAAI,SAAS,CAAC;QAC3E,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAS,oBAAoB,CAAC,CAAC;QAC9D,IAAI,OAAO;YAAE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;aAC/B,IAAI,IAAI,CAAC,MAAM;YAAE,IAAI,CAAC,OAAO,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,wBAAwB,CAAC;IAClG,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAa;QAC7B,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QAE1D,IAAI,CAAC;YACH,iEAAiE;YACjE,MAAM,IAAI,GAAG,wDAAa,MAAM,GAAC,CAAC;YAClC,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5D,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE;gBACpD,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS;gBAChC,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACjB,CAAC,CAAC;YAEV,OAAO,OAA8B,CAAC;QACxC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAG,CAAW,CAAC,OAAO,CAAC,CAAC;YACrE,MAAM,CAAC,CAAC;QACV,CAAC;IACH,CAAC;CACF,CAAA;AAhCY,kCAAW;sBAAX,WAAW;IADvB,IAAA,mBAAU,GAAE;qCAO0B,sBAAa;GANvC,WAAW,CAgCvB"} \ No newline at end of file diff --git a/dist/modules/auth/pkce/redis-pkce.store.js b/dist/modules/auth/pkce/redis-pkce.store.js new file mode 100644 index 0000000..fcf24b9 --- /dev/null +++ b/dist/modules/auth/pkce/redis-pkce.store.js @@ -0,0 +1,56 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RedisPkceStore = void 0; +const common_1 = require("@nestjs/common"); +let RedisPkceStore = class RedisPkceStore { + // In-memory PKCE store replacing Redis-backed implementation + map = new Map(); + cleanupInterval; + constructor() { + // periodic cleanup + this.cleanupInterval = setInterval(() => { + const now = Date.now(); + for (const [k, v] of this.map.entries()) { + if (v.expiresAt <= now) + this.map.delete(k); + } + }, 60 * 1000); + } + key(state) { return state; } + async save(state, data, ttlSeconds = 300) { + const expiresAt = Date.now() + ttlSeconds * 1000; + this.map.set(this.key(state), { ...data, expiresAt }); + } + async get(state) { + const v = this.map.get(this.key(state)); + if (!v) + return null; + if (v.expiresAt <= Date.now()) { + this.map.delete(this.key(state)); + return null; + } + return { code_verifier: v.code_verifier, nonce: v.nonce, returnTo: v.returnTo }; + } + async remove(state) { + this.map.delete(this.key(state)); + } + onModuleDestroy() { + if (this.cleanupInterval) + clearInterval(this.cleanupInterval); + } +}; +exports.RedisPkceStore = RedisPkceStore; +exports.RedisPkceStore = RedisPkceStore = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", []) +], RedisPkceStore); +//# sourceMappingURL=redis-pkce.store.js.map \ No newline at end of file diff --git a/dist/modules/auth/pkce/redis-pkce.store.js.map b/dist/modules/auth/pkce/redis-pkce.store.js.map new file mode 100644 index 0000000..eadcf0a --- /dev/null +++ b/dist/modules/auth/pkce/redis-pkce.store.js.map @@ -0,0 +1 @@ +{"version":3,"file":"redis-pkce.store.js","sourceRoot":"","sources":["../../../../src/modules/auth/pkce/redis-pkce.store.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA6D;AAKtD,IAAM,cAAc,GAApB,MAAM,cAAc;IACzB,6DAA6D;IACrD,GAAG,GAAG,IAAI,GAAG,EAAqB,CAAC;IACnC,eAAe,CAAkB;IAEzC;QACE,mBAAmB;QACnB,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE;YACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;gBACxC,IAAI,CAAC,CAAC,SAAS,IAAI,GAAG;oBAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IAChB,CAAC;IAEO,GAAG,CAAC,KAAa,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC;IAE5C,KAAK,CAAC,IAAI,CAAC,KAAa,EAAE,IAAiE,EAAE,UAAU,GAAG,GAAG;QAC3G,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,GAAG,IAAI,CAAC;QACjD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa;QACrB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QACxC,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACpB,IAAI,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QACjF,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;IAClF,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa;QACxB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IACnC,CAAC;IAED,eAAe;QACb,IAAI,IAAI,CAAC,eAAe;YAAE,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAChE,CAAC;CACF,CAAA;AApCY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,mBAAU,GAAE;;GACA,cAAc,CAoC1B"} \ No newline at end of file diff --git a/dist/modules/auth/refresh/inmemory-refresh.store.js b/dist/modules/auth/refresh/inmemory-refresh.store.js new file mode 100644 index 0000000..5ca1a3f --- /dev/null +++ b/dist/modules/auth/refresh/inmemory-refresh.store.js @@ -0,0 +1,53 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.InMemoryRefreshStore = void 0; +const common_1 = require("@nestjs/common"); +let InMemoryRefreshStore = class InMemoryRefreshStore { + map = new Map(); + cleanupInterval; + constructor() { + this.cleanupInterval = setInterval(() => { + const now = Date.now(); + for (const [k, v] of this.map.entries()) { + if (v.expiresAt <= now) + this.map.delete(k); + } + }, 60 * 1000); + } + async set(token, data, ttlSeconds) { + const expiresAt = Date.now() + ttlSeconds * 1000; + this.map.set(token, { userId: data.userId, expiresAt }); + } + async get(token) { + const v = this.map.get(token); + if (!v) + return null; + if (v.expiresAt <= Date.now()) { + this.map.delete(token); + return null; + } + return { userId: v.userId }; + } + async del(token) { + this.map.delete(token); + } + onModuleDestroy() { + if (this.cleanupInterval) + clearInterval(this.cleanupInterval); + } +}; +exports.InMemoryRefreshStore = InMemoryRefreshStore; +exports.InMemoryRefreshStore = InMemoryRefreshStore = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", []) +], InMemoryRefreshStore); +//# sourceMappingURL=inmemory-refresh.store.js.map \ No newline at end of file diff --git a/dist/modules/auth/refresh/inmemory-refresh.store.js.map b/dist/modules/auth/refresh/inmemory-refresh.store.js.map new file mode 100644 index 0000000..21759c7 --- /dev/null +++ b/dist/modules/auth/refresh/inmemory-refresh.store.js.map @@ -0,0 +1 @@ +{"version":3,"file":"inmemory-refresh.store.js","sourceRoot":"","sources":["../../../../src/modules/auth/refresh/inmemory-refresh.store.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA6D;AAKtD,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACvB,GAAG,GAAG,IAAI,GAAG,EAAwB,CAAC;IACtC,eAAe,CAAkB;IAEzC;QACE,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE;YACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;gBACxC,IAAI,CAAC,CAAC,SAAS,IAAI,GAAG;oBAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa,EAAE,IAAwB,EAAE,UAAkB;QACnE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,GAAG,IAAI,CAAC;QACjD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa;QACrB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACpB,IAAI,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QACvE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa;QACrB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,eAAe;QACb,IAAI,IAAI,CAAC,eAAe;YAAE,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAChE,CAAC;CACF,CAAA;AAhCY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;;GACA,oBAAoB,CAgChC"} \ No newline at end of file diff --git a/dist/modules/auth/role-sync.service.js b/dist/modules/auth/role-sync.service.js new file mode 100644 index 0000000..e9db195 --- /dev/null +++ b/dist/modules/auth/role-sync.service.js @@ -0,0 +1,88 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var RoleSyncService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RoleSyncService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../shared/prisma.service"); +const event_bus_service_1 = require("../../core/event-bus/event-bus.service"); +const group_hash_service_1 = require("./group-hash.service"); +const authorization_service_1 = require("../authorization/authorization.service"); +let RoleSyncService = RoleSyncService_1 = class RoleSyncService { + prisma; + groupHash; + permissionCache; + events; + logger = new common_1.Logger(RoleSyncService_1.name); + constructor(prisma, groupHash, permissionCache, events) { + this.prisma = prisma; + this.groupHash = groupHash; + this.permissionCache = permissionCache; + this.events = events; + } + computeGroupHash(groups) { + return this.groupHash.compute(groups || []); + } + async mapGroupsToRoleIds(groups) { + if (!groups || groups.length === 0) + return []; + const mappings = await this.prisma.authGroupRoleMapping.findMany({ where: { authGroup: { in: groups } } }); + const roleIds = mappings.map((m) => m.roleId); + return Array.from(new Set(roleIds)); + } + async syncUserRolesFromAuthentik(userId, groups) { + const groupHash = this.computeGroupHash(groups || []); + const user = await this.prisma.user.findUnique({ where: { id: userId } }); + if (!user) + throw new Error('User not found'); + if (user.lastGroupHash === groupHash) { + this.logger.debug('Group hash unchanged, skipping sync'); + return { skipped: true }; + } + const roleIds = await this.mapGroupsToRoleIds(groups || []); + const previousRoleRows = await this.prisma.userRole.findMany({ where: { userId, source: 'AUTHENTIK' }, select: { roleId: true } }); + const previousRoles = previousRoleRows.map((r) => r.roleId); + await this.prisma.$transaction(async (tx) => { + await tx.userRole.deleteMany({ where: { userId: userId, source: 'AUTHENTIK' } }); + if (roleIds.length > 0) { + const createData = roleIds.map((rid) => ({ userId, roleId: rid, source: 'AUTHENTIK' })); + await tx.userRole.createMany({ data: createData, skipDuplicates: true }); + } + await tx.user.update({ where: { id: userId }, data: { lastGroupHash: groupHash } }); + }); + // Invalidate permission cache through abstraction + try { + await this.permissionCache.invalidate(userId); + } + catch (e) { + this.logger.error('Failed to invalidate permission cache', e); + } + // Publish event for audit and other subscribers + this.events.publish({ + id: require('crypto').randomUUID(), + timestamp: new Date().toISOString(), + type: 'RolesSynchronized', + payload: { userId, groups, assignedRoleIds: roleIds, previousRoles }, + }); + return { skipped: false, assignedRoleIds: roleIds }; + } +}; +exports.RoleSyncService = RoleSyncService; +exports.RoleSyncService = RoleSyncService = RoleSyncService_1 = __decorate([ + (0, common_1.Injectable)(), + __param(2, (0, common_1.Inject)(authorization_service_1.PERMISSION_CACHE)), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + group_hash_service_1.GroupHashService, Object, event_bus_service_1.EventBus]) +], RoleSyncService); +//# sourceMappingURL=role-sync.service.js.map \ No newline at end of file diff --git a/dist/modules/auth/role-sync.service.js.map b/dist/modules/auth/role-sync.service.js.map new file mode 100644 index 0000000..0865491 --- /dev/null +++ b/dist/modules/auth/role-sync.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"role-sync.service.js","sourceRoot":"","sources":["../../../src/modules/auth/role-sync.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/authorization/application/authorization.service.js b/dist/modules/authorization/application/authorization.service.js new file mode 100644 index 0000000..6ce7dcf --- /dev/null +++ b/dist/modules/authorization/application/authorization.service.js @@ -0,0 +1,2 @@ +"use strict"; +//# sourceMappingURL=authorization.service.js.map \ No newline at end of file diff --git a/dist/modules/authorization/application/authorization.service.js.map b/dist/modules/authorization/application/authorization.service.js.map new file mode 100644 index 0000000..dea3510 --- /dev/null +++ b/dist/modules/authorization/application/authorization.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"authorization.service.js","sourceRoot":"","sources":["../../../../src/modules/authorization/application/authorization.service.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/modules/authorization/authorization.module.js b/dist/modules/authorization/authorization.module.js new file mode 100644 index 0000000..2458ab5 --- /dev/null +++ b/dist/modules/authorization/authorization.module.js @@ -0,0 +1,29 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthorizationModule = void 0; +const common_1 = require("@nestjs/common"); +const authorization_service_1 = require("./authorization.service"); +const prisma_service_1 = require("../../shared/prisma.service"); +const redis_service_1 = require("../../shared/redis.service"); +const redis_permission_cache_service_1 = require("./cache/redis-permission-cache.service"); +let AuthorizationModule = class AuthorizationModule { +}; +exports.AuthorizationModule = AuthorizationModule; +exports.AuthorizationModule = AuthorizationModule = __decorate([ + (0, common_1.Module)({ + providers: [ + authorization_service_1.AuthorizationService, + prisma_service_1.PrismaService, + redis_service_1.RedisService, + { provide: authorization_service_1.PERMISSION_CACHE, useClass: redis_permission_cache_service_1.RedisPermissionCache }, + ], + exports: [authorization_service_1.AuthorizationService, authorization_service_1.PERMISSION_CACHE], + }) +], AuthorizationModule); +//# sourceMappingURL=authorization.module.js.map \ No newline at end of file diff --git a/dist/modules/authorization/authorization.module.js.map b/dist/modules/authorization/authorization.module.js.map new file mode 100644 index 0000000..bd807bc --- /dev/null +++ b/dist/modules/authorization/authorization.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"authorization.module.js","sourceRoot":"","sources":["../../../src/modules/authorization/authorization.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,mEAAiF;AACjF,gEAA4D;AAC5D,8DAA0D;AAC1D,2FAA8E;AAWvE,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;CAAG,CAAA;AAAtB,kDAAmB;8BAAnB,mBAAmB;IAT/B,IAAA,eAAM,EAAC;QACN,SAAS,EAAE;YACT,4CAAoB;YACpB,8BAAa;YACb,4BAAY;YACZ,EAAE,OAAO,EAAE,wCAAgB,EAAE,QAAQ,EAAE,qDAAoB,EAAE;SAC9D;QACD,OAAO,EAAE,CAAC,4CAAoB,EAAE,wCAAgB,CAAC;KAClD,CAAC;GACW,mBAAmB,CAAG"} \ No newline at end of file diff --git a/dist/modules/authorization/authorization.service.js b/dist/modules/authorization/authorization.service.js new file mode 100644 index 0000000..2c33ba6 --- /dev/null +++ b/dist/modules/authorization/authorization.service.js @@ -0,0 +1,71 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var AuthorizationService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthorizationService = exports.PERMISSION_CACHE = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../shared/prisma.service"); +exports.PERMISSION_CACHE = 'PERMISSION_CACHE'; +let AuthorizationService = AuthorizationService_1 = class AuthorizationService { + prisma; + cache; + logger = new common_1.Logger(AuthorizationService_1.name); + constructor(prisma, cache) { + this.prisma = prisma; + this.cache = cache; + } + async getUserPermissions(userId) { + try { + const cached = await this.cache.get(userId); + if (cached) + return cached; + } + catch (e) { + this.logger.debug('PermissionCache get failed', e); + } + const rows = await this.prisma.$queryRaw ` + SELECT p.code as code + FROM "UserRole" ur + JOIN "RolePermission" rp ON rp.role_id = ur.role_id + JOIN "Permission" p ON p.id = rp.permission_id + WHERE ur.user_id = ${userId}`; + const perms = Array.isArray(rows) ? rows.map((r) => r.code) : []; + try { + await this.cache.set(userId, perms); + } + catch (e) { + this.logger.debug('PermissionCache set failed', e); + } + return perms; + } + async hasPermission(userId, permissionCode) { + const perms = await this.getUserPermissions(userId); + return perms.includes(permissionCode); + } + async invalidateUserPermissions(userId) { + try { + await this.cache.invalidate(userId); + } + catch (e) { + this.logger.debug('PermissionCache invalidate failed', e); + } + } +}; +exports.AuthorizationService = AuthorizationService; +exports.AuthorizationService = AuthorizationService = AuthorizationService_1 = __decorate([ + (0, common_1.Injectable)(), + __param(1, (0, common_1.Inject)(exports.PERMISSION_CACHE)), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, Object]) +], AuthorizationService); +//# sourceMappingURL=authorization.service.js.map \ No newline at end of file diff --git a/dist/modules/authorization/authorization.service.js.map b/dist/modules/authorization/authorization.service.js.map new file mode 100644 index 0000000..c548026 --- /dev/null +++ b/dist/modules/authorization/authorization.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"authorization.service.js","sourceRoot":"","sources":["../../../src/modules/authorization/authorization.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAA4D;AAC5D,gEAA4D;AAG/C,QAAA,gBAAgB,GAAG,kBAAkB,CAAC;AAG5C,IAAM,oBAAoB,4BAA1B,MAAM,oBAAoB;IAGF;IAAkE;IAF9E,MAAM,GAAG,IAAI,eAAM,CAAC,sBAAoB,CAAC,IAAI,CAAC,CAAC;IAEhE,YAA6B,MAAqB,EAA6C,KAAsB;QAAxF,WAAM,GAAN,MAAM,CAAe;QAA6C,UAAK,GAAL,KAAK,CAAiB;IAAG,CAAC;IAEzH,KAAK,CAAC,kBAAkB,CAAC,MAAc;QACrC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAC;QAC5B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE,CAAQ,CAAC,CAAC;QAC5D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAA;;;;;2BAKjB,MAAM,EAAE,CAAC;QAEhC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAEtE,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE,CAAQ,CAAC,CAAC;QAC5D,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,MAAc,EAAE,cAAsB;QACxD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACpD,OAAO,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,yBAAyB,CAAC,MAAc;QAC5C,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE,CAAQ,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;CACF,CAAA;AA3CY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;IAI0C,WAAA,IAAA,eAAM,EAAC,wBAAgB,CAAC,CAAA;qCAAxC,8BAAa;GAHvC,oBAAoB,CA2ChC"} \ No newline at end of file diff --git a/dist/modules/authorization/cache/permission-cache.interface.js b/dist/modules/authorization/cache/permission-cache.interface.js new file mode 100644 index 0000000..f6e02dc --- /dev/null +++ b/dist/modules/authorization/cache/permission-cache.interface.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=permission-cache.interface.js.map \ No newline at end of file diff --git a/dist/modules/authorization/cache/permission-cache.interface.js.map b/dist/modules/authorization/cache/permission-cache.interface.js.map new file mode 100644 index 0000000..cc80661 --- /dev/null +++ b/dist/modules/authorization/cache/permission-cache.interface.js.map @@ -0,0 +1 @@ +{"version":3,"file":"permission-cache.interface.js","sourceRoot":"","sources":["../../../../src/modules/authorization/cache/permission-cache.interface.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/modules/authorization/cache/redis-permission-cache.service.js b/dist/modules/authorization/cache/redis-permission-cache.service.js new file mode 100644 index 0000000..1ab7d5a --- /dev/null +++ b/dist/modules/authorization/cache/redis-permission-cache.service.js @@ -0,0 +1,48 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RedisPermissionCache = void 0; +const common_1 = require("@nestjs/common"); +const redis_service_1 = require("../../../shared/redis.service"); +let RedisPermissionCache = class RedisPermissionCache { + redis; + TTL = 60 * 5; + constructor(redis) { + this.redis = redis; + } + key(userId) { + const { CacheKeys } = require('../../../shared/cache-keys'); + return CacheKeys.permission(userId); + } + async get(userId) { + const data = await this.redis.get(this.key(userId)); + if (!data) + return null; + try { + return JSON.parse(data); + } + catch { + return null; + } + } + async set(userId, permissions, ttlSeconds) { + await this.redis.set(this.key(userId), JSON.stringify(permissions), ttlSeconds ?? this.TTL); + } + async invalidate(userId) { + await this.redis.del(this.key(userId)); + } +}; +exports.RedisPermissionCache = RedisPermissionCache; +exports.RedisPermissionCache = RedisPermissionCache = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [redis_service_1.RedisService]) +], RedisPermissionCache); +//# sourceMappingURL=redis-permission-cache.service.js.map \ No newline at end of file diff --git a/dist/modules/authorization/cache/redis-permission-cache.service.js.map b/dist/modules/authorization/cache/redis-permission-cache.service.js.map new file mode 100644 index 0000000..c9a7615 --- /dev/null +++ b/dist/modules/authorization/cache/redis-permission-cache.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"redis-permission-cache.service.js","sourceRoot":"","sources":["../../../../src/modules/authorization/cache/redis-permission-cache.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAE5C,iEAA6D;AAGtD,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAGF;IAFZ,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;IAE9B,YAA6B,KAAmB;QAAnB,UAAK,GAAL,KAAK,CAAc;IAAG,CAAC;IAE5C,GAAG,CAAC,MAAc;QACxB,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;QAC5D,OAAO,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,MAAc;QACtB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAa,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,MAAc,EAAE,WAAqB,EAAE,UAAmB;QAClE,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,UAAU,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9F,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAAc;QAC7B,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACzC,CAAC;CACF,CAAA;AA3BY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;qCAIyB,4BAAY;GAHrC,oBAAoB,CA2BhC"} \ No newline at end of file diff --git a/dist/modules/authorization/guards/permission.guard.js b/dist/modules/authorization/guards/permission.guard.js new file mode 100644 index 0000000..4863b8f --- /dev/null +++ b/dist/modules/authorization/guards/permission.guard.js @@ -0,0 +1,52 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createPermissionGuard = exports.PermissionGuard = void 0; +const common_1 = require("@nestjs/common"); +const authorization_service_1 = require("../authorization.service"); +let PermissionGuard = class PermissionGuard { + authz; + permission; + constructor(authz, permission) { + this.authz = authz; + this.permission = permission; + } + async canActivate(context) { + const req = context.switchToHttp().getRequest(); + const user = req.raylab?.user; + if (!user) + throw new common_1.ForbiddenException('Missing user'); + const allowed = await this.authz.hasPermission(user.id, this.permission); + if (!allowed) + throw new common_1.ForbiddenException('Forbidden'); + return true; + } +}; +exports.PermissionGuard = PermissionGuard; +exports.PermissionGuard = PermissionGuard = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [authorization_service_1.AuthorizationService, String]) +], PermissionGuard); +// Factory to create guard instances with permission string (used in decorators) +const createPermissionGuard = (permission) => { + let _Guard = class _Guard extends PermissionGuard { + constructor(authz) { + super(authz, permission); + } + }; + _Guard = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [authorization_service_1.AuthorizationService]) + ], _Guard); + return _Guard; +}; +exports.createPermissionGuard = createPermissionGuard; +//# sourceMappingURL=permission.guard.js.map \ No newline at end of file diff --git a/dist/modules/authorization/guards/permission.guard.js.map b/dist/modules/authorization/guards/permission.guard.js.map new file mode 100644 index 0000000..0346629 --- /dev/null +++ b/dist/modules/authorization/guards/permission.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"permission.guard.js","sourceRoot":"","sources":["../../../../src/modules/authorization/guards/permission.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+F;AAC/F,oEAAgE;AAGzD,IAAM,eAAe,GAArB,MAAM,eAAe;IACG;IAA8C;IAA3E,YAA6B,KAA2B,EAAmB,UAAkB;QAAhE,UAAK,GAAL,KAAK,CAAsB;QAAmB,eAAU,GAAV,UAAU,CAAQ;IAAG,CAAC;IAEjG,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC;QAC9B,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,2BAAkB,CAAC,cAAc,CAAC,CAAC;QAExD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACzE,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,2BAAkB,CAAC,WAAW,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAZY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAEyB,4CAAoB;GAD7C,eAAe,CAY3B;AAED,gFAAgF;AACzE,MAAM,qBAAqB,GAAG,CAAC,UAAkB,EAAE,EAAE;IAC1D,IACM,MAAM,GADZ,MACM,MAAO,SAAQ,eAAe;QAClC,YAAY,KAA2B;YACrC,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAC3B,CAAC;KACF,CAAA;IAJK,MAAM;QADX,IAAA,mBAAU,GAAE;yCAEQ,4CAAoB;OADnC,MAAM,CAIX;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AARW,QAAA,qBAAqB,yBAQhC"} \ No newline at end of file diff --git a/dist/modules/authorization/presentation/decorators/permission.decorator.js b/dist/modules/authorization/presentation/decorators/permission.decorator.js new file mode 100644 index 0000000..2547583 --- /dev/null +++ b/dist/modules/authorization/presentation/decorators/permission.decorator.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Permissions = void 0; +const common_1 = require("@nestjs/common"); +const Permissions = (...permissions) => (0, common_1.SetMetadata)('permissions', permissions); +exports.Permissions = Permissions; +//# sourceMappingURL=permission.decorator.js.map \ No newline at end of file diff --git a/dist/modules/authorization/presentation/decorators/permission.decorator.js.map b/dist/modules/authorization/presentation/decorators/permission.decorator.js.map new file mode 100644 index 0000000..cdad7da --- /dev/null +++ b/dist/modules/authorization/presentation/decorators/permission.decorator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"permission.decorator.js","sourceRoot":"","sources":["../../../../../src/modules/authorization/presentation/decorators/permission.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAEtC,MAAM,WAAW,GAAG,CAAC,GAAG,WAAqB,EAAE,EAAE,CACtD,IAAA,oBAAW,EAAC,aAAa,EAAE,WAAW,CAAC,CAAC;AAD7B,QAAA,WAAW,eACkB"} \ No newline at end of file diff --git a/dist/modules/authorization/presentation/guards/permission.guard.js b/dist/modules/authorization/presentation/guards/permission.guard.js new file mode 100644 index 0000000..4fb2446 --- /dev/null +++ b/dist/modules/authorization/presentation/guards/permission.guard.js @@ -0,0 +1,46 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PermissionGuard = void 0; +const common_1 = require("@nestjs/common"); +const core_1 = require("@nestjs/core"); +let PermissionGuard = class PermissionGuard { + reflector; + constructor(reflector) { + this.reflector = reflector; + } + canActivate(context) { + const permissions = this.reflector.getAllAndOverride('permissions', [ + context.getHandler(), + context.getClass(), + ]); + if (!permissions) { + return true; + } + const request = context.switchToHttp().getRequest(); + const user = request.currentUser; + if (!user) { + throw new common_1.UnauthorizedException('Current user is missing.'); + } + const hasAll = permissions.every((permission) => { + return user.hasPermission(permission); + }); + if (!hasAll) + throw new common_1.ForbiddenException('Insufficient permissions.'); + return true; + } +}; +exports.PermissionGuard = PermissionGuard; +exports.PermissionGuard = PermissionGuard = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [core_1.Reflector]) +], PermissionGuard); +//# sourceMappingURL=permission.guard.js.map \ No newline at end of file diff --git a/dist/modules/authorization/presentation/guards/permission.guard.js.map b/dist/modules/authorization/presentation/guards/permission.guard.js.map new file mode 100644 index 0000000..f868731 --- /dev/null +++ b/dist/modules/authorization/presentation/guards/permission.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"permission.guard.js","sourceRoot":"","sources":["../../../../../src/modules/authorization/presentation/guards/permission.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAMwB;AACxB,uCAAyC;AAGlC,IAAM,eAAe,GAArB,MAAM,eAAe;IAEP;IADnB,YACmB,SAAoB;QAApB,cAAS,GAAT,SAAS,CAAW;IACpC,CAAC;IAEJ,WAAW,CAAC,OAAyB;QACnC,MAAM,WAAW,GACf,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAC9B,aAAa,EACb;YACE,OAAO,CAAC,UAAU,EAAE;YACpB,OAAO,CAAC,QAAQ,EAAE;SACnB,CACF,CAAC;QAEJ,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAS,CAAC;QAE3D,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC;QAEjC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,8BAAqB,CAAC,0BAA0B,CAAC,CAAC;QAC9D,CAAC;QAEG,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,EAAE;YAClD,OAAO,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,2BAAkB,CAAC,2BAA2B,CAAC,CAAC;QAEvE,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAnCY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAGmB,gBAAS;GAF5B,eAAe,CAmC3B"} \ No newline at end of file diff --git a/dist/modules/health/health.controller.js b/dist/modules/health/health.controller.js new file mode 100644 index 0000000..d008ea9 --- /dev/null +++ b/dist/modules/health/health.controller.js @@ -0,0 +1,35 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HealthController = void 0; +const common_1 = require("@nestjs/common"); +const health_service_1 = require("./health.service"); +let HealthController = class HealthController { + healthService; + constructor(healthService) { + this.healthService = healthService; + } + async get() { + return this.healthService.getHealth(); + } +}; +exports.HealthController = HealthController; +__decorate([ + (0, common_1.Get)(), + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", Promise) +], HealthController.prototype, "get", null); +exports.HealthController = HealthController = __decorate([ + (0, common_1.Controller)('api/health'), + __metadata("design:paramtypes", [health_service_1.HealthService]) +], HealthController); +//# sourceMappingURL=health.controller.js.map \ No newline at end of file diff --git a/dist/modules/health/health.controller.js.map b/dist/modules/health/health.controller.js.map new file mode 100644 index 0000000..1461be4 --- /dev/null +++ b/dist/modules/health/health.controller.js.map @@ -0,0 +1 @@ +{"version":3,"file":"health.controller.js","sourceRoot":"","sources":["../../../src/modules/health/health.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiD;AACjD,qDAAiD;AAG1C,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IACE;IAA7B,YAA6B,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAGvD,AAAN,KAAK,CAAC,GAAG;QACP,OAAO,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,CAAC;IACxC,CAAC;CACF,CAAA;AAPY,4CAAgB;AAIrB;IADL,IAAA,YAAG,GAAE;;;;2CAGL;2BANU,gBAAgB;IAD5B,IAAA,mBAAU,EAAC,YAAY,CAAC;qCAEqB,8BAAa;GAD9C,gBAAgB,CAO5B"} \ No newline at end of file diff --git a/dist/modules/health/health.module.js b/dist/modules/health/health.module.js new file mode 100644 index 0000000..2ff3522 --- /dev/null +++ b/dist/modules/health/health.module.js @@ -0,0 +1,23 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HealthModule = void 0; +const common_1 = require("@nestjs/common"); +const health_controller_1 = require("./health.controller"); +const health_service_1 = require("./health.service"); +let HealthModule = class HealthModule { +}; +exports.HealthModule = HealthModule; +exports.HealthModule = HealthModule = __decorate([ + (0, common_1.Module)({ + controllers: [health_controller_1.HealthController], + providers: [health_service_1.HealthService], + exports: [health_service_1.HealthService], + }) +], HealthModule); +//# sourceMappingURL=health.module.js.map \ No newline at end of file diff --git a/dist/modules/health/health.module.js.map b/dist/modules/health/health.module.js.map new file mode 100644 index 0000000..5fab47e --- /dev/null +++ b/dist/modules/health/health.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"health.module.js","sourceRoot":"","sources":["../../../src/modules/health/health.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2DAAuD;AACvD,qDAAiD;AAO1C,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,oCAAY;uBAAZ,YAAY;IALxB,IAAA,eAAM,EAAC;QACN,WAAW,EAAE,CAAC,oCAAgB,CAAC;QAC/B,SAAS,EAAE,CAAC,8BAAa,CAAC;QAC1B,OAAO,EAAE,CAAC,8BAAa,CAAC;KACzB,CAAC;GACW,YAAY,CAAG"} \ No newline at end of file diff --git a/dist/modules/health/health.service.js b/dist/modules/health/health.service.js new file mode 100644 index 0000000..bd95e68 --- /dev/null +++ b/dist/modules/health/health.service.js @@ -0,0 +1,24 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HealthService = void 0; +const common_1 = require("@nestjs/common"); +let HealthService = class HealthService { + async getHealth() { + return { + status: 'ok', + timestamp: new Date().toISOString(), + service: 'raylab-core', + }; + } +}; +exports.HealthService = HealthService; +exports.HealthService = HealthService = __decorate([ + (0, common_1.Injectable)() +], HealthService); +//# sourceMappingURL=health.service.js.map \ No newline at end of file diff --git a/dist/modules/health/health.service.js.map b/dist/modules/health/health.service.js.map new file mode 100644 index 0000000..14ed2a4 --- /dev/null +++ b/dist/modules/health/health.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"health.service.js","sourceRoot":"","sources":["../../../src/modules/health/health.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAGrC,IAAM,aAAa,GAAnB,MAAM,aAAa;IACxB,KAAK,CAAC,SAAS;QACb,OAAO;YACL,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,OAAO,EAAE,aAAa;SACvB,CAAC;IACJ,CAAC;CACF,CAAA;AARY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;GACA,aAAa,CAQzB"} \ No newline at end of file diff --git a/dist/modules/identity/application/config/env-auth-config.js b/dist/modules/identity/application/config/env-auth-config.js new file mode 100644 index 0000000..726f871 --- /dev/null +++ b/dist/modules/identity/application/config/env-auth-config.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EnvAuthConfig = void 0; +class EnvAuthConfig { + autoCreateUser() { + return (process.env.AUTH_AUTO_CREATE_USER ?? 'true') === 'true'; + } + syncEmail() { + return (process.env.AUTH_SYNC_EMAIL ?? 'true') === 'true'; + } + syncUsername() { + return (process.env.AUTH_SYNC_USERNAME ?? 'false') === 'true'; + } +} +exports.EnvAuthConfig = EnvAuthConfig; +//# sourceMappingURL=env-auth-config.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/config/env-auth-config.js.map b/dist/modules/identity/application/config/env-auth-config.js.map new file mode 100644 index 0000000..0323378 --- /dev/null +++ b/dist/modules/identity/application/config/env-auth-config.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env-auth-config.js","sourceRoot":"","sources":["../../../../../src/modules/identity/application/config/env-auth-config.ts"],"names":[],"mappings":";;;AAEA,MAAa,aAAa;IACxB,cAAc;QACZ,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,MAAM,CAAC,KAAK,MAAM,CAAC;IAClE,CAAC;IAED,SAAS;QACP,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,MAAM,CAAC,KAAK,MAAM,CAAC;IAC5D,CAAC;IAED,YAAY;QACV,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,KAAK,MAAM,CAAC;IAChE,CAAC;CACF;AAZD,sCAYC"} \ No newline at end of file diff --git a/dist/modules/identity/application/config/i-auth-config.js b/dist/modules/identity/application/config/i-auth-config.js new file mode 100644 index 0000000..8ed71cd --- /dev/null +++ b/dist/modules/identity/application/config/i-auth-config.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.IAuthConfig = void 0; +class IAuthConfig { +} +exports.IAuthConfig = IAuthConfig; +//# sourceMappingURL=i-auth-config.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/config/i-auth-config.js.map b/dist/modules/identity/application/config/i-auth-config.js.map new file mode 100644 index 0000000..9c3285a --- /dev/null +++ b/dist/modules/identity/application/config/i-auth-config.js.map @@ -0,0 +1 @@ +{"version":3,"file":"i-auth-config.js","sourceRoot":"","sources":["../../../../../src/modules/identity/application/config/i-auth-config.ts"],"names":[],"mappings":";;;AAAA,MAAsB,WAAW;CAIhC;AAJD,kCAIC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/create-permission.handler.js b/dist/modules/identity/application/handlers/permission/create-permission.handler.js new file mode 100644 index 0000000..57980c4 --- /dev/null +++ b/dist/modules/identity/application/handlers/permission/create-permission.handler.js @@ -0,0 +1,47 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreatePermissionHandler = void 0; +const common_1 = require("@nestjs/common"); +const permission_interface_1 = require("../../../domain/repositories/permission.interface"); +const permission_entity_1 = require("../../../domain/entities/permission.entity"); +const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); +const crypto = require('crypto'); +let CreatePermissionHandler = class CreatePermissionHandler { + permissionRepository; + events; + constructor(permissionRepository, events) { + this.permissionRepository = permissionRepository; + this.events = events; + } + async execute(dto) { + if (!dto.name || !dto.code) + throw new common_1.ConflictException('Missing required fields'); + const perm = permission_entity_1.PermissionData.restore({ + id: crypto.randomUUID(), + code: dto.code || dto.name, + name: dto.name, + displayName: dto.displayName || dto.name, + description: dto.description || '', + createdAt: new Date(), + updatedAt: new Date(), + }); + const created = await this.permissionRepository.create(perm); + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'PermissionCreated', payload: { permissionId: created.id } }); + return created; + } +}; +exports.CreatePermissionHandler = CreatePermissionHandler; +exports.CreatePermissionHandler = CreatePermissionHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [permission_interface_1.IPermission, event_bus_service_1.EventBus]) +], CreatePermissionHandler); +//# sourceMappingURL=create-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/create-permission.handler.js.map b/dist/modules/identity/application/handlers/permission/create-permission.handler.js.map new file mode 100644 index 0000000..96dc5a0 --- /dev/null +++ b/dist/modules/identity/application/handlers/permission/create-permission.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"create-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/permission/create-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,4FAAgF;AAChF,kFAA4E;AAC5E,uFAA2E;AAE3E,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAG1B,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAAoD;IAAjF,YAA6B,oBAAiC,EAAmB,MAAgB;QAApE,yBAAoB,GAApB,oBAAoB,CAAa;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAErG,KAAK,CAAC,OAAO,CAAC,GAAQ;QACpB,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,yBAAyB,CAAC,CAAC;QAEnF,MAAM,IAAI,GAAG,kCAAc,CAAC,OAAO,CAAC;YAClC,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;YACvB,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI;YAC1B,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,IAAI;YACxC,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE;YAClC,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,SAAS,EAAE,IAAI,IAAI,EAAE;SACf,CAAC,CAAC;QAEV,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7D,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACnK,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AApBY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;qCAEwC,kCAAW,EAA2B,4BAAQ;GADtF,uBAAuB,CAoBnC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/delete-permission.handler.js b/dist/modules/identity/application/handlers/permission/delete-permission.handler.js new file mode 100644 index 0000000..68f7f61 --- /dev/null +++ b/dist/modules/identity/application/handlers/permission/delete-permission.handler.js @@ -0,0 +1,54 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeletePermissionHandler = void 0; +const common_1 = require("@nestjs/common"); +const permission_interface_1 = require("../../../domain/repositories/permission.interface"); +const role_interface_1 = require("../../../domain/repositories/role.interface"); +const authorization_service_1 = require("../../../../authorization/authorization.service"); +const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); +let DeletePermissionHandler = class DeletePermissionHandler { + permissionRepository; + roleRepository; + authorizationService; + events; + constructor(permissionRepository, roleRepository, authorizationService, events) { + this.permissionRepository = permissionRepository; + this.roleRepository = roleRepository; + this.authorizationService = authorizationService; + this.events = events; + } + async execute(id) { + const p = await this.permissionRepository.getById(id); + if (!p) + throw new common_1.NotFoundException('Permission not found.'); + // find roles that reference this permission + const rolesWithPerm = await this.permissionRepository.findRoleIdsByPermission(id); + // delete permission + await this.permissionRepository.delete(id); + // invalidate caches for users who have affected roles + const userSet = new Set(); + for (const rid of rolesWithPerm) { + const uids = await this.roleRepository.getAssignedUserIds(rid); + uids.forEach(u => userSet.add(u)); + } + for (const uid of Array.from(userSet)) + await this.authorizationService.invalidateUserPermissions(uid); + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'PermissionDeleted', payload: { permissionId: id, affectedRoles: rolesWithPerm } }); + return; + } +}; +exports.DeletePermissionHandler = DeletePermissionHandler; +exports.DeletePermissionHandler = DeletePermissionHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [permission_interface_1.IPermission, role_interface_1.IRole, authorization_service_1.AuthorizationService, event_bus_service_1.EventBus]) +], DeletePermissionHandler); +//# sourceMappingURL=delete-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/delete-permission.handler.js.map b/dist/modules/identity/application/handlers/permission/delete-permission.handler.js.map new file mode 100644 index 0000000..9f54506 --- /dev/null +++ b/dist/modules/identity/application/handlers/permission/delete-permission.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"delete-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/permission/delete-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,4FAAgF;AAChF,gFAAoE;AACpE,2FAAuF;AACvF,uFAA2E;AAGpE,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAAoD;IAAwC;IAA6D;IAAtL,YAA6B,oBAAiC,EAAmB,cAAqB,EAAmB,oBAA0C,EAAmB,MAAgB;QAAzK,yBAAoB,GAApB,oBAAoB,CAAa;QAAmB,mBAAc,GAAd,cAAc,CAAO;QAAmB,yBAAoB,GAApB,oBAAoB,CAAsB;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAE1M,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,0BAAiB,CAAC,uBAAuB,CAAC,CAAC;QAE7D,4CAA4C;QAC5C,MAAM,aAAa,GAAG,MAAO,IAAI,CAAC,oBAA4B,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC;QAE3F,oBAAoB;QACpB,MAAM,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAE3C,sDAAsD;QACtD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;YAChC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;YAC/D,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,MAAM,IAAI,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC;QAEtG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,aAAa,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC;QAEzL,OAAO;IACT,CAAC;CACF,CAAA;AA1BY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;qCAEwC,kCAAW,EAAmC,sBAAK,EAAyC,4CAAoB,EAA2B,4BAAQ;GAD3L,uBAAuB,CA0BnC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/get-permission.handler.js b/dist/modules/identity/application/handlers/permission/get-permission.handler.js new file mode 100644 index 0000000..12e92c1 --- /dev/null +++ b/dist/modules/identity/application/handlers/permission/get-permission.handler.js @@ -0,0 +1,29 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetPermissionHandler = void 0; +const common_1 = require("@nestjs/common"); +const permission_interface_1 = require("../../../domain/repositories/permission.interface"); +let GetPermissionHandler = class GetPermissionHandler { + permissionRepository; + constructor(permissionRepository) { + this.permissionRepository = permissionRepository; + } + async execute(id) { + return this.permissionRepository.getById(id); + } +}; +exports.GetPermissionHandler = GetPermissionHandler; +exports.GetPermissionHandler = GetPermissionHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [permission_interface_1.IPermission]) +], GetPermissionHandler); +//# sourceMappingURL=get-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/get-permission.handler.js.map b/dist/modules/identity/application/handlers/permission/get-permission.handler.js.map new file mode 100644 index 0000000..e0308b5 --- /dev/null +++ b/dist/modules/identity/application/handlers/permission/get-permission.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/permission/get-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,4FAAgF;AAGzE,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACF;IAA7B,YAA6B,oBAAiC;QAAjC,yBAAoB,GAApB,oBAAoB,CAAa;IAAG,CAAC;IAElE,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,OAAO,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC/C,CAAC;CACF,CAAA;AANY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;qCAEwC,kCAAW;GADnD,oBAAoB,CAMhC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/get-permissions.handler.js b/dist/modules/identity/application/handlers/permission/get-permissions.handler.js new file mode 100644 index 0000000..d86ca2c --- /dev/null +++ b/dist/modules/identity/application/handlers/permission/get-permissions.handler.js @@ -0,0 +1,29 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetPermissionsHandler = void 0; +const common_1 = require("@nestjs/common"); +const permission_interface_1 = require("../../../domain/repositories/permission.interface"); +let GetPermissionsHandler = class GetPermissionsHandler { + permissionRepository; + constructor(permissionRepository) { + this.permissionRepository = permissionRepository; + } + async execute(query) { + return this.permissionRepository.find({ page: query.page, limit: query.limit, search: query.search || null }); + } +}; +exports.GetPermissionsHandler = GetPermissionsHandler; +exports.GetPermissionsHandler = GetPermissionsHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [permission_interface_1.IPermission]) +], GetPermissionsHandler); +//# sourceMappingURL=get-permissions.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/get-permissions.handler.js.map b/dist/modules/identity/application/handlers/permission/get-permissions.handler.js.map new file mode 100644 index 0000000..ac7e3a5 --- /dev/null +++ b/dist/modules/identity/application/handlers/permission/get-permissions.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get-permissions.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/permission/get-permissions.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,4FAAgF;AAGzE,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IACH;IAA7B,YAA6B,oBAAiC;QAAjC,yBAAoB,GAApB,oBAAoB,CAAa;IAAG,CAAC;IAElE,KAAK,CAAC,OAAO,CAAC,KAAyD;QACrE,OAAO,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IAChH,CAAC;CACF,CAAA;AANY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;qCAEwC,kCAAW;GADnD,qBAAqB,CAMjC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/update-permission.handler.js b/dist/modules/identity/application/handlers/permission/update-permission.handler.js new file mode 100644 index 0000000..d7c86dc --- /dev/null +++ b/dist/modules/identity/application/handlers/permission/update-permission.handler.js @@ -0,0 +1,58 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UpdatePermissionHandler = void 0; +const common_1 = require("@nestjs/common"); +const permission_interface_1 = require("../../../domain/repositories/permission.interface"); +const role_interface_1 = require("../../../domain/repositories/role.interface"); +const authorization_service_1 = require("../../../../authorization/authorization.service"); +const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); +let UpdatePermissionHandler = class UpdatePermissionHandler { + permissionRepository; + roleRepository; + authorizationService; + events; + constructor(permissionRepository, roleRepository, authorizationService, events) { + this.permissionRepository = permissionRepository; + this.roleRepository = roleRepository; + this.authorizationService = authorizationService; + this.events = events; + } + async execute(id, dto) { + const perm = await this.permissionRepository.getById(id); + if (!perm) + throw new common_1.NotFoundException('Permission not found.'); + if (dto.name) + perm.changeName(dto.name); + if (dto.description !== undefined) + perm.changeDescription(dto.description); + if (dto.code) + perm.changeCode(dto.code); + const updated = await this.permissionRepository.update(perm); + // invalidate caches for users who belong to roles that reference this permission + const roleIds = await this.permissionRepository.findRoleIdsByPermission(id); + const userSet = new Set(); + for (const rid of roleIds) { + const uids = await this.roleRepository.getAssignedUserIds(rid); + uids.forEach(u => userSet.add(u)); + } + for (const uid of Array.from(userSet)) + await this.authorizationService.invalidateUserPermissions(uid); + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'PermissionUpdated', payload: { permissionId: id } }); + return updated; + } +}; +exports.UpdatePermissionHandler = UpdatePermissionHandler; +exports.UpdatePermissionHandler = UpdatePermissionHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [permission_interface_1.IPermission, role_interface_1.IRole, authorization_service_1.AuthorizationService, event_bus_service_1.EventBus]) +], UpdatePermissionHandler); +//# sourceMappingURL=update-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/update-permission.handler.js.map b/dist/modules/identity/application/handlers/permission/update-permission.handler.js.map new file mode 100644 index 0000000..01dc6ed --- /dev/null +++ b/dist/modules/identity/application/handlers/permission/update-permission.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"update-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/permission/update-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,4FAAgF;AAChF,gFAAoE;AACpE,2FAAuF;AACvF,uFAA2E;AAGpE,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAAoD;IAAwC;IAA6D;IAAtL,YAA6B,oBAAiC,EAAmB,cAAqB,EAAmB,oBAA0C,EAAmB,MAAgB;QAAzK,yBAAoB,GAApB,oBAAoB,CAAa;QAAmB,mBAAc,GAAd,cAAc,CAAO;QAAmB,yBAAoB,GAApB,oBAAoB,CAAsB;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAE1M,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,GAAQ;QAChC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,uBAAuB,CAAC,CAAC;QAEhE,IAAI,GAAG,CAAC,IAAI;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,GAAG,CAAC,WAAW,KAAK,SAAS;YAAE,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC3E,IAAI,GAAG,CAAC,IAAI;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAExC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAE7D,iFAAiF;QACjF,MAAM,OAAO,GAAG,MAAO,IAAI,CAAC,oBAA4B,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC;QACrF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAElC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;YAC/D,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,MAAM,IAAI,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC;QAEtG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAE3J,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AA5BY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;qCAEwC,kCAAW,EAAmC,sBAAK,EAAyC,4CAAoB,EAA2B,4BAAQ;GAD3L,uBAAuB,CA4BnC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/assign-permission.handler.js b/dist/modules/identity/application/handlers/role/assign-permission.handler.js new file mode 100644 index 0000000..7ec4e30 --- /dev/null +++ b/dist/modules/identity/application/handlers/role/assign-permission.handler.js @@ -0,0 +1,56 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RoleAssignPermissionHandler = void 0; +const common_1 = require("@nestjs/common"); +const role_interface_1 = require("../../../domain/repositories/role.interface"); +const permission_interface_1 = require("../../../domain/repositories/permission.interface"); +const authorization_service_1 = require("../../../../authorization/authorization.service"); +const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); +let RoleAssignPermissionHandler = class RoleAssignPermissionHandler { + roleRepository; + permissionRepository; + authorizationService; + events; + constructor(roleRepository, permissionRepository, authorizationService, events) { + this.roleRepository = roleRepository; + this.permissionRepository = permissionRepository; + this.authorizationService = authorizationService; + this.events = events; + } + async execute(roleId, permissionId) { + const role = await this.roleRepository.findById(roleId); + if (!role) + throw new common_1.NotFoundException('Role not found.'); + const perm = await this.permissionRepository.getById(permissionId); + if (!perm) + throw new common_1.NotFoundException('Permission not found.'); + role.assignPermission(perm); + const updated = await this.roleRepository.update(role); + // Invalidate permissions cache for users who have this role + const userIds = await this.roleRepository.getAssignedUserIds(roleId); + for (const uid of userIds) { + await this.authorizationService.invalidateUserPermissions(uid); + } + // Publish role.updated event + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleUpdated', payload: { roleId, permissionId } }); + return updated; + } +}; +exports.RoleAssignPermissionHandler = RoleAssignPermissionHandler; +exports.RoleAssignPermissionHandler = RoleAssignPermissionHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [role_interface_1.IRole, + permission_interface_1.IPermission, + authorization_service_1.AuthorizationService, + event_bus_service_1.EventBus]) +], RoleAssignPermissionHandler); +//# sourceMappingURL=assign-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/assign-permission.handler.js.map b/dist/modules/identity/application/handlers/role/assign-permission.handler.js.map new file mode 100644 index 0000000..1ab4742 --- /dev/null +++ b/dist/modules/identity/application/handlers/role/assign-permission.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"assign-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/assign-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAuE;AACvE,gFAAoE;AACpE,4FAAgF;AAChF,2FAAuF;AACvF,uFAA2E;AAGpE,IAAM,2BAA2B,GAAjC,MAAM,2BAA2B;IAEnB;IACA;IACA;IACA;IAJnB,YACmB,cAAqB,EACrB,oBAAiC,EACjC,oBAA0C,EAC1C,MAAgB;QAHhB,mBAAc,GAAd,cAAc,CAAO;QACrB,yBAAoB,GAApB,oBAAoB,CAAa;QACjC,yBAAoB,GAApB,oBAAoB,CAAsB;QAC1C,WAAM,GAAN,MAAM,CAAU;IAChC,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,YAAoB;QAChD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACnE,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,uBAAuB,CAAC,CAAC;QAEhE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAE5B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEvD,4DAA4D;QAC5D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACrE,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC;QACjE,CAAC;QAED,6BAA6B;QAC7B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;QAEzJ,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AA9BY,kEAA2B;sCAA3B,2BAA2B;IADvC,IAAA,mBAAU,GAAE;qCAGwB,sBAAK;QACC,kCAAW;QACX,4CAAoB;QAClC,4BAAQ;GALxB,2BAA2B,CA8BvC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/create-role.handler.js b/dist/modules/identity/application/handlers/role/create-role.handler.js new file mode 100644 index 0000000..c61e3d9 --- /dev/null +++ b/dist/modules/identity/application/handlers/role/create-role.handler.js @@ -0,0 +1,54 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateRoleHandler = void 0; +const common_1 = require("@nestjs/common"); +const role_interface_1 = require("../../../domain/repositories/role.interface"); +const role_entity_1 = require("../../../domain/entities/role.entity"); +const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); +const crypto = require('crypto'); +let CreateRoleHandler = class CreateRoleHandler { + roleRepository; + events; + constructor(roleRepository, events) { + this.roleRepository = roleRepository; + this.events = events; + } + async execute(dto) { + // basic validation + if (!dto.code || !dto.name) + throw new common_1.ConflictException('Missing required fields'); + try { + const role = role_entity_1.RoleData.restore({ + id: crypto.randomUUID(), + code: dto.code, + name: dto.name, + description: dto.description || '', + permissions: [], + isDefault: dto.isDefault || false, + createdAt: new Date(), + updatedAt: new Date(), + }); + const created = await this.roleRepository.create(role); + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleCreated', payload: { roleId: created.id } }); + return created; + } + catch (e) { + throw new common_1.ConflictException('Role creation failed.'); + } + } +}; +exports.CreateRoleHandler = CreateRoleHandler; +exports.CreateRoleHandler = CreateRoleHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [role_interface_1.IRole, event_bus_service_1.EventBus]) +], CreateRoleHandler); +//# sourceMappingURL=create-role.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/create-role.handler.js.map b/dist/modules/identity/application/handlers/role/create-role.handler.js.map new file mode 100644 index 0000000..d6382d7 --- /dev/null +++ b/dist/modules/identity/application/handlers/role/create-role.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"create-role.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/create-role.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AACpE,sEAAgE;AAChE,uFAA2E;AAE3E,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAG1B,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAAwC;IAArE,YAA6B,cAAqB,EAAmB,MAAgB;QAAxD,mBAAc,GAAd,cAAc,CAAO;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAEzF,KAAK,CAAC,OAAO,CAAC,GAAQ;QACpB,mBAAmB;QACnB,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,yBAAyB,CAAC,CAAC;QAEnF,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,sBAAQ,CAAC,OAAO,CAAC;gBAC5B,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;gBACvB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE;gBAClC,WAAW,EAAE,EAAE;gBACf,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,KAAK;gBACjC,SAAS,EAAE,IAAI,IAAI,EAAE;gBACrB,SAAS,EAAE,IAAI,IAAI,EAAE;aACf,CAAC,CAAC;YAEV,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAEvD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAEvJ,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,0BAAiB,CAAC,uBAAuB,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;CACF,CAAA;AA5BY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK,EAA2B,4BAAQ;GAD1E,iBAAiB,CA4B7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/delete-role.handler.js b/dist/modules/identity/application/handlers/role/delete-role.handler.js new file mode 100644 index 0000000..364e58b --- /dev/null +++ b/dist/modules/identity/application/handlers/role/delete-role.handler.js @@ -0,0 +1,45 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeleteRoleHandler = void 0; +const common_1 = require("@nestjs/common"); +const role_interface_1 = require("../../../domain/repositories/role.interface"); +const authorization_service_1 = require("../../../../authorization/authorization.service"); +const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); +let DeleteRoleHandler = class DeleteRoleHandler { + roleRepository; + authorizationService; + events; + constructor(roleRepository, authorizationService, events) { + this.roleRepository = roleRepository; + this.authorizationService = authorizationService; + this.events = events; + } + async execute(id) { + const role = await this.roleRepository.findById(id); + if (!role) + throw new common_1.NotFoundException('Role not found.'); + // get affected users before delete + const userIds = await this.roleRepository.getAssignedUserIds(id); + await this.roleRepository.delete(id); + // invalidate caches + for (const uid of userIds) + await this.authorizationService.invalidateUserPermissions(uid); + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleDeleted', payload: { roleId: id } }); + return; + } +}; +exports.DeleteRoleHandler = DeleteRoleHandler; +exports.DeleteRoleHandler = DeleteRoleHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [role_interface_1.IRole, authorization_service_1.AuthorizationService, event_bus_service_1.EventBus]) +], DeleteRoleHandler); +//# sourceMappingURL=delete-role.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/delete-role.handler.js.map b/dist/modules/identity/application/handlers/role/delete-role.handler.js.map new file mode 100644 index 0000000..cb54805 --- /dev/null +++ b/dist/modules/identity/application/handlers/role/delete-role.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"delete-role.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/delete-role.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AACpE,2FAAuF;AACvF,uFAA2E;AAGpE,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAAwC;IAA6D;IAAlI,YAA6B,cAAqB,EAAmB,oBAA0C,EAAmB,MAAgB;QAArH,mBAAc,GAAd,cAAc,CAAO;QAAmB,yBAAoB,GAApB,oBAAoB,CAAsB;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAEtJ,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,mCAAmC;QACnC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;QAEjE,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAErC,oBAAoB;QACpB,KAAK,MAAM,GAAG,IAAI,OAAO;YAAE,MAAM,IAAI,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC;QAE1F,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAE/I,OAAO;IACT,CAAC;CACF,CAAA;AAnBY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK,EAAyC,4CAAoB,EAA2B,4BAAQ;GADvI,iBAAiB,CAmB7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/get-role.handler.js b/dist/modules/identity/application/handlers/role/get-role.handler.js new file mode 100644 index 0000000..52296bd --- /dev/null +++ b/dist/modules/identity/application/handlers/role/get-role.handler.js @@ -0,0 +1,29 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetRoleHandler = void 0; +const common_1 = require("@nestjs/common"); +const role_interface_1 = require("../../../domain/repositories/role.interface"); +let GetRoleHandler = class GetRoleHandler { + roleRepository; + constructor(roleRepository) { + this.roleRepository = roleRepository; + } + async execute(id) { + return this.roleRepository.findById(id); + } +}; +exports.GetRoleHandler = GetRoleHandler; +exports.GetRoleHandler = GetRoleHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [role_interface_1.IRole]) +], GetRoleHandler); +//# sourceMappingURL=get-role.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/get-role.handler.js.map b/dist/modules/identity/application/handlers/role/get-role.handler.js.map new file mode 100644 index 0000000..d43f1e7 --- /dev/null +++ b/dist/modules/identity/application/handlers/role/get-role.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get-role.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/get-role.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,gFAAoE;AAG7D,IAAM,cAAc,GAApB,MAAM,cAAc;IACI;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,OAAO,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC;CACF,CAAA;AANY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,cAAc,CAM1B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/get-roles.handler.js b/dist/modules/identity/application/handlers/role/get-roles.handler.js new file mode 100644 index 0000000..1994d0c --- /dev/null +++ b/dist/modules/identity/application/handlers/role/get-roles.handler.js @@ -0,0 +1,29 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetRolesHandler = void 0; +const common_1 = require("@nestjs/common"); +const role_interface_1 = require("../../../domain/repositories/role.interface"); +let GetRolesHandler = class GetRolesHandler { + roleRepository; + constructor(roleRepository) { + this.roleRepository = roleRepository; + } + async execute(query) { + return this.roleRepository.find({ page: query.page, limit: query.limit, search: query.search || null }); + } +}; +exports.GetRolesHandler = GetRolesHandler; +exports.GetRolesHandler = GetRolesHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [role_interface_1.IRole]) +], GetRolesHandler); +//# sourceMappingURL=get-roles.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/get-roles.handler.js.map b/dist/modules/identity/application/handlers/role/get-roles.handler.js.map new file mode 100644 index 0000000..25c135a --- /dev/null +++ b/dist/modules/identity/application/handlers/role/get-roles.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get-roles.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/get-roles.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,gFAAoE;AAG7D,IAAM,eAAe,GAArB,MAAM,eAAe;IACG;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,KAAyD;QACrE,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IAC1G,CAAC;CACF,CAAA;AANY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,eAAe,CAM3B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/remove-permission.handler.js b/dist/modules/identity/application/handlers/role/remove-permission.handler.js new file mode 100644 index 0000000..40382ed --- /dev/null +++ b/dist/modules/identity/application/handlers/role/remove-permission.handler.js @@ -0,0 +1,46 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RoleRemovePermissionHandler = void 0; +const common_1 = require("@nestjs/common"); +const role_interface_1 = require("../../../domain/repositories/role.interface"); +const authorization_service_1 = require("../../../../authorization/authorization.service"); +const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); +let RoleRemovePermissionHandler = class RoleRemovePermissionHandler { + roleRepository; + authorizationService; + events; + constructor(roleRepository, authorizationService, events) { + this.roleRepository = roleRepository; + this.authorizationService = authorizationService; + this.events = events; + } + async execute(roleId, permissionId) { + const role = await this.roleRepository.findById(roleId); + if (!role) + throw new common_1.NotFoundException('Role not found.'); + role.removePermission(permissionId); + const updated = await this.roleRepository.update(role); + // Invalidate caches for users with the role + const userIds = await this.roleRepository.getAssignedUserIds(roleId); + for (const uid of userIds) + await this.authorizationService.invalidateUserPermissions(uid); + // Publish event + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleUpdated', payload: { roleId, removedPermissionId: permissionId } }); + return updated; + } +}; +exports.RoleRemovePermissionHandler = RoleRemovePermissionHandler; +exports.RoleRemovePermissionHandler = RoleRemovePermissionHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [role_interface_1.IRole, authorization_service_1.AuthorizationService, event_bus_service_1.EventBus]) +], RoleRemovePermissionHandler); +//# sourceMappingURL=remove-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/remove-permission.handler.js.map b/dist/modules/identity/application/handlers/role/remove-permission.handler.js.map new file mode 100644 index 0000000..d788e58 --- /dev/null +++ b/dist/modules/identity/application/handlers/role/remove-permission.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"remove-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/remove-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AACpE,2FAAuF;AACvF,uFAA2E;AAGpE,IAAM,2BAA2B,GAAjC,MAAM,2BAA2B;IACT;IAAwC;IAA6D;IAAlI,YAA6B,cAAqB,EAAmB,oBAA0C,EAAmB,MAAgB;QAArH,mBAAc,GAAd,cAAc,CAAO;QAAmB,yBAAoB,GAApB,oBAAoB,CAAsB;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAEtJ,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,YAAoB;QAChD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;QAEpC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEvD,4CAA4C;QAC5C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACrE,KAAK,MAAM,GAAG,IAAI,OAAO;YAAE,MAAM,IAAI,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC;QAE1F,gBAAgB;QAChB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,mBAAmB,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;QAE9K,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AApBY,kEAA2B;sCAA3B,2BAA2B;IADvC,IAAA,mBAAU,GAAE;qCAEkC,sBAAK,EAAyC,4CAAoB,EAA2B,4BAAQ;GADvI,2BAA2B,CAoBvC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/update-role.handler.js b/dist/modules/identity/application/handlers/role/update-role.handler.js new file mode 100644 index 0000000..e763d40 --- /dev/null +++ b/dist/modules/identity/application/handlers/role/update-role.handler.js @@ -0,0 +1,53 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UpdateRoleHandler = void 0; +const common_1 = require("@nestjs/common"); +const role_interface_1 = require("../../../domain/repositories/role.interface"); +const authorization_service_1 = require("../../../../authorization/authorization.service"); +const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); +let UpdateRoleHandler = class UpdateRoleHandler { + roleRepository; + authorizationService; + events; + constructor(roleRepository, authorizationService, events) { + this.roleRepository = roleRepository; + this.authorizationService = authorizationService; + this.events = events; + } + async execute(id, dto) { + const role = await this.roleRepository.findById(id); + if (!role) + throw new common_1.NotFoundException('Role not found.'); + // validation + if (dto.name && dto.name.length < 2) + throw new common_1.BadRequestException('Name too short'); + if (dto.name) + role.changeName(dto.name); + if (dto.description !== undefined) + role.changeDescription(dto.description); + if (dto.isDefault !== undefined) + role.setDefault(!!dto.isDefault); + const updated = await this.roleRepository.update(role); + // invalidate caches for users with this role + const userIds = await this.roleRepository.getAssignedUserIds(id); + for (const uid of userIds) + await this.authorizationService.invalidateUserPermissions(uid); + this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleUpdated', payload: { roleId: id } }); + return updated; + } +}; +exports.UpdateRoleHandler = UpdateRoleHandler; +exports.UpdateRoleHandler = UpdateRoleHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [role_interface_1.IRole, authorization_service_1.AuthorizationService, event_bus_service_1.EventBus]) +], UpdateRoleHandler); +//# sourceMappingURL=update-role.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/update-role.handler.js.map b/dist/modules/identity/application/handlers/role/update-role.handler.js.map new file mode 100644 index 0000000..f285491 --- /dev/null +++ b/dist/modules/identity/application/handlers/role/update-role.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"update-role.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/update-role.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAoF;AACpF,gFAAoE;AACpE,2FAAuF;AACvF,uFAA2E;AAGpE,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAAwC;IAA6D;IAAlI,YAA6B,cAAqB,EAAmB,oBAA0C,EAAmB,MAAgB;QAArH,mBAAc,GAAd,cAAc,CAAO;QAAmB,yBAAoB,GAApB,oBAAoB,CAAsB;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAEtJ,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,GAAQ;QAChC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,aAAa;QACb,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,4BAAmB,CAAC,gBAAgB,CAAC,CAAC;QAErF,IAAI,GAAG,CAAC,IAAI;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,GAAG,CAAC,WAAW,KAAK,SAAS;YAAE,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC3E,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS;YAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAElE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEvD,6CAA6C;QAC7C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;QACjE,KAAK,MAAM,GAAG,IAAI,OAAO;YAAE,MAAM,IAAI,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC;QAE1F,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAE/I,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AAxBY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK,EAAyC,4CAAoB,EAA2B,4BAAQ;GADvI,iBAAiB,CAwB7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/assign-permission.handler.js b/dist/modules/identity/application/handlers/user/assign-permission.handler.js new file mode 100644 index 0000000..ebbfad0 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/assign-permission.handler.js @@ -0,0 +1,42 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AssignPermissionHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_interface_1 = require("../../../domain/repositories/user.interface"); +const permission_entity_1 = require("../../../domain/entities/permission.entity"); +let AssignPermissionHandler = class AssignPermissionHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(userId, permissionId) { + const user = await this.userRepository.getById(userId); + if (!user) + throw new common_1.NotFoundException('User not found.'); + // Permission repository is not available; create a minimal PermissionData + const perm = permission_entity_1.PermissionData.restore({ + id: permissionId, + name: permissionId, + description: '', + createdAt: new Date(), + updatedAt: new Date(), + }); + user.assignPermission(perm); + return this.userRepository.update(user); + } +}; +exports.AssignPermissionHandler = AssignPermissionHandler; +exports.AssignPermissionHandler = AssignPermissionHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_interface_1.IUser]) +], AssignPermissionHandler); +//# sourceMappingURL=assign-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/assign-permission.handler.js.map b/dist/modules/identity/application/handlers/user/assign-permission.handler.js.map new file mode 100644 index 0000000..69bfd0d --- /dev/null +++ b/dist/modules/identity/application/handlers/user/assign-permission.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"assign-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/assign-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AACpE,kFAA4E;AAGrE,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,YAAoB;QAChD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,0EAA0E;QAC1E,MAAM,IAAI,GAAG,kCAAc,CAAC,OAAO,CAAC;YAClC,EAAE,EAAE,YAAY;YAChB,IAAI,EAAE,YAAY;YAClB,WAAW,EAAE,EAAE;YACf,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,SAAS,EAAE,IAAI,IAAI,EAAE;SACtB,CAAC,CAAC;QAEH,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAE5B,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF,CAAA;AApBY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,uBAAuB,CAoBnC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/assign-role.handler.js b/dist/modules/identity/application/handlers/user/assign-role.handler.js new file mode 100644 index 0000000..b0f6e6d --- /dev/null +++ b/dist/modules/identity/application/handlers/user/assign-role.handler.js @@ -0,0 +1,40 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AssignRoleHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_interface_1 = require("../../../domain/repositories/user.interface"); +const role_interface_1 = require("../../../domain/repositories/role.interface"); +let AssignRoleHandler = class AssignRoleHandler { + userRepository; + roleRepository; + constructor(userRepository, roleRepository) { + this.userRepository = userRepository; + this.roleRepository = roleRepository; + } + async execute(userId, roleId) { + const user = await this.userRepository.getById(userId); + if (!user) + throw new common_1.NotFoundException('User not found.'); + const role = await this.roleRepository.findById(roleId); + if (!role) + throw new common_1.NotFoundException('Role not found.'); + user.assignRole(role); + return this.userRepository.update(user); + } +}; +exports.AssignRoleHandler = AssignRoleHandler; +exports.AssignRoleHandler = AssignRoleHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_interface_1.IUser, + role_interface_1.IRole]) +], AssignRoleHandler); +//# sourceMappingURL=assign-role.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/assign-role.handler.js.map b/dist/modules/identity/application/handlers/user/assign-role.handler.js.map new file mode 100644 index 0000000..7998815 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/assign-role.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"assign-role.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/assign-role.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AACpE,gFAAoE;AAG7D,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAET;IACA;IAFnB,YACmB,cAAqB,EACrB,cAAqB;QADrB,mBAAc,GAAd,cAAc,CAAO;QACrB,mBAAc,GAAd,cAAc,CAAO;IACrC,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,MAAc;QAC1C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAEtB,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF,CAAA;AAjBY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAGwB,sBAAK;QACL,sBAAK;GAH7B,iBAAiB,CAiB7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/create-user.handler.js b/dist/modules/identity/application/handlers/user/create-user.handler.js new file mode 100644 index 0000000..5c860df --- /dev/null +++ b/dist/modules/identity/application/handlers/user/create-user.handler.js @@ -0,0 +1,39 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateUserHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_interface_1 = require("../../../domain/repositories/user.interface"); +const user_service_1 = require("../../services/user.service"); +let CreateUserHandler = class CreateUserHandler { + userRepository; + userService; + constructor(userRepository, userService) { + this.userRepository = userRepository; + this.userService = userService; + } + async execute(dto) { + const exists = await this.userRepository.existByEmail(dto.email); + if (exists) { + throw new common_1.ConflictException('Email already exists.'); + } + // Provisioning disabled: Do not create users in external IdP from RayLab. + // Reject attempts to create users via API to enforce creation-at-Authentik policy. + throw new common_1.BadRequestException('User creation via RayLab API is disabled. Create users in Authentik and then login to sync.'); + } +}; +exports.CreateUserHandler = CreateUserHandler; +exports.CreateUserHandler = CreateUserHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_interface_1.IUser, + user_service_1.UserService]) +], CreateUserHandler); +//# sourceMappingURL=create-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/create-user.handler.js.map b/dist/modules/identity/application/handlers/user/create-user.handler.js.map new file mode 100644 index 0000000..fae7a16 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/create-user.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"create-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/create-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAIwB;AAGxB,gFAAoE;AAEpE,8DAA0D;AAGnD,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAET;IACA;IAFnB,YACmB,cAAqB,EACrB,WAAwB;QADxB,mBAAc,GAAd,cAAc,CAAO;QACrB,gBAAW,GAAX,WAAW,CAAa;IACxC,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,GAAkB;QAE9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAEjE,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,IAAI,0BAAiB,CAAC,uBAAuB,CAAC,CAAC;QACvD,CAAC;QAED,0EAA0E;QAC1E,mFAAmF;QACnF,MAAM,IAAI,4BAAmB,CAAC,6FAA6F,CAAC,CAAC;IAC/H,CAAC;CACF,CAAA;AAlBY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAGwB,sBAAK;QACR,0BAAW;GAHhC,iBAAiB,CAkB7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/delete-user.handler.js b/dist/modules/identity/application/handlers/user/delete-user.handler.js new file mode 100644 index 0000000..f7e24b5 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/delete-user.handler.js @@ -0,0 +1,32 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeleteUserHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_interface_1 = require("../../../domain/repositories/user.interface"); +let DeleteUserHandler = class DeleteUserHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(id) { + const exists = await this.userRepository.existsById(id); + if (!exists) + throw new common_1.NotFoundException('User not found.'); + await this.userRepository.softDelete(id); + } +}; +exports.DeleteUserHandler = DeleteUserHandler; +exports.DeleteUserHandler = DeleteUserHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_interface_1.IUser]) +], DeleteUserHandler); +//# sourceMappingURL=delete-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/delete-user.handler.js.map b/dist/modules/identity/application/handlers/user/delete-user.handler.js.map new file mode 100644 index 0000000..572dd11 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/delete-user.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"delete-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/delete-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AAG7D,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE5D,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC3C,CAAC;CACF,CAAA;AATY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,iBAAiB,CAS7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/disable-user.handler.js b/dist/modules/identity/application/handlers/user/disable-user.handler.js new file mode 100644 index 0000000..193959b --- /dev/null +++ b/dist/modules/identity/application/handlers/user/disable-user.handler.js @@ -0,0 +1,32 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DisableUserHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_interface_1 = require("../../../domain/repositories/user.interface"); +let DisableUserHandler = class DisableUserHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(id) { + const exists = await this.userRepository.existsById(id); + if (!exists) + throw new common_1.NotFoundException('User not found.'); + return this.userRepository.disable(id); + } +}; +exports.DisableUserHandler = DisableUserHandler; +exports.DisableUserHandler = DisableUserHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_interface_1.IUser]) +], DisableUserHandler); +//# sourceMappingURL=disable-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/disable-user.handler.js.map b/dist/modules/identity/application/handlers/user/disable-user.handler.js.map new file mode 100644 index 0000000..8aa2cea --- /dev/null +++ b/dist/modules/identity/application/handlers/user/disable-user.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"disable-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/disable-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AAG7D,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IACA;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE5D,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;CACF,CAAA;AATY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,kBAAkB,CAS9B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/enable-user.handler.js b/dist/modules/identity/application/handlers/user/enable-user.handler.js new file mode 100644 index 0000000..cebcbe8 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/enable-user.handler.js @@ -0,0 +1,32 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EnableUserHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_interface_1 = require("../../../domain/repositories/user.interface"); +let EnableUserHandler = class EnableUserHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(id) { + const exists = await this.userRepository.existsById(id); + if (!exists) + throw new common_1.NotFoundException('User not found.'); + return this.userRepository.enable(id); + } +}; +exports.EnableUserHandler = EnableUserHandler; +exports.EnableUserHandler = EnableUserHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_interface_1.IUser]) +], EnableUserHandler); +//# sourceMappingURL=enable-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/enable-user.handler.js.map b/dist/modules/identity/application/handlers/user/enable-user.handler.js.map new file mode 100644 index 0000000..9b501d8 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/enable-user.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"enable-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/enable-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AAG7D,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE5D,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC;CACF,CAAA;AATY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,iBAAiB,CAS7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/get-current-user.handler.js b/dist/modules/identity/application/handlers/user/get-current-user.handler.js new file mode 100644 index 0000000..39c215e --- /dev/null +++ b/dist/modules/identity/application/handlers/user/get-current-user.handler.js @@ -0,0 +1,21 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetCurrentUserHandler = void 0; +const common_1 = require("@nestjs/common"); +let GetCurrentUserHandler = class GetCurrentUserHandler { + async execute(currentUser) { + // currentUser is already domain user attached by CurrentUserGuard + return currentUser; + } +}; +exports.GetCurrentUserHandler = GetCurrentUserHandler; +exports.GetCurrentUserHandler = GetCurrentUserHandler = __decorate([ + (0, common_1.Injectable)() +], GetCurrentUserHandler); +//# sourceMappingURL=get-current-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/get-current-user.handler.js.map b/dist/modules/identity/application/handlers/user/get-current-user.handler.js.map new file mode 100644 index 0000000..e7a5319 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/get-current-user.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get-current-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/get-current-user.handler.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAIrC,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAChC,KAAK,CAAC,OAAO,CAAC,WAAqB;QACjC,kEAAkE;QAClE,OAAO,WAAW,CAAC;IACrB,CAAC;CACF,CAAA;AALY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;GACA,qBAAqB,CAKjC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/get-user.handler.js b/dist/modules/identity/application/handlers/user/get-user.handler.js new file mode 100644 index 0000000..29b5ea2 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/get-user.handler.js @@ -0,0 +1,29 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetUserHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_interface_1 = require("../../../domain/repositories/user.interface"); +let GetUserHandler = class GetUserHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(id) { + return this.userRepository.getById(id); + } +}; +exports.GetUserHandler = GetUserHandler; +exports.GetUserHandler = GetUserHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_interface_1.IUser]) +], GetUserHandler); +//# sourceMappingURL=get-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/get-user.handler.js.map b/dist/modules/identity/application/handlers/user/get-user.handler.js.map new file mode 100644 index 0000000..3c5bb24 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/get-user.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/get-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,gFAAoE;AAG7D,IAAM,cAAc,GAApB,MAAM,cAAc;IACI;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;CACF,CAAA;AANY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,cAAc,CAM1B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/get-users.handler.js b/dist/modules/identity/application/handlers/user/get-users.handler.js new file mode 100644 index 0000000..1c4587d --- /dev/null +++ b/dist/modules/identity/application/handlers/user/get-users.handler.js @@ -0,0 +1,37 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GetUsersHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_interface_1 = require("../../../domain/repositories/user.interface"); +let GetUsersHandler = class GetUsersHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(query) { + const res = await this.userRepository.find({ + page: query.page, + limit: query.limit, + search: query.search || null, + isActive: query.isActive !== undefined ? query.isActive : null, + deleted: query.deleted !== undefined ? query.deleted : null, + roleId: query.roleId || null, + }); + return res; + } +}; +exports.GetUsersHandler = GetUsersHandler; +exports.GetUsersHandler = GetUsersHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_interface_1.IUser]) +], GetUsersHandler); +//# sourceMappingURL=get-users.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/get-users.handler.js.map b/dist/modules/identity/application/handlers/user/get-users.handler.js.map new file mode 100644 index 0000000..3b2604e --- /dev/null +++ b/dist/modules/identity/application/handlers/user/get-users.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get-users.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/get-users.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,gFAAoE;AAG7D,IAAM,eAAe,GAArB,MAAM,eAAe;IACG;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,KAOb;QACC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC;YACzC,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;YAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;YAC9D,OAAO,EAAE,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;YAC3D,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;SAC7B,CAAC,CAAC;QAEH,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAA;AAtBY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,eAAe,CAsB3B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/remove-permission.handler.js b/dist/modules/identity/application/handlers/user/remove-permission.handler.js new file mode 100644 index 0000000..936c638 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/remove-permission.handler.js @@ -0,0 +1,33 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RemovePermissionHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_interface_1 = require("../../../domain/repositories/user.interface"); +let RemovePermissionHandler = class RemovePermissionHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(userId, permissionId) { + const user = await this.userRepository.getById(userId); + if (!user) + throw new common_1.NotFoundException('User not found.'); + user.removePermission(permissionId); + return this.userRepository.update(user); + } +}; +exports.RemovePermissionHandler = RemovePermissionHandler; +exports.RemovePermissionHandler = RemovePermissionHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_interface_1.IUser]) +], RemovePermissionHandler); +//# sourceMappingURL=remove-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/remove-permission.handler.js.map b/dist/modules/identity/application/handlers/user/remove-permission.handler.js.map new file mode 100644 index 0000000..5110506 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/remove-permission.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"remove-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/remove-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AAG7D,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,YAAoB;QAChD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;QAEpC,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF,CAAA;AAXY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,uBAAuB,CAWnC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/remove-role.handler.js b/dist/modules/identity/application/handlers/user/remove-role.handler.js new file mode 100644 index 0000000..93010d3 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/remove-role.handler.js @@ -0,0 +1,33 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RemoveRoleHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_interface_1 = require("../../../domain/repositories/user.interface"); +let RemoveRoleHandler = class RemoveRoleHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(userId, roleId) { + const user = await this.userRepository.getById(userId); + if (!user) + throw new common_1.NotFoundException('User not found.'); + user.removeRole(roleId); + return this.userRepository.update(user); + } +}; +exports.RemoveRoleHandler = RemoveRoleHandler; +exports.RemoveRoleHandler = RemoveRoleHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_interface_1.IUser]) +], RemoveRoleHandler); +//# sourceMappingURL=remove-role.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/remove-role.handler.js.map b/dist/modules/identity/application/handlers/user/remove-role.handler.js.map new file mode 100644 index 0000000..750bb57 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/remove-role.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"remove-role.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/remove-role.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AAG7D,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,MAAc;QAC1C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAExB,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF,CAAA;AAXY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,iBAAiB,CAW7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/restore-user.handler.js b/dist/modules/identity/application/handlers/user/restore-user.handler.js new file mode 100644 index 0000000..0b0cd79 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/restore-user.handler.js @@ -0,0 +1,32 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RestoreUserHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_interface_1 = require("../../../domain/repositories/user.interface"); +let RestoreUserHandler = class RestoreUserHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(id) { + const exists = await this.userRepository.existsById(id); + if (!exists) + throw new common_1.NotFoundException('User not found.'); + return this.userRepository.restore(id); + } +}; +exports.RestoreUserHandler = RestoreUserHandler; +exports.RestoreUserHandler = RestoreUserHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_interface_1.IUser]) +], RestoreUserHandler); +//# sourceMappingURL=restore-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/restore-user.handler.js.map b/dist/modules/identity/application/handlers/user/restore-user.handler.js.map new file mode 100644 index 0000000..9a5b0c1 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/restore-user.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"restore-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/restore-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AAG7D,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IACA;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE5D,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;CACF,CAAA;AATY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,kBAAkB,CAS9B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/sync-identity.handler.js b/dist/modules/identity/application/handlers/user/sync-identity.handler.js new file mode 100644 index 0000000..cc1980f --- /dev/null +++ b/dist/modules/identity/application/handlers/user/sync-identity.handler.js @@ -0,0 +1,92 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SyncIdentityHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_interface_1 = require("../../../domain/repositories/user.interface"); +const role_interface_1 = require("../../../domain/repositories/role.interface"); +const i_auth_config_1 = require("../../config/i-auth-config"); +const user_entity_1 = require("../../../domain/entities/user.entity"); +let SyncIdentityHandler = class SyncIdentityHandler { + userRepository; + roleRepository; + authConfig; + constructor(userRepository, roleRepository, authConfig) { + this.userRepository = userRepository; + this.roleRepository = roleRepository; + this.authConfig = authConfig; + } + async execute(identity) { + const { sub, preferred_username, email } = identity; + if (!sub) + throw new common_1.UnauthorizedException('Invalid identity payload.'); + // Try find by authentikId + let user = await this.userRepository.findByAuthentikId(sub); + const syncEmail = this.authConfig.syncEmail(); + const syncUsername = this.authConfig.syncUsername(); + if (!user) { + // User does not exist locally: create minimal local record per new architecture + const username = preferred_username || email || sub; + const userEntity = user_entity_1.UserData.create({ username, email: email || '', authentikId: sub }); + // assign default role if available + try { + const defaultRole = await this.roleRepository.getDefaultRole(); + if (defaultRole) { + userEntity.assignRole(defaultRole); + } + } + catch (e) { + // ignore if role repo not available or no default role + } + // persist local user + user = await this.userRepository.create(userEntity); + // return freshly created user + return user; + } + else { + let changed = false; + if (syncEmail && email && user.email !== email) { + user.changeEmail(email); + changed = true; + // prepare event class instance if needed (no dispatch) + } + if (syncUsername && preferred_username && user.username !== preferred_username) { + user.changeUsername(preferred_username); + changed = true; + // prepare event class instance if needed (no dispatch) + } + // update last seen + user.touchLastSeen(); + changed = true; + if (changed) { + user = await this.userRepository.update(user); + } + } + // Validations + if (!user.isActive) { + // Authentication succeeded but user is disabled + throw new common_1.ForbiddenException('User is not active.'); + } + if (user.deletedAt) { + throw new common_1.ForbiddenException('User is deleted.'); + } + // Ensure roles/permissions loaded (repo should return includes) + return user; + } +}; +exports.SyncIdentityHandler = SyncIdentityHandler; +exports.SyncIdentityHandler = SyncIdentityHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_interface_1.IUser, + role_interface_1.IRole, + i_auth_config_1.IAuthConfig]) +], SyncIdentityHandler); +//# sourceMappingURL=sync-identity.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/sync-identity.handler.js.map b/dist/modules/identity/application/handlers/user/sync-identity.handler.js.map new file mode 100644 index 0000000..3431800 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/sync-identity.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"sync-identity.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/sync-identity.handler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/update-user.handler.js b/dist/modules/identity/application/handlers/user/update-user.handler.js new file mode 100644 index 0000000..e80e2da --- /dev/null +++ b/dist/modules/identity/application/handlers/user/update-user.handler.js @@ -0,0 +1,45 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UpdateUserHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_interface_1 = require("../../../domain/repositories/user.interface"); +let UpdateUserHandler = class UpdateUserHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(id, dto) { + // Password management is not allowed in RayLab Core + if (dto.password) + throw new common_1.BadRequestException('Password management is not allowed.'); + const user = await this.userRepository.getById(id); + if (!user) + throw new common_1.NotFoundException('User not found.'); + // Only allow updating profile fields: name, email, metadata, storage settings + if (dto.name !== undefined) + user.changeUsername(dto.name); + if (dto.email !== undefined) + user.changeEmail(dto.email); + // metadata field not handled at domain level yet + if (dto.storageQuota !== undefined) + user.setStorageQuota(dto.storageQuota); + if (dto.storageUsed !== undefined) + user.setStorageUsed(dto.storageUsed); + return this.userRepository.update(user); + } +}; +exports.UpdateUserHandler = UpdateUserHandler; +exports.UpdateUserHandler = UpdateUserHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_interface_1.IUser]) +], UpdateUserHandler); +//# sourceMappingURL=update-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/update-user.handler.js.map b/dist/modules/identity/application/handlers/user/update-user.handler.js.map new file mode 100644 index 0000000..4687313 --- /dev/null +++ b/dist/modules/identity/application/handlers/user/update-user.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"update-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/update-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAoF;AACpF,gFAAoE;AAI7D,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,GAAkB;QAC1C,oDAAoD;QACpD,IAAK,GAAW,CAAC,QAAQ;YAAE,MAAM,IAAI,4BAAmB,CAAC,qCAAqC,CAAC,CAAC;QAEhG,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,8EAA8E;QAC9E,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS;YAAE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1D,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzD,iDAAiD;QACjD,IAAK,GAAW,CAAC,YAAY,KAAK,SAAS;YAAE,IAAI,CAAC,eAAe,CAAE,GAAW,CAAC,YAAY,CAAC,CAAC;QAC7F,IAAK,GAAW,CAAC,WAAW,KAAK,SAAS;YAAE,IAAI,CAAC,cAAc,CAAE,GAAW,CAAC,WAAW,CAAC,CAAC;QAE1F,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF,CAAA;AAnBY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,iBAAiB,CAmB7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/services/user.service.js b/dist/modules/identity/application/services/user.service.js new file mode 100644 index 0000000..74f5ebf --- /dev/null +++ b/dist/modules/identity/application/services/user.service.js @@ -0,0 +1,37 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var UserService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../../../shared/prisma.service"); +const user_interface_1 = require("../../domain/repositories/user.interface"); +let UserService = UserService_1 = class UserService { + prisma; + userRepository; + logger = new common_1.Logger(UserService_1.name); + constructor(prisma, userRepository) { + this.prisma = prisma; + this.userRepository = userRepository; + } + async createUser(input) { + // Provisioning to external Identity Provider (Authentik) has been disabled. + // All users must be created in Authentik first. RayLab will create local record on first successful login. + throw new common_1.BadRequestException('Provisioning disabled: create users in Authentik and login to sync to RayLab'); + } +}; +exports.UserService = UserService; +exports.UserService = UserService = UserService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + user_interface_1.IUser]) +], UserService); +//# sourceMappingURL=user.service.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/services/user.service.js.map b/dist/modules/identity/application/services/user.service.js.map new file mode 100644 index 0000000..b3e3655 --- /dev/null +++ b/dist/modules/identity/application/services/user.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"user.service.js","sourceRoot":"","sources":["../../../../../src/modules/identity/application/services/user.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAyE;AACzE,sEAAkE;AAClE,6EAAiE;AAI1D,IAAM,WAAW,mBAAjB,MAAM,WAAW;IAIH;IACA;IAJF,MAAM,GAAG,IAAI,eAAM,CAAC,aAAW,CAAC,IAAI,CAAC,CAAC;IAEvD,YACmB,MAAqB,EACrB,cAAqB;QADrB,WAAM,GAAN,MAAM,CAAe;QACrB,mBAAc,GAAd,cAAc,CAAO;IACrC,CAAC;IAEJ,KAAK,CAAC,UAAU,CAAC,KAAgF;QAC/F,4EAA4E;QAC5E,2GAA2G;QAC3G,MAAM,IAAI,4BAAmB,CAAC,8EAA8E,CAAC,CAAC;IAChH,CAAC;CACF,CAAA;AAbY,kCAAW;sBAAX,WAAW;IADvB,IAAA,mBAAU,GAAE;qCAKgB,8BAAa;QACL,sBAAK;GAL7B,WAAW,CAavB"} \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/permission.entity.js b/dist/modules/identity/domain/entities/permission.entity.js new file mode 100644 index 0000000..f1238a0 --- /dev/null +++ b/dist/modules/identity/domain/entities/permission.entity.js @@ -0,0 +1,33 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PermissionData = void 0; +class PermissionData { + id; + code; + name; + description; + createdAt; + updatedAt; + constructor(id, code, name, description, createdAt, updatedAt) { + this.id = id; + this.code = code; + this.name = name; + this.description = description; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } + changeName(name) { + this.name = name; + } + changeDescription(description) { + this.description = description; + } + changeCode(code) { + this.code = code; + } + static restore(props) { + return new PermissionData(props.id, props.code || props.name, props.name, props.description, props.createdAt, props.updatedAt); + } +} +exports.PermissionData = PermissionData; +//# sourceMappingURL=permission.entity.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/permission.entity.js.map b/dist/modules/identity/domain/entities/permission.entity.js.map new file mode 100644 index 0000000..cb03fd7 --- /dev/null +++ b/dist/modules/identity/domain/entities/permission.entity.js.map @@ -0,0 +1 @@ +{"version":3,"file":"permission.entity.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/entities/permission.entity.ts"],"names":[],"mappings":";;;AAAA,MAAa,cAAc;IAEP;IACT;IACA;IACA;IACA;IACA;IANT,YACkB,EAAU,EACnB,IAAY,EACZ,IAAY,EACZ,WAAmB,EACnB,SAAe,EACf,SAAe;QALN,OAAE,GAAF,EAAE,CAAQ;QACnB,SAAI,GAAJ,IAAI,CAAQ;QACZ,SAAI,GAAJ,IAAI,CAAQ;QACZ,gBAAW,GAAX,WAAW,CAAQ;QACnB,cAAS,GAAT,SAAS,CAAM;QACf,cAAS,GAAT,SAAS,CAAM;IACrB,CAAC;IAEJ,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,iBAAiB,CAAC,WAAmB;QACnC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,KAOd;QACC,OAAO,IAAI,cAAc,CACvB,KAAK,CAAC,EAAE,EACR,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,EACxB,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,SAAS,CAChB,CAAC;IACJ,CAAC;CACF;AAvCD,wCAuCC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/role.entity.js b/dist/modules/identity/domain/entities/role.entity.js new file mode 100644 index 0000000..5234e7d --- /dev/null +++ b/dist/modules/identity/domain/entities/role.entity.js @@ -0,0 +1,51 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RoleData = void 0; +class RoleData { + id; + code; + name; + description; + permissions; + isDefault; + createdAt; + updatedAt; + constructor(id, code, name, description, permissions, isDefault, createdAt, updatedAt) { + this.id = id; + this.code = code; + this.name = name; + this.description = description; + this.permissions = permissions; + this.isDefault = isDefault; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } + hasPermissions(permissionId) { + return this.permissions.some(x => x.id.toLowerCase() === permissionId.toLowerCase()); + } + assignPermission(permissionData) { + if (this.hasPermissions(permissionData.id)) + throw new Error("Permission sudah dimiliki."); + this.permissions.push(permissionData); + } + removePermission(permissionId) { + const idx = this.permissions.findIndex(p => p.id.toLowerCase() === permissionId.toLowerCase()); + if (idx === -1) + throw new Error('Permission tidak ditemukan pada role.'); + this.permissions.splice(idx, 1); + } + changeName(name) { + this.name = name; + } + changeDescription(description) { + this.description = description; + } + setDefault(isDefault) { + this.isDefault = isDefault; + } + static restore(props) { + return new RoleData(props.id, props.code, props.name, props.description, props.permissions || [], props.isDefault ?? false, props.createdAt, props.updatedAt); + } +} +exports.RoleData = RoleData; +//# sourceMappingURL=role.entity.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/role.entity.js.map b/dist/modules/identity/domain/entities/role.entity.js.map new file mode 100644 index 0000000..22436e0 --- /dev/null +++ b/dist/modules/identity/domain/entities/role.entity.js.map @@ -0,0 +1 @@ +{"version":3,"file":"role.entity.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/entities/role.entity.ts"],"names":[],"mappings":";;;AAEA,MAAa,QAAQ;IAED;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IART,YACkB,EAAU,EACnB,IAAY,EACZ,IAAY,EACZ,WAAmB,EACnB,WAA6B,EAC7B,SAAkB,EAClB,SAAe,EACf,SAAe;QAPN,OAAE,GAAF,EAAE,CAAQ;QACnB,SAAI,GAAJ,IAAI,CAAQ;QACZ,SAAI,GAAJ,IAAI,CAAQ;QACZ,gBAAW,GAAX,WAAW,CAAQ;QACnB,gBAAW,GAAX,WAAW,CAAkB;QAC7B,cAAS,GAAT,SAAS,CAAS;QAClB,cAAS,GAAT,SAAS,CAAM;QACf,cAAS,GAAT,SAAS,CAAM;IACrB,CAAC;IAEJ,cAAc,CAAC,YAAoB;QACjC,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;IACvF,CAAC;IAED,gBAAgB,CAAC,cAA8B;QAC7C,IAAI,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAE1F,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACxC,CAAC;IAED,gBAAgB,CAAC,YAAoB;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/F,IAAI,GAAG,KAAK,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QACzE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,iBAAiB,CAAC,WAAmB;QACnC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;IAED,UAAU,CAAC,SAAkB;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,KASd;QACC,OAAO,IAAI,QAAQ,CACjB,KAAK,CAAC,EAAE,EACR,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,WAAW,IAAI,EAAE,EACvB,KAAK,CAAC,SAAS,IAAI,KAAK,EACxB,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,SAAS,CAChB,CAAC;IACJ,CAAC;CACF;AA7DD,4BA6DC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/user.entity.js b/dist/modules/identity/domain/entities/user.entity.js new file mode 100644 index 0000000..9b244a8 --- /dev/null +++ b/dist/modules/identity/domain/entities/user.entity.js @@ -0,0 +1,143 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserData = void 0; +class UserData { + id; + authentikId; + username; + email; + password; + roles; + permissions; + isActive; + deletedAt; + lastSeenAt; + storageQuota; + storageUsed; + constructor(id, authentikId, username, email, password, roles, permissions, isActive, deletedAt, lastSeenAt, storageQuota, storageUsed) { + this.id = id; + this.authentikId = authentikId; + this.username = username; + this.email = email; + this.password = password; + this.roles = roles; + this.permissions = permissions; + this.isActive = isActive; + this.deletedAt = deletedAt; + this.lastSeenAt = lastSeenAt; + this.storageQuota = storageQuota; + this.storageUsed = storageUsed; + } + //#region Create + static create(data) { + const quota = data.storageQuota !== undefined ? data.storageQuota : 10737418240; // 10 GB + const used = data.storageUsed !== undefined ? data.storageUsed : 0; + return new UserData(crypto.randomUUID(), data.authentikId || null, data.username, data.email, data.password ?? null, [], [], true, null, null, quota, used); + } + //#endregion + //#region Read + hasRole(roleId) { + return this.roles.some(x => x.id.toLowerCase() === roleId.toLowerCase()); + } + assignRole(roleData) { + if (this.hasRole(roleData.id)) + throw new Error("Role Sudah dimiliki."); + this.roles.push(roleData); + } + hasPermissions(permissionId) { + return this.permissions.some(x => x.id.toLowerCase() === permissionId.toLowerCase()); + } + hasPermission(permission) { + const byPerm = this.permissions.some(x => x.id.toLowerCase() === permission.toLowerCase() || x.name.toLowerCase() === permission.toLowerCase()); + if (byPerm) + return true; + // check roles + return this.roles.some(r => r.permissions.some(p => p.id.toLowerCase() === permission.toLowerCase() || p.name.toLowerCase() === permission.toLowerCase())); + } + assignPermission(permissionData) { + if (this.hasPermissions(permissionData.id)) + throw new Error("Permission sudah dimiliki."); + this.permissions.push(permissionData); + } + //#endregion + //#region Update + changeEmail(email) { + this.email = email; + } + changeUsername(username) { + this.username = username; + } + touchLastSeen() { + this.lastSeenAt = new Date(); + } + enable() { + this.isActive = true; + } + disable() { + this.isActive = false; + } + //#endregion + //#region Delete + softDelete() { + this.deletedAt = new Date(); + this.isActive = false; + } + restoreInstance() { + this.deletedAt = null; + } + //#endregion + static restore(props) { + return new UserData(props.id, props.authentikId || null, props.username, props.email, props.password, props.roles || [], props.permissions || [], props.isActive !== undefined ? props.isActive : true, props.deletedAt || null, props.lastSeenAt || null, props.storageQuota !== undefined && props.storageQuota !== null ? props.storageQuota : 10737418240, props.storageUsed !== undefined && props.storageUsed !== null ? props.storageUsed : 0); + } + removeRole(roleId) { + const idx = this.roles.findIndex(r => r.id.toLowerCase() === roleId.toLowerCase()); + if (idx === -1) + throw new Error('Role tidak ditemukan pada user.'); + this.roles.splice(idx, 1); + } + removePermission(permissionId) { + const idx = this.permissions.findIndex(p => p.id.toLowerCase() === permissionId.toLowerCase()); + if (idx === -1) + throw new Error('Permission tidak ditemukan pada user.'); + this.permissions.splice(idx, 1); + } + // Storage helpers + setStorageQuota(bytes) { + if (bytes < 0) + throw new Error('storageQuota must be >= 0'); + if (this.storageUsed > bytes) + throw new Error('storageQuota cannot be less than storageUsed'); + this.storageQuota = bytes; + } + setStorageUsed(bytes) { + if (bytes < 0) + throw new Error('storageUsed must be >= 0'); + if (bytes > this.storageQuota) + throw new Error('storageUsed cannot exceed storageQuota'); + this.storageUsed = bytes; + } + // Response helper for API + toResponse() { + const remaining = this.storageQuota - this.storageUsed; + const usagePercentage = this.storageQuota > 0 ? Math.round((this.storageUsed / this.storageQuota) * 100) : 0; + return { + id: this.id, + authentikId: this.authentikId, + username: this.username, + email: this.email, + roles: this.roles, + permissions: this.permissions, + isActive: this.isActive, + deletedAt: this.deletedAt, + lastSeenAt: this.lastSeenAt, + storage: { + quota: this.storageQuota, + used: this.storageUsed, + remaining, + usagePercentage, + }, + }; + } +} +exports.UserData = UserData; +//# sourceMappingURL=user.entity.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/user.entity.js.map b/dist/modules/identity/domain/entities/user.entity.js.map new file mode 100644 index 0000000..876a488 --- /dev/null +++ b/dist/modules/identity/domain/entities/user.entity.js.map @@ -0,0 +1 @@ +{"version":3,"file":"user.entity.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/entities/user.entity.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-created.event.js b/dist/modules/identity/domain/events/user-created.event.js new file mode 100644 index 0000000..f9edf2c --- /dev/null +++ b/dist/modules/identity/domain/events/user-created.event.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserCreatedEvent = void 0; +class UserCreatedEvent { + payload; + name = 'UserCreated'; + occurredAt = new Date(); + constructor(payload) { + this.payload = payload; + } +} +exports.UserCreatedEvent = UserCreatedEvent; +//# sourceMappingURL=user-created.event.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-created.event.js.map b/dist/modules/identity/domain/events/user-created.event.js.map new file mode 100644 index 0000000..32401a7 --- /dev/null +++ b/dist/modules/identity/domain/events/user-created.event.js.map @@ -0,0 +1 @@ +{"version":3,"file":"user-created.event.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/events/user-created.event.ts"],"names":[],"mappings":";;;AAEA,MAAa,gBAAgB;IAGC;IAFnB,IAAI,GAAG,aAAa,CAAC;IACrB,UAAU,GAAS,IAAI,IAAI,EAAE,CAAC;IACvC,YAA4B,OAAiF;QAAjF,YAAO,GAAP,OAAO,CAA0E;IAAG,CAAC;CAClH;AAJD,4CAIC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-disabled.event.js b/dist/modules/identity/domain/events/user-disabled.event.js new file mode 100644 index 0000000..8d3546b --- /dev/null +++ b/dist/modules/identity/domain/events/user-disabled.event.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserDisabledEvent = void 0; +class UserDisabledEvent { + payload; + name = 'UserDisabled'; + occurredAt = new Date(); + constructor(payload) { + this.payload = payload; + } +} +exports.UserDisabledEvent = UserDisabledEvent; +//# sourceMappingURL=user-disabled.event.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-disabled.event.js.map b/dist/modules/identity/domain/events/user-disabled.event.js.map new file mode 100644 index 0000000..dec6363 --- /dev/null +++ b/dist/modules/identity/domain/events/user-disabled.event.js.map @@ -0,0 +1 @@ +{"version":3,"file":"user-disabled.event.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/events/user-disabled.event.ts"],"names":[],"mappings":";;;AAEA,MAAa,iBAAiB;IAGA;IAFnB,IAAI,GAAG,cAAc,CAAC;IACtB,UAAU,GAAS,IAAI,IAAI,EAAE,CAAC;IACvC,YAA4B,OAA2B;QAA3B,YAAO,GAAP,OAAO,CAAoB;IAAG,CAAC;CAC5D;AAJD,8CAIC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-email-changed.event.js b/dist/modules/identity/domain/events/user-email-changed.event.js new file mode 100644 index 0000000..c5d1ddb --- /dev/null +++ b/dist/modules/identity/domain/events/user-email-changed.event.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserEmailChangedEvent = void 0; +class UserEmailChangedEvent { + payload; + name = 'UserEmailChanged'; + occurredAt = new Date(); + constructor(payload) { + this.payload = payload; + } +} +exports.UserEmailChangedEvent = UserEmailChangedEvent; +//# sourceMappingURL=user-email-changed.event.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-email-changed.event.js.map b/dist/modules/identity/domain/events/user-email-changed.event.js.map new file mode 100644 index 0000000..a57fb24 --- /dev/null +++ b/dist/modules/identity/domain/events/user-email-changed.event.js.map @@ -0,0 +1 @@ +{"version":3,"file":"user-email-changed.event.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/events/user-email-changed.event.ts"],"names":[],"mappings":";;;AAEA,MAAa,qBAAqB;IAGJ;IAFnB,IAAI,GAAG,kBAAkB,CAAC;IAC1B,UAAU,GAAS,IAAI,IAAI,EAAE,CAAC;IACvC,YAA4B,OAA+D;QAA/D,YAAO,GAAP,OAAO,CAAwD;IAAG,CAAC;CAChG;AAJD,sDAIC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-enabled.event.js b/dist/modules/identity/domain/events/user-enabled.event.js new file mode 100644 index 0000000..81a05d9 --- /dev/null +++ b/dist/modules/identity/domain/events/user-enabled.event.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserEnabledEvent = void 0; +class UserEnabledEvent { + payload; + name = 'UserEnabled'; + occurredAt = new Date(); + constructor(payload) { + this.payload = payload; + } +} +exports.UserEnabledEvent = UserEnabledEvent; +//# sourceMappingURL=user-enabled.event.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-enabled.event.js.map b/dist/modules/identity/domain/events/user-enabled.event.js.map new file mode 100644 index 0000000..dec602b --- /dev/null +++ b/dist/modules/identity/domain/events/user-enabled.event.js.map @@ -0,0 +1 @@ +{"version":3,"file":"user-enabled.event.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/events/user-enabled.event.ts"],"names":[],"mappings":";;;AAEA,MAAa,gBAAgB;IAGC;IAFnB,IAAI,GAAG,aAAa,CAAC;IACrB,UAAU,GAAS,IAAI,IAAI,EAAE,CAAC;IACvC,YAA4B,OAA2B;QAA3B,YAAO,GAAP,OAAO,CAAoB;IAAG,CAAC;CAC5D;AAJD,4CAIC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-username-changed.event.js b/dist/modules/identity/domain/events/user-username-changed.event.js new file mode 100644 index 0000000..dd9949a --- /dev/null +++ b/dist/modules/identity/domain/events/user-username-changed.event.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserUsernameChangedEvent = void 0; +class UserUsernameChangedEvent { + payload; + name = 'UserUsernameChanged'; + occurredAt = new Date(); + constructor(payload) { + this.payload = payload; + } +} +exports.UserUsernameChangedEvent = UserUsernameChangedEvent; +//# sourceMappingURL=user-username-changed.event.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-username-changed.event.js.map b/dist/modules/identity/domain/events/user-username-changed.event.js.map new file mode 100644 index 0000000..fc35c36 --- /dev/null +++ b/dist/modules/identity/domain/events/user-username-changed.event.js.map @@ -0,0 +1 @@ +{"version":3,"file":"user-username-changed.event.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/events/user-username-changed.event.ts"],"names":[],"mappings":";;;AAEA,MAAa,wBAAwB;IAGP;IAFnB,IAAI,GAAG,qBAAqB,CAAC;IAC7B,UAAU,GAAS,IAAI,IAAI,EAAE,CAAC;IACvC,YAA4B,OAAqE;QAArE,YAAO,GAAP,OAAO,CAA8D;IAAG,CAAC;CACtG;AAJD,4DAIC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/permission.interface.js b/dist/modules/identity/domain/repositories/permission.interface.js new file mode 100644 index 0000000..11c253e --- /dev/null +++ b/dist/modules/identity/domain/repositories/permission.interface.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.IPermission = void 0; +class IPermission { +} +exports.IPermission = IPermission; +//# sourceMappingURL=permission.interface.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/permission.interface.js.map b/dist/modules/identity/domain/repositories/permission.interface.js.map new file mode 100644 index 0000000..534e501 --- /dev/null +++ b/dist/modules/identity/domain/repositories/permission.interface.js.map @@ -0,0 +1 @@ +{"version":3,"file":"permission.interface.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/repositories/permission.interface.ts"],"names":[],"mappings":";;;AAEA,MAAsB,WAAW;CAShC;AATD,kCASC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/role.interface.js b/dist/modules/identity/domain/repositories/role.interface.js new file mode 100644 index 0000000..9119f3b --- /dev/null +++ b/dist/modules/identity/domain/repositories/role.interface.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.IRole = void 0; +class IRole { +} +exports.IRole = IRole; +//# sourceMappingURL=role.interface.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/role.interface.js.map b/dist/modules/identity/domain/repositories/role.interface.js.map new file mode 100644 index 0000000..27bc8fe --- /dev/null +++ b/dist/modules/identity/domain/repositories/role.interface.js.map @@ -0,0 +1 @@ +{"version":3,"file":"role.interface.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/repositories/role.interface.ts"],"names":[],"mappings":";;;AAEA,MAAsB,KAAK;CAY1B;AAZD,sBAYC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/user.interface.js b/dist/modules/identity/domain/repositories/user.interface.js new file mode 100644 index 0000000..3315c95 --- /dev/null +++ b/dist/modules/identity/domain/repositories/user.interface.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.IUser = void 0; +class IUser { +} +exports.IUser = IUser; +//# sourceMappingURL=user.interface.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/user.interface.js.map b/dist/modules/identity/domain/repositories/user.interface.js.map new file mode 100644 index 0000000..e657448 --- /dev/null +++ b/dist/modules/identity/domain/repositories/user.interface.js.map @@ -0,0 +1 @@ +{"version":3,"file":"user.interface.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/repositories/user.interface.ts"],"names":[],"mappings":";;;AAEA,MAAsB,KAAK;CA0B1B;AA1BD,sBA0BC"} \ No newline at end of file diff --git a/dist/modules/identity/identity.module.js b/dist/modules/identity/identity.module.js new file mode 100644 index 0000000..8899f4e --- /dev/null +++ b/dist/modules/identity/identity.module.js @@ -0,0 +1,113 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.IdentityModule = void 0; +const common_1 = require("@nestjs/common"); +const users_controller_1 = require("./presentation/controllers/users.controller"); +const roles_controller_1 = require("./presentation/controllers/roles.controller"); +const permissions_controller_1 = require("./presentation/controllers/permissions.controller"); +const prisma_service_1 = require("../../shared/prisma.service"); +const user_service_1 = require("./application/services/user.service"); +const prisma_user_repository_1 = require("./infrastructure/repositories/prisma-user.repository"); +const user_interface_1 = require("./domain/repositories/user.interface"); +const jwt_auth_guard_1 = require("../../core/auth/guards/jwt-auth.guard"); +const permission_guard_1 = require("../authorization/presentation/guards/permission.guard"); +const core_1 = require("@nestjs/core"); +const sync_identity_handler_1 = require("./application/handlers/user/sync-identity.handler"); +const current_user_guard_1 = require("./presentation/guards/current-user.guard"); +const prisma_role_repository_1 = require("./infrastructure/repositories/prisma-role.repository"); +const prisma_permission_repository_1 = require("./infrastructure/repositories/prisma-permission.repository"); +const role_interface_1 = require("./domain/repositories/role.interface"); +const permission_interface_1 = require("./domain/repositories/permission.interface"); +const i_auth_config_1 = require("./application/config/i-auth-config"); +const env_auth_config_1 = require("./application/config/env-auth-config"); +const auth_module_1 = require("../auth/auth.module"); +const authorization_module_1 = require("../authorization/authorization.module"); +const get_users_handler_1 = require("./application/handlers/user/get-users.handler"); +const get_user_handler_1 = require("./application/handlers/user/get-user.handler"); +const get_current_user_handler_1 = require("./application/handlers/user/get-current-user.handler"); +const enable_user_handler_1 = require("./application/handlers/user/enable-user.handler"); +const disable_user_handler_1 = require("./application/handlers/user/disable-user.handler"); +const delete_user_handler_1 = require("./application/handlers/user/delete-user.handler"); +const restore_user_handler_1 = require("./application/handlers/user/restore-user.handler"); +const update_user_handler_1 = require("./application/handlers/user/update-user.handler"); +const get_roles_handler_1 = require("./application/handlers/role/get-roles.handler"); +const get_role_handler_1 = require("./application/handlers/role/get-role.handler"); +const create_role_handler_1 = require("./application/handlers/role/create-role.handler"); +const update_role_handler_1 = require("./application/handlers/role/update-role.handler"); +const delete_role_handler_1 = require("./application/handlers/role/delete-role.handler"); +const assign_permission_handler_1 = require("./application/handlers/role/assign-permission.handler"); +const remove_permission_handler_1 = require("./application/handlers/role/remove-permission.handler"); +const get_permissions_handler_1 = require("./application/handlers/permission/get-permissions.handler"); +const get_permission_handler_1 = require("./application/handlers/permission/get-permission.handler"); +const create_permission_handler_1 = require("./application/handlers/permission/create-permission.handler"); +const update_permission_handler_1 = require("./application/handlers/permission/update-permission.handler"); +const delete_permission_handler_1 = require("./application/handlers/permission/delete-permission.handler"); +let IdentityModule = class IdentityModule { +}; +exports.IdentityModule = IdentityModule; +exports.IdentityModule = IdentityModule = __decorate([ + (0, common_1.Module)({ + imports: [auth_module_1.AuthModule, authorization_module_1.AuthorizationModule], + providers: [ + //#region User + // CreateUserHandler has been removed: provisioning disabled; users must be created in Authentik. + sync_identity_handler_1.SyncIdentityHandler, + get_users_handler_1.GetUsersHandler, + get_user_handler_1.GetUserHandler, + get_current_user_handler_1.GetCurrentUserHandler, + enable_user_handler_1.EnableUserHandler, + disable_user_handler_1.DisableUserHandler, + delete_user_handler_1.DeleteUserHandler, + restore_user_handler_1.RestoreUserHandler, + update_user_handler_1.UpdateUserHandler, + //#endregion + // role & permission handlers + get_roles_handler_1.GetRolesHandler, + get_role_handler_1.GetRoleHandler, + create_role_handler_1.CreateRoleHandler, + update_role_handler_1.UpdateRoleHandler, + delete_role_handler_1.DeleteRoleHandler, + assign_permission_handler_1.RoleAssignPermissionHandler, + remove_permission_handler_1.RoleRemovePermissionHandler, + get_permissions_handler_1.GetPermissionsHandler, + get_permission_handler_1.GetPermissionHandler, + create_permission_handler_1.CreatePermissionHandler, + update_permission_handler_1.UpdatePermissionHandler, + delete_permission_handler_1.DeletePermissionHandler, + jwt_auth_guard_1.JwtAuthGuard, + current_user_guard_1.CurrentUserGuard, + permission_guard_1.PermissionGuard, + core_1.Reflector, + prisma_service_1.PrismaService, + prisma_user_repository_1.PrismaUserRepository, + prisma_role_repository_1.PrismaRoleRepository, + prisma_permission_repository_1.PrismaPermissionRepository, + user_service_1.UserService, + update_user_handler_1.UpdateUserHandler, + { + provide: user_interface_1.IUser, + useClass: prisma_user_repository_1.PrismaUserRepository, + }, + { + provide: role_interface_1.IRole, + useClass: prisma_role_repository_1.PrismaRoleRepository, + }, + { + provide: permission_interface_1.IPermission, + useClass: prisma_permission_repository_1.PrismaPermissionRepository, + }, + { + provide: i_auth_config_1.IAuthConfig, + useClass: env_auth_config_1.EnvAuthConfig, + }, + ], + controllers: [users_controller_1.UsersController, roles_controller_1.RolesController, permissions_controller_1.PermissionsController], + }) +], IdentityModule); +//# sourceMappingURL=identity.module.js.map \ No newline at end of file diff --git a/dist/modules/identity/identity.module.js.map b/dist/modules/identity/identity.module.js.map new file mode 100644 index 0000000..661d266 --- /dev/null +++ b/dist/modules/identity/identity.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"identity.module.js","sourceRoot":"","sources":["../../../src/modules/identity/identity.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,kFAA8E;AAC9E,kFAA8E;AAC9E,8FAA0F;AAC1F,gEAA4D;AAC5D,sEAAkE;AAClE,iGAA4F;AAC5F,yEAA6D;AAG7D,0EAAqE;AAErE,4FAAwF;AACxF,uCAAyC;AACzC,6FAAwF;AACxF,iFAA4E;AAC5E,iGAA4F;AAC5F,6GAAwG;AACxG,yEAA6D;AAC7D,qFAAyE;AACzE,sEAAiE;AACjE,0EAAqE;AACrE,qDAAiD;AACjD,gFAA4E;AAG5E,qFAAgF;AAChF,mFAA8E;AAC9E,mGAA6F;AAC7F,yFAAoF;AACpF,2FAAsF;AACtF,yFAAoF;AACpF,2FAAsF;AACtF,yFAAoF;AAIpF,qFAAgF;AAChF,mFAA8E;AAC9E,yFAAoF;AACpF,yFAAoF;AACpF,yFAAoF;AACpF,qGAAoG;AACpG,qGAAoG;AAEpG,uGAAkG;AAClG,qGAAgG;AAChG,2GAAsG;AACtG,2GAAsG;AACtG,2GAAsG;AAmE/F,IAAM,cAAc,GAApB,MAAM,cAAc;CAAG,CAAA;AAAjB,wCAAc;yBAAd,cAAc;IAjE1B,IAAA,eAAM,EAAC;QACJ,OAAO,EAAE,CAAC,wBAAU,EAAE,0CAAmB,CAAC;QAC5C,SAAS,EAAE;YAEL,eAAe;YACnB,iGAAiG;YACjG,2CAAmB;YACnB,mCAAe;YAEf,iCAAc;YACd,gDAAqB;YACrB,uCAAiB;YACjB,yCAAkB;YAClB,uCAAiB;YACb,yCAAkB;YACtB,uCAAiB;YAEjB,YAAY;YAEZ,6BAA6B;YAC7B,mCAAe;YACf,iCAAc;YACd,uCAAiB;YACjB,uCAAiB;YACjB,uCAAiB;YACjB,uDAA2B;YAC3B,uDAA2B;YAEvB,+CAAqB;YACzB,6CAAoB;YACpB,mDAAuB;YACvB,mDAAuB;YACvB,mDAAuB;YAEvB,6BAAY;YAEZ,qCAAgB;YAChB,kCAAe;YACf,gBAAS;YACT,8BAAa;YACb,6CAAoB;YACpB,6CAAoB;YACpB,yDAA0B;YACtB,0BAAW;YACf,uCAAiB;YAEjB;gBACE,OAAO,EAAE,sBAAK;gBACd,QAAQ,EAAE,6CAAoB;aAC/B;YACD;gBACE,OAAO,EAAE,sBAAK;gBACd,QAAQ,EAAE,6CAAoB;aAC/B;YACD;gBACE,OAAO,EAAE,kCAAW;gBACpB,QAAQ,EAAE,yDAA0B;aACrC;YACD;gBACE,OAAO,EAAE,2BAAW;gBACpB,QAAQ,EAAE,+BAAa;aACxB;SACI;QACP,WAAW,EAAE,CAAC,kCAAe,EAAE,kCAAe,EAAE,8CAAqB,CAAC;KACvE,CAAC;GACW,cAAc,CAAG"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js b/dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js new file mode 100644 index 0000000..a879ac7 --- /dev/null +++ b/dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js @@ -0,0 +1,26 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrismaPermissionMapper = void 0; +const permission_entity_1 = require("../../domain/entities/permission.entity"); +class PrismaPermissionMapper { + static toDomain(model) { + return permission_entity_1.PermissionData.restore({ + id: model.id, + code: model.code || model.name, + name: model.name, + description: model.description, + createdAt: model.createdAt, + updatedAt: model.updatedAt, + }); + } + static toPersistence(permission) { + return { + id: permission.id, + code: permission.code, + name: permission.name, + description: permission.description, + }; + } +} +exports.PrismaPermissionMapper = PrismaPermissionMapper; +//# sourceMappingURL=prisma-permission.mapper.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js.map b/dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js.map new file mode 100644 index 0000000..9d55fc3 --- /dev/null +++ b/dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prisma-permission.mapper.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/mappers/prisma-permission.mapper.ts"],"names":[],"mappings":";;;AAAA,+EAAyE;AAEzE,MAAa,sBAAsB;IACjC,MAAM,CAAC,QAAQ,CAAC,KAAU;QACxB,OAAO,kCAAc,CAAC,OAAO,CAAC;YAC5B,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI;YAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,UAA0B;QAC7C,OAAO;YACL,EAAE,EAAE,UAAU,CAAC,EAAE;YACjB,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,WAAW,EAAE,UAAU,CAAC,WAAW;SACpC,CAAC;IACJ,CAAC;CACF;AApBD,wDAoBC"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js b/dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js new file mode 100644 index 0000000..846e987 --- /dev/null +++ b/dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js @@ -0,0 +1,31 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrismaRoleMapper = void 0; +const role_entity_1 = require("../../domain/entities/role.entity"); +const permission_entity_1 = require("../../domain/entities/permission.entity"); +class PrismaRoleMapper { + static toDomain(model) { + const permissions = (model.permissions || []).map((p) => { + const perm = p.permission || p; // p may be RolePermission with nested permission or permission itself + return permission_entity_1.PermissionData.restore({ + id: perm.id, + name: perm.name, + description: perm.description, + createdAt: perm.createdAt, + updatedAt: perm.updatedAt, + }); + }); + return role_entity_1.RoleData.restore({ + id: model.id, + code: model.code, + name: model.name, + description: model.description, + permissions, + isDefault: model.isDefault ?? false, + createdAt: model.createdAt, + updatedAt: model.updatedAt, + }); + } +} +exports.PrismaRoleMapper = PrismaRoleMapper; +//# sourceMappingURL=prisma-role.mapper.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js.map b/dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js.map new file mode 100644 index 0000000..370d49b --- /dev/null +++ b/dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prisma-role.mapper.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/mappers/prisma-role.mapper.ts"],"names":[],"mappings":";;;AAAA,mEAA6D;AAC7D,+EAAyE;AAEzE,MAAa,gBAAgB;IAC3B,MAAM,CAAC,QAAQ,CAAC,KAAU;QACxB,MAAM,WAAW,GAAqB,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE;YAC7E,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,sEAAsE;YACtG,OAAO,kCAAc,CAAC,OAAO,CAAC;gBAC5B,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,OAAO,sBAAQ,CAAC,OAAO,CAAC;YACtB,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,WAAW;YACX,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,KAAK;YACnC,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B,CAAC,CAAC;IACL,CAAC;CACF;AAxBD,4CAwBC"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js b/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js new file mode 100644 index 0000000..65dfcb2 --- /dev/null +++ b/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js @@ -0,0 +1,69 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrismaUserMapper = void 0; +const user_entity_1 = require("../../domain/entities/user.entity"); +const role_entity_1 = require("../../domain/entities/role.entity"); +const permission_entity_1 = require("../../domain/entities/permission.entity"); +class PrismaUserMapper { + static toDomain(model) { + const roles = (model.roles || []).map((ur) => { + const r = ur.role; + const permissions = (r?.permissions || []).map((rp) => permission_entity_1.PermissionData.restore({ + id: rp.permission.id, + name: rp.permission.name, + description: rp.permission.description, + createdAt: rp.permission.createdAt, + updatedAt: rp.permission.updatedAt, + })); + return role_entity_1.RoleData.restore({ + id: r.id, + code: r.code, + name: r.name, + description: r.description, + permissions, + createdAt: r.createdAt, + updatedAt: r.updatedAt, + }); + }); + const permissions = (model.permissions || []).map((up) => permission_entity_1.PermissionData.restore({ + id: up.permission.id, + name: up.permission.name, + description: up.permission.description, + createdAt: up.permission.createdAt, + updatedAt: up.permission.updatedAt, + })); + return user_entity_1.UserData.restore({ + id: model.id, + authentikId: model.authentikUserId || model.authentikId || null, + username: model.username || model.name || model.username, + email: model.email, + password: model.password ?? null, + roles, + permissions, + isActive: model.isActive ?? true, + deletedAt: model.deletedAt || null, + lastSeenAt: model.lastSeenAt || null, + storageQuota: model.storageQuota !== undefined && model.storageQuota !== null ? Number(model.storageQuota) : undefined, + storageUsed: model.storageUsed !== undefined && model.storageUsed !== null ? Number(model.storageUsed) : undefined, + }); + } + static toPersistence(userData) { + return { + id: userData.id, + authentikUserId: userData.authentikUserId || userData.authentikId, + authentikId: userData.authentikId, + username: userData.username, + email: userData.email, + password: userData.password ?? null, + isActive: userData.isActive, + deletedAt: userData.deletedAt, + lastSeenAt: userData.lastSeenAt, + lastSyncedAt: userData.lastSyncedAt, + syncStatus: userData.syncStatus, + storageQuota: userData.storageQuota, + storageUsed: userData.storageUsed, + }; + } +} +exports.PrismaUserMapper = PrismaUserMapper; +//# sourceMappingURL=prisma-user.mapper.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js.map b/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js.map new file mode 100644 index 0000000..d9962ec --- /dev/null +++ b/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prisma-user.mapper.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js b/dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js new file mode 100644 index 0000000..f31859d --- /dev/null +++ b/dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js @@ -0,0 +1,67 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrismaPermissionRepository = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../../../shared/prisma.service"); +const prisma_permission_mapper_1 = require("../mappers/prisma-permission.mapper"); +let PrismaPermissionRepository = class PrismaPermissionRepository { + prisma; + constructor(prisma) { + this.prisma = prisma; + } + async find(params) { + const page = params.page && params.page > 0 ? params.page : 1; + const limit = params.limit && params.limit > 0 ? params.limit : 10; + const where = {}; + if (params.search) { + where.OR = [ + { name: { contains: params.search, mode: 'insensitive' } }, + { code: { contains: params.search, mode: 'insensitive' } }, + { description: { contains: params.search, mode: 'insensitive' } }, + ]; + } + const [total, items] = await Promise.all([ + this.prisma.permission.count({ where }), + this.prisma.permission.findMany({ where, skip: (page - 1) * limit, take: limit, orderBy: { createdAt: 'desc' } }), + ]); + return { data: items.map(i => prisma_permission_mapper_1.PrismaPermissionMapper.toDomain(i)), total }; + } + async getById(id) { + const p = await this.prisma.permission.findUnique({ where: { id } }); + if (!p) + throw new Error('Permission not found.'); + return prisma_permission_mapper_1.PrismaPermissionMapper.toDomain(p); + } + async create(permission) { + const base = prisma_permission_mapper_1.PrismaPermissionMapper.toPersistence(permission); + const created = await this.prisma.permission.create({ data: base }); + return prisma_permission_mapper_1.PrismaPermissionMapper.toDomain(created); + } + async update(permission) { + const base = prisma_permission_mapper_1.PrismaPermissionMapper.toPersistence(permission); + const updated = await this.prisma.permission.update({ where: { id: permission.id }, data: base }); + return prisma_permission_mapper_1.PrismaPermissionMapper.toDomain(updated); + } + async delete(id) { + await this.prisma.permission.delete({ where: { id } }); + } + async findRoleIdsByPermission(permissionId) { + const rows = await this.prisma.rolePermission.findMany({ where: { permissionId }, select: { roleId: true } }); + return rows.map(r => r.roleId); + } +}; +exports.PrismaPermissionRepository = PrismaPermissionRepository; +exports.PrismaPermissionRepository = PrismaPermissionRepository = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], PrismaPermissionRepository); +//# sourceMappingURL=prisma-permission.repository.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js.map b/dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js.map new file mode 100644 index 0000000..9c8feb7 --- /dev/null +++ b/dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prisma-permission.repository.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/repositories/prisma-permission.repository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-role.repository.js b/dist/modules/identity/infrastructure/repositories/prisma-role.repository.js new file mode 100644 index 0000000..3e86090 --- /dev/null +++ b/dist/modules/identity/infrastructure/repositories/prisma-role.repository.js @@ -0,0 +1,110 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrismaRoleRepository = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../../../shared/prisma.service"); +const prisma_role_mapper_1 = require("../mappers/prisma-role.mapper"); +let PrismaRoleRepository = class PrismaRoleRepository { + prisma; + constructor(prisma) { + this.prisma = prisma; + } + async getDefaultRole() { + const role = await this.prisma.role.findFirst({ + where: { isDefault: true }, + include: { permissions: { include: { permission: true } } }, + }); + if (!role) + return null; + return prisma_role_mapper_1.PrismaRoleMapper.toDomain(role); + } + async findById(roleId) { + const role = await this.prisma.role.findUnique({ + where: { id: roleId }, + include: { permissions: { include: { permission: true } } }, + }); + if (!role) + throw new Error('Role not found.'); + return prisma_role_mapper_1.PrismaRoleMapper.toDomain(role); + } + async find(params) { + const page = params.page && params.page > 0 ? params.page : 1; + const limit = params.limit && params.limit > 0 ? params.limit : 10; + const where = {}; + if (params.search) { + where.OR = [ + { name: { contains: params.search, mode: 'insensitive' } }, + { code: { contains: params.search, mode: 'insensitive' } }, + { description: { contains: params.search, mode: 'insensitive' } }, + ]; + } + const [total, items] = await Promise.all([ + this.prisma.role.count({ where }), + this.prisma.role.findMany({ where, skip: (page - 1) * limit, take: limit, orderBy: { createdAt: 'desc' }, include: { permissions: { include: { permission: true } } } }), + ]); + return { data: items.map(i => prisma_role_mapper_1.PrismaRoleMapper.toDomain(i)), total }; + } + async create(role) { + const base = { + id: role.id, + code: role.code, + name: role.name, + description: role.description, + isDefault: role.isDefault, + }; + if (role.permissions && role.permissions.length > 0) { + base.permissions = { + create: role.permissions.map(p => ({ permission: { connect: { id: p.id } }, assignedBy: 'system' })), + }; + } + const created = await this.prisma.role.create({ data: base, include: { permissions: { include: { permission: true } } } }); + return prisma_role_mapper_1.PrismaRoleMapper.toDomain(created); + } + async update(role) { + const base = { + code: role.code, + name: role.name, + description: role.description, + isDefault: role.isDefault, + }; + // sync permissions via transaction + const permIds = role.permissions ? role.permissions.map(p => p.id) : []; + await this.prisma.$transaction(async (prisma) => { + const currentPerms = await prisma.rolePermission.findMany({ where: { roleId: role.id } }); + const currentIds = currentPerms.map(p => p.permissionId); + const toAdd = permIds.filter(id => !currentIds.includes(id)); + const toRemove = currentIds.filter(id => !permIds.includes(id)); + if (toRemove.length > 0) { + await prisma.rolePermission.deleteMany({ where: { roleId: role.id, permissionId: { in: toRemove } } }); + } + if (toAdd.length > 0) { + await prisma.rolePermission.createMany({ data: toAdd.map(pid => ({ roleId: role.id, permissionId: pid, assignedBy: 'system' })), skipDuplicates: true }); + } + await prisma.role.update({ where: { id: role.id }, data: base }); + }); + const updated = await this.prisma.role.findUnique({ where: { id: role.id }, include: { permissions: { include: { permission: true } } } }); + return prisma_role_mapper_1.PrismaRoleMapper.toDomain(updated); + } + async delete(roleId) { + await this.prisma.role.delete({ where: { id: roleId } }); + } + async getAssignedUserIds(roleId) { + const rows = await this.prisma.userRole.findMany({ where: { roleId }, select: { userId: true } }); + return rows.map(r => r.userId); + } +}; +exports.PrismaRoleRepository = PrismaRoleRepository; +exports.PrismaRoleRepository = PrismaRoleRepository = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], PrismaRoleRepository); +//# sourceMappingURL=prisma-role.repository.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-role.repository.js.map b/dist/modules/identity/infrastructure/repositories/prisma-role.repository.js.map new file mode 100644 index 0000000..743d10a --- /dev/null +++ b/dist/modules/identity/infrastructure/repositories/prisma-role.repository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prisma-role.repository.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/repositories/prisma-role.repository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js b/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js new file mode 100644 index 0000000..8cf324e --- /dev/null +++ b/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js @@ -0,0 +1,216 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrismaUserRepository = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../../../shared/prisma.service"); +const prisma_user_mapper_1 = require("../mappers/prisma-user.mapper"); +let PrismaUserRepository = class PrismaUserRepository { + prisma; + constructor(prisma) { + this.prisma = prisma; + } + //#region Create + async create(userData) { + const base = prisma_user_mapper_1.PrismaUserMapper.toPersistence(userData); + const data = { ...base }; + if (userData.roles && userData.roles.length > 0) { + data.roles = { + create: userData.roles.map(r => ({ role: { connect: { id: r.id } }, assignedBy: 'system' })), + }; + } + const created = await this.prisma.user.create({ + data, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + return prisma_user_mapper_1.PrismaUserMapper.toDomain(created); + } + //#endregion + //#region Read + async existsById(userId) { + return ((await this.prisma.user.findUnique({ + where: { id: userId }, + select: { id: true }, + })) !== null); + } + async existByEmail(userEmail) { + return ((await this.prisma.user.findUnique({ + where: { email: userEmail }, + select: { email: true }, + })) !== null); + } + async getById(userId) { + const userData = await this.prisma.user.findUnique({ + where: { id: userId }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + if (userData === null) + throw new Error('User tidak ditemukan.'); + return prisma_user_mapper_1.PrismaUserMapper.toDomain(userData); + } + async getByEmail(userEmail) { + const userData = await this.prisma.user.findUnique({ + where: { email: userEmail }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + if (userData === null) + throw new Error('User tidak ditemukan.'); + return prisma_user_mapper_1.PrismaUserMapper.toDomain(userData); + } + async findByAuthentikId(authentikId) { + const userData = await this.prisma.user.findUnique({ + where: { authentikId }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + if (!userData) + return null; + return prisma_user_mapper_1.PrismaUserMapper.toDomain(userData); + } + async find(params) { + const page = params.page && params.page > 0 ? params.page : 1; + const limit = params.limit && params.limit > 0 ? params.limit : 10; + const where = {}; + if (params.search) { + where.OR = [ + { username: { contains: params.search, mode: 'insensitive' } }, + { email: { contains: params.search, mode: 'insensitive' } }, + ]; + } + if (params.isActive !== undefined && params.isActive !== null) { + where.isActive = params.isActive; + } + if (params.deleted !== undefined && params.deleted !== null) { + if (params.deleted) + where.deletedAt = { not: null }; + else + where.deletedAt = null; + } + if (params.roleId) { + where.roles = { some: { roleId: params.roleId } }; + } + const [total, items] = await Promise.all([ + this.prisma.user.count({ where }), + this.prisma.user.findMany({ + where, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + skip: (page - 1) * limit, + take: limit, + orderBy: { createdAt: 'desc' }, + }), + ]); + return { data: items.map(i => prisma_user_mapper_1.PrismaUserMapper.toDomain(i)), total }; + } + async softDelete(userId) { + await this.prisma.user.update({ where: { id: userId }, data: { deletedAt: new Date(), isActive: false } }); + } + async restore(userId) { + const updated = await this.prisma.user.update({ + where: { id: userId }, + data: { deletedAt: null }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + return prisma_user_mapper_1.PrismaUserMapper.toDomain(updated); + } + async enable(userId) { + const updated = await this.prisma.user.update({ + where: { id: userId }, + data: { isActive: true }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + return prisma_user_mapper_1.PrismaUserMapper.toDomain(updated); + } + async disable(userId) { + const updated = await this.prisma.user.update({ + where: { id: userId }, + data: { isActive: false }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + return prisma_user_mapper_1.PrismaUserMapper.toDomain(updated); + } + async update(userData) { + const base = prisma_user_mapper_1.PrismaUserMapper.toPersistence(userData); + const data = { ...base }; + // sync roles and permissions using transaction + const roleIds = userData.roles ? userData.roles.map(r => r.id) : []; + const permissionIds = userData.permissions ? userData.permissions.map(p => p.id) : []; + // perform transaction: delete removed relations, create missing ones, update user + await this.prisma.$transaction(async (prisma) => { + // current roles + const currentRoles = await prisma.userRole.findMany({ where: { userId: userData.id } }); + const currentRoleIds = currentRoles.map(r => r.roleId); + const toAddRoles = roleIds.filter(id => !currentRoleIds.includes(id)); + const toRemoveRoles = currentRoleIds.filter(id => !roleIds.includes(id)); + if (toRemoveRoles.length > 0) { + await prisma.userRole.deleteMany({ where: { userId: userData.id, roleId: { in: toRemoveRoles } } }); + } + if (toAddRoles.length > 0) { + await prisma.userRole.createMany({ + data: toAddRoles.map(rid => ({ userId: userData.id, roleId: rid, assignedBy: 'system' })), + skipDuplicates: true, + }); + } + // permissions + const currentPerms = await prisma.userPermission.findMany({ where: { userId: userData.id } }); + const currentPermIds = currentPerms.map(p => p.permissionId); + const toAddPerms = permissionIds.filter(id => !currentPermIds.includes(id)); + const toRemovePerms = currentPermIds.filter(id => !permissionIds.includes(id)); + if (toRemovePerms.length > 0) { + await prisma.userPermission.deleteMany({ where: { userId: userData.id, permissionId: { in: toRemovePerms } } }); + } + if (toAddPerms.length > 0) { + await prisma.userPermission.createMany({ + data: toAddPerms.map(pid => ({ userId: userData.id, permissionId: pid, assignedBy: 'system' })), + skipDuplicates: true, + }); + } + // update base user fields + await prisma.user.update({ where: { id: userData.id }, data }); + }); + const updated = await this.prisma.user.findUnique({ + where: { id: userData.id }, + include: { + roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, + permissions: { include: { permission: true } }, + }, + }); + return prisma_user_mapper_1.PrismaUserMapper.toDomain(updated); + } +}; +exports.PrismaUserRepository = PrismaUserRepository; +exports.PrismaUserRepository = PrismaUserRepository = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], PrismaUserRepository); +//# sourceMappingURL=prisma-user.repository.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js.map b/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js.map new file mode 100644 index 0000000..e689eeb --- /dev/null +++ b/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prisma-user.repository.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/repositories/prisma-user.repository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/permissions.controller.js b/dist/modules/identity/presentation/controllers/permissions.controller.js new file mode 100644 index 0000000..f1bf211 --- /dev/null +++ b/dist/modules/identity/presentation/controllers/permissions.controller.js @@ -0,0 +1,119 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PermissionsController = void 0; +const common_1 = require("@nestjs/common"); +const swagger_1 = require("@nestjs/swagger"); +const jwt_auth_guard_1 = require("../../../../core/auth/guards/jwt-auth.guard"); +const permission_guard_1 = require("../../../authorization/presentation/guards/permission.guard"); +const current_user_guard_1 = require("../guards/current-user.guard"); +const permission_decorator_1 = require("../../../authorization/presentation/decorators/permission.decorator"); +const permission_constants_1 = require("../../../../common/constants/permission.constants"); +const get_permissions_handler_1 = require("../../application/handlers/permission/get-permissions.handler"); +const get_permission_handler_1 = require("../../application/handlers/permission/get-permission.handler"); +const create_permission_handler_1 = require("../../application/handlers/permission/create-permission.handler"); +const update_permission_handler_1 = require("../../application/handlers/permission/update-permission.handler"); +const delete_permission_handler_1 = require("../../application/handlers/permission/delete-permission.handler"); +let PermissionsController = class PermissionsController { + getPermissionsHandler; + getPermissionHandler; + createPermissionHandler; + updatePermissionHandler; + deletePermissionHandler; + constructor(getPermissionsHandler, getPermissionHandler, createPermissionHandler, updatePermissionHandler, deletePermissionHandler) { + this.getPermissionsHandler = getPermissionsHandler; + this.getPermissionHandler = getPermissionHandler; + this.createPermissionHandler = createPermissionHandler; + this.updatePermissionHandler = updatePermissionHandler; + this.deletePermissionHandler = deletePermissionHandler; + } + async findAll(query) { + const res = await this.getPermissionsHandler.execute(query); + return { success: true, data: res.data, meta: { total: res.total } }; + } + async findOne(id) { + const p = await this.getPermissionHandler.execute(id); + return { success: true, data: p, meta: {} }; + } + async create(body) { + const created = await this.createPermissionHandler.execute(body); + return { success: true, data: created, meta: {} }; + } + async update(id, body) { + const updated = await this.updatePermissionHandler.execute(id, body); + return { success: true, data: updated, meta: {} }; + } + async remove(id) { + await this.deletePermissionHandler.execute(id); + return { success: true, data: null, meta: {} }; + } +}; +exports.PermissionsController = PermissionsController; +__decorate([ + (0, common_1.Get)(), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.PERMISSION_READ), + (0, swagger_1.ApiOperation)({ summary: 'List permissions' }), + __param(0, (0, common_1.Query)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], PermissionsController.prototype, "findAll", null); +__decorate([ + (0, common_1.Get)(':id'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.PERMISSION_READ), + (0, swagger_1.ApiOperation)({ summary: 'Get permission' }), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], PermissionsController.prototype, "findOne", null); +__decorate([ + (0, common_1.Post)(), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.PERMISSION_CREATE), + (0, swagger_1.ApiOperation)({ summary: 'Create permission' }), + __param(0, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], PermissionsController.prototype, "create", null); +__decorate([ + (0, common_1.Patch)(':id'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.PERMISSION_UPDATE), + (0, swagger_1.ApiOperation)({ summary: 'Update permission' }), + __param(0, (0, common_1.Param)('id')), + __param(1, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object]), + __metadata("design:returntype", Promise) +], PermissionsController.prototype, "update", null); +__decorate([ + (0, common_1.Delete)(':id'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.PERMISSION_DELETE), + (0, swagger_1.ApiOperation)({ summary: 'Delete permission' }), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], PermissionsController.prototype, "remove", null); +exports.PermissionsController = PermissionsController = __decorate([ + (0, swagger_1.ApiTags)('Permissions'), + (0, common_1.Controller)('permissions'), + (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard, permission_guard_1.PermissionGuard), + __metadata("design:paramtypes", [get_permissions_handler_1.GetPermissionsHandler, + get_permission_handler_1.GetPermissionHandler, + create_permission_handler_1.CreatePermissionHandler, + update_permission_handler_1.UpdatePermissionHandler, + delete_permission_handler_1.DeletePermissionHandler]) +], PermissionsController); +//# sourceMappingURL=permissions.controller.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/permissions.controller.js.map b/dist/modules/identity/presentation/controllers/permissions.controller.js.map new file mode 100644 index 0000000..18a3e1b --- /dev/null +++ b/dist/modules/identity/presentation/controllers/permissions.controller.js.map @@ -0,0 +1 @@ +{"version":3,"file":"permissions.controller.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/controllers/permissions.controller.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/roles.controller.js b/dist/modules/identity/presentation/controllers/roles.controller.js new file mode 100644 index 0000000..f9f2f60 --- /dev/null +++ b/dist/modules/identity/presentation/controllers/roles.controller.js @@ -0,0 +1,156 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RolesController = void 0; +const common_1 = require("@nestjs/common"); +const swagger_1 = require("@nestjs/swagger"); +const jwt_auth_guard_1 = require("../../../../core/auth/guards/jwt-auth.guard"); +const permission_guard_1 = require("../../../authorization/presentation/guards/permission.guard"); +const current_user_guard_1 = require("../guards/current-user.guard"); +const permission_decorator_1 = require("../../../authorization/presentation/decorators/permission.decorator"); +const permission_constants_1 = require("../../../../common/constants/permission.constants"); +const get_roles_handler_1 = require("../../application/handlers/role/get-roles.handler"); +const get_role_handler_1 = require("../../application/handlers/role/get-role.handler"); +const create_role_handler_1 = require("../../application/handlers/role/create-role.handler"); +const update_role_handler_1 = require("../../application/handlers/role/update-role.handler"); +const delete_role_handler_1 = require("../../application/handlers/role/delete-role.handler"); +const assign_permission_handler_1 = require("../../application/handlers/role/assign-permission.handler"); +const remove_permission_handler_1 = require("../../application/handlers/role/remove-permission.handler"); +let RolesController = class RolesController { + getRolesHandler; + getRoleHandler; + createRoleHandler; + updateRoleHandler; + deleteRoleHandler; + roleAssignPermissionHandler; + roleRemovePermissionHandler; + constructor(getRolesHandler, getRoleHandler, createRoleHandler, updateRoleHandler, deleteRoleHandler, roleAssignPermissionHandler, roleRemovePermissionHandler) { + this.getRolesHandler = getRolesHandler; + this.getRoleHandler = getRoleHandler; + this.createRoleHandler = createRoleHandler; + this.updateRoleHandler = updateRoleHandler; + this.deleteRoleHandler = deleteRoleHandler; + this.roleAssignPermissionHandler = roleAssignPermissionHandler; + this.roleRemovePermissionHandler = roleRemovePermissionHandler; + } + async findAll(query) { + const res = await this.getRolesHandler.execute(query); + return { success: true, data: res.data, meta: { total: res.total } }; + } + async findOne(id) { + const role = await this.getRoleHandler.execute(id); + return { success: true, data: role, meta: {} }; + } + async create(body) { + const created = await this.createRoleHandler.execute(body); + return { success: true, data: created, meta: {} }; + } + async update(id, body) { + const updated = await this.updateRoleHandler.execute(id, body); + return { success: true, data: updated, meta: {} }; + } + async remove(id) { + await this.deleteRoleHandler.execute(id); + return { success: true, data: null, meta: {} }; + } + async assignPermission(id, body) { + const permissionId = body.permissionId; + const updated = await this.roleAssignPermissionHandler.execute(id, permissionId); + return { success: true, data: updated, meta: {} }; + } + async removePermission(id, permissionId) { + const updated = await this.roleRemovePermissionHandler.execute(id, permissionId); + return { success: true, data: updated, meta: {} }; + } +}; +exports.RolesController = RolesController; +__decorate([ + (0, common_1.Get)(), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_READ), + (0, swagger_1.ApiOperation)({ summary: 'List roles' }), + __param(0, (0, common_1.Query)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], RolesController.prototype, "findAll", null); +__decorate([ + (0, common_1.Get)(':id'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_READ), + (0, swagger_1.ApiOperation)({ summary: 'Get role' }), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], RolesController.prototype, "findOne", null); +__decorate([ + (0, common_1.Post)(), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_CREATE), + (0, swagger_1.ApiOperation)({ summary: 'Create role' }), + __param(0, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], RolesController.prototype, "create", null); +__decorate([ + (0, common_1.Patch)(':id'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_UPDATE), + (0, swagger_1.ApiOperation)({ summary: 'Update role' }), + __param(0, (0, common_1.Param)('id')), + __param(1, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object]), + __metadata("design:returntype", Promise) +], RolesController.prototype, "update", null); +__decorate([ + (0, common_1.Delete)(':id'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_DELETE), + (0, swagger_1.ApiOperation)({ summary: 'Delete role' }), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], RolesController.prototype, "remove", null); +__decorate([ + (0, common_1.Post)(':id/permissions'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_UPDATE), + (0, swagger_1.ApiOperation)({ summary: 'Assign permission to role' }), + __param(0, (0, common_1.Param)('id')), + __param(1, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object]), + __metadata("design:returntype", Promise) +], RolesController.prototype, "assignPermission", null); +__decorate([ + (0, common_1.Delete)(':id/permissions/:permissionId'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_UPDATE), + (0, swagger_1.ApiOperation)({ summary: 'Remove permission from role' }), + __param(0, (0, common_1.Param)('id')), + __param(1, (0, common_1.Param)('permissionId')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], RolesController.prototype, "removePermission", null); +exports.RolesController = RolesController = __decorate([ + (0, swagger_1.ApiTags)('Roles'), + (0, common_1.Controller)('roles'), + (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard, permission_guard_1.PermissionGuard), + __metadata("design:paramtypes", [get_roles_handler_1.GetRolesHandler, + get_role_handler_1.GetRoleHandler, + create_role_handler_1.CreateRoleHandler, + update_role_handler_1.UpdateRoleHandler, + delete_role_handler_1.DeleteRoleHandler, + assign_permission_handler_1.RoleAssignPermissionHandler, + remove_permission_handler_1.RoleRemovePermissionHandler]) +], RolesController); +//# sourceMappingURL=roles.controller.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/roles.controller.js.map b/dist/modules/identity/presentation/controllers/roles.controller.js.map new file mode 100644 index 0000000..95d1c04 --- /dev/null +++ b/dist/modules/identity/presentation/controllers/roles.controller.js.map @@ -0,0 +1 @@ +{"version":3,"file":"roles.controller.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/controllers/roles.controller.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/users.controller.js b/dist/modules/identity/presentation/controllers/users.controller.js new file mode 100644 index 0000000..0870038 --- /dev/null +++ b/dist/modules/identity/presentation/controllers/users.controller.js @@ -0,0 +1,173 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UsersController = void 0; +const common_1 = require("@nestjs/common"); +const swagger_1 = require("@nestjs/swagger"); +const jwt_auth_guard_1 = require("../../../../core/auth/guards/jwt-auth.guard"); +const permission_guard_1 = require("../../../authorization/presentation/guards/permission.guard"); +const current_user_guard_1 = require("../guards/current-user.guard"); +const permission_decorator_1 = require("../../../authorization/presentation/decorators/permission.decorator"); +const permission_constants_1 = require("../../../../common/constants/permission.constants"); +const get_users_handler_1 = require("../../application/handlers/user/get-users.handler"); +const get_user_handler_1 = require("../../application/handlers/user/get-user.handler"); +const get_current_user_handler_1 = require("../../application/handlers/user/get-current-user.handler"); +const enable_user_handler_1 = require("../../application/handlers/user/enable-user.handler"); +const disable_user_handler_1 = require("../../application/handlers/user/disable-user.handler"); +const delete_user_handler_1 = require("../../application/handlers/user/delete-user.handler"); +const restore_user_handler_1 = require("../../application/handlers/user/restore-user.handler"); +const update_user_handler_1 = require("../../application/handlers/user/update-user.handler"); +let UsersController = class UsersController { + getUsersHandler; + getUserHandler; + getCurrentUserHandler; + enableUserHandler; + disableUserHandler; + deleteUserHandler; + restoreUserHandler; + updateUserHandler; + constructor(getUsersHandler, getUserHandler, getCurrentUserHandler, enableUserHandler, disableUserHandler, deleteUserHandler, restoreUserHandler, updateUserHandler) { + this.getUsersHandler = getUsersHandler; + this.getUserHandler = getUserHandler; + this.getCurrentUserHandler = getCurrentUserHandler; + this.enableUserHandler = enableUserHandler; + this.disableUserHandler = disableUserHandler; + this.deleteUserHandler = deleteUserHandler; + this.restoreUserHandler = restoreUserHandler; + this.updateUserHandler = updateUserHandler; + } + async findAll(query) { + const res = await this.getUsersHandler.execute(query); + return { + success: true, + data: res.data.map(u => u.toResponse ? u.toResponse() : u), + meta: { total: res.total }, + }; + } + async me(req) { + const user = await this.getCurrentUserHandler.execute(req.currentUser); + return { success: true, data: user.toResponse ? user.toResponse() : user, meta: {} }; + } + async findOne(id) { + const user = await this.getUserHandler.execute(id); + return { success: true, data: user.toResponse ? user.toResponse() : user, meta: {} }; + } + async enable(id) { + const updated = await this.enableUserHandler.execute(id); + return { success: true, data: updated.toResponse ? updated.toResponse() : updated, meta: {} }; + } + async disable(id) { + const updated = await this.disableUserHandler.execute(id); + return { success: true, data: updated.toResponse ? updated.toResponse() : updated, meta: {} }; + } + async update(id, body) { + const updated = await this.updateUserHandler.execute(id, body); + return { success: true, data: updated.toResponse ? updated.toResponse() : updated, meta: {} }; + } + async remove(id) { + await this.deleteUserHandler.execute(id); + return { success: true, data: null, meta: {} }; + } + async restore(id) { + const restored = await this.restoreUserHandler.execute(id); + return { success: true, data: restored.toResponse ? restored.toResponse() : restored, meta: {} }; + } +}; +exports.UsersController = UsersController; +__decorate([ + (0, common_1.Get)(), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_READ), + (0, swagger_1.ApiOperation)({ summary: 'List users' }), + __param(0, (0, common_1.Query)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], UsersController.prototype, "findAll", null); +__decorate([ + (0, common_1.Get)('me'), + (0, swagger_1.ApiOperation)({ summary: 'Get current user' }), + __param(0, (0, common_1.Req)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], UsersController.prototype, "me", null); +__decorate([ + (0, common_1.Get)(':id'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_READ), + (0, swagger_1.ApiOperation)({ summary: 'Get user by id' }), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], UsersController.prototype, "findOne", null); +__decorate([ + (0, common_1.Patch)(':id/enable'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_UPDATE), + (0, swagger_1.ApiOperation)({ summary: 'Enable user' }), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], UsersController.prototype, "enable", null); +__decorate([ + (0, common_1.Patch)(':id/disable'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_UPDATE), + (0, swagger_1.ApiOperation)({ summary: 'Disable user' }), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], UsersController.prototype, "disable", null); +__decorate([ + (0, common_1.Patch)(':id'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_UPDATE), + (0, swagger_1.ApiOperation)({ summary: 'Update user profile' }), + __param(0, (0, common_1.Param)('id')), + __param(1, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object]), + __metadata("design:returntype", Promise) +], UsersController.prototype, "update", null); +__decorate([ + (0, common_1.Delete)(':id'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_DELETE), + (0, swagger_1.ApiOperation)({ summary: 'Soft delete user' }), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], UsersController.prototype, "remove", null); +__decorate([ + (0, common_1.Post)(':id/restore'), + (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_UPDATE), + (0, swagger_1.ApiOperation)({ summary: 'Restore user' }), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], UsersController.prototype, "restore", null); +exports.UsersController = UsersController = __decorate([ + (0, swagger_1.ApiTags)('Users'), + (0, common_1.Controller)('users'), + (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard, permission_guard_1.PermissionGuard), + __metadata("design:paramtypes", [get_users_handler_1.GetUsersHandler, + get_user_handler_1.GetUserHandler, + get_current_user_handler_1.GetCurrentUserHandler, + enable_user_handler_1.EnableUserHandler, + disable_user_handler_1.DisableUserHandler, + delete_user_handler_1.DeleteUserHandler, + restore_user_handler_1.RestoreUserHandler, + update_user_handler_1.UpdateUserHandler]) +], UsersController); +//# sourceMappingURL=users.controller.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/users.controller.js.map b/dist/modules/identity/presentation/controllers/users.controller.js.map new file mode 100644 index 0000000..b096e72 --- /dev/null +++ b/dist/modules/identity/presentation/controllers/users.controller.js.map @@ -0,0 +1 @@ +{"version":3,"file":"users.controller.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/controllers/users.controller.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-permission.dto.js b/dist/modules/identity/presentation/dto/create-permission.dto.js new file mode 100644 index 0000000..00c6af3 --- /dev/null +++ b/dist/modules/identity/presentation/dto/create-permission.dto.js @@ -0,0 +1,39 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreatePermissionDto = void 0; +const swagger_1 = require("@nestjs/swagger"); +const class_validator_1 = require("class-validator"); +class CreatePermissionDto { + code; + name; + description; +} +exports.CreatePermissionDto = CreatePermissionDto; +__decorate([ + (0, swagger_1.ApiProperty)(), + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", String) +], CreatePermissionDto.prototype, "code", void 0); +__decorate([ + (0, swagger_1.ApiProperty)(), + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", String) +], CreatePermissionDto.prototype, "name", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ required: false }), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata("design:type", String) +], CreatePermissionDto.prototype, "description", void 0); +//# sourceMappingURL=create-permission.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-permission.dto.js.map b/dist/modules/identity/presentation/dto/create-permission.dto.js.map new file mode 100644 index 0000000..f4bd483 --- /dev/null +++ b/dist/modules/identity/presentation/dto/create-permission.dto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"create-permission.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/create-permission.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6CAA8C;AAC9C,qDAAmE;AAEnE,MAAa,mBAAmB;IAI9B,IAAI,CAAU;IAKd,IAAI,CAAU;IAKd,WAAW,CAAU;CACtB;AAfD,kDAeC;AAXC;IAHC,IAAA,qBAAW,GAAE;IACb,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;iDACC;AAKd;IAHC,IAAA,qBAAW,GAAE;IACb,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;iDACC;AAKd;IAHC,IAAA,qBAAW,EAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAChC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;wDACU"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-role.dto.js b/dist/modules/identity/presentation/dto/create-role.dto.js new file mode 100644 index 0000000..5636063 --- /dev/null +++ b/dist/modules/identity/presentation/dto/create-role.dto.js @@ -0,0 +1,45 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateRoleDto = void 0; +const swagger_1 = require("@nestjs/swagger"); +const class_validator_1 = require("class-validator"); +class CreateRoleDto { + code; + name; + description; + isDefault; +} +exports.CreateRoleDto = CreateRoleDto; +__decorate([ + (0, swagger_1.ApiProperty)(), + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", String) +], CreateRoleDto.prototype, "code", void 0); +__decorate([ + (0, swagger_1.ApiProperty)(), + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", String) +], CreateRoleDto.prototype, "name", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ required: false }), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata("design:type", String) +], CreateRoleDto.prototype, "description", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ required: false }), + (0, class_validator_1.IsOptional)(), + __metadata("design:type", Boolean) +], CreateRoleDto.prototype, "isDefault", void 0); +//# sourceMappingURL=create-role.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-role.dto.js.map b/dist/modules/identity/presentation/dto/create-role.dto.js.map new file mode 100644 index 0000000..142f10b --- /dev/null +++ b/dist/modules/identity/presentation/dto/create-role.dto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"create-role.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/create-role.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6CAA8C;AAC9C,qDAAmE;AAEnE,MAAa,aAAa;IAIxB,IAAI,CAAU;IAKd,IAAI,CAAU;IAKd,WAAW,CAAU;IAIrB,SAAS,CAAW;CACrB;AAnBD,sCAmBC;AAfC;IAHC,IAAA,qBAAW,GAAE;IACb,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;2CACC;AAKd;IAHC,IAAA,qBAAW,GAAE;IACb,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;2CACC;AAKd;IAHC,IAAA,qBAAW,EAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAChC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;kDACU;AAIrB;IAFC,IAAA,qBAAW,EAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAChC,IAAA,4BAAU,GAAE;;gDACO"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-user.dto.js b/dist/modules/identity/presentation/dto/create-user.dto.js new file mode 100644 index 0000000..fd68ca9 --- /dev/null +++ b/dist/modules/identity/presentation/dto/create-user.dto.js @@ -0,0 +1,54 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateUserDto = void 0; +const class_validator_1 = require("class-validator"); +const swagger_1 = require("@nestjs/swagger"); +class CreateUserDto { + username; + email; + password; + storageQuota; + storageUsed; +} +exports.CreateUserDto = CreateUserDto; +__decorate([ + (0, swagger_1.ApiProperty)({ example: 'John Doe' }), + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", String) +], CreateUserDto.prototype, "username", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ example: 'user@example.com' }), + (0, class_validator_1.IsEmail)(), + __metadata("design:type", String) +], CreateUserDto.prototype, "email", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ example: 'strongpassword' }), + (0, class_validator_1.IsString)(), + (0, class_validator_1.Length)(8, 128), + __metadata("design:type", String) +], CreateUserDto.prototype, "password", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)({ example: 10737418240 }), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsInt)(), + (0, class_validator_1.Min)(0), + __metadata("design:type", Number) +], CreateUserDto.prototype, "storageQuota", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)({ example: 0 }), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsInt)(), + (0, class_validator_1.Min)(0), + __metadata("design:type", Number) +], CreateUserDto.prototype, "storageUsed", void 0); +//# sourceMappingURL=create-user.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-user.dto.js.map b/dist/modules/identity/presentation/dto/create-user.dto.js.map new file mode 100644 index 0000000..437e888 --- /dev/null +++ b/dist/modules/identity/presentation/dto/create-user.dto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"create-user.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/create-user.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAAgG;AAChG,6CAAmE;AAEnE,MAAa,aAAa;IAIxB,QAAQ,CAAU;IAIlB,KAAK,CAAU;IAKf,QAAQ,CAAU;IAMlB,YAAY,CAAU;IAMtB,WAAW,CAAU;CACtB;AA1BD,sCA0BC;AAtBC;IAHC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IACpC,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;+CACK;AAIlB;IAFC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC5C,IAAA,yBAAO,GAAE;;4CACK;AAKf;IAHC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC1C,IAAA,0BAAQ,GAAE;IACV,IAAA,wBAAM,EAAC,CAAC,EAAE,GAAG,CAAC;;+CACG;AAMlB;IAJC,IAAA,6BAAmB,EAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAC7C,IAAA,4BAAU,GAAE;IACZ,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;;mDACe;AAMtB;IAJC,IAAA,6BAAmB,EAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACnC,IAAA,4BAAU,GAAE;IACZ,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;;kDACc"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-permission.dto.js b/dist/modules/identity/presentation/dto/update-permission.dto.js new file mode 100644 index 0000000..4327f0c --- /dev/null +++ b/dist/modules/identity/presentation/dto/update-permission.dto.js @@ -0,0 +1,41 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UpdatePermissionDto = void 0; +const mapped_types_1 = require("@nestjs/mapped-types"); +const create_permission_dto_1 = require("./create-permission.dto"); +const swagger_1 = require("@nestjs/swagger"); +const class_validator_1 = require("class-validator"); +class UpdatePermissionDto extends (0, mapped_types_1.PartialType)(create_permission_dto_1.CreatePermissionDto) { + name; + description; + code; +} +exports.UpdatePermissionDto = UpdatePermissionDto; +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata("design:type", String) +], UpdatePermissionDto.prototype, "name", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata("design:type", String) +], UpdatePermissionDto.prototype, "description", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata("design:type", String) +], UpdatePermissionDto.prototype, "code", void 0); +//# sourceMappingURL=update-permission.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-permission.dto.js.map b/dist/modules/identity/presentation/dto/update-permission.dto.js.map new file mode 100644 index 0000000..9b4514d --- /dev/null +++ b/dist/modules/identity/presentation/dto/update-permission.dto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"update-permission.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/update-permission.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uDAAmD;AACnD,mEAA8D;AAC9D,6CAAsD;AACtD,qDAAuD;AAEvD,MAAa,mBAAoB,SAAQ,IAAA,0BAAW,EAAC,2CAAmB,CAAC;IAIvE,IAAI,CAAU;IAKd,WAAW,CAAU;IAKrB,IAAI,CAAU;CACf;AAfD,kDAeC;AAXC;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;iDACG;AAKd;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;wDACU;AAKrB;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;iDACG"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-role.dto.js b/dist/modules/identity/presentation/dto/update-role.dto.js new file mode 100644 index 0000000..b02841b --- /dev/null +++ b/dist/modules/identity/presentation/dto/update-role.dto.js @@ -0,0 +1,40 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UpdateRoleDto = void 0; +const mapped_types_1 = require("@nestjs/mapped-types"); +const create_role_dto_1 = require("./create-role.dto"); +const swagger_1 = require("@nestjs/swagger"); +const class_validator_1 = require("class-validator"); +class UpdateRoleDto extends (0, mapped_types_1.PartialType)(create_role_dto_1.CreateRoleDto) { + name; + description; + isDefault; +} +exports.UpdateRoleDto = UpdateRoleDto; +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata("design:type", String) +], UpdateRoleDto.prototype, "name", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata("design:type", String) +], UpdateRoleDto.prototype, "description", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + __metadata("design:type", Boolean) +], UpdateRoleDto.prototype, "isDefault", void 0); +//# sourceMappingURL=update-role.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-role.dto.js.map b/dist/modules/identity/presentation/dto/update-role.dto.js.map new file mode 100644 index 0000000..a4905b2 --- /dev/null +++ b/dist/modules/identity/presentation/dto/update-role.dto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"update-role.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/update-role.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uDAAmD;AACnD,uDAAkD;AAClD,6CAAsD;AACtD,qDAAuD;AAEvD,MAAa,aAAc,SAAQ,IAAA,0BAAW,EAAC,+BAAa,CAAC;IAI3D,IAAI,CAAU;IAKd,WAAW,CAAU;IAIrB,SAAS,CAAW;CACrB;AAdD,sCAcC;AAVC;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;2CACG;AAKd;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;kDACU;AAIrB;IAFC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;;gDACO"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-user.dto.js b/dist/modules/identity/presentation/dto/update-user.dto.js new file mode 100644 index 0000000..ada8a2b --- /dev/null +++ b/dist/modules/identity/presentation/dto/update-user.dto.js @@ -0,0 +1,63 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UpdateUserDto = void 0; +const mapped_types_1 = require("@nestjs/mapped-types"); +const create_user_dto_1 = require("./create-user.dto"); +const swagger_1 = require("@nestjs/swagger"); +const class_validator_1 = require("class-validator"); +class UpdateUserDto extends (0, mapped_types_1.PartialType)(create_user_dto_1.CreateUserDto) { + name; + email; + password; + metadata; + storageQuota; + storageUsed; +} +exports.UpdateUserDto = UpdateUserDto; +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata("design:type", String) +], UpdateUserDto.prototype, "name", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsEmail)(), + __metadata("design:type", String) +], UpdateUserDto.prototype, "email", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.Length)(8, 128), + __metadata("design:type", String) +], UpdateUserDto.prototype, "password", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + __metadata("design:type", Object) +], UpdateUserDto.prototype, "metadata", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)({ example: 10737418240 }), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsInt)(), + (0, class_validator_1.Min)(0), + __metadata("design:type", Number) +], UpdateUserDto.prototype, "storageQuota", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)({ example: 0 }), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsInt)(), + (0, class_validator_1.Min)(0), + __metadata("design:type", Number) +], UpdateUserDto.prototype, "storageUsed", void 0); +//# sourceMappingURL=update-user.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-user.dto.js.map b/dist/modules/identity/presentation/dto/update-user.dto.js.map new file mode 100644 index 0000000..32d013b --- /dev/null +++ b/dist/modules/identity/presentation/dto/update-user.dto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"update-user.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/update-user.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uDAAmD;AACnD,uDAAkD;AAClD,6CAAsD;AACtD,qDAAoF;AAEpF,MAAa,aAAc,SAAQ,IAAA,0BAAW,EAAC,+BAAa,CAAC;IAI3D,IAAI,CAAU;IAKd,KAAK,CAAU;IAKf,QAAQ,CAAU;IAIlB,QAAQ,CAAuB;IAM/B,YAAY,CAAU;IAMtB,WAAW,CAAU;CACtB;AA/BD,sCA+BC;AA3BC;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;2CACG;AAKd;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,yBAAO,GAAE;;4CACK;AAKf;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAM,EAAC,CAAC,EAAE,GAAG,CAAC;;+CACG;AAIlB;IAFC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;;+CACkB;AAM/B;IAJC,IAAA,6BAAmB,EAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAC7C,IAAA,4BAAU,GAAE;IACZ,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;;mDACe;AAMtB;IAJC,IAAA,6BAAmB,EAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACnC,IAAA,4BAAU,GAAE;IACZ,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;;kDACc"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/guards/current-user.guard.js b/dist/modules/identity/presentation/guards/current-user.guard.js new file mode 100644 index 0000000..7d1bc03 --- /dev/null +++ b/dist/modules/identity/presentation/guards/current-user.guard.js @@ -0,0 +1,44 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CurrentUserGuard = void 0; +const common_1 = require("@nestjs/common"); +const sync_identity_handler_1 = require("../../application/handlers/user/sync-identity.handler"); +let CurrentUserGuard = class CurrentUserGuard { + syncIdentityHandler; + constructor(syncIdentityHandler) { + this.syncIdentityHandler = syncIdentityHandler; + } + async canActivate(context) { + const request = context.switchToHttp().getRequest(); + // Prefer RequestContext produced by AuthenticationService + const ctx = request.raylabContext; + if (ctx && ctx.user) { + request.currentUser = ctx.user; + return true; + } + // Fallback to legacy identity if present + const identity = request.identity; + if (!identity) { + throw new common_1.UnauthorizedException('Identity is missing.'); + } + const user = await this.syncIdentityHandler.execute(identity); + // attach domain user as currentUser + request.currentUser = user; + return true; + } +}; +exports.CurrentUserGuard = CurrentUserGuard; +exports.CurrentUserGuard = CurrentUserGuard = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [sync_identity_handler_1.SyncIdentityHandler]) +], CurrentUserGuard); +//# sourceMappingURL=current-user.guard.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/guards/current-user.guard.js.map b/dist/modules/identity/presentation/guards/current-user.guard.js.map new file mode 100644 index 0000000..d22bdf2 --- /dev/null +++ b/dist/modules/identity/presentation/guards/current-user.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"current-user.guard.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/guards/current-user.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAkG;AAClG,iGAA4F;AAIrF,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAER;IADnB,YACmB,mBAAwC;QAAxC,wBAAmB,GAAnB,mBAAmB,CAAqB;IACxD,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAS,CAAC;QAE3D,0DAA0D;QAC1D,MAAM,GAAG,GAAG,OAAO,CAAC,aAAoB,CAAC;QACzC,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YACpB,OAAO,CAAC,WAAW,GAAG,GAAG,CAAC,IAAI,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,yCAAyC;QACzC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAoC,CAAC;QAE9D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,8BAAqB,CAAC,sBAAsB,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAE9D,oCAAoC;QACpC,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;QAE3B,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AA7BY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;qCAG6B,2CAAmB;GAFhD,gBAAgB,CA6B5B"} \ No newline at end of file diff --git a/dist/shared/cache-keys.js b/dist/shared/cache-keys.js new file mode 100644 index 0000000..d4ddb65 --- /dev/null +++ b/dist/shared/cache-keys.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CacheKeys = void 0; +exports.CacheKeys = { + permission: (userId) => `permissions:${userId}`, +}; +//# sourceMappingURL=cache-keys.js.map \ No newline at end of file diff --git a/dist/shared/cache-keys.js.map b/dist/shared/cache-keys.js.map new file mode 100644 index 0000000..2d423a2 --- /dev/null +++ b/dist/shared/cache-keys.js.map @@ -0,0 +1 @@ +{"version":3,"file":"cache-keys.js","sourceRoot":"","sources":["../../src/shared/cache-keys.ts"],"names":[],"mappings":";;;AAAa,QAAA,SAAS,GAAG;IACvB,UAAU,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,eAAe,MAAM,EAAE;CACxD,CAAC"} \ No newline at end of file diff --git a/dist/shared/prisma.service.js b/dist/shared/prisma.service.js new file mode 100644 index 0000000..a5f7bec --- /dev/null +++ b/dist/shared/prisma.service.js @@ -0,0 +1,24 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrismaService = void 0; +const common_1 = require("@nestjs/common"); +const client_1 = require("@prisma/client"); +let PrismaService = class PrismaService extends client_1.PrismaClient { + async onModuleInit() { + await this.$connect(); + } + async onModuleDestroy() { + await this.$disconnect(); + } +}; +exports.PrismaService = PrismaService; +exports.PrismaService = PrismaService = __decorate([ + (0, common_1.Injectable)() +], PrismaService); +//# sourceMappingURL=prisma.service.js.map \ No newline at end of file diff --git a/dist/shared/prisma.service.js.map b/dist/shared/prisma.service.js.map new file mode 100644 index 0000000..0776229 --- /dev/null +++ b/dist/shared/prisma.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prisma.service.js","sourceRoot":"","sources":["../../src/shared/prisma.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA2E;AAC3E,2CAA8C;AAGvC,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,qBAAY;IAC7C,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;IAC3B,CAAC;CACF,CAAA;AARY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;GACA,aAAa,CAQzB"} \ No newline at end of file diff --git a/dist/shared/redis.service.js b/dist/shared/redis.service.js new file mode 100644 index 0000000..0567a3a --- /dev/null +++ b/dist/shared/redis.service.js @@ -0,0 +1,121 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +var RedisService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RedisService = void 0; +const common_1 = require("@nestjs/common"); +const ioredis_1 = __importDefault(require("ioredis")); +let RedisService = RedisService_1 = class RedisService { + // Keep client typed as any to avoid tight coupling to ioredis types in tests + client = null; + logger = new common_1.Logger(RedisService_1.name); + lastLogAt = 0; + LOG_THROTTLE_MS = 5000; // throttle repeated error logs + onModuleInit() { + const enabled = (process.env.REDIS_ENABLED || 'true').toLowerCase() === 'true'; + if (!enabled) { + this.logger.log('Redis disabled via REDIS_ENABLED=false'); + return; + } + const url = process.env.REDIS_URL || 'redis://localhost:6379'; + // Use lazyConnect so application can start even if Redis is unavailable temporarily + this.client = new ioredis_1.default(url, { + lazyConnect: true, + // limit retries to avoid infinite reconnect storms + maxRetriesPerRequest: 5, + // automatic reconnection strategy + reconnectOnError: (err) => { + return true; + }, + enableOfflineQueue: true, + // optional reconnect strategy + retryStrategy: (times) => { + // exponential backoff capped at 5s + const delay = Math.min(50 * Math.pow(2, times), 5000); + return delay; + }, + }); + this.client.on('connect', () => this.logger.log('Connected to Redis')); + this.client.on('ready', () => this.logger.log('Redis ready')); + this.client.on('error', (err) => this.handleError(err)); + this.client.on('close', () => this.logger.warn('Redis connection closed')); + this.client.on('reconnecting', () => this.logger.log('Redis reconnecting')); + // attempt to connect but do not throw if it fails + this.client.connect().catch((err) => { + this.handleError(err); + }); + } + handleError(err) { + const now = Date.now(); + if (now - this.lastLogAt > this.LOG_THROTTLE_MS) { + this.logger.error('Redis error', err instanceof Error ? err.message : err); + this.lastLogAt = now; + } + } + onModuleDestroy() { + if (this.client) { + try { + this.client.disconnect(); + } + catch (e) { + // ignore + } + } + } + getClient() { + return this.client; + } + ensureClient() { + // returns true if client is connected/usable + return this.client && this.client.status && this.client.status !== 'end' && this.client.status !== 'close'; + } + async get(key) { + if (!this.ensureClient()) + return null; + try { + return await this.client.get(key); + } + catch (e) { + this.handleError(e); + return null; + } + } + async set(key, value, ttlSeconds) { + if (!this.ensureClient()) + return; + try { + if (ttlSeconds) { + await this.client.set(key, value, 'EX', ttlSeconds); + } + else { + await this.client.set(key, value); + } + } + catch (e) { + this.handleError(e); + } + } + async del(key) { + if (!this.ensureClient()) + return; + try { + await this.client.del(key); + } + catch (e) { + this.handleError(e); + } + } +}; +exports.RedisService = RedisService; +exports.RedisService = RedisService = RedisService_1 = __decorate([ + (0, common_1.Injectable)() +], RedisService); +//# sourceMappingURL=redis.service.js.map \ No newline at end of file diff --git a/dist/shared/redis.service.js.map b/dist/shared/redis.service.js.map new file mode 100644 index 0000000..c3ef62d --- /dev/null +++ b/dist/shared/redis.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"redis.service.js","sourceRoot":"","sources":["../../src/shared/redis.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/shared/types/request-context.js b/dist/shared/types/request-context.js new file mode 100644 index 0000000..8a495a4 --- /dev/null +++ b/dist/shared/types/request-context.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=request-context.js.map \ No newline at end of file diff --git a/dist/shared/types/request-context.js.map b/dist/shared/types/request-context.js.map new file mode 100644 index 0000000..31d227a --- /dev/null +++ b/dist/shared/types/request-context.js.map @@ -0,0 +1 @@ +{"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../../src/shared/types/request-context.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/shared/utils/index.js b/dist/shared/utils/index.js new file mode 100644 index 0000000..0dc1a78 --- /dev/null +++ b/dist/shared/utils/index.js @@ -0,0 +1,5 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.noop = noop; +function noop() { } +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/shared/utils/index.js.map b/dist/shared/utils/index.js.map new file mode 100644 index 0000000..a39412e --- /dev/null +++ b/dist/shared/utils/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/shared/utils/index.ts"],"names":[],"mappings":";;AAAA,oBAAyB;AAAzB,SAAgB,IAAI,KAAI,CAAC"} \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 0672dc1..491d146 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -11,28 +11,29 @@ datasource db { } model User { - id String @id @default(uuid()) @db.Uuid - authentikId String? @unique - authentikUserId String? @unique - authentikSubject String? @unique - username String? @unique @db.VarChar(255) - email String? @unique @db.VarChar(320) - name String? @db.VarChar(255) + id String @id @default(uuid()) @db.Uuid + authentikId String? @unique + authentikUserId String? @unique + authentikSubject String? @unique + username String? @unique @db.VarChar(255) + email String? @unique @db.VarChar(320) + name String? @db.VarChar(255) picture String? - isActive Boolean @default(true) + isActive Boolean @default(true) deletedAt DateTime? lastSeenAt DateTime? lastSyncedAt DateTime? syncStatus String? - storageQuota BigInt? @default(10737418240) - storageUsed BigInt? @default(0) + storageQuota BigInt? @default(10737418240) + storageUsed BigInt? @default(0) metadata Json? - userRoles UserRole[] + roles UserRole[] + permissions UserPermission[] auditLogs AuditLog[] mediaObjects MediaObject[] - lastGroupHash String? @db.Text - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + lastGroupHash String? @db.Text + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt @@index([authentikId]) @@index([username]) @@ -40,17 +41,17 @@ model User { } model Role { - id String @id @default(uuid()) @db.Uuid - code String @unique @db.VarChar(100) - name String @db.VarChar(255) - displayName String? @db.VarChar(255) - description String? - isDefault Boolean @default(false) - rolePermissions RolePermission[] - userRoles UserRole[] - mappings AuthGroupRoleMapping[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(uuid()) @db.Uuid + code String @unique @db.VarChar(100) + name String @db.VarChar(255) + displayName String? @db.VarChar(255) + description String? + isDefault Boolean @default(false) + permissions RolePermission[] + userRoles UserRole[] + mappings AuthGroupRoleMapping[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model Permission { @@ -62,6 +63,7 @@ model Permission { rolePermissions RolePermission[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + UserPermission UserPermission[] } model RolePermission { @@ -77,6 +79,20 @@ model RolePermission { @@index([permissionId]) } +model UserPermission { + id String @id @default(uuid()) @db.Uuid + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String @db.Uuid + permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) + permissionId String @db.Uuid + assignedBy String? @db.VarChar(100) + createdAt DateTime @default(now()) + + @@unique([userId, permissionId]) + @@index([userId]) + @@index([permissionId]) +} + enum UserRoleSource { AUTHENTIK SYSTEM diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index f488309..acea84b 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -7,7 +7,7 @@ import { PrismaService } from '../../shared/prisma.service'; import { RedisPkceStore } from './pkce/redis-pkce.store'; import { InMemoryRefreshStore } from './refresh/inmemory-refresh.store'; -import { Issuer, generators, TokenSet } from 'openid-client'; +const openidClient: any = require('openid-client'); @Injectable() export class AuthService { @@ -28,7 +28,7 @@ export class AuthService { if (this.issuer) return this.issuer; const issuerUrl = this.config.get('AUTHENTIK_ISSUER'); if (!issuerUrl) throw new Error('AUTHENTIK_ISSUER not configured'); - this.issuer = await Issuer.discover(issuerUrl); + this.issuer = await openidClient.Issuer.discover(issuerUrl); return this.issuer; } @@ -47,9 +47,9 @@ export class AuthService { const redirectUri = this.config.get('AUTH_CALLBACK_URL') || `${this.config.get('APP_URL') || ''}/auth/callback`; const state = require('crypto').randomUUID(); - const code_verifier = generators.codeVerifier(); - const code_challenge = await generators.codeChallenge(code_verifier); - const nonce = generators.nonce(); + const code_verifier = openidClient.generators.codeVerifier(); + const code_challenge = await openidClient.generators.codeChallenge(code_verifier); + const nonce = openidClient.generators.nonce(); await this.pkceStore.save(state, { code_verifier, nonce, returnTo }, 300); @@ -77,7 +77,7 @@ export class AuthService { const redirectUri = this.config.get('AUTH_CALLBACK_URL') || `${this.config.get('APP_URL') || ''}/auth/callback`; // exchange code - const tokenSet: TokenSet = await client.callback(redirectUri, { code, state }, { code_verifier: pkce.code_verifier, nonce: pkce.nonce }); + const tokenSet: any = await client.callback(redirectUri, { code, state }, { code_verifier: pkce.code_verifier, nonce: pkce.nonce }); // verify id_token and get claims const claims = tokenSet.claims(); @@ -144,7 +144,7 @@ export class AuthService { const userId = data.userId; // load user - const domainUser = await this.userRepository.findById(userId); + const domainUser = await this.userRepository.getById(userId); if (!domainUser) throw new UnauthorizedException('User not found'); if (!domainUser.isActive) throw new UnauthorizedException('User is not active'); @@ -183,7 +183,7 @@ export class AuthService { if (!token) throw new UnauthorizedException('Missing token'); try { const payload: any = this.jwtService.verify(token); - const user = await this.userRepository.findById(payload.sub); + const user = await this.userRepository.getById(payload.sub); if (!user) throw new UnauthorizedException('User not found'); return { id: user.id, username: user.username, email: user.email, roles: user.roles || [] }; } catch (e) { diff --git a/src/modules/identity/infrastructure/mappers/prisma-role.mapper.ts b/src/modules/identity/infrastructure/mappers/prisma-role.mapper.ts index 133431d..71ecf55 100644 --- a/src/modules/identity/infrastructure/mappers/prisma-role.mapper.ts +++ b/src/modules/identity/infrastructure/mappers/prisma-role.mapper.ts @@ -3,15 +3,16 @@ import { PermissionData } from '../../domain/entities/permission.entity'; export class PrismaRoleMapper { static toDomain(model: any): RoleData { - const permissions: PermissionData[] = (model.permissions || []).map((p: any) => - PermissionData.restore({ - id: p.id, - name: p.name, - description: p.description, - createdAt: p.createdAt, - updatedAt: p.updatedAt, - }), - ); + const permissions: PermissionData[] = (model.permissions || []).map((p: any) => { + const perm = p.permission || p; // p may be RolePermission with nested permission or permission itself + return PermissionData.restore({ + id: perm.id, + name: perm.name, + description: perm.description, + createdAt: perm.createdAt, + updatedAt: perm.updatedAt, + }); + }); return RoleData.restore({ id: model.id, diff --git a/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts b/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts index 038fa3f..8d0f28e 100644 --- a/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts +++ b/src/modules/identity/infrastructure/repositories/prisma-role.repository.ts @@ -11,7 +11,7 @@ export class PrismaRoleRepository implements IRole { async getDefaultRole() { const role = await this.prisma.role.findFirst({ where: { isDefault: true }, - include: { permissions: true }, + include: { permissions: { include: { permission: true } } }, }); if (!role) return null; @@ -22,7 +22,7 @@ export class PrismaRoleRepository implements IRole { async findById(roleId: string) { const role = await this.prisma.role.findUnique({ where: { id: roleId }, - include: { permissions: true }, + include: { permissions: { include: { permission: true } } }, }); if (!role) throw new Error('Role not found.'); @@ -45,7 +45,7 @@ export class PrismaRoleRepository implements IRole { const [total, items] = await Promise.all([ this.prisma.role.count({ where }), - this.prisma.role.findMany({ where, skip: (page - 1) * limit, take: limit, orderBy: { createdAt: 'desc' }, include: { permissions: true } }), + this.prisma.role.findMany({ where, skip: (page - 1) * limit, take: limit, orderBy: { createdAt: 'desc' }, include: { permissions: { include: { permission: true } } } }), ]); return { data: items.map(i => PrismaRoleMapper.toDomain(i)!), total }; @@ -66,7 +66,7 @@ export class PrismaRoleRepository implements IRole { }; } - const created = await this.prisma.role.create({ data: base, include: { permissions: true } }); + const created = await this.prisma.role.create({ data: base, include: { permissions: { include: { permission: true } } } }); return PrismaRoleMapper.toDomain(created)!; } @@ -99,7 +99,7 @@ export class PrismaRoleRepository implements IRole { await prisma.role.update({ where: { id: role.id }, data: base }); }); - const updated = await this.prisma.role.findUnique({ where: { id: role.id }, include: { permissions: true } }); + const updated = await this.prisma.role.findUnique({ where: { id: role.id }, include: { permissions: { include: { permission: true } } } }); return PrismaRoleMapper.toDomain(updated)!; } From 5a43e8322ea7925c468e7c4e3328dc92b29fb121 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Sun, 2 Aug 2026 21:12:49 +0700 Subject: [PATCH 06/34] Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d91abad..630495e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules RayLab-Core.zip RayLab-Core.rar -.env \ No newline at end of file +.env +dist \ No newline at end of file From 8f36f2fb1261a32d3b7512ec18b19b2aa7c2ef68 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Sun, 2 Aug 2026 22:46:59 +0700 Subject: [PATCH 07/34] Fix npm start --- .../modules/application/application.module.js | 5 ++--- .../application/application.module.js.map | 2 +- dist/modules/identity/identity.module.js | 1 + dist/modules/identity/identity.module.js.map | 2 +- package-lock.json | 7 +++++-- package.json | 21 +++++++++++-------- src/modules/application/application.module.ts | 5 ++--- src/modules/identity/identity.module.ts | 1 + 8 files changed, 25 insertions(+), 19 deletions(-) diff --git a/dist/modules/application/application.module.js b/dist/modules/application/application.module.js index 93b38ff..923c5b5 100644 --- a/dist/modules/application/application.module.js +++ b/dist/modules/application/application.module.js @@ -20,13 +20,13 @@ const delete_application_handler_1 = require("./application/handlers/delete-appl const get_me_applications_handler_1 = require("./application/handlers/get-me-applications.handler"); const application_validator_1 = require("./application/validators/application.validator"); const jwt_auth_guard_1 = require("../../core/auth/guards/jwt-auth.guard"); -const current_user_guard_1 = require("../identity/presentation/guards/current-user.guard"); +const identity_module_1 = require("../identity/identity.module"); let ApplicationModule = class ApplicationModule { }; exports.ApplicationModule = ApplicationModule; exports.ApplicationModule = ApplicationModule = __decorate([ (0, common_1.Module)({ - imports: [], + imports: [identity_module_1.IdentityModule], providers: [ prisma_service_1.PrismaService, prisma_application_repository_1.PrismaApplicationRepository, @@ -38,7 +38,6 @@ exports.ApplicationModule = ApplicationModule = __decorate([ get_me_applications_handler_1.GetMeApplicationsHandler, application_validator_1.ApplicationValidator, jwt_auth_guard_1.JwtAuthGuard, - current_user_guard_1.CurrentUserGuard, { provide: application_interface_1.IApplication, useClass: prisma_application_repository_1.PrismaApplicationRepository, diff --git a/dist/modules/application/application.module.js.map b/dist/modules/application/application.module.js.map index d908fa3..100e91b 100644 --- a/dist/modules/application/application.module.js.map +++ b/dist/modules/application/application.module.js.map @@ -1 +1 @@ -{"version":3,"file":"application.module.js","sourceRoot":"","sources":["../../../src/modules/application/application.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,gEAA4D;AAC5D,gGAA4F;AAC5F,+GAA0G;AAC1G,uFAA2E;AAC3E,8FAAyF;AACzF,4FAAuF;AACvF,kGAA6F;AAC7F,kGAA6F;AAC7F,kGAA6F;AAC7F,oGAA8F;AAC9F,0FAAsF;AACtF,0EAAqE;AACrE,2FAAsF;AAuB/E,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;CAAG,CAAA;AAApB,8CAAiB;4BAAjB,iBAAiB;IArB7B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,EAAE;QACX,SAAS,EAAE;YACT,8BAAa;YACb,2DAA2B;YAC3B,iDAAsB;YACtB,+CAAqB;YACrB,qDAAwB;YACxB,qDAAwB;YACxB,qDAAwB;YACxB,sDAAwB;YACxB,4CAAoB;YACpB,6BAAY;YACZ,qCAAgB;YAChB;gBACE,OAAO,EAAE,oCAAY;gBACrB,QAAQ,EAAE,2DAA2B;aACtC;SACF;QACD,WAAW,EAAE,CAAC,gDAAsB,CAAC;KACtC,CAAC;GACW,iBAAiB,CAAG"} \ No newline at end of file +{"version":3,"file":"application.module.js","sourceRoot":"","sources":["../../../src/modules/application/application.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,gEAA4D;AAC5D,gGAA4F;AAC5F,+GAA0G;AAC1G,uFAA2E;AAC3E,8FAAyF;AACzF,4FAAuF;AACvF,kGAA6F;AAC7F,kGAA6F;AAC7F,kGAA6F;AAC7F,oGAA8F;AAC9F,0FAAsF;AACtF,0EAAqE;AACrE,iEAA6D;AAsBtD,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;CAAG,CAAA;AAApB,8CAAiB;4BAAjB,iBAAiB;IApB7B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,gCAAc,CAAC;QACzB,SAAS,EAAE;YACT,8BAAa;YACb,2DAA2B;YAC3B,iDAAsB;YACtB,+CAAqB;YACrB,qDAAwB;YACxB,qDAAwB;YACxB,qDAAwB;YACxB,sDAAwB;YACxB,4CAAoB;YACpB,6BAAY;YACZ;gBACE,OAAO,EAAE,oCAAY;gBACrB,QAAQ,EAAE,2DAA2B;aACtC;SACF;QACD,WAAW,EAAE,CAAC,gDAAsB,CAAC;KACtC,CAAC;GACW,iBAAiB,CAAG"} \ No newline at end of file diff --git a/dist/modules/identity/identity.module.js b/dist/modules/identity/identity.module.js index 8899f4e..29042db 100644 --- a/dist/modules/identity/identity.module.js +++ b/dist/modules/identity/identity.module.js @@ -107,6 +107,7 @@ exports.IdentityModule = IdentityModule = __decorate([ useClass: env_auth_config_1.EnvAuthConfig, }, ], + exports: [sync_identity_handler_1.SyncIdentityHandler, current_user_guard_1.CurrentUserGuard], controllers: [users_controller_1.UsersController, roles_controller_1.RolesController, permissions_controller_1.PermissionsController], }) ], IdentityModule); diff --git a/dist/modules/identity/identity.module.js.map b/dist/modules/identity/identity.module.js.map index 661d266..2d3cb78 100644 --- a/dist/modules/identity/identity.module.js.map +++ b/dist/modules/identity/identity.module.js.map @@ -1 +1 @@ -{"version":3,"file":"identity.module.js","sourceRoot":"","sources":["../../../src/modules/identity/identity.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,kFAA8E;AAC9E,kFAA8E;AAC9E,8FAA0F;AAC1F,gEAA4D;AAC5D,sEAAkE;AAClE,iGAA4F;AAC5F,yEAA6D;AAG7D,0EAAqE;AAErE,4FAAwF;AACxF,uCAAyC;AACzC,6FAAwF;AACxF,iFAA4E;AAC5E,iGAA4F;AAC5F,6GAAwG;AACxG,yEAA6D;AAC7D,qFAAyE;AACzE,sEAAiE;AACjE,0EAAqE;AACrE,qDAAiD;AACjD,gFAA4E;AAG5E,qFAAgF;AAChF,mFAA8E;AAC9E,mGAA6F;AAC7F,yFAAoF;AACpF,2FAAsF;AACtF,yFAAoF;AACpF,2FAAsF;AACtF,yFAAoF;AAIpF,qFAAgF;AAChF,mFAA8E;AAC9E,yFAAoF;AACpF,yFAAoF;AACpF,yFAAoF;AACpF,qGAAoG;AACpG,qGAAoG;AAEpG,uGAAkG;AAClG,qGAAgG;AAChG,2GAAsG;AACtG,2GAAsG;AACtG,2GAAsG;AAmE/F,IAAM,cAAc,GAApB,MAAM,cAAc;CAAG,CAAA;AAAjB,wCAAc;yBAAd,cAAc;IAjE1B,IAAA,eAAM,EAAC;QACJ,OAAO,EAAE,CAAC,wBAAU,EAAE,0CAAmB,CAAC;QAC5C,SAAS,EAAE;YAEL,eAAe;YACnB,iGAAiG;YACjG,2CAAmB;YACnB,mCAAe;YAEf,iCAAc;YACd,gDAAqB;YACrB,uCAAiB;YACjB,yCAAkB;YAClB,uCAAiB;YACb,yCAAkB;YACtB,uCAAiB;YAEjB,YAAY;YAEZ,6BAA6B;YAC7B,mCAAe;YACf,iCAAc;YACd,uCAAiB;YACjB,uCAAiB;YACjB,uCAAiB;YACjB,uDAA2B;YAC3B,uDAA2B;YAEvB,+CAAqB;YACzB,6CAAoB;YACpB,mDAAuB;YACvB,mDAAuB;YACvB,mDAAuB;YAEvB,6BAAY;YAEZ,qCAAgB;YAChB,kCAAe;YACf,gBAAS;YACT,8BAAa;YACb,6CAAoB;YACpB,6CAAoB;YACpB,yDAA0B;YACtB,0BAAW;YACf,uCAAiB;YAEjB;gBACE,OAAO,EAAE,sBAAK;gBACd,QAAQ,EAAE,6CAAoB;aAC/B;YACD;gBACE,OAAO,EAAE,sBAAK;gBACd,QAAQ,EAAE,6CAAoB;aAC/B;YACD;gBACE,OAAO,EAAE,kCAAW;gBACpB,QAAQ,EAAE,yDAA0B;aACrC;YACD;gBACE,OAAO,EAAE,2BAAW;gBACpB,QAAQ,EAAE,+BAAa;aACxB;SACI;QACP,WAAW,EAAE,CAAC,kCAAe,EAAE,kCAAe,EAAE,8CAAqB,CAAC;KACvE,CAAC;GACW,cAAc,CAAG"} \ No newline at end of file +{"version":3,"file":"identity.module.js","sourceRoot":"","sources":["../../../src/modules/identity/identity.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,kFAA8E;AAC9E,kFAA8E;AAC9E,8FAA0F;AAC1F,gEAA4D;AAC5D,sEAAkE;AAClE,iGAA4F;AAC5F,yEAA6D;AAG7D,0EAAqE;AAErE,4FAAwF;AACxF,uCAAyC;AACzC,6FAAwF;AACxF,iFAA4E;AAC5E,iGAA4F;AAC5F,6GAAwG;AACxG,yEAA6D;AAC7D,qFAAyE;AACzE,sEAAiE;AACjE,0EAAqE;AACrE,qDAAiD;AACjD,gFAA4E;AAG5E,qFAAgF;AAChF,mFAA8E;AAC9E,mGAA6F;AAC7F,yFAAoF;AACpF,2FAAsF;AACtF,yFAAoF;AACpF,2FAAsF;AACtF,yFAAoF;AAIpF,qFAAgF;AAChF,mFAA8E;AAC9E,yFAAoF;AACpF,yFAAoF;AACpF,yFAAoF;AACpF,qGAAoG;AACpG,qGAAoG;AAEpG,uGAAkG;AAClG,qGAAgG;AAChG,2GAAsG;AACtG,2GAAsG;AACtG,2GAAsG;AAoE/F,IAAM,cAAc,GAApB,MAAM,cAAc;CAAG,CAAA;AAAjB,wCAAc;yBAAd,cAAc;IAlE1B,IAAA,eAAM,EAAC;QACJ,OAAO,EAAE,CAAC,wBAAU,EAAE,0CAAmB,CAAC;QAC5C,SAAS,EAAE;YAEL,eAAe;YACnB,iGAAiG;YACjG,2CAAmB;YACnB,mCAAe;YAEf,iCAAc;YACd,gDAAqB;YACrB,uCAAiB;YACjB,yCAAkB;YAClB,uCAAiB;YACb,yCAAkB;YACtB,uCAAiB;YAEjB,YAAY;YAEZ,6BAA6B;YAC7B,mCAAe;YACf,iCAAc;YACd,uCAAiB;YACjB,uCAAiB;YACjB,uCAAiB;YACjB,uDAA2B;YAC3B,uDAA2B;YAEvB,+CAAqB;YACzB,6CAAoB;YACpB,mDAAuB;YACvB,mDAAuB;YACvB,mDAAuB;YAEvB,6BAAY;YAEZ,qCAAgB;YAChB,kCAAe;YACf,gBAAS;YACT,8BAAa;YACb,6CAAoB;YACpB,6CAAoB;YACpB,yDAA0B;YACtB,0BAAW;YACf,uCAAiB;YAEjB;gBACE,OAAO,EAAE,sBAAK;gBACd,QAAQ,EAAE,6CAAoB;aAC/B;YACD;gBACE,OAAO,EAAE,sBAAK;gBACd,QAAQ,EAAE,6CAAoB;aAC/B;YACD;gBACE,OAAO,EAAE,kCAAW;gBACpB,QAAQ,EAAE,yDAA0B;aACrC;YACD;gBACE,OAAO,EAAE,2BAAW;gBACpB,QAAQ,EAAE,+BAAa;aACxB;SACI;QACP,OAAO,EAAE,CAAC,2CAAmB,EAAE,qCAAgB,CAAC;QAChD,WAAW,EAAE,CAAC,kCAAe,EAAE,kCAAe,EAAE,8CAAqB,CAAC;KACvE,CAAC;GACW,cAAc,CAAG"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 42e9515..8d2fbe3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "@playwright/test": "^1.62.1", "@types/bcrypt": "^5.0.2", "@types/jest": "^29.5.14", - "@types/node": "^22.13.10", + "@types/node": "^22.20.1", "@types/passport-jwt": "^4.0.1", "@types/supertest": "^6.0.3", "@types/swagger-ui-express": "^4.1.8", @@ -59,7 +59,7 @@ "ts-jest": "^29.2.6", "ts-node": "^10.9.2", "ts-node-dev": "^2.0.0", - "typescript": "^5.8.2" + "typescript": "^5.9.3" }, "engines": { "node": ">=20.0.0" @@ -2377,6 +2377,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", "dev": true, + "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } @@ -7913,6 +7914,7 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, + "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -8100,6 +8102,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index 59403a7..63a581e 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "prisma:seed": "ts-node prisma/seed.ts" }, "dependencies": { + "@aws-sdk/client-s3": "^3.379.0", "@nestjs/common": "^10.4.20", "@nestjs/config": "^3.3.0", "@nestjs/core": "^10.4.20", @@ -34,25 +35,24 @@ "@nestjs/platform-express": "^10.4.20", "@nestjs/swagger": "^7.4.2", "@prisma/client": "^5.22.0", + "axios": "^1.4.0", "bcrypt": "^5.1.1", + "bullmq": "^1.73.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.2", "dotenv": "^16.6.1", + "ioredis": "^5.3.2", "jose": "^6.2.6", - "openid-client": "^6.5.0", "jsonwebtoken": "^9.0.2", + "nodemailer": "^6.9.4", + "openid-client": "^6.5.0", "passport": "^0.7.0", "passport-jwt": "^4.0.1", "pino": "^9.7.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.2", "swagger-ui-express": "^5.0.1", - "uuid": "^11.1.0", - "ioredis": "^5.3.2", - "bullmq": "^1.73.0", - "nodemailer": "^6.9.4", - "axios": "^1.4.0", - "@aws-sdk/client-s3": "^3.379.0" + "uuid": "^11.1.0" }, "devDependencies": { "@eslint/js": "^9.21.0", @@ -60,7 +60,7 @@ "@playwright/test": "^1.62.1", "@types/bcrypt": "^5.0.2", "@types/jest": "^29.5.14", - "@types/node": "^22.13.10", + "@types/node": "^22.20.1", "@types/passport-jwt": "^4.0.1", "@types/supertest": "^6.0.3", "@types/swagger-ui-express": "^4.1.8", @@ -75,6 +75,9 @@ "ts-jest": "^29.2.6", "ts-node": "^10.9.2", "ts-node-dev": "^2.0.0", - "typescript": "^5.8.2" + "typescript": "^5.9.3" + }, + "prisma": { + "seed": "node --loader ts-node/esm prisma/seed.ts" } } diff --git a/src/modules/application/application.module.ts b/src/modules/application/application.module.ts index 0b9ce52..f54a3ed 100644 --- a/src/modules/application/application.module.ts +++ b/src/modules/application/application.module.ts @@ -11,10 +11,10 @@ import { DeleteApplicationHandler } from './application/handlers/delete-applicat import { GetMeApplicationsHandler } from './application/handlers/get-me-applications.handler'; import { ApplicationValidator } from './application/validators/application.validator'; import { JwtAuthGuard } from '../../core/auth/guards/jwt-auth.guard'; -import { CurrentUserGuard } from '../identity/presentation/guards/current-user.guard'; +import { IdentityModule } from '../identity/identity.module'; @Module({ - imports: [], + imports: [IdentityModule], providers: [ PrismaService, PrismaApplicationRepository, @@ -26,7 +26,6 @@ import { CurrentUserGuard } from '../identity/presentation/guards/current-user.g GetMeApplicationsHandler, ApplicationValidator, JwtAuthGuard, - CurrentUserGuard, { provide: IApplication, useClass: PrismaApplicationRepository, diff --git a/src/modules/identity/identity.module.ts b/src/modules/identity/identity.module.ts index 5aae086..e024552 100644 --- a/src/modules/identity/identity.module.ts +++ b/src/modules/identity/identity.module.ts @@ -112,6 +112,7 @@ import { DeletePermissionHandler } from './application/handlers/permission/delet useClass: EnvAuthConfig, }, ], + exports: [SyncIdentityHandler, CurrentUserGuard], controllers: [UsersController, RolesController, PermissionsController], }) export class IdentityModule {} From c960fa046098be05efa27afa57f281530623f9a9 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Sun, 2 Aug 2026 23:29:11 +0700 Subject: [PATCH 08/34] fix npm run build --- dist/core/event-bus/event-bus.service.js | 14 +++++++++++--- dist/core/event-bus/event-bus.service.js.map | 2 +- dist/modules/auth/authentication.service.js | 4 ++-- dist/modules/auth/authentication.service.js.map | 2 +- src/core/event-bus/event-bus.service.ts | 14 +++++++++++--- src/modules/auth/authentication.service.ts | 4 ++-- 6 files changed, 28 insertions(+), 12 deletions(-) diff --git a/dist/core/event-bus/event-bus.service.js b/dist/core/event-bus/event-bus.service.js index 2421563..ce7de3c 100644 --- a/dist/core/event-bus/event-bus.service.js +++ b/dist/core/event-bus/event-bus.service.js @@ -12,11 +12,19 @@ const common_1 = require("@nestjs/common"); let EventBus = EventBus_1 = class EventBus { handlers = new Map(); logger = new common_1.Logger(EventBus_1.name); - publish(event) { - const handlers = this.handlers.get(event.type) || []; + // Accept either an EventEnvelope or (type, payload) signature for backward compatibility + publish(eventOrType, payload) { + let envelope; + if (typeof eventOrType === 'string') { + envelope = { id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: eventOrType, payload: payload ?? {} }; + } + else { + envelope = eventOrType; + } + const handlers = this.handlers.get(envelope.type) || []; for (const h of handlers) { try { - Promise.resolve(h(event)).catch((err) => this.logger.error('Event handler error', err)); + Promise.resolve(h(envelope)).catch((err) => this.logger.error('Event handler error', err)); } catch (e) { this.logger.error('Event handler threw', e); diff --git a/dist/core/event-bus/event-bus.service.js.map b/dist/core/event-bus/event-bus.service.js.map index 3aa167f..e8e8fcd 100644 --- a/dist/core/event-bus/event-bus.service.js.map +++ b/dist/core/event-bus/event-bus.service.js.map @@ -1 +1 @@ -{"version":3,"file":"event-bus.service.js","sourceRoot":"","sources":["../../../src/core/event-bus/event-bus.service.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAoD;AAO7C,IAAM,QAAQ,gBAAd,MAAM,QAAQ;IACX,QAAQ,GAA2B,IAAI,GAAG,EAAE,CAAC;IACpC,MAAM,GAAG,IAAI,eAAM,CAAC,UAAQ,CAAC,IAAI,CAAC,CAAC;IAEpD,OAAO,CAAC,KAAyB;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACrD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC,CAAC;YAC1F,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAQ,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;IACH,CAAC;IAED,SAAS,CAAC,SAAiB,EAAE,OAAgB;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;CACF,CAAA;AApBY,4BAAQ;mBAAR,QAAQ;IADpB,IAAA,mBAAU,GAAE;GACA,QAAQ,CAoBpB"} \ No newline at end of file +{"version":3,"file":"event-bus.service.js","sourceRoot":"","sources":["../../../src/core/event-bus/event-bus.service.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAoD;AAO7C,IAAM,QAAQ,gBAAd,MAAM,QAAQ;IACX,QAAQ,GAA2B,IAAI,GAAG,EAAE,CAAC;IACpC,MAAM,GAAG,IAAI,eAAM,CAAC,UAAQ,CAAC,IAAI,CAAC,CAAC;IAEpD,yFAAyF;IACzF,OAAO,CAAC,WAAwC,EAAE,OAAa;QAC7D,IAAI,QAA4B,CAAC;QACjC,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;YACpC,QAAQ,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;QACpI,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,WAAW,CAAC;QACzB,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACxD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC,CAAC;YAC7F,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAQ,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;IACH,CAAC;IAED,SAAS,CAAC,SAAiB,EAAE,OAAgB;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;CACF,CAAA;AA5BY,4BAAQ;mBAAR,QAAQ;IADpB,IAAA,mBAAU,GAAE;GACA,QAAQ,CA4BpB"} \ No newline at end of file diff --git a/dist/modules/auth/authentication.service.js b/dist/modules/auth/authentication.service.js index d95cea1..e37a0c8 100644 --- a/dist/modules/auth/authentication.service.js +++ b/dist/modules/auth/authentication.service.js @@ -53,8 +53,8 @@ let AuthenticationService = AuthenticationService_1 = class AuthenticationServic const rolesRows = await this.prisma.userRole.findMany({ where: { userId: user.id } }); const roles = rolesRows.map((r) => r.roleId); const ctx = { user, identity, roles, permissions }; - // Publish domain event - this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'UserAuthenticated', payload: { userId: user.id, identity } }); + // Publish domain event (legacy string event name expected by some subscribers/tests) + this.events.publish('user.authenticated', { userId: user.id, identity }); return ctx; } }; diff --git a/dist/modules/auth/authentication.service.js.map b/dist/modules/auth/authentication.service.js.map index dd36d16..ad8598e 100644 --- a/dist/modules/auth/authentication.service.js.map +++ b/dist/modules/auth/authentication.service.js.map @@ -1 +1 @@ -{"version":3,"file":"authentication.service.js","sourceRoot":"","sources":["../../../src/modules/auth/authentication.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAoD;AACpD,iDAA6C;AAC7C,gEAA4D;AAC5D,2DAAsD;AACtD,kFAA8E;AAC9E,8EAAkE;AAI3D,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IAIb;IACA;IACA;IACA;IACA;IAPF,MAAM,GAAG,IAAI,eAAM,CAAC,uBAAqB,CAAC,IAAI,CAAC,CAAC;IAEjE,YACmB,IAAiB,EACjB,MAAqB,EACrB,QAAyB,EACzB,aAAmC,EACnC,MAAgB;QAJhB,SAAI,GAAJ,IAAI,CAAa;QACjB,WAAM,GAAN,MAAM,CAAe;QACrB,aAAQ,GAAR,QAAQ,CAAiB;QACzB,kBAAa,GAAb,aAAa,CAAsB;QACnC,WAAM,GAAN,MAAM,CAAU;IAChC,CAAC;IAEJ,KAAK,CAAC,YAAY,CAAC,WAAmB;QACpC,yCAAyC;QACzC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;QAExD,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;QACvB,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAExD,mBAAmB;QACnB,MAAM,QAAQ,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;QAE1G,oCAAoC;QACpC,IAAI,IAAI,GAAG,MAAO,IAAI,CAAC,MAAc,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QACvF,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,GAAG,MAAO,IAAI,CAAC,MAAc,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,kBAAkB,IAAI,QAAQ,CAAC,KAAK,IAAI,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;QAC/K,CAAC;QAED,oBAAoB;QACpB,MAAM,MAAM,GAAa,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,CAAC,QAAQ,CAAC,0BAA0B,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAEhE,mBAAmB;QACnB,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEzE,wBAAwB;QACxB,MAAM,SAAS,GAAG,MAAO,IAAI,CAAC,MAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/F,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAElD,MAAM,GAAG,GAAmB,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;QAEnE,uBAAuB;QACvB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QAEpK,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAA;AA7CY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;qCAKc,0BAAW;QACT,8BAAa;QACX,mCAAe;QACV,4CAAoB;QAC3B,4BAAQ;GARxB,qBAAqB,CA6CjC"} \ No newline at end of file +{"version":3,"file":"authentication.service.js","sourceRoot":"","sources":["../../../src/modules/auth/authentication.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/core/event-bus/event-bus.service.ts b/src/core/event-bus/event-bus.service.ts index b8b0336..1b805a8 100644 --- a/src/core/event-bus/event-bus.service.ts +++ b/src/core/event-bus/event-bus.service.ts @@ -9,11 +9,19 @@ export class EventBus { private handlers: Map = new Map(); private readonly logger = new Logger(EventBus.name); - publish(event: EventEnvelope) { - const handlers = this.handlers.get(event.type) || []; + // Accept either an EventEnvelope or (type, payload) signature for backward compatibility + publish(eventOrType: EventEnvelope | string, payload?: any) { + let envelope: EventEnvelope; + if (typeof eventOrType === 'string') { + envelope = { id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: eventOrType, payload: payload ?? {} }; + } else { + envelope = eventOrType; + } + + const handlers = this.handlers.get(envelope.type) || []; for (const h of handlers) { try { - Promise.resolve(h(event)).catch((err) => this.logger.error('Event handler error', err)); + Promise.resolve(h(envelope)).catch((err) => this.logger.error('Event handler error', err)); } catch (e) { this.logger.error('Event handler threw', e as any); } diff --git a/src/modules/auth/authentication.service.ts b/src/modules/auth/authentication.service.ts index 7f22740..9817ba6 100644 --- a/src/modules/auth/authentication.service.ts +++ b/src/modules/auth/authentication.service.ts @@ -47,8 +47,8 @@ export class AuthenticationService { const ctx: RequestContext = { user, identity, roles, permissions }; - // Publish domain event - this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'UserAuthenticated', payload: { userId: user.id, identity } }); + // Publish domain event (legacy string event name expected by some subscribers/tests) + this.events.publish('user.authenticated', { userId: user.id, identity }); return ctx; } From fee8e680896481fad5b56244a47fbdfbe68e30d8 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Sun, 2 Aug 2026 23:44:02 +0700 Subject: [PATCH 09/34] Update schema.prisma --- prisma/schema.prisma | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 491d146..68320f1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -172,18 +172,19 @@ model MediaObject { } model Application { - id String @id @default(uuid()) @db.Uuid - code String @unique @db.VarChar(100) - name String @db.VarChar(255) - description String? - icon String? - url String? - applicationsClaim String @unique @db.VarChar(255) - isActive Boolean @default(true) - displayOrder Int @default(0) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid @map("id") + code String @unique @db.VarChar(100) @map("code") + name String @db.VarChar(255) @map("name") + description String? @db.Text @map("description") + icon String? @db.Text @map("icon") + url String? @db.Text @map("url") + applicationsClaim String @unique @db.VarChar(255) @map("applicationsClaim") + isActive Boolean @default(true) @map("isActive") + displayOrder Int @default(0) @db.Integer @map("displayOrder") + createdAt DateTime @default(dbgenerated("now()")) @db.Timestamptz @map("createdAt") + updatedAt DateTime @default(dbgenerated("now()")) @db.Timestamptz @map("updatedAt") - @@index([code]) - @@index([applicationsClaim]) + @@index([code], name: "Application_code_idx") + @@index([applicationsClaim], name: "Application_applicationsClaim_idx") + @@map("Application") } From f2af2cb6e24683f1f2e00b83d2288e6ef6c31034 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Sun, 2 Aug 2026 23:46:51 +0700 Subject: [PATCH 10/34] Update schema.prisma --- prisma/schema.prisma | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 68320f1..24e5945 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -172,17 +172,17 @@ model MediaObject { } model Application { - id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid @map("id") - code String @unique @db.VarChar(100) @map("code") - name String @db.VarChar(255) @map("name") - description String? @db.Text @map("description") - icon String? @db.Text @map("icon") - url String? @db.Text @map("url") - applicationsClaim String @unique @db.VarChar(255) @map("applicationsClaim") + id String @id @default(dbgenerated("gen_random_uuid()")) @map("id") @db.Uuid + code String @unique @map("code") @db.VarChar(100) + name String @map("name") @db.VarChar(255) + description String? @map("description") @db.Text + icon String? @map("icon") @db.Text + url String? @map("url") @db.Text + applicationsClaim String @unique @map("applicationsClaim") @db.VarChar(255) isActive Boolean @default(true) @map("isActive") - displayOrder Int @default(0) @db.Integer @map("displayOrder") - createdAt DateTime @default(dbgenerated("now()")) @db.Timestamptz @map("createdAt") - updatedAt DateTime @default(dbgenerated("now()")) @db.Timestamptz @map("updatedAt") + displayOrder Int @default(0) @map("displayOrder") @db.Integer + createdAt DateTime @default(dbgenerated("now()")) @map("createdAt") @db.Timestamptz + updatedAt DateTime @default(dbgenerated("now()")) @map("updatedAt") @db.Timestamptz @@index([code], name: "Application_code_idx") @@index([applicationsClaim], name: "Application_applicationsClaim_idx") From c475e76ad074ceea368582cece3bcdc768443286 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 00:29:33 +0700 Subject: [PATCH 11/34] Create deploy.yml --- .gitea/workflows/deploy.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .gitea/workflows/deploy.yml diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..7b5b233 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,27 @@ +name: Deploy + +on: + push: + branches: + - main + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + + - name: Deploy + + run: | + + cd /DATA/git/RayLab-Core + + git fetch origin + + git reset --hard origin/main + + cd /DATA/docker/raylab-core + + docker compose up -d --build \ No newline at end of file From 4abeb3c62acfedfa858dea647fe5fc43b5706230 Mon Sep 17 00:00:00 2001 From: Rayyan Date: Sun, 2 Aug 2026 17:38:06 +0000 Subject: [PATCH 12/34] Fix workflow --- .gitea/workflows/deploy.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 7b5b233..a04aff6 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -7,21 +7,13 @@ on: jobs: deploy: - runs-on: ubuntu-latest steps: - - name: Deploy - run: | - cd /DATA/git/RayLab-Core - git fetch origin - git reset --hard origin/main - cd /DATA/docker/raylab-core - - docker compose up -d --build \ No newline at end of file + docker compose up -d --build From ae30b9bf1636185620645c6df047c22b05935633 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 00:49:38 +0700 Subject: [PATCH 13/34] Test Gitea Actions From d3a8eab11844d1af5c6e5f537401cf3fd7f00754 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 00:53:18 +0700 Subject: [PATCH 14/34] Delete PHASE1_README.md --- PHASE1_README.md | 165 ----------------------------------------------- 1 file changed, 165 deletions(-) delete mode 100644 PHASE1_README.md diff --git a/PHASE1_README.md b/PHASE1_README.md deleted file mode 100644 index 4a6aefd..0000000 --- a/PHASE1_README.md +++ /dev/null @@ -1,165 +0,0 @@ -Phase 1 Deliverables - RayLab Core -================================= - -Contents --------- -1. Architecture explanation -2. Folder structure -3. Source code references (what was added/changed) -4. Database schema (Prisma) -5. Migration (SQL) -6. Seed data (script) -7. API design (initial endpoints) -8. Design decisions -9. Advantages -10. Possible future extensions -11. How to run (migrate & seed) - -1) Architecture explanation --------------------------- -Phase 1 establishes the foundational data model and project layout for RayLab Core. It prepares the system for secure integration with Authentik (OIDC) and supports role/permission based authorization owned by RayLab Core. The design follows Clean Architecture, SOLID principles, modular NestJS structure, and Prisma for type-safe DB access. - -Key principles applied: -- RayLab Core does NOT implement authentication; it only validates external OIDC tokens (Phase 2). -- Roles, permissions, role mappings, users, and audit logs are owned by RayLab Core. Media Manager and Scheduler are intentionally excluded from Phase 1 and will be implemented in Phase 3. -- No Group entity is created in RayLab Core; only mapping configuration (auth group name string -> role) is stored. -- Critical operations (synchronization) will be transactional (Phase 2 implementation). - -2) Folder structure -------------------- -(Only top-level relevant directories shown) - -- prisma/ - - schema.prisma // Prisma schema (source of truth for DB) - - migrations/0001_init/ // Initial SQL migration - - seed.ts // Seed script (idempotent upserts) - -- src/ - - main.ts - - app.module.ts - - modules/ - - identity/ // DDD-based users/roles/permissions implementation - - auth/ // (Phase 2) OIDC integration - - authorization/ // (Phase 2) permission guards & services - - health/ // basic health endpoint (added) - - media/ // media manager module (phase 3) - - audit/ // audit module (phase 3) - - ... other domain modules - - shared/ - - prisma.service.ts - - ... - -3) Source code (what was added/changed) ---------------------------------------- -- prisma/schema.prisma (UPDATED): replaced with production-ready schema containing: - User, Role, Permission, RolePermission, UserRole, AuthGroupRoleMapping, AuditLog - -- prisma/migrations/0001_init/migration.sql (ADDED): initial SQL migration DDL - -- prisma/seed.ts (UPDATED): seed script that creates default roles, permissions, role-permission assignments, and Authentik group -> role mappings - -- src/modules/health/* (ADDED): HealthModule, Controller, Service (thin controller) - -- src/shared/prisma.service.ts (EXISTING): PrismaService (already present) - -4) Database schema ------------------- -The Prisma schema (prisma/schema.prisma) is the authoritative schema. High-level model summary: -- User: id (uuid), externalId (unique), email, name, picture, metadata, createdAt, updatedAt -- Role: id, name (unique), displayName, description -- Permission: id, name (unique), description -- RolePermission: join table role <-> permission (unique constraint roleId+permissionId) -- UserRole: join table user <-> role with source (e.g., 'authentik') and syncedAt timestamp (unique userId+roleId) -- AuthGroupRoleMapping: mapping table authGroup (string) -> roleId. One authGroup may map to multiple roles (no uniqueness enforced on authGroup). -- AuditLog: append-only audit logs with JSON details - - -Indexes and constraints are included for common lookup paths. The migration SQL contains explicit DDL statements. - -5) Migration ------------- -- prisma/migrations/0001_init/migration.sql contains the SQL to create the schema. -- The migration file includes CREATE EXTENSION IF NOT EXISTS "pgcrypto" to provide gen_random_uuid(). -- Recommended migration procedure in production: - 1. Ensure backups and schedule maintenance window for initial deployment. - 2. Run migrations with Prisma or psql: "psql < migration.sql" or use "prisma migrate deploy" after generating migrations from schema.prisma. - -6) Seed data ------------- -- prisma/seed.ts seeds the following: - - Default roles: Owner, Employee, Family - - Default permissions following resource.action naming convention (users.create, users.read, ..., scheduler.manage) - - RolePermission assignments: - - Owner receives all permissions - - Employee and Family receive reasonable subsets (editable later) - - Authentik group mappings: - - RL-Owner -> Owner - - RL-Employee -> Employee - - RL-Family -> Family - -The seed script is idempotent using upsert operations. - -7) API design (initial) ------------------------ -Phase 1 provides a minimal, safe public surface to verify system health and readiness. -- GET /api/health - - Returns: { status: 'ok', timestamp, service } - -Identity module controllers (users, roles, permissions) are present under src/modules/identity/presentation/controllers and follow RESTful conventions, but full auth/guards are implemented in Phase 2. - -8) Design decisions -------------------- -- Prisma selected for type-safe DB access and migrations. (User requested Prisma) -- UUID primary keys (Postgres gen_random_uuid()) for scalability and horizontal distribution. -- JSONB (Prisma Json) used for flexible metadata and audit details. -- AuthGroupRoleMapping stores external group string only; RayLab Core does NOT model Groups as domain entities. -- Seed sets conservative permission assignments; owners get full permissions. -- Minimal initial API surface (health) avoids unintentionally exposing functionality before auth & guards are in place. - -9) Advantages -------------- -- Production-ready DB schema with indices and constraints for performance and data integrity. -- Modular NestJS structure aligned with Clean Architecture / DDD; easy to extend in subsequent phases. -- Prisma provides type-safety and reduces runtime errors. -- Seed script allows reproducible environments and CI setup. -- Audit and media tables are present to support required domain features. - -10) Possible future extensions ------------------------------- -Planned for Phase 2 and 3 (once Phase 1 is approved): -- OIDC validation using openid-client and configuration from env vars -- Role synchronization flow (validate token -> read groups -> map -> compute hash -> replace UserRole in transaction -> invalidate permission cache -> write audit) -- Permission cache using Redis, invalidation hooks -- Auth guards and AuthorizationService for permission checks -- Media Manager: presigned URLs, storage backend (S3/MinIO) -- Scheduler: BullMQ integration with Redis -- Monitoring: Prometheus metrics, structured logging, Sentry - -11) How to run (migrate & seed) -------------------------------- -Prerequisites: -- Node 20+ -- PostgreSQL reachable via DATABASE_URL env var -- Install dependencies: npm ci - -Generate Prisma client and run migration: - -1) Generate Prisma client: - npm run prisma:generate - -2) Apply migration (development): - npm run prisma:migrate - - Or run SQL directly against the database: - psql "$DATABASE_URL" -f prisma/migrations/0001_init/migration.sql - -3) Run seed script: - npm run prisma:seed - -4) Start the app (development): - npm run start:dev - -Next steps ----------- -Please review Phase 1 deliverables. After your approval I will implement Phase 2 (OIDC module, Authentik integration, Group->Role Sync, Authorization Guard, Permission Cache, Audit logging) following the same level of production-quality implementation. - From 0bb9212a10a83650125f853bb374b6a16f0339b6 Mon Sep 17 00:00:00 2001 From: Rayyan Date: Sun, 2 Aug 2026 17:58:59 +0000 Subject: [PATCH 15/34] Test deploy From 81e8258c1a92a926be7b0c833fbd72522164ec8f Mon Sep 17 00:00:00 2001 From: Rayyan Date: Sun, 2 Aug 2026 18:00:50 +0000 Subject: [PATCH 16/34] Test deploy From 95d03bb987b5c9dea9b5b925943ce62eb21172ed Mon Sep 17 00:00:00 2001 From: Rayyan Date: Sun, 2 Aug 2026 18:07:38 +0000 Subject: [PATCH 17/34] test: trigger deploy From 6d6a771c7cf641eccb2359d5c1adc849d426d6fd Mon Sep 17 00:00:00 2001 From: Rayyan Date: Sun, 2 Aug 2026 18:08:58 +0000 Subject: [PATCH 18/34] Update deploy workflow --- .gitea/workflows/deploy.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index a04aff6..21bee14 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -12,8 +12,13 @@ jobs: steps: - name: Deploy run: | + git config --global --add safe.directory /DATA/git/RayLab-Core + cd /DATA/git/RayLab-Core + git fetch origin git reset --hard origin/main + cd /DATA/docker/raylab-core + docker compose up -d --build From 1826bc789e2d35629b3fac33dcf18e1ebe453676 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:48:52 +0700 Subject: [PATCH 19/34] Update api_spec.txt --- api_spec.txt | 305 ++++++++++++++++++++++++++------------------------- 1 file changed, 155 insertions(+), 150 deletions(-) diff --git a/api_spec.txt b/api_spec.txt index 8682759..20b2fb8 100644 --- a/api_spec.txt +++ b/api_spec.txt @@ -1,146 +1,174 @@ -API Spec - Contoh REST API (Bahasa Indonesia) +API Spec - Identity & Authorization API (Bahasa Indonesia) Ringkasan -------- -API ini adalah contoh RESTful API yang bisa dipakai untuk manajemen pengguna, item, dan pesanan. Menggunakan JSON untuk request/response. Autentikasi memakai Bearer Token (JWT). +Spesifikasi berikut disesuaikan dengan controller aktual pada aplikasi: modul Auth (OIDC + internal JWT), modul Identity (Users, Roles, Permissions) dan mekanisme otorisasi berbasis permission. Base URL -------- -- https://api.example.com/v1 +- https://api.example.com -Header Umum ------------ -- Authorization: Bearer (kecuali endpoint login/register) +Header Umum & Auth +------------------ - Content-Type: application/json - Accept: application/json +- Otentikasi dapat diberikan dalam dua cara: + 1) Cookie internal (default flow): + - raylab_jwt (internal JWT, httpOnly cookie) + - raylab_refresh (internal refresh token, httpOnly cookie) + 2) Authorization header: Bearer +- Banyak endpoint melindungi akses dengan JWT dan permission checks. Respons sukses dari controller umumnya dibungkus sebagai: + { "success": true, "data": ..., "meta": { ... } } -Autentikasi ------------ -- POST /auth/login - - Body (application/json): - { - "email": "user@example.com", - "password": "string" - } - - Response 200: - { - "access_token": "", - "token_type": "Bearer", - "expires_in": 3600 - } - - Kesalahan: 400 (invalid input), 401 (invalid credentials) +Auth (modul /auth) +------------------- +1) GET /auth/login +- Deskripsi: Mulai flow Authorization Code + PKCE. Redirect (302) ke Identity Provider. +- Query params: + - returnTo (optional) - URL tujuan setelah login +- Response: 302 Redirect -- POST /auth/register - - Body: - { - "name": "Nama User", - "email": "user@example.com", - "password": "password123" - } - - Response 201: user created (id, name, email) +2) GET /auth/callback +- Deskripsi: Endpoint callback OIDC. Menukarkan code/state, membuat internal JWT & refresh token, dan menyetel cookie httpOnly. +- Response: 302 Redirect ke returnTo atau '/' +- Cookie yang disetel: + - raylab_jwt (internal JWT, maxAge sesuai expiresIn) + - raylab_refresh (refresh token) + +3) POST /auth/logout +- Deskripsi: Invalidate internal refresh token (opsional) dan redirect ke logout identity provider. +- Body (optional): { "refreshToken": "..." } +- Response: 302 Redirect + +4) POST /auth/refresh +- Deskripsi: Tukar refresh token menjadi internal JWT baru. +- Input: refresh token di cookie raylab_refresh atau di body { "refreshToken": "..." } +- Response 200: { "success": true, "data": { "accessToken": "...", "expiresIn": 3600, ... } } + +5) GET /auth/me +- Deskripsi: Ambil data user saat ini dari internal JWT (cookie atau Authorization header). +- Response 200: { "success": true, "data": { /* user object */ } } + +Catatan: tidak ada endpoint "/auth/register" atau POST /auth/login berbasis email/password pada controller saat ini — login terjadi via OIDC dan internal session cookies. + +Users (modul /users) +-------------------- +Semua endpoint Users dijalankan di bawah guards: JwtAuthGuard, CurrentUserGuard dan PermissionGuard. Respons mengikuti format { success, data, meta }. -Endpoint Pengguna (Users) -------------------------- 1) GET /users -- Deskripsi: Mendapatkan daftar pengguna (admin) -- Query params: - - page (int, optional, default=1) - - per_page (int, optional, default=20, max=100) - - sort (string, optional, contoh: "created_at:desc") - - q (string, optional) - pencarian nama/email +- Permission: PermissionType.USER_READ +- Deskripsi: List pengguna (paged) +- Query params umum: page, per_page, sort, q - Response 200: - { - "data": [ {"id":1, "name":"...", "email":"..."} , ...], - "meta": {"page":1, "per_page":20, "total":123} - } + { "success": true, "data": [ /* user list (toResponse) */ ], "meta": { "total": 123 } } -2) GET /users/{id} -- Path params: - - id (integer, required) -- Response 200: user object -- Errors: 404 jika tidak ditemukan +2) GET /users/me +- Deskripsi: Ambil profil user saat ini (token harus ada di cookie atau header) +- Response 200: { "success": true, "data": { /* current user */ }, "meta": {} } -3) PUT /users/{id} -- Path params: id -- Body (application/json): - { - "name": "Nama Baru", - "email": "email@baru.com" - } -- Validasi: email harus format valid; name min 2 karakter -- Response 200: updated user - -4) DELETE /users/{id} -- Path params: id -- Response 204: no content -- Permissions: hanya admin - -Endpoint Item (Items) ---------------------- -1) GET /items -- Query params: - - page, per_page, sort (lihat Users) - - category (string, optional) - - min_price, max_price (decimal, optional) -- Response: list items dengan fields id, name, description, price, stock - -2) POST /items -- Body: - { - "name": "Nama Item", - "description": "Deskripsi...", - "price": 125000.50, - "stock": 10, - "category": "Elektronik" - } -- Validasi: - - name (required, max 255) - - price (required, >=0) - - stock (integer, >=0) -- Response 201: created item -- Permissions: admin atau vendor - -3) GET /items/{id} -- Response 200: item object +3) GET /users/:id +- Permission: PermissionType.USER_READ +- Deskripsi: Ambil user berdasarkan id +- Response 200: { "success": true, "data": { /* user */ }, "meta": {} } - 404 jika tidak ditemukan -4) PUT /items/{id} -- Body: fields yang boleh diupdate (name, description, price, stock, category) +4) PATCH /users/:id/enable +- Permission: PermissionType.USER_UPDATE +- Deskripsi: Enable user +- Response 200: { "success": true, "data": { /* updated user */ }, "meta": {} } + +5) PATCH /users/:id/disable +- Permission: PermissionType.USER_UPDATE +- Deskripsi: Disable user - Response 200 -5) DELETE /items/{id} -- Response 204 +6) PATCH /users/:id +- Permission: PermissionType.USER_UPDATE +- Deskripsi: Update profil user (body berisi fields yang diperbolehkan) +- Body contoh: { "name": "Nama Baru", "email": "baru@example.com" } +- Response 200: { "success": true, "data": { /* updated user */ }, "meta": {} } -Endpoint Pesanan (Orders) -------------------------- -1) POST /orders -- Body: - { - "user_id": 12, // optional jika token sudah mewakili user - "items": [ - {"item_id": 5, "quantity": 2}, - {"item_id": 7, "quantity": 1} - ], - "shipping_address": "Alamat lengkap", - "note": "Catatan opsional" - } -- Validasi: setiap item quantity >= 1 dan tersedia di stock -- Response 201: - { - "order_id": 987, - "status": "pending", - "total": 375000.00 - } +7) DELETE /users/:id +- Permission: PermissionType.USER_DELETE +- Deskripsi: Soft delete user +- Response 200: { "success": true, "data": null, "meta": {} } -2) GET /orders/{id} -- Path param: id -- Response 200: full order detail (items, prices, shipping, status) -- Permissions: hanya pemilik order atau admin +8) POST /users/:id/restore +- Permission: PermissionType.USER_UPDATE +- Deskripsi: Restore user yang di-soft-delete +- Response 200: { "success": true, "data": { /* restored user */ }, "meta": {} } -3) PATCH /orders/{id}/status -- Body: {"status": "shipped"} -- Allowed status: pending, confirmed, shipped, delivered, cancelled -- Permissions: hanya admin atau staff +Roles (modul /roles) +-------------------- +Semua route dilindungi oleh JwtAuthGuard, CurrentUserGuard dan PermissionGuard. + +1) GET /roles +- Permission: PermissionType.ROLE_READ +- Deskripsi: List roles +- Query params: page, per_page, q +- Response 200: { "success": true, "data": [ /* roles */ ], "meta": { "total": 10 } } + +2) GET /roles/:id +- Permission: PermissionType.ROLE_READ +- Deskripsi: Ambil role +- Response 200 + +3) POST /roles +- Permission: PermissionType.ROLE_CREATE +- Deskripsi: Buat role baru +- Body contoh: { "name": "editor", "description": "..." } +- Response 200: { "success": true, "data": { /* created role */ }, "meta": {} } + +4) PATCH /roles/:id +- Permission: PermissionType.ROLE_UPDATE +- Deskripsi: Update role +- Response 200 + +5) DELETE /roles/:id +- Permission: PermissionType.ROLE_DELETE +- Deskripsi: Hapus role +- Response 200: { "success": true, "data": null, "meta": {} } + +6) POST /roles/:id/permissions +- Permission: PermissionType.ROLE_UPDATE +- Deskripsi: Assign permission ke role +- Body: { "permissionId": "..." } +- Response 200: { "success": true, "data": { /* updated role */ }, "meta": {} } + +7) DELETE /roles/:id/permissions/:permissionId +- Permission: PermissionType.ROLE_UPDATE +- Deskripsi: Remove permission dari role +- Response 200 + +Permissions (modul /permissions) +------------------------------- +Semua route dilindungi oleh JwtAuthGuard, CurrentUserGuard dan PermissionGuard. + +1) GET /permissions +- Permission: PermissionType.PERMISSION_READ +- Deskripsi: List permissions +- Response 200: { "success": true, "data": [ /* permissions */ ], "meta": { "total": 20 } } + +2) GET /permissions/:id +- Permission: PermissionType.PERMISSION_READ +- Deskripsi: Ambil permission +- Response 200 + +3) POST /permissions +- Permission: PermissionType.PERMISSION_CREATE +- Deskripsi: Buat permission baru +- Body contoh: { "name": "user:create", "description": "..." } +- Response 200: { "success": true, "data": { /* created */ }, "meta": {} } + +4) PATCH /permissions/:id +- Permission: PermissionType.PERMISSION_UPDATE +- Deskripsi: Update permission +- Response 200 + +5) DELETE /permissions/:id +- Permission: PermissionType.PERMISSION_DELETE +- Deskripsi: Delete permission +- Response 200: { "success": true, "data": null, "meta": {} } Format Tanggal dan Numerik -------------------------- @@ -150,12 +178,13 @@ Format Tanggal dan Numerik Pagination ---------- - Gunakan page & per_page -- Meta object harus mengandung total, page, per_page, total_pages +- Meta object pada controller ini biasanya minimal: { total } Response Error Umum ------------------- - 400 Bad Request - payload tidak valid { + "success": false, "error": "invalid_request", "message": "Deskripsi kesalahan", "details": { "field": ["pesan validasi"] } @@ -166,38 +195,14 @@ Response Error Umum - 429 Too Many Requests - rate limit - 500 Internal Server Error -Rate Limiting -------------- -- Contoh: 1000 requests per 1 jam per API key -- Header terkait: - - X-RateLimit-Limit: 1000 - - X-RateLimit-Remaining: 750 - - X-RateLimit-Reset: 1650000000 (epoch seconds) - Keamanan dan Persyaratan ------------------------ - Semua permintaan harus lewat HTTPS (TLS 1.2+) -- Gunakan header Authorization: Bearer -- CORS: domain yang diijinkan harus didaftarkan +- Gunakan cookie httpOnly (raylab_jwt / raylab_refresh) untuk flow internal atau header Authorization: Bearer - Validasi input di server (length, tipe, range) - Sanitasi data untuk mencegah injection -Versioning ----------- -- Versi di URL: /v1/ -- Buat v2 jika ada breaking change - -Contoh Request/Response (cURL) ------------------------------ -Login: -curl -X POST "https://api.example.com/v1/auth/login" \ - -H "Content-Type: application/json" \ - -d '{"email":"user@example.com","password":"password123"}' - -Ambil daftar item: -curl "https://api.example.com/v1/items?page=1&per_page=20" \ - -H "Authorization: Bearer " - -Footer ------- -Spesifikasi ini adalah contoh umum. Sesuaikan endpoint, nama field, aturan autentikasi, dan kebijakan rate limit sesuai kebutuhan aplikasi Anda. \ No newline at end of file +Catatan Akhir +------------ +- Spesifikasi ini disesuaikan dengan controller yang ada: /auth, /users, /roles, /permissions dan mekanisme otorisasi berbasis PermissionType. +- Perhatikan bahwa detail field respons (mis. bentuk toResponse pada entitas) dapat berbeda antar resource. Untuk integrasi, gunakan endpoint /auth/me dan /users/me untuk memvalidasi data user yang tersedia. \ No newline at end of file From 470396c2f175b115d67abf997dd5ebe7f919b0c1 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 20:32:17 +0700 Subject: [PATCH 20/34] fix cors --- src/app.module.ts | 17 +++++++++++++++-- src/main.ts | 20 +++++++++++++++++++- src/modules/auth/auth.controller.ts | 13 +++++++++---- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index b2459ba..d91512d 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -12,8 +12,21 @@ import { ApplicationModule } from './modules/application/application.module'; @Module({ imports: [ ConfigModule.forRoot({ - isGlobal: true, - }), + isGlobal: true, + // Load .env files depending on NODE_ENV. Default to development .env + envFilePath: process.env.NODE_ENV === 'production' ? '.env.production' : '.env', + // Basic validation: ensure expected frontend URLs are present + validate: (env: Record) => { + const errors: string[] = []; + if (!env.FRONTEND_URL) errors.push('FRONTEND_URL is not set'); + if (!env.PRODUCTION_FRONTEND_URL) { + // production frontend URL is recommended but not mandatory for local development + if (process.env.NODE_ENV === 'production') errors.push('PRODUCTION_FRONTEND_URL is not set'); + } + if (errors.length > 0) throw new Error('Environment validation error: ' + errors.join('; ')); + return env; + }, + }), IdentityModule, AuthModule, diff --git a/src/main.ts b/src/main.ts index f6e22bd..71d5c0c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -20,7 +20,25 @@ async function bootstrap() { }), ); - app.enableCors(); + // CORS configuration: only allow configured frontend origins and enable credentials + const allowedOrigins: string[] = []; + const frontend = config.get('FRONTEND_URL'); + const prodFrontend = config.get('PRODUCTION_FRONTEND_URL'); + if (frontend) allowedOrigins.push(frontend); + if (prodFrontend) allowedOrigins.push(prodFrontend); + + app.enableCors({ + origin: allowedOrigins, + credentials: true, + methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], + allowedHeaders: [ + 'Content-Type', + 'Authorization', + 'Accept', + 'Origin', + 'X-Requested-With', + ], + }); const swaggerEnabled = config.get('SWAGGER_ENABLED') === 'true'; diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 5ef7553..79df924 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -3,11 +3,12 @@ import { Controller, Get, Post, Query, Res, Req, Body, HttpCode, HttpStatus } fr import { ApiTags, ApiOperation } from '@nestjs/swagger'; import { AuthService } from './auth.service'; import { Response, Request } from 'express'; +import { ConfigService } from '@nestjs/config'; @ApiTags('Auth') @Controller('auth') export class AuthController { - constructor(private readonly authService: AuthService) {} + constructor(private readonly authService: AuthService, private readonly config: ConfigService) {} @Get('login') @ApiOperation({ summary: 'Start Authorization Code + PKCE login (redirect to Identity Provider)' }) @@ -21,13 +22,17 @@ export class AuthController { async callback(@Query('code') code: string, @Query('state') state: string, @Res() res: Response) { const result = await this.authService.handleCallback(code, state); - // set cookies + // set cookies with environment-aware options + const isProd = this.config.get('NODE_ENV') === 'production' || process.env.NODE_ENV === 'production'; + const cookieDomain = this.config.get('RAYLAB_COOKIE_DOMAIN') || (isProd ? '.raylab.site' : undefined); + const cookieOptions: any = { httpOnly: true, - secure: process.env.NODE_ENV === 'production', - sameSite: 'lax', + secure: isProd, // secure in production + sameSite: isProd ? 'none' : 'lax', // cross-site in production path: '/', }; + if (cookieDomain) cookieOptions.domain = cookieDomain; // access token cookie (internal JWT) res.cookie('raylab_jwt', result.accessToken, { ...cookieOptions, maxAge: result.expiresIn * 1000 }); From 2cd377ca7a4a53d385a13677b9330f67185eb048 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 20:47:05 +0700 Subject: [PATCH 21/34] Update auth.service.ts --- src/modules/auth/auth.service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index acea84b..9fb01ed 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -7,7 +7,7 @@ import { PrismaService } from '../../shared/prisma.service'; import { RedisPkceStore } from './pkce/redis-pkce.store'; import { InMemoryRefreshStore } from './refresh/inmemory-refresh.store'; -const openidClient: any = require('openid-client'); +const { Issuer, generators } = require('openid-client'); @Injectable() export class AuthService { @@ -28,7 +28,7 @@ export class AuthService { if (this.issuer) return this.issuer; const issuerUrl = this.config.get('AUTHENTIK_ISSUER'); if (!issuerUrl) throw new Error('AUTHENTIK_ISSUER not configured'); - this.issuer = await openidClient.Issuer.discover(issuerUrl); + this.issuer = await Issuer.discover(issuerUrl); return this.issuer; } @@ -47,9 +47,9 @@ export class AuthService { const redirectUri = this.config.get('AUTH_CALLBACK_URL') || `${this.config.get('APP_URL') || ''}/auth/callback`; const state = require('crypto').randomUUID(); - const code_verifier = openidClient.generators.codeVerifier(); - const code_challenge = await openidClient.generators.codeChallenge(code_verifier); - const nonce = openidClient.generators.nonce(); + const code_verifier = generators.codeVerifier(); + const code_challenge = await generators.codeChallenge(code_verifier); + const nonce = generators.nonce(); await this.pkceStore.save(state, { code_verifier, nonce, returnTo }, 300); From 6f814632803aba07a7f2e49fd1de46ea3adb4c23 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 22:29:22 +0700 Subject: [PATCH 22/34] Update auth.service.ts --- src/modules/auth/auth.service.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 9fb01ed..9a8c2b6 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -44,8 +44,17 @@ export class AuthService { async createAuthorizationRedirect(returnTo?: string) { const client = await this.getClient(); - const redirectUri = this.config.get('AUTH_CALLBACK_URL') || `${this.config.get('APP_URL') || ''}/auth/callback`; + const redirectUri = + this.config.get('AUTHENTIK_REDIRECT_URI') ?? + this.config.get('AUTH_CALLBACK_URL') ?? + `${this.config.get('APP_URL')}/auth/callback`; + if (!redirectUri) { + throw new Error( + 'No redirect URI configured. Set AUTHENTIK_REDIRECT_URI or AUTH_CALLBACK_URL.', + ); + } + const state = require('crypto').randomUUID(); const code_verifier = generators.codeVerifier(); const code_challenge = await generators.codeChallenge(code_verifier); From 737c4fa5d1dbc448a4064f47c85c28c7647f0763 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 22:45:01 +0700 Subject: [PATCH 23/34] fix --- src/app.module.ts | 16 ++++--- src/modules/auth/auth.service.ts | 77 +++++++++++++++++++------------- 2 files changed, 57 insertions(+), 36 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index d91512d..8e0b09d 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -15,14 +15,20 @@ import { ApplicationModule } from './modules/application/application.module'; isGlobal: true, // Load .env files depending on NODE_ENV. Default to development .env envFilePath: process.env.NODE_ENV === 'production' ? '.env.production' : '.env', - // Basic validation: ensure expected frontend URLs are present + // Basic validation: ensure expected frontend URLs and OIDC settings are present validate: (env: Record) => { const errors: string[] = []; + const nodeEnv = env.NODE_ENV || process.env.NODE_ENV || 'development'; + if (!env.FRONTEND_URL) errors.push('FRONTEND_URL is not set'); - if (!env.PRODUCTION_FRONTEND_URL) { - // production frontend URL is recommended but not mandatory for local development - if (process.env.NODE_ENV === 'production') errors.push('PRODUCTION_FRONTEND_URL is not set'); - } + if (nodeEnv === 'production' && !env.PRODUCTION_FRONTEND_URL) errors.push('PRODUCTION_FRONTEND_URL is not set'); + + // OIDC required settings + if (!env.AUTHENTIK_ISSUER) errors.push('AUTHENTIK_ISSUER is not set'); + if (!env.AUTHENTIK_CLIENT_ID) errors.push('AUTHENTIK_CLIENT_ID is not set'); + if (!env.AUTHENTIK_CLIENT_SECRET) errors.push('AUTHENTIK_CLIENT_SECRET is not set'); + if (!env.AUTHENTIK_REDIRECT_URI) errors.push('AUTHENTIK_REDIRECT_URI is not set'); + if (errors.length > 0) throw new Error('Environment validation error: ' + errors.join('; ')); return env; }, diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 9a8c2b6..717d289 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -1,4 +1,4 @@ -import { Injectable, UnauthorizedException, Inject } from '@nestjs/common'; +import { Injectable, UnauthorizedException, Inject, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { JwtService } from '@nestjs/jwt'; import { SyncIdentityHandler } from '../identity/application/handlers/user/sync-identity.handler'; @@ -6,8 +6,10 @@ import { IUser } from '../identity/domain/repositories/user.interface'; import { PrismaService } from '../../shared/prisma.service'; import { RedisPkceStore } from './pkce/redis-pkce.store'; import { InMemoryRefreshStore } from './refresh/inmemory-refresh.store'; +import { Issuer, generators, Client, TokenSet } from 'openid-client'; +import crypto from 'crypto'; -const { Issuer, generators } = require('openid-client'); +const logger = new Logger('AuthService'); @Injectable() export class AuthService { @@ -21,8 +23,8 @@ export class AuthService { private readonly refreshStore: InMemoryRefreshStore, ) {} - private issuer: any = null; - private client: any = null; + private issuer: Issuer | null = null; + private client: Client | null = null; private async getIssuer() { if (this.issuer) return this.issuer; @@ -42,24 +44,20 @@ export class AuthService { return this.client; } - async createAuthorizationRedirect(returnTo?: string) { + async createAuthorizationRedirect(returnTo?: string): Promise { const client = await this.getClient(); - const redirectUri = - this.config.get('AUTHENTIK_REDIRECT_URI') ?? - this.config.get('AUTH_CALLBACK_URL') ?? - `${this.config.get('APP_URL')}/auth/callback`; + const redirectUri = this.config.get('AUTHENTIK_REDIRECT_URI'); if (!redirectUri) { - throw new Error( - 'No redirect URI configured. Set AUTHENTIK_REDIRECT_URI or AUTH_CALLBACK_URL.', - ); + throw new Error('AUTHENTIK_REDIRECT_URI is not configured'); } - - const state = require('crypto').randomUUID(); + + const state = crypto.randomUUID(); const code_verifier = generators.codeVerifier(); const code_challenge = await generators.codeChallenge(code_verifier); const nonce = generators.nonce(); + // save PKCE session keyed by state await this.pkceStore.save(state, { code_verifier, nonce, returnTo }, 300); const url = client.authorizationUrl({ @@ -77,34 +75,51 @@ export class AuthService { async handleCallback(code: string, state: string) { const client = await this.getClient(); + + // retrieve PKCE session using state provided by the IdP const pkce = await this.pkceStore.get(state); if (!pkce) throw new UnauthorizedException('Invalid or expired state'); - // remove one-time state + const redirectUri = this.config.get('AUTHENTIK_REDIRECT_URI'); + if (!redirectUri) throw new Error('AUTHENTIK_REDIRECT_URI is not configured'); + + // Exchange code for tokens. Provide explicit checks: state, nonce and code_verifier. + let tokenSet: TokenSet; + try { + tokenSet = await client.callback( + redirectUri, + { code, state }, + { state, nonce: pkce.nonce, code_verifier: pkce.code_verifier }, + ); + } catch (err) { + logger.debug('Authorization code exchange failed: ' + (err as Error).message); + // remove PKCE entry to avoid replay + await this.pkceStore.remove(state); + throw new UnauthorizedException('Authorization code exchange failed'); + } + + // remove PKCE entry after successful exchange await this.pkceStore.remove(state); - const redirectUri = this.config.get('AUTH_CALLBACK_URL') || `${this.config.get('APP_URL') || ''}/auth/callback`; - - // exchange code - const tokenSet: any = await client.callback(redirectUri, { code, state }, { code_verifier: pkce.code_verifier, nonce: pkce.nonce }); - // verify id_token and get claims const claims = tokenSet.claims(); // fetch userinfo if available - let userInfo = null; + let userInfo: Record | null = null; try { - if (tokenSet.access_token && client.userinfo) { - userInfo = await client.userinfo(tokenSet.access_token); + if ((tokenSet as any).access_token && typeof client.userinfo === 'function') { + userInfo = await client.userinfo((tokenSet as any).access_token); } } catch (e) { - // ignore + // ignore userinfo errors } const identity = { - sub: userInfo?.sub || claims.sub || null, - preferred_username: userInfo?.preferred_username || userInfo?.username || userInfo?.email || claims.preferred_username || claims.email, - email: userInfo?.email || claims.email, + sub: (userInfo && (userInfo as any).sub) || (claims as any).sub || null, + preferred_username: + (userInfo && ((userInfo as any).preferred_username || (userInfo as any).username || (userInfo as any).email)) || + (claims as any).preferred_username || (claims as any).email, + email: (userInfo && (userInfo as any).email) || (claims as any).email, raw: { tokenSet, userInfo, claims }, } as any; @@ -120,13 +135,13 @@ export class AuthService { sub: domainUser.id, preferred_username: domainUser.username, email: domainUser.email, - } as any; + }; const expiresIn = Number(this.config.get('RAYLAB_JWT_EXPIRES_IN') || 3600); const access = this.jwtService.sign(jwtPayload, { expiresIn }); // create internal refresh token - const refreshToken = require('crypto').randomUUID(); + const refreshToken = crypto.randomUUID(); const refreshTtl = Number(this.config.get('RAYLAB_REFRESH_EXPIRES_IN') || 30 * 24 * 3600); // default 30 days await this.refreshStore.set(refreshToken, { userId: domainUser.id }, refreshTtl); @@ -159,7 +174,7 @@ export class AuthService { // rotate refresh token await this.refreshStore.del(refreshToken); - const newRefresh = require('crypto').randomUUID(); + const newRefresh = crypto.randomUUID(); const refreshTtl = Number(this.config.get('RAYLAB_REFRESH_EXPIRES_IN') || 30 * 24 * 3600); await this.refreshStore.set(newRefresh, { userId }, refreshTtl); @@ -176,7 +191,7 @@ export class AuthService { const issuer = await this.getIssuer(); const endSession = issuer.metadata.end_session_endpoint; - const postLogout = this.config.get('APP_URL') || '/'; + const postLogout = this.config.get('AUTHENTIK_POST_LOGOUT_REDIRECT') || '/'; if (endSession) { // Redirect to identity provider logout From b78a6bce2c6c9b18a9e225cadc42b282a1ae2b8b Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 23:05:30 +0700 Subject: [PATCH 24/34] Fix migration --- prisma/migrations/.gitkeep | 0 prisma/migrations/0001_init/migration.sql | 112 ------- .../migrations/0002_phase4_init/migration.sql | 37 --- .../20260730165554_permission/migration.sql | 93 ------ .../20260801084138_change/migration.sql | 16 - .../migration.sql | 2 - .../migration.sql | 11 - .../migration.sql | 10 - .../migration.sql | 15 - .../20260802_add_application/migration.sql | 18 -- .../migration.sql | 17 -- .../20260803160408_init/migration.sql | 277 ++++++++++++++++++ .../20260803_add_storage/migration.sql | 7 - 13 files changed, 277 insertions(+), 338 deletions(-) delete mode 100644 prisma/migrations/.gitkeep delete mode 100644 prisma/migrations/0001_init/migration.sql delete mode 100644 prisma/migrations/0002_phase4_init/migration.sql delete mode 100644 prisma/migrations/20260730165554_permission/migration.sql delete mode 100644 prisma/migrations/20260801084138_change/migration.sql delete mode 100644 prisma/migrations/20260801101438_add_role_is_default/migration.sql delete mode 100644 prisma/migrations/20260801101841_add_authentik_id/migration.sql delete mode 100644 prisma/migrations/20260801104311_add_deleted_at/migration.sql delete mode 100644 prisma/migrations/20260801132311_add_authentik_fields/migration.sql delete mode 100644 prisma/migrations/20260802_add_application/migration.sql delete mode 100644 prisma/migrations/20260802_add_authentik_fields/migration.sql create mode 100644 prisma/migrations/20260803160408_init/migration.sql delete mode 100644 prisma/migrations/20260803_add_storage/migration.sql diff --git a/prisma/migrations/.gitkeep b/prisma/migrations/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/prisma/migrations/0001_init/migration.sql b/prisma/migrations/0001_init/migration.sql deleted file mode 100644 index 54f181d..0000000 --- a/prisma/migrations/0001_init/migration.sql +++ /dev/null @@ -1,112 +0,0 @@ --- Initial migration for RayLab Core (PostgreSQL) --- Requires: CREATE EXTENSION IF NOT EXISTS pgcrypto; (for gen_random_uuid()) - -BEGIN; - --- Extension for UUID generation -CREATE EXTENSION IF NOT EXISTS "pgcrypto"; - --- Enum types -DO $$ BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'userrolesource') THEN - CREATE TYPE "UserRoleSource" AS ENUM ('AUTHENTIK', 'SYSTEM'); - END IF; -END $$; - --- Users -CREATE TABLE IF NOT EXISTS "User" ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - authentik_id text UNIQUE, - authentik_user_id text UNIQUE, - authentik_subject text UNIQUE, - username varchar(255) UNIQUE, - email varchar(320) UNIQUE, - name varchar(255), - picture text, - is_active boolean NOT NULL DEFAULT true, - deleted_at timestamptz, - last_seen_at timestamptz, - last_synced_at timestamptz, - sync_status text, - storage_quota bigint DEFAULT 10737418240, - storage_used bigint DEFAULT 0, - metadata jsonb, - last_group_hash text, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX IF NOT EXISTS idx_user_authentik_id ON "User" (authentik_id); -CREATE INDEX IF NOT EXISTS idx_user_username ON "User" (username); -CREATE INDEX IF NOT EXISTS idx_user_email ON "User" (email); - --- Roles -CREATE TABLE IF NOT EXISTS "Role" ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - code varchar(100) NOT NULL UNIQUE, - name varchar(255) NOT NULL, - display_name varchar(255), - description text, - is_default boolean NOT NULL DEFAULT false, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); - --- Permissions -CREATE TABLE IF NOT EXISTS "Permission" ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - code varchar(150) NOT NULL UNIQUE, - name varchar(255) NOT NULL, - display_name varchar(255), - description text, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); - --- RolePermission -CREATE TABLE IF NOT EXISTS "RolePermission" ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - role_id uuid NOT NULL REFERENCES "Role"(id) ON DELETE CASCADE, - permission_id uuid NOT NULL REFERENCES "Permission"(id) ON DELETE CASCADE, - created_at timestamptz NOT NULL DEFAULT now(), - CONSTRAINT uq_role_permission UNIQUE (role_id, permission_id) -); -CREATE INDEX IF NOT EXISTS idx_rolepermission_role_id ON "RolePermission" (role_id); -CREATE INDEX IF NOT EXISTS idx_rolepermission_permission_id ON "RolePermission" (permission_id); - --- UserRole -CREATE TABLE IF NOT EXISTS "UserRole" ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES "User"(id) ON DELETE CASCADE, - role_id uuid NOT NULL REFERENCES "Role"(id) ON DELETE CASCADE, - source "UserRoleSource" NOT NULL DEFAULT 'AUTHENTIK', - synced_at timestamptz NOT NULL DEFAULT now(), - created_at timestamptz NOT NULL DEFAULT now(), - CONSTRAINT uq_user_role UNIQUE (user_id, role_id) -); -CREATE INDEX IF NOT EXISTS idx_userrole_user_id ON "UserRole" (user_id); -CREATE INDEX IF NOT EXISTS idx_userrole_role_id ON "UserRole" (role_id); - --- AuthGroupRoleMapping -CREATE TABLE IF NOT EXISTS "AuthGroupRoleMapping" ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - auth_group varchar(255) NOT NULL, - role_id uuid NOT NULL REFERENCES "Role"(id) ON DELETE CASCADE, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX IF NOT EXISTS idx_authgroup_name ON "AuthGroupRoleMapping" (auth_group); - --- AuditLog -CREATE TABLE IF NOT EXISTS "AuditLog" ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid REFERENCES "User"(id) ON DELETE SET NULL, - action varchar(200) NOT NULL, - resource varchar(200), - resource_id text, - details jsonb, - created_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX IF NOT EXISTS idx_auditlog_user_id ON "AuditLog" (user_id); -CREATE INDEX IF NOT EXISTS idx_auditlog_action ON "AuditLog" (action); - -COMMIT; diff --git a/prisma/migrations/0002_phase4_init/migration.sql b/prisma/migrations/0002_phase4_init/migration.sql deleted file mode 100644 index 7c13bc6..0000000 --- a/prisma/migrations/0002_phase4_init/migration.sql +++ /dev/null @@ -1,37 +0,0 @@ --- Phase 4 migration: add ScheduledJob and MediaObject - -BEGIN; - --- ScheduledJob -CREATE TABLE IF NOT EXISTS "ScheduledJob" ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name varchar(255) NOT NULL, - payload jsonb, - cron varchar(255), - run_at timestamptz, - status varchar(50) NOT NULL DEFAULT 'pending', - attempts integer NOT NULL DEFAULT 0, - last_run_at timestamptz, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX IF NOT EXISTS idx_scheduledjob_name ON "ScheduledJob" (name); -CREATE INDEX IF NOT EXISTS idx_scheduledjob_status ON "ScheduledJob" (status); - --- MediaObject -CREATE TABLE IF NOT EXISTS "MediaObject" ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - owner_user_id uuid REFERENCES "User"(id) ON DELETE SET NULL, - storage_key varchar(1024) NOT NULL, - filename varchar(1024), - mime_type varchar(255), - size bigint, - is_public boolean NOT NULL DEFAULT false, - metadata jsonb, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); -CREATE INDEX IF NOT EXISTS idx_media_owner_user_id ON "MediaObject" (owner_user_id); -CREATE INDEX IF NOT EXISTS idx_media_storage_key ON "MediaObject" (storage_key); - -COMMIT; diff --git a/prisma/migrations/20260730165554_permission/migration.sql b/prisma/migrations/20260730165554_permission/migration.sql deleted file mode 100644 index 23e8e28..0000000 --- a/prisma/migrations/20260730165554_permission/migration.sql +++ /dev/null @@ -1,93 +0,0 @@ --- CreateTable -CREATE TABLE "User" ( - "id" TEXT NOT NULL, - "username" TEXT NOT NULL, - "email" TEXT NOT NULL, - "password" TEXT NOT NULL, - "isActive" BOOLEAN NOT NULL DEFAULT true, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "User_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Role" ( - "id" TEXT NOT NULL, - "code" TEXT NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "Role_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Permission" ( - "id" TEXT NOT NULL, - "code" TEXT NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "Permission_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "UserRole" ( - "userId" TEXT NOT NULL, - "roleId" TEXT NOT NULL, - "assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "UserRole_pkey" PRIMARY KEY ("userId","roleId") -); - --- CreateTable -CREATE TABLE "RolePermission" ( - "roleId" TEXT NOT NULL, - "permissionId" TEXT NOT NULL, - "assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "RolePermission_pkey" PRIMARY KEY ("roleId","permissionId") -); - --- CreateTable -CREATE TABLE "UserPermission" ( - "userId" TEXT NOT NULL, - "permissionId" TEXT NOT NULL, - "assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "UserPermission_pkey" PRIMARY KEY ("userId","permissionId") -); - --- CreateIndex -CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); - --- CreateIndex -CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); - --- CreateIndex -CREATE UNIQUE INDEX "Role_code_key" ON "Role"("code"); - --- CreateIndex -CREATE UNIQUE INDEX "Permission_code_key" ON "Permission"("code"); - --- AddForeignKey -ALTER TABLE "UserRole" ADD CONSTRAINT "UserRole_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "UserRole" ADD CONSTRAINT "UserRole_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "Role"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "RolePermission" ADD CONSTRAINT "RolePermission_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "Role"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "RolePermission" ADD CONSTRAINT "RolePermission_permissionId_fkey" FOREIGN KEY ("permissionId") REFERENCES "Permission"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "UserPermission" ADD CONSTRAINT "UserPermission_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "UserPermission" ADD CONSTRAINT "UserPermission_permissionId_fkey" FOREIGN KEY ("permissionId") REFERENCES "Permission"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260801084138_change/migration.sql b/prisma/migrations/20260801084138_change/migration.sql deleted file mode 100644 index f73805a..0000000 --- a/prisma/migrations/20260801084138_change/migration.sql +++ /dev/null @@ -1,16 +0,0 @@ -/* - Warnings: - - - Added the required column `assignedBy` to the `RolePermission` table without a default value. This is not possible if the table is not empty. - - Added the required column `assignedBy` to the `UserPermission` table without a default value. This is not possible if the table is not empty. - - Added the required column `assignedBy` to the `UserRole` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE "RolePermission" ADD COLUMN "assignedBy" TEXT NOT NULL; - --- AlterTable -ALTER TABLE "UserPermission" ADD COLUMN "assignedBy" TEXT NOT NULL; - --- AlterTable -ALTER TABLE "UserRole" ADD COLUMN "assignedBy" TEXT NOT NULL; diff --git a/prisma/migrations/20260801101438_add_role_is_default/migration.sql b/prisma/migrations/20260801101438_add_role_is_default/migration.sql deleted file mode 100644 index dabc294..0000000 --- a/prisma/migrations/20260801101438_add_role_is_default/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Role" ADD COLUMN "isDefault" BOOLEAN NOT NULL DEFAULT false; diff --git a/prisma/migrations/20260801101841_add_authentik_id/migration.sql b/prisma/migrations/20260801101841_add_authentik_id/migration.sql deleted file mode 100644 index aa49398..0000000 --- a/prisma/migrations/20260801101841_add_authentik_id/migration.sql +++ /dev/null @@ -1,11 +0,0 @@ -/* - Warnings: - - - A unique constraint covering the columns `[authentikId]` on the table `User` will be added. If there are existing duplicate values, this will fail. - -*/ --- AlterTable -ALTER TABLE "User" ADD COLUMN "authentikId" TEXT; - --- CreateIndex -CREATE UNIQUE INDEX "User_authentikId_key" ON "User"("authentikId"); diff --git a/prisma/migrations/20260801104311_add_deleted_at/migration.sql b/prisma/migrations/20260801104311_add_deleted_at/migration.sql deleted file mode 100644 index a9d12c0..0000000 --- a/prisma/migrations/20260801104311_add_deleted_at/migration.sql +++ /dev/null @@ -1,10 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `password` on the `User` table. All the data in the column will be lost. - -*/ --- AlterTable -ALTER TABLE "User" DROP COLUMN "password", -ADD COLUMN "deletedAt" TIMESTAMP(3), -ADD COLUMN "lastSeenAt" TIMESTAMP(3); diff --git a/prisma/migrations/20260801132311_add_authentik_fields/migration.sql b/prisma/migrations/20260801132311_add_authentik_fields/migration.sql deleted file mode 100644 index 32ee861..0000000 --- a/prisma/migrations/20260801132311_add_authentik_fields/migration.sql +++ /dev/null @@ -1,15 +0,0 @@ -/* - Warnings: - - - A unique constraint covering the columns `[authentikUserId]` on the table `User` will be added. If there are existing duplicate values, this will fail. - - A unique constraint covering the columns `[authentikSubject]` on the table `User` will be added. If there are existing duplicate values, this will fail. - -*/ --- AlterTable -ALTER TABLE "User" ALTER COLUMN "lastSyncedAt" SET DATA TYPE TIMESTAMP(3); - --- CreateIndex -CREATE UNIQUE INDEX "User_authentikUserId_key" ON "User"("authentikUserId"); - --- CreateIndex -CREATE UNIQUE INDEX "User_authentikSubject_key" ON "User"("authentikSubject"); diff --git a/prisma/migrations/20260802_add_application/migration.sql b/prisma/migrations/20260802_add_application/migration.sql deleted file mode 100644 index ba75c12..0000000 --- a/prisma/migrations/20260802_add_application/migration.sql +++ /dev/null @@ -1,18 +0,0 @@ --- Migration: add Application table - -CREATE TABLE IF NOT EXISTS "Application" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), - "code" varchar(100) NOT NULL UNIQUE, - "name" varchar(255) NOT NULL, - "description" text, - "icon" text, - "url" text, - "applicationsClaim" varchar(255) NOT NULL UNIQUE, - "isActive" boolean NOT NULL DEFAULT true, - "displayOrder" integer NOT NULL DEFAULT 0, - "createdAt" timestamptz NOT NULL DEFAULT now(), - "updatedAt" timestamptz NOT NULL DEFAULT now() -); - -CREATE INDEX IF NOT EXISTS "Application_code_idx" ON "Application" ("code"); -CREATE INDEX IF NOT EXISTS "Application_applicationsClaim_idx" ON "Application" ("applicationsClaim"); diff --git a/prisma/migrations/20260802_add_authentik_fields/migration.sql b/prisma/migrations/20260802_add_authentik_fields/migration.sql deleted file mode 100644 index fc9ffb9..0000000 --- a/prisma/migrations/20260802_add_authentik_fields/migration.sql +++ /dev/null @@ -1,17 +0,0 @@ --- Migration: add_authentik_fields - -ALTER TABLE "User" -ADD COLUMN IF NOT EXISTS "authentikUserId" TEXT; - -ALTER TABLE "User" -ADD COLUMN IF NOT EXISTS "authentikSubject" TEXT; - -ALTER TABLE "User" -ADD COLUMN IF NOT EXISTS "lastSyncedAt" TIMESTAMP; - -ALTER TABLE "User" -ADD COLUMN IF NOT EXISTS "syncStatus" TEXT; - --- Optional: add unique constraints if desired --- DO NOT add unique constraints without verifying existing data --- ALTER TABLE "User" ADD CONSTRAINT "User_authentikUserId_key" UNIQUE ("authentikUserId"); diff --git a/prisma/migrations/20260803160408_init/migration.sql b/prisma/migrations/20260803160408_init/migration.sql new file mode 100644 index 0000000..8d9d1ce --- /dev/null +++ b/prisma/migrations/20260803160408_init/migration.sql @@ -0,0 +1,277 @@ +-- CreateEnum +CREATE TYPE "UserRoleSource" AS ENUM ('AUTHENTIK', 'SYSTEM'); + +-- CreateTable +CREATE TABLE "User" ( + "id" UUID NOT NULL, + "authentikId" TEXT, + "authentikUserId" TEXT, + "authentikSubject" TEXT, + "username" VARCHAR(255), + "email" VARCHAR(320), + "name" VARCHAR(255), + "picture" TEXT, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "deletedAt" TIMESTAMP(3), + "lastSeenAt" TIMESTAMP(3), + "lastSyncedAt" TIMESTAMP(3), + "syncStatus" TEXT, + "storageQuota" BIGINT DEFAULT 10737418240, + "storageUsed" BIGINT DEFAULT 0, + "metadata" JSONB, + "lastGroupHash" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Role" ( + "id" UUID NOT NULL, + "code" VARCHAR(100) NOT NULL, + "name" VARCHAR(255) NOT NULL, + "displayName" VARCHAR(255), + "description" TEXT, + "isDefault" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Role_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Permission" ( + "id" UUID NOT NULL, + "code" VARCHAR(150) NOT NULL, + "name" VARCHAR(255) NOT NULL, + "displayName" VARCHAR(255), + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Permission_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "RolePermission" ( + "id" UUID NOT NULL, + "roleId" UUID NOT NULL, + "permissionId" UUID NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "RolePermission_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "UserPermission" ( + "id" UUID NOT NULL, + "userId" UUID NOT NULL, + "permissionId" UUID NOT NULL, + "assignedBy" VARCHAR(100), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "UserPermission_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "UserRole" ( + "id" UUID NOT NULL, + "userId" UUID NOT NULL, + "roleId" UUID NOT NULL, + "source" "UserRoleSource" NOT NULL DEFAULT 'AUTHENTIK', + "syncedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "UserRole_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "AuthGroupRoleMapping" ( + "id" UUID NOT NULL, + "authGroup" VARCHAR(255) NOT NULL, + "roleId" UUID NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "AuthGroupRoleMapping_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "AuditLog" ( + "id" UUID NOT NULL, + "userId" UUID, + "action" VARCHAR(200) NOT NULL, + "resource" VARCHAR(200), + "resourceId" TEXT, + "details" JSONB, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "AuditLog_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ScheduledJob" ( + "id" UUID NOT NULL, + "name" VARCHAR(255) NOT NULL, + "payload" JSONB, + "cron" TEXT, + "runAt" TIMESTAMP(3), + "status" VARCHAR(50) NOT NULL DEFAULT 'pending', + "attempts" INTEGER NOT NULL DEFAULT 0, + "lastRunAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ScheduledJob_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "MediaObject" ( + "id" UUID NOT NULL, + "ownerUserId" UUID, + "storageKey" VARCHAR(1024) NOT NULL, + "filename" VARCHAR(1024), + "mimeType" VARCHAR(255), + "size" BIGINT, + "isPublic" BOOLEAN NOT NULL DEFAULT false, + "metadata" JSONB, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "MediaObject_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Application" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "code" VARCHAR(100) NOT NULL, + "name" VARCHAR(255) NOT NULL, + "description" TEXT, + "icon" TEXT, + "url" TEXT, + "applicationsClaim" VARCHAR(255) NOT NULL, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "displayOrder" INTEGER NOT NULL DEFAULT 0, + "createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(), + "updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now(), + + CONSTRAINT "Application_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_authentikId_key" ON "User"("authentikId"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_authentikUserId_key" ON "User"("authentikUserId"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_authentikSubject_key" ON "User"("authentikSubject"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE INDEX "User_authentikId_idx" ON "User"("authentikId"); + +-- CreateIndex +CREATE INDEX "User_username_idx" ON "User"("username"); + +-- CreateIndex +CREATE INDEX "User_email_idx" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Role_code_key" ON "Role"("code"); + +-- CreateIndex +CREATE UNIQUE INDEX "Permission_code_key" ON "Permission"("code"); + +-- CreateIndex +CREATE INDEX "RolePermission_roleId_idx" ON "RolePermission"("roleId"); + +-- CreateIndex +CREATE INDEX "RolePermission_permissionId_idx" ON "RolePermission"("permissionId"); + +-- CreateIndex +CREATE UNIQUE INDEX "RolePermission_roleId_permissionId_key" ON "RolePermission"("roleId", "permissionId"); + +-- CreateIndex +CREATE INDEX "UserPermission_userId_idx" ON "UserPermission"("userId"); + +-- CreateIndex +CREATE INDEX "UserPermission_permissionId_idx" ON "UserPermission"("permissionId"); + +-- CreateIndex +CREATE UNIQUE INDEX "UserPermission_userId_permissionId_key" ON "UserPermission"("userId", "permissionId"); + +-- CreateIndex +CREATE INDEX "UserRole_userId_idx" ON "UserRole"("userId"); + +-- CreateIndex +CREATE INDEX "UserRole_roleId_idx" ON "UserRole"("roleId"); + +-- CreateIndex +CREATE UNIQUE INDEX "UserRole_userId_roleId_key" ON "UserRole"("userId", "roleId"); + +-- CreateIndex +CREATE INDEX "AuthGroupRoleMapping_authGroup_idx" ON "AuthGroupRoleMapping"("authGroup"); + +-- CreateIndex +CREATE INDEX "AuditLog_userId_idx" ON "AuditLog"("userId"); + +-- CreateIndex +CREATE INDEX "AuditLog_action_idx" ON "AuditLog"("action"); + +-- CreateIndex +CREATE INDEX "ScheduledJob_name_idx" ON "ScheduledJob"("name"); + +-- CreateIndex +CREATE INDEX "ScheduledJob_status_idx" ON "ScheduledJob"("status"); + +-- CreateIndex +CREATE INDEX "MediaObject_ownerUserId_idx" ON "MediaObject"("ownerUserId"); + +-- CreateIndex +CREATE INDEX "MediaObject_storageKey_idx" ON "MediaObject"("storageKey"); + +-- CreateIndex +CREATE UNIQUE INDEX "Application_code_key" ON "Application"("code"); + +-- CreateIndex +CREATE UNIQUE INDEX "Application_applicationsClaim_key" ON "Application"("applicationsClaim"); + +-- CreateIndex +CREATE INDEX "Application_code_idx" ON "Application"("code"); + +-- CreateIndex +CREATE INDEX "Application_applicationsClaim_idx" ON "Application"("applicationsClaim"); + +-- AddForeignKey +ALTER TABLE "RolePermission" ADD CONSTRAINT "RolePermission_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "Role"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RolePermission" ADD CONSTRAINT "RolePermission_permissionId_fkey" FOREIGN KEY ("permissionId") REFERENCES "Permission"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UserPermission" ADD CONSTRAINT "UserPermission_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UserPermission" ADD CONSTRAINT "UserPermission_permissionId_fkey" FOREIGN KEY ("permissionId") REFERENCES "Permission"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UserRole" ADD CONSTRAINT "UserRole_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UserRole" ADD CONSTRAINT "UserRole_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "Role"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "AuthGroupRoleMapping" ADD CONSTRAINT "AuthGroupRoleMapping_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "Role"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "AuditLog" ADD CONSTRAINT "AuditLog_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "MediaObject" ADD CONSTRAINT "MediaObject_ownerUserId_fkey" FOREIGN KEY ("ownerUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20260803_add_storage/migration.sql b/prisma/migrations/20260803_add_storage/migration.sql deleted file mode 100644 index d7df7a3..0000000 --- a/prisma/migrations/20260803_add_storage/migration.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Migration: add_storage - -ALTER TABLE "User" - ADD COLUMN IF NOT EXISTS "storageQuota" bigint NOT NULL DEFAULT 10737418240; - -ALTER TABLE "User" - ADD COLUMN IF NOT EXISTS "storageUsed" bigint NOT NULL DEFAULT 0; From 54c01e3c6cb33a75ffbd5c732bb4438d42624547 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 23:09:33 +0700 Subject: [PATCH 25/34] Update user.entity.ts --- src/modules/identity/domain/entities/user.entity.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/modules/identity/domain/entities/user.entity.ts b/src/modules/identity/domain/entities/user.entity.ts index 335462e..abb64e4 100644 --- a/src/modules/identity/domain/entities/user.entity.ts +++ b/src/modules/identity/domain/entities/user.entity.ts @@ -7,7 +7,6 @@ export class UserData { public authentikId: string | null, public username: string, public email: string, - public password: string | null, public roles: RoleData[], public permissions: PermissionData[], public isActive: boolean, @@ -22,7 +21,6 @@ export class UserData { static create(data: { username: string; email: string; - password?: string | null; authentikId?: string | null; storageQuota?: number; storageUsed?: number; @@ -34,7 +32,6 @@ export class UserData { data.authentikId || null, data.username, data.email, - data.password ?? null, [], [], true, @@ -119,7 +116,6 @@ export class UserData { authentikId?: string | null; username: string; email: string; - password: string | null; roles?: RoleData[]; permissions?: PermissionData[]; isActive?: boolean; @@ -134,7 +130,6 @@ export class UserData { props.authentikId || null, props.username, props.email, - props.password, props.roles || [], props.permissions || [], props.isActive !== undefined ? props.isActive : true, From 583dd9bc4b54b0793bf1745ac04db59cb048f70d Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 23:10:39 +0700 Subject: [PATCH 26/34] Revert "Update user.entity.ts" This reverts commit 54c01e3c6cb33a75ffbd5c732bb4438d42624547. --- src/modules/identity/domain/entities/user.entity.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/identity/domain/entities/user.entity.ts b/src/modules/identity/domain/entities/user.entity.ts index abb64e4..335462e 100644 --- a/src/modules/identity/domain/entities/user.entity.ts +++ b/src/modules/identity/domain/entities/user.entity.ts @@ -7,6 +7,7 @@ export class UserData { public authentikId: string | null, public username: string, public email: string, + public password: string | null, public roles: RoleData[], public permissions: PermissionData[], public isActive: boolean, @@ -21,6 +22,7 @@ export class UserData { static create(data: { username: string; email: string; + password?: string | null; authentikId?: string | null; storageQuota?: number; storageUsed?: number; @@ -32,6 +34,7 @@ export class UserData { data.authentikId || null, data.username, data.email, + data.password ?? null, [], [], true, @@ -116,6 +119,7 @@ export class UserData { authentikId?: string | null; username: string; email: string; + password: string | null; roles?: RoleData[]; permissions?: PermissionData[]; isActive?: boolean; @@ -130,6 +134,7 @@ export class UserData { props.authentikId || null, props.username, props.email, + props.password, props.roles || [], props.permissions || [], props.isActive !== undefined ? props.isActive : true, From a5db9d69b3341ecdb6a04b1ce11ce30e681f14b8 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 23:16:56 +0700 Subject: [PATCH 27/34] Fix password still exist --- .../identity/domain/entities/user.entity.ts | 16 ++++++---------- .../infrastructure/mappers/prisma-user.mapper.ts | 6 ++---- .../identity/presentation/dto/create-user.dto.ts | 5 +---- .../identity/presentation/dto/update-user.dto.ts | 5 +---- 4 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/modules/identity/domain/entities/user.entity.ts b/src/modules/identity/domain/entities/user.entity.ts index 335462e..e63b1f4 100644 --- a/src/modules/identity/domain/entities/user.entity.ts +++ b/src/modules/identity/domain/entities/user.entity.ts @@ -1,13 +1,13 @@ import { RoleData } from "./role.entity"; import { PermissionData } from "./permission.entity"; +import crypto from 'crypto'; export class UserData { private constructor( public readonly id: string, public authentikId: string | null, public username: string, - public email: string, - public password: string | null, + public email: string, public roles: RoleData[], public permissions: PermissionData[], public isActive: boolean, @@ -20,9 +20,8 @@ export class UserData { //#region Create static create(data: { - username: string; + username: string; email: string; - password?: string | null; authentikId?: string | null; storageQuota?: number; storageUsed?: number; @@ -34,8 +33,7 @@ export class UserData { data.authentikId || null, data.username, data.email, - data.password ?? null, - [], + [], [], true, null, @@ -118,8 +116,7 @@ export class UserData { id: string; authentikId?: string | null; username: string; - email: string; - password: string | null; + email: string; roles?: RoleData[]; permissions?: PermissionData[]; isActive?: boolean; @@ -133,8 +130,7 @@ export class UserData { props.id, props.authentikId || null, props.username, - props.email, - props.password, + props.email, props.roles || [], props.permissions || [], props.isActive !== undefined ? props.isActive : true, diff --git a/src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts b/src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts index 2a8dcad..6c3cbc0 100644 --- a/src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts +++ b/src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts @@ -41,8 +41,7 @@ export class PrismaUserMapper { id: model.id, authentikId: model.authentikUserId || model.authentikId || null, username: model.username || model.name || model.username, - email: model.email, - password: model.password ?? null, + email: model.email, roles, permissions, isActive: model.isActive ?? true, @@ -59,8 +58,7 @@ export class PrismaUserMapper { authentikUserId: (userData as any).authentikUserId || userData.authentikId, authentikId: userData.authentikId, username: userData.username, - email: userData.email, - password: userData.password ?? null, + email: userData.email, isActive: userData.isActive, deletedAt: userData.deletedAt, lastSeenAt: userData.lastSeenAt, diff --git a/src/modules/identity/presentation/dto/create-user.dto.ts b/src/modules/identity/presentation/dto/create-user.dto.ts index ee203a6..8996836 100644 --- a/src/modules/identity/presentation/dto/create-user.dto.ts +++ b/src/modules/identity/presentation/dto/create-user.dto.ts @@ -11,10 +11,7 @@ export class CreateUserDto { @IsEmail() email!: string; - @ApiProperty({ example: 'strongpassword' }) - @IsString() - @Length(8, 128) - password!: string; + @ApiPropertyOptional({ example: 10737418240 }) @IsOptional() diff --git a/src/modules/identity/presentation/dto/update-user.dto.ts b/src/modules/identity/presentation/dto/update-user.dto.ts index 968d85a..0fa3899 100644 --- a/src/modules/identity/presentation/dto/update-user.dto.ts +++ b/src/modules/identity/presentation/dto/update-user.dto.ts @@ -14,10 +14,7 @@ export class UpdateUserDto extends PartialType(CreateUserDto) { @IsEmail() email?: string; - @ApiPropertyOptional() - @IsOptional() - @Length(8, 128) - password?: string; + @ApiPropertyOptional() @IsOptional() From e0bf3882be0111c4e1feac4aa03e05535254aad9 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 23:33:40 +0700 Subject: [PATCH 28/34] make debugger --- src/core/auth/guards/jwt-auth.guard.ts | 32 +++++++++++++++++++------- src/main.ts | 14 ++++++++--- src/modules/auth/auth.controller.ts | 9 +++++++- src/modules/auth/auth.service.ts | 23 +++++++++++++++--- 4 files changed, 63 insertions(+), 15 deletions(-) diff --git a/src/core/auth/guards/jwt-auth.guard.ts b/src/core/auth/guards/jwt-auth.guard.ts index 7b657bb..0283ee1 100644 --- a/src/core/auth/guards/jwt-auth.guard.ts +++ b/src/core/auth/guards/jwt-auth.guard.ts @@ -3,6 +3,7 @@ import { CanActivate, ExecutionContext, UnauthorizedException, + Logger, } from '@nestjs/common'; import { Request } from 'express'; import { createRemoteJWKSet, jwtVerify } from 'jose'; @@ -16,27 +17,37 @@ import * as jwt from 'jsonwebtoken'; @Injectable() export class JwtAuthGuard implements CanActivate { private jwks: ReturnType | null = null; + private readonly logger = new Logger('JwtAuthGuard'); constructor() {} async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); - const authHeader = request.headers.authorization; + let token: string | undefined; - if (!authHeader) { - throw new UnauthorizedException('Authorization header is missing.'); + const authHeader = request.headers.authorization; + if (authHeader) { + const [type, t] = authHeader.split(' '); + if (type === 'Bearer' && t) token = t; } - const [type, token] = authHeader.split(' '); + // fallback to cookie if no Authorization header + if (!token) { + token = (request as any).cookies?.raylab_jwt; + this.logger.debug(`No Authorization header. Trying cookie. cookiePresent=${!!(request as any).cookies} tokenFromCookie=${!!token}`); + } else { + this.logger.debug('Authorization header found. Using Bearer token.'); + } - if (type !== 'Bearer' || !token) { - throw new UnauthorizedException('Invalid authorization header.'); + if (!token) { + this.logger.debug('No token found in Authorization header or cookie.'); + throw new UnauthorizedException('Authorization token is missing.'); } const jwksUri = process.env.AUTHENTIK_JWKS_URI; - // First try verifying with external JWKS (Authentik) + // First try verifying with external JWKS (Authentik) if (jwksUri) { try { if (!this.jwks) this.jwks = createRemoteJWKSet(new URL(jwksUri)); @@ -54,15 +65,18 @@ export class JwtAuthGuard implements CanActivate { ); request.identity = identity; + this.logger.debug(`Verified token using external JWKS. sub=${payload.sub}`); return true; } catch (err) { + this.logger.debug(`External JWKS verification failed: ${(err as Error).message}`); // ignore and try internal verification } } // Fallback: verify with internal symmetric secret - const secret = process.env.RAYLAB_JWT_SECRET; + const secret = process.env.RAYLAB_JWT_SECRET; if (!secret) { + this.logger.error('RAYLAB_JWT_SECRET is not configured.'); throw new UnauthorizedException('Invalid or expired token.'); } @@ -77,8 +91,10 @@ export class JwtAuthGuard implements CanActivate { ); request.identity = identity; + this.logger.debug(`Verified token using internal secret. sub=${payload.sub}`); return true; } catch (err: any) { + this.logger.debug(`Internal token verification failed: ${(err as Error).message}`); throw new UnauthorizedException('Invalid or expired token.'); } } diff --git a/src/main.ts b/src/main.ts index 71d5c0c..0255dd3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,5 @@ -import { ValidationPipe } from '@nestjs/common'; +import { ValidationPipe, Logger } from '@nestjs/common'; +import cookieParser from 'cookie-parser'; import { NestFactory } from '@nestjs/core'; import { ConfigService } from '@nestjs/config'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; @@ -9,6 +10,10 @@ async function bootstrap() { const app = await NestFactory.create(AppModule); const config = app.get(ConfigService); + const logger = new Logger('Bootstrap'); + + // enable cookie parser so req.cookies is populated + app.use(cookieParser()); app.setGlobalPrefix(''); @@ -55,11 +60,14 @@ async function bootstrap() { SwaggerModule.setup('ApiList', app, document); } - const port = config.get('PORT') || 3000; + const port = config.get('PORT') || 3000; + + logger.log(`Allowed CORS origins: ${JSON.stringify(allowedOrigins)}`); + logger.log(`Server listening on port ${port}`); await app.listen(port); - console.log(`Server running on http://localhost:${port}`); + logger.log(`Server running on http://localhost:${port}`); } bootstrap(); \ No newline at end of file diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 79df924..8efcb12 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -1,5 +1,5 @@ -import { Controller, Get, Post, Query, Res, Req, Body, HttpCode, HttpStatus } from '@nestjs/common'; +import { Controller, Get, Post, Query, Res, Req, Body, HttpCode, HttpStatus, Logger } from '@nestjs/common'; import { ApiTags, ApiOperation } from '@nestjs/swagger'; import { AuthService } from './auth.service'; import { Response, Request } from 'express'; @@ -8,6 +8,7 @@ import { ConfigService } from '@nestjs/config'; @ApiTags('Auth') @Controller('auth') export class AuthController { + private readonly logger = new Logger('AuthController'); constructor(private readonly authService: AuthService, private readonly config: ConfigService) {} @Get('login') @@ -35,11 +36,13 @@ export class AuthController { if (cookieDomain) cookieOptions.domain = cookieDomain; // access token cookie (internal JWT) + this.logger.log(`Setting access & refresh cookies. cookieOptions=${JSON.stringify(cookieOptions)}`); res.cookie('raylab_jwt', result.accessToken, { ...cookieOptions, maxAge: result.expiresIn * 1000 }); // refresh token cookie res.cookie('raylab_refresh', result.refreshToken, { ...cookieOptions, maxAge: result.refreshTtl * 1000 }); + this.logger.debug(`Callback complete. returnTo=${result.returnTo} user=${JSON.stringify(result.user)}`); return res.redirect(302, result.returnTo || '/'); } @@ -56,7 +59,9 @@ export class AuthController { @ApiOperation({ summary: 'Refresh internal JWT using internal refresh token' }) async refresh(@Req() req: Request) { const refreshToken = req.cookies?.raylab_refresh || req.body?.refreshToken; + this.logger.debug(`Refresh called. cookies present=${!!req.cookies} refreshTokenProvided=${!!refreshToken}`); const result = await this.authService.refresh(refreshToken); + this.logger.debug(`Refresh result for user. expiresIn=${result.expiresIn}`); return { success: true, data: result }; } @@ -64,7 +69,9 @@ export class AuthController { @ApiOperation({ summary: 'Get current user from internal JWT (cookie or Authorization header)' }) async me(@Req() req: Request) { const token = (req.cookies?.raylab_jwt) || (req.headers.authorization && (req.headers.authorization as string).replace(/^Bearer\s+/i, '')); + this.logger.debug(`Me called. cookies present=${!!req.cookies} tokenProvided=${!!token}`); const user = await this.authService.me(token); + this.logger.debug(`Me returning user id=${user?.id}`); return { success: true, data: user }; } } diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 717d289..5f83179 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -140,11 +140,14 @@ export class AuthService { const expiresIn = Number(this.config.get('RAYLAB_JWT_EXPIRES_IN') || 3600); const access = this.jwtService.sign(jwtPayload, { expiresIn }); + logger.debug(`Created internal access token for user=${domainUser.id} expiresIn=${expiresIn}`); + // create internal refresh token const refreshToken = crypto.randomUUID(); const refreshTtl = Number(this.config.get('RAYLAB_REFRESH_EXPIRES_IN') || 30 * 24 * 3600); // default 30 days await this.refreshStore.set(refreshToken, { userId: domainUser.id }, refreshTtl); + logger.debug(`Stored refresh token for user=${domainUser.id} ttl=${refreshTtl}`); return { accessToken: access, @@ -162,9 +165,16 @@ export class AuthService { } async refresh(refreshToken?: string) { - if (!refreshToken) throw new UnauthorizedException('Missing refresh token'); + if (!refreshToken) { + logger.debug('Refresh called without refresh token'); + throw new UnauthorizedException('Missing refresh token'); + } const data = await this.refreshStore.get(refreshToken); - if (!data) throw new UnauthorizedException('Invalid refresh token'); + if (!data) { + logger.debug(`Refresh token not found or expired: ${refreshToken}`); + throw new UnauthorizedException('Invalid refresh token'); + } + logger.debug(`Refresh token validated for userId=${data.userId}`); const userId = data.userId; // load user @@ -177,9 +187,11 @@ export class AuthService { const newRefresh = crypto.randomUUID(); const refreshTtl = Number(this.config.get('RAYLAB_REFRESH_EXPIRES_IN') || 30 * 24 * 3600); await this.refreshStore.set(newRefresh, { userId }, refreshTtl); + logger.debug(`Rotated refresh token for userId=${userId} newRefresh=${newRefresh} ttl=${refreshTtl}`); const expiresIn = Number(this.config.get('RAYLAB_JWT_EXPIRES_IN') || 3600); const access = this.jwtService.sign({ sub: domainUser.id, preferred_username: domainUser.username, email: domainUser.email }, { expiresIn }); + logger.debug(`Issued new access token for user=${userId} expiresIn=${expiresIn}`); return { accessToken: access, refreshToken: newRefresh, expiresIn, refreshTtl }; } @@ -204,13 +216,18 @@ export class AuthService { } async me(token?: string) { - if (!token) throw new UnauthorizedException('Missing token'); + if (!token) { + logger.debug('Me called without token'); + throw new UnauthorizedException('Missing token'); + } try { const payload: any = this.jwtService.verify(token); + logger.debug(`Token verified successfully. payload.sub=${payload.sub}`); const user = await this.userRepository.getById(payload.sub); if (!user) throw new UnauthorizedException('User not found'); return { id: user.id, username: user.username, email: user.email, roles: user.roles || [] }; } catch (e) { + logger.debug(`Token verification failed: ${(e as Error).message}`); throw new UnauthorizedException('Invalid token'); } } From 6ffff9ef7bed55117a4f7e6440aa674c46ec943a Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 23:46:19 +0700 Subject: [PATCH 29/34] add logger --- package-lock.json | 31 +++++++++++ package.json | 4 +- src/core/auth/guards/jwt.guard.ts | 22 ++++++++ src/core/auth/guards/refresh.guard.ts | 22 ++++++++ src/core/auth/strategies/jwt.strategy.ts | 29 ++++++++++ src/core/auth/strategies/refresh.strategy.ts | 58 ++++++++++++++++++++ src/modules/auth/auth.controller.ts | 42 +++++++++++--- src/modules/auth/auth.module.ts | 12 ++++ 8 files changed, 210 insertions(+), 10 deletions(-) create mode 100644 src/core/auth/guards/jwt.guard.ts create mode 100644 src/core/auth/guards/refresh.guard.ts create mode 100644 src/core/auth/strategies/jwt.strategy.ts create mode 100644 src/core/auth/strategies/refresh.strategy.ts diff --git a/package-lock.json b/package-lock.json index 8d2fbe3..8a7bebd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "bullmq": "^1.73.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.2", + "cookie-parser": "^1.4.7", "dotenv": "^16.6.1", "ioredis": "^5.3.2", "jose": "^6.2.6", @@ -43,6 +44,7 @@ "@nestjs/testing": "^10.4.22", "@playwright/test": "^1.62.1", "@types/bcrypt": "^5.0.2", + "@types/cookie-parser": "^1.4.10", "@types/jest": "^29.5.14", "@types/node": "^22.20.1", "@types/passport-jwt": "^4.0.1", @@ -2260,6 +2262,16 @@ "@types/node": "*" } }, + "node_modules/@types/cookie-parser": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.10.tgz", + "integrity": "sha512-B4xqkqfZ8Wek+rCOeRxsjMS9OgvzebEzzLYw7NHYuvzb7IdxOkI0ZHGgeEBX4PUM7QGVvNSK60T3OvWj3YfBRg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/express": "*" + } + }, "node_modules/@types/cookiejar": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", @@ -3667,6 +3679,25 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, "node_modules/cookie-signature": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", diff --git a/package.json b/package.json index 63a581e..9558769 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "bullmq": "^1.73.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.2", + "cookie-parser": "^1.4.7", "dotenv": "^16.6.1", "ioredis": "^5.3.2", "jose": "^6.2.6", @@ -59,6 +60,7 @@ "@nestjs/testing": "^10.4.22", "@playwright/test": "^1.62.1", "@types/bcrypt": "^5.0.2", + "@types/cookie-parser": "^1.4.10", "@types/jest": "^29.5.14", "@types/node": "^22.20.1", "@types/passport-jwt": "^4.0.1", @@ -78,6 +80,6 @@ "typescript": "^5.9.3" }, "prisma": { - "seed": "node --loader ts-node/esm prisma/seed.ts" + "seed": "node --loader ts-node/esm prisma/seed.ts" } } diff --git a/src/core/auth/guards/jwt.guard.ts b/src/core/auth/guards/jwt.guard.ts new file mode 100644 index 0000000..0b7a1b5 --- /dev/null +++ b/src/core/auth/guards/jwt.guard.ts @@ -0,0 +1,22 @@ +import { Injectable, Logger, ExecutionContext } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +@Injectable() +export class JwtGuard extends AuthGuard('jwt') { + private readonly logger = new Logger('JwtGuard'); + + canActivate(context: ExecutionContext) { + const req = context.switchToHttp().getRequest(); + this.logger.debug(`JwtGuard invoked. cookies=${JSON.stringify(req.cookies)} authHeader=${req.headers?.authorization}`); + return super.canActivate(context); + } + + handleRequest(err: any, user: any, info: any) { + if (err || !user) { + this.logger.debug(`JwtGuard handleRequest failed. err=${err} user=${!!user} info=${JSON.stringify(info)}`); + } else { + this.logger.debug(`JwtGuard handleRequest success user=${JSON.stringify(user)}`); + } + return super.handleRequest(err, user, info); + } +} diff --git a/src/core/auth/guards/refresh.guard.ts b/src/core/auth/guards/refresh.guard.ts new file mode 100644 index 0000000..817a0c9 --- /dev/null +++ b/src/core/auth/guards/refresh.guard.ts @@ -0,0 +1,22 @@ +import { Injectable, Logger, ExecutionContext } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +@Injectable() +export class RefreshGuard extends AuthGuard('refresh') { + private readonly logger = new Logger('RefreshGuard'); + + canActivate(context: ExecutionContext) { + const req = context.switchToHttp().getRequest(); + this.logger.debug(`RefreshGuard invoked. cookies=${JSON.stringify(req.cookies)} body=${JSON.stringify(req.body)}`); + return super.canActivate(context); + } + + handleRequest(err: any, user: any, info: any) { + if (err || !user) { + this.logger.debug(`RefreshGuard handleRequest failed. err=${err} user=${!!user} info=${JSON.stringify(info)}`); + } else { + this.logger.debug(`RefreshGuard handleRequest success userId=${user.id} authInfo=${JSON.stringify(info)}`); + } + return super.handleRequest(err, user, info); + } +} diff --git a/src/core/auth/strategies/jwt.strategy.ts b/src/core/auth/strategies/jwt.strategy.ts new file mode 100644 index 0000000..8ed4df9 --- /dev/null +++ b/src/core/auth/strategies/jwt.strategy.ts @@ -0,0 +1,29 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { Strategy as JwtStrategyBase, ExtractJwt } from 'passport-jwt'; +import { ConfigService } from '@nestjs/config'; + +function cookieExtractor(req: any): string | null { + if (!req) return null; + if (req.cookies && req.cookies.raylab_jwt) return req.cookies.raylab_jwt as string; + return null; +} + +@Injectable() +export class JwtStrategy extends PassportStrategy(JwtStrategyBase, 'jwt') { + private readonly logger = new Logger('JwtStrategy'); + + constructor(private config: ConfigService) { + super({ + jwtFromRequest: ExtractJwt.fromExtractors([cookieExtractor, ExtractJwt.fromAuthHeaderAsBearerToken()]), + secretOrKey: config.get('RAYLAB_JWT_SECRET') || process.env.RAYLAB_JWT_SECRET || 'raylab-secret', + algorithms: ['HS256'], + }); + this.logger.debug('JwtStrategy initialized'); + } + + async validate(payload: any) { + this.logger.debug(`JwtStrategy.validate payload=${JSON.stringify(payload)}`); + return payload; // attached to req.user + } +} diff --git a/src/core/auth/strategies/refresh.strategy.ts b/src/core/auth/strategies/refresh.strategy.ts new file mode 100644 index 0000000..0d51b30 --- /dev/null +++ b/src/core/auth/strategies/refresh.strategy.ts @@ -0,0 +1,58 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { Strategy } from 'passport-strategy'; +import { InMemoryRefreshStore } from '../../../modules/auth/refresh/inmemory-refresh.store'; +import { IUser } from '../../../modules/identity/domain/repositories/user.interface'; +import { Inject } from '@nestjs/common'; + +// Minimal Passport Strategy for refresh tokens (non-JWT random tokens) +class RefreshTokenStrategy extends Strategy { + name = 'refresh'; + authenticate(req: any) { + // This will be overridden in PassportStrategy wrapper + this.error(new Error('Not implemented')); + } +} + +@Injectable() +export class RefreshStrategy extends PassportStrategy(RefreshTokenStrategy, 'refresh') { + private readonly logger = new Logger('RefreshStrategy'); + + constructor(private refreshStore: InMemoryRefreshStore, @Inject(IUser) private userRepository: IUser) { + super(); + this.logger.debug('RefreshStrategy initialized'); + } + + async authenticate(req: any, options?: any) { + const cookies = req.cookies || null; + const token = (cookies && cookies.raylab_refresh) || (req.body && req.body.refreshToken); + this.logger.debug(`RefreshStrategy.authenticate cookies=${JSON.stringify(cookies)} tokenExtracted=${!!token}`); + + if (!token) { + this.logger.debug('RefreshStrategy: no refresh token provided'); + return this.fail('Missing refresh token', 401); + } + + try { + const data = await this.refreshStore.get(token); + if (!data) { + this.logger.debug('RefreshStrategy: refresh token not found/expired'); + return this.fail('Invalid refresh token', 401); + } + + const user = await this.userRepository.getById(data.userId); + if (!user) { + this.logger.debug('RefreshStrategy: user not found for refresh token'); + return this.fail('User not found', 401); + } + + // success - attach user + token info + const info = { refreshToken: token, userId: data.userId }; + this.logger.debug(`RefreshStrategy: validated refresh token for userId=${data.userId}`); + return this.success(user, info); + } catch (e) { + this.logger.debug(`RefreshStrategy error: ${(e as Error).message}`); + return this.error(e as Error); + } + } +} diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 8efcb12..cfbc36b 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -1,8 +1,10 @@ -import { Controller, Get, Post, Query, Res, Req, Body, HttpCode, HttpStatus, Logger } from '@nestjs/common'; +import { Controller, Get, Post, Query, Res, Req, Body, HttpCode, HttpStatus, Logger, UseGuards } from '@nestjs/common'; import { ApiTags, ApiOperation } from '@nestjs/swagger'; import { AuthService } from './auth.service'; import { Response, Request } from 'express'; +import { JwtGuard } from '../../../core/auth/guards/jwt.guard'; +import { RefreshGuard } from '../../../core/auth/guards/refresh.guard'; import { ConfigService } from '@nestjs/config'; @ApiTags('Auth') @@ -56,21 +58,43 @@ export class AuthController { } @Post('refresh') + @UseGuards(RefreshGuard) @ApiOperation({ summary: 'Refresh internal JWT using internal refresh token' }) - async refresh(@Req() req: Request) { - const refreshToken = req.cookies?.raylab_refresh || req.body?.refreshToken; - this.logger.debug(`Refresh called. cookies present=${!!req.cookies} refreshTokenProvided=${!!refreshToken}`); + async refresh(@Req() req: Request, @Res() res: Response) { + // req.authInfo should contain { refreshToken } + const refreshToken = (req as any).authInfo?.refreshToken || (req.cookies?.raylab_refresh) || (req.body?.refreshToken); + this.logger.debug(`Refresh called (guarded). cookies=${JSON.stringify((req as any).cookies)} authInfo=${JSON.stringify((req as any).authInfo)}`); + const result = await this.authService.refresh(refreshToken); - this.logger.debug(`Refresh result for user. expiresIn=${result.expiresIn}`); - return { success: true, data: result }; + + // set cookies with environment-aware options + const isProd = this.config.get('NODE_ENV') === 'production' || process.env.NODE_ENV === 'production'; + const cookieDomain = this.config.get('RAYLAB_COOKIE_DOMAIN') || (isProd ? '.raylab.site' : undefined); + + const cookieOptions: any = { + httpOnly: true, + secure: isProd, + sameSite: isProd ? 'none' : 'lax', + path: '/', + }; + if (cookieDomain) cookieOptions.domain = cookieDomain; + + // set new cookies (rotate) + res.cookie('raylab_jwt', result.accessToken, { ...cookieOptions, maxAge: result.expiresIn * 1000 }); + res.cookie('raylab_refresh', result.refreshToken, { ...cookieOptions, maxAge: result.refreshTtl * 1000 }); + + this.logger.debug(`Refresh complete. set new cookies for user`); + return res.json({ success: true, data: result }); } @Get('me') + @UseGuards(JwtGuard) @ApiOperation({ summary: 'Get current user from internal JWT (cookie or Authorization header)' }) async me(@Req() req: Request) { - const token = (req.cookies?.raylab_jwt) || (req.headers.authorization && (req.headers.authorization as string).replace(/^Bearer\s+/i, '')); - this.logger.debug(`Me called. cookies present=${!!req.cookies} tokenProvided=${!!token}`); - const user = await this.authService.me(token); + // With JwtGuard, req.user should be payload from token + this.logger.debug(`Me called (guarded). cookies=${JSON.stringify((req as any).cookies)} user=${JSON.stringify((req as any).user)}`); + const payload = (req as any).user; + const user = await this.authService.me((req.cookies?.raylab_jwt) || (req.headers.authorization && (req.headers.authorization as string).replace(/^Bearer\s+/i, ''))); this.logger.debug(`Me returning user id=${user?.id}`); return { success: true, data: user }; } diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts index 0e02aff..5f16b6f 100644 --- a/src/modules/auth/auth.module.ts +++ b/src/modules/auth/auth.module.ts @@ -1,4 +1,9 @@ import { Module } from '@nestjs/common'; +import { PassportModule } from '@nestjs/passport'; +import { JwtStrategy } from '../../core/auth/strategies/jwt.strategy'; +import { RefreshStrategy } from '../../core/auth/strategies/refresh.strategy'; +import { JwtGuard } from '../../core/auth/guards/jwt.guard'; +import { RefreshGuard } from '../../core/auth/guards/refresh.guard'; import { AuthController } from './auth.controller'; import { AuthService } from './auth.service'; import { ConfigModule, ConfigService } from '@nestjs/config'; @@ -27,6 +32,7 @@ import { AuthorizationModule } from '../authorization/authorization.module'; imports: [ ConfigModule, AuthorizationModule, + PassportModule.register({ defaultStrategy: 'jwt', session: false }), JwtModule.registerAsync({ imports: [ConfigModule], useFactory: async (config: ConfigService) => ({ @@ -59,6 +65,12 @@ import { AuthorizationModule } from '../authorization/authorization.module'; EventBus, AuditService, RedisService, + + // Strategies & Guards + JwtStrategy, + RefreshStrategy, + JwtGuard, + RefreshGuard, ], exports: [AuthService, OidcService, RoleSyncService, AuthenticationService, EventBus], }) From 6ee7a7cda453d4715491d6a8179e0478531fcc32 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Sun, 9 Aug 2026 18:23:58 +0700 Subject: [PATCH 30/34] update --- .gitignore | 2 +- dist/app.module.js | 37 --- dist/app.module.js.map | 1 - dist/common/constants/permission.constants.js | 18 -- .../constants/permission.constants.js.map | 1 - dist/core/auth/guards/jwt-auth.guard.js | 106 --------- dist/core/auth/guards/jwt-auth.guard.js.map | 1 - .../authenticated-request.interface.js | 3 - .../authenticated-request.interface.js.map | 1 - dist/core/auth/interfaces/identity-data.js | 17 -- .../core/auth/interfaces/identity-data.js.map | 1 - dist/core/config/database.config.js | 13 -- dist/core/config/database.config.js.map | 1 - dist/core/event-bus/event-bus.service.js | 44 ---- dist/core/event-bus/event-bus.service.js.map | 1 - dist/core/event-bus/event.interface.js | 3 - dist/core/event-bus/event.interface.js.map | 1 - dist/core/events/event-dispatcher.js | 14 -- dist/core/events/event-dispatcher.js.map | 1 - dist/core/events/event-handler.js | 3 - dist/core/events/event-handler.js.map | 1 - dist/core/events/event-publisher.js | 14 -- dist/core/events/event-publisher.js.map | 1 - dist/core/events/event.interface.js | 3 - dist/core/events/event.interface.js.map | 1 - dist/core/exceptions/http-exception.filter.js | 29 --- .../exceptions/http-exception.filter.js.map | 1 - dist/core/logger/logger.service.js | 11 - dist/core/logger/logger.service.js.map | 1 - dist/core/middleware/request-id.middleware.js | 23 -- .../middleware/request-id.middleware.js.map | 1 - dist/main.js | 34 --- dist/main.js.map | 1 - .../modules/application/application.module.js | 49 ---- .../application/application.module.js.map | 1 - .../handlers/create-application.handler.js | 44 ---- .../create-application.handler.js.map | 1 - .../handlers/delete-application.handler.js | 31 --- .../delete-application.handler.js.map | 1 - .../handlers/get-application.handler.js | 30 --- .../handlers/get-application.handler.js.map | 1 - .../handlers/get-applications.handler.js | 30 --- .../handlers/get-applications.handler.js.map | 1 - .../handlers/get-me-applications.handler.js | 35 --- .../get-me-applications.handler.js.map | 1 - .../handlers/update-application.handler.js | 38 --- .../update-application.handler.js.map | 1 - .../validators/application.validator.js | 84 ------- .../validators/application.validator.js.map | 1 - .../domain/entities/application.entity.js | 67 ------ .../domain/entities/application.entity.js.map | 1 - .../repositories/application.interface.js | 7 - .../repositories/application.interface.js.map | 1 - .../mappers/prisma-application.mapper.js | 23 -- .../mappers/prisma-application.mapper.js.map | 1 - .../prisma-application.repository.js | 98 -------- .../prisma-application.repository.js.map | 1 - .../controllers/applications.controller.js | 136 ----------- .../applications.controller.js.map | 1 - dist/modules/audit/audit.event-handler.js | 65 ------ dist/modules/audit/audit.event-handler.js.map | 1 - dist/modules/audit/audit.module.js | 24 -- dist/modules/audit/audit.module.js.map | 1 - dist/modules/audit/audit.service.js | 46 ---- dist/modules/audit/audit.service.js.map | 1 - dist/modules/auth/auth.controller.js | 110 --------- dist/modules/auth/auth.controller.js.map | 1 - dist/modules/auth/auth.module.js | 75 ------ dist/modules/auth/auth.module.js.map | 1 - dist/modules/auth/auth.service.js | 209 ----------------- dist/modules/auth/auth.service.js.map | 1 - dist/modules/auth/authentication.service.js | 70 ------ .../auth/authentication.service.js.map | 1 - dist/modules/auth/dto/login.dto.js | 4 - dist/modules/auth/dto/login.dto.js.map | 1 - dist/modules/auth/group-hash.service.js | 56 ----- dist/modules/auth/group-hash.service.js.map | 1 - dist/modules/auth/guards/oidc.guard.js | 48 ---- dist/modules/auth/guards/oidc.guard.js.map | 1 - dist/modules/auth/oidc.service.js | 89 -------- dist/modules/auth/oidc.service.js.map | 1 - dist/modules/auth/pkce/redis-pkce.store.js | 56 ----- .../modules/auth/pkce/redis-pkce.store.js.map | 1 - .../auth/refresh/inmemory-refresh.store.js | 53 ----- .../refresh/inmemory-refresh.store.js.map | 1 - dist/modules/auth/role-sync.service.js | 88 ------- dist/modules/auth/role-sync.service.js.map | 1 - .../application/authorization.service.js | 2 - .../application/authorization.service.js.map | 1 - .../authorization/authorization.module.js | 29 --- .../authorization/authorization.module.js.map | 1 - .../authorization/authorization.service.js | 71 ------ .../authorization.service.js.map | 1 - .../cache/permission-cache.interface.js | 3 - .../cache/permission-cache.interface.js.map | 1 - .../cache/redis-permission-cache.service.js | 48 ---- .../redis-permission-cache.service.js.map | 1 - .../authorization/guards/permission.guard.js | 52 ----- .../guards/permission.guard.js.map | 1 - .../decorators/permission.decorator.js | 7 - .../decorators/permission.decorator.js.map | 1 - .../presentation/guards/permission.guard.js | 46 ---- .../guards/permission.guard.js.map | 1 - dist/modules/health/health.controller.js | 35 --- dist/modules/health/health.controller.js.map | 1 - dist/modules/health/health.module.js | 23 -- dist/modules/health/health.module.js.map | 1 - dist/modules/health/health.service.js | 24 -- dist/modules/health/health.service.js.map | 1 - .../application/config/env-auth-config.js | 16 -- .../application/config/env-auth-config.js.map | 1 - .../application/config/i-auth-config.js | 7 - .../application/config/i-auth-config.js.map | 1 - .../permission/create-permission.handler.js | 47 ---- .../create-permission.handler.js.map | 1 - .../permission/delete-permission.handler.js | 54 ----- .../delete-permission.handler.js.map | 1 - .../permission/get-permission.handler.js | 29 --- .../permission/get-permission.handler.js.map | 1 - .../permission/get-permissions.handler.js | 29 --- .../permission/get-permissions.handler.js.map | 1 - .../permission/update-permission.handler.js | 58 ----- .../update-permission.handler.js.map | 1 - .../role/assign-permission.handler.js | 56 ----- .../role/assign-permission.handler.js.map | 1 - .../handlers/role/create-role.handler.js | 54 ----- .../handlers/role/create-role.handler.js.map | 1 - .../handlers/role/delete-role.handler.js | 45 ---- .../handlers/role/delete-role.handler.js.map | 1 - .../handlers/role/get-role.handler.js | 29 --- .../handlers/role/get-role.handler.js.map | 1 - .../handlers/role/get-roles.handler.js | 29 --- .../handlers/role/get-roles.handler.js.map | 1 - .../role/remove-permission.handler.js | 46 ---- .../role/remove-permission.handler.js.map | 1 - .../handlers/role/update-role.handler.js | 53 ----- .../handlers/role/update-role.handler.js.map | 1 - .../user/assign-permission.handler.js | 42 ---- .../user/assign-permission.handler.js.map | 1 - .../handlers/user/assign-role.handler.js | 40 ---- .../handlers/user/assign-role.handler.js.map | 1 - .../handlers/user/create-user.handler.js | 39 ---- .../handlers/user/create-user.handler.js.map | 1 - .../handlers/user/delete-user.handler.js | 32 --- .../handlers/user/delete-user.handler.js.map | 1 - .../handlers/user/disable-user.handler.js | 32 --- .../handlers/user/disable-user.handler.js.map | 1 - .../handlers/user/enable-user.handler.js | 32 --- .../handlers/user/enable-user.handler.js.map | 1 - .../handlers/user/get-current-user.handler.js | 21 -- .../user/get-current-user.handler.js.map | 1 - .../handlers/user/get-user.handler.js | 29 --- .../handlers/user/get-user.handler.js.map | 1 - .../handlers/user/get-users.handler.js | 37 --- .../handlers/user/get-users.handler.js.map | 1 - .../user/remove-permission.handler.js | 33 --- .../user/remove-permission.handler.js.map | 1 - .../handlers/user/remove-role.handler.js | 33 --- .../handlers/user/remove-role.handler.js.map | 1 - .../handlers/user/restore-user.handler.js | 32 --- .../handlers/user/restore-user.handler.js.map | 1 - .../handlers/user/sync-identity.handler.js | 92 -------- .../user/sync-identity.handler.js.map | 1 - .../handlers/user/update-user.handler.js | 45 ---- .../handlers/user/update-user.handler.js.map | 1 - .../application/services/user.service.js | 37 --- .../application/services/user.service.js.map | 1 - .../domain/entities/permission.entity.js | 33 --- .../domain/entities/permission.entity.js.map | 1 - .../identity/domain/entities/role.entity.js | 51 ----- .../domain/entities/role.entity.js.map | 1 - .../identity/domain/entities/user.entity.js | 143 ------------ .../domain/entities/user.entity.js.map | 1 - .../domain/events/user-created.event.js | 13 -- .../domain/events/user-created.event.js.map | 1 - .../domain/events/user-disabled.event.js | 13 -- .../domain/events/user-disabled.event.js.map | 1 - .../domain/events/user-email-changed.event.js | 13 -- .../events/user-email-changed.event.js.map | 1 - .../domain/events/user-enabled.event.js | 13 -- .../domain/events/user-enabled.event.js.map | 1 - .../events/user-username-changed.event.js | 13 -- .../events/user-username-changed.event.js.map | 1 - .../repositories/permission.interface.js | 7 - .../repositories/permission.interface.js.map | 1 - .../domain/repositories/role.interface.js | 7 - .../domain/repositories/role.interface.js.map | 1 - .../domain/repositories/user.interface.js | 7 - .../domain/repositories/user.interface.js.map | 1 - dist/modules/identity/identity.module.js | 114 --------- dist/modules/identity/identity.module.js.map | 1 - .../mappers/prisma-permission.mapper.js | 26 --- .../mappers/prisma-permission.mapper.js.map | 1 - .../mappers/prisma-role.mapper.js | 31 --- .../mappers/prisma-role.mapper.js.map | 1 - .../mappers/prisma-user.mapper.js | 69 ------ .../mappers/prisma-user.mapper.js.map | 1 - .../prisma-permission.repository.js | 67 ------ .../prisma-permission.repository.js.map | 1 - .../repositories/prisma-role.repository.js | 110 --------- .../prisma-role.repository.js.map | 1 - .../repositories/prisma-user.repository.js | 216 ------------------ .../prisma-user.repository.js.map | 1 - .../controllers/permissions.controller.js | 119 ---------- .../controllers/permissions.controller.js.map | 1 - .../controllers/roles.controller.js | 156 ------------- .../controllers/roles.controller.js.map | 1 - .../controllers/users.controller.js | 173 -------------- .../controllers/users.controller.js.map | 1 - .../presentation/dto/create-permission.dto.js | 39 ---- .../dto/create-permission.dto.js.map | 1 - .../presentation/dto/create-role.dto.js | 45 ---- .../presentation/dto/create-role.dto.js.map | 1 - .../presentation/dto/create-user.dto.js | 54 ----- .../presentation/dto/create-user.dto.js.map | 1 - .../presentation/dto/update-permission.dto.js | 41 ---- .../dto/update-permission.dto.js.map | 1 - .../presentation/dto/update-role.dto.js | 40 ---- .../presentation/dto/update-role.dto.js.map | 1 - .../presentation/dto/update-user.dto.js | 63 ----- .../presentation/dto/update-user.dto.js.map | 1 - .../presentation/guards/current-user.guard.js | 44 ---- .../guards/current-user.guard.js.map | 1 - dist/shared/cache-keys.js | 7 - dist/shared/cache-keys.js.map | 1 - dist/shared/prisma.service.js | 24 -- dist/shared/prisma.service.js.map | 1 - dist/shared/redis.service.js | 121 ---------- dist/shared/redis.service.js.map | 1 - dist/shared/types/request-context.js | 3 - dist/shared/types/request-context.js.map | 1 - dist/shared/utils/index.js | 5 - dist/shared/utils/index.js.map | 1 - src/core/auth/guards/jwt-auth.guard.ts | 8 +- .../authenticated-request.interface.ts | 5 + .../presentation/guards/current-user.guard.ts | 20 +- tests/unit/current-user.guard.spec.ts | 65 ++++++ tests/unit/jwt-auth.guard.spec.ts | 69 ++++++ 238 files changed, 164 insertions(+), 5506 deletions(-) delete mode 100644 dist/app.module.js delete mode 100644 dist/app.module.js.map delete mode 100644 dist/common/constants/permission.constants.js delete mode 100644 dist/common/constants/permission.constants.js.map delete mode 100644 dist/core/auth/guards/jwt-auth.guard.js delete mode 100644 dist/core/auth/guards/jwt-auth.guard.js.map delete mode 100644 dist/core/auth/interfaces/authenticated-request.interface.js delete mode 100644 dist/core/auth/interfaces/authenticated-request.interface.js.map delete mode 100644 dist/core/auth/interfaces/identity-data.js delete mode 100644 dist/core/auth/interfaces/identity-data.js.map delete mode 100644 dist/core/config/database.config.js delete mode 100644 dist/core/config/database.config.js.map delete mode 100644 dist/core/event-bus/event-bus.service.js delete mode 100644 dist/core/event-bus/event-bus.service.js.map delete mode 100644 dist/core/event-bus/event.interface.js delete mode 100644 dist/core/event-bus/event.interface.js.map delete mode 100644 dist/core/events/event-dispatcher.js delete mode 100644 dist/core/events/event-dispatcher.js.map delete mode 100644 dist/core/events/event-handler.js delete mode 100644 dist/core/events/event-handler.js.map delete mode 100644 dist/core/events/event-publisher.js delete mode 100644 dist/core/events/event-publisher.js.map delete mode 100644 dist/core/events/event.interface.js delete mode 100644 dist/core/events/event.interface.js.map delete mode 100644 dist/core/exceptions/http-exception.filter.js delete mode 100644 dist/core/exceptions/http-exception.filter.js.map delete mode 100644 dist/core/logger/logger.service.js delete mode 100644 dist/core/logger/logger.service.js.map delete mode 100644 dist/core/middleware/request-id.middleware.js delete mode 100644 dist/core/middleware/request-id.middleware.js.map delete mode 100644 dist/main.js delete mode 100644 dist/main.js.map delete mode 100644 dist/modules/application/application.module.js delete mode 100644 dist/modules/application/application.module.js.map delete mode 100644 dist/modules/application/application/handlers/create-application.handler.js delete mode 100644 dist/modules/application/application/handlers/create-application.handler.js.map delete mode 100644 dist/modules/application/application/handlers/delete-application.handler.js delete mode 100644 dist/modules/application/application/handlers/delete-application.handler.js.map delete mode 100644 dist/modules/application/application/handlers/get-application.handler.js delete mode 100644 dist/modules/application/application/handlers/get-application.handler.js.map delete mode 100644 dist/modules/application/application/handlers/get-applications.handler.js delete mode 100644 dist/modules/application/application/handlers/get-applications.handler.js.map delete mode 100644 dist/modules/application/application/handlers/get-me-applications.handler.js delete mode 100644 dist/modules/application/application/handlers/get-me-applications.handler.js.map delete mode 100644 dist/modules/application/application/handlers/update-application.handler.js delete mode 100644 dist/modules/application/application/handlers/update-application.handler.js.map delete mode 100644 dist/modules/application/application/validators/application.validator.js delete mode 100644 dist/modules/application/application/validators/application.validator.js.map delete mode 100644 dist/modules/application/domain/entities/application.entity.js delete mode 100644 dist/modules/application/domain/entities/application.entity.js.map delete mode 100644 dist/modules/application/domain/repositories/application.interface.js delete mode 100644 dist/modules/application/domain/repositories/application.interface.js.map delete mode 100644 dist/modules/application/infrastructure/mappers/prisma-application.mapper.js delete mode 100644 dist/modules/application/infrastructure/mappers/prisma-application.mapper.js.map delete mode 100644 dist/modules/application/infrastructure/repositories/prisma-application.repository.js delete mode 100644 dist/modules/application/infrastructure/repositories/prisma-application.repository.js.map delete mode 100644 dist/modules/application/presentation/controllers/applications.controller.js delete mode 100644 dist/modules/application/presentation/controllers/applications.controller.js.map delete mode 100644 dist/modules/audit/audit.event-handler.js delete mode 100644 dist/modules/audit/audit.event-handler.js.map delete mode 100644 dist/modules/audit/audit.module.js delete mode 100644 dist/modules/audit/audit.module.js.map delete mode 100644 dist/modules/audit/audit.service.js delete mode 100644 dist/modules/audit/audit.service.js.map delete mode 100644 dist/modules/auth/auth.controller.js delete mode 100644 dist/modules/auth/auth.controller.js.map delete mode 100644 dist/modules/auth/auth.module.js delete mode 100644 dist/modules/auth/auth.module.js.map delete mode 100644 dist/modules/auth/auth.service.js delete mode 100644 dist/modules/auth/auth.service.js.map delete mode 100644 dist/modules/auth/authentication.service.js delete mode 100644 dist/modules/auth/authentication.service.js.map delete mode 100644 dist/modules/auth/dto/login.dto.js delete mode 100644 dist/modules/auth/dto/login.dto.js.map delete mode 100644 dist/modules/auth/group-hash.service.js delete mode 100644 dist/modules/auth/group-hash.service.js.map delete mode 100644 dist/modules/auth/guards/oidc.guard.js delete mode 100644 dist/modules/auth/guards/oidc.guard.js.map delete mode 100644 dist/modules/auth/oidc.service.js delete mode 100644 dist/modules/auth/oidc.service.js.map delete mode 100644 dist/modules/auth/pkce/redis-pkce.store.js delete mode 100644 dist/modules/auth/pkce/redis-pkce.store.js.map delete mode 100644 dist/modules/auth/refresh/inmemory-refresh.store.js delete mode 100644 dist/modules/auth/refresh/inmemory-refresh.store.js.map delete mode 100644 dist/modules/auth/role-sync.service.js delete mode 100644 dist/modules/auth/role-sync.service.js.map delete mode 100644 dist/modules/authorization/application/authorization.service.js delete mode 100644 dist/modules/authorization/application/authorization.service.js.map delete mode 100644 dist/modules/authorization/authorization.module.js delete mode 100644 dist/modules/authorization/authorization.module.js.map delete mode 100644 dist/modules/authorization/authorization.service.js delete mode 100644 dist/modules/authorization/authorization.service.js.map delete mode 100644 dist/modules/authorization/cache/permission-cache.interface.js delete mode 100644 dist/modules/authorization/cache/permission-cache.interface.js.map delete mode 100644 dist/modules/authorization/cache/redis-permission-cache.service.js delete mode 100644 dist/modules/authorization/cache/redis-permission-cache.service.js.map delete mode 100644 dist/modules/authorization/guards/permission.guard.js delete mode 100644 dist/modules/authorization/guards/permission.guard.js.map delete mode 100644 dist/modules/authorization/presentation/decorators/permission.decorator.js delete mode 100644 dist/modules/authorization/presentation/decorators/permission.decorator.js.map delete mode 100644 dist/modules/authorization/presentation/guards/permission.guard.js delete mode 100644 dist/modules/authorization/presentation/guards/permission.guard.js.map delete mode 100644 dist/modules/health/health.controller.js delete mode 100644 dist/modules/health/health.controller.js.map delete mode 100644 dist/modules/health/health.module.js delete mode 100644 dist/modules/health/health.module.js.map delete mode 100644 dist/modules/health/health.service.js delete mode 100644 dist/modules/health/health.service.js.map delete mode 100644 dist/modules/identity/application/config/env-auth-config.js delete mode 100644 dist/modules/identity/application/config/env-auth-config.js.map delete mode 100644 dist/modules/identity/application/config/i-auth-config.js delete mode 100644 dist/modules/identity/application/config/i-auth-config.js.map delete mode 100644 dist/modules/identity/application/handlers/permission/create-permission.handler.js delete mode 100644 dist/modules/identity/application/handlers/permission/create-permission.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/permission/delete-permission.handler.js delete mode 100644 dist/modules/identity/application/handlers/permission/delete-permission.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/permission/get-permission.handler.js delete mode 100644 dist/modules/identity/application/handlers/permission/get-permission.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/permission/get-permissions.handler.js delete mode 100644 dist/modules/identity/application/handlers/permission/get-permissions.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/permission/update-permission.handler.js delete mode 100644 dist/modules/identity/application/handlers/permission/update-permission.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/role/assign-permission.handler.js delete mode 100644 dist/modules/identity/application/handlers/role/assign-permission.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/role/create-role.handler.js delete mode 100644 dist/modules/identity/application/handlers/role/create-role.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/role/delete-role.handler.js delete mode 100644 dist/modules/identity/application/handlers/role/delete-role.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/role/get-role.handler.js delete mode 100644 dist/modules/identity/application/handlers/role/get-role.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/role/get-roles.handler.js delete mode 100644 dist/modules/identity/application/handlers/role/get-roles.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/role/remove-permission.handler.js delete mode 100644 dist/modules/identity/application/handlers/role/remove-permission.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/role/update-role.handler.js delete mode 100644 dist/modules/identity/application/handlers/role/update-role.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/assign-permission.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/assign-permission.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/assign-role.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/assign-role.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/create-user.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/create-user.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/delete-user.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/delete-user.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/disable-user.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/disable-user.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/enable-user.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/enable-user.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/get-current-user.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/get-current-user.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/get-user.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/get-user.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/get-users.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/get-users.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/remove-permission.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/remove-permission.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/remove-role.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/remove-role.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/restore-user.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/restore-user.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/sync-identity.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/sync-identity.handler.js.map delete mode 100644 dist/modules/identity/application/handlers/user/update-user.handler.js delete mode 100644 dist/modules/identity/application/handlers/user/update-user.handler.js.map delete mode 100644 dist/modules/identity/application/services/user.service.js delete mode 100644 dist/modules/identity/application/services/user.service.js.map delete mode 100644 dist/modules/identity/domain/entities/permission.entity.js delete mode 100644 dist/modules/identity/domain/entities/permission.entity.js.map delete mode 100644 dist/modules/identity/domain/entities/role.entity.js delete mode 100644 dist/modules/identity/domain/entities/role.entity.js.map delete mode 100644 dist/modules/identity/domain/entities/user.entity.js delete mode 100644 dist/modules/identity/domain/entities/user.entity.js.map delete mode 100644 dist/modules/identity/domain/events/user-created.event.js delete mode 100644 dist/modules/identity/domain/events/user-created.event.js.map delete mode 100644 dist/modules/identity/domain/events/user-disabled.event.js delete mode 100644 dist/modules/identity/domain/events/user-disabled.event.js.map delete mode 100644 dist/modules/identity/domain/events/user-email-changed.event.js delete mode 100644 dist/modules/identity/domain/events/user-email-changed.event.js.map delete mode 100644 dist/modules/identity/domain/events/user-enabled.event.js delete mode 100644 dist/modules/identity/domain/events/user-enabled.event.js.map delete mode 100644 dist/modules/identity/domain/events/user-username-changed.event.js delete mode 100644 dist/modules/identity/domain/events/user-username-changed.event.js.map delete mode 100644 dist/modules/identity/domain/repositories/permission.interface.js delete mode 100644 dist/modules/identity/domain/repositories/permission.interface.js.map delete mode 100644 dist/modules/identity/domain/repositories/role.interface.js delete mode 100644 dist/modules/identity/domain/repositories/role.interface.js.map delete mode 100644 dist/modules/identity/domain/repositories/user.interface.js delete mode 100644 dist/modules/identity/domain/repositories/user.interface.js.map delete mode 100644 dist/modules/identity/identity.module.js delete mode 100644 dist/modules/identity/identity.module.js.map delete mode 100644 dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js delete mode 100644 dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js.map delete mode 100644 dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js delete mode 100644 dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js.map delete mode 100644 dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js delete mode 100644 dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js.map delete mode 100644 dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js delete mode 100644 dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js.map delete mode 100644 dist/modules/identity/infrastructure/repositories/prisma-role.repository.js delete mode 100644 dist/modules/identity/infrastructure/repositories/prisma-role.repository.js.map delete mode 100644 dist/modules/identity/infrastructure/repositories/prisma-user.repository.js delete mode 100644 dist/modules/identity/infrastructure/repositories/prisma-user.repository.js.map delete mode 100644 dist/modules/identity/presentation/controllers/permissions.controller.js delete mode 100644 dist/modules/identity/presentation/controllers/permissions.controller.js.map delete mode 100644 dist/modules/identity/presentation/controllers/roles.controller.js delete mode 100644 dist/modules/identity/presentation/controllers/roles.controller.js.map delete mode 100644 dist/modules/identity/presentation/controllers/users.controller.js delete mode 100644 dist/modules/identity/presentation/controllers/users.controller.js.map delete mode 100644 dist/modules/identity/presentation/dto/create-permission.dto.js delete mode 100644 dist/modules/identity/presentation/dto/create-permission.dto.js.map delete mode 100644 dist/modules/identity/presentation/dto/create-role.dto.js delete mode 100644 dist/modules/identity/presentation/dto/create-role.dto.js.map delete mode 100644 dist/modules/identity/presentation/dto/create-user.dto.js delete mode 100644 dist/modules/identity/presentation/dto/create-user.dto.js.map delete mode 100644 dist/modules/identity/presentation/dto/update-permission.dto.js delete mode 100644 dist/modules/identity/presentation/dto/update-permission.dto.js.map delete mode 100644 dist/modules/identity/presentation/dto/update-role.dto.js delete mode 100644 dist/modules/identity/presentation/dto/update-role.dto.js.map delete mode 100644 dist/modules/identity/presentation/dto/update-user.dto.js delete mode 100644 dist/modules/identity/presentation/dto/update-user.dto.js.map delete mode 100644 dist/modules/identity/presentation/guards/current-user.guard.js delete mode 100644 dist/modules/identity/presentation/guards/current-user.guard.js.map delete mode 100644 dist/shared/cache-keys.js delete mode 100644 dist/shared/cache-keys.js.map delete mode 100644 dist/shared/prisma.service.js delete mode 100644 dist/shared/prisma.service.js.map delete mode 100644 dist/shared/redis.service.js delete mode 100644 dist/shared/redis.service.js.map delete mode 100644 dist/shared/types/request-context.js delete mode 100644 dist/shared/types/request-context.js.map delete mode 100644 dist/shared/utils/index.js delete mode 100644 dist/shared/utils/index.js.map create mode 100644 tests/unit/current-user.guard.spec.ts create mode 100644 tests/unit/jwt-auth.guard.spec.ts diff --git a/.gitignore b/.gitignore index 630495e..241a13f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ node_modules RayLab-Core.zip RayLab-Core.rar .env -dist \ No newline at end of file +dist/ diff --git a/dist/app.module.js b/dist/app.module.js deleted file mode 100644 index ba0f573..0000000 --- a/dist/app.module.js +++ /dev/null @@ -1,37 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AppModule = void 0; -const common_1 = require("@nestjs/common"); -const config_1 = require("@nestjs/config"); -const identity_module_1 = require("./modules/identity/identity.module"); -const auth_module_1 = require("./modules/auth/auth.module"); -const health_module_1 = require("./modules/health/health.module"); -const authorization_module_1 = require("./modules/authorization/authorization.module"); -const audit_module_1 = require("./modules/audit/audit.module"); -const application_module_1 = require("./modules/application/application.module"); -let AppModule = class AppModule { -}; -exports.AppModule = AppModule; -exports.AppModule = AppModule = __decorate([ - (0, common_1.Module)({ - imports: [ - config_1.ConfigModule.forRoot({ - isGlobal: true, - }), - identity_module_1.IdentityModule, - auth_module_1.AuthModule, - health_module_1.HealthModule, - // Authorization module provides permission checks and cache - authorization_module_1.AuthorizationModule, - audit_module_1.AuditModule, - application_module_1.ApplicationModule, - ], - }) -], AppModule); -//# sourceMappingURL=app.module.js.map \ No newline at end of file diff --git a/dist/app.module.js.map b/dist/app.module.js.map deleted file mode 100644 index 4fef502..0000000 --- a/dist/app.module.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2CAA8C;AAE9C,wEAAoE;AACpE,4DAAwD;AACxD,kEAA8D;AAC9D,uFAAmF;AACnF,+DAA2D;AAC3D,iFAA6E;AAmBtE,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IAhBrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,CAAC;gBACnB,QAAQ,EAAE,IAAI;aACf,CAAC;YAEE,gCAAc;YACd,wBAAU;YACV,4BAAY;YACZ,4DAA4D;YACpD,0CAAmB;YAC3B,0BAAW;YACX,sCAAiB;SAEtB;KACF,CAAC;GACW,SAAS,CAAG"} \ No newline at end of file diff --git a/dist/common/constants/permission.constants.js b/dist/common/constants/permission.constants.js deleted file mode 100644 index ffc60e9..0000000 --- a/dist/common/constants/permission.constants.js +++ /dev/null @@ -1,18 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PermissionType = void 0; -exports.PermissionType = { - USER_CREATE: 'USER_CREATE', - USER_READ: 'USER_READ', - USER_UPDATE: 'USER_UPDATE', - USER_DELETE: 'USER_DELETE', - ROLE_CREATE: 'ROLE_CREATE', - ROLE_READ: 'ROLE_READ', - ROLE_UPDATE: 'ROLE_UPDATE', - ROLE_DELETE: 'ROLE_DELETE', - PERMISSION_CREATE: 'PERMISSION_CREATE', - PERMISSION_READ: 'PERMISSION_READ', - PERMISSION_UPDATE: 'PERMISSION_UPDATE', - PERMISSION_DELETE: 'PERMISSION_DELETE', -}; -//# sourceMappingURL=permission.constants.js.map \ No newline at end of file diff --git a/dist/common/constants/permission.constants.js.map b/dist/common/constants/permission.constants.js.map deleted file mode 100644 index 2749d2f..0000000 --- a/dist/common/constants/permission.constants.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"permission.constants.js","sourceRoot":"","sources":["../../../src/common/constants/permission.constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,cAAc,GAAG;IAC5B,WAAW,EAAE,aAAa;IAC1B,SAAS,EAAE,WAAW;IACtB,WAAW,EAAE,aAAa;IAC1B,WAAW,EAAE,aAAa;IAE1B,WAAW,EAAE,aAAa;IAC1B,SAAS,EAAE,WAAW;IACtB,WAAW,EAAE,aAAa;IAC1B,WAAW,EAAE,aAAa;IAE1B,iBAAiB,EAAE,mBAAmB;IACtC,eAAe,EAAE,iBAAiB;IAClC,iBAAiB,EAAE,mBAAmB;IACtC,iBAAiB,EAAE,mBAAmB;CAC9B,CAAC"} \ No newline at end of file diff --git a/dist/core/auth/guards/jwt-auth.guard.js b/dist/core/auth/guards/jwt-auth.guard.js deleted file mode 100644 index 5007d31..0000000 --- a/dist/core/auth/guards/jwt-auth.guard.js +++ /dev/null @@ -1,106 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.JwtAuthGuard = void 0; -const common_1 = require("@nestjs/common"); -const jose_1 = require("jose"); -const identity_data_1 = require("../interfaces/identity-data"); -const jwt = __importStar(require("jsonwebtoken")); -/** - * JwtAuthGuard verifies JWTs issued by the external Identity Provider (Authentik) - * using JWKS (RS256). It also accepts internal RayLab JWTs signed with a local secret. - */ -let JwtAuthGuard = class JwtAuthGuard { - jwks = null; - constructor() { } - async canActivate(context) { - const request = context.switchToHttp().getRequest(); - const authHeader = request.headers.authorization; - if (!authHeader) { - throw new common_1.UnauthorizedException('Authorization header is missing.'); - } - const [type, token] = authHeader.split(' '); - if (type !== 'Bearer' || !token) { - throw new common_1.UnauthorizedException('Invalid authorization header.'); - } - const jwksUri = process.env.AUTHENTIK_JWKS_URI; - // First try verifying with external JWKS (Authentik) - if (jwksUri) { - try { - if (!this.jwks) - this.jwks = (0, jose_1.createRemoteJWKSet)(new URL(jwksUri)); - const { payload } = await (0, jose_1.jwtVerify)(token, this.jwks, { - issuer: process.env.AUTHENTIK_ISSUER, - audience: process.env.AUTHENTIK_AUDIENCE, - }); - const identity = new identity_data_1.IdentityData(payload.sub, payload.preferred_username, payload.email, payload); - request.identity = identity; - return true; - } - catch (err) { - // ignore and try internal verification - } - } - // Fallback: verify with internal symmetric secret - const secret = process.env.RAYLAB_JWT_SECRET; - if (!secret) { - throw new common_1.UnauthorizedException('Invalid or expired token.'); - } - try { - const payload = jwt.verify(token, secret); - const identity = new identity_data_1.IdentityData(payload.sub, payload.preferred_username, payload.email, payload); - request.identity = identity; - return true; - } - catch (err) { - throw new common_1.UnauthorizedException('Invalid or expired token.'); - } - } -}; -exports.JwtAuthGuard = JwtAuthGuard; -exports.JwtAuthGuard = JwtAuthGuard = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", []) -], JwtAuthGuard); -//# sourceMappingURL=jwt-auth.guard.js.map \ No newline at end of file diff --git a/dist/core/auth/guards/jwt-auth.guard.js.map b/dist/core/auth/guards/jwt-auth.guard.js.map deleted file mode 100644 index fa4c38d..0000000 --- a/dist/core/auth/guards/jwt-auth.guard.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"jwt-auth.guard.js","sourceRoot":"","sources":["../../../../src/core/auth/guards/jwt-auth.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAKwB;AAExB,+BAAqD;AACrD,+DAA2D;AAC3D,kDAAoC;AAEpC;;;GAGG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAY;IACf,IAAI,GAAiD,IAAI,CAAC;IAElE,gBAAe,CAAC;IAEhB,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAyC,CAAC;QAE3F,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QAEjD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,8BAAqB,CAAC,kCAAkC,CAAC,CAAC;QACtE,CAAC;QAED,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE5C,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;YAChC,MAAM,IAAI,8BAAqB,CAAC,+BAA+B,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;QAE/C,qDAAqD;QACrD,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,IAAI,CAAC,IAAI,CAAC,IAAI;oBAAE,IAAI,CAAC,IAAI,GAAG,IAAA,yBAAkB,EAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;gBAEjE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAA,gBAAS,EAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE;oBACpD,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB;oBACpC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB;iBACzC,CAAC,CAAC;gBAEH,MAAM,QAAQ,GAAG,IAAI,4BAAY,CAC/B,OAAO,CAAC,GAAa,EACpB,OAAe,CAAC,kBAAwC,EACxD,OAAe,CAAC,KAA2B,EAC5C,OAA8B,CAC/B,CAAC;gBAEF,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;gBAC5B,OAAO,IAAI,CAAC;YACd,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,uCAAuC;YACzC,CAAC;QACH,CAAC;QAED,kDAAkD;QAClD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,8BAAqB,CAAC,2BAA2B,CAAC,CAAC;QAC/D,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAQ,CAAC;YAEjD,MAAM,QAAQ,GAAG,IAAI,4BAAY,CAC/B,OAAO,CAAC,GAAa,EACrB,OAAO,CAAC,kBAAwC,EAChD,OAAO,CAAC,KAA2B,EACnC,OAA8B,CAC/B,CAAC;YAEF,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,MAAM,IAAI,8BAAqB,CAAC,2BAA2B,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;CACF,CAAA;AApEY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;;GACA,YAAY,CAoExB"} \ No newline at end of file diff --git a/dist/core/auth/interfaces/authenticated-request.interface.js b/dist/core/auth/interfaces/authenticated-request.interface.js deleted file mode 100644 index c514541..0000000 --- a/dist/core/auth/interfaces/authenticated-request.interface.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=authenticated-request.interface.js.map \ No newline at end of file diff --git a/dist/core/auth/interfaces/authenticated-request.interface.js.map b/dist/core/auth/interfaces/authenticated-request.interface.js.map deleted file mode 100644 index 220894c..0000000 --- a/dist/core/auth/interfaces/authenticated-request.interface.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"authenticated-request.interface.js","sourceRoot":"","sources":["../../../../src/core/auth/interfaces/authenticated-request.interface.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/core/auth/interfaces/identity-data.js b/dist/core/auth/interfaces/identity-data.js deleted file mode 100644 index e130fd7..0000000 --- a/dist/core/auth/interfaces/identity-data.js +++ /dev/null @@ -1,17 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.IdentityData = void 0; -class IdentityData { - sub; - preferred_username; - email; - claims; - constructor(sub, preferred_username, email, claims) { - this.sub = sub; - this.preferred_username = preferred_username; - this.email = email; - this.claims = claims; - } -} -exports.IdentityData = IdentityData; -//# sourceMappingURL=identity-data.js.map \ No newline at end of file diff --git a/dist/core/auth/interfaces/identity-data.js.map b/dist/core/auth/interfaces/identity-data.js.map deleted file mode 100644 index 292e6dc..0000000 --- a/dist/core/auth/interfaces/identity-data.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"identity-data.js","sourceRoot":"","sources":["../../../../src/core/auth/interfaces/identity-data.ts"],"names":[],"mappings":";;;AAAA,MAAa,YAAY;IAEL;IACA;IACA;IACA;IAJlB,YACkB,GAAW,EACX,kBAA2B,EAC3B,KAAc,EACd,MAA4B;QAH5B,QAAG,GAAH,GAAG,CAAQ;QACX,uBAAkB,GAAlB,kBAAkB,CAAS;QAC3B,UAAK,GAAL,KAAK,CAAS;QACd,WAAM,GAAN,MAAM,CAAsB;IAC3C,CAAC;CACL;AAPD,oCAOC"} \ No newline at end of file diff --git a/dist/core/config/database.config.js b/dist/core/config/database.config.js deleted file mode 100644 index c4bf0dd..0000000 --- a/dist/core/config/database.config.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = () => ({ - database: { - url: `postgresql://` + - `${process.env.DB_USER}:` + - `${process.env.DB_PASSWORD}@` + - `${process.env.SERVER_HOST}:` + - `${process.env.DB_PORT}/` + - `${process.env.DB_NAME}?schema=public` - } -}); -//# sourceMappingURL=database.config.js.map \ No newline at end of file diff --git a/dist/core/config/database.config.js.map b/dist/core/config/database.config.js.map deleted file mode 100644 index 4f98463..0000000 --- a/dist/core/config/database.config.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"database.config.js","sourceRoot":"","sources":["../../../src/core/config/database.config.ts"],"names":[],"mappings":";;AAAA,kBAAe,GAAG,EAAE,CAAC,CAAC;IAClB,QAAQ,EAAE;QACN,GAAG,EACC,eAAe;YACf,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG;YACzB,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG;YAC7B,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG;YAC7B,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG;YACzB,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,gBAAgB;KAC7C;CACJ,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/core/event-bus/event-bus.service.js b/dist/core/event-bus/event-bus.service.js deleted file mode 100644 index ce7de3c..0000000 --- a/dist/core/event-bus/event-bus.service.js +++ /dev/null @@ -1,44 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var EventBus_1; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EventBus = void 0; -const common_1 = require("@nestjs/common"); -let EventBus = EventBus_1 = class EventBus { - handlers = new Map(); - logger = new common_1.Logger(EventBus_1.name); - // Accept either an EventEnvelope or (type, payload) signature for backward compatibility - publish(eventOrType, payload) { - let envelope; - if (typeof eventOrType === 'string') { - envelope = { id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: eventOrType, payload: payload ?? {} }; - } - else { - envelope = eventOrType; - } - const handlers = this.handlers.get(envelope.type) || []; - for (const h of handlers) { - try { - Promise.resolve(h(envelope)).catch((err) => this.logger.error('Event handler error', err)); - } - catch (e) { - this.logger.error('Event handler threw', e); - } - } - } - subscribe(eventType, handler) { - const list = this.handlers.get(eventType) || []; - list.push(handler); - this.handlers.set(eventType, list); - } -}; -exports.EventBus = EventBus; -exports.EventBus = EventBus = EventBus_1 = __decorate([ - (0, common_1.Injectable)() -], EventBus); -//# sourceMappingURL=event-bus.service.js.map \ No newline at end of file diff --git a/dist/core/event-bus/event-bus.service.js.map b/dist/core/event-bus/event-bus.service.js.map deleted file mode 100644 index e8e8fcd..0000000 --- a/dist/core/event-bus/event-bus.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"event-bus.service.js","sourceRoot":"","sources":["../../../src/core/event-bus/event-bus.service.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAoD;AAO7C,IAAM,QAAQ,gBAAd,MAAM,QAAQ;IACX,QAAQ,GAA2B,IAAI,GAAG,EAAE,CAAC;IACpC,MAAM,GAAG,IAAI,eAAM,CAAC,UAAQ,CAAC,IAAI,CAAC,CAAC;IAEpD,yFAAyF;IACzF,OAAO,CAAC,WAAwC,EAAE,OAAa;QAC7D,IAAI,QAA4B,CAAC;QACjC,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;YACpC,QAAQ,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;QACpI,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,WAAW,CAAC;QACzB,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACxD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC,CAAC;YAC7F,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAQ,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;IACH,CAAC;IAED,SAAS,CAAC,SAAiB,EAAE,OAAgB;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;CACF,CAAA;AA5BY,4BAAQ;mBAAR,QAAQ;IADpB,IAAA,mBAAU,GAAE;GACA,QAAQ,CA4BpB"} \ No newline at end of file diff --git a/dist/core/event-bus/event.interface.js b/dist/core/event-bus/event.interface.js deleted file mode 100644 index 051ab18..0000000 --- a/dist/core/event-bus/event.interface.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=event.interface.js.map \ No newline at end of file diff --git a/dist/core/event-bus/event.interface.js.map b/dist/core/event-bus/event.interface.js.map deleted file mode 100644 index db1995e..0000000 --- a/dist/core/event-bus/event.interface.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"event.interface.js","sourceRoot":"","sources":["../../../src/core/event-bus/event.interface.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/core/events/event-dispatcher.js b/dist/core/events/event-dispatcher.js deleted file mode 100644 index ad25771..0000000 --- a/dist/core/events/event-dispatcher.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EventDispatcher = void 0; -class EventDispatcher { - publisher = new (require('./event-publisher').InMemoryEventPublisher)(); - register(handler) { - this.publisher.register(handler); - } - async dispatch(event) { - await this.publisher.publish(event); - } -} -exports.EventDispatcher = EventDispatcher; -//# sourceMappingURL=event-dispatcher.js.map \ No newline at end of file diff --git a/dist/core/events/event-dispatcher.js.map b/dist/core/events/event-dispatcher.js.map deleted file mode 100644 index 065bcf8..0000000 --- a/dist/core/events/event-dispatcher.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"event-dispatcher.js","sourceRoot":"","sources":["../../../src/core/events/event-dispatcher.ts"],"names":[],"mappings":";;;AAEA,MAAa,eAAe;IAClB,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,sBAAsB,CAAC,EAAE,CAAC;IAEhF,QAAQ,CAAC,OAA0C;QACjD,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAkB;QAC/B,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;CACF;AAVD,0CAUC"} \ No newline at end of file diff --git a/dist/core/events/event-handler.js b/dist/core/events/event-handler.js deleted file mode 100644 index 6950f78..0000000 --- a/dist/core/events/event-handler.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=event-handler.js.map \ No newline at end of file diff --git a/dist/core/events/event-handler.js.map b/dist/core/events/event-handler.js.map deleted file mode 100644 index efbfd3f..0000000 --- a/dist/core/events/event-handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"event-handler.js","sourceRoot":"","sources":["../../../src/core/events/event-handler.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/core/events/event-publisher.js b/dist/core/events/event-publisher.js deleted file mode 100644 index 7429352..0000000 --- a/dist/core/events/event-publisher.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.InMemoryEventPublisher = void 0; -class InMemoryEventPublisher { - handlers = []; - register(handler) { - this.handlers.push(handler); - } - async publish(event) { - await Promise.all(this.handlers.map((h) => h(event))); - } -} -exports.InMemoryEventPublisher = InMemoryEventPublisher; -//# sourceMappingURL=event-publisher.js.map \ No newline at end of file diff --git a/dist/core/events/event-publisher.js.map b/dist/core/events/event-publisher.js.map deleted file mode 100644 index c2c673c..0000000 --- a/dist/core/events/event-publisher.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"event-publisher.js","sourceRoot":"","sources":["../../../src/core/events/event-publisher.ts"],"names":[],"mappings":";;;AAMA,MAAa,sBAAsB;IACzB,QAAQ,GAA6C,EAAE,CAAC;IAEhE,QAAQ,CAAC,OAA0C;QACjD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAkB;QAC9B,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;CACF;AAVD,wDAUC"} \ No newline at end of file diff --git a/dist/core/events/event.interface.js b/dist/core/events/event.interface.js deleted file mode 100644 index 051ab18..0000000 --- a/dist/core/events/event.interface.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=event.interface.js.map \ No newline at end of file diff --git a/dist/core/events/event.interface.js.map b/dist/core/events/event.interface.js.map deleted file mode 100644 index 2fa0bd2..0000000 --- a/dist/core/events/event.interface.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"event.interface.js","sourceRoot":"","sources":["../../../src/core/events/event.interface.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/core/exceptions/http-exception.filter.js b/dist/core/exceptions/http-exception.filter.js deleted file mode 100644 index 36ced11..0000000 --- a/dist/core/exceptions/http-exception.filter.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.HttpExceptionFilter = void 0; -const common_1 = require("@nestjs/common"); -let HttpExceptionFilter = class HttpExceptionFilter { - catch(exception, host) { - const ctx = host.switchToHttp(); - const response = ctx.getResponse(); - const status = exception.getStatus ? exception.getStatus() : 500; - response.status(status).json({ - success: false, - error: { - code: exception.code || 'INTERNAL_ERROR', - message: exception.message || 'Internal server error', - }, - }); - } -}; -exports.HttpExceptionFilter = HttpExceptionFilter; -exports.HttpExceptionFilter = HttpExceptionFilter = __decorate([ - (0, common_1.Catch)() -], HttpExceptionFilter); -//# sourceMappingURL=http-exception.filter.js.map \ No newline at end of file diff --git a/dist/core/exceptions/http-exception.filter.js.map b/dist/core/exceptions/http-exception.filter.js.map deleted file mode 100644 index 9774136..0000000 --- a/dist/core/exceptions/http-exception.filter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"http-exception.filter.js","sourceRoot":"","sources":["../../../src/core/exceptions/http-exception.filter.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAuE;AAGhE,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC9B,KAAK,CAAC,SAAc,EAAE,IAAmB;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;QAEjE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;YAC3B,OAAO,EAAE,KAAK;YACd,KAAK,EAAE;gBACL,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,gBAAgB;gBACxC,OAAO,EAAE,SAAS,CAAC,OAAO,IAAI,uBAAuB;aACtD;SACF,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AAdY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,cAAK,GAAE;GACK,mBAAmB,CAc/B"} \ No newline at end of file diff --git a/dist/core/logger/logger.service.js b/dist/core/logger/logger.service.js deleted file mode 100644 index 8de8f09..0000000 --- a/dist/core/logger/logger.service.js +++ /dev/null @@ -1,11 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.logger = void 0; -const pino_1 = __importDefault(require("pino")); -exports.logger = (0, pino_1.default)({ - level: process.env.LOG_LEVEL || 'info', -}); -//# sourceMappingURL=logger.service.js.map \ No newline at end of file diff --git a/dist/core/logger/logger.service.js.map b/dist/core/logger/logger.service.js.map deleted file mode 100644 index 459e62c..0000000 --- a/dist/core/logger/logger.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"logger.service.js","sourceRoot":"","sources":["../../../src/core/logger/logger.service.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AAEX,QAAA,MAAM,GAAG,IAAA,cAAI,EAAC;IACzB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;CACvC,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/core/middleware/request-id.middleware.js b/dist/core/middleware/request-id.middleware.js deleted file mode 100644 index 4fa05fb..0000000 --- a/dist/core/middleware/request-id.middleware.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RequestIdMiddleware = void 0; -const common_1 = require("@nestjs/common"); -const uuid_1 = require("uuid"); -let RequestIdMiddleware = class RequestIdMiddleware { - use(req, res, next) { - req.requestId = req.headers['x-request-id'] || (0, uuid_1.v4)(); - res.setHeader('X-Request-ID', req.requestId); - next(); - } -}; -exports.RequestIdMiddleware = RequestIdMiddleware; -exports.RequestIdMiddleware = RequestIdMiddleware = __decorate([ - (0, common_1.Injectable)() -], RequestIdMiddleware); -//# sourceMappingURL=request-id.middleware.js.map \ No newline at end of file diff --git a/dist/core/middleware/request-id.middleware.js.map b/dist/core/middleware/request-id.middleware.js.map deleted file mode 100644 index 393bde4..0000000 --- a/dist/core/middleware/request-id.middleware.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"request-id.middleware.js","sourceRoot":"","sources":["../../../src/core/middleware/request-id.middleware.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4D;AAC5D,+BAAoC;AAG7B,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC9B,GAAG,CAAC,GAAQ,EAAE,GAAQ,EAAE,IAAgB;QACtC,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,IAAA,SAAM,GAAE,CAAC;QACxD,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,EAAE,CAAC;IACT,CAAC;CACF,CAAA;AANY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,mBAAU,GAAE;GACA,mBAAmB,CAM/B"} \ No newline at end of file diff --git a/dist/main.js b/dist/main.js deleted file mode 100644 index 7a2a568..0000000 --- a/dist/main.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const common_1 = require("@nestjs/common"); -const core_1 = require("@nestjs/core"); -const config_1 = require("@nestjs/config"); -const swagger_1 = require("@nestjs/swagger"); -const app_module_1 = require("./app.module"); -async function bootstrap() { - const app = await core_1.NestFactory.create(app_module_1.AppModule); - const config = app.get(config_1.ConfigService); - app.setGlobalPrefix(''); - app.useGlobalPipes(new common_1.ValidationPipe({ - whitelist: true, - transform: true, - forbidNonWhitelisted: true, - })); - app.enableCors(); - const swaggerEnabled = config.get('SWAGGER_ENABLED') === 'true'; - if (swaggerEnabled) { - const swaggerConfig = new swagger_1.DocumentBuilder() - .setTitle('RayLab Core API') - .setDescription('RayLab Core REST API') - .setVersion('1.0.0') - .addBearerAuth() - .build(); - const document = swagger_1.SwaggerModule.createDocument(app, swaggerConfig); - swagger_1.SwaggerModule.setup('ApiList', app, document); - } - const port = config.get('PORT') || 3000; - await app.listen(port); - console.log(`Server running on http://localhost:${port}`); -} -bootstrap(); -//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/dist/main.js.map b/dist/main.js.map deleted file mode 100644 index 8c9087b..0000000 --- a/dist/main.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;AAAA,2CAAgD;AAChD,uCAA2C;AAC3C,2CAA+C;AAC/C,6CAAiE;AAEjE,6CAAyC;AAEzC,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,CAAC;IAEhD,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,sBAAa,CAAC,CAAC;IAEtC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IAExB,GAAG,CAAC,cAAc,CAChB,IAAI,uBAAc,CAAC;QACjB,SAAS,EAAE,IAAI;QACf,SAAS,EAAE,IAAI;QACf,oBAAoB,EAAE,IAAI;KAC3B,CAAC,CACH,CAAC;IAEF,GAAG,CAAC,UAAU,EAAE,CAAC;IAEjB,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAS,iBAAiB,CAAC,KAAK,MAAM,CAAC;IAExE,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,aAAa,GAAG,IAAI,yBAAe,EAAE;aACxC,QAAQ,CAAC,iBAAiB,CAAC;aAC3B,cAAc,CAAC,sBAAsB,CAAC;aACtC,UAAU,CAAC,OAAO,CAAC;aACnB,aAAa,EAAE;aACf,KAAK,EAAE,CAAC;QAEX,MAAM,QAAQ,GAAG,uBAAa,CAAC,cAAc,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAElE,uBAAa,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAS,MAAM,CAAC,IAAI,IAAI,CAAC;IAEhD,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAEvB,OAAO,CAAC,GAAG,CAAC,sCAAsC,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/modules/application/application.module.js b/dist/modules/application/application.module.js deleted file mode 100644 index 923c5b5..0000000 --- a/dist/modules/application/application.module.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ApplicationModule = void 0; -const common_1 = require("@nestjs/common"); -const prisma_service_1 = require("../../shared/prisma.service"); -const applications_controller_1 = require("./presentation/controllers/applications.controller"); -const prisma_application_repository_1 = require("./infrastructure/repositories/prisma-application.repository"); -const application_interface_1 = require("./domain/repositories/application.interface"); -const get_applications_handler_1 = require("./application/handlers/get-applications.handler"); -const get_application_handler_1 = require("./application/handlers/get-application.handler"); -const create_application_handler_1 = require("./application/handlers/create-application.handler"); -const update_application_handler_1 = require("./application/handlers/update-application.handler"); -const delete_application_handler_1 = require("./application/handlers/delete-application.handler"); -const get_me_applications_handler_1 = require("./application/handlers/get-me-applications.handler"); -const application_validator_1 = require("./application/validators/application.validator"); -const jwt_auth_guard_1 = require("../../core/auth/guards/jwt-auth.guard"); -const identity_module_1 = require("../identity/identity.module"); -let ApplicationModule = class ApplicationModule { -}; -exports.ApplicationModule = ApplicationModule; -exports.ApplicationModule = ApplicationModule = __decorate([ - (0, common_1.Module)({ - imports: [identity_module_1.IdentityModule], - providers: [ - prisma_service_1.PrismaService, - prisma_application_repository_1.PrismaApplicationRepository, - get_applications_handler_1.GetApplicationsHandler, - get_application_handler_1.GetApplicationHandler, - create_application_handler_1.CreateApplicationHandler, - update_application_handler_1.UpdateApplicationHandler, - delete_application_handler_1.DeleteApplicationHandler, - get_me_applications_handler_1.GetMeApplicationsHandler, - application_validator_1.ApplicationValidator, - jwt_auth_guard_1.JwtAuthGuard, - { - provide: application_interface_1.IApplication, - useClass: prisma_application_repository_1.PrismaApplicationRepository, - }, - ], - controllers: [applications_controller_1.ApplicationsController], - }) -], ApplicationModule); -//# sourceMappingURL=application.module.js.map \ No newline at end of file diff --git a/dist/modules/application/application.module.js.map b/dist/modules/application/application.module.js.map deleted file mode 100644 index 100e91b..0000000 --- a/dist/modules/application/application.module.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"application.module.js","sourceRoot":"","sources":["../../../src/modules/application/application.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,gEAA4D;AAC5D,gGAA4F;AAC5F,+GAA0G;AAC1G,uFAA2E;AAC3E,8FAAyF;AACzF,4FAAuF;AACvF,kGAA6F;AAC7F,kGAA6F;AAC7F,kGAA6F;AAC7F,oGAA8F;AAC9F,0FAAsF;AACtF,0EAAqE;AACrE,iEAA6D;AAsBtD,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;CAAG,CAAA;AAApB,8CAAiB;4BAAjB,iBAAiB;IApB7B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,gCAAc,CAAC;QACzB,SAAS,EAAE;YACT,8BAAa;YACb,2DAA2B;YAC3B,iDAAsB;YACtB,+CAAqB;YACrB,qDAAwB;YACxB,qDAAwB;YACxB,qDAAwB;YACxB,sDAAwB;YACxB,4CAAoB;YACpB,6BAAY;YACZ;gBACE,OAAO,EAAE,oCAAY;gBACrB,QAAQ,EAAE,2DAA2B;aACtC;SACF;QACD,WAAW,EAAE,CAAC,gDAAsB,CAAC;KACtC,CAAC;GACW,iBAAiB,CAAG"} \ No newline at end of file diff --git a/dist/modules/application/application/handlers/create-application.handler.js b/dist/modules/application/application/handlers/create-application.handler.js deleted file mode 100644 index ada011b..0000000 --- a/dist/modules/application/application/handlers/create-application.handler.js +++ /dev/null @@ -1,44 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CreateApplicationHandler = void 0; -const common_1 = require("@nestjs/common"); -const application_interface_1 = require("../../domain/repositories/application.interface"); -const application_validator_1 = require("../../application/validators/application.validator"); -const application_entity_1 = require("../../domain/entities/application.entity"); -let CreateApplicationHandler = class CreateApplicationHandler { - appRepo; - validator; - constructor(appRepo, validator) { - this.appRepo = appRepo; - this.validator = validator; - } - async execute(payload) { - await this.validator.validateCreate(payload); - const app = application_entity_1.ApplicationData.create({ - code: payload.code, - name: payload.name, - description: payload.description, - icon: payload.icon, - url: payload.url, - applicationsClaim: payload.applicationsClaim, - displayOrder: payload.displayOrder, - }); - const created = await this.appRepo.create(app); - return created; - } -}; -exports.CreateApplicationHandler = CreateApplicationHandler; -exports.CreateApplicationHandler = CreateApplicationHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [application_interface_1.IApplication, application_validator_1.ApplicationValidator]) -], CreateApplicationHandler); -//# sourceMappingURL=create-application.handler.js.map \ No newline at end of file diff --git a/dist/modules/application/application/handlers/create-application.handler.js.map b/dist/modules/application/application/handlers/create-application.handler.js.map deleted file mode 100644 index cb30776..0000000 --- a/dist/modules/application/application/handlers/create-application.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"create-application.handler.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/handlers/create-application.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiE;AACjE,2FAA+E;AAC/E,8FAA0F;AAC1F,iFAA2E;AAGpE,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IACN;IAAwC;IAArE,YAA6B,OAAqB,EAAmB,SAA+B;QAAvE,YAAO,GAAP,OAAO,CAAc;QAAmB,cAAS,GAAT,SAAS,CAAsB;IAAG,CAAC;IAExG,KAAK,CAAC,OAAO,CAAC,OAAY;QACxB,MAAM,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAE7C,MAAM,GAAG,GAAG,oCAAe,CAAC,MAAM,CAAC;YACjC,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;YAC5C,YAAY,EAAE,OAAO,CAAC,YAAY;SACnC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC/C,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AAnBY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;qCAE2B,oCAAY,EAA8B,4CAAoB;GADzF,wBAAwB,CAmBpC"} \ No newline at end of file diff --git a/dist/modules/application/application/handlers/delete-application.handler.js b/dist/modules/application/application/handlers/delete-application.handler.js deleted file mode 100644 index 97a35cf..0000000 --- a/dist/modules/application/application/handlers/delete-application.handler.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DeleteApplicationHandler = void 0; -const common_1 = require("@nestjs/common"); -const application_interface_1 = require("../../domain/repositories/application.interface"); -let DeleteApplicationHandler = class DeleteApplicationHandler { - appRepo; - constructor(appRepo) { - this.appRepo = appRepo; - } - async execute(id) { - // ensure exists - await this.appRepo.findById(id); - await this.appRepo.delete(id); - } -}; -exports.DeleteApplicationHandler = DeleteApplicationHandler; -exports.DeleteApplicationHandler = DeleteApplicationHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [application_interface_1.IApplication]) -], DeleteApplicationHandler); -//# sourceMappingURL=delete-application.handler.js.map \ No newline at end of file diff --git a/dist/modules/application/application/handlers/delete-application.handler.js.map b/dist/modules/application/application/handlers/delete-application.handler.js.map deleted file mode 100644 index 7bb6d4d..0000000 --- a/dist/modules/application/application/handlers/delete-application.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"delete-application.handler.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/handlers/delete-application.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2FAA+E;AAGxE,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IACN;IAA7B,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,gBAAgB;QAChB,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;CACF,CAAA;AARY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;qCAE2B,oCAAY;GADvC,wBAAwB,CAQpC"} \ No newline at end of file diff --git a/dist/modules/application/application/handlers/get-application.handler.js b/dist/modules/application/application/handlers/get-application.handler.js deleted file mode 100644 index 0021280..0000000 --- a/dist/modules/application/application/handlers/get-application.handler.js +++ /dev/null @@ -1,30 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GetApplicationHandler = void 0; -const common_1 = require("@nestjs/common"); -const application_interface_1 = require("../../domain/repositories/application.interface"); -let GetApplicationHandler = class GetApplicationHandler { - appRepo; - constructor(appRepo) { - this.appRepo = appRepo; - } - async execute(id) { - const app = await this.appRepo.findById(id); - return app; - } -}; -exports.GetApplicationHandler = GetApplicationHandler; -exports.GetApplicationHandler = GetApplicationHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [application_interface_1.IApplication]) -], GetApplicationHandler); -//# sourceMappingURL=get-application.handler.js.map \ No newline at end of file diff --git a/dist/modules/application/application/handlers/get-application.handler.js.map b/dist/modules/application/application/handlers/get-application.handler.js.map deleted file mode 100644 index 4c93749..0000000 --- a/dist/modules/application/application/handlers/get-application.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"get-application.handler.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/handlers/get-application.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2FAA+E;AAGxE,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IACH;IAA7B,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC5C,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAA;AAPY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;qCAE2B,oCAAY;GADvC,qBAAqB,CAOjC"} \ No newline at end of file diff --git a/dist/modules/application/application/handlers/get-applications.handler.js b/dist/modules/application/application/handlers/get-applications.handler.js deleted file mode 100644 index 2fb1069..0000000 --- a/dist/modules/application/application/handlers/get-applications.handler.js +++ /dev/null @@ -1,30 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GetApplicationsHandler = void 0; -const common_1 = require("@nestjs/common"); -const application_interface_1 = require("../../domain/repositories/application.interface"); -let GetApplicationsHandler = class GetApplicationsHandler { - appRepo; - constructor(appRepo) { - this.appRepo = appRepo; - } - async execute(query) { - const res = await this.appRepo.find({ page: query.page, limit: query.limit, search: query.search || null }); - return res; - } -}; -exports.GetApplicationsHandler = GetApplicationsHandler; -exports.GetApplicationsHandler = GetApplicationsHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [application_interface_1.IApplication]) -], GetApplicationsHandler); -//# sourceMappingURL=get-applications.handler.js.map \ No newline at end of file diff --git a/dist/modules/application/application/handlers/get-applications.handler.js.map b/dist/modules/application/application/handlers/get-applications.handler.js.map deleted file mode 100644 index 5d78953..0000000 --- a/dist/modules/application/application/handlers/get-applications.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"get-applications.handler.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/handlers/get-applications.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2FAA+E;AAGxE,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IACJ;IAA7B,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,KAAyD;QACrE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QAC5G,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAA;AAPY,wDAAsB;iCAAtB,sBAAsB;IADlC,IAAA,mBAAU,GAAE;qCAE2B,oCAAY;GADvC,sBAAsB,CAOlC"} \ No newline at end of file diff --git a/dist/modules/application/application/handlers/get-me-applications.handler.js b/dist/modules/application/application/handlers/get-me-applications.handler.js deleted file mode 100644 index db284f1..0000000 --- a/dist/modules/application/application/handlers/get-me-applications.handler.js +++ /dev/null @@ -1,35 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GetMeApplicationsHandler = void 0; -const common_1 = require("@nestjs/common"); -const application_interface_1 = require("../../domain/repositories/application.interface"); -let GetMeApplicationsHandler = class GetMeApplicationsHandler { - appRepo; - constructor(appRepo) { - this.appRepo = appRepo; - } - async execute(applicationsClaimList) { - if (!applicationsClaimList || applicationsClaimList.length === 0) - return { data: [], total: 0 }; - // Only return active applications whose applicationsClaim exists in provided list - const res = await this.appRepo.find({ page: 1, limit: 1000, isActive: true, applicationsClaimIn: applicationsClaimList }); - // sort by displayOrder asc - res.data.sort((a, b) => (a.displayOrder ?? 0) - (b.displayOrder ?? 0)); - return res; - } -}; -exports.GetMeApplicationsHandler = GetMeApplicationsHandler; -exports.GetMeApplicationsHandler = GetMeApplicationsHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [application_interface_1.IApplication]) -], GetMeApplicationsHandler); -//# sourceMappingURL=get-me-applications.handler.js.map \ No newline at end of file diff --git a/dist/modules/application/application/handlers/get-me-applications.handler.js.map b/dist/modules/application/application/handlers/get-me-applications.handler.js.map deleted file mode 100644 index 7983311..0000000 --- a/dist/modules/application/application/handlers/get-me-applications.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"get-me-applications.handler.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/handlers/get-me-applications.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2FAA+E;AAGxE,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IACN;IAA7B,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,qBAAkD;QAC9D,IAAI,CAAC,qBAAqB,IAAI,qBAAqB,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAEhG,kFAAkF;QAClF,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,CAAC,CAAC;QAC1H,2BAA2B;QAC3B,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC;QACvE,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAA;AAZY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;qCAE2B,oCAAY;GADvC,wBAAwB,CAYpC"} \ No newline at end of file diff --git a/dist/modules/application/application/handlers/update-application.handler.js b/dist/modules/application/application/handlers/update-application.handler.js deleted file mode 100644 index 242bd8e..0000000 --- a/dist/modules/application/application/handlers/update-application.handler.js +++ /dev/null @@ -1,38 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UpdateApplicationHandler = void 0; -const common_1 = require("@nestjs/common"); -const application_interface_1 = require("../../domain/repositories/application.interface"); -const application_validator_1 = require("../../application/validators/application.validator"); -let UpdateApplicationHandler = class UpdateApplicationHandler { - appRepo; - validator; - constructor(appRepo, validator) { - this.appRepo = appRepo; - this.validator = validator; - } - async execute(id, payload) { - const existing = await this.appRepo.findById(id); - if (!existing) - throw new Error('Application not found'); - await this.validator.validateUpdate(id, payload); - existing.update(payload); - const updated = await this.appRepo.update(existing); - return updated; - } -}; -exports.UpdateApplicationHandler = UpdateApplicationHandler; -exports.UpdateApplicationHandler = UpdateApplicationHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [application_interface_1.IApplication, application_validator_1.ApplicationValidator]) -], UpdateApplicationHandler); -//# sourceMappingURL=update-application.handler.js.map \ No newline at end of file diff --git a/dist/modules/application/application/handlers/update-application.handler.js.map b/dist/modules/application/application/handlers/update-application.handler.js.map deleted file mode 100644 index 83a74d2..0000000 --- a/dist/modules/application/application/handlers/update-application.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"update-application.handler.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/handlers/update-application.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,2FAA+E;AAC/E,8FAA0F;AAGnF,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IACN;IAAwC;IAArE,YAA6B,OAAqB,EAAmB,SAA+B;QAAvE,YAAO,GAAP,OAAO,CAAc;QAAmB,cAAS,GAAT,SAAS,CAAsB;IAAG,CAAC;IAExG,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,OAAY;QACpC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAExD,MAAM,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAEjD,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACpD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AAbY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;qCAE2B,oCAAY,EAA8B,4CAAoB;GADzF,wBAAwB,CAapC"} \ No newline at end of file diff --git a/dist/modules/application/application/validators/application.validator.js b/dist/modules/application/application/validators/application.validator.js deleted file mode 100644 index dd91f08..0000000 --- a/dist/modules/application/application/validators/application.validator.js +++ /dev/null @@ -1,84 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ApplicationValidator = void 0; -const common_1 = require("@nestjs/common"); -const application_interface_1 = require("../../domain/repositories/application.interface"); -let ApplicationValidator = class ApplicationValidator { - appRepo; - constructor(appRepo) { - this.appRepo = appRepo; - } - async validateCreate(payload) { - if (!payload || !payload.code) - throw new common_1.BadRequestException('code is required'); - if (!payload.name) - throw new common_1.BadRequestException('name is required'); - if (!payload.applicationsClaim) - throw new common_1.BadRequestException('applicationsClaim is required'); - // url validation if provided - if (payload.url) { - try { - // allow relative urls - if (!payload.url.startsWith('/') && !payload.url.startsWith('http')) { - throw new Error('invalid'); - } - // new URL(payload.url) // avoid throwing for relative - } - catch (e) { - throw new common_1.BadRequestException('invalid url'); - } - } - // displayOrder - if (payload.displayOrder !== undefined && typeof payload.displayOrder !== 'number') { - throw new common_1.BadRequestException('invalid displayOrder'); - } - // unique code - const byCode = await this.appRepo.findByCode(payload.code); - if (byCode) - throw new common_1.BadRequestException('duplicate code'); - const byClaim = await this.appRepo.findByApplicationsClaim(payload.applicationsClaim); - if (byClaim) - throw new common_1.BadRequestException('duplicate applicationsClaim'); - } - async validateUpdate(id, payload) { - if (!payload) - return; - if (payload.url) { - try { - if (!payload.url.startsWith('/') && !payload.url.startsWith('http')) - throw new Error('invalid'); - } - catch (e) { - throw new common_1.BadRequestException('invalid url'); - } - } - if (payload.displayOrder !== undefined && typeof payload.displayOrder !== 'number') { - throw new common_1.BadRequestException('invalid displayOrder'); - } - if (payload.code) { - const existing = await this.appRepo.findByCode(payload.code); - if (existing && existing.id !== id) - throw new common_1.BadRequestException('duplicate code'); - } - if (payload.applicationsClaim) { - const existing = await this.appRepo.findByApplicationsClaim(payload.applicationsClaim); - if (existing && existing.id !== id) - throw new common_1.BadRequestException('duplicate applicationsClaim'); - } - } -}; -exports.ApplicationValidator = ApplicationValidator; -exports.ApplicationValidator = ApplicationValidator = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [application_interface_1.IApplication]) -], ApplicationValidator); -//# sourceMappingURL=application.validator.js.map \ No newline at end of file diff --git a/dist/modules/application/application/validators/application.validator.js.map b/dist/modules/application/application/validators/application.validator.js.map deleted file mode 100644 index 7f4f045..0000000 --- a/dist/modules/application/application/validators/application.validator.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"application.validator.js","sourceRoot":"","sources":["../../../../../src/modules/application/application/validators/application.validator.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/application/domain/entities/application.entity.js b/dist/modules/application/domain/entities/application.entity.js deleted file mode 100644 index 6319c92..0000000 --- a/dist/modules/application/domain/entities/application.entity.js +++ /dev/null @@ -1,67 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ApplicationData = void 0; -class ApplicationData { - id; - code; - name; - description; - icon; - url; - applicationsClaim; - isActive; - displayOrder; - createdAt; - updatedAt; - constructor(id, code, name, description, icon, url, applicationsClaim, isActive, displayOrder, createdAt, updatedAt) { - this.id = id; - this.code = code; - this.name = name; - this.description = description; - this.icon = icon; - this.url = url; - this.applicationsClaim = applicationsClaim; - this.isActive = isActive; - this.displayOrder = displayOrder; - this.createdAt = createdAt; - this.updatedAt = updatedAt; - } - static create(data) { - return new ApplicationData(crypto.randomUUID(), data.code, data.name, data.description ?? null, data.icon ?? null, data.url ?? null, data.applicationsClaim, true, data.displayOrder ?? 0, new Date(), new Date()); - } - static restore(props) { - return new ApplicationData(props.id, props.code, props.name, props.description ?? null, props.icon ?? null, props.url ?? null, props.applicationsClaim, props.isActive !== undefined ? props.isActive : true, props.displayOrder ?? 0, props.createdAt ?? null, props.updatedAt ?? null); - } - update(data) { - if (data.code !== undefined) - this.code = data.code; - if (data.name !== undefined) - this.name = data.name; - if (data.description !== undefined) - this.description = data.description; - if (data.icon !== undefined) - this.icon = data.icon; - if (data.url !== undefined) - this.url = data.url; - if (data.applicationsClaim !== undefined) - this.applicationsClaim = data.applicationsClaim; - if (data.isActive !== undefined) - this.isActive = data.isActive; - if (data.displayOrder !== undefined) - this.displayOrder = data.displayOrder; - this.updatedAt = new Date(); - } - toResponse() { - return { - id: this.id, - code: this.code, - name: this.name, - description: this.description, - icon: this.icon, - url: this.url, - displayOrder: this.displayOrder, - }; - } -} -exports.ApplicationData = ApplicationData; -//# sourceMappingURL=application.entity.js.map \ No newline at end of file diff --git a/dist/modules/application/domain/entities/application.entity.js.map b/dist/modules/application/domain/entities/application.entity.js.map deleted file mode 100644 index fb200f8..0000000 --- a/dist/modules/application/domain/entities/application.entity.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"application.entity.js","sourceRoot":"","sources":["../../../../../src/modules/application/domain/entities/application.entity.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/application/domain/repositories/application.interface.js b/dist/modules/application/domain/repositories/application.interface.js deleted file mode 100644 index a6bf53a..0000000 --- a/dist/modules/application/domain/repositories/application.interface.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.IApplication = void 0; -class IApplication { -} -exports.IApplication = IApplication; -//# sourceMappingURL=application.interface.js.map \ No newline at end of file diff --git a/dist/modules/application/domain/repositories/application.interface.js.map b/dist/modules/application/domain/repositories/application.interface.js.map deleted file mode 100644 index 906e160..0000000 --- a/dist/modules/application/domain/repositories/application.interface.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"application.interface.js","sourceRoot":"","sources":["../../../../../src/modules/application/domain/repositories/application.interface.ts"],"names":[],"mappings":";;;AAEA,MAAsB,YAAY;CASjC;AATD,oCASC"} \ No newline at end of file diff --git a/dist/modules/application/infrastructure/mappers/prisma-application.mapper.js b/dist/modules/application/infrastructure/mappers/prisma-application.mapper.js deleted file mode 100644 index dd8942e..0000000 --- a/dist/modules/application/infrastructure/mappers/prisma-application.mapper.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PrismaApplicationMapper = void 0; -const application_entity_1 = require("../../domain/entities/application.entity"); -class PrismaApplicationMapper { - static toDomain(model) { - return application_entity_1.ApplicationData.restore({ - id: model.id, - code: model.code, - name: model.name, - description: model.description, - icon: model.icon, - url: model.url, - applicationsClaim: model.applicationsClaim, - isActive: model.isActive, - displayOrder: model.displayOrder, - createdAt: model.createdAt, - updatedAt: model.updatedAt, - }); - } -} -exports.PrismaApplicationMapper = PrismaApplicationMapper; -//# sourceMappingURL=prisma-application.mapper.js.map \ No newline at end of file diff --git a/dist/modules/application/infrastructure/mappers/prisma-application.mapper.js.map b/dist/modules/application/infrastructure/mappers/prisma-application.mapper.js.map deleted file mode 100644 index 4c74586..0000000 --- a/dist/modules/application/infrastructure/mappers/prisma-application.mapper.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"prisma-application.mapper.js","sourceRoot":"","sources":["../../../../../src/modules/application/infrastructure/mappers/prisma-application.mapper.ts"],"names":[],"mappings":";;;AAAA,iFAA2E;AAE3E,MAAa,uBAAuB;IAClC,MAAM,CAAC,QAAQ,CAAC,KAAU;QACxB,OAAO,oCAAe,CAAC,OAAO,CAAC;YAC7B,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;YAC1C,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B,CAAC,CAAC;IACL,CAAC;CACF;AAhBD,0DAgBC"} \ No newline at end of file diff --git a/dist/modules/application/infrastructure/repositories/prisma-application.repository.js b/dist/modules/application/infrastructure/repositories/prisma-application.repository.js deleted file mode 100644 index 50a273b..0000000 --- a/dist/modules/application/infrastructure/repositories/prisma-application.repository.js +++ /dev/null @@ -1,98 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PrismaApplicationRepository = void 0; -const common_1 = require("@nestjs/common"); -const prisma_service_1 = require("../../../../shared/prisma.service"); -const prisma_application_mapper_1 = require("../mappers/prisma-application.mapper"); -let PrismaApplicationRepository = class PrismaApplicationRepository { - prisma; - constructor(prisma) { - this.prisma = prisma; - } - async find(params) { - const page = params.page && params.page > 0 ? params.page : 1; - const limit = params.limit && params.limit > 0 ? params.limit : 25; - const where = {}; - if (params.search) { - where.OR = [ - { name: { contains: params.search, mode: 'insensitive' } }, - { code: { contains: params.search, mode: 'insensitive' } }, - { description: { contains: params.search, mode: 'insensitive' } }, - ]; - } - if (params.isActive !== undefined && params.isActive !== null) { - where.isActive = params.isActive; - } - if (params.applicationsClaimIn && params.applicationsClaimIn.length > 0) { - where.applicationsClaim = { in: params.applicationsClaimIn }; - } - const [total, items] = await Promise.all([ - this.prisma.application.count({ where }), - this.prisma.application.findMany({ where, skip: (page - 1) * limit, take: limit, orderBy: { displayOrder: 'asc', createdAt: 'asc' } }), - ]); - return { data: items.map(i => prisma_application_mapper_1.PrismaApplicationMapper.toDomain(i)), total }; - } - async findById(id) { - const row = await this.prisma.application.findUnique({ where: { id } }); - if (!row) - throw new Error('Application not found'); - return prisma_application_mapper_1.PrismaApplicationMapper.toDomain(row); - } - async findByCode(code) { - const row = await this.prisma.application.findUnique({ where: { code } }); - if (!row) - return null; - return prisma_application_mapper_1.PrismaApplicationMapper.toDomain(row); - } - async findByApplicationsClaim(claim) { - const row = await this.prisma.application.findUnique({ where: { applicationsClaim: claim } }); - if (!row) - return null; - return prisma_application_mapper_1.PrismaApplicationMapper.toDomain(row); - } - async create(app) { - const created = await this.prisma.application.create({ data: { - id: app.id, - code: app.code, - name: app.name, - description: app.description, - icon: app.icon, - url: app.url, - applicationsClaim: app.applicationsClaim, - isActive: app.isActive, - displayOrder: app.displayOrder, - } }); - return prisma_application_mapper_1.PrismaApplicationMapper.toDomain(created); - } - async update(app) { - const updated = await this.prisma.application.update({ where: { id: app.id }, data: { - code: app.code, - name: app.name, - description: app.description, - icon: app.icon, - url: app.url, - applicationsClaim: app.applicationsClaim, - isActive: app.isActive, - displayOrder: app.displayOrder, - } }); - return prisma_application_mapper_1.PrismaApplicationMapper.toDomain(updated); - } - async delete(appId) { - await this.prisma.application.delete({ where: { id: appId } }); - } -}; -exports.PrismaApplicationRepository = PrismaApplicationRepository; -exports.PrismaApplicationRepository = PrismaApplicationRepository = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [prisma_service_1.PrismaService]) -], PrismaApplicationRepository); -//# sourceMappingURL=prisma-application.repository.js.map \ No newline at end of file diff --git a/dist/modules/application/infrastructure/repositories/prisma-application.repository.js.map b/dist/modules/application/infrastructure/repositories/prisma-application.repository.js.map deleted file mode 100644 index 87825ed..0000000 --- a/dist/modules/application/infrastructure/repositories/prisma-application.repository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"prisma-application.repository.js","sourceRoot":"","sources":["../../../../../src/modules/application/infrastructure/repositories/prisma-application.repository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/application/presentation/controllers/applications.controller.js b/dist/modules/application/presentation/controllers/applications.controller.js deleted file mode 100644 index 92d3eb3..0000000 --- a/dist/modules/application/presentation/controllers/applications.controller.js +++ /dev/null @@ -1,136 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var __param = (this && this.__param) || function (paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ApplicationsController = void 0; -const common_1 = require("@nestjs/common"); -const swagger_1 = require("@nestjs/swagger"); -const jwt_auth_guard_1 = require("../../../../core/auth/guards/jwt-auth.guard"); -const current_user_guard_1 = require("../../../identity/presentation/guards/current-user.guard"); -const get_applications_handler_1 = require("../../application/handlers/get-applications.handler"); -const get_application_handler_1 = require("../../application/handlers/get-application.handler"); -const create_application_handler_1 = require("../../application/handlers/create-application.handler"); -const update_application_handler_1 = require("../../application/handlers/update-application.handler"); -const delete_application_handler_1 = require("../../application/handlers/delete-application.handler"); -const get_me_applications_handler_1 = require("../../application/handlers/get-me-applications.handler"); -let ApplicationsController = class ApplicationsController { - getApplicationsHandler; - getApplicationHandler; - createApplicationHandler; - updateApplicationHandler; - deleteApplicationHandler; - getMeApplicationsHandler; - constructor(getApplicationsHandler, getApplicationHandler, createApplicationHandler, updateApplicationHandler, deleteApplicationHandler, getMeApplicationsHandler) { - this.getApplicationsHandler = getApplicationsHandler; - this.getApplicationHandler = getApplicationHandler; - this.createApplicationHandler = createApplicationHandler; - this.updateApplicationHandler = updateApplicationHandler; - this.deleteApplicationHandler = deleteApplicationHandler; - this.getMeApplicationsHandler = getMeApplicationsHandler; - } - async findAll(query) { - const res = await this.getApplicationsHandler.execute({ page: query.page, limit: query.limit, search: query.search }); - return { success: true, data: res.data.map(a => a.toResponse ? a.toResponse() : a), meta: { total: res.total } }; - } - async findOne(id) { - const app = await this.getApplicationHandler.execute(id); - return { success: true, data: app.toResponse(), meta: {} }; - } - async create(body) { - const created = await this.createApplicationHandler.execute(body); - return { success: true, data: created.toResponse(), meta: {} }; - } - async update(id, body) { - const updated = await this.updateApplicationHandler.execute(id, body); - return { success: true, data: updated.toResponse(), meta: {} }; - } - async remove(id) { - await this.deleteApplicationHandler.execute(id); - return { success: true, data: null, meta: {} }; - } - // Dashboard endpoint - async me(req) { - const ctx = req.raylabContext; - const identity = ctx && ctx.identity ? ctx.identity : {}; - const applicationsClaimList = identity.applications || []; - const res = await this.getMeApplicationsHandler.execute(applicationsClaimList); - return { success: true, data: res.data.map(a => a.toResponse ? a.toResponse() : a), meta: { total: res.total } }; - } -}; -exports.ApplicationsController = ApplicationsController; -__decorate([ - (0, common_1.Get)('applications'), - (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard), - (0, swagger_1.ApiOperation)({ summary: 'List applications' }), - __param(0, (0, common_1.Query)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], ApplicationsController.prototype, "findAll", null); -__decorate([ - (0, common_1.Get)('applications/:id'), - (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard), - (0, swagger_1.ApiOperation)({ summary: 'Get application by id' }), - __param(0, (0, common_1.Param)('id')), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], ApplicationsController.prototype, "findOne", null); -__decorate([ - (0, common_1.Post)('applications'), - (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard), - (0, swagger_1.ApiOperation)({ summary: 'Create application' }), - __param(0, (0, common_1.Body)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], ApplicationsController.prototype, "create", null); -__decorate([ - (0, common_1.Patch)('applications/:id'), - (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard), - (0, swagger_1.ApiOperation)({ summary: 'Update application' }), - __param(0, (0, common_1.Param)('id')), - __param(1, (0, common_1.Body)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String, Object]), - __metadata("design:returntype", Promise) -], ApplicationsController.prototype, "update", null); -__decorate([ - (0, common_1.Delete)('applications/:id'), - (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard), - (0, swagger_1.ApiOperation)({ summary: 'Delete application' }), - __param(0, (0, common_1.Param)('id')), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], ApplicationsController.prototype, "remove", null); -__decorate([ - (0, common_1.Get)('me/applications'), - (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard), - (0, swagger_1.ApiOperation)({ summary: "Get current user's applications (filtered by Authentik claims)" }), - __param(0, (0, common_1.Req)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], ApplicationsController.prototype, "me", null); -exports.ApplicationsController = ApplicationsController = __decorate([ - (0, swagger_1.ApiTags)('Applications'), - (0, common_1.Controller)(), - __metadata("design:paramtypes", [get_applications_handler_1.GetApplicationsHandler, - get_application_handler_1.GetApplicationHandler, - create_application_handler_1.CreateApplicationHandler, - update_application_handler_1.UpdateApplicationHandler, - delete_application_handler_1.DeleteApplicationHandler, - get_me_applications_handler_1.GetMeApplicationsHandler]) -], ApplicationsController); -//# sourceMappingURL=applications.controller.js.map \ No newline at end of file diff --git a/dist/modules/application/presentation/controllers/applications.controller.js.map b/dist/modules/application/presentation/controllers/applications.controller.js.map deleted file mode 100644 index 29a4e4a..0000000 --- a/dist/modules/application/presentation/controllers/applications.controller.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"applications.controller.js","sourceRoot":"","sources":["../../../../../src/modules/application/presentation/controllers/applications.controller.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/audit/audit.event-handler.js b/dist/modules/audit/audit.event-handler.js deleted file mode 100644 index ad7e864..0000000 --- a/dist/modules/audit/audit.event-handler.js +++ /dev/null @@ -1,65 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var AuditEventHandler_1; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AuditEventHandler = void 0; -const common_1 = require("@nestjs/common"); -const event_bus_service_1 = require("../../core/event-bus/event-bus.service"); -const audit_service_1 = require("./audit.service"); -let AuditEventHandler = AuditEventHandler_1 = class AuditEventHandler { - events; - auditService; - logger = new common_1.Logger(AuditEventHandler_1.name); - constructor(events, auditService) { - this.events = events; - this.auditService = auditService; - this.events.subscribe('RolesSynchronized', (e) => this.handleRolesSynchronized(e)); - this.events.subscribe('UserAuthenticated', (e) => this.handleUserAuthenticated(e)); - this.events.subscribe('RoleCreated', (e) => this.handleGeneric(e)); - this.events.subscribe('RoleUpdated', (e) => this.handleGeneric(e)); - this.events.subscribe('RoleDeleted', (e) => this.handleGeneric(e)); - this.events.subscribe('PermissionCreated', (e) => this.handleGeneric(e)); - this.events.subscribe('PermissionUpdated', (e) => this.handleGeneric(e)); - this.events.subscribe('PermissionDeleted', (e) => this.handleGeneric(e)); - this.events.subscribe('UserActivated', (e) => this.handleGeneric(e)); - this.events.subscribe('UserDeactivated', (e) => this.handleGeneric(e)); - } - async handleRolesSynchronized(event) { - try { - await this.auditService.createFromEvent(event); - } - catch (e) { - this.logger.error('Audit handler failed', e); - } - } - async handleUserAuthenticated(event) { - try { - await this.auditService.createFromEvent(event); - } - catch (e) { - this.logger.error('Audit handler failed', e); - } - } - async handleGeneric(event) { - try { - await this.auditService.createFromEvent(event); - } - catch (e) { - this.logger.error('Audit handler failed', e); - } - } -}; -exports.AuditEventHandler = AuditEventHandler; -exports.AuditEventHandler = AuditEventHandler = AuditEventHandler_1 = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [event_bus_service_1.EventBus, audit_service_1.AuditService]) -], AuditEventHandler); -//# sourceMappingURL=audit.event-handler.js.map \ No newline at end of file diff --git a/dist/modules/audit/audit.event-handler.js.map b/dist/modules/audit/audit.event-handler.js.map deleted file mode 100644 index e0434a4..0000000 --- a/dist/modules/audit/audit.event-handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"audit.event-handler.js","sourceRoot":"","sources":["../../../src/modules/audit/audit.event-handler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/audit/audit.module.js b/dist/modules/audit/audit.module.js deleted file mode 100644 index efa72d9..0000000 --- a/dist/modules/audit/audit.module.js +++ /dev/null @@ -1,24 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AuditModule = void 0; -const common_1 = require("@nestjs/common"); -const audit_service_1 = require("./audit.service"); -const audit_event_handler_1 = require("./audit.event-handler"); -const prisma_service_1 = require("../../shared/prisma.service"); -const event_bus_service_1 = require("../../core/event-bus/event-bus.service"); -let AuditModule = class AuditModule { -}; -exports.AuditModule = AuditModule; -exports.AuditModule = AuditModule = __decorate([ - (0, common_1.Module)({ - providers: [audit_service_1.AuditService, audit_event_handler_1.AuditEventHandler, prisma_service_1.PrismaService, event_bus_service_1.EventBus], - exports: [audit_service_1.AuditService], - }) -], AuditModule); -//# sourceMappingURL=audit.module.js.map \ No newline at end of file diff --git a/dist/modules/audit/audit.module.js.map b/dist/modules/audit/audit.module.js.map deleted file mode 100644 index e4509ad..0000000 --- a/dist/modules/audit/audit.module.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"audit.module.js","sourceRoot":"","sources":["../../../src/modules/audit/audit.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,mDAA+C;AAC/C,+DAA0D;AAC1D,gEAA4D;AAC5D,8EAAkE;AAM3D,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAG,CAAA;AAAd,kCAAW;sBAAX,WAAW;IAJvB,IAAA,eAAM,EAAC;QACN,SAAS,EAAE,CAAC,4BAAY,EAAE,uCAAiB,EAAE,8BAAa,EAAE,4BAAQ,CAAC;QACrE,OAAO,EAAE,CAAC,4BAAY,CAAC;KACxB,CAAC;GACW,WAAW,CAAG"} \ No newline at end of file diff --git a/dist/modules/audit/audit.service.js b/dist/modules/audit/audit.service.js deleted file mode 100644 index fbe5659..0000000 --- a/dist/modules/audit/audit.service.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var AuditService_1; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AuditService = void 0; -const common_1 = require("@nestjs/common"); -const prisma_service_1 = require("../../shared/prisma.service"); -let AuditService = AuditService_1 = class AuditService { - prisma; - logger = new common_1.Logger(AuditService_1.name); - constructor(prisma) { - this.prisma = prisma; - } - async createFromEvent(event) { - try { - // Map standard events to AuditLog entries - await this.prisma.auditLog.create({ - data: { - userId: event.payload?.userId || null, - action: event.type, - resource: event.payload?.resource || null, - resourceId: event.payload?.resourceId || null, - details: event.payload, - createdAt: new Date(event.timestamp), - }, - }); - } - catch (e) { - this.logger.error('Failed to write audit log', e); - } - } -}; -exports.AuditService = AuditService; -exports.AuditService = AuditService = AuditService_1 = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [prisma_service_1.PrismaService]) -], AuditService); -//# sourceMappingURL=audit.service.js.map \ No newline at end of file diff --git a/dist/modules/audit/audit.service.js.map b/dist/modules/audit/audit.service.js.map deleted file mode 100644 index 3ee3df5..0000000 --- a/dist/modules/audit/audit.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"audit.service.js","sourceRoot":"","sources":["../../../src/modules/audit/audit.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAoD;AACpD,gEAA4D;AAIrD,IAAM,YAAY,oBAAlB,MAAM,YAAY;IAGM;IAFZ,MAAM,GAAG,IAAI,eAAM,CAAC,cAAY,CAAC,IAAI,CAAC,CAAC;IAExD,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,KAAK,CAAC,eAAe,CAAC,KAAyB;QAC7C,IAAI,CAAC;YACH,0CAA0C;YAC1C,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAChC,IAAI,EAAE;oBACJ,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,IAAI,IAAI;oBACrC,MAAM,EAAE,KAAK,CAAC,IAAI;oBAClB,QAAQ,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,IAAI,IAAI;oBACzC,UAAU,EAAE,KAAK,CAAC,OAAO,EAAE,UAAU,IAAI,IAAI;oBAC7C,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,SAAS,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;iBACrC;aACF,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,CAAQ,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;CACF,CAAA;AAtBY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;qCAI0B,8BAAa;GAHvC,YAAY,CAsBxB"} \ No newline at end of file diff --git a/dist/modules/auth/auth.controller.js b/dist/modules/auth/auth.controller.js deleted file mode 100644 index 3052f6a..0000000 --- a/dist/modules/auth/auth.controller.js +++ /dev/null @@ -1,110 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var __param = (this && this.__param) || function (paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AuthController = void 0; -const common_1 = require("@nestjs/common"); -const swagger_1 = require("@nestjs/swagger"); -const auth_service_1 = require("./auth.service"); -let AuthController = class AuthController { - authService; - constructor(authService) { - this.authService = authService; - } - async login(returnTo, res) { - const redirect = await this.authService.createAuthorizationRedirect(returnTo); - return res.redirect(302, redirect); - } - async callback(code, state, res) { - const result = await this.authService.handleCallback(code, state); - // set cookies - const cookieOptions = { - httpOnly: true, - secure: process.env.NODE_ENV === 'production', - sameSite: 'lax', - path: '/', - }; - // access token cookie (internal JWT) - res.cookie('raylab_jwt', result.accessToken, { ...cookieOptions, maxAge: result.expiresIn * 1000 }); - // refresh token cookie - res.cookie('raylab_refresh', result.refreshToken, { ...cookieOptions, maxAge: result.refreshTtl * 1000 }); - return res.redirect(302, result.returnTo || '/'); - } - async logout(req, res) { - const refreshToken = req.cookies?.raylab_refresh || req.body?.refreshToken; - const redirect = await this.authService.logout(refreshToken); - return res.redirect(302, redirect); - } - async refresh(req) { - const refreshToken = req.cookies?.raylab_refresh || req.body?.refreshToken; - const result = await this.authService.refresh(refreshToken); - return { success: true, data: result }; - } - async me(req) { - const token = (req.cookies?.raylab_jwt) || (req.headers.authorization && req.headers.authorization.replace(/^Bearer\s+/i, '')); - const user = await this.authService.me(token); - return { success: true, data: user }; - } -}; -exports.AuthController = AuthController; -__decorate([ - (0, common_1.Get)('login'), - (0, swagger_1.ApiOperation)({ summary: 'Start Authorization Code + PKCE login (redirect to Identity Provider)' }), - __param(0, (0, common_1.Query)('returnTo')), - __param(1, (0, common_1.Res)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object, Object]), - __metadata("design:returntype", Promise) -], AuthController.prototype, "login", null); -__decorate([ - (0, common_1.Get)('callback'), - (0, swagger_1.ApiOperation)({ summary: 'OIDC callback endpoint' }), - __param(0, (0, common_1.Query)('code')), - __param(1, (0, common_1.Query)('state')), - __param(2, (0, common_1.Res)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String, String, Object]), - __metadata("design:returntype", Promise) -], AuthController.prototype, "callback", null); -__decorate([ - (0, common_1.Post)('logout'), - (0, common_1.HttpCode)(common_1.HttpStatus.OK), - (0, swagger_1.ApiOperation)({ summary: 'Logout (invalidate internal session and redirect to identity provider logout)' }), - __param(0, (0, common_1.Req)()), - __param(1, (0, common_1.Res)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object, Object]), - __metadata("design:returntype", Promise) -], AuthController.prototype, "logout", null); -__decorate([ - (0, common_1.Post)('refresh'), - (0, swagger_1.ApiOperation)({ summary: 'Refresh internal JWT using internal refresh token' }), - __param(0, (0, common_1.Req)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], AuthController.prototype, "refresh", null); -__decorate([ - (0, common_1.Get)('me'), - (0, swagger_1.ApiOperation)({ summary: 'Get current user from internal JWT (cookie or Authorization header)' }), - __param(0, (0, common_1.Req)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], AuthController.prototype, "me", null); -exports.AuthController = AuthController = __decorate([ - (0, swagger_1.ApiTags)('Auth'), - (0, common_1.Controller)('auth'), - __metadata("design:paramtypes", [auth_service_1.AuthService]) -], AuthController); -//# sourceMappingURL=auth.controller.js.map \ No newline at end of file diff --git a/dist/modules/auth/auth.controller.js.map b/dist/modules/auth/auth.controller.js.map deleted file mode 100644 index 33fa0bb..0000000 --- a/dist/modules/auth/auth.controller.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"auth.controller.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AACA,2CAAoG;AACpG,6CAAwD;AACxD,iDAA6C;AAKtC,IAAM,cAAc,GAApB,MAAM,cAAc;IACI;IAA7B,YAA6B,WAAwB;QAAxB,gBAAW,GAAX,WAAW,CAAa;IAAG,CAAC;IAInD,AAAN,KAAK,CAAC,KAAK,CAAoB,QAA4B,EAAS,GAAa;QAC/E,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,2BAA2B,CAAC,QAAQ,CAAC,CAAC;QAC9E,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;IAIK,AAAN,KAAK,CAAC,QAAQ,CAAgB,IAAY,EAAkB,KAAa,EAAS,GAAa;QAC7F,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAElE,cAAc;QACd,MAAM,aAAa,GAAQ;YACzB,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;YAC7C,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,GAAG;SACV,CAAC;QAEF,qCAAqC;QACrC,GAAG,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,EAAE,GAAG,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,SAAS,GAAG,IAAI,EAAE,CAAC,CAAC;QAEpG,uBAAuB;QACvB,GAAG,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,YAAY,EAAE,EAAE,GAAG,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,UAAU,GAAG,IAAI,EAAE,CAAC,CAAC;QAE1G,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,QAAQ,IAAI,GAAG,CAAC,CAAC;IACnD,CAAC;IAKK,AAAN,KAAK,CAAC,MAAM,CAAQ,GAAY,EAAS,GAAa;QACpD,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,EAAE,cAAc,IAAI,GAAG,CAAC,IAAI,EAAE,YAAY,CAAC;QAC3E,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC7D,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;IAIK,AAAN,KAAK,CAAC,OAAO,CAAQ,GAAY;QAC/B,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,EAAE,cAAc,IAAI,GAAG,CAAC,IAAI,EAAE,YAAY,CAAC;QAC3E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC5D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACzC,CAAC;IAIK,AAAN,KAAK,CAAC,EAAE,CAAQ,GAAY;QAC1B,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,IAAK,GAAG,CAAC,OAAO,CAAC,aAAwB,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC;QAC3I,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QAC9C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACvC,CAAC;CACF,CAAA;AAxDY,wCAAc;AAKnB;IAFL,IAAA,YAAG,EAAC,OAAO,CAAC;IACZ,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,uEAAuE,EAAE,CAAC;IACtF,WAAA,IAAA,cAAK,EAAC,UAAU,CAAC,CAAA;IAAgC,WAAA,IAAA,YAAG,GAAE,CAAA;;;;2CAGlE;AAIK;IAFL,IAAA,YAAG,EAAC,UAAU,CAAC;IACf,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,WAAA,IAAA,cAAK,EAAC,MAAM,CAAC,CAAA;IAAgB,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;IAAiB,WAAA,IAAA,YAAG,GAAE,CAAA;;;;8CAkBhF;AAKK;IAHL,IAAA,aAAI,EAAC,QAAQ,CAAC;IACd,IAAA,iBAAQ,EAAC,mBAAU,CAAC,EAAE,CAAC;IACvB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,+EAA+E,EAAE,CAAC;IAC7F,WAAA,IAAA,YAAG,GAAE,CAAA;IAAgB,WAAA,IAAA,YAAG,GAAE,CAAA;;;;4CAIvC;AAIK;IAFL,IAAA,aAAI,EAAC,SAAS,CAAC;IACf,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,mDAAmD,EAAE,CAAC;IAChE,WAAA,IAAA,YAAG,GAAE,CAAA;;;;6CAInB;AAIK;IAFL,IAAA,YAAG,EAAC,IAAI,CAAC;IACT,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,qEAAqE,EAAE,CAAC;IACvF,WAAA,IAAA,YAAG,GAAE,CAAA;;;;wCAId;yBAvDU,cAAc;IAF1B,IAAA,iBAAO,EAAC,MAAM,CAAC;IACf,IAAA,mBAAU,EAAC,MAAM,CAAC;qCAEyB,0BAAW;GAD1C,cAAc,CAwD1B"} \ No newline at end of file diff --git a/dist/modules/auth/auth.module.js b/dist/modules/auth/auth.module.js deleted file mode 100644 index a07a073..0000000 --- a/dist/modules/auth/auth.module.js +++ /dev/null @@ -1,75 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AuthModule = void 0; -const common_1 = require("@nestjs/common"); -const auth_controller_1 = require("./auth.controller"); -const auth_service_1 = require("./auth.service"); -const config_1 = require("@nestjs/config"); -const jwt_1 = require("@nestjs/jwt"); -const prisma_service_1 = require("../../shared/prisma.service"); -const user_interface_1 = require("../identity/domain/repositories/user.interface"); -const prisma_user_repository_1 = require("../identity/infrastructure/repositories/prisma-user.repository"); -const sync_identity_handler_1 = require("../identity/application/handlers/user/sync-identity.handler"); -const role_interface_1 = require("../identity/domain/repositories/role.interface"); -const prisma_role_repository_1 = require("../identity/infrastructure/repositories/prisma-role.repository"); -const i_auth_config_1 = require("../identity/application/config/i-auth-config"); -const env_auth_config_1 = require("../identity/application/config/env-auth-config"); -const redis_pkce_store_1 = require("./pkce/redis-pkce.store"); -const inmemory_refresh_store_1 = require("./refresh/inmemory-refresh.store"); -const oidc_service_1 = require("./oidc.service"); -const role_sync_service_1 = require("./role-sync.service"); -const redis_service_1 = require("../../shared/redis.service"); -const group_hash_service_1 = require("./group-hash.service"); -const authentication_service_1 = require("./authentication.service"); -const event_bus_service_1 = require("../../core/event-bus/event-bus.service"); -const audit_service_1 = require("../audit/audit.service"); -const authorization_module_1 = require("../authorization/authorization.module"); -let AuthModule = class AuthModule { -}; -exports.AuthModule = AuthModule; -exports.AuthModule = AuthModule = __decorate([ - (0, common_1.Module)({ - imports: [ - config_1.ConfigModule, - authorization_module_1.AuthorizationModule, - jwt_1.JwtModule.registerAsync({ - imports: [config_1.ConfigModule], - useFactory: async (config) => ({ - secret: config.get('RAYLAB_JWT_SECRET') || 'raylab-secret', - signOptions: { algorithm: 'HS256' }, - }), - inject: [config_1.ConfigService], - }), - ], - controllers: [auth_controller_1.AuthController], - providers: [ - auth_service_1.AuthService, - prisma_service_1.PrismaService, - prisma_user_repository_1.PrismaUserRepository, - prisma_role_repository_1.PrismaRoleRepository, - sync_identity_handler_1.SyncIdentityHandler, - { provide: user_interface_1.IUser, useClass: prisma_user_repository_1.PrismaUserRepository }, - { provide: role_interface_1.IRole, useClass: prisma_role_repository_1.PrismaRoleRepository }, - { provide: i_auth_config_1.IAuthConfig, useClass: env_auth_config_1.EnvAuthConfig }, - // In-memory PKCE and Refresh stores (Redis removed) - redis_pkce_store_1.RedisPkceStore, - inmemory_refresh_store_1.InMemoryRefreshStore, - // OIDC & Role Sync - oidc_service_1.OidcService, - role_sync_service_1.RoleSyncService, - group_hash_service_1.GroupHashService, - authentication_service_1.AuthenticationService, - event_bus_service_1.EventBus, - audit_service_1.AuditService, - redis_service_1.RedisService, - ], - exports: [auth_service_1.AuthService, oidc_service_1.OidcService, role_sync_service_1.RoleSyncService, authentication_service_1.AuthenticationService, event_bus_service_1.EventBus], - }) -], AuthModule); -//# sourceMappingURL=auth.module.js.map \ No newline at end of file diff --git a/dist/modules/auth/auth.module.js.map b/dist/modules/auth/auth.module.js.map deleted file mode 100644 index 4cdac68..0000000 --- a/dist/modules/auth/auth.module.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"auth.module.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,uDAAmD;AACnD,iDAA6C;AAC7C,2CAA6D;AAC7D,qCAAwC;AACxC,gEAA4D;AAC5D,mFAAuE;AACvE,2GAAsG;AACtG,uGAAkG;AAClG,mFAAuE;AACvE,2GAAsG;AACtG,gFAA2E;AAC3E,oFAA+E;AAC/E,8DAAyD;AACzD,6EAAwE;AACxE,iDAA6C;AAC7C,2DAAsD;AACtD,8DAA0D;AAC1D,6DAAwD;AACxD,qEAAiE;AACjE,8EAAkE;AAClE,0DAAsD;AACtD,gFAA4E;AA0CrE,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IAvCtB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY;YACZ,0CAAmB;YACnB,eAAS,CAAC,aAAa,CAAC;gBACtB,OAAO,EAAE,CAAC,qBAAY,CAAC;gBACvB,UAAU,EAAE,KAAK,EAAE,MAAqB,EAAE,EAAE,CAAC,CAAC;oBAC5C,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,eAAe;oBAC1D,WAAW,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE;iBACpC,CAAC;gBACF,MAAM,EAAE,CAAC,sBAAa,CAAC;aACxB,CAAC;SACH;QACD,WAAW,EAAE,CAAC,gCAAc,CAAC;QAC7B,SAAS,EAAE;YACT,0BAAW;YACX,8BAAa;YACb,6CAAoB;YACpB,6CAAoB;YACpB,2CAAmB;YACnB,EAAE,OAAO,EAAE,sBAAK,EAAE,QAAQ,EAAE,6CAAoB,EAAE;YAClD,EAAE,OAAO,EAAE,sBAAK,EAAE,QAAQ,EAAE,6CAAoB,EAAE;YAClD,EAAE,OAAO,EAAE,2BAAW,EAAE,QAAQ,EAAE,+BAAa,EAAE;YAEjD,oDAAoD;YACpD,iCAAc;YACd,6CAAoB;YAEpB,mBAAmB;YACnB,0BAAW;YACX,mCAAe;YACf,qCAAgB;YAChB,8CAAqB;YACrB,4BAAQ;YACR,4BAAY;YACZ,4BAAY;SACb;QACD,OAAO,EAAE,CAAC,0BAAW,EAAE,0BAAW,EAAE,mCAAe,EAAE,8CAAqB,EAAE,4BAAQ,CAAC;KACtF,CAAC;GACW,UAAU,CAAG"} \ No newline at end of file diff --git a/dist/modules/auth/auth.service.js b/dist/modules/auth/auth.service.js deleted file mode 100644 index 754d15e..0000000 --- a/dist/modules/auth/auth.service.js +++ /dev/null @@ -1,209 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var __param = (this && this.__param) || function (paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AuthService = void 0; -const common_1 = require("@nestjs/common"); -const config_1 = require("@nestjs/config"); -const jwt_1 = require("@nestjs/jwt"); -const sync_identity_handler_1 = require("../identity/application/handlers/user/sync-identity.handler"); -const user_interface_1 = require("../identity/domain/repositories/user.interface"); -const prisma_service_1 = require("../../shared/prisma.service"); -const redis_pkce_store_1 = require("./pkce/redis-pkce.store"); -const inmemory_refresh_store_1 = require("./refresh/inmemory-refresh.store"); -const openidClient = require('openid-client'); -let AuthService = class AuthService { - config; - jwtService; - prisma; - userRepository; - syncIdentityHandler; - pkceStore; - refreshStore; - constructor(config, jwtService, prisma, userRepository, syncIdentityHandler, pkceStore, refreshStore) { - this.config = config; - this.jwtService = jwtService; - this.prisma = prisma; - this.userRepository = userRepository; - this.syncIdentityHandler = syncIdentityHandler; - this.pkceStore = pkceStore; - this.refreshStore = refreshStore; - } - issuer = null; - client = null; - async getIssuer() { - if (this.issuer) - return this.issuer; - const issuerUrl = this.config.get('AUTHENTIK_ISSUER'); - if (!issuerUrl) - throw new Error('AUTHENTIK_ISSUER not configured'); - this.issuer = await openidClient.Issuer.discover(issuerUrl); - return this.issuer; - } - async getClient() { - if (this.client) - return this.client; - const issuer = await this.getIssuer(); - const clientId = this.config.get('AUTHENTIK_CLIENT_ID'); - const clientSecret = this.config.get('AUTHENTIK_CLIENT_SECRET'); - if (!clientId) - throw new Error('AUTHENTIK_CLIENT_ID not configured'); - this.client = new issuer.Client({ client_id: clientId, client_secret: clientSecret }); - return this.client; - } - async createAuthorizationRedirect(returnTo) { - const client = await this.getClient(); - const redirectUri = this.config.get('AUTH_CALLBACK_URL') || `${this.config.get('APP_URL') || ''}/auth/callback`; - const state = require('crypto').randomUUID(); - const code_verifier = openidClient.generators.codeVerifier(); - const code_challenge = await openidClient.generators.codeChallenge(code_verifier); - const nonce = openidClient.generators.nonce(); - await this.pkceStore.save(state, { code_verifier, nonce, returnTo }, 300); - const url = client.authorizationUrl({ - redirect_uri: redirectUri, - scope: this.config.get('AUTHENTIK_DEFAULT_SCOPE') || 'openid email profile', - response_type: 'code', - code_challenge, - code_challenge_method: 'S256', - state, - nonce, - }); - return url; - } - async handleCallback(code, state) { - const client = await this.getClient(); - const pkce = await this.pkceStore.get(state); - if (!pkce) - throw new common_1.UnauthorizedException('Invalid or expired state'); - // remove one-time state - await this.pkceStore.remove(state); - const redirectUri = this.config.get('AUTH_CALLBACK_URL') || `${this.config.get('APP_URL') || ''}/auth/callback`; - // exchange code - const tokenSet = await client.callback(redirectUri, { code, state }, { code_verifier: pkce.code_verifier, nonce: pkce.nonce }); - // verify id_token and get claims - const claims = tokenSet.claims(); - // fetch userinfo if available - let userInfo = null; - try { - if (tokenSet.access_token && client.userinfo) { - userInfo = await client.userinfo(tokenSet.access_token); - } - } - catch (e) { - // ignore - } - const identity = { - sub: userInfo?.sub || claims.sub || null, - preferred_username: userInfo?.preferred_username || userInfo?.username || userInfo?.email || claims.preferred_username || claims.email, - email: userInfo?.email || claims.email, - raw: { tokenSet, userInfo, claims }, - }; - // Sync identity to local user (create if needed) - const domainUser = await this.syncIdentityHandler.execute(identity); - // ensure active/not deleted - if (!domainUser.isActive) - throw new common_1.UnauthorizedException('User is not active'); - if (domainUser.deletedAt) - throw new common_1.UnauthorizedException('User is deleted'); - // create internal JWT - const jwtPayload = { - sub: domainUser.id, - preferred_username: domainUser.username, - email: domainUser.email, - }; - const expiresIn = Number(this.config.get('RAYLAB_JWT_EXPIRES_IN') || 3600); - const access = this.jwtService.sign(jwtPayload, { expiresIn }); - // create internal refresh token - const refreshToken = require('crypto').randomUUID(); - const refreshTtl = Number(this.config.get('RAYLAB_REFRESH_EXPIRES_IN') || 30 * 24 * 3600); // default 30 days - await this.refreshStore.set(refreshToken, { userId: domainUser.id }, refreshTtl); - return { - accessToken: access, - refreshToken, - expiresIn, - refreshTtl, - user: { - id: domainUser.id, - username: domainUser.username, - email: domainUser.email, - roles: domainUser.roles || [], - }, - returnTo: pkce.returnTo, - }; - } - async refresh(refreshToken) { - if (!refreshToken) - throw new common_1.UnauthorizedException('Missing refresh token'); - const data = await this.refreshStore.get(refreshToken); - if (!data) - throw new common_1.UnauthorizedException('Invalid refresh token'); - const userId = data.userId; - // load user - const domainUser = await this.userRepository.getById(userId); - if (!domainUser) - throw new common_1.UnauthorizedException('User not found'); - if (!domainUser.isActive) - throw new common_1.UnauthorizedException('User is not active'); - // rotate refresh token - await this.refreshStore.del(refreshToken); - const newRefresh = require('crypto').randomUUID(); - const refreshTtl = Number(this.config.get('RAYLAB_REFRESH_EXPIRES_IN') || 30 * 24 * 3600); - await this.refreshStore.set(newRefresh, { userId }, refreshTtl); - const expiresIn = Number(this.config.get('RAYLAB_JWT_EXPIRES_IN') || 3600); - const access = this.jwtService.sign({ sub: domainUser.id, preferred_username: domainUser.username, email: domainUser.email }, { expiresIn }); - return { accessToken: access, refreshToken: newRefresh, expiresIn, refreshTtl }; - } - async logout(refreshToken) { - if (refreshToken) { - await this.refreshStore.del(refreshToken); - } - const issuer = await this.getIssuer(); - const endSession = issuer.metadata.end_session_endpoint; - const postLogout = this.config.get('APP_URL') || '/'; - if (endSession) { - // Redirect to identity provider logout - const url = new URL(endSession); - if (postLogout) - url.searchParams.set('post_logout_redirect_uri', postLogout); - return url.toString(); - } - return postLogout; - } - async me(token) { - if (!token) - throw new common_1.UnauthorizedException('Missing token'); - try { - const payload = this.jwtService.verify(token); - const user = await this.userRepository.getById(payload.sub); - if (!user) - throw new common_1.UnauthorizedException('User not found'); - return { id: user.id, username: user.username, email: user.email, roles: user.roles || [] }; - } - catch (e) { - throw new common_1.UnauthorizedException('Invalid token'); - } - } -}; -exports.AuthService = AuthService; -exports.AuthService = AuthService = __decorate([ - (0, common_1.Injectable)(), - __param(3, (0, common_1.Inject)(user_interface_1.IUser)), - __metadata("design:paramtypes", [config_1.ConfigService, - jwt_1.JwtService, - prisma_service_1.PrismaService, - user_interface_1.IUser, - sync_identity_handler_1.SyncIdentityHandler, - redis_pkce_store_1.RedisPkceStore, - inmemory_refresh_store_1.InMemoryRefreshStore]) -], AuthService); -//# sourceMappingURL=auth.service.js.map \ No newline at end of file diff --git a/dist/modules/auth/auth.service.js.map b/dist/modules/auth/auth.service.js.map deleted file mode 100644 index 9581060..0000000 --- a/dist/modules/auth/auth.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/auth/authentication.service.js b/dist/modules/auth/authentication.service.js deleted file mode 100644 index e37a0c8..0000000 --- a/dist/modules/auth/authentication.service.js +++ /dev/null @@ -1,70 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var AuthenticationService_1; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AuthenticationService = void 0; -const common_1 = require("@nestjs/common"); -const oidc_service_1 = require("./oidc.service"); -const prisma_service_1 = require("../../shared/prisma.service"); -const role_sync_service_1 = require("./role-sync.service"); -const authorization_service_1 = require("../authorization/authorization.service"); -const event_bus_service_1 = require("../../core/event-bus/event-bus.service"); -let AuthenticationService = AuthenticationService_1 = class AuthenticationService { - oidc; - prisma; - roleSync; - authorization; - events; - logger = new common_1.Logger(AuthenticationService_1.name); - constructor(oidc, prisma, roleSync, authorization, events) { - this.oidc = oidc; - this.prisma = prisma; - this.roleSync = roleSync; - this.authorization = authorization; - this.events = events; - } - async authenticate(bearerToken) { - // Validate token (signature/iss/aud/exp) - const claims = await this.oidc.verifyToken(bearerToken); - const sub = claims.sub; - if (!sub) - throw new Error('Invalid token: missing sub'); - // Resolve identity - const identity = { sub, email: claims.email, preferred_username: claims.preferred_username, raw: claims }; - // Find or create user (materialize) - let user = await this.prisma.user.findUnique({ where: { authentikId: sub } }); - if (!user) { - user = await this.prisma.user.create({ data: { authentikId: sub, username: identity.preferred_username || identity.email || sub, email: identity.email || null } }); - } - // Synchronize roles - const groups = Array.isArray(claims.groups) ? claims.groups : []; - await this.roleSync.syncUserRolesFromAuthentik(user.id, groups); - // Load permissions - const permissions = await this.authorization.getUserPermissions(user.id); - // Build request context - const rolesRows = await this.prisma.userRole.findMany({ where: { userId: user.id } }); - const roles = rolesRows.map((r) => r.roleId); - const ctx = { user, identity, roles, permissions }; - // Publish domain event (legacy string event name expected by some subscribers/tests) - this.events.publish('user.authenticated', { userId: user.id, identity }); - return ctx; - } -}; -exports.AuthenticationService = AuthenticationService; -exports.AuthenticationService = AuthenticationService = AuthenticationService_1 = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [oidc_service_1.OidcService, - prisma_service_1.PrismaService, - role_sync_service_1.RoleSyncService, - authorization_service_1.AuthorizationService, - event_bus_service_1.EventBus]) -], AuthenticationService); -//# sourceMappingURL=authentication.service.js.map \ No newline at end of file diff --git a/dist/modules/auth/authentication.service.js.map b/dist/modules/auth/authentication.service.js.map deleted file mode 100644 index ad8598e..0000000 --- a/dist/modules/auth/authentication.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"authentication.service.js","sourceRoot":"","sources":["../../../src/modules/auth/authentication.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/auth/dto/login.dto.js b/dist/modules/auth/dto/login.dto.js deleted file mode 100644 index 72ae07d..0000000 --- a/dist/modules/auth/dto/login.dto.js +++ /dev/null @@ -1,4 +0,0 @@ -"use strict"; -// Login DTO removed. Password grant has been removed in favor of Authorization Code + PKCE flow. -// Formerly contained username/password properties. -//# sourceMappingURL=login.dto.js.map \ No newline at end of file diff --git a/dist/modules/auth/dto/login.dto.js.map b/dist/modules/auth/dto/login.dto.js.map deleted file mode 100644 index 1cf14bb..0000000 --- a/dist/modules/auth/dto/login.dto.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"login.dto.js","sourceRoot":"","sources":["../../../../src/modules/auth/dto/login.dto.ts"],"names":[],"mappings":";AAAA,iGAAiG;AACjG,mDAAmD"} \ No newline at end of file diff --git a/dist/modules/auth/group-hash.service.js b/dist/modules/auth/group-hash.service.js deleted file mode 100644 index 8b2219c..0000000 --- a/dist/modules/auth/group-hash.service.js +++ /dev/null @@ -1,56 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GroupHashService = void 0; -const common_1 = require("@nestjs/common"); -const crypto = __importStar(require("crypto")); -let GroupHashService = class GroupHashService { - compute(groups) { - const sorted = (groups || []).slice().sort(); - const data = sorted.join(','); - return crypto.createHash('sha256').update(data, 'utf8').digest('hex'); - } -}; -exports.GroupHashService = GroupHashService; -exports.GroupHashService = GroupHashService = __decorate([ - (0, common_1.Injectable)() -], GroupHashService); -//# sourceMappingURL=group-hash.service.js.map \ No newline at end of file diff --git a/dist/modules/auth/group-hash.service.js.map b/dist/modules/auth/group-hash.service.js.map deleted file mode 100644 index c5f2db7..0000000 --- a/dist/modules/auth/group-hash.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"group-hash.service.js","sourceRoot":"","sources":["../../../src/modules/auth/group-hash.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAA4C;AAC5C,+CAAiC;AAG1B,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAC3B,OAAO,CAAC,MAAgB;QACtB,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxE,CAAC;CACF,CAAA;AANY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;GACA,gBAAgB,CAM5B"} \ No newline at end of file diff --git a/dist/modules/auth/guards/oidc.guard.js b/dist/modules/auth/guards/oidc.guard.js deleted file mode 100644 index 06308ac..0000000 --- a/dist/modules/auth/guards/oidc.guard.js +++ /dev/null @@ -1,48 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var OidcGuard_1; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.OidcGuard = void 0; -const common_1 = require("@nestjs/common"); -const oidc_service_1 = require("../oidc.service"); -const authentication_service_1 = require("../authentication.service"); -let OidcGuard = OidcGuard_1 = class OidcGuard { - oidc; - authn; - logger = new common_1.Logger(OidcGuard_1.name); - constructor(oidc, authn) { - this.oidc = oidc; - this.authn = authn; - } - async canActivate(context) { - const req = context.switchToHttp().getRequest(); - const auth = req.headers['authorization'] || req.headers['Authorization']; - if (!auth || typeof auth !== 'string' || !auth.startsWith('Bearer ')) - throw new common_1.UnauthorizedException('Missing bearer token'); - const token = auth.substring(7).trim(); - try { - const ctx = await this.authn.authenticate(token); - // attach context to request under a structured key - req.raylabContext = ctx; - return true; - } - catch (e) { - this.logger.debug('Authentication failed', e.message); - throw new common_1.UnauthorizedException('Invalid token or authentication failed'); - } - } -}; -exports.OidcGuard = OidcGuard; -exports.OidcGuard = OidcGuard = OidcGuard_1 = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [oidc_service_1.OidcService, authentication_service_1.AuthenticationService]) -], OidcGuard); -//# sourceMappingURL=oidc.guard.js.map \ No newline at end of file diff --git a/dist/modules/auth/guards/oidc.guard.js.map b/dist/modules/auth/guards/oidc.guard.js.map deleted file mode 100644 index c66917b..0000000 --- a/dist/modules/auth/guards/oidc.guard.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"oidc.guard.js","sourceRoot":"","sources":["../../../../src/modules/auth/guards/oidc.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAkH;AAClH,kDAA8C;AAC9C,sEAAkE;AAG3D,IAAM,SAAS,iBAAf,MAAM,SAAS;IAES;IAAoC;IADhD,MAAM,GAAG,IAAI,eAAM,CAAC,WAAS,CAAC,IAAI,CAAC,CAAC;IACrD,YAA6B,IAAiB,EAAmB,KAA4B;QAAhE,SAAI,GAAJ,IAAI,CAAa;QAAmB,UAAK,GAAL,KAAK,CAAuB;IAAG,CAAC;IAEjG,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,MAAM,IAAI,8BAAqB,CAAC,sBAAsB,CAAC,CAAC;QAC9H,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAEvC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YACjD,mDAAmD;YACnD,GAAG,CAAC,aAAa,GAAG,GAAG,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAG,CAAS,CAAC,OAAO,CAAC,CAAC;YAC/D,MAAM,IAAI,8BAAqB,CAAC,wCAAwC,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;CACF,CAAA;AApBY,8BAAS;oBAAT,SAAS;IADrB,IAAA,mBAAU,GAAE;qCAGwB,0BAAW,EAA0B,8CAAqB;GAFlF,SAAS,CAoBrB"} \ No newline at end of file diff --git a/dist/modules/auth/oidc.service.js b/dist/modules/auth/oidc.service.js deleted file mode 100644 index cf2ba55..0000000 --- a/dist/modules/auth/oidc.service.js +++ /dev/null @@ -1,89 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var OidcService_1; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.OidcService = void 0; -const common_1 = require("@nestjs/common"); -const config_1 = require("@nestjs/config"); -let OidcService = OidcService_1 = class OidcService { - config; - jwksUri = null; - issuer; - audience; - logger = new common_1.Logger(OidcService_1.name); - constructor(config) { - this.config = config; - this.issuer = this.config.get('AUTHENTIK_ISSUER') || ''; - this.audience = this.config.get('AUTHENTIK_AUDIENCE') || undefined; - const jwksUri = this.config.get('AUTHENTIK_JWKS_URI'); - if (jwksUri) - this.jwksUri = jwksUri; - else if (this.issuer) - this.jwksUri = `${this.issuer.replace(/\/+$/, '')}/.well-known/jwks.json`; - } - async verifyToken(token) { - if (!this.jwksUri) - throw new Error('JWKS not configured'); - try { - // dynamic import to avoid ESM loading issues in test environment - const jose = await Promise.resolve().then(() => __importStar(require('jose'))); - const jwks = jose.createRemoteJWKSet(new URL(this.jwksUri)); - const { payload } = await jose.jwtVerify(token, jwks, { - issuer: this.issuer || undefined, - audience: this.audience, - }); - return payload; - } - catch (e) { - this.logger.debug('Token verification failed', e.message); - throw e; - } - } -}; -exports.OidcService = OidcService; -exports.OidcService = OidcService = OidcService_1 = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [config_1.ConfigService]) -], OidcService); -//# sourceMappingURL=oidc.service.js.map \ No newline at end of file diff --git a/dist/modules/auth/oidc.service.js.map b/dist/modules/auth/oidc.service.js.map deleted file mode 100644 index d8e5d3b..0000000 --- a/dist/modules/auth/oidc.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"oidc.service.js","sourceRoot":"","sources":["../../../src/modules/auth/oidc.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAoD;AACpD,2CAA+C;AAGxC,IAAM,WAAW,mBAAjB,MAAM,WAAW;IAMO;IALrB,OAAO,GAAkB,IAAI,CAAC;IAC9B,MAAM,CAAS;IACf,QAAQ,CAAgC;IACxC,MAAM,GAAG,IAAI,eAAM,CAAC,aAAW,CAAC,IAAI,CAAC,CAAC;IAE9C,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;QAChD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAS,kBAAkB,CAAC,IAAI,EAAE,CAAC;QAChE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAS,oBAAoB,CAAC,IAAI,SAAS,CAAC;QAC3E,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAS,oBAAoB,CAAC,CAAC;QAC9D,IAAI,OAAO;YAAE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;aAC/B,IAAI,IAAI,CAAC,MAAM;YAAE,IAAI,CAAC,OAAO,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,wBAAwB,CAAC;IAClG,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAa;QAC7B,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QAE1D,IAAI,CAAC;YACH,iEAAiE;YACjE,MAAM,IAAI,GAAG,wDAAa,MAAM,GAAC,CAAC;YAClC,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5D,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE;gBACpD,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS;gBAChC,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACjB,CAAC,CAAC;YAEV,OAAO,OAA8B,CAAC;QACxC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAG,CAAW,CAAC,OAAO,CAAC,CAAC;YACrE,MAAM,CAAC,CAAC;QACV,CAAC;IACH,CAAC;CACF,CAAA;AAhCY,kCAAW;sBAAX,WAAW;IADvB,IAAA,mBAAU,GAAE;qCAO0B,sBAAa;GANvC,WAAW,CAgCvB"} \ No newline at end of file diff --git a/dist/modules/auth/pkce/redis-pkce.store.js b/dist/modules/auth/pkce/redis-pkce.store.js deleted file mode 100644 index fcf24b9..0000000 --- a/dist/modules/auth/pkce/redis-pkce.store.js +++ /dev/null @@ -1,56 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RedisPkceStore = void 0; -const common_1 = require("@nestjs/common"); -let RedisPkceStore = class RedisPkceStore { - // In-memory PKCE store replacing Redis-backed implementation - map = new Map(); - cleanupInterval; - constructor() { - // periodic cleanup - this.cleanupInterval = setInterval(() => { - const now = Date.now(); - for (const [k, v] of this.map.entries()) { - if (v.expiresAt <= now) - this.map.delete(k); - } - }, 60 * 1000); - } - key(state) { return state; } - async save(state, data, ttlSeconds = 300) { - const expiresAt = Date.now() + ttlSeconds * 1000; - this.map.set(this.key(state), { ...data, expiresAt }); - } - async get(state) { - const v = this.map.get(this.key(state)); - if (!v) - return null; - if (v.expiresAt <= Date.now()) { - this.map.delete(this.key(state)); - return null; - } - return { code_verifier: v.code_verifier, nonce: v.nonce, returnTo: v.returnTo }; - } - async remove(state) { - this.map.delete(this.key(state)); - } - onModuleDestroy() { - if (this.cleanupInterval) - clearInterval(this.cleanupInterval); - } -}; -exports.RedisPkceStore = RedisPkceStore; -exports.RedisPkceStore = RedisPkceStore = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", []) -], RedisPkceStore); -//# sourceMappingURL=redis-pkce.store.js.map \ No newline at end of file diff --git a/dist/modules/auth/pkce/redis-pkce.store.js.map b/dist/modules/auth/pkce/redis-pkce.store.js.map deleted file mode 100644 index eadcf0a..0000000 --- a/dist/modules/auth/pkce/redis-pkce.store.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"redis-pkce.store.js","sourceRoot":"","sources":["../../../../src/modules/auth/pkce/redis-pkce.store.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA6D;AAKtD,IAAM,cAAc,GAApB,MAAM,cAAc;IACzB,6DAA6D;IACrD,GAAG,GAAG,IAAI,GAAG,EAAqB,CAAC;IACnC,eAAe,CAAkB;IAEzC;QACE,mBAAmB;QACnB,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE;YACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;gBACxC,IAAI,CAAC,CAAC,SAAS,IAAI,GAAG;oBAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IAChB,CAAC;IAEO,GAAG,CAAC,KAAa,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC;IAE5C,KAAK,CAAC,IAAI,CAAC,KAAa,EAAE,IAAiE,EAAE,UAAU,GAAG,GAAG;QAC3G,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,GAAG,IAAI,CAAC;QACjD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa;QACrB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QACxC,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACpB,IAAI,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QACjF,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;IAClF,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa;QACxB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IACnC,CAAC;IAED,eAAe;QACb,IAAI,IAAI,CAAC,eAAe;YAAE,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAChE,CAAC;CACF,CAAA;AApCY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,mBAAU,GAAE;;GACA,cAAc,CAoC1B"} \ No newline at end of file diff --git a/dist/modules/auth/refresh/inmemory-refresh.store.js b/dist/modules/auth/refresh/inmemory-refresh.store.js deleted file mode 100644 index 5ca1a3f..0000000 --- a/dist/modules/auth/refresh/inmemory-refresh.store.js +++ /dev/null @@ -1,53 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.InMemoryRefreshStore = void 0; -const common_1 = require("@nestjs/common"); -let InMemoryRefreshStore = class InMemoryRefreshStore { - map = new Map(); - cleanupInterval; - constructor() { - this.cleanupInterval = setInterval(() => { - const now = Date.now(); - for (const [k, v] of this.map.entries()) { - if (v.expiresAt <= now) - this.map.delete(k); - } - }, 60 * 1000); - } - async set(token, data, ttlSeconds) { - const expiresAt = Date.now() + ttlSeconds * 1000; - this.map.set(token, { userId: data.userId, expiresAt }); - } - async get(token) { - const v = this.map.get(token); - if (!v) - return null; - if (v.expiresAt <= Date.now()) { - this.map.delete(token); - return null; - } - return { userId: v.userId }; - } - async del(token) { - this.map.delete(token); - } - onModuleDestroy() { - if (this.cleanupInterval) - clearInterval(this.cleanupInterval); - } -}; -exports.InMemoryRefreshStore = InMemoryRefreshStore; -exports.InMemoryRefreshStore = InMemoryRefreshStore = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", []) -], InMemoryRefreshStore); -//# sourceMappingURL=inmemory-refresh.store.js.map \ No newline at end of file diff --git a/dist/modules/auth/refresh/inmemory-refresh.store.js.map b/dist/modules/auth/refresh/inmemory-refresh.store.js.map deleted file mode 100644 index 21759c7..0000000 --- a/dist/modules/auth/refresh/inmemory-refresh.store.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"inmemory-refresh.store.js","sourceRoot":"","sources":["../../../../src/modules/auth/refresh/inmemory-refresh.store.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA6D;AAKtD,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACvB,GAAG,GAAG,IAAI,GAAG,EAAwB,CAAC;IACtC,eAAe,CAAkB;IAEzC;QACE,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE;YACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;gBACxC,IAAI,CAAC,CAAC,SAAS,IAAI,GAAG;oBAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa,EAAE,IAAwB,EAAE,UAAkB;QACnE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,GAAG,IAAI,CAAC;QACjD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa;QACrB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACpB,IAAI,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QACvE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAa;QACrB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,eAAe;QACb,IAAI,IAAI,CAAC,eAAe;YAAE,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAChE,CAAC;CACF,CAAA;AAhCY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;;GACA,oBAAoB,CAgChC"} \ No newline at end of file diff --git a/dist/modules/auth/role-sync.service.js b/dist/modules/auth/role-sync.service.js deleted file mode 100644 index e9db195..0000000 --- a/dist/modules/auth/role-sync.service.js +++ /dev/null @@ -1,88 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var __param = (this && this.__param) || function (paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -}; -var RoleSyncService_1; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RoleSyncService = void 0; -const common_1 = require("@nestjs/common"); -const prisma_service_1 = require("../../shared/prisma.service"); -const event_bus_service_1 = require("../../core/event-bus/event-bus.service"); -const group_hash_service_1 = require("./group-hash.service"); -const authorization_service_1 = require("../authorization/authorization.service"); -let RoleSyncService = RoleSyncService_1 = class RoleSyncService { - prisma; - groupHash; - permissionCache; - events; - logger = new common_1.Logger(RoleSyncService_1.name); - constructor(prisma, groupHash, permissionCache, events) { - this.prisma = prisma; - this.groupHash = groupHash; - this.permissionCache = permissionCache; - this.events = events; - } - computeGroupHash(groups) { - return this.groupHash.compute(groups || []); - } - async mapGroupsToRoleIds(groups) { - if (!groups || groups.length === 0) - return []; - const mappings = await this.prisma.authGroupRoleMapping.findMany({ where: { authGroup: { in: groups } } }); - const roleIds = mappings.map((m) => m.roleId); - return Array.from(new Set(roleIds)); - } - async syncUserRolesFromAuthentik(userId, groups) { - const groupHash = this.computeGroupHash(groups || []); - const user = await this.prisma.user.findUnique({ where: { id: userId } }); - if (!user) - throw new Error('User not found'); - if (user.lastGroupHash === groupHash) { - this.logger.debug('Group hash unchanged, skipping sync'); - return { skipped: true }; - } - const roleIds = await this.mapGroupsToRoleIds(groups || []); - const previousRoleRows = await this.prisma.userRole.findMany({ where: { userId, source: 'AUTHENTIK' }, select: { roleId: true } }); - const previousRoles = previousRoleRows.map((r) => r.roleId); - await this.prisma.$transaction(async (tx) => { - await tx.userRole.deleteMany({ where: { userId: userId, source: 'AUTHENTIK' } }); - if (roleIds.length > 0) { - const createData = roleIds.map((rid) => ({ userId, roleId: rid, source: 'AUTHENTIK' })); - await tx.userRole.createMany({ data: createData, skipDuplicates: true }); - } - await tx.user.update({ where: { id: userId }, data: { lastGroupHash: groupHash } }); - }); - // Invalidate permission cache through abstraction - try { - await this.permissionCache.invalidate(userId); - } - catch (e) { - this.logger.error('Failed to invalidate permission cache', e); - } - // Publish event for audit and other subscribers - this.events.publish({ - id: require('crypto').randomUUID(), - timestamp: new Date().toISOString(), - type: 'RolesSynchronized', - payload: { userId, groups, assignedRoleIds: roleIds, previousRoles }, - }); - return { skipped: false, assignedRoleIds: roleIds }; - } -}; -exports.RoleSyncService = RoleSyncService; -exports.RoleSyncService = RoleSyncService = RoleSyncService_1 = __decorate([ - (0, common_1.Injectable)(), - __param(2, (0, common_1.Inject)(authorization_service_1.PERMISSION_CACHE)), - __metadata("design:paramtypes", [prisma_service_1.PrismaService, - group_hash_service_1.GroupHashService, Object, event_bus_service_1.EventBus]) -], RoleSyncService); -//# sourceMappingURL=role-sync.service.js.map \ No newline at end of file diff --git a/dist/modules/auth/role-sync.service.js.map b/dist/modules/auth/role-sync.service.js.map deleted file mode 100644 index 0865491..0000000 --- a/dist/modules/auth/role-sync.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"role-sync.service.js","sourceRoot":"","sources":["../../../src/modules/auth/role-sync.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/authorization/application/authorization.service.js b/dist/modules/authorization/application/authorization.service.js deleted file mode 100644 index 6ce7dcf..0000000 --- a/dist/modules/authorization/application/authorization.service.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict"; -//# sourceMappingURL=authorization.service.js.map \ No newline at end of file diff --git a/dist/modules/authorization/application/authorization.service.js.map b/dist/modules/authorization/application/authorization.service.js.map deleted file mode 100644 index dea3510..0000000 --- a/dist/modules/authorization/application/authorization.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"authorization.service.js","sourceRoot":"","sources":["../../../../src/modules/authorization/application/authorization.service.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/modules/authorization/authorization.module.js b/dist/modules/authorization/authorization.module.js deleted file mode 100644 index 2458ab5..0000000 --- a/dist/modules/authorization/authorization.module.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AuthorizationModule = void 0; -const common_1 = require("@nestjs/common"); -const authorization_service_1 = require("./authorization.service"); -const prisma_service_1 = require("../../shared/prisma.service"); -const redis_service_1 = require("../../shared/redis.service"); -const redis_permission_cache_service_1 = require("./cache/redis-permission-cache.service"); -let AuthorizationModule = class AuthorizationModule { -}; -exports.AuthorizationModule = AuthorizationModule; -exports.AuthorizationModule = AuthorizationModule = __decorate([ - (0, common_1.Module)({ - providers: [ - authorization_service_1.AuthorizationService, - prisma_service_1.PrismaService, - redis_service_1.RedisService, - { provide: authorization_service_1.PERMISSION_CACHE, useClass: redis_permission_cache_service_1.RedisPermissionCache }, - ], - exports: [authorization_service_1.AuthorizationService, authorization_service_1.PERMISSION_CACHE], - }) -], AuthorizationModule); -//# sourceMappingURL=authorization.module.js.map \ No newline at end of file diff --git a/dist/modules/authorization/authorization.module.js.map b/dist/modules/authorization/authorization.module.js.map deleted file mode 100644 index bd807bc..0000000 --- a/dist/modules/authorization/authorization.module.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"authorization.module.js","sourceRoot":"","sources":["../../../src/modules/authorization/authorization.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,mEAAiF;AACjF,gEAA4D;AAC5D,8DAA0D;AAC1D,2FAA8E;AAWvE,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;CAAG,CAAA;AAAtB,kDAAmB;8BAAnB,mBAAmB;IAT/B,IAAA,eAAM,EAAC;QACN,SAAS,EAAE;YACT,4CAAoB;YACpB,8BAAa;YACb,4BAAY;YACZ,EAAE,OAAO,EAAE,wCAAgB,EAAE,QAAQ,EAAE,qDAAoB,EAAE;SAC9D;QACD,OAAO,EAAE,CAAC,4CAAoB,EAAE,wCAAgB,CAAC;KAClD,CAAC;GACW,mBAAmB,CAAG"} \ No newline at end of file diff --git a/dist/modules/authorization/authorization.service.js b/dist/modules/authorization/authorization.service.js deleted file mode 100644 index 2c33ba6..0000000 --- a/dist/modules/authorization/authorization.service.js +++ /dev/null @@ -1,71 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var __param = (this && this.__param) || function (paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -}; -var AuthorizationService_1; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AuthorizationService = exports.PERMISSION_CACHE = void 0; -const common_1 = require("@nestjs/common"); -const prisma_service_1 = require("../../shared/prisma.service"); -exports.PERMISSION_CACHE = 'PERMISSION_CACHE'; -let AuthorizationService = AuthorizationService_1 = class AuthorizationService { - prisma; - cache; - logger = new common_1.Logger(AuthorizationService_1.name); - constructor(prisma, cache) { - this.prisma = prisma; - this.cache = cache; - } - async getUserPermissions(userId) { - try { - const cached = await this.cache.get(userId); - if (cached) - return cached; - } - catch (e) { - this.logger.debug('PermissionCache get failed', e); - } - const rows = await this.prisma.$queryRaw ` - SELECT p.code as code - FROM "UserRole" ur - JOIN "RolePermission" rp ON rp.role_id = ur.role_id - JOIN "Permission" p ON p.id = rp.permission_id - WHERE ur.user_id = ${userId}`; - const perms = Array.isArray(rows) ? rows.map((r) => r.code) : []; - try { - await this.cache.set(userId, perms); - } - catch (e) { - this.logger.debug('PermissionCache set failed', e); - } - return perms; - } - async hasPermission(userId, permissionCode) { - const perms = await this.getUserPermissions(userId); - return perms.includes(permissionCode); - } - async invalidateUserPermissions(userId) { - try { - await this.cache.invalidate(userId); - } - catch (e) { - this.logger.debug('PermissionCache invalidate failed', e); - } - } -}; -exports.AuthorizationService = AuthorizationService; -exports.AuthorizationService = AuthorizationService = AuthorizationService_1 = __decorate([ - (0, common_1.Injectable)(), - __param(1, (0, common_1.Inject)(exports.PERMISSION_CACHE)), - __metadata("design:paramtypes", [prisma_service_1.PrismaService, Object]) -], AuthorizationService); -//# sourceMappingURL=authorization.service.js.map \ No newline at end of file diff --git a/dist/modules/authorization/authorization.service.js.map b/dist/modules/authorization/authorization.service.js.map deleted file mode 100644 index c548026..0000000 --- a/dist/modules/authorization/authorization.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"authorization.service.js","sourceRoot":"","sources":["../../../src/modules/authorization/authorization.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAA4D;AAC5D,gEAA4D;AAG/C,QAAA,gBAAgB,GAAG,kBAAkB,CAAC;AAG5C,IAAM,oBAAoB,4BAA1B,MAAM,oBAAoB;IAGF;IAAkE;IAF9E,MAAM,GAAG,IAAI,eAAM,CAAC,sBAAoB,CAAC,IAAI,CAAC,CAAC;IAEhE,YAA6B,MAAqB,EAA6C,KAAsB;QAAxF,WAAM,GAAN,MAAM,CAAe;QAA6C,UAAK,GAAL,KAAK,CAAiB;IAAG,CAAC;IAEzH,KAAK,CAAC,kBAAkB,CAAC,MAAc;QACrC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAC;QAC5B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE,CAAQ,CAAC,CAAC;QAC5D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAA;;;;;2BAKjB,MAAM,EAAE,CAAC;QAEhC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAEtE,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE,CAAQ,CAAC,CAAC;QAC5D,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,MAAc,EAAE,cAAsB;QACxD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACpD,OAAO,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,yBAAyB,CAAC,MAAc;QAC5C,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE,CAAQ,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;CACF,CAAA;AA3CY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;IAI0C,WAAA,IAAA,eAAM,EAAC,wBAAgB,CAAC,CAAA;qCAAxC,8BAAa;GAHvC,oBAAoB,CA2ChC"} \ No newline at end of file diff --git a/dist/modules/authorization/cache/permission-cache.interface.js b/dist/modules/authorization/cache/permission-cache.interface.js deleted file mode 100644 index f6e02dc..0000000 --- a/dist/modules/authorization/cache/permission-cache.interface.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=permission-cache.interface.js.map \ No newline at end of file diff --git a/dist/modules/authorization/cache/permission-cache.interface.js.map b/dist/modules/authorization/cache/permission-cache.interface.js.map deleted file mode 100644 index cc80661..0000000 --- a/dist/modules/authorization/cache/permission-cache.interface.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"permission-cache.interface.js","sourceRoot":"","sources":["../../../../src/modules/authorization/cache/permission-cache.interface.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/modules/authorization/cache/redis-permission-cache.service.js b/dist/modules/authorization/cache/redis-permission-cache.service.js deleted file mode 100644 index 1ab7d5a..0000000 --- a/dist/modules/authorization/cache/redis-permission-cache.service.js +++ /dev/null @@ -1,48 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RedisPermissionCache = void 0; -const common_1 = require("@nestjs/common"); -const redis_service_1 = require("../../../shared/redis.service"); -let RedisPermissionCache = class RedisPermissionCache { - redis; - TTL = 60 * 5; - constructor(redis) { - this.redis = redis; - } - key(userId) { - const { CacheKeys } = require('../../../shared/cache-keys'); - return CacheKeys.permission(userId); - } - async get(userId) { - const data = await this.redis.get(this.key(userId)); - if (!data) - return null; - try { - return JSON.parse(data); - } - catch { - return null; - } - } - async set(userId, permissions, ttlSeconds) { - await this.redis.set(this.key(userId), JSON.stringify(permissions), ttlSeconds ?? this.TTL); - } - async invalidate(userId) { - await this.redis.del(this.key(userId)); - } -}; -exports.RedisPermissionCache = RedisPermissionCache; -exports.RedisPermissionCache = RedisPermissionCache = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [redis_service_1.RedisService]) -], RedisPermissionCache); -//# sourceMappingURL=redis-permission-cache.service.js.map \ No newline at end of file diff --git a/dist/modules/authorization/cache/redis-permission-cache.service.js.map b/dist/modules/authorization/cache/redis-permission-cache.service.js.map deleted file mode 100644 index c9a7615..0000000 --- a/dist/modules/authorization/cache/redis-permission-cache.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"redis-permission-cache.service.js","sourceRoot":"","sources":["../../../../src/modules/authorization/cache/redis-permission-cache.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAE5C,iEAA6D;AAGtD,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAGF;IAFZ,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;IAE9B,YAA6B,KAAmB;QAAnB,UAAK,GAAL,KAAK,CAAc;IAAG,CAAC;IAE5C,GAAG,CAAC,MAAc;QACxB,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;QAC5D,OAAO,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,MAAc;QACtB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAa,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,MAAc,EAAE,WAAqB,EAAE,UAAmB;QAClE,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,UAAU,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9F,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAAc;QAC7B,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACzC,CAAC;CACF,CAAA;AA3BY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;qCAIyB,4BAAY;GAHrC,oBAAoB,CA2BhC"} \ No newline at end of file diff --git a/dist/modules/authorization/guards/permission.guard.js b/dist/modules/authorization/guards/permission.guard.js deleted file mode 100644 index 4863b8f..0000000 --- a/dist/modules/authorization/guards/permission.guard.js +++ /dev/null @@ -1,52 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createPermissionGuard = exports.PermissionGuard = void 0; -const common_1 = require("@nestjs/common"); -const authorization_service_1 = require("../authorization.service"); -let PermissionGuard = class PermissionGuard { - authz; - permission; - constructor(authz, permission) { - this.authz = authz; - this.permission = permission; - } - async canActivate(context) { - const req = context.switchToHttp().getRequest(); - const user = req.raylab?.user; - if (!user) - throw new common_1.ForbiddenException('Missing user'); - const allowed = await this.authz.hasPermission(user.id, this.permission); - if (!allowed) - throw new common_1.ForbiddenException('Forbidden'); - return true; - } -}; -exports.PermissionGuard = PermissionGuard; -exports.PermissionGuard = PermissionGuard = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [authorization_service_1.AuthorizationService, String]) -], PermissionGuard); -// Factory to create guard instances with permission string (used in decorators) -const createPermissionGuard = (permission) => { - let _Guard = class _Guard extends PermissionGuard { - constructor(authz) { - super(authz, permission); - } - }; - _Guard = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [authorization_service_1.AuthorizationService]) - ], _Guard); - return _Guard; -}; -exports.createPermissionGuard = createPermissionGuard; -//# sourceMappingURL=permission.guard.js.map \ No newline at end of file diff --git a/dist/modules/authorization/guards/permission.guard.js.map b/dist/modules/authorization/guards/permission.guard.js.map deleted file mode 100644 index 0346629..0000000 --- a/dist/modules/authorization/guards/permission.guard.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"permission.guard.js","sourceRoot":"","sources":["../../../../src/modules/authorization/guards/permission.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+F;AAC/F,oEAAgE;AAGzD,IAAM,eAAe,GAArB,MAAM,eAAe;IACG;IAA8C;IAA3E,YAA6B,KAA2B,EAAmB,UAAkB;QAAhE,UAAK,GAAL,KAAK,CAAsB;QAAmB,eAAU,GAAV,UAAU,CAAQ;IAAG,CAAC;IAEjG,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC;QAC9B,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,2BAAkB,CAAC,cAAc,CAAC,CAAC;QAExD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACzE,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,2BAAkB,CAAC,WAAW,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAZY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAEyB,4CAAoB;GAD7C,eAAe,CAY3B;AAED,gFAAgF;AACzE,MAAM,qBAAqB,GAAG,CAAC,UAAkB,EAAE,EAAE;IAC1D,IACM,MAAM,GADZ,MACM,MAAO,SAAQ,eAAe;QAClC,YAAY,KAA2B;YACrC,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAC3B,CAAC;KACF,CAAA;IAJK,MAAM;QADX,IAAA,mBAAU,GAAE;yCAEQ,4CAAoB;OADnC,MAAM,CAIX;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AARW,QAAA,qBAAqB,yBAQhC"} \ No newline at end of file diff --git a/dist/modules/authorization/presentation/decorators/permission.decorator.js b/dist/modules/authorization/presentation/decorators/permission.decorator.js deleted file mode 100644 index 2547583..0000000 --- a/dist/modules/authorization/presentation/decorators/permission.decorator.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Permissions = void 0; -const common_1 = require("@nestjs/common"); -const Permissions = (...permissions) => (0, common_1.SetMetadata)('permissions', permissions); -exports.Permissions = Permissions; -//# sourceMappingURL=permission.decorator.js.map \ No newline at end of file diff --git a/dist/modules/authorization/presentation/decorators/permission.decorator.js.map b/dist/modules/authorization/presentation/decorators/permission.decorator.js.map deleted file mode 100644 index cdad7da..0000000 --- a/dist/modules/authorization/presentation/decorators/permission.decorator.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"permission.decorator.js","sourceRoot":"","sources":["../../../../../src/modules/authorization/presentation/decorators/permission.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAEtC,MAAM,WAAW,GAAG,CAAC,GAAG,WAAqB,EAAE,EAAE,CACtD,IAAA,oBAAW,EAAC,aAAa,EAAE,WAAW,CAAC,CAAC;AAD7B,QAAA,WAAW,eACkB"} \ No newline at end of file diff --git a/dist/modules/authorization/presentation/guards/permission.guard.js b/dist/modules/authorization/presentation/guards/permission.guard.js deleted file mode 100644 index 4fb2446..0000000 --- a/dist/modules/authorization/presentation/guards/permission.guard.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PermissionGuard = void 0; -const common_1 = require("@nestjs/common"); -const core_1 = require("@nestjs/core"); -let PermissionGuard = class PermissionGuard { - reflector; - constructor(reflector) { - this.reflector = reflector; - } - canActivate(context) { - const permissions = this.reflector.getAllAndOverride('permissions', [ - context.getHandler(), - context.getClass(), - ]); - if (!permissions) { - return true; - } - const request = context.switchToHttp().getRequest(); - const user = request.currentUser; - if (!user) { - throw new common_1.UnauthorizedException('Current user is missing.'); - } - const hasAll = permissions.every((permission) => { - return user.hasPermission(permission); - }); - if (!hasAll) - throw new common_1.ForbiddenException('Insufficient permissions.'); - return true; - } -}; -exports.PermissionGuard = PermissionGuard; -exports.PermissionGuard = PermissionGuard = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [core_1.Reflector]) -], PermissionGuard); -//# sourceMappingURL=permission.guard.js.map \ No newline at end of file diff --git a/dist/modules/authorization/presentation/guards/permission.guard.js.map b/dist/modules/authorization/presentation/guards/permission.guard.js.map deleted file mode 100644 index f868731..0000000 --- a/dist/modules/authorization/presentation/guards/permission.guard.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"permission.guard.js","sourceRoot":"","sources":["../../../../../src/modules/authorization/presentation/guards/permission.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAMwB;AACxB,uCAAyC;AAGlC,IAAM,eAAe,GAArB,MAAM,eAAe;IAEP;IADnB,YACmB,SAAoB;QAApB,cAAS,GAAT,SAAS,CAAW;IACpC,CAAC;IAEJ,WAAW,CAAC,OAAyB;QACnC,MAAM,WAAW,GACf,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAC9B,aAAa,EACb;YACE,OAAO,CAAC,UAAU,EAAE;YACpB,OAAO,CAAC,QAAQ,EAAE;SACnB,CACF,CAAC;QAEJ,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAS,CAAC;QAE3D,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC;QAEjC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,8BAAqB,CAAC,0BAA0B,CAAC,CAAC;QAC9D,CAAC;QAEG,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,EAAE;YAClD,OAAO,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,2BAAkB,CAAC,2BAA2B,CAAC,CAAC;QAEvE,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAnCY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAGmB,gBAAS;GAF5B,eAAe,CAmC3B"} \ No newline at end of file diff --git a/dist/modules/health/health.controller.js b/dist/modules/health/health.controller.js deleted file mode 100644 index d008ea9..0000000 --- a/dist/modules/health/health.controller.js +++ /dev/null @@ -1,35 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.HealthController = void 0; -const common_1 = require("@nestjs/common"); -const health_service_1 = require("./health.service"); -let HealthController = class HealthController { - healthService; - constructor(healthService) { - this.healthService = healthService; - } - async get() { - return this.healthService.getHealth(); - } -}; -exports.HealthController = HealthController; -__decorate([ - (0, common_1.Get)(), - __metadata("design:type", Function), - __metadata("design:paramtypes", []), - __metadata("design:returntype", Promise) -], HealthController.prototype, "get", null); -exports.HealthController = HealthController = __decorate([ - (0, common_1.Controller)('api/health'), - __metadata("design:paramtypes", [health_service_1.HealthService]) -], HealthController); -//# sourceMappingURL=health.controller.js.map \ No newline at end of file diff --git a/dist/modules/health/health.controller.js.map b/dist/modules/health/health.controller.js.map deleted file mode 100644 index 1461be4..0000000 --- a/dist/modules/health/health.controller.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"health.controller.js","sourceRoot":"","sources":["../../../src/modules/health/health.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiD;AACjD,qDAAiD;AAG1C,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IACE;IAA7B,YAA6B,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAGvD,AAAN,KAAK,CAAC,GAAG;QACP,OAAO,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,CAAC;IACxC,CAAC;CACF,CAAA;AAPY,4CAAgB;AAIrB;IADL,IAAA,YAAG,GAAE;;;;2CAGL;2BANU,gBAAgB;IAD5B,IAAA,mBAAU,EAAC,YAAY,CAAC;qCAEqB,8BAAa;GAD9C,gBAAgB,CAO5B"} \ No newline at end of file diff --git a/dist/modules/health/health.module.js b/dist/modules/health/health.module.js deleted file mode 100644 index 2ff3522..0000000 --- a/dist/modules/health/health.module.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.HealthModule = void 0; -const common_1 = require("@nestjs/common"); -const health_controller_1 = require("./health.controller"); -const health_service_1 = require("./health.service"); -let HealthModule = class HealthModule { -}; -exports.HealthModule = HealthModule; -exports.HealthModule = HealthModule = __decorate([ - (0, common_1.Module)({ - controllers: [health_controller_1.HealthController], - providers: [health_service_1.HealthService], - exports: [health_service_1.HealthService], - }) -], HealthModule); -//# sourceMappingURL=health.module.js.map \ No newline at end of file diff --git a/dist/modules/health/health.module.js.map b/dist/modules/health/health.module.js.map deleted file mode 100644 index 5fab47e..0000000 --- a/dist/modules/health/health.module.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"health.module.js","sourceRoot":"","sources":["../../../src/modules/health/health.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2DAAuD;AACvD,qDAAiD;AAO1C,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,oCAAY;uBAAZ,YAAY;IALxB,IAAA,eAAM,EAAC;QACN,WAAW,EAAE,CAAC,oCAAgB,CAAC;QAC/B,SAAS,EAAE,CAAC,8BAAa,CAAC;QAC1B,OAAO,EAAE,CAAC,8BAAa,CAAC;KACzB,CAAC;GACW,YAAY,CAAG"} \ No newline at end of file diff --git a/dist/modules/health/health.service.js b/dist/modules/health/health.service.js deleted file mode 100644 index bd95e68..0000000 --- a/dist/modules/health/health.service.js +++ /dev/null @@ -1,24 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.HealthService = void 0; -const common_1 = require("@nestjs/common"); -let HealthService = class HealthService { - async getHealth() { - return { - status: 'ok', - timestamp: new Date().toISOString(), - service: 'raylab-core', - }; - } -}; -exports.HealthService = HealthService; -exports.HealthService = HealthService = __decorate([ - (0, common_1.Injectable)() -], HealthService); -//# sourceMappingURL=health.service.js.map \ No newline at end of file diff --git a/dist/modules/health/health.service.js.map b/dist/modules/health/health.service.js.map deleted file mode 100644 index 14ed2a4..0000000 --- a/dist/modules/health/health.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"health.service.js","sourceRoot":"","sources":["../../../src/modules/health/health.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAGrC,IAAM,aAAa,GAAnB,MAAM,aAAa;IACxB,KAAK,CAAC,SAAS;QACb,OAAO;YACL,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,OAAO,EAAE,aAAa;SACvB,CAAC;IACJ,CAAC;CACF,CAAA;AARY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;GACA,aAAa,CAQzB"} \ No newline at end of file diff --git a/dist/modules/identity/application/config/env-auth-config.js b/dist/modules/identity/application/config/env-auth-config.js deleted file mode 100644 index 726f871..0000000 --- a/dist/modules/identity/application/config/env-auth-config.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EnvAuthConfig = void 0; -class EnvAuthConfig { - autoCreateUser() { - return (process.env.AUTH_AUTO_CREATE_USER ?? 'true') === 'true'; - } - syncEmail() { - return (process.env.AUTH_SYNC_EMAIL ?? 'true') === 'true'; - } - syncUsername() { - return (process.env.AUTH_SYNC_USERNAME ?? 'false') === 'true'; - } -} -exports.EnvAuthConfig = EnvAuthConfig; -//# sourceMappingURL=env-auth-config.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/config/env-auth-config.js.map b/dist/modules/identity/application/config/env-auth-config.js.map deleted file mode 100644 index 0323378..0000000 --- a/dist/modules/identity/application/config/env-auth-config.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"env-auth-config.js","sourceRoot":"","sources":["../../../../../src/modules/identity/application/config/env-auth-config.ts"],"names":[],"mappings":";;;AAEA,MAAa,aAAa;IACxB,cAAc;QACZ,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,MAAM,CAAC,KAAK,MAAM,CAAC;IAClE,CAAC;IAED,SAAS;QACP,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,MAAM,CAAC,KAAK,MAAM,CAAC;IAC5D,CAAC;IAED,YAAY;QACV,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,KAAK,MAAM,CAAC;IAChE,CAAC;CACF;AAZD,sCAYC"} \ No newline at end of file diff --git a/dist/modules/identity/application/config/i-auth-config.js b/dist/modules/identity/application/config/i-auth-config.js deleted file mode 100644 index 8ed71cd..0000000 --- a/dist/modules/identity/application/config/i-auth-config.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.IAuthConfig = void 0; -class IAuthConfig { -} -exports.IAuthConfig = IAuthConfig; -//# sourceMappingURL=i-auth-config.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/config/i-auth-config.js.map b/dist/modules/identity/application/config/i-auth-config.js.map deleted file mode 100644 index 9c3285a..0000000 --- a/dist/modules/identity/application/config/i-auth-config.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"i-auth-config.js","sourceRoot":"","sources":["../../../../../src/modules/identity/application/config/i-auth-config.ts"],"names":[],"mappings":";;;AAAA,MAAsB,WAAW;CAIhC;AAJD,kCAIC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/create-permission.handler.js b/dist/modules/identity/application/handlers/permission/create-permission.handler.js deleted file mode 100644 index 57980c4..0000000 --- a/dist/modules/identity/application/handlers/permission/create-permission.handler.js +++ /dev/null @@ -1,47 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CreatePermissionHandler = void 0; -const common_1 = require("@nestjs/common"); -const permission_interface_1 = require("../../../domain/repositories/permission.interface"); -const permission_entity_1 = require("../../../domain/entities/permission.entity"); -const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); -const crypto = require('crypto'); -let CreatePermissionHandler = class CreatePermissionHandler { - permissionRepository; - events; - constructor(permissionRepository, events) { - this.permissionRepository = permissionRepository; - this.events = events; - } - async execute(dto) { - if (!dto.name || !dto.code) - throw new common_1.ConflictException('Missing required fields'); - const perm = permission_entity_1.PermissionData.restore({ - id: crypto.randomUUID(), - code: dto.code || dto.name, - name: dto.name, - displayName: dto.displayName || dto.name, - description: dto.description || '', - createdAt: new Date(), - updatedAt: new Date(), - }); - const created = await this.permissionRepository.create(perm); - this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'PermissionCreated', payload: { permissionId: created.id } }); - return created; - } -}; -exports.CreatePermissionHandler = CreatePermissionHandler; -exports.CreatePermissionHandler = CreatePermissionHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [permission_interface_1.IPermission, event_bus_service_1.EventBus]) -], CreatePermissionHandler); -//# sourceMappingURL=create-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/create-permission.handler.js.map b/dist/modules/identity/application/handlers/permission/create-permission.handler.js.map deleted file mode 100644 index 96dc5a0..0000000 --- a/dist/modules/identity/application/handlers/permission/create-permission.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"create-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/permission/create-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,4FAAgF;AAChF,kFAA4E;AAC5E,uFAA2E;AAE3E,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAG1B,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAAoD;IAAjF,YAA6B,oBAAiC,EAAmB,MAAgB;QAApE,yBAAoB,GAApB,oBAAoB,CAAa;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAErG,KAAK,CAAC,OAAO,CAAC,GAAQ;QACpB,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,yBAAyB,CAAC,CAAC;QAEnF,MAAM,IAAI,GAAG,kCAAc,CAAC,OAAO,CAAC;YAClC,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;YACvB,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI;YAC1B,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,IAAI;YACxC,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE;YAClC,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,SAAS,EAAE,IAAI,IAAI,EAAE;SACf,CAAC,CAAC;QAEV,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7D,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACnK,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AApBY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;qCAEwC,kCAAW,EAA2B,4BAAQ;GADtF,uBAAuB,CAoBnC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/delete-permission.handler.js b/dist/modules/identity/application/handlers/permission/delete-permission.handler.js deleted file mode 100644 index 68f7f61..0000000 --- a/dist/modules/identity/application/handlers/permission/delete-permission.handler.js +++ /dev/null @@ -1,54 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DeletePermissionHandler = void 0; -const common_1 = require("@nestjs/common"); -const permission_interface_1 = require("../../../domain/repositories/permission.interface"); -const role_interface_1 = require("../../../domain/repositories/role.interface"); -const authorization_service_1 = require("../../../../authorization/authorization.service"); -const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); -let DeletePermissionHandler = class DeletePermissionHandler { - permissionRepository; - roleRepository; - authorizationService; - events; - constructor(permissionRepository, roleRepository, authorizationService, events) { - this.permissionRepository = permissionRepository; - this.roleRepository = roleRepository; - this.authorizationService = authorizationService; - this.events = events; - } - async execute(id) { - const p = await this.permissionRepository.getById(id); - if (!p) - throw new common_1.NotFoundException('Permission not found.'); - // find roles that reference this permission - const rolesWithPerm = await this.permissionRepository.findRoleIdsByPermission(id); - // delete permission - await this.permissionRepository.delete(id); - // invalidate caches for users who have affected roles - const userSet = new Set(); - for (const rid of rolesWithPerm) { - const uids = await this.roleRepository.getAssignedUserIds(rid); - uids.forEach(u => userSet.add(u)); - } - for (const uid of Array.from(userSet)) - await this.authorizationService.invalidateUserPermissions(uid); - this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'PermissionDeleted', payload: { permissionId: id, affectedRoles: rolesWithPerm } }); - return; - } -}; -exports.DeletePermissionHandler = DeletePermissionHandler; -exports.DeletePermissionHandler = DeletePermissionHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [permission_interface_1.IPermission, role_interface_1.IRole, authorization_service_1.AuthorizationService, event_bus_service_1.EventBus]) -], DeletePermissionHandler); -//# sourceMappingURL=delete-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/delete-permission.handler.js.map b/dist/modules/identity/application/handlers/permission/delete-permission.handler.js.map deleted file mode 100644 index 9f54506..0000000 --- a/dist/modules/identity/application/handlers/permission/delete-permission.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"delete-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/permission/delete-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,4FAAgF;AAChF,gFAAoE;AACpE,2FAAuF;AACvF,uFAA2E;AAGpE,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAAoD;IAAwC;IAA6D;IAAtL,YAA6B,oBAAiC,EAAmB,cAAqB,EAAmB,oBAA0C,EAAmB,MAAgB;QAAzK,yBAAoB,GAApB,oBAAoB,CAAa;QAAmB,mBAAc,GAAd,cAAc,CAAO;QAAmB,yBAAoB,GAApB,oBAAoB,CAAsB;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAE1M,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,0BAAiB,CAAC,uBAAuB,CAAC,CAAC;QAE7D,4CAA4C;QAC5C,MAAM,aAAa,GAAG,MAAO,IAAI,CAAC,oBAA4B,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC;QAE3F,oBAAoB;QACpB,MAAM,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAE3C,sDAAsD;QACtD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;YAChC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;YAC/D,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,MAAM,IAAI,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC;QAEtG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,aAAa,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC;QAEzL,OAAO;IACT,CAAC;CACF,CAAA;AA1BY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;qCAEwC,kCAAW,EAAmC,sBAAK,EAAyC,4CAAoB,EAA2B,4BAAQ;GAD3L,uBAAuB,CA0BnC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/get-permission.handler.js b/dist/modules/identity/application/handlers/permission/get-permission.handler.js deleted file mode 100644 index 12e92c1..0000000 --- a/dist/modules/identity/application/handlers/permission/get-permission.handler.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GetPermissionHandler = void 0; -const common_1 = require("@nestjs/common"); -const permission_interface_1 = require("../../../domain/repositories/permission.interface"); -let GetPermissionHandler = class GetPermissionHandler { - permissionRepository; - constructor(permissionRepository) { - this.permissionRepository = permissionRepository; - } - async execute(id) { - return this.permissionRepository.getById(id); - } -}; -exports.GetPermissionHandler = GetPermissionHandler; -exports.GetPermissionHandler = GetPermissionHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [permission_interface_1.IPermission]) -], GetPermissionHandler); -//# sourceMappingURL=get-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/get-permission.handler.js.map b/dist/modules/identity/application/handlers/permission/get-permission.handler.js.map deleted file mode 100644 index e0308b5..0000000 --- a/dist/modules/identity/application/handlers/permission/get-permission.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"get-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/permission/get-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,4FAAgF;AAGzE,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACF;IAA7B,YAA6B,oBAAiC;QAAjC,yBAAoB,GAApB,oBAAoB,CAAa;IAAG,CAAC;IAElE,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,OAAO,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC/C,CAAC;CACF,CAAA;AANY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;qCAEwC,kCAAW;GADnD,oBAAoB,CAMhC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/get-permissions.handler.js b/dist/modules/identity/application/handlers/permission/get-permissions.handler.js deleted file mode 100644 index d86ca2c..0000000 --- a/dist/modules/identity/application/handlers/permission/get-permissions.handler.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GetPermissionsHandler = void 0; -const common_1 = require("@nestjs/common"); -const permission_interface_1 = require("../../../domain/repositories/permission.interface"); -let GetPermissionsHandler = class GetPermissionsHandler { - permissionRepository; - constructor(permissionRepository) { - this.permissionRepository = permissionRepository; - } - async execute(query) { - return this.permissionRepository.find({ page: query.page, limit: query.limit, search: query.search || null }); - } -}; -exports.GetPermissionsHandler = GetPermissionsHandler; -exports.GetPermissionsHandler = GetPermissionsHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [permission_interface_1.IPermission]) -], GetPermissionsHandler); -//# sourceMappingURL=get-permissions.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/get-permissions.handler.js.map b/dist/modules/identity/application/handlers/permission/get-permissions.handler.js.map deleted file mode 100644 index ac7e3a5..0000000 --- a/dist/modules/identity/application/handlers/permission/get-permissions.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"get-permissions.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/permission/get-permissions.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,4FAAgF;AAGzE,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IACH;IAA7B,YAA6B,oBAAiC;QAAjC,yBAAoB,GAApB,oBAAoB,CAAa;IAAG,CAAC;IAElE,KAAK,CAAC,OAAO,CAAC,KAAyD;QACrE,OAAO,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IAChH,CAAC;CACF,CAAA;AANY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;qCAEwC,kCAAW;GADnD,qBAAqB,CAMjC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/update-permission.handler.js b/dist/modules/identity/application/handlers/permission/update-permission.handler.js deleted file mode 100644 index d7c86dc..0000000 --- a/dist/modules/identity/application/handlers/permission/update-permission.handler.js +++ /dev/null @@ -1,58 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UpdatePermissionHandler = void 0; -const common_1 = require("@nestjs/common"); -const permission_interface_1 = require("../../../domain/repositories/permission.interface"); -const role_interface_1 = require("../../../domain/repositories/role.interface"); -const authorization_service_1 = require("../../../../authorization/authorization.service"); -const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); -let UpdatePermissionHandler = class UpdatePermissionHandler { - permissionRepository; - roleRepository; - authorizationService; - events; - constructor(permissionRepository, roleRepository, authorizationService, events) { - this.permissionRepository = permissionRepository; - this.roleRepository = roleRepository; - this.authorizationService = authorizationService; - this.events = events; - } - async execute(id, dto) { - const perm = await this.permissionRepository.getById(id); - if (!perm) - throw new common_1.NotFoundException('Permission not found.'); - if (dto.name) - perm.changeName(dto.name); - if (dto.description !== undefined) - perm.changeDescription(dto.description); - if (dto.code) - perm.changeCode(dto.code); - const updated = await this.permissionRepository.update(perm); - // invalidate caches for users who belong to roles that reference this permission - const roleIds = await this.permissionRepository.findRoleIdsByPermission(id); - const userSet = new Set(); - for (const rid of roleIds) { - const uids = await this.roleRepository.getAssignedUserIds(rid); - uids.forEach(u => userSet.add(u)); - } - for (const uid of Array.from(userSet)) - await this.authorizationService.invalidateUserPermissions(uid); - this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'PermissionUpdated', payload: { permissionId: id } }); - return updated; - } -}; -exports.UpdatePermissionHandler = UpdatePermissionHandler; -exports.UpdatePermissionHandler = UpdatePermissionHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [permission_interface_1.IPermission, role_interface_1.IRole, authorization_service_1.AuthorizationService, event_bus_service_1.EventBus]) -], UpdatePermissionHandler); -//# sourceMappingURL=update-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/permission/update-permission.handler.js.map b/dist/modules/identity/application/handlers/permission/update-permission.handler.js.map deleted file mode 100644 index 01dc6ed..0000000 --- a/dist/modules/identity/application/handlers/permission/update-permission.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"update-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/permission/update-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,4FAAgF;AAChF,gFAAoE;AACpE,2FAAuF;AACvF,uFAA2E;AAGpE,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAAoD;IAAwC;IAA6D;IAAtL,YAA6B,oBAAiC,EAAmB,cAAqB,EAAmB,oBAA0C,EAAmB,MAAgB;QAAzK,yBAAoB,GAApB,oBAAoB,CAAa;QAAmB,mBAAc,GAAd,cAAc,CAAO;QAAmB,yBAAoB,GAApB,oBAAoB,CAAsB;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAE1M,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,GAAQ;QAChC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,uBAAuB,CAAC,CAAC;QAEhE,IAAI,GAAG,CAAC,IAAI;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,GAAG,CAAC,WAAW,KAAK,SAAS;YAAE,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC3E,IAAI,GAAG,CAAC,IAAI;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAExC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAE7D,iFAAiF;QACjF,MAAM,OAAO,GAAG,MAAO,IAAI,CAAC,oBAA4B,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC;QACrF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAElC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;YAC/D,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,MAAM,IAAI,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC;QAEtG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAE3J,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AA5BY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;qCAEwC,kCAAW,EAAmC,sBAAK,EAAyC,4CAAoB,EAA2B,4BAAQ;GAD3L,uBAAuB,CA4BnC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/assign-permission.handler.js b/dist/modules/identity/application/handlers/role/assign-permission.handler.js deleted file mode 100644 index 7ec4e30..0000000 --- a/dist/modules/identity/application/handlers/role/assign-permission.handler.js +++ /dev/null @@ -1,56 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RoleAssignPermissionHandler = void 0; -const common_1 = require("@nestjs/common"); -const role_interface_1 = require("../../../domain/repositories/role.interface"); -const permission_interface_1 = require("../../../domain/repositories/permission.interface"); -const authorization_service_1 = require("../../../../authorization/authorization.service"); -const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); -let RoleAssignPermissionHandler = class RoleAssignPermissionHandler { - roleRepository; - permissionRepository; - authorizationService; - events; - constructor(roleRepository, permissionRepository, authorizationService, events) { - this.roleRepository = roleRepository; - this.permissionRepository = permissionRepository; - this.authorizationService = authorizationService; - this.events = events; - } - async execute(roleId, permissionId) { - const role = await this.roleRepository.findById(roleId); - if (!role) - throw new common_1.NotFoundException('Role not found.'); - const perm = await this.permissionRepository.getById(permissionId); - if (!perm) - throw new common_1.NotFoundException('Permission not found.'); - role.assignPermission(perm); - const updated = await this.roleRepository.update(role); - // Invalidate permissions cache for users who have this role - const userIds = await this.roleRepository.getAssignedUserIds(roleId); - for (const uid of userIds) { - await this.authorizationService.invalidateUserPermissions(uid); - } - // Publish role.updated event - this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleUpdated', payload: { roleId, permissionId } }); - return updated; - } -}; -exports.RoleAssignPermissionHandler = RoleAssignPermissionHandler; -exports.RoleAssignPermissionHandler = RoleAssignPermissionHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [role_interface_1.IRole, - permission_interface_1.IPermission, - authorization_service_1.AuthorizationService, - event_bus_service_1.EventBus]) -], RoleAssignPermissionHandler); -//# sourceMappingURL=assign-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/assign-permission.handler.js.map b/dist/modules/identity/application/handlers/role/assign-permission.handler.js.map deleted file mode 100644 index 1ab4742..0000000 --- a/dist/modules/identity/application/handlers/role/assign-permission.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"assign-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/assign-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAuE;AACvE,gFAAoE;AACpE,4FAAgF;AAChF,2FAAuF;AACvF,uFAA2E;AAGpE,IAAM,2BAA2B,GAAjC,MAAM,2BAA2B;IAEnB;IACA;IACA;IACA;IAJnB,YACmB,cAAqB,EACrB,oBAAiC,EACjC,oBAA0C,EAC1C,MAAgB;QAHhB,mBAAc,GAAd,cAAc,CAAO;QACrB,yBAAoB,GAApB,oBAAoB,CAAa;QACjC,yBAAoB,GAApB,oBAAoB,CAAsB;QAC1C,WAAM,GAAN,MAAM,CAAU;IAChC,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,YAAoB;QAChD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACnE,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,uBAAuB,CAAC,CAAC;QAEhE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAE5B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEvD,4DAA4D;QAC5D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACrE,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC;QACjE,CAAC;QAED,6BAA6B;QAC7B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;QAEzJ,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AA9BY,kEAA2B;sCAA3B,2BAA2B;IADvC,IAAA,mBAAU,GAAE;qCAGwB,sBAAK;QACC,kCAAW;QACX,4CAAoB;QAClC,4BAAQ;GALxB,2BAA2B,CA8BvC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/create-role.handler.js b/dist/modules/identity/application/handlers/role/create-role.handler.js deleted file mode 100644 index c61e3d9..0000000 --- a/dist/modules/identity/application/handlers/role/create-role.handler.js +++ /dev/null @@ -1,54 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CreateRoleHandler = void 0; -const common_1 = require("@nestjs/common"); -const role_interface_1 = require("../../../domain/repositories/role.interface"); -const role_entity_1 = require("../../../domain/entities/role.entity"); -const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); -const crypto = require('crypto'); -let CreateRoleHandler = class CreateRoleHandler { - roleRepository; - events; - constructor(roleRepository, events) { - this.roleRepository = roleRepository; - this.events = events; - } - async execute(dto) { - // basic validation - if (!dto.code || !dto.name) - throw new common_1.ConflictException('Missing required fields'); - try { - const role = role_entity_1.RoleData.restore({ - id: crypto.randomUUID(), - code: dto.code, - name: dto.name, - description: dto.description || '', - permissions: [], - isDefault: dto.isDefault || false, - createdAt: new Date(), - updatedAt: new Date(), - }); - const created = await this.roleRepository.create(role); - this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleCreated', payload: { roleId: created.id } }); - return created; - } - catch (e) { - throw new common_1.ConflictException('Role creation failed.'); - } - } -}; -exports.CreateRoleHandler = CreateRoleHandler; -exports.CreateRoleHandler = CreateRoleHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [role_interface_1.IRole, event_bus_service_1.EventBus]) -], CreateRoleHandler); -//# sourceMappingURL=create-role.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/create-role.handler.js.map b/dist/modules/identity/application/handlers/role/create-role.handler.js.map deleted file mode 100644 index d6382d7..0000000 --- a/dist/modules/identity/application/handlers/role/create-role.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"create-role.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/create-role.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AACpE,sEAAgE;AAChE,uFAA2E;AAE3E,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAG1B,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAAwC;IAArE,YAA6B,cAAqB,EAAmB,MAAgB;QAAxD,mBAAc,GAAd,cAAc,CAAO;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAEzF,KAAK,CAAC,OAAO,CAAC,GAAQ;QACpB,mBAAmB;QACnB,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,yBAAyB,CAAC,CAAC;QAEnF,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,sBAAQ,CAAC,OAAO,CAAC;gBAC5B,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;gBACvB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE;gBAClC,WAAW,EAAE,EAAE;gBACf,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,KAAK;gBACjC,SAAS,EAAE,IAAI,IAAI,EAAE;gBACrB,SAAS,EAAE,IAAI,IAAI,EAAE;aACf,CAAC,CAAC;YAEV,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAEvD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAEvJ,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,0BAAiB,CAAC,uBAAuB,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;CACF,CAAA;AA5BY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK,EAA2B,4BAAQ;GAD1E,iBAAiB,CA4B7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/delete-role.handler.js b/dist/modules/identity/application/handlers/role/delete-role.handler.js deleted file mode 100644 index 364e58b..0000000 --- a/dist/modules/identity/application/handlers/role/delete-role.handler.js +++ /dev/null @@ -1,45 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DeleteRoleHandler = void 0; -const common_1 = require("@nestjs/common"); -const role_interface_1 = require("../../../domain/repositories/role.interface"); -const authorization_service_1 = require("../../../../authorization/authorization.service"); -const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); -let DeleteRoleHandler = class DeleteRoleHandler { - roleRepository; - authorizationService; - events; - constructor(roleRepository, authorizationService, events) { - this.roleRepository = roleRepository; - this.authorizationService = authorizationService; - this.events = events; - } - async execute(id) { - const role = await this.roleRepository.findById(id); - if (!role) - throw new common_1.NotFoundException('Role not found.'); - // get affected users before delete - const userIds = await this.roleRepository.getAssignedUserIds(id); - await this.roleRepository.delete(id); - // invalidate caches - for (const uid of userIds) - await this.authorizationService.invalidateUserPermissions(uid); - this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleDeleted', payload: { roleId: id } }); - return; - } -}; -exports.DeleteRoleHandler = DeleteRoleHandler; -exports.DeleteRoleHandler = DeleteRoleHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [role_interface_1.IRole, authorization_service_1.AuthorizationService, event_bus_service_1.EventBus]) -], DeleteRoleHandler); -//# sourceMappingURL=delete-role.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/delete-role.handler.js.map b/dist/modules/identity/application/handlers/role/delete-role.handler.js.map deleted file mode 100644 index cb54805..0000000 --- a/dist/modules/identity/application/handlers/role/delete-role.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"delete-role.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/delete-role.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AACpE,2FAAuF;AACvF,uFAA2E;AAGpE,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAAwC;IAA6D;IAAlI,YAA6B,cAAqB,EAAmB,oBAA0C,EAAmB,MAAgB;QAArH,mBAAc,GAAd,cAAc,CAAO;QAAmB,yBAAoB,GAApB,oBAAoB,CAAsB;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAEtJ,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,mCAAmC;QACnC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;QAEjE,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAErC,oBAAoB;QACpB,KAAK,MAAM,GAAG,IAAI,OAAO;YAAE,MAAM,IAAI,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC;QAE1F,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAE/I,OAAO;IACT,CAAC;CACF,CAAA;AAnBY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK,EAAyC,4CAAoB,EAA2B,4BAAQ;GADvI,iBAAiB,CAmB7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/get-role.handler.js b/dist/modules/identity/application/handlers/role/get-role.handler.js deleted file mode 100644 index 52296bd..0000000 --- a/dist/modules/identity/application/handlers/role/get-role.handler.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GetRoleHandler = void 0; -const common_1 = require("@nestjs/common"); -const role_interface_1 = require("../../../domain/repositories/role.interface"); -let GetRoleHandler = class GetRoleHandler { - roleRepository; - constructor(roleRepository) { - this.roleRepository = roleRepository; - } - async execute(id) { - return this.roleRepository.findById(id); - } -}; -exports.GetRoleHandler = GetRoleHandler; -exports.GetRoleHandler = GetRoleHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [role_interface_1.IRole]) -], GetRoleHandler); -//# sourceMappingURL=get-role.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/get-role.handler.js.map b/dist/modules/identity/application/handlers/role/get-role.handler.js.map deleted file mode 100644 index d43f1e7..0000000 --- a/dist/modules/identity/application/handlers/role/get-role.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"get-role.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/get-role.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,gFAAoE;AAG7D,IAAM,cAAc,GAApB,MAAM,cAAc;IACI;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,OAAO,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC;CACF,CAAA;AANY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,cAAc,CAM1B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/get-roles.handler.js b/dist/modules/identity/application/handlers/role/get-roles.handler.js deleted file mode 100644 index 1994d0c..0000000 --- a/dist/modules/identity/application/handlers/role/get-roles.handler.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GetRolesHandler = void 0; -const common_1 = require("@nestjs/common"); -const role_interface_1 = require("../../../domain/repositories/role.interface"); -let GetRolesHandler = class GetRolesHandler { - roleRepository; - constructor(roleRepository) { - this.roleRepository = roleRepository; - } - async execute(query) { - return this.roleRepository.find({ page: query.page, limit: query.limit, search: query.search || null }); - } -}; -exports.GetRolesHandler = GetRolesHandler; -exports.GetRolesHandler = GetRolesHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [role_interface_1.IRole]) -], GetRolesHandler); -//# sourceMappingURL=get-roles.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/get-roles.handler.js.map b/dist/modules/identity/application/handlers/role/get-roles.handler.js.map deleted file mode 100644 index 25c135a..0000000 --- a/dist/modules/identity/application/handlers/role/get-roles.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"get-roles.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/get-roles.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,gFAAoE;AAG7D,IAAM,eAAe,GAArB,MAAM,eAAe;IACG;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,KAAyD;QACrE,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IAC1G,CAAC;CACF,CAAA;AANY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,eAAe,CAM3B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/remove-permission.handler.js b/dist/modules/identity/application/handlers/role/remove-permission.handler.js deleted file mode 100644 index 40382ed..0000000 --- a/dist/modules/identity/application/handlers/role/remove-permission.handler.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RoleRemovePermissionHandler = void 0; -const common_1 = require("@nestjs/common"); -const role_interface_1 = require("../../../domain/repositories/role.interface"); -const authorization_service_1 = require("../../../../authorization/authorization.service"); -const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); -let RoleRemovePermissionHandler = class RoleRemovePermissionHandler { - roleRepository; - authorizationService; - events; - constructor(roleRepository, authorizationService, events) { - this.roleRepository = roleRepository; - this.authorizationService = authorizationService; - this.events = events; - } - async execute(roleId, permissionId) { - const role = await this.roleRepository.findById(roleId); - if (!role) - throw new common_1.NotFoundException('Role not found.'); - role.removePermission(permissionId); - const updated = await this.roleRepository.update(role); - // Invalidate caches for users with the role - const userIds = await this.roleRepository.getAssignedUserIds(roleId); - for (const uid of userIds) - await this.authorizationService.invalidateUserPermissions(uid); - // Publish event - this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleUpdated', payload: { roleId, removedPermissionId: permissionId } }); - return updated; - } -}; -exports.RoleRemovePermissionHandler = RoleRemovePermissionHandler; -exports.RoleRemovePermissionHandler = RoleRemovePermissionHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [role_interface_1.IRole, authorization_service_1.AuthorizationService, event_bus_service_1.EventBus]) -], RoleRemovePermissionHandler); -//# sourceMappingURL=remove-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/remove-permission.handler.js.map b/dist/modules/identity/application/handlers/role/remove-permission.handler.js.map deleted file mode 100644 index d788e58..0000000 --- a/dist/modules/identity/application/handlers/role/remove-permission.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"remove-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/remove-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AACpE,2FAAuF;AACvF,uFAA2E;AAGpE,IAAM,2BAA2B,GAAjC,MAAM,2BAA2B;IACT;IAAwC;IAA6D;IAAlI,YAA6B,cAAqB,EAAmB,oBAA0C,EAAmB,MAAgB;QAArH,mBAAc,GAAd,cAAc,CAAO;QAAmB,yBAAoB,GAApB,oBAAoB,CAAsB;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAEtJ,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,YAAoB;QAChD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;QAEpC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEvD,4CAA4C;QAC5C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACrE,KAAK,MAAM,GAAG,IAAI,OAAO;YAAE,MAAM,IAAI,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC;QAE1F,gBAAgB;QAChB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,mBAAmB,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;QAE9K,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AApBY,kEAA2B;sCAA3B,2BAA2B;IADvC,IAAA,mBAAU,GAAE;qCAEkC,sBAAK,EAAyC,4CAAoB,EAA2B,4BAAQ;GADvI,2BAA2B,CAoBvC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/update-role.handler.js b/dist/modules/identity/application/handlers/role/update-role.handler.js deleted file mode 100644 index e763d40..0000000 --- a/dist/modules/identity/application/handlers/role/update-role.handler.js +++ /dev/null @@ -1,53 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UpdateRoleHandler = void 0; -const common_1 = require("@nestjs/common"); -const role_interface_1 = require("../../../domain/repositories/role.interface"); -const authorization_service_1 = require("../../../../authorization/authorization.service"); -const event_bus_service_1 = require("../../../../../core/event-bus/event-bus.service"); -let UpdateRoleHandler = class UpdateRoleHandler { - roleRepository; - authorizationService; - events; - constructor(roleRepository, authorizationService, events) { - this.roleRepository = roleRepository; - this.authorizationService = authorizationService; - this.events = events; - } - async execute(id, dto) { - const role = await this.roleRepository.findById(id); - if (!role) - throw new common_1.NotFoundException('Role not found.'); - // validation - if (dto.name && dto.name.length < 2) - throw new common_1.BadRequestException('Name too short'); - if (dto.name) - role.changeName(dto.name); - if (dto.description !== undefined) - role.changeDescription(dto.description); - if (dto.isDefault !== undefined) - role.setDefault(!!dto.isDefault); - const updated = await this.roleRepository.update(role); - // invalidate caches for users with this role - const userIds = await this.roleRepository.getAssignedUserIds(id); - for (const uid of userIds) - await this.authorizationService.invalidateUserPermissions(uid); - this.events.publish({ id: require('crypto').randomUUID(), timestamp: new Date().toISOString(), type: 'RoleUpdated', payload: { roleId: id } }); - return updated; - } -}; -exports.UpdateRoleHandler = UpdateRoleHandler; -exports.UpdateRoleHandler = UpdateRoleHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [role_interface_1.IRole, authorization_service_1.AuthorizationService, event_bus_service_1.EventBus]) -], UpdateRoleHandler); -//# sourceMappingURL=update-role.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/role/update-role.handler.js.map b/dist/modules/identity/application/handlers/role/update-role.handler.js.map deleted file mode 100644 index f285491..0000000 --- a/dist/modules/identity/application/handlers/role/update-role.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"update-role.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/role/update-role.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAoF;AACpF,gFAAoE;AACpE,2FAAuF;AACvF,uFAA2E;AAGpE,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAAwC;IAA6D;IAAlI,YAA6B,cAAqB,EAAmB,oBAA0C,EAAmB,MAAgB;QAArH,mBAAc,GAAd,cAAc,CAAO;QAAmB,yBAAoB,GAApB,oBAAoB,CAAsB;QAAmB,WAAM,GAAN,MAAM,CAAU;IAAG,CAAC;IAEtJ,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,GAAQ;QAChC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,aAAa;QACb,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,4BAAmB,CAAC,gBAAgB,CAAC,CAAC;QAErF,IAAI,GAAG,CAAC,IAAI;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,GAAG,CAAC,WAAW,KAAK,SAAS;YAAE,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC3E,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS;YAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAElE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEvD,6CAA6C;QAC7C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;QACjE,KAAK,MAAM,GAAG,IAAI,OAAO;YAAE,MAAM,IAAI,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,GAAG,CAAC,CAAC;QAE1F,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAE/I,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AAxBY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK,EAAyC,4CAAoB,EAA2B,4BAAQ;GADvI,iBAAiB,CAwB7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/assign-permission.handler.js b/dist/modules/identity/application/handlers/user/assign-permission.handler.js deleted file mode 100644 index ebbfad0..0000000 --- a/dist/modules/identity/application/handlers/user/assign-permission.handler.js +++ /dev/null @@ -1,42 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AssignPermissionHandler = void 0; -const common_1 = require("@nestjs/common"); -const user_interface_1 = require("../../../domain/repositories/user.interface"); -const permission_entity_1 = require("../../../domain/entities/permission.entity"); -let AssignPermissionHandler = class AssignPermissionHandler { - userRepository; - constructor(userRepository) { - this.userRepository = userRepository; - } - async execute(userId, permissionId) { - const user = await this.userRepository.getById(userId); - if (!user) - throw new common_1.NotFoundException('User not found.'); - // Permission repository is not available; create a minimal PermissionData - const perm = permission_entity_1.PermissionData.restore({ - id: permissionId, - name: permissionId, - description: '', - createdAt: new Date(), - updatedAt: new Date(), - }); - user.assignPermission(perm); - return this.userRepository.update(user); - } -}; -exports.AssignPermissionHandler = AssignPermissionHandler; -exports.AssignPermissionHandler = AssignPermissionHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [user_interface_1.IUser]) -], AssignPermissionHandler); -//# sourceMappingURL=assign-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/assign-permission.handler.js.map b/dist/modules/identity/application/handlers/user/assign-permission.handler.js.map deleted file mode 100644 index 69bfd0d..0000000 --- a/dist/modules/identity/application/handlers/user/assign-permission.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"assign-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/assign-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AACpE,kFAA4E;AAGrE,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,YAAoB;QAChD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,0EAA0E;QAC1E,MAAM,IAAI,GAAG,kCAAc,CAAC,OAAO,CAAC;YAClC,EAAE,EAAE,YAAY;YAChB,IAAI,EAAE,YAAY;YAClB,WAAW,EAAE,EAAE;YACf,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,SAAS,EAAE,IAAI,IAAI,EAAE;SACtB,CAAC,CAAC;QAEH,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAE5B,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF,CAAA;AApBY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,uBAAuB,CAoBnC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/assign-role.handler.js b/dist/modules/identity/application/handlers/user/assign-role.handler.js deleted file mode 100644 index b0f6e6d..0000000 --- a/dist/modules/identity/application/handlers/user/assign-role.handler.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AssignRoleHandler = void 0; -const common_1 = require("@nestjs/common"); -const user_interface_1 = require("../../../domain/repositories/user.interface"); -const role_interface_1 = require("../../../domain/repositories/role.interface"); -let AssignRoleHandler = class AssignRoleHandler { - userRepository; - roleRepository; - constructor(userRepository, roleRepository) { - this.userRepository = userRepository; - this.roleRepository = roleRepository; - } - async execute(userId, roleId) { - const user = await this.userRepository.getById(userId); - if (!user) - throw new common_1.NotFoundException('User not found.'); - const role = await this.roleRepository.findById(roleId); - if (!role) - throw new common_1.NotFoundException('Role not found.'); - user.assignRole(role); - return this.userRepository.update(user); - } -}; -exports.AssignRoleHandler = AssignRoleHandler; -exports.AssignRoleHandler = AssignRoleHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [user_interface_1.IUser, - role_interface_1.IRole]) -], AssignRoleHandler); -//# sourceMappingURL=assign-role.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/assign-role.handler.js.map b/dist/modules/identity/application/handlers/user/assign-role.handler.js.map deleted file mode 100644 index 7998815..0000000 --- a/dist/modules/identity/application/handlers/user/assign-role.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"assign-role.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/assign-role.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AACpE,gFAAoE;AAG7D,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAET;IACA;IAFnB,YACmB,cAAqB,EACrB,cAAqB;QADrB,mBAAc,GAAd,cAAc,CAAO;QACrB,mBAAc,GAAd,cAAc,CAAO;IACrC,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,MAAc;QAC1C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAEtB,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF,CAAA;AAjBY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAGwB,sBAAK;QACL,sBAAK;GAH7B,iBAAiB,CAiB7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/create-user.handler.js b/dist/modules/identity/application/handlers/user/create-user.handler.js deleted file mode 100644 index 5c860df..0000000 --- a/dist/modules/identity/application/handlers/user/create-user.handler.js +++ /dev/null @@ -1,39 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CreateUserHandler = void 0; -const common_1 = require("@nestjs/common"); -const user_interface_1 = require("../../../domain/repositories/user.interface"); -const user_service_1 = require("../../services/user.service"); -let CreateUserHandler = class CreateUserHandler { - userRepository; - userService; - constructor(userRepository, userService) { - this.userRepository = userRepository; - this.userService = userService; - } - async execute(dto) { - const exists = await this.userRepository.existByEmail(dto.email); - if (exists) { - throw new common_1.ConflictException('Email already exists.'); - } - // Provisioning disabled: Do not create users in external IdP from RayLab. - // Reject attempts to create users via API to enforce creation-at-Authentik policy. - throw new common_1.BadRequestException('User creation via RayLab API is disabled. Create users in Authentik and then login to sync.'); - } -}; -exports.CreateUserHandler = CreateUserHandler; -exports.CreateUserHandler = CreateUserHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [user_interface_1.IUser, - user_service_1.UserService]) -], CreateUserHandler); -//# sourceMappingURL=create-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/create-user.handler.js.map b/dist/modules/identity/application/handlers/user/create-user.handler.js.map deleted file mode 100644 index fae7a16..0000000 --- a/dist/modules/identity/application/handlers/user/create-user.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"create-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/create-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAIwB;AAGxB,gFAAoE;AAEpE,8DAA0D;AAGnD,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAET;IACA;IAFnB,YACmB,cAAqB,EACrB,WAAwB;QADxB,mBAAc,GAAd,cAAc,CAAO;QACrB,gBAAW,GAAX,WAAW,CAAa;IACxC,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,GAAkB;QAE9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAEjE,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,IAAI,0BAAiB,CAAC,uBAAuB,CAAC,CAAC;QACvD,CAAC;QAED,0EAA0E;QAC1E,mFAAmF;QACnF,MAAM,IAAI,4BAAmB,CAAC,6FAA6F,CAAC,CAAC;IAC/H,CAAC;CACF,CAAA;AAlBY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAGwB,sBAAK;QACR,0BAAW;GAHhC,iBAAiB,CAkB7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/delete-user.handler.js b/dist/modules/identity/application/handlers/user/delete-user.handler.js deleted file mode 100644 index f7e24b5..0000000 --- a/dist/modules/identity/application/handlers/user/delete-user.handler.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DeleteUserHandler = void 0; -const common_1 = require("@nestjs/common"); -const user_interface_1 = require("../../../domain/repositories/user.interface"); -let DeleteUserHandler = class DeleteUserHandler { - userRepository; - constructor(userRepository) { - this.userRepository = userRepository; - } - async execute(id) { - const exists = await this.userRepository.existsById(id); - if (!exists) - throw new common_1.NotFoundException('User not found.'); - await this.userRepository.softDelete(id); - } -}; -exports.DeleteUserHandler = DeleteUserHandler; -exports.DeleteUserHandler = DeleteUserHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [user_interface_1.IUser]) -], DeleteUserHandler); -//# sourceMappingURL=delete-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/delete-user.handler.js.map b/dist/modules/identity/application/handlers/user/delete-user.handler.js.map deleted file mode 100644 index 572dd11..0000000 --- a/dist/modules/identity/application/handlers/user/delete-user.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"delete-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/delete-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AAG7D,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE5D,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC3C,CAAC;CACF,CAAA;AATY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,iBAAiB,CAS7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/disable-user.handler.js b/dist/modules/identity/application/handlers/user/disable-user.handler.js deleted file mode 100644 index 193959b..0000000 --- a/dist/modules/identity/application/handlers/user/disable-user.handler.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DisableUserHandler = void 0; -const common_1 = require("@nestjs/common"); -const user_interface_1 = require("../../../domain/repositories/user.interface"); -let DisableUserHandler = class DisableUserHandler { - userRepository; - constructor(userRepository) { - this.userRepository = userRepository; - } - async execute(id) { - const exists = await this.userRepository.existsById(id); - if (!exists) - throw new common_1.NotFoundException('User not found.'); - return this.userRepository.disable(id); - } -}; -exports.DisableUserHandler = DisableUserHandler; -exports.DisableUserHandler = DisableUserHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [user_interface_1.IUser]) -], DisableUserHandler); -//# sourceMappingURL=disable-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/disable-user.handler.js.map b/dist/modules/identity/application/handlers/user/disable-user.handler.js.map deleted file mode 100644 index 8aa2cea..0000000 --- a/dist/modules/identity/application/handlers/user/disable-user.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"disable-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/disable-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AAG7D,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IACA;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE5D,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;CACF,CAAA;AATY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,kBAAkB,CAS9B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/enable-user.handler.js b/dist/modules/identity/application/handlers/user/enable-user.handler.js deleted file mode 100644 index cebcbe8..0000000 --- a/dist/modules/identity/application/handlers/user/enable-user.handler.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EnableUserHandler = void 0; -const common_1 = require("@nestjs/common"); -const user_interface_1 = require("../../../domain/repositories/user.interface"); -let EnableUserHandler = class EnableUserHandler { - userRepository; - constructor(userRepository) { - this.userRepository = userRepository; - } - async execute(id) { - const exists = await this.userRepository.existsById(id); - if (!exists) - throw new common_1.NotFoundException('User not found.'); - return this.userRepository.enable(id); - } -}; -exports.EnableUserHandler = EnableUserHandler; -exports.EnableUserHandler = EnableUserHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [user_interface_1.IUser]) -], EnableUserHandler); -//# sourceMappingURL=enable-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/enable-user.handler.js.map b/dist/modules/identity/application/handlers/user/enable-user.handler.js.map deleted file mode 100644 index 9b501d8..0000000 --- a/dist/modules/identity/application/handlers/user/enable-user.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"enable-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/enable-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AAG7D,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE5D,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC;CACF,CAAA;AATY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,iBAAiB,CAS7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/get-current-user.handler.js b/dist/modules/identity/application/handlers/user/get-current-user.handler.js deleted file mode 100644 index 39c215e..0000000 --- a/dist/modules/identity/application/handlers/user/get-current-user.handler.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GetCurrentUserHandler = void 0; -const common_1 = require("@nestjs/common"); -let GetCurrentUserHandler = class GetCurrentUserHandler { - async execute(currentUser) { - // currentUser is already domain user attached by CurrentUserGuard - return currentUser; - } -}; -exports.GetCurrentUserHandler = GetCurrentUserHandler; -exports.GetCurrentUserHandler = GetCurrentUserHandler = __decorate([ - (0, common_1.Injectable)() -], GetCurrentUserHandler); -//# sourceMappingURL=get-current-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/get-current-user.handler.js.map b/dist/modules/identity/application/handlers/user/get-current-user.handler.js.map deleted file mode 100644 index e7a5319..0000000 --- a/dist/modules/identity/application/handlers/user/get-current-user.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"get-current-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/get-current-user.handler.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAIrC,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAChC,KAAK,CAAC,OAAO,CAAC,WAAqB;QACjC,kEAAkE;QAClE,OAAO,WAAW,CAAC;IACrB,CAAC;CACF,CAAA;AALY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;GACA,qBAAqB,CAKjC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/get-user.handler.js b/dist/modules/identity/application/handlers/user/get-user.handler.js deleted file mode 100644 index 29b5ea2..0000000 --- a/dist/modules/identity/application/handlers/user/get-user.handler.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GetUserHandler = void 0; -const common_1 = require("@nestjs/common"); -const user_interface_1 = require("../../../domain/repositories/user.interface"); -let GetUserHandler = class GetUserHandler { - userRepository; - constructor(userRepository) { - this.userRepository = userRepository; - } - async execute(id) { - return this.userRepository.getById(id); - } -}; -exports.GetUserHandler = GetUserHandler; -exports.GetUserHandler = GetUserHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [user_interface_1.IUser]) -], GetUserHandler); -//# sourceMappingURL=get-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/get-user.handler.js.map b/dist/modules/identity/application/handlers/user/get-user.handler.js.map deleted file mode 100644 index 3c5bb24..0000000 --- a/dist/modules/identity/application/handlers/user/get-user.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"get-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/get-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,gFAAoE;AAG7D,IAAM,cAAc,GAApB,MAAM,cAAc;IACI;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;CACF,CAAA;AANY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,cAAc,CAM1B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/get-users.handler.js b/dist/modules/identity/application/handlers/user/get-users.handler.js deleted file mode 100644 index 1c4587d..0000000 --- a/dist/modules/identity/application/handlers/user/get-users.handler.js +++ /dev/null @@ -1,37 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GetUsersHandler = void 0; -const common_1 = require("@nestjs/common"); -const user_interface_1 = require("../../../domain/repositories/user.interface"); -let GetUsersHandler = class GetUsersHandler { - userRepository; - constructor(userRepository) { - this.userRepository = userRepository; - } - async execute(query) { - const res = await this.userRepository.find({ - page: query.page, - limit: query.limit, - search: query.search || null, - isActive: query.isActive !== undefined ? query.isActive : null, - deleted: query.deleted !== undefined ? query.deleted : null, - roleId: query.roleId || null, - }); - return res; - } -}; -exports.GetUsersHandler = GetUsersHandler; -exports.GetUsersHandler = GetUsersHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [user_interface_1.IUser]) -], GetUsersHandler); -//# sourceMappingURL=get-users.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/get-users.handler.js.map b/dist/modules/identity/application/handlers/user/get-users.handler.js.map deleted file mode 100644 index 3b2604e..0000000 --- a/dist/modules/identity/application/handlers/user/get-users.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"get-users.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/get-users.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,gFAAoE;AAG7D,IAAM,eAAe,GAArB,MAAM,eAAe;IACG;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,KAOb;QACC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC;YACzC,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;YAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;YAC9D,OAAO,EAAE,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;YAC3D,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;SAC7B,CAAC,CAAC;QAEH,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAA;AAtBY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,eAAe,CAsB3B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/remove-permission.handler.js b/dist/modules/identity/application/handlers/user/remove-permission.handler.js deleted file mode 100644 index 936c638..0000000 --- a/dist/modules/identity/application/handlers/user/remove-permission.handler.js +++ /dev/null @@ -1,33 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RemovePermissionHandler = void 0; -const common_1 = require("@nestjs/common"); -const user_interface_1 = require("../../../domain/repositories/user.interface"); -let RemovePermissionHandler = class RemovePermissionHandler { - userRepository; - constructor(userRepository) { - this.userRepository = userRepository; - } - async execute(userId, permissionId) { - const user = await this.userRepository.getById(userId); - if (!user) - throw new common_1.NotFoundException('User not found.'); - user.removePermission(permissionId); - return this.userRepository.update(user); - } -}; -exports.RemovePermissionHandler = RemovePermissionHandler; -exports.RemovePermissionHandler = RemovePermissionHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [user_interface_1.IUser]) -], RemovePermissionHandler); -//# sourceMappingURL=remove-permission.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/remove-permission.handler.js.map b/dist/modules/identity/application/handlers/user/remove-permission.handler.js.map deleted file mode 100644 index 5110506..0000000 --- a/dist/modules/identity/application/handlers/user/remove-permission.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"remove-permission.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/remove-permission.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AAG7D,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,YAAoB;QAChD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;QAEpC,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF,CAAA;AAXY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,uBAAuB,CAWnC"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/remove-role.handler.js b/dist/modules/identity/application/handlers/user/remove-role.handler.js deleted file mode 100644 index 93010d3..0000000 --- a/dist/modules/identity/application/handlers/user/remove-role.handler.js +++ /dev/null @@ -1,33 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RemoveRoleHandler = void 0; -const common_1 = require("@nestjs/common"); -const user_interface_1 = require("../../../domain/repositories/user.interface"); -let RemoveRoleHandler = class RemoveRoleHandler { - userRepository; - constructor(userRepository) { - this.userRepository = userRepository; - } - async execute(userId, roleId) { - const user = await this.userRepository.getById(userId); - if (!user) - throw new common_1.NotFoundException('User not found.'); - user.removeRole(roleId); - return this.userRepository.update(user); - } -}; -exports.RemoveRoleHandler = RemoveRoleHandler; -exports.RemoveRoleHandler = RemoveRoleHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [user_interface_1.IUser]) -], RemoveRoleHandler); -//# sourceMappingURL=remove-role.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/remove-role.handler.js.map b/dist/modules/identity/application/handlers/user/remove-role.handler.js.map deleted file mode 100644 index 750bb57..0000000 --- a/dist/modules/identity/application/handlers/user/remove-role.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"remove-role.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/remove-role.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AAG7D,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,MAAc;QAC1C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAExB,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF,CAAA;AAXY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,iBAAiB,CAW7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/restore-user.handler.js b/dist/modules/identity/application/handlers/user/restore-user.handler.js deleted file mode 100644 index 0b0cd79..0000000 --- a/dist/modules/identity/application/handlers/user/restore-user.handler.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RestoreUserHandler = void 0; -const common_1 = require("@nestjs/common"); -const user_interface_1 = require("../../../domain/repositories/user.interface"); -let RestoreUserHandler = class RestoreUserHandler { - userRepository; - constructor(userRepository) { - this.userRepository = userRepository; - } - async execute(id) { - const exists = await this.userRepository.existsById(id); - if (!exists) - throw new common_1.NotFoundException('User not found.'); - return this.userRepository.restore(id); - } -}; -exports.RestoreUserHandler = RestoreUserHandler; -exports.RestoreUserHandler = RestoreUserHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [user_interface_1.IUser]) -], RestoreUserHandler); -//# sourceMappingURL=restore-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/restore-user.handler.js.map b/dist/modules/identity/application/handlers/user/restore-user.handler.js.map deleted file mode 100644 index 9a5b0c1..0000000 --- a/dist/modules/identity/application/handlers/user/restore-user.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"restore-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/restore-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,gFAAoE;AAG7D,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IACA;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE5D,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;CACF,CAAA;AATY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,kBAAkB,CAS9B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/sync-identity.handler.js b/dist/modules/identity/application/handlers/user/sync-identity.handler.js deleted file mode 100644 index cc1980f..0000000 --- a/dist/modules/identity/application/handlers/user/sync-identity.handler.js +++ /dev/null @@ -1,92 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SyncIdentityHandler = void 0; -const common_1 = require("@nestjs/common"); -const user_interface_1 = require("../../../domain/repositories/user.interface"); -const role_interface_1 = require("../../../domain/repositories/role.interface"); -const i_auth_config_1 = require("../../config/i-auth-config"); -const user_entity_1 = require("../../../domain/entities/user.entity"); -let SyncIdentityHandler = class SyncIdentityHandler { - userRepository; - roleRepository; - authConfig; - constructor(userRepository, roleRepository, authConfig) { - this.userRepository = userRepository; - this.roleRepository = roleRepository; - this.authConfig = authConfig; - } - async execute(identity) { - const { sub, preferred_username, email } = identity; - if (!sub) - throw new common_1.UnauthorizedException('Invalid identity payload.'); - // Try find by authentikId - let user = await this.userRepository.findByAuthentikId(sub); - const syncEmail = this.authConfig.syncEmail(); - const syncUsername = this.authConfig.syncUsername(); - if (!user) { - // User does not exist locally: create minimal local record per new architecture - const username = preferred_username || email || sub; - const userEntity = user_entity_1.UserData.create({ username, email: email || '', authentikId: sub }); - // assign default role if available - try { - const defaultRole = await this.roleRepository.getDefaultRole(); - if (defaultRole) { - userEntity.assignRole(defaultRole); - } - } - catch (e) { - // ignore if role repo not available or no default role - } - // persist local user - user = await this.userRepository.create(userEntity); - // return freshly created user - return user; - } - else { - let changed = false; - if (syncEmail && email && user.email !== email) { - user.changeEmail(email); - changed = true; - // prepare event class instance if needed (no dispatch) - } - if (syncUsername && preferred_username && user.username !== preferred_username) { - user.changeUsername(preferred_username); - changed = true; - // prepare event class instance if needed (no dispatch) - } - // update last seen - user.touchLastSeen(); - changed = true; - if (changed) { - user = await this.userRepository.update(user); - } - } - // Validations - if (!user.isActive) { - // Authentication succeeded but user is disabled - throw new common_1.ForbiddenException('User is not active.'); - } - if (user.deletedAt) { - throw new common_1.ForbiddenException('User is deleted.'); - } - // Ensure roles/permissions loaded (repo should return includes) - return user; - } -}; -exports.SyncIdentityHandler = SyncIdentityHandler; -exports.SyncIdentityHandler = SyncIdentityHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [user_interface_1.IUser, - role_interface_1.IRole, - i_auth_config_1.IAuthConfig]) -], SyncIdentityHandler); -//# sourceMappingURL=sync-identity.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/sync-identity.handler.js.map b/dist/modules/identity/application/handlers/user/sync-identity.handler.js.map deleted file mode 100644 index 3431800..0000000 --- a/dist/modules/identity/application/handlers/user/sync-identity.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"sync-identity.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/sync-identity.handler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/update-user.handler.js b/dist/modules/identity/application/handlers/user/update-user.handler.js deleted file mode 100644 index e80e2da..0000000 --- a/dist/modules/identity/application/handlers/user/update-user.handler.js +++ /dev/null @@ -1,45 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UpdateUserHandler = void 0; -const common_1 = require("@nestjs/common"); -const user_interface_1 = require("../../../domain/repositories/user.interface"); -let UpdateUserHandler = class UpdateUserHandler { - userRepository; - constructor(userRepository) { - this.userRepository = userRepository; - } - async execute(id, dto) { - // Password management is not allowed in RayLab Core - if (dto.password) - throw new common_1.BadRequestException('Password management is not allowed.'); - const user = await this.userRepository.getById(id); - if (!user) - throw new common_1.NotFoundException('User not found.'); - // Only allow updating profile fields: name, email, metadata, storage settings - if (dto.name !== undefined) - user.changeUsername(dto.name); - if (dto.email !== undefined) - user.changeEmail(dto.email); - // metadata field not handled at domain level yet - if (dto.storageQuota !== undefined) - user.setStorageQuota(dto.storageQuota); - if (dto.storageUsed !== undefined) - user.setStorageUsed(dto.storageUsed); - return this.userRepository.update(user); - } -}; -exports.UpdateUserHandler = UpdateUserHandler; -exports.UpdateUserHandler = UpdateUserHandler = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [user_interface_1.IUser]) -], UpdateUserHandler); -//# sourceMappingURL=update-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/user/update-user.handler.js.map b/dist/modules/identity/application/handlers/user/update-user.handler.js.map deleted file mode 100644 index 4687313..0000000 --- a/dist/modules/identity/application/handlers/user/update-user.handler.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"update-user.handler.js","sourceRoot":"","sources":["../../../../../../src/modules/identity/application/handlers/user/update-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAoF;AACpF,gFAAoE;AAI7D,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAA7B,YAA6B,cAAqB;QAArB,mBAAc,GAAd,cAAc,CAAO;IAAG,CAAC;IAEtD,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,GAAkB;QAC1C,oDAAoD;QACpD,IAAK,GAAW,CAAC,QAAQ;YAAE,MAAM,IAAI,4BAAmB,CAAC,qCAAqC,CAAC,CAAC;QAEhG,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QAE1D,8EAA8E;QAC9E,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS;YAAE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1D,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzD,iDAAiD;QACjD,IAAK,GAAW,CAAC,YAAY,KAAK,SAAS;YAAE,IAAI,CAAC,eAAe,CAAE,GAAW,CAAC,YAAY,CAAC,CAAC;QAC7F,IAAK,GAAW,CAAC,WAAW,KAAK,SAAS;YAAE,IAAI,CAAC,cAAc,CAAE,GAAW,CAAC,WAAW,CAAC,CAAC;QAE1F,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF,CAAA;AAnBY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAEkC,sBAAK;GADvC,iBAAiB,CAmB7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/services/user.service.js b/dist/modules/identity/application/services/user.service.js deleted file mode 100644 index 74f5ebf..0000000 --- a/dist/modules/identity/application/services/user.service.js +++ /dev/null @@ -1,37 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var UserService_1; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UserService = void 0; -const common_1 = require("@nestjs/common"); -const prisma_service_1 = require("../../../../shared/prisma.service"); -const user_interface_1 = require("../../domain/repositories/user.interface"); -let UserService = UserService_1 = class UserService { - prisma; - userRepository; - logger = new common_1.Logger(UserService_1.name); - constructor(prisma, userRepository) { - this.prisma = prisma; - this.userRepository = userRepository; - } - async createUser(input) { - // Provisioning to external Identity Provider (Authentik) has been disabled. - // All users must be created in Authentik first. RayLab will create local record on first successful login. - throw new common_1.BadRequestException('Provisioning disabled: create users in Authentik and login to sync to RayLab'); - } -}; -exports.UserService = UserService; -exports.UserService = UserService = UserService_1 = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [prisma_service_1.PrismaService, - user_interface_1.IUser]) -], UserService); -//# sourceMappingURL=user.service.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/services/user.service.js.map b/dist/modules/identity/application/services/user.service.js.map deleted file mode 100644 index b3e3655..0000000 --- a/dist/modules/identity/application/services/user.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"user.service.js","sourceRoot":"","sources":["../../../../../src/modules/identity/application/services/user.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAyE;AACzE,sEAAkE;AAClE,6EAAiE;AAI1D,IAAM,WAAW,mBAAjB,MAAM,WAAW;IAIH;IACA;IAJF,MAAM,GAAG,IAAI,eAAM,CAAC,aAAW,CAAC,IAAI,CAAC,CAAC;IAEvD,YACmB,MAAqB,EACrB,cAAqB;QADrB,WAAM,GAAN,MAAM,CAAe;QACrB,mBAAc,GAAd,cAAc,CAAO;IACrC,CAAC;IAEJ,KAAK,CAAC,UAAU,CAAC,KAAgF;QAC/F,4EAA4E;QAC5E,2GAA2G;QAC3G,MAAM,IAAI,4BAAmB,CAAC,8EAA8E,CAAC,CAAC;IAChH,CAAC;CACF,CAAA;AAbY,kCAAW;sBAAX,WAAW;IADvB,IAAA,mBAAU,GAAE;qCAKgB,8BAAa;QACL,sBAAK;GAL7B,WAAW,CAavB"} \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/permission.entity.js b/dist/modules/identity/domain/entities/permission.entity.js deleted file mode 100644 index f1238a0..0000000 --- a/dist/modules/identity/domain/entities/permission.entity.js +++ /dev/null @@ -1,33 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PermissionData = void 0; -class PermissionData { - id; - code; - name; - description; - createdAt; - updatedAt; - constructor(id, code, name, description, createdAt, updatedAt) { - this.id = id; - this.code = code; - this.name = name; - this.description = description; - this.createdAt = createdAt; - this.updatedAt = updatedAt; - } - changeName(name) { - this.name = name; - } - changeDescription(description) { - this.description = description; - } - changeCode(code) { - this.code = code; - } - static restore(props) { - return new PermissionData(props.id, props.code || props.name, props.name, props.description, props.createdAt, props.updatedAt); - } -} -exports.PermissionData = PermissionData; -//# sourceMappingURL=permission.entity.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/permission.entity.js.map b/dist/modules/identity/domain/entities/permission.entity.js.map deleted file mode 100644 index cb03fd7..0000000 --- a/dist/modules/identity/domain/entities/permission.entity.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"permission.entity.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/entities/permission.entity.ts"],"names":[],"mappings":";;;AAAA,MAAa,cAAc;IAEP;IACT;IACA;IACA;IACA;IACA;IANT,YACkB,EAAU,EACnB,IAAY,EACZ,IAAY,EACZ,WAAmB,EACnB,SAAe,EACf,SAAe;QALN,OAAE,GAAF,EAAE,CAAQ;QACnB,SAAI,GAAJ,IAAI,CAAQ;QACZ,SAAI,GAAJ,IAAI,CAAQ;QACZ,gBAAW,GAAX,WAAW,CAAQ;QACnB,cAAS,GAAT,SAAS,CAAM;QACf,cAAS,GAAT,SAAS,CAAM;IACrB,CAAC;IAEJ,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,iBAAiB,CAAC,WAAmB;QACnC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,KAOd;QACC,OAAO,IAAI,cAAc,CACvB,KAAK,CAAC,EAAE,EACR,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,EACxB,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,SAAS,CAChB,CAAC;IACJ,CAAC;CACF;AAvCD,wCAuCC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/role.entity.js b/dist/modules/identity/domain/entities/role.entity.js deleted file mode 100644 index 5234e7d..0000000 --- a/dist/modules/identity/domain/entities/role.entity.js +++ /dev/null @@ -1,51 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RoleData = void 0; -class RoleData { - id; - code; - name; - description; - permissions; - isDefault; - createdAt; - updatedAt; - constructor(id, code, name, description, permissions, isDefault, createdAt, updatedAt) { - this.id = id; - this.code = code; - this.name = name; - this.description = description; - this.permissions = permissions; - this.isDefault = isDefault; - this.createdAt = createdAt; - this.updatedAt = updatedAt; - } - hasPermissions(permissionId) { - return this.permissions.some(x => x.id.toLowerCase() === permissionId.toLowerCase()); - } - assignPermission(permissionData) { - if (this.hasPermissions(permissionData.id)) - throw new Error("Permission sudah dimiliki."); - this.permissions.push(permissionData); - } - removePermission(permissionId) { - const idx = this.permissions.findIndex(p => p.id.toLowerCase() === permissionId.toLowerCase()); - if (idx === -1) - throw new Error('Permission tidak ditemukan pada role.'); - this.permissions.splice(idx, 1); - } - changeName(name) { - this.name = name; - } - changeDescription(description) { - this.description = description; - } - setDefault(isDefault) { - this.isDefault = isDefault; - } - static restore(props) { - return new RoleData(props.id, props.code, props.name, props.description, props.permissions || [], props.isDefault ?? false, props.createdAt, props.updatedAt); - } -} -exports.RoleData = RoleData; -//# sourceMappingURL=role.entity.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/role.entity.js.map b/dist/modules/identity/domain/entities/role.entity.js.map deleted file mode 100644 index 22436e0..0000000 --- a/dist/modules/identity/domain/entities/role.entity.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"role.entity.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/entities/role.entity.ts"],"names":[],"mappings":";;;AAEA,MAAa,QAAQ;IAED;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IART,YACkB,EAAU,EACnB,IAAY,EACZ,IAAY,EACZ,WAAmB,EACnB,WAA6B,EAC7B,SAAkB,EAClB,SAAe,EACf,SAAe;QAPN,OAAE,GAAF,EAAE,CAAQ;QACnB,SAAI,GAAJ,IAAI,CAAQ;QACZ,SAAI,GAAJ,IAAI,CAAQ;QACZ,gBAAW,GAAX,WAAW,CAAQ;QACnB,gBAAW,GAAX,WAAW,CAAkB;QAC7B,cAAS,GAAT,SAAS,CAAS;QAClB,cAAS,GAAT,SAAS,CAAM;QACf,cAAS,GAAT,SAAS,CAAM;IACrB,CAAC;IAEJ,cAAc,CAAC,YAAoB;QACjC,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;IACvF,CAAC;IAED,gBAAgB,CAAC,cAA8B;QAC7C,IAAI,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAE1F,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACxC,CAAC;IAED,gBAAgB,CAAC,YAAoB;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/F,IAAI,GAAG,KAAK,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QACzE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,iBAAiB,CAAC,WAAmB;QACnC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;IAED,UAAU,CAAC,SAAkB;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,KASd;QACC,OAAO,IAAI,QAAQ,CACjB,KAAK,CAAC,EAAE,EACR,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,WAAW,IAAI,EAAE,EACvB,KAAK,CAAC,SAAS,IAAI,KAAK,EACxB,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,SAAS,CAChB,CAAC;IACJ,CAAC;CACF;AA7DD,4BA6DC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/user.entity.js b/dist/modules/identity/domain/entities/user.entity.js deleted file mode 100644 index 9b244a8..0000000 --- a/dist/modules/identity/domain/entities/user.entity.js +++ /dev/null @@ -1,143 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UserData = void 0; -class UserData { - id; - authentikId; - username; - email; - password; - roles; - permissions; - isActive; - deletedAt; - lastSeenAt; - storageQuota; - storageUsed; - constructor(id, authentikId, username, email, password, roles, permissions, isActive, deletedAt, lastSeenAt, storageQuota, storageUsed) { - this.id = id; - this.authentikId = authentikId; - this.username = username; - this.email = email; - this.password = password; - this.roles = roles; - this.permissions = permissions; - this.isActive = isActive; - this.deletedAt = deletedAt; - this.lastSeenAt = lastSeenAt; - this.storageQuota = storageQuota; - this.storageUsed = storageUsed; - } - //#region Create - static create(data) { - const quota = data.storageQuota !== undefined ? data.storageQuota : 10737418240; // 10 GB - const used = data.storageUsed !== undefined ? data.storageUsed : 0; - return new UserData(crypto.randomUUID(), data.authentikId || null, data.username, data.email, data.password ?? null, [], [], true, null, null, quota, used); - } - //#endregion - //#region Read - hasRole(roleId) { - return this.roles.some(x => x.id.toLowerCase() === roleId.toLowerCase()); - } - assignRole(roleData) { - if (this.hasRole(roleData.id)) - throw new Error("Role Sudah dimiliki."); - this.roles.push(roleData); - } - hasPermissions(permissionId) { - return this.permissions.some(x => x.id.toLowerCase() === permissionId.toLowerCase()); - } - hasPermission(permission) { - const byPerm = this.permissions.some(x => x.id.toLowerCase() === permission.toLowerCase() || x.name.toLowerCase() === permission.toLowerCase()); - if (byPerm) - return true; - // check roles - return this.roles.some(r => r.permissions.some(p => p.id.toLowerCase() === permission.toLowerCase() || p.name.toLowerCase() === permission.toLowerCase())); - } - assignPermission(permissionData) { - if (this.hasPermissions(permissionData.id)) - throw new Error("Permission sudah dimiliki."); - this.permissions.push(permissionData); - } - //#endregion - //#region Update - changeEmail(email) { - this.email = email; - } - changeUsername(username) { - this.username = username; - } - touchLastSeen() { - this.lastSeenAt = new Date(); - } - enable() { - this.isActive = true; - } - disable() { - this.isActive = false; - } - //#endregion - //#region Delete - softDelete() { - this.deletedAt = new Date(); - this.isActive = false; - } - restoreInstance() { - this.deletedAt = null; - } - //#endregion - static restore(props) { - return new UserData(props.id, props.authentikId || null, props.username, props.email, props.password, props.roles || [], props.permissions || [], props.isActive !== undefined ? props.isActive : true, props.deletedAt || null, props.lastSeenAt || null, props.storageQuota !== undefined && props.storageQuota !== null ? props.storageQuota : 10737418240, props.storageUsed !== undefined && props.storageUsed !== null ? props.storageUsed : 0); - } - removeRole(roleId) { - const idx = this.roles.findIndex(r => r.id.toLowerCase() === roleId.toLowerCase()); - if (idx === -1) - throw new Error('Role tidak ditemukan pada user.'); - this.roles.splice(idx, 1); - } - removePermission(permissionId) { - const idx = this.permissions.findIndex(p => p.id.toLowerCase() === permissionId.toLowerCase()); - if (idx === -1) - throw new Error('Permission tidak ditemukan pada user.'); - this.permissions.splice(idx, 1); - } - // Storage helpers - setStorageQuota(bytes) { - if (bytes < 0) - throw new Error('storageQuota must be >= 0'); - if (this.storageUsed > bytes) - throw new Error('storageQuota cannot be less than storageUsed'); - this.storageQuota = bytes; - } - setStorageUsed(bytes) { - if (bytes < 0) - throw new Error('storageUsed must be >= 0'); - if (bytes > this.storageQuota) - throw new Error('storageUsed cannot exceed storageQuota'); - this.storageUsed = bytes; - } - // Response helper for API - toResponse() { - const remaining = this.storageQuota - this.storageUsed; - const usagePercentage = this.storageQuota > 0 ? Math.round((this.storageUsed / this.storageQuota) * 100) : 0; - return { - id: this.id, - authentikId: this.authentikId, - username: this.username, - email: this.email, - roles: this.roles, - permissions: this.permissions, - isActive: this.isActive, - deletedAt: this.deletedAt, - lastSeenAt: this.lastSeenAt, - storage: { - quota: this.storageQuota, - used: this.storageUsed, - remaining, - usagePercentage, - }, - }; - } -} -exports.UserData = UserData; -//# sourceMappingURL=user.entity.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/user.entity.js.map b/dist/modules/identity/domain/entities/user.entity.js.map deleted file mode 100644 index 876a488..0000000 --- a/dist/modules/identity/domain/entities/user.entity.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"user.entity.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/entities/user.entity.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-created.event.js b/dist/modules/identity/domain/events/user-created.event.js deleted file mode 100644 index f9edf2c..0000000 --- a/dist/modules/identity/domain/events/user-created.event.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UserCreatedEvent = void 0; -class UserCreatedEvent { - payload; - name = 'UserCreated'; - occurredAt = new Date(); - constructor(payload) { - this.payload = payload; - } -} -exports.UserCreatedEvent = UserCreatedEvent; -//# sourceMappingURL=user-created.event.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-created.event.js.map b/dist/modules/identity/domain/events/user-created.event.js.map deleted file mode 100644 index 32401a7..0000000 --- a/dist/modules/identity/domain/events/user-created.event.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"user-created.event.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/events/user-created.event.ts"],"names":[],"mappings":";;;AAEA,MAAa,gBAAgB;IAGC;IAFnB,IAAI,GAAG,aAAa,CAAC;IACrB,UAAU,GAAS,IAAI,IAAI,EAAE,CAAC;IACvC,YAA4B,OAAiF;QAAjF,YAAO,GAAP,OAAO,CAA0E;IAAG,CAAC;CAClH;AAJD,4CAIC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-disabled.event.js b/dist/modules/identity/domain/events/user-disabled.event.js deleted file mode 100644 index 8d3546b..0000000 --- a/dist/modules/identity/domain/events/user-disabled.event.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UserDisabledEvent = void 0; -class UserDisabledEvent { - payload; - name = 'UserDisabled'; - occurredAt = new Date(); - constructor(payload) { - this.payload = payload; - } -} -exports.UserDisabledEvent = UserDisabledEvent; -//# sourceMappingURL=user-disabled.event.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-disabled.event.js.map b/dist/modules/identity/domain/events/user-disabled.event.js.map deleted file mode 100644 index dec6363..0000000 --- a/dist/modules/identity/domain/events/user-disabled.event.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"user-disabled.event.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/events/user-disabled.event.ts"],"names":[],"mappings":";;;AAEA,MAAa,iBAAiB;IAGA;IAFnB,IAAI,GAAG,cAAc,CAAC;IACtB,UAAU,GAAS,IAAI,IAAI,EAAE,CAAC;IACvC,YAA4B,OAA2B;QAA3B,YAAO,GAAP,OAAO,CAAoB;IAAG,CAAC;CAC5D;AAJD,8CAIC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-email-changed.event.js b/dist/modules/identity/domain/events/user-email-changed.event.js deleted file mode 100644 index c5d1ddb..0000000 --- a/dist/modules/identity/domain/events/user-email-changed.event.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UserEmailChangedEvent = void 0; -class UserEmailChangedEvent { - payload; - name = 'UserEmailChanged'; - occurredAt = new Date(); - constructor(payload) { - this.payload = payload; - } -} -exports.UserEmailChangedEvent = UserEmailChangedEvent; -//# sourceMappingURL=user-email-changed.event.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-email-changed.event.js.map b/dist/modules/identity/domain/events/user-email-changed.event.js.map deleted file mode 100644 index a57fb24..0000000 --- a/dist/modules/identity/domain/events/user-email-changed.event.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"user-email-changed.event.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/events/user-email-changed.event.ts"],"names":[],"mappings":";;;AAEA,MAAa,qBAAqB;IAGJ;IAFnB,IAAI,GAAG,kBAAkB,CAAC;IAC1B,UAAU,GAAS,IAAI,IAAI,EAAE,CAAC;IACvC,YAA4B,OAA+D;QAA/D,YAAO,GAAP,OAAO,CAAwD;IAAG,CAAC;CAChG;AAJD,sDAIC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-enabled.event.js b/dist/modules/identity/domain/events/user-enabled.event.js deleted file mode 100644 index 81a05d9..0000000 --- a/dist/modules/identity/domain/events/user-enabled.event.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UserEnabledEvent = void 0; -class UserEnabledEvent { - payload; - name = 'UserEnabled'; - occurredAt = new Date(); - constructor(payload) { - this.payload = payload; - } -} -exports.UserEnabledEvent = UserEnabledEvent; -//# sourceMappingURL=user-enabled.event.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-enabled.event.js.map b/dist/modules/identity/domain/events/user-enabled.event.js.map deleted file mode 100644 index dec602b..0000000 --- a/dist/modules/identity/domain/events/user-enabled.event.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"user-enabled.event.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/events/user-enabled.event.ts"],"names":[],"mappings":";;;AAEA,MAAa,gBAAgB;IAGC;IAFnB,IAAI,GAAG,aAAa,CAAC;IACrB,UAAU,GAAS,IAAI,IAAI,EAAE,CAAC;IACvC,YAA4B,OAA2B;QAA3B,YAAO,GAAP,OAAO,CAAoB;IAAG,CAAC;CAC5D;AAJD,4CAIC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-username-changed.event.js b/dist/modules/identity/domain/events/user-username-changed.event.js deleted file mode 100644 index dd9949a..0000000 --- a/dist/modules/identity/domain/events/user-username-changed.event.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UserUsernameChangedEvent = void 0; -class UserUsernameChangedEvent { - payload; - name = 'UserUsernameChanged'; - occurredAt = new Date(); - constructor(payload) { - this.payload = payload; - } -} -exports.UserUsernameChangedEvent = UserUsernameChangedEvent; -//# sourceMappingURL=user-username-changed.event.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-username-changed.event.js.map b/dist/modules/identity/domain/events/user-username-changed.event.js.map deleted file mode 100644 index fc35c36..0000000 --- a/dist/modules/identity/domain/events/user-username-changed.event.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"user-username-changed.event.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/events/user-username-changed.event.ts"],"names":[],"mappings":";;;AAEA,MAAa,wBAAwB;IAGP;IAFnB,IAAI,GAAG,qBAAqB,CAAC;IAC7B,UAAU,GAAS,IAAI,IAAI,EAAE,CAAC;IACvC,YAA4B,OAAqE;QAArE,YAAO,GAAP,OAAO,CAA8D;IAAG,CAAC;CACtG;AAJD,4DAIC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/permission.interface.js b/dist/modules/identity/domain/repositories/permission.interface.js deleted file mode 100644 index 11c253e..0000000 --- a/dist/modules/identity/domain/repositories/permission.interface.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.IPermission = void 0; -class IPermission { -} -exports.IPermission = IPermission; -//# sourceMappingURL=permission.interface.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/permission.interface.js.map b/dist/modules/identity/domain/repositories/permission.interface.js.map deleted file mode 100644 index 534e501..0000000 --- a/dist/modules/identity/domain/repositories/permission.interface.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"permission.interface.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/repositories/permission.interface.ts"],"names":[],"mappings":";;;AAEA,MAAsB,WAAW;CAShC;AATD,kCASC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/role.interface.js b/dist/modules/identity/domain/repositories/role.interface.js deleted file mode 100644 index 9119f3b..0000000 --- a/dist/modules/identity/domain/repositories/role.interface.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.IRole = void 0; -class IRole { -} -exports.IRole = IRole; -//# sourceMappingURL=role.interface.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/role.interface.js.map b/dist/modules/identity/domain/repositories/role.interface.js.map deleted file mode 100644 index 27bc8fe..0000000 --- a/dist/modules/identity/domain/repositories/role.interface.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"role.interface.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/repositories/role.interface.ts"],"names":[],"mappings":";;;AAEA,MAAsB,KAAK;CAY1B;AAZD,sBAYC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/user.interface.js b/dist/modules/identity/domain/repositories/user.interface.js deleted file mode 100644 index 3315c95..0000000 --- a/dist/modules/identity/domain/repositories/user.interface.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.IUser = void 0; -class IUser { -} -exports.IUser = IUser; -//# sourceMappingURL=user.interface.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/user.interface.js.map b/dist/modules/identity/domain/repositories/user.interface.js.map deleted file mode 100644 index e657448..0000000 --- a/dist/modules/identity/domain/repositories/user.interface.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"user.interface.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/repositories/user.interface.ts"],"names":[],"mappings":";;;AAEA,MAAsB,KAAK;CA0B1B;AA1BD,sBA0BC"} \ No newline at end of file diff --git a/dist/modules/identity/identity.module.js b/dist/modules/identity/identity.module.js deleted file mode 100644 index 29042db..0000000 --- a/dist/modules/identity/identity.module.js +++ /dev/null @@ -1,114 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.IdentityModule = void 0; -const common_1 = require("@nestjs/common"); -const users_controller_1 = require("./presentation/controllers/users.controller"); -const roles_controller_1 = require("./presentation/controllers/roles.controller"); -const permissions_controller_1 = require("./presentation/controllers/permissions.controller"); -const prisma_service_1 = require("../../shared/prisma.service"); -const user_service_1 = require("./application/services/user.service"); -const prisma_user_repository_1 = require("./infrastructure/repositories/prisma-user.repository"); -const user_interface_1 = require("./domain/repositories/user.interface"); -const jwt_auth_guard_1 = require("../../core/auth/guards/jwt-auth.guard"); -const permission_guard_1 = require("../authorization/presentation/guards/permission.guard"); -const core_1 = require("@nestjs/core"); -const sync_identity_handler_1 = require("./application/handlers/user/sync-identity.handler"); -const current_user_guard_1 = require("./presentation/guards/current-user.guard"); -const prisma_role_repository_1 = require("./infrastructure/repositories/prisma-role.repository"); -const prisma_permission_repository_1 = require("./infrastructure/repositories/prisma-permission.repository"); -const role_interface_1 = require("./domain/repositories/role.interface"); -const permission_interface_1 = require("./domain/repositories/permission.interface"); -const i_auth_config_1 = require("./application/config/i-auth-config"); -const env_auth_config_1 = require("./application/config/env-auth-config"); -const auth_module_1 = require("../auth/auth.module"); -const authorization_module_1 = require("../authorization/authorization.module"); -const get_users_handler_1 = require("./application/handlers/user/get-users.handler"); -const get_user_handler_1 = require("./application/handlers/user/get-user.handler"); -const get_current_user_handler_1 = require("./application/handlers/user/get-current-user.handler"); -const enable_user_handler_1 = require("./application/handlers/user/enable-user.handler"); -const disable_user_handler_1 = require("./application/handlers/user/disable-user.handler"); -const delete_user_handler_1 = require("./application/handlers/user/delete-user.handler"); -const restore_user_handler_1 = require("./application/handlers/user/restore-user.handler"); -const update_user_handler_1 = require("./application/handlers/user/update-user.handler"); -const get_roles_handler_1 = require("./application/handlers/role/get-roles.handler"); -const get_role_handler_1 = require("./application/handlers/role/get-role.handler"); -const create_role_handler_1 = require("./application/handlers/role/create-role.handler"); -const update_role_handler_1 = require("./application/handlers/role/update-role.handler"); -const delete_role_handler_1 = require("./application/handlers/role/delete-role.handler"); -const assign_permission_handler_1 = require("./application/handlers/role/assign-permission.handler"); -const remove_permission_handler_1 = require("./application/handlers/role/remove-permission.handler"); -const get_permissions_handler_1 = require("./application/handlers/permission/get-permissions.handler"); -const get_permission_handler_1 = require("./application/handlers/permission/get-permission.handler"); -const create_permission_handler_1 = require("./application/handlers/permission/create-permission.handler"); -const update_permission_handler_1 = require("./application/handlers/permission/update-permission.handler"); -const delete_permission_handler_1 = require("./application/handlers/permission/delete-permission.handler"); -let IdentityModule = class IdentityModule { -}; -exports.IdentityModule = IdentityModule; -exports.IdentityModule = IdentityModule = __decorate([ - (0, common_1.Module)({ - imports: [auth_module_1.AuthModule, authorization_module_1.AuthorizationModule], - providers: [ - //#region User - // CreateUserHandler has been removed: provisioning disabled; users must be created in Authentik. - sync_identity_handler_1.SyncIdentityHandler, - get_users_handler_1.GetUsersHandler, - get_user_handler_1.GetUserHandler, - get_current_user_handler_1.GetCurrentUserHandler, - enable_user_handler_1.EnableUserHandler, - disable_user_handler_1.DisableUserHandler, - delete_user_handler_1.DeleteUserHandler, - restore_user_handler_1.RestoreUserHandler, - update_user_handler_1.UpdateUserHandler, - //#endregion - // role & permission handlers - get_roles_handler_1.GetRolesHandler, - get_role_handler_1.GetRoleHandler, - create_role_handler_1.CreateRoleHandler, - update_role_handler_1.UpdateRoleHandler, - delete_role_handler_1.DeleteRoleHandler, - assign_permission_handler_1.RoleAssignPermissionHandler, - remove_permission_handler_1.RoleRemovePermissionHandler, - get_permissions_handler_1.GetPermissionsHandler, - get_permission_handler_1.GetPermissionHandler, - create_permission_handler_1.CreatePermissionHandler, - update_permission_handler_1.UpdatePermissionHandler, - delete_permission_handler_1.DeletePermissionHandler, - jwt_auth_guard_1.JwtAuthGuard, - current_user_guard_1.CurrentUserGuard, - permission_guard_1.PermissionGuard, - core_1.Reflector, - prisma_service_1.PrismaService, - prisma_user_repository_1.PrismaUserRepository, - prisma_role_repository_1.PrismaRoleRepository, - prisma_permission_repository_1.PrismaPermissionRepository, - user_service_1.UserService, - update_user_handler_1.UpdateUserHandler, - { - provide: user_interface_1.IUser, - useClass: prisma_user_repository_1.PrismaUserRepository, - }, - { - provide: role_interface_1.IRole, - useClass: prisma_role_repository_1.PrismaRoleRepository, - }, - { - provide: permission_interface_1.IPermission, - useClass: prisma_permission_repository_1.PrismaPermissionRepository, - }, - { - provide: i_auth_config_1.IAuthConfig, - useClass: env_auth_config_1.EnvAuthConfig, - }, - ], - exports: [sync_identity_handler_1.SyncIdentityHandler, current_user_guard_1.CurrentUserGuard], - controllers: [users_controller_1.UsersController, roles_controller_1.RolesController, permissions_controller_1.PermissionsController], - }) -], IdentityModule); -//# sourceMappingURL=identity.module.js.map \ No newline at end of file diff --git a/dist/modules/identity/identity.module.js.map b/dist/modules/identity/identity.module.js.map deleted file mode 100644 index 2d3cb78..0000000 --- a/dist/modules/identity/identity.module.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"identity.module.js","sourceRoot":"","sources":["../../../src/modules/identity/identity.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,kFAA8E;AAC9E,kFAA8E;AAC9E,8FAA0F;AAC1F,gEAA4D;AAC5D,sEAAkE;AAClE,iGAA4F;AAC5F,yEAA6D;AAG7D,0EAAqE;AAErE,4FAAwF;AACxF,uCAAyC;AACzC,6FAAwF;AACxF,iFAA4E;AAC5E,iGAA4F;AAC5F,6GAAwG;AACxG,yEAA6D;AAC7D,qFAAyE;AACzE,sEAAiE;AACjE,0EAAqE;AACrE,qDAAiD;AACjD,gFAA4E;AAG5E,qFAAgF;AAChF,mFAA8E;AAC9E,mGAA6F;AAC7F,yFAAoF;AACpF,2FAAsF;AACtF,yFAAoF;AACpF,2FAAsF;AACtF,yFAAoF;AAIpF,qFAAgF;AAChF,mFAA8E;AAC9E,yFAAoF;AACpF,yFAAoF;AACpF,yFAAoF;AACpF,qGAAoG;AACpG,qGAAoG;AAEpG,uGAAkG;AAClG,qGAAgG;AAChG,2GAAsG;AACtG,2GAAsG;AACtG,2GAAsG;AAoE/F,IAAM,cAAc,GAApB,MAAM,cAAc;CAAG,CAAA;AAAjB,wCAAc;yBAAd,cAAc;IAlE1B,IAAA,eAAM,EAAC;QACJ,OAAO,EAAE,CAAC,wBAAU,EAAE,0CAAmB,CAAC;QAC5C,SAAS,EAAE;YAEL,eAAe;YACnB,iGAAiG;YACjG,2CAAmB;YACnB,mCAAe;YAEf,iCAAc;YACd,gDAAqB;YACrB,uCAAiB;YACjB,yCAAkB;YAClB,uCAAiB;YACb,yCAAkB;YACtB,uCAAiB;YAEjB,YAAY;YAEZ,6BAA6B;YAC7B,mCAAe;YACf,iCAAc;YACd,uCAAiB;YACjB,uCAAiB;YACjB,uCAAiB;YACjB,uDAA2B;YAC3B,uDAA2B;YAEvB,+CAAqB;YACzB,6CAAoB;YACpB,mDAAuB;YACvB,mDAAuB;YACvB,mDAAuB;YAEvB,6BAAY;YAEZ,qCAAgB;YAChB,kCAAe;YACf,gBAAS;YACT,8BAAa;YACb,6CAAoB;YACpB,6CAAoB;YACpB,yDAA0B;YACtB,0BAAW;YACf,uCAAiB;YAEjB;gBACE,OAAO,EAAE,sBAAK;gBACd,QAAQ,EAAE,6CAAoB;aAC/B;YACD;gBACE,OAAO,EAAE,sBAAK;gBACd,QAAQ,EAAE,6CAAoB;aAC/B;YACD;gBACE,OAAO,EAAE,kCAAW;gBACpB,QAAQ,EAAE,yDAA0B;aACrC;YACD;gBACE,OAAO,EAAE,2BAAW;gBACpB,QAAQ,EAAE,+BAAa;aACxB;SACI;QACP,OAAO,EAAE,CAAC,2CAAmB,EAAE,qCAAgB,CAAC;QAChD,WAAW,EAAE,CAAC,kCAAe,EAAE,kCAAe,EAAE,8CAAqB,CAAC;KACvE,CAAC;GACW,cAAc,CAAG"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js b/dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js deleted file mode 100644 index a879ac7..0000000 --- a/dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js +++ /dev/null @@ -1,26 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PrismaPermissionMapper = void 0; -const permission_entity_1 = require("../../domain/entities/permission.entity"); -class PrismaPermissionMapper { - static toDomain(model) { - return permission_entity_1.PermissionData.restore({ - id: model.id, - code: model.code || model.name, - name: model.name, - description: model.description, - createdAt: model.createdAt, - updatedAt: model.updatedAt, - }); - } - static toPersistence(permission) { - return { - id: permission.id, - code: permission.code, - name: permission.name, - description: permission.description, - }; - } -} -exports.PrismaPermissionMapper = PrismaPermissionMapper; -//# sourceMappingURL=prisma-permission.mapper.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js.map b/dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js.map deleted file mode 100644 index 9d55fc3..0000000 --- a/dist/modules/identity/infrastructure/mappers/prisma-permission.mapper.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"prisma-permission.mapper.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/mappers/prisma-permission.mapper.ts"],"names":[],"mappings":";;;AAAA,+EAAyE;AAEzE,MAAa,sBAAsB;IACjC,MAAM,CAAC,QAAQ,CAAC,KAAU;QACxB,OAAO,kCAAc,CAAC,OAAO,CAAC;YAC5B,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI;YAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,UAA0B;QAC7C,OAAO;YACL,EAAE,EAAE,UAAU,CAAC,EAAE;YACjB,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,WAAW,EAAE,UAAU,CAAC,WAAW;SACpC,CAAC;IACJ,CAAC;CACF;AApBD,wDAoBC"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js b/dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js deleted file mode 100644 index 846e987..0000000 --- a/dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PrismaRoleMapper = void 0; -const role_entity_1 = require("../../domain/entities/role.entity"); -const permission_entity_1 = require("../../domain/entities/permission.entity"); -class PrismaRoleMapper { - static toDomain(model) { - const permissions = (model.permissions || []).map((p) => { - const perm = p.permission || p; // p may be RolePermission with nested permission or permission itself - return permission_entity_1.PermissionData.restore({ - id: perm.id, - name: perm.name, - description: perm.description, - createdAt: perm.createdAt, - updatedAt: perm.updatedAt, - }); - }); - return role_entity_1.RoleData.restore({ - id: model.id, - code: model.code, - name: model.name, - description: model.description, - permissions, - isDefault: model.isDefault ?? false, - createdAt: model.createdAt, - updatedAt: model.updatedAt, - }); - } -} -exports.PrismaRoleMapper = PrismaRoleMapper; -//# sourceMappingURL=prisma-role.mapper.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js.map b/dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js.map deleted file mode 100644 index 370d49b..0000000 --- a/dist/modules/identity/infrastructure/mappers/prisma-role.mapper.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"prisma-role.mapper.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/mappers/prisma-role.mapper.ts"],"names":[],"mappings":";;;AAAA,mEAA6D;AAC7D,+EAAyE;AAEzE,MAAa,gBAAgB;IAC3B,MAAM,CAAC,QAAQ,CAAC,KAAU;QACxB,MAAM,WAAW,GAAqB,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE;YAC7E,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,sEAAsE;YACtG,OAAO,kCAAc,CAAC,OAAO,CAAC;gBAC5B,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,OAAO,sBAAQ,CAAC,OAAO,CAAC;YACtB,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,WAAW;YACX,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,KAAK;YACnC,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B,CAAC,CAAC;IACL,CAAC;CACF;AAxBD,4CAwBC"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js b/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js deleted file mode 100644 index 65dfcb2..0000000 --- a/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js +++ /dev/null @@ -1,69 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PrismaUserMapper = void 0; -const user_entity_1 = require("../../domain/entities/user.entity"); -const role_entity_1 = require("../../domain/entities/role.entity"); -const permission_entity_1 = require("../../domain/entities/permission.entity"); -class PrismaUserMapper { - static toDomain(model) { - const roles = (model.roles || []).map((ur) => { - const r = ur.role; - const permissions = (r?.permissions || []).map((rp) => permission_entity_1.PermissionData.restore({ - id: rp.permission.id, - name: rp.permission.name, - description: rp.permission.description, - createdAt: rp.permission.createdAt, - updatedAt: rp.permission.updatedAt, - })); - return role_entity_1.RoleData.restore({ - id: r.id, - code: r.code, - name: r.name, - description: r.description, - permissions, - createdAt: r.createdAt, - updatedAt: r.updatedAt, - }); - }); - const permissions = (model.permissions || []).map((up) => permission_entity_1.PermissionData.restore({ - id: up.permission.id, - name: up.permission.name, - description: up.permission.description, - createdAt: up.permission.createdAt, - updatedAt: up.permission.updatedAt, - })); - return user_entity_1.UserData.restore({ - id: model.id, - authentikId: model.authentikUserId || model.authentikId || null, - username: model.username || model.name || model.username, - email: model.email, - password: model.password ?? null, - roles, - permissions, - isActive: model.isActive ?? true, - deletedAt: model.deletedAt || null, - lastSeenAt: model.lastSeenAt || null, - storageQuota: model.storageQuota !== undefined && model.storageQuota !== null ? Number(model.storageQuota) : undefined, - storageUsed: model.storageUsed !== undefined && model.storageUsed !== null ? Number(model.storageUsed) : undefined, - }); - } - static toPersistence(userData) { - return { - id: userData.id, - authentikUserId: userData.authentikUserId || userData.authentikId, - authentikId: userData.authentikId, - username: userData.username, - email: userData.email, - password: userData.password ?? null, - isActive: userData.isActive, - deletedAt: userData.deletedAt, - lastSeenAt: userData.lastSeenAt, - lastSyncedAt: userData.lastSyncedAt, - syncStatus: userData.syncStatus, - storageQuota: userData.storageQuota, - storageUsed: userData.storageUsed, - }; - } -} -exports.PrismaUserMapper = PrismaUserMapper; -//# sourceMappingURL=prisma-user.mapper.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js.map b/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js.map deleted file mode 100644 index d9962ec..0000000 --- a/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"prisma-user.mapper.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js b/dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js deleted file mode 100644 index f31859d..0000000 --- a/dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js +++ /dev/null @@ -1,67 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PrismaPermissionRepository = void 0; -const common_1 = require("@nestjs/common"); -const prisma_service_1 = require("../../../../shared/prisma.service"); -const prisma_permission_mapper_1 = require("../mappers/prisma-permission.mapper"); -let PrismaPermissionRepository = class PrismaPermissionRepository { - prisma; - constructor(prisma) { - this.prisma = prisma; - } - async find(params) { - const page = params.page && params.page > 0 ? params.page : 1; - const limit = params.limit && params.limit > 0 ? params.limit : 10; - const where = {}; - if (params.search) { - where.OR = [ - { name: { contains: params.search, mode: 'insensitive' } }, - { code: { contains: params.search, mode: 'insensitive' } }, - { description: { contains: params.search, mode: 'insensitive' } }, - ]; - } - const [total, items] = await Promise.all([ - this.prisma.permission.count({ where }), - this.prisma.permission.findMany({ where, skip: (page - 1) * limit, take: limit, orderBy: { createdAt: 'desc' } }), - ]); - return { data: items.map(i => prisma_permission_mapper_1.PrismaPermissionMapper.toDomain(i)), total }; - } - async getById(id) { - const p = await this.prisma.permission.findUnique({ where: { id } }); - if (!p) - throw new Error('Permission not found.'); - return prisma_permission_mapper_1.PrismaPermissionMapper.toDomain(p); - } - async create(permission) { - const base = prisma_permission_mapper_1.PrismaPermissionMapper.toPersistence(permission); - const created = await this.prisma.permission.create({ data: base }); - return prisma_permission_mapper_1.PrismaPermissionMapper.toDomain(created); - } - async update(permission) { - const base = prisma_permission_mapper_1.PrismaPermissionMapper.toPersistence(permission); - const updated = await this.prisma.permission.update({ where: { id: permission.id }, data: base }); - return prisma_permission_mapper_1.PrismaPermissionMapper.toDomain(updated); - } - async delete(id) { - await this.prisma.permission.delete({ where: { id } }); - } - async findRoleIdsByPermission(permissionId) { - const rows = await this.prisma.rolePermission.findMany({ where: { permissionId }, select: { roleId: true } }); - return rows.map(r => r.roleId); - } -}; -exports.PrismaPermissionRepository = PrismaPermissionRepository; -exports.PrismaPermissionRepository = PrismaPermissionRepository = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [prisma_service_1.PrismaService]) -], PrismaPermissionRepository); -//# sourceMappingURL=prisma-permission.repository.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js.map b/dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js.map deleted file mode 100644 index 9c8feb7..0000000 --- a/dist/modules/identity/infrastructure/repositories/prisma-permission.repository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"prisma-permission.repository.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/repositories/prisma-permission.repository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-role.repository.js b/dist/modules/identity/infrastructure/repositories/prisma-role.repository.js deleted file mode 100644 index 3e86090..0000000 --- a/dist/modules/identity/infrastructure/repositories/prisma-role.repository.js +++ /dev/null @@ -1,110 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PrismaRoleRepository = void 0; -const common_1 = require("@nestjs/common"); -const prisma_service_1 = require("../../../../shared/prisma.service"); -const prisma_role_mapper_1 = require("../mappers/prisma-role.mapper"); -let PrismaRoleRepository = class PrismaRoleRepository { - prisma; - constructor(prisma) { - this.prisma = prisma; - } - async getDefaultRole() { - const role = await this.prisma.role.findFirst({ - where: { isDefault: true }, - include: { permissions: { include: { permission: true } } }, - }); - if (!role) - return null; - return prisma_role_mapper_1.PrismaRoleMapper.toDomain(role); - } - async findById(roleId) { - const role = await this.prisma.role.findUnique({ - where: { id: roleId }, - include: { permissions: { include: { permission: true } } }, - }); - if (!role) - throw new Error('Role not found.'); - return prisma_role_mapper_1.PrismaRoleMapper.toDomain(role); - } - async find(params) { - const page = params.page && params.page > 0 ? params.page : 1; - const limit = params.limit && params.limit > 0 ? params.limit : 10; - const where = {}; - if (params.search) { - where.OR = [ - { name: { contains: params.search, mode: 'insensitive' } }, - { code: { contains: params.search, mode: 'insensitive' } }, - { description: { contains: params.search, mode: 'insensitive' } }, - ]; - } - const [total, items] = await Promise.all([ - this.prisma.role.count({ where }), - this.prisma.role.findMany({ where, skip: (page - 1) * limit, take: limit, orderBy: { createdAt: 'desc' }, include: { permissions: { include: { permission: true } } } }), - ]); - return { data: items.map(i => prisma_role_mapper_1.PrismaRoleMapper.toDomain(i)), total }; - } - async create(role) { - const base = { - id: role.id, - code: role.code, - name: role.name, - description: role.description, - isDefault: role.isDefault, - }; - if (role.permissions && role.permissions.length > 0) { - base.permissions = { - create: role.permissions.map(p => ({ permission: { connect: { id: p.id } }, assignedBy: 'system' })), - }; - } - const created = await this.prisma.role.create({ data: base, include: { permissions: { include: { permission: true } } } }); - return prisma_role_mapper_1.PrismaRoleMapper.toDomain(created); - } - async update(role) { - const base = { - code: role.code, - name: role.name, - description: role.description, - isDefault: role.isDefault, - }; - // sync permissions via transaction - const permIds = role.permissions ? role.permissions.map(p => p.id) : []; - await this.prisma.$transaction(async (prisma) => { - const currentPerms = await prisma.rolePermission.findMany({ where: { roleId: role.id } }); - const currentIds = currentPerms.map(p => p.permissionId); - const toAdd = permIds.filter(id => !currentIds.includes(id)); - const toRemove = currentIds.filter(id => !permIds.includes(id)); - if (toRemove.length > 0) { - await prisma.rolePermission.deleteMany({ where: { roleId: role.id, permissionId: { in: toRemove } } }); - } - if (toAdd.length > 0) { - await prisma.rolePermission.createMany({ data: toAdd.map(pid => ({ roleId: role.id, permissionId: pid, assignedBy: 'system' })), skipDuplicates: true }); - } - await prisma.role.update({ where: { id: role.id }, data: base }); - }); - const updated = await this.prisma.role.findUnique({ where: { id: role.id }, include: { permissions: { include: { permission: true } } } }); - return prisma_role_mapper_1.PrismaRoleMapper.toDomain(updated); - } - async delete(roleId) { - await this.prisma.role.delete({ where: { id: roleId } }); - } - async getAssignedUserIds(roleId) { - const rows = await this.prisma.userRole.findMany({ where: { roleId }, select: { userId: true } }); - return rows.map(r => r.userId); - } -}; -exports.PrismaRoleRepository = PrismaRoleRepository; -exports.PrismaRoleRepository = PrismaRoleRepository = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [prisma_service_1.PrismaService]) -], PrismaRoleRepository); -//# sourceMappingURL=prisma-role.repository.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-role.repository.js.map b/dist/modules/identity/infrastructure/repositories/prisma-role.repository.js.map deleted file mode 100644 index 743d10a..0000000 --- a/dist/modules/identity/infrastructure/repositories/prisma-role.repository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"prisma-role.repository.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/repositories/prisma-role.repository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js b/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js deleted file mode 100644 index 8cf324e..0000000 --- a/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js +++ /dev/null @@ -1,216 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PrismaUserRepository = void 0; -const common_1 = require("@nestjs/common"); -const prisma_service_1 = require("../../../../shared/prisma.service"); -const prisma_user_mapper_1 = require("../mappers/prisma-user.mapper"); -let PrismaUserRepository = class PrismaUserRepository { - prisma; - constructor(prisma) { - this.prisma = prisma; - } - //#region Create - async create(userData) { - const base = prisma_user_mapper_1.PrismaUserMapper.toPersistence(userData); - const data = { ...base }; - if (userData.roles && userData.roles.length > 0) { - data.roles = { - create: userData.roles.map(r => ({ role: { connect: { id: r.id } }, assignedBy: 'system' })), - }; - } - const created = await this.prisma.user.create({ - data, - include: { - roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, - permissions: { include: { permission: true } }, - }, - }); - return prisma_user_mapper_1.PrismaUserMapper.toDomain(created); - } - //#endregion - //#region Read - async existsById(userId) { - return ((await this.prisma.user.findUnique({ - where: { id: userId }, - select: { id: true }, - })) !== null); - } - async existByEmail(userEmail) { - return ((await this.prisma.user.findUnique({ - where: { email: userEmail }, - select: { email: true }, - })) !== null); - } - async getById(userId) { - const userData = await this.prisma.user.findUnique({ - where: { id: userId }, - include: { - roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, - permissions: { include: { permission: true } }, - }, - }); - if (userData === null) - throw new Error('User tidak ditemukan.'); - return prisma_user_mapper_1.PrismaUserMapper.toDomain(userData); - } - async getByEmail(userEmail) { - const userData = await this.prisma.user.findUnique({ - where: { email: userEmail }, - include: { - roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, - permissions: { include: { permission: true } }, - }, - }); - if (userData === null) - throw new Error('User tidak ditemukan.'); - return prisma_user_mapper_1.PrismaUserMapper.toDomain(userData); - } - async findByAuthentikId(authentikId) { - const userData = await this.prisma.user.findUnique({ - where: { authentikId }, - include: { - roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, - permissions: { include: { permission: true } }, - }, - }); - if (!userData) - return null; - return prisma_user_mapper_1.PrismaUserMapper.toDomain(userData); - } - async find(params) { - const page = params.page && params.page > 0 ? params.page : 1; - const limit = params.limit && params.limit > 0 ? params.limit : 10; - const where = {}; - if (params.search) { - where.OR = [ - { username: { contains: params.search, mode: 'insensitive' } }, - { email: { contains: params.search, mode: 'insensitive' } }, - ]; - } - if (params.isActive !== undefined && params.isActive !== null) { - where.isActive = params.isActive; - } - if (params.deleted !== undefined && params.deleted !== null) { - if (params.deleted) - where.deletedAt = { not: null }; - else - where.deletedAt = null; - } - if (params.roleId) { - where.roles = { some: { roleId: params.roleId } }; - } - const [total, items] = await Promise.all([ - this.prisma.user.count({ where }), - this.prisma.user.findMany({ - where, - include: { - roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, - permissions: { include: { permission: true } }, - }, - skip: (page - 1) * limit, - take: limit, - orderBy: { createdAt: 'desc' }, - }), - ]); - return { data: items.map(i => prisma_user_mapper_1.PrismaUserMapper.toDomain(i)), total }; - } - async softDelete(userId) { - await this.prisma.user.update({ where: { id: userId }, data: { deletedAt: new Date(), isActive: false } }); - } - async restore(userId) { - const updated = await this.prisma.user.update({ - where: { id: userId }, - data: { deletedAt: null }, - include: { - roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, - permissions: { include: { permission: true } }, - }, - }); - return prisma_user_mapper_1.PrismaUserMapper.toDomain(updated); - } - async enable(userId) { - const updated = await this.prisma.user.update({ - where: { id: userId }, - data: { isActive: true }, - include: { - roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, - permissions: { include: { permission: true } }, - }, - }); - return prisma_user_mapper_1.PrismaUserMapper.toDomain(updated); - } - async disable(userId) { - const updated = await this.prisma.user.update({ - where: { id: userId }, - data: { isActive: false }, - include: { - roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, - permissions: { include: { permission: true } }, - }, - }); - return prisma_user_mapper_1.PrismaUserMapper.toDomain(updated); - } - async update(userData) { - const base = prisma_user_mapper_1.PrismaUserMapper.toPersistence(userData); - const data = { ...base }; - // sync roles and permissions using transaction - const roleIds = userData.roles ? userData.roles.map(r => r.id) : []; - const permissionIds = userData.permissions ? userData.permissions.map(p => p.id) : []; - // perform transaction: delete removed relations, create missing ones, update user - await this.prisma.$transaction(async (prisma) => { - // current roles - const currentRoles = await prisma.userRole.findMany({ where: { userId: userData.id } }); - const currentRoleIds = currentRoles.map(r => r.roleId); - const toAddRoles = roleIds.filter(id => !currentRoleIds.includes(id)); - const toRemoveRoles = currentRoleIds.filter(id => !roleIds.includes(id)); - if (toRemoveRoles.length > 0) { - await prisma.userRole.deleteMany({ where: { userId: userData.id, roleId: { in: toRemoveRoles } } }); - } - if (toAddRoles.length > 0) { - await prisma.userRole.createMany({ - data: toAddRoles.map(rid => ({ userId: userData.id, roleId: rid, assignedBy: 'system' })), - skipDuplicates: true, - }); - } - // permissions - const currentPerms = await prisma.userPermission.findMany({ where: { userId: userData.id } }); - const currentPermIds = currentPerms.map(p => p.permissionId); - const toAddPerms = permissionIds.filter(id => !currentPermIds.includes(id)); - const toRemovePerms = currentPermIds.filter(id => !permissionIds.includes(id)); - if (toRemovePerms.length > 0) { - await prisma.userPermission.deleteMany({ where: { userId: userData.id, permissionId: { in: toRemovePerms } } }); - } - if (toAddPerms.length > 0) { - await prisma.userPermission.createMany({ - data: toAddPerms.map(pid => ({ userId: userData.id, permissionId: pid, assignedBy: 'system' })), - skipDuplicates: true, - }); - } - // update base user fields - await prisma.user.update({ where: { id: userData.id }, data }); - }); - const updated = await this.prisma.user.findUnique({ - where: { id: userData.id }, - include: { - roles: { include: { role: { include: { permissions: { include: { permission: true } } } } } }, - permissions: { include: { permission: true } }, - }, - }); - return prisma_user_mapper_1.PrismaUserMapper.toDomain(updated); - } -}; -exports.PrismaUserRepository = PrismaUserRepository; -exports.PrismaUserRepository = PrismaUserRepository = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [prisma_service_1.PrismaService]) -], PrismaUserRepository); -//# sourceMappingURL=prisma-user.repository.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js.map b/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js.map deleted file mode 100644 index e689eeb..0000000 --- a/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"prisma-user.repository.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/repositories/prisma-user.repository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/permissions.controller.js b/dist/modules/identity/presentation/controllers/permissions.controller.js deleted file mode 100644 index f1bf211..0000000 --- a/dist/modules/identity/presentation/controllers/permissions.controller.js +++ /dev/null @@ -1,119 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var __param = (this && this.__param) || function (paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PermissionsController = void 0; -const common_1 = require("@nestjs/common"); -const swagger_1 = require("@nestjs/swagger"); -const jwt_auth_guard_1 = require("../../../../core/auth/guards/jwt-auth.guard"); -const permission_guard_1 = require("../../../authorization/presentation/guards/permission.guard"); -const current_user_guard_1 = require("../guards/current-user.guard"); -const permission_decorator_1 = require("../../../authorization/presentation/decorators/permission.decorator"); -const permission_constants_1 = require("../../../../common/constants/permission.constants"); -const get_permissions_handler_1 = require("../../application/handlers/permission/get-permissions.handler"); -const get_permission_handler_1 = require("../../application/handlers/permission/get-permission.handler"); -const create_permission_handler_1 = require("../../application/handlers/permission/create-permission.handler"); -const update_permission_handler_1 = require("../../application/handlers/permission/update-permission.handler"); -const delete_permission_handler_1 = require("../../application/handlers/permission/delete-permission.handler"); -let PermissionsController = class PermissionsController { - getPermissionsHandler; - getPermissionHandler; - createPermissionHandler; - updatePermissionHandler; - deletePermissionHandler; - constructor(getPermissionsHandler, getPermissionHandler, createPermissionHandler, updatePermissionHandler, deletePermissionHandler) { - this.getPermissionsHandler = getPermissionsHandler; - this.getPermissionHandler = getPermissionHandler; - this.createPermissionHandler = createPermissionHandler; - this.updatePermissionHandler = updatePermissionHandler; - this.deletePermissionHandler = deletePermissionHandler; - } - async findAll(query) { - const res = await this.getPermissionsHandler.execute(query); - return { success: true, data: res.data, meta: { total: res.total } }; - } - async findOne(id) { - const p = await this.getPermissionHandler.execute(id); - return { success: true, data: p, meta: {} }; - } - async create(body) { - const created = await this.createPermissionHandler.execute(body); - return { success: true, data: created, meta: {} }; - } - async update(id, body) { - const updated = await this.updatePermissionHandler.execute(id, body); - return { success: true, data: updated, meta: {} }; - } - async remove(id) { - await this.deletePermissionHandler.execute(id); - return { success: true, data: null, meta: {} }; - } -}; -exports.PermissionsController = PermissionsController; -__decorate([ - (0, common_1.Get)(), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.PERMISSION_READ), - (0, swagger_1.ApiOperation)({ summary: 'List permissions' }), - __param(0, (0, common_1.Query)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], PermissionsController.prototype, "findAll", null); -__decorate([ - (0, common_1.Get)(':id'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.PERMISSION_READ), - (0, swagger_1.ApiOperation)({ summary: 'Get permission' }), - __param(0, (0, common_1.Param)('id')), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], PermissionsController.prototype, "findOne", null); -__decorate([ - (0, common_1.Post)(), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.PERMISSION_CREATE), - (0, swagger_1.ApiOperation)({ summary: 'Create permission' }), - __param(0, (0, common_1.Body)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], PermissionsController.prototype, "create", null); -__decorate([ - (0, common_1.Patch)(':id'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.PERMISSION_UPDATE), - (0, swagger_1.ApiOperation)({ summary: 'Update permission' }), - __param(0, (0, common_1.Param)('id')), - __param(1, (0, common_1.Body)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String, Object]), - __metadata("design:returntype", Promise) -], PermissionsController.prototype, "update", null); -__decorate([ - (0, common_1.Delete)(':id'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.PERMISSION_DELETE), - (0, swagger_1.ApiOperation)({ summary: 'Delete permission' }), - __param(0, (0, common_1.Param)('id')), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], PermissionsController.prototype, "remove", null); -exports.PermissionsController = PermissionsController = __decorate([ - (0, swagger_1.ApiTags)('Permissions'), - (0, common_1.Controller)('permissions'), - (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard, permission_guard_1.PermissionGuard), - __metadata("design:paramtypes", [get_permissions_handler_1.GetPermissionsHandler, - get_permission_handler_1.GetPermissionHandler, - create_permission_handler_1.CreatePermissionHandler, - update_permission_handler_1.UpdatePermissionHandler, - delete_permission_handler_1.DeletePermissionHandler]) -], PermissionsController); -//# sourceMappingURL=permissions.controller.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/permissions.controller.js.map b/dist/modules/identity/presentation/controllers/permissions.controller.js.map deleted file mode 100644 index 18a3e1b..0000000 --- a/dist/modules/identity/presentation/controllers/permissions.controller.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"permissions.controller.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/controllers/permissions.controller.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/roles.controller.js b/dist/modules/identity/presentation/controllers/roles.controller.js deleted file mode 100644 index f9f2f60..0000000 --- a/dist/modules/identity/presentation/controllers/roles.controller.js +++ /dev/null @@ -1,156 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var __param = (this && this.__param) || function (paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RolesController = void 0; -const common_1 = require("@nestjs/common"); -const swagger_1 = require("@nestjs/swagger"); -const jwt_auth_guard_1 = require("../../../../core/auth/guards/jwt-auth.guard"); -const permission_guard_1 = require("../../../authorization/presentation/guards/permission.guard"); -const current_user_guard_1 = require("../guards/current-user.guard"); -const permission_decorator_1 = require("../../../authorization/presentation/decorators/permission.decorator"); -const permission_constants_1 = require("../../../../common/constants/permission.constants"); -const get_roles_handler_1 = require("../../application/handlers/role/get-roles.handler"); -const get_role_handler_1 = require("../../application/handlers/role/get-role.handler"); -const create_role_handler_1 = require("../../application/handlers/role/create-role.handler"); -const update_role_handler_1 = require("../../application/handlers/role/update-role.handler"); -const delete_role_handler_1 = require("../../application/handlers/role/delete-role.handler"); -const assign_permission_handler_1 = require("../../application/handlers/role/assign-permission.handler"); -const remove_permission_handler_1 = require("../../application/handlers/role/remove-permission.handler"); -let RolesController = class RolesController { - getRolesHandler; - getRoleHandler; - createRoleHandler; - updateRoleHandler; - deleteRoleHandler; - roleAssignPermissionHandler; - roleRemovePermissionHandler; - constructor(getRolesHandler, getRoleHandler, createRoleHandler, updateRoleHandler, deleteRoleHandler, roleAssignPermissionHandler, roleRemovePermissionHandler) { - this.getRolesHandler = getRolesHandler; - this.getRoleHandler = getRoleHandler; - this.createRoleHandler = createRoleHandler; - this.updateRoleHandler = updateRoleHandler; - this.deleteRoleHandler = deleteRoleHandler; - this.roleAssignPermissionHandler = roleAssignPermissionHandler; - this.roleRemovePermissionHandler = roleRemovePermissionHandler; - } - async findAll(query) { - const res = await this.getRolesHandler.execute(query); - return { success: true, data: res.data, meta: { total: res.total } }; - } - async findOne(id) { - const role = await this.getRoleHandler.execute(id); - return { success: true, data: role, meta: {} }; - } - async create(body) { - const created = await this.createRoleHandler.execute(body); - return { success: true, data: created, meta: {} }; - } - async update(id, body) { - const updated = await this.updateRoleHandler.execute(id, body); - return { success: true, data: updated, meta: {} }; - } - async remove(id) { - await this.deleteRoleHandler.execute(id); - return { success: true, data: null, meta: {} }; - } - async assignPermission(id, body) { - const permissionId = body.permissionId; - const updated = await this.roleAssignPermissionHandler.execute(id, permissionId); - return { success: true, data: updated, meta: {} }; - } - async removePermission(id, permissionId) { - const updated = await this.roleRemovePermissionHandler.execute(id, permissionId); - return { success: true, data: updated, meta: {} }; - } -}; -exports.RolesController = RolesController; -__decorate([ - (0, common_1.Get)(), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_READ), - (0, swagger_1.ApiOperation)({ summary: 'List roles' }), - __param(0, (0, common_1.Query)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], RolesController.prototype, "findAll", null); -__decorate([ - (0, common_1.Get)(':id'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_READ), - (0, swagger_1.ApiOperation)({ summary: 'Get role' }), - __param(0, (0, common_1.Param)('id')), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], RolesController.prototype, "findOne", null); -__decorate([ - (0, common_1.Post)(), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_CREATE), - (0, swagger_1.ApiOperation)({ summary: 'Create role' }), - __param(0, (0, common_1.Body)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], RolesController.prototype, "create", null); -__decorate([ - (0, common_1.Patch)(':id'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_UPDATE), - (0, swagger_1.ApiOperation)({ summary: 'Update role' }), - __param(0, (0, common_1.Param)('id')), - __param(1, (0, common_1.Body)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String, Object]), - __metadata("design:returntype", Promise) -], RolesController.prototype, "update", null); -__decorate([ - (0, common_1.Delete)(':id'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_DELETE), - (0, swagger_1.ApiOperation)({ summary: 'Delete role' }), - __param(0, (0, common_1.Param)('id')), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], RolesController.prototype, "remove", null); -__decorate([ - (0, common_1.Post)(':id/permissions'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_UPDATE), - (0, swagger_1.ApiOperation)({ summary: 'Assign permission to role' }), - __param(0, (0, common_1.Param)('id')), - __param(1, (0, common_1.Body)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String, Object]), - __metadata("design:returntype", Promise) -], RolesController.prototype, "assignPermission", null); -__decorate([ - (0, common_1.Delete)(':id/permissions/:permissionId'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.ROLE_UPDATE), - (0, swagger_1.ApiOperation)({ summary: 'Remove permission from role' }), - __param(0, (0, common_1.Param)('id')), - __param(1, (0, common_1.Param)('permissionId')), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String, String]), - __metadata("design:returntype", Promise) -], RolesController.prototype, "removePermission", null); -exports.RolesController = RolesController = __decorate([ - (0, swagger_1.ApiTags)('Roles'), - (0, common_1.Controller)('roles'), - (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard, permission_guard_1.PermissionGuard), - __metadata("design:paramtypes", [get_roles_handler_1.GetRolesHandler, - get_role_handler_1.GetRoleHandler, - create_role_handler_1.CreateRoleHandler, - update_role_handler_1.UpdateRoleHandler, - delete_role_handler_1.DeleteRoleHandler, - assign_permission_handler_1.RoleAssignPermissionHandler, - remove_permission_handler_1.RoleRemovePermissionHandler]) -], RolesController); -//# sourceMappingURL=roles.controller.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/roles.controller.js.map b/dist/modules/identity/presentation/controllers/roles.controller.js.map deleted file mode 100644 index 95d1c04..0000000 --- a/dist/modules/identity/presentation/controllers/roles.controller.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"roles.controller.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/controllers/roles.controller.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/users.controller.js b/dist/modules/identity/presentation/controllers/users.controller.js deleted file mode 100644 index 0870038..0000000 --- a/dist/modules/identity/presentation/controllers/users.controller.js +++ /dev/null @@ -1,173 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var __param = (this && this.__param) || function (paramIndex, decorator) { - return function (target, key) { decorator(target, key, paramIndex); } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UsersController = void 0; -const common_1 = require("@nestjs/common"); -const swagger_1 = require("@nestjs/swagger"); -const jwt_auth_guard_1 = require("../../../../core/auth/guards/jwt-auth.guard"); -const permission_guard_1 = require("../../../authorization/presentation/guards/permission.guard"); -const current_user_guard_1 = require("../guards/current-user.guard"); -const permission_decorator_1 = require("../../../authorization/presentation/decorators/permission.decorator"); -const permission_constants_1 = require("../../../../common/constants/permission.constants"); -const get_users_handler_1 = require("../../application/handlers/user/get-users.handler"); -const get_user_handler_1 = require("../../application/handlers/user/get-user.handler"); -const get_current_user_handler_1 = require("../../application/handlers/user/get-current-user.handler"); -const enable_user_handler_1 = require("../../application/handlers/user/enable-user.handler"); -const disable_user_handler_1 = require("../../application/handlers/user/disable-user.handler"); -const delete_user_handler_1 = require("../../application/handlers/user/delete-user.handler"); -const restore_user_handler_1 = require("../../application/handlers/user/restore-user.handler"); -const update_user_handler_1 = require("../../application/handlers/user/update-user.handler"); -let UsersController = class UsersController { - getUsersHandler; - getUserHandler; - getCurrentUserHandler; - enableUserHandler; - disableUserHandler; - deleteUserHandler; - restoreUserHandler; - updateUserHandler; - constructor(getUsersHandler, getUserHandler, getCurrentUserHandler, enableUserHandler, disableUserHandler, deleteUserHandler, restoreUserHandler, updateUserHandler) { - this.getUsersHandler = getUsersHandler; - this.getUserHandler = getUserHandler; - this.getCurrentUserHandler = getCurrentUserHandler; - this.enableUserHandler = enableUserHandler; - this.disableUserHandler = disableUserHandler; - this.deleteUserHandler = deleteUserHandler; - this.restoreUserHandler = restoreUserHandler; - this.updateUserHandler = updateUserHandler; - } - async findAll(query) { - const res = await this.getUsersHandler.execute(query); - return { - success: true, - data: res.data.map(u => u.toResponse ? u.toResponse() : u), - meta: { total: res.total }, - }; - } - async me(req) { - const user = await this.getCurrentUserHandler.execute(req.currentUser); - return { success: true, data: user.toResponse ? user.toResponse() : user, meta: {} }; - } - async findOne(id) { - const user = await this.getUserHandler.execute(id); - return { success: true, data: user.toResponse ? user.toResponse() : user, meta: {} }; - } - async enable(id) { - const updated = await this.enableUserHandler.execute(id); - return { success: true, data: updated.toResponse ? updated.toResponse() : updated, meta: {} }; - } - async disable(id) { - const updated = await this.disableUserHandler.execute(id); - return { success: true, data: updated.toResponse ? updated.toResponse() : updated, meta: {} }; - } - async update(id, body) { - const updated = await this.updateUserHandler.execute(id, body); - return { success: true, data: updated.toResponse ? updated.toResponse() : updated, meta: {} }; - } - async remove(id) { - await this.deleteUserHandler.execute(id); - return { success: true, data: null, meta: {} }; - } - async restore(id) { - const restored = await this.restoreUserHandler.execute(id); - return { success: true, data: restored.toResponse ? restored.toResponse() : restored, meta: {} }; - } -}; -exports.UsersController = UsersController; -__decorate([ - (0, common_1.Get)(), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_READ), - (0, swagger_1.ApiOperation)({ summary: 'List users' }), - __param(0, (0, common_1.Query)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], UsersController.prototype, "findAll", null); -__decorate([ - (0, common_1.Get)('me'), - (0, swagger_1.ApiOperation)({ summary: 'Get current user' }), - __param(0, (0, common_1.Req)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], UsersController.prototype, "me", null); -__decorate([ - (0, common_1.Get)(':id'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_READ), - (0, swagger_1.ApiOperation)({ summary: 'Get user by id' }), - __param(0, (0, common_1.Param)('id')), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], UsersController.prototype, "findOne", null); -__decorate([ - (0, common_1.Patch)(':id/enable'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_UPDATE), - (0, swagger_1.ApiOperation)({ summary: 'Enable user' }), - __param(0, (0, common_1.Param)('id')), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], UsersController.prototype, "enable", null); -__decorate([ - (0, common_1.Patch)(':id/disable'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_UPDATE), - (0, swagger_1.ApiOperation)({ summary: 'Disable user' }), - __param(0, (0, common_1.Param)('id')), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], UsersController.prototype, "disable", null); -__decorate([ - (0, common_1.Patch)(':id'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_UPDATE), - (0, swagger_1.ApiOperation)({ summary: 'Update user profile' }), - __param(0, (0, common_1.Param)('id')), - __param(1, (0, common_1.Body)()), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String, Object]), - __metadata("design:returntype", Promise) -], UsersController.prototype, "update", null); -__decorate([ - (0, common_1.Delete)(':id'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_DELETE), - (0, swagger_1.ApiOperation)({ summary: 'Soft delete user' }), - __param(0, (0, common_1.Param)('id')), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], UsersController.prototype, "remove", null); -__decorate([ - (0, common_1.Post)(':id/restore'), - (0, permission_decorator_1.Permissions)(permission_constants_1.PermissionType.USER_UPDATE), - (0, swagger_1.ApiOperation)({ summary: 'Restore user' }), - __param(0, (0, common_1.Param)('id')), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], UsersController.prototype, "restore", null); -exports.UsersController = UsersController = __decorate([ - (0, swagger_1.ApiTags)('Users'), - (0, common_1.Controller)('users'), - (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, current_user_guard_1.CurrentUserGuard, permission_guard_1.PermissionGuard), - __metadata("design:paramtypes", [get_users_handler_1.GetUsersHandler, - get_user_handler_1.GetUserHandler, - get_current_user_handler_1.GetCurrentUserHandler, - enable_user_handler_1.EnableUserHandler, - disable_user_handler_1.DisableUserHandler, - delete_user_handler_1.DeleteUserHandler, - restore_user_handler_1.RestoreUserHandler, - update_user_handler_1.UpdateUserHandler]) -], UsersController); -//# sourceMappingURL=users.controller.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/users.controller.js.map b/dist/modules/identity/presentation/controllers/users.controller.js.map deleted file mode 100644 index b096e72..0000000 --- a/dist/modules/identity/presentation/controllers/users.controller.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"users.controller.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/controllers/users.controller.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-permission.dto.js b/dist/modules/identity/presentation/dto/create-permission.dto.js deleted file mode 100644 index 00c6af3..0000000 --- a/dist/modules/identity/presentation/dto/create-permission.dto.js +++ /dev/null @@ -1,39 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CreatePermissionDto = void 0; -const swagger_1 = require("@nestjs/swagger"); -const class_validator_1 = require("class-validator"); -class CreatePermissionDto { - code; - name; - description; -} -exports.CreatePermissionDto = CreatePermissionDto; -__decorate([ - (0, swagger_1.ApiProperty)(), - (0, class_validator_1.IsString)(), - (0, class_validator_1.IsNotEmpty)(), - __metadata("design:type", String) -], CreatePermissionDto.prototype, "code", void 0); -__decorate([ - (0, swagger_1.ApiProperty)(), - (0, class_validator_1.IsString)(), - (0, class_validator_1.IsNotEmpty)(), - __metadata("design:type", String) -], CreatePermissionDto.prototype, "name", void 0); -__decorate([ - (0, swagger_1.ApiProperty)({ required: false }), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsString)(), - __metadata("design:type", String) -], CreatePermissionDto.prototype, "description", void 0); -//# sourceMappingURL=create-permission.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-permission.dto.js.map b/dist/modules/identity/presentation/dto/create-permission.dto.js.map deleted file mode 100644 index f4bd483..0000000 --- a/dist/modules/identity/presentation/dto/create-permission.dto.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"create-permission.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/create-permission.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6CAA8C;AAC9C,qDAAmE;AAEnE,MAAa,mBAAmB;IAI9B,IAAI,CAAU;IAKd,IAAI,CAAU;IAKd,WAAW,CAAU;CACtB;AAfD,kDAeC;AAXC;IAHC,IAAA,qBAAW,GAAE;IACb,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;iDACC;AAKd;IAHC,IAAA,qBAAW,GAAE;IACb,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;iDACC;AAKd;IAHC,IAAA,qBAAW,EAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAChC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;wDACU"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-role.dto.js b/dist/modules/identity/presentation/dto/create-role.dto.js deleted file mode 100644 index 5636063..0000000 --- a/dist/modules/identity/presentation/dto/create-role.dto.js +++ /dev/null @@ -1,45 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CreateRoleDto = void 0; -const swagger_1 = require("@nestjs/swagger"); -const class_validator_1 = require("class-validator"); -class CreateRoleDto { - code; - name; - description; - isDefault; -} -exports.CreateRoleDto = CreateRoleDto; -__decorate([ - (0, swagger_1.ApiProperty)(), - (0, class_validator_1.IsString)(), - (0, class_validator_1.IsNotEmpty)(), - __metadata("design:type", String) -], CreateRoleDto.prototype, "code", void 0); -__decorate([ - (0, swagger_1.ApiProperty)(), - (0, class_validator_1.IsString)(), - (0, class_validator_1.IsNotEmpty)(), - __metadata("design:type", String) -], CreateRoleDto.prototype, "name", void 0); -__decorate([ - (0, swagger_1.ApiProperty)({ required: false }), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsString)(), - __metadata("design:type", String) -], CreateRoleDto.prototype, "description", void 0); -__decorate([ - (0, swagger_1.ApiProperty)({ required: false }), - (0, class_validator_1.IsOptional)(), - __metadata("design:type", Boolean) -], CreateRoleDto.prototype, "isDefault", void 0); -//# sourceMappingURL=create-role.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-role.dto.js.map b/dist/modules/identity/presentation/dto/create-role.dto.js.map deleted file mode 100644 index 142f10b..0000000 --- a/dist/modules/identity/presentation/dto/create-role.dto.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"create-role.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/create-role.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6CAA8C;AAC9C,qDAAmE;AAEnE,MAAa,aAAa;IAIxB,IAAI,CAAU;IAKd,IAAI,CAAU;IAKd,WAAW,CAAU;IAIrB,SAAS,CAAW;CACrB;AAnBD,sCAmBC;AAfC;IAHC,IAAA,qBAAW,GAAE;IACb,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;2CACC;AAKd;IAHC,IAAA,qBAAW,GAAE;IACb,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;2CACC;AAKd;IAHC,IAAA,qBAAW,EAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAChC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;kDACU;AAIrB;IAFC,IAAA,qBAAW,EAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAChC,IAAA,4BAAU,GAAE;;gDACO"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-user.dto.js b/dist/modules/identity/presentation/dto/create-user.dto.js deleted file mode 100644 index fd68ca9..0000000 --- a/dist/modules/identity/presentation/dto/create-user.dto.js +++ /dev/null @@ -1,54 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CreateUserDto = void 0; -const class_validator_1 = require("class-validator"); -const swagger_1 = require("@nestjs/swagger"); -class CreateUserDto { - username; - email; - password; - storageQuota; - storageUsed; -} -exports.CreateUserDto = CreateUserDto; -__decorate([ - (0, swagger_1.ApiProperty)({ example: 'John Doe' }), - (0, class_validator_1.IsString)(), - (0, class_validator_1.IsNotEmpty)(), - __metadata("design:type", String) -], CreateUserDto.prototype, "username", void 0); -__decorate([ - (0, swagger_1.ApiProperty)({ example: 'user@example.com' }), - (0, class_validator_1.IsEmail)(), - __metadata("design:type", String) -], CreateUserDto.prototype, "email", void 0); -__decorate([ - (0, swagger_1.ApiProperty)({ example: 'strongpassword' }), - (0, class_validator_1.IsString)(), - (0, class_validator_1.Length)(8, 128), - __metadata("design:type", String) -], CreateUserDto.prototype, "password", void 0); -__decorate([ - (0, swagger_1.ApiPropertyOptional)({ example: 10737418240 }), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsInt)(), - (0, class_validator_1.Min)(0), - __metadata("design:type", Number) -], CreateUserDto.prototype, "storageQuota", void 0); -__decorate([ - (0, swagger_1.ApiPropertyOptional)({ example: 0 }), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsInt)(), - (0, class_validator_1.Min)(0), - __metadata("design:type", Number) -], CreateUserDto.prototype, "storageUsed", void 0); -//# sourceMappingURL=create-user.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-user.dto.js.map b/dist/modules/identity/presentation/dto/create-user.dto.js.map deleted file mode 100644 index 437e888..0000000 --- a/dist/modules/identity/presentation/dto/create-user.dto.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"create-user.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/create-user.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAAgG;AAChG,6CAAmE;AAEnE,MAAa,aAAa;IAIxB,QAAQ,CAAU;IAIlB,KAAK,CAAU;IAKf,QAAQ,CAAU;IAMlB,YAAY,CAAU;IAMtB,WAAW,CAAU;CACtB;AA1BD,sCA0BC;AAtBC;IAHC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IACpC,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;+CACK;AAIlB;IAFC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC5C,IAAA,yBAAO,GAAE;;4CACK;AAKf;IAHC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC1C,IAAA,0BAAQ,GAAE;IACV,IAAA,wBAAM,EAAC,CAAC,EAAE,GAAG,CAAC;;+CACG;AAMlB;IAJC,IAAA,6BAAmB,EAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAC7C,IAAA,4BAAU,GAAE;IACZ,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;;mDACe;AAMtB;IAJC,IAAA,6BAAmB,EAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACnC,IAAA,4BAAU,GAAE;IACZ,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;;kDACc"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-permission.dto.js b/dist/modules/identity/presentation/dto/update-permission.dto.js deleted file mode 100644 index 4327f0c..0000000 --- a/dist/modules/identity/presentation/dto/update-permission.dto.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UpdatePermissionDto = void 0; -const mapped_types_1 = require("@nestjs/mapped-types"); -const create_permission_dto_1 = require("./create-permission.dto"); -const swagger_1 = require("@nestjs/swagger"); -const class_validator_1 = require("class-validator"); -class UpdatePermissionDto extends (0, mapped_types_1.PartialType)(create_permission_dto_1.CreatePermissionDto) { - name; - description; - code; -} -exports.UpdatePermissionDto = UpdatePermissionDto; -__decorate([ - (0, swagger_1.ApiPropertyOptional)(), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsString)(), - __metadata("design:type", String) -], UpdatePermissionDto.prototype, "name", void 0); -__decorate([ - (0, swagger_1.ApiPropertyOptional)(), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsString)(), - __metadata("design:type", String) -], UpdatePermissionDto.prototype, "description", void 0); -__decorate([ - (0, swagger_1.ApiPropertyOptional)(), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsString)(), - __metadata("design:type", String) -], UpdatePermissionDto.prototype, "code", void 0); -//# sourceMappingURL=update-permission.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-permission.dto.js.map b/dist/modules/identity/presentation/dto/update-permission.dto.js.map deleted file mode 100644 index 9b4514d..0000000 --- a/dist/modules/identity/presentation/dto/update-permission.dto.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"update-permission.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/update-permission.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uDAAmD;AACnD,mEAA8D;AAC9D,6CAAsD;AACtD,qDAAuD;AAEvD,MAAa,mBAAoB,SAAQ,IAAA,0BAAW,EAAC,2CAAmB,CAAC;IAIvE,IAAI,CAAU;IAKd,WAAW,CAAU;IAKrB,IAAI,CAAU;CACf;AAfD,kDAeC;AAXC;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;iDACG;AAKd;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;wDACU;AAKrB;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;iDACG"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-role.dto.js b/dist/modules/identity/presentation/dto/update-role.dto.js deleted file mode 100644 index b02841b..0000000 --- a/dist/modules/identity/presentation/dto/update-role.dto.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UpdateRoleDto = void 0; -const mapped_types_1 = require("@nestjs/mapped-types"); -const create_role_dto_1 = require("./create-role.dto"); -const swagger_1 = require("@nestjs/swagger"); -const class_validator_1 = require("class-validator"); -class UpdateRoleDto extends (0, mapped_types_1.PartialType)(create_role_dto_1.CreateRoleDto) { - name; - description; - isDefault; -} -exports.UpdateRoleDto = UpdateRoleDto; -__decorate([ - (0, swagger_1.ApiPropertyOptional)(), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsString)(), - __metadata("design:type", String) -], UpdateRoleDto.prototype, "name", void 0); -__decorate([ - (0, swagger_1.ApiPropertyOptional)(), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsString)(), - __metadata("design:type", String) -], UpdateRoleDto.prototype, "description", void 0); -__decorate([ - (0, swagger_1.ApiPropertyOptional)(), - (0, class_validator_1.IsOptional)(), - __metadata("design:type", Boolean) -], UpdateRoleDto.prototype, "isDefault", void 0); -//# sourceMappingURL=update-role.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-role.dto.js.map b/dist/modules/identity/presentation/dto/update-role.dto.js.map deleted file mode 100644 index a4905b2..0000000 --- a/dist/modules/identity/presentation/dto/update-role.dto.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"update-role.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/update-role.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uDAAmD;AACnD,uDAAkD;AAClD,6CAAsD;AACtD,qDAAuD;AAEvD,MAAa,aAAc,SAAQ,IAAA,0BAAW,EAAC,+BAAa,CAAC;IAI3D,IAAI,CAAU;IAKd,WAAW,CAAU;IAIrB,SAAS,CAAW;CACrB;AAdD,sCAcC;AAVC;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;2CACG;AAKd;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;kDACU;AAIrB;IAFC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;;gDACO"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-user.dto.js b/dist/modules/identity/presentation/dto/update-user.dto.js deleted file mode 100644 index ada8a2b..0000000 --- a/dist/modules/identity/presentation/dto/update-user.dto.js +++ /dev/null @@ -1,63 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UpdateUserDto = void 0; -const mapped_types_1 = require("@nestjs/mapped-types"); -const create_user_dto_1 = require("./create-user.dto"); -const swagger_1 = require("@nestjs/swagger"); -const class_validator_1 = require("class-validator"); -class UpdateUserDto extends (0, mapped_types_1.PartialType)(create_user_dto_1.CreateUserDto) { - name; - email; - password; - metadata; - storageQuota; - storageUsed; -} -exports.UpdateUserDto = UpdateUserDto; -__decorate([ - (0, swagger_1.ApiPropertyOptional)(), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsString)(), - __metadata("design:type", String) -], UpdateUserDto.prototype, "name", void 0); -__decorate([ - (0, swagger_1.ApiPropertyOptional)(), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsEmail)(), - __metadata("design:type", String) -], UpdateUserDto.prototype, "email", void 0); -__decorate([ - (0, swagger_1.ApiPropertyOptional)(), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.Length)(8, 128), - __metadata("design:type", String) -], UpdateUserDto.prototype, "password", void 0); -__decorate([ - (0, swagger_1.ApiPropertyOptional)(), - (0, class_validator_1.IsOptional)(), - __metadata("design:type", Object) -], UpdateUserDto.prototype, "metadata", void 0); -__decorate([ - (0, swagger_1.ApiPropertyOptional)({ example: 10737418240 }), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsInt)(), - (0, class_validator_1.Min)(0), - __metadata("design:type", Number) -], UpdateUserDto.prototype, "storageQuota", void 0); -__decorate([ - (0, swagger_1.ApiPropertyOptional)({ example: 0 }), - (0, class_validator_1.IsOptional)(), - (0, class_validator_1.IsInt)(), - (0, class_validator_1.Min)(0), - __metadata("design:type", Number) -], UpdateUserDto.prototype, "storageUsed", void 0); -//# sourceMappingURL=update-user.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-user.dto.js.map b/dist/modules/identity/presentation/dto/update-user.dto.js.map deleted file mode 100644 index 32d013b..0000000 --- a/dist/modules/identity/presentation/dto/update-user.dto.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"update-user.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/update-user.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uDAAmD;AACnD,uDAAkD;AAClD,6CAAsD;AACtD,qDAAoF;AAEpF,MAAa,aAAc,SAAQ,IAAA,0BAAW,EAAC,+BAAa,CAAC;IAI3D,IAAI,CAAU;IAKd,KAAK,CAAU;IAKf,QAAQ,CAAU;IAIlB,QAAQ,CAAuB;IAM/B,YAAY,CAAU;IAMtB,WAAW,CAAU;CACtB;AA/BD,sCA+BC;AA3BC;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;2CACG;AAKd;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,yBAAO,GAAE;;4CACK;AAKf;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAM,EAAC,CAAC,EAAE,GAAG,CAAC;;+CACG;AAIlB;IAFC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;;+CACkB;AAM/B;IAJC,IAAA,6BAAmB,EAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAC7C,IAAA,4BAAU,GAAE;IACZ,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;;mDACe;AAMtB;IAJC,IAAA,6BAAmB,EAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACnC,IAAA,4BAAU,GAAE;IACZ,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;;kDACc"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/guards/current-user.guard.js b/dist/modules/identity/presentation/guards/current-user.guard.js deleted file mode 100644 index 7d1bc03..0000000 --- a/dist/modules/identity/presentation/guards/current-user.guard.js +++ /dev/null @@ -1,44 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CurrentUserGuard = void 0; -const common_1 = require("@nestjs/common"); -const sync_identity_handler_1 = require("../../application/handlers/user/sync-identity.handler"); -let CurrentUserGuard = class CurrentUserGuard { - syncIdentityHandler; - constructor(syncIdentityHandler) { - this.syncIdentityHandler = syncIdentityHandler; - } - async canActivate(context) { - const request = context.switchToHttp().getRequest(); - // Prefer RequestContext produced by AuthenticationService - const ctx = request.raylabContext; - if (ctx && ctx.user) { - request.currentUser = ctx.user; - return true; - } - // Fallback to legacy identity if present - const identity = request.identity; - if (!identity) { - throw new common_1.UnauthorizedException('Identity is missing.'); - } - const user = await this.syncIdentityHandler.execute(identity); - // attach domain user as currentUser - request.currentUser = user; - return true; - } -}; -exports.CurrentUserGuard = CurrentUserGuard; -exports.CurrentUserGuard = CurrentUserGuard = __decorate([ - (0, common_1.Injectable)(), - __metadata("design:paramtypes", [sync_identity_handler_1.SyncIdentityHandler]) -], CurrentUserGuard); -//# sourceMappingURL=current-user.guard.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/guards/current-user.guard.js.map b/dist/modules/identity/presentation/guards/current-user.guard.js.map deleted file mode 100644 index d22bdf2..0000000 --- a/dist/modules/identity/presentation/guards/current-user.guard.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"current-user.guard.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/guards/current-user.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAkG;AAClG,iGAA4F;AAIrF,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAER;IADnB,YACmB,mBAAwC;QAAxC,wBAAmB,GAAnB,mBAAmB,CAAqB;IACxD,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAS,CAAC;QAE3D,0DAA0D;QAC1D,MAAM,GAAG,GAAG,OAAO,CAAC,aAAoB,CAAC;QACzC,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YACpB,OAAO,CAAC,WAAW,GAAG,GAAG,CAAC,IAAI,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,yCAAyC;QACzC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAoC,CAAC;QAE9D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,8BAAqB,CAAC,sBAAsB,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAE9D,oCAAoC;QACpC,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;QAE3B,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AA7BY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;qCAG6B,2CAAmB;GAFhD,gBAAgB,CA6B5B"} \ No newline at end of file diff --git a/dist/shared/cache-keys.js b/dist/shared/cache-keys.js deleted file mode 100644 index d4ddb65..0000000 --- a/dist/shared/cache-keys.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CacheKeys = void 0; -exports.CacheKeys = { - permission: (userId) => `permissions:${userId}`, -}; -//# sourceMappingURL=cache-keys.js.map \ No newline at end of file diff --git a/dist/shared/cache-keys.js.map b/dist/shared/cache-keys.js.map deleted file mode 100644 index 2d423a2..0000000 --- a/dist/shared/cache-keys.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"cache-keys.js","sourceRoot":"","sources":["../../src/shared/cache-keys.ts"],"names":[],"mappings":";;;AAAa,QAAA,SAAS,GAAG;IACvB,UAAU,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,eAAe,MAAM,EAAE;CACxD,CAAC"} \ No newline at end of file diff --git a/dist/shared/prisma.service.js b/dist/shared/prisma.service.js deleted file mode 100644 index a5f7bec..0000000 --- a/dist/shared/prisma.service.js +++ /dev/null @@ -1,24 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PrismaService = void 0; -const common_1 = require("@nestjs/common"); -const client_1 = require("@prisma/client"); -let PrismaService = class PrismaService extends client_1.PrismaClient { - async onModuleInit() { - await this.$connect(); - } - async onModuleDestroy() { - await this.$disconnect(); - } -}; -exports.PrismaService = PrismaService; -exports.PrismaService = PrismaService = __decorate([ - (0, common_1.Injectable)() -], PrismaService); -//# sourceMappingURL=prisma.service.js.map \ No newline at end of file diff --git a/dist/shared/prisma.service.js.map b/dist/shared/prisma.service.js.map deleted file mode 100644 index 0776229..0000000 --- a/dist/shared/prisma.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"prisma.service.js","sourceRoot":"","sources":["../../src/shared/prisma.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA2E;AAC3E,2CAA8C;AAGvC,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,qBAAY;IAC7C,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;IAC3B,CAAC;CACF,CAAA;AARY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;GACA,aAAa,CAQzB"} \ No newline at end of file diff --git a/dist/shared/redis.service.js b/dist/shared/redis.service.js deleted file mode 100644 index 0567a3a..0000000 --- a/dist/shared/redis.service.js +++ /dev/null @@ -1,121 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -var RedisService_1; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RedisService = void 0; -const common_1 = require("@nestjs/common"); -const ioredis_1 = __importDefault(require("ioredis")); -let RedisService = RedisService_1 = class RedisService { - // Keep client typed as any to avoid tight coupling to ioredis types in tests - client = null; - logger = new common_1.Logger(RedisService_1.name); - lastLogAt = 0; - LOG_THROTTLE_MS = 5000; // throttle repeated error logs - onModuleInit() { - const enabled = (process.env.REDIS_ENABLED || 'true').toLowerCase() === 'true'; - if (!enabled) { - this.logger.log('Redis disabled via REDIS_ENABLED=false'); - return; - } - const url = process.env.REDIS_URL || 'redis://localhost:6379'; - // Use lazyConnect so application can start even if Redis is unavailable temporarily - this.client = new ioredis_1.default(url, { - lazyConnect: true, - // limit retries to avoid infinite reconnect storms - maxRetriesPerRequest: 5, - // automatic reconnection strategy - reconnectOnError: (err) => { - return true; - }, - enableOfflineQueue: true, - // optional reconnect strategy - retryStrategy: (times) => { - // exponential backoff capped at 5s - const delay = Math.min(50 * Math.pow(2, times), 5000); - return delay; - }, - }); - this.client.on('connect', () => this.logger.log('Connected to Redis')); - this.client.on('ready', () => this.logger.log('Redis ready')); - this.client.on('error', (err) => this.handleError(err)); - this.client.on('close', () => this.logger.warn('Redis connection closed')); - this.client.on('reconnecting', () => this.logger.log('Redis reconnecting')); - // attempt to connect but do not throw if it fails - this.client.connect().catch((err) => { - this.handleError(err); - }); - } - handleError(err) { - const now = Date.now(); - if (now - this.lastLogAt > this.LOG_THROTTLE_MS) { - this.logger.error('Redis error', err instanceof Error ? err.message : err); - this.lastLogAt = now; - } - } - onModuleDestroy() { - if (this.client) { - try { - this.client.disconnect(); - } - catch (e) { - // ignore - } - } - } - getClient() { - return this.client; - } - ensureClient() { - // returns true if client is connected/usable - return this.client && this.client.status && this.client.status !== 'end' && this.client.status !== 'close'; - } - async get(key) { - if (!this.ensureClient()) - return null; - try { - return await this.client.get(key); - } - catch (e) { - this.handleError(e); - return null; - } - } - async set(key, value, ttlSeconds) { - if (!this.ensureClient()) - return; - try { - if (ttlSeconds) { - await this.client.set(key, value, 'EX', ttlSeconds); - } - else { - await this.client.set(key, value); - } - } - catch (e) { - this.handleError(e); - } - } - async del(key) { - if (!this.ensureClient()) - return; - try { - await this.client.del(key); - } - catch (e) { - this.handleError(e); - } - } -}; -exports.RedisService = RedisService; -exports.RedisService = RedisService = RedisService_1 = __decorate([ - (0, common_1.Injectable)() -], RedisService); -//# sourceMappingURL=redis.service.js.map \ No newline at end of file diff --git a/dist/shared/redis.service.js.map b/dist/shared/redis.service.js.map deleted file mode 100644 index c3ef62d..0000000 --- a/dist/shared/redis.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"redis.service.js","sourceRoot":"","sources":["../../src/shared/redis.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/shared/types/request-context.js b/dist/shared/types/request-context.js deleted file mode 100644 index 8a495a4..0000000 --- a/dist/shared/types/request-context.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=request-context.js.map \ No newline at end of file diff --git a/dist/shared/types/request-context.js.map b/dist/shared/types/request-context.js.map deleted file mode 100644 index 31d227a..0000000 --- a/dist/shared/types/request-context.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../../src/shared/types/request-context.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/shared/utils/index.js b/dist/shared/utils/index.js deleted file mode 100644 index 0dc1a78..0000000 --- a/dist/shared/utils/index.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.noop = noop; -function noop() { } -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/shared/utils/index.js.map b/dist/shared/utils/index.js.map deleted file mode 100644 index a39412e..0000000 --- a/dist/shared/utils/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/shared/utils/index.ts"],"names":[],"mappings":";;AAAA,oBAAyB;AAAzB,SAAgB,IAAI,KAAI,CAAC"} \ No newline at end of file diff --git a/src/core/auth/guards/jwt-auth.guard.ts b/src/core/auth/guards/jwt-auth.guard.ts index 0283ee1..15e7082 100644 --- a/src/core/auth/guards/jwt-auth.guard.ts +++ b/src/core/auth/guards/jwt-auth.guard.ts @@ -57,7 +57,7 @@ export class JwtAuthGuard implements CanActivate { audience: process.env.AUTHENTIK_AUDIENCE, }); - const identity = new IdentityData( + const identity = new IdentityData( payload.sub as string, (payload as any).preferred_username as string | undefined, (payload as any).email as string | undefined, @@ -65,6 +65,8 @@ export class JwtAuthGuard implements CanActivate { ); request.identity = identity; + // mark as external (verified by Authentik JWKS) + (request as any).identitySource = 'external'; this.logger.debug(`Verified token using external JWKS. sub=${payload.sub}`); return true; } catch (err) { @@ -81,7 +83,7 @@ export class JwtAuthGuard implements CanActivate { } try { - const payload = jwt.verify(token, secret) as any; + const payload = jwt.verify(token, secret) as any; const identity = new IdentityData( payload.sub as string, @@ -91,6 +93,8 @@ export class JwtAuthGuard implements CanActivate { ); request.identity = identity; + // mark as internal (verified by RayLab internal secret) + (request as any).identitySource = 'internal'; this.logger.debug(`Verified token using internal secret. sub=${payload.sub}`); return true; } catch (err: any) { diff --git a/src/core/auth/interfaces/authenticated-request.interface.ts b/src/core/auth/interfaces/authenticated-request.interface.ts index 03019a1..4e9046d 100644 --- a/src/core/auth/interfaces/authenticated-request.interface.ts +++ b/src/core/auth/interfaces/authenticated-request.interface.ts @@ -2,11 +2,16 @@ import { Request } from 'express'; import { UserData } from '../../../modules/identity/domain/entities/user.entity'; import { IdentityData } from './identity-data'; +export type IdentitySource = 'internal' | 'external'; + export interface AuthenticatedRequest extends Request { // Identity comes from the external Identity Provider (Authentik) // JwtAuthGuard must set request.identity = payload identity?: IdentityData; + // Indicate verification source: 'external' => verified via Authentik JWKS; 'internal' => verified via RayLab internal secret + identitySource?: IdentitySource; + // After CurrentUserGuard resolves the user from repository, it must set request.currentUser = User Domain currentUser?: UserData; } \ No newline at end of file diff --git a/src/modules/identity/presentation/guards/current-user.guard.ts b/src/modules/identity/presentation/guards/current-user.guard.ts index 2b1743a..a837015 100644 --- a/src/modules/identity/presentation/guards/current-user.guard.ts +++ b/src/modules/identity/presentation/guards/current-user.guard.ts @@ -1,15 +1,18 @@ -import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common'; +import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, Inject } from '@nestjs/common'; import { SyncIdentityHandler } from '../../application/handlers/user/sync-identity.handler'; import { IdentityData } from '../../../../core/auth/interfaces/identity-data'; +import { IUser } from '../../domain/repositories/user.interface'; +import { AuthenticatedRequest, IdentitySource } from '../../../../core/auth/interfaces/authenticated-request.interface'; @Injectable() export class CurrentUserGuard implements CanActivate { constructor( private readonly syncIdentityHandler: SyncIdentityHandler, + @Inject(IUser) private readonly userRepository: IUser, ) {} async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest() as any; + const request = context.switchToHttp().getRequest() as AuthenticatedRequest & { raylabContext?: any }; // Prefer RequestContext produced by AuthenticationService const ctx = request.raylabContext as any; @@ -25,6 +28,19 @@ export class CurrentUserGuard implements CanActivate { throw new UnauthorizedException('Identity is missing.'); } + const source: IdentitySource | undefined = request.identitySource; + + if (source === 'internal') { + try { + const user = await this.userRepository.getById(identity.sub); + request.currentUser = user; + return true; + } catch (e) { + // convert repository not-found to Unauthorized + throw new UnauthorizedException('User not found.'); + } + } + const user = await this.syncIdentityHandler.execute(identity); // attach domain user as currentUser diff --git a/tests/unit/current-user.guard.spec.ts b/tests/unit/current-user.guard.spec.ts new file mode 100644 index 0000000..e7581b4 --- /dev/null +++ b/tests/unit/current-user.guard.spec.ts @@ -0,0 +1,65 @@ +import { CurrentUserGuard } from '../../src/modules/identity/presentation/guards/current-user.guard'; +import { SyncIdentityHandler } from '../../src/modules/identity/application/handlers/user/sync-identity.handler'; +import { IUser } from '../../src/modules/identity/domain/repositories/user.interface'; +import { IdentityData } from '../../src/core/auth/interfaces/identity-data'; +import { UnauthorizedException } from '@nestjs/common'; + +describe('CurrentUserGuard', () => { + let guard: CurrentUserGuard; + let mockSync: Partial; + let mockUserRepo: Partial; + + beforeEach(() => { + mockSync = { execute: jest.fn() }; + mockUserRepo = { getById: jest.fn() }; + guard = new CurrentUserGuard(mockSync as any, mockUserRepo as any); + }); + + function makeContext(req: any): any { + return { switchToHttp: () => ({ getRequest: () => req }) } as any; + } + + test('raylabContext fast path sets currentUser and returns true', async () => { + const user = { id: 'u1' } as any; + const req: any = { raylabContext: { user } }; + const res = await guard.canActivate(makeContext(req)); + expect(res).toBe(true); + expect(req.currentUser).toBe(user); + }); + + test('missing identity throws UnauthorizedException', async () => { + const req: any = {}; + await expect(guard.canActivate(makeContext(req))).rejects.toThrow(UnauthorizedException); + }); + + test('internal identity resolves by getById and does not call sync', async () => { + const identity = new IdentityData('uid', 'u', 'e'); + const req: any = { identity, identitySource: 'internal' }; + (mockUserRepo.getById as jest.Mock).mockResolvedValue({ id: 'uid', roles: [], permissions: [] }); + + const res = await guard.canActivate(makeContext(req)); + expect(res).toBe(true); + expect(req.currentUser).toBeDefined(); + expect((mockSync.execute as jest.Mock)).not.toHaveBeenCalled(); + }); + + test('internal identity with missing user throws UnauthorizedException', async () => { + const identity = new IdentityData('missing', 'u', 'e'); + const req: any = { identity, identitySource: 'internal' }; + (mockUserRepo.getById as jest.Mock).mockRejectedValue(new Error('User tidak ditemukan.')); + + await expect(guard.canActivate(makeContext(req))).rejects.toThrow(UnauthorizedException); + expect((mockSync.execute as jest.Mock)).not.toHaveBeenCalled(); + }); + + test('external identity calls syncIdentityHandler', async () => { + const identity = new IdentityData('extsub', 'u', 'e'); + const req: any = { identity, identitySource: 'external' }; + (mockSync.execute as jest.Mock).mockResolvedValue({ id: 'user-ext' }); + + const res = await guard.canActivate(makeContext(req)); + expect(res).toBe(true); + expect(req.currentUser).toBeDefined(); + expect((mockUserRepo.getById as jest.Mock)).not.toHaveBeenCalled(); + }); +}); diff --git a/tests/unit/jwt-auth.guard.spec.ts b/tests/unit/jwt-auth.guard.spec.ts new file mode 100644 index 0000000..b1a65be --- /dev/null +++ b/tests/unit/jwt-auth.guard.spec.ts @@ -0,0 +1,69 @@ +// Mock 'jose' module before importing JwtAuthGuard so Jest doesn't try to parse ESM from node_modules +let mockJwtVerify: (token: any) => Promise = async () => { throw new Error('no jwks') }; + +jest.mock('jose', () => ({ + createRemoteJWKSet: () => ({}), + jwtVerify: async (token: any, jwks: any, opts: any) => mockJwtVerify(token), +})); + +import { JwtAuthGuard } from '../../src/core/auth/guards/jwt-auth.guard'; +import { ExecutionContext } from '@nestjs/common'; +import * as jwt from 'jsonwebtoken'; + +// These tests focus on identitySource being set based on verification path. + +describe('JwtAuthGuard', () => { + let guard: JwtAuthGuard; + + beforeEach(() => { + guard = new JwtAuthGuard(); + process.env.RAYLAB_JWT_SECRET = 'test-secret'; + delete process.env.AUTHENTIK_JWKS_URI; // ensure JWKS not attempted unless test sets it + // reset mock behavior + mockJwtVerify = async () => { throw new Error('no jwks') }; + }); + + function makeCtxWithAuthHeader(token: string) { + const req: any = { headers: { authorization: `Bearer ${token}` }, cookies: {} }; + const ctx: any = { switchToHttp: () => ({ getRequest: () => req }) } as ExecutionContext; + return { ctx, req }; + } + + test('internal verification sets identitySource=internal', async () => { + const token = jwt.sign({ sub: 'uid', preferred_username: 'u', email: 'e' }, process.env.RAYLAB_JWT_SECRET || 'test-secret'); + const { ctx, req } = makeCtxWithAuthHeader(token); + + const res = await guard.canActivate(ctx); + expect(res).toBe(true); + expect(req.identity).toBeDefined(); + expect((req.identitySource)).toBe('internal'); + }); + + test('external verification sets identitySource=external if JWKS verifies', async () => { + process.env.AUTHENTIK_JWKS_URI = 'https://example.com/.well-known/jwks.json'; + // set mock to succeed + mockJwtVerify = async (token: any) => ({ payload: { sub: 'extsub', preferred_username: 'eu', email: 'ee' } }); + + const token = 'dummy'; + const { ctx, req } = makeCtxWithAuthHeader(token); + + const res = await guard.canActivate(ctx); + expect(res).toBe(true); + expect(req.identity).toBeDefined(); + expect((req.identitySource)).toBe('external'); + }); + + test('external verification fails then internal succeeds -> identitySource=internal', async () => { + process.env.AUTHENTIK_JWKS_URI = 'https://example.com/.well-known/jwks.json'; + // make jwks fail + mockJwtVerify = async (token: any) => { throw new Error('jwks fail'); }; + + const token = jwt.sign({ sub: 'uid2', preferred_username: 'u2', email: 'e2' }, process.env.RAYLAB_JWT_SECRET || 'test-secret'); + const { ctx, req } = makeCtxWithAuthHeader(token); + + const res = await guard.canActivate(ctx); + expect(res).toBe(true); + expect(req.identity).toBeDefined(); + expect((req.identitySource)).toBe('internal'); + }); +}); From a50b57b5ea0a527914d68d6d1d3f949953cb17b9 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Sun, 9 Aug 2026 18:43:55 +0700 Subject: [PATCH 31/34] fix cant build --- src/core/auth/guards/jwt.guard.ts | 4 ++-- src/core/auth/guards/refresh.guard.ts | 4 ++-- src/modules/auth/auth.controller.ts | 4 ++-- src/modules/auth/auth.service.ts | 18 ++++++++++-------- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/core/auth/guards/jwt.guard.ts b/src/core/auth/guards/jwt.guard.ts index 0b7a1b5..f6e68bb 100644 --- a/src/core/auth/guards/jwt.guard.ts +++ b/src/core/auth/guards/jwt.guard.ts @@ -11,12 +11,12 @@ export class JwtGuard extends AuthGuard('jwt') { return super.canActivate(context); } - handleRequest(err: any, user: any, info: any) { + handleRequest(err: any, user: any, info: any, context?: any) { if (err || !user) { this.logger.debug(`JwtGuard handleRequest failed. err=${err} user=${!!user} info=${JSON.stringify(info)}`); } else { this.logger.debug(`JwtGuard handleRequest success user=${JSON.stringify(user)}`); } - return super.handleRequest(err, user, info); + return super.handleRequest(err, user, info, context); } } diff --git a/src/core/auth/guards/refresh.guard.ts b/src/core/auth/guards/refresh.guard.ts index 817a0c9..e407d2d 100644 --- a/src/core/auth/guards/refresh.guard.ts +++ b/src/core/auth/guards/refresh.guard.ts @@ -11,12 +11,12 @@ export class RefreshGuard extends AuthGuard('refresh') { return super.canActivate(context); } - handleRequest(err: any, user: any, info: any) { + handleRequest(err: any, user: any, info: any, context?: any) { if (err || !user) { this.logger.debug(`RefreshGuard handleRequest failed. err=${err} user=${!!user} info=${JSON.stringify(info)}`); } else { this.logger.debug(`RefreshGuard handleRequest success userId=${user.id} authInfo=${JSON.stringify(info)}`); } - return super.handleRequest(err, user, info); + return super.handleRequest(err, user, info, context); } } diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index cfbc36b..42516e9 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -3,8 +3,8 @@ import { Controller, Get, Post, Query, Res, Req, Body, HttpCode, HttpStatus, Log import { ApiTags, ApiOperation } from '@nestjs/swagger'; import { AuthService } from './auth.service'; import { Response, Request } from 'express'; -import { JwtGuard } from '../../../core/auth/guards/jwt.guard'; -import { RefreshGuard } from '../../../core/auth/guards/refresh.guard'; +import { JwtGuard } from '../../core/auth/guards/jwt.guard'; +import { RefreshGuard } from '../../core/auth/guards/refresh.guard'; import { ConfigService } from '@nestjs/config'; @ApiTags('Auth') diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 5f83179..05d95dc 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -6,7 +6,9 @@ import { IUser } from '../identity/domain/repositories/user.interface'; import { PrismaService } from '../../shared/prisma.service'; import { RedisPkceStore } from './pkce/redis-pkce.store'; import { InMemoryRefreshStore } from './refresh/inmemory-refresh.store'; -import { Issuer, generators, Client, TokenSet } from 'openid-client'; +// dynamic require to avoid TypeScript typing issues with installed openid-client +// eslint-disable-next-line @typescript-eslint/no-var-requires +const OpenIDClient = require('openid-client'); import crypto from 'crypto'; const logger = new Logger('AuthService'); @@ -23,14 +25,14 @@ export class AuthService { private readonly refreshStore: InMemoryRefreshStore, ) {} - private issuer: Issuer | null = null; - private client: Client | null = null; + private issuer: any | null = null; + private client: any | null = null; private async getIssuer() { if (this.issuer) return this.issuer; const issuerUrl = this.config.get('AUTHENTIK_ISSUER'); if (!issuerUrl) throw new Error('AUTHENTIK_ISSUER not configured'); - this.issuer = await Issuer.discover(issuerUrl); + this.issuer = await OpenIDClient.Issuer.discover(issuerUrl); return this.issuer; } @@ -53,9 +55,9 @@ export class AuthService { } const state = crypto.randomUUID(); - const code_verifier = generators.codeVerifier(); - const code_challenge = await generators.codeChallenge(code_verifier); - const nonce = generators.nonce(); + const code_verifier = OpenIDClient.generators.codeVerifier(); + const code_challenge = await OpenIDClient.generators.codeChallenge(code_verifier); + const nonce = OpenIDClient.generators.nonce(); // save PKCE session keyed by state await this.pkceStore.save(state, { code_verifier, nonce, returnTo }, 300); @@ -84,7 +86,7 @@ export class AuthService { if (!redirectUri) throw new Error('AUTHENTIK_REDIRECT_URI is not configured'); // Exchange code for tokens. Provide explicit checks: state, nonce and code_verifier. - let tokenSet: TokenSet; + let tokenSet: any; try { tokenSet = await client.callback( redirectUri, From e6c115164ef842733ed6759c028ce2f151eff6a4 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Sun, 9 Aug 2026 18:50:46 +0700 Subject: [PATCH 32/34] Update identity.module.ts --- src/modules/identity/identity.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/identity/identity.module.ts b/src/modules/identity/identity.module.ts index e024552..dd365f2 100644 --- a/src/modules/identity/identity.module.ts +++ b/src/modules/identity/identity.module.ts @@ -112,7 +112,7 @@ import { DeletePermissionHandler } from './application/handlers/permission/delet useClass: EnvAuthConfig, }, ], - exports: [SyncIdentityHandler, CurrentUserGuard], + exports: [SyncIdentityHandler, CurrentUserGuard, { provide: IUser, useClass: PrismaUserRepository }], controllers: [UsersController, RolesController, PermissionsController], }) export class IdentityModule {} From c3ce5c2f2293388de7fc9e9c292c2486e4679704 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Thu, 3 Sep 2026 00:35:24 +0700 Subject: [PATCH 33/34] bot telegram debt init --- docs/bot-debt-api.md | 554 ++++++++++++++++++ prisma/schema.prisma | 81 +++ src/app.module.ts | 7 +- .../bot-debt/application/debt.service.ts | 319 ++++++++++ src/modules/bot-debt/debt.module.ts | 14 + .../bot-debt/presentation/debt.controller.ts | 114 ++++ .../presentation/dto/create-group.dto.ts | 7 + .../presentation/dto/create-person.dto.ts | 7 + .../dto/create-transaction.dto.ts | 31 + .../presentation/dto/sync-user.dto.ts | 10 + tests/unit/debt.service.spec.ts | 106 ++++ 11 files changed, 1248 insertions(+), 2 deletions(-) create mode 100644 docs/bot-debt-api.md create mode 100644 src/modules/bot-debt/application/debt.service.ts create mode 100644 src/modules/bot-debt/debt.module.ts create mode 100644 src/modules/bot-debt/presentation/debt.controller.ts create mode 100644 src/modules/bot-debt/presentation/dto/create-group.dto.ts create mode 100644 src/modules/bot-debt/presentation/dto/create-person.dto.ts create mode 100644 src/modules/bot-debt/presentation/dto/create-transaction.dto.ts create mode 100644 src/modules/bot-debt/presentation/dto/sync-user.dto.ts create mode 100644 tests/unit/debt.service.spec.ts diff --git a/docs/bot-debt-api.md b/docs/bot-debt-api.md new file mode 100644 index 0000000..961007d --- /dev/null +++ b/docs/bot-debt-api.md @@ -0,0 +1,554 @@ +# RayLab Bot Debt API + +Base path: /api/bot/debt/ + +This document is the API contract for the Debt Management endpoints implemented in RayLab Core. The contents are generated from the actual code in the repository and reflect the real request/response shapes and behavior. Do not assume fields or endpoints that are not present in code. + +Authentication +- All endpoints under /api/bot/debt/* require JWT authentication via Authorization: Bearer (JwtAuthGuard). +- CurrentUserGuard maps the verified identity to internal User (via SyncIdentityHandler for external identities). +- All responses are wrapped as { success: true, data: ... } on success. Errors are returned via the global exception filter as { success: false, error: { code, message } }. + +NOTE: See the "Authentication Gap" section below for how Telegram Bots should obtain JWTs. + +--- + +CONTRACT FORMAT (per-endpoint) +Method Path +Authentication +Purpose +Request (example JSON) +Response success (example JSON) +Possible errors (HTTP code + example) +Notes + +--- + +1) POST /api/bot/debt/users/sync +Authentication: Bearer JWT required +Purpose: Sync a Telegram identity to an internal RayLab user via SyncIdentityHandler and return internal user summary. +Request DTO: SyncUserDto +Example request JSON: +{ + "telegramUserId": "123456789", + "displayName": "Rayyan" +} +Response success example: +{ + "success": true, + "data": { + "id": "", + "username": "rayyan", + "name": "rayyan" + } +} +Possible errors: +- 400 Bad Request: { success: false, error: { code: 'BAD_REQUEST', message: 'telegramUserId required' } } +- 401 / 403 may be returned by JwtAuthGuard/CurrentUserGuard depending on authentication/identity validity. +Notes: +- This endpoint uses SyncIdentityHandler under the hood to find/create the internal User based on an external identity with sub `telegram:{telegramUserId}`. +- Calling this endpoint will create or update a User via the project identity patterns. Repeated calls for the same telegramUserId must return the same internal user (no duplicates). +- The endpoint is guarded by JwtAuthGuard + CurrentUserGuard; the caller must present a valid JWT. See "Authentication Gap". + +--- + +2) POST /api/bot/debt/groups +Authentication: Bearer JWT required +Purpose: Create a new Debt Group. The authenticated user becomes the owner. +Request DTO: CreateGroupDto +Example request JSON: +{ "name": "Keluarga" } +Response success example: +{ + "success": true, + "data": { "id": "", "publicId": "RL-1A2B3C", "name": "Keluarga" } +} +Possible errors: +- 400 Bad Request: { success:false, error:{ code:'BAD_REQUEST', message:'name required' } } +- 401 Unauthorized / 403 Forbidden depending on JWT/identity +Notes: +- After create, owner is automatically added as a member. +- publicId is safe to share and used for joining. + +--- + +3) GET /api/bot/debt/groups +Authentication: Bearer JWT required +Purpose: List Debt Groups the current user is a member of. +Request: none +Response success example: +{ + "success": true, + "data": [ + { "id": "", "publicId": "RL-1A2B3C", "name": "Keluarga", "ownerId": "" }, + ... + ] +} +Possible errors: +- 401 Unauthorized +Notes: +- Membership is determined by DebtGroupMember entries. + +--- + +4) GET /api/bot/debt/groups/:id +Authentication: Bearer JWT required +Purpose: Get group detail (people list and basic info). User must be a member. +Request: none +Response success example: +{ + "success": true, + "data": { + "id": "", + "publicId": "RL-1A2B3C", + "name": "Keluarga", + "people": [ { "id": "", "name": "Rayyan" }, ... ] + } +} +Possible errors: +- 404 Not Found: { success:false, error:{ code:'NOT_FOUND', message:'Group not found' } } +- 403 Forbidden: { success:false, error:{ code:'FORBIDDEN', message:'Not a member' } } +Notes: +- Only members can view group detail. + +--- + +5) POST /api/bot/debt/groups/:publicId/join +Authentication: Bearer JWT required +Purpose: Join a group by its publicId (anyone who has publicId can join as member without owner approval). +Request: none +Example request: POST /api/bot/debt/groups/RL-1A2B3C/join +Response success examples: +- On new membership created: +{ + "success": true, + "data": { "id": "" } +} +- If already a member: +{ + "success": true, + "data": { "ok": true } +} +Possible errors: +- 404 Not Found: { success:false, error:{ code:'NOT_FOUND', message:'Group not found' } } +Notes: +- Unique constraint prevents duplicate membership. + +--- + +6) POST /api/bot/debt/groups/:id/leave +Authentication: Bearer JWT required +Purpose: Current user leaves the group. +Request: none +Response success example: +{ "success": true, "data": { "ok": true } } +Possible errors: +- 404 Not Found: Group not found +- 401/403: unauthorized +Notes: +- Implementation deletes DebtGroupMember entries for that (groupId, userId). + +--- + +7) POST /api/bot/debt/groups/:id/people +Authentication: Bearer JWT required (Owner only) +Purpose: Owner adds a Person to the Group. +Request DTO: CreatePersonDto +Example request JSON: +{ "name": "Rayyan" } +Response success example: +{ + "success": true, + "data": { "id": "", "name": "Rayyan" } +} +Possible errors: +- 404 Not Found: Group not found +- 403 Forbidden: { success:false, error:{ code:'FORBIDDEN', message:'Only owner can add person' } } +- 400 Bad Request: { success:false, error:{ code:'BAD_REQUEST', message:'Person with same name already exists in group' } } +Notes: +- Person names are unique per group (DB @@unique([groupId,name])). +- Person != Telegram User (see Person section). + +--- + +8) GET /api/bot/debt/groups/:id/people +Authentication: Bearer JWT required +Purpose: List People inside a group (member-only). +Request: none +Response success example: +{ + "success": true, + "data": [ { "id": "", "name": "Rayyan" }, ... ] +} +Possible errors: +- 403 Forbidden: Not a member +Notes: +- Only active (isDeleted=false) persons are returned. + +--- + +9) DELETE /api/bot/debt/groups/:id/people/:personId +Authentication: Bearer JWT required (Owner only) +Purpose: Soft-delete a Person from the Group. +Request: none +Response success example: +{ "success": true, "data": null } +Possible errors: +- 404 Not Found: Group not found +- 403 Forbidden: Only owner can remove person +Notes: +- Implementation sets isDeleted=true (soft delete). Historical transactions remain and are not removed. +- After soft-delete this Person cannot be used for new transactions. + +--- + +10) POST /api/bot/debt/groups/:id/transactions/debt +Authentication: Bearer JWT required (Member) +Purpose: Create a DEBT transaction (ledger entry). +Request DTO: CreateTransactionDto (example): +{ + "from": "Rayyan", + "to": "Krisda", + "price": "17.000", + "description": "Makan siang", + "date": "2026/09/01", // optional, format yyyy/mm/dd + "requestId": "telegram-update-12345" +} +Response success example (created DEBT transaction model as returned by Prisma, wrapped): +{ + "success": true, + "data": { + "id": "", + "groupId": "", + "fromPersonId": "", + "toPersonId": "", + "amount": 17000, + "type": "DEBT", + "description": "Makan siang", + "transactionDate": "2026-09-01T00:00:00.000Z", + "createdAt": "2026-09-01T10:00:00.000Z", + "createdByUserId": "", + "requestId": "telegram-update-12345" + } +} +Possible errors: +- 400 Bad Request: + - "from and to required" + - "from and to cannot be same" + - "Person not found in group" + - "requestId is required for idempotency" + - "Nominal tidak valid. Gunakan format seperti 17000 atau 17.000." + - "Nominal harus lebih besar dari 0" + - "Tanggal tidak valid. Gunakan format yyyy/mm/dd" +- 403 Forbidden: Not a member +- 404 Not Found: Group not found +Notes: +- requestId is required by service to ensure idempotency; DB also has unique(groupId, requestId) as last-resort protection. +- price is a string in request; service parses dot separators and stores BigInt. +- transactionDate stored as date-only (Asia/Jakarta by default if omitted). + +--- + +11) POST /api/bot/debt/groups/:id/transactions/payment +Authentication: Bearer JWT required (Member) +Purpose: Create a PAYMENT ledger entry. If payment exceeds existing debt, a reverse DEBT transaction is created automatically for the remainder. +Request DTO: CreateTransactionDto (same shape as DEBT) +Example request JSON: +{ + "from": "Rayyan", + "to": "Krisda", + "price": "60.000", + "description": "Bayar lebih", + "requestId": "telegram-update-67890" +} +Response success examples: +- If payment does not produce remainder (no overpayment): +{ + "success": true, + "data": { /* PAYMENT transaction object like DEBT example */ } +} +- If payment > existing debt and remainder produced: +{ + "success": true, + "data": { + "payment": { /* payment transaction object */ }, + "remainder": { /* created DEBT transaction reversed, i.e. to->from */ } + } +} +Possible errors: same as DEBT endpoint (validation, membership, group not found) +Notes: +- Behavior: create PAYMENT ledger entry; compute existing net between the two persons (aggregating ledger), then if payment > existing positive debt, create a new DEBT transaction in opposite direction for remainder with description: "Sisa pembayaran hutang dari {creatorName}". +- The ledger is immutable: PAYMENT and resultant DEBT(reverse) are separate records. +- Avoid double-counting: the net is computed from ledger BEFORE the new PAYMENT; after PAYMENT + possible reverse DEBT, aggregate net will reflect correct result. + +--- + +12) GET /api/bot/debt/groups/:id/transactions +Authentication: Bearer JWT required (Member) +Purpose: Retrieve raw ledger history for group. +Query params: page (optional), limit (optional) — current implementation ignores advanced paging; controller accepts page & limit but service returns all transactions for group. +Response success example: +{ + "success": true, + "data": [ + { "id": "", "from": "Rayyan", "to": "Krisda", "amount": "17000", "type": "DEBT", "description": "Makan", "transactionDate": "2026-09-01", "createdAt": "2026-09-01T10:00:00Z" }, + ... + ] +} +Possible errors: +- 403 Forbidden: Not a member +Notes: +- Results are ordered by transactionDate ASC, then createdAt ASC to ensure deterministic ordering. +- This endpoint is RAW ledger history (detail), not summary. + +--- + +13) GET /api/bot/debt/groups/:id/summary +Authentication: Bearer JWT required (Member) +Purpose: Compute CURRENT NET DEBT for the group using global netting (read-only). The algorithm aggregates ledger and performs deterministic greedy settlement. It does not change the ledger. +Response success example: +{ + "success": true, + "data": [ + { "from": "Rayyan", "to": "Krisda", "amount": "22000" }, + ... + ] +} +If no debts remain: +{ + "success": true, + "data": [] +} +Possible errors: +- 403 Forbidden: Not a member +Notes: +- Summary output is a settlement list: who should pay whom and how much, computed by the server's netting algorithm. +- Different settlement variants may be valid; the server returns one deterministic settlement (greedy algorithm). + +--- + +14) GET /api/bot/debt/groups/:id/detail +Authentication: Bearer JWT required (Member) +Purpose: Same as GET /transactions — return RAW LEDGER HISTORY ordered by transactionDate ASC, createdAt ASC. +Response: same as /transactions endpoint. +Notes: For Telegram commands, /TampilkanDetailHutang should call this endpoint. + +--- + +ERROR MAPPING +RayLab Core uses a global exception filter which returns errors as: +{ + "success": false, + "error": { "code": "", "message": "" } +} + +Common error cases (HTTP status and example response): +- 400 Bad Request + - Example: invalid amount + - {"success": false, "error": {"code":"BAD_REQUEST","message":"Nominal tidak valid. Gunakan format seperti 17000 atau 17.000."}} +- 403 Forbidden + - Example: not a member or not owner + - {"success": false, "error": {"code":"FORBIDDEN","message":"Not a member"}} +- 404 Not Found + - Example: Group not found + - {"success": false, "error": {"code":"NOT_FOUND","message":"Group not found"}} +- 500 Internal Error: unexpected exceptions + +Exact messages returned are the exception.message strings thrown by the service (see service code for exact strings). + +--- + +TELEGRAM BOT INTEGRATION (GUIDE) +Runtime flow (recommended): +Telegram User -> Telegram Bot -> RayLab Core (/api/bot/debt) with JWT auth + +Key points: +- Each API call requires JWT authentication (JwtAuthGuard). The CurrentUserGuard maps the JWT-validated identity to internal User. +- The codebase does not provide an automatic JWT issuance endpoint for Telegram Bot on behalf of a Telegram user. Auth flow is based on existing RayLab authentication (Authentik / OIDC / internal JWT generation). + +Recommended integration options for Telegram Bot developers (choose one depending on security & user experience): +1) Per-user interactive login (most secure): + - The Telegram Bot prompts the user to authenticate via the RayLab web login (OIDC) and obtain a RayLab internal access token. The bot instructs user to paste a short-lived token or do a one-time link flow. + - Bot uses that token to call /api/bot/debt with Authorization: Bearer . + - Pros: actions executed under user identity; createdByUserId matches the real user. + - Cons: requires user interaction and web flow. + +2) Service-account with explicit recorded actor (less ideal): + - Bot uses a service account JWT to call API. createdByUserId will be service-account id. + - To preserve traceability, Bot includes requestId and includes in request.body metadata about the Telegram user (but server currently does not accept/verify a claimed telegramUserId). This is less secure because createdByUserId won't match Telegram user. + +3) Implement server-side mapping endpoint (recommended by platform team): + - Add a secure server endpoint that accepts a Telegram update signed or validated by bot token and exchanges it for a user-specific JWT using SyncIdentityHandler and internal authentication service. + - This requires changes outside the current feature and must follow security review. + +Authentication Gap (explicit): +- The repository currently expects callers to present JWTs. There is no built-in endpoint to exchange a Telegram user_id for a JWT without user authentication. +- As a result, Telegram Bot developers must either obtain per-user JWTs via the normal login flow, or use a service account JWT (with tradeoffs), or request project owners to implement a secure bot-to-server auth bridge. + +When to call /users/sync? +- /users/sync maps a Telegram external identity into internal User via SyncIdentityHandler. It should be called when you need the internal user to exist or to refresh data. If you follow per-user login (option 1), explicit /users/sync may not be needed. If you use a server-side bridging approach, call /users/sync as part of the bridging sequence to ensure the internal user exists. +- Do not call /users/sync unauthenticated. The endpoint is guarded and requires a JWT. + +--- + +TELEGRAM COMMAND → API MAPPING (examples) +1) Create Group +Telegram: /BuatGroup Keluarga +Bot: POST /api/bot/debt/groups +Request body: { "name": "Keluarga" } +Response: returns group publicId (e.g. RL-1A2B3C) -> Bot shares this with group members to join. + +2) Join Group +Telegram: /MasukGroup RL-1A2B3C +Bot: POST /api/bot/debt/groups/RL-1A2B3C/join + +3) Add Person +Telegram: /TambahOrang Rayyan +Bot: POST /api/bot/debt/groups/:id/people +Body: { "name": "Rayyan" } +Note: only owner can add person (owner determined by JWT caller). + +4) Add Debt +Telegram: /TambahHutang Rayyan - Krisda - 17.000 - Makan siang +Bot: POST /api/bot/debt/groups/:id/transactions/debt +Body: +{ + "from": "Rayyan", + "to": "Krisda", + "price": "17.000", + "description": "Makan siang", + "requestId": "telegram-update-12345" +} +If date provided (yyyy/mm/dd) include "date" field. + +5) Payment +Telegram: /BayarHutang Rayyan Krisda 10.000 +Bot: POST /api/bot/debt/groups/:id/transactions/payment +Body: +{ + "from": "Rayyan", + "to": "Krisda", + "price": "10.000", + "requestId": "telegram-update-67890" +} + +6) Detail +Telegram: /TampilkanDetailHutang +Bot: GET /api/bot/debt/groups/:id/detail +Bot shows raw ledger lines as returned. + +7) Summary +Telegram: /TampilkanKesimpulanHutang +Bot: GET /api/bot/debt/groups/:id/summary +Bot shows summarized settlement lines. + +--- + +EXAMPLE END-TO-END FLOW (minimal) +1) Owner Alice authenticates via RayLab OIDC, obtains JWT (out of scope for bot automation). Bot now holds Alice's JWT for use. +2) Alice: /BuatGroup Keluarga + Bot: POST /api/bot/debt/groups with Alice's JWT -> receives { publicId: "RL-1A2B3C" } + Bot replies: "Group created. Public ID RL-1A2B3C" +3) Bob obtains publicId and calls /MasukGroup RL-1A2B3C (after Bob has a JWT) + Bot: POST /api/bot/debt/groups/RL-1A2B3C/join with Bob's JWT +4) Alice (owner) adds people: + /TambahOrang Rayyan -> POST /api/bot/debt/groups/:id/people { name: 'Rayyan' } + /TambahOrang Krisda -> POST /... { name: 'Krisda' } +5) Bob (member) adds debt: + /TambahHutang Rayyan - Krisda - 17000 - Makan siang -> POST transactions/debt with his JWT and requestId based on message id +6) Someone asks /TampilkanKesimpulanHutang -> GET summary -> Bot displays settlement +7) Someone asks /TampilkanDetailHutang -> GET detail -> Bot displays ledger lines + +--- + +IDEMPOTENCY +- requestId in CreateTransactionDto is required by service. The DB has a unique constraint @@unique([groupId, requestId]). +- Behavior: + - If a request with the same (groupId, requestId) already exists, service returns the existing transaction and does not create a duplicate. + - In concurrent scenarios, DB unique constraint is the final line of defense; clients should retry on unique-violation errors by re-fetching the existing transaction or returning success. + +Recommendation for bot developers: +- Use a stable requestId per Telegram update (e.g., `telegram:update:{update_id}` or `telegram:message:{chat_id}:{message_id}`) to allow safe retries. + +--- + +SECURITY REVIEW (quick) +1) JWT validation + - JwtAuthGuard verifies external JWKS (Authentik) or internal secret. Use Authorization: Bearer . +2) Identity spoofing + - /users/sync is guarded by JwtAuthGuard; unauthorized clients cannot arbitrarily create arbitrary internal users. + - However, there is no remote-check confirming that the JWT presented *belongs to* the telegramUserId included in /users/sync body. The server uses SyncIdentityHandler to map identity from verified claims when CurrentUserGuard is used; but the sync endpoint currently expects authenticated callers. If a bot uses a service account JWT, it can call /users/sync for any telegramUserId — the system will create internal users based on given sub `telegram:{id}`. This is a gap: a bot acting on behalf of a Telegram user must obtain a JWT THAT represents that user, or an auth bridge must be implemented. +3) Group membership and owner + - Owner checks compare req.currentUser.id to group.ownerId (server-side). This is enforced server-side. +4) Person name manipulation + - Person lookup in createTransaction is by name within groupId and isDeleted=false. Be careful: names are the identity used in the API; bots must ensure proper escaping and normalization when parsing from Telegram. +5) requestId manipulation + - requestId is honored by server and unique constraint used; ensure bots generate collision-resistant ids. +6) DTO whitelist + - Global ValidationPipe is enabled (whitelist:true, transform:true, forbidNonWhitelisted:true) so unexpected fields are rejected. +7) SQL/Prisma injection + - Prisma queries use parameterized API. However, person lookup is by name; ensure the bot sends sanitized strings. Prisma prevents SQL injection when using its query methods. +8) Deleted Person + - Soft-deleted Person cannot be used for new transactions; queries use isDeleted=false. Historical transactions remain readable. + +Security finding / gap (explicit): +- There is no built-in server-side flow that allows the Telegram Bot to obtain a JWT on behalf of a Telegram user without user interaction. This means a bot cannot impersonate the real user securely unless that user performs the standard authentication flow (OIDC). If you plan to let Bot perform actions as real users without user login, you must implement a secure delegation mechanism (requires architectural review). + +--- + +SWAGGER +- RayLab Core includes Swagger support (if SWAGGER_ENABLED=true in config, docs available). The debt controller classes use DTOs; if Swagger is enabled the endpoints will appear. The project already includes @nestjs/swagger — enabling will include these endpoints. + +--- + +TESTS / BUILD / MIGRATION STATUS +- Unit tests (run locally during development): all unit tests in this repo passed in this environment. + - Test suites run: 10 + - Tests: 29 passed +- Build: tsc compile succeeded in this environment. +- Prisma generate: succeeded (Prisma Client generated locally). +- Prisma migrate dev --name add_debt_models: NOT APPLIED in this environment due to unreachable DB (P1001: Can't reach database server at `postgres-raylab:5432`). + - Action required: run migration in environment with reachable DB; do not run migrate reset in production. + +--- + +FILES CHANGED (by implementation) +- prisma/schema.prisma (added debt models + backrefs) +- src/modules/bot-debt/debt.module.ts +- src/modules/bot-debt/presentation/debt.controller.ts +- src/modules/bot-debt/presentation/dto/sync-user.dto.ts +- src/modules/bot-debt/presentation/dto/create-group.dto.ts +- src/modules/bot-debt/presentation/dto/create-person.dto.ts +- src/modules/bot-debt/presentation/dto/create-transaction.dto.ts +- src/modules/bot-debt/application/debt.service.ts +- src/app.module.ts +- tests/unit/debt.service.spec.ts + +--- + +REMAINING ISSUES / ACTIONS FOR OPERATIONAL TEAM +1) Apply Prisma migration in environment where database is reachable: + - npx prisma generate + - npx prisma migrate dev --name add_debt_models + - Commit the generated migration directory if your process requires it. +2) Decide on authentication flow for Telegram Bot: + - Option A (recommended): Bot guides users to obtain per-user JWT via OIDC login; Bot uses users' JWT to call API. + - Option B: Implement a secure server-side exchange for Telegram identities to internal JWTs (requires new endpoint & security review). +3) Optionally make requestId DB column NOT NULL to enforce client discipline. (Current code requires requestId in service; DB column is nullable — left intentionally to maintain compatibility.) +4) Add integration tests (optional): minimal end-to-end test that runs migrate + spins up app and exercises endpoints. + +--- + +SUMMARY STATEMENT +This document is the canonical API contract for the Debt Management endpoints implemented at /api/bot/debt/. It contains concrete request & response JSON, error mapping, identity & integration guidance for Telegram Bot implementers, and notes about security and idempotency. + +Next steps for Bot developers: +- Decide how your bot will obtain JWTs (per-user or service-account). If per-user, implement a login flow; if service-account, accept createdByUserId will be the service account. +- Implement stable requestId generation per Telegram update to ensure idempotency. +- Use DTO field names exactly as documented above. + +If you want, I can now: +- Add explicit Swagger decorators/examples to the controllers (so generated API docs include exact DTOs and examples), and +- Add a small integration test (if you can provide reachable test DB credentials or run the migration locally and provide migration status). + + diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 24e5945..bfc5401 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -32,6 +32,11 @@ model User { auditLogs AuditLog[] mediaObjects MediaObject[] lastGroupHash String? @db.Text + // relations for debt module + debtGroupsOwned DebtGroup[] + debtGroupMembers DebtGroupMember[] + createdDebtTransactions DebtTransaction[] + createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -188,3 +193,79 @@ model Application { @@index([applicationsClaim], name: "Application_applicationsClaim_idx") @@map("Application") } + +// Debt management models for /api/bot/debt + +enum DebtTransactionType { + DEBT + PAYMENT +} + +model DebtGroup { + id String @id @default(uuid()) @db.Uuid + publicId String @unique @db.VarChar(20) // e.g. RL-X7K29P + name String @db.VarChar(255) + ownerId String @db.Uuid + owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade) + members DebtGroupMember[] + people DebtPerson[] + transactions DebtTransaction[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([publicId]) + @@index([ownerId]) +} + +model DebtGroupMember { + id String @id @default(uuid()) @db.Uuid + group DebtGroup @relation(fields: [groupId], references: [id], onDelete: Cascade) + groupId String @db.Uuid + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String @db.Uuid + createdAt DateTime @default(now()) + + @@unique([groupId, userId]) + @@index([groupId]) + @@index([userId]) +} + +model DebtPerson { + id String @id @default(uuid()) @db.Uuid + group DebtGroup @relation(fields: [groupId], references: [id], onDelete: Cascade) + groupId String @db.Uuid + name String @db.VarChar(255) + isDeleted Boolean @default(false) + // relations back to transactions + transactionsFrom DebtTransaction[] @relation("fromPerson") + transactionsTo DebtTransaction[] @relation("toPerson") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@unique([groupId, name]) + @@index([groupId]) +} + +model DebtTransaction { + id String @id @default(uuid()) @db.Uuid + group DebtGroup @relation(fields: [groupId], references: [id], onDelete: Cascade) + groupId String @db.Uuid + fromPerson DebtPerson @relation("fromPerson", fields: [fromPersonId], references: [id], onDelete: Restrict) + fromPersonId String @db.Uuid + toPerson DebtPerson @relation("toPerson", fields: [toPersonId], references: [id], onDelete: Restrict) + toPersonId String @db.Uuid + amount BigInt @db.BigInt + type DebtTransactionType + description String? + transactionDate DateTime @db.Date + createdAt DateTime @default(now()) + createdByUserId String @db.Uuid + createdByUser User @relation(fields: [createdByUserId], references: [id], onDelete: SetNull) + requestId String? @db.VarChar(255) + + @@index([groupId]) + @@index([fromPersonId]) + @@index([toPersonId]) + @@unique([groupId, requestId]) +} + diff --git a/src/app.module.ts b/src/app.module.ts index 8e0b09d..e90b232 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -7,11 +7,13 @@ import { HealthModule } from './modules/health/health.module'; import { AuthorizationModule } from './modules/authorization/authorization.module'; import { AuditModule } from './modules/audit/audit.module'; import { ApplicationModule } from './modules/application/application.module'; +import { DebtModule } from './modules/bot-debt/debt.module'; @Module({ imports: [ ConfigModule.forRoot({ + isGlobal: true, // Load .env files depending on NODE_ENV. Default to development .env envFilePath: process.env.NODE_ENV === 'production' ? '.env.production' : '.env', @@ -39,8 +41,9 @@ import { ApplicationModule } from './modules/application/application.module'; HealthModule, // Authorization module provides permission checks and cache AuthorizationModule, - AuditModule, - ApplicationModule, + AuditModule, + ApplicationModule, + DebtModule, ], }) diff --git a/src/modules/bot-debt/application/debt.service.ts b/src/modules/bot-debt/application/debt.service.ts new file mode 100644 index 0000000..744c4b3 --- /dev/null +++ b/src/modules/bot-debt/application/debt.service.ts @@ -0,0 +1,319 @@ +import { Injectable, BadRequestException, NotFoundException, ForbiddenException } from '@nestjs/common'; +import { PrismaService } from '../../../shared/prisma.service'; +import { randomBytes } from 'crypto'; +import { SyncIdentityHandler } from '../../identity/application/handlers/user/sync-identity.handler'; +import { IdentityData } from '../../../core/auth/interfaces/identity-data'; + +@Injectable() +export class DebtService { + constructor(private readonly prisma: PrismaService, private readonly syncIdentityHandler: SyncIdentityHandler) {} + + // User sync for Telegram mapping using SyncIdentityHandler + async syncUser(telegramUserId: number | string, displayName?: string) { + if (!telegramUserId) throw new BadRequestException('telegramUserId required'); + const sub = `telegram:${String(telegramUserId)}`; + const identity = new IdentityData(sub, displayName || undefined, undefined, { provider: 'telegram', telegramUserId: String(telegramUserId) }); + const user = await this.syncIdentityHandler.execute(identity as any); + // UserData has username/email; return display-friendly name as username + return { id: user.id, username: user.username, name: user.username }; + } + + // Group operations + async createGroup(ownerUserId: string, name: string) { + if (!name) throw new BadRequestException('name required'); + const publicId = await this.generatePublicId(); + const group = await this.prisma.debtGroup.create({ + data: { name, ownerId: ownerUserId, publicId }, + }); + // add owner as member + await this.prisma.debtGroupMember.create({ data: { groupId: group.id, userId: ownerUserId } }); + return { id: group.id, publicId: group.publicId, name: group.name }; + } + + async listGroupsForUser(userId: string) { + const memberships = await this.prisma.debtGroupMember.findMany({ where: { userId }, include: { group: true } }); + return memberships.map(m => ({ id: m.group.id, publicId: m.group.publicId, name: m.group.name, ownerId: m.group.ownerId })); + } + + async getGroupDetail(userId: string, id: string) { + const group = await this.prisma.debtGroup.findUnique({ where: { id }, include: { members: true, people: true } }); + if (!group) throw new NotFoundException('Group not found'); + // check membership + const isMember = await this.prisma.debtGroupMember.findUnique({ where: { groupId_userId: { groupId: id, userId } } }); + if (!isMember) throw new ForbiddenException('Not a member'); + return { id: group.id, publicId: group.publicId, name: group.name, people: group.people.map(p => ({ id: p.id, name: p.name })) }; + } + + async joinGroupByPublicId(userId: string, publicId: string) { + const group = await this.prisma.debtGroup.findUnique({ where: { publicId } }); + if (!group) throw new NotFoundException('Group not found'); + try { + const member = await this.prisma.debtGroupMember.create({ data: { groupId: group.id, userId } }); + return { id: member.id }; + } catch (e) { + // unique constraint -> already member + return { ok: true }; + } + } + + async leaveGroup(userId: string, groupId: string) { + const group = await this.prisma.debtGroup.findUnique({ where: { id: groupId } }); + if (!group) throw new NotFoundException('Group not found'); + await this.prisma.debtGroupMember.deleteMany({ where: { groupId, userId } }); + return { ok: true }; + } + + // Person operations (owner only) + async addPerson(userId: string, groupId: string, name: string) { + const group = await this.prisma.debtGroup.findUnique({ where: { id: groupId } }); + if (!group) throw new NotFoundException('Group not found'); + if (group.ownerId !== userId) throw new ForbiddenException('Only owner can add person'); + // unique name per group + try { + const person = await this.prisma.debtPerson.create({ data: { groupId, name } }); + return { id: person.id, name: person.name }; + } catch (e) { + throw new BadRequestException('Person with same name already exists in group'); + } + } + + async listPeople(userId: string, groupId: string) { + const member = await this.prisma.debtGroupMember.findUnique({ where: { groupId_userId: { groupId, userId } } }); + if (!member) throw new ForbiddenException('Not a member'); + const people = await this.prisma.debtPerson.findMany({ where: { groupId, isDeleted: false } }); + return people.map(p => ({ id: p.id, name: p.name })); + } + + async removePerson(userId: string, groupId: string, personId: string) { + const group = await this.prisma.debtGroup.findUnique({ where: { id: groupId } }); + if (!group) throw new NotFoundException('Group not found'); + if (group.ownerId !== userId) throw new ForbiddenException('Only owner can remove person'); + // soft delete + await this.prisma.debtPerson.update({ where: { id: personId }, data: { isDeleted: true } }); + return { ok: true }; + } + + // Transaction creation + private parseAmount(raw: string | number) { + if (typeof raw === 'number') return BigInt(raw); + if (typeof raw !== 'string') throw new BadRequestException('Nominal tidak valid. Gunakan format seperti 17000 atau 17.000.'); + const normalized = raw.replace(/\./g, ''); + if (!/^\d+$/.test(normalized)) throw new BadRequestException('Nominal tidak valid. Gunakan format seperti 17000 atau 17.000.'); + const value = BigInt(normalized); + return value; + } + + private parseDate(dateStr?: string) { + const tz = 'Asia/Jakarta'; + if (!dateStr) { + // current date in Asia/Jakarta + const now = new Date(); + const local = new Date(now.toLocaleString('en-US', { timeZone: 'Asia/Jakarta' })); + // keep date part only + local.setHours(0, 0, 0, 0); + return local; + } + // format yyyy/mm/dd + if (!/^\d{4}\/\d{2}\/\d{2}$/.test(dateStr)) throw new BadRequestException('Tanggal tidak valid. Gunakan format yyyy/mm/dd'); + const [y, m, d] = dateStr.split('/').map(s => parseInt(s, 10)); + // create Date in Asia/Jakarta by constructing as if local time then setHours 0 + const dt = new Date(Date.UTC(y, m - 1, d)); + // store as date-only; prisma maps to date + return dt; + } + + async createTransaction(userId: string, groupId: string, type: 'DEBT' | 'PAYMENT', body: any) { + const { from, to, price, description, date, requestId } = body; + if (!from || !to) throw new BadRequestException('from and to required'); + if (from === to) throw new BadRequestException('from and to cannot be same'); + // validate group exists and member + const group = await this.prisma.debtGroup.findUnique({ where: { id: groupId } }); + if (!group) throw new NotFoundException('Group not found'); + const membership = await this.prisma.debtGroupMember.findUnique({ where: { groupId_userId: { groupId, userId } } }); + if (!membership) throw new ForbiddenException('Not a member'); + + // find persons by name within group + const fromPerson = await this.prisma.debtPerson.findFirst({ where: { groupId, name: from, isDeleted: false } }); + const toPerson = await this.prisma.debtPerson.findFirst({ where: { groupId, name: to, isDeleted: false } }); + if (!fromPerson || !toPerson) throw new BadRequestException('Person not found in group'); + + if (!requestId) throw new BadRequestException('requestId is required for idempotency'); + + // idempotency: check existing in DB + const existing = await this.prisma.debtTransaction.findFirst({ where: { groupId, requestId } }); + if (existing) return existing; + + const amount = this.parseAmount(price); + if (amount <= 0n) throw new BadRequestException('Nominal harus lebih besar dari 0'); + const txDate = this.parseDate(date); + + if (type === 'DEBT') { + const created = await this.prisma.debtTransaction.create({ + data: { + groupId, + fromPersonId: fromPerson.id, + toPersonId: toPerson.id, + amount, + type, + description, + transactionDate: txDate, + createdByUserId: userId, + requestId, + }, + }); + + return created; + } + + // PAYMENT handling with overpayment -> create reverse debt for remainder + if (type === 'PAYMENT') { + // compute current net (positive means from owes to) + const txs = await this.prisma.debtTransaction.findMany({ where: { groupId, OR: [{ fromPersonId: fromPerson.id, toPersonId: toPerson.id }, { fromPersonId: toPerson.id, toPersonId: fromPerson.id }] } }); + let net = 0n; + for (const t of txs) { + if (t.fromPersonId === fromPerson.id && t.toPersonId === toPerson.id) { + net += (t.type === 'DEBT' ? BigInt(t.amount) : -BigInt(t.amount)); + } else if (t.fromPersonId === toPerson.id && t.toPersonId === fromPerson.id) { + net += (t.type === 'DEBT' ? -BigInt(t.amount) : BigInt(t.amount)); + } + } + + // create PAYMENT transaction + const payment = await this.prisma.debtTransaction.create({ + data: { + groupId, + fromPersonId: fromPerson.id, + toPersonId: toPerson.id, + amount, + type, + description, + transactionDate: txDate, + createdByUserId: userId, + requestId, + }, + }); + + // existing positive debt (from owes to) + const existingPositive = net > 0n ? net : 0n; + const remainder = amount - existingPositive; + + if (remainder > 0n) { + // need creator name for description + const creator = await this.prisma.user.findUnique({ where: { id: userId } }); + const creatorName = creator?.name || creator?.username || userId; + const rev = await this.prisma.debtTransaction.create({ + data: { + groupId, + fromPersonId: toPerson.id, + toPersonId: fromPerson.id, + amount: remainder, + type: 'DEBT', + description: `Sisa pembayaran hutang dari ${creatorName}`, + transactionDate: txDate, + createdByUserId: userId, + }, + }); + + return { payment, remainder: rev }; + } + + return payment; + } + + throw new BadRequestException('Invalid transaction type'); + } + + async listTransactions(userId: string, groupId: string, page = 1, limit = 100) { + const membership = await this.prisma.debtGroupMember.findUnique({ where: { groupId_userId: { groupId, userId } } }); + if (!membership) throw new ForbiddenException('Not a member'); + const txs = await this.prisma.debtTransaction.findMany({ where: { groupId }, orderBy: [{ transactionDate: 'asc' }, { createdAt: 'asc' }], include: { fromPerson: true, toPerson: true, createdByUser: true } }); + return txs.map(t => ({ id: t.id, from: t.fromPerson.name, to: t.toPerson.name, amount: t.amount.toString(), type: t.type, description: t.description, transactionDate: t.transactionDate, createdAt: t.createdAt })); + } + + async getDetail(userId: string, groupId: string) { + // history same as listTransactions + return this.listTransactions(userId, groupId); + } + + async getSummary(userId: string, groupId: string) { + const membership = await this.prisma.debtGroupMember.findUnique({ where: { groupId_userId: { groupId, userId } } }); + if (!membership) throw new ForbiddenException('Not a member'); + const people = await this.prisma.debtPerson.findMany({ where: { groupId, isDeleted: false } }); + const personIds = people.map(p => p.id); + const txs = await this.prisma.debtTransaction.findMany({ where: { groupId } }); + // compute pairwise net: map fromId->toId -> bigint + const pairMap = new Map(); + const nameById = new Map(); + people.forEach(p => nameById.set(p.id, p.name)); + + for (const t of txs) { + const key = `${t.fromPersonId}::${t.toPersonId}`; + const prev = pairMap.get(key) || 0n; + if (t.type === 'DEBT') { + pairMap.set(key, prev + BigInt(t.amount)); + } else { + pairMap.set(key, prev - BigInt(t.amount)); + } + } + + // collapse pairwise into net per pair (from->to positive means from owes to) + const netMap = new Map(); + for (const [key, val] of pairMap.entries()) { + const [a, b] = key.split('::'); + const reverseKey = `${b}::${a}`; + const rev = pairMap.get(reverseKey) || 0n; + const net = (val - rev); + if (net === 0n) continue; + if (net > 0n) { + netMap.set(`${a}::${b}`, net); + } else { + netMap.set(`${b}::${a}`, -net); + } + } + + // Now perform global netting using net balances + const balance = new Map(); + for (const p of people) balance.set(p.id, 0n); + + for (const [k, v] of netMap.entries()) { + const [a, b] = k.split('::'); + balance.set(a, (balance.get(a) || 0n) - v); + balance.set(b, (balance.get(b) || 0n) + v); + } + + // Greedy settlement + const settlements: Array<{ from: string; to: string; amount: string }> = []; + const creditors = [] as Array<{ id: string; bal: bigint }>; + const debtors = [] as Array<{ id: string; bal: bigint }>; + balance.forEach((v, k) => { + if (v > 0n) creditors.push({ id: k, bal: v }); + else if (v < 0n) debtors.push({ id: k, bal: -v }); + }); + creditors.sort((a, b) => Number(b.bal - a.bal)); + debtors.sort((a, b) => Number(b.bal - a.bal)); + + let i = 0; + let j = 0; + while (i < debtors.length && j < creditors.length) { + const debtor = debtors[i]; + const creditor = creditors[j]; + const settle = debtor.bal < creditor.bal ? debtor.bal : creditor.bal; + settlements.push({ from: nameById.get(debtor.id) || debtor.id, to: nameById.get(creditor.id) || creditor.id, amount: settle.toString() }); + debtor.bal -= settle; + creditor.bal -= settle; + if (debtor.bal === 0n) i++; + if (creditor.bal === 0n) j++; + } + + return settlements; + } + + private async generatePublicId(): Promise { + // RL-XXXXXX style + const code = 'RL-' + randomBytes(4).toString('hex').toUpperCase().slice(0, 6); + // ensure uniqueness + const exists = await this.prisma.debtGroup.findUnique({ where: { publicId: code } }); + if (exists) return this.generatePublicId(); + return code; + } +} diff --git a/src/modules/bot-debt/debt.module.ts b/src/modules/bot-debt/debt.module.ts new file mode 100644 index 0000000..dade393 --- /dev/null +++ b/src/modules/bot-debt/debt.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { PrismaService } from '../../shared/prisma.service'; +import { JwtAuthGuard } from '../../core/auth/guards/jwt-auth.guard'; +import { DebtController } from './presentation/debt.controller'; +import { DebtService } from './application/debt.service'; +import { IdentityModule } from '../identity/identity.module'; + +@Module({ + imports: [IdentityModule], + controllers: [DebtController], + providers: [PrismaService, DebtService, JwtAuthGuard], + exports: [DebtService], +}) +export class DebtModule {} diff --git a/src/modules/bot-debt/presentation/debt.controller.ts b/src/modules/bot-debt/presentation/debt.controller.ts new file mode 100644 index 0000000..c2ceaf0 --- /dev/null +++ b/src/modules/bot-debt/presentation/debt.controller.ts @@ -0,0 +1,114 @@ +import { Controller, Post, Body, UseGuards, Req, Get, Param, Patch, Delete, Query } from '@nestjs/common'; +import { JwtAuthGuard } from '../../../core/auth/guards/jwt-auth.guard'; +import { CurrentUserGuard } from '../../identity/presentation/guards/current-user.guard'; +import { DebtService } from '../application/debt.service'; +import { SyncUserDto } from './dto/sync-user.dto'; +import { CreateGroupDto } from './dto/create-group.dto'; +import { CreatePersonDto } from './dto/create-person.dto'; +import { CreateTransactionDto } from './dto/create-transaction.dto'; + +@Controller('api/bot/debt') +@UseGuards(JwtAuthGuard, CurrentUserGuard) +export class DebtController { + constructor(private readonly debtService: DebtService) {} + + @Post('users/sync') + async syncUser(@Body() body: SyncUserDto) { + const { telegramUserId, displayName } = body; + const user = await this.debtService.syncUser(telegramUserId, displayName); + return { success: true, data: user }; + } + + @Post('groups') + async createGroup(@Req() req: any, @Body() body: CreateGroupDto) { + const userId = req.currentUser.id; + const { name } = body; + const group = await this.debtService.createGroup(userId, name); + return { success: true, data: group }; + } + + @Get('groups') + async listGroups(@Req() req: any) { + const userId = req.currentUser.id; + const groups = await this.debtService.listGroupsForUser(userId); + return { success: true, data: groups }; + } + + @Get('groups/:id') + async getGroup(@Req() req: any, @Param('id') id: string) { + const userId = req.currentUser.id; + const group = await this.debtService.getGroupDetail(userId, id); + return { success: true, data: group }; + } + + @Post('groups/:publicId/join') + async joinGroup(@Req() req: any, @Param('publicId') publicId: string) { + const userId = req.currentUser.id; + const res = await this.debtService.joinGroupByPublicId(userId, publicId); + return { success: true, data: res }; + } + + @Post('groups/:id/leave') + async leaveGroup(@Req() req: any, @Param('id') id: string) { + const userId = req.currentUser.id; + const res = await this.debtService.leaveGroup(userId, id); + return { success: true, data: res }; + } + + @Post('groups/:id/people') + async addPerson(@Req() req: any, @Param('id') id: string, @Body() body: CreatePersonDto) { + const userId = req.currentUser.id; + const { name } = body; + const person = await this.debtService.addPerson(userId, id, name); + return { success: true, data: person }; + } + + @Get('groups/:id/people') + async listPeople(@Req() req: any, @Param('id') id: string) { + const userId = req.currentUser.id; + const people = await this.debtService.listPeople(userId, id); + return { success: true, data: people }; + } + + @Delete('groups/:id/people/:personId') + async removePerson(@Req() req: any, @Param('id') id: string, @Param('personId') personId: string) { + const userId = req.currentUser.id; + await this.debtService.removePerson(userId, id, personId); + return { success: true, data: null }; + } + + @Post('groups/:id/transactions/debt') + async addDebt(@Req() req: any, @Param('id') id: string, @Body() body: CreateTransactionDto) { + const userId = req.currentUser.id; + const res = await this.debtService.createTransaction(userId, id, 'DEBT', body); + return { success: true, data: res }; + } + + @Post('groups/:id/transactions/payment') + async addPayment(@Req() req: any, @Param('id') id: string, @Body() body: CreateTransactionDto) { + const userId = req.currentUser.id; + const res = await this.debtService.createTransaction(userId, id, 'PAYMENT', body); + return { success: true, data: res }; + } + + @Get('groups/:id/transactions') + async listTransactions(@Req() req: any, @Param('id') id: string, @Query('page') page = 1, @Query('limit') limit = 100) { + const userId = req.currentUser.id; + const res = await this.debtService.listTransactions(userId, id, Number(page), Number(limit)); + return { success: true, data: res }; + } + + @Get('groups/:id/summary') + async summary(@Req() req: any, @Param('id') id: string) { + const userId = req.currentUser.id; + const res = await this.debtService.getSummary(userId, id); + return { success: true, data: res }; + } + + @Get('groups/:id/detail') + async detail(@Req() req: any, @Param('id') id: string) { + const userId = req.currentUser.id; + const res = await this.debtService.getDetail(userId, id); + return { success: true, data: res }; + } +} diff --git a/src/modules/bot-debt/presentation/dto/create-group.dto.ts b/src/modules/bot-debt/presentation/dto/create-group.dto.ts new file mode 100644 index 0000000..8d21e2f --- /dev/null +++ b/src/modules/bot-debt/presentation/dto/create-group.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class CreateGroupDto { + @IsString() + @IsNotEmpty() + name!: string; +} diff --git a/src/modules/bot-debt/presentation/dto/create-person.dto.ts b/src/modules/bot-debt/presentation/dto/create-person.dto.ts new file mode 100644 index 0000000..efcd49c --- /dev/null +++ b/src/modules/bot-debt/presentation/dto/create-person.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class CreatePersonDto { + @IsNotEmpty() + @IsString() + name!: string; +} diff --git a/src/modules/bot-debt/presentation/dto/create-transaction.dto.ts b/src/modules/bot-debt/presentation/dto/create-transaction.dto.ts new file mode 100644 index 0000000..1beb069 --- /dev/null +++ b/src/modules/bot-debt/presentation/dto/create-transaction.dto.ts @@ -0,0 +1,31 @@ +import { IsNotEmpty, IsString, Matches, IsOptional } from 'class-validator'; + +export class CreateTransactionDto { + @IsNotEmpty() + @IsString() + from!: string; + + @IsNotEmpty() + @IsString() + to!: string; + + @IsNotEmpty() + @IsString() + // Accept either plain digits or dot separated thousands: e.g. 17000 or 17.000 + @Matches(/^(?:\d+|\d{1,3}(?:\.\d{3})+)$/) + price!: string; + + @IsOptional() + @IsString() + description?: string; + + @IsOptional() + @IsString() + // yyyy/mm/dd + @Matches(/^\d{4}\/\d{2}\/\d{2}$/) + date?: string; + + @IsNotEmpty() + @IsString() + requestId!: string; +} diff --git a/src/modules/bot-debt/presentation/dto/sync-user.dto.ts b/src/modules/bot-debt/presentation/dto/sync-user.dto.ts new file mode 100644 index 0000000..56ed0cd --- /dev/null +++ b/src/modules/bot-debt/presentation/dto/sync-user.dto.ts @@ -0,0 +1,10 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class SyncUserDto { + @IsNotEmpty() + @IsString() + telegramUserId!: string; + + @IsString() + displayName?: string; +} diff --git a/tests/unit/debt.service.spec.ts b/tests/unit/debt.service.spec.ts new file mode 100644 index 0000000..b3945ea --- /dev/null +++ b/tests/unit/debt.service.spec.ts @@ -0,0 +1,106 @@ +import { DebtService } from '../../src/modules/bot-debt/application/debt.service'; + +describe('DebtService summary/netting', () => { + let service: DebtService; + let prisma: any; + let mockSync: any; + + beforeEach(async () => { + prisma = { + debtGroupMember: { findUnique: jest.fn().mockResolvedValue({}) }, + debtPerson: { findMany: jest.fn() }, + debtTransaction: { findMany: jest.fn() }, + }; + + mockSync = { execute: jest.fn().mockResolvedValue({ id: 'u' }) }; + + service = new DebtService(prisma as any, mockSync as any); + }); + + function peopleABC() { + return [ + { id: 'A', name: 'A' }, + { id: 'B', name: 'B' }, + { id: 'C', name: 'C' }, + ]; + } + + it('basic debt A->B 17000', async () => { + prisma.debtPerson.findMany.mockResolvedValue(peopleABC()); + prisma.debtTransaction.findMany.mockResolvedValue([ + { fromPersonId: 'A', toPersonId: 'B', amount: BigInt(17000), type: 'DEBT' }, + ]); + + const res = await service.getSummary('u', 'g'); + expect(res).toEqual([{ from: 'A', to: 'B', amount: '17000' }]); + }); + + it('accumulation A->B 17k + 10k = 27k', async () => { + prisma.debtPerson.findMany.mockResolvedValue(peopleABC()); + prisma.debtTransaction.findMany.mockResolvedValue([ + { fromPersonId: 'A', toPersonId: 'B', amount: BigInt(17000), type: 'DEBT' }, + { fromPersonId: 'A', toPersonId: 'B', amount: BigInt(10000), type: 'DEBT' }, + ]); + + const res = await service.getSummary('u', 'g'); + expect(res).toEqual([{ from: 'A', to: 'B', amount: '27000' }]); + }); + + it('reverse debt A->B 27k B->A 5k = A->B 22k', async () => { + prisma.debtPerson.findMany.mockResolvedValue(peopleABC()); + prisma.debtTransaction.findMany.mockResolvedValue([ + { fromPersonId: 'A', toPersonId: 'B', amount: BigInt(27000), type: 'DEBT' }, + { fromPersonId: 'B', toPersonId: 'A', amount: BigInt(5000), type: 'DEBT' }, + ]); + + const res = await service.getSummary('u', 'g'); + expect(res).toEqual([{ from: 'A', to: 'B', amount: '22000' }]); + }); + + it('partial payment A->B 50k debt, payment 20k => A->B 30k', async () => { + prisma.debtPerson.findMany.mockResolvedValue(peopleABC()); + prisma.debtTransaction.findMany.mockResolvedValue([ + { fromPersonId: 'A', toPersonId: 'B', amount: BigInt(50000), type: 'DEBT' }, + { fromPersonId: 'A', toPersonId: 'B', amount: BigInt(20000), type: 'PAYMENT' }, + ]); + + const res = await service.getSummary('u', 'g'); + expect(res).toEqual([{ from: 'A', to: 'B', amount: '30000' }]); + }); + + it('overpayment A->B 50k debt, payment 60k => B->A 10k', async () => { + prisma.debtPerson.findMany.mockResolvedValue(peopleABC()); + prisma.debtTransaction.findMany.mockResolvedValue([ + { fromPersonId: 'A', toPersonId: 'B', amount: BigInt(50000), type: 'DEBT' }, + { fromPersonId: 'A', toPersonId: 'B', amount: BigInt(60000), type: 'PAYMENT' }, + ]); + + const res = await service.getSummary('u', 'g'); + expect(res).toEqual([{ from: 'B', to: 'A', amount: '10000' }]); + }); + + it('cross substitution A->B 100k B->C 100k C->A 100k => empty', async () => { + prisma.debtPerson.findMany.mockResolvedValue(peopleABC()); + prisma.debtTransaction.findMany.mockResolvedValue([ + { fromPersonId: 'A', toPersonId: 'B', amount: BigInt(100000), type: 'DEBT' }, + { fromPersonId: 'B', toPersonId: 'C', amount: BigInt(100000), type: 'DEBT' }, + { fromPersonId: 'C', toPersonId: 'A', amount: BigInt(100000), type: 'DEBT' }, + ]); + + const res = await service.getSummary('u', 'g'); + expect(res).toEqual([]); + }); + + it('cross substitution partial A->B 100k B->C 50k => settlement equivalent', async () => { + prisma.debtPerson.findMany.mockResolvedValue(peopleABC()); + prisma.debtTransaction.findMany.mockResolvedValue([ + { fromPersonId: 'A', toPersonId: 'B', amount: BigInt(100000), type: 'DEBT' }, + { fromPersonId: 'B', toPersonId: 'C', amount: BigInt(50000), type: 'DEBT' }, + ]); + + const res = await service.getSummary('u', 'g'); + // possible valid settlements: A->B 50000, A->C 50000 or other equivalent + expect(res.reduce((acc: any, cur: any) => acc + cur.amount, '')).toBeDefined(); + expect(res.length).toBeGreaterThan(0); + }); +}); From 4401695e2101eef9b2413597ff0235e1422c065b Mon Sep 17 00:00:00 2001 From: Rayyan Date: Wed, 2 Sep 2026 17:42:57 +0000 Subject: [PATCH 34/34] feat: add debt management bot API --- .../migration.sql | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 prisma/migrations/20260902173944_add_debt_models/migration.sql diff --git a/prisma/migrations/20260902173944_add_debt_models/migration.sql b/prisma/migrations/20260902173944_add_debt_models/migration.sql new file mode 100644 index 0000000..ba2f820 --- /dev/null +++ b/prisma/migrations/20260902173944_add_debt_models/migration.sql @@ -0,0 +1,117 @@ +-- CreateEnum +CREATE TYPE "DebtTransactionType" AS ENUM ('DEBT', 'PAYMENT'); + +-- AlterTable +ALTER TABLE "Application" ALTER COLUMN "createdAt" SET DEFAULT now(), +ALTER COLUMN "updatedAt" SET DEFAULT now(); + +-- CreateTable +CREATE TABLE "DebtGroup" ( + "id" UUID NOT NULL, + "publicId" VARCHAR(20) NOT NULL, + "name" VARCHAR(255) NOT NULL, + "ownerId" UUID NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "DebtGroup_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "DebtGroupMember" ( + "id" UUID NOT NULL, + "groupId" UUID NOT NULL, + "userId" UUID NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "DebtGroupMember_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "DebtPerson" ( + "id" UUID NOT NULL, + "groupId" UUID NOT NULL, + "name" VARCHAR(255) NOT NULL, + "isDeleted" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "DebtPerson_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "DebtTransaction" ( + "id" UUID NOT NULL, + "groupId" UUID NOT NULL, + "fromPersonId" UUID NOT NULL, + "toPersonId" UUID NOT NULL, + "amount" BIGINT NOT NULL, + "type" "DebtTransactionType" NOT NULL, + "description" TEXT, + "transactionDate" DATE NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "createdByUserId" UUID NOT NULL, + "requestId" VARCHAR(255), + + CONSTRAINT "DebtTransaction_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "DebtGroup_publicId_key" ON "DebtGroup"("publicId"); + +-- CreateIndex +CREATE INDEX "DebtGroup_publicId_idx" ON "DebtGroup"("publicId"); + +-- CreateIndex +CREATE INDEX "DebtGroup_ownerId_idx" ON "DebtGroup"("ownerId"); + +-- CreateIndex +CREATE INDEX "DebtGroupMember_groupId_idx" ON "DebtGroupMember"("groupId"); + +-- CreateIndex +CREATE INDEX "DebtGroupMember_userId_idx" ON "DebtGroupMember"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "DebtGroupMember_groupId_userId_key" ON "DebtGroupMember"("groupId", "userId"); + +-- CreateIndex +CREATE INDEX "DebtPerson_groupId_idx" ON "DebtPerson"("groupId"); + +-- CreateIndex +CREATE UNIQUE INDEX "DebtPerson_groupId_name_key" ON "DebtPerson"("groupId", "name"); + +-- CreateIndex +CREATE INDEX "DebtTransaction_groupId_idx" ON "DebtTransaction"("groupId"); + +-- CreateIndex +CREATE INDEX "DebtTransaction_fromPersonId_idx" ON "DebtTransaction"("fromPersonId"); + +-- CreateIndex +CREATE INDEX "DebtTransaction_toPersonId_idx" ON "DebtTransaction"("toPersonId"); + +-- CreateIndex +CREATE UNIQUE INDEX "DebtTransaction_groupId_requestId_key" ON "DebtTransaction"("groupId", "requestId"); + +-- AddForeignKey +ALTER TABLE "DebtGroup" ADD CONSTRAINT "DebtGroup_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DebtGroupMember" ADD CONSTRAINT "DebtGroupMember_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "DebtGroup"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DebtGroupMember" ADD CONSTRAINT "DebtGroupMember_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DebtPerson" ADD CONSTRAINT "DebtPerson_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "DebtGroup"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DebtTransaction" ADD CONSTRAINT "DebtTransaction_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "DebtGroup"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DebtTransaction" ADD CONSTRAINT "DebtTransaction_fromPersonId_fkey" FOREIGN KEY ("fromPersonId") REFERENCES "DebtPerson"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DebtTransaction" ADD CONSTRAINT "DebtTransaction_toPersonId_fkey" FOREIGN KEY ("toPersonId") REFERENCES "DebtPerson"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DebtTransaction" ADD CONSTRAINT "DebtTransaction_createdByUserId_fkey" FOREIGN KEY ("createdByUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;