feat(services): 🚀 UserService
This commit is contained in:
parent
742330d6fe
commit
2a33f45257
234
src/services/user.service.ts
Normal file
234
src/services/user.service.ts
Normal file
@ -0,0 +1,234 @@
|
||||
import {Logger} from "tslog";
|
||||
|
||||
import Argon2id from "@node-rs/argon2";
|
||||
import MySqlService from "@services/mysql.service";
|
||||
|
||||
|
||||
const logger = new Logger({ name: "UserService" });
|
||||
|
||||
const DbHandler = new MySqlService.Handler('UserService')
|
||||
|
||||
/**
|
||||
* Retrieves a user object from the database based on the given username.
|
||||
*
|
||||
* @param {string} username - The username of the user to retrieve.
|
||||
* @returns {Promise<Object | null>} - The user object if found, or null if not found.
|
||||
*/
|
||||
async function getUserFromUsername(username: string): Promise<object | null> {
|
||||
const dbUser = await MySqlService.User.getByUsername(DbHandler, username)
|
||||
if (dbUser === undefined) return null;
|
||||
return dbUser;
|
||||
}
|
||||
|
||||
async function getUserFromIdService(id: string | undefined) {
|
||||
const dbUser = await MySqlService.User.getById(DbHandler, id);
|
||||
if (dbUser === undefined) return null;
|
||||
return dbUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new user by creating a UserService object, generating a JWT token, and inserting the user into the database.
|
||||
*
|
||||
* @param {Object} sanitizedData - The sanitized user data.
|
||||
* @param {string} sanitizedData.username - The username of the new user.
|
||||
* @param {string} sanitizedData.displayName - The display namcoe of the new user.
|
||||
* @param {string} sanitizedData.firstName
|
||||
* @param {string} sanitizedData.lastName
|
||||
* @param {string} sanitizedData.password - The password of the new user.
|
||||
* @param {boolean} sanitizedData.gdpr - Indicates whether the new user has accepted GDPR.
|
||||
*
|
||||
* @returns {Object} - An object containing the registered user's data and JWT token.
|
||||
* @returns {string} error - The error name, if any. "none" if registration was successful.
|
||||
* @returns {string|null} jwt - The JWT token for the registered user. Null if registration was not successful.
|
||||
* @returns {Object|null} user - The registered user's data. Null if registration was not successful.
|
||||
* @returns {string|null} user.id - The ID of the registered user. Null if registration was not successful.
|
||||
* @returns {string|null} user.username - The username of the registered user. Null if registration was not successful.
|
||||
* @returns {string|null} user.displayName - The display name of the registered user. Null if registration was not successful.
|
||||
*/
|
||||
async function RegisterService(sanitizedData) {
|
||||
if (sanitizedData.password.length < 6) {
|
||||
logger.info(`REGISTER :> Invalid password (${sanitizedData.username})`)
|
||||
return { error: "invalidPassword" };
|
||||
}
|
||||
const passwordHash = await getHashFromPassword(sanitizedData.password)
|
||||
|
||||
// Does the new user has accepted GDPR ?
|
||||
if (sanitizedData.gdpr !== true) {
|
||||
logger.info(`REGISTER :> GDPR not validated (${sanitizedData.username})`)
|
||||
return { error: "gdprNotApproved" }
|
||||
}
|
||||
|
||||
// Check if exist and return
|
||||
|
||||
const dbUserIfExist = await getUserFromUsername(sanitizedData.username)
|
||||
if (dbUserIfExist) {
|
||||
logger.info(`REGISTER :> User exist (${dbUserIfExist.username})\n ID:${dbUserIfExist.id}`)
|
||||
return { error: "exist" }
|
||||
}
|
||||
|
||||
const currentDate = new Date();
|
||||
|
||||
// New UserService (class)
|
||||
|
||||
const NewUser = new User(sanitizedData.username, sanitizedData.displayName, passwordHash, currentDate);
|
||||
NewUser.setFirstName(sanitizedData.firstName);
|
||||
NewUser.setLastName(sanitizedData.lastName);
|
||||
|
||||
// JWT
|
||||
|
||||
const alg = 'HS512'
|
||||
const token = await JwtSign({
|
||||
sub: NewUser.id
|
||||
}, alg,
|
||||
'1d',
|
||||
'user')
|
||||
|
||||
const userData = {
|
||||
error: "none",
|
||||
jwt: token,
|
||||
user: {
|
||||
id: NewUser.id,
|
||||
username: NewUser.username,
|
||||
displayName: NewUser.displayName,
|
||||
firstName: NewUser.firstName,
|
||||
lastName: NewUser.lastName
|
||||
}};
|
||||
logger.info(userData)
|
||||
await Db.collection("users").insertOne(NewUser);
|
||||
logger.info(`REGISTER :> Inserted new user (${NewUser.username})`)
|
||||
return userData
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the login process by verifying the provided credentials.
|
||||
* @param {Object} sanitizedData - The sanitized user login data.
|
||||
* @param {string} sanitizedData.username - The username provided by the user.
|
||||
* @param {string} sanitizedData.password - The password provided by the user.
|
||||
* @returns {Object} - The login result object.
|
||||
* @returns {string} result.error - The error code if there is an error during the login process.
|
||||
* @returns {string} result.jwt - The JSON Web Token (JWT) generated upon successful login.
|
||||
* @returns {Object} result.user - The user information.
|
||||
* @returns {number} result.user.id - The ID of the user.
|
||||
* @returns {string} result.user.username - The username of the user.
|
||||
* @returns {string} result.user.displayName - The display name of the user.
|
||||
*/
|
||||
async function LoginService(sanitizedData) {
|
||||
//const passwordHash = await getHashFromPassword(sanitizedData.password);
|
||||
const dbUser = await getUserFromUsername(sanitizedData.username);
|
||||
if (!dbUser) {
|
||||
console.log(`LoginService :> User does not exist (${sanitizedData.username})`);
|
||||
return { error: "userNotFound" };
|
||||
}
|
||||
if (sanitizedData.password.length < 6) {
|
||||
console.log('X')
|
||||
console.log(`LoginService :> Invalid password (${sanitizedData.username})`);
|
||||
return { error: "invalidPassword" };
|
||||
}
|
||||
const isPasswordValid = await Argon2id.verify(
|
||||
Buffer.from(dbUser.passwordHash),
|
||||
Buffer.from(sanitizedData.password),
|
||||
{
|
||||
secret: Buffer.from(`${process.env.HASH_SECRET}`),
|
||||
algorithm: 2
|
||||
});
|
||||
if (!isPasswordValid) {
|
||||
console.log(isPasswordValid)
|
||||
console.log(`LoginService :> Invalid password (${sanitizedData.username})`);
|
||||
return { error: "invalidPassword" };
|
||||
}
|
||||
// biome-ignore lint/style/useConst: <explanation>
|
||||
let userData = {
|
||||
error: "none",
|
||||
jwt: null,
|
||||
user: {
|
||||
id: dbUser.id,
|
||||
username: dbUser.username,
|
||||
displayName: dbUser.displayName,
|
||||
}
|
||||
};
|
||||
|
||||
const alg = 'HS512';
|
||||
userData.jwt = await JwtSign({sub: dbUser.id}, alg, '1d', 'user')
|
||||
|
||||
|
||||
console.log("USERDATA :>");
|
||||
console.log(userData);
|
||||
return userData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all users from the database.
|
||||
*
|
||||
* @async
|
||||
* @function getAllUsersService
|
||||
* @returns {Promise<{iat: number, users: Array<user>, length: number}>} - The response object containing the users array and its length.
|
||||
*/
|
||||
async function getAllUsersService() {
|
||||
const users = await Db.collection("users").find().toArray();
|
||||
// biome-ignore lint/complexity/noForEach: <explanation>
|
||||
users.forEach(user => {
|
||||
delete user.passwordHash
|
||||
delete user._id
|
||||
delete user.gdpr
|
||||
});
|
||||
logger.info(`Query ${users.length} user(s)`)
|
||||
return {
|
||||
iat: Date.now(),
|
||||
users: users,
|
||||
length: users.length
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits a user in the database.
|
||||
*
|
||||
* @param {string} targetId - The ID of the user to be edited.
|
||||
* @param {object} sanitizedData - The sanitized data to update the user with.
|
||||
* @returns {object} - An object indicating the result of the operation.
|
||||
* If the user is not found, the error property will be a string "userNotFound".
|
||||
* Otherwise, the error property will be a string "none".
|
||||
*/
|
||||
async function editUserService(targetId, sanitizedData) {
|
||||
if (sanitizedData.password) {
|
||||
const passwordHash = await getHashFromPassword(sanitizedData.password)
|
||||
delete sanitizedData.password
|
||||
logger.info(`Changing password for user "${targetId}"`)
|
||||
sanitizedData.passwordHash = passwordHash
|
||||
}
|
||||
const updatedUserResult = await Db.collection("users").updateOne({id: targetId}, {$set: sanitizedData});
|
||||
if (updatedUserResult.modifiedCount === 0) {
|
||||
logger.info(`EDIT :> User not found (${targetId})`);
|
||||
return { error: "userNotFound" };
|
||||
}
|
||||
|
||||
logger.info(`EDIT :> User updated (${targetId})`);
|
||||
return { error: "none" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user from the database.
|
||||
*
|
||||
* @param {string} targetId - The ID of the user to be deleted.
|
||||
* @return {Promise<boolean>} - A promise that resolves to true if the user is successfully deleted, or false if an error occurs.
|
||||
*/
|
||||
async function deleteUserService(targetId) {
|
||||
logger.info(`Deleting user ${targetId}`)
|
||||
try {
|
||||
await Db.collection("users").deleteOne({id: targetId});
|
||||
return true
|
||||
} catch (e) {
|
||||
logger.warn(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const UserService = {
|
||||
register: RegisterService,
|
||||
login: LoginService,
|
||||
getAll: getAllUsersService,
|
||||
getFromId: getUserFromIdService,
|
||||
edit: editUserService,
|
||||
delete: deleteUserService
|
||||
}
|
||||
|
||||
export default UserService;
|
Loading…
x
Reference in New Issue
Block a user