/** * Batch user creation script. * * Usage: * node create_users.js user1 pass1 [user2 pass2 ...] * node create_users.js --file users.json * node create_users.js --interactive * * JSON format (users.json): * [ * { "username": "user1", "password": "pass1", "role": "user", "expires_at": null }, * { "username": "user2", "password": "pass2", "role": "user", "expires_at": "2026-12-31" } * ] */ require('dotenv').config({ path: require('path').join(__dirname, '.env') }); const fs = require('fs'); const readline = require('readline'); const db = require('./db'); const auth = require('./auth'); db.initDB(); async function createOne(username, password, role = 'user', expiresAt = null) { const existing = db.getUserByUsername(username); if (existing) { console.log(` [SKIP] '${username}' already exists`); return false; } const hash = await auth.hashPassword(password); db.createUser(username, hash, role, expiresAt || null); console.log(` [OK] '${username}' created (role: ${role}${expiresAt ? ', expires: ' + expiresAt : ''})`); return true; } async function main() { const args = process.argv.slice(2); let created = 0; if (args.length === 0 || args.includes('--interactive') || args.includes('-i')) { // ── Interactive mode ── console.log('=== Interactive User Creation ==='); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); while (true) { const username = await new Promise(resolve => rl.question('\nUsername (empty to finish): ', resolve)); if (!username) break; const password = await new Promise(resolve => rl.question('Password: ', resolve)); if (!password) { console.log(' Password required, skipping.'); continue; } const role = await new Promise(resolve => rl.question('Role [user/admin] (default: user): ', resolve)); const expires = await new Promise(resolve => rl.question('Expiry YYYY-MM-DD (default: never): ', resolve)); if (await createOne(username, password, role || 'user', expires || null)) created++; } rl.close(); } else if (args.includes('--file') || args.includes('-f')) { // ── JSON file mode ── const fileIdx = args.indexOf('--file'); const fIdx = args.indexOf('-f'); const filePath = args[fileIdx !== -1 ? fileIdx + 1 : fIdx + 1]; if (!filePath || !fs.existsSync(filePath)) { console.error('File not found:', filePath); process.exit(1); } const users = JSON.parse(fs.readFileSync(filePath, 'utf8')); if (!Array.isArray(users)) { console.error('JSON must be an array of user objects.'); process.exit(1); } console.log(`Creating ${users.length} users from ${filePath}...\n`); for (const u of users) { if (await createOne(u.username, u.password, u.role || 'user', u.expires_at || null)) created++; } } else { // ── CLI args mode: pairs of username password ── if (args.length % 2 !== 0) { console.error('Arguments must be in pairs: username password [username password ...]'); process.exit(1); } console.log(`Creating ${args.length / 2} users...\n`); for (let i = 0; i < args.length; i += 2) { if (await createOne(args[i], args[i + 1])) created++; } } console.log(`\nDone. ${created} user(s) created.`); process.exit(0); } main().catch(err => { console.error('Error:', err); process.exit(1); });