check / check (push) Canceled after 0s
The CSRF middleware answered a refused login or generator form with the library's bare text, such as "Forbidden - origin invalid". It now answers 403 with a page that names the cause and what to do, and logs a warning with the request ID and the library's reason. A page opened over plain HTTP with debug off and a form from another host both fail the origin check; the page tells them apart by the host in the Origin or Referer header. Model: opus-5-5
100 lines
2.9 KiB
Go
100 lines
2.9 KiB
Go
// Package handlers provides HTTP request handlers.
|
|
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
|
|
"go.uber.org/fx"
|
|
"sneak.berlin/go/pixa/internal/allowlist"
|
|
"sneak.berlin/go/pixa/internal/config"
|
|
"sneak.berlin/go/pixa/internal/encurl"
|
|
"sneak.berlin/go/pixa/internal/healthcheck"
|
|
"sneak.berlin/go/pixa/internal/imageproxy"
|
|
"sneak.berlin/go/pixa/internal/logger"
|
|
"sneak.berlin/go/pixa/internal/seal"
|
|
"sneak.berlin/go/pixa/internal/session"
|
|
"sneak.berlin/go/pixa/internal/signature"
|
|
)
|
|
|
|
// Params defines dependencies for Handlers.
|
|
type Params struct {
|
|
fx.In
|
|
|
|
Logger *logger.Logger
|
|
Healthcheck *healthcheck.Healthcheck
|
|
Config *config.Config
|
|
Keys seal.Keys
|
|
|
|
ImageService *imageproxy.Proxy
|
|
SessionManager *session.Manager
|
|
EncryptedURLGenerator *encurl.Generator
|
|
}
|
|
|
|
// Handlers provides HTTP request handlers.
|
|
type Handlers struct {
|
|
log *slog.Logger
|
|
hc *healthcheck.Healthcheck
|
|
config *config.Config
|
|
imgSvc *imageproxy.Proxy
|
|
sessMgr *session.Manager
|
|
encGen *encurl.Generator
|
|
csrfProtect func(http.Handler) http.Handler
|
|
|
|
// signer checks the signature of a /v1/image/ URL with signing_key.
|
|
signer *signature.Signer
|
|
// allowlist matches the hosts of allowlist_hosts, which a /v1/image/
|
|
// URL may name without a signature.
|
|
allowlist *allowlist.HostAllowList
|
|
// refererBlocklist matches the hosts of referer_blocklist; its IsAllowed
|
|
// reports whether a URL's host is on that list.
|
|
refererBlocklist *allowlist.HostAllowList
|
|
}
|
|
|
|
// New creates a new Handlers instance.
|
|
func New(_ fx.Lifecycle, params Params) *Handlers {
|
|
log := params.Logger.Get()
|
|
|
|
return &Handlers{
|
|
log: log,
|
|
hc: params.Healthcheck,
|
|
config: params.Config,
|
|
imgSvc: params.ImageService,
|
|
sessMgr: params.SessionManager,
|
|
encGen: params.EncryptedURLGenerator,
|
|
csrfProtect: newCSRFProtect(params.Keys.CSRFKey, params.Config.Debug, log),
|
|
signer: signature.New(params.Config.SigningKey),
|
|
allowlist: allowlist.New(params.Config.AllowlistHosts),
|
|
refererBlocklist: allowlist.New(params.Config.RefererBlocklist),
|
|
}
|
|
}
|
|
|
|
// WaitForProcessing waits until no image is being processed, or until ctx
|
|
// ends, and returns how many images were still being processed then.
|
|
func (s *Handlers) WaitForProcessing(ctx context.Context) int {
|
|
return s.imgSvc.WaitForProcessing(ctx)
|
|
}
|
|
|
|
func (s *Handlers) respondJSON(w http.ResponseWriter, data any, status int) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(status)
|
|
|
|
if data != nil {
|
|
err := json.NewEncoder(w).Encode(data)
|
|
if err != nil {
|
|
s.log.Error("json encode error", "error", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Handlers) respondError(w http.ResponseWriter, message string, status int) {
|
|
s.respondJSON(w, map[string]any{
|
|
"error": message,
|
|
"status": status,
|
|
"timestamp": time.Now().UTC().Format(time.RFC3339),
|
|
}, status)
|
|
}
|