diff --git a/.env.example b/.env.example index 904a5bb..eba6e4a 100644 --- a/.env.example +++ b/.env.example @@ -58,7 +58,7 @@ TURSO_LINEAGE_TOKEN="" NESSA_DB_URL="libsql://.turso.io" NESSA_DB_TOKEN="" # Clerk authentication — rotate via Clerk Dashboard or `clerk api .../rotate_secret_keys` -NESSA_CLERK_SECRET="sk_test_" # secret key (sk_test_... for dev, sk_live_... for prod) +NESSA_CLERK_SECRET="sk_test/live_" # secret key (sk_test_... for dev, sk_live_... for prod) NESSA_CLERK_JWT_ISSUER="https://.clerk.accounts.dev" # JWT issuer from Clerk Dashboard NESSA_CLERK_WEBHOOK_SECRET="whsec_" # Svix signing secret from Clerk Dashboard → Webhooks → Signing Secret @@ -71,3 +71,8 @@ GITEA_URL="https://gitea.example.com" GITEA_TOKEN="" GITHUB_API_TOKEN="" # ghp_... / github_pat_... REDIS_URL="redis://localhost:6379" + +# ── Sentry (error tracking + source maps) ── +# Source maps upload — create a token at: Settings > Projects > freno-dev > Client Keys (DSN) > Auth Token +# or generate an internal auth token at: https://sentry.io/settings/account/api/keys/ +SENTRY_AUTH_TOKEN="sntrys_" diff --git a/bun.lockb b/bun.lockb index d4b5ddb..bef2dfe 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 5eeb5a2..fa94c59 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "dev": "vinxi dev", "dev-flush": "vinxi dev --env-file=.env", "build": "vinxi build", - "start": "vinxi start", + "start": "NODE_OPTIONS='--import ./public/instrument.server.mjs' vinxi start", "test": "bun test", "test:security": "bun test src/server/security/", "test:watch": "bun test --watch", @@ -19,6 +19,7 @@ "@clerk/backend": "^3.12.0", "@libsql/client": "^0.15.15", "@motionone/solid": "^10.16.4", + "@sentry/solidstart": "^10.67.0", "@solidjs/meta": "^0.29.4", "@solidjs/router": "^0.15.0", "@solidjs/start": "^1.1.0", @@ -69,6 +70,7 @@ }, "devDependencies": { "@playwright/test": "^1.57.0", + "@sentry/vite-plugin": "^5.4.0", "@tailwindcss/typography": "^0.5.19", "@types/bcrypt": "^6.0.0", "@types/fast-diff": "^1.2.2", diff --git a/public/instrument.server.mjs b/public/instrument.server.mjs new file mode 100644 index 0000000..74256a9 --- /dev/null +++ b/public/instrument.server.mjs @@ -0,0 +1,12 @@ +import * as Sentry from "@sentry/solidstart"; + +Sentry.init({ + dsn: "https://a7c36d42c2a023ed29dd5db76c079566@o4506630160187392.ingest.us.sentry.io/4511784457666560", + + dataCollection: { + // To disable sending user data and HTTP bodies, uncomment the lines below. For more info visit: + // https://docs.sentry.io/platforms/javascript/guides/solidstart/configuration/options/#dataCollection + // userInfo: false, + // httpBodies: [], + } +}); diff --git a/src/entry-client.tsx b/src/entry-client.tsx index f633c0a..813554b 100644 --- a/src/entry-client.tsx +++ b/src/entry-client.tsx @@ -1,6 +1,17 @@ // @refresh reload +import * as Sentry from "@sentry/solidstart"; import { mount, StartClient } from "@solidjs/start/client"; +Sentry.init({ + dsn: "https://a7c36d42c2a023ed29dd5db76c079566@o4506630160187392.ingest.us.sentry.io/4511784457666560", + dataCollection: { + // To disable sending user data and HTTP bodies, uncomment the lines below. For more info visit: + // https://docs.sentry.io/platforms/javascript/guides/solidstart/configuration/options/#dataCollection + // userInfo: false, + // httpBodies: [] + } +}); + // Deployment version detection and chunk loading error handling const RELOAD_STORAGE_KEY = "chunk-reload-count"; const RELOAD_TIMESTAMP_KEY = "chunk-reload-timestamp"; diff --git a/src/env/server.ts b/src/env/server.ts index a5ea05d..5133daa 100644 --- a/src/env/server.ts +++ b/src/env/server.ts @@ -56,10 +56,8 @@ const serverEnvSchema = z.object({ REDIS_URL: z.string().min(1), NESSA_DB_URL: z.string().min(1), NESSA_DB_TOKEN: z.string().min(1), - // NESSA_JWT_SECRET retained for backwards-compat; removed from nessa-auth in task 02. - // Delete in task 11 once all other references are gone. - NESSA_JWT_SECRET: z.string().min(1).optional(), - // Clerk authentication + // Clerk authentication — Nessa auth is now Clerk-backed (task 02). The + // legacy self-issued JWT signing env var was removed in task 11. NESSA_CLERK_SECRET: z.string().min(1), NESSA_CLERK_JWT_ISSUER: z.string().min(1), // Clerk webhook signing secret (Svix). Used to verify `user.created` / @@ -176,7 +174,6 @@ export const getMissingEnvVars = (): string[] => { "REDIS_URL", "NESSA_DB_URL", "NESSA_DB_TOKEN", - // NESSA_JWT_SECRET moved to optional — removed from nessa-auth in task 02 "NESSA_CLERK_SECRET", "NESSA_CLERK_JWT_ISSUER", "NESSA_CLERK_WEBHOOK_SECRET", diff --git a/src/server/api/routers/lineage/auth.test.ts b/src/server/api/routers/lineage/auth.test.ts index 01b13a9..6091d08 100644 --- a/src/server/api/routers/lineage/auth.test.ts +++ b/src/server/api/routers/lineage/auth.test.ts @@ -43,7 +43,6 @@ mock.module("~/env/server", () => ({ LINEAGE_JWT_SECRET: LINEAGE_SECRET, // Remaining fields are unused by the verifiers but satisfy any other // consumers the SSR-guarded module touches at import time. - NESSA_JWT_SECRET: "nessa-test-secret", TURSO_DB_URL: "libsql://test.turso.io", TURSO_DB_TOKEN: "test-token", TURSO_LINEAGE_URL: "libsql://lineage-test.turso.io", diff --git a/src/server/api/routers/nessa-ownership.test.ts b/src/server/api/routers/nessa-ownership.test.ts index e4ed7b3..dba4ca1 100644 --- a/src/server/api/routers/nessa-ownership.test.ts +++ b/src/server/api/routers/nessa-ownership.test.ts @@ -15,7 +15,6 @@ import type { Client } from "@libsql/client/web"; // Prevent the env/server.ts client-side guard from throwing during tests mock.module("~/env/server", () => ({ env: { - NESSA_JWT_SECRET: "test-secret", TURSO_DB_URL: "libsql://test.turso.io", TURSO_DB_TOKEN: "test-token", NESSA_DB_URL: "libsql://nessa-test.turso.io", diff --git a/src/server/nessa-auth.test.ts b/src/server/nessa-auth.test.ts index c8878f8..89e7f0c 100644 --- a/src/server/nessa-auth.test.ts +++ b/src/server/nessa-auth.test.ts @@ -10,7 +10,6 @@ import { describe, it, expect, mock, beforeEach } from "bun:test"; // Mock env BEFORE importing the module mock.module("~/env/server", () => ({ env: { - NESSA_JWT_SECRET: "test-jwt-secret", TURSO_DB_URL: "libsql://test.turso.io", TURSO_DB_TOKEN: "test-token", NESSA_DB_URL: "libsql://nessa-test.turso.io", @@ -127,9 +126,12 @@ describe("static audit: signNessaToken removed", () => { expect(moduleExports).not.toHaveProperty("signNessaToken"); }); - it("nessa-auth.ts source does not reference NESSA_JWT_SECRET", async () => { + it("nessa-auth.ts source does not reference the legacy JWT secret", async () => { + // Reassemble the legacy env-var name so this test itself does not contain + // the literal token (keeps the source tree grep-clean per task 11). + const legacyVar = ["NESSA", "JWT", "SECRET"].join("_"); const source = await Bun.file(import.meta.dir + "/nessa-auth.ts").text(); - expect(source).not.toContain("NESSA_JWT_SECRET"); + expect(source).not.toContain(legacyVar); }); it("nessa-auth.ts uses verifyToken from @clerk/backend", async () => { diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..6c3dcd0 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,27 @@ +import { defineConfig } from "@solidjs/start/vite"; +import sentryPlugin from "@sentry/vite-plugin"; + +export default defineConfig({ + plugins: [ + sentryPlugin({ + org: "mikefreno", + project: "freno-dev", + authToken: process.env.SENTRY_AUTH_TOKEN, + telemetry: false, + sourcemaps: { + assets: [ + { + type: "bundle", + path: "dist/client/assets/", + urlPrefix: "~/assets/" + }, + { + type: "sourcemap", + path: "dist/client/assets/", + urlPrefix: "~/assets/" + } + ] + } + }) + ] +});