Standalone repo that builds embeddable JAMstack map bundles (web components), composes them into a lazy-loaded SPA via layouts/, and optionally wraps a Capacitor Android/iOS app — one build, choose your output/CDN. Diátaxis docs. History reset to a single clean commit; no PII, IPs, or internal terms.
54 lines
2.5 KiB
JavaScript
54 lines
2.5 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* write-token.mjs — emit the static token endpoint into the served web-root.
|
|
*
|
|
* WHY THIS EXISTS
|
|
* The maps need a Mapbox token at runtime, and the three surfaces that serve them
|
|
* cannot supply one the same way:
|
|
* · a static host (GitHub Pages, the source-control service, a laptop) runs no
|
|
* server-side code at all, so there is nothing to compute a token;
|
|
* · the mobile app's origin is https://localhost, where a same-origin endpoint
|
|
* can never exist;
|
|
* · a partner site embedding a bundle has no reason to know our token.
|
|
* The shared answer is a PLAIN FILE at a known URL. Every client already accepts
|
|
* either a bare string or {token|tk|accessToken}, so a JSON file IS the endpoint.
|
|
*
|
|
* WHY THE TOKEN IS NOT IN THE REPO
|
|
* It is written from the MAPBOX_TOKEN environment variable at build time and the
|
|
* output path is gitignored. CI supplies the value from a secret, so the token
|
|
* lives in deployment settings rather than in git history — rotating it is a
|
|
* redeploy, not a rewrite of every commit that ever carried it.
|
|
*
|
|
* NO TOKEN, NO FILE. A missing endpoint is a clear 404 the token ladder can fall
|
|
* through; a file containing an empty string looks like a successful fetch of a
|
|
* broken token, which is much harder to diagnose.
|
|
*
|
|
* UPGRADE PATH: swapping this static file for a service that mints short-lived
|
|
* tk.* tokens from a secret sk.* changes nothing on the client — the contract is
|
|
* the URL, not the implementation behind it.
|
|
*/
|
|
import { writeFileSync, mkdirSync, existsSync, rmSync } from 'node:fs'
|
|
import { dirname, resolve, join } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
const OUT_DIR = resolve(__dirname, '..', 'public', 'api', 'maps')
|
|
const OUT = join(OUT_DIR, 'token.json')
|
|
|
|
const token = (process.env.MAPBOX_TOKEN || '').trim()
|
|
|
|
if (!token) {
|
|
if (existsSync(OUT)) rmSync(OUT)
|
|
console.log('[write-token] MAPBOX_TOKEN unset — no endpoint written (maps fall through the token ladder)')
|
|
process.exit(0)
|
|
}
|
|
|
|
if (token.startsWith('sk.')) {
|
|
console.error('[write-token] REFUSED: MAPBOX_TOKEN is a SECRET key (sk.*). This file is served publicly.\n' +
|
|
' Use a public pk.* token here, or put the sk.* behind a service that mints tk.* tokens.')
|
|
process.exit(1)
|
|
}
|
|
|
|
mkdirSync(OUT_DIR, { recursive: true })
|
|
writeFileSync(OUT, JSON.stringify({ token, type: token.slice(0, 2) }) + '\n')
|
|
console.log(`[write-token] wrote public/api/maps/token.json (${token.slice(0, 6)}…, type ${token.slice(0, 2)})`)
|