package store import ( "context" "database/sql" "errors" "time" ) type Snapshot struct { ID int64 RepoID int64 Label string CreatedAt time.Time } type SnapshotPackage struct { ID int64 SnapshotID int64 Name string Version string Arch string Location string Checksum string ChecksumType string Size int64 } type SnapshotStore interface { CreateSnapshot(ctx context.Context, repoID int64, label string) (int64, error) AddSnapshotPackages(ctx context.Context, snapshotID int64, pkgs []SnapshotPackage) error ListSnapshots(ctx context.Context, repoID int64) ([]Snapshot, error) GetSnapshot(ctx context.Context, id int64) (*Snapshot, error) GetSnapshotPackages(ctx context.Context, snapshotID int64) ([]SnapshotPackage, error) DeleteSnapshot(ctx context.Context, id int64) error } type SQLiteSnapshotStore struct { db *sql.DB } func NewSnapshotStore(db *sql.DB) *SQLiteSnapshotStore { return &SQLiteSnapshotStore{db: db} } func (s *SQLiteSnapshotStore) CreateSnapshot(ctx context.Context, repoID int64, label string) (int64, error) { res, err := s.db.ExecContext(ctx, `INSERT INTO snapshots (repo_id, label) VALUES (?, ?)`, repoID, label) if err != nil { return 0, err } return res.LastInsertId() } func (s *SQLiteSnapshotStore) AddSnapshotPackages(ctx context.Context, snapshotID int64, pkgs []SnapshotPackage) error { tx, err := s.db.BeginTx(ctx, nil) if err != nil { return err } defer tx.Rollback() stmt, err := tx.PrepareContext(ctx, `INSERT INTO snapshot_packages (snapshot_id, name, version, arch, location, checksum, checksum_type, size) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`) if err != nil { return err } defer stmt.Close() for _, p := range pkgs { if _, err := stmt.ExecContext(ctx, snapshotID, p.Name, p.Version, p.Arch, p.Location, p.Checksum, p.ChecksumType, p.Size); err != nil { return err } } return tx.Commit() } func (s *SQLiteSnapshotStore) ListSnapshots(ctx context.Context, repoID int64) ([]Snapshot, error) { rows, err := s.db.QueryContext(ctx, `SELECT id, repo_id, label, created_at FROM snapshots WHERE repo_id = ? ORDER BY id DESC`, repoID) if err != nil { return nil, err } defer rows.Close() var snaps []Snapshot for rows.Next() { var snap Snapshot if err := rows.Scan(&snap.ID, &snap.RepoID, &snap.Label, &snap.CreatedAt); err != nil { return nil, err } snaps = append(snaps, snap) } return snaps, rows.Err() } func (s *SQLiteSnapshotStore) GetSnapshot(ctx context.Context, id int64) (*Snapshot, error) { var snap Snapshot err := s.db.QueryRowContext(ctx, `SELECT id, repo_id, label, created_at FROM snapshots WHERE id = ?`, id). Scan(&snap.ID, &snap.RepoID, &snap.Label, &snap.CreatedAt) if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } if err != nil { return nil, err } return &snap, nil } func (s *SQLiteSnapshotStore) GetSnapshotPackages(ctx context.Context, snapshotID int64) ([]SnapshotPackage, error) { rows, err := s.db.QueryContext(ctx, `SELECT id, snapshot_id, name, version, arch, location, checksum, checksum_type, size FROM snapshot_packages WHERE snapshot_id = ?`, snapshotID) if err != nil { return nil, err } defer rows.Close() var pkgs []SnapshotPackage for rows.Next() { var p SnapshotPackage if err := rows.Scan(&p.ID, &p.SnapshotID, &p.Name, &p.Version, &p.Arch, &p.Location, &p.Checksum, &p.ChecksumType, &p.Size); err != nil { return nil, err } pkgs = append(pkgs, p) } return pkgs, rows.Err() } func (s *SQLiteSnapshotStore) DeleteSnapshot(ctx context.Context, id int64) error { res, err := s.db.ExecContext(ctx, `DELETE FROM snapshots WHERE id = ?`, id) if err != nil { return err } n, err := res.RowsAffected() if err != nil { return err } if n == 0 { return ErrNotFound } return nil }