feat(domains): 301-Weiterleitung Domain→Domain (redirect_to) — v1.2.108
Neue Domain kann per 301 auf eine andere Domain/URL umgeleitet werden, statt auf ein Backend zu routen (Use-Case: kvs.netcell-it.de → https://zkm.netcell-it.de, inkl. HTTPS). Variante (a): immer auf Ziel-Root (`redirect location`), pfad- unabhängig. - Migration 0043: domains.redirect_to text NOT NULL DEFAULT '' - Model + domains-Service (SELECT/INSERT/UPDATE/scan) - HAProxy-Generator: buildRedirectTo() sanitisiert (nur http(s), kein Whitespace/Quotes → sonst kein Redirect statt kaputter Config); Template emittiert `http-request redirect location <url> code 301 if hdr(host)`. Terminiert vor use_backend → Redirect-Domain routet auf kein Backend. http→https läuft über den vorhandenen :80-Redirect (zwei Hops, inkl. TLS). - UI: Feld „Weiterleitung (301) nach" im Domain-Formular (de/en) - Tests: Render-Zeile + buildRedirectTo-Sanitisierung Hinweis: Die Redirect-Domain braucht weiterhin ein eigenes TLS-Zert (ACME). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -212,6 +212,11 @@ type DomainView struct {
|
||||
// to-www → Name="www.example.com" → "example.com" (strip www.-Prefix)
|
||||
RedirectFromHost string
|
||||
|
||||
// RedirectTo: HAProxy-safe 301-Ziel-URL für eine Domain→Domain-Weiterleitung
|
||||
// (z. B. "https://zkm.netcell-it.de"). Leer = kein Redirect. Schattet das
|
||||
// gleichnamige Feld aus dem eingebetteten models.Domain (sanitisiert).
|
||||
RedirectTo string
|
||||
|
||||
// ResponseHeaders: Custom-Headers die HAProxy auf jede Response für
|
||||
// diese Domain setzt. Werte sind bereits HAProxy-safe escaped
|
||||
// (Quotes → ', Newlines → Space).
|
||||
@@ -313,6 +318,7 @@ func (g *Generator) loadView(ctx context.Context) (*View, error) {
|
||||
HSTSHeader: buildHSTSHeader(d),
|
||||
MaintMessage: buildMaintMessage(d),
|
||||
RedirectFromHost: buildRedirectFromHost(d),
|
||||
RedirectTo: buildRedirectTo(d),
|
||||
ResponseHeaders: headersByDomain[d.ID],
|
||||
}
|
||||
if d.MaxBodyKB > 0 {
|
||||
@@ -429,3 +435,23 @@ func buildRedirectFromHost(d models.Domain) string {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// buildRedirectTo liefert die 301-Ziel-URL HAProxy-safe, oder "" wenn kein
|
||||
// Redirect gesetzt ist bzw. die URL ungültig erscheint. Defensiv: nur
|
||||
// http(s)-URLs ohne Whitespace/Steuerzeichen/Quotes — sonst würde die
|
||||
// `redirect location <url>`-Zeile die HAProxy-Config sprengen. Im Zweifel
|
||||
// lieber KEIN Redirect rendern als eine kaputte Config auszuliefern.
|
||||
func buildRedirectTo(d models.Domain) string {
|
||||
u := strings.TrimSpace(d.RedirectTo)
|
||||
if u == "" {
|
||||
return ""
|
||||
}
|
||||
lower := strings.ToLower(u)
|
||||
if !strings.HasPrefix(lower, "http://") && !strings.HasPrefix(lower, "https://") {
|
||||
return ""
|
||||
}
|
||||
if strings.ContainsAny(u, " \t\r\n\"'`\\{}") {
|
||||
return ""
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user