feat(profile): implement user-initiated GDPR-compliant profile deletion
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Failing after 4m36s
Test Suite / flutter-quality (push) Failing after 40s

- Add DELETE /users/me endpoint with cascading data removal
- Implement frontend confirmation dialog and deletion flow
- Add audit logging for deletion requests
- Update localization files for new UI strings
- Add scheduled cleanup service for AI traces
- Document GDPR compliance in technical specification

BREAKING CHANGE: Users can now permanently delete their profiles and associated data
This commit is contained in:
Nils-Johan Gynther
2026-05-21 22:19:50 +02:00
parent 6ddb58dc7c
commit 8c9da36312
23 changed files with 776 additions and 34 deletions
+154
View File
@@ -0,0 +1,154 @@
# Plan: Implementera automatiserad datarensning för AiTrace
## Mål
Implementera en automatiserad rensning av gamla `AiTrace`-poster för att säkerställa att känsliga data inte lagras längre än nödvändigt och för att följa GDPR-krav.
## Bakgrund
- `AiTrace`-tabellen lagrar känsliga data (t.ex. maskerade promptar och AI-svar) utan någon retention-policy.
- Risk för att data ackumuleras obehindrat, vilket kan leda till lagringsproblem och GDPR-brott.
- Enligt GDPR bör känsliga data rensas efter en viss tid om de inte längre behövs för felsökning eller analys.
## Krav
1. Rensa `AiTrace`-poster äldre än 30 dagar.
2. Schemalägg rensningen att köra dagligen vid midnatt.
3. Logga antalet rensade poster för övervakning.
4. Säkerställa att rensningen inte påverkar systemets prestanda.
## Implementeringsplan
### Steg 1: Installera beroenden
Installera `NestJS Schedule`-modulen för att möjliggöra schemalagda jobb.
**Kommando:**
```bash
npm install @nestjs/schedule
```
### Steg 2: Konfigurera ScheduleModule
Lägg till `ScheduleModule` i `AppModule` för att aktivera schemalagda jobb.
**Fil:** `backend/src/app.module.ts`
**Ändring:**
```typescript
import { ScheduleModule } from '@nestjs/schedule';
@Module({
imports: [ScheduleModule.forRoot()],
})
export class AppModule {}
```
### Steg 3: Skapa AiTraceCleanupService
Skapa en ny tjänst för att hantera rensningen av `AiTrace`-poster.
**Fil:** `backend/src/ai/ai-trace-cleanup.service.ts`
**Innehåll:**
```typescript
import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { PrismaService } from '../prisma/prisma.service';
@Injectable()
export class AiTraceCleanupService {
private readonly logger = new Logger(AiTraceCleanupService.name);
constructor(private readonly prisma: PrismaService) {}
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
async cleanupOldTraces() {
this.logger.log('Starting cleanup of old AiTrace records...');
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
const result = await this.prisma.aiTrace.deleteMany({
where: {
createdAt: {
lt: thirtyDaysAgo,
},
},
});
this.logger.log(`Cleaned up ${result.count} old AiTrace records.`);
}
}
```
### Steg 4: Registrera AiTraceCleanupService
Lägg till `AiTraceCleanupService` i `AiTraceModule` för att aktivera tjänsten.
**Fil:** `backend/src/ai/ai-trace.module.ts`
**Ändring:**
```typescript
import { AiTraceCleanupService } from './ai-trace-cleanup.service';
@Module({
providers: [AiTraceService, AiTraceCleanupService],
})
export class AiTraceModule {}
```
### Steg 5: Testa rensningen manuellt
Skapa en testmetod för att manuellt köra rensningen och verifiera att den fungerar som förväntat.
**Fil:** `backend/src/ai/ai-trace.controller.ts`
**Ändring:**
```typescript
import { AiTraceCleanupService } from './ai-trace-cleanup.service';
@Controller('ai/traces')
export class AiTraceController {
constructor(
private readonly aiTraceService: AiTraceService,
private readonly aiTraceCleanupService: AiTraceCleanupService,
) {}
@Post('cleanup')
@Roles('admin')
async manualCleanup() {
await this.aiTraceCleanupService.cleanupOldTraces();
return { success: true, message: 'Manual cleanup completed.' };
}
}
```
### Steg 6: Verifiera implementationen
1. Skapa några testposter i `AiTrace`-tabellen med olika `createdAt`-datum.
2. Kör den manuella rensningen via `POST /api/ai/traces/cleanup`.
3. Verifiera att endast poster äldre än 30 dagar har rensats.
4. Kontrollera loggarna för att säkerställa att antalet rensade poster loggas korrekt.
### Steg 7: Dokumentera ändringarna
Uppdatera `TEKNISK_BESKRIVNING.md` med information om den automatiserade rensningen.
**Fil:** `TEKNISK_BESKRIVNING.md`
**Ändring:**
```markdown
## Automatiserad datarensning för AiTrace
För att säkerställa att känsliga data inte lagras längre än nödvändigt, har en automatiserad rensning implementerats:
- **Rensningsintervall**: Dagligen vid midnatt.
- **Retention-period**: 30 dagar.
- **Loggning**: Antalet rensade poster loggas för övervakning.
### Implementation
- **Tjänst**: `AiTraceCleanupService` i `backend/src/ai/ai-trace-cleanup.service.ts`.
- **Schemaläggning**: Använder `@nestjs/schedule` för att köra rensningen dagligen.
- **Manuell rensning**: Tillgänglig via `POST /api/ai/traces/cleanup` (kräver admin-behörighet).
### GDPR-efterlevnad
- Rensningen säkerställer att känsliga data raderas efter 30 dagar, vilket uppfyller GDPR-krav på dataminimering.
- Användare kan begära att deras data raderas tidigare via admin-panelen.
```
## Frågor och överväganden
1. **Retention-period**: Är 30 dagar en lämplig period, eller bör den justeras baserat påelsökningsbehov?
2. **Loggning**: Bör loggarna för rensningen sparas längre för revisionsändamål?
3. **Prestanda**: Bör rensningen köra under lågtrafikperioder för att minimera påverkan på systemet?
## Nästa steg
1. Implementera planen enligt ovan.
2. Testa rensningen i en testmiljö.
3. Verifiera att loggarna är korrekta och att rensningen fungerar som förväntat.
4. Dokumentera ändringarna i `TEKNISK_BESKRIVNING.md`.
5. Informera teamet om den nya funktionaliteten och dess påverkan på GDPR-efterlevnaden.
+215
View File
@@ -0,0 +1,215 @@
# Plan: Implementera användarinitierad radering av personuppgifter
## Mål
Skapa en tydlig och stegvis plan för hur användare kan ta bort sina personuppgifter från sin profil på plattformen, inklusive teknisk implementation och användarflöde.
## Bakgrund
- GDPR kräver att användare ska kunna begära radering av sina personuppgifter.
- Nuvarande plattform saknar ett tydligt användarflöde för att initiera radering av personuppgifter.
- Användare bör kunna ta bort sin profil och associerade data på ett säkert och transparent sätt.
## Krav
1. Lägg till en "Ta bort min profil"-knapp i användarens profilinställningar.
2. Implementera en bekräftelsedialog för att förhindra oavsiktlig radering.
3. Skapa en backend-endpoint för att hantera raderingsbegäran.
4. Se till att all användardata (profil, produkter, recept, etc.) raderas eller anonymiseras.
5. Logga raderingsbegäran för revisionsändamål.
6. Skicka ett bekräftelsemeddelande till användaren efter radering.
## Implementeringsplan
### Steg 1: Lägg till "Ta bort min profil"-knapp i Flutter UI
Lägg till en knapp i användarens profilinställningar som låter användaren initiera radering av sin profil.
**Fil:** `flutter/lib/features/profile/presentation/profile_screen.dart`
**Ändring:**
```dart
ElevatedButton(
onPressed: () {
_showDeleteProfileConfirmation();
},
child: Text('Ta bort min profil'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
),
)
```
### Steg 2: Implementera bekräftelsedialog
Skapa en dialog som kräver att användaren bekräftar raderingen.
**Fil:** `flutter/lib/features/profile/presentation/profile_screen.dart`
**Ändring:**
```dart
Future<void> _showDeleteProfileConfirmation() async {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Bekräfta radering'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('Är du säker på att du vill ta bort din profil?'),
Text('Alla dina data kommer att raderas permanent.'),
],
),
),
actions: <Widget>[
TextButton(
child: Text('Avbryt'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: Text('Ta bort'),
onPressed: () {
_deleteProfile();
Navigator.of(context).pop();
},
),
],
);
},
);
}
```
### Steg 3: Skapa backend-endpoint för radering
Implementera en endpoint som hanterar raderingsbegäran och raderar all associerad data.
**Fil:** `backend/src/users/users.controller.ts`
**Ändring:**
```typescript
@Delete('me')
@UseGuards(JwtAuthGuard)
async deleteProfile(@CurrentUser() user: UserEntity) {
await this.usersService.deleteUserAndData(user.id);
return { success: true, message: 'Din profil och data har tagits bort.' };
}
```
### Steg 4: Implementera raderingslogik i UsersService
Skapa en metod som raderar användaren och all associerad data.
**Fil:** `backend/src/users/users.service.ts`
**Ändring:**
```typescript
async deleteUserAndData(userId: number) {
await this.prisma.$transaction([
this.prisma.product.deleteMany({ where: { ownerId: userId } }),
this.prisma.recipe.deleteMany({ where: { ownerId: userId } }),
this.prisma.inventoryItem.deleteMany({ where: { userId } }),
this.prisma.mealPlanEntry.deleteMany({ where: { userId } }),
this.prisma.user.delete({ where: { id: userId } }),
]);
this.logger.log(`User ${userId} and associated data deleted.`);
}
```
### Steg 5: Logga raderingsbegäran
Lägg till loggning för att spåra raderingsbegäran för revisionsändamål.
**Fil:** `backend/src/users/users.service.ts`
**Ändring:**
```typescript
async logDeletionRequest(userId: number, userEmail: string) {
await this.prisma.auditLog.create({
data: {
action: 'USER_DELETION',
userId,
email: userEmail,
metadata: { message: 'User initiated deletion of their profile and data.' },
},
});
}
```
### Steg 6: Skicka bekräftelsemeddelande
Skicka ett e-postmeddelande till användaren för att bekräfta raderingen.
**Fil:** `backend/src/users/users.service.ts`
**Ändring:**
```typescript
async sendDeletionConfirmationEmail(email: string) {
await this.emailService.sendEmail({
to: email,
subject: 'Bekräftelse på radering av din profil',
text: 'Din profil och alla associerade data har tagits bort från vår plattform.',
});
}
```
### Steg 7: Uppdatera Flutter för att anropa backend-endpoint
Implementera metoden för att anropa backend-endpoint för radering.
**Fil:** `flutter/lib/features/profile/presentation/profile_screen.dart`
**Ändring:**
```dart
Future<void> _deleteProfile() async {
try {
final response = await ApiService.delete('/users/me');
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Din profil har tagits bort.'));
);
Navigator.of(context).pushNamedAndRemoveUntil('/login', (route) => false);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Ett fel uppstod vid radering av din profil.'));
);
}
}
```
### Steg 8: Testa implementeringen
1. Skapa en testanvändare i systemet.
2. Navigera till profilinställningar och klicka på "Ta bort min profil".
3. Bekräfta raderingen i dialogrutan.
4. Verifiera att användaren och all associerad data har tagits bort från databasen.
5. Kontrollera att ett bekräftelsemeddelande har skickats till användarens e-post.
6. Verifiera att raderingsbegäran har loggats i `AuditLog`.
### Steg 9: Dokumentera ändringarna
Uppdatera `TEKNISK_BESKRIVNING.md` med information om den nya funktionaliteten.
**Fil:** `TEKNISK_BESKRIVNING.md`
**Ändring:**
```markdown
## Användarinitierad radering av personuppgifter
För att uppfylla GDPR-krav har en funktion implementerats som låter användare ta bort sin profil och associerade data:
- **Användarflöde**: Användaren kan initiera radering via en knapp i profilinställningarna.
- **Bekräftelse**: En dialog kräver bekräftelse för att förhindra oavsiktlig radering.
- **Backend-endpoint**: `DELETE /users/me` hanterar raderingsbegäran.
- **Data som raderas**: Profil, produkter, recept, inventarieposter och matplaner.
- **Loggning**: Raderingsbegäran loggas i `AuditLog` för revisionsändamål.
- **Bekräftelse**: Ett e-postmeddelande skickas till användaren efter radering.
### Implementation
- **Frontend**: `flutter/lib/features/profile/presentation/profile_screen.dart`
- **Backend**: `backend/src/users/users.controller.ts` och `backend/src/users/users.service.ts`
- **Loggning**: `AuditLog`-poster skapas för varje raderingsbegäran.
- **E-postbekräftelse**: Skickas via `EmailService`.
### GDPR-efterlevnad
- Användare har full kontroll över sina personuppgifter.
- Raderingsprocessen är transparent och dokumenterad.
- All data raderas eller anonymiseras enligt GDPR-krav.
```
## Frågor och överväganden
1. **Dataretention**: Bör vissa data (t.ex. transaktionshistorik) sparas för revisionsändamål även efter radering?
2. **Anonymisering**: Bör vi anonymisera data istället för att radera dem helt?
3. **Ångerperiod**: Bör vi implementera en ångerperiod där användaren kan återställa sin profil inom en viss tid?
## Nästa steg
1. Implementera planen enligt ovan.
2. Testa funktionaliteten i en testmiljö.
3. Verifiera att all data raderas korrekt och att loggning fungerar.
4. Dokumentera ändringarna i `TEKNISK_BESKRIVNING.md`.
5. Informera teamet om den nya funktionaliteten och dess påverkan på GDPR-efterlevnaden.