- Add authMiddleware to all AI/transcribe routes (were unauthenticated) - Add full admin panel: user management, registration toggle, stats - Fix XSS in email verification (escape user.name in HTML) - Fix missing APP_URL fallback in password reset email - Add per-tab model selector (respects OpenRouter/Bedrock/Azure lists) - Fix transcribeAudio to send Authorization header - Fix labs input: textarea instead of single-line input - Add structured logging: audit_log, api_log, access_log tables - Add admin CLI (admin-cli.js) for Docker exec management - Fix duplicate var duration declaration in ai.js catch block - Fix RETURNING check case-sensitivity in database.js
187 lines
8.5 KiB
JavaScript
187 lines
8.5 KiB
JavaScript
#!/usr/bin/env node
|
|
// ============================================================
|
|
// ADMIN CLI — Command line admin tool
|
|
// Run inside Docker: docker exec pediatric-ai-scribe node admin-cli.js <command>
|
|
// ============================================================
|
|
|
|
require('dotenv').config();
|
|
var db = require('./src/db/database');
|
|
var bcrypt = require('bcryptjs');
|
|
|
|
var args = process.argv.slice(2);
|
|
var command = args[0];
|
|
|
|
async function main() {
|
|
// Verify DB connection before proceeding
|
|
await db.query('SELECT 1', []);
|
|
|
|
switch (command) {
|
|
|
|
case 'make-admin': {
|
|
var email = args[1];
|
|
if (!email) { console.log('Usage: node admin-cli.js make-admin <email>'); process.exit(1); }
|
|
var user = await db.get('SELECT id, name, role FROM users WHERE email = ?', [email.toLowerCase()]);
|
|
if (!user) { console.log('❌ User not found: ' + email); process.exit(1); }
|
|
await db.run('UPDATE users SET role = ? WHERE id = ?', ['admin', user.id]);
|
|
console.log('✅ ' + user.name + ' (' + email + ') is now ADMIN');
|
|
break;
|
|
}
|
|
|
|
case 'remove-admin': {
|
|
var email = args[1];
|
|
if (!email) { console.log('Usage: node admin-cli.js remove-admin <email>'); process.exit(1); }
|
|
var user = await db.get('SELECT id, name, role FROM users WHERE email = ?', [email.toLowerCase()]);
|
|
if (!user) { console.log('❌ User not found: ' + email); process.exit(1); }
|
|
await db.run('UPDATE users SET role = ? WHERE id = ?', ['user', user.id]);
|
|
console.log('✅ ' + user.name + ' (' + email + ') is now regular user');
|
|
break;
|
|
}
|
|
|
|
case 'verify-user': {
|
|
var email = args[1];
|
|
if (!email) { console.log('Usage: node admin-cli.js verify-user <email>'); process.exit(1); }
|
|
var user = await db.get('SELECT id, name FROM users WHERE email = ?', [email.toLowerCase()]);
|
|
if (!user) { console.log('❌ User not found: ' + email); process.exit(1); }
|
|
await db.run('UPDATE users SET email_verified = true, verify_token = NULL WHERE id = ?', [user.id]);
|
|
console.log('✅ ' + user.name + ' (' + email + ') email verified');
|
|
break;
|
|
}
|
|
|
|
case 'disable-user': {
|
|
var email = args[1];
|
|
if (!email) { console.log('Usage: node admin-cli.js disable-user <email>'); process.exit(1); }
|
|
var user = await db.get('SELECT id, name FROM users WHERE email = ?', [email.toLowerCase()]);
|
|
if (!user) { console.log('❌ User not found: ' + email); process.exit(1); }
|
|
await db.run('UPDATE users SET disabled = true WHERE id = ?', [user.id]);
|
|
console.log('✅ ' + user.name + ' (' + email + ') DISABLED');
|
|
break;
|
|
}
|
|
|
|
case 'enable-user': {
|
|
var email = args[1];
|
|
if (!email) { console.log('Usage: node admin-cli.js enable-user <email>'); process.exit(1); }
|
|
var user = await db.get('SELECT id, name FROM users WHERE email = ?', [email.toLowerCase()]);
|
|
if (!user) { console.log('❌ User not found: ' + email); process.exit(1); }
|
|
await db.run('UPDATE users SET disabled = false WHERE id = ?', [user.id]);
|
|
console.log('✅ ' + user.name + ' (' + email + ') ENABLED');
|
|
break;
|
|
}
|
|
|
|
case 'delete-user': {
|
|
var email = args[1];
|
|
if (!email) { console.log('Usage: node admin-cli.js delete-user <email>'); process.exit(1); }
|
|
var user = await db.get('SELECT id, name FROM users WHERE email = ?', [email.toLowerCase()]);
|
|
if (!user) { console.log('❌ User not found: ' + email); process.exit(1); }
|
|
await db.run('DELETE FROM users WHERE id = ?', [user.id]);
|
|
console.log('✅ ' + user.name + ' (' + email + ') DELETED');
|
|
break;
|
|
}
|
|
|
|
case 'reset-password': {
|
|
var email = args[1];
|
|
var newPass = args[2];
|
|
if (!email || !newPass) { console.log('Usage: node admin-cli.js reset-password <email> <new-password>'); process.exit(1); }
|
|
if (newPass.length < 8) { console.log('❌ Password must be 8+ characters'); process.exit(1); }
|
|
var user = await db.get('SELECT id, name FROM users WHERE email = ?', [email.toLowerCase()]);
|
|
if (!user) { console.log('❌ User not found: ' + email); process.exit(1); }
|
|
var hash = await bcrypt.hash(newPass, 12);
|
|
await db.run('UPDATE users SET password = ? WHERE id = ?', [hash, user.id]);
|
|
console.log('✅ Password reset for ' + user.name + ' (' + email + ')');
|
|
break;
|
|
}
|
|
|
|
case 'create-admin': {
|
|
var email = args[1];
|
|
var password = args[2];
|
|
var name = args[3];
|
|
if (!email || !password || !name) {
|
|
console.log('Usage: node admin-cli.js create-admin <email> <password> <name>');
|
|
process.exit(1);
|
|
}
|
|
if (password.length < 8) { console.log('❌ Password must be 8+ characters'); process.exit(1); }
|
|
var existing = await db.get('SELECT id FROM users WHERE email = ?', [email.toLowerCase()]);
|
|
if (existing) { console.log('❌ Email already exists: ' + email); process.exit(1); }
|
|
var hash = await bcrypt.hash(password, 12);
|
|
await db.run(
|
|
'INSERT INTO users (email, password, name, role, email_verified) VALUES (?, ?, ?, ?, true)',
|
|
[email.toLowerCase(), hash, name, 'admin']
|
|
);
|
|
console.log('✅ Admin user created: ' + name + ' (' + email + ')');
|
|
break;
|
|
}
|
|
|
|
case 'list-users': {
|
|
var users = await db.all('SELECT id, email, name, role, email_verified, disabled, created_at FROM users ORDER BY id', []);
|
|
console.log('');
|
|
console.log('ID | Email | Name | Role | Verified | Disabled');
|
|
console.log('---+---------------------------+-------------------+-------+----------+---------');
|
|
users.forEach(function(u) {
|
|
console.log(
|
|
String(u.id).padEnd(3) + '| ' +
|
|
u.email.padEnd(26) + '| ' +
|
|
(u.name || '').padEnd(18) + '| ' +
|
|
(u.role || 'user').padEnd(6) + '| ' +
|
|
(u.email_verified ? '✅' : '❌').padEnd(9) + '| ' +
|
|
(u.disabled ? '🚫 Yes' : '✅ No')
|
|
);
|
|
});
|
|
console.log('');
|
|
console.log('Total: ' + users.length + ' users');
|
|
break;
|
|
}
|
|
|
|
case 'toggle-registration': {
|
|
var current = await db.getSetting('registration_enabled');
|
|
var newValue = current === 'false' ? 'true' : 'false';
|
|
await db.setSetting('registration_enabled', newValue);
|
|
console.log('✅ Registration is now: ' + (newValue === 'true' ? 'ENABLED' : 'DISABLED'));
|
|
break;
|
|
}
|
|
|
|
case 'stats': {
|
|
var userCount = await db.get('SELECT COUNT(*) as count FROM users', []);
|
|
var apiCount = await db.get('SELECT COUNT(*) as count FROM api_log', []);
|
|
var todayApi = await db.get("SELECT COUNT(*) as count FROM api_log WHERE timestamp > NOW() - INTERVAL '1 day'", []);
|
|
var regEnabled = await db.getSetting('registration_enabled');
|
|
|
|
console.log('');
|
|
console.log('📊 App Statistics');
|
|
console.log('─────────────────');
|
|
console.log('Users: ' + (userCount ? userCount.count : 0));
|
|
console.log('Total API calls: ' + (apiCount ? apiCount.count : 0));
|
|
console.log('Today API calls: ' + (todayApi ? todayApi.count : 0));
|
|
console.log('Registration: ' + (regEnabled !== 'false' ? '✅ Enabled' : '❌ Disabled'));
|
|
console.log('');
|
|
break;
|
|
}
|
|
|
|
default:
|
|
console.log('');
|
|
console.log('🏥 Pediatric AI Scribe — Admin CLI');
|
|
console.log('');
|
|
console.log('Usage: docker exec pediatric-ai-scribe node admin-cli.js <command>');
|
|
console.log('');
|
|
console.log('User Management:');
|
|
console.log(' list-users List all users');
|
|
console.log(' create-admin <email> <password> <name> Create admin user');
|
|
console.log(' make-admin <email> Promote user to admin');
|
|
console.log(' remove-admin <email> Demote admin to user');
|
|
console.log(' verify-user <email> Verify user email');
|
|
console.log(' disable-user <email> Disable user account');
|
|
console.log(' enable-user <email> Enable user account');
|
|
console.log(' delete-user <email> Delete user permanently');
|
|
console.log(' reset-password <email> <new-password> Reset user password');
|
|
console.log('');
|
|
console.log('App Settings:');
|
|
console.log(' toggle-registration Toggle registration on/off');
|
|
console.log(' stats Show app statistics');
|
|
console.log('');
|
|
}
|
|
|
|
process.exit(0);
|
|
}
|
|
|
|
main().catch(function(err) {
|
|
console.error('❌ Error:', err.message);
|
|
process.exit(1);
|
|
});
|