A reverse proxy can now forward /api/ to the API with or without stripping the prefix (NPMplus custom locations don't strip it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
This commit is contained in:
+9
-1
@@ -22,7 +22,15 @@ import { webhookRoutes } from './routes/webhooks';
|
||||
|
||||
/** Builds the API without listening, so tests can drive it with app.inject(). */
|
||||
export async function buildApp({ logger = true, rateLimits = true }: { logger?: boolean; rateLimits?: boolean } = {}) {
|
||||
const app = Fastify({ logger, trustProxy: true });
|
||||
const app = Fastify({
|
||||
logger,
|
||||
trustProxy: true,
|
||||
// Serve /api/... as well as /..., so a reverse proxy can forward /api/ with or without stripping it.
|
||||
rewriteUrl: (req) => {
|
||||
const url = req.url ?? '/';
|
||||
return url === '/api' || url.startsWith('/api/') || url.startsWith('/api?') ? url.slice(4) || '/' : url;
|
||||
},
|
||||
});
|
||||
|
||||
await app.register(cors, {
|
||||
origin: [env.WEB_URL],
|
||||
|
||||
@@ -26,6 +26,18 @@ describe('auth', () => {
|
||||
expect(viaCookie.json().id).toBe(user.id);
|
||||
});
|
||||
|
||||
it('serves the same routes under /api for proxies that keep the prefix', async () => {
|
||||
const res = await app().inject({
|
||||
method: 'POST',
|
||||
url: '/api/auth/register',
|
||||
payload: { username: 'bob', email: 'bob@example.com', password: 'password123' },
|
||||
});
|
||||
expect(res.statusCode).toBe(200);
|
||||
const me = await app().inject({ url: '/api/me?x=1', headers: bearer(res.json().token) });
|
||||
expect(me.json().username).toBe('bob');
|
||||
expect((await app().inject({ url: '/apiary' })).statusCode).toBe(404);
|
||||
});
|
||||
|
||||
it('stores only a hash of the session token', async () => {
|
||||
const { token } = await createUser(app(), 'alice');
|
||||
const rows = await sql`select id from sessions`;
|
||||
|
||||
Reference in New Issue
Block a user