add first operation in user role

This commit is contained in:
rafiarrafif
2025-05-06 18:48:33 +07:00
parent 9e90d7c4f4
commit 40a7ffc005
9 changed files with 241 additions and 3 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -8,7 +8,11 @@
},
"dependencies": {
"@prisma/client": "^6.7.0",
"elysia": "latest"
"@types/jsonwebtoken": "^9.0.9",
"cookie": "^1.0.2",
"elysia": "latest",
"joi": "^17.13.3",
"jsonwebtoken": "^9.0.2"
},
"devDependencies": {
"bun-types": "latest",

View File

@ -0,0 +1,46 @@
import jwt from "jsonwebtoken";
import { Context } from "elysia";
import { JWTAuthToken } from "./types";
import { returnErrorResponse } from "../../callback/httpResponse";
import { parse } from "cookie";
/**
* Verifies the authentication cookie from the request header.
*
* This helper function is used in an ElysiaJS context to check the validity of
* a user's authentication token stored in cookies. If the cookie is not found,
* it returns a `400 Bad Request`. If the token is invalid or expired, it returns
* a `401 Unauthorized`. If the token is valid, it returns the decoded user data.
*
* @param ctx - The request context from Elysia, used to read headers and set the response.
*
* @returns The decoded JWT payload if the token is valid,
* or a standardized error response if the cookie is missing or invalid.
*
* @example
* const decodedToken = decodeAuthToken(ctx);
* ctx => Elysia context
*/
export const JWTDecodeToken = (ctx: Context): JWTAuthToken => {
const cookiePayload = ctx.request.headers.get("Cookie");
if (!cookiePayload)
throw returnErrorResponse(ctx.set, 400, "Bad Request", "No cookies found");
const cookies = parse(cookiePayload);
const cookiesToken = cookies.auth_token!;
try {
const decodedToken = jwt.verify(
cookiesToken,
process.env.JWT_SECRET!
) as JWTAuthToken;
return decodedToken;
} catch (error) {
throw returnErrorResponse(
ctx.set,
401,
"Unauthorized",
"Invalid or expired token"
);
}
};

View File

@ -0,0 +1,62 @@
export interface JWTAuthToken {
id: string;
isAuthenticated: boolean;
userId: string;
deviceType: string;
deviceOs: string;
deviceIp: string;
isOnline: boolean;
lastOnline: Date;
validUntil: Date;
deletedAt: null;
createdAt: Date;
updatedAt: Date;
user: User;
iat: number;
exp: number;
}
interface User {
id: string;
name: string;
username: string;
email: string;
birthDate: null;
gender: null;
phoneCC: null;
phoneNumber: null;
bioProfile: null;
profilePicture: null;
commentPicture: null;
preferenceId: null;
verifiedAt: null;
disabledAt: null;
deletedAt: null;
createdAt: Date;
updatedAt: Date;
roles: Role[];
}
interface Role {
id: string;
name: string;
primaryColor: string;
secondaryColor: string;
pictureImage: string;
badgeImage: null;
isSuperadmin: boolean;
canEditMedia: boolean;
canManageMedia: boolean;
canEditEpisodes: boolean;
canManageEpisodes: boolean;
canEditComment: boolean;
canManageComment: boolean;
canEditUser: boolean;
canManageUser: boolean;
canEditSystem: boolean;
canManageSystem: boolean;
createdBy: string;
deletedAt: null;
createdAt: Date;
updatedAt: Date;
}

View File

@ -1,5 +1,80 @@
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
import {
returnErrorResponse,
returnWriteResponse,
} from "../../../helpers/callback/httpResponse";
import { handlePrismaError } from "../../../utils/databases/prisma/error/handler";
import { createUserRoleSchema } from "../userRole.schema";
import { JWTDecodeToken } from "../../../helpers/jwt/decodeToken";
import { prisma } from "../../../utils/databases/prisma/connection";
import { createUserRoleService } from "../services/createUserRole.service";
export const createUserRole = (ctx: Context) => {
return "Hello User Role Module";
/**
* @function createUserRole
* @description Creates a new user role in the database.
*
* @param {Context & { body: UserRole }} ctx - The context object containing the request body.
* @param {UserRole} ctx.body - The user role data to be created.
*
* @returns {Promise<Object>} A response object indicating success or failure.
* @throws {Object} An error response object if validation fails or an error occurs during role creation.
*
* @example
* Request route: POST /roles
* Request body:
* {
* "userID": "e31668e6-c261-4a7e-9469-ffad734cf2dd",
* "name": "Admin",
* "primaryColor": "#D9D9D9",
* "secondaryColor": "#FFFFFF",
* "pictureImage": "https://example.com/picture.jpg",
* "badgeImage": "https://example.com/badge.jpg",
* "isSuperadmin": false,
* "canEditMedia": false,
* "canManageMedia": false,
* "canEditEpisodes": false,
* "canManageEpisodes": false,
* "canEditComment": false,
* "canManageComment": false,
* "canEditUser": false,
* "canManageUser": false,
* "canEditSystem": false,
* "canManageSystem": false
* }
*/
export const createUserRole = async (
ctx: Context & { body: Prisma.UserRoleUncheckedCreateInput }
) => {
const { error } = createUserRoleSchema.validate(ctx.body);
if (error)
return returnErrorResponse(ctx.set, 400, "Invalid user input", error);
const formData: Prisma.UserRoleUncheckedCreateInput = { ...ctx.body };
const userCreator = JWTDecodeToken(ctx);
const dataPayload = {
...formData,
isSuperadmin: Boolean(formData.isSuperadmin),
canEditMedia: Boolean(formData.canEditMedia),
canManageMedia: Boolean(formData.canManageMedia),
canEditEpisodes: Boolean(formData.canEditEpisodes),
canManageEpisodes: Boolean(formData.canManageEpisodes),
canEditComment: Boolean(formData.canEditComment),
canManageComment: Boolean(formData.canManageComment),
canEditUser: Boolean(formData.canEditUser),
canManageUser: Boolean(formData.canManageUser),
canEditSystem: Boolean(formData.canEditSystem),
canManageSystem: Boolean(formData.canManageSystem),
createdBy: userCreator.user.id,
deletedAt: null,
};
createUserRoleService(dataPayload)
.then((result) =>
returnWriteResponse(ctx.set, 201, "User role created", result)
)
.catch((error) =>
returnErrorResponse(ctx.set, 500, "Internal Server Error", error)
);
};

View File

@ -0,0 +1,20 @@
import { Prisma } from "@prisma/client";
import { userRoleModel } from "../userRole.model";
import { handlePrismaError } from "../../../utils/databases/prisma/error/handler";
import { returnErrorResponse } from "../../../helpers/callback/httpResponse";
import { Context } from "elysia";
export const createUserRoleService = async (
ctx: Context,
userRoleData: Prisma.UserRoleUncheckedCreateInput
) => {
try {
const newUserRole = await userRoleModel.create({
data: userRoleData,
});
return newUserRole;
} catch (error) {
const { status, message, details } = handlePrismaError(error);
throw returnErrorResponse(ctx.set, status, message, details);
}
};

View File

@ -0,0 +1,3 @@
import { prisma } from "../../utils/databases/prisma/connection";
export const userRoleModel = prisma.userRole;

View File

@ -0,0 +1,28 @@
import Joi from "joi";
export const createUserRoleSchema = Joi.object({
name: Joi.string().min(4).max(255).required(),
primaryColor: Joi.string()
.pattern(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/)
.optional(),
secondaryColor: Joi.string()
.pattern(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/)
.optional(),
pictureImage: Joi.string()
.uri({ scheme: ["http", "https"] })
.optional(),
badgeImage: Joi.string()
.uri({ scheme: ["http", "https"] })
.optional(),
isSuperadmin: Joi.boolean().required(),
canEditMedia: Joi.boolean().required(),
canManageMedia: Joi.boolean().required(),
canEditEpisodes: Joi.boolean().required(),
canManageEpisodes: Joi.boolean().required(),
canEditComment: Joi.boolean().required(),
canManageComment: Joi.boolean().required(),
canEditUser: Joi.boolean().required(),
canManageUser: Joi.boolean().required(),
canEditSystem: Joi.boolean().required(),
canManageSystem: Joi.boolean().required(),
});