diff --git a/apps/server-nestjs/package.json b/apps/server-nestjs/package.json index 908dae4410..177c86eed0 100644 --- a/apps/server-nestjs/package.json +++ b/apps/server-nestjs/package.json @@ -68,6 +68,8 @@ "@ts-rest/fastify": "^3.52.1", "@ts-rest/open-api": "^3.52.1", "axios": "^1.13.6", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.4", "date-fns": "^4.1.0", "dotenv": "^16.6.1", "fastify": "^4.29.1", diff --git a/apps/server-nestjs/src/main.module.ts b/apps/server-nestjs/src/main.module.ts index 6fbba7bb4e..c5f978cbca 100644 --- a/apps/server-nestjs/src/main.module.ts +++ b/apps/server-nestjs/src/main.module.ts @@ -4,6 +4,7 @@ import { ScheduleModule } from '@nestjs/schedule' import { CpinModule } from './cpin-module/cpin.module' import { HealthzModule } from './modules/healthz/healthz.module' import { KeycloakModule } from './modules/keycloak/keycloak.module' +import { SystemSettingsModule } from './modules/system-settings/system-settings.module' import { VersionModule } from './modules/version/version.module' @Module({ @@ -11,6 +12,7 @@ import { VersionModule } from './modules/version/version.module' CpinModule, KeycloakModule, HealthzModule, + SystemSettingsModule, VersionModule, EventEmitterModule.forRoot(), ScheduleModule.forRoot(), diff --git a/apps/server-nestjs/src/main.ts b/apps/server-nestjs/src/main.ts index c531b8b5da..209ad5fa0a 100644 --- a/apps/server-nestjs/src/main.ts +++ b/apps/server-nestjs/src/main.ts @@ -1,3 +1,4 @@ +import { ValidationPipe } from '@nestjs/common' import { NestFactory } from '@nestjs/core' import { Logger } from 'nestjs-pino' import { ConfigurationService } from './cpin-module/infrastructure/configuration/configuration.service' @@ -6,6 +7,7 @@ import { MainModule } from './main.module' async function bootstrap() { const app = await NestFactory.create(MainModule, { bufferLogs: true }) app.useLogger(app.get(Logger)) + app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true })) app.flushLogs() app.enableShutdownHooks() const config = app.get(ConfigurationService) diff --git a/apps/server-nestjs/src/modules/system-settings/dto/list-system-settings-query.dto.ts b/apps/server-nestjs/src/modules/system-settings/dto/list-system-settings-query.dto.ts new file mode 100644 index 0000000000..19f014ad11 --- /dev/null +++ b/apps/server-nestjs/src/modules/system-settings/dto/list-system-settings-query.dto.ts @@ -0,0 +1,7 @@ +import { IsOptional, IsString } from 'class-validator' + +export class ListSystemSettingsQueryDto { + @IsString() + @IsOptional() + key?: string +} diff --git a/apps/server-nestjs/src/modules/system-settings/dto/system-setting.dto.ts b/apps/server-nestjs/src/modules/system-settings/dto/system-setting.dto.ts new file mode 100644 index 0000000000..fe712c5f3d --- /dev/null +++ b/apps/server-nestjs/src/modules/system-settings/dto/system-setting.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString } from 'class-validator' + +export class SystemSettingDto { + @IsString() + @IsNotEmpty() + value!: string +} diff --git a/apps/server-nestjs/src/modules/system-settings/system-settings.controller.ts b/apps/server-nestjs/src/modules/system-settings/system-settings.controller.ts new file mode 100644 index 0000000000..0385aa7652 --- /dev/null +++ b/apps/server-nestjs/src/modules/system-settings/system-settings.controller.ts @@ -0,0 +1,24 @@ +import type { ListSystemSettingsQueryDto } from './dto/list-system-settings-query.dto' +import type { SystemSettingDto } from './dto/system-setting.dto' +import { Body, Controller, Get, Inject, Param, Put, Query } from '@nestjs/common' +import { SystemSettingsService } from './system-settings.service' + +@Controller('api/v1/system/settings') +export class SystemSettingsController { + constructor(@Inject(SystemSettingsService) private readonly service: SystemSettingsService) {} + + @Get() + async list( + @Query() query: ListSystemSettingsQueryDto, + ) { + return this.service.list(query.key) + } + + @Put(':key') + async upsert( + @Param('key') key: string, + @Body() body: SystemSettingDto, + ) { + return this.service.upsert(key, body) + } +} diff --git a/apps/server-nestjs/src/modules/system-settings/system-settings.module.ts b/apps/server-nestjs/src/modules/system-settings/system-settings.module.ts new file mode 100644 index 0000000000..3209b109e2 --- /dev/null +++ b/apps/server-nestjs/src/modules/system-settings/system-settings.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common' +import { InfrastructureModule } from '../../cpin-module/infrastructure/infrastructure.module' +import { SystemSettingsController } from './system-settings.controller' +import { SystemSettingsService } from './system-settings.service' + +@Module({ + imports: [InfrastructureModule], + controllers: [SystemSettingsController], + providers: [SystemSettingsService], + exports: [SystemSettingsService], +}) +export class SystemSettingsModule {} diff --git a/apps/server-nestjs/src/modules/system-settings/system-settings.service.ts b/apps/server-nestjs/src/modules/system-settings/system-settings.service.ts new file mode 100644 index 0000000000..27b4049cd7 --- /dev/null +++ b/apps/server-nestjs/src/modules/system-settings/system-settings.service.ts @@ -0,0 +1,21 @@ +import type { SystemSetting } from '@prisma/client' +import type { SystemSettingDto } from './dto/system-setting.dto' +import { Inject, Injectable } from '@nestjs/common' +import { PrismaService } from '../../cpin-module/infrastructure/database/prisma.service' + +@Injectable() +export class SystemSettingsService { + constructor(@Inject(PrismaService) private readonly prisma: PrismaService) {} + + async list(key?: string): Promise { + return this.prisma.systemSetting.findMany({ where: { key } }) + } + + async upsert(key: string, systemSetting: SystemSettingDto): Promise { + return this.prisma.systemSetting.upsert({ + create: { key, ...systemSetting }, + update: systemSetting, + where: { key }, + }) + } +} diff --git a/apps/server-nestjs/test/system-settings.e2e-spec.ts b/apps/server-nestjs/test/system-settings.e2e-spec.ts new file mode 100644 index 0000000000..6cfb8239ec --- /dev/null +++ b/apps/server-nestjs/test/system-settings.e2e-spec.ts @@ -0,0 +1,120 @@ +import type { INestApplication } from '@nestjs/common' +import { faker } from '@faker-js/faker' +import { ValidationPipe } from '@nestjs/common' +import { Test } from '@nestjs/testing' +import request from 'supertest' +import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import { PrismaService } from '../src/cpin-module/infrastructure/database/prisma.service' +import { SystemSettingsModule } from '../src/modules/system-settings/system-settings.module' + +const canRunSystemSettingsE2E = Boolean(process.env.E2E) + +const describeWithSystemSettings = describe.runIf(canRunSystemSettingsE2E) + +describeWithSystemSettings('systemSettingsController (e2e)', () => { + let app: INestApplication + let prisma: PrismaService + const headerUserId = 'x-test-user-id' + + let testUserId: string + let testSystemSettingKey: string + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [SystemSettingsModule], + }).compile() + + app = moduleRef.createNestApplication() + app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true })) + await app.init() + + prisma = app.get(PrismaService) + await prisma.$connect() + + testUserId = faker.string.uuid() + testSystemSettingKey = faker.lorem.slug() + + await prisma.user.create({ + data: { + id: testUserId, + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: faker.internet.email(), + type: 'human', + adminRoleIds: [], + }, + }) + + await prisma.systemSetting.upsert({ + where: { key: testSystemSettingKey }, + create: { key: testSystemSettingKey, value: faker.lorem.word() }, + update: { value: faker.lorem.word() }, + }) + }) + + afterAll(async () => { + await prisma.systemSetting.deleteMany({ where: { key: testSystemSettingKey } }) + await prisma.user.deleteMany({ where: { id: testUserId } }) + await prisma.$disconnect() + await app.close() + }) + + it('read', async () => { + const userId = faker.string.uuid() + await prisma.user.create({ + data: { + id: userId, + firstName: 'Test', + lastName: 'User', + email: `user-${userId}@test.local`, + type: 'human', + }, + }) + const key = `e2e-system-setting-${faker.string.uuid()}` + await prisma.systemSetting.upsert({ + where: { key }, + create: { key, value: 'bar' }, + update: { value: 'bar' }, + }) + + await request(app.getHttpServer()) + .get(`/api/v1/system/settings?key=${encodeURIComponent(key)}`) + .set(headerUserId, userId) + .expect(200) + .expect(({ body }) => { + expect(body).toEqual([expect.objectContaining({ key, value: 'bar' })]) + }) + + await prisma.systemSetting.delete({ where: { key } }) + await prisma.user.delete({ where: { id: userId } }) + }) + + it('upserts', async () => { + const userId = faker.string.uuid() + await prisma.user.create({ + data: { + id: userId, + firstName: 'Test', + lastName: 'User', + email: `user-${userId}@test.local`, + type: 'human', + adminRoleIds: [], + }, + }) + const key = `e2e-system-setting-${faker.string.uuid()}` + await request(app.getHttpServer()) + .put(`/api/v1/system/settings/${encodeURIComponent(key)}`) + .set(headerUserId, userId) + .send({ value: 'bar' }) + .expect(200) + .expect(({ body }) => { + expect(body).toEqual(expect.objectContaining({ key, value: 'bar' })) + }) + + const saved = await prisma.systemSetting.findUnique({ where: { key } }) + expect(saved).toEqual(expect.objectContaining({ key, value: 'bar' })) + + await prisma.systemSetting.delete({ where: { key } }) + await prisma.user.delete({ where: { id: userId } }) + }) +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 920f2aca05..e7d0f55dac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -423,25 +423,25 @@ importers: version: 2.7.2 '@nestjs/common': specifier: ^11.1.16 - version: 11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/config': specifier: ^4.0.3 - version: 4.0.3(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2) + version: 4.0.3(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2) '@nestjs/core': specifier: ^11.1.16 - version: 11.1.16(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/event-emitter': specifier: ^3.0.1 - version: 3.0.1(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16) + version: 3.0.1(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16) '@nestjs/platform-express': specifier: ^11.1.16 - version: 11.1.16(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16) + version: 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16) '@nestjs/schedule': specifier: ^6.1.1 - version: 6.1.1(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16) + version: 6.1.1(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16) '@nestjs/terminus': specifier: ^11.1.1 - version: 11.1.1(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)(@prisma/client@6.19.2(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 11.1.1(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)(@prisma/client@6.19.2(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2) '@opentelemetry/api': specifier: ^1.9.0 version: 1.9.0 @@ -484,6 +484,12 @@ importers: axios: specifier: ^1.13.6 version: 1.13.6 + class-transformer: + specifier: ^0.5.1 + version: 0.5.1 + class-validator: + specifier: ^0.14.4 + version: 0.14.4 date-fns: specifier: ^4.1.0 version: 4.1.0 @@ -504,7 +510,7 @@ importers: version: 4.2.0 nestjs-pino: specifier: ^4.6.0 - version: 4.6.0(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.3.1)(rxjs@7.8.2) + version: 4.6.0(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.3.1)(rxjs@7.8.2) pino-http: specifier: ^11.0.0 version: 11.0.0 @@ -553,7 +559,7 @@ importers: version: 11.0.9(chokidar@4.0.3)(typescript@5.9.3) '@nestjs/testing': specifier: ^11.1.16 - version: 11.1.16(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)(@nestjs/platform-express@11.1.16) + version: 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)(@nestjs/platform-express@11.1.16) '@types/express': specifier: ^5.0.6 version: 5.0.6 @@ -4137,6 +4143,9 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/validator@13.15.10': + resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==} + '@typescript-eslint/eslint-plugin@8.57.0': resolution: {integrity: sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4890,6 +4899,12 @@ packages: cjs-module-lexer@2.2.0: resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} + class-transformer@0.5.1: + resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} + + class-validator@0.14.4: + resolution: {integrity: sha512-AwNusCCam51q703dW82x95tOqQp6oC9HNUl724KxJJOfnKscI8dOloXFgyez7LbTTKWuRBA37FScqVbJEoq8Yw==} + class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} @@ -6614,6 +6629,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + libphonenumber-js@1.12.41: + resolution: {integrity: sha512-lsmMmGXBxXIK/VMLEj0kL6MtUs1kBGj1nTCzi6zgQoG1DEwqwt2DQyHxcLykceIxAnfE3hya7NuIh6PpC6S3fA==} + lie@3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} @@ -8617,6 +8635,10 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + validator@13.15.35: + resolution: {integrity: sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==} + engines: {node: '>= 0.10'} + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -10972,7 +10994,7 @@ snapshots: - uglify-js - webpack-cli - '@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: file-type: 21.3.0 iterare: 1.2.1 @@ -10981,20 +11003,23 @@ snapshots: rxjs: 7.8.2 tslib: 2.8.1 uid: 2.0.2 + optionalDependencies: + class-transformer: 0.5.1 + class-validator: 0.14.4 transitivePeerDependencies: - supports-color - '@nestjs/config@4.0.3(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)': + '@nestjs/config@4.0.3(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) dotenv: 17.2.3 dotenv-expand: 12.0.3 lodash: 4.17.23 rxjs: 7.8.2 - '@nestjs/core@11.1.16(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/core@11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nuxt/opencollective': 0.4.1 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -11004,18 +11029,18 @@ snapshots: tslib: 2.8.1 uid: 2.0.2 optionalDependencies: - '@nestjs/platform-express': 11.1.16(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16) + '@nestjs/platform-express': 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16) - '@nestjs/event-emitter@3.0.1(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)': + '@nestjs/event-emitter@3.0.1(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)': dependencies: - '@nestjs/common': 11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.16(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) eventemitter2: 6.4.9 - '@nestjs/platform-express@11.1.16(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)': + '@nestjs/platform-express@11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)': dependencies: - '@nestjs/common': 11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.16(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) cors: 2.8.6 express: 5.2.1 multer: 2.1.1 @@ -11024,10 +11049,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/schedule@6.1.1(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)': + '@nestjs/schedule@6.1.1(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)': dependencies: - '@nestjs/common': 11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.16(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) cron: 4.4.0 '@nestjs/schematics@11.0.9(chokidar@4.0.3)(typescript@5.9.3)': @@ -11041,10 +11066,10 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/terminus@11.1.1(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)(@prisma/client@6.19.2(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/terminus@11.1.1(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)(@prisma/client@6.19.2(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.16(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) boxen: 5.1.2 check-disk-space: 3.4.0 reflect-metadata: 0.2.2 @@ -11054,13 +11079,13 @@ snapshots: '@grpc/proto-loader': 0.8.0 '@prisma/client': 6.19.2(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))(typescript@5.9.3) - '@nestjs/testing@11.1.16(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)(@nestjs/platform-express@11.1.16)': + '@nestjs/testing@11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16)(@nestjs/platform-express@11.1.16)': dependencies: - '@nestjs/common': 11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.16(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.16)(reflect-metadata@0.2.2)(rxjs@7.8.2) tslib: 2.8.1 optionalDependencies: - '@nestjs/platform-express': 11.1.16(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16) + '@nestjs/platform-express': 11.1.16(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.16) '@noble/hashes@1.8.0': {} @@ -12439,6 +12464,8 @@ snapshots: '@types/unist@3.0.3': {} + '@types/validator@13.15.10': {} + '@typescript-eslint/eslint-plugin@8.57.0(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -13545,6 +13572,14 @@ snapshots: cjs-module-lexer@2.2.0: {} + class-transformer@0.5.1: {} + + class-validator@0.14.4: + dependencies: + '@types/validator': 13.15.10 + libphonenumber-js: 1.12.41 + validator: 13.15.35 + class-variance-authority@0.7.1: dependencies: clsx: 2.1.1 @@ -15555,6 +15590,8 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + libphonenumber-js@1.12.41: {} + lie@3.3.0: dependencies: immediate: 3.0.6 @@ -16210,9 +16247,9 @@ snapshots: neo-async@2.6.2: {} - nestjs-pino@4.6.0(@nestjs/common@11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.3.1)(rxjs@7.8.2): + nestjs-pino@4.6.0(@nestjs/common@11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.3.1)(rxjs@7.8.2): dependencies: - '@nestjs/common': 11.1.16(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.16(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) pino: 10.3.1 pino-http: 11.0.0 rxjs: 7.8.2 @@ -18023,6 +18060,8 @@ snapshots: v8-compile-cache-lib@3.0.1: {} + validator@13.15.35: {} + vary@1.1.2: {} vite-node@2.1.9(@types/node@22.19.15)(terser@5.46.0):