import { v4 as uuidv4 } from 'uuid'; import licenseRepository from '../repositories/licenseRepository'; import userRepository from '../repositories/userRepository'; import { NotFoundError, BadRequestError, ForbiddenError } from '../utils/helpers'; import { LicensePlan, LicenseStatus, UserRole } from '@prisma/client'; import { PLANS, LICENSE_CHECK_INTERVAL_HOURS } from '../utils/constants'; export class LicenseService { async validate(licenseKey: string) { const license = await licenseRepository.findByKey(licenseKey); if (!license) { throw new NotFoundError('License not found'); } // Check if expired if (license.expiredAt && new Date() > license.expiredAt) { if (license.status === LicenseStatus.ACTIVE) { await licenseRepository.update(license.id, { status: LicenseStatus.EXPIRED }); } throw new ForbiddenError('License expired'); } if (license.status !== LicenseStatus.ACTIVE) { throw new ForbiddenError('License is not active'); } // Update last check time await licenseRepository.updateLastCheck(license.id); const plan = PLANS[license.plan as keyof typeof PLANS]; return { valid: true, plan: license.plan, maxScan: license.maxScan, features: plan.features, expiredAt: license.expiredAt, userId: license.userId, }; } async generateLicense(userId: string, plan: LicensePlan, durationDays?: number) { const user = await userRepository.findById(userId); if (!user) { throw new NotFoundError('User not found'); } // Deactivate existing licenses const existingLicenses = await licenseRepository.findByUserId(userId); for (const lic of existingLicenses) { if (lic.status === LicenseStatus.ACTIVE) { await licenseRepository.update(lic.id, { status: LicenseStatus.REVOKED }); } } const planConfig = PLANS[plan as keyof typeof PLANS]; const prefix = plan === 'FREE' ? 'FREE' : 'PRO'; const licenseKey = `MS-${prefix}-${uuidv4().substring(0, 8).toUpperCase()}`; let expiredAt: Date | null = null; if (plan !== 'FREE' && durationDays) { expiredAt = new Date(); expiredAt.setDate(expiredAt.getDate() + durationDays); } else if (plan === 'PRO_MONTHLY') { expiredAt = new Date(); expiredAt.setDate(expiredAt.getDate() + 30); } else if (plan === 'PRO_YEARLY') { expiredAt = new Date(); expiredAt.setDate(expiredAt.getDate() + 365); } const license = await licenseRepository.create({ key: licenseKey, plan, maxScan: planConfig.maxScan, expiredAt, user: { connect: { id: userId } }, }); // Update user role const newRole: UserRole = plan === 'FREE' ? 'USER_FREE' : 'USER_PRO'; await userRepository.updateRole(userId, newRole); return license; } async getUserLicense(userId: string) { const license = await licenseRepository.findActiveByUserId(userId); if (!license) { throw new NotFoundError('No active license found'); } return license; } async revokeLicense(licenseId: string) { const license = await licenseRepository.findById(licenseId); if (!license) { throw new NotFoundError('License not found'); } await licenseRepository.update(licenseId, { status: LicenseStatus.REVOKED }); await userRepository.updateRole(license.userId, 'USER_FREE'); // Generate free license await this.generateLicense(license.userId, 'FREE'); return { message: 'License revoked successfully' }; } async checkAndDeactivateExpired() { const result = await licenseRepository.deactivateExpired(); return { deactivated: result.count }; } } export default new LicenseService();