diff --git a/bun.lockb b/bun.lockb index 9e8bf1e..4fb12fd 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index b677444..0942d5e 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "cookie": "^1.0.2", "elysia": "latest", "joi": "^17.13.3", - "jsonwebtoken": "^9.0.2" + "jsonwebtoken": "^9.0.2", + "ua-parser-js": "^2.0.3" }, "devDependencies": { "bun-types": "latest", diff --git a/src/helpers/cookies/userHeader/getUserHeaderInformation/index.ts b/src/helpers/cookies/userHeader/getUserHeaderInformation/index.ts new file mode 100644 index 0000000..b9dd853 --- /dev/null +++ b/src/helpers/cookies/userHeader/getUserHeaderInformation/index.ts @@ -0,0 +1,26 @@ +import { Context } from "elysia"; +import { UAParser } from "ua-parser-js"; +import { UserHeaderInformation } from "./types"; + +export const getUserHeaderInformation = ( + ctx: Context +): UserHeaderInformation => { + const headers = ctx.request.headers; + const userAgentHeader = headers.get("user-agent") || "desktop"; + const userAgent = new UAParser(userAgentHeader); + + const userIP = + headers.get("cf-connecting-ip") || + headers.get("x-real-ip") || + headers.get("x-forwarded-for")?.split(",")[0] || + "Unknown IP"; + + const userHeaderInformation = { + ip: userIP, + deviceType: userAgent.getDevice().type || "desktop", + deviceOS: userAgent.getOS().name + " " + userAgent.getOS().version, + browser: userAgent.getBrowser().name + " " + userAgent.getBrowser().version, + }; + + return userHeaderInformation; +}; diff --git a/src/helpers/cookies/userHeader/getUserHeaderInformation/types.ts b/src/helpers/cookies/userHeader/getUserHeaderInformation/types.ts new file mode 100644 index 0000000..f1a54d1 --- /dev/null +++ b/src/helpers/cookies/userHeader/getUserHeaderInformation/types.ts @@ -0,0 +1,6 @@ +export interface UserHeaderInformation { + ip: string; + deviceType: string; + deviceOS: string; + browser: string; +} diff --git a/src/modules/userSession/controller/createUserSession.controller.ts b/src/modules/userSession/controller/createUserSession.controller.ts new file mode 100644 index 0000000..c5a0ff3 --- /dev/null +++ b/src/modules/userSession/controller/createUserSession.controller.ts @@ -0,0 +1,30 @@ +import { Context } from "elysia"; +import { createUserSessionService } from "../services/createUserSession.service"; +import { getUserHeaderInformation } from "../../../helpers/cookies/userHeader/getUserHeaderInformation"; +import { handlePrismaError } from "../../../utils/databases/prisma/error/handler"; +import { + returnErrorResponse, + returnWriteResponse, +} from "../../../helpers/callback/httpResponse"; + +export const createUserSessionRole = async ( + ctx: Context & { body: { userId: string } } +) => { + const userHeaderData = getUserHeaderInformation(ctx); + + try { + const newUserSession = await createUserSessionService({ + userId: ctx.body.userId, + userHeaderInformation: userHeaderData, + }); + return returnWriteResponse( + ctx.set, + 201, + "User session created", + newUserSession + ); + } catch (error) { + const { status, message, details } = handlePrismaError(error); + return returnErrorResponse(ctx.set, status, message, details); + } +}; diff --git a/src/modules/userSession/index.ts b/src/modules/userSession/index.ts new file mode 100644 index 0000000..d4087c2 --- /dev/null +++ b/src/modules/userSession/index.ts @@ -0,0 +1,7 @@ +import Elysia from "elysia"; +import { createUserSessionRole } from "./controller/createUserSession.controller"; + +export const userSessionModule = new Elysia({ prefix: "/user-sessions" }).post( + "/", + createUserSessionRole +); diff --git a/src/modules/userSession/services/createUserSession.service.ts b/src/modules/userSession/services/createUserSession.service.ts new file mode 100644 index 0000000..c940e18 --- /dev/null +++ b/src/modules/userSession/services/createUserSession.service.ts @@ -0,0 +1,20 @@ +import { createUserSessionServiceParams } from "../userSession.types"; +import { createUserSessionRepo } from "../userSession.repository"; + +export const createUserSessionService = async ( + data: createUserSessionServiceParams +) => { + const sessionLifetime = Number(process.env.SESSION_EXPIRE!); + try { + const newUserSession = await createUserSessionRepo({ + userId: data.userId, + isAuthenticated: true, + deviceType: data.userHeaderInformation.deviceType, + deviceOs: data.userHeaderInformation.deviceOS, + deviceIp: data.userHeaderInformation.ip, + validUntil: new Date(new Date().getTime() + sessionLifetime * 1000), + }); + } catch (error) { + throw error; + } +}; diff --git a/src/modules/userSession/userSession.model.ts b/src/modules/userSession/userSession.model.ts new file mode 100644 index 0000000..6c993a4 --- /dev/null +++ b/src/modules/userSession/userSession.model.ts @@ -0,0 +1,3 @@ +import { prisma } from "../../utils/databases/prisma/connection"; + +export const userSessionModel = prisma.userSession; diff --git a/src/modules/userSession/userSession.repository.ts b/src/modules/userSession/userSession.repository.ts new file mode 100644 index 0000000..4f2f945 --- /dev/null +++ b/src/modules/userSession/userSession.repository.ts @@ -0,0 +1,25 @@ +import { Prisma } from "@prisma/client"; +import { userSessionModel } from "./userSession.model"; + +export const createUserSessionRepo = async ( + data: Prisma.UserSessionUncheckedCreateInput +) => { + try { + const newUserRole = await userSessionModel.create({ + data: data, + include: { + user: { + omit: { + password: true, + }, + include: { + roles: true, + }, + }, + }, + }); + return newUserRole; + } catch (error) { + throw error; + } +}; diff --git a/src/modules/userSession/userSession.schema.ts b/src/modules/userSession/userSession.schema.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/userSession/userSession.types.ts b/src/modules/userSession/userSession.types.ts new file mode 100644 index 0000000..c4bd913 --- /dev/null +++ b/src/modules/userSession/userSession.types.ts @@ -0,0 +1,6 @@ +import { UserHeaderInformation } from "../../helpers/cookies/userHeader/getUserHeaderInformation/types"; + +export interface createUserSessionServiceParams { + userId: string; + userHeaderInformation: UserHeaderInformation; +}