Erster Aufbau Forum

This commit is contained in:
Torsten Schulz
2024-10-15 16:28:42 +02:00
parent c31be3f879
commit 663564aa96
163 changed files with 9449 additions and 116 deletions

View File

@@ -4,6 +4,8 @@ import UserParamType from '../models/type/user_param.js';
import UserParamVisibility from '../models/community/user_param_visibility.js';
import UserParamVisibilityType from '../models/type/user_param_visibility.js';
import { Op } from 'sequelize';
import UserRight from '../models/community/user_right.js';
import UserRightType from '../models/type/user_right.js';
class BaseService {
async getUserByHashedId(hashedId) {
@@ -63,6 +65,33 @@ class BaseService {
return age >= 18;
}
async checkUserAccess(hashedId) {
const user = await this.getUserByHashedId(hashedId);
if (!user || !user.active) throw new Error('Access denied: User not found or inactive');
return user.id;
}
async getUserRights(userId) {
const userRights = await UserRight.findAll({
where: { userId },
include: [
{
model: UserRightType,
as: 'rightType',
attributes: ['title']
}
]
});
return userRights.map((right) => right.rightType.title);
}
async hasUserRight(userId, requiredRights) {
console.log(requiredRights);
const userRights = await this.getUserRights(userId);
return userRights.some((right) => requiredRights.includes(right));
}
}
export default BaseService;

View File

@@ -0,0 +1,280 @@
import BaseService from './BaseService.js';
import Forum from '../models/forum/forum.js';
import ForumPermission from '../models/forum/forum_permission.js';
import ForumUserPermission from '../models/forum/forum_user_permission.js';
import UserRight from '../models/community/user_right.js';
import UserRightType from '../models/type/user_right.js';
import { Op } from 'sequelize';
import { sequelize } from '../utils/sequelize.js';
import User from '../models/community/user.js';
import ForumForumPermission from '../models/forum/forum_forum_permission.js';
import Title from '../models/forum/title.js';
import Message from '../models/forum/message.js';
class ForumService extends BaseService {
async createForum(hashedUserId, name, permissions) {
const user = await this.getUserByHashedId(hashedUserId);
const isAdmin = await this.hasUserRight(user.id, ['mainadmin', 'forum']);
if (!isAdmin) {
throw new Error('Access denied: Only admins can create forums.');
}
const newForum = await Forum.create({ name });
if (permissions && permissions.length > 0) {
for (const permission of permissions) {
const forumPermission = await ForumPermission.findOne({
where: { name: permission.value }
});
if (forumPermission) {
await newForum.addAssociatedPermissions(forumPermission);
}
}
}
return newForum;
}
async deleteForum(hashedUserId, forumId) {
const user = await this.getUserByHashedId(hashedUserId);
const isAdmin = await this.hasUserRight(user.id, ['mainadmin', 'forum']);
if (!isAdmin) {
throw new Error('Access denied: Only admins can delete forums.');
}
const forum = await Forum.findByPk(forumId);
if (!forum) {
throw new Error('Forum not found.');
}
const transaction = await sequelize.transaction();
try {
const forumFormRights = await ForumForumPermission.findAll({
where: { forumId: forumId }
});
for (const forumFormRight of forumFormRights) {
await forumFormRight.destroy({ transaction });
}
const forumUserRights = await ForumUserPermission.findAll({
where: { forumId: forumId }
});
for (const forumUserRight of forumUserRights) {
await forumUserRight.destroy({ transaction });
}
await forum.destroy({ transaction });
await transaction.commit();
return forum;
} catch (error) {
await transaction.rollback();
throw new Error(`Error deleting forum: ${error.message}`);
}
}
async getAllForums(hashedUserId) {
const user = await this.getUserByHashedId(hashedUserId);
if (!user) throw new Error('User not found.');
const forums = await Forum.findAll({
include: [
{
model: ForumPermission,
as: 'associatedPermissions',
},
{
model: ForumUserPermission,
as: 'userPermissions',
}
]
});
const accessibleForums = [];
for (const forum of forums) {
const hasAccess = await this.checkForumAccess(forum, user);
if (hasAccess) {
accessibleForums.push(forum);
}
}
return accessibleForums;
}
async getForum(hashedUserId, forumId, page) {
const user = await this.getUserByHashedId(hashedUserId);
if (!user) throw new Error('User not found.');
const forum = await Forum.findByPk(forumId, {
include: [
{
model: ForumPermission,
as: 'associatedPermissions',
},
{
model: ForumUserPermission,
as: 'userPermissions',
}
]
});
const hasAccess = await this.checkForumAccess(forum, user);
if (!hasAccess) {
throw new Error('Access denied.');
}
const titlesRaw = await Title.findAll({
where: {
forumId
},
attributes: [
'id',
'title',
'createdBy',
'forumId',
'createdAt',
'updatedAt',
[sequelize.literal(`(SELECT COUNT(*) FROM forum.message WHERE message.title_id = title.id)`), 'messageCount'],
[sequelize.literal(`(SELECT MAX("created_at") FROM forum.message WHERE message.title_id = title.id)`), 'lastMessageDate']
],
include: [
{
model: User,
as: 'createdByUser',
attributes: ['id', 'username', 'hashedId']
},
{
model: Message,
as: 'messages',
attributes: [],
include: [
{
model: User,
as: 'lastMessageUser',
attributes: ['id', 'username']
}
]
}
],
offset: (page - 1) * 25,
limit: 25,
order: [
['createdAt', 'DESC']
],
group: [
'title.id',
'title.created_at',
]
});
const totalTopics = await Title.count({ where: { forumId } });
const titles = titlesRaw.map(title => ({
id: title.dataValues.id,
title: title.dataValues.title,
createdBy: title.createdByUser.username,
createdByHash: title.createdByUser.hashedId,
createdAt: title.dataValues.createdAt,
numberOfItems: title.dataValues.messageCount,
lastMessageDate: title.dataValues.lastMessageDate
}));
return {
name: forum.name,
titles,
page,
totalTopics,
};
}
async createTopic(hashedUserId, forumId, title, content) {
const user = await this.getUserByHashedId(hashedUserId);
if (!user) throw new Error('User not found.');
const forum = await Forum.findByPk(forumId, {
include: [
{
model: ForumPermission,
as: 'associatedPermissions',
},
{
model: ForumUserPermission,
as: 'userPermissions',
}
]
});
const hasAccess = await this.checkForumAccess(forum, user);
if (!hasAccess) {
throw new Error('Access denied.');
}
const newTopic = await Title.create({ title, forumId, createdBy: user.id });
await Message.create({ titleId: newTopic.id, text: content, createdBy: user.id})
return this.getForum(hashedUserId, forumId, 1);
}
async checkForumAccess(forum, user) {
console.log('[ForumService.checkForumAccess] - start');
const { age } = user;
console.log('[ForumService.checkForumAccess] - read user permissions');
const userPermission = await ForumUserPermission.findOne({
where: {
userId: user.id,
forumId: forum.id
}
});
if (userPermission) {
return true;
}
console.log('[ForumService.checkForumAccess] - read forum permissions');
const forumPermissions = await ForumForumPermission.findAll({
where: {
forumId: forum.id
}
});
console.log('[ForumService.checkForumAccess] - filter persmissions');
for (const permission of forumPermissions) {
if (permission.permission === 'age' && age >= parseInt(permission.value, 10)) {
return true;
}
if (permission.permission === 'all' || permission.permission === 'user') {
return true;
}
const userRight = await UserRight.findOne({
where: {
userId: user.id,
},
include: {
model: UserRightType,
as: 'rightType',
where: {
[Op.or]: [
{ title: 'mainadmin' },
{ title: 'forum' }
]
}
}
});
if (userRight) {
return true;
}
}
console.log('[ForumService.checkForumAccess] - not successful');
return false;
}
async setPermissions(forumId, permissions) {
if (permissions && Array.isArray(permissions)) {
for (const permission of permissions) {
await ForumPermission.create({
forumId,
permission: permission.type,
value: permission.value
});
}
}
}
async checkAdminRights(userId) {
const userRight = await UserRight.findOne({
where: { userId },
include: {
model: UserRightType,
as: 'rightType',
where: { title: ['mainadmin', 'forum'] }
}
});
if (!userRight) {
throw new Error('Unauthorized: Only admins can perform this action.');
}
}
}
export default new ForumService();

View File

@@ -22,6 +22,7 @@ import GuestbookEntry from '../models/community/guestbook.js';
import { JSDOM } from 'jsdom';
import DOMPurify from 'dompurify';
import sharp from 'sharp';
import Diary from '../models/community/diary.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -42,9 +43,7 @@ class SocialNetworkService extends BaseService {
async createFolder(hashedUserId, data, folderId) {
await this.checkUserAccess(hashedUserId);
const user = await User.findOne({
where: { hashedId: hashedUserId }
});
const user = await this.loadUserByHash(hashedUserId);
if (!user) {
throw new Error('User not found');
}
@@ -266,10 +265,12 @@ class SocialNetworkService extends BaseService {
return image;
}
async checkUserAccess(hashedId) {
const user = await User.findOne({ hashedId: hashedId });
if (!user || !user.active) throw new Error('Access denied: User not found or inactive');
return user.id;
async loadUserByHash(hashedId) {
return await User.findOne({ hashedId });
}
async loadUserByName(userName) {
return await User.findOne({ username: userName});
}
validateFolderData(data) {
@@ -354,9 +355,7 @@ class SocialNetworkService extends BaseService {
async constructUserProfile(user, hashedUserId) {
const userParams = {};
const requestingUser = await User.findOne({
where: { hashedId: hashedUserId },
});
const requestingUser = await this.loadUserByHash(hashedUserId);
if (!requestingUser) {
throw new Error('User not found');
}
@@ -461,7 +460,7 @@ class SocialNetworkService extends BaseService {
}
async getFoldersByUsername(username, hashedUserId) {
const user = await User.findOne({ where: { username } });
const user = await this.loadUserByName(username);
if (!user) {
throw new Error('User not found');
}
@@ -528,11 +527,11 @@ class SocialNetworkService extends BaseService {
}
async createGuestbookEntry(hashedSenderId, recipientName, htmlContent, image) {
const sender = await User.findOne({ where: { hashedId: hashedSenderId } });
const sender = await this.loadUserByHash(hashedSenderId);
if (!sender) {
throw new Error('Sender not found');
}
const recipient = await User.findOne({ where: { username: recipientName } });
const recipient = await this.loadUserByName(recipientName);
if (!recipient) {
throw new Error('Recipient not found');
}
@@ -594,7 +593,7 @@ class SocialNetworkService extends BaseService {
const pageSize = 20;
const offset = (page - 1) * pageSize;
this.checkUserAccess(hashedUserId);
const user = await User.findOne({ where: { username: username } });
const user = await this.loadUserByName(username);
if (!user) {
throw new Error('User not found');
}
@@ -624,9 +623,7 @@ class SocialNetworkService extends BaseService {
async getGuestbookImageFilePath(hashedUserId, guestbookOwnerName, entryId) {
await this.checkUserAccess(hashedUserId);
const guestbookOwner = await User.findOne({
where: { username: guestbookOwnerName },
});
const guestbookOwner = await this.loadUserByName(guestbookOwnerName);
if (!guestbookOwner) {
throw new Error('usernotfound');
}
@@ -649,7 +646,7 @@ class SocialNetworkService extends BaseService {
}
async deleteGuestbookEntry(hashedUserId, entryId) {
const user = await User.findOne({ where: { hashedId: hashedUserId } });
const user = await this.loadUserByHash(hashedUserId);
if (!user) {
throw new Error('User not found');
}
@@ -663,7 +660,8 @@ class SocialNetworkService extends BaseService {
await entry.destroy();
}
async createDiaryEntry(userId, text) {
async createDiaryEntry(hashedUserId, text) {
const userId = await this.checkUserAccess(hashedUserId);
const newEntry = await Diary.create({
userId: userId,
text: text,
@@ -673,29 +671,24 @@ class SocialNetworkService extends BaseService {
return newEntry;
}
async updateDiaryEntry(diaryId, userId, newText) {
async updateDiaryEntry(diaryEntryId, hashedUserId, newText) {
const userId = await this.checkUserAccess(hashedUserId);
const existingEntry = await Diary.findOne({
where: { id: diaryId, userId: userId }
where: { id: diaryEntryId, userId: userId }
});
if (!existingEntry) {
throw new Error('Diary entry not found or unauthorized access');
}
await DiaryHistory.create({
diaryId: existingEntry.id,
userId: existingEntry.userId,
oldText: existingEntry.text,
oldCreatedAt: existingEntry.createdAt,
oldUpdatedAt: existingEntry.updatedAt,
});
existingEntry.text = newText;
existingEntry.updatedAt = new Date();
await existingEntry.save();
return existingEntry;
}
async deleteDiaryEntry(diaryId, userId) {
async deleteDiaryEntry(diaryEntryId, hashedUserId) {
const userId = await this.checkUserAccess(hashedUserId);
const entryToDelete = await Diary.findOne({
where: { id: diaryId, userId: userId }
where: { id: diaryEntryId, userId: userId }
});
if (!entryToDelete) {
throw new Error('Diary entry not found or unauthorized access');
@@ -704,13 +697,15 @@ class SocialNetworkService extends BaseService {
return true;
}
async getDiaryEntries(userId) {
const entries = await Diary.findAll({
async getDiaryEntries(hashedUserId, page) {
const userId = await this.checkUserAccess(hashedUserId);
const entries = await Diary.findAndCountAll({
where: { userId: userId },
order: [['createdAt', 'DESC']]
order: [['createdAt', 'DESC']],
offset: (page - 1) * 20,
limit: 20,
});
return entries;
return { entries: entries.rows, totalPages: Math.ceil(entries.count / 20) };
}
}
export default SocialNetworkService;
export default SocialNetworkService;