Watch
1
0
Fork
You've already forked pkg-proxy
1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-11-11 08:39:17 -05:00
pkg-proxy/internal/database/local_artifacts.go
simon chrzanowski e7449d1540
Read the package manifest of pushed archives
Resolvers take a version's dependencies from the registry metadata, not from the archive, so listing a pushed version needs its manifest. A push now reads package.json from the top directory of an npm tarball, or composer.json from the root or the single top directory of a Composer zip, and keeps it in the new artifacts.manifest column (migration 013_add_artifact_manifest).

The manifest has to name the pushed package and version. A Composer manifest may leave the version out, and its name is compared case-insensitively. An archive that can't be read or whose manifest names something else is refused with 400 and removed from storage.

The version's integrity is now the archive's sha512, which npm clients expect in dist.integrity.

Refs #134
2026-10-14 13:23:19 +02:00

70 lines
2.2 KiB
Go

package database
import (
"database/sql"
"fmt"
"time"
)
// LocalArtifact is a pushed artifact with the integrity of its version.
type LocalArtifact struct {
Artifact
Integrity sql.NullString `db:"integrity"`
}
// GetLocalArtifacts returns the artifacts pushed to the proxy for a package,
// ordered by version and filename.
func (db *DB) GetLocalArtifacts(packagePURL string) ([]LocalArtifact, error) {
var artifacts []LocalArtifact
query := db.Rebind(`
SELECT a.id, a.version_purl, a.filename, a.upstream_url, a.storage_path, a.content_hash,
a.size, a.content_type, a.fetched_at, a.hit_count, a.last_accessed_at,
a.created_at, a.updated_at, a.source, a.manifest, v.integrity
FROM artifacts a
JOIN versions v ON v.purl = a.version_purl
WHERE v.package_purl = ? AND a.source = 'local'
ORDER BY a.version_purl, a.filename
`)
if err := db.Select(&artifacts, query, packagePURL); err != nil {
return nil, err
}
return artifacts, nil
}
// DeleteLocalArtifacts removes the pushed artifacts of a version and queues
// their stored objects for deletion in the same transaction, so a failed
// queue keeps the records instead of orphaning the objects. Artifacts cached
// from an upstream are left alone. It returns how many records it removed.
func (db *DB) DeleteLocalArtifacts(versionPURL string) (int, error) {
tx, err := db.Beginx()
if err != nil {
return 0, err
}
defer func() { _ = tx.Rollback() }()
var paths []string
selectQuery := db.Rebind(`
SELECT storage_path FROM artifacts
WHERE version_purl = ? AND source = 'local' AND storage_path IS NOT NULL
`)
if err := tx.Select(&paths, selectQuery, versionPURL); err != nil {
return 0, fmt.Errorf("reading local artifacts: %w", err)
}
res, err := tx.Exec(db.Rebind(`DELETE FROM artifacts WHERE version_purl = ? AND source = 'local'`), versionPURL)
if err != nil {
return 0, fmt.Errorf("deleting local artifacts: %w", err)
}
deleted, err := res.RowsAffected()
if err != nil {
return 0, err
}
queuedAt := time.Now().UTC()
for _, path := range paths {
if _, err := tx.Exec(db.Rebind(queuePendingDeleteQuery), path, queuedAt); err != nil {
return 0, fmt.Errorf("queueing %s for deletion: %w", path, err)
}
}
return int(deleted), tx.Commit()
}