224 lines
6.5 KiB
Go
224 lines
6.5 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
|
|
"git.nakama.town/fmartingr/hako/internal/model"
|
|
"github.com/huandu/go-sqlbuilder"
|
|
)
|
|
|
|
// ArchiveStore handles database operations for archives
|
|
type ArchiveStore struct {
|
|
readDB *sql.DB
|
|
writeDB *sql.DB
|
|
}
|
|
|
|
// NewArchiveStore creates a new ArchiveStore
|
|
func NewArchiveStore(readDB, writeDB *sql.DB) *ArchiveStore {
|
|
return &ArchiveStore{
|
|
readDB: readDB,
|
|
writeDB: writeDB,
|
|
}
|
|
}
|
|
|
|
// Create creates a new archive
|
|
func (s *ArchiveStore) Create(ctx context.Context, archive *model.Archive) error {
|
|
ib := sqlbuilder.NewInsertBuilder()
|
|
ib.InsertInto("archives")
|
|
ib.Cols("id", "link_id", "user_id", "status", "title", "error_message", "created_at")
|
|
ib.Values(archive.ID, archive.LinkID, archive.UserID, string(archive.Status), archive.Title, archive.ErrorMessage, archive.CreatedAt.Format("2006-01-02 15:04:05"))
|
|
|
|
query, args := ib.Build()
|
|
_, err := s.writeDB.ExecContext(ctx, query, args...)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create archive: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetByID retrieves an archive by ID
|
|
func (s *ArchiveStore) GetByID(ctx context.Context, id string) (*model.Archive, error) {
|
|
sb := sqlbuilder.NewSelectBuilder()
|
|
sb.Select("id", "link_id", "user_id", "status", "title", "error_message", "created_at", "completed_at")
|
|
sb.From("archives")
|
|
sb.Where(sb.Equal("id", id))
|
|
|
|
query, args := sb.Build()
|
|
row := s.readDB.QueryRowContext(ctx, query, args...)
|
|
|
|
var archive model.Archive
|
|
var createdAt string
|
|
var completedAt sql.NullString
|
|
|
|
err := row.Scan(&archive.ID, &archive.LinkID, &archive.UserID, &archive.Status, &archive.Title, &archive.ErrorMessage, &createdAt, &completedAt)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, fmt.Errorf("archive not found")
|
|
}
|
|
return nil, fmt.Errorf("failed to get archive: %w", err)
|
|
}
|
|
|
|
// Parse timestamps
|
|
archive.CreatedAt, _ = parseTimestamp(createdAt)
|
|
if completedAt.Valid {
|
|
t, _ := parseTimestamp(completedAt.String)
|
|
archive.CompletedAt = &t
|
|
}
|
|
|
|
return &archive, nil
|
|
}
|
|
|
|
// ListByLinkID retrieves all archives for a link
|
|
func (s *ArchiveStore) ListByLinkID(ctx context.Context, opts ArchiveListOptions) ([]*model.Archive, error) {
|
|
opts.Defaults()
|
|
if err := opts.IsValid(); err != nil {
|
|
return nil, fmt.Errorf("invalid options: %w", err)
|
|
}
|
|
|
|
sb := sqlbuilder.NewSelectBuilder()
|
|
sb.Select("id", "link_id", "user_id", "status", "title", "error_message", "created_at", "completed_at")
|
|
sb.From("archives")
|
|
sb.Where(sb.Equal("link_id", opts.LinkID))
|
|
sb.OrderBy("created_at DESC")
|
|
|
|
query, args := sb.Build()
|
|
rows, err := s.readDB.QueryContext(ctx, query, args...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list archives: %w", err)
|
|
}
|
|
defer func() { _ = rows.Close() }()
|
|
|
|
var archives []*model.Archive
|
|
for rows.Next() {
|
|
var archive model.Archive
|
|
var createdAt string
|
|
var completedAt sql.NullString
|
|
|
|
if err := rows.Scan(&archive.ID, &archive.LinkID, &archive.UserID, &archive.Status, &archive.Title, &archive.ErrorMessage, &createdAt, &completedAt); err != nil {
|
|
return nil, fmt.Errorf("failed to scan archive: %w", err)
|
|
}
|
|
|
|
// Parse timestamps
|
|
archive.CreatedAt, _ = parseTimestamp(createdAt)
|
|
if completedAt.Valid {
|
|
t, _ := parseTimestamp(completedAt.String)
|
|
archive.CompletedAt = &t
|
|
}
|
|
|
|
archives = append(archives, &archive)
|
|
}
|
|
|
|
return archives, nil
|
|
}
|
|
|
|
// UpdateStatus updates the status of an archive
|
|
func (s *ArchiveStore) UpdateStatus(ctx context.Context, id string, status model.ArchiveStatus, errorMsg string) error {
|
|
ub := sqlbuilder.NewUpdateBuilder()
|
|
ub.Update("archives")
|
|
|
|
// Build the assignments
|
|
assignments := []string{
|
|
ub.Assign("status", string(status)),
|
|
ub.Assign("error_message", errorMsg),
|
|
}
|
|
|
|
// If status is completed, failed, or partial, set completed_at
|
|
if status == model.ArchiveStatusCompleted || status == model.ArchiveStatusFailed || status == model.ArchiveStatusPartial {
|
|
assignments = append(assignments, ub.Assign("completed_at", time.Now().Format("2006-01-02 15:04:05")))
|
|
}
|
|
|
|
ub.Set(assignments...)
|
|
ub.Where(ub.Equal("id", id))
|
|
|
|
query, args := ub.Build()
|
|
_, err := s.writeDB.ExecContext(ctx, query, args...)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update archive status: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// UpdateTitle updates the title of an archive
|
|
func (s *ArchiveStore) UpdateTitle(ctx context.Context, id string, title string) error {
|
|
ub := sqlbuilder.NewUpdateBuilder()
|
|
ub.Update("archives")
|
|
ub.Set(ub.Assign("title", title))
|
|
ub.Where(ub.Equal("id", id))
|
|
|
|
query, args := ub.Build()
|
|
_, err := s.writeDB.ExecContext(ctx, query, args...)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update archive title: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetLatestByLinkID retrieves the latest archive for a link
|
|
func (s *ArchiveStore) GetLatestByLinkID(ctx context.Context, linkID string) (*model.Archive, error) {
|
|
sb := sqlbuilder.NewSelectBuilder()
|
|
sb.Select("id", "link_id", "user_id", "status", "title", "error_message", "created_at", "completed_at")
|
|
sb.From("archives")
|
|
sb.Where(sb.Equal("link_id", linkID))
|
|
sb.OrderBy("created_at DESC")
|
|
sb.Limit(1)
|
|
|
|
query, args := sb.Build()
|
|
row := s.readDB.QueryRowContext(ctx, query, args...)
|
|
|
|
var archive model.Archive
|
|
var createdAt string
|
|
var completedAt sql.NullString
|
|
|
|
err := row.Scan(&archive.ID, &archive.LinkID, &archive.UserID, &archive.Status, &archive.Title, &archive.ErrorMessage, &createdAt, &completedAt)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil // No archive found is not an error
|
|
}
|
|
return nil, fmt.Errorf("failed to get latest archive: %w", err)
|
|
}
|
|
|
|
// Parse timestamps
|
|
archive.CreatedAt, _ = parseTimestamp(createdAt)
|
|
if completedAt.Valid {
|
|
t, _ := parseTimestamp(completedAt.String)
|
|
archive.CompletedAt = &t
|
|
}
|
|
|
|
return &archive, nil
|
|
}
|
|
|
|
// Delete deletes a single archive by ID
|
|
func (s *ArchiveStore) Delete(ctx context.Context, id string) error {
|
|
db := sqlbuilder.NewDeleteBuilder()
|
|
db.DeleteFrom("archives")
|
|
db.Where(db.Equal("id", id))
|
|
|
|
query, args := db.Build()
|
|
_, err := s.writeDB.ExecContext(ctx, query, args...)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete archive: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeleteByLinkID deletes all archives for a link
|
|
func (s *ArchiveStore) DeleteByLinkID(ctx context.Context, linkID string) error {
|
|
db := sqlbuilder.NewDeleteBuilder()
|
|
db.DeleteFrom("archives")
|
|
db.Where(db.Equal("link_id", linkID))
|
|
|
|
query, args := db.Build()
|
|
_, err := s.writeDB.ExecContext(ctx, query, args...)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete archives by link ID: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|