2026-10-13 12:35:22 +02:00
|
|
|
package config
|
|
|
|
|
|
2026-10-13 13:42:34 +02:00
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
2026-10-13 12:35:22 +02:00
|
|
|
|
|
|
|
|
const defaultHostedMaxUploadSize = "100MB"
|
|
|
|
|
|
|
|
|
|
// HostedConfig configures packages pushed to the proxy through the
|
2026-10-13 13:37:58 +02:00
|
|
|
// /api/hosted endpoints. Pushed versions are served in place of upstream
|
2026-10-13 12:35:22 +02:00
|
|
|
// versions with the same number, so writes always require a token.
|
|
|
|
|
type HostedConfig struct {
|
2026-10-13 13:37:58 +02:00
|
|
|
// Enabled registers the /api/hosted endpoints. Off by default.
|
2026-10-13 12:35:22 +02:00
|
|
|
Enabled bool `json:"enabled" yaml:"enabled"`
|
|
|
|
|
|
|
|
|
|
// Token is the bearer token required to push and delete packages.
|
|
|
|
|
// Required when Enabled. Supports ${VAR_NAME} expansion like AuthConfig
|
|
|
|
|
// fields.
|
|
|
|
|
Token string `json:"token" yaml:"token"`
|
|
|
|
|
|
|
|
|
|
// MaxUploadSize caps a single pushed archive (e.g., "100MB"). Must be > 0.
|
|
|
|
|
// Default: "100MB".
|
|
|
|
|
MaxUploadSize string `json:"max_upload_size" yaml:"max_upload_size"`
|
|
|
|
|
|
2026-10-13 13:42:34 +02:00
|
|
|
// NPMScopes are npm scopes (e.g. "@acme") served only from pushed
|
|
|
|
|
// packages. The upstream registry is never asked about them, so a package
|
|
|
|
|
// published there under the same name can't be mixed in.
|
|
|
|
|
NPMScopes []string `json:"npm_scopes" yaml:"npm_scopes"`
|
|
|
|
|
|
|
|
|
|
// ComposerVendors are Composer vendors (e.g. "acme") served only from
|
|
|
|
|
// pushed packages, like NPMScopes.
|
|
|
|
|
ComposerVendors []string `json:"composer_vendors" yaml:"composer_vendors"`
|
|
|
|
|
|
2026-10-13 12:35:22 +02:00
|
|
|
maxUploadBytes int64
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TokenExpanded returns Token with ${VAR_NAME} references expanded.
|
|
|
|
|
func (h *HostedConfig) TokenExpanded() string {
|
|
|
|
|
return expandEnv(h.Token)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MaxUploadBytes returns the parsed MaxUploadSize. Validate must run first.
|
|
|
|
|
func (h *HostedConfig) MaxUploadBytes() int64 {
|
|
|
|
|
return h.maxUploadBytes
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate checks the hosted package settings, applying the default upload
|
|
|
|
|
// size if unset.
|
|
|
|
|
func (h *HostedConfig) Validate() error {
|
|
|
|
|
if h.MaxUploadSize == "" {
|
|
|
|
|
h.MaxUploadSize = defaultHostedMaxUploadSize
|
|
|
|
|
}
|
|
|
|
|
size, err := ParseSize(h.MaxUploadSize)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("invalid hosted.max_upload_size: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if size <= 0 {
|
|
|
|
|
return fmt.Errorf("invalid hosted.max_upload_size %q: must be > 0", h.MaxUploadSize)
|
|
|
|
|
}
|
|
|
|
|
h.maxUploadBytes = size
|
|
|
|
|
|
|
|
|
|
if h.Enabled && h.TokenExpanded() == "" {
|
|
|
|
|
return fmt.Errorf("hosted.token is required when hosted.enabled is true")
|
|
|
|
|
}
|
2026-10-13 13:42:34 +02:00
|
|
|
for _, scope := range h.NPMScopes {
|
|
|
|
|
if len(scope) < 2 || scope[0] != '@' || strings.Contains(scope, "/") {
|
|
|
|
|
return fmt.Errorf("invalid hosted.npm_scopes entry %q: must be a scope such as \"@acme\"", scope)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, vendor := range h.ComposerVendors {
|
|
|
|
|
if vendor == "" || strings.Contains(vendor, "/") {
|
|
|
|
|
return fmt.Errorf("invalid hosted.composer_vendors entry %q: must be a vendor such as \"acme\"", vendor)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-13 12:35:22 +02:00
|
|
|
return nil
|
|
|
|
|
}
|