@@ -3,6 +3,16 @@ import * as hasher from '../security/hash.js';
33import { nanoid } from 'nanoid' ;
44import SqliteConnection from './SqliteConnection.js' ;
55
6+ /**
7+ * Get all users.
8+ *
9+ * Notes:
10+ * - Password hashes are omitted by default to avoid leaking them to callers that don’t need them.
11+ * - numberOfJobs is computed via a subquery for each user.
12+ *
13+ * @param {boolean } withPassword - If true, include the hashed password in the returned objects; otherwise set password to null.
14+ * @returns {User[] } Array of users ordered by username.
15+ */
616export const getUsers = ( withPassword ) => {
717 const rows = SqliteConnection . query (
818 `SELECT u.id, u.username, u.password, u.last_login AS lastLogin, u.is_admin AS isAdmin,
@@ -17,6 +27,12 @@ export const getUsers = (withPassword) => {
1727 } ) ) ;
1828} ;
1929
30+ /**
31+ * Get a single user by id.
32+ *
33+ * @param {string } id - User id (primary key).
34+ * @returns {User|null } The user when found; otherwise null. The password field is included but callers should not expose it.
35+ */
2036export const getUser = ( id ) => {
2137 const rows = SqliteConnection . query (
2238 `SELECT u.id, u.username, u.password, u.last_login AS lastLogin, u.is_admin AS isAdmin,
@@ -31,6 +47,21 @@ export const getUser = (id) => {
3147 return { ...u , isAdmin : ! ! u . isAdmin } ;
3248} ;
3349
50+ /**
51+ * Insert a new user or update an existing one.
52+ *
53+ * Behavior:
54+ * - When userId is provided and exists: updates username and isAdmin. Password is only updated when a non-empty password is provided.
55+ * - When userId is missing or does not exist: inserts a new user with a freshly generated id. last_login is initialized to null.
56+ * - Passwords are hashed using the same hashing function used for login comparison.
57+ *
58+ * @param {Object } params
59+ * @param {string } params.username - Username (must be unique in DB).
60+ * @param {string } [params.password] - Plain text password to set; if omitted on update, existing hash is preserved.
61+ * @param {string } [params.userId] - Existing user id to update; if missing, a new id is generated.
62+ * @param {boolean } params.isAdmin - Whether the user should have admin privileges.
63+ * @returns {void }
64+ */
3465export const upsertUser = ( { username, password, userId, isAdmin } ) => {
3566 const id = userId || nanoid ( ) ;
3667 // Check if user exists
@@ -64,18 +95,39 @@ export const upsertUser = ({ username, password, userId, isAdmin }) => {
6495 }
6596} ;
6697
98+ /**
99+ * Update the last_login timestamp to now for the given user.
100+ *
101+ * @param {{userId: string} } params - Parameters.
102+ * @param {string } params.userId - The user's id.
103+ * @returns {void }
104+ */
67105export const setLastLoginToNow = ( { userId } ) => {
68106 SqliteConnection . execute ( `UPDATE users SET last_login = @now WHERE id = @id` , { id : userId , now : Date . now ( ) } ) ;
69107} ;
70108
109+ /**
110+ * Remove a user by id.
111+ *
112+ * Notes:
113+ * - In the SQLite schema, jobs reference users with ON DELETE CASCADE, so jobs (and their listings via jobs) are removed automatically.
114+ *
115+ * @param {string } userId - The id of the user to remove.
116+ * @returns {void }
117+ */
71118export const removeUser = ( userId ) => {
72119 SqliteConnection . execute ( `DELETE FROM users WHERE id = @id` , { id : userId } ) ;
73120} ;
74121
75122/**
76123 * Ensure the demo user matches the demo mode setting.
124+ *
125+ * Behavior:
77126 * - When config.demoMode is false: remove the demo user (and its cascading data via FKs).
78127 * - When config.demoMode is true: ensure a 'demo' user exists with password 'demo' and admin rights.
128+ *
129+ * Security: The demo user's password is set to a known value ('demo') and should only be enabled in demoMode.
130+ * @returns {void }
79131 */
80132export const ensureDemoUserExists = ( ) => {
81133 if ( ! config . demoMode ) {
@@ -96,9 +148,13 @@ export const ensureDemoUserExists = () => {
96148
97149/**
98150 * Ensure there is at least one administrator in the system.
151+ *
99152 * Behavior:
100153 * - If there are no users at all, create default 'admin' user with password 'admin'.
101154 * - If users exist but none is admin, promote the first existing user to admin.
155+ *
156+ * Security: On a fresh instance, a default admin/admin is created; change this password immediately.
157+ * @returns {void }
102158 */
103159export const ensureAdminUserExists = ( ) => {
104160 const anyUser = SqliteConnection . query ( `SELECT id FROM users LIMIT 1` ) . length > 0 ;
0 commit comments