first commit: Complete phishing test management panel with Node.js backend and React frontend

This commit is contained in:
salvacybersec
2025-11-10 17:00:40 +03:00
commit 19e551f33b
77 changed files with 6677 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
const { TrackingToken, Company, MailTemplate } = require('../models');
const { generateTrackingToken } = require('../utils/tokenGenerator');
const mailService = require('./mail.service');
const logger = require('../config/logger');
class TokenService {
async createToken(data) {
const { company_id, target_email, employee_name, template_type } = data;
// Generate unique token
let token = generateTrackingToken();
let isUnique = false;
let attempts = 0;
// Ensure token is unique
while (!isUnique && attempts < 5) {
const existing = await TrackingToken.findOne({ where: { token } });
if (!existing) {
isUnique = true;
} else {
token = generateTrackingToken();
attempts++;
}
}
if (!isUnique) {
throw new Error('Failed to generate unique token');
}
// Get company and template
const company = await Company.findByPk(company_id);
if (!company) {
throw new Error('Company not found');
}
const template = await MailTemplate.findOne({ where: { template_type } });
if (!template) {
throw new Error('Mail template not found');
}
// Create tracking token
const trackingToken = await TrackingToken.create({
token,
company_id,
target_email,
employee_name,
template_type,
mail_subject: template.subject_template.replace('{{company_name}}', company.name),
});
// Update company stats
await company.increment('total_tokens');
logger.info(`Token created: ${token} for ${target_email}`);
return trackingToken;
}
async sendMail(tokenId) {
const token = await TrackingToken.findByPk(tokenId, {
include: [{ model: Company, as: 'company' }],
});
if (!token) {
throw new Error('Token not found');
}
if (token.mail_sent) {
throw new Error('Mail already sent for this token');
}
// Get mail template
const template = await MailTemplate.findOne({
where: { template_type: token.template_type },
});
if (!template) {
throw new Error('Mail template not found');
}
// Prepare template data
const trackingUrl = `${process.env.BASE_URL}/t/${token.token}`;
const currentDate = new Date().toLocaleDateString('tr-TR', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
const currentYear = new Date().getFullYear();
const templateData = {
company_name: token.company.name,
employee_name: token.employee_name,
tracking_url: trackingUrl,
current_date: currentDate,
current_year: currentYear,
};
// Render mail body
const htmlBody = mailService.renderTemplate(template.body_html, templateData);
const subject = mailService.renderTemplate(template.subject_template, templateData);
// Send mail
await mailService.sendMail(token.target_email, subject, htmlBody);
// Update token
await token.update({
mail_sent: true,
sent_at: new Date(),
});
logger.info(`Mail sent for token: ${token.token} to ${token.target_email}`);
return token;
}
async updateCompanyStats(companyId) {
const company = await Company.findByPk(companyId);
if (!company) return;
// Count tokens
const totalTokens = await TrackingToken.count({
where: { company_id: companyId },
});
const clickedTokens = await TrackingToken.count({
where: { company_id: companyId, clicked: true },
});
const totalClicks = await TrackingToken.sum('click_count', {
where: { company_id: companyId },
});
const clickRate = totalTokens > 0 ? ((clickedTokens / totalTokens) * 100).toFixed(2) : 0;
await company.update({
total_tokens: totalTokens,
total_clicks: totalClicks || 0,
click_rate: clickRate,
});
logger.info(`Company stats updated for: ${company.name}`);
}
}
module.exports = new TokenService();