import BaseService from './BaseService.js'; import { Op } from 'sequelize'; import User from '../models/community/user.js'; import UserParam from '../models/community/user_param.js'; import UserParamType from '../models/type/user_param.js'; import UserParamValue from '../models/type/user_param_value.js'; import UserParamVisibility from '../models/community/user_param_visibility.js'; import UserParamVisibilityType from '../models/type/user_param_visibility.js'; import Folder from '../models/community/folder.js'; import Image from '../models/community/image.js'; import ImageVisibilityType from '../models/type/image_visibility.js'; import FolderImageVisibility from '../models/community/folder_image_visibility.js'; class SocialNetworkService extends BaseService { async searchUsers({ username, ageFrom, ageTo, genders }) { const whereClause = this.buildSearchWhereClause(username); const users = await User.findAll({ where: whereClause, include: this.getUserParamsInclude() }); return this.filterUsersByCriteria(users, ageFrom, ageTo, genders); } async getProfile(hashedUserId, requestingUserId) { await this.checkUserAccess(requestingUserId); const user = await this.fetchUserProfile(hashedUserId); if (!user) return null; return this.constructUserProfile(user, requestingUserId); } async createFolder(hashedUserId, data) { await this.checkUserAccess(hashedUserId); const user = await User.findOne({ hashedId: hashedUserId }); const parentFolder = Folder.findOne({ id: data.parentId, userId: user.id }); if (!parentFolder) { throw new Error('foldernotfound'); } const newFolder = await Folder.create({ parentId: data.parentId, userId: user.id, name: data.name }); for (const visibilityId of data.visibilities) { await FolderImageVisibility.create({ folderId: newFolder.id, visibilityTypeId: visibilityId }); } return newFolder; } async getFolders(hashedId) { const userId = await this.checkUserAccess(hashedId); let rootFolder = await Folder.findOne({ where: { parentId: null, userId } }); if (!rootFolder) { const user = await User.findOne({ where: { id: userId } }); const visibility = await ImageVisibilityType.findOne({ where: { description: 'everyone' } }); rootFolder = await Folder.create({ name: user.username, parentId: null, userId, visibilityTypeId: visibility.id }); } const children = await this.getSubFolders(rootFolder.id, userId); rootFolder = rootFolder.get(); rootFolder.children = children; return rootFolder; } async getSubFolders(parentId, userId) { const folders = await Folder.findAll({ where: { parentId, userId } }); for (const folder of folders) { const children = await this.getSubFolders(folder.id, userId); folder.setDataValue('children', children); } return folders.map(folder => folder.get()); } async getFolderImageList(hashedId, folderId) { const userId = await this.checkUserAccess(hashedId); const folder = await Folder.findOne({ where: { id: folderId, userId } }); if (!folder) throw new Error('Folder not found'); return await Image.findAll({ where: { folderId: folder.id } }); } async uploadImage(hashedId, imageData) { this.validateImageData(imageData); const userId = await this.checkUserAccess(hashedId); imageData.id = userId; return await Image.create(imageData); } async getImage(imageId) { const image = await Image.findByPk(imageId); if (!image) throw new Error('Image not found'); await this.checkUserAccess(image.userId); 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; } validateFolderData(data) { if (!data.name || typeof data.name !== 'string') throw new Error('Invalid folder data: Name is required'); } validateImageData(imageData) { if (!imageData.url || typeof imageData.url !== 'string') throw new Error('Invalid image data: URL is required'); } buildSearchWhereClause(username) { const whereClause = { active: true, searchable: true }; if (username) { whereClause.username = { [Op.iLike]: `%${username}%` }; } return whereClause; } getUserParamsInclude() { return [ { model: UserParam, as: 'user_params', include: [{ model: UserParamType, as: 'paramType', required: true }] } ]; } filterUsersByCriteria(users, ageFrom, ageTo, genders) { const results = []; for (const user of users) { const userDetails = this.extractUserDetails(user); if (this.isUserValid(userDetails, ageFrom, ageTo, genders)) { results.push(userDetails); } } return results; } extractUserDetails(user) { const birthdateParam = user.user_params.find(param => param.paramType.description === 'birthdate'); const genderParam = user.user_params.find(param => param.paramType.description === 'gender'); const age = birthdateParam ? this.calculateAge(birthdateParam.value) : null; const gender = genderParam ? this.getGenderValue(genderParam.value) : null; return { id: user.hashedId, username: user.username, email: user.email, gender, age }; } async getGenderValue(genderId) { const genderValue = await UserParamValue.findOne({ where: { id: genderId } }); return genderValue ? genderValue.value : null; } isUserValid(userDetails, ageFrom, ageTo, genders) { const { age, gender } = userDetails; const isWithinAgeRange = (!ageFrom || age >= ageFrom) && (!ageTo || age <= ageTo); const isGenderValid = !genders || !genders.length || (gender && genders.includes(gender)); return isWithinAgeRange && isGenderValid && age >= 14; } async fetchUserProfile(hashedUserId) { return await User.findOne({ where: { hashedId: hashedUserId, active: true, searchable: true }, include: [ { model: UserParam, as: 'user_params', include: [ { model: UserParamType, as: 'paramType' }, { model: UserParamVisibility, as: 'param_visibilities', include: [{ model: UserParamVisibilityType, as: 'visibility_type' }] } ], order: [['order_id', 'asc']] } ] }); } async constructUserProfile(user, requestingUserId) { const userParams = {}; const requestingUserAge = await this.getUserAge(requestingUserId); for (const param of user.user_params) { const visibility = param.param_visibilities?.[0]?.visibility_type?.description || 'Invisible'; if (visibility === 'Invisible') continue; if (this.isVisibleToUser(visibility, requestingUserAge)) { userParams[param.paramType.description] = { type: param.paramType.datatype, value: await this.getParamValue(param) }; } } return { username: user.username, registrationDate: user.registrationDate, params: userParams }; } async getUserAge(userId) { const params = await this.getUserParams(userId, ['birthdate']); const birthdateParam = params.find(param => param.paramType.description === 'birthdate'); return birthdateParam ? this.calculateAge(birthdateParam.value) : 0; } isVisibleToUser(visibility, requestingUserAge) { return visibility === 'All' || (visibility === 'FriendsAndAdults' && requestingUserAge >= 18) || (visibility === 'AdultsOnly' && requestingUserAge >= 18); } async getParamValue(param) { let paramValue = param.value; try { const parsedValue = JSON.parse(paramValue); if (Array.isArray(parsedValue)) { paramValue = await Promise.all(parsedValue.map(value => this.getValueFromDatabase(value, param.paramTypeId))); } else if (/^\d+$/.test(paramValue)) { paramValue = await this.getValueFromDatabase(paramValue, param.paramTypeId); } } catch (e) { } return paramValue; } async getValueFromDatabase(value, paramTypeId) { const userParamValue = await UserParamValue.findOne({ where: { id: parseInt(value, 10), userParamTypeId: paramTypeId } }); return userParamValue ? userParamValue.value : value; } async getPossibleImageVisibilities() { return await ImageVisibilityType.findAll(); } } export default SocialNetworkService;