Domain support
This commit is contained in:
@@ -14,10 +14,41 @@ const PORT = process.env.PORT || 3000;
|
||||
|
||||
// Security middleware
|
||||
app.use(helmet());
|
||||
app.use(cors({
|
||||
origin: process.env.FRONTEND_URL || 'http://localhost:3001',
|
||||
|
||||
// Dynamic CORS configuration (will be updated from settings)
|
||||
let corsOptions = {
|
||||
origin: process.env.FRONTEND_URL || 'http://localhost:5173',
|
||||
credentials: true,
|
||||
}));
|
||||
};
|
||||
|
||||
app.use((req, res, next) => {
|
||||
cors(corsOptions)(req, res, next);
|
||||
});
|
||||
|
||||
// Function to update CORS from database
|
||||
const updateCorsFromSettings = async () => {
|
||||
try {
|
||||
const { Settings } = require('./models');
|
||||
const corsEnabledSetting = await Settings.findOne({ where: { key: 'cors_enabled' } });
|
||||
const frontendUrlSetting = await Settings.findOne({ where: { key: 'frontend_url' } });
|
||||
|
||||
if (corsEnabledSetting && corsEnabledSetting.value === 'true' && frontendUrlSetting) {
|
||||
corsOptions.origin = frontendUrlSetting.value;
|
||||
logger.info(`CORS enabled for: ${frontendUrlSetting.value}`);
|
||||
} else {
|
||||
// Default: allow both frontend and backend on same origin
|
||||
corsOptions.origin = process.env.FRONTEND_URL || 'http://localhost:5173';
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn('Could not load CORS settings from database, using defaults');
|
||||
}
|
||||
};
|
||||
|
||||
// Update CORS on startup (with delay to ensure DB is ready)
|
||||
setTimeout(updateCorsFromSettings, 2000);
|
||||
|
||||
// Export for use in settings controller
|
||||
app.updateCorsSettings = updateCorsFromSettings;
|
||||
|
||||
// Body parsing middleware
|
||||
app.use(express.json());
|
||||
|
||||
@@ -97,6 +97,89 @@ exports.updateTelegramSettings = async (req, res, next) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Update System settings (domain, etc.)
|
||||
exports.updateSystemSettings = async (req, res, next) => {
|
||||
try {
|
||||
const { base_url, frontend_url, cors_enabled } = req.body;
|
||||
|
||||
if (base_url !== undefined) {
|
||||
if (base_url) {
|
||||
// Remove trailing slash if exists
|
||||
const cleanUrl = base_url.trim().replace(/\/$/, '');
|
||||
|
||||
// Basic URL validation
|
||||
try {
|
||||
new URL(cleanUrl);
|
||||
} catch (e) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Geçersiz Base URL formatı. Örnek: https://yourdomain.com',
|
||||
});
|
||||
}
|
||||
|
||||
await Settings.upsert({
|
||||
key: 'base_url',
|
||||
value: cleanUrl,
|
||||
is_encrypted: false,
|
||||
description: 'Base URL for tracking links (backend)',
|
||||
});
|
||||
|
||||
// Update process.env for immediate use
|
||||
process.env.BASE_URL = cleanUrl;
|
||||
} else {
|
||||
await Settings.destroy({ where: { key: 'base_url' } });
|
||||
}
|
||||
}
|
||||
|
||||
if (frontend_url !== undefined) {
|
||||
if (frontend_url) {
|
||||
// Remove trailing slash if exists
|
||||
const cleanUrl = frontend_url.trim().replace(/\/$/, '');
|
||||
|
||||
// Basic URL validation
|
||||
try {
|
||||
new URL(cleanUrl);
|
||||
} catch (e) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Geçersiz Frontend URL formatı. Örnek: https://panel.yourdomain.com',
|
||||
});
|
||||
}
|
||||
|
||||
await Settings.upsert({
|
||||
key: 'frontend_url',
|
||||
value: cleanUrl,
|
||||
is_encrypted: false,
|
||||
description: 'Frontend URL (for CORS)',
|
||||
});
|
||||
} else {
|
||||
await Settings.destroy({ where: { key: 'frontend_url' } });
|
||||
}
|
||||
}
|
||||
|
||||
if (cors_enabled !== undefined) {
|
||||
await Settings.upsert({
|
||||
key: 'cors_enabled',
|
||||
value: cors_enabled ? 'true' : 'false',
|
||||
is_encrypted: false,
|
||||
description: 'Enable CORS for separate domains',
|
||||
});
|
||||
}
|
||||
|
||||
// Update CORS configuration if available
|
||||
if (req.app && req.app.updateCorsSettings) {
|
||||
await req.app.updateCorsSettings();
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Sistem ayarları güncellendi. CORS ayarları uygulandı.',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
// Test Gmail connection
|
||||
exports.testGmail = async (req, res, next) => {
|
||||
try {
|
||||
|
||||
@@ -30,6 +30,11 @@ const TrackingToken = sequelize.define('TrackingToken', {
|
||||
type: DataTypes.STRING(50),
|
||||
defaultValue: 'bank',
|
||||
},
|
||||
from_name: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: true,
|
||||
comment: 'Custom sender name for email (e.g., "HR Department", "Management")',
|
||||
},
|
||||
mail_subject: {
|
||||
type: DataTypes.STRING(500),
|
||||
allowNull: true,
|
||||
|
||||
@@ -9,6 +9,7 @@ router.use(requireAuth);
|
||||
router.get('/', settingsController.getAllSettings);
|
||||
router.put('/gmail', settingsController.updateGmailSettings);
|
||||
router.put('/telegram', settingsController.updateTelegramSettings);
|
||||
router.put('/system', settingsController.updateSystemSettings);
|
||||
router.post('/test-gmail', settingsController.testGmail);
|
||||
router.post('/test-telegram', settingsController.testTelegram);
|
||||
|
||||
|
||||
@@ -45,14 +45,19 @@ class MailService {
|
||||
}
|
||||
}
|
||||
|
||||
async sendMail(to, subject, htmlBody) {
|
||||
async sendMail(to, subject, htmlBody, fromName = null) {
|
||||
try {
|
||||
if (!this.transporter) {
|
||||
await this.initializeTransporter();
|
||||
}
|
||||
|
||||
// Use custom from_name if provided, otherwise use default
|
||||
const from = fromName
|
||||
? `${fromName} <${this.fromAddress.match(/<(.+)>/)?.[1] || this.fromAddress}>`
|
||||
: this.fromAddress;
|
||||
|
||||
const mailOptions = {
|
||||
from: this.fromAddress,
|
||||
from,
|
||||
to,
|
||||
subject,
|
||||
html: htmlBody,
|
||||
|
||||
@@ -5,7 +5,7 @@ const logger = require('../config/logger');
|
||||
|
||||
class TokenService {
|
||||
async createToken(data) {
|
||||
const { company_id, target_email, employee_name, template_type } = data;
|
||||
const { company_id, target_email, employee_name, template_type, from_name } = data;
|
||||
|
||||
// Generate unique token
|
||||
let token = generateTrackingToken();
|
||||
@@ -45,6 +45,7 @@ class TokenService {
|
||||
target_email,
|
||||
employee_name,
|
||||
template_type,
|
||||
from_name: from_name || null,
|
||||
mail_subject: template.subject_template.replace('{{company_name}}', company.name),
|
||||
});
|
||||
|
||||
@@ -78,8 +79,13 @@ class TokenService {
|
||||
throw new Error('Mail template not found');
|
||||
}
|
||||
|
||||
// Get base URL from settings or env
|
||||
const { Settings } = require('../models');
|
||||
const baseUrlSetting = await Settings.findOne({ where: { key: 'base_url' } });
|
||||
const baseUrl = baseUrlSetting?.value || process.env.BASE_URL || 'http://localhost:3000';
|
||||
|
||||
// Prepare template data
|
||||
const trackingUrl = `${process.env.BASE_URL}/t/${token.token}`;
|
||||
const trackingUrl = `${baseUrl}/t/${token.token}`;
|
||||
const currentDate = new Date().toLocaleDateString('tr-TR', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
@@ -99,8 +105,8 @@ class TokenService {
|
||||
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);
|
||||
// Send mail with custom from_name if provided
|
||||
await mailService.sendMail(token.target_email, subject, htmlBody, token.from_name);
|
||||
|
||||
// Update token
|
||||
await token.update({
|
||||
|
||||
@@ -20,6 +20,13 @@ const createTokenSchema = Joi.object({
|
||||
.max(255)
|
||||
.allow(null, '')
|
||||
.optional(),
|
||||
from_name: Joi.string()
|
||||
.max(255)
|
||||
.allow(null, '')
|
||||
.optional()
|
||||
.messages({
|
||||
'string.max': 'From name must be less than 255 characters',
|
||||
}),
|
||||
template_type: Joi.string()
|
||||
.max(50)
|
||||
.default('bank')
|
||||
|
||||
Reference in New Issue
Block a user