diff --git a/services/mana-core-auth/src/admin/user-data.service.ts b/services/mana-core-auth/src/admin/user-data.service.ts
index 4fdbae95f..973cfd7b4 100644
--- a/services/mana-core-auth/src/admin/user-data.service.ts
+++ b/services/mana-core-auth/src/admin/user-data.service.ts
@@ -338,6 +338,118 @@ export class UserDataService {
};
}
+ /**
+ * Get full export data including sessions, security events, and transactions
+ */
+ async getFullExportData(userId: string) {
+ const summary = await this.getUserDataSummary(userId);
+
+ // Get additional details for export
+ const [sessions, securityEvents, transactions] = await Promise.all([
+ this.getSessionHistory(userId),
+ this.getSecurityEvents(userId),
+ this.getTransactionHistory(userId),
+ ]);
+
+ return {
+ ...summary,
+ exportedAt: new Date().toISOString(),
+ exportVersion: '2.0',
+ sessions: {
+ active: sessions.filter((s) => !s.revokedAt && new Date(s.expiresAt) > new Date()),
+ history: sessions,
+ },
+ securityEvents,
+ creditTransactions: transactions,
+ };
+ }
+
+ /**
+ * Get session history for a user
+ */
+ private async getSessionHistory(userId: string) {
+ const db = this.getDatabase();
+
+ return db
+ .select({
+ id: schema.sessions.id,
+ createdAt: schema.sessions.createdAt,
+ expiresAt: schema.sessions.expiresAt,
+ lastActivityAt: schema.sessions.lastActivityAt,
+ ipAddress: schema.sessions.ipAddress,
+ userAgent: schema.sessions.userAgent,
+ deviceName: schema.sessions.deviceName,
+ revokedAt: schema.sessions.revokedAt,
+ })
+ .from(schema.sessions)
+ .where(eq(schema.sessions.userId, userId))
+ .orderBy(desc(schema.sessions.createdAt))
+ .limit(100);
+ }
+
+ /**
+ * Get security events for a user
+ */
+ private async getSecurityEvents(userId: string) {
+ const db = this.getDatabase();
+
+ return db
+ .select({
+ id: schema.securityEvents.id,
+ eventType: schema.securityEvents.eventType,
+ ipAddress: schema.securityEvents.ipAddress,
+ userAgent: schema.securityEvents.userAgent,
+ metadata: schema.securityEvents.metadata,
+ createdAt: schema.securityEvents.createdAt,
+ })
+ .from(schema.securityEvents)
+ .where(eq(schema.securityEvents.userId, userId))
+ .orderBy(desc(schema.securityEvents.createdAt))
+ .limit(100);
+ }
+
+ /**
+ * Get transaction history for a user
+ */
+ private async getTransactionHistory(userId: string) {
+ const db = this.getDatabase();
+
+ return db
+ .select({
+ id: schema.transactions.id,
+ type: schema.transactions.type,
+ status: schema.transactions.status,
+ amount: schema.transactions.amount,
+ balanceBefore: schema.transactions.balanceBefore,
+ balanceAfter: schema.transactions.balanceAfter,
+ appId: schema.transactions.appId,
+ description: schema.transactions.description,
+ createdAt: schema.transactions.createdAt,
+ completedAt: schema.transactions.completedAt,
+ })
+ .from(schema.transactions)
+ .where(eq(schema.transactions.userId, userId))
+ .orderBy(desc(schema.transactions.createdAt));
+ }
+
+ /**
+ * Get user data for email (before deletion)
+ */
+ async getUserForEmail(userId: string) {
+ const db = this.getDatabase();
+
+ const user = await db
+ .select({
+ email: schema.users.email,
+ name: schema.users.name,
+ })
+ .from(schema.users)
+ .where(eq(schema.users.id, userId))
+ .limit(1);
+
+ return user[0] || null;
+ }
+
/**
* Query a single backend for user data
*/
diff --git a/services/mana-core-auth/src/email/email.service.ts b/services/mana-core-auth/src/email/email.service.ts
index 7b4887c7d..34df867d2 100644
--- a/services/mana-core-auth/src/email/email.service.ts
+++ b/services/mana-core-auth/src/email/email.service.ts
@@ -224,6 +224,56 @@ export async function sendVerificationEmail(
});
}
+/**
+ * Send account deletion confirmation email
+ */
+export async function sendAccountDeletionEmail(email: string, userName?: string): Promise
{
+ const name = userName || email.split('@')[0];
+
+ return sendEmail({
+ to: email,
+ subject: 'Dein ManaCore-Konto wurde gelöscht',
+ html: `
+
+
+
+
+
+
+
+
+
ManaCore
+
+
+ Hallo ${name},
+
+ dein ManaCore-Konto und alle damit verbundenen Daten wurden erfolgreich gelöscht.
+
+
+
Folgende Daten wurden entfernt:
+
+ - Benutzerprofil und Anmeldedaten
+ - Alle Sessions und verknüpften Accounts
+ - Credits und Transaktionshistorie
+ - Daten in allen verbundenen Apps
+
+
+
+ Diese Aktion ist unwiderruflich. Falls du ManaCore erneut nutzen möchtest, kannst du jederzeit ein neues Konto erstellen.
+
+ Bei Fragen erreichst du uns unter support@mana.how.
+
+
+
+
+ Diese E-Mail wurde automatisch von ManaCore gesendet.
+
+
+
+`,
+ });
+}
+
/**
* Send welcome/verification email
*/
diff --git a/services/mana-core-auth/src/me/me.service.ts b/services/mana-core-auth/src/me/me.service.ts
index 49da2699e..9125f2428 100644
--- a/services/mana-core-auth/src/me/me.service.ts
+++ b/services/mana-core-auth/src/me/me.service.ts
@@ -1,6 +1,7 @@
import { Injectable, Logger } from '@nestjs/common';
import { UserDataService } from '../admin/user-data.service';
import type { UserDataSummary, DeleteUserDataResponse } from '../admin/dto/user-data.dto';
+import { sendAccountDeletionEmail } from '../email/email.service';
/**
* Self-service data management for authenticated users.
@@ -22,24 +23,38 @@ export class MeService {
/**
* Export the authenticated user's data as a complete JSON object
+ * Includes sessions, security events, and credit transactions for GDPR compliance
*/
- async exportMyData(userId: string): Promise {
+ async exportMyData(userId: string): Promise {
this.logger.log(`User ${userId} exporting own data`);
- const summary = await this.userDataService.getUserDataSummary(userId);
-
- return {
- exportedAt: new Date().toISOString(),
- exportVersion: '1.0',
- data: summary,
- };
+ return this.userDataService.getFullExportData(userId);
}
/**
* Delete all data for the authenticated user (GDPR right to be forgotten)
+ * Sends confirmation email after successful deletion
*/
async deleteMyData(userId: string): Promise {
this.logger.log(`User ${userId} requesting deletion of own data`);
- return this.userDataService.deleteUserData(userId);
+
+ // Get user data BEFORE deletion for sending confirmation email
+ const user = await this.userDataService.getUserForEmail(userId);
+
+ // Perform deletion
+ const result = await this.userDataService.deleteUserData(userId);
+
+ // Send confirmation email if deletion was successful
+ if (result.success && user?.email) {
+ try {
+ await sendAccountDeletionEmail(user.email, user.name || undefined);
+ this.logger.log(`Account deletion confirmation email sent to ${user.email}`);
+ } catch (error) {
+ // Log but don't fail the deletion if email fails
+ this.logger.error(`Failed to send deletion confirmation email to ${user.email}`, error);
+ }
+ }
+
+ return result;
}
}
@@ -48,3 +63,47 @@ export interface UserDataExport {
exportVersion: string;
data: UserDataSummary;
}
+
+export interface SessionExport {
+ id: string;
+ createdAt: Date;
+ expiresAt: Date;
+ lastActivityAt: Date | null;
+ ipAddress: string | null;
+ userAgent: string | null;
+ deviceName: string | null;
+ revokedAt: Date | null;
+}
+
+export interface SecurityEventExport {
+ id: string;
+ eventType: string;
+ ipAddress: string | null;
+ userAgent: string | null;
+ metadata: unknown;
+ createdAt: Date;
+}
+
+export interface TransactionExport {
+ id: string;
+ type: string;
+ status: string;
+ amount: number;
+ balanceBefore: number;
+ balanceAfter: number;
+ appId: string;
+ description: string;
+ createdAt: Date;
+ completedAt: Date | null;
+}
+
+export interface FullUserDataExport extends UserDataSummary {
+ exportedAt: string;
+ exportVersion: string;
+ sessions: {
+ active: SessionExport[];
+ history: SessionExport[];
+ };
+ securityEvents: SecurityEventExport[];
+ creditTransactions: TransactionExport[];
+}