Add path-prefix deployment support for migrating to podsteadr's domain
Regress needs to live at podsteadr.atobitcoin.io/regress/ since / is already podsteadr. Two coordinated pieces: - VITE_BASE build arg (frontend asset/script paths, %BASE_URL% in index.html for the nostr-provider.js script tag) - ROUTE_PREFIX runtime env (backend routes registered under the prefix via Fastify's plugin-encapsulation, including /api/health) The nginx location must forward the prefix unstripped (proxy_pass with no trailing path) — NIP-98 login signs the exact URL it calls, so a stripped prefix makes the backend reconstruct a different URL than what was signed and every login fails. Caught this with a real nginx+docker integration test locally before it could break the live migration, then fixed a matching bug in auth.ts (it was signing the unprefixed URL while api.ts fetched the prefixed one). New routePrefix.test.ts proves the prefix is actually enforced, including a negative case. 40 tests passing. Also fixes a latent bug: import.meta.env usage had no vite/client type reference, so it only ever passed typecheck by accident in earlier local runs — added the standard vite-env.d.ts.
This commit is contained in:
+26
-16
@@ -44,25 +44,35 @@ export async function buildApp(opts: BuildAppOptions): Promise<FastifyInstance>
|
||||
await app.register(cors, { origin: true, credentials: true, methods: ['GET', 'POST', 'DELETE'] });
|
||||
await app.register(nostrAuth, { db, config });
|
||||
|
||||
app.get('/api/health', async () => ({ status: 'ok' }));
|
||||
// Everything below is mounted under ROUTE_PREFIX (empty by default, i.e.
|
||||
// root) so a single build can be deployed either standalone or under a
|
||||
// shared domain's path — see config.ts for why the prefix must be
|
||||
// preserved end-to-end rather than stripped by the reverse proxy.
|
||||
const prefix = config.ROUTE_PREFIX;
|
||||
await app.register(
|
||||
async (scoped) => {
|
||||
scoped.get('/api/health', async () => ({ status: 'ok' }));
|
||||
|
||||
await app.register(authRoutes);
|
||||
await app.register(placesRoutes);
|
||||
await app.register(claimsRoutes);
|
||||
await app.register(linksRoutes);
|
||||
await app.register(fieldsRoutes);
|
||||
await app.register(syncRoutes);
|
||||
await scoped.register(authRoutes);
|
||||
await scoped.register(placesRoutes);
|
||||
await scoped.register(claimsRoutes);
|
||||
await scoped.register(linksRoutes);
|
||||
await scoped.register(fieldsRoutes);
|
||||
await scoped.register(syncRoutes);
|
||||
|
||||
const staticDir = config.STATIC_DIR;
|
||||
if (staticDir && existsSync(staticDir)) {
|
||||
await app.register(fastifyStatic, { root: staticDir });
|
||||
app.setNotFoundHandler((req, reply) => {
|
||||
if (req.raw.url?.startsWith('/api/')) {
|
||||
return reply.code(404).send({ error: 'not found' });
|
||||
const staticDir = config.STATIC_DIR;
|
||||
if (staticDir && existsSync(staticDir)) {
|
||||
await scoped.register(fastifyStatic, { root: staticDir, prefix: '/' });
|
||||
scoped.setNotFoundHandler((req, reply) => {
|
||||
if (req.raw.url?.startsWith(`${prefix}/api/`)) {
|
||||
return reply.code(404).send({ error: 'not found' });
|
||||
}
|
||||
return reply.sendFile('index.html');
|
||||
});
|
||||
}
|
||||
return reply.sendFile('index.html');
|
||||
});
|
||||
}
|
||||
},
|
||||
{ prefix },
|
||||
);
|
||||
|
||||
app.addHook('onClose', async () => {
|
||||
db.close();
|
||||
|
||||
Reference in New Issue
Block a user