feat: add Web UI artist detail, archive, and ignore actions

Implements Task 8: artist detail page (local albums + found-missing
with ignore buttons), ignored-releases archive with restore, and POST
handlers toggling ignore flags and ignore_singles. Adds ErrArtistNotFound
sentinel so callers can distinguish missing artists, and wires routes via
the enhanced ServeMux path wildcard.
This commit is contained in:
2026-07-19 22:43:47 +03:00
parent cea20957e7
commit 44f3b0a2a7
11 changed files with 682 additions and 13 deletions

View File

@@ -2,6 +2,7 @@ package database
import (
"database/sql"
"errors"
"fmt"
)
@@ -17,6 +18,9 @@ func GetArtistSettings(db *DB, id string) (*ArtistSettings, error) {
id,
).Scan(&s.ID, &s.Name, &mbid, &s.IgnoreSingles, &s.IgnoreCompilations, &s.Monitored)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrArtistNotFound
}
return nil, err
}
s.MBID = mbid.String

View File

@@ -44,7 +44,7 @@ func TestGetArtistSettings_Found(t *testing.T) {
}
}
// TestGetArtistSettings_NotFound verifies that a missing artist returns sql.ErrNoRows.
// TestGetArtistSettings_NotFound verifies that a missing artist returns ErrArtistNotFound.
func TestGetArtistSettings_NotFound(t *testing.T) {
db, err := New(":memory:")
if err != nil {
@@ -53,8 +53,8 @@ func TestGetArtistSettings_NotFound(t *testing.T) {
defer db.Close()
_, err = GetArtistSettings(db, "nonexistent")
if err != sql.ErrNoRows {
t.Errorf("expected sql.ErrNoRows, got %v", err)
if err != ErrArtistNotFound {
t.Errorf("expected ErrArtistNotFound, got %v", err)
}
}

View File

@@ -2,6 +2,7 @@ package database
import (
"database/sql"
"errors"
"fmt"
"time"
@@ -13,6 +14,11 @@ type DB struct {
conn *sql.DB
}
// ErrArtistNotFound is returned by artist lookups when no row matches the given
// ID. It is a sentinel so callers (e.g. the web UI) can distinguish "missing"
// from other errors.
var ErrArtistNotFound = errors.New("artist not found")
// New opens a SQLite database at dbPath and runs schema migrations.
func New(dbPath string) (*DB, error) {
// The _foreign_keys=on DSN parameter enables foreign key enforcement on