Phase 5 (OCI pull): internal/oci/ package with manifest GET/HEAD by tag/digest, blob GET/HEAD with repo membership check, tag listing with OCI pagination, catalog listing. Multi-segment repo names via parseOCIPath() right-split routing. DB query layer in internal/db/repository.go. Phase 6 (OCI push): blob uploads (monolithic and chunked) with uploadManager tracking in-progress BlobWriters, manifest push implementing full ARCHITECTURE.md §5 flow in a single SQLite transaction (create repo, upsert manifest, populate manifest_blobs, atomic tag move). Digest verification on both blob commit and manifest push-by-digest. Phase 8 (admin REST): /v1 endpoints for auth (login/logout/health), repository management (list/detail/delete), policy CRUD with engine reload, audit log listing with filters, GC trigger/status stubs. RequireAdmin middleware, platform-standard error format. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
81 lines
2.0 KiB
Go
81 lines
2.0 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// ErrUploadNotFound indicates the requested upload UUID does not exist.
|
|
var ErrUploadNotFound = errors.New("db: upload not found")
|
|
|
|
// UploadRow represents a row in the uploads table.
|
|
type UploadRow struct {
|
|
ID int64
|
|
UUID string
|
|
RepositoryID int64
|
|
ByteOffset int64
|
|
}
|
|
|
|
// CreateUpload inserts a new upload row and returns its ID.
|
|
func (d *DB) CreateUpload(uuid string, repoID int64) error {
|
|
_, err := d.Exec(
|
|
`INSERT INTO uploads (uuid, repository_id) VALUES (?, ?)`,
|
|
uuid, repoID,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("db: create upload: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetUpload returns the upload with the given UUID.
|
|
func (d *DB) GetUpload(uuid string) (*UploadRow, error) {
|
|
var u UploadRow
|
|
err := d.QueryRow(
|
|
`SELECT id, uuid, repository_id, byte_offset FROM uploads WHERE uuid = ?`, uuid,
|
|
).Scan(&u.ID, &u.UUID, &u.RepositoryID, &u.ByteOffset)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, ErrUploadNotFound
|
|
}
|
|
return nil, fmt.Errorf("db: get upload: %w", err)
|
|
}
|
|
return &u, nil
|
|
}
|
|
|
|
// UpdateUploadOffset sets the byte_offset for an upload.
|
|
func (d *DB) UpdateUploadOffset(uuid string, offset int64) error {
|
|
result, err := d.Exec(
|
|
`UPDATE uploads SET byte_offset = ? WHERE uuid = ?`,
|
|
offset, uuid,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("db: update upload offset: %w", err)
|
|
}
|
|
n, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("db: update upload offset rows affected: %w", err)
|
|
}
|
|
if n == 0 {
|
|
return ErrUploadNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DeleteUpload removes the upload row with the given UUID.
|
|
func (d *DB) DeleteUpload(uuid string) error {
|
|
result, err := d.Exec(`DELETE FROM uploads WHERE uuid = ?`, uuid)
|
|
if err != nil {
|
|
return fmt.Errorf("db: delete upload: %w", err)
|
|
}
|
|
n, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("db: delete upload rows affected: %w", err)
|
|
}
|
|
if n == 0 {
|
|
return ErrUploadNotFound
|
|
}
|
|
return nil
|
|
}
|