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:
+1
-1
@@ -14,7 +14,7 @@
|
||||
window === window.top guard) — safe to always include. Provides
|
||||
window.nostr + auto sign-in via the node's selected nostr identity
|
||||
when opened inside the Archipelago shell. -->
|
||||
<script src="/nostr-provider.js" data-session-url="/api/auth/login" data-session-mode="cookie" data-me-url="/api/auth/me" data-health-url="/api/health"></script>
|
||||
<script src="%BASE_URL%nostr-provider.js" data-session-url="%BASE_URL%api/auth/login" data-session-mode="cookie" data-me-url="%BASE_URL%api/auth/me" data-health-url="%BASE_URL%api/health"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
+19
-1
@@ -6,8 +6,26 @@ export class ApiError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
// import.meta.env.BASE_URL is Vite's configured `base` (e.g. "/" or
|
||||
// "/regress/") — API paths must resolve under it too when this app is
|
||||
// deployed under a path prefix, not just its own static assets.
|
||||
export function withBase(path: string): string {
|
||||
const base = import.meta.env.BASE_URL.replace(/\/$/, '');
|
||||
return `${base}${path}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full absolute URL for a given API path, base-prefix included. NIP-98 login
|
||||
* must sign exactly this — the same URL that will actually be requested —
|
||||
* or the reverse proxy's forwarded path won't match the signature and every
|
||||
* login under a path-prefixed deployment fails.
|
||||
*/
|
||||
export function apiUrl(path: string): string {
|
||||
return `${location.origin}${withBase(path)}`;
|
||||
}
|
||||
|
||||
async function request<T>(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<T> {
|
||||
const res = await fetch(path, {
|
||||
const res = await fetch(withBase(path), {
|
||||
method,
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { api, ApiError } from '../lib/api';
|
||||
import { api, apiUrl, ApiError } from '../lib/api';
|
||||
import { buildNip98Header, hasNip07 } from '../lib/nip07';
|
||||
import { buildNip98HeaderWithNsec, isValidNsec } from '../lib/nsec';
|
||||
|
||||
@@ -34,7 +34,7 @@ export const useAuthStore = defineStore('auth', {
|
||||
},
|
||||
async login() {
|
||||
if (!hasNip07()) throw new Error('No nostr extension found — install Alby or nos2x first.');
|
||||
const url = `${location.origin}/api/auth/login`;
|
||||
const url = apiUrl('/api/auth/login');
|
||||
const header = await buildNip98Header(url, 'POST');
|
||||
const res = await api.post<{ pubkey: string; team: Team | null }>(
|
||||
'/api/auth/login',
|
||||
@@ -53,7 +53,7 @@ export const useAuthStore = defineStore('auth', {
|
||||
*/
|
||||
async loginWithNsec(nsec: string) {
|
||||
if (!isValidNsec(nsec)) throw new Error('That doesn\'t look like a valid nsec.');
|
||||
const url = `${location.origin}/api/auth/login`;
|
||||
const url = apiUrl('/api/auth/login');
|
||||
const header = buildNip98HeaderWithNsec(nsec, url, 'POST');
|
||||
const res = await api.post<{ pubkey: string; team: Team | null }>(
|
||||
'/api/auth/login',
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -2,6 +2,7 @@ import { defineConfig } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
|
||||
export default defineConfig({
|
||||
base: process.env.VITE_BASE || '/',
|
||||
plugins: [vue()],
|
||||
server: {
|
||||
proxy: {
|
||||
|
||||
Reference in New Issue
Block a user