Changed controllers to classes, added image functionality

This commit is contained in:
Torsten Schulz
2024-09-21 15:26:29 +02:00
parent e494fe41db
commit f1b6dd74f7
20 changed files with 836 additions and 581 deletions

View File

@@ -1,186 +1,188 @@
import BaseService from './BaseService.js';
import { Op, col } from 'sequelize';
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';
class SocialNetworkService extends BaseService {
async searchUsers({ username, ageFrom, ageTo, genders }) {
try {
const whereClause = {
active: true,
searchable: true
};
if (username) {
whereClause.username = { [Op.iLike]: `%${username}%` };
}
const users = await User.findAll({
where: whereClause,
include: [
{
model: UserParam,
as: 'user_params',
include: [
{
model: UserParamType,
as: 'paramType',
where: {
description: {
[Op.in]: ['gender', 'birthdate']
}
},
required: true
}
],
required: true
}
]
});
const results = [];
for (const user of users) {
const id = user.hashedId;
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 decryptedGenderValue = genderParam ? genderParam.value : null;
let gender = null;
if (decryptedGenderValue) {
const genderValue = await UserParamValue.findOne({
where: {
id: decryptedGenderValue
}
});
gender = genderValue ? genderValue.value : null;
}
const isWithinAgeRange = (!ageFrom || age >= ageFrom) && (!ageTo || age <= ageTo);
if (isWithinAgeRange && (!genders || !genders.length || (gender && genders.includes(gender))) && age >= 14) {
results.push({
id: id,
username: user.username,
email: user.email,
gender: gender,
age: age
});
}
}
return results;
} catch (error) {
console.error('Error in searchUsers:', error);
throw new Error('Error searching users');
}
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) {
try {
const requestingUser = await this.getUserByHashedId(requestingUserId);
const requestingUserParams = await this.getUserParams(requestingUser.id, ['birthdate']);
let requestingUserAge = 0;
await this.checkUserAccess(requestingUserId);
const user = await this.fetchUserProfile(hashedUserId);
if (!user) return null;
return this.constructUserProfile(user, requestingUserId);
}
for (const param of requestingUserParams) {
if (param.paramType.description === 'birthdate') {
requestingUserAge = this.calculateAge(param.value);
break;
}
}
const user = 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']]
}
]
});
if (user) {
const userParams = {};
await Promise.all(user.user_params.map(async (param) => {
const visibilityData = param.param_visibilities?.[0]?.visibility_type;
const visibility = visibilityData ? visibilityData.description : 'Invisible';
let paramValue = param.value;
let paramValueChanged = false;
try {
const parsedValue = JSON.parse(paramValue);
if (Array.isArray(parsedValue)) {
paramValue = await Promise.all(parsedValue.map(async (value) => {
if (/^\d+$/.test(value)) {
const userParamValue = await UserParamValue.findOne({
where: {
id: parseInt(value, 10),
userParamTypeId: param.paramTypeId
}
});
paramValueChanged = true;
return userParamValue ? userParamValue.value : value;
}
return value;
}));
}
} catch (e) {
}
if (!paramValueChanged) {
if (/^\d+$/.test(paramValue)) {
const userParamValue = await UserParamValue.findOne({
where: {
id: parseInt(paramValue, 10),
userParamTypeId: param.paramTypeId
}
});
if (userParamValue) {
paramValue = userParamValue.value;
}
}
}
const paramTypeDescription = param.paramType.description;
if (visibility === 'Invisible') {
return;
}
if (visibility === 'All' || (visibility === 'FriendsAndAdults' && requestingUserAge >= 18) || (visibility === 'AdultsOnly' && requestingUserAge >= 18)) {
userParams[paramTypeDescription] = {
type: param.paramType.datatype,
value: paramValue
};
if (paramTypeDescription === 'birthdate') {
userParams['age'] = { value: this.calculateAge(Date.parse(paramValue)), type: "int"};
}
}
}));
const userProfile = {
username: user.username,
registrationDate: user.registrationDate,
params: userParams
};
async createFolder(data) {
this.validateFolderData(data);
await this.checkUserAccess(data.userId);
return await Folder.create(data);
}
return userProfile;
}
return null;
} catch (error) {
console.error('Error in getProfile:', error);
throw new Error('Error getting profile');
async getFolders(userId) {
await this.checkUserAccess(userId);
return await Folder.findAll({ where: { userId } });
}
async uploadImage(imageData) {
this.validateImageData(imageData);
await this.checkUserAccess(imageData.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(userId) {
const user = await User.findByPk(userId);
if (!user || !user.active) throw new Error('Access denied: User not found or inactive');
}
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;
}
}