Files
Art-gallery/server/migrate.js
Danila KhodjaefandCursor 0466b77328 Add curator roles/permissions with Users admin, and fix lineage branch joins.
Staff accounts use admin/curator roles and fine-grained flags; transitions connect source-to-target with color gradients and stream cutout masks so overlaps stay seamless.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 18:04:17 +03:00

92 lines
2.5 KiB
JavaScript

require('dotenv').config();
const fs = require('fs');
const path = require('path');
const pool = require('./db');
const INCREMENTAL_MIGRATIONS = [
'migrate-checkup-flags.sql',
'migrate-artist-checkup-flags.sql',
'migrate-influence-sources.sql',
'migrate-painting-annotations.sql',
'migrate-artist-palette.sql',
'migrate-auth.sql',
'migrate-portrait-thumbs.sql',
'migrate-perf-indexes.sql',
'migrate-search.sql',
'migrate-sync-timestamps.sql',
'migrate-i18n.sql',
'migrate-tours.sql',
'migrate-curator-notes.sql',
'migrate-user-roles.sql',
];
const BOOTSTRAP_ADMIN_PERMISSIONS = [
'images',
'checkup',
'curator_notes',
'translations',
'influences',
'tours',
'users',
];
async function bootstrapCurator() {
const { rows } = await pool.query('SELECT COUNT(*)::int AS n FROM users');
if (rows[0].n > 0) {
console.log(' curator bootstrap: users table already populated');
return;
}
const username = (process.env.CURATOR_USERNAME || 'curator').trim();
const password = process.env.CURATOR_PASSWORD;
if (!password) {
console.warn(' curator bootstrap skipped: set CURATOR_PASSWORD to create the first curator account');
return;
}
const bcrypt = require('bcryptjs');
const passwordHash = await bcrypt.hash(password, 10);
await pool.query(
`INSERT INTO users (username, password_hash, role, permissions, is_active)
VALUES ($1, $2, 'admin', $3::text[], true)`,
[username, passwordHash, BOOTSTRAP_ADMIN_PERMISSIONS]
);
console.log(` bootstrap admin account: ${username}`);
}
async function applySqlFile(label, filePath) {
const sql = fs.readFileSync(filePath, 'utf8');
await pool.query(sql);
console.log(` ${label}`);
}
async function migrate() {
const dbDir = path.join(__dirname, '..', 'db');
const schemaPath = path.join(dbDir, 'schema.sql');
console.log('Applying db/schema.sql …');
await applySqlFile('schema.sql', schemaPath);
console.log('Applying incremental migrations …');
for (const file of INCREMENTAL_MIGRATIONS) {
const filePath = path.join(dbDir, file);
if (!fs.existsSync(filePath)) {
console.warn(` skipped (missing): ${file}`);
continue;
}
await applySqlFile(file, filePath);
}
console.log('Bootstrapping curator account (if needed) …');
await bootstrapCurator();
console.log('Database migration complete.');
}
migrate()
.then(() => pool.end())
.catch((err) => {
console.error('Migration failed:', err.message);
pool.end().finally(() => process.exit(1));
});