nessa: retire the API copy now served by nessa-api.freno.me

The Nessa API (nessaDb.*, nessa.community.*, Clerk auth and the Clerk user
webhook) lives in the Nessa repo's server/ on pan. This copy never served
the app: RemoteAPIClient wraps every input in {"input": …}, which the stock
tRPC adapter here reads as the input itself (queries 404, writes no-op).
Clerk's webhook already targets nessa-api.freno.me.

Drops the routers, nessa-auth, the webhook, NessaConnectionFactory, the
NESSA_* env and the now-unused @clerk/backend and svix. Nessa's marketing,
privacy and deletion pages stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 13:54:14 -04:00
parent 9ff095a82c
commit 6898186df5
21 changed files with 12 additions and 5978 deletions

View File

@@ -1,52 +0,0 @@
import type { APIEvent } from "@solidjs/start/server";
import { env } from "~/env/server";
import { NessaConnectionFactory } from "~/server/database";
import { handleClerkUserWebhook } from "~/server/clerk-user-webhook";
function json(status: number, body: unknown): Response {
return new Response(JSON.stringify(body), {
status,
headers: { "Content-Type": "application/json" }
});
}
/**
* Clerk webhook endpoint — receives `user.created` / `user.updated` events.
*
* Configure the endpoint URL in the Clerk Dashboard → Webhooks
* (e.g. https://freno.me/api/clerk-webhook in prod, or an ngrok/dev url for
* local dev). The signing secret (`whsec_...`) is stored in
* `NESSA_CLERK_WEBHOOK_SECRET` and used to verify each request via Svix.
*
* The raw body is read verbatim from the inflight request so the Svix
* signature is computed over the exact bytes Clerk sent.
*/
export async function POST(event: APIEvent) {
const svixId = event.request.headers.get("svix-id");
const svixTimestamp = event.request.headers.get("svix-timestamp");
const svixSignature = event.request.headers.get("svix-signature");
if (!svixId || !svixTimestamp || !svixSignature) {
return json(400, { error: "Missing Svix signature headers" });
}
let rawBody: string;
try {
rawBody = await event.request.text();
} catch {
return json(400, { error: "Missing request body" });
}
const result = await handleClerkUserWebhook({
rawBody,
headers: {
"svix-id": svixId,
"svix-timestamp": svixTimestamp,
"svix-signature": svixSignature
},
webhookSecret: env.NESSA_CLERK_WEBHOOK_SECRET,
conn: NessaConnectionFactory()
});
return json(result.status, result.body);
}

View File

@@ -4,17 +4,16 @@
* Net-new privacy policy for the Nessa subdomain. Modeled on the Life and
* Lineage policy (the template for products with user accounts) but scoped to Nessa's real data practices:
*
* - Authentication: user accounts are managed by Clerk
* (`src/server/nessa-auth.ts` verifies Clerk session JWTs via the Clerk
* Backend JWKS endpoint). Nessa itself does not store passwords — Clerk is
* the identity provider.
* - Authentication: user accounts are managed by Clerk (the Nessa API server
* verifies Clerk session JWTs). Nessa itself does not store passwords —
* Clerk is the identity provider.
* - Community content: clubs, club challenges, club posts, post likes, and
* post comments (see `src/server/api/routers/nessa-community.ts`,
* `clubMemberships`, `clubChallenges`, `clubChallengeParticipations`,
* `clubPosts`, `clubPostLikes`, `clubPostComments`).
* - Storage: the Nessa data lives in a dedicated Turso (libSQL) database
* (`NessaConnectionFactory` in `src/server/db-connections.ts`), separate
* from the freno.me main DB and the Lineage DB.
* post comments (`clubMemberships`, `clubChallenges`,
* `clubChallengeParticipations`, `clubPosts`, `clubPostLikes`,
* `clubPostComments`).
* - Storage: the Nessa data lives in a dedicated Turso (libSQL) database,
* served by the Nessa API (`nessa-api.freno.me`, the Nessa repo's
* `server/`), separate from the freno.me main DB and the Lineage DB.
*
* PageHead is site-aware: only the base title is supplied; the
* ` | Nessa` suffix, `https://nessa.freno.me/privacy` canonical, and OG image