subscription.go
⎇
Raw
1package repository
2
3import (
4 "database/sql"
5 "time"
6
7 "vidarchive/internal/models"
8)
9
10type SubscriptionRepository struct {
11 db *sql.DB
12}
13
14func NewSubscriptionRepository(db *sql.DB) *SubscriptionRepository {
15 return &SubscriptionRepository{db: db}
16}
17
18const subscriptionColumns = `id, name, url, enabled, refresh_mode, schedule_kind, cron_expr,
19 preset_id, format_override, custom_flags, output_dir, prune_removed,
20 last_run_at, next_run_at, last_status, created_at`
21
22func (r *SubscriptionRepository) Create(s *models.Subscription) error {
23 result, err := r.db.Exec(
24 `INSERT INTO subscriptions
25 (name, url, enabled, refresh_mode, schedule_kind, cron_expr, preset_id,
26 format_override, custom_flags, output_dir, prune_removed, next_run_at)
27 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
28 s.Name, s.URL, boolToInt(s.Enabled), s.RefreshMode, s.ScheduleKind, s.CronExpr,
29 s.PresetID, s.FormatOverride, s.CustomFlags, s.OutputDir, boolToInt(s.PruneRemoved),
30 s.NextRunAt,
31 )
32 if err != nil {
33 return err
34 }
35 s.ID, _ = result.LastInsertId()
36 return nil
37}
38
39func (r *SubscriptionRepository) GetByID(id int64) (*models.Subscription, error) {
40 row := r.db.QueryRow(`SELECT `+subscriptionColumns+` FROM subscriptions WHERE id = ?`, id)
41 return scanSubscription(row)
42}
43
44func (r *SubscriptionRepository) GetAll() ([]*models.Subscription, error) {
45 rows, err := r.db.Query(`SELECT ` + subscriptionColumns + ` FROM subscriptions ORDER BY created_at DESC`)
46 if err != nil {
47 return nil, err
48 }
49 defer rows.Close()
50 return scanSubscriptions(rows)
51}
52
53// GetDue returns enabled subscriptions whose next run is at or before now (or
54// has never run). The scheduler uses this to decide what to enqueue.
55func (r *SubscriptionRepository) GetDue(now time.Time) ([]*models.Subscription, error) {
56 rows, err := r.db.Query(
57 `SELECT `+subscriptionColumns+`
58 FROM subscriptions
59 WHERE enabled = 1 AND (next_run_at IS NULL OR next_run_at <= ?)
60 ORDER BY created_at ASC`,
61 now,
62 )
63 if err != nil {
64 return nil, err
65 }
66 defer rows.Close()
67 return scanSubscriptions(rows)
68}
69
70func (r *SubscriptionRepository) Update(s *models.Subscription) error {
71 _, err := r.db.Exec(
72 `UPDATE subscriptions SET name=?, url=?, enabled=?, refresh_mode=?, schedule_kind=?,
73 cron_expr=?, preset_id=?, format_override=?, custom_flags=?, output_dir=?,
74 prune_removed=?, next_run_at=?
75 WHERE id=?`,
76 s.Name, s.URL, boolToInt(s.Enabled), s.RefreshMode, s.ScheduleKind, s.CronExpr,
77 s.PresetID, s.FormatOverride, s.CustomFlags, s.OutputDir, boolToInt(s.PruneRemoved),
78 s.NextRunAt, s.ID,
79 )
80 return err
81}
82
83func (r *SubscriptionRepository) SetEnabled(id int64, enabled bool) error {
84 _, err := r.db.Exec(`UPDATE subscriptions SET enabled = ? WHERE id = ?`, boolToInt(enabled), id)
85 return err
86}
87
88// MarkRun records a run's outcome and the computed next run time.
89func (r *SubscriptionRepository) MarkRun(id int64, lastRunAt, nextRunAt time.Time, status string) error {
90 _, err := r.db.Exec(
91 `UPDATE subscriptions SET last_run_at = ?, next_run_at = ?, last_status = ? WHERE id = ?`,
92 lastRunAt, nextRunAt, status, id,
93 )
94 return err
95}
96
97func (r *SubscriptionRepository) Delete(id int64) error {
98 _, err := r.db.Exec(`DELETE FROM subscriptions WHERE id = ?`, id)
99 return err
100}
101
102func scanSubscriptions(rows *sql.Rows) ([]*models.Subscription, error) {
103 var subs []*models.Subscription
104 for rows.Next() {
105 s, err := scanSubscription(rows)
106 if err != nil {
107 return nil, err
108 }
109 subs = append(subs, s)
110 }
111 return subs, rows.Err()
112}
113
114func scanSubscription(row interface{ Scan(...interface{}) error }) (*models.Subscription, error) {
115 var s models.Subscription
116 var enabled, pruneRemoved int
117 var refreshMode, scheduleKind, cronExpr, formatOverride, customFlags, outputDir sql.NullString
118 err := row.Scan(
119 &s.ID, &s.Name, &s.URL, &enabled, &refreshMode, &scheduleKind, &cronExpr,
120 &s.PresetID, &formatOverride, &customFlags, &outputDir, &pruneRemoved,
121 &s.LastRunAt, &s.NextRunAt, &s.LastStatus, &s.CreatedAt,
122 )
123 if err != nil {
124 return nil, err
125 }
126 s.Enabled = enabled == 1
127 s.PruneRemoved = pruneRemoved == 1
128 s.RefreshMode = refreshMode.String
129 s.ScheduleKind = scheduleKind.String
130 s.CronExpr = cronExpr.String
131 s.FormatOverride = formatOverride.String
132 s.CustomFlags = customFlags.String
133 s.OutputDir = outputDir.String
134 return &s, nil
135}
136