feat: redirect engine with loop refusal and chain notices

- rule set: exact sources by key, patterns by literal prefix, regex by id
  (stdlib RE2, compiled once per source); ignore case, trailing slash,
  query ignore/pass, a source with ? matches its query exactly
- targets: captures escaped, a relative target never leaves the site, an
  absolute one keeps its host
- SaveRedirect validates, simulates the rule in place (exact, pattern and
  regex probes, 10 hops) and refuses a loop; chains answer the notice
- resolve follows the chain to its final target, 410 answers gone, a
  runtime cycle answers not_found and is logged once
This commit is contained in:
2026-10-27 11:26:42 +01:00
parent e31ac7c1c6
commit 34a65b846e
8 changed files with 1221 additions and 21 deletions

View File

@@ -195,3 +195,11 @@ snippet:
invalid: Żądanie podglądu jest nieprawidłowe.
redirects:
auto_created: Adres się zmienił, więc utworzono przekierowanie 301 z „:old” na „:new”. Wcześniejsze przekierowania (:count) prowadzą teraz bezpośrednio na nowy adres.
loop: "To przekierowanie utworzyłoby pętlę: :path. Nic nie zapisano."
chain_notice: "Zapisano, ale to przekierowanie jest częścią łańcucha: :path. Odwiedzający przechodzą :count przekierowania. Ustaw „:source” bezpośrednio na „:final”."
target_required: "Wpisz adres docelowy lub wybierz 410 Usunięto."
target_gone: "Reguła 410 Usunięto nikogo nie przekierowuje. Wyczyść adres docelowy lub wybierz 301 albo 302."
target_invalid: "Wpisz ścieżkę zaczynającą się od / lub pełny adres http(s)."
regex_invalid: "To nie jest poprawne wyrażenie regularne: :error"
source_invalid: "Zacznij adres od /. Używaj * tylko we wzorcu."
source_taken: "Inne przekierowanie już używa tego adresu i dopasowania."