Files
sub2api-cn-relay-manager/internal/store/sqlite/packs_repo.go
phamnazage-jpg 85d495dd16 feat(control-plane): harden host-scoped reconcile and acceptance evidence
- add batch-scoped reconcile_runs persistence and queries
- route batch detail and reconcile writes through batch_id/host_id
- refresh production boards with host-scope acceptance artifacts
- include latest real-host acceptance evidence for self_service and subscription
2026-05-18 22:22:22 +08:00

203 lines
5.1 KiB
Go

package sqlite
import (
"context"
"fmt"
"strings"
)
type Pack struct {
ID int64
PackID string
Version string
Checksum string
Vendor string
TargetHost string
MinHostVersion string
MaxHostVersion string
ManifestJSON string
}
type PacksRepo struct {
db execQuerier
}
func newPacksRepo(db execQuerier) *PacksRepo {
return &PacksRepo{db: db}
}
func (r *PacksRepo) GetByID(ctx context.Context, id int64) (Pack, error) {
if id <= 0 {
return Pack{}, fmt.Errorf("id is required")
}
var pack Pack
if err := r.db.QueryRowContext(ctx, `SELECT id, pack_id, version, checksum, vendor, target_host, min_host_version, max_host_version, manifest_json FROM packs WHERE id = ?`, id).Scan(
&pack.ID,
&pack.PackID,
&pack.Version,
&pack.Checksum,
&pack.Vendor,
&pack.TargetHost,
&pack.MinHostVersion,
&pack.MaxHostVersion,
&pack.ManifestJSON,
); err != nil {
return Pack{}, err
}
return pack, nil
}
func (r *PacksRepo) GetByPackID(ctx context.Context, packID string) (Pack, error) {
packID = strings.TrimSpace(packID)
if packID == "" {
return Pack{}, fmt.Errorf("pack_id is required")
}
var pack Pack
if err := r.db.QueryRowContext(ctx, `SELECT id, pack_id, version, checksum, vendor, target_host, min_host_version, max_host_version, manifest_json FROM packs WHERE pack_id = ?`, packID).Scan(
&pack.ID,
&pack.PackID,
&pack.Version,
&pack.Checksum,
&pack.Vendor,
&pack.TargetHost,
&pack.MinHostVersion,
&pack.MaxHostVersion,
&pack.ManifestJSON,
); err != nil {
return Pack{}, err
}
return pack, nil
}
func (r *PacksRepo) Create(ctx context.Context, pack Pack) (int64, error) {
packID := strings.TrimSpace(pack.PackID)
version := strings.TrimSpace(pack.Version)
checksum := strings.TrimSpace(pack.Checksum)
manifestJSON := strings.TrimSpace(pack.ManifestJSON)
if manifestJSON == "" {
manifestJSON = "{}"
}
switch {
case packID == "":
return 0, fmt.Errorf("pack_id is required")
case version == "":
return 0, fmt.Errorf("version is required")
case checksum == "":
return 0, fmt.Errorf("checksum is required")
}
result, err := r.db.ExecContext(
ctx,
`INSERT INTO packs (pack_id, version, checksum, vendor, target_host, min_host_version, max_host_version, manifest_json)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
packID,
version,
checksum,
strings.TrimSpace(pack.Vendor),
strings.TrimSpace(pack.TargetHost),
strings.TrimSpace(pack.MinHostVersion),
strings.TrimSpace(pack.MaxHostVersion),
manifestJSON,
)
if err != nil {
return 0, fmt.Errorf("insert pack %q: %w", packID, err)
}
id, err := result.LastInsertId()
if err != nil {
return 0, fmt.Errorf("read inserted pack id for %q: %w", packID, err)
}
return id, nil
}
func (r *PacksRepo) ListAll(ctx context.Context) ([]Pack, error) {
rows, err := r.db.QueryContext(ctx, `SELECT id, pack_id, version, checksum, vendor, target_host, min_host_version, max_host_version, manifest_json FROM packs ORDER BY id`)
if err != nil {
return nil, fmt.Errorf("list packs: %w", err)
}
defer rows.Close()
var packs []Pack
for rows.Next() {
var pack Pack
if err := rows.Scan(
&pack.ID,
&pack.PackID,
&pack.Version,
&pack.Checksum,
&pack.Vendor,
&pack.TargetHost,
&pack.MinHostVersion,
&pack.MaxHostVersion,
&pack.ManifestJSON,
); err != nil {
return nil, fmt.Errorf("scan pack: %w", err)
}
packs = append(packs, pack)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate packs: %w", err)
}
return packs, nil
}
func (r *PacksRepo) Upsert(ctx context.Context, pack Pack) (int64, error) {
packID := strings.TrimSpace(pack.PackID)
version := strings.TrimSpace(pack.Version)
checksum := strings.TrimSpace(pack.Checksum)
manifestJSON := strings.TrimSpace(pack.ManifestJSON)
if manifestJSON == "" {
manifestJSON = "{}"
}
switch {
case packID == "":
return 0, fmt.Errorf("pack_id is required")
case version == "":
return 0, fmt.Errorf("version is required")
case checksum == "":
return 0, fmt.Errorf("checksum is required")
}
result, err := r.db.ExecContext(
ctx,
`INSERT INTO packs (pack_id, version, checksum, vendor, target_host, min_host_version, max_host_version, manifest_json)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(pack_id) DO UPDATE SET
version = excluded.version,
checksum = excluded.checksum,
vendor = excluded.vendor,
target_host = excluded.target_host,
min_host_version = excluded.min_host_version,
max_host_version = excluded.max_host_version,
manifest_json = excluded.manifest_json`,
packID,
version,
checksum,
strings.TrimSpace(pack.Vendor),
strings.TrimSpace(pack.TargetHost),
strings.TrimSpace(pack.MinHostVersion),
strings.TrimSpace(pack.MaxHostVersion),
manifestJSON,
)
if err != nil {
return 0, fmt.Errorf("upsert pack %q: %w", packID, err)
}
id, err := result.LastInsertId()
if err == nil && id > 0 {
return id, nil
}
persisted, getErr := r.GetByPackID(ctx, packID)
if getErr != nil {
if err != nil {
return 0, fmt.Errorf("read upserted pack %q: %w", packID, getErr)
}
return 0, getErr
}
return persisted.ID, nil
}