package downloads import ( "context" "database/sql" "errors" "fmt" "github.com/google/uuid" ) type Repository struct { db *sql.DB } func NewRepository(db *sql.DB) *Repository { return &Repository{db: db} } var ErrNotFound = errors.New("download job not found") func (r *Repository) Create(ctx context.Context, galleryID uuid.UUID, visitorID string) (Job, error) { job := Job{ID: uuid.New(), GalleryID: galleryID, VisitorID: visitorID, Status: StatusQueued} _, err := r.db.ExecContext(ctx, ` INSERT INTO download_jobs (id, gallery_id, visitor_id, status) VALUES ($1, $2, $3, $4) `, job.ID, job.GalleryID, job.VisitorID, job.Status) if err != nil { return Job{}, fmt.Errorf("create download job: %w", err) } return r.GetForVisitor(ctx, job.ID, galleryID, visitorID) } func (r *Repository) GetForVisitor(ctx context.Context, jobID, galleryID uuid.UUID, visitorID string) (Job, error) { return r.get(ctx, ` WHERE id = $1 AND gallery_id = $2 AND visitor_id = $3 `, jobID, galleryID, visitorID) } func (r *Repository) Get(ctx context.Context, jobID uuid.UUID) (Job, error) { return r.get(ctx, `WHERE id = $1`, jobID) } func (r *Repository) get(ctx context.Context, predicate string, args ...any) (Job, error) { var ( job Job storageKey, jobError, createdAt, updatedAt sql.NullString completedAt sql.NullString ) err := r.db.QueryRowContext(ctx, ` SELECT id, gallery_id, visitor_id, status, storage_key, error, created_at, updated_at, completed_at FROM download_jobs `+predicate+` `, args...).Scan( &job.ID, &job.GalleryID, &job.VisitorID, &job.Status, &storageKey, &jobError, &createdAt, &updatedAt, &completedAt, ) if errors.Is(err, sql.ErrNoRows) { return Job{}, ErrNotFound } if err != nil { return Job{}, fmt.Errorf("find download job: %w", err) } job.StorageKey = storageKey.String job.Error = jobError.String job.CreatedAt = createdAt.String job.UpdatedAt = updatedAt.String job.CompletedAt = completedAt.String return job, nil } func (r *Repository) MarkProcessing(ctx context.Context, jobID uuid.UUID) error { _, err := r.db.ExecContext(ctx, ` UPDATE download_jobs SET status = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2 `, StatusProcessing, jobID) return err } func (r *Repository) MarkReady(ctx context.Context, jobID uuid.UUID, storageKey string) error { _, err := r.db.ExecContext(ctx, ` UPDATE download_jobs SET status = $1, storage_key = $2, error = NULL, updated_at = CURRENT_TIMESTAMP, completed_at = CURRENT_TIMESTAMP WHERE id = $3 `, StatusReady, storageKey, jobID) return err } func (r *Repository) MarkFailed(ctx context.Context, jobID uuid.UUID, message string) error { _, err := r.db.ExecContext(ctx, ` UPDATE download_jobs SET status = $1, error = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $3 `, StatusFailed, message, jobID) return err }