feat: statements table with positive/negative words M2M

- Trilingual positive + negative sentences, status, auto-timestamps
- statement_positive_words + statement_negative_words junction tables
- /api/statements CRUD + link/unlink for both word relations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 10:16:56 +02:00
parent 227247d51c
commit 9eac7b47fc
3 changed files with 202 additions and 4 deletions

View File

@@ -149,15 +149,36 @@ async function migrate() {
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()
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
status VARCHAR(20) NOT NULL DEFAULT 'draft'
CHECK (status IN ('draft', 'blocked', 'published')),
negative_sentence_de TEXT,
negative_sentence_en TEXT,
negative_sentence_se TEXT,
positive_sentence_de TEXT,
positive_sentence_en TEXT,
positive_sentence_se TEXT,
blocked_topic TEXT,
published_at TIMESTAMPTZ,
blocked_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`);
const stmtCols = [
"status VARCHAR(20) NOT NULL DEFAULT 'draft'",
'negative_sentence_de TEXT', 'negative_sentence_en TEXT', 'negative_sentence_se TEXT',
'positive_sentence_de TEXT', 'positive_sentence_en TEXT', 'positive_sentence_se TEXT',
'blocked_topic TEXT', 'published_at TIMESTAMPTZ', 'blocked_at TIMESTAMPTZ',
];
for (const col of stmtCols)
await query(`ALTER TABLE statements ADD COLUMN IF NOT EXISTS ${col}`).catch(() => {});
await query(`ALTER TABLE statements DROP CONSTRAINT IF EXISTS statements_status_check`).catch(() => {});
await query(`ALTER TABLE statements ADD CONSTRAINT statements_status_check CHECK (status IN ('draft', 'blocked', 'published'))`).catch(() => {});
await query(`
DROP TRIGGER IF EXISTS statements_updated_at ON statements;
CREATE TRIGGER statements_updated_at
@@ -165,6 +186,24 @@ async function migrate() {
FOR EACH ROW EXECUTE FUNCTION update_updated_at()
`);
// M2M: statements <-> words (positive)
await query(`
CREATE TABLE IF NOT EXISTS statement_positive_words (
statement_id UUID NOT NULL REFERENCES statements(id) ON DELETE CASCADE,
word_id UUID NOT NULL REFERENCES words(id) ON DELETE CASCADE,
PRIMARY KEY (statement_id, word_id)
)
`);
// M2M: statements <-> words (negative)
await query(`
CREATE TABLE IF NOT EXISTS statement_negative_words (
statement_id UUID NOT NULL REFERENCES statements(id) ON DELETE CASCADE,
word_id UUID NOT NULL REFERENCES words(id) ON DELETE CASCADE,
PRIMARY KEY (statement_id, word_id)
)
`);
// pairs
await query(`
CREATE TABLE IF NOT EXISTS pairs (