feat: categories table with full CRUD

- Fills out placeholder categories table with all fields
- Trilingual titles, status enum, difficulty level, auto-timestamps
- ALTER TABLE IF NOT EXISTS for safe migration on existing table
- /api/categories CRUD route, word_ids included in responses

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 14:10:22 +02:00
parent 8751d7ceae
commit 8bd4240ea9
4 changed files with 164 additions and 10 deletions

View File

@@ -68,15 +68,38 @@ async function migrate() {
)
`);
// categories (Platzhalter — Felder folgen im nächsten Schritt)
await query(`
CREATE TABLE IF NOT EXISTS categories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
titel_de TEXT,
titel_en TEXT,
titel_se TEXT,
status VARCHAR(20) NOT NULL DEFAULT 'requested'
CHECK (status IN ('requested', 'blocked', 'published')),
difficulty_level SMALLINT CHECK (difficulty_level BETWEEN 1 AND 50),
requested_at TIMESTAMPTZ,
published_at TIMESTAMPTZ,
blocked_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`);
// Felder nachrüsten falls Tabelle schon als Platzhalter existiert
const catCols = ['titel_de TEXT', 'titel_en TEXT', 'titel_se TEXT',
"status VARCHAR(20) NOT NULL DEFAULT 'requested'",
'difficulty_level SMALLINT', 'requested_at TIMESTAMPTZ',
'published_at TIMESTAMPTZ', 'blocked_at TIMESTAMPTZ'];
for (const col of catCols) {
const name = col.split(' ')[0];
await query(`ALTER TABLE categories ADD COLUMN IF NOT EXISTS ${col}`).catch(() => {});
// CHECK constraint nur wenn noch nicht vorhanden
if (name === 'status') {
await query(`ALTER TABLE categories DROP CONSTRAINT IF EXISTS categories_status_check`).catch(() => {});
await query(`ALTER TABLE categories ADD CONSTRAINT categories_status_check CHECK (status IN ('requested', 'blocked', 'published'))`).catch(() => {});
}
}
await query(`
DROP TRIGGER IF EXISTS categories_updated_at ON categories;
CREATE TRIGGER categories_updated_at