diff --git a/.env.example b/.env.example index 0c224c80..667db781 100644 --- a/.env.example +++ b/.env.example @@ -60,7 +60,7 @@ GITHUB_TOKEN= # -------------------------------------------- # Sentry (error tracking) -# Get from sentry.io → Project → Settings → Client Keys +# Get from sentry.io → Project → Settings → Cli# Sentry Observability (smartmanagementesystem: ufrn-universidade-federal-do-r) NEXT_PUBLIC_SENTRY_DSN= SENTRY_AUTH_TOKEN= SENTRY_ENVIRONMENT=production diff --git a/.sentryclirc b/.sentryclirc new file mode 100644 index 00000000..b76130f4 --- /dev/null +++ b/.sentryclirc @@ -0,0 +1,7 @@ +[defaults] +org=ufrn-universidade-federal-do-r +project=smartmanagementesystem + +[auth] +# Note: The auth token is usually provided via SENTRY_AUTH_TOKEN env var in CI. +# If running locally, you can run 'sentry-cli login'. diff --git a/CHANGELOG.md b/CHANGELOG.md index f8711573..43ea5714 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] — 2026-04-11 — Sentry & Build Stability + +### Added + +- **Sentry v10 Modernization**: migrated to Next.js 15 standards using `src/instrumentation-client.ts` for navigation tracing. +- **Deep PII Scrubbing**: implemented a recursive sanitization engine in `sentry.server.config.ts` to protect student data (CPF, health hashes) in server logs. +- **Privacy-First Replays**: enabled Sentry Replay with strict `maskAllInputs: true` and `maskAllText: true` to prevent PII leakage. +- **DB Connection Heartbeat**: added a surgical `SELECT 1` check in `instrumentation.ts` to verify database health on application boot. +- **Full-stack User Traceability**: linked Supabase UUID/Email to Sentry context on both client and server. +- **Vercel CI/CD Integration**: Linked Sentry to Vercel for automated environment variable synchronization and sourcemap deployments. + +### Changed + +- **Prisma 7 Type Elevation**: removed generic `as any` casts by pinning `@types/pg` to version `8.11.11` via package `overrides`. +- **Database Pool Governance**: implemented `max: 20`, `idleTimeout`, and `connectionTimeout` in `src/lib/prisma.ts` for improved resilience. + +### Fixed + +- **ESLint Flat Config**: repaired missing `@typescript-eslint` plugin and parser configuration in `eslint.config.mjs` that was breaking production builds. + ## [0.4.0] — 2026-04-10 — Elite Workflow Setup (004) ### Added diff --git a/CLAUDE.md b/CLAUDE.md index f1f977e5..36d59a62 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,13 +1,13 @@ # PWeb_Project Development Guidelines -Auto-generated from all feature plans. Last updated: 2026-04-10 +Auto-generated from all feature plans. Last updated: 2026-04-11 ## Active Technologies - TypeScript 5 (strict mode, `useUnknownInCatchVariables`) + Node.js 20 + Next.js 15 App Router, Prisma 7, Supabase SSR, Zod 3, Genkit 1.31 (004-elite-workflow-setup) - PostgreSQL via Prisma; local Supabase CLI for E2E (ports 54321/54322) (004-elite-workflow-setup) -- TypeScript 5 (strict mode) + Next.js 15, Playwright, @sentry/nextjs, Vitest (004-elite-workflow-setup) +- TypeScript 5 (strict mode) + Next.js 15, Playwright, @sentry/nextjs (v10), Vitest, instrumentation-client.ts (fix/telemetry-and-e2e-stability) - PostgreSQL via Prisma (produção + branch de staging) (004-elite-workflow-setup) - TypeScript 5 (strict mode) + Next.js 15 App Router, Supabase SSR (`@supabase/ssr`), Prisma 7, Zod 3 (003-002-us00-financial) @@ -40,6 +40,7 @@ TypeScript 5 (strict mode, `useUnknownInCatchVariables` enabled): Follow standar ## Recent Changes - 004-elite-workflow-setup: Added TypeScript 5 (strict mode, `useUnknownInCatchVariables`) + Node.js 20 + Next.js 15 App Router, Prisma 7, Supabase SSR, Zod 3, Genkit 1.31 +- fix/telemetry-and-e2e-stability: Modernized Sentry to v10 + Next.js 15, implemented Privacy-First Replay, linked Supabase User IDs to observability, and repaired ESLint flat config. - 004-elite-workflow-setup: Added TypeScript 5 (strict mode) + Next.js 15, Playwright, @sentry/nextjs, Vitest diff --git a/docs/.obsidian/graph.json b/docs/.obsidian/graph.json index 5a15c502..45e164e4 100644 --- a/docs/.obsidian/graph.json +++ b/docs/.obsidian/graph.json @@ -19,4 +19,4 @@ "linkDistance": 250, "scale": 0.19753086419753077, "close": true -} +} \ No newline at end of file diff --git a/docs/.obsidian/workspace.json b/docs/.obsidian/workspace.json index fd273d3b..0ce58f47 100644 --- a/docs/.obsidian/workspace.json +++ b/docs/.obsidian/workspace.json @@ -4,11 +4,11 @@ "type": "split", "children": [ { - "id": "aee62c2e7cdadf80", + "id": "ef3a367f18f4a73f", "type": "tabs", "children": [ { - "id": "bad7fb61f0dc5d1f", + "id": "7a54e2cabf4474d1", "type": "leaf", "state": { "type": "graph", @@ -16,16 +16,6 @@ "icon": "lucide-git-fork", "title": "Graph view" } - }, - { - "id": "8171e32c308ccd3f", - "type": "leaf", - "state": { - "type": "smart-release-notes-view", - "state": {}, - "icon": "file-text", - "title": "Release Notes" - } } ] } @@ -201,8 +191,26 @@ "obsidian-textgenerator-plugin:Text Generator: Templates Packages Manager": false } }, - "active": "bad7fb61f0dc5d1f", + "active": "7a54e2cabf4474d1", "lastOpenFiles": [ + "dev-errors.md", + "security/THREAT-MODEL.md", + "security", + "process/RFC-TEMPLATE.md", + "process/POSTMORTEM-TEMPLATE.md", + "process", + "operations/RUNBOOK.md", + "operations/INCIDENT-RESPONSE.md", + "operations", + "observability/SLOS.md", + "observability", + "decisions/INSIGHTS-CI-E2E-PIPELINE.md", + "DEFINITION-OF-DONE.md", + "CURRENT-STATE.md", + "archive/architecture/ANALYSIS.md", + "decisions/ADR-004-prisma-migration-governance.md", + "decisions/ADR-003-render-phase-sync.md", + "README.md", "specs/specs/001-fix-type-safety-bugs/checklists/requirements.md", "specs/specs/004-elite-workflow-setup/spec.md", "specs/specs/004-elite-workflow-setup/research.md", @@ -215,13 +223,10 @@ "specs/specs/003-002-us00-financial/plan.md", "specs/specs/001-fix-type-safety-bugs/tasks.md", "specs/specs/001-fix-type-safety-bugs/spec.md", - "specs/specs/001-fix-type-safety-bugs/research.md", - "specs/specs/001-fix-type-safety-bugs/quickstart.md", - "specs/specs/001-fix-type-safety-bugs/plan.md", "specs/specs/001-fix-type-safety-bugs/checklists", "specs/specs/004-elite-workflow-setup", "specs/specs/003-002-us00-financial", "specs/specs/001-fix-type-safety-bugs", "specs/specs" ] -} +} \ No newline at end of file diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 09624c53..7397f4ff 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -1,40 +1,41 @@ # Current State — Five Star Academy -**Last Updated**: 2026-04-10 -**Branch**: `004-elite-workflow-setup` -**Version**: 0.4.0 (pre-release) +**Last Updated**: 2026-04-11 +**Branch**: `fix/telemetry-and-e2e-stability` +**Version**: 0.4.1 (pre-release) ## What Works Today -| Feature | Status | Notes | -| ------------------------------------------- | ---------- | ------------------------------ | -| Admin login | ✅ Working | Supabase Auth SSR | -| Student login (Portal do Aluno) | ✅ Working | Separate session | -| Admin dashboard | ✅ Working | GERENTE + RECEPCIONISTA | -| Financial routes (`/financeiro`, `/planos`) | ✅ Working | GERENTE-only gate | -| Student workout view | ✅ Working | `meus-treinos` | -| AI workout generator | ✅ Working | Genkit + Gemini | -| Student enrollment | ✅ Working | Admin creates aluno | -| Gamification (XP, streaks) | ✅ Working | Hook `use-workout-tracker` | -| Prisma migrations | ✅ Tracked | `prisma/migrations/` | -| ESLint quality gate | ✅ Done | 0 errors — `any` + unused vars | -| TypeScript typecheck | ✅ Clean | 0 errors (strict mode) | -| Unit tests | ✅ Passing | 18/18 (Vitest) | -| Ops documentation | ✅ Done | Runbook, SLOs, threat model | -| Process documentation | ✅ Done | RFC + Postmortem templates | -| Local E2E stack | ✅ Done | `supabase start` (Docker) | -| E2E seed script | ✅ Done | `prisma/seed-e2e.ts` (4 users) | -| Playwright E2E suite | ✅ Done | 15/15 passing | -| CI E2E job | ✅ Done | `.github/workflows/ci.yml` | -| Sentry error tracking | ✅ Done | SDK + MCP + CLI configurados | +| Feature | Status | Notes | +| ------------------------------------------- | ------------- | ------------------------------ | +| Admin login | ✅ Working | Supabase Auth SSR | +| Student login (Portal do Aluno) | ✅ Working | Separate session | +| Admin dashboard | ✅ Working | GERENTE + RECEPCIONISTA | +| Financial routes (`/financeiro`, `/planos`) | ✅ Working | GERENTE-only gate | +| Student workout view | ✅ Working | `meus-treinos` | +| AI workout generator | ✅ Working | Genkit + Gemini | +| Student enrollment | ✅ Working | Admin creates aluno | +| Gamification (XP, streaks) | ✅ Working | Hook `use-workout-tracker` | +| Prisma migrations | ✅ Tracked | `prisma/migrations/` | +| ESLint quality gate | ✅ Done | 0 errors — `any` + unused vars | +| TypeScript typecheck | ✅ Clean | 0 errors (strict mode) | +| Unit tests | ✅ Passing | 18/18 (Vitest) | +| Ops documentation | ✅ Done | Runbook, SLOs, threat model | +| Process documentation | ✅ Done | RFC + Postmortem templates | +| Local E2E stack | ✅ Done | `supabase start` (Docker) | +| E2E seed script | ✅ Done | `prisma/seed-e2e.ts` (4 users) | +| Playwright E2E suite | ✅ Done | 15/15 passing | +| CI E2E job | ✅ Done | `.github/workflows/ci.yml` | +| Sentry error tracking | ✅ Modernized | Next.js 15, v10, User Context | ## What Is Incomplete -| Area | Gap | Priority | -| ------------- | -------------------------------------------------------------- | -------- | -| CI security | 3 moderate vulns in `@prisma/dev` (transitive, non-production) | P3 | -| Lint warnings | 31 `no-console` warnings remain (not errors) | P3 | -| GitHub secret | `SUPABASE_LOCAL_SERVICE_ROLE_KEY` must be set for CI seed step | P1 | +| Area | Gap | Priority | +| ------------- | ------------------------------------------------------------------ | -------- | +| CI security | 3 moderate vulns in `@prisma/dev` (transitive, awaiting upstream) | P3 | +| Lint warnings | 31 `no-console` warnings remain (accepted for server debug) | P3 | +| GitHub secret | `SUPABASE_LOCAL_SERVICE_ROLE_KEY` must be set for CI seed | P1 | +| Env Sync | Ensure `NEXT_PUBLIC_SENTRY_DSN` is set in production for scrubbing | P1 | ## Quality Gates (current status) @@ -80,22 +81,29 @@ npm run e2e → ✅ 15/15 passing ## Phase Progress (004-elite-workflow-setup) -| Phase | Description | Status | -| ----- | --------------------------- | ----------- | -| 1–3 | Governance & constitution | ✅ Complete | -| 4 | Staging environment (local) | ✅ Complete | -| 5 | ESLint quality gates | ✅ Complete | -| 6 | Coverage thresholds | ✅ Complete | -| 7 | Playwright E2E | ✅ Complete | -| 8 | Sentry error tracking | ✅ Complete | -| 9 | Polish & PR | ✅ Complete | +| Phase | Description | Status | +| ----- | ------------------------------- | ----------- | +| 1–3 | Governance & constitution | ✅ Complete | +| 4 | Staging environment (local) | ✅ Complete | +| 5 | ESLint quality gates | ✅ Complete | +| 6 | Coverage thresholds | ✅ Complete | +| 7 | Playwright E2E | ✅ Complete | +| 8 | Sentry & Infra Hardening | ✅ Complete | +| 9 | Tailwind 4 & Type Safety Update | ✅ Complete | + +## Technical Debt Governance + +The following items are recognized as "Managed Debt" — intentional compromises for compatibility or performance: + +1. **Sentry Boundary Casts**: Use of `any` in `beforeSend` (suppressed with `eslint-disable`) is required due to Sentry SDK interface rigidness. +2. **Server console.log**: 31 warnings in Server Actions are preserved to ensure minimal observability in the terminal during development. +3. **Prisma Type Overrides**: We use `@pg/types` pinning to resolve Prisma 7/Next 15 conflicts until `@prisma/adapter-pg` upstream fixes land. ## Known Issues -- 31 `no-console` warnings in server actions and `data.ts` — accepted for now (server-side debug logging) -- `SUPABASE_LOCAL_ANON_KEY` GitHub Actions secret must be set before CI E2E job runs (see RUNBOOK) -- `SUPABASE_LOCAL_SERVICE_ROLE_KEY` GitHub Actions secret must be set for seed step to create test users -- 3 moderate vulnerabilities in `@prisma/dev` (transitive via `prisma@7.7.0`); not reachable in production; awaiting upstream fix +- 31 `no-console` warnings in server actions — accepted for server-side debug logging. +- GitHub Secrets for CI (Playwright) require one-time manual setup. +- Transitive dependency vulnerabilities in `@prisma/dev` (not reachable in production). ## Update Protocol diff --git a/docs/decisions/ADR-003-infrastructure-observability-hardening.md b/docs/decisions/ADR-003-infrastructure-observability-hardening.md new file mode 100644 index 00000000..caac4114 --- /dev/null +++ b/docs/decisions/ADR-003-infrastructure-observability-hardening.md @@ -0,0 +1,60 @@ +# ADR-003: Infrastructure & Observability Hardening + +## Status +Accepted + +## Context +As part of the modernization for Next.js 15, Sentry 10, and Prisma 7, several structural challenges were identified: +1. **PII Vulnerability**: Server-side error logs and client-side session replays were potentially capturing sensitive student data (CPF, health hashes). +2. **Type Safety Decay**: The move to Prisma 7 with the `pg` driver adapter introduced a version conflict in `@types/pg`, leading to the widespread use of `as any` in the database layer. +3. **Connection Governance**: Migrating to a driver adapter pattern (required for some Next.js environments) necessitated more explicit connection pool management to prevent exhaustion in serverless or PgBouncer environments. + +## Decisions + +### 1. Recursive PII Scrubbing (Sentry) +Instead of relying on basic header scrubbing, we implemented a dedicated recursive sanitization engine in `sentry.server.config.ts`. This engine traverses all event breadcrumbs and extra data, replacing values for a defined list of sensitive keys (`cpf`, `password`, `biometriaHash`, etc.) with `[SCRUBBED]`. + +### 2. Dependency Pinning via Overrides +To resolve the `ClientBase` return type mismatch between `@types/pg@v8.18+` and `@prisma/adapter-pg`, we decided to: +- Pin `@types/pg` to version `8.11.11` in both `devDependencies` and the `overrides` field of `package.json`. +- This ensures that all internal and external consumers of the `pg` types are aligned with the Prisma runtime requirements, allowing for the complete removal of unsafe type assertions in `src/lib/prisma.ts`. + +### 3. Connection Resilience & Heartbeat +We enhanced the `PrismaClient` initialization with: +- **Pool Governance**: Explicit `idleTimeout` and `connectionTimeout` to handle transient network issues. +- **Boot Heartbeat**: Integrated a `SELECT 1` check into the Next.js `instrumentation.ts` with a 5-second `Promise.race` timeout to verify database health during application startup, preventing "zombie" boot states. + +### 4. Genkit AI Flow Contract Hardening +To prevent technical debt in AI integration, we implemented: +- **Strict Schema Enforcement**: All Genkit flows now utilize Zod schemas for input, output, and streaming, eliminating `as any` casts in the frontend. +- **Hallucination Detection**: Added Sentry monitoring to log unrecognized exercise suggestions, ensuring that AI-generated plans remain within physiological and logistical constraints defined in `src/lib/constants.ts`. + +### 5. Tailwind 4 & OKLCH Unified Theme +Migrated the entire design system to Tailwind 4 (Oxide engine) to leverage: +- **Native Custom Properties**: Replaced legacy HSL `:root` variables with a unified `@theme` block. +- **OKLCH Color Space**: Adopted OKLCH tokens for consistent vibrancy and accessibility across the "Premium Dark" interface. + +## Consequences + +### Positive +- **Privacy Compliance**: Strict adherence to data protection standards for student information. +- **Developer Experience**: Restored full IDE intellisense and type-checking in the database and AI layers. +- **Aesthetic Excellence**: Unified, vibrant UI with optimized build performance via Tailwind 4. +- **Operational Visibility**: Sentry alerts for both infrastructure failures and AI "hallucinations". + +### Negative +- **Maintenance**: We must monitor Prisma and Genkit SDK updates to revert specific type overrides or casts once upstream packages reach full alignment with Next.js 15. + +--- + +## Visual Documentation + +### 1. Premium Dark Interface (Tailwind 4) +The new design system ensures a high-fidelity, high-contrast experience using OKLCH color tokens. + +![Student Dashboard - Premium Dark](file:///home/emiyakiritsugu/.gemini/antigravity/brain/f9bac6cb-9ccd-44da-af5f-669178bd052a/student_dashboard_view_1775903233752.png) + +### 2. AI Workout Generator (Genkit) +Strictly typed integration between the AI service and the student portal. + +![AI Workout Generator UI](file:///home/emiyakiritsugu/.gemini/antigravity/brain/f9bac6cb-9ccd-44da-af5f-669178bd052a/workout_generator_final_result_1775903924170.png) diff --git a/docs/observability/SLOS.md b/docs/observability/SLOS.md index 46341b14..c8554fa9 100644 --- a/docs/observability/SLOS.md +++ b/docs/observability/SLOS.md @@ -1,6 +1,6 @@ # Service Level Objectives — Five Star Academy -**Last Updated**: 2026-04-10 +**Last Updated**: 2026-04-11 **Review Cadence**: Monthly --- @@ -31,12 +31,10 @@ --- -### SLO-03: Student Portal Availability - -| Metric | Target | Measurement | -| -------------------------- | ------------- | ------------------------------ | -| Portal load success rate | ≥ 99% / month | HTTP 200 / total on `/aluno/*` | -| Workout view latency (p95) | < 2s | Time to render `meus-treinos` | +| Metric | Target | Measurement | Status | +| ------------------------------------------ | ------ | -------------------------------------------------------------- | ---------------------------- | +| Server error exposes stack trace to client | Medium | Next.js hides stack in production; Sentry captures server-side | ✅ Mitigated (Sentry active) | +| CPF/email logged to stdout in plain text | High | `beforeSend` in Sentry config scrubs PII fields | ✅ Mitigated (Scrub active) | --- @@ -79,13 +77,12 @@ freeze non-critical feature work until budget recovers. ## Gaps & Tooling -| SLO | Current Tooling | Gap | -| ------ | ---------------------------------- | ------------------------- | -| SLO-01 | Supabase Auth logs | No automated alerting yet | -| SLO-02 | Sentry (pending T053) | No latency tracking yet | -| SLO-03 | Sentry (pending T053) | No latency tracking yet | -| SLO-04 | Sentry `beforeSend` (pending T055) | No success rate metric | -| SLO-05 | GitHub Actions badges | No duration trending | +| SLO | Current Tooling | Status | +| ------ | --------------------------------- | ----------- | +| SLO-01 | Supabase Auth logs + Sentry | ✅ Tracking | +| SLO-02 | Sentry Performance Monitoring | ✅ Active | +| SLO-03 | Sentry Performance Monitoring | ✅ Active | +| SLO-04 | Genkit + Sentry User Traceability | ✅ Unified | +| SLO-05 | GitHub Actions badges | ✅ Active | -**Next step**: After Sentry is configured (Phase 8), add performance monitoring -to capture latency SLOs automatically. +**Update**: Sentry is now fully integrated with Next.js 15 (v10) and linked to project: `smartmanagementesystem`. CI/CD automation is handled via **Vercel Integration**, ensuring synchronized environment variables and automated sourcemap deployment for every release. diff --git a/docs/security/THREAT-MODEL.md b/docs/security/THREAT-MODEL.md index 786d7788..3259e5ed 100644 --- a/docs/security/THREAT-MODEL.md +++ b/docs/security/THREAT-MODEL.md @@ -48,12 +48,12 @@ ### I — Information Disclosure (Data Leak) -| Threat | Likelihood | Impact | Mitigation | Status | -| --------------------------------------------- | ---------- | -------- | ------------------------------------------------------------------- | ----------------------------- | -| Server error exposes stack trace to client | Medium | Medium | Next.js hides stack in production; Sentry captures server-side | ✅ Mitigated (Sentry pending) | -| CPF/email logged to stdout in plain text | Medium | High | `beforeSend` in Sentry config scrubs PII fields | ✅ Planned (T055) | -| `.env` committed with secrets | Low | Critical | `.gitignore` covers `.env*`; `.env.example` uses placeholders | ✅ Mitigated | -| DB connection string exposed in client bundle | Low | Critical | Prisma only runs server-side; `DIRECT_URL` never in `NEXT_PUBLIC_*` | ✅ Mitigated | +| Threat | Likelihood | Impact | Mitigation | Status | +| --------------------------------------------- | ---------- | -------- | ------------------------------------------------------------------- | -------------------------------- | +| Server error exposes stack trace to client | Medium | Medium | Next.js hides stack in production; Sentry captures server-side | ✅ Mitigated (Sentry active) | +| CPF/email logged to stdout in plain text | Medium | High | `beforeSend` in Sentry config scrubs PII fields | ✅ Mitigated (beforeSend active) | +| `.env` committed with secrets | Low | Critical | `.gitignore` covers `.env*`; `.env.example` uses placeholders | ✅ Mitigated | +| DB connection string exposed in client bundle | Low | Critical | Prisma only runs server-side; `DIRECT_URL` never in `NEXT_PUBLIC_*` | ✅ Mitigated | ### D — Denial of Service diff --git a/eslint.config.mjs b/eslint.config.mjs index 9fc68ca0..87e8f3da 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,5 +1,7 @@ import nextConfig from 'eslint-config-next/core-web-vitals'; import prettierConfig from 'eslint-config-prettier'; +import tsPlugin from '@typescript-eslint/eslint-plugin'; +import tsParser from '@typescript-eslint/parser'; export default [ { @@ -8,6 +10,16 @@ export default [ ...nextConfig, prettierConfig, { + plugins: { + '@typescript-eslint': tsPlugin, + }, + languageOptions: { + parser: tsParser, + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + }, rules: { '@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/no-unused-vars': [ diff --git a/instrumentation-client.ts b/instrumentation-client.ts deleted file mode 100644 index 5934f67f..00000000 --- a/instrumentation-client.ts +++ /dev/null @@ -1,14 +0,0 @@ -import * as Sentry from '@sentry/nextjs'; - -Sentry.init({ - dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, - environment: process.env.NODE_ENV, - enableLogs: true, - - tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0, - - replaysSessionSampleRate: 0.1, - replaysOnErrorSampleRate: 1.0, - - integrations: [Sentry.replayIntegration()], -}); diff --git a/instrumentation.ts b/instrumentation.ts index c500eaa8..3ab675a2 100644 --- a/instrumentation.ts +++ b/instrumentation.ts @@ -1,12 +1,35 @@ import * as Sentry from '@sentry/nextjs'; export async function register() { + // Initialize Sentry if (process.env.NEXT_RUNTIME === 'nodejs') { await import('./sentry.server.config'); } if (process.env.NEXT_RUNTIME === 'edge') { await import('./sentry.edge.config'); } + + // Database Connection Heartbeat + if (process.env.NEXT_RUNTIME === 'nodejs') { + try { + const { prisma } = await import('./src/lib/prisma'); + // [DE] Timeout Wrapper: Ensure boot doesn't hang indefinitely + const heartbeat = prisma.$queryRaw`SELECT 1`; + const timeout = (ms: number) => + new Promise((_, reject) => + setTimeout(() => reject(new Error(`Database heartbeat timed out after ${ms}ms`)), ms) + ); + + await Promise.race([heartbeat, timeout(5000)]); + + // eslint-disable-next-line no-console + console.log('✅ Database connection established successfully'); + } catch (error) { + // eslint-disable-next-line no-console + console.error('❌ Database connection heartbeat failed during startup:', error); + Sentry.captureException(error); + } + } } export const onRequestError = Sentry.captureRequestError; diff --git a/next.config.ts b/next.config.ts index e6aa0956..dfc541b1 100644 --- a/next.config.ts +++ b/next.config.ts @@ -35,7 +35,7 @@ const nextConfig: NextConfig = { export default withSentryConfig(nextConfig, { org: 'ufrn-universidade-federal-do-r', - project: 'javascript-nextjs', + project: 'smartmanagementesystem', // Auth token for source map uploads (set in CI via SENTRY_AUTH_TOKEN secret) authToken: process.env.SENTRY_AUTH_TOKEN, @@ -47,5 +47,16 @@ export default withSentryConfig(nextConfig, { silent: !process.env.CI, // Tree-shake Sentry debug code in production builds - disableLogger: true, + widenClientFileUpload: true, + + // Modern replacement for disableLogger as per Sentry warning + bundleSizeOptimizations: { + excludeDebugStatements: true, + }, + + // OPTIMIZATION: Disable Sentry plugins in development to resolve + // [webpack.cache.PackFileCacheStrategy] warnings and speed up dev builds. + sourcemaps: { + disable: process.env.NODE_ENV === 'development', + }, }); diff --git a/package-lock.json b/package-lock.json index c26def2f..14b18f2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,13 +67,14 @@ "devDependencies": { "@commitlint/cli": "^20.5.0", "@commitlint/config-conventional": "^20.5.0", + "@opentelemetry/exporter-jaeger": "^1.25.1", "@playwright/test": "^1.59.1", "@tanstack/react-table": "^8.19.2", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/eslint": "^9.6.1", "@types/node": "^20", - "@types/pg": "^8.20.0", + "@types/pg": "8.11.11", "@types/react": "^18", "@types/react-dom": "^18", "@vitejs/plugin-react": "^6.0.1", @@ -1823,6 +1824,23 @@ "@genkit-ai/firebase": "^1.16.1" } }, + "node_modules/@genkit-ai/core/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, "node_modules/@genkit-ai/firebase": { "version": "1.31.0", "license": "Apache-2.0", @@ -1870,6 +1888,24 @@ "genkit": "^1.31.0" } }, + "node_modules/@genkit-ai/google-cloud/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, "node_modules/@genkit-ai/google-genai": { "version": "1.31.0", "license": "Apache-2.0", @@ -3534,6 +3570,87 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, + "node_modules/@opentelemetry/exporter-jaeger": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-jaeger/-/exporter-jaeger-1.30.1.tgz", + "integrity": "sha512-7Ki+x7cZ/PEQxp3UyB+CWkWBqLk22yRGQ4AWIGwZlEs6rpCOdWwIFOyQDO9DdeyWtTPTvO3An/7chPZcRHOgzQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/sdk-trace-base": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0", + "jaeger-client": "^3.15.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-jaeger/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-jaeger/node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-jaeger/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-jaeger/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { "version": "0.52.1", "license": "Apache-2.0", @@ -3552,97 +3669,147 @@ "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.214.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.214.0.tgz", - "integrity": "sha512-kIN8nTBMgV2hXzV/a20BCFilPZdAIMYYJGSgfMMRm/Xa+07y5hRDS2Vm12A/z8Cdu3Sq++ZvJfElokX2rkgGgw==", + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.6.1", - "@opentelemetry/otlp-exporter-base": "0.214.0", - "@opentelemetry/otlp-transformer": "0.214.0", - "@opentelemetry/resources": "2.6.1", - "@opentelemetry/sdk-trace-base": "2.6.1" + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.6.1.tgz", - "integrity": "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==", + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" + "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", - "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.6.1", - "@opentelemetry/semantic-conventions": "^1.29.0" + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=14" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.6.1.tgz", - "integrity": "sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw==", + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.52.1", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.6.1", - "@opentelemetry/resources": "2.6.1", - "@opentelemetry/semantic-conventions": "^1.29.0" + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" + "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz", - "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==", + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, "engines": { "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/otlp-transformer": { "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", "license": "Apache-2.0", "dependencies": { + "@opentelemetry/api-logs": "0.52.1", "@opentelemetry/core": "1.25.1", - "@opentelemetry/otlp-exporter-base": "0.52.1", - "@opentelemetry/otlp-transformer": "0.52.1", "@opentelemetry/resources": "1.25.1", - "@opentelemetry/sdk-trace-base": "1.25.1" + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" }, "engines": { "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": "^1.0.0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "node_modules/@opentelemetry/exporter-zipkin": { @@ -4053,6 +4220,16 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-mongodb/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@opentelemetry/instrumentation-mongoose": { "version": "0.41.0", "license": "Apache-2.0", @@ -4149,16 +4326,6 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@opentelemetry/instrumentation-pg/node_modules/@types/pg": { - "version": "8.6.1", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^2.2.0" - } - }, "node_modules/@opentelemetry/instrumentation-pino": { "version": "0.41.0", "license": "Apache-2.0", @@ -4299,46 +4466,6 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.214.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.214.0.tgz", - "integrity": "sha512-u1Gdv0/E9wP+apqWf7Wv2npXmgJtxsW2XL0TEv9FZloTZRuMBKmu8cYVXwS4Hm3q/f/3FuCnPTgiwYvIqRSpRg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.6.1", - "@opentelemetry/otlp-transformer": "0.214.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.6.1.tgz", - "integrity": "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz", - "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@opentelemetry/otlp-grpc-exporter-base": { "version": "0.52.1", "license": "Apache-2.0", @@ -4355,96 +4482,77 @@ "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.214.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.214.0.tgz", - "integrity": "sha512-DSaYcuBRh6uozfsWN3R8HsN0yDhCuWP7tOFdkUOVaWD1KVJg8m4qiLUsg/tNhTLS9HUYUcwNpwL2eroLtsZZ/w==", + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.214.0", - "@opentelemetry/core": "2.6.1", - "@opentelemetry/resources": "2.6.1", - "@opentelemetry/sdk-logs": "0.214.0", - "@opentelemetry/sdk-metrics": "2.6.1", - "@opentelemetry/sdk-trace-base": "2.6.1", - "protobufjs": "^7.0.0" + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/api-logs": { - "version": "0.214.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.214.0.tgz", - "integrity": "sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" + "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.6.1.tgz", - "integrity": "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==", + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", - "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.6.1", - "@opentelemetry/semantic-conventions": "^1.29.0" + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" + "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.6.1.tgz", - "integrity": "sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw==", + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.6.1", - "@opentelemetry/resources": "2.6.1", - "@opentelemetry/semantic-conventions": "^1.29.0" + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=14" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz", - "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@opentelemetry/propagation-utils": { "version": "0.30.16", "license": "Apache-2.0", @@ -4680,149 +4788,173 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-logs": { - "version": "0.214.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.214.0.tgz", - "integrity": "sha512-zf6acnScjhsaBUU22zXZ/sLWim1dfhUAbGXdMmHmNG3LfBnQ3DKsOCITb2IZwoUsNNMTogqFKBnlIPPftUgGwA==", + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", "license": "Apache-2.0", + "optional": true, "dependencies": { - "@opentelemetry/api-logs": "0.214.0", - "@opentelemetry/core": "2.6.1", - "@opentelemetry/resources": "2.6.1", - "@opentelemetry/semantic-conventions": "^1.29.0" + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.10.0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/api-logs": { - "version": "0.214.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.214.0.tgz", - "integrity": "sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==", + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", "license": "Apache-2.0", + "optional": true, "dependencies": { - "@opentelemetry/api": "^1.3.0" + "@opentelemetry/semantic-conventions": "1.28.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.6.1.tgz", - "integrity": "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==", + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", "license": "Apache-2.0", + "optional": true, "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=14" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", - "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.6.1", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, + "optional": true, "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" + "node": ">=14" } }, - "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz", - "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==", + "node_modules/@opentelemetry/sdk-node": { + "version": "0.52.1", "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", + "@opentelemetry/exporter-zipkin": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/sdk-trace-node": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, "engines": { "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-metrics": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.6.1.tgz", - "integrity": "sha512-9t9hJHX15meBy2NmTJxL+NJfXmnausR2xUDvE19XQce0Qi/GBtDGamU8nS1RMbdgDmhgpm3VaOu2+fiS/SfTpQ==", + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", + "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.6.1", - "@opentelemetry/resources": "2.6.1" + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.9.0 <1.10.0" + "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/core": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.6.1.tgz", - "integrity": "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==", + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" + "@opentelemetry/api": "^1.0.0" } }, - "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", - "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.6.1", - "@opentelemetry/semantic-conventions": "^1.29.0" + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=14" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz", - "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==", + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + }, "engines": { "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-node": { - "version": "0.52.1", + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.52.1", "@opentelemetry/core": "1.25.1", - "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", - "@opentelemetry/exporter-trace-otlp-http": "0.52.1", - "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", - "@opentelemetry/exporter-zipkin": "1.25.1", - "@opentelemetry/instrumentation": "0.52.1", "@opentelemetry/resources": "1.25.1", - "@opentelemetry/sdk-logs": "0.52.1", - "@opentelemetry/sdk-metrics": "1.25.1", - "@opentelemetry/sdk-trace-base": "1.25.1", - "@opentelemetry/sdk-trace-node": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" + "lodash.merge": "^4.6.2" }, "engines": { "node": ">=14" @@ -4925,31 +5057,6 @@ "postgres-array": "3.0.4" } }, - "node_modules/@prisma/adapter-pg/node_modules/@types/pg": { - "version": "8.11.11", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^4.0.1" - } - }, - "node_modules/@prisma/adapter-pg/node_modules/pg-types": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "pg-numeric": "1.0.2", - "postgres-array": "~3.0.1", - "postgres-bytea": "~3.0.0", - "postgres-date": "~2.1.0", - "postgres-interval": "^3.0.0", - "postgres-range": "^1.1.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@prisma/adapter-pg/node_modules/postgres-array": { "version": "3.0.4", "license": "MIT", @@ -4957,30 +5064,6 @@ "node": ">=12" } }, - "node_modules/@prisma/adapter-pg/node_modules/postgres-bytea": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "obuf": "~1.1.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@prisma/adapter-pg/node_modules/postgres-date": { - "version": "2.1.0", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/@prisma/adapter-pg/node_modules/postgres-interval": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, "node_modules/@prisma/client": { "version": "7.5.0", "license": "Apache-2.0", @@ -8867,6 +8950,35 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-pg/node_modules/@types/pg": { + "version": "8.11.11", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.11.tgz", + "integrity": "sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^4.0.1" + } + }, + "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-pg/node_modules/pg-types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.1.0.tgz", + "integrity": "sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation-redis": { "version": "0.62.0", "license": "Apache-2.0", @@ -9047,20 +9159,40 @@ "@types/node": "*" } }, - "node_modules/@sentry/node/node_modules/@types/pg": { - "version": "8.15.6", + "node_modules/@sentry/node/node_modules/@types/pg-pool": { + "version": "2.0.7", + "license": "MIT", + "dependencies": { + "@types/pg": "*" + } + }, + "node_modules/@sentry/node/node_modules/@types/pg-pool/node_modules/@types/pg": { + "version": "8.11.11", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.11.tgz", + "integrity": "sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==", "license": "MIT", "dependencies": { "@types/node": "*", "pg-protocol": "*", - "pg-types": "^2.2.0" + "pg-types": "^4.0.1" } }, - "node_modules/@sentry/node/node_modules/@types/pg-pool": { - "version": "2.0.7", + "node_modules/@sentry/node/node_modules/@types/pg-pool/node_modules/pg-types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.1.0.tgz", + "integrity": "sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==", "license": "MIT", "dependencies": { - "@types/pg": "*" + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" } }, "node_modules/@sentry/node/node_modules/cjs-module-lexer": { @@ -9080,6 +9212,45 @@ "node": ">=18" } }, + "node_modules/@sentry/node/node_modules/postgres-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz", + "integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@sentry/node/node_modules/postgres-bytea": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "license": "MIT", + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@sentry/node/node_modules/postgres-date": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", + "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@sentry/node/node_modules/postgres-interval": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/@sentry/node/node_modules/require-in-the-middle": { "version": "8.0.1", "license": "MIT", @@ -9833,13 +10004,14 @@ } }, "node_modules/@types/pg": { - "version": "8.20.0", - "devOptional": true, + "version": "8.11.11", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.11.tgz", + "integrity": "sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==", "license": "MIT", "dependencies": { "@types/node": "*", "pg-protocol": "*", - "pg-types": "^2.2.0" + "pg-types": "^4.0.1" } }, "node_modules/@types/pg-pool": { @@ -9850,6 +10022,63 @@ "@types/pg": "*" } }, + "node_modules/@types/pg/node_modules/pg-types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.1.0.tgz", + "integrity": "sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/pg/node_modules/postgres-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz", + "integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/pg/node_modules/postgres-bytea": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "license": "MIT", + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/pg/node_modules/postgres-date": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", + "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/pg/node_modules/postgres-interval": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/@types/prop-types": { "version": "15.7.14", "devOptional": true, @@ -10871,6 +11100,13 @@ "ajv": "^8.8.2" } }, + "node_modules/ansi-color": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ansi-color/-/ansi-color-0.2.2.tgz", + "integrity": "sha512-qPx7iZZDHITYrrfzaUFXQpIcF2xYifcQHQflP1pFz8yY3lfU6GgCHb0+hJD7nimYKO7f2iaYYwBpZ+GaNcAhcA==", + "dev": true, + "license": "BSD License" + }, "node_modules/ansi-escapes": { "version": "7.3.0", "dev": true, @@ -11370,6 +11606,21 @@ "license": "MIT", "peer": true }, + "node_modules/bufrw": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/bufrw/-/bufrw-1.4.0.tgz", + "integrity": "sha512-sWm8iPbqvL9+5SiYxXH73UOkyEbGQg7kyHQmReF89WJHQJw2eV4P/yZ0E+b71cczJ4pPobVhXxgQcmfSTgGHxQ==", + "dev": true, + "dependencies": { + "ansi-color": "^0.2.1", + "error": "^7.0.0", + "hexer": "^1.5.0", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.10.x" + } + }, "node_modules/bytes": { "version": "3.1.2", "license": "MIT", @@ -12605,6 +12856,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/error": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz", + "integrity": "sha512-UtVv4l5MhijsYUxPJo4390gzfZvAnTHreNnDjnTZaKIiZ/SemXxAhBkYSKtWa5RtBXbLP8tMgn/n0RUa/H7jXw==", + "dev": true, + "dependencies": { + "string-template": "~0.2.1", + "xtend": "~4.0.0" + } + }, "node_modules/error-ex": { "version": "1.3.4", "dev": true, @@ -14553,6 +14814,24 @@ "hermes-estree": "0.25.1" } }, + "node_modules/hexer": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/hexer/-/hexer-1.5.0.tgz", + "integrity": "sha512-dyrPC8KzBzUJ19QTIo1gXNqIISRXQ0NwteW6OeQHRN4ZuZeHkdODfj0zHBdOlHbRY8GqbqK57C9oWSvQZizFsg==", + "dev": true, + "dependencies": { + "ansi-color": "^0.2.1", + "minimist": "^1.1.0", + "process": "^0.10.0", + "xtend": "^4.0.0" + }, + "bin": { + "hexer": "cli.js" + }, + "engines": { + "node": ">= 0.10.x" + } + }, "node_modules/hono": { "version": "4.12.12", "devOptional": true, @@ -15290,6 +15569,33 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jaeger-client": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/jaeger-client/-/jaeger-client-3.19.0.tgz", + "integrity": "sha512-M0c7cKHmdyEUtjemnJyx/y9uX16XHocL46yQvyqDlPdvAcwPDbHrIbKjQdBqtiE4apQ/9dmr+ZLJYYPGnurgpw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0", + "opentracing": "^0.14.4", + "thriftrw": "^3.5.0", + "uuid": "^8.3.2", + "xorshift": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jaeger-client/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/jest-worker": { "version": "27.5.1", "license": "MIT", @@ -16093,7 +16399,6 @@ }, "node_modules/lodash.merge": { "version": "4.6.2", - "dev": true, "license": "MIT" }, "node_modules/lodash.mergewith": { @@ -16719,6 +17024,13 @@ "node": ">= 6.13.0" } }, + "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, + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.37", "license": "MIT" @@ -16866,6 +17178,8 @@ }, "node_modules/obuf": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "license": "MIT" }, "node_modules/obug": { @@ -16922,6 +17236,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opentracing": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/opentracing/-/opentracing-0.14.7.tgz", + "integrity": "sha512-vz9iS7MJ5+Bp1URw8Khvdyw1H/hGvzHWlKQ7eRrQojSCDL1/SrWfrY9QebLw97n2deyRtzHRC3MkQfVNUCo91Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/optionator": { "version": "0.9.4", "dev": true, @@ -17191,6 +17515,8 @@ }, "node_modules/pg-numeric": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", + "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", "license": "ISC", "engines": { "node": ">=4" @@ -17359,6 +17685,8 @@ }, "node_modules/postgres-range": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz", + "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", "license": "MIT" }, "node_modules/prelude-ls": { @@ -17464,6 +17792,15 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/process": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/process/-/process-0.10.1.tgz", + "integrity": "sha512-dyIett8dgGIZ/TXKUzeYExt7WA6ldDzys9vTDU/cCA9L17Ypme+KzS+NjQCjpn9xsvi/shbMC+yP/BcFMBz0NA==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/progress": { "version": "2.0.3", "license": "MIT", @@ -18702,6 +19039,12 @@ "node": ">=0.6.19" } }, + "node_modules/string-template": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", + "integrity": "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==", + "dev": true + }, "node_modules/string-width": { "version": "5.1.2", "license": "MIT", @@ -19196,6 +19539,33 @@ "license": "MIT", "optional": true }, + "node_modules/thriftrw": { + "version": "3.11.4", + "resolved": "https://registry.npmjs.org/thriftrw/-/thriftrw-3.11.4.tgz", + "integrity": "sha512-UcuBd3eanB3T10nXWRRMwfwoaC6VMk7qe3/5YIWP2Jtw+EbHqJ0p1/K3x8ixiR5dozKSSfcg1W+0e33G1Di3XA==", + "dev": true, + "dependencies": { + "bufrw": "^1.2.1", + "error": "7.0.2", + "long": "^2.4.0" + }, + "bin": { + "thrift2json": "thrift2json.js" + }, + "engines": { + "node": ">= 0.10.x" + } + }, + "node_modules/thriftrw/node_modules/long": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/long/-/long-2.4.0.tgz", + "integrity": "sha512-ijUtjmO/n2A5PaosNG9ZGDsQ3vxJg7ZW8vsY8Kp0f2yIZWhSJvjmegV7t+9RPQKxKrvj8yKGehhS+po14hPLGQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.6" + } + }, "node_modules/tiny-invariant": { "version": "1.3.3", "license": "MIT" @@ -20459,6 +20829,13 @@ "dev": true, "license": "MIT" }, + "node_modules/xorshift": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/xorshift/-/xorshift-1.2.0.tgz", + "integrity": "sha512-iYgNnGyeeJ4t6U11NpA/QiKy+PXn5Aa3Azg5qkwIFz1tBLllQrjjsk9yzD7IAK0naNU4JxdeDgqW9ov4u/hc4g==", + "dev": true, + "license": "MIT" + }, "node_modules/xtend": { "version": "4.0.2", "license": "MIT", diff --git a/package.json b/package.json index cd296091..4c366970 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "@testing-library/react": "^16.3.2", "@types/eslint": "^9.6.1", "@types/node": "^20", - "@types/pg": "^8.20.0", + "@types/pg": "8.11.11", "@types/react": "^18", "@types/react-dom": "^18", "@vitejs/plugin-react": "^6.0.1", @@ -124,16 +124,13 @@ "tailwindcss": "^4.2.2", "tsx": "^4.20.6", "typescript": "^5", - "vitest": "^4.1.2" + "vitest": "^4.1.2", + "@opentelemetry/exporter-jaeger": "^1.25.1" }, "overrides": { "http-proxy-agent": "^7.0.2", "teeny-request": "^9.0.0", "retry-request": "^8.0.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.214.0", - "@opentelemetry/otlp-exporter-base": "^0.214.0", - "@opentelemetry/otlp-transformer": "^0.214.0", - "@opentelemetry/sdk-logs": "^0.214.0", - "@opentelemetry/sdk-metrics": "^2.6.0" + "@types/pg": "8.11.11" } } diff --git a/sentry.edge.config.ts b/sentry.edge.config.ts index b6a4896e..19cb2d7f 100644 --- a/sentry.edge.config.ts +++ b/sentry.edge.config.ts @@ -4,7 +4,8 @@ Sentry.init({ dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, environment: process.env.NODE_ENV, + // Performance Monitoring tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0, - enabled: process.env.NODE_ENV === 'production', + enabled: !!process.env.NEXT_PUBLIC_SENTRY_DSN, }); diff --git a/sentry.server.config.ts b/sentry.server.config.ts index b6a4896e..981d7de1 100644 --- a/sentry.server.config.ts +++ b/sentry.server.config.ts @@ -4,7 +4,75 @@ Sentry.init({ dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, environment: process.env.NODE_ENV, + // Performance Monitoring tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0, - enabled: process.env.NODE_ENV === 'production', + // Prisma instrumentation is automatic in Sentry v10+ for Prisma v6/v7 + // No additional integration required here. + + enabled: !!process.env.NEXT_PUBLIC_SENTRY_DSN, + + // Scrub potentially sensitive data from server logs + beforeSend(event) { + if (event.request?.headers) { + delete event.request.headers['cookie']; + delete event.request.headers['authorization']; + } + + // Deep sanitize breadcrumbs and extra data for PII + // Specialists: Security Auditor (Lexicon) + Senior Architect (Logic) + const sensitiveKeys = [ + 'cpf', + 'rg', + 'email', + 'password', + 'biometria', + 'biometriaHash', + 'fotoUrl', + 'telefone', + 'matricula', + 'token', + 'secret', + ]; + + const scrub = (obj: unknown, cache = new WeakSet()): unknown => { + // Specialist Guard: Primitive/Null early exit + if (!obj || typeof obj !== 'object') return obj; + + // Specialist Guard: Circular reference detection + if (cache.has(obj)) return '[CIRCULAR]'; + cache.add(obj); + + if (Array.isArray(obj)) { + return obj.map((item) => scrub(item, cache)); + } + + const newObj = { ...(obj as Record) }; + for (const key in newObj) { + if (sensitiveKeys.includes(key.toLowerCase())) { + newObj[key] = '[SCRUBBED]'; + } else { + newObj[key] = scrub(newObj[key], cache); + } + } + return newObj; + }; + + if (event.breadcrumbs) { + event.breadcrumbs = event.breadcrumbs.map((breadcrumb) => ({ + ...breadcrumb, + // Boundary Cast: Sentry expects Record + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data: scrub(breadcrumb.data) as Record, + })); + } + + if (event.extra) { + // Boundary Cast: Sentry expects Extras (Record) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + event.extra = scrub(event.extra) as Record; + } + + return event; + }, }); diff --git a/src/ai/flows/workout-generator-flow.ts b/src/ai/flows/workout-generator-flow.ts index f4bf733f..39e6095a 100644 --- a/src/ai/flows/workout-generator-flow.ts +++ b/src/ai/flows/workout-generator-flow.ts @@ -56,16 +56,16 @@ export const streamWorkoutPlan = ai.defineFlow( }); for await (const chunk of responseStream.stream) { - if (chunk && chunk.output) { + if (chunk?.output) { sendChunk(chunk.output as WorkoutGeneratorOutput); } } const finalOutput = await responseStream.response; - if (!finalOutput || !finalOutput.output) { + if (!finalOutput?.output) { throw new Error('A IA não retornou um plano de treino válido.'); } - return finalOutput.output as WorkoutGeneratorOutput; + return finalOutput.output; } ); diff --git a/src/app/aluno/meus-treinos/meus-treinos-client.tsx b/src/app/aluno/meus-treinos/meus-treinos-client.tsx index c3a88515..1479f5c1 100644 --- a/src/app/aluno/meus-treinos/meus-treinos-client.tsx +++ b/src/app/aluno/meus-treinos/meus-treinos-client.tsx @@ -11,6 +11,7 @@ import { SelectValue, } from '@/components/ui/select'; import { EXERCICIOS_POR_GRUPO, DIAS_DA_SEMANA } from '@/lib/constants'; +import * as Sentry from '@sentry/nextjs'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { PlusCircle, Trash2, Pencil, FileSignature, User, Play } from 'lucide-react'; @@ -161,17 +162,26 @@ export default function MeusTreinosClient({ if (result && result.workouts) { for (const workout of result.workouts) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Genkit streaming output lacks precise exercise type - const novosExercicios = workout.exercicios.map((ex: any) => ({ - nomeExercicio: ex.nomeExercicio, - series: ex.series, - repeticoes: ex.repeticoes, - observacoes: ex.observacoes, - descricao: - EXERCICIOS_POR_GRUPO.flatMap((g) => g.exercicios).find( - (ez) => ez.nomeExercicio === ex.nomeExercicio - )?.descricao || '', - })); + const novosExercicios = workout.exercicios.map((ex) => { + const exercicioMaster = EXERCICIOS_POR_GRUPO.flatMap((g) => g.exercicios).find( + (ez) => ez.nomeExercicio === ex.nomeExercicio + ); + + if (!exercicioMaster) { + Sentry.captureMessage(`AI Hallucination: Exercise "${ex.nomeExercicio}" not found in constants.`, { + level: 'warning', + extra: { workout: workout.nome, goal: data.objetivo }, + }); + } + + return { + nomeExercicio: ex.nomeExercicio, + series: ex.series, + repeticoes: ex.repeticoes, + observacoes: ex.observacoes, + descricao: exercicioMaster?.descricao || '', + }; + }); const diaSugerido = workout.diaSugerido; const isDayOccupied = meusTreinos.some((t) => t.diaSemana === diaSugerido); diff --git a/src/app/design-system.css b/src/app/design-system.css deleted file mode 100644 index 78fb7424..00000000 --- a/src/app/design-system.css +++ /dev/null @@ -1,58 +0,0 @@ -@theme { - /* Glassmorphism Tokens */ - --background-glass: oklch(0.18 0.05 230 / 0.6); - --backdrop-blur-glass: 20px; - --border-glass: oklch(1 0 0 / 0.05); - - /* Glow Tokens */ - --shadow-glow-cyan: 0 0 20px oklch(0.7 0.25 190 / 0.15); - --shadow-glow-cyan-hover: 0 0 25px oklch(0.7 0.25 190 / 0.25); - - --shadow-glow-gold: 0 0 20px oklch(0.8 0.15 70 / 0.15); - --shadow-glow-gold-hover: 0 0 25px oklch(0.8 0.15 70 / 0.25); -} - -@layer components { - /* Componentes semânticos seguindo Clean Code */ - - .glass-card { - background-color: var(--background-glass); - backdrop-filter: blur(var(--backdrop-blur-glass)); - border: 1px solid var(--border-glass); - box-shadow: var(--shadow-2xl); - } - - .glow-cyan { - box-shadow: var(--shadow-glow-cyan); - transition: box-shadow 0.3s ease-in-out; - - &:hover { - box-shadow: var(--shadow-glow-cyan-hover); - } - } - - .glow-gold { - box-shadow: var(--shadow-glow-gold); - transition: box-shadow 0.3s ease-in-out; - - &:hover { - box-shadow: var(--shadow-glow-gold-hover); - } - } - - .text-gradient-cyan { - background: linear-gradient(to right, oklch(0.8 0.15 190), oklch(0.6 0.2 230)); - -webkit-background-clip: text; - background-clip: text; - color: transparent; - font-weight: var(--font-weight-bold); - } - - .text-gradient-gold { - background: linear-gradient(to right, oklch(0.85 0.1 80), oklch(0.65 0.15 60)); - -webkit-background-clip: text; - background-clip: text; - color: transparent; - font-weight: var(--font-weight-bold); - } -} diff --git a/src/app/globals.css b/src/app/globals.css index a7ddd153..9be9cb73 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -10,7 +10,7 @@ --radius-sm: calc(0.75rem - 4px); /* Premium Dark Concept 5 Palette - OKLCH mapping */ - --color-background: oklch(0.15 0.05 230); /* Deep Charcoal/Navy */ + --color-background: oklch(0.15 0.05 230); --color-foreground: oklch(0.92 0.02 230); --color-card: oklch(0.18 0.05 230); @@ -35,12 +35,21 @@ --color-destructive: oklch(0.55 0.2 25); --color-destructive-foreground: oklch(0.98 0.01 230); + /* Semantic Borders & Inputs (Shadcn compatibility) */ --color-border: oklch(0.25 0.05 230 / 0.5); --color-input: oklch(0.25 0.05 230 / 0.5); --color-ring: oklch(0.7 0.25 190); - /* Neon Gold / Amber */ + /* Custom Specialist Tokens */ --color-gold: oklch(0.8 0.15 70); + --background-glass: oklch(0.18 0.05 230 / 0.6); + --backdrop-blur-glass: 20px; + --border-glass: oklch(1 0 0 / 0.05); + + --shadow-glow-cyan: 0 0 20px oklch(0.7 0.25 190 / 0.15); + --shadow-glow-cyan-hover: 0 0 25px oklch(0.7 0.25 190 / 0.25); + --shadow-glow-gold: 0 0 20px oklch(0.8 0.15 70 / 0.15); + --shadow-glow-gold-hover: 0 0 25px oklch(0.8 0.15 70 / 0.25); --animate-accordion-down: accordion-down 0.2s ease-out; --animate-accordion-up: accordion-up 0.2s ease-out; @@ -49,99 +58,27 @@ --animate-glow-pulse: glow-pulse 2s ease-in-out infinite; @keyframes accordion-down { - from { - height: 0; - } - to { - height: var(--radix-accordion-content-height); - } + from { height: 0; } + to { height: var(--radix-accordion-content-height); } } @keyframes accordion-up { - from { - height: var(--radix-accordion-content-height); - } - to { - height: 0; - } + from { height: var(--radix-accordion-content-height); } + to { height: 0; } } @keyframes circular-progress { - 0% { - stroke-dashoffset: 100; - } - 100% { - stroke-dashoffset: 0; - } + 0% { stroke-dashoffset: 100; } + 100% { stroke-dashoffset: 0; } } @keyframes float { - 0%, - 100% { - transform: translateY(0); - } - 50% { - transform: translateY(-10px); - } + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-10px); } } @keyframes glow-pulse { - 0%, - 100% { - opacity: 0.5; - } - 50% { - opacity: 1; - } + 0%, 100% { opacity: 0.5; } + 50% { opacity: 1; } } } -/* Compatibility Layer for Legacy Shadcn UI Components (HSL) */ -@layer base { - :root { - --background: 210 20% 98%; - --foreground: 224 71.4% 4.1%; - --card: 0 0% 100%; - --card-foreground: 224 71.4% 4.1%; - --popover: 0 0% 100%; - --popover-foreground: 224 71.4% 4.1%; - --primary: 220 70% 50%; - --primary-foreground: 210 20% 98%; - --secondary: 220 14.3% 95.9%; - --secondary-foreground: 220.9 39.3% 11%; - --muted: 220 14.3% 95.9%; - --muted-foreground: 220 8.9% 46.1%; - --accent: 220 14.3% 95.9%; - --accent-foreground: 220.9 39.3% 11%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 20% 98%; - --border: 220 13% 91%; - --input: 220 13% 91%; - --ring: 224 71.4% 4.1%; - --radius: 0.75rem; - } - - .dark { - --background: 222 47% 6%; - --foreground: 213 31% 91%; - --card: 222 47% 8%; - --card-foreground: 213 31% 91%; - --popover: 222 47% 6%; - --popover-foreground: 213 31% 91%; - --primary: 191 91% 50%; - --primary-foreground: 222 47% 6%; - --secondary: 217 32% 17%; - --secondary-foreground: 210 40% 98%; - --muted: 217 33% 12%; - --muted-foreground: 215 20% 65%; - --accent: 217 33% 15%; - --accent-foreground: 210 40% 98%; - --destructive: 0 84% 60%; - --destructive-foreground: 210 40% 98%; - --border: 217 33% 18%; - --input: 217 33% 18%; - --ring: 191 91% 50%; - } -} - -@import './design-system.css'; - @layer base { * { border-color: var(--color-border); @@ -149,34 +86,55 @@ body { background-color: var(--color-background); color: var(--color-foreground); - font-feature-settings: - 'rlig' 1, - 'calt' 1; + font-feature-settings: 'rlig' 1, 'calt' 1; } } -@layer utilities { - .animate-pulse-subtle { - animation: pulse-subtle 3s cubic-bezier(0.4, 0, 0.6, 1) infinite; +@layer components { + .glass-card { + background-color: var(--background-glass); + backdrop-filter: blur(var(--backdrop-blur-glass)); + border: 1px solid var(--border-glass); + box-shadow: var(--shadow-2xl); } - @keyframes pulse-subtle { - 0%, - 100% { - opacity: 1; - transform: scale(1); - } - 50% { - opacity: 0.85; - transform: scale(0.98); - } + .glow-cyan { + box-shadow: var(--shadow-glow-cyan); + transition: box-shadow 0.3s ease-in-out; + &:hover { box-shadow: var(--shadow-glow-cyan-hover); } + } + + .glow-gold { + box-shadow: var(--shadow-glow-gold); + transition: box-shadow 0.3s ease-in-out; + &:hover { box-shadow: var(--shadow-glow-gold-hover); } } - .perspective-1000 { - perspective: 1000px; + .text-gradient-cyan { + background: linear-gradient(to right, oklch(0.8 0.15 190), oklch(0.6 0.2 230)); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + font-weight: 700; } - .preserve-3d { - transform-style: preserve-3d; + .text-gradient-gold { + background: linear-gradient(to right, oklch(0.85 0.1 80), oklch(0.65 0.15 60)); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + font-weight: 700; + } +} + +@layer utilities { + .animate-pulse-subtle { + animation: pulse-subtle 3s cubic-bezier(0.4, 0, 0.6, 1) infinite; + } + @keyframes pulse-subtle { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.85; transform: scale(0.98); } } + .perspective-1000 { perspective: 1000px; } + .preserve-3d { transform-style: preserve-3d; } } diff --git a/src/components/providers/auth-provider.tsx b/src/components/providers/auth-provider.tsx index e71767c8..0deb7eb2 100644 --- a/src/components/providers/auth-provider.tsx +++ b/src/components/providers/auth-provider.tsx @@ -4,6 +4,7 @@ import type { ReactNode } from 'react'; import React, { createContext, useContext, useMemo, useState, useEffect } from 'react'; import { createClient } from '@/utils/supabase/client'; import type { User } from '@supabase/supabase-js'; +import * as Sentry from '@sentry/nextjs'; // Internal state for user authentication interface UserAuthState { @@ -41,6 +42,16 @@ export const SupabaseAuthProvider: React.FC<{ children: ReactNode }> = ({ childr data: { user: sbUser }, error, } = await supabase.auth.getUser(); + + if (sbUser) { + Sentry.setUser({ + id: sbUser.id, + email: sbUser.email, + }); + } else { + Sentry.setUser(null); + } + setUserAuthState({ user: sbUser, isUserLoading: false, @@ -53,8 +64,19 @@ export const SupabaseAuthProvider: React.FC<{ children: ReactNode }> = ({ childr const { data: { subscription }, } = supabase.auth.onAuthStateChange((_event, session) => { + const user = session?.user || null; + + if (user) { + Sentry.setUser({ + id: user.id, + email: user.email, + }); + } else { + Sentry.setUser(null); + } + setUserAuthState({ - user: session?.user || null, + user: user, isUserLoading: false, userError: null, }); diff --git a/src/instrumentation-client.ts b/src/instrumentation-client.ts new file mode 100644 index 00000000..889e6e5b --- /dev/null +++ b/src/instrumentation-client.ts @@ -0,0 +1,34 @@ +import * as Sentry from '@sentry/nextjs'; + +Sentry.init({ + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, + + // Replay is only available in the browser + integrations: [ + Sentry.replayIntegration({ + // Mask all text and block all media to protect student privacy (PII) + maskAllText: true, + blockAllMedia: true, + // Avoid recording sensitive student management areas + maskAllInputs: true, + networkDetailAllowUrls: [window.location.origin], + networkCaptureBodies: false, + }), + ], + + // Performance Monitoring + tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0, + + // Session Replay + replaysSessionSampleRate: 0.1, + replaysOnErrorSampleRate: 1.0, + + environment: process.env.NODE_ENV, + enabled: !!process.env.NEXT_PUBLIC_SENTRY_DSN, + + // Filter out noisy errors typical in local dev or browser extensions + ignoreErrors: ['top.GLOBALS', 'chrome-extension://', 'moz-extension://'], +}); + +// Specialist Hook: Sentry v10 requires this for navigation instrumentation +export const onRouterTransitionStart = Sentry.captureRouterTransitionStart; diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index a502e153..594909bf 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -7,30 +7,47 @@ const globalForPrisma = globalThis as unknown as { }; function createPrismaClient() { + if (!process.env.DATABASE_URL) { + throw new Error('DATABASE_URL is not defined in environment variables'); + } + const pool = new Pool({ connectionString: process.env.DATABASE_URL, + max: 20, // Connection pooling governance + idleTimeoutMillis: 30000, + connectionTimeoutMillis: 2000, + }); + + // Handle pool errors to prevent process crashes + pool.on('error', (err) => { + // eslint-disable-next-line no-console + console.error('Unexpected error on idle database client', err); }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- PrismaPg adapter has a type mismatch with pg@8 Pool; upstream issue - const adapter = new PrismaPg(pool as any); - return new PrismaClient({ adapter }).$extends({ + + const adapter = new PrismaPg(pool); + const client = new PrismaClient({ adapter }).$extends({ result: { aluno: { xpToNextLevel: { needs: { nivel: true }, compute(aluno) { - return aluno.nivel * 1500; + return (aluno.nivel ?? 1) * 1500; }, }, progressPerc: { needs: { exp: true, nivel: true }, compute(aluno) { - const xpReq = aluno.nivel * 1500; - return Math.min(Math.round((aluno.exp / xpReq) * 100), 100); + const nivel = aluno.nivel ?? 1; + const exp = aluno.exp ?? 0; + const xpReq = nivel * 1500; + return Math.min(Math.round((exp / xpReq) * 100), 100); }, }, }, }, }); + + return client; } export const prisma = globalForPrisma.prisma ?? createPrismaClient(); diff --git a/src/utils/supabase/server.ts b/src/utils/supabase/server.ts index b823d3b2..9060aab2 100644 --- a/src/utils/supabase/server.ts +++ b/src/utils/supabase/server.ts @@ -1,5 +1,6 @@ import { createServerClient } from '@supabase/ssr'; import { cookies } from 'next/headers'; +import * as Sentry from '@sentry/nextjs'; const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY; @@ -23,3 +24,26 @@ export const createClient = async () => { }, }); }; + +/** + * Server-side helper to get the current user and automatically + * link their state to Sentry for error tracking and performance. + */ +export const getUser = async () => { + const supabase = await createClient(); + const { + data: { user }, + error, + } = await supabase.auth.getUser(); + + if (user) { + Sentry.setUser({ + id: user.id, + email: user.email, + }); + } else { + Sentry.setUser(null); + } + + return { user, error }; +};