fix(AdminService, AdminController): enhance error handling and character deletion logic
All checks were successful
Deploy to production / deploy (push) Successful in 1m44s

- 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.
This commit is contained in:
Torsten Schulz (local)
2026-04-20 16:27:08 +02:00
parent 7417736daf
commit 27d42c0a3a
2 changed files with 60 additions and 24 deletions

View File

@@ -518,7 +518,7 @@ class AdminController {
console.log(error); console.log(error);
const status = error.message === 'noaccess' const status = error.message === 'noaccess'
? 403 ? 403
: (['invalidCharacter', 'notfound'].includes(error.message) ? 400 : 500); : (['invalidCharacter', 'notfound', 'targetnotdead'].includes(error.message) ? 400 : 500);
res.status(status).json({ error: error.message }); res.status(status).json({ error: error.message });
} }
} }

View File

@@ -1186,14 +1186,50 @@ class AdminService {
} }
const character = await FalukantCharacter.findByPk(parsedCharacterId, { const character = await FalukantCharacter.findByPk(parsedCharacterId, {
attributes: ['id', 'userId'] attributes: ['id', 'userId', 'health']
}); });
if (!character) { if (!character) {
throw new Error('notfound'); 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 = { const summary = {
characterId: parsedCharacterId, requestedCharacterId: parsedCharacterId,
characterId: targetCharacterId,
knowledgeDeleted: 0, knowledgeDeleted: 0,
debtorsPrismDeleted: 0, debtorsPrismDeleted: 0,
politicalOfficeArchived: 0, politicalOfficeArchived: 0,
@@ -1223,15 +1259,15 @@ class AdminService {
}; };
summary.knowledgeDeleted = await Knowledge.destroy({ summary.knowledgeDeleted = await Knowledge.destroy({
where: { characterId: parsedCharacterId }, where: { characterId: targetCharacterId },
transaction: t transaction: t
}); });
summary.debtorsPrismDeleted = await DebtorsPrism.destroy({ summary.debtorsPrismDeleted = await DebtorsPrism.destroy({
where: { characterId: parsedCharacterId }, where: { characterId: targetCharacterId },
transaction: t transaction: t
}); });
summary.electionCandidateDeleted = await Candidate.destroy({ summary.electionCandidateDeleted = await Candidate.destroy({
where: { characterId: parsedCharacterId }, where: { characterId: targetCharacterId },
transaction: t transaction: t
}); });
@@ -1257,14 +1293,14 @@ class AdminService {
RETURNING id RETURNING id
`, `,
{ {
replacements: { characterId: parsedCharacterId }, replacements: { characterId: targetCharacterId },
type: QueryTypes.SELECT, type: QueryTypes.SELECT,
transaction: t transaction: t
} }
); );
summary.politicalOfficeArchived = archivedRows.length; summary.politicalOfficeArchived = archivedRows.length;
summary.politicalOfficeDeleted = await PoliticalOffice.destroy({ summary.politicalOfficeDeleted = await PoliticalOffice.destroy({
where: { characterId: parsedCharacterId }, where: { characterId: targetCharacterId },
transaction: t transaction: t
}); });
@@ -1274,7 +1310,7 @@ class AdminService {
DELETE FROM falukant_data.occupied_political_office DELETE FROM falukant_data.occupied_political_office
WHERE character_id = :characterId WHERE character_id = :characterId
`, `,
{ characterId: parsedCharacterId } { characterId: targetCharacterId }
); );
await deleteIfTableExists( await deleteIfTableExists(
'falukant_data.political_benefit_last_tick', 'falukant_data.political_benefit_last_tick',
@@ -1282,7 +1318,7 @@ class AdminService {
DELETE FROM falukant_data.political_benefit_last_tick DELETE FROM falukant_data.political_benefit_last_tick
WHERE character_id = :characterId WHERE character_id = :characterId
`, `,
{ characterId: parsedCharacterId } { characterId: targetCharacterId }
); );
await deleteIfTableExists( await deleteIfTableExists(
'falukant_data.falukant_character_trait', 'falukant_data.falukant_character_trait',
@@ -1290,7 +1326,7 @@ class AdminService {
DELETE FROM falukant_data.falukant_character_trait DELETE FROM falukant_data.falukant_character_trait
WHERE character_id = :characterId WHERE character_id = :characterId
`, `,
{ characterId: parsedCharacterId } { characterId: targetCharacterId }
); );
await deleteIfTableExists( await deleteIfTableExists(
'falukant_data.church_application', 'falukant_data.church_application',
@@ -1299,7 +1335,7 @@ class AdminService {
WHERE character_id = :characterId WHERE character_id = :characterId
OR supervisor_id = :characterId OR supervisor_id = :characterId
`, `,
{ characterId: parsedCharacterId } { characterId: targetCharacterId }
); );
await deleteIfTableExists( await deleteIfTableExists(
'falukant_data.church_office', 'falukant_data.church_office',
@@ -1308,7 +1344,7 @@ class AdminService {
WHERE character_id = :characterId WHERE character_id = :characterId
OR supervisor_id = :characterId OR supervisor_id = :characterId
`, `,
{ characterId: parsedCharacterId } { characterId: targetCharacterId }
); );
await deleteIfTableExists( await deleteIfTableExists(
'falukant_data.political_appointment', 'falukant_data.political_appointment',
@@ -1317,7 +1353,7 @@ class AdminService {
WHERE appointer_character_id = :characterId WHERE appointer_character_id = :characterId
OR target_character_id = :characterId OR target_character_id = :characterId
`, `,
{ characterId: parsedCharacterId } { characterId: targetCharacterId }
); );
await deleteIfTableExists( await deleteIfTableExists(
'falukant_data.marriage_proposals', 'falukant_data.marriage_proposals',
@@ -1326,7 +1362,7 @@ class AdminService {
WHERE requester_character_id = :characterId WHERE requester_character_id = :characterId
OR proposed_character_id = :characterId OR proposed_character_id = :characterId
`, `,
{ characterId: parsedCharacterId } { characterId: targetCharacterId }
); );
await deleteIfTableExists( await deleteIfTableExists(
'falukant_data.child_relation', 'falukant_data.child_relation',
@@ -1336,7 +1372,7 @@ class AdminService {
OR mother_character_id = :characterId OR mother_character_id = :characterId
OR child_character_id = :characterId OR child_character_id = :characterId
`, `,
{ characterId: parsedCharacterId } { characterId: targetCharacterId }
); );
await deleteIfTableExists( await deleteIfTableExists(
'falukant_data.relationship_state', 'falukant_data.relationship_state',
@@ -1348,7 +1384,7 @@ class AdminService {
OR character2_id = :characterId OR character2_id = :characterId
) )
`, `,
{ characterId: parsedCharacterId } { characterId: targetCharacterId }
); );
await deleteIfTableExists( await deleteIfTableExists(
'falukant_data.relationship', 'falukant_data.relationship',
@@ -1357,7 +1393,7 @@ class AdminService {
WHERE character1_id = :characterId WHERE character1_id = :characterId
OR character2_id = :characterId OR character2_id = :characterId
`, `,
{ characterId: parsedCharacterId } { characterId: targetCharacterId }
); );
await deleteIfTableExists( await deleteIfTableExists(
'falukant_data.region_tax_history', 'falukant_data.region_tax_history',
@@ -1365,7 +1401,7 @@ class AdminService {
DELETE FROM falukant_data.region_tax_history DELETE FROM falukant_data.region_tax_history
WHERE setter_character_id = :characterId WHERE setter_character_id = :characterId
`, `,
{ characterId: parsedCharacterId } { characterId: targetCharacterId }
); );
await deleteIfTableExists( await deleteIfTableExists(
'falukant_log.health_activity', 'falukant_log.health_activity',
@@ -1373,7 +1409,7 @@ class AdminService {
DELETE FROM falukant_log.health_activity DELETE FROM falukant_log.health_activity
WHERE character_id = :characterId WHERE character_id = :characterId
`, `,
{ characterId: parsedCharacterId } { characterId: targetCharacterId }
); );
await deleteIfTableExists( await deleteIfTableExists(
'falukant_log.political_office_history', 'falukant_log.political_office_history',
@@ -1381,18 +1417,18 @@ class AdminService {
DELETE FROM falukant_log.political_office_history DELETE FROM falukant_log.political_office_history
WHERE character_id = :characterId WHERE character_id = :characterId
`, `,
{ characterId: parsedCharacterId } { characterId: targetCharacterId }
); );
const deletedCharacters = await FalukantCharacter.destroy({ const deletedCharacters = await FalukantCharacter.destroy({
where: { id: parsedCharacterId }, where: { id: targetCharacterId },
transaction: t transaction: t
}); });
summary.characterDeleted = deletedCharacters > 0; summary.characterDeleted = deletedCharacters > 0;
}); });
if (character.userId) { if (targetCharacter.userId) {
const falukantUser = await FalukantUser.findByPk(character.userId, { const falukantUser = await FalukantUser.findByPk(targetCharacter.userId, {
include: [{ model: User, as: 'user', attributes: ['hashedId'] }] include: [{ model: User, as: 'user', attributes: ['hashedId'] }]
}); });
const hashedId = falukantUser?.user?.hashedId; const hashedId = falukantUser?.user?.hashedId;