fix: address code review findings

- Preserve MBID and last_synced across periodic artist syncs (INSERT OR
  REPLACE was wiping them, forcing MusicBrainz re-resolution every cycle)
- Exclude unmonitored artists from unnotified-release digest query
- Require telegram.cron_schedule when telegram.enabled is true
- Add same-origin CSRF defense to state-changing web POST routes
- Skip WAL/busy_timeout pragmas for :memory: databases (no-op there)
- Scan mbid as sql.NullString in GetAllArtistSettings to tolerate NULLs
This commit is contained in:
2026-07-19 23:51:15 +03:00
parent 7cdb473d9c
commit ce1c39e14b
7 changed files with 90 additions and 18 deletions

View File

@@ -52,11 +52,24 @@ func TouchArtistSynced(db DBer, artistID string, syncedAt time.Time) error {
return nil
}
// SaveArtistSettings inserts or replaces an artist_settings row.
// SaveArtistSettings inserts or updates an artist_settings row. Columns not
// present in the struct's intended set are preserved on conflict rather than
// reset to their zero value: mbid and last_synced are carried over from the
// existing row when the caller does not supply new values. This protects the
// MusicBrainz-resolution cache and the sync TTL markers from being wiped on
// every periodic artist sync.
func SaveArtistSettings(db *DB, settings *ArtistSettings) error {
_, err := db.Conn().Exec(
"INSERT OR REPLACE INTO artist_settings (id, name, mbid, ignore_singles, ignore_compilations, monitored) VALUES (?, ?, ?, ?, ?, ?)",
settings.ID, settings.Name, settings.MBID, settings.IgnoreSingles, settings.IgnoreCompilations, settings.Monitored,
_, err := db.Conn().Exec(`
INSERT INTO artist_settings (id, name, mbid, ignore_singles, ignore_compilations, monitored, last_synced)
VALUES (?, ?, ?, ?, ?, ?, (SELECT last_synced FROM artist_settings WHERE id = ?))
ON CONFLICT(id) DO UPDATE SET
name = excluded.name,
mbid = COALESCE(excluded.mbid, artist_settings.mbid),
ignore_singles = excluded.ignore_singles,
ignore_compilations = excluded.ignore_compilations,
monitored = excluded.monitored
`,
settings.ID, settings.Name, nullIfEmpty(settings.MBID), settings.IgnoreSingles, settings.IgnoreCompilations, settings.Monitored, settings.ID,
)
if err != nil {
return fmt.Errorf("save artist settings: %w", err)
@@ -64,6 +77,15 @@ func SaveArtistSettings(db *DB, settings *ArtistSettings) error {
return nil
}
// nullIfEmpty returns nil for an empty string so COALESCE-preserving columns
// (e.g. mbid) keep their existing value when the caller supplies no new one.
func nullIfEmpty(s string) interface{} {
if s == "" {
return nil
}
return s
}
// GetAllArtistSettings returns all rows from artist_settings.
func GetAllArtistSettings(db *DB) ([]ArtistSettings, error) {
rows, err := db.Conn().Query(
@@ -77,9 +99,11 @@ func GetAllArtistSettings(db *DB) ([]ArtistSettings, error) {
var results []ArtistSettings
for rows.Next() {
var s ArtistSettings
if err := rows.Scan(&s.ID, &s.Name, &s.MBID, &s.IgnoreSingles, &s.IgnoreCompilations, &s.Monitored); err != nil {
var mbid sql.NullString
if err := rows.Scan(&s.ID, &s.Name, &mbid, &s.IgnoreSingles, &s.IgnoreCompilations, &s.Monitored); err != nil {
return nil, fmt.Errorf("scan artist settings: %w", err)
}
s.MBID = mbid.String
results = append(results, s)
}
if err := rows.Err(); err != nil {