/*
CREATE TABLE app_user (
id TEXT PRIMARY KEY,
email_address TEXT NOT NULL UNIQUE,
password_hash BYTEA NOT NULL,
password_salt BYTEA NOT NULL,
password_hash_algorithm_id TEXT NOT NULL,
disabled BOOLEAN NOT NULL DEFAULT FALSE,
email_address_counter INTEGER NOT NULL DEFAULT 0,
password_hash_counter INTEGER NOT NULL DEFAULT 0,
disabled_counter INTEGER NOT NULL DEFAULT 0,
sessions_counter INTEGER NOT NULL DEFAULT 0
);
*/
import * as faroe_user_server from "@faroe/user-server";
const actions: faroe_user_server.Actions = {
async createUserAction(_actionInvocationId, emailAddress, passwordHash, passwordHashAlgorithmId, passwordSalt) {
const id = generateId(); // TODO
try {
await db.execute(
"INSERT INTO app_user (id, email_address, password_hash, password_hash_algorithm_id, password_salt) VALUES ($1, $2, $3, $4, $5)",
[
id,
emailAddress,
passwordHash,
passwordHashAlgorithmId,
passwordSalt,
]
);
} catch (error: unknown) {
// TODO
if (error is uniqueConstraintFailedError) {
throw new faroe_user_server.ActionError("email_address_already_used");
}
throw new faroe_user_server.ActionError("unexpected_error");
}
const user: faroe_user_server.User = {
id,
emailAddress,
passwordHash,
passwordHashAlgorithmId,
passwordSalt,
disabled: false,
displayName: emailAddress,
emailAddressCounter: 0,
passwordHashCounter: 0,
disabledCounter: 0,
sessionsCounter: 0,
};
return user;
},
async getUserAction(_actionInvocationId, userId) {
let result: QueryResult;
try {
result = await db.execute(
"SELECT email_address, password_hash, password_hash_algorithm_id, password_salt, disabled, email_address_counter, password_hash_counter, disabled_counter, sessions_counter FROM app_user WHERE id = $1",
[userId]
);
} catch {
throw new faroe_user_server.ActionError("unexpected_error");
}
if (result.rows.length === 0) {
throw new faroe_user_server.ActionError("user_not_found");
}
const row = result.rows[0];
const user: faroe_user_server.User = {
id: userId,
emailAddress: row[0],
passwordHash: row[1],
passwordHashAlgorithmId: row[2],
passwordSalt: row[3],
disabled: row[4],
displayName: "",
emailAddressCounter: row[5],
passwordHashCounter: row[6],
disabledCounter: row[7],
sessionsCounter: row[8],
};
return user;
},
async getUserByEmailAddressAction(_actionInvocationId, emailAddress) {
let result: QueryResult;
try {
result = await db.execute(
"SELECT id, password_hash, password_hash_algorithm_id, password_salt, disabled, email_address_counter, password_hash_counter, disabled_counter, sessions_counter FROM app_user WHERE email_address = $1",
[emailAddress]
);
} catch {
throw new faroe_user_server.ActionError("unexpected_error");
}
if (result.rows.length === 0) {
throw new faroe_user_server.ActionError("user_not_found");
}
const row = result.rows[0];
const user: faroe_user_server.User = {
id: row[0],
emailAddress: emailAddress,
passwordHash: row[1],
passwordHashAlgorithmId: row[2],
passwordSalt: row[3],
disabled: row[4],
displayName: "",
emailAddressCounter: row[5],
passwordHashCounter: row[6],
disabledCounter: row[7],
sessionsCounter: row[8],
};
return user;
},
async updateUserEmailAddressAction(_actionInvocationId, userId, emailAddress, userEmailAddressCounter) {
let result;
try {
result = await db.execute(
"UPDATE app_user SET email_address = $1, email_address_counter = email_address_counter + 1 WHERE id = $2 AND email_address_counter = $3",
[emailAddress, userId, userEmailAddressCounter]
);
} catch {
throw new faroe_user_server.ActionError("unexpected_error");
}
if (result.changes === 0) {
throw new faroe_user_server.ActionError("user_not_found");
}
},
async updateUserPasswordHashAction(_actionInvocationId, userId, passwordHash, passwordHashAlgorithmId, passwordSalt, userPasswordHashCounter) {
let result;
try {
result = await db.execute(
"UPDATE app_user SET password_hash = $1, password_hash_algorithm_id = $2, password_salt = $3, password_hash_counter = password_hash_counter + 1 WHERE id = $4 AND password_hash_counter = $5",
[
passwordHash,
passwordHashAlgorithmId,
passwordSalt,
userId,
userPasswordHashCounter,
]
);
} catch {
throw new faroe_user_server.ActionError("unexpected_error");
}
if (result.changes === 0) {
throw new faroe_user_server.UserNotFoundError();
}
},
async incrementUserSessionsCounterAction(_actionInvocationId, userId, userSessionsCounter) {
let result;
try {
result = await db.execute(
"UPDATE app_user SET sessions_counter = sessions_counter + 1 WHERE id = $1 AND sessions_counter = $2",
[userId, userSessionsCounter]
);
} catch {
throw new faroe_user_server.ActionError("unexpected_error");
}
if (result.changes === 0) {
throw new faroe_user_server.ActionError("user_not_found");
}
},
async deleteUserAction(_actionInvocationId, userId) {
let result;
try {
result = await db.execute("DELETE FROM app_user WHERE id = $1", [
userId,
]);
} catch {
throw new faroe_user_server.ActionError("unexpected_error");
}
if (result.changes === 0) {
throw new faroe_user_server.ActionError("user_not_found");
}
},
};