Something went wrong. Try again.
An atproto Tilemap editor
Something went wrong. Try again.
tilemap.world vite.config.ts
3.4 kB · 85 lines
TypeScript
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586import { fileURLToPath, URL } from 'node:url'import { defineConfig, loadEnv, type Plugin } from 'vite'import react from '@vitejs/plugin-react'import tailwindcss from '@tailwindcss/vite'
/** The atproto OAuth client metadata document, served/emitted at * `/client-metadata.json`. In production the client_id is this URL, so its * fields must use the deployed origin (set `VITE_APP_URL`). Local dev uses a * loopback client instead (see src/lib/oauth.ts) and ignores this. */function clientMetadata(appUrl: string): string { const base = appUrl.replace(/\/$/, '') return JSON.stringify( { client_id: `${base}/client-metadata.json`, client_name: 'Tilemap World', client_uri: base, redirect_uris: [`${base}/`], // Keep byte-for-byte in sync with OAUTH_SCOPE in src/lib/oauth.ts. scope: 'atproto repo:world.tilemap.tileset repo:world.tilemap.map blob:image/* blob:application/json', grant_types: ['authorization_code', 'refresh_token'], response_types: ['code'], application_type: 'web', token_endpoint_auth_method: 'none', dpop_bound_access_tokens: true, }, null, 2, )}
function oauthClientMetadataPlugin(appUrl: string): Plugin { const json = clientMetadata(appUrl) return { name: 'oauth-client-metadata', configureServer(server) { server.middlewares.use('/client-metadata.json', (_req, res) => { res.setHeader('Content-Type', 'application/json') res.end(json) }) }, generateBundle() { this.emitFile({ type: 'asset', fileName: 'client-metadata.json', source: json }) }, }}
// https://vite.dev/config/export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), '') const isProd = mode === 'production'
// VITE_APP_URL is the single source of truth for the deployed origin. It // drives BOTH the OAuth client metadata (the `client_id` / `redirect_uris` // in /client-metadata.json) and the built asset `base`, so the two can never // drift apart. A production build without it would otherwise silently bake a // loopback client_id that the OAuth server rejects at login — so we stop the // build with a clear message instead of shipping a broken artifact. if (isProd && !env.VITE_APP_URL) { throw new Error( 'VITE_APP_URL is required for production builds — it sets the atproto OAuth ' + 'client_id (/client-metadata.json) and the asset base URL. Set it to the ' + 'deployed https origin, e.g. VITE_APP_URL=https://tilemap.world. ' + '(Netlify sets this in netlify.toml; export it locally to build prod.)', ) }
// Dev fallback to the loopback origin; atproto OAuth needs the loopback IP // (not `localhost`) in development, so we also bind the dev server to it. const appUrl = (env.VITE_APP_URL || 'http://127.0.0.1:5173').replace(/\/$/, '')
return { // Absolute base in production so assets resolve from the canonical origin // (and match the OAuth client metadata); relative in dev so the loopback // dev server serves them directly. base: isProd ? `${appUrl}/` : '/', plugins: [react(), tailwindcss(), oauthClientMetadataPlugin(appUrl)], server: { host: '127.0.0.1' }, resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)), }, }, }})