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>
This commit is contained in:
zacharyandClaude Opus 5 committed 2026-10-03 21:57:25 +02:00
1 parent c0452661d4
commit 804436ff70
12 files changed
+163 -35

No files matched your search

+2 -1
View File
@@ -85,6 +85,7 @@ import JellyfinImportModal from '@/components/settings/JellyfinImportModal.vue'
import SettingsSection from '@/components/settings/SettingsSection.vue'
import SettingsToggle from '@/components/settings/SettingsToggle.vue'
import { useToast } from '@/composables/useToast'
import { absoluteUrl } from '@/base-url'
type Option = 'jellyfinLogin' | 'jellyfinNewUserLogin' | 'jellyfinAutoImport'
@@ -97,7 +98,7 @@ const options = ref<Record<Option, boolean>>({ jellyfinLogin: true, jellyfi
const savedSection = ref<'users' | 'login' | null>(null)
let savedTimer: ReturnType<typeof setTimeout> | null = null
const apiBase = `${window.location.origin}/api/v1`
const apiBase = absoluteUrl('/api/v1')
const apiEndpoints = [
{ method: 'GET', path: '/series/search?q=', desc: 'Chercher une série dans le catalogue' },
+2 -1
View File
@@ -233,6 +233,7 @@ import { copyToClipboard } from '@/composables/useClipboard'
import { formatRelative, plural } from '@/utils/format'
import SettingsSection from '@/components/settings/SettingsSection.vue'
import SettingsToggle from '@/components/settings/SettingsToggle.vue'
import { absoluteUrl } from '@/base-url'
interface User {
id : string
@@ -449,7 +450,7 @@ async function deleteInvite(code: string) {
}
function inviteUrl(code: string) {
return `${window.location.origin}/invite/${code}`
return absoluteUrl(`/invite/${code}`)
}
async function copyInvite(code: string) {