diff --git a/LICENSE-3rdparty.csv b/LICENSE-3rdparty.csv index 54a299e..13a4b96 100644 --- a/LICENSE-3rdparty.csv +++ b/LICENSE-3rdparty.csv @@ -1,7 +1,6 @@ Origin,License,"License URL",Copyright git.sr.ht/~jamesponddotco/acopw-go,MIT,https://git.sr.ht/~jamesponddotco/acopw-go/tree/v1.0.2/LICENSE.md,2024 James Pond git.sr.ht/~jamesponddotco/credential-go,MIT,https://git.sr.ht/~jamesponddotco/credential-go/tree/v1.0.0/LICENSE.md,2024 James Pond -git.sr.ht/~jamesponddotco/xstd-go,MIT,https://git.sr.ht/~jamesponddotco/xstd-go/tree/v0.13.1/LICENSE.md,2023 James Pond github.com/RoaringBitmap/roaring/v2,Apache-2.0,https://github.com/RoaringBitmap/roaring/blob/v2.4.5/LICENSE,2014 Daniel Lemire github.com/alecthomas/chroma/v2,MIT,https://github.com/alecthomas/chroma/blob/v2.17.0/COPYING,2017 Alec Thomas github.com/aymerick/douceur,MIT,https://github.com/aymerick/douceur/blob/v0.2.0/LICENSE,2015 Aymerick JEHANNE diff --git a/go.mod b/go.mod index 0615dda..6a95ba3 100644 --- a/go.mod +++ b/go.mod @@ -9,17 +9,18 @@ go 1.24 require ( git.sr.ht/~jamesponddotco/acopw-go v1.0.2 git.sr.ht/~jamesponddotco/credential-go v1.0.0 - git.sr.ht/~jamesponddotco/xstd-go v0.13.1 github.com/alecthomas/chroma/v2 v2.17.0 github.com/blevesearch/bleve/v2 v2.5.0 github.com/json-iterator/go v1.1.12 github.com/microcosm-cc/bluemonday v1.0.27 go.cipher.host/cmdkit v1.0.2 + go.cipher.host/x v1.0.0 go.etcd.io/bbolt v1.4.0 golang.org/x/time v0.11.0 ) require ( + git.sr.ht/~jamesponddotco/xstd-go v0.13.1 // indirect github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/bits-and-blooms/bitset v1.22.0 // indirect @@ -49,5 +50,5 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mschoch/smat v0.2.0 // indirect golang.org/x/net v0.39.0 // indirect - golang.org/x/sys v0.32.0 // indirect + golang.org/x/sys v0.33.0 // indirect ) diff --git a/go.sum b/go.sum index 5ebd856..9735265 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.cipher.host/cmdkit v1.0.2 h1:M06Ms0a9tZaEjZ+YK+ASMKK+VNk9pMcdEqaQLcscn+w= go.cipher.host/cmdkit v1.0.2/go.mod h1:w9YbDUEIo/ljm7eOpnlsBRGKHJUCrQVxHCjRodIFGYs= +go.cipher.host/x v1.0.0 h1:1SoLtic2X4yGhzeivKv0Y4sd+NdvJF9eM3kV/XXttI0= +go.cipher.host/x v1.0.0/go.mod h1:x3siBGj0CadsToxdghaATU3qdmQ2NWNlAus92Fg/30c= go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= @@ -97,8 +99,8 @@ golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/app/usage.go b/internal/app/usage.go index d908bd9..900e30e 100644 --- a/internal/app/usage.go +++ b/internal/app/usage.go @@ -10,7 +10,6 @@ import ( "os" "strings" - "git.sr.ht/~jamesponddotco/xstd-go/xunsafe" "github.com/alecthomas/chroma/v2" "github.com/alecthomas/chroma/v2/formatters/html" "github.com/alecthomas/chroma/v2/lexers" @@ -18,6 +17,7 @@ import ( jsoniter "github.com/json-iterator/go" "go.cipher.host/cmdkit" "go.cipher.host/pkgdex/internal/errors" + "go.cipher.host/x/xunsafe" ) const ( diff --git a/internal/config/config_internal_test.go b/internal/config/config_internal_test.go index 2ff3d7e..d57d59c 100644 --- a/internal/config/config_internal_test.go +++ b/internal/config/config_internal_test.go @@ -9,9 +9,9 @@ import ( "testing" "time" - "git.sr.ht/~jamesponddotco/xstd-go/xtime" - "git.sr.ht/~jamesponddotco/xstd-go/xunsafe" "go.cipher.host/pkgdex/internal/testhelper" + "go.cipher.host/x/xtime" + "go.cipher.host/x/xunsafe" ) func TestConfig_Validate(t *testing.T) { diff --git a/internal/config/config_test.go b/internal/config/config_test.go index c193371..a3cd77a 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -9,9 +9,9 @@ import ( "testing" "time" - "git.sr.ht/~jamesponddotco/xstd-go/xtime" "go.cipher.host/pkgdex/internal/config" "go.cipher.host/pkgdex/internal/testhelper" + "go.cipher.host/x/xtime" ) func TestLoad(t *testing.T) { diff --git a/internal/config/server.go b/internal/config/server.go index 29f53d6..e9759aa 100644 --- a/internal/config/server.go +++ b/internal/config/server.go @@ -9,9 +9,9 @@ import ( "time" "git.sr.ht/~jamesponddotco/credential-go" - "git.sr.ht/~jamesponddotco/xstd-go/xtime" "go.cipher.host/cmdkit" "go.cipher.host/pkgdex/internal/meta" + "go.cipher.host/x/xtime" ) const ( diff --git a/internal/database/store.go b/internal/database/store.go index 80d0377..97869ae 100644 --- a/internal/database/store.go +++ b/internal/database/store.go @@ -8,8 +8,8 @@ import ( "fmt" "time" - "git.sr.ht/~jamesponddotco/xstd-go/xunsafe" "go.cipher.host/cmdkit" + "go.cipher.host/x/xunsafe" bolt "go.etcd.io/bbolt" ) diff --git a/internal/key/key.go b/internal/key/key.go index 073d874..c9c487e 100644 --- a/internal/key/key.go +++ b/internal/key/key.go @@ -11,8 +11,8 @@ import ( "strings" "git.sr.ht/~jamesponddotco/acopw-go" - "git.sr.ht/~jamesponddotco/xstd-go/xunsafe" "go.cipher.host/pkgdex/internal/meta" + "go.cipher.host/x/xunsafe" ) const ( diff --git a/internal/metric/store.go b/internal/metric/store.go index 2b76128..5eae4d9 100644 --- a/internal/metric/store.go +++ b/internal/metric/store.go @@ -9,9 +9,9 @@ import ( "fmt" "time" - "git.sr.ht/~jamesponddotco/xstd-go/xunsafe" "go.cipher.host/cmdkit" "go.cipher.host/pkgdex/internal/database" + "go.cipher.host/x/xunsafe" bolt "go.etcd.io/bbolt" ) diff --git a/internal/server/handler/downloads.go b/internal/server/handler/downloads.go index 210574f..90430bb 100644 --- a/internal/server/handler/downloads.go +++ b/internal/server/handler/downloads.go @@ -9,11 +9,11 @@ import ( "log/slog" "net/http" - "git.sr.ht/~jamesponddotco/xstd-go/xnet/xhttp" jsoniter "github.com/json-iterator/go" "go.cipher.host/pkgdex/internal/config" "go.cipher.host/pkgdex/internal/metric" "go.cipher.host/pkgdex/internal/server/model" + "go.cipher.host/x/xnet/xhttp" ) // DownloadsHandler is the HTTP handler for the /meta/downloads endpoint. @@ -33,7 +33,7 @@ func NewDownloadsHandler(logger *slog.Logger, metrics *metric.Store, packages [] } // ServeHTTP handles HTTP requests for the /meta/downloads endpoint. -func (d *DownloadsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (d *DownloadsHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { report := &model.Report{ Packages: make([]model.Package, 0, len(d.packages)), } @@ -68,12 +68,16 @@ func (d *DownloadsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { slog.Any("error", err), ) - response := xhttp.ResponseError{ - Message: "Internal server error. Failed to encode download report response.", - Code: http.StatusInternalServerError, + response := xhttp.DetailError{ + Detail: "Failed to encode download report response.", + Status: http.StatusInternalServerError, } - response.Write(r.Context(), d.logger, w) + if err = response.WriteJSON(w); err != nil { + d.logger.Error("failed to write error response", + slog.Any("error", err), + ) + } return } diff --git a/internal/server/handler/health.go b/internal/server/handler/health.go index a3cd035..c8abf1e 100644 --- a/internal/server/handler/health.go +++ b/internal/server/handler/health.go @@ -10,10 +10,10 @@ import ( "net/http" "time" - "git.sr.ht/~jamesponddotco/xstd-go/xnet/xhttp" jsoniter "github.com/json-iterator/go" "go.cipher.host/pkgdex/internal/meta" "go.cipher.host/pkgdex/internal/server/model" + "go.cipher.host/x/xnet/xhttp" ) const ( @@ -34,7 +34,7 @@ func NewHealthHandler(logger *slog.Logger) *HealthHandler { } // ServeHTTP handles HTTP requests for the /health endpoint. -func (h *HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (h *HealthHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { health := model.Health{ FinishedAt: time.Now().Unix(), CheckResults: []model.Result{ @@ -60,12 +60,16 @@ func (h *HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { slog.Any("error", err), ) - response := xhttp.ResponseError{ - Message: "Internal server error. Failed to encode health response.", - Code: http.StatusInternalServerError, + response := xhttp.DetailError{ + Detail: "Failed to encode health response.", + Status: http.StatusInternalServerError, } - response.Write(r.Context(), h.logger, w) + if err = response.WriteJSON(w); err != nil { + h.logger.Error("failed to write error response", + slog.Any("error", err), + ) + } return } diff --git a/internal/server/instance.go b/internal/server/instance.go index 0c16817..fda7d80 100644 --- a/internal/server/instance.go +++ b/internal/server/instance.go @@ -15,8 +15,6 @@ import ( "net/http/pprof" "time" - "git.sr.ht/~jamesponddotco/xstd-go/xcrypto/xtls" - "git.sr.ht/~jamesponddotco/xstd-go/xnet/xhttp/xmiddleware" "go.cipher.host/cmdkit" "go.cipher.host/pkgdex/internal/config" "go.cipher.host/pkgdex/internal/database" @@ -30,6 +28,8 @@ import ( "go.cipher.host/pkgdex/internal/version" "go.cipher.host/pkgdex/internal/wayback" "go.cipher.host/pkgdex/static" + "go.cipher.host/x/xcrypto/xtls" + "go.cipher.host/x/xnet/xhttp/xmiddleware" ) const ( @@ -95,23 +95,12 @@ func NewInstance(ctx context.Context, cfg *config.Config, db *database.Store, lo return nil, fmt.Errorf("%w: %w", ErrTLSCertificates, err) } - tlsConfig := xtls.ModernServerConfig() + tlsConfig := xtls.ModernConfig() tlsConfig.Certificates = []tls.Certificate{cert} middlewares := []func(http.Handler) http.Handler{ func(h http.Handler) http.Handler { return xmiddleware.PanicRecovery(logger, h) }, - func(h http.Handler) http.Handler { return xmiddleware.UserAgent(logger, h) }, - func(h http.Handler) http.Handler { - return xmiddleware.AcceptRequests( - []string{ - http.MethodGet, - http.MethodHead, - http.MethodOptions, - }, - logger, - h, - ) - }, + xmiddleware.UserAgent, middleware.CSP, } diff --git a/internal/server/middleware/authorization.go b/internal/server/middleware/authorization.go index 8b2ccf6..f350e23 100644 --- a/internal/server/middleware/authorization.go +++ b/internal/server/middleware/authorization.go @@ -8,14 +8,14 @@ import ( "log/slog" "net/http" - "git.sr.ht/~jamesponddotco/xstd-go/xcrypto/xsubtle" - "git.sr.ht/~jamesponddotco/xstd-go/xnet/xhttp" + "go.cipher.host/x/xcrypto/xsubtle" + "go.cipher.host/x/xnet/xhttp" ) // Authorization ensures that the request has a valid API key. func Authorization(apiKey string, logger *slog.Logger, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !xsubtle.ConstantTimeStringEqual(r.Header.Get("Authorization"), "Bearer "+apiKey) { + if !xsubtle.ConstantTimeCompareString(r.Header.Get("Authorization"), "Bearer "+apiKey) { logger.Warn("unauthorized API access attempt", slog.String("remote_addr", r.RemoteAddr), slog.String("user_agent", r.UserAgent()), @@ -23,12 +23,16 @@ func Authorization(apiKey string, logger *slog.Logger, next http.Handler) http.H slog.String("key", r.Header.Get("Authorization")), ) - response := xhttp.ResponseError{ - Code: http.StatusUnauthorized, - Message: "Access denied. Please provide a valid API key in the Authorization header.", + response := xhttp.DetailError{ + Status: http.StatusUnauthorized, + Detail: "Access denied. Please provide a valid API key in the Authorization header.", } - response.Write(r.Context(), logger, w) + if err := response.WriteJSON(w); err != nil { + logger.Error("failed to write response", + slog.Any("error", err), + ) + } return } diff --git a/internal/server/middleware/csp.go b/internal/server/middleware/csp.go index dd33ab4..00ea79a 100644 --- a/internal/server/middleware/csp.go +++ b/internal/server/middleware/csp.go @@ -6,8 +6,7 @@ package middleware import ( "net/http" - - "git.sr.ht/~jamesponddotco/xstd-go/xstrings" + "strings" ) // CSP ensures that the Content-Security-Policy header is set. @@ -37,7 +36,7 @@ func CSP(next http.Handler) http.Handler { "block-all-mixed-content", "upgrade-insecure-requests", } - csp = xstrings.JoinWithSeparator("; ", directives...) + csp = strings.Join(directives, "; ") ) w.Header().Set("Content-Security-Policy", csp) diff --git a/internal/version/manager.go b/internal/version/manager.go index b6c838a..93330c4 100644 --- a/internal/version/manager.go +++ b/internal/version/manager.go @@ -9,11 +9,11 @@ import ( "fmt" "time" - "git.sr.ht/~jamesponddotco/xstd-go/xunsafe" jsoniter "github.com/json-iterator/go" "go.cipher.host/cmdkit" "go.cipher.host/pkgdex/internal/config" "go.cipher.host/pkgdex/internal/database" + "go.cipher.host/x/xunsafe" bolt "go.etcd.io/bbolt" ) diff --git a/internal/wayback/archive.go b/internal/wayback/archive.go index d3456c4..4147694 100644 --- a/internal/wayback/archive.go +++ b/internal/wayback/archive.go @@ -44,9 +44,13 @@ func (a *Archive) Save(ctx context.Context, uri string) error { // already archived. func (a *Archive) SavePackages(ctx context.Context, cfg *config.Config) error { for _, pkg := range cfg.Packages { - uri := PackageURI("https://"+cfg.Service.BaseURL, pkg.Name) + uri, err := PackageURI("https://"+cfg.Service.BaseURL, pkg.Name) + if err != nil { + return err + } - if err := a.Save(ctx, uri); err != nil { + err = a.Save(ctx, uri) + if err != nil { return err } } diff --git a/internal/wayback/client.go b/internal/wayback/client.go index fcc46af..6b184ad 100644 --- a/internal/wayback/client.go +++ b/internal/wayback/client.go @@ -14,10 +14,9 @@ import ( "strings" "time" - "git.sr.ht/~jamesponddotco/xstd-go/xnet/xhttp" - "git.sr.ht/~jamesponddotco/xstd-go/xstrings" jsoniter "github.com/json-iterator/go" "go.cipher.host/cmdkit" + "go.cipher.host/x/xnet/xhttp" "golang.org/x/time/rate" ) @@ -51,7 +50,7 @@ type ( // New returns a new instance of Client. func NewClient(httpClient Doer) *Client { if httpClient == nil { - httpClient = xhttp.NewClient(15 * time.Second) + httpClient = xhttp.NewModernClient(15 * time.Second) } return &Client{ @@ -62,9 +61,12 @@ func NewClient(httpClient Doer) *Client { // Archive attempts to save the given URI to the Wayback Machine. func (c *Client) Archive(ctx context.Context, uri string) error { - reqURI := xstrings.JoinWithSeparator("/", archiveBaseURL, "save", uri) + reqURI, err := url.JoinPath(archiveBaseURL, "save", uri) + if err != nil { + return fmt.Errorf("%w", err) + } - if _, err := url.ParseRequestURI(reqURI); err != nil { + if _, err = url.ParseRequestURI(reqURI); err != nil { return fmt.Errorf("%w", err) } diff --git a/internal/wayback/wayback.go b/internal/wayback/wayback.go index 429a1eb..df89f06 100644 --- a/internal/wayback/wayback.go +++ b/internal/wayback/wayback.go @@ -4,9 +4,17 @@ package wayback -import "git.sr.ht/~jamesponddotco/xstd-go/xstrings" +import ( + "fmt" + "net/url" +) // PackageURI returns the URI of the given package. -func PackageURI(baseURL, pkg string) string { - return xstrings.JoinWithSeparator("/", baseURL, pkg) +func PackageURI(baseURL, pkg string) (string, error) { + uri, err := url.JoinPath(baseURL, pkg) + if err != nil { + return "", fmt.Errorf("%w", err) + } + + return uri, nil }