check / check (push) Canceled after 0s
The new package internal/imageproxy holds Proxy, which was imgcache.Service, with its Config, ImageResponse, CacheStatus and errors. NewProxy makes and registers the counters of upstream fetches and transforms. internal/imgcache holds the cache only, with its hit and miss counters, and imgcache.CacheConfig is imgcache.Config. The cache reads and writes each kind it keeps with one pair of methods: Variant and PutVariant, Source and PutSource, Failure and PutFailure. Source replaces LookupSource followed by GetSourceContent. imgcache.go and module.go are gone. The proxy's tests moved with it. The cache's tests check that the reconciliation pass removes an empty or cut file and its row. The proxy's tests remove them by hand, then check that Get fetches or converts again. 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/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 {
|
|
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)
|
|
}
|