Files
FanKarr/server/config.ts
T
zacharyandClaude Opus 5 804436ff70 feat: support d'un chemin de base (BASE_URL) pour reverse proxy
FanKarr supposait jusqu'ici être servi à la racine du domaine. La variable
d'environnement BASE_URL (ex. /fankarr) permet de l'héberger dans un
sous-dossier, sans reconstruire l'image : le préfixe est appliqué au démarrage.

- server/config.ts : BASE_URL normalisée et validée ('' = racine)
- server/index.ts  : toutes les routes montées sous BASE_URL via un Router,
  index.html servi avec <base href> et window.__BASE_URL__, redirection de /
- server/auth.ts   : cookie de session limité au chemin de base
- src/base-url.ts  : préfixage des appels fetch('/api/…') en un point unique,
  helpers withBase / absoluteUrl
- src/router       : createWebHistory(BASE_URL)
- vite.config.ts   : base './' (assets relatifs, résolus par <base href>)

Comportement inchangé lorsque BASE_URL n'est pas défini.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-03 21:57:25 +02:00

77 lines
2.7 KiB
TypeScript

import fs from 'fs'
import path from 'path'
export const _isBunBinary = typeof (globalThis as any).Bun !== 'undefined'
&& path.dirname((process as any).execPath) !== process.cwd()
// Charge le .env du binaire Bun avant tout module qui lit process.env
;(() => {
if (!_isBunBinary) return
const binDir = path.dirname((process as any).execPath)
const envPath = path.join(binDir, '.env')
if (!fs.existsSync(envPath)) return
try {
const lines = fs.readFileSync(envPath, 'utf-8').split('\n')
for (const raw of lines) {
const line = raw.trim()
if (!line || line.startsWith('#')) continue
const eq = line.indexOf('=')
if (eq === -1) continue
const key = line.slice(0, eq).trim()
let val = line.slice(eq + 1).trim()
if ((val.startsWith('"') && val.endsWith('"')) ||
(val.startsWith("'") && val.endsWith("'"))) {
val = val.slice(1, -1)
}
if (key && !(key in process.env)) process.env[key] = val
}
console.log(`[config] .env chargé depuis ${envPath}`)
} catch (err) {
console.warn(`[config] Impossible de charger le fichier .env : ${err}`)
}
})()
export const BASE_DIR = _isBunBinary
? path.dirname((process as any).execPath)
: process.cwd()
const isInContainer = (): boolean => {
if (fs.existsSync('/.dockerenv')) return true;
// Podman
if (fs.existsSync('/.containerenv')) return true;
try {
const cgroup = fs.readFileSync('/proc/1/cgroup', 'utf8');
if (/docker|podman|containerd|lxc/i.test(cgroup)) return true;
} catch {}
try {
const environ = fs.readFileSync('/proc/1/environ', 'utf8');
if (environ.includes('container=')) return true;
} catch {}
return false;
};
export const DATA_DIR = isInContainer()
? '/config'
: path.join(BASE_DIR, 'config');
// ── Chemin de base (reverse proxy en sous-dossier) ─────────────
// BASE_URL=/fankarr : l'application est servie sous https://exemple.tld/fankarr
// Valeur normalisée : '' (racine) ou '/xxx' sans slash final
function normalizeBaseUrl(raw: string | undefined): string {
const trimmed = (raw ?? '').trim()
if (!trimmed || trimmed === '/') return ''
const withLead = trimmed.startsWith('/') ? trimmed : `/${trimmed}`
const cleaned = withLead.replace(/\/+$/, '')
if (!/^(\/[A-Za-z0-9._~-]+)+$/.test(cleaned)) {
console.warn(`[config] BASE_URL « ${trimmed} » invalide (attendu : /mon-app), valeur ignorée`)
return ''
}
return cleaned
}
export const BASE_URL = normalizeBaseUrl(process.env.BASE_URL)