From 300b58cd705050a79c500aa26d013dcbb41db306 Mon Sep 17 00:00:00 2001 From: Guide Fari Date: Sat, 11 Jul 2026 01:12:44 +0200 Subject: [PATCH] feat(vps): port better-auth route onto the Effect router (Step 2c) Step 2c of the Hono -> Effect HttpApi migration (docs/migration-effect-http-api.md). Isolates the one third-party routing integration so an auth regression is diagnosable to this one small diff, separate from the topology cutover in 2b. Why this exists: /auth/* now needs to be handled correctly before any authed route can be trusted on the new router. better-auth owns its own routing (auth.handler is opaque), so it can't become an HttpApiEndpoint -- it's mounted as its own wildcard route ahead of the Hono fallback, same basePath as before. - apps/vps/src/http/routes.ts: new betterAuthRoute wildcards /auth/* to auth.handler(prepareAuthRequest(...)), registered before honoFallback in the layer so it takes precedence. - apps/vps/src/routes/user/better-auth.routes.ts: exported prepareAuthRequest so it's reusable outside the Hono wrapper. - apps/vps/src/app.ts: removed the Hono /auth mount -- it's not used anywhere else (no other test or code path calls app.fetch directly since Step 2b made effectWebHandler the sole entry point). betterAuthMiddleware/attachSessionContext are unaffected; they call auth.api.getSession() directly, independent of the /auth HTTP route. Verified router precedence empirically rather than trusting the doc's claim: new tests hit /auth/get-session and /auth/does-not-exist through the live handler and confirm better-auth's own responses are returned, not the Hono fallback's 404. Full apps/vps suite: 142/142 passing (140 existing + 2 new), across 15 files. --- apps/vps/src/app.ts | 6 ++--- apps/vps/src/http/routes.blackbox.test.ts | 22 +++++++++++++++++++ apps/vps/src/http/routes.ts | 18 ++++++++++++++- .../vps/src/routes/user/better-auth.routes.ts | 2 +- 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/apps/vps/src/app.ts b/apps/vps/src/app.ts index f3ebe56c..8299426a 100644 --- a/apps/vps/src/app.ts +++ b/apps/vps/src/app.ts @@ -21,7 +21,6 @@ import shows from '@/routes/shows/show.index' import spotify from '@/routes/spotify/spotify.index' import upload from '@/routes/upload/upload.index' import uploadMultipart from '@/routes/upload-multipart/upload-multipart.index' -import betterAuthRoutes from '@/routes/user/better-auth.routes' import user from '@/routes/user/user.index' import { db } from './db' import { regenerateSitemap } from './routes/redirect/seo/sitemap' @@ -66,8 +65,9 @@ const setupRoutesEffect = Effect.gen(function* () { app.route('/api/music', music) app.route('/api/music-reminders', musicReminders) - // Kept at root, not under /api: auth has its own basePath/email links, and these are externally-referenced public URLs. - app.route('/auth', betterAuthRoutes) + // Kept at root, not under /api: these are externally-referenced public URLs. + // /auth is handled by the Effect router directly (apps/vps/src/http/routes.ts, + // step 2c) -- not mounted here. app.route('/s', shareRouter) app.route('', rss) app.route('', seoRouter) diff --git a/apps/vps/src/http/routes.blackbox.test.ts b/apps/vps/src/http/routes.blackbox.test.ts index 8207f038..5dcf82e0 100644 --- a/apps/vps/src/http/routes.blackbox.test.ts +++ b/apps/vps/src/http/routes.blackbox.test.ts @@ -51,3 +51,25 @@ describe('Effect toWebHandler fallback', () => { expect(res.status).toBe(404) }) }) + +describe('better-auth route (Step 2c)', () => { + it('GET /auth/get-session is handled by the auth route, not the Hono fallback', async () => { + const res = await webHandler.handler(new Request('http://localhost/auth/get-session')) + + // better-auth's own response for an unauthenticated session check, not a 404 -- + // proves /auth/* is matched ahead of the wildcard fallback. + expect(res.status).toBe(200) + expect(await res.json()).toBeNull() + }) + + it('unknown /auth/* paths are handled by better-auth (404 from better-auth, not the Hono fallback)', async () => { + const withoutAuth = await webHandler.handler(new Request('http://localhost/does-not-exist')) + const withAuth = await webHandler.handler(new Request('http://localhost/auth/does-not-exist')) + + expect(withoutAuth.status).toBe(404) + expect(withAuth.status).toBe(404) + // Different bodies would indicate the two 404s come from different sources + // (Hono's notFound handler vs. better-auth's own routing). + expect(await withAuth.text()).not.toEqual(await withoutAuth.text()) + }) +}) diff --git a/apps/vps/src/http/routes.ts b/apps/vps/src/http/routes.ts index 59e747bf..f9556ef0 100644 --- a/apps/vps/src/http/routes.ts +++ b/apps/vps/src/http/routes.ts @@ -1,6 +1,8 @@ import { Effect, Layer } from 'effect' import { HttpRouter, HttpServer, HttpServerRequest, HttpServerResponse } from 'effect/unstable/http' import type { AppType } from '@/app' +import { auth } from '@/lib/auth' +import { prepareAuthRequest } from '@/routes/user/better-auth.routes' // Routes every request to the existing Hono app unchanged. Removed one HttpApi group at a time as routes are ported (docs/migration-effect-http-api.md). export const honoFallback = (honoApp: AppType) => @@ -12,5 +14,19 @@ export const honoFallback = (honoApp: AppType) => }) ) +// better-auth owns its own routing; we can't redefine it as HttpApiEndpoints. Kept at +// its own path (not under /api) since its basePath appears in emailed links. +const betterAuthRoute = HttpRouter.add('*', '/auth/*', (request) => + Effect.gen(function* () { + const webRequest = yield* HttpServerRequest.toWeb(request) + const webResponse = yield* Effect.promise(() => + Promise.resolve(auth.handler(prepareAuthRequest(webRequest))) + ) + return HttpServerResponse.fromWeb(webResponse) + }) +) + export const createWebHandler = (honoApp: AppType) => - HttpRouter.toWebHandler(Layer.mergeAll(honoFallback(honoApp), HttpServer.layerServices)) + HttpRouter.toWebHandler( + Layer.mergeAll(betterAuthRoute, honoFallback(honoApp), HttpServer.layerServices) + ) diff --git a/apps/vps/src/routes/user/better-auth.routes.ts b/apps/vps/src/routes/user/better-auth.routes.ts index 86b6a148..9930aad9 100644 --- a/apps/vps/src/routes/user/better-auth.routes.ts +++ b/apps/vps/src/routes/user/better-auth.routes.ts @@ -3,7 +3,7 @@ import { auth } from '@/lib/auth' const betterAuthApp = new Hono() -function prepareAuthRequest(request: Request) { +export function prepareAuthRequest(request: Request) { const hasOrigin = request.headers.has('origin') || request.headers.has('referer') if (request.method === 'POST' && !hasOrigin && request.headers.has('cookie')) {