import { PrismaClient, ChatRole, Role } from '@prisma/client';
import { Request, Response, NextFunction } from 'express';
const prisma = new PrismaClient();
interface CustomRequest extends Request {
user?: {
id: string;
role: Role;
};
}
/**
* Middleware to check if the user allows chats from non-followers.
*
* @param {CustomRequest} req - Express request object, extended to include the user property.
* @param {Response} res - Express response object.
* @param {NextFunction} next - Express next middleware function.
* @returns {Promise<void>} - Calls `next()` if the user allows chats or the sender is a follower, otherwise returns a 403 Forbidden response.
*/
export const checkChatPermission = async (
req: CustomRequest,
res: Response,
next: NextFunction
): Promise<void> => {
if (!req.user || !req.user.id) {
res.status(401).json({ message: 'Unauthorized' });
return;
}
const { authorId } = req.body;
const userId = req.user.id;
try {
const userPreferences = await prisma.userPreferences.findUnique({
where: { userId: Number(userId) },
});
if (!userPreferences?.allowChatsFromNonFollowers) {
const isFollower = await prisma.follow.findFirst({
where: {
OR: [
{ followerId: Number(userId), followingId: Number(authorId) },
{ followerId: Number(authorId), followingId: Number(userId) },
],
},
});
if (!isFollower) {
res.status(403).json({ message: 'You only allow chats from followers' });
return;
}
}
next();
} catch (error) {
res.status(500).json({ message: 'Internal server error', error: (error as any).message });
return;
}
};
/**
* Middleware to check if the current user is an admin of a chat group.
*
* @param {CustomRequest} req - Express request object.
* @param {Response} res - Express response object.
* @param {NextFunction} next - Express next middleware function.
* @returns {Promise<void>} - Calls `next()` if the user is an admin, otherwise returns a 403 Forbidden response.
*/
export const checkAdminPermission = async (
req: CustomRequest,
res: Response,
next: NextFunction
) => {
const { chatGroupId } = req.params;
const userId = req.user?.id;
try {
// Check if the user has an ADMIN role in the chat group
const userRole = await prisma.userRoleOnChatGroup.findFirst({
where: {
chatGroupId: Number(chatGroupId),
userId: Number(userId),
role: 'ADMIN',
},
});
// If no admin role found, return forbidden
if (!userRole) {
return res.status(403).json({ message: 'You are not an admin of this group' });
}
// Proceed to next middleware if the user is an admin
next();
} catch (error) {
return res
.status(500)
.json({ message: 'Internal server error', error: (error as any).message });
}
};
/**
* Middleware to check if the current user is the owner of a chat message.
*
* @param {CustomRequest} req - Express request object.
* @param {Response} res - Express response object.
* @param {NextFunction} next - Express next middleware function.
* @returns {Promise<void>} - Calls `next()` if the user owns the message, otherwise returns a 403 Forbidden response.
*/
export const checkChatOwnership = async (req: CustomRequest, res: Response, next: NextFunction) => {
const { id } = req.params;
const userId = req.user?.id;
try {
const chatMessage = await prisma.chatMessage.findUnique({
where: { id: Number(id) },
});
if (!chatMessage || chatMessage.senderId !== Number(userId)) {
return res.status(403).json({ message: 'You do not have permission to modify this message' });
}
next();
} catch (error) {
return res
.status(500)
.json({ message: 'Internal server error', error: (error as any).message });
}
};
/**
* Middleware to check if the user is a member of a chat group.
*
* @param {CustomRequest} req - Express request object.
* @param {Response} res - Express response object.
* @param {NextFunction} next - Express next middleware function.
* @returns {Promise<void>} - Calls `next()` if the user is a group member, otherwise returns a 403 Forbidden response.
*/
export const checkGroupMembership = async (
req: CustomRequest,
res: Response,
next: NextFunction
) => {
const { chatGroupId } = req.params;
const userId = req.user?.id;
try {
const membership = await prisma.userOnChat.findFirst({
where: {
chatGroupId: Number(chatGroupId),
userId: Number(userId),
},
});
if (!membership) {
return res.status(403).json({ message: 'You are not a member of this chat group' });
}
next();
} catch (error) {
return res
.status(500)
.json({ message: 'Internal server error', error: (error as any).message });
}
};
/**
* Middleware to check if the user has been blocked from a specific chat group.
*
* @param {CustomRequest} req - Express request object.
* @param {Response} res - Express response object.
* @param {NextFunction} next - Express next middleware function.
* @returns {Promise<void>} - Calls `next()` if the user is not blocked, otherwise returns a 403 Forbidden response.
*/
export const checkBlockedInChat = async (
req: CustomRequest,
res: Response,
next: NextFunction
): Promise<void> => {
const { chatGroupId } = req.params;
const userId = req.user?.id;
try {
const blocked = await prisma.blockChat.findFirst({
where: {
blockedUserId: Number(userId),
chatGroupId: Number(chatGroupId),
},
});
if (blocked) {
res.status(403).json({ message: 'You have been blocked from this chat group' });
return;
}
next();
} catch (error) {
res.status(500).json({ message: 'Internal server error', error: (error as any).message });
return;
}
};
/**
* Middleware to check if a user has a specific role (e.g., MODERATOR, ADMIN, OWNER) in a chat group.
*
* @param {ChatRole} requiredRole - The minimum role required to perform an action.
* @returns {Function} - Express middleware function.
*/
/**
* Middleware to check if a user has a specific role (e.g., MODERATOR, ADMIN, OWNER) in a chat group.
*
* @param {ChatRole} requiredRole - The minimum role required to perform an action.
* @returns {Function} - Express middleware function.
*/
export const checkRole = (requiredRole: ChatRole) => {
return async (req: CustomRequest, res: Response, next: NextFunction) => {
const userId = req.user?.id;
const { chatGroupId } = req.params;
try {
// Use the compound unique key `userId_chatGroupId`
const userRole = await prisma.userRoleOnChatGroup.findUnique({
where: {
userId_chatGroupId: {
userId: Number(userId),
chatGroupId: Number(chatGroupId),
},
},
});
// Check if the user role exists and if the user can perform the required action
if (!userRole || !canPerformAction(userRole.role, requiredRole)) {
return res.status(403).json({ message: 'Insufficient permissions' });
}
next();
} catch (error) {
return res
.status(500)
.json({ message: 'Internal server error', error: (error as any).message });
}
};
};
/**
* Utility function to check if a user can perform an action based on their role.
*
* @param {ChatRole} userRole - The role of the user.
* @param {ChatRole} requiredRole - The minimum role required for the action.
* @returns {boolean} - True if the user has sufficient permissions, false otherwise.
*/
function canPerformAction(userRole: ChatRole, requiredRole: ChatRole): boolean {
const rolesHierarchy = {
[ChatRole.USER]: 1,
[ChatRole.MODERATOR]: 2,
[ChatRole.ADMIN]: 3,
[ChatRole.OWNER]: 4,
};
return rolesHierarchy[userRole] >= rolesHierarchy[requiredRole];
}