106 lines
4.1 KiB
JavaScript
Executable file
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); });
|