mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-22 13:26:42 +02:00
✨ 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:
parent
759b227355
commit
9881e84ee3
9 changed files with 928 additions and 13 deletions
|
|
@ -17,5 +17,6 @@ import { AuthModule } from '../auth/auth.module';
|
|||
],
|
||||
controllers: [UserDataController],
|
||||
providers: [UserDataService, AdminGuard],
|
||||
exports: [UserDataService],
|
||||
})
|
||||
export class AdminModule {}
|
||||
|
|
|
|||
|
|
@ -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: [
|
||||
{
|
||||
|
|
|
|||
54
services/mana-core-auth/src/me/me.controller.ts
Normal file
54
services/mana-core-auth/src/me/me.controller.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
20
services/mana-core-auth/src/me/me.module.ts
Normal file
20
services/mana-core-auth/src/me/me.module.ts
Normal 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 {}
|
||||
50
services/mana-core-auth/src/me/me.service.ts
Normal file
50
services/mana-core-auth/src/me/me.service.ts
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue