From 3561dc0fda51f1769f7a88b63884e39ddeb93834 Mon Sep 17 00:00:00 2001 From: Rotimi Best Date: Sun, 7 Jun 2026 19:35:24 +0100 Subject: [PATCH] fix: undo org search from tenant-router --- .../src/lib/features/app/layout-setup.ts | 55 +++--------- apps/tenant-router/package.json | 4 - apps/tenant-router/src/index.ts | 90 +------------------ apps/tenant-router/wrangler.toml | 13 --- pnpm-lock.yaml | 7 -- 5 files changed, 11 insertions(+), 158 deletions(-) diff --git a/apps/dashboard/src/lib/features/app/layout-setup.ts b/apps/dashboard/src/lib/features/app/layout-setup.ts index ab321ab6..6d058f03 100644 --- a/apps/dashboard/src/lib/features/app/layout-setup.ts +++ b/apps/dashboard/src/lib/features/app/layout-setup.ts @@ -1,6 +1,5 @@ import { getFirstOrg, getOrgBySiteName, getOrgsByCustomDomain } from '$features/org/api/org.server'; -import { dev } from '$app/environment'; import type { AccountOrg } from '$features/app/types'; import type { Cookies } from '@sveltejs/kit'; import { PUBLIC_IS_SELFHOSTED } from '$env/static/public'; @@ -54,10 +53,15 @@ export async function getOrgSiteInfo(url: URL, cookies: Cookies): Promise.myclassroomio.com) because that host is -// fronted by the Worker, which serves /internal/org-lookup from KV (≈5ms) or -// Hyperdrive→Postgres on a miss (≈50ms). No API/Render hop, no extra env var. -// -// In dev / self-hosted there is no Worker in front, so fall back to the -// direct API call (same path the code used before the Worker cache existed). -async function lookupOrgFromWorker( - origin: string, - params: { siteName?: string; customDomain?: string } -): Promise { - if (dev || PUBLIC_IS_SELFHOSTED === 'true' || origin.includes('localhost')) { - const apiKeyHeaders = getApiKeyHeaders(); - if (params.siteName) { - return (await getOrgBySiteName(params.siteName, apiKeyHeaders)) ?? null; - } - if (params.customDomain) { - const orgs = await getOrgsByCustomDomain(params.customDomain, true, apiKeyHeaders); - return (orgs?.[0] as AccountOrg) ?? null; - } - - return null; - } - - const qs = new URLSearchParams(); - if (params.siteName) qs.set('siteName', params.siteName); - if (params.customDomain) qs.set('customDomain', params.customDomain); - - try { - const res = await fetch(`${origin}/internal/org-lookup?${qs}`, { - headers: getApiKeyHeaders().headers - }); - if (!res.ok) return null; - - const json = (await res.json()) as { success: boolean; data: AccountOrg | null }; - return json.success ? json.data : null; - } catch { - return null; - } -} - function isURLCustomDomain(url: URL) { if (url.host.includes('localhost')) { return false; diff --git a/apps/tenant-router/package.json b/apps/tenant-router/package.json index 7aabc26f..8fc1c050 100644 --- a/apps/tenant-router/package.json +++ b/apps/tenant-router/package.json @@ -12,11 +12,7 @@ }, "devDependencies": { "@cloudflare/workers-types": "^4.20250109.0", - "@types/pg": "^8.16.0", "typescript": "^5.5.2", "wrangler": "4.21.x" - }, - "dependencies": { - "pg": "^8.18.0" } } diff --git a/apps/tenant-router/src/index.ts b/apps/tenant-router/src/index.ts index 0181ee1d..4d61041e 100644 --- a/apps/tenant-router/src/index.ts +++ b/apps/tenant-router/src/index.ts @@ -1,5 +1,3 @@ -import { Client } from 'pg'; - /** * @cio/tenant-router — Cloudflare Worker that fronts every browser-facing host. * @@ -45,21 +43,8 @@ interface Env { HLS_SIGNING_SECRET: string; /** R2 bucket binding for the videos bucket (HLS manifests + segments + audio). */ VIDEOS_BUCKET: R2Bucket; - /** KV namespace for caching org lookups (60 s TTL). */ - ORG_CACHE: KVNamespace; - /** Hyperdrive binding for direct Postgres access. */ - HYPERDRIVE: Hyperdrive; - /** - * Shared secret the Dashboard SSR server sends as `Authorization: Bearer …` - * to call /internal/org-lookup. Must equal the Dashboard's PRIVATE_SERVER_KEY. - * Set via `wrangler secret put PRIVATE_SERVER_KEY`. - */ - PRIVATE_SERVER_KEY: string; } -const ORG_LOOKUP_PATH = '/internal/org-lookup'; -const ORG_CACHE_TTL = 60; - // PostHog first-party proxy: requests to /ingest/* are forwarded to PostHog EU. // This makes PostHog cookies first-party (set on the org's domain, not eu.i.posthog.com), // eliminating the "third-party cookie" Lighthouse Best Practices deduction. @@ -218,38 +203,8 @@ async function handleHlsRequest(request: Request, env: Env): Promise { return new Response((object as R2ObjectBody).body, { status: 200, headers }); } -// Drizzle stores these columns in camelCase with no snake_case conversion, so -// the identifiers are case-sensitive in Postgres and must be double-quoted. -async function lookupOrgFromDb( - hyperdrive: Hyperdrive, - ctx: ExecutionContext, - params: { siteName: string | null; customDomain: string | null } -): Promise | null> { - const client = new Client({ connectionString: hyperdrive.connectionString }); - await client.connect(); - - try { - if (params.siteName) { - const result = await client.query('SELECT * FROM organization WHERE "siteName" = $1 LIMIT 1', [params.siteName]); - return result.rows[0] ?? null; - } - if (params.customDomain) { - const result = await client.query( - 'SELECT * FROM organization WHERE "customDomain" = $1 AND "isCustomDomainVerified" = true LIMIT 1', - [params.customDomain.toLowerCase()] - ); - return result.rows[0] ?? null; - } - - return null; - } finally { - // Close in the background so connection teardown never blocks the response. - ctx.waitUntil(client.end()); - } -} - export default { - async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { + async fetch(request: Request, env: Env): Promise { const url = new URL(request.url); const originalHost = request.headers.get('host') ?? url.host; @@ -286,49 +241,6 @@ export default { }); } - // Dedicated org lookup endpoint called directly by the Dashboard SSR server. - // Dashboard → Worker (KV hit: ~5ms | KV miss + Hyperdrive: ~50ms). - // The API server and Render are never in this path. - if (request.method === 'GET' && url.pathname === ORG_LOOKUP_PATH) { - // Server-to-server only: require the shared secret. This endpoint is - // reachable from the public internet, so without this anyone could read - // org config by site name. - const auth = request.headers.get('authorization'); - const expected = `Bearer ${env.PRIVATE_SERVER_KEY}`; - if (!env.PRIVATE_SERVER_KEY || !auth || !timingSafeEqual(auth, expected)) { - return new Response(JSON.stringify({ success: false, error: 'Unauthorized' }), { - status: 401, - headers: { 'Content-Type': 'application/json' } - }); - } - - const siteName = url.searchParams.get('siteName'); - const customDomain = url.searchParams.get('customDomain'); - const lookupKey = siteName ? `siteName:${siteName}` : customDomain ? `customDomain:${customDomain}` : null; - - if (!lookupKey) { - return new Response(JSON.stringify({ success: false, error: 'siteName or customDomain required' }), { - status: 400, - headers: { 'Content-Type': 'application/json' } - }); - } - - const cached = await env.ORG_CACHE.get(lookupKey); - if (cached !== null) { - return new Response(cached, { - headers: { 'Content-Type': 'application/json', 'X-Cache': 'HIT' } - }); - } - - const org = await lookupOrgFromDb(env.HYPERDRIVE, ctx, { siteName, customDomain }); - const body = JSON.stringify({ success: true, data: org }); - await env.ORG_CACHE.put(lookupKey, body, { expirationTtl: ORG_CACHE_TTL }); - - return new Response(body, { - headers: { 'Content-Type': 'application/json', 'X-Cache': 'MISS' } - }); - } - const isProxiedApiCall = url.pathname === PROXY_PREFIX || url.pathname.startsWith(`${PROXY_PREFIX}/`); const isDirectAuthCall = url.pathname === AUTH_PREFIX || url.pathname.startsWith(`${AUTH_PREFIX}/`); const isApiCall = isProxiedApiCall || isDirectAuthCall; diff --git a/apps/tenant-router/wrangler.toml b/apps/tenant-router/wrangler.toml index d2274832..21ad2c6f 100644 --- a/apps/tenant-router/wrangler.toml +++ b/apps/tenant-router/wrangler.toml @@ -1,8 +1,6 @@ name = "cio-tenant-router" main = "src/index.ts" compatibility_date = "2026-05-01" -# Required for the `pg` (node-postgres) driver used to query Hyperdrive. -compatibility_flags = ["nodejs_compat"] # Production routes — bind the Worker to every browser-facing host. # Tenant subdomains and the apex live on the `myclassroomio.com` zone. @@ -34,16 +32,5 @@ bucket_name = "videos" # never commit it. The same value must be set as HLS_SIGNING_SECRET on the # API (Render env) so the cookie minted by the API verifies on the Worker. -# Org lookup cache. Create with: -# wrangler kv namespace create ORG_CACHE -# then paste the returned id below. -[[kv_namespaces]] -binding = "ORG_CACHE" -id = "d07dc73ade284d41af53b387623df29b" - -[[hyperdrive]] -binding = "HYPERDRIVE" -id = "9ecbfa45699e452889e72acc64af455b" - [observability] enabled = true diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76143841..c1b1b569 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -779,17 +779,10 @@ importers: version: 4.2.1 apps/tenant-router: - dependencies: - pg: - specifier: ^8.18.0 - version: 8.18.0 devDependencies: '@cloudflare/workers-types': specifier: ^4.20250109.0 version: 4.20260511.1 - '@types/pg': - specifier: ^8.16.0 - version: 8.16.0 typescript: specifier: ^5.5.2 version: 5.9.3