261 lines
9.5 KiB
JavaScript
261 lines
9.5 KiB
JavaScript
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;
|