85 lines
2.5 KiB
JavaScript
85 lines
2.5 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const bcrypt = require('bcryptjs');
|
|
|
|
const USERS_PATH = process.env.USERS_PATH || path.join(__dirname, '..', 'users.json');
|
|
|
|
function load() {
|
|
try {
|
|
return JSON.parse(fs.readFileSync(USERS_PATH, 'utf8'));
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function save(users) {
|
|
fs.writeFileSync(USERS_PATH, JSON.stringify(users, null, 2), 'utf8');
|
|
}
|
|
|
|
/**
|
|
* On first run, create a default admin user if no users exist.
|
|
* Prints a warning to remind the user to change the password.
|
|
*/
|
|
function ensureDefaultAdmin() {
|
|
const users = load();
|
|
if (users.length === 0) {
|
|
const hash = bcrypt.hashSync('admin', 10);
|
|
save([{ id: '1', username: 'admin', passwordHash: hash, createdAt: new Date().toISOString() }]);
|
|
console.warn('⚠️ No users found — created default admin account.');
|
|
console.warn('⚠️ Username: admin Password: admin');
|
|
console.warn('⚠️ Change this password immediately in Settings → Users.');
|
|
}
|
|
}
|
|
|
|
function getAll() {
|
|
return load().map(({ passwordHash, ...u }) => u); // strip hash from output
|
|
}
|
|
|
|
function findByUsername(username) {
|
|
return load().find(u => u.username.toLowerCase() === username.toLowerCase()) ?? null;
|
|
}
|
|
|
|
function findById(id) {
|
|
return load().find(u => u.id === id) ?? null;
|
|
}
|
|
|
|
function create(username, password) {
|
|
const users = load();
|
|
if (users.find(u => u.username.toLowerCase() === username.toLowerCase())) {
|
|
throw new Error('Username already exists');
|
|
}
|
|
const hash = bcrypt.hashSync(password, 10);
|
|
const user = {
|
|
id: Date.now().toString(),
|
|
username,
|
|
passwordHash: hash,
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
users.push(user);
|
|
save(users);
|
|
const { passwordHash, ...safe } = user;
|
|
return safe;
|
|
}
|
|
|
|
function updatePassword(id, newPassword) {
|
|
const users = load();
|
|
const idx = users.findIndex(u => u.id === id);
|
|
if (idx === -1) throw new Error('User not found');
|
|
users[idx].passwordHash = bcrypt.hashSync(newPassword, 10);
|
|
save(users);
|
|
}
|
|
|
|
function remove(id) {
|
|
const users = load();
|
|
if (users.length === 1) throw new Error('Cannot delete the last user');
|
|
const filtered = users.filter(u => u.id !== id);
|
|
if (filtered.length === users.length) throw new Error('User not found');
|
|
save(filtered);
|
|
}
|
|
|
|
function verifyPassword(user, password) {
|
|
return bcrypt.compareSync(password, user.passwordHash);
|
|
}
|
|
|
|
module.exports = { ensureDefaultAdmin, getAll, findByUsername, findById, create, updatePassword, remove, verifyPassword };
|