// ═══════════════════════════════════════════════════════════════════════════════ // Waar staat de relay, en waar mogen de gegevens staan. // // Het adres van de Umbrel staat NIET in deze repo en hoort daar ook niet in: // dit is een publieke app store, en de .gitignore van de repo-root heeft er een // eigen kopje over. Het komt dus uit de omgeving, met `.env` als de gewone weg // en `.env.sample` als het voorbeeld dat wél in de repo staat. // // Geen dotenv-pakket: dat is een afhankelijkheid voor twintig regels, en die // twintig regels zijn hier beter te lezen dan de instellingen van een pakket. // ═══════════════════════════════════════════════════════════════════════════════ import { readFileSync } from 'node:fs'; import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; /** De wortel van dit gereedschap, ongeacht vanwaar het gestart wordt. */ export const WORTEL = resolve(dirname(fileURLToPath(import.meta.url)), '..'); /** * Leest een `.env`-bestand. * * Regels van de vorm `NAAM=waarde`. Lege regels en regels die met `#` beginnen * worden overgeslagen, en aanhalingstekens om de waarde gaan eraf. Meer niet: wie * hier een regel met een dubbele bodem in zet, krijgt hem letterlijk terug. */ export const leesEnvBestand = (inhoud) => { const uit = {}; for (const regel of inhoud.split(/\r?\n/)) { const kaal = regel.trim(); if (kaal === '' || kaal.startsWith('#')) continue; const isGelijk = kaal.indexOf('='); if (isGelijk <= 0) continue; const naam = kaal.slice(0, isGelijk).trim(); let waarde = kaal.slice(isGelijk + 1).trim(); if ( (waarde.startsWith('"') && waarde.endsWith('"') && waarde.length >= 2) || (waarde.startsWith("'") && waarde.endsWith("'") && waarde.length >= 2) ) { waarde = waarde.slice(1, -1); } uit[naam] = waarde; } return uit; }; /** * Maakt van een adres een WebSocket-URL, of gooit met uitleg. * * De relay is een WebSocket-server, dus `ws://` of `wss://`. Een `http://` dat * iemand uit de adresbalk plakt wordt omgezet in plaats van geweigerd: dat is * de meest voorkomende vergissing en er is niets dubbelzinnigs aan. * * Wat er wél uitkomt is een adres zonder schema (`umbrel.local:3852`), en dat * krijgt `ws://`. Zonder poort blijft het zoals het is: achter een reverse proxy * met TLS is `wss://host` zonder poort juist de goede vorm. */ export const naarRelayUrl = (adres) => { if (typeof adres !== 'string' || adres.trim() === '') { throw new Error('Geen relay-adres. Zet RELAY_URL in .env; zie .env.sample.'); } const kaal = adres.trim(); if (kaal.startsWith('ws://') || kaal.startsWith('wss://')) return kaal; if (kaal.startsWith('http://')) return `ws://${kaal.slice('http://'.length)}`; if (kaal.startsWith('https://')) return `wss://${kaal.slice('https://'.length)}`; if (kaal.includes('://')) { throw new Error(`"${kaal}" heeft een schema dat hier niets betekent. Gebruik ws:// of wss://.`); } return `ws://${kaal}`; }; /** * De instellingen: eerst de omgeving, dan `.env`, dan de standaardwaarden. * * De omgeving wint van het bestand, zodat je één keer iets anders kunt proberen * zonder het bestand aan te raken. */ export const leesConfig = ({ env = process.env, wortel = WORTEL } = {}) => { let uitBestand = {}; try { uitBestand = leesEnvBestand(readFileSync(join(wortel, '.env'), 'utf8')); } catch (oorzaak) { if (oorzaak.code !== 'ENOENT') throw oorzaak; } const haal = (naam) => env[naam] ?? uitBestand[naam]; const adres = haal('RELAY_URL'); return { /** Het adres van de relay, of `null` als het er niet is. */ relayUrl: adres ? naarRelayUrl(adres) : null, /** Waar het register en de databases staan. */ dataDir: resolve(wortel, haal('DATA_DIR') ?? 'data'), /** De poort van het bedieningsvlak. */ uiPoort: Number.parseInt(haal('UI_PORT') ?? '4380', 10), }; };