feat: Add GDPR compliance and improve UI/UX
GDPR Compliance: - Delete CV files immediately after processing - Add DataRetentionJob to auto-purge data after 30 days - Add DeleteAllUserDataAsync for right to erasure - Add Privacy Policy page with GDPR information - Add privacy link and GDPR badge to footer UI/UX Improvements: - Add "Why Choose RealCV" benefits section to homepage - Fix pricing page: Professional card highlight, consistent button styles - Improve text contrast on dark backgrounds (0.75 → 0.85 opacity) - Fix auth page messaging consistency - Fix CSS --realcv-accent variable (was self-referencing) UK Terminology: - Change "hiring" to "recruitment" throughout - Change "bad hires" to "poor appointments" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -185,17 +185,78 @@ public sealed class CVCheckService : ICVCheckService
|
||||
|
||||
var fileName = cvCheck.OriginalFileName;
|
||||
|
||||
// GDPR: Delete the uploaded file if it still exists
|
||||
if (!string.IsNullOrWhiteSpace(cvCheck.BlobUrl))
|
||||
{
|
||||
try
|
||||
{
|
||||
await _fileStorageService.DeleteAsync(cvCheck.BlobUrl);
|
||||
_logger.LogDebug("Deleted file for CV check {CheckId}", checkId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to delete file for CV check {CheckId}", checkId);
|
||||
// Continue with deletion even if file deletion fails
|
||||
}
|
||||
}
|
||||
|
||||
_dbContext.CVFlags.RemoveRange(cvCheck.Flags);
|
||||
_dbContext.CVChecks.Remove(cvCheck);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Deleted CV check {CheckId} for user {UserId}", checkId, userId);
|
||||
_logger.LogInformation("GDPR: Deleted CV check {CheckId} and associated data for user {UserId}", checkId, userId);
|
||||
|
||||
await _auditService.LogAsync(userId, AuditActions.CVDeleted, "CVCheck", checkId, $"File: {fileName}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<int> DeleteAllUserDataAsync(Guid userId)
|
||||
{
|
||||
_logger.LogInformation("GDPR: Deleting all CV data for user {UserId}", userId);
|
||||
|
||||
var userChecks = await _dbContext.CVChecks
|
||||
.Include(c => c.Flags)
|
||||
.Where(c => c.UserId == userId)
|
||||
.ToListAsync();
|
||||
|
||||
if (userChecks.Count == 0)
|
||||
{
|
||||
_logger.LogDebug("No CV checks found for user {UserId}", userId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
var deletedCount = 0;
|
||||
|
||||
foreach (var check in userChecks)
|
||||
{
|
||||
// Delete the file if it exists
|
||||
if (!string.IsNullOrWhiteSpace(check.BlobUrl))
|
||||
{
|
||||
try
|
||||
{
|
||||
await _fileStorageService.DeleteAsync(check.BlobUrl);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to delete file for CV check {CheckId}", check.Id);
|
||||
}
|
||||
}
|
||||
|
||||
_dbContext.CVFlags.RemoveRange(check.Flags);
|
||||
_dbContext.CVChecks.Remove(check);
|
||||
deletedCount++;
|
||||
}
|
||||
|
||||
await _dbContext.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("GDPR: Deleted {Count} CV checks for user {UserId}", deletedCount, userId);
|
||||
|
||||
await _auditService.LogAsync(userId, AuditActions.CVDeleted, null, null, $"Deleted all data: {deletedCount} checks");
|
||||
|
||||
return deletedCount;
|
||||
}
|
||||
|
||||
private static CVCheckDto MapToDto(CVCheck cvCheck)
|
||||
{
|
||||
return new CVCheckDto
|
||||
|
||||
Reference in New Issue
Block a user