196 lines
6.1 KiB
JavaScript
196 lines
6.1 KiB
JavaScript
#!/usr/bin/env node
|
||
import { input, password, select, confirm } from '@inquirer/prompts';
|
||
import Database from 'better-sqlite3';
|
||
import bcrypt from 'bcrypt';
|
||
|
||
const DB_FILE = 'ac_data.db';
|
||
|
||
const db = new Database(DB_FILE);
|
||
db.exec(`
|
||
CREATE TABLE IF NOT EXISTS users (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
username TEXT UNIQUE NOT NULL,
|
||
password_hash TEXT NOT NULL,
|
||
role TEXT NOT NULL DEFAULT 'user',
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
)
|
||
`);
|
||
|
||
const insertUser = db.prepare('INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)');
|
||
const getAllUsers = db.prepare('SELECT id, username, role, created_at FROM users ORDER BY id');
|
||
const getUserById = db.prepare('SELECT * FROM users WHERE id = ?');
|
||
const updateUserRole = db.prepare('UPDATE users SET role = ? WHERE id = ?');
|
||
const updateUserPassword = db.prepare('UPDATE users SET password_hash = ? WHERE id = ?');
|
||
const deleteUser = db.prepare('DELETE FROM users WHERE id = ?');
|
||
|
||
console.log('\n╔════════════════════════════════════╗');
|
||
console.log('║ 🔐 User Manager - AC Dashboard ║');
|
||
console.log('╚════════════════════════════════════╝\n');
|
||
|
||
async function listUsers() {
|
||
const users = getAllUsers.all();
|
||
if (users.length === 0) {
|
||
console.log(' No users found.\n');
|
||
return;
|
||
}
|
||
console.log(' ID │ Username │ Role │ Created');
|
||
console.log(' ────┼────────────────┼────────┼─────────────────────');
|
||
users.forEach(u => {
|
||
const id = String(u.id).padStart(3);
|
||
const name = u.username.padEnd(14);
|
||
const role = u.role.padEnd(6);
|
||
const date = u.created_at?.slice(0, 16) || 'N/A';
|
||
const roleColor = u.role === 'admin' ? '\x1b[35m' : '\x1b[33m';
|
||
console.log(` ${id} │ ${name} │ ${roleColor}${role}\x1b[0m │ ${date}`);
|
||
});
|
||
console.log('');
|
||
}
|
||
|
||
async function createUser() {
|
||
const username = await input({
|
||
message: 'Username:',
|
||
validate: v => v.length >= 3 || 'Min 3 characters'
|
||
});
|
||
|
||
const pwd = await password({
|
||
message: 'Password:',
|
||
mask: '*',
|
||
validate: v => v.length >= 4 || 'Min 4 characters'
|
||
});
|
||
|
||
const role = await select({
|
||
message: 'Role:',
|
||
choices: [
|
||
{ name: '👤 user', value: 'user' },
|
||
{ name: '👑 admin', value: 'admin' }
|
||
]
|
||
});
|
||
|
||
try {
|
||
const hash = await bcrypt.hash(pwd, 10);
|
||
insertUser.run(username, hash, role);
|
||
console.log(`\n✅ User "${username}" created as ${role}\n`);
|
||
} catch (e) {
|
||
if (e.code === 'SQLITE_CONSTRAINT_UNIQUE') {
|
||
console.log(`\n❌ User "${username}" already exists\n`);
|
||
} else throw e;
|
||
}
|
||
}
|
||
|
||
async function editUser() {
|
||
const users = getAllUsers.all();
|
||
if (users.length === 0) {
|
||
console.log(' No users to edit.\n');
|
||
return;
|
||
}
|
||
|
||
const userId = await select({
|
||
message: 'Select user to edit:',
|
||
choices: users.map(u => ({
|
||
name: `${u.username} (${u.role})`,
|
||
value: u.id
|
||
}))
|
||
});
|
||
|
||
const action = await select({
|
||
message: 'What to change?',
|
||
choices: [
|
||
{ name: '🔑 Change password', value: 'password' },
|
||
{ name: '👤 Change role', value: 'role' },
|
||
{ name: '← Back', value: 'back' }
|
||
]
|
||
});
|
||
|
||
if (action === 'back') return;
|
||
|
||
if (action === 'password') {
|
||
const pwd = await password({
|
||
message: 'New password:',
|
||
mask: '*',
|
||
validate: v => v.length >= 4 || 'Min 4 characters'
|
||
});
|
||
const hash = await bcrypt.hash(pwd, 10);
|
||
updateUserPassword.run(hash, userId);
|
||
console.log('\n✅ Password updated\n');
|
||
}
|
||
|
||
if (action === 'role') {
|
||
const user = getUserById.get(userId);
|
||
const newRole = await select({
|
||
message: 'New role:',
|
||
choices: [
|
||
{ name: '👤 user', value: 'user' },
|
||
{ name: '👑 admin', value: 'admin' }
|
||
],
|
||
default: user.role
|
||
});
|
||
updateUserRole.run(newRole, userId);
|
||
console.log(`\n✅ Role changed to ${newRole}\n`);
|
||
}
|
||
}
|
||
|
||
async function removeUser() {
|
||
const users = getAllUsers.all();
|
||
if (users.length === 0) {
|
||
console.log(' No users to delete.\n');
|
||
return;
|
||
}
|
||
|
||
const userId = await select({
|
||
message: 'Select user to delete:',
|
||
choices: users.map(u => ({
|
||
name: `${u.username} (${u.role})`,
|
||
value: u.id
|
||
}))
|
||
});
|
||
|
||
const user = getUserById.get(userId);
|
||
const confirmed = await confirm({
|
||
message: `Delete user "${user.username}"?`,
|
||
default: false
|
||
});
|
||
|
||
if (confirmed) {
|
||
deleteUser.run(userId);
|
||
console.log(`\n✅ User "${user.username}" deleted\n`);
|
||
} else {
|
||
console.log('\n❌ Cancelled\n');
|
||
}
|
||
}
|
||
|
||
async function main() {
|
||
while (true) {
|
||
try {
|
||
const action = await select({
|
||
message: 'What would you like to do?',
|
||
choices: [
|
||
{ name: '📋 List users', value: 'list' },
|
||
{ name: '➕ Create user', value: 'create' },
|
||
{ name: '✏️ Edit user', value: 'edit' },
|
||
{ name: '🗑️ Delete user', value: 'delete' },
|
||
{ name: '🚪 Exit', value: 'exit' }
|
||
]
|
||
});
|
||
|
||
if (action === 'exit') {
|
||
console.log('Bye!\n');
|
||
break;
|
||
}
|
||
|
||
if (action === 'list') await listUsers();
|
||
if (action === 'create') await createUser();
|
||
if (action === 'edit') await editUser();
|
||
if (action === 'delete') await removeUser();
|
||
|
||
} catch (e) {
|
||
if (e.name === 'ExitPromptError') {
|
||
console.log('\nBye!\n');
|
||
break;
|
||
}
|
||
throw e;
|
||
}
|
||
}
|
||
}
|
||
|
||
main();
|