feat(ops): Sentry error-monitoring (v1-readiness item 2)
Vier config-files volgens Next.js 15+ conventie: - instrumentation.ts (root) → koppelt server/edge config aan runtime-hook - instrumentation-client.ts → client-init + onRouterTransitionStart - sentry.server.config.ts → node-runtime - sentry.edge.config.ts → edge-runtime (proxy.ts) next.config.ts gewrapped met withSentryConfig: - Source-map-upload ALLEEN als SENTRY_AUTH_TOKEN gezet is - Tunnel /monitoring omzeilt ad-blockers (*.sentry.io) - Silent buiten CI SDK is no-op zonder NEXT_PUBLIC_SENTRY_DSN — geen network/overhead in dev of bij ontbrekende creds. Sample-rates conservatief: errors 100%, performance 10% in productie / 100% in dev. Geen Replay (privacy-review nodig + overkill voor MVP). sendDefaultPii uit. .env.example gedocumenteerd; architectuur-doc bijgewerkt met nieuwe sleutelbeslissing en file-tree-aanvulling. v1-readiness #1 verschoven naar 'done', #2 hiermee in flight. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
70c5be6750
commit
ac11483c68
10 changed files with 2516 additions and 30 deletions
11
.env.example
11
.env.example
|
|
@ -14,3 +14,14 @@ NODE_ENV="development"
|
||||||
# local dev (the route returns 401 if the Authorization header doesn't match).
|
# local dev (the route returns 401 if the Authorization header doesn't match).
|
||||||
# Generate with: openssl rand -base64 32
|
# Generate with: openssl rand -base64 32
|
||||||
CRON_SECRET=""
|
CRON_SECRET=""
|
||||||
|
|
||||||
|
# v1-readiness item 2 — Sentry error monitoring.
|
||||||
|
# Optional. Without DSN, the SDK is a no-op (no network, no overhead).
|
||||||
|
# Get a DSN at https://sentry.io → Project → Settings → Client Keys (DSN).
|
||||||
|
NEXT_PUBLIC_SENTRY_DSN=""
|
||||||
|
|
||||||
|
# Required ONLY if you want source-map upload during build (production deploy).
|
||||||
|
# In Vercel: project settings → Environment Variables → add as encrypted.
|
||||||
|
SENTRY_ORG=""
|
||||||
|
SENTRY_PROJECT=""
|
||||||
|
SENTRY_AUTH_TOKEN=""
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,10 @@ scrum4me/
|
||||||
│ └── seed.ts # Testdata uit Product Backlog document
|
│ └── seed.ts # Testdata uit Product Backlog document
|
||||||
├── proxy.ts # Next.js 16 proxy voor route protection
|
├── proxy.ts # Next.js 16 proxy voor route protection
|
||||||
├── prisma.config.ts # Prisma v7 config (DATABASE_URL)
|
├── prisma.config.ts # Prisma v7 config (DATABASE_URL)
|
||||||
|
├── instrumentation.ts # Next.js hook → koppelt Sentry-config aan runtime
|
||||||
|
├── instrumentation-client.ts # Sentry client-init + router-transitions
|
||||||
|
├── sentry.server.config.ts # Sentry node-runtime init (no-op zonder DSN)
|
||||||
|
├── sentry.edge.config.ts # Sentry edge-runtime init (proxy.ts)
|
||||||
└── .env.example
|
└── .env.example
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -133,6 +137,11 @@ scrum4me/
|
||||||
**Rationale:** Dialog-fullscreen op mobile op vier plekken bewaken zou drift introduceren. De gedeelde constant geeft één bron van waarheid. Het regressie-vangnet (`__tests__/components/shared/entity-dialog-layout.test.ts`) verifieert dat elke dialog deze constant blijft gebruiken.
|
**Rationale:** Dialog-fullscreen op mobile op vier plekken bewaken zou drift introduceren. De gedeelde constant geeft één bron van waarheid. Het regressie-vangnet (`__tests__/components/shared/entity-dialog-layout.test.ts`) verifieert dat elke dialog deze constant blijft gebruiken.
|
||||||
**Trade-off:** Eén dialog kan niet afwijken zonder de constant te verlaten — bewuste keuze voor consistentie.
|
**Trade-off:** Eén dialog kan niet afwijken zonder de constant te verlaten — bewuste keuze voor consistentie.
|
||||||
|
|
||||||
|
### Beslissing: Sentry voor error-monitoring (v1-readiness item 2)
|
||||||
|
**Keuze:** `@sentry/nextjs` met vier config-files in repo-root: `instrumentation.ts`, `instrumentation-client.ts`, `sentry.server.config.ts`, `sentry.edge.config.ts`. DSN via `NEXT_PUBLIC_SENTRY_DSN`. Zonder DSN draait de SDK als no-op — geen overhead in dev of bij ontbrekende creds in CI.
|
||||||
|
**Rationale:** Een echte v1-launch zonder runtime-monitoring is een blinde vlek; build-fouten vangen we in CI maar productie-fouten zien we anders pas via een gebruiker. Vercel + Sentry koppelen via env-vars (geen native marketplace-integratie nodig). Source-maps uploaden alleen als `SENTRY_AUTH_TOKEN` aanwezig is — anders skip-build voor lokale dev. Tunnel-route `/monitoring` omzeilt ad-blockers die `*.sentry.io` blokkeren.
|
||||||
|
**Trade-off:** Extra dependency (~150 KB client-bundle additioneel). Sample-rates conservatief (10% performance in productie, 100% errors). Geen Replay-integratie — vereist eigen privacy-review en is overkill voor MVP.
|
||||||
|
|
||||||
### Beslissing: Gescheiden SplitPane cookie-key voor mobile (PBI-11)
|
### Beslissing: Gescheiden SplitPane cookie-key voor mobile (PBI-11)
|
||||||
**Keuze:** `BacklogSplitPane` op `app/(mobile)/m/products/[id]/page.tsx` gebruikt `cookieKey={\`backlog-${id}-mobile\`}` (versus desktop `backlog-${id}`).
|
**Keuze:** `BacklogSplitPane` op `app/(mobile)/m/products/[id]/page.tsx` gebruikt `cookieKey={\`backlog-${id}-mobile\`}` (versus desktop `backlog-${id}`).
|
||||||
**Rationale:** Op mobile rendert de `SplitPane` in tab-mode (`<1024px`), waar split-percentages niet aangepast worden. Zonder gescheiden key zou dezelfde cookie hergebruikt worden — telefoon-rotaties of orientatie-wisselingen hadden anders ongewenste interactie met de desktop-split-state.
|
**Rationale:** Op mobile rendert de `SplitPane` in tab-mode (`<1024px`), waar split-percentages niet aangepast worden. Zonder gescheiden key zou dezelfde cookie hergebruikt worden — telefoon-rotaties of orientatie-wisselingen hadden anders ongewenste interactie met de desktop-split-state.
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ De kernfunctionaliteit (auth, producten, PBI/story/task-hiërarchie, sprints, so
|
||||||
|
|
||||||
## What's already done
|
## What's already done
|
||||||
|
|
||||||
|
- **#1 Edit-icoon op Product** (todo `cmoq3ox51`) — pencil-icoon op dashboard-card via PR [#83](https://github.com/madhura68/Scrum4Me/pull/83); product-detail-header behoudt tekst
|
||||||
- v0.9.0 ([release](https://github.com/madhura68/Scrum4Me/releases/tag/v0.9.0)): mobile-shell met landscape-lock (PBI-11, 7 stories, 21 tasks)
|
- v0.9.0 ([release](https://github.com/madhura68/Scrum4Me/releases/tag/v0.9.0)): mobile-shell met landscape-lock (PBI-11, 7 stories, 21 tasks)
|
||||||
- v0.4.0 t/m v0.8.x: ondermeer sprint-screen filter-popover + edit-iconen, PBI/story/task edit-icons, code-velden verplicht, demo read-only, M11 Claude-vragen-kanaal, M10 QR-pairing
|
- v0.4.0 t/m v0.8.x: ondermeer sprint-screen filter-popover + edit-iconen, PBI/story/task edit-icons, code-velden verplicht, demo read-only, M11 Claude-vragen-kanaal, M10 QR-pairing
|
||||||
- CI op `main` en PR's: lint + typecheck + prisma validate + test + build via [`.github/workflows/ci.yml`](../../.github/workflows/ci.yml)
|
- CI op `main` en PR's: lint + typecheck + prisma validate + test + build via [`.github/workflows/ci.yml`](../../.github/workflows/ci.yml)
|
||||||
|
|
@ -33,14 +34,9 @@ De kernfunctionaliteit (auth, producten, PBI/story/task-hiërarchie, sprints, so
|
||||||
|
|
||||||
Korte lijst (3-5 items) die je vóór de v1.0-tag wil afronden. Deze blokkeren een betekenisvolle launch.
|
Korte lijst (3-5 items) die je vóór de v1.0-tag wil afronden. Deze blokkeren een betekenisvolle launch.
|
||||||
|
|
||||||
### 1. Edit-icoon op Product — todo `cmoq3ox51` ([dashboard + product-detail])
|
### 1. ~~Edit-icoon op Product~~ ✅ klaar in PR [#83](https://github.com/madhura68/Scrum4Me/pull/83)
|
||||||
|
|
||||||
`ProductDialog` en `updateProductAction` bestaan al (commits `4103e36`, `1b94f32`), maar er is **nergens een zichtbare edit-trigger** voor de eindgebruiker. Op story-niveau hetzelfde patroon als ST-1109 PBI/Story/Task edit-iconen die we deze sprint geshipt hebben.
|
Verschoven naar *What's already done*. Pencil-icoon op dashboard-card; product-detail page-header behoudt tekst (matched naast andere text-acties).
|
||||||
|
|
||||||
Concreet:
|
|
||||||
- Pen-icoon in product-card ([components/dashboard/product-list.tsx](../../components/dashboard/product-list.tsx)) → opent `<ProductDialog mode="edit">` via `<EditProductButton>` (bestaat al, alleen niet zichtbaar voor non-owners?)
|
|
||||||
- Pen-icoon in product-header op `/products/[id]` is al aanwezig sinds PR#76 — verifieer of die voor de owner wel werkt en breed genoeg
|
|
||||||
- Demo-blok: `EditProductButton` heeft al `disabled={isDemo}`-check; verifieer
|
|
||||||
|
|
||||||
### 2. Error monitoring (Sentry of vergelijkbaar)
|
### 2. Error monitoring (Sentry of vergelijkbaar)
|
||||||
|
|
||||||
|
|
|
||||||
14
instrumentation-client.ts
Normal file
14
instrumentation-client.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
// PBI/v1-readiness item 2: Sentry error-monitoring (client runtime).
|
||||||
|
// Geen Replay-integratie — overkill voor MVP, vereist eigen privacy-review.
|
||||||
|
|
||||||
|
import * as Sentry from '@sentry/nextjs'
|
||||||
|
|
||||||
|
Sentry.init({
|
||||||
|
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||||
|
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
|
||||||
|
sendDefaultPii: false,
|
||||||
|
enabled: !!process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||||
|
debug: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart
|
||||||
16
instrumentation.ts
Normal file
16
instrumentation.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
// PBI/v1-readiness item 2: Next.js instrumentation hook — koppelt Sentry's
|
||||||
|
// server- en edge-configs aan de juiste runtime. Bestand moet in project-root
|
||||||
|
// staan voor Next.js 15+.
|
||||||
|
|
||||||
|
import * as Sentry from '@sentry/nextjs'
|
||||||
|
|
||||||
|
export async function register() {
|
||||||
|
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
||||||
|
await import('./sentry.server.config')
|
||||||
|
}
|
||||||
|
if (process.env.NEXT_RUNTIME === 'edge') {
|
||||||
|
await import('./sentry.edge.config')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const onRequestError = Sentry.captureRequestError
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import type { NextConfig } from "next"
|
import type { NextConfig } from "next"
|
||||||
|
import { withSentryConfig } from "@sentry/nextjs"
|
||||||
import pkg from "./package.json"
|
import pkg from "./package.json"
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
|
|
@ -10,4 +11,17 @@ const nextConfig: NextConfig = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default nextConfig
|
// PBI/v1-readiness item 2: source-map-upload + tunnel pas actief als DSN +
|
||||||
|
// auth-token aanwezig zijn. Zonder env-vars draait `withSentryConfig` als
|
||||||
|
// no-op zodat lokale dev en CI zonder Sentry-creds blijft werken.
|
||||||
|
export default withSentryConfig(nextConfig, {
|
||||||
|
org: process.env.SENTRY_ORG,
|
||||||
|
project: process.env.SENTRY_PROJECT,
|
||||||
|
authToken: process.env.SENTRY_AUTH_TOKEN,
|
||||||
|
silent: !process.env.CI,
|
||||||
|
// Tunnel /monitoring → omzeilt ad-blockers die *.sentry.io blokkeren.
|
||||||
|
tunnelRoute: '/monitoring',
|
||||||
|
// Source-maps niet uploaden als auth-token ontbreekt.
|
||||||
|
sourcemaps: { disable: !process.env.SENTRY_AUTH_TOKEN },
|
||||||
|
disableLogger: true,
|
||||||
|
})
|
||||||
|
|
|
||||||
2439
package-lock.json
generated
2439
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -29,6 +29,7 @@
|
||||||
"@hookform/resolvers": "^5.2.2",
|
"@hookform/resolvers": "^5.2.2",
|
||||||
"@prisma/adapter-pg": "^7.8.0",
|
"@prisma/adapter-pg": "^7.8.0",
|
||||||
"@prisma/client": "^7.8.0",
|
"@prisma/client": "^7.8.0",
|
||||||
|
"@sentry/nextjs": "^10.51.0",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@vercel/analytics": "^2.0.1",
|
"@vercel/analytics": "^2.0.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
|
|
|
||||||
12
sentry.edge.config.ts
Normal file
12
sentry.edge.config.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// PBI/v1-readiness item 2: Sentry error-monitoring (edge runtime).
|
||||||
|
// Edge wordt voornamelijk gebruikt door middleware (proxy.ts).
|
||||||
|
|
||||||
|
import * as Sentry from '@sentry/nextjs'
|
||||||
|
|
||||||
|
Sentry.init({
|
||||||
|
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||||
|
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
|
||||||
|
sendDefaultPii: false,
|
||||||
|
enabled: !!process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||||
|
debug: false,
|
||||||
|
})
|
||||||
18
sentry.server.config.ts
Normal file
18
sentry.server.config.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
// PBI/v1-readiness item 2: Sentry error-monitoring (server runtime).
|
||||||
|
// Geen DSN gezet → SDK is no-op (geen network, geen overhead in dev).
|
||||||
|
|
||||||
|
import * as Sentry from '@sentry/nextjs'
|
||||||
|
|
||||||
|
Sentry.init({
|
||||||
|
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||||
|
|
||||||
|
// Errors altijd 100%; performance-traces conservatief.
|
||||||
|
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
|
||||||
|
|
||||||
|
// Geen PII in events — usernames + product-ids zijn voldoende identificatie.
|
||||||
|
sendDefaultPii: false,
|
||||||
|
|
||||||
|
// Quiet in dev als DSN ontbreekt; verbose-logs uit in productie.
|
||||||
|
enabled: !!process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||||
|
debug: false,
|
||||||
|
})
|
||||||
Loading…
Add table
Add a link
Reference in a new issue