import RoomType from '../models/chat/room_type.js'; import ChatRight from '../models/chat/rights.js'; import UserRight from "../models/community/user_right.js"; import UserRightType from "../models/type/user_right.js"; import InterestType from "../models/type/interest.js"; import InterestTranslationType from "../models/type/interest_translation.js"; import User from "../models/community/user.js"; import UserParamValue from "../models/type/user_param_value.js"; import UserParamType from "../models/type/user_param.js"; import ContactMessage from "../models/service/contactmessage.js"; import ContactService from "./ContactService.js"; import { sendAnswerEmail } from './emailService.js'; import { Op } from 'sequelize'; import FalukantUser from "../models/falukant/data/user.js"; import FalukantCharacter from "../models/falukant/data/character.js"; import FalukantPredefineFirstname from "../models/falukant/predefine/firstname.js"; import FalukantPredefineLastname from "../models/falukant/predefine/lastname.js"; import Branch from "../models/falukant/data/branch.js"; import FalukantStock from "../models/falukant/data/stock.js"; import FalukantStockType from "../models/falukant/type/stock.js"; import RegionData from "../models/falukant/data/region.js"; import BranchType from "../models/falukant/type/branch.js"; import Room from '../models/chat/room.js'; class AdminService { async hasUserAccess(userId, section) { const userRights = await UserRight.findAll({ include: [{ model: UserRightType, as: 'rightType', where: { title: [section, 'mainadmin'], } }, { model: User, as: 'user_with_rights', where: { hashedId: userId, } } ] }); return userRights.length > 0; } async getOpenInterests(userId) { if (!this.hasUserAccess(userId, 'interests')) { throw new Error('noaccess'); } const openInterests = await InterestType.findAll({ where: { allowed: false }, include: { model: InterestTranslationType, as: 'interest_translations', } }) return openInterests; } async changeInterest(userId, interestId, active, adultOnly) { if (!this.hasUserAccess(userId, 'interests')) { throw new Error('noaccess'); } const interest = await InterestType.findOne({ where: { id: interestId } }); if (interest) { interest.allowed = active; interest.adultOnly = adultOnly; await interest.save(); } } async deleteInterest(userId, interestId) { if (!this.hasUserAccess(userId, 'interests')) { throw new Error('noaccess'); } const interest = await InterestType.findOne({ where: { id: interestId } }); if (interest) { await interest.destroy(); } } async changeTranslation(userId, interestId, translations) { if (!this.hasUserAccess(userId, 'interests')) { throw new Error('noaccess'); } const interest = await InterestType.findOne({ id: interestId }); if (!interest) { throw new Error('notexisting'); } for (const languageId of Object.keys(translations)) { const languageObject = await UserParamValue.findOne( { where: { id: languageId } } ); if (!languageObject) { throw new Error('wronglanguage'); } const translation = await InterestTranslationType.findOne( { where: { interestsId: interestId, language: languageObject.id } } ); if (translation) { translation.translation = translations[languageId]; translation.save(); } else { await InterestTranslationType.create({ interestsId: interestId, language: languageObject.id, translation: translations[languageId] }); } } } async getOpenContacts(userId) { if (!this.hasUserAccess(userId, 'contacts')) { throw new Error('noaccess'); } const openContacts = await ContactMessage.findAll({ where: { isFinished: false, } }) return openContacts; } async answerContact(contactId, answer) { const contact = await ContactService.getContactById(contactId); await ContactService.saveAnswer(contact, answer); await sendAnswerEmail(contact.email, answer, contact.language || 'en'); } async getFalukantUser(userId, userName, characterName) { if (!(await this.hasUserAccess(userId, 'falukantusers'))) { throw new Error('noaccess'); } let users; if (userName) { users = await User.findAll({ where: { username: { [Op.like]: '%' + userName + '%' } }, include: [{ model: FalukantUser, as: 'falukantData', required: true, include: [{ model: FalukantCharacter, as: 'character', required: true, include: [{ model: FalukantPredefineFirstname, as: 'definedFirstName', required: true }, { model: FalukantPredefineLastname, as: 'definedLastName', required: true }] }] }] }); } else if (characterName) { const [firstname, lastname] = characterName.split(' '); users = await User.findAll({ include: [{ model: FalukantUser, as: 'falukantData', required: true, include: [{ model: FalukantCharacter, as: 'character', required: true, include: [{ model: FalukantPredefineFirstname, as: 'definedFirstName', required: true, where: { name: firstname } }, { model: FalukantPredefineLastname, as: 'definedLastName', required: true, where: { name: lastname } }] }] }] }); } else { throw new Error('no search parameter'); } return users.map(user => { return { id: user.hashedId, username: user.username, falukantUser: user.falukantData } }); } async getFalukantUserById(userId, hashedId) { if (!(await this.hasUserAccess(userId, 'falukantusers'))) { throw new Error('noaccess'); } const user = await User.findOne({ where: { hashedId: hashedId }, attributes: ['hashedId', 'username'], include: [{ model: FalukantUser, as: 'falukantData', required: true, attributes: ['money', 'certificate', 'id'], include: [{ model: FalukantCharacter, as: 'character', attributes: ['birthdate', 'health', 'title_of_nobility'], include: [{ model: FalukantPredefineFirstname, as: 'definedFirstName', }, { model: FalukantPredefineLastname, as: 'definedLastName', }] }] }] }); return user; } async getFalukantUserBranches(userId, falukantUserId) { if (!(await this.hasUserAccess(userId, 'falukantusers'))) { throw new Error('noaccess'); } try { // Zuerst die Branches laden const branches = await Branch.findAll({ where: { falukantUserId: falukantUserId } }); // Dann für jede Branch die zusätzlichen Daten laden const branchesWithData = await Promise.all(branches.map(async (branch) => { const region = await RegionData.findByPk(branch.regionId); const branchType = await BranchType.findByPk(branch.branchTypeId); const stocks = await FalukantStock.findAll({ where: { branchId: branch.id }, include: [{ model: FalukantStockType, as: 'stockType', attributes: ['labelTr'] }] }); return { ...branch.toJSON(), region: region ? { name: region.name } : null, branchType: branchType ? { labelTr: branchType.labelTr } : null, stocks: stocks }; })); return branchesWithData; } catch (error) { console.error('Error in getFalukantUserBranches:', error); throw error; } } async updateFalukantStock(userId, stockId, quantity) { if (!(await this.hasUserAccess(userId, 'falukantusers'))) { throw new Error('noaccess'); } const stock = await FalukantStock.findByPk(stockId); if (!stock) { throw new Error('Stock not found'); } stock.quantity = quantity; await stock.save(); return stock; } async addFalukantStock(userId, branchId, stockTypeId, quantity) { if (!(await this.hasUserAccess(userId, 'falukantusers'))) { throw new Error('noaccess'); } // Prüfe ob Branch existiert const branch = await Branch.findByPk(branchId); if (!branch) { throw new Error('Branch not found'); } // Prüfe ob StockType existiert const stockType = await FalukantStockType.findByPk(stockTypeId); if (!stockType) { throw new Error('Stock type not found'); } // Prüfe ob bereits ein Stock dieses Typs für diesen Branch existiert const existingStock = await FalukantStock.findOne({ where: { branchId: branchId, stockTypeId: stockTypeId } }); if (existingStock) { throw new Error('Stock of this type already exists for this branch'); } // Erstelle neuen Stock const newStock = await FalukantStock.create({ branchId: branchId, stockTypeId: stockTypeId, quantity: quantity }); // Lade den neuen Stock mit allen Beziehungen const stockWithData = await FalukantStock.findByPk(newStock.id, { include: [{ model: FalukantStockType, as: 'stockType', attributes: ['labelTr'] }] }); return stockWithData; } async getFalukantStockTypes(userId) { if (!(await this.hasUserAccess(userId, 'falukantusers'))) { throw new Error('noaccess'); } const stockTypes = await FalukantStockType.findAll({ attributes: ['id', 'labelTr'] }); return stockTypes; } async changeFalukantUser(userId, falukantUserId, falukantData) { if (!(await this.hasUserAccess(userId, 'falukantusers'))) { throw new Error('noaccess'); } const falukantUser = await FalukantUser.findOne({ where: { id: falukantUserId } }); if (!falukantUser) { throw new Error('notfound'); } const character = await FalukantCharacter.findOne({ where: { userId: falukantUserId } }); if (!character) { throw new Error('notfound'); } if (Object.keys(falukantData).indexOf('age') >= 0) { const birthDate = (new Date()) - (falukantData.age * 24 * 3600000); await character.update({ birthdate: birthDate }); } if (Object.keys(falukantData).indexOf('money') >= 0) { await falukantUser.update({ money: falukantData.money }); } if (Object.keys(falukantData).indexOf('title_of_nobility') >= 0) { await character.update({ titleOfNobility: falukantData.title_of_nobility }); } await falukantUser.save(); await character.save(); } // --- Chat Room Admin --- async getRoomTypes(userId) { if (!(await this.hasUserAccess(userId, 'chatrooms'))) { throw new Error('noaccess'); } return await RoomType.findAll(); } async getGenderRestrictions(userId) { if (!(await this.hasUserAccess(userId, 'chatrooms'))) { throw new Error('noaccess'); } // Find the UserParamType for gender restriction (e.g. description = 'gender') const genderType = await UserParamType.findOne({ where: { description: 'gender' } }); if (!genderType) return []; return await UserParamValue.findAll({ where: { userParamTypeId: genderType.id } }); } async getUserRights(userId) { if (!(await this.hasUserAccess(userId, 'chatrooms'))) { throw new Error('noaccess'); } return await ChatRight.findAll(); } async getRooms(userId) { if (!(await this.hasUserAccess(userId, 'chatrooms'))) { throw new Error('noaccess'); } // Only return necessary fields to the frontend return await Room.findAll({ attributes: [ 'id', 'title', 'roomTypeId', 'isPublic', 'genderRestrictionId', 'minAge', 'maxAge', 'friendsOfOwnerOnly', 'requiredUserRightId', 'password' // only if needed for editing, otherwise remove ], include: [ { model: RoomType, as: 'roomType' }, { model: UserParamValue, as: 'genderRestriction' }, ] }); } async updateRoom(userId, id, data) { if (!(await this.hasUserAccess(userId, 'chatrooms'))) { throw new Error('noaccess'); } const room = await Room.findByPk(id); if (!room) throw new Error('Room not found'); await room.update(data); return room; } async createRoom(userId, data) { if (!(await this.hasUserAccess(userId, 'chatrooms'))) { throw new Error('noaccess'); } return await Room.create(data); } async deleteRoom(userId, id) { if (!(await this.hasUserAccess(userId, 'chatrooms'))) { throw new Error('noaccess'); } return await Room.destroy({ where: { id } }); } // --- Match3 Admin Methods --- async getMatch3Campaigns(userId) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Campaign = (await import('../models/match3/campaign.js')).default; return await Match3Campaign.findAll({ include: [{ model: (await import('../models/match3/level.js')).default, as: 'levels', include: [{ model: (await import('../models/match3/objective.js')).default, as: 'objectives', required: false }], required: false }] }); } async getMatch3Campaign(userId, id) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Campaign = (await import('../models/match3/campaign.js')).default; return await Match3Campaign.findByPk(id, { include: [{ model: (await import('../models/match3/level.js')).default, as: 'levels', include: [{ model: (await import('../models/match3/objective.js')).default, as: 'objectives', required: false }], required: false }] }); } async createMatch3Campaign(userId, data) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Campaign = (await import('../models/match3/campaign.js')).default; return await Match3Campaign.create(data); } async updateMatch3Campaign(userId, id, data) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Campaign = (await import('../models/match3/campaign.js')).default; const campaign = await Match3Campaign.findByPk(id); if (!campaign) throw new Error('Campaign not found'); await campaign.update(data); return campaign; } async deleteMatch3Campaign(userId, id) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Campaign = (await import('../models/match3/campaign.js')).default; return await Match3Campaign.destroy({ where: { id } }); } async getMatch3Levels(userId) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Level = (await import('../models/match3/level.js')).default; return await Match3Level.findAll({ include: [ { model: (await import('../models/match3/campaign.js')).default, as: 'campaign', required: false }, { model: (await import('../models/match3/objective.js')).default, as: 'objectives', required: false } ], order: [['order', 'ASC']] }); } async getMatch3Level(userId, id) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Level = (await import('../models/match3/level.js')).default; return await Match3Level.findByPk(id, { include: [ { model: (await import('../models/match3/campaign.js')).default, as: 'campaign', required: false }, { model: (await import('../models/match3/objective.js')).default, as: 'objectives', required: false } ] }); } async createMatch3Level(userId, data) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Level = (await import('../models/match3/level.js')).default; // Wenn keine campaignId gesetzt ist, setze eine Standard-Campaign-ID if (!data.campaignId) { // Versuche eine Standard-Campaign zu finden oder erstelle eine const Match3Campaign = (await import('../models/match3/campaign.js')).default; let defaultCampaign = await Match3Campaign.findOne({ where: { isActive: true } }); if (!defaultCampaign) { // Erstelle eine Standard-Campaign falls keine existiert defaultCampaign = await Match3Campaign.create({ name: 'Standard Campaign', description: 'Standard Campaign für Match3 Levels', isActive: true, order: 1 }); } data.campaignId = defaultCampaign.id; } // Validiere, dass campaignId gesetzt ist if (!data.campaignId) { throw new Error('CampaignId ist erforderlich'); } return await Match3Level.create(data); } async updateMatch3Level(userId, id, data) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Level = (await import('../models/match3/level.js')).default; const level = await Match3Level.findByPk(id); if (!level) throw new Error('Level not found'); await level.update(data); return level; } async deleteMatch3Level(userId, id) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Level = (await import('../models/match3/level.js')).default; return await Match3Level.destroy({ where: { id } }); } // Match3 Objectives async getMatch3Objectives(userId) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Objective = (await import('../models/match3/objective.js')).default; return await Match3Objective.findAll({ include: [{ model: (await import('../models/match3/level.js')).default, as: 'level', required: false }], order: [['order', 'ASC']] }); } async getMatch3Objective(userId, id) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Objective = (await import('../models/match3/objective.js')).default; return await Match3Objective.findByPk(id, { include: [{ model: (await import('../models/match3/level.js')).default, as: 'level', required: false }] }); } async createMatch3Objective(userId, data) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Objective = (await import('../models/match3/objective.js')).default; // Validiere, dass levelId gesetzt ist if (!data.levelId) { throw new Error('LevelId ist erforderlich'); } // Validiere, dass target eine ganze Zahl ist if (data.target && !Number.isInteger(Number(data.target))) { throw new Error('Target muss eine ganze Zahl sein'); } return await Match3Objective.create(data); } async updateMatch3Objective(userId, id, data) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Objective = (await import('../models/match3/objective.js')).default; const objective = await Match3Objective.findByPk(id); if (!objective) throw new Error('Objective not found'); // Validiere, dass target eine ganze Zahl ist if (data.target && !Number.isInteger(Number(data.target))) { throw new Error('Target muss eine ganze Zahl sein'); } await objective.update(data); return objective; } async deleteMatch3Objective(userId, id) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3Objective = (await import('../models/match3/objective.js')).default; return await Match3Objective.destroy({ where: { id } }); } async getMatch3TileTypes(userId) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3TileType = (await import('../models/match3/tileType.js')).default; return await Match3TileType.findAll({ order: [['name', 'ASC']] }); } async createMatch3TileType(userId, data) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3TileType = (await import('../models/match3/tileType.js')).default; return await Match3TileType.create(data); } async updateMatch3TileType(userId, id, data) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3TileType = (await import('../models/match3/tileType.js')).default; const tileType = await Match3TileType.findByPk(id); if (!tileType) throw new Error('Tile type not found'); await tileType.update(data); return tileType; } async deleteMatch3TileType(userId, id) { if (!(await this.hasUserAccess(userId, 'match3'))) { throw new Error('noaccess'); } const Match3TileType = (await import('../models/match3/tileType.js')).default; return await Match3TileType.destroy({ where: { id } }); } } export default new AdminService();