diff --git a/backend/controllers/adminController.js b/backend/controllers/adminController.js index f65fc10..ca4a804 100644 --- a/backend/controllers/adminController.js +++ b/backend/controllers/adminController.js @@ -2,6 +2,7 @@ import AdminService from '../services/adminService.js'; import Joi from 'joi'; class AdminController { + // --- Chat Room Admin --- constructor() { this.getOpenInterests = this.getOpenInterests.bind(this); this.changeInterest = this.changeInterest.bind(this); @@ -9,6 +10,15 @@ class AdminController { this.changeTranslation = this.changeTranslation.bind(this); this.getOpenContacts = this.getOpenContacts.bind(this); this.answerContact = this.answerContact.bind(this); + this.searchUser = this.searchUser.bind(this); + this.getFalukantUserById = this.getFalukantUserById.bind(this); + this.changeFalukantUser = this.changeFalukantUser.bind(this); + this.getRoomTypes = this.getRoomTypes.bind(this); + this.getGenderRestrictions = this.getGenderRestrictions.bind(this); + this.getUserRights = this.getUserRights.bind(this); + this.getRooms = this.getRooms.bind(this); + this.createRoom = this.createRoom.bind(this); + this.deleteRoom = this.deleteRoom.bind(this); } async getOpenInterests(req, res) { @@ -126,6 +136,66 @@ class AdminController { res.status(403).json({ error: error.message }); } } + + async getRoomTypes(req, res) { + try { + const types = await AdminService.getRoomTypes(); + res.status(200).json(types); + } catch (error) { + console.log(error); + res.status(500).json({ error: error.message }); + } + } + + async getGenderRestrictions(req, res) { + try { + const restrictions = await AdminService.getGenderRestrictions(); + res.status(200).json(restrictions); + } catch (error) { + console.log(error); + res.status(500).json({ error: error.message }); + } + } + + async getUserRights(req, res) { + try { + const rights = await AdminService.getUserRights(); + res.status(200).json(rights); + } catch (error) { + console.log(error); + res.status(500).json({ error: error.message }); + } + } + + async getRooms(req, res) { + try { + const rooms = await AdminService.getRooms(); + res.status(200).json(rooms); + } catch (error) { + console.log(error); + res.status(500).json({ error: error.message }); + } + } + + async createRoom(req, res) { + try { + const room = await AdminService.createRoom(req.body); + res.status(201).json(room); + } catch (error) { + console.log(error); + res.status(500).json({ error: error.message }); + } + } + + async deleteRoom(req, res) { + try { + await AdminService.deleteRoom(req.params.id); + res.sendStatus(204); + } catch (error) { + console.log(error); + res.status(500).json({ error: error.message }); + } + } } export default AdminController; diff --git a/backend/controllers/falukantController.js b/backend/controllers/falukantController.js index 13c628e..0e033c5 100644 --- a/backend/controllers/falukantController.js +++ b/backend/controllers/falukantController.js @@ -1,834 +1,229 @@ import FalukantService from '../services/falukantService.js'; +function extractHashedUserId(req) { + return req.headers?.userid; +} + class FalukantController { constructor() { - this.getUser = this.getUser.bind(this); - this.createUser = this.createUser.bind(this); - this.randomFirstName = this.randomFirstName.bind(this); - this.randomLastName = this.randomLastName.bind(this); - this.getInfo = this.getInfo.bind(this); - this.getInventory = this.getInventory.bind(this); - this.sellProduct = this.sellProduct.bind(this); - this.sellAllProducts = this.sellAllProducts.bind(this); - this.moneyHistory = this.moneyHistory.bind(this); - this.getStorage = this.getStorage.bind(this); - this.buyStorage = this.buyStorage.bind(this); - this.sellStorage = this.sellStorage.bind(this); - this.getStockTypes = this.getStockTypes.bind(this); - this.getStockOverview = this.getStockOverview.bind(this); - this.getAllProductions = this.getAllProductions.bind(this); - this.getDirectorProposals = this.getDirectorProposals.bind(this); - this.convertProposalToDirector = this.convertProposalToDirector.bind(this); - this.getDirectorForBranch = this.getDirectorForBranch.bind(this); - this.getAllDirectors = this.getAllDirectors.bind(this); - this.setSetting = this.setSetting.bind(this); - this.getFamily = this.getFamily.bind(this); - this.acceptMarriageProposal = this.acceptMarriageProposal.bind(this); - this.getGifts = this.getGifts.bind(this); - this.sendGift = this.sendGift.bind(this); - this.getHouseTypes = this.getHouseTypes.bind(this); - this.getTitelsOfNobility = this.getTitelsOfNobility.bind(this); - this.getMoodAffect = this.getMoodAffect.bind(this); - this.getCharacterAffect = this.getCharacterAffect.bind(this); - this.getUserHouse = this.getUserHouse.bind(this); - this.getBuyableHouses = this.getBuyableHouses.bind(this); - this.buyUserHouse = this.buyUserHouse.bind(this); - this.getPartyTypes = this.getPartyTypes.bind(this); - this.createParty = this.createParty.bind(this); - this.getParties = this.getParties.bind(this); - this.getNotBaptisedChildren = this.getNotBaptisedChildren.bind(this); - this.baptise = this.baptise.bind(this); - this.getEducation = this.getEducation.bind(this); - this.getChildren = this.getChildren.bind(this); - this.sendToSchool = this.sendToSchool.bind(this); - this.getBankOverview = this.getBankOverview.bind(this); - this.getBankCredits = this.getBankCredits.bind(this); - this.takeBankCredits = this.takeBankCredits.bind(this); - this.getNobility = this.getNobility.bind(this); - this.advanceNobility = this.advanceNobility.bind(this); - this.getHealth = this.getHealth.bind(this); - this.healthActivity = this.healthActivity.bind(this); - this.getPoliticsOverview = this.getPoliticsOverview.bind(this); - this.getOpenPolitics = this.getOpenPolitics.bind(this); - this.getElections = this.getElections.bind(this); - this.vote = this.vote.bind(this); - this.getOpenPolitics = this.getOpenPolitics.bind(this); - this.applyForElections = this.applyForElections.bind(this); - this.getRegions = this.getRegions.bind(this); - this.renovate = this.renovate.bind(this); - this.renovateAll = this.renovateAll.bind(this); - this.createBranch = this.createBranch.bind(this); - this.getUndergroundTypes = this.getUndergroundTypes.bind(this); - this.getNotifications = this.getNotifications.bind(this); - } + this.service = FalukantService; - async getUser(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getUser(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } + // No-user endpoints + this.randomFirstName = this._wrapNoUser(this.service.randomFirstName.bind(this.service), { + successStatus: 200, + transform: gender => ({ name: gender }) + }); + this.randomLastName = this._wrapNoUser(this.service.randomLastName.bind(this.service), { + successStatus: 200, + transform: name => ({ name }) + }); - async createUser(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { gender, firstname: firstName, lastname: lastName } = req.body; - console.log(req.body); - const result = await FalukantService.createUser(hashedUserId, gender, firstName, lastName); - res.status(201).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } + // User-scoped endpoints + this.getUser = this._wrapWithUser((userId) => this.service.getUser(userId)); + this.createUser = this._wrapWithUser((userId, req) => { + const { gender, firstname, lastname } = req.body; + return this.service.createUser(userId, gender, firstname, lastname); + }, { successStatus: 201 }); - async randomFirstName(req, res) { - try { - const { gender } = req.params; - const result = await FalukantService.randomFirstName(gender); - res.status(200).json({ name: result }); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async randomLastName(req, res) { - try { - const result = await FalukantService.randomLastName(); - res.status(200).json({ name: result }); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getInfo(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getInfo(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getBranches(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getBranches(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async createBranch(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { cityId, branchTypeId } = req.body; - const result = await FalukantService.createBranch(hashedUserId, cityId, branchTypeId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getBranchTypes(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getBranchTypes(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getBranch(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { branch: branchId } = req.params; - console.log(branchId, req.params); - const result = await FalukantService.getBranch(hashedUserId, branchId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async createProduction(req, res) { - try { - const { userid: hashedUserId } = req.headers; + this.getInfo = this._wrapWithUser((userId) => this.service.getInfo(userId)); + this.getBranches = this._wrapWithUser((userId) => this.service.getBranches(userId)); + this.createBranch = this._wrapWithUser((userId, req) => this.service.createBranch(userId, req.body.cityId, req.body.branchTypeId)); + this.getBranchTypes = this._wrapWithUser((userId) => this.service.getBranchTypes(userId)); + this.getBranch = this._wrapWithUser((userId, req) => this.service.getBranch(userId, req.params.branch)); + this.createProduction = this._wrapWithUser((userId, req) => { const { branchId, productId, quantity } = req.body; - const result = await FalukantService.createProduction(hashedUserId, branchId, productId, quantity); - res.status(201).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getProduction(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { branchId } = req.params; - const result = await FalukantService.getProduction(hashedUserId, branchId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getStock(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { branchId } = req.params || null; - const result = await FalukantService.getStock(hashedUserId, branchId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async createStock(req, res) { - console.log('build stock'); - try { - const { userid: hashedUserId } = req.headers; + return this.service.createProduction(userId, branchId, productId, quantity); + }, { successStatus: 201 }); + this.getProduction = this._wrapWithUser((userId, req) => this.service.getProduction(userId, req.params.branchId)); + this.getStock = this._wrapWithUser((userId, req) => this.service.getStock(userId, req.params.branchId || null)); + this.createStock = this._wrapWithUser((userId, req) => { const { branchId, stockTypeId, stockSize } = req.body; - const result = await FalukantService.createStock(hashedUserId, branchId, stockTypeId, stockSize); - res.status(201).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getProducts(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getProducts(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getInventory(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { branchId } = req.params; - const result = await FalukantService.getInventory(hashedUserId, branchId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async sellProduct(req, res) { - try { - const { userid: hashedUserId } = req.headers; + return this.service.createStock(userId, branchId, stockTypeId, stockSize); + }, { successStatus: 201 }); + this.getProducts = this._wrapWithUser((userId) => this.service.getProducts(userId)); + this.getInventory = this._wrapWithUser((userId, req) => this.service.getInventory(userId, req.params.branchId)); + this.sellProduct = this._wrapWithUser((userId, req) => { const { branchId, productId, quality, quantity } = req.body; - const result = await FalukantService.sellProduct(hashedUserId, branchId, productId, quality, quantity); - res.status(201).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async sellAllProducts(req, res) { - try { - const { userid: hashedUserId } = req.headers; + return this.service.sellProduct(userId, branchId, productId, quality, quantity); + }, { successStatus: 201 }); + this.sellAllProducts = this._wrapWithUser((userId, req) => { const { branchId } = req.body; - const result = await FalukantService.sellAllProducts(hashedUserId, branchId); - res.status(201).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async moneyHistory(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { page, filter } = req.body; - if (!page) { - page = 1; - } - const result = await FalukantService.moneyHistory(hashedUserId, page, filter); - res.status(201).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getStorage(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { branchId } = req.params; - const result = await FalukantService.getStorage(hashedUserId, branchId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message}); - console.log(error); - } - } - - async buyStorage(req, res) { - try { - const { userid: hashedUserId } = req.headers; + return this.service.sellAllProducts(userId, branchId); + }, { successStatus: 201 }); + this.moneyHistory = this._wrapWithUser((userId, req) => { + let { page, filter } = req.body; + if (!page) page = 1; + return this.service.moneyHistory(userId, page, filter); + }); + this.getStorage = this._wrapWithUser((userId, req) => this.service.getStorage(userId, req.params.branchId)); + this.buyStorage = this._wrapWithUser((userId, req) => { const { branchId, amount, stockTypeId } = req.body; - const result = await FalukantService.buyStorage(hashedUserId, branchId, amount, stockTypeId); - res.status(201).json(result); - } catch (error) { - res.status(500).json({ error: error.message}); - console.log(error); - } - } - - async sellStorage(req, res) { - try { - const { userid: hashedUserId } = req.headers; + return this.service.buyStorage(userId, branchId, amount, stockTypeId); + }, { successStatus: 201 }); + this.sellStorage = this._wrapWithUser((userId, req) => { const { branchId, amount, stockTypeId } = req.body; - const result = await FalukantService.sellStorage(hashedUserId, branchId, amount, stockTypeId); - res.status(202).json(result); - } catch (error) { - res.status(500).json({ error: error.message}); - console.log(error); - } - } + return this.service.sellStorage(userId, branchId, amount, stockTypeId); + }, { successStatus: 202 }); - async getStockTypes(req, res) { - console.log('load stock'); - try { - const result = await FalukantService.getStockTypes(); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } + this.getStockTypes = this._wrapSimple(() => this.service.getStockTypes()); + this.getStockOverview = this._wrapSimple(() => this.service.getStockOverview()); + this.getAllProductions = this._wrapWithUser((userId) => this.service.getAllProductions(userId)); - async getStockOverview(req, res) { - try { - const result = await FalukantService.getStockOverview(); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - } - } - - async getAllProductions(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getAllProductions(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - } - } - - async getDirectorProposals(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { branchId } = req.body; - const result = await FalukantService.getDirectorProposals(hashedUserId, branchId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - } - } - - async convertProposalToDirector(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { proposalId } = req.body; - const result = await FalukantService.convertProposalToDirector(hashedUserId, proposalId); - res.status(200).json(result); - } catch (error) { - console.log(error.message, error.stack); - res.status(500).json({ error: error.message }); - } - } - - async getDirectorForBranch(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { branchId } = req.params; - const result = await FalukantService.getDirectorForBranch(hashedUserId, branchId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - } - } - - async getAllDirectors(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getAllDirectors(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - } - } - - async updateDirector(req, res) { - try { - const { userid: hashedUserId } = req.headers; + this.getDirectorProposals = this._wrapWithUser((userId, req) => this.service.getDirectorProposals(userId, req.body.branchId)); + this.convertProposalToDirector = this._wrapWithUser((userId, req) => this.service.convertProposalToDirector(userId, req.body.proposalId)); + this.getDirectorForBranch = this._wrapWithUser((userId, req) => this.service.getDirectorForBranch(userId, req.params.branchId)); + this.getAllDirectors = this._wrapWithUser((userId) => this.service.getAllDirectors(userId)); + this.updateDirector = this._wrapWithUser((userId, req) => { const { directorId, income } = req.body; - const result = await FalukantService.updateDirector(hashedUserId, directorId, income); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - } - } + return this.service.updateDirector(userId, directorId, income); + }); - async setSetting(req, res) { - try { - const { userid: hashedUserId } = req.headers; + this.setSetting = this._wrapWithUser((userId, req) => { const { branchId, directorId, settingKey, value } = req.body; - const result = await FalukantService.setSetting(hashedUserId, branchId, directorId, settingKey, value); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } + return this.service.setSetting(userId, branchId, directorId, settingKey, value); + }); - async getFamily(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getFamily(hashedUserId); - if (!result) { - res.status(404).json({ error: 'No family data found' }); - } - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } + this.getFamily = this._wrapWithUser(async (userId) => { + const result = await this.service.getFamily(userId); + if (!result) throw { status: 404, message: 'No family data found' }; + return result; + }); + this.acceptMarriageProposal = this._wrapWithUser((userId, req) => this.service.acceptMarriageProposal(userId, req.body.proposalId)); + this.getGifts = this._wrapWithUser((userId) => this.service.getGifts(userId)); + this.getChildren = this._wrapWithUser((userId) => this.service.getChildren(userId)); + this.sendGift = this._wrapWithUser((userId, req) => this.service.sendGift(userId, req.body.giftId)); - async acceptMarriageProposal(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { proposalId } = req.body; - const result = await FalukantService.acceptMarriageProposal(hashedUserId, proposalId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } + this.getTitlesOfNobility = this._wrapWithUser((userId) => this.service.getTitlesOfNobility(userId)); + this.getHouseTypes = this._wrapWithUser((userId) => this.service.getHouseTypes(userId)); + this.getMoodAffect = this._wrapWithUser((userId) => this.service.getMoodAffect(userId)); + this.getCharacterAffect = this._wrapWithUser((userId) => this.service.getCharacterAffect(userId)); + this.getUserHouse = this._wrapWithUser((userId) => this.service.getUserHouse(userId)); + this.getBuyableHouses = this._wrapWithUser((userId) => this.service.getBuyableHouses(userId)); + this.buyUserHouse = this._wrapWithUser((userId, req) => this.service.buyUserHouse(userId, req.body.houseId), { successStatus: 201 }); - async getGifts(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getGifts(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getChildren(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getChildren(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async sendGift(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { giftId } = req.body; - const result = await FalukantService.sendGift(hashedUserId, giftId); - res.status(200).json(result); - } catch (error) { - const status = error.status === 412 ? 412 : 500; - res.status(status).json({ error: error.message }); - console.error(error); - } - } - - async getTitelsOfNobility(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getTitlesOfNobility(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getHouseTypes(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getHouseTypes(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getMoodAffect(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getMoodAffect(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getCharacterAffect(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getCharacterAffect(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getUserHouse(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getUserHouse(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getBuyableHouses(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getBuyableHouses(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async buyUserHouse(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { houseId } = req.body; - const result = await FalukantService.buyUserHouse(hashedUserId, houseId); - res.status(201).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getPartyTypes(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getPartyTypes(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async createParty(req, res) { - try { - const { userid: hashedUserId } = req.headers; + this.getPartyTypes = this._wrapWithUser((userId) => this.service.getPartyTypes(userId)); + this.createParty = this._wrapWithUser((userId, req) => { const { partyTypeId, musicId, banquetteId, nobilityIds, servantRatio } = req.body; - const result = await FalukantService.createParty(hashedUserId, partyTypeId, musicId, banquetteId, nobilityIds, servantRatio); - res.status(201).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } + return this.service.createParty(userId, partyTypeId, musicId, banquetteId, nobilityIds, servantRatio); + }, { successStatus: 201 }); + this.getParties = this._wrapWithUser((userId) => this.service.getParties(userId)); - async getParties(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getParties(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getNotBaptisedChildren(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getNotBaptisedChildren(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async baptise(req, res) { - try { - const { userid: hashedUserId } = req.headers; + this.getNotBaptisedChildren = this._wrapWithUser((userId) => this.service.getNotBaptisedChildren(userId)); + this.baptise = this._wrapWithUser((userId, req) => { const { characterId: childId, firstName } = req.body; - const result = await FalukantService.baptise(hashedUserId, childId, firstName); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } + return this.service.baptise(userId, childId, firstName); + }); - async getEducation(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getEducation(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async sendToSchool(req, res) { - try { - const { userid: hashedUserId } = req.headers; + this.getEducation = this._wrapWithUser((userId) => this.service.getEducation(userId)); + this.sendToSchool = this._wrapWithUser((userId, req) => { const { item, student, studentId } = req.body; - const result = await FalukantService.sendToSchool(hashedUserId, item, student, studentId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } + return this.service.sendToSchool(userId, item, student, studentId); + }); + + this.getBankOverview = this._wrapWithUser((userId) => this.service.getBankOverview(userId)); + this.getBankCredits = this._wrapWithUser((userId) => this.service.getBankCredits(userId)); + this.takeBankCredits = this._wrapWithUser((userId, req) => this.service.takeBankCredits(userId, req.body.height)); + + this.getNobility = this._wrapWithUser((userId) => this.service.getNobility(userId)); + this.advanceNobility = this._wrapWithUser((userId) => this.service.advanceNobility(userId)); + + this.getHealth = this._wrapWithUser((userId) => this.service.getHealth(userId)); + this.healthActivity = this._wrapWithUser((userId, req) => this.service.healthActivity(userId, req.body.measureTr)); + + this.getPoliticsOverview = this._wrapWithUser((userId) => this.service.getPoliticsOverview(userId)); + this.getOpenPolitics = this._wrapWithUser((userId) => this.service.getOpenPolitics(userId)); + this.getElections = this._wrapWithUser((userId) => this.service.getElections(userId)); + this.vote = this._wrapWithUser((userId, req) => this.service.vote(userId, req.body.votes)); + this.applyForElections = this._wrapWithUser((userId, req) => this.service.applyForElections(userId, req.body.electionIds)); + + this.getRegions = this._wrapWithUser((userId) => this.service.getRegions(userId)); + this.renovate = this._wrapWithUser((userId, req) => this.service.renovate(userId, req.body.element)); + this.renovateAll = this._wrapWithUser((userId) => this.service.renovateAll(userId)); + + this.getUndergroundTypes = this._wrapWithUser((userId) => this.service.getUndergroundTypes(userId)); + this.getNotifications = this._wrapWithUser((userId) => this.service.getNotifications(userId)); + this.getUndergroundTargets = this._wrapWithUser((userId) => this.service.getPoliticalOfficeHolders(userId)); + + this.searchUsers = this._wrapWithUser((userId, req) => { + const q = req.query.q?.trim() || ''; + if (q.length < 1) return []; + return this.service.searchUsers(userId, q); + }); + + this.createUndergroundActivity = this._wrapWithUser((userId, req) => { + const payload = req.body; + if (!payload.typeId) { + throw { status: 400, message: 'typeId is required' }; + } + if (payload.typeId === 'sabotage' && !payload.target) { + throw { status: 400, message: 'target is required for sabotage' }; + } + if (payload.typeId === 'corrupt_politician' && !payload.goal) { + throw { status: 400, message: 'goal is required for corrupt_politician' }; + } + return this.service.createUndergroundActivity(userId, payload); + }, { successStatus: 201 }); + + this.getUndergroundAttacks = this._wrapWithUser((userId, req) => { + const direction = (req.query.direction || '').toLowerCase(); + return this.service.getUndergroundAttacks(userId).then(result => { + if (direction === 'sent') return result.sent; + if (direction === 'received') return result.received; + return result; // both + }); + }); + } - async getBankOverview(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getBankOverview(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } + + _wrapWithUser(fn, { successStatus = 200, postProcess } = {}) { + return async (req, res) => { + try { + const hashedUserId = extractHashedUserId(req); + if (!hashedUserId) { + return res.status(400).json({ error: 'Missing user identifier' }); + } + const result = await fn(hashedUserId, req, res); + const toSend = postProcess ? postProcess(result) : result; + res.status(successStatus).json(toSend); + } catch (error) { + console.error('Controller error:', error); + const status = error.status && typeof error.status === 'number' ? error.status : 500; + res.status(status).json({ error: error.message || 'Internal error' }); + } + }; } - async getBankCredits(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getBankCredits(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } + _wrapSimple(fn, { successStatus = 200, postProcess } = {}) { + return async (req, res) => { + try { + const result = await fn(req, res); + const toSend = postProcess ? postProcess(result) : result; + res.status(successStatus).json(toSend); + } catch (error) { + console.error('Controller error:', error); + const status = error.status && typeof error.status === 'number' ? error.status : 500; + res.status(status).json({ error: error.message || 'Internal error' }); + } + }; } - async takeBankCredits(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { height } = req.body; - const result = await FalukantService.takeBankCredits(hashedUserId, height); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getNobility(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getNobility(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async advanceNobility(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.advanceNobility(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getHealth(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getHealth(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async healthActivity(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { measureTr: activity } = req.body; - const result = await FalukantService.healthActivity(hashedUserId, activity); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getPoliticsOverview(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getPoliticsOverview(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getOpenPolitics(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getOpenPolitics(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getElections(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getElections(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async vote(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { votes } = req.body; - const result = await FalukantService.vote(hashedUserId, votes); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getOpenPolitics(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getOpenPolitics(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async applyForElections(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { electionIds } = req.body; - const result = await FalukantService.applyForElections(hashedUserId, electionIds); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getRegions(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getRegions(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async renovate(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const { element } = req.body; - const result = await FalukantService.renovate(hashedUserId, element); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async renovateAll(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.renovateAll(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getUndergroundTypes(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getUndergroundTypes(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } - } - - async getNotifications(req, res) { - try { - const { userid: hashedUserId } = req.headers; - const result = await FalukantService.getNotifications(hashedUserId); - res.status(200).json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - console.log(error); - } + _wrapNoUser(fn, { successStatus = 200, transform } = {}) { + return async (req, res) => { + try { + let result; + if (req.params && Object.keys(req.params).length) { + result = await fn(req.params.gender); + } else { + result = await fn(); + } + if (transform) result = transform(result); + res.status(successStatus).json(result); + } catch (error) { + console.error('Controller error:', error); + res.status(500).json({ error: error.message || 'Internal error' }); + } + }; } } diff --git a/backend/controllers/navigationController.js b/backend/controllers/navigationController.js index 291ffda..ba36fd8 100644 --- a/backend/controllers/navigationController.js +++ b/backend/controllers/navigationController.js @@ -219,6 +219,10 @@ const menuStructure = { visible: ["mainadmin", "forum"], path: "/admin/forum" }, + chatrooms: { + visible: ["mainadmin", "chatrooms"], + path: "/admin/chatrooms" + }, userrights: { visible: ["mainadmin", "rights"], path: "/admin/rights" diff --git a/backend/jobs/sessionCleanup.js b/backend/jobs/sessionCleanup.js index 9da0664..8f8edb0 100644 --- a/backend/jobs/sessionCleanup.js +++ b/backend/jobs/sessionCleanup.js @@ -3,3 +3,4 @@ import { cleanupExpiredSessions } from '../utils/redis.js'; setInterval(async () => { await cleanupExpiredSessions(); }, 5000); + \ No newline at end of file diff --git a/backend/models/associations.js b/backend/models/associations.js index 2bb8ed0..82fd012 100644 --- a/backend/models/associations.js +++ b/backend/models/associations.js @@ -1,3 +1,8 @@ +import RoomType from './chat/room_type.js'; +import ChatRight from './chat/rights.js'; +import ChatUserRight from './chat/user_rights.js'; +import ChatUser from './chat/user.js'; +import Room from './chat/room.js'; import User from './community/user.js'; import UserParam from './community/user_param.js'; import UserParamType from './type/user_param.js'; @@ -92,6 +97,31 @@ import Underground from './falukant/data/underground.js'; import UndergroundType from './falukant/type/underground.js'; export default function setupAssociations() { + // RoomType 1:n Room + RoomType.hasMany(Room, { foreignKey: 'roomTypeId', as: 'rooms' }); + Room.belongsTo(RoomType, { foreignKey: 'roomTypeId', as: 'roomType' }); + // ChatUser <-> ChatRight n:m + ChatUser.belongsToMany(ChatRight, { + through: ChatUserRight, + foreignKey: 'chat_user_id', + otherKey: 'chat_right_id', + as: 'rights', + }); + ChatRight.belongsToMany(ChatUser, { + through: ChatUserRight, + foreignKey: 'chat_right_id', + otherKey: 'chat_user_id', + as: 'users', + }); + // ChatUser zu FalukantUser + ChatUser.belongsTo(FalukantUser, { foreignKey: 'falukant_user_id', as: 'falukantUser' }); + FalukantUser.hasOne(ChatUser, { foreignKey: 'falukant_user_id', as: 'chatUser' }); + // Chat Room associations + Room.belongsTo(User, { foreignKey: 'owner_id', as: 'owner' }); + User.hasMany(Room, { foreignKey: 'owner_id', as: 'ownedRooms' }); + + Room.belongsTo(UserParamValue, { foreignKey: 'gender_restriction_id', as: 'genderRestriction' }); + UserParamValue.hasMany(Room, { foreignKey: 'gender_restriction_id', as: 'roomsWithGenderRestriction' }); // UserParam related associations SettingsType.hasMany(UserParamType, { foreignKey: 'settingsId', as: 'user_param_types' }); UserParamType.belongsTo(SettingsType, { foreignKey: 'settingsId', as: 'settings_type' }); diff --git a/backend/models/chat/index.js b/backend/models/chat/index.js new file mode 100644 index 0000000..e69de29 diff --git a/backend/models/chat/rights.js b/backend/models/chat/rights.js new file mode 100644 index 0000000..a171e11 --- /dev/null +++ b/backend/models/chat/rights.js @@ -0,0 +1,22 @@ +import { DataTypes } from 'sequelize'; +import { sequelize } from '../../utils/sequelize.js'; + +const ChatRight = sequelize.define('ChatRight', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + tr: { + type: DataTypes.STRING(32), + allowNull: false, + unique: true, + }, +}, { + schema: 'chat', + tableName: 'rights', + timestamps: false, + underscored: true, +}); + +export default ChatRight; diff --git a/backend/models/chat/room.js b/backend/models/chat/room.js new file mode 100644 index 0000000..d02eec6 --- /dev/null +++ b/backend/models/chat/room.js @@ -0,0 +1,69 @@ +import { DataTypes } from 'sequelize'; +import { sequelize } from '../../utils/sequelize.js'; + +const Room = sequelize.define('Room', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + title: { + type: DataTypes.TEXT, + allowNull: false, + }, + ownerId: { + type: DataTypes.INTEGER, + allowNull: true, // kann null sein, wenn system-owned + }, + roomTypeId: { + type: DataTypes.INTEGER, + allowNull: true, + }, + isPublic: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true, + }, + genderRestrictionId: { + type: DataTypes.INTEGER, + allowNull: true, + }, + password: { + type: DataTypes.STRING, + allowNull: true, + }, + minAge: { + type: DataTypes.INTEGER, + allowNull: true, + }, + maxAge: { + type: DataTypes.INTEGER, + allowNull: true, + }, + passwordHash: { + type: DataTypes.TEXT, + allowNull: true, + }, + friendsOfOwnerOnly: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + }, + requiredUserRightId: { + type: DataTypes.INTEGER, + allowNull: true, + }, +}, { + schema: 'chat', + tableName: 'room', + timestamps: true, + underscored: true, + indexes: [ + { + name: 'idx_chat_room_owner', + fields: ['owner_id'], + }, + ], +}); + +export default Room; diff --git a/backend/models/chat/room_type.js b/backend/models/chat/room_type.js new file mode 100644 index 0000000..3dfd64c --- /dev/null +++ b/backend/models/chat/room_type.js @@ -0,0 +1,22 @@ +import { DataTypes } from 'sequelize'; +import { sequelize } from '../../utils/sequelize.js'; + +const RoomType = sequelize.define('RoomType', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + tr: { + type: DataTypes.STRING(32), + allowNull: false, + unique: true, + }, +}, { + schema: 'chat', + tableName: 'room_type', + timestamps: false, + underscored: true, +}); + +export default RoomType; diff --git a/backend/models/chat/user.js b/backend/models/chat/user.js new file mode 100644 index 0000000..6d73fe2 --- /dev/null +++ b/backend/models/chat/user.js @@ -0,0 +1,41 @@ +import { DataTypes } from 'sequelize'; +import { sequelize } from '../../utils/sequelize.js'; + +const ChatUser = sequelize.define('ChatUser', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + falukant_user_id: { + type: DataTypes.INTEGER, + allowNull: false, + comment: 'Verknüpfung zu community.falukant_user', + }, + display_name: { + type: DataTypes.STRING(64), + allowNull: false, + }, + color: { + type: DataTypes.STRING(16), // z.B. Hex-Code + allowNull: false, + defaultValue: '#000000', + }, + show_gender: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true, + }, + show_age: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true, + }, +}, { + schema: 'chat', + tableName: 'user', + timestamps: true, + underscored: true, +}); + +export default ChatUser; diff --git a/backend/models/chat/user_rights.js b/backend/models/chat/user_rights.js new file mode 100644 index 0000000..e0b807d --- /dev/null +++ b/backend/models/chat/user_rights.js @@ -0,0 +1,26 @@ +import { DataTypes } from 'sequelize'; +import { sequelize } from '../../utils/sequelize.js'; +import ChatUser from './user.js'; +import ChatRight from './rights.js'; + +const ChatUserRight = sequelize.define('ChatUserRight', { + chatUserId: { + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + references: null, // Assoziation wird separat gesetzt + }, + chatRightId: { + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + references: null, // Assoziation wird separat gesetzt + }, +}, { + schema: 'chat', + tableName: 'user_rights', + timestamps: false, + underscored: true, +}); + +export default ChatUserRight; diff --git a/backend/models/index.js b/backend/models/index.js index 81927aa..3849673 100644 --- a/backend/models/index.js +++ b/backend/models/index.js @@ -99,6 +99,12 @@ import ElectionHistory from './falukant/log/election_history.js'; import UndergroundType from './falukant/type/underground.js'; import Underground from './falukant/data/underground.js'; +import Room from './chat/room.js'; +import ChatUser from './chat/user.js'; +import ChatRight from './chat/rights.js'; +import ChatUserRight from './chat/user_rights.js'; +import RoomType from './chat/room_type.js'; + const models = { SettingsType, UserParamValue, @@ -197,6 +203,11 @@ const models = { ElectionHistory, UndergroundType, Underground, + Room, + ChatUser, + ChatRight, + ChatUserRight, + RoomType, }; export default models; diff --git a/backend/routers/adminRouter.js b/backend/routers/adminRouter.js index 5b5ed15..d7396b6 100644 --- a/backend/routers/adminRouter.js +++ b/backend/routers/adminRouter.js @@ -1,3 +1,4 @@ + import { Router } from 'express'; import { authenticate } from '../middleware/authMiddleware.js'; import AdminController from '../controllers/adminController.js'; @@ -5,6 +6,14 @@ import AdminController from '../controllers/adminController.js'; const router = Router(); const adminController = new AdminController(); +// --- Chat Room Admin --- +router.get('/chat/room-types', authenticate, adminController.getRoomTypes); +router.get('/chat/gender-restrictions', authenticate, adminController.getGenderRestrictions); +router.get('/chat/user-rights', authenticate, adminController.getUserRights); +router.get('/chat/rooms', authenticate, adminController.getRooms); +router.post('/chat/rooms', authenticate, adminController.createRoom); +router.delete('/chat/rooms/:id', authenticate, adminController.deleteRoom); + router.get('/interests/open', authenticate, adminController.getOpenInterests); router.post('/interest', authenticate, adminController.changeInterest); router.post('/interest/translation', authenticate, adminController.changeTranslation); diff --git a/backend/routers/falukantRouter.js b/backend/routers/falukantRouter.js index 214cf72..83d73c7 100644 --- a/backend/routers/falukantRouter.js +++ b/backend/routers/falukantRouter.js @@ -41,7 +41,7 @@ router.get('/family/gifts', falukantController.getGifts); router.get('/family/children', falukantController.getChildren); router.post('/family/gift', falukantController.sendGift); router.get('/family', falukantController.getFamily); -router.get('/nobility/titels', falukantController.getTitelsOfNobility); +router.get('/nobility/titels', falukantController.getTitlesOfNobility); router.get('/houses/types', falukantController.getHouseTypes); router.get('/houses/buyable', falukantController.getBuyableHouses); router.get('/houses', falukantController.getUserHouse); @@ -71,5 +71,9 @@ router.post('/politics/open', falukantController.applyForElections); router.get('/cities', falukantController.getRegions); router.get('/underground/types', falukantController.getUndergroundTypes); router.get('/notifications', falukantController.getNotifications); +router.get('/underground/targets', falukantController.getUndergroundTargets); +router.post('/underground/activities', falukantController.createUndergroundActivity); +router.get('/users/search', falukantController.searchUsers); +router.get('/underground/attacks', falukantController.getUndergroundAttacks); export default router; diff --git a/backend/services/adminService.js b/backend/services/adminService.js index b45abc0..eb56554 100644 --- a/backend/services/adminService.js +++ b/backend/services/adminService.js @@ -1,9 +1,12 @@ +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 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'; @@ -12,6 +15,7 @@ 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 Room from '../models/chat/room.js'; class AdminService { async hasUserAccess(userId, section) { @@ -286,6 +290,39 @@ class AdminService { await falukantUser.save(); await character.save(); } + + // --- Chat Room Admin --- + async getRoomTypes() { + return await RoomType.findAll(); + } + + async getGenderRestrictions() { + // 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() { + return await ChatRight.findAll(); + } + + async getRooms() { + return await Room.findAll({ + include: [ + { model: RoomType, as: 'roomType' }, + { model: UserParamValue, as: 'genderRestriction' }, + ] + }); + } + + async createRoom(data) { + return await Room.create(data); + } + + async deleteRoom(id) { + return await Room.destroy({ where: { id } }); + } } export default new AdminService(); \ No newline at end of file diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index a2e5133..8bc7e8c 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -55,6 +55,8 @@ import PoliticalOfficePrerequisite from '../models/falukant/predefine/political_ import PoliticalOfficeHistory from '../models/falukant/log/political_office_history.js'; import UndergroundType from '../models/falukant/type/underground.js'; import Notification from '../models/falukant/log/notification.js'; +import PoliticalOffice from '../models/falukant/data/political_office.js'; +import Underground from '../models/falukant/data/underground.js'; function calcAge(birthdate) { const b = new Date(birthdate); b.setHours(0, 0); @@ -1066,7 +1068,7 @@ class FalukantService extends BaseService { }); if (regionUserDirectorProposals.length > 0) { for (const p of regionUserDirectorProposals) { - await p.destroy(); + await p.destroy(); } } notifyUser(hashedUserId, 'directorchanged'); @@ -2795,6 +2797,297 @@ class FalukantService extends BaseService { }); return user.notifications; } + + async getPoliticalOfficeHolders(hashedUserId) { + const user = await getFalukantUserOrFail(hashedUserId); + const character = await FalukantCharacter.findOne({ + where: { + userId: user.id + } + }); + const relevantRegionIds = await this.getRegionAndParentIds(character.regionId); + const now = new Date(); + const histories = await PoliticalOffice.findAll({ + where: { + regionId: { + [Op.in]: relevantRegionIds + }, + }, + include: [{ + model: FalukantCharacter, + as: 'holder', + include: [ + { model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] }, + { model: FalukantPredefineLastname, as: 'definedLastName', attributes: ['name'] }, + { model: TitleOfNobility, as: 'nobleTitle', attributes: ['labelTr'] } + ], + attributes: ['id', 'gender'] + }, { + model: PoliticalOfficeType, + as: 'type', + }] + }); + + // Unikate nach character.id + const map = new Map(); + histories.forEach(h => { + const c = h.holder; + if (c && c.id && !map.has(c.id)) { + map.set(c.id, { + id: c.id, + name: `${c.definedFirstName.name} ${c.definedLastName.name}`, + title: c.nobleTitle.labelTr, + officeType: h.type.name, + gender: c.gender + }); + } + }); + + return Array.from(map.values()); + } + + async getRegionAndParentIds(regionId) { + const relevantRegionIds = new Set(); + let currentRegionId = regionId; + + while (currentRegionId !== null) { + relevantRegionIds.add(currentRegionId); + const region = await RegionData.findByPk(currentRegionId, { + attributes: ['parentId'] + }); + + if (region && region.parentId) { + currentRegionId = region.parentId; + } else { + currentRegionId = null; // Keine weitere Elternregion gefunden + } + } + return Array.from(relevantRegionIds); + } + + // vorher: async searchUsers(q) { + async searchUsers(hashedUserId, q) { + // User-Prüfung wie bei anderen Methoden + await getFalukantUserOrFail(hashedUserId); // wir brauchen das Ergebnis hier nicht weiter, nur Validierung + + const chars = await FalukantCharacter.findAll({ + where: { + userId: { [Op.ne]: null }, + [Op.or]: [ + { '$user.user.username$': { [Op.iLike]: `%${q}%` } }, + { '$definedFirstName.name$': { [Op.iLike]: `%${q}%` } }, + { '$definedLastName.name$': { [Op.iLike]: `%${q}%` } } + ] + }, + include: [ + { + model: FalukantUser, + as: 'user', + include: [ + { + model: User, + as: 'user', + attributes: ['username'] + } + ] + }, + { + model: FalukantPredefineFirstname, + as: 'definedFirstName', + attributes: ['name'] + }, + { + model: FalukantPredefineLastname, + as: 'definedLastName', + attributes: ['name'] + }, + { + model: RegionData, + as: 'region', + attributes: ['name'] + } + ], + limit: 50, + raw: false + }); + + // Debug-Log (optional) + console.log('FalukantService.searchUsers raw result for', q, chars); + + const mapped = chars + .map(c => ({ + username: c.user?.user?.username || null, + firstname: c.definedFirstName?.name || null, + lastname: c.definedLastName?.name || null, + town: c.region?.name || null + })) + .filter(u => u.username); + + console.log('FalukantService.searchUsers mapped result for', q, mapped); + return mapped; + } + + async createUndergroundActivity(hashedUserId, payload) { + const { typeId, victimUsername, target, goal, politicalTargets } = payload; + + // 1) Performer auflösen + const falukantUser = await this.getFalukantUserByHashedId(hashedUserId); + if (!falukantUser || !falukantUser.character) { + throw new Error('Performer not found'); + } + const performerChar = falukantUser.character; + + // 2) Victim auflösen über Username (inner join) + const victimChar = await FalukantCharacter.findOne({ + include: [ + { + model: FalukantUser, + as: 'user', + required: true, // inner join + attributes: [], + include: [ + { + model: User, + as: 'user', + required: true, // inner join + where: { username: victimUsername }, + attributes: [] + } + ] + } + ] + }); + + if (!victimChar) { + throw new PreconditionError('Victim character not found'); + } + + // 3) Selbstangriff verhindern + if (victimChar.id === performerChar.id) { + throw new PreconditionError('Cannot target yourself'); + } + + // 4) Typ-spezifische Validierung + const undergroundType = await UndergroundType.findByPk(typeId); + if (!undergroundType) { + throw new Error('Invalid underground type'); + } + + if (undergroundType.tr === 'sabotage') { + if (!target) { + throw new PreconditionError('Sabotage target missing'); + } + } + + if (undergroundType.tr === 'corrupt_politician') { + if (!goal) { + throw new PreconditionError('Corrupt goal missing'); + } + // politicalTargets kann optional sein, falls benötigt prüfen + } + + // 5) Eintrag anlegen (optional: in Transaction) + const newEntry = await Underground.create({ + undergroundTypeId: typeId, + performerId: performerChar.id, + victimId: victimChar.id, + result: null, + parameters: { + target: target || null, + goal: goal || null, + politicalTargets: politicalTargets || null + } + }); + + return newEntry; + } + + + async getUndergroundAttacks(hashedUserId) { + const falukantUser = await getFalukantUserOrFail(hashedUserId); + const character = await FalukantCharacter.findOne({ + where: { userId: falukantUser.id } + }); + if (!character) throw new Error('Character not found'); + + const charId = character.id; + + const attacks = await Underground.findAll({ + where: { + [Op.or]: [ + { performerId: charId }, + { victimId: charId } + ] + }, + include: [ + { + model: FalukantCharacter, + as: 'performer', + include: [ + { model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] }, + { model: FalukantPredefineLastname, as: 'definedLastName', attributes: ['name'] }, + { + model: FalukantUser, + as: 'user', + include: [{ model: User, as: 'user', attributes: ['username'] }], + attributes: [] + } + ], + attributes: ['id', 'gender'] + }, + { + model: FalukantCharacter, + as: 'victim', + include: [ + { model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] }, + { model: FalukantPredefineLastname, as: 'definedLastName', attributes: ['name'] }, + { + model: FalukantUser, + as: 'user', + include: [{ model: User, as: 'user', attributes: ['username'] }], + attributes: [] + } + ], + attributes: ['id', 'gender'] + }, + { + model: UndergroundType, + as: 'undergroundType', // hier der korrekte Alias + attributes: ['tr'] + } + ], + order: [['createdAt', 'DESC']] + }); + + const formatCharacter = (c) => { + if (!c) return null; + return { + id: c.id, + username: c.user?.user?.username || null, + firstname: c.definedFirstName?.name || null, + lastname: c.definedLastName?.name || null, + gender: c.gender + }; + }; + + const mapped = attacks.map(a => ({ + id: a.id, + result: a.result, + createdAt: a.createdAt, + updatedAt: a.updatedAt, + type: a.undergroundType?.tr || null, // angepasst + parameters: a.parameters, + performer: formatCharacter(a.performer), + victim: formatCharacter(a.victim), + success: !!a.result + })); + + return { + sent: mapped.filter(a => a.performer?.id === charId), + received: mapped.filter(a => a.victim?.id === charId), + all: mapped + }; + } } export default new FalukantService(); diff --git a/backend/utils/initializeChat.js b/backend/utils/initializeChat.js new file mode 100644 index 0000000..ef72258 --- /dev/null +++ b/backend/utils/initializeChat.js @@ -0,0 +1,43 @@ +import ChatRight from "../models/chat/rights.js"; +import RoomType from "../models/chat/room_type.js"; + +const initializeChat = async () => { + initializeChatRights(); + initializeRoomTypes(); +} + +const RoomTypes = [ + 'chat', + 'dice', + 'poker', + 'hangman' +]; + +const ChatRights = [ + 'talk', + 'scream', + 'whisper', + 'start game', + 'open room', + 'systemmessage' +]; + +const initializeChatRights = async () => { + for (const right of ChatRights) { + await ChatRight.findOrCreate({ + where: { tr: right }, + defaults: { tr: right } + }); + } +} + +const initializeRoomTypes = async () => { + for (const type of RoomTypes) { + await RoomType.findOrCreate({ + where: { tr: type }, + defaults: { tr: type } + }); + } +} + +export default initializeChat; \ No newline at end of file diff --git a/backend/utils/sequelize.js b/backend/utils/sequelize.js index f5b1115..68a6518 100644 --- a/backend/utils/sequelize.js +++ b/backend/utils/sequelize.js @@ -21,6 +21,7 @@ const createSchemas = async () => { await sequelize.query('CREATE SCHEMA IF NOT EXISTS falukant_type'); await sequelize.query('CREATE SCHEMA IF NOT EXISTS falukant_predefine'); await sequelize.query('CREATE SCHEMA IF NOT EXISTS falukant_log'); + await sequelize.query('CREATE SCHEMA IF NOT EXISTS chat'); }; const initializeDatabase = async () => { diff --git a/backend/utils/syncDatabase.js b/backend/utils/syncDatabase.js index be13356..e826ea5 100644 --- a/backend/utils/syncDatabase.js +++ b/backend/utils/syncDatabase.js @@ -10,6 +10,7 @@ import setupAssociations from '../models/associations.js'; import models from '../models/index.js'; import { createTriggers } from '../models/trigger.js'; import initializeForum from './initializeForum.js'; +import initializeChat from './initializeChat.js'; const syncDatabase = async () => { try { @@ -43,6 +44,9 @@ const syncDatabase = async () => { console.log("Creating triggers..."); await createTriggers(); + console.log("Initializing chat..."); + await initializeChat(); + console.log('Database synchronization complete.'); } catch (error) { console.error('Unable to synchronize the database:', error); diff --git a/dump.rdb b/dump.rdb index 9612a68..58efa8c 100644 Binary files a/dump.rdb and b/dump.rdb differ diff --git a/frontend/src/dialogues/admin/RoomDialog.vue b/frontend/src/dialogues/admin/RoomDialog.vue new file mode 100644 index 0000000..a213b2a --- /dev/null +++ b/frontend/src/dialogues/admin/RoomDialog.vue @@ -0,0 +1,191 @@ + + + + + diff --git a/frontend/src/i18n/locales/de/admin.json b/frontend/src/i18n/locales/de/admin.json index 86a339f..ecb8cce 100644 --- a/frontend/src/i18n/locales/de/admin.json +++ b/frontend/src/i18n/locales/de/admin.json @@ -48,6 +48,50 @@ "success": "Die Änderungen wurden gespeichert.", "error": "Die Änderungen konnten nicht gespeichert werden." } + }, + "chatrooms": { + "title": "[Admin] - Chaträume verwalten", + "roomName": "Raumname", + "create": "Chatraum anlegen", + "edit": "Chatraum bearbeiten", + "type": "Typ", + "isPublic": "Öffentlich sichtbar", + "actions": "Aktionen", + "genderRestriction": { + "show": "Geschlechtsbeschränkung aktivieren", + "label": "Geschlechtsbeschränkung" + }, + "minAge": { + "show": "Mindestalter angeben", + "label": "Mindestalter" + }, + "maxAge": { + "show": "Höchstalter angeben", + "label": "Höchstalter" + }, + "password": { + "show": "Passwortschutz aktivieren", + "label": "Passwort" + }, + "friendsOfOwnerOnly": "Nur Freunde des Besitzers", + "requiredUserRight": { + "show": "Benötigtes Benutzerrecht angeben", + "label": "Benötigtes Benutzerrecht" + }, + "roomtype": { + "chat": "Reden", + "dice": "Würfeln", + "poker": "Poker", + "hangman": "Hangman" + }, + "rights": { + "talk": "Reden", + "scream": "Schreien", + "whisper": "Flüstern", + "start game": "Spiel starten", + "open room": "Raum öffnen", + "systemmessage": "Systemnachricht" + } } } } \ No newline at end of file diff --git a/frontend/src/router/adminRoutes.js b/frontend/src/router/adminRoutes.js index e884a9d..2fe6031 100644 --- a/frontend/src/router/adminRoutes.js +++ b/frontend/src/router/adminRoutes.js @@ -1,5 +1,6 @@ import AdminInterestsView from '../views/admin/InterestsView.vue'; import AdminContactsView from '../views/admin/ContactsView.vue'; +import ChatRoomsView from '../views/admin/ChatRoomsView.vue'; import ForumAdminView from '../dialogues/admin/ForumAdminView.vue'; import AdminFalukantEditUserView from '../views/admin/falukant/EditUserView.vue' @@ -22,6 +23,12 @@ const adminRoutes = [ component: ForumAdminView, meta: { requiresAuth: true } }, + { + path: '/admin/chatrooms', + name: 'AdminChatRooms', + component: ChatRoomsView, + meta: { requiresAuth: true } + }, { path: '/admin/falukant/edituser', name: 'AdminFalukantEditUserView', diff --git a/frontend/src/views/admin/ChatRoomsView.vue b/frontend/src/views/admin/ChatRoomsView.vue new file mode 100644 index 0000000..69ef42f --- /dev/null +++ b/frontend/src/views/admin/ChatRoomsView.vue @@ -0,0 +1,109 @@ + + + + + + diff --git a/frontend/src/views/admin/RoomDialog.vue b/frontend/src/views/admin/RoomDialog.vue new file mode 100644 index 0000000..685f201 --- /dev/null +++ b/frontend/src/views/admin/RoomDialog.vue @@ -0,0 +1,175 @@ + + + \ No newline at end of file diff --git a/frontend/src/views/falukant/UndergroundView.vue b/frontend/src/views/falukant/UndergroundView.vue index b775218..188558b 100644 --- a/frontend/src/views/falukant/UndergroundView.vue +++ b/frontend/src/views/falukant/UndergroundView.vue @@ -13,6 +13,7 @@

{{ $t('falukant.underground.activities.create') }}

+ + +
+ +
+ + + @@ -42,9 +68,15 @@ @@ -69,13 +101,9 @@ - {{ $t(`falukant.underground.types.${act.type}`) }} - {{ act.victimName }} - {{ formatCost(act.cost) }} -