check / check (push) Canceled after 0s
seal.DeriveKeys derives the session hash and block keys, the encrypted URL key and the CSRF key from the signing key, with their salts declared beside it, and returns them as one seal.Keys. internal/providers calls it once at startup and provides the value to the fx graph. The session manager, the encrypted URL generator and the CSRF protection each take their own key. session.NewManager, encurl.NewGenerator, newCSRFProtect and handlers.New return no error, as nothing in them can fail now. Each key is derived as before, with seal.DeriveKey and the same salt. The signing key itself stays the HMAC key of signed URLs and the login password. README.md says under signing_key what it protects. Model: opus-5-5
98 lines
2.9 KiB
Go
98 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/imgcache"
|
|
"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 *imgcache.Service
|
|
SessionManager *session.Manager
|
|
EncryptedURLGenerator *encurl.Generator
|
|
}
|
|
|
|
// Handlers provides HTTP request handlers.
|
|
type Handlers struct {
|
|
log *slog.Logger
|
|
hc *healthcheck.Healthcheck
|
|
config *config.Config
|
|
imgSvc *imgcache.Service
|
|
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 {
|
|
return &Handlers{
|
|
log: params.Logger.Get(),
|
|
hc: params.Healthcheck,
|
|
config: params.Config,
|
|
imgSvc: params.ImageService,
|
|
sessMgr: params.SessionManager,
|
|
encGen: params.EncryptedURLGenerator,
|
|
csrfProtect: newCSRFProtect(params.Keys.CSRFKey, params.Config.Debug),
|
|
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)
|
|
}
|