Files
stripstream-librarian/infra/migrations/0070_create_series_table.sql
Froidefond Julien ccc7f375f6 feat: table series avec UUID PK — migration complète backend + frontend
Migration DB (0070 + 0071):
- Backup automatique de book_reading_progress avant migration
- Crée table series (fusion de series_metadata) avec UUID PK
- Ajoute series_id FK à books, external_metadata_links, anilist_series_links,
  available_downloads, download_detection_results
- Supprime les colonnes TEXT legacy et la table series_metadata

Backend API + Indexer:
- Toutes les queries SQL migrées vers series_id FK + JOIN series
- Routes /series/:name → /series/:series_id (UUID)
- Nouvel endpoint GET /series/by-name/:name pour lookup par nom
- match_title_volumes() factorisé entre prowlarr.rs et download_detection.rs
- Fix scheduler.rs: settings → app_settings
- OpenAPI mis à jour avec les nouveaux endpoints

Frontend:
- Routes /libraries/[id]/series/[name] → /series/[seriesId]
- Tous les composants (Edit, Delete, MarkRead, Prowlarr, Metadata,
  ReadingStatus) utilisent seriesId
- compressVolumes() pour afficher T1→3 au lieu de T1 T2 T3
- Titre release en entier (plus de truncate) dans available downloads

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 22:51:00 +02:00

99 lines
3.9 KiB
SQL

-- =============================================================================
-- Migration 0070: Create proper `series` table with UUID PK
-- Fuses series_metadata into series. All related tables get series_id FK.
-- =============================================================================
-- 0. Safety: backup reading progress before any schema changes
CREATE TABLE IF NOT EXISTS _backup_book_reading_progress AS
SELECT * FROM book_reading_progress;
-- 1. Create the series table (fusion of series_metadata)
CREATE TABLE IF NOT EXISTS series (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
library_id UUID NOT NULL REFERENCES libraries(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description TEXT,
authors TEXT[] NOT NULL DEFAULT '{}',
publishers TEXT[] NOT NULL DEFAULT '{}',
start_year INTEGER,
total_volumes INTEGER,
status TEXT,
locked_fields JSONB NOT NULL DEFAULT '{}',
original_name TEXT,
book_author TEXT,
book_language TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (library_id, name)
);
CREATE INDEX IF NOT EXISTS idx_series_library_id ON series(library_id);
-- 2. Populate from existing data
-- From books (all series with at least one book)
INSERT INTO series (library_id, name)
SELECT DISTINCT b.library_id, COALESCE(NULLIF(b.series, ''), 'unclassified')
FROM books b
ON CONFLICT (library_id, name) DO NOTHING;
-- From series_metadata (series with metadata but possibly no books)
INSERT INTO series (library_id, name)
SELECT sm.library_id, sm.name FROM series_metadata sm
ON CONFLICT (library_id, name) DO NOTHING;
-- Merge series_metadata columns into series
UPDATE series s SET
description = sm.description,
authors = sm.authors,
publishers = sm.publishers,
start_year = sm.start_year,
total_volumes = sm.total_volumes,
status = sm.status,
locked_fields = sm.locked_fields,
original_name = sm.original_name,
created_at = sm.created_at,
updated_at = sm.updated_at
FROM series_metadata sm
WHERE sm.library_id = s.library_id AND sm.name = s.name;
-- 3. Add series_id FK to books
ALTER TABLE books ADD COLUMN IF NOT EXISTS series_id UUID REFERENCES series(id) ON DELETE SET NULL;
UPDATE books b SET series_id = s.id
FROM series s
WHERE s.library_id = b.library_id
AND s.name = COALESCE(NULLIF(b.series, ''), 'unclassified')
AND b.series_id IS NULL;
CREATE INDEX IF NOT EXISTS idx_books_series_id ON books(series_id);
-- 4. Add series_id FK to external_metadata_links
ALTER TABLE external_metadata_links ADD COLUMN IF NOT EXISTS series_id UUID REFERENCES series(id) ON DELETE CASCADE;
UPDATE external_metadata_links eml SET series_id = s.id
FROM series s
WHERE s.library_id = eml.library_id AND LOWER(s.name) = LOWER(eml.series_name)
AND eml.series_id IS NULL;
-- 5. Add series_id FK to anilist_series_links
ALTER TABLE anilist_series_links ADD COLUMN IF NOT EXISTS series_id UUID REFERENCES series(id) ON DELETE CASCADE;
UPDATE anilist_series_links asl SET series_id = s.id
FROM series s
WHERE s.library_id = asl.library_id AND LOWER(s.name) = LOWER(asl.series_name)
AND asl.series_id IS NULL;
-- 6. Add series_id FK to available_downloads
ALTER TABLE available_downloads ADD COLUMN IF NOT EXISTS series_id UUID REFERENCES series(id) ON DELETE CASCADE;
UPDATE available_downloads ad SET series_id = s.id
FROM series s
WHERE s.library_id = ad.library_id AND LOWER(s.name) = LOWER(ad.series_name)
AND ad.series_id IS NULL;
-- 7. Add series_id FK to download_detection_results
ALTER TABLE download_detection_results ADD COLUMN IF NOT EXISTS series_id UUID REFERENCES series(id) ON DELETE CASCADE;
UPDATE download_detection_results ddr SET series_id = s.id
FROM series s
WHERE s.library_id = ddr.library_id AND LOWER(s.name) = LOWER(ddr.series_name)
AND ddr.series_id IS NULL;
-- NOTE: Old TEXT columns (books.series, *.series_name) are kept for now.
-- They will be dropped in a future migration (0071) once all code is migrated.