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 @@
+
+ {{ $t('admin.chatrooms.title') }}
+
+
+
+
+
+
+
+
+ {{ $t('admin.chatrooms.roomName') }}
+ {{ $t('admin.chatrooms.type') }}
+ {{ $t('admin.chatrooms.isPublic') }}
+ {{ $t('admin.chatrooms.actions') }}
+
+
+
+ {{ room.title }}
+ {{ room.roomTypeTr || room.roomTypeId }}
+ {{ room.isPublic ? $t('common.yes') : $t('common.no') }}
+
+
+
+
+