feat(dashboard,admin): widget group customization and FDISK data purge

This commit is contained in:
Matthias Hochmeister
2026-04-13 15:06:34 +02:00
parent f4690cf185
commit dd5cd71fd1
7 changed files with 491 additions and 156 deletions

View File

@@ -0,0 +1,28 @@
-- Migration: 083_allow_null_profile_status
-- Allow mitglieder_profile.status to be NULL (for FDISK data purge).
-- Rollback:
-- ALTER TABLE mitglieder_profile DROP CONSTRAINT IF EXISTS mitglieder_profile_status_check;
-- ALTER TABLE mitglieder_profile ALTER COLUMN status SET NOT NULL;
-- ALTER TABLE mitglieder_profile ALTER COLUMN status SET DEFAULT 'aktiv';
-- ALTER TABLE mitglieder_profile ADD CONSTRAINT mitglieder_profile_status_check
-- CHECK (status IN ('aktiv','passiv','ehrenmitglied','jugendfeuerwehr','anwärter','ausgetreten'));
-- 1. Drop existing CHECK constraint
ALTER TABLE mitglieder_profile DROP CONSTRAINT IF EXISTS mitglieder_profile_status_check;
-- 2. Allow NULLs
ALTER TABLE mitglieder_profile ALTER COLUMN status DROP NOT NULL;
-- 3. Remove default
ALTER TABLE mitglieder_profile ALTER COLUMN status DROP DEFAULT;
-- 4. Re-add CHECK allowing NULL
ALTER TABLE mitglieder_profile ADD CONSTRAINT mitglieder_profile_status_check
CHECK (status IS NULL OR status IN (
'aktiv',
'passiv',
'ehrenmitglied',
'jugendfeuerwehr',
'anwärter',
'ausgetreten'
));

View File

@@ -368,31 +368,74 @@ router.delete(
);
// ---------------------------------------------------------------------------
// DELETE /api/admin/debug/user/:userId/profile — delete mitglieder_profile row
// DELETE /api/admin/users/:userId/fdisk-data — purge FDISK-synced data
// ---------------------------------------------------------------------------
router.delete(
'/debug/user/:userId/profile',
'/users/:userId/fdisk-data',
authenticate,
requirePermission('admin:write'),
async (req: Request, res: Response): Promise<void> => {
const targetUserId = req.params.userId;
const client = await pool.connect();
try {
const userId = req.params.userId;
const result = await pool.query(
'DELETE FROM mitglieder_profile WHERE user_id = $1',
[userId]
await client.query('BEGIN');
// Null out FDISK-synced profile fields
const profileResult = await client.query(
`UPDATE mitglieder_profile
SET status = NULL,
eintrittsdatum = NULL,
austrittsdatum = NULL,
geburtsdatum = NULL,
geburtsort = NULL,
geschlecht = NULL,
beruf = NULL,
wohnort = NULL,
plz = NULL,
dienstgrad = NULL,
updated_at = NOW()
WHERE user_id = $1`,
[targetUserId]
);
if ((result.rowCount ?? 0) === 0) {
res.status(404).json({ success: false, message: 'Kein Profil fuer diesen Benutzer gefunden' });
return;
}
const ausbildungen = await client.query(
'DELETE FROM ausbildungen WHERE user_id = $1',
[targetUserId]
);
const befoerderungen = await client.query(
'DELETE FROM befoerderungen WHERE user_id = $1',
[targetUserId]
);
const untersuchungen = await client.query(
'DELETE FROM untersuchungen WHERE user_id = $1',
[targetUserId]
);
const fahrgenehmigungen = await client.query(
'DELETE FROM fahrgenehmigungen WHERE user_id = $1',
[targetUserId]
);
logger.info('Admin deleted user profile data', { userId, admin: req.user?.id });
res.json({ success: true, message: 'Profildaten geloescht' });
await client.query('COMMIT');
logger.info('Admin purged FDISK data', { targetUserId, adminId: req.user?.id });
res.json({
success: true,
data: {
profileFieldsCleared: profileResult.rowCount ?? 0,
ausbildungen: ausbildungen.rowCount ?? 0,
befoerderungen: befoerderungen.rowCount ?? 0,
untersuchungen: untersuchungen.rowCount ?? 0,
fahrgenehmigungen: fahrgenehmigungen.rowCount ?? 0,
},
});
} catch (error) {
logger.error('Failed to delete user profile', { error, userId: req.params.userId });
res.status(500).json({ success: false, message: 'Fehler beim Loeschen der Profildaten' });
await client.query('ROLLBACK');
logger.error('Failed to purge FDISK data', { error, targetUserId });
res.status(500).json({ success: false, message: 'Fehler beim Löschen der FDISK-Daten' });
} finally {
client.release();
}
}
);