add:module:auth:*logout | add logout module and clean all session in system
This commit is contained in:
31
src/modules/auth/controller/logout.controller.ts
Normal file
31
src/modules/auth/controller/logout.controller.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { Context } from "elysia";
|
||||
import { getCookie } from "../../../helpers/http/userHeader/cookies/getCookies";
|
||||
import { clearCookies } from "../../../helpers/http/userHeader/cookies/clearCookies";
|
||||
import { mainErrorHandler } from "../../../helpers/error/handler";
|
||||
import { COOKIE_KEYS } from "../../../constants/cookie.keys";
|
||||
import {
|
||||
returnErrorResponse,
|
||||
returnWriteResponse,
|
||||
} from "../../../helpers/callback/httpResponse";
|
||||
import { logoutService } from "../services/logout.service";
|
||||
|
||||
export const logoutController = async (ctx: Context) => {
|
||||
try {
|
||||
const userCookie = getCookie(ctx);
|
||||
if (!userCookie || !userCookie.auth_token) {
|
||||
return returnErrorResponse(ctx.set, 401, "You're not logged in yet");
|
||||
}
|
||||
|
||||
const clearSession = logoutService(userCookie.auth_token);
|
||||
|
||||
clearCookies(ctx.set, [COOKIE_KEYS.AUTH]);
|
||||
return returnWriteResponse(
|
||||
ctx.set,
|
||||
200,
|
||||
"Successfully logged out",
|
||||
clearSession
|
||||
);
|
||||
} catch (error) {
|
||||
return mainErrorHandler(ctx.set, error);
|
||||
}
|
||||
};
|
||||
@ -2,9 +2,11 @@ import Elysia from "elysia";
|
||||
import { loginWithPassword } from "./controller/loginWithPassword.controller";
|
||||
import { authMiddleware } from "../../middleware/auth.middleware";
|
||||
import { authVerification } from "./controller/authVerification.controller";
|
||||
import { logoutController } from "./controller/logout.controller";
|
||||
|
||||
export const authModule = new Elysia({ prefix: "/auth" })
|
||||
.post("/legacy", loginWithPassword)
|
||||
.post("/verification", authVerification, {
|
||||
beforeHandle: authMiddleware,
|
||||
});
|
||||
})
|
||||
.post("/logout", logoutController);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { AppError } from "../../../helpers/error/instances/app";
|
||||
import { ErrorForwarder } from "../../../helpers/error/instances/forwarder";
|
||||
import { jwtDecode } from "../../../helpers/http/jwt/decode";
|
||||
|
||||
1
src/modules/auth/services/loginFromSystem.service.ts
Normal file
1
src/modules/auth/services/loginFromSystem.service.ts
Normal file
@ -0,0 +1 @@
|
||||
export const loginFromSystemService = (userId: string) => {};
|
||||
15
src/modules/auth/services/logout.service.ts
Normal file
15
src/modules/auth/services/logout.service.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { ErrorForwarder } from "../../../helpers/error/instances/forwarder";
|
||||
import { jwtDecode } from "../../../helpers/http/jwt/decode";
|
||||
import { deleteUserSessionInCacheAndDBService } from "../../userSession/services/deleteUserSessionInCacheAndDB.service";
|
||||
|
||||
export const logoutService = async (userCookie: string) => {
|
||||
try {
|
||||
const jwtToken = jwtDecode(userCookie);
|
||||
const deleteUserSessionInCacheAndDB =
|
||||
deleteUserSessionInCacheAndDBService(jwtToken);
|
||||
|
||||
return deleteUserSessionInCacheAndDB;
|
||||
} catch (error) {
|
||||
ErrorForwarder(error, 500, "Logout service had encountered error");
|
||||
}
|
||||
};
|
||||
@ -7,6 +7,7 @@ import { Context } from "elysia";
|
||||
import { createUserService } from "../services/createUser.service";
|
||||
import { mainErrorHandler } from "../../../helpers/error/handler";
|
||||
import { createUserSchema } from "../schemas/createUser.schema";
|
||||
import { getCookie } from "../../../helpers/http/userHeader/cookies/getCookies";
|
||||
|
||||
/**
|
||||
* @function createUser
|
||||
@ -30,6 +31,19 @@ import { createUserSchema } from "../schemas/createUser.schema";
|
||||
export const createUserController = async (
|
||||
ctx: Context & { body: Prisma.UserCreateInput }
|
||||
) => {
|
||||
// Check if the user is already logged in by checking the auth token in cookies. If the user is logged in, return an error response
|
||||
try {
|
||||
const cookie = getCookie(ctx);
|
||||
if (cookie && cookie.auth_token)
|
||||
return returnErrorResponse(
|
||||
ctx.set,
|
||||
401,
|
||||
"You are already logged in. Please log out first if you want to create a new account."
|
||||
);
|
||||
} catch (_) {
|
||||
// Pass
|
||||
}
|
||||
|
||||
// Validate the user input using a validation schema
|
||||
const { error } = createUserSchema.validate(ctx.body);
|
||||
if (error)
|
||||
|
||||
@ -25,6 +25,7 @@ export const editUserService = async (
|
||||
"The username or email has already taken by another user."
|
||||
);
|
||||
|
||||
// Prepare the fields to update, only include fields that are provided in the payload
|
||||
const fieldsToUpdate: Partial<Prisma.UserUpdateInput> = {
|
||||
...(payload.username && payload.username !== jwtSession.user.username
|
||||
? { username: payload.username }
|
||||
@ -53,6 +54,7 @@ export const editUserService = async (
|
||||
: {}),
|
||||
};
|
||||
|
||||
// Update the user in the database, use username from the JWT session to find the user
|
||||
const updateUser = await updateUserRepo(
|
||||
jwtSession.user.username,
|
||||
fieldsToUpdate
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
import { AppError } from "../../../helpers/error/instances/app";
|
||||
import { redis } from "../../../utils/databases/redis/connection";
|
||||
|
||||
export const deleteUserSessionFromCacheRepo = async (
|
||||
userId: string,
|
||||
sessionId: string
|
||||
) => {
|
||||
try {
|
||||
const deleteUserSessionFromCache = redis.del(
|
||||
`${process.env.APP_NAME}:users:${userId}:sessions:${sessionId}`
|
||||
);
|
||||
return deleteUserSessionFromCache;
|
||||
} catch (error) {
|
||||
throw new AppError(500, "Error while remove data from cache", error);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,19 @@
|
||||
import { AppError } from "../../../helpers/error/instances/app";
|
||||
import { prisma } from "../../../utils/databases/prisma/connection";
|
||||
|
||||
export const deleteUserSessionFromDBRepo = async (sessionId: string) => {
|
||||
try {
|
||||
const deleteUserSessionFromCacheDB = await prisma.userSession.update({
|
||||
where: {
|
||||
id: sessionId,
|
||||
},
|
||||
data: {
|
||||
deletedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
return deleteUserSessionFromCacheDB;
|
||||
} catch (error) {
|
||||
throw new AppError(500, "Error while remove delete from database", error);
|
||||
}
|
||||
};
|
||||
@ -19,7 +19,6 @@ export const findUniqueUserSessionInDBRepo = async (identifier: string) => {
|
||||
},
|
||||
},
|
||||
omit: {
|
||||
deletedAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
@ -20,7 +20,6 @@ export const createUserSessionRepo = async (
|
||||
},
|
||||
omit: {
|
||||
lastOnline: true,
|
||||
deletedAt: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
|
||||
@ -8,7 +8,7 @@ export const storeUserSessionToCacheRepo = async (
|
||||
) => {
|
||||
try {
|
||||
await redis.set(
|
||||
`${process.env.app_name}:users:${userSession.userId}:sessions:${userSession.id}`,
|
||||
`${process.env.APP_NAME}:users:${userSession.userId}:sessions:${userSession.id}`,
|
||||
String(userSession.validUntil),
|
||||
"EX",
|
||||
timeExpires
|
||||
|
||||
@ -7,7 +7,7 @@ export const checkUserSessionInCacheService = async (
|
||||
) => {
|
||||
try {
|
||||
// Construct the Redis key name using the userId and sessionId
|
||||
const redisKeyName = `${process.env.app_name}:users:${userId}:sessions:${sessionId}`;
|
||||
const redisKeyName = `${process.env.APP_NAME}:users:${userId}:sessions:${sessionId}`;
|
||||
|
||||
// Check if the user session exists in Redis
|
||||
const userSessionInRedis = await checkUserSessionInCacheRepo(redisKeyName);
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
import { ErrorForwarder } from "../../../helpers/error/instances/forwarder";
|
||||
import { JWTAuthToken } from "../../../helpers/http/jwt/decode/types";
|
||||
import { deleteUserSessionFromCacheRepo } from "../repositories/deleteUserSessionFromCache.repository";
|
||||
import { deleteUserSessionFromDBRepo } from "../repositories/deleteUserSessionFromDB.repository";
|
||||
|
||||
export const deleteUserSessionInCacheAndDBService = async (
|
||||
jwtToken: JWTAuthToken
|
||||
) => {
|
||||
try {
|
||||
const userId = jwtToken.userId;
|
||||
const sessionId = jwtToken.id;
|
||||
|
||||
await deleteUserSessionFromCacheRepo(userId, sessionId);
|
||||
const deleteUserSessionFromDB = await deleteUserSessionFromDBRepo(
|
||||
sessionId
|
||||
);
|
||||
return deleteUserSessionFromDB;
|
||||
} catch (error) {
|
||||
ErrorForwarder(
|
||||
error,
|
||||
500,
|
||||
"Delete user session service had encountered error"
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -10,6 +10,7 @@ export const getUserSessionFromDBService = async (identifier: string) => {
|
||||
if (
|
||||
!userSession ||
|
||||
!userSession.isAuthenticated ||
|
||||
userSession.deletedAt ||
|
||||
new Date(userSession.validUntil) < new Date()
|
||||
)
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user