asset_browser/scripts/create-user.js
2026-03-27 09:18:39 -04:00

106 lines
4.1 KiB
JavaScript
Executable file

#!/usr/bin/env node
'use strict';
// Usage:
// node scripts/create-user.js — interactive: create a user
// node scripts/create-user.js list — list all users
// node scripts/create-user.js deactivate 3 — deactivate user by ID
// node scripts/create-user.js reset 3 — reset a user's password
require('dotenv').config({ path: require('path').join(__dirname, '..', '.env') });
const readline = require('readline');
const bcrypt = require('bcryptjs');
const path = require('path');
const fs = require('fs');
// Ensure data dir exists before DB init
const dataDir = path.join(__dirname, '..', 'data');
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
const { initDb, getAllUsers, createUser, updateUser } = require('../auth/db');
const ROLES = ['superduperadmin', 'admin', 'tech', 'client'];
initDb();
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const ask = (q) => new Promise(resolve => rl.question(q, resolve));
async function promptPassword(label = 'Password') {
const pass = await ask(`${label} (min 12 chars): `);
const confirm = await ask('Confirm password: ');
if (pass !== confirm) { console.error('Passwords do not match.'); process.exit(1); }
if (pass.length < 12) { console.error('Password must be at least 12 characters.'); process.exit(1); }
return pass;
}
async function main() {
const [,, cmd, arg] = process.argv;
if (cmd === 'list') {
const users = getAllUsers();
if (!users.length) { console.log('No users found.'); }
else {
console.log('\n ID Username Role Company Name');
console.log(' ───────────────────────────────────────────────────────────────────────');
for (const u of users) {
const status = u.active ? '' : ' [INACTIVE]';
console.log(` ${String(u.id).padEnd(4)}${u.username.padEnd(21)}${u.role.padEnd(19)}${(u.company || '—').padEnd(21)}${u.name}${status}`);
}
}
rl.close();
return;
}
if (cmd === 'deactivate') {
const id = parseInt(arg);
if (!id) { console.error('Usage: create-user.js deactivate <id>'); rl.close(); return; }
updateUser(id, { active: 0 });
console.log(`User ${id} deactivated.`);
rl.close();
return;
}
if (cmd === 'reset') {
const id = parseInt(arg);
if (!id) { console.error('Usage: create-user.js reset <id>'); rl.close(); return; }
const pass = await promptPassword('New password');
const hash = await bcrypt.hash(pass, 12);
updateUser(id, { password_hash: hash });
console.log(`Password reset for user ${id}.`);
rl.close();
return;
}
// Default: create new user
console.log('\n── Create New User ─────────────────────────────\n');
const username = (await ask('Username: ')).trim().toLowerCase();
if (!username) { console.error('Username cannot be empty.'); rl.close(); return; }
const name = (await ask('Full name: ')).trim();
if (!name) { console.error('Name cannot be empty.'); rl.close(); return; }
const company = (await ask('Company (optional, press Enter to skip): ')).trim();
console.log(`Roles: ${ROLES.join(' | ')}`);
const role = (await ask('Role: ')).trim().toLowerCase();
if (!ROLES.includes(role)) {
console.error(`Invalid role. Choose from: ${ROLES.join(', ')}`);
rl.close(); return;
}
const pass = await promptPassword();
const hash = await bcrypt.hash(pass, 12);
try {
const result = createUser(username, hash, name, role, company);
console.log(`\n✓ User "${username}" created (id=${result.lastInsertRowid}) with role "${role}"${company ? ` at "${company}"` : ''}.`);
} catch (err) {
if (err.message.includes('UNIQUE')) console.error(`Username "${username}" already exists.`);
else console.error(err.message);
}
rl.close();
}
main().catch(err => { console.error(err.message); rl.close(); process.exit(1); });