Umbau-Projekt (SSR und Rest)

This commit is contained in:
2026-09-03 22:20:55 +02:00
parent 13363cb568
commit c66df283d1
37 changed files with 3719 additions and 344 deletions
+35 -13
View File
@@ -5,6 +5,7 @@ import (
"log"
"net"
"net/http"
"os"
)
type ctxKey string
@@ -20,7 +21,14 @@ const uidKey ctxKey = "uid"
var torNet *net.IPNet
func init() {
cidr := envOr("KVER_TOR_NET", "10.89.0.0/16")
// LookupEnv statt envOr: envOr würde bei leer GESETZTER Variable den
// Default liefern -- dann ließe sich die Sperre nie abschalten. Nur eine
// UNgesetzte Variable fällt auf den Default zurück; KVER_TOR_NET= (leer)
// deaktiviert die Sperre.
cidr, ok := os.LookupEnv("KVER_TOR_NET")
if !ok {
cidr = "10.89.0.0/16"
}
if cidr == "" {
return
}
@@ -34,24 +42,38 @@ func init() {
// blockTOR weist Anfragen aus dem TOR-Netz ab (siehe torNet). Die volle IP wird
// hier nur für diese Zugriffsentscheidung geprüft, nicht gespeichert oder
// geloggt. Ist kein Netz konfiguriert, ist die Middleware ein No-op.
func blockTOR(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if torNet != nil {
if ip := clientIP(r.RemoteAddr); ip != nil && torNet.Contains(ip) {
writeError(w, http.StatusForbidden, "Beiträge über TOR sind derzeit nicht möglich.")
return
}
}
next.ServeHTTP(w, r)
})
var blockTOR = guard(func(_ http.ResponseWriter, r *http.Request) error {
if isTOR(r) {
return errTORBlocked
}
return nil
})
var errTORBlocked = Forbidden("tor.blocked", "Beiträge über TOR sind derzeit nicht möglich.")
// isTOR erkennt Anfragen aus dem TOR-Netz. Ausgelagert, damit die
// serverseitig gerenderten Seiten das Beitragsformular erst gar nicht anbieten,
// statt es anzuzeigen und beim Absenden abzulehnen.
func isTOR(r *http.Request) bool {
if torNet == nil {
return false
}
ip := clientIP(r.RemoteAddr)
return ip != nil && torNet.Contains(ip)
}
// requireAuth lehnt Anfragen ohne gültige Session ab und legt die uid in den Context.
var errAuthRequired = Unauth("auth.required", "Nicht angemeldet")
// requireAuth lehnt Anfragen ohne gültige Session ab und legt die uid in den
// Context. Nicht über guard, weil hier nicht nur geprüft, sondern auch der
// Request angereichert wird.
func requireAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
s, ok := getSession(r)
if !ok {
writeError(w, http.StatusUnauthorized, "Nicht angemeldet")
// Über failRequest: ohne JavaScript ist die Session-Abfrage sonst
// eine JSON-Zeile mitten im Browser.
failRequest(w, r, errAuthRequired)
return
}
ctx := context.WithValue(r.Context(), uidKey, s.UID)