Files
skeet_loader/server/create_users.js
T
2026-06-08 15:51:52 +08:00

99 lines
3.4 KiB
JavaScript

/**
* 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); });