From 17a6b48895b1a391d72789d7a13abe6cc33d1345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=AD=E1=86=BC?= =?UTF-8?q?=E1=84=86=E1=85=B5=E1=86=AB?= Date: Tue, 24 Feb 2026 20:12:39 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat(api):=20Sentry=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=88=ED=84=B0=EB=A7=81=20=ED=86=B5=ED=95=A9=20?= =?UTF-8?q?=EB=B0=8F=20Docker=20=EB=B0=B0=ED=8F=AC=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=A0=95=EB=B9=84=20(#212)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 2 +- .env.docker.dev.example | 60 +- .env.docker.prod.example | 43 +- apps/api/Dockerfile | 26 +- apps/api/package.json | 1 + apps/api/prisma.config.ts | 10 +- apps/api/src/app.module.ts | 10 +- .../src/common/config/schemas/email.schema.ts | 4 +- .../common/config/schemas/external.schema.ts | 3 +- .../common/config/services/config.service.ts | 7 +- .../src/common/exception/exception.module.ts | 6 + apps/api/src/instrument.ts | 7 + apps/api/src/main.ts | 2 + docker-compose.prod.yml | 2 + pnpm-lock.yaml | 733 +++++++++++++++++- 15 files changed, 826 insertions(+), 90 deletions(-) create mode 100644 apps/api/src/instrument.ts diff --git a/.dockerignore b/.dockerignore index 5357a149..26471021 100644 --- a/.dockerignore +++ b/.dockerignore @@ -30,7 +30,7 @@ apps/mobile/ # Documentation *.md -!README.md +myDocs/ # Tests coverage/ diff --git a/.env.docker.dev.example b/.env.docker.dev.example index 9c197a8f..b2ee1148 100644 --- a/.env.docker.dev.example +++ b/.env.docker.dev.example @@ -10,42 +10,64 @@ PORT=8080 # --- Database --- # 호스트명 'db'는 docker-compose.dev.yml의 서비스명 DATABASE_URL=postgresql://postgres:postgres@db:5432/aido -# 호스트 포트 (기본 5432와 충돌 방지) +# 호스트 포트 (로컬 5432와 충돌 방지) DB_PORT=5433 -# --- JWT (개발용 기본값) --- -JWT_SECRET=dev-jwt-secret-minimum-32-characters-long -JWT_REFRESH_SECRET=dev-jwt-refresh-secret-minimum-32-chars +# --- JWT --- +JWT_SECRET=dev-jwt-secret-key-minimum-32-characters-long-for-security +JWT_REFRESH_SECRET=dev-refresh-secret-key-minimum-32-characters-long-here JWT_EXPIRES_IN=15m JWT_REFRESH_EXPIRES_IN=7d # --- Security --- -TOKEN_ENCRYPTION_KEY=dev-token-encryption-key-32chars! +TOKEN_ENCRYPTION_KEY=dev-token-encryption-key-minimum-32-characters-long +CORS_ORIGINS=http://localhost:3000,http://localhost:8081,exp://localhost:8081 +THROTTLE_TTL=60000 +THROTTLE_LIMIT=1000 -# --- OAuth (선택사항 - 필요한 것만 설정) --- +# --- OAuth: Google --- # GOOGLE_CLIENT_ID= # GOOGLE_CLIENT_SECRET= -# GOOGLE_CALLBACK_URL=http://localhost:8080/auth/google/callback +# GOOGLE_CALLBACK_URL=http://localhost:8080/v1/auth/google/web-callback +# --- OAuth: Apple --- +# APPLE_TEAM_ID= +# APPLE_CLIENT_ID= +# APPLE_KEY_ID= +# APPLE_PRIVATE_KEY= +# APPLE_CALLBACK_URL=http://localhost:8080/v1/auth/apple/callback + +# --- OAuth: Kakao --- # KAKAO_CLIENT_ID= # KAKAO_CLIENT_SECRET= -# KAKAO_CALLBACK_URL=http://localhost:8080/auth/kakao/callback +# KAKAO_CALLBACK_URL=http://localhost:8080/v1/auth/kakao/web-callback +# --- OAuth: Naver --- # NAVER_CLIENT_ID= # NAVER_CLIENT_SECRET= -# NAVER_CALLBACK_URL=http://localhost:8080/auth/naver/callback - -# APPLE_CLIENT_ID= -# APPLE_SERVICE_ID= -# APPLE_TEAM_ID= -# APPLE_KEY_ID= -# APPLE_PRIVATE_KEY= -# APPLE_CALLBACK_URL=http://localhost:8080/auth/apple/callback +# NAVER_CALLBACK_URL=http://localhost:8080/v1/auth/naver/web-callback -# --- Email (선택사항) --- +# --- Email --- # RESEND_API_KEY= +# EMAIL_FROM=hello@your-domain.com +# EMAIL_FROM_NAME=Aido -# --- External Services (선택사항) --- -# EXPO_ACCESS_TOKEN= +# --- External Services --- # GOOGLE_GENERATIVE_AI_API_KEY= # DISCORD_SIGNUP_WEBHOOK_URL= + +# --- RevenueCat --- +# REVENUECAT_SECRET_API_KEY= +# REVENUECAT_WEBHOOK_SECRET= + +# --- Cache --- +# CACHE_TYPE=memory +# CACHE_DEFAULT_TTL_MS=60000 +# CACHE_MAX_ITEMS=1000 +# CACHE_CLEANUP_INTERVAL_MS=30000 + +# --- Push Notifications --- +# EXPO_ACCESS_TOKEN= + +# --- Monitoring --- +# SENTRY_DSN= diff --git a/.env.docker.prod.example b/.env.docker.prod.example index 25779898..d8a496b3 100644 --- a/.env.docker.prod.example +++ b/.env.docker.prod.example @@ -9,7 +9,7 @@ NODE_ENV=production PORT=8080 # --- Database --- -# DB_USER, DB_PASSWORD, DB_NAME을 변경하면 DATABASE_URL도 맞춰 수정하세요 +# ⚠️ DB_PASSWORD 변경 시 DATABASE_URL도 동일한 값으로 맞추세요 DB_USER=postgres DB_PASSWORD=CHANGE_ME DB_NAME=aido @@ -28,25 +28,27 @@ CORS_ORIGINS=https://your-domain.com THROTTLE_TTL=60000 THROTTLE_LIMIT=100 -# --- OAuth (프로덕션에서 최소 1개 필수) --- +# --- OAuth: Google --- GOOGLE_CLIENT_ID=CHANGE_ME GOOGLE_CLIENT_SECRET=CHANGE_ME -GOOGLE_CALLBACK_URL=https://your-domain.com/auth/google/callback +GOOGLE_CALLBACK_URL=https://your-domain.com/v1/auth/google/web-callback +# --- OAuth: Apple --- +# APPLE_TEAM_ID= +# APPLE_CLIENT_ID= +# APPLE_KEY_ID= +# APPLE_PRIVATE_KEY= +# APPLE_CALLBACK_URL=https://your-domain.com/v1/auth/apple/callback + +# --- OAuth: Kakao --- # KAKAO_CLIENT_ID= # KAKAO_CLIENT_SECRET= -# KAKAO_CALLBACK_URL= +# KAKAO_CALLBACK_URL=https://your-domain.com/v1/auth/kakao/web-callback +# --- OAuth: Naver --- # NAVER_CLIENT_ID= # NAVER_CLIENT_SECRET= -# NAVER_CALLBACK_URL= - -# APPLE_CLIENT_ID= -# APPLE_SERVICE_ID= -# APPLE_TEAM_ID= -# APPLE_KEY_ID= -# APPLE_PRIVATE_KEY= -# APPLE_CALLBACK_URL= +# NAVER_CALLBACK_URL=https://your-domain.com/v1/auth/naver/web-callback # --- Email (프로덕션 필수) --- RESEND_API_KEY=CHANGE_ME @@ -55,16 +57,21 @@ EMAIL_FROM_NAME=Aido SUPPORT_EMAIL=support@your-domain.com # --- External Services --- -# EXPO_ACCESS_TOKEN= # GOOGLE_GENERATIVE_AI_API_KEY= # DISCORD_SIGNUP_WEBHOOK_URL= +# --- RevenueCat --- +# REVENUECAT_SECRET_API_KEY= +# REVENUECAT_WEBHOOK_SECRET= + # --- Cache --- -# CACHE_TYPE=memory -# REDIS_HOST= -# REDIS_PORT=6379 -# REDIS_PASSWORD= -# REDIS_DB=0 +CACHE_TYPE=memory +CACHE_DEFAULT_TTL_MS=60000 +CACHE_MAX_ITEMS=1000 +CACHE_CLEANUP_INTERVAL_MS=30000 + +# --- Push Notifications --- +# EXPO_ACCESS_TOKEN= # --- Monitoring --- # SENTRY_DSN= diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index fcde270c..f0f5da85 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -1,13 +1,10 @@ -# ============================================================================== # Stage 1: base - Node + pnpm -# ============================================================================== FROM node:22-alpine AS base -RUN corepack enable && corepack prepare pnpm@9.15.4 --activate +RUN corepack enable && corepack prepare pnpm@10.29.3 --activate WORKDIR /app -# ============================================================================== + # Stage 2: deps - 의존성 설치 -# ============================================================================== FROM base AS deps COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ @@ -25,9 +22,7 @@ COPY tooling/vitest/package.json ./tooling/vitest/ RUN pnpm install --frozen-lockfile --ignore-scripts -# ============================================================================== # Stage 3: builder - 빌드 -# ============================================================================== FROM base AS builder COPY --from=deps /app ./ @@ -40,27 +35,28 @@ RUN pnpm --filter @aido/errors build && \ pnpm --filter @aido/validators build # Prisma generate + API build -RUN pnpm --filter @aido/api prisma generate +RUN cd apps/api && pnpm prisma generate RUN pnpm --filter @aido/api build # Production 의존성만 추출 RUN pnpm --filter @aido/api deploy --prod /app/production -# ============================================================================== -# Stage 4: migrate - Prisma 마이그레이션 (ECS 별도 태스크) -# ============================================================================== + +# Stage 4: migrate - Prisma 마이그레이션 FROM base AS migrate COPY --from=deps /app ./ COPY apps/api/prisma ./apps/api/prisma +COPY apps/api/prisma.config.ts ./apps/api/ WORKDIR /app/apps/api +RUN pnpm prisma generate + CMD ["pnpm", "prisma", "migrate", "deploy"] -# ============================================================================== + # Stage 5: development - 개발 핫리로드 -# ============================================================================== FROM base AS development COPY --from=deps /app ./ @@ -70,7 +66,7 @@ RUN pnpm --filter @aido/errors build && \ pnpm --filter @aido/utils build && \ pnpm --filter @aido/validators build -RUN pnpm --filter @aido/api prisma generate +RUN cd apps/api && pnpm prisma generate WORKDIR /app/apps/api @@ -79,9 +75,7 @@ EXPOSE 8080 CMD ["pnpm", "dev"] -# ============================================================================== # Stage 6: production - 최종 런타임 -# ============================================================================== FROM node:22-alpine AS production WORKDIR /app diff --git a/apps/api/package.json b/apps/api/package.json index a9e83783..b056a5dc 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -45,6 +45,7 @@ "@nestjs/throttler": "^6.5.0", "@prisma/adapter-pg": "^7.4.0", "@prisma/client": "^7.4.0", + "@sentry/nestjs": "^10.39.0", "ai": "^6.0.86", "argon2": "^0.44.0", "date-fns": "^4.1.0", diff --git a/apps/api/prisma.config.ts b/apps/api/prisma.config.ts index 6e0a413e..9f3778e6 100644 --- a/apps/api/prisma.config.ts +++ b/apps/api/prisma.config.ts @@ -1,9 +1,13 @@ import path from "node:path"; -import { config } from "dotenv"; import { defineConfig } from "prisma/config"; -// .env 파일에서 환경변수 로드 -config({ path: path.resolve(__dirname, ".env") }); +// .env 파일에서 환경변수 로드 (dotenv가 없는 Docker 빌드 환경에서도 동작) +try { + const { config } = require("dotenv"); + config({ path: path.resolve(__dirname, ".env") }); +} catch { + // dotenv 미설치 환경 (Docker build) — DATABASE_URL fallback 사용 +} // DATABASE_URL (prisma generate는 실제 연결 불필요, placeholder 허용) const DATABASE_URL = diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts index 7462ec34..15cd1c39 100644 --- a/apps/api/src/app.module.ts +++ b/apps/api/src/app.module.ts @@ -3,6 +3,7 @@ import { ConfigService } from "@nestjs/config"; import { APP_GUARD } from "@nestjs/core"; import { EventEmitterModule } from "@nestjs/event-emitter"; import { ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler"; +import { SentryModule } from "@sentry/nestjs/setup"; import { AppConfigModule, CacheModule, @@ -38,7 +39,10 @@ import { AppService } from "./app.service"; // 1. Configuration (Must be loaded first) AppConfigModule, - // 2. Infrastructure + // 2. Monitoring + SentryModule.forRoot(), + + // 3. Infrastructure DatabaseModule, EncryptionModule, CacheModule.forRoot(), @@ -52,7 +56,7 @@ import { AppService } from "./app.service"; ignoreErrors: false, }), - // 3. Global Modules + // 4. Global Modules LoggerModule.forRootAsync(), ExceptionModule, ResponseModule, @@ -67,7 +71,7 @@ import { AppService } from "./app.service"; ], }), - // 4. Features + // 5. Features AdminModule, AdminNotificationModule, AiModule, diff --git a/apps/api/src/common/config/schemas/email.schema.ts b/apps/api/src/common/config/schemas/email.schema.ts index 27150b98..9c4b69de 100644 --- a/apps/api/src/common/config/schemas/email.schema.ts +++ b/apps/api/src/common/config/schemas/email.schema.ts @@ -8,13 +8,13 @@ export const emailSchema = z.object({ RESEND_API_KEY: z.string().optional(), /** 발신자 이메일 주소 */ - EMAIL_FROM: z.email().default("dydals3440@gmail.com"), + EMAIL_FROM: z.email().default("noreply@aido.kr"), /** 발신자 이름 */ EMAIL_FROM_NAME: z.string().default("Aido"), /** 문의 수신 이메일 */ - SUPPORT_EMAIL: z.email().default("dydals3440@gmail.com"), + SUPPORT_EMAIL: z.email().default("support@aido.kr"), }); export type EmailConfig = z.infer; diff --git a/apps/api/src/common/config/schemas/external.schema.ts b/apps/api/src/common/config/schemas/external.schema.ts index c8e72a59..b74b48b7 100644 --- a/apps/api/src/common/config/schemas/external.schema.ts +++ b/apps/api/src/common/config/schemas/external.schema.ts @@ -6,7 +6,8 @@ import { z } from "zod"; */ export const externalSchema = z.object({ // RevenueCat 구독 관리 - REVENUECAT_API_KEY: z.string().optional(), + REVENUECAT_SECRET_API_KEY: z.string().optional(), + REVENUECAT_WEBHOOK_SECRET: z.string().optional(), // Redis 캐시/세션 (선택) - 빈 문자열 허용 REDIS_URL: z diff --git a/apps/api/src/common/config/services/config.service.ts b/apps/api/src/common/config/services/config.service.ts index 7199be77..17b5d954 100644 --- a/apps/api/src/common/config/services/config.service.ts +++ b/apps/api/src/common/config/services/config.service.ts @@ -182,8 +182,11 @@ export class TypedConfigService { return this.get("EXPO_ACCESS_TOKEN"); } - get revenueCatApiKey(): string | undefined { - return this.get("REVENUECAT_API_KEY"); + get revenuecat() { + return { + secretApiKey: this.get("REVENUECAT_SECRET_API_KEY"), + webhookSecret: this.get("REVENUECAT_WEBHOOK_SECRET"), + }; } get redisUrl(): string | undefined { diff --git a/apps/api/src/common/exception/exception.module.ts b/apps/api/src/common/exception/exception.module.ts index f69a31ed..c9f4591b 100644 --- a/apps/api/src/common/exception/exception.module.ts +++ b/apps/api/src/common/exception/exception.module.ts @@ -1,13 +1,19 @@ import { Module } from "@nestjs/common"; import { APP_FILTER } from "@nestjs/core"; +import { SentryGlobalFilter } from "@sentry/nestjs/setup"; import { GlobalExceptionFilter } from "./filters/global-exception.filter"; /** * Exception 모듈 * 전역 예외 처리를 담당 + * SentryGlobalFilter → GlobalExceptionFilter 순서로 처리 */ @Module({ providers: [ + { + provide: APP_FILTER, + useClass: SentryGlobalFilter, + }, { provide: APP_FILTER, useClass: GlobalExceptionFilter, diff --git a/apps/api/src/instrument.ts b/apps/api/src/instrument.ts new file mode 100644 index 00000000..3b71f3ee --- /dev/null +++ b/apps/api/src/instrument.ts @@ -0,0 +1,7 @@ +import * as Sentry from "@sentry/nestjs"; + +Sentry.init({ + dsn: process.env.SENTRY_DSN, + environment: process.env.NODE_ENV || "development", + tracesSampleRate: process.env.NODE_ENV === "production" ? 0.2 : 1.0, +}); diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index 7c57f44f..5eabf578 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -1,3 +1,5 @@ +import "./instrument"; + import { ConfigService } from "@nestjs/config"; import { NestFactory } from "@nestjs/core"; import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger"; diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index f0eb4e34..2a3d3639 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -19,6 +19,7 @@ services: interval: 5s timeout: 5s retries: 5 + restart: unless-stopped deploy: resources: limits: @@ -63,6 +64,7 @@ services: condition: service_healthy migrate: condition: service_completed_successfully + restart: unless-stopped deploy: resources: limits: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7ac56a9f..f9880df4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -110,6 +110,9 @@ importers: '@prisma/client': specifier: ^7.4.0 version: 7.4.0(prisma@7.4.0(@types/react@19.1.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3))(typescript@5.9.3) + '@sentry/nestjs': + specifier: ^10.39.0 + version: 10.39.0(@nestjs/common@11.1.13(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.13) ai: specifier: ^6.0.86 version: 6.0.86(zod@4.3.6) @@ -272,7 +275,7 @@ importers: version: 10.28.0 ts-jest: specifier: ^29.2.5 - version: 29.4.6(@babel/core@7.28.6)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.6))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.7)(ts-node@10.9.2(@swc/core@1.15.11)(@types/node@22.19.7)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.0))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.7)(ts-node@10.9.2(@swc/core@1.15.11)(@types/node@22.19.7)(typescript@5.9.3)))(typescript@5.9.3) ts-loader: specifier: ^9.5.2 version: 9.5.4(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.15.11)) @@ -685,6 +688,12 @@ packages: resolution: {integrity: sha512-J4Jarr0SohdrHcb40gTL4wGPCQ952IMWF1G/MSAQfBAPvA9ZKApYhpxcY7PmehVePve+ujpus1dGsJ7dPxz8Kg==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + '@apm-js-collab/code-transformer@0.8.2': + resolution: {integrity: sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA==} + + '@apm-js-collab/tracing-hooks@0.3.1': + resolution: {integrity: sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw==} + '@babel/code-frame@7.10.4': resolution: {integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==} @@ -2560,10 +2569,212 @@ packages: engines: {node: ^14.18.0 || >=16.10.0, npm: '>=5.10.0'} hasBin: true + '@opentelemetry/api-logs@0.207.0': + resolution: {integrity: sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api-logs@0.211.0': + resolution: {integrity: sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==} + engines: {node: '>=8.0.0'} + '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} + '@opentelemetry/context-async-hooks@2.5.1': + resolution: {integrity: sha512-MHbu8XxCHcBn6RwvCt2Vpn1WnLMNECfNKYB14LI5XypcgH4IE0/DiVifVR9tAkwPMyLXN8dOoPJfya3IryLQVw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.5.0': + resolution: {integrity: sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.5.1': + resolution: {integrity: sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/instrumentation-amqplib@0.58.0': + resolution: {integrity: sha512-fjpQtH18J6GxzUZ+cwNhWUpb71u+DzT7rFkg5pLssDGaEber91Y2WNGdpVpwGivfEluMlNMZumzjEqfg8DeKXQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-connect@0.54.0': + resolution: {integrity: sha512-43RmbhUhqt3uuPnc16cX6NsxEASEtn8z/cYV8Zpt6EP4p2h9s4FNuJ4Q9BbEQ2C0YlCCB/2crO1ruVz/hWt8fA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-dataloader@0.28.0': + resolution: {integrity: sha512-ExXGBp0sUj8yhm6Znhf9jmuOaGDsYfDES3gswZnKr4MCqoBWQdEFn6EoDdt5u+RdbxQER+t43FoUihEfTSqsjA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-express@0.59.0': + resolution: {integrity: sha512-pMKV/qnHiW/Q6pmbKkxt0eIhuNEtvJ7sUAyee192HErlr+a1Jx+FZ3WjfmzhQL1geewyGEiPGkmjjAgNY8TgDA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-fs@0.30.0': + resolution: {integrity: sha512-n3Cf8YhG7reaj5dncGlRIU7iT40bxPOjsBEA5Bc1a1g6e9Qvb+JFJ7SEiMlPbUw4PBmxE3h40ltE8LZ3zVt6OA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-generic-pool@0.54.0': + resolution: {integrity: sha512-8dXMBzzmEdXfH/wjuRvcJnUFeWzZHUnExkmFJ2uPfa31wmpyBCMxO59yr8f/OXXgSogNgi/uPo9KW9H7LMIZ+g==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-graphql@0.58.0': + resolution: {integrity: sha512-+yWVVY7fxOs3j2RixCbvue8vUuJ1inHxN2q1sduqDB0Wnkr4vOzVKRYl/Zy7B31/dcPS72D9lo/kltdOTBM3bQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-hapi@0.57.0': + resolution: {integrity: sha512-Os4THbvls8cTQTVA8ApLfZZztuuqGEeqog0XUnyRW7QVF0d/vOVBEcBCk1pazPFmllXGEdNbbat8e2fYIWdFbw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-http@0.211.0': + resolution: {integrity: sha512-n0IaQ6oVll9PP84SjbOCwDjaJasWRHi6BLsbMLiT6tNj7QbVOkuA5sk/EfZczwI0j5uTKl1awQPivO/ldVtsqA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-ioredis@0.59.0': + resolution: {integrity: sha512-875UxzBHWkW+P4Y45SoFM2AR8f8TzBMD8eO7QXGCyFSCUMP5s9vtt/BS8b/r2kqLyaRPK6mLbdnZznK3XzQWvw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-kafkajs@0.20.0': + resolution: {integrity: sha512-yJXOuWZROzj7WmYCUiyT27tIfqBrVtl1/TwVbQyWPz7rL0r1Lu7kWjD0PiVeTCIL6CrIZ7M2s8eBxsTAOxbNvw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-knex@0.55.0': + resolution: {integrity: sha512-FtTL5DUx5Ka/8VK6P1VwnlUXPa3nrb7REvm5ddLUIeXXq4tb9pKd+/ThB1xM/IjefkRSN3z8a5t7epYw1JLBJQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-koa@0.59.0': + resolution: {integrity: sha512-K9o2skADV20Skdu5tG2bogPKiSpXh4KxfLjz6FuqIVvDJNibwSdu5UvyyBzRVp1rQMV6UmoIk6d3PyPtJbaGSg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + + '@opentelemetry/instrumentation-lru-memoizer@0.55.0': + resolution: {integrity: sha512-FDBfT7yDGcspN0Cxbu/k8A0Pp1Jhv/m7BMTzXGpcb8ENl3tDj/51U65R5lWzUH15GaZA15HQ5A5wtafklxYj7g==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongodb@0.64.0': + resolution: {integrity: sha512-pFlCJjweTqVp7B220mCvCld1c1eYKZfQt1p3bxSbcReypKLJTwat+wbL2YZoX9jPi5X2O8tTKFEOahO5ehQGsA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongoose@0.57.0': + resolution: {integrity: sha512-MthiekrU/BAJc5JZoZeJmo0OTX6ycJMiP6sMOSRTkvz5BrPMYDqaJos0OgsLPL/HpcgHP7eo5pduETuLguOqcg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql2@0.57.0': + resolution: {integrity: sha512-nHSrYAwF7+aV1E1V9yOOP9TchOodb6fjn4gFvdrdQXiRE7cMuffyLLbCZlZd4wsspBzVwOXX8mpURdRserAhNA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql@0.57.0': + resolution: {integrity: sha512-HFS/+FcZ6Q7piM7Il7CzQ4VHhJvGMJWjx7EgCkP5AnTntSN5rb5Xi3TkYJHBKeR27A0QqPlGaCITi93fUDs++Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-nestjs-core@0.57.0': + resolution: {integrity: sha512-mzTjjethjuk70o/vWUeV12QwMG9EAFJpkn13/q8zi++sNosf2hoGXTplIdbs81U8S3PJ4GxHKsBjM0bj1CGZ0g==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-pg@0.63.0': + resolution: {integrity: sha512-dKm/ODNN3GgIQVlbD6ZPxwRc3kleLf95hrRWXM+l8wYo+vSeXtEpQPT53afEf6VFWDVzJK55VGn8KMLtSve/cg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-redis@0.59.0': + resolution: {integrity: sha512-JKv1KDDYA2chJ1PC3pLP+Q9ISMQk6h5ey+99mB57/ARk0vQPGZTTEb4h4/JlcEpy7AYT8HIGv7X6l+br03Neeg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-tedious@0.30.0': + resolution: {integrity: sha512-bZy9Q8jFdycKQ2pAsyuHYUHNmCxCOGdG6eg1Mn75RvQDccq832sU5OWOBnc12EFUELI6icJkhR7+EQKMBam2GA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-undici@0.21.0': + resolution: {integrity: sha512-gok0LPUOTz2FQ1YJMZzaHcOzDFyT64XJ8M9rNkugk923/p6lDGms/cRW1cqgqp6N6qcd6K6YdVHwPEhnx9BWbw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.7.0 + + '@opentelemetry/instrumentation@0.207.0': + resolution: {integrity: sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.211.0': + resolution: {integrity: sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/redis-common@0.38.2': + resolution: {integrity: sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==} + engines: {node: ^18.19.0 || >=20.6.0} + + '@opentelemetry/resources@2.5.1': + resolution: {integrity: sha512-BViBCdE/GuXRlp9k7nS1w6wJvY5fnFX5XvuEtWsTAOQFIO89Eru7lGW3WbfbxtCuZ/GbrJfAziXG0w0dpxL7eQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.5.1': + resolution: {integrity: sha512-iZH3Gw8cxQn0gjpOjJMmKLd9GIaNh/E3v3ST67vyzLSxHBs14HsG4dy7jMYyC5WXGdBVEcM7U/XTF5hCQxjDMw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.39.0': + resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==} + engines: {node: '>=14'} + + '@opentelemetry/sql-common@0.41.2': + resolution: {integrity: sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@paralleldrive/cuid2@2.3.1': resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} @@ -2626,6 +2837,11 @@ packages: '@prisma/get-platform@7.4.0': resolution: {integrity: sha512-fOUIoGzAPgtjHVs4DsVSnEDPBEauAmFeZr4Ej3tMwxywam7hHdRtCzgKagQBKcYIJuya8gzYrTqUoukzXtWJaA==} + '@prisma/instrumentation@7.2.0': + resolution: {integrity: sha512-Rh9Z4x5kEj1OdARd7U18AtVrnL6rmLSI0qYShaB4W7Wx5BKbgzndWF+QnuzMb7GLfVdlT5aYCXoPQVYuYtVu0g==} + peerDependencies: + '@opentelemetry/api': ^1.8 + '@prisma/query-plan-executor@7.2.0': resolution: {integrity: sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==} @@ -3168,6 +3384,58 @@ packages: '@scarf/scarf@1.4.0': resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} + '@sentry/core@10.39.0': + resolution: {integrity: sha512-xCLip2mBwCdRrvXHtVEULX0NffUTYZZBhEUGht0WFL+GNdNQ7gmBOGOczhZlrf2hgFFtDO0fs1xiP9bqq5orEQ==} + engines: {node: '>=18'} + + '@sentry/nestjs@10.39.0': + resolution: {integrity: sha512-PsYpdPzHjtoSP4dJLMENzh0riWSjxtnUIN4/efITdFLQYy7VQIGVvKydo7tomMhsYGfRpscqFRhHm4b74v0QIA==} + engines: {node: '>=18'} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + + '@sentry/node-core@10.39.0': + resolution: {integrity: sha512-xdeBG00TmtAcGvXnZNbqOCvnZ5kY3s5aT/L8wUQ0w0TT2KmrC9XL/7UHUfJ45TLbjl10kZOtaMQXgUjpwSJW+g==} + engines: {node: '>=18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 + '@opentelemetry/core': ^1.30.1 || ^2.1.0 + '@opentelemetry/instrumentation': '>=0.57.1 <1' + '@opentelemetry/resources': ^1.30.1 || ^2.1.0 + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 + '@opentelemetry/semantic-conventions': ^1.39.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@opentelemetry/context-async-hooks': + optional: true + '@opentelemetry/core': + optional: true + '@opentelemetry/instrumentation': + optional: true + '@opentelemetry/resources': + optional: true + '@opentelemetry/sdk-trace-base': + optional: true + '@opentelemetry/semantic-conventions': + optional: true + + '@sentry/node@10.39.0': + resolution: {integrity: sha512-dx66DtU/xkCTPEDsjU+mYSIEbzu06pzKNQcDA2wvx7wvwsUciZ5yA32Ce/o6p2uHHgy0/joJX9rP5J/BIijaOA==} + engines: {node: '>=18'} + + '@sentry/opentelemetry@10.39.0': + resolution: {integrity: sha512-eU8t/pyxjy7xYt6PNCVxT+8SJw5E3pnupdcUNN4ClqG4O5lX4QCDLtId48ki7i30VqrLtR7vmCHMSvqXXdvXPA==} + engines: {node: '>=18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 + '@opentelemetry/core': ^1.30.1 || ^2.1.0 + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 + '@opentelemetry/semantic-conventions': ^1.39.0 + '@shopify/flash-list@2.2.2': resolution: {integrity: sha512-YrvLBK5FCpvuX+d9QvJvjVqyi4eBUaEamkyfh9CjPdF6c+AukP0RSBh97qHyTwOEaVq21A5ukwgyWMDIbmxpmQ==} peerDependencies: @@ -3639,6 +3907,9 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/mysql@2.15.27': + resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==} + '@types/node@18.19.130': resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} @@ -3675,6 +3946,12 @@ packages: '@types/passport@1.0.17': resolution: {integrity: sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==} + '@types/pg-pool@2.0.7': + resolution: {integrity: sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==} + + '@types/pg@8.15.6': + resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==} + '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -3708,6 +3985,9 @@ packages: '@types/supertest@6.0.3': resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==} + '@types/tedious@4.0.14': + resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==} + '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} @@ -3887,6 +4167,11 @@ packages: acorn-globals@7.0.1: resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + acorn-import-phases@1.0.4: resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} engines: {node: '>=10.13.0'} @@ -4492,6 +4777,9 @@ packages: cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + cjs-module-lexer@2.2.0: + resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} + class-transformer@0.5.1: resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} @@ -5737,6 +6025,9 @@ packages: resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} engines: {node: '>=14.0.0'} + forwarded-parse@2.1.2: + resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -6071,6 +6362,9 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} + import-in-the-middle@2.0.6: + resolution: {integrity: sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==} + import-local@3.2.0: resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} engines: {node: '>=8'} @@ -7111,6 +7405,9 @@ packages: engines: {node: '>=10'} hasBin: true + module-details-from-path@1.0.4: + resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} + ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -8021,6 +8318,10 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + require-in-the-middle@8.0.1: + resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==} + engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} + requireg@0.2.2: resolution: {integrity: sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==} engines: {node: '>= 4.0.0'} @@ -8639,6 +8940,7 @@ packages: tar@7.5.7: resolution: {integrity: sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==} engines: {node: '>=18'} + 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 temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} @@ -9493,6 +9795,16 @@ snapshots: transitivePeerDependencies: - chokidar + '@apm-js-collab/code-transformer@0.8.2': {} + + '@apm-js-collab/tracing-hooks@0.3.1': + dependencies: + '@apm-js-collab/code-transformer': 0.8.2 + debug: 4.4.3 + module-details-from-path: 1.0.4 + transitivePeerDependencies: + - supports-color + '@babel/code-frame@7.10.4': dependencies: '@babel/highlight': 7.25.9 @@ -11893,8 +12205,269 @@ snapshots: dependencies: consola: 3.4.2 + '@opentelemetry/api-logs@0.207.0': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api-logs@0.211.0': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api@1.9.0': {} + '@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/instrumentation-amqplib@0.58.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-connect@0.54.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@types/connect': 3.4.38 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-dataloader@0.28.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-express@0.59.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-fs@0.30.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-generic-pool@0.54.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-graphql@0.58.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-hapi@0.57.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-http@0.211.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + forwarded-parse: 2.1.2 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-ioredis@0.59.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.38.2 + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-kafkajs@0.20.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-knex@0.55.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-koa@0.59.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-lru-memoizer@0.55.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mongodb@0.64.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mongoose@0.57.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mysql2@0.57.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mysql@0.57.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@types/mysql': 2.15.27 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-nestjs-core@0.57.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-pg@0.63.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) + '@types/pg': 8.15.6 + '@types/pg-pool': 2.0.7 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-redis@0.59.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.38.2 + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-tedious@0.30.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@types/tedious': 4.0.14 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-undici@0.21.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.207.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.207.0 + import-in-the-middle: 2.0.6 + require-in-the-middle: 8.0.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.211.0 + import-in-the-middle: 2.0.6 + require-in-the-middle: 8.0.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/redis-common@0.38.2': {} + + '@opentelemetry/resources@2.5.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/semantic-conventions@1.39.0': {} + + '@opentelemetry/sql-common@0.41.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@paralleldrive/cuid2@2.3.1': dependencies: '@noble/hashes': 1.8.0 @@ -11985,6 +12558,13 @@ snapshots: dependencies: '@prisma/debug': 7.4.0 + '@prisma/instrumentation@7.2.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.207.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + '@prisma/query-plan-executor@7.2.0': {} '@prisma/studio-core@0.13.1(@types/react@19.1.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': @@ -12491,6 +13071,88 @@ snapshots: '@scarf/scarf@1.4.0': {} + '@sentry/core@10.39.0': {} + + '@sentry/nestjs@10.39.0(@nestjs/common@11.1.13(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.13)': + dependencies: + '@nestjs/common': 11.1.13(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.13(@nestjs/common@11.1.13(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@sentry/core': 10.39.0 + '@sentry/node': 10.39.0 + transitivePeerDependencies: + - supports-color + + '@sentry/node-core@10.39.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)': + dependencies: + '@apm-js-collab/tracing-hooks': 0.3.1 + '@sentry/core': 10.39.0 + '@sentry/opentelemetry': 10.39.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0) + import-in-the-middle: 2.0.6 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + transitivePeerDependencies: + - supports-color + + '@sentry/node@10.39.0': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-amqplib': 0.58.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-connect': 0.54.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-dataloader': 0.28.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fs': 0.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-generic-pool': 0.54.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.58.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-kafkajs': 0.20.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-knex': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-lru-memoizer': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.64.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.63.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-tedious': 0.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-undici': 0.21.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@prisma/instrumentation': 7.2.0(@opentelemetry/api@1.9.0) + '@sentry/core': 10.39.0 + '@sentry/node-core': 10.39.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0) + '@sentry/opentelemetry': 10.39.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0) + import-in-the-middle: 2.0.6 + minimatch: 9.0.5 + transitivePeerDependencies: + - supports-color + + '@sentry/opentelemetry@10.39.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.39.0 + '@sentry/core': 10.39.0 + '@shopify/flash-list@2.2.2(@babel/runtime@7.28.6)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.28.6 @@ -12950,6 +13612,10 @@ snapshots: '@types/ms@2.1.0': {} + '@types/mysql@2.15.27': + dependencies: + '@types/node': 22.19.7 + '@types/node@18.19.130': dependencies: undici-types: 5.26.5 @@ -13008,6 +13674,16 @@ snapshots: dependencies: '@types/express': 5.0.6 + '@types/pg-pool@2.0.7': + dependencies: + '@types/pg': 8.15.6 + + '@types/pg@8.15.6': + dependencies: + '@types/node': 22.19.7 + pg-protocol: 1.11.0 + pg-types: 2.2.0 + '@types/qs@6.14.0': {} '@types/range-parser@1.2.7': {} @@ -13052,6 +13728,10 @@ snapshots: '@types/methods': 1.1.4 '@types/superagent': 8.1.9 + '@types/tedious@4.0.14': + dependencies: + '@types/node': 22.19.7 + '@types/tough-cookie@4.0.5': {} '@types/validator@13.15.10': @@ -13330,6 +14010,10 @@ snapshots: acorn: 8.15.0 acorn-walk: 8.3.4 + acorn-import-attributes@1.9.5(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + acorn-import-phases@1.0.4(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -13556,20 +14240,6 @@ snapshots: transitivePeerDependencies: - supports-color - babel-jest@30.2.0(@babel/core@7.28.6): - dependencies: - '@babel/core': 7.28.6 - '@jest/transform': 30.2.0 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 7.0.1 - babel-preset-jest: 30.2.0(@babel/core@7.28.6) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - optional: true - babel-jest@30.2.0(@babel/core@7.29.0): dependencies: '@babel/core': 7.29.0 @@ -13747,13 +14417,6 @@ snapshots: babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) - babel-preset-jest@30.2.0(@babel/core@7.28.6): - dependencies: - '@babel/core': 7.28.6 - babel-plugin-jest-hoist: 30.2.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.6) - optional: true - babel-preset-jest@30.2.0(@babel/core@7.29.0): dependencies: '@babel/core': 7.29.0 @@ -14081,6 +14744,8 @@ snapshots: cjs-module-lexer@1.4.3: {} + cjs-module-lexer@2.2.0: {} + class-transformer@0.5.1: optional: true @@ -15484,6 +16149,8 @@ snapshots: dezalgo: 1.0.4 once: 1.4.0 + forwarded-parse@2.1.2: {} + forwarded@0.2.0: {} freeport-async@2.0.0: {} @@ -15848,6 +16515,13 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 + import-in-the-middle@2.0.6: + dependencies: + acorn: 8.15.0 + acorn-import-attributes: 1.9.5(acorn@8.15.0) + cjs-module-lexer: 2.2.0 + module-details-from-path: 1.0.4 + import-local@3.2.0: dependencies: pkg-dir: 4.2.0 @@ -17233,6 +17907,8 @@ snapshots: mkdirp@1.0.4: {} + module-details-from-path@1.0.4: {} + ms@2.0.0: {} ms@2.1.3: {} @@ -18213,6 +18889,13 @@ snapshots: require-from-string@2.0.2: {} + require-in-the-middle@8.0.1: + dependencies: + debug: 4.4.3 + module-details-from-path: 1.0.4 + transitivePeerDependencies: + - supports-color + requireg@0.2.2: dependencies: nested-error-stacks: 2.0.1 @@ -19026,7 +19709,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.4.6(@babel/core@7.28.6)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.6))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.7)(ts-node@10.9.2(@swc/core@1.15.11)(@types/node@22.19.7)(typescript@5.9.3)))(typescript@5.9.3): + ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.0))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.7)(ts-node@10.9.2(@swc/core@1.15.11)(@types/node@22.19.7)(typescript@5.9.3)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -19040,10 +19723,10 @@ snapshots: typescript: 5.9.3 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.29.0 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - babel-jest: 30.2.0(@babel/core@7.28.6) + babel-jest: 30.2.0(@babel/core@7.29.0) jest-util: 30.2.0 ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.0))(jest-util@30.2.0)(jest@29.7.0(@types/node@25.2.3))(typescript@5.9.3): From 152b6e1b66f20f2ba2022c4f29d3eea6dfaae6ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=AD=E1=86=BC?= =?UTF-8?q?=E1=84=86=E1=85=B5=E1=86=AB?= Date: Tue, 24 Feb 2026 20:25:21 +0900 Subject: [PATCH 2/3] =?UTF-8?q?chore(api):=20=EB=B0=B0=ED=8F=AC=20?= =?UTF-8?q?=EA=B0=80=EC=9D=B4=EB=93=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=8F=20CI/CD=20=EC=9B=8C=ED=81=AC=ED=94=8C?= =?UTF-8?q?=EB=A1=9C=EC=9A=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - deploy.yml: CI 통과 후 EC2 자동 배포 워크플로우 추가 - Sentry: SentryGlobalFilter 제거, GlobalExceptionFilter에서 5xx만 직접 캡처 - 배포 가이드: nano→vim, Git URL 통일, Phase 9 CI/CD 섹션 추가 --- .github/workflows/deploy.yml | 26 ++++++++++ .../src/common/exception/exception.module.ts | 7 +-- .../filters/global-exception.filter.spec.ts | 47 +++++++++++++++++++ .../filters/global-exception.filter.ts | 6 +++ 4 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..18114308 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,26 @@ +name: Deploy to EC2 + +on: + workflow_run: + workflows: ["CI"] + types: [completed] + branches: [main] + +concurrency: + group: deploy-production + cancel-in-progress: false + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + + steps: + - name: Deploy via SSH + uses: appleboy/ssh-action@v1 + with: + host: ${{ secrets.EC2_HOST }} + username: ${{ secrets.EC2_USER }} + key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} + script: ~/apps/deploy.sh diff --git a/apps/api/src/common/exception/exception.module.ts b/apps/api/src/common/exception/exception.module.ts index c9f4591b..f831b5a5 100644 --- a/apps/api/src/common/exception/exception.module.ts +++ b/apps/api/src/common/exception/exception.module.ts @@ -1,19 +1,14 @@ import { Module } from "@nestjs/common"; import { APP_FILTER } from "@nestjs/core"; -import { SentryGlobalFilter } from "@sentry/nestjs/setup"; import { GlobalExceptionFilter } from "./filters/global-exception.filter"; /** * Exception 모듈 * 전역 예외 처리를 담당 - * SentryGlobalFilter → GlobalExceptionFilter 순서로 처리 + * GlobalExceptionFilter에서 Sentry 캡처를 직접 수행 */ @Module({ providers: [ - { - provide: APP_FILTER, - useClass: SentryGlobalFilter, - }, { provide: APP_FILTER, useClass: GlobalExceptionFilter, diff --git a/apps/api/src/common/exception/filters/global-exception.filter.spec.ts b/apps/api/src/common/exception/filters/global-exception.filter.spec.ts index 5dca8212..dde163b2 100644 --- a/apps/api/src/common/exception/filters/global-exception.filter.spec.ts +++ b/apps/api/src/common/exception/filters/global-exception.filter.spec.ts @@ -6,12 +6,17 @@ import { ErrorCode } from "@aido/errors"; import { HttpException, HttpStatus } from "@nestjs/common"; +import * as Sentry from "@sentry/nestjs"; import { PinoLogger } from "nestjs-pino"; import type { TypedConfigService } from "@/common/config/services/config.service"; import { Prisma } from "@/generated/prisma/client"; import { BusinessExceptions } from "../services/business-exception.service"; import { GlobalExceptionFilter } from "./global-exception.filter"; +jest.mock("@sentry/nestjs", () => ({ + captureException: jest.fn(), +})); + describe("GlobalExceptionFilter", () => { let filter: GlobalExceptionFilter; let mockLogger: jest.Mocked; @@ -227,6 +232,48 @@ describe("GlobalExceptionFilter", () => { }); }); + describe("Sentry 캡처", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it("5xx 서버 에러는 Sentry에 캡처해야 한다", () => { + // Given + const exception = new Error("unexpected server error"); + + // When + filter.catch(exception, mockHost as never); + + // Then + expect(Sentry.captureException).toHaveBeenCalledWith(exception); + }); + + it("4xx 클라이언트 에러는 Sentry에 캡처하지 않아야 한다", () => { + // Given + const exception = new HttpException( + { message: "Bad Request" }, + HttpStatus.BAD_REQUEST, + ); + + // When + filter.catch(exception, mockHost as never); + + // Then + expect(Sentry.captureException).not.toHaveBeenCalled(); + }); + + it("BusinessException(4xx)은 Sentry에 캡처하지 않아야 한다", () => { + // Given + const exception = BusinessExceptions.todoCategoryNotFound(1); + + // When + filter.catch(exception, mockHost as never); + + // Then + expect(Sentry.captureException).not.toHaveBeenCalled(); + }); + }); + describe("details 노출 제어", () => { it("development 환경에서 HttpException의 details가 포함된다", () => { // Given diff --git a/apps/api/src/common/exception/filters/global-exception.filter.ts b/apps/api/src/common/exception/filters/global-exception.filter.ts index becdaa9d..55176e09 100644 --- a/apps/api/src/common/exception/filters/global-exception.filter.ts +++ b/apps/api/src/common/exception/filters/global-exception.filter.ts @@ -6,6 +6,7 @@ import { HttpException, HttpStatus, } from "@nestjs/common"; +import * as Sentry from "@sentry/nestjs"; import type { Request, Response } from "express"; import { PinoLogger } from "nestjs-pino"; import { TypedConfigService } from "@/common/config/services/config.service"; @@ -113,6 +114,11 @@ export class GlobalExceptionFilter implements ExceptionFilter { }; } + // 서버 에러(5xx)만 Sentry에 캡처 (4xx 클라이언트 에러는 노이즈 방지) + if (statusCode >= 500) { + Sentry.captureException(exception); + } + // 에러 로깅 (pinoHttp가 요청/응답은 자동 로깅하므로 에러 정보만 간결하게) const userId = (request as Request & { user?: { userId?: string } }).user?.userId ?? From 08de2678dc53f99b039da64f610cc81aa4b35a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=8B=E1=85=AD=E1=86=BC?= =?UTF-8?q?=E1=84=86=E1=85=B5=E1=86=AB?= Date: Tue, 24 Feb 2026 20:37:23 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix(api):=20pnpm=20v10=20deploy=20--legacy?= =?UTF-8?q?=20=ED=94=8C=EB=9E=98=EA=B7=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index f0f5da85..c57ce3df 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -39,7 +39,7 @@ RUN cd apps/api && pnpm prisma generate RUN pnpm --filter @aido/api build # Production 의존성만 추출 -RUN pnpm --filter @aido/api deploy --prod /app/production +RUN pnpm --filter @aido/api deploy --legacy --prod /app/production # Stage 4: migrate - Prisma 마이그레이션