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/webui/src/lib/premium.ts
Normal file
96
apps/webui/src/lib/premium.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import type { PrismaClient } from '@prisma/client';
|
||||
import {
|
||||
DEFAULT_PREMIUM_TIER_CONFIGS,
|
||||
featureKeyForResource,
|
||||
isAssignmentActive,
|
||||
limitKeyForResource,
|
||||
parsePremiumFeatures,
|
||||
PremiumTierSchema,
|
||||
type PremiumLimitResource,
|
||||
type PremiumTier,
|
||||
type PremiumTierConfig
|
||||
} from '@nexumi/shared';
|
||||
|
||||
export class PremiumLimitError extends Error {
|
||||
constructor(
|
||||
public readonly code: 'feature_disabled' | 'limit_reached',
|
||||
public readonly resource: PremiumLimitResource,
|
||||
public readonly limit: number,
|
||||
public readonly current: number,
|
||||
public readonly tier: PremiumTier
|
||||
) {
|
||||
super(code);
|
||||
}
|
||||
}
|
||||
|
||||
function toConfig(row: {
|
||||
tier: string;
|
||||
maxTags: number;
|
||||
maxFeeds: number;
|
||||
maxBackups: number;
|
||||
features: unknown;
|
||||
}): PremiumTierConfig {
|
||||
return {
|
||||
tier: PremiumTierSchema.parse(row.tier),
|
||||
maxTags: row.maxTags,
|
||||
maxFeeds: row.maxFeeds,
|
||||
maxBackups: row.maxBackups,
|
||||
features: parsePremiumFeatures(row.features)
|
||||
};
|
||||
}
|
||||
|
||||
export async function ensurePremiumTierConfigs(prisma: PrismaClient): Promise<PremiumTierConfig[]> {
|
||||
const configs: PremiumTierConfig[] = [];
|
||||
for (const tier of PremiumTierSchema.options) {
|
||||
const defaults = DEFAULT_PREMIUM_TIER_CONFIGS[tier];
|
||||
const row = await prisma.premiumTierConfig.upsert({
|
||||
where: { tier },
|
||||
create: {
|
||||
tier,
|
||||
maxTags: defaults.maxTags,
|
||||
maxFeeds: defaults.maxFeeds,
|
||||
maxBackups: defaults.maxBackups,
|
||||
features: defaults.features
|
||||
},
|
||||
update: {}
|
||||
});
|
||||
configs.push(toConfig(row));
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
|
||||
export async function resolveGuildTier(prisma: PrismaClient, guildId: string): Promise<PremiumTier> {
|
||||
const assignment = await prisma.guildPremiumAssignment.findUnique({ where: { guildId } });
|
||||
if (!assignment || !isAssignmentActive(assignment.expiresAt)) {
|
||||
return 'FREE';
|
||||
}
|
||||
const parsed = PremiumTierSchema.safeParse(assignment.tier);
|
||||
return parsed.success ? parsed.data : 'FREE';
|
||||
}
|
||||
|
||||
export async function resolveGuildPremiumConfig(
|
||||
prisma: PrismaClient,
|
||||
guildId: string
|
||||
): Promise<PremiumTierConfig> {
|
||||
const tier = await resolveGuildTier(prisma, guildId);
|
||||
const configs = await ensurePremiumTierConfigs(prisma);
|
||||
return configs.find((config) => config.tier === tier) ?? DEFAULT_PREMIUM_TIER_CONFIGS[tier];
|
||||
}
|
||||
|
||||
export async function assertPremiumLimit(
|
||||
prisma: PrismaClient,
|
||||
guildId: string,
|
||||
resource: PremiumLimitResource,
|
||||
currentCount: number
|
||||
): Promise<PremiumTierConfig> {
|
||||
const config = await resolveGuildPremiumConfig(prisma, guildId);
|
||||
const featureKey = featureKeyForResource(resource);
|
||||
if (!config.features[featureKey]) {
|
||||
throw new PremiumLimitError('feature_disabled', resource, 0, currentCount, config.tier);
|
||||
}
|
||||
const limit = config[limitKeyForResource(resource)];
|
||||
if (currentCount >= limit) {
|
||||
throw new PremiumLimitError('limit_reached', resource, limit, currentCount, config.tier);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
Reference in New Issue
Block a user