CentralBackend/controller/referral/ReferralTokenController.js
2025-09-07 12:44:01 +02:00

142 lines
5.8 KiB
JavaScript

const UnitOfWork = require('../../repositories/UnitOfWork');
const ReferralService = require('../../services/ReferralService'); // <-- use unified service
const { logger } = require('../../middleware/logger');
// Helper to get frontend URL without trailing slash, no localhost fallback
function getFrontendUrl() {
let url = process.env.FRONTEND_URL || '';
if (url.endsWith('/')) {
url = url.slice(0, -1);
}
return url;
}
class ReferralTokenController {
static async create(req, res) {
const userId = req.user.userId;
const userRole = req.user.role;
const { expiresInDays, maxUses } = req.body;
// Normalize unlimited expiry from frontend (0 or -1) to -1 sentinel
let normalizedExpiresInDays = (expiresInDays === 0) ? -1 : expiresInDays;
logger.info('ReferralTokenController:createReferralToken:start', { userId, normalizedExpiresInDays, maxUses });
const unitOfWork = new UnitOfWork();
await unitOfWork.start();
try {
let maxActiveLinks = 5;
if (userRole === 'admin' || userRole === 'super_admin') {
maxActiveLinks = 15;
}
const activeCount = await ReferralService.countActiveReferralTokens(userId, unitOfWork);
if (activeCount >= maxActiveLinks) {
await unitOfWork.rollback();
return res.status(403).json({
success: false,
message: `Maximum active referral links reached (${maxActiveLinks}) for your role (${userRole}).`
});
}
const token = await ReferralService.createReferralToken({
userId,
expiresInDays: normalizedExpiresInDays,
maxUses,
unitOfWork
});
if (normalizedExpiresInDays === -1) {
logger.info('ReferralTokenController:createReferralToken:unlimited_expiry', { userId });
}
await unitOfWork.commit();
logger.info('ReferralTokenController:createReferralToken:success', { token });
const link = `${getFrontendUrl()}/register?ref=${token.token}`;
res.json({ success: true, token: token.token, link, expiresAt: token.expiresAt, maxUses: token.maxUses });
} catch (error) {
await unitOfWork.rollback(error);
logger.error('ReferralTokenController:createReferralToken:error', { userId, error });
res.status(400).json({ success: false, message: error.message });
}
}
static async list(req, res) {
const userId = req.user.userId;
logger.info('ReferralTokenController:getUserReferralTokens:start', { userId });
const unitOfWork = new UnitOfWork();
await unitOfWork.start();
try {
const tokens = await ReferralService.getUserReferralTokens(userId, unitOfWork);
await unitOfWork.commit();
logger.info('ReferralTokenController:getUserReferralTokens:success', { count: tokens.length });
const frontendUrl = getFrontendUrl();
// FIX: tokens already transformed by service; use provided fields instead of raw DB column names
const result = tokens.map(t => {
const isUnlimitedExpiry = t.isUnlimited || !t.expires;
const usage = t.usage || (t.isUnlimited ? `${t.used}/∞` : `${t.used}/${t.maxUses}`);
return {
id: t.id,
status: t.status,
created: t.created,
created_at: t.created, // legacy alias
expires: isUnlimitedExpiry ? '∞' : t.expires,
expires_at: t.expires, // legacy alias
link: t.link || `${frontendUrl}/register?ref=${t.token}`,
token: t.token,
// normalized usage fields
used: t.used,
maxUses: t.maxUses,
isUnlimited: t.isUnlimited,
usage,
usageDisplay: usage,
// backward compatibility (old FE expected these)
max_uses: t.isUnlimited ? -1 : t.maxUses,
usage_count: t.used
};
});
return res.json({ success: true, tokens: result });
} catch (error) {
await unitOfWork.rollback(error);
logger.error('ReferralTokenController:getUserReferralTokens:error', { userId, error });
res.status(500).json({ success: false, message: error.message });
}
}
static async stats(req, res) {
const userId = req.user.userId;
logger.info('ReferralTokenController:getUserReferralStats:start', { userId });
const unitOfWork = new UnitOfWork();
await unitOfWork.start();
try {
const stats = await ReferralService.getUserReferralStats(userId, unitOfWork);
await unitOfWork.commit();
logger.info('ReferralTokenController:getUserReferralStats:success', { stats });
// Response now includes companyUsersReferred and personalUsersReferred
res.json({ success: true, stats });
} catch (error) {
await unitOfWork.rollback(error);
logger.error('ReferralTokenController:getUserReferralStats:error', { userId, error });
res.status(500).json({ success: false, message: error.message });
}
}
static async deactivate(req, res) {
const userId = req.user.userId;
const { tokenId } = req.body;
logger.info('ReferralTokenController:deactivateReferralToken:start', { userId, tokenId });
if (!tokenId) {
return res.status(400).json({ success: false, message: 'tokenId is required' });
}
const unitOfWork = new UnitOfWork();
await unitOfWork.start();
try {
await ReferralService.deactivateReferralToken(userId, tokenId, unitOfWork);
await unitOfWork.commit();
logger.info('ReferralTokenController:deactivateReferralToken:success', { userId, tokenId });
res.json({ success: true, message: 'Referral link deactivated' });
} catch (error) {
await unitOfWork.rollback(error);
logger.error('ReferralTokenController:deactivateReferralToken:error', { userId, tokenId, error });
res.status(500).json({ success: false, message: error.message });
}
}
}
module.exports = ReferralTokenController;