Implement premium features and GDPR compliance in bot and WebUI
- Added new models for premium tier configurations and assignments, enabling feature limits for free and premium users. - Introduced GDPR deletion logging and commands for user data management. - Enhanced logging configuration with retention days for audit logs. - Updated bot commands and job processing to include new premium and GDPR functionalities. - Improved localization with new keys for premium features and GDPR commands in both English and German. - Added UI components for managing premium settings and logging retention in the WebUI.
This commit is contained in:
96
apps/bot/src/modules/gdpr/service.ts
Normal file
96
apps/bot/src/modules/gdpr/service.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import type { PrismaClient } from '@prisma/client';
|
||||
|
||||
export type GdprDeletionSummary = {
|
||||
warnings: number;
|
||||
modNotes: number;
|
||||
memberLevels: number;
|
||||
memberEconomies: number;
|
||||
inventoryItems: number;
|
||||
birthdays: number;
|
||||
afkStatuses: number;
|
||||
reminders: number;
|
||||
inviteStats: number;
|
||||
casesAnonymized: number;
|
||||
userPremium: number;
|
||||
userRecord: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes or anonymizes personal data for a Discord user across guilds.
|
||||
* Moderation case history is anonymized (targetUserId cleared to a placeholder)
|
||||
* so server audit integrity is preserved.
|
||||
*/
|
||||
export async function deleteUserPersonalData(
|
||||
prisma: PrismaClient,
|
||||
userId: string,
|
||||
guildId: string | null
|
||||
): Promise<GdprDeletionSummary> {
|
||||
const scope = guildId ? { guildId, userId } : { userId };
|
||||
const caseScope = guildId
|
||||
? { guildId, targetUserId: userId }
|
||||
: { targetUserId: userId };
|
||||
|
||||
const [
|
||||
warnings,
|
||||
modNotes,
|
||||
memberLevels,
|
||||
memberEconomies,
|
||||
inventoryItems,
|
||||
birthdays,
|
||||
afkStatuses,
|
||||
reminders,
|
||||
inviteStats,
|
||||
inviteJoins,
|
||||
casesAnonymized,
|
||||
userPremium
|
||||
] = await prisma.$transaction([
|
||||
prisma.warning.deleteMany({ where: scope }),
|
||||
prisma.modNote.deleteMany({ where: scope }),
|
||||
prisma.memberLevel.deleteMany({ where: scope }),
|
||||
prisma.memberEconomy.deleteMany({ where: scope }),
|
||||
prisma.inventoryItem.deleteMany({ where: scope }),
|
||||
prisma.birthday.deleteMany({ where: scope }),
|
||||
prisma.afkStatus.deleteMany({ where: scope }),
|
||||
prisma.reminder.deleteMany({ where: { userId, ...(guildId ? { guildId } : {}) } }),
|
||||
prisma.inviteStat.deleteMany({
|
||||
where: guildId ? { guildId, inviterId: userId } : { inviterId: userId }
|
||||
}),
|
||||
prisma.inviteJoin.deleteMany({ where: scope }),
|
||||
prisma.case.updateMany({
|
||||
where: caseScope,
|
||||
data: { targetUserId: '0', reason: '[redacted – GDPR deletion]' }
|
||||
}),
|
||||
prisma.userPremiumAssignment.deleteMany({ where: { userId } })
|
||||
]);
|
||||
|
||||
let userRecord = 0;
|
||||
if (!guildId) {
|
||||
const deleted = await prisma.user.deleteMany({ where: { id: userId } });
|
||||
userRecord = deleted.count;
|
||||
}
|
||||
|
||||
const summary: GdprDeletionSummary = {
|
||||
warnings: warnings.count,
|
||||
modNotes: modNotes.count,
|
||||
memberLevels: memberLevels.count,
|
||||
memberEconomies: memberEconomies.count,
|
||||
inventoryItems: inventoryItems.count,
|
||||
birthdays: birthdays.count,
|
||||
afkStatuses: afkStatuses.count,
|
||||
reminders: reminders.count,
|
||||
inviteStats: inviteStats.count + inviteJoins.count,
|
||||
casesAnonymized: casesAnonymized.count,
|
||||
userPremium: userPremium.count,
|
||||
userRecord
|
||||
};
|
||||
|
||||
await prisma.gdprDeletionLog.create({
|
||||
data: {
|
||||
userId,
|
||||
guildId,
|
||||
summary
|
||||
}
|
||||
});
|
||||
|
||||
return summary;
|
||||
}
|
||||
Reference in New Issue
Block a user