mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-23 23:28:04 +02:00
Cliquer sur « Connecter » sans avoir enregistré l'URL publique du site renvoyait une erreur, et la redirection retombait sur https://0.0.0.0 : la directive upgrade-insecure-requests était posée même sur une page servie en HTTP, le navigateur basculait donc la navigation vers un https:// inexistant. - `publicBaseUrl()` : réglage `site_public_url`, variables d'environnement, puis origine de la requête (X-Forwarded-Host / X-Forwarded-Proto). L'adresse de retour OAuth et le lien des publications l'utilisent ; `seo-server` s'appuie dessus au lieu de dupliquer la déduction. - `src/lib/site-url.ts` : déduction pure et testée de l'origine, HTTP par défaut pour localhost et les IP nues, HTTPS pour un nom de domaine. - Middleware : `upgrade-insecure-requests` uniquement quand la page est servie en HTTPS. - Page Réseaux : l'adresse détectée est annoncée comme valeur par défaut, avec le rappel que Facebook et LinkedIn exigent du HTTPS sur un domaine. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
51 lines
1.8 KiB
TypeScript
51 lines
1.8 KiB
TypeScript
/**
|
||
* Déduction de l'origine publique du site à partir des en-têtes d'une requête.
|
||
*
|
||
* Sans dépendance à la base ni à Next : utilisable partout et testable. Sert de
|
||
* repli quand aucune URL publique n'est enregistrée dans Backend › Réseaux
|
||
* sociaux : derrière Caddy ou Nginx Proxy Manager, `X-Forwarded-Host` et
|
||
* `X-Forwarded-Proto` décrivent l'adresse vue par le visiteur.
|
||
*/
|
||
|
||
type HeaderReader = { get(name: string): string | null };
|
||
|
||
const LOCAL_HOSTS = /^(localhost|.*\.localhost|127(?:\.\d{1,3}){3}|0\.0\.0\.0|\[::1\]|\[::\])$/i;
|
||
const IPV4 = /^\d{1,3}(?:\.\d{1,3}){3}$/;
|
||
|
||
function first(value: string | null): string {
|
||
return (value ?? "").split(",")[0]?.trim() ?? "";
|
||
}
|
||
|
||
/** Nom d'hôte sans port, en minuscules. */
|
||
function hostname(host: string): string {
|
||
const match = /^(\[[^\]]*\]|[^:]+)/.exec(host);
|
||
return (match?.[1] ?? host).toLowerCase();
|
||
}
|
||
|
||
/**
|
||
* Sans `X-Forwarded-Proto`, on présume HTTPS pour un nom de domaine — c'est le
|
||
* cas de tout déploiement réel — et HTTP pour une adresse locale ou une IP nue,
|
||
* qu'aucun certificat ne couvre.
|
||
*/
|
||
export function defaultProtocol(host: string): "http" | "https" {
|
||
const name = hostname(host);
|
||
if (!name || LOCAL_HOSTS.test(name) || IPV4.test(name)) return "http";
|
||
return "https";
|
||
}
|
||
|
||
/**
|
||
* Origine (`https://pleinr.example.fr`) vue par le visiteur, ou chaîne vide si
|
||
* la requête ne porte aucun hôte.
|
||
*/
|
||
export function originFromHeaders(headers: HeaderReader): string {
|
||
const host = first(headers.get("x-forwarded-host")) || first(headers.get("host"));
|
||
if (!host || /[\s/\\]/.test(host)) return "";
|
||
const forwarded = first(headers.get("x-forwarded-proto")).toLowerCase();
|
||
const protocol = forwarded === "https" || forwarded === "http" ? forwarded : defaultProtocol(host);
|
||
try {
|
||
return new URL(`${protocol}://${host}`).origin;
|
||
} catch {
|
||
return "";
|
||
}
|
||
}
|