3a6721c2a7
ci/woodpecker/tag/docker Pipeline was successful
## Summary Move package-type-specific local repo logic out of centralized handlers into provider packages via optional Go interfaces. **New interfaces in `provider` package:** - \`LocalUploader\`: \`ValidateUpload(filePath) → (storagePath, contentType, error)\` + \`UploadResponse(...)\` - \`LocalIndexer\`: \`ServeLocalIndex(w, r, files, repoName, path) → bool\` + \`GenerateLocalIndex(ctx, files, repoName, path) → ([]byte, error)\` - \`FileStore\`: \`ListFilesByPrefix\` + \`ListPackages\` (implemented by database.DB) **Providers implement these interfaces:** - PyPI: upload validation (wheel/sdist naming), simple index serving + generation - Terraform: upload validation (provider zip naming), mirror protocol serving **Handlers simplified to generic dispatch:** - \`local.go\`: type-asserts to \`LocalUploader\`, falls back to generic upload - \`proxy.go\`: type-asserts to \`LocalIndexer\`, falls back to raw file serving - \`engine.go\`: type-asserts to \`LocalIndexer\` for local virtual members Adding a new local repo type (e.g. RPM) = implement the interfaces in its provider package. Zero handler changes. ## Test plan - [x] Build + unit tests pass - [x] E2E: PyPI local upload → simple index → uv pip install (smoke test after refactor) Reviewed-on: #52 Co-authored-by: Ben Vincent <ben@unkin.net> Co-committed-by: Ben Vincent <ben@unkin.net>
73 lines
1.7 KiB
Go
73 lines
1.7 KiB
Go
package provider
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"git.unkin.net/unkin/artifactapi/pkg/models"
|
|
)
|
|
|
|
type Mutability int
|
|
|
|
const (
|
|
Immutable Mutability = iota
|
|
Mutable
|
|
)
|
|
|
|
type Provider interface {
|
|
Type() models.PackageType
|
|
Classify(path string) Mutability
|
|
ContentType(path string) string
|
|
UpstreamURL(remote models.Remote, path string) string
|
|
RewriteResponse(body []byte, remote models.Remote, proxyBaseURL string) ([]byte, error)
|
|
AuthHeaders(ctx context.Context, remote models.Remote) (http.Header, error)
|
|
}
|
|
|
|
type FileEntry struct {
|
|
FilePath string
|
|
ContentHash string
|
|
}
|
|
|
|
type FileStore interface {
|
|
ListFilesByPrefix(ctx context.Context, repoName, prefix string) ([]FileEntry, error)
|
|
ListPackages(ctx context.Context, repoName string) ([]string, error)
|
|
}
|
|
|
|
type LocalUploader interface {
|
|
ValidateUpload(filePath string) (storagePath, contentType string, err error)
|
|
UploadResponse(storagePath, contentHash string, sizeBytes int64) map[string]any
|
|
}
|
|
|
|
type LocalIndexer interface {
|
|
ServeLocalIndex(w http.ResponseWriter, r *http.Request, files FileStore, repoName, path string) bool
|
|
GenerateLocalIndex(ctx context.Context, files FileStore, repoName, path string) ([]byte, error)
|
|
}
|
|
|
|
type IndexMerger interface {
|
|
MergeIndexes(members []MemberIndex, proxyBaseURL string) ([]byte, error)
|
|
}
|
|
|
|
type MemberIndex struct {
|
|
RemoteName string
|
|
Body []byte
|
|
}
|
|
|
|
var registry = map[models.PackageType]Provider{}
|
|
|
|
func Register(p Provider) {
|
|
registry[p.Type()] = p
|
|
}
|
|
|
|
func Get(t models.PackageType) (Provider, error) {
|
|
p, ok := registry[t]
|
|
if !ok {
|
|
return nil, fmt.Errorf("no provider registered for package type %q", t)
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
func All() map[models.PackageType]Provider {
|
|
return registry
|
|
}
|