feat(auth): add GDPR self-service endpoints for user data

Add /api/v1/me/data endpoints for users to view, export, and delete
their own data without admin privileges (GDPR compliance).

Backend:
- New MeModule with MeController and MeService
- GET /api/v1/me/data - view own data summary
- GET /api/v1/me/data/export - download as JSON
- DELETE /api/v1/me/data - delete all own data

Frontend:
- New /settings/my-data page with full data overview
- Export button for JSON download
- DeleteConfirmationModal with email verification
- Link from settings page to my-data

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2026-02-12 13:20:08 +01:00
parent 759b227355
commit 9881e84ee3
9 changed files with 928 additions and 13 deletions

View file

@ -17,5 +17,6 @@ import { AuthModule } from '../auth/auth.module';
],
controllers: [UserDataController],
providers: [UserDataService, AdminGuard],
exports: [UserDataService],
})
export class AdminModule {}

View file

@ -13,6 +13,7 @@ import { HealthModule } from './health/health.module';
import { ReferralsModule } from './referrals/referrals.module';
import { SettingsModule } from './settings/settings.module';
import { TagsModule } from './tags/tags.module';
import { MeModule } from './me/me.module';
import { AnalyticsModule } from './analytics';
import { MetricsModule } from './metrics';
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
@ -43,6 +44,7 @@ import { LoggerModule } from './common/logger';
ReferralsModule,
SettingsModule,
TagsModule,
MeModule,
],
providers: [
{

View file

@ -0,0 +1,54 @@
import { Controller, Get, Delete, UseGuards, Res, HttpStatus } from '@nestjs/common';
import type { Response } from 'express';
import { MeService } from './me.service';
import { JwtAuthGuard } from '../common/guards/jwt-auth.guard';
import { CurrentUser, type CurrentUserData } from '../common/decorators/current-user.decorator';
/**
* Self-service endpoints for users to manage their own data.
* GDPR compliance: view, export, and delete personal data.
*
* All endpoints require authentication via JwtAuthGuard.
* User ID is extracted from the JWT token - no userId parameter needed.
*/
@Controller('api/v1/me')
@UseGuards(JwtAuthGuard)
export class MeController {
constructor(private readonly meService: MeService) {}
/**
* Get the authenticated user's data summary.
* Returns aggregated data from auth, credits, and all connected project backends.
*/
@Get('data')
async getMyData(@CurrentUser() user: CurrentUserData) {
return this.meService.getMyData(user.userId);
}
/**
* Export the authenticated user's data as a JSON file download.
* GDPR Article 20: Right to data portability.
*/
@Get('data/export')
async exportMyData(@CurrentUser() user: CurrentUserData, @Res() res: Response) {
const exportData = await this.meService.exportMyData(user.userId);
const filename = `meine-daten-${new Date().toISOString().split('T')[0]}.json`;
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
res.status(HttpStatus.OK).send(JSON.stringify(exportData, null, 2));
}
/**
* Delete all data for the authenticated user.
* GDPR Article 17: Right to erasure ("right to be forgotten").
*
* This performs a soft-delete of the user account and hard-deletes all associated data.
* The operation is irreversible.
*/
@Delete('data')
async deleteMyData(@CurrentUser() user: CurrentUserData) {
return this.meService.deleteMyData(user.userId);
}
}

View file

@ -0,0 +1,20 @@
import { Module } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { ConfigModule } from '@nestjs/config';
import { MeController } from './me.controller';
import { MeService } from './me.service';
import { AdminModule } from '../admin/admin.module';
@Module({
imports: [
ConfigModule,
HttpModule.register({
timeout: 5000,
maxRedirects: 3,
}),
AdminModule,
],
controllers: [MeController],
providers: [MeService],
})
export class MeModule {}

View file

@ -0,0 +1,50 @@
import { Injectable, Logger } from '@nestjs/common';
import { UserDataService } from '../admin/user-data.service';
import type { UserDataSummary, DeleteUserDataResponse } from '../admin/dto/user-data.dto';
/**
* Self-service data management for authenticated users.
* Wraps UserDataService to allow users to access their own data without admin privileges.
*/
@Injectable()
export class MeService {
private readonly logger = new Logger(MeService.name);
constructor(private readonly userDataService: UserDataService) {}
/**
* Get the authenticated user's data summary
*/
async getMyData(userId: string): Promise<UserDataSummary> {
this.logger.log(`User ${userId} requesting own data`);
return this.userDataService.getUserDataSummary(userId);
}
/**
* Export the authenticated user's data as a complete JSON object
*/
async exportMyData(userId: string): Promise<UserDataExport> {
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,
};
}
/**
* Delete all data for the authenticated user (GDPR right to be forgotten)
*/
async deleteMyData(userId: string): Promise<DeleteUserDataResponse> {
this.logger.log(`User ${userId} requesting deletion of own data`);
return this.userDataService.deleteUserData(userId);
}
}
export interface UserDataExport {
exportedAt: string;
exportVersion: string;
data: UserDataSummary;
}