feat: Implement account deletion feature with UI and API integration
Some checks failed
Deploy tt-tagebuch / deploy (push) Has been cancelled

This commit is contained in:
Torsten Schulz (local)
2026-05-18 14:12:14 +02:00
parent b9bbd45ae9
commit ecfd3bf851
13 changed files with 265 additions and 9 deletions

View File

@@ -1,7 +1,18 @@
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import sequelize from '../database.js';
import User from '../models/User.js';
import UserToken from '../models/UserToken.js';
import UserClub from '../models/UserClub.js';
import Log from '../models/Log.js';
import ApiLog from '../models/ApiLog.js';
import MyTischtennis from '../models/MyTischtennis.js';
import MyTischtennisUpdateHistory from '../models/MyTischtennisUpdateHistory.js';
import MyTischtennisFetchLog from '../models/MyTischtennisFetchLog.js';
import ClickTtAccount from '../models/ClickTtAccount.js';
import BillingUserSetting from '../models/BillingUserSetting.js';
import BillingTemplate from '../models/BillingTemplate.js';
import BillingRun from '../models/BillingRun.js';
import crypto from 'crypto';
import { sendActivationEmail, sendPasswordResetEmail } from './emailService.js';
@@ -106,6 +117,47 @@ const logout = async (token) => {
return { message: 'Logout erfolgreich' };
};
const deleteOwnAccount = async (userId, password) => {
if (!userId || !password) {
const err = new Error('Passwort ist erforderlich');
err.status = 400;
throw err;
}
const user = await User.findByPk(userId);
const validPassword = user && await bcrypt.compare(password, user.password);
if (!validPassword) {
const err = new Error('Ungültiges Passwort');
err.status = 401;
throw err;
}
await sequelize.transaction(async (transaction) => {
await UserToken.destroy({ where: { userId }, transaction });
await UserClub.destroy({ where: { userId }, transaction });
await Log.destroy({ where: { userId }, transaction });
await ApiLog.update({ userId: null }, { where: { userId }, transaction });
await MyTischtennis.destroy({ where: { userId }, transaction });
await MyTischtennisUpdateHistory.destroy({ where: { userId }, transaction });
await MyTischtennisFetchLog.destroy({ where: { userId }, transaction });
await ClickTtAccount.destroy({ where: { userId }, transaction });
await BillingUserSetting.destroy({ where: { userId }, transaction });
await BillingTemplate.update({ createdByUserId: null }, { where: { createdByUserId: userId }, transaction });
await BillingRun.update(
{
createdByUserId: null,
selfRecipientName: 'Gelöschter Benutzer',
iban: null,
},
{ where: { selfRecipientUserId: userId }, transaction },
);
await BillingRun.update({ createdByUserId: null }, { where: { createdByUserId: userId }, transaction });
await user.destroy({ transaction });
});
return { success: true };
};
const requestPasswordReset = async (email) => {
if (!email) {
const err = new Error('E-Mail-Adresse ist erforderlich.');
@@ -182,4 +234,4 @@ const resetPassword = async (token, newPassword) => {
return { message: 'Passwort wurde erfolgreich geändert.' };
};
export { register, activateUser, login, logout, requestPasswordReset, resetPassword };
export { register, activateUser, login, logout, deleteOwnAccount, requestPasswordReset, resetPassword };