Add languages, user_names, users_public tables and routes; fix _se→_sv rename
- Fix broken rename migration array (sed had corrupted from values to _sv) - Add languages table with status lifecycle and trilingual titles - Add user_names table with unique lowercase index - Add users_public table linking to user_names and languages (native/target) - Wire all three new routes under /api/languages, /api/user-names, /api/users-public Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,18 @@
|
||||
const { query } = require('./db');
|
||||
|
||||
async function migrate() {
|
||||
// Rename _se → _sv (Swedish ISO 639-1 correction)
|
||||
const renames = [
|
||||
['words', 'titel_se', 'titel_sv'],
|
||||
['categories', 'titel_se', 'titel_sv'],
|
||||
['questions', 'sentence_se', 'sentence_sv'],
|
||||
['statements', 'negative_sentence_se', 'negative_sentence_sv'],
|
||||
['statements', 'positive_sentence_se', 'positive_sentence_sv'],
|
||||
];
|
||||
for (const [table, from, to] of renames) {
|
||||
await query(`ALTER TABLE ${table} RENAME COLUMN ${from} TO ${to}`).catch(() => {});
|
||||
}
|
||||
|
||||
await query(`
|
||||
CREATE TABLE IF NOT EXISTS pictures (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
@@ -40,7 +52,7 @@ async function migrate() {
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
titel_de TEXT,
|
||||
titel_en TEXT,
|
||||
titel_se TEXT,
|
||||
titel_sv TEXT,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'requested'
|
||||
CHECK (status IN ('requested', 'translated', 'generated', 'blocked', 'published')),
|
||||
difficulty_level SMALLINT CHECK (difficulty_level BETWEEN 1 AND 50),
|
||||
@@ -73,7 +85,7 @@ async function migrate() {
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
titel_de TEXT,
|
||||
titel_en TEXT,
|
||||
titel_se TEXT,
|
||||
titel_sv TEXT,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'requested'
|
||||
CHECK (status IN ('requested', 'blocked', 'published')),
|
||||
difficulty_level SMALLINT CHECK (difficulty_level BETWEEN 1 AND 50),
|
||||
@@ -86,7 +98,7 @@ async function migrate() {
|
||||
`);
|
||||
|
||||
// Felder nachrüsten falls Tabelle schon als Platzhalter existiert
|
||||
const catCols = ['titel_de TEXT', 'titel_en TEXT', 'titel_se TEXT',
|
||||
const catCols = ['titel_de TEXT', 'titel_en TEXT', 'titel_sv TEXT',
|
||||
"status VARCHAR(20) NOT NULL DEFAULT 'requested'",
|
||||
'difficulty_level SMALLINT', 'requested_at TIMESTAMPTZ',
|
||||
'published_at TIMESTAMPTZ', 'blocked_at TIMESTAMPTZ'];
|
||||
@@ -123,7 +135,7 @@ async function migrate() {
|
||||
CHECK (status IN ('draft', 'blocked', 'published')),
|
||||
sentence_de TEXT,
|
||||
sentence_en TEXT,
|
||||
sentence_se TEXT,
|
||||
sentence_sv TEXT,
|
||||
blocked_topic TEXT,
|
||||
published_at TIMESTAMPTZ,
|
||||
blocked_at TIMESTAMPTZ,
|
||||
@@ -134,7 +146,7 @@ async function migrate() {
|
||||
|
||||
const questionCols = [
|
||||
"status VARCHAR(20) NOT NULL DEFAULT 'draft'",
|
||||
'sentence_de TEXT', 'sentence_en TEXT', 'sentence_se TEXT',
|
||||
'sentence_de TEXT', 'sentence_en TEXT', 'sentence_sv TEXT',
|
||||
'blocked_topic TEXT', 'published_at TIMESTAMPTZ', 'blocked_at TIMESTAMPTZ',
|
||||
];
|
||||
for (const col of questionCols)
|
||||
@@ -156,10 +168,10 @@ async function migrate() {
|
||||
CHECK (status IN ('draft', 'blocked', 'published')),
|
||||
negative_sentence_de TEXT,
|
||||
negative_sentence_en TEXT,
|
||||
negative_sentence_se TEXT,
|
||||
negative_sentence_sv TEXT,
|
||||
positive_sentence_de TEXT,
|
||||
positive_sentence_en TEXT,
|
||||
positive_sentence_se TEXT,
|
||||
positive_sentence_sv TEXT,
|
||||
blocked_topic TEXT,
|
||||
published_at TIMESTAMPTZ,
|
||||
blocked_at TIMESTAMPTZ,
|
||||
@@ -170,8 +182,8 @@ async function migrate() {
|
||||
|
||||
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',
|
||||
'negative_sentence_de TEXT', 'negative_sentence_en TEXT', 'negative_sentence_sv TEXT',
|
||||
'positive_sentence_de TEXT', 'positive_sentence_en TEXT', 'positive_sentence_sv TEXT',
|
||||
'blocked_topic TEXT', 'published_at TIMESTAMPTZ', 'blocked_at TIMESTAMPTZ',
|
||||
];
|
||||
for (const col of stmtCols)
|
||||
@@ -358,6 +370,62 @@ async function migrate() {
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at()
|
||||
`);
|
||||
|
||||
// languages
|
||||
await query(`
|
||||
CREATE TABLE IF NOT EXISTS languages (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
titel_en TEXT,
|
||||
titel_de TEXT,
|
||||
titel_sv TEXT,
|
||||
short_en VARCHAR(10),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'draft'
|
||||
CHECK (status IN ('draft', 'blocked', 'published')),
|
||||
published_at TIMESTAMPTZ,
|
||||
blocked_at TIMESTAMPTZ,
|
||||
blocked_topic TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
|
||||
await query(`
|
||||
DROP TRIGGER IF EXISTS languages_updated_at ON languages;
|
||||
CREATE TRIGGER languages_updated_at
|
||||
BEFORE UPDATE ON languages
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at()
|
||||
`);
|
||||
|
||||
// user_names
|
||||
await query(`
|
||||
CREATE TABLE IF NOT EXISTS user_names (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
username_lowercase TEXT NOT NULL UNIQUE,
|
||||
username TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
|
||||
await query(`CREATE UNIQUE INDEX IF NOT EXISTS user_names_lowercase_idx ON user_names (username_lowercase)`);
|
||||
|
||||
// users_public
|
||||
await query(`
|
||||
CREATE TABLE IF NOT EXISTS users_public (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
username_id UUID REFERENCES user_names(id) ON DELETE SET NULL,
|
||||
language_native_id UUID REFERENCES languages(id) ON DELETE SET NULL,
|
||||
language_target_id UUID REFERENCES languages(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
|
||||
await query(`
|
||||
DROP TRIGGER IF EXISTS users_public_updated_at ON users_public;
|
||||
CREATE TRIGGER users_public_updated_at
|
||||
BEFORE UPDATE ON users_public
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at()
|
||||
`);
|
||||
|
||||
console.log('Migration complete');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user