more nessa prep
This commit is contained in:
@@ -58,7 +58,7 @@ TURSO_LINEAGE_TOKEN="<rotate-in-turso-dashboard>"
|
|||||||
NESSA_DB_URL="libsql://<nessa-db>.turso.io"
|
NESSA_DB_URL="libsql://<nessa-db>.turso.io"
|
||||||
NESSA_DB_TOKEN="<rotate-in-turso-dashboard>"
|
NESSA_DB_TOKEN="<rotate-in-turso-dashboard>"
|
||||||
# Clerk authentication — rotate via Clerk Dashboard or `clerk api .../rotate_secret_keys`
|
# Clerk authentication — rotate via Clerk Dashboard or `clerk api .../rotate_secret_keys`
|
||||||
NESSA_CLERK_SECRET="sk_test_<rotate-in-clerk-dashboard>" # secret key (sk_test_... for dev, sk_live_... for prod)
|
NESSA_CLERK_SECRET="sk_test/live_<rotate-in-clerk-dashboard>" # secret key (sk_test_... for dev, sk_live_... for prod)
|
||||||
NESSA_CLERK_JWT_ISSUER="https://<your-app>.clerk.accounts.dev" # JWT issuer from Clerk Dashboard
|
NESSA_CLERK_JWT_ISSUER="https://<your-app>.clerk.accounts.dev" # JWT issuer from Clerk Dashboard
|
||||||
NESSA_CLERK_WEBHOOK_SECRET="whsec_<rotate-in-clerk-dashboard>" # Svix signing secret from Clerk Dashboard → Webhooks → Signing Secret
|
NESSA_CLERK_WEBHOOK_SECRET="whsec_<rotate-in-clerk-dashboard>" # Svix signing secret from Clerk Dashboard → Webhooks → Signing Secret
|
||||||
|
|
||||||
@@ -71,3 +71,8 @@ GITEA_URL="https://gitea.example.com"
|
|||||||
GITEA_TOKEN="<rotate-in-gitea>"
|
GITEA_TOKEN="<rotate-in-gitea>"
|
||||||
GITHUB_API_TOKEN="<rotate-in-github-settings>" # ghp_... / github_pat_...
|
GITHUB_API_TOKEN="<rotate-in-github-settings>" # ghp_... / github_pat_...
|
||||||
REDIS_URL="redis://localhost:6379"
|
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_<generate-in-sentry-dashboard>"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"dev": "vinxi dev",
|
"dev": "vinxi dev",
|
||||||
"dev-flush": "vinxi dev --env-file=.env",
|
"dev-flush": "vinxi dev --env-file=.env",
|
||||||
"build": "vinxi build",
|
"build": "vinxi build",
|
||||||
"start": "vinxi start",
|
"start": "NODE_OPTIONS='--import ./public/instrument.server.mjs' vinxi start",
|
||||||
"test": "bun test",
|
"test": "bun test",
|
||||||
"test:security": "bun test src/server/security/",
|
"test:security": "bun test src/server/security/",
|
||||||
"test:watch": "bun test --watch",
|
"test:watch": "bun test --watch",
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
"@clerk/backend": "^3.12.0",
|
"@clerk/backend": "^3.12.0",
|
||||||
"@libsql/client": "^0.15.15",
|
"@libsql/client": "^0.15.15",
|
||||||
"@motionone/solid": "^10.16.4",
|
"@motionone/solid": "^10.16.4",
|
||||||
|
"@sentry/solidstart": "^10.67.0",
|
||||||
"@solidjs/meta": "^0.29.4",
|
"@solidjs/meta": "^0.29.4",
|
||||||
"@solidjs/router": "^0.15.0",
|
"@solidjs/router": "^0.15.0",
|
||||||
"@solidjs/start": "^1.1.0",
|
"@solidjs/start": "^1.1.0",
|
||||||
@@ -69,6 +70,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.57.0",
|
"@playwright/test": "^1.57.0",
|
||||||
|
"@sentry/vite-plugin": "^5.4.0",
|
||||||
"@tailwindcss/typography": "^0.5.19",
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@types/bcrypt": "^6.0.0",
|
"@types/bcrypt": "^6.0.0",
|
||||||
"@types/fast-diff": "^1.2.2",
|
"@types/fast-diff": "^1.2.2",
|
||||||
|
|||||||
12
public/instrument.server.mjs
Normal file
12
public/instrument.server.mjs
Normal file
@@ -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: [],
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,6 +1,17 @@
|
|||||||
// @refresh reload
|
// @refresh reload
|
||||||
|
import * as Sentry from "@sentry/solidstart";
|
||||||
import { mount, StartClient } from "@solidjs/start/client";
|
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
|
// Deployment version detection and chunk loading error handling
|
||||||
const RELOAD_STORAGE_KEY = "chunk-reload-count";
|
const RELOAD_STORAGE_KEY = "chunk-reload-count";
|
||||||
const RELOAD_TIMESTAMP_KEY = "chunk-reload-timestamp";
|
const RELOAD_TIMESTAMP_KEY = "chunk-reload-timestamp";
|
||||||
|
|||||||
7
src/env/server.ts
vendored
7
src/env/server.ts
vendored
@@ -56,10 +56,8 @@ const serverEnvSchema = z.object({
|
|||||||
REDIS_URL: z.string().min(1),
|
REDIS_URL: z.string().min(1),
|
||||||
NESSA_DB_URL: z.string().min(1),
|
NESSA_DB_URL: z.string().min(1),
|
||||||
NESSA_DB_TOKEN: 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.
|
// Clerk authentication — Nessa auth is now Clerk-backed (task 02). The
|
||||||
// Delete in task 11 once all other references are gone.
|
// legacy self-issued JWT signing env var was removed in task 11.
|
||||||
NESSA_JWT_SECRET: z.string().min(1).optional(),
|
|
||||||
// Clerk authentication
|
|
||||||
NESSA_CLERK_SECRET: z.string().min(1),
|
NESSA_CLERK_SECRET: z.string().min(1),
|
||||||
NESSA_CLERK_JWT_ISSUER: z.string().min(1),
|
NESSA_CLERK_JWT_ISSUER: z.string().min(1),
|
||||||
// Clerk webhook signing secret (Svix). Used to verify `user.created` /
|
// Clerk webhook signing secret (Svix). Used to verify `user.created` /
|
||||||
@@ -176,7 +174,6 @@ export const getMissingEnvVars = (): string[] => {
|
|||||||
"REDIS_URL",
|
"REDIS_URL",
|
||||||
"NESSA_DB_URL",
|
"NESSA_DB_URL",
|
||||||
"NESSA_DB_TOKEN",
|
"NESSA_DB_TOKEN",
|
||||||
// NESSA_JWT_SECRET moved to optional — removed from nessa-auth in task 02
|
|
||||||
"NESSA_CLERK_SECRET",
|
"NESSA_CLERK_SECRET",
|
||||||
"NESSA_CLERK_JWT_ISSUER",
|
"NESSA_CLERK_JWT_ISSUER",
|
||||||
"NESSA_CLERK_WEBHOOK_SECRET",
|
"NESSA_CLERK_WEBHOOK_SECRET",
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ mock.module("~/env/server", () => ({
|
|||||||
LINEAGE_JWT_SECRET: LINEAGE_SECRET,
|
LINEAGE_JWT_SECRET: LINEAGE_SECRET,
|
||||||
// Remaining fields are unused by the verifiers but satisfy any other
|
// Remaining fields are unused by the verifiers but satisfy any other
|
||||||
// consumers the SSR-guarded module touches at import time.
|
// consumers the SSR-guarded module touches at import time.
|
||||||
NESSA_JWT_SECRET: "nessa-test-secret",
|
|
||||||
TURSO_DB_URL: "libsql://test.turso.io",
|
TURSO_DB_URL: "libsql://test.turso.io",
|
||||||
TURSO_DB_TOKEN: "test-token",
|
TURSO_DB_TOKEN: "test-token",
|
||||||
TURSO_LINEAGE_URL: "libsql://lineage-test.turso.io",
|
TURSO_LINEAGE_URL: "libsql://lineage-test.turso.io",
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import type { Client } from "@libsql/client/web";
|
|||||||
// Prevent the env/server.ts client-side guard from throwing during tests
|
// Prevent the env/server.ts client-side guard from throwing during tests
|
||||||
mock.module("~/env/server", () => ({
|
mock.module("~/env/server", () => ({
|
||||||
env: {
|
env: {
|
||||||
NESSA_JWT_SECRET: "test-secret",
|
|
||||||
TURSO_DB_URL: "libsql://test.turso.io",
|
TURSO_DB_URL: "libsql://test.turso.io",
|
||||||
TURSO_DB_TOKEN: "test-token",
|
TURSO_DB_TOKEN: "test-token",
|
||||||
NESSA_DB_URL: "libsql://nessa-test.turso.io",
|
NESSA_DB_URL: "libsql://nessa-test.turso.io",
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { describe, it, expect, mock, beforeEach } from "bun:test";
|
|||||||
// Mock env BEFORE importing the module
|
// Mock env BEFORE importing the module
|
||||||
mock.module("~/env/server", () => ({
|
mock.module("~/env/server", () => ({
|
||||||
env: {
|
env: {
|
||||||
NESSA_JWT_SECRET: "test-jwt-secret",
|
|
||||||
TURSO_DB_URL: "libsql://test.turso.io",
|
TURSO_DB_URL: "libsql://test.turso.io",
|
||||||
TURSO_DB_TOKEN: "test-token",
|
TURSO_DB_TOKEN: "test-token",
|
||||||
NESSA_DB_URL: "libsql://nessa-test.turso.io",
|
NESSA_DB_URL: "libsql://nessa-test.turso.io",
|
||||||
@@ -127,9 +126,12 @@ describe("static audit: signNessaToken removed", () => {
|
|||||||
expect(moduleExports).not.toHaveProperty("signNessaToken");
|
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();
|
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 () => {
|
it("nessa-auth.ts uses verifyToken from @clerk/backend", async () => {
|
||||||
|
|||||||
27
vite.config.ts
Normal file
27
vite.config.ts
Normal file
@@ -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/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user