Files
FarmaFinder/backend/create-admin.js
T
Antoni Nuñez Romeu 31b3eb21b4
Build & Push Docker Images / test-backend (push) Successful in 44s
Build & Push Docker Images / test-frontend (push) Successful in 53s
Build & Push Docker Images / build-backend (push) Failing after 50s
Build & Push Docker Images / build-frontend (push) Successful in 54s
feat(auth): migrate user store from SQLite to PostgreSQL
Users were wiped on container recreation because they shared the
backend_data volume with ephemeral pharmacy/medicine data. Postgres
gets its own named volume (postgres_data) that survives deploys.

- Add postgres:16-alpine service + postgres_data volume to compose
- Add pg + connect-pg-simple deps
- userDbGet/userDbRun helpers: PG when PG_URL set, SQLite fallback
- Sessions use connect-pg-simple when pgPool available
- create-admin.js: PG-aware, SQLite fallback for local dev
- push_subscriptions.user_id: plain INTEGER (cross-DB FK dropped)
- Tests unchanged — no PG_URL in test env = SQLite fallback

Set PG_PASSWORD in server .env before next deploy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 17:50:34 +02:00

98 lines
3.1 KiB
JavaScript

import sqlite3 from 'sqlite3';
import { promisify } from 'util';
import bcrypt from 'bcrypt';
import path from 'path';
import { fileURLToPath } from 'url';
import pg from 'pg';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const PG_URL = process.env.PG_URL;
async function createAdmin() {
const username = process.env.ADMIN_USERNAME || 'admin';
const password = process.env.ADMIN_PASSWORD || 'admin123';
const passwordHash = await bcrypt.hash(password, 10);
if (PG_URL) {
const { Pool } = pg;
const pool = new Pool({ connectionString: PG_URL });
try {
await pool.query(`
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
is_admin INTEGER NOT NULL DEFAULT 0,
address TEXT,
latitude DOUBLE PRECISION,
longitude DOUBLE PRECISION,
created_at TIMESTAMPTZ DEFAULT NOW()
)
`);
const existing = await pool.query('SELECT id FROM users WHERE username = $1', [username]);
if (existing.rows.length > 0) {
console.log(`Admin user '${username}' already exists.`);
console.log('To reset, delete the user first and re-run.');
await pool.end();
return;
}
await pool.query(
'INSERT INTO users (username, password_hash, is_admin) VALUES ($1, $2, 1)',
[username, passwordHash]
);
console.log(`Admin user '${username}' created in PostgreSQL.`);
} finally {
await pool.end();
}
} else {
const dbPath = process.env.DATABASE_PATH || path.join(__dirname, 'database.sqlite');
const db = new sqlite3.Database(dbPath);
const dbRun = (sql, params = []) =>
new Promise((resolve, reject) =>
db.run(sql, params, function (err) { err ? reject(err) : resolve({ lastID: this.lastID }); })
);
const dbGet = promisify(db.get.bind(db));
try {
await dbRun(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
try { await dbRun('ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0'); } catch {}
const existing = await dbGet('SELECT id FROM users WHERE username = ?', [username]);
if (existing) {
console.log(`Admin user '${username}' already exists.`);
console.log('To reset, delete the user first and re-run.');
db.close();
return;
}
await dbRun(
'INSERT INTO users (username, password_hash, is_admin) VALUES (?, ?, 1)',
[username, passwordHash]
);
console.log(`Admin user '${username}' created in SQLite.`);
} finally {
db.close();
}
}
console.log(`Username: ${username}`);
console.log(`Password: ${password}`);
console.log('\nIMPORTANT: Change the default password after first login!');
}
createAdmin().catch((err) => {
console.error('Error creating admin user:', err);
process.exit(1);
});