Files
Nexumi/packages/shared/src/premium.ts
smueller 08af99ed52 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.
2026-07-22 16:59:23 +02:00

95 lines
3.1 KiB
TypeScript

import { z } from 'zod';
const snowflake = z.string().regex(/^\d{17,20}$/);
export const PremiumTierSchema = z.enum(['FREE', 'PREMIUM']);
export type PremiumTier = z.infer<typeof PremiumTierSchema>;
export const PremiumFeatureKeySchema = z.enum(['tags', 'feeds', 'guildbackup']);
export type PremiumFeatureKey = z.infer<typeof PremiumFeatureKeySchema>;
export const PremiumFeaturesSchema = z.object({
tags: z.boolean(),
feeds: z.boolean(),
guildbackup: z.boolean()
});
export type PremiumFeatures = z.infer<typeof PremiumFeaturesSchema>;
export const PremiumTierConfigSchema = z.object({
tier: PremiumTierSchema,
maxTags: z.number().int().min(0).max(10_000),
maxFeeds: z.number().int().min(0).max(10_000),
maxBackups: z.number().int().min(0).max(10_000),
features: PremiumFeaturesSchema
});
export type PremiumTierConfig = z.infer<typeof PremiumTierConfigSchema>;
export const PremiumTierConfigPatchSchema = PremiumTierConfigSchema.omit({ tier: true }).partial().refine(
(value) => Object.keys(value).length > 0,
{ message: 'At least one field is required' }
);
export type PremiumTierConfigPatch = z.infer<typeof PremiumTierConfigPatchSchema>;
export const DEFAULT_PREMIUM_TIER_CONFIGS: Record<PremiumTier, PremiumTierConfig> = {
FREE: {
tier: 'FREE',
maxTags: 15,
maxFeeds: 3,
maxBackups: 2,
features: { tags: true, feeds: true, guildbackup: true }
},
PREMIUM: {
tier: 'PREMIUM',
maxTags: 100,
maxFeeds: 25,
maxBackups: 25,
features: { tags: true, feeds: true, guildbackup: true }
}
};
export const GuildPremiumAssignSchema = z.object({
guildId: snowflake,
tier: PremiumTierSchema,
note: z.string().max(500).nullable().optional(),
expiresAt: z.string().datetime().nullable().optional()
});
export type GuildPremiumAssign = z.infer<typeof GuildPremiumAssignSchema>;
export const UserPremiumAssignSchema = z.object({
userId: snowflake,
tier: PremiumTierSchema,
note: z.string().max(500).nullable().optional(),
expiresAt: z.string().datetime().nullable().optional()
});
export type UserPremiumAssign = z.infer<typeof UserPremiumAssignSchema>;
export const PremiumLimitResourceSchema = z.enum(['tags', 'feeds', 'backups']);
export type PremiumLimitResource = z.infer<typeof PremiumLimitResourceSchema>;
export function limitKeyForResource(resource: PremiumLimitResource): 'maxTags' | 'maxFeeds' | 'maxBackups' {
if (resource === 'tags') return 'maxTags';
if (resource === 'feeds') return 'maxFeeds';
return 'maxBackups';
}
export function featureKeyForResource(resource: PremiumLimitResource): PremiumFeatureKey {
if (resource === 'backups') return 'guildbackup';
return resource;
}
export function parsePremiumFeatures(raw: unknown): PremiumFeatures {
const parsed = PremiumFeaturesSchema.safeParse(raw);
if (parsed.success) {
return parsed.data;
}
return { tags: true, feeds: true, guildbackup: true };
}
export function isAssignmentActive(expiresAt: Date | string | null | undefined, now = new Date()): boolean {
if (!expiresAt) {
return true;
}
const date = typeof expiresAt === 'string' ? new Date(expiresAt) : expiresAt;
return date.getTime() > now.getTime();
}