#!/usr/bin/env bun /** * Admin CLI Tool * * Usage: * bun src/backend/scripts/admin-cli.js create * bun src/backend/scripts/admin-cli.js promote * bun src/backend/scripts/admin-cli.js demote * bun src/backend/scripts/admin-cli.js list * bun src/backend/scripts/admin-cli.js check * bun src/backend/scripts/admin-cli.js help */ import Database from 'bun:sqlite'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; // ES module equivalent of __dirname const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const dbPath = join(__dirname, '../award.db'); const sqlite = new Database(dbPath); // Enable foreign keys sqlite.exec('PRAGMA foreign_keys = ON'); function help() { console.log(` Admin CLI Tool - Manage admin users Commands: create Create a new admin user promote Promote existing user to admin demote Demote admin to regular user list List all admin users check Check if user is admin help Show this help message Examples: bun src/backend/scripts/admin-cli.js create admin@example.com secretPassword ADMIN bun src/backend/scripts/admin-cli.js promote user@example.com bun src/backend/scripts/admin-cli.js list bun src/backend/scripts/admin-cli.js check user@example.com `); } function createAdminUser(email, password, callsign) { console.log(`Creating admin user: ${email}`); // Check if user already exists const existingUser = sqlite.query(` SELECT id, email FROM users WHERE email = ? `).get(email); if (existingUser) { console.error(`Error: User with email ${email} already exists`); process.exit(1); } // Hash password const passwordHash = Bun.password.hashSync(password, { algorithm: 'bcrypt', cost: 10, }); // Ensure passwordHash is a string const hashString = String(passwordHash); // Insert admin user const result = sqlite.query(` INSERT INTO users (email, password_hash, callsign, role, is_admin, created_at, updated_at) VALUES (?, ?, ?, 'admin', 1, strftime('%s', 'now') * 1000, strftime('%s', 'now') * 1000) `).run(email, hashString, callsign); console.log(`✓ Admin user created successfully!`); console.log(` ID: ${result.lastInsertRowid}`); console.log(` Email: ${email}`); console.log(` Callsign: ${callsign}`); console.log(` Role: admin`); console.log(`\nYou can now log in with these credentials.`); } function promoteUser(email) { console.log(`Promoting user to admin: ${email}`); // Check if user exists const user = sqlite.query(` SELECT id, email, role, is_admin FROM users WHERE email = ? `).get(email); if (!user) { console.error(`Error: User with email ${email} not found`); process.exit(1); } if (user.role === 'admin' && user.is_admin === 1) { console.log(`User ${email} is already an admin`); return; } // Update user to admin sqlite.query(` UPDATE users SET role = 'admin', is_admin = 1, updated_at = strftime('%s', 'now') * 1000 WHERE email = ? `).run(email); console.log(`✓ User ${email} has been promoted to admin`); } function demoteUser(email) { console.log(`Demoting admin to regular user: ${email}`); // Check if user exists const user = sqlite.query(` SELECT id, email, role, is_admin FROM users WHERE email = ? `).get(email); if (!user) { console.error(`Error: User with email ${email} not found`); process.exit(1); } if (user.role !== 'admin' || user.is_admin !== 1) { console.log(`User ${email} is not an admin`); return; } // Check if this is the last admin const adminCount = sqlite.query(` SELECT COUNT(*) as count FROM users WHERE role = 'admin' AND is_admin = 1 `).get(); if (adminCount.count === 1) { console.error(`Error: Cannot demote the last admin user. At least one admin must exist.`); process.exit(1); } // Update user to regular user sqlite.query(` UPDATE users SET role = 'user', is_admin = 0, updated_at = strftime('%s', 'now') * 1000 WHERE email = ? `).run(email); console.log(`✓ User ${email} has been demoted to regular user`); } function listAdmins() { console.log('Listing all admin users...\n'); const admins = sqlite.query(` SELECT id, email, callsign, created_at FROM users WHERE role = 'admin' AND is_admin = 1 ORDER BY created_at ASC `).all(); if (admins.length === 0) { console.log('No admin users found'); return; } console.log(`Found ${admins.length} admin user(s):\n`); console.log('ID | Email | Callsign | Created At'); console.log('----+----------------------------+----------+---------------------'); admins.forEach((admin) => { const createdAt = new Date(admin.created_at).toLocaleString(); console.log(`${String(admin.id).padEnd(3)} | ${admin.email.padEnd(26)} | ${admin.callsign.padEnd(8)} | ${createdAt}`); }); } function checkUser(email) { console.log(`Checking user status: ${email}\n`); const user = sqlite.query(` SELECT id, email, callsign, role, is_admin FROM users WHERE email = ? `).get(email); if (!user) { console.log(`User not found: ${email}`); process.exit(1); } const isAdmin = user.role === 'admin' && user.is_admin === 1; console.log(`User found:`); console.log(` Email: ${user.email}`); console.log(` Callsign: ${user.callsign}`); console.log(` Role: ${user.role}`); console.log(` Is Admin: ${isAdmin ? 'Yes ✓' : 'No'}`); } // Main CLI logic const command = process.argv[2]; const args = process.argv.slice(3); switch (command) { case 'create': if (args.length !== 3) { console.error('Error: create command requires 3 arguments: '); help(); process.exit(1); } createAdminUser(args[0], args[1], args[2]); break; case 'promote': if (args.length !== 1) { console.error('Error: promote command requires 1 argument: '); help(); process.exit(1); } promoteUser(args[0]); break; case 'demote': if (args.length !== 1) { console.error('Error: demote command requires 1 argument: '); help(); process.exit(1); } demoteUser(args[0]); break; case 'list': listAdmins(); break; case 'check': if (args.length !== 1) { console.error('Error: check command requires 1 argument: '); help(); process.exit(1); } checkUser(args[0]); break; case 'help': case '--help': case '-h': help(); break; default: console.error(`Error: Unknown command '${command}'`); help(); process.exit(1); } sqlite.close();