Files
poshmanager/server/models/userModel.js
2026-06-25 12:05:53 -05:00

59 lines
2.4 KiB
JavaScript

import bcrypt from 'bcryptjs';
import { db, id, now } from '../db.js';
import { publicUser } from '../auth.js';
export function listUsers() {
return db.prepare('SELECT * FROM users ORDER BY display_name').all().map((row) => ({
...publicUser(row),
groups: db.prepare(`
SELECT g.id, g.name
FROM groups g
INNER JOIN user_groups ug ON ug.group_id = g.id
WHERE ug.user_id = ?
ORDER BY g.name
`).all(row.id)
}));
}
// Provision (or refresh) a user that authenticated through Microsoft Entra.
// Matches on email: an existing account is marked Entra-backed in place so the
// operator keeps their resources, and a brand-new account is created with no
// local password.
export function findOrProvisionEntraUser({ email, displayName }) {
const normalized = String(email || '').toLowerCase();
const existing = db.prepare('SELECT * FROM users WHERE email = ?').get(normalized);
if (existing) {
db.prepare('UPDATE users SET display_name = ?, auth_provider = ?, updated_at = ? WHERE id = ?')
.run(displayName || existing.display_name, 'entra', now(), existing.id);
return db.prepare('SELECT * FROM users WHERE id = ?').get(existing.id);
}
const userId = id('usr');
const timestamp = now();
// First user on a fresh install becomes admin; subsequent Entra users are standard.
const isFirstUser = db.prepare('SELECT COUNT(*) AS count FROM users').get().count === 0;
db.prepare(`
INSERT INTO users (id, email, display_name, password_hash, auth_provider, role, created_at, updated_at)
VALUES (?, ?, ?, NULL, 'entra', ?, ?, ?)
`).run(userId, normalized, displayName || normalized, isFirstUser ? 'admin' : 'user', timestamp, timestamp);
return db.prepare('SELECT * FROM users WHERE id = ?').get(userId);
}
export function createUser(payload) {
const userId = id('usr');
const timestamp = now();
db.prepare(`
INSERT INTO users (id, email, display_name, password_hash, auth_provider, role, created_at)
VALUES (?, ?, ?, ?, 'local', ?, ?)
`).run(
userId,
payload.email.toLowerCase(),
payload.displayName,
payload.password ? bcrypt.hashSync(payload.password, 12) : null,
payload.role,
timestamp
);
const groupStmt = db.prepare('INSERT OR IGNORE INTO user_groups (user_id, group_id) VALUES (?, ?)');
for (const groupId of payload.groupIds || []) groupStmt.run(userId, groupId);
return listUsers().find((user) => user.id === userId);
}