feat: pairs table with questions/statements placeholders

- pairs: status, answer_type enum (yes_no/text/word), difficulty_level,
  FK to questions + 2x statements (positive/negative), auto-timestamps
- questions + statements placeholder tables for future use
- Safe ALTER TABLE migration for existing pairs placeholder
- /api/pairs CRUD route, answer_type required on create

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 10:00:35 +02:00
parent dac991c861
commit 30d180a3de
4 changed files with 213 additions and 5 deletions

View File

@@ -116,15 +116,84 @@ async function migrate() {
)
`);
// pairs placeholder (Felder folgen später)
// questions placeholder (Felder folgen später)
await query(`
CREATE TABLE IF NOT EXISTS pairs (
CREATE TABLE IF NOT EXISTS questions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`);
await query(`
DROP TRIGGER IF EXISTS questions_updated_at ON questions;
CREATE TRIGGER questions_updated_at
BEFORE UPDATE ON questions
FOR EACH ROW EXECUTE FUNCTION update_updated_at()
`);
// statements placeholder (Felder folgen später)
await query(`
CREATE TABLE IF NOT EXISTS statements (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`);
await query(`
DROP TRIGGER IF EXISTS statements_updated_at ON statements;
CREATE TRIGGER statements_updated_at
BEFORE UPDATE ON statements
FOR EACH ROW EXECUTE FUNCTION update_updated_at()
`);
// pairs
await query(`
CREATE TABLE IF NOT EXISTS pairs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
status VARCHAR(20) NOT NULL DEFAULT 'draft'
CHECK (status IN ('draft', 'blocked', 'published')),
difficulty_level SMALLINT CHECK (difficulty_level BETWEEN 1 AND 50),
answer_type VARCHAR(20) NOT NULL
CHECK (answer_type IN ('yes_no', 'text', 'word')),
blocked_topic TEXT,
question_id UUID REFERENCES questions(id) ON DELETE SET NULL,
positive_statement_id UUID REFERENCES statements(id) ON DELETE SET NULL,
negative_statement_id UUID REFERENCES statements(id) ON DELETE SET NULL,
published_at TIMESTAMPTZ,
blocked_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`);
// Spalten nachrüsten falls Platzhalter-Tabelle bereits existiert
const pairCols = [
"status VARCHAR(20) NOT NULL DEFAULT 'draft'",
'difficulty_level SMALLINT',
"answer_type VARCHAR(20)",
'blocked_topic TEXT',
'question_id UUID',
'positive_statement_id UUID',
'negative_statement_id UUID',
'published_at TIMESTAMPTZ',
'blocked_at TIMESTAMPTZ',
];
for (const col of pairCols) {
const name = col.split(' ')[0];
await query(`ALTER TABLE pairs ADD COLUMN IF NOT EXISTS ${col}`).catch(() => {});
}
await query(`ALTER TABLE pairs DROP CONSTRAINT IF EXISTS pairs_status_check`).catch(() => {});
await query(`ALTER TABLE pairs ADD CONSTRAINT pairs_status_check CHECK (status IN ('draft', 'blocked', 'published'))`).catch(() => {});
await query(`ALTER TABLE pairs DROP CONSTRAINT IF EXISTS pairs_answer_type_check`).catch(() => {});
await query(`ALTER TABLE pairs ADD CONSTRAINT pairs_answer_type_check CHECK (answer_type IN ('yes_no', 'text', 'word'))`).catch(() => {});
await query(`ALTER TABLE pairs DROP CONSTRAINT IF EXISTS pairs_difficulty_level_check`).catch(() => {});
await query(`ALTER TABLE pairs ADD CONSTRAINT pairs_difficulty_level_check CHECK (difficulty_level BETWEEN 1 AND 50)`).catch(() => {});
await query(`ALTER TABLE pairs ADD CONSTRAINT IF NOT EXISTS pairs_question_id_fkey FOREIGN KEY (question_id) REFERENCES questions(id) ON DELETE SET NULL`).catch(() => {});
await query(`ALTER TABLE pairs ADD CONSTRAINT IF NOT EXISTS pairs_positive_statement_id_fkey FOREIGN KEY (positive_statement_id) REFERENCES statements(id) ON DELETE SET NULL`).catch(() => {});
await query(`ALTER TABLE pairs ADD CONSTRAINT IF NOT EXISTS pairs_negative_statement_id_fkey FOREIGN KEY (negative_statement_id) REFERENCES statements(id) ON DELETE SET NULL`).catch(() => {});
await query(`
DROP TRIGGER IF EXISTS pairs_updated_at ON pairs;
CREATE TRIGGER pairs_updated_at