#!/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 '); 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 '); 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); });