chore: add pre-commit hooks, tighten ESLint, activate Sentry DSN, publish CI coverage (Phase 1)

- Install husky v9 + lint-staged: pre-commit runs eslint --fix and prettier on staged files
- Tighten ESLint base config: no-console→error, ban-ts-comment (ts-ignore banned, ts-expect-error with description allowed), reportUnusedDisableDirectives→error
- Migrate web app from deprecated `next lint` to `eslint src/` with flat config and react-hooks plugin
- Convert all 5 @ts-ignore to @ts-expect-error with descriptions, remove stale disable comments
- Add NEXT_PUBLIC_SENTRY_DSN to docker-compose.prod.yml and .env.example
- Add coverage artifact upload step to CI test job
- Pre-existing violations (102 warnings) downgraded to warn in web config for Phase 2 cleanup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-10 14:49:29 +02:00
parent 605fd7cea1
commit 82acc56b8d
38 changed files with 2901 additions and 1251 deletions
+15 -13
View File
@@ -2,7 +2,8 @@ import { loadRoleDefaults } from "@capakraken/api";
import { deriveUserSseSubscription, eventBus } from "@capakraken/api/sse";
import { startReminderScheduler } from "@capakraken/api/lib/reminder-scheduler";
import { prisma } from "@capakraken/db";
import { SSE_EVENT_TYPES, SystemRole, type PermissionOverrides } from "@capakraken/shared";
import type { SystemRole } from "@capakraken/shared";
import { SSE_EVENT_TYPES, type PermissionOverrides } from "@capakraken/shared";
import { auth } from "~/server/auth.js";
export const dynamic = "force-dynamic";
@@ -59,26 +60,27 @@ export async function GET() {
start(controller) {
// Send initial connection confirmation
controller.enqueue(
encoder.encode(`data: ${JSON.stringify({ type: SSE_EVENT_TYPES.PING, timestamp: new Date().toISOString() })}\n\n`),
encoder.encode(
`data: ${JSON.stringify({ type: SSE_EVENT_TYPES.PING, timestamp: new Date().toISOString() })}\n\n`,
),
);
// Subscribe to event bus
const unsubscribe = eventBus.subscribe(
(event) => {
try {
controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
} catch {
// Client disconnected
}
},
subscription,
);
const unsubscribe = eventBus.subscribe((event) => {
try {
controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
} catch {
// Client disconnected
}
}, subscription);
// Heartbeat every 30 seconds
const heartbeat = setInterval(() => {
try {
controller.enqueue(
encoder.encode(`data: ${JSON.stringify({ type: SSE_EVENT_TYPES.PING, timestamp: new Date().toISOString() })}\n\n`),
encoder.encode(
`data: ${JSON.stringify({ type: SSE_EVENT_TYPES.PING, timestamp: new Date().toISOString() })}\n\n`,
),
);
} catch {
clearInterval(heartbeat);