From 27d42c0a3a998b388d7bafcd928165800cec86e0 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Mon, 20 Apr 2026 16:27:08 +0200 Subject: [PATCH] fix(AdminService, AdminController): enhance error handling and character deletion logic - Updated error handling in AdminController to include 'targetnotdead' status for improved response accuracy. - Modified character deletion logic in AdminService to ensure correct target character is identified, especially when dealing with living characters, enhancing data integrity during cleanup operations. - Adjusted attributes fetched for characters to include 'health', facilitating better decision-making in deletion processes. --- backend/controllers/adminController.js | 2 +- backend/services/adminService.js | 82 ++++++++++++++++++-------- 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/backend/controllers/adminController.js b/backend/controllers/adminController.js index 0fe1a77..273dd20 100644 --- a/backend/controllers/adminController.js +++ b/backend/controllers/adminController.js @@ -518,7 +518,7 @@ class AdminController { console.log(error); const status = error.message === 'noaccess' ? 403 - : (['invalidCharacter', 'notfound'].includes(error.message) ? 400 : 500); + : (['invalidCharacter', 'notfound', 'targetnotdead'].includes(error.message) ? 400 : 500); res.status(status).json({ error: error.message }); } } diff --git a/backend/services/adminService.js b/backend/services/adminService.js index 9150728..497ee15 100644 --- a/backend/services/adminService.js +++ b/backend/services/adminService.js @@ -1186,14 +1186,50 @@ class AdminService { } const character = await FalukantCharacter.findByPk(parsedCharacterId, { - attributes: ['id', 'userId'] + attributes: ['id', 'userId', 'health'] }); if (!character) { throw new Error('notfound'); } + let targetCharacterId = parsedCharacterId; + let targetCharacter = character; + + // Schutz gegen versehentliches Löschen des lebenden Erben: + // Wenn ein lebender Charakter gewählt wurde, versuchen wir den eindeutig + // verstorbenen Eltern-Charakter als Cleanup-Ziel zu ermitteln. + if (Number(character.health) > 0) { + const deadParents = await sequelize.query( + ` + SELECT DISTINCT c.id + FROM falukant_data.child_relation cr + JOIN falukant_data.character c + ON c.id = cr.mother_character_id + OR c.id = cr.father_character_id + WHERE cr.child_character_id = :childCharacterId + AND c.health <= 0 + ORDER BY c.id ASC + `, + { + replacements: { childCharacterId: parsedCharacterId }, + type: QueryTypes.SELECT + } + ); + if (deadParents.length !== 1) { + throw new Error('targetnotdead'); + } + targetCharacterId = Number(deadParents[0].id); + targetCharacter = await FalukantCharacter.findByPk(targetCharacterId, { + attributes: ['id', 'userId', 'health'] + }); + if (!targetCharacter) { + throw new Error('notfound'); + } + } + const summary = { - characterId: parsedCharacterId, + requestedCharacterId: parsedCharacterId, + characterId: targetCharacterId, knowledgeDeleted: 0, debtorsPrismDeleted: 0, politicalOfficeArchived: 0, @@ -1223,15 +1259,15 @@ class AdminService { }; summary.knowledgeDeleted = await Knowledge.destroy({ - where: { characterId: parsedCharacterId }, + where: { characterId: targetCharacterId }, transaction: t }); summary.debtorsPrismDeleted = await DebtorsPrism.destroy({ - where: { characterId: parsedCharacterId }, + where: { characterId: targetCharacterId }, transaction: t }); summary.electionCandidateDeleted = await Candidate.destroy({ - where: { characterId: parsedCharacterId }, + where: { characterId: targetCharacterId }, transaction: t }); @@ -1257,14 +1293,14 @@ class AdminService { RETURNING id `, { - replacements: { characterId: parsedCharacterId }, + replacements: { characterId: targetCharacterId }, type: QueryTypes.SELECT, transaction: t } ); summary.politicalOfficeArchived = archivedRows.length; summary.politicalOfficeDeleted = await PoliticalOffice.destroy({ - where: { characterId: parsedCharacterId }, + where: { characterId: targetCharacterId }, transaction: t }); @@ -1274,7 +1310,7 @@ class AdminService { DELETE FROM falukant_data.occupied_political_office WHERE character_id = :characterId `, - { characterId: parsedCharacterId } + { characterId: targetCharacterId } ); await deleteIfTableExists( 'falukant_data.political_benefit_last_tick', @@ -1282,7 +1318,7 @@ class AdminService { DELETE FROM falukant_data.political_benefit_last_tick WHERE character_id = :characterId `, - { characterId: parsedCharacterId } + { characterId: targetCharacterId } ); await deleteIfTableExists( 'falukant_data.falukant_character_trait', @@ -1290,7 +1326,7 @@ class AdminService { DELETE FROM falukant_data.falukant_character_trait WHERE character_id = :characterId `, - { characterId: parsedCharacterId } + { characterId: targetCharacterId } ); await deleteIfTableExists( 'falukant_data.church_application', @@ -1299,7 +1335,7 @@ class AdminService { WHERE character_id = :characterId OR supervisor_id = :characterId `, - { characterId: parsedCharacterId } + { characterId: targetCharacterId } ); await deleteIfTableExists( 'falukant_data.church_office', @@ -1308,7 +1344,7 @@ class AdminService { WHERE character_id = :characterId OR supervisor_id = :characterId `, - { characterId: parsedCharacterId } + { characterId: targetCharacterId } ); await deleteIfTableExists( 'falukant_data.political_appointment', @@ -1317,7 +1353,7 @@ class AdminService { WHERE appointer_character_id = :characterId OR target_character_id = :characterId `, - { characterId: parsedCharacterId } + { characterId: targetCharacterId } ); await deleteIfTableExists( 'falukant_data.marriage_proposals', @@ -1326,7 +1362,7 @@ class AdminService { WHERE requester_character_id = :characterId OR proposed_character_id = :characterId `, - { characterId: parsedCharacterId } + { characterId: targetCharacterId } ); await deleteIfTableExists( 'falukant_data.child_relation', @@ -1336,7 +1372,7 @@ class AdminService { OR mother_character_id = :characterId OR child_character_id = :characterId `, - { characterId: parsedCharacterId } + { characterId: targetCharacterId } ); await deleteIfTableExists( 'falukant_data.relationship_state', @@ -1348,7 +1384,7 @@ class AdminService { OR character2_id = :characterId ) `, - { characterId: parsedCharacterId } + { characterId: targetCharacterId } ); await deleteIfTableExists( 'falukant_data.relationship', @@ -1357,7 +1393,7 @@ class AdminService { WHERE character1_id = :characterId OR character2_id = :characterId `, - { characterId: parsedCharacterId } + { characterId: targetCharacterId } ); await deleteIfTableExists( 'falukant_data.region_tax_history', @@ -1365,7 +1401,7 @@ class AdminService { DELETE FROM falukant_data.region_tax_history WHERE setter_character_id = :characterId `, - { characterId: parsedCharacterId } + { characterId: targetCharacterId } ); await deleteIfTableExists( 'falukant_log.health_activity', @@ -1373,7 +1409,7 @@ class AdminService { DELETE FROM falukant_log.health_activity WHERE character_id = :characterId `, - { characterId: parsedCharacterId } + { characterId: targetCharacterId } ); await deleteIfTableExists( 'falukant_log.political_office_history', @@ -1381,18 +1417,18 @@ class AdminService { DELETE FROM falukant_log.political_office_history WHERE character_id = :characterId `, - { characterId: parsedCharacterId } + { characterId: targetCharacterId } ); const deletedCharacters = await FalukantCharacter.destroy({ - where: { id: parsedCharacterId }, + where: { id: targetCharacterId }, transaction: t }); summary.characterDeleted = deletedCharacters > 0; }); - if (character.userId) { - const falukantUser = await FalukantUser.findByPk(character.userId, { + if (targetCharacter.userId) { + const falukantUser = await FalukantUser.findByPk(targetCharacter.userId, { include: [{ model: User, as: 'user', attributes: ['hashedId'] }] }); const hashedId = falukantUser?.user?.hashedId;