Merge pull request #64 from the-luap/claude/investigate-issue-62-01GFcj5uDDgojgX5D8ytoF9W
Add option to ignore SSL/TLS certificate errors for email (Issue #53)
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
const { addColumnIfNotExists } = require('../helpers');
|
||||
|
||||
exports.up = async function up(knex) {
|
||||
// Add tls_reject_unauthorized column to email_configs table
|
||||
// Default is true (validate certificates), false means ignore SSL/TLS certificate errors
|
||||
await addColumnIfNotExists(knex, 'email_configs', 'tls_reject_unauthorized', (table) => {
|
||||
table.boolean('tls_reject_unauthorized').defaultTo(true);
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = async function down(knex) {
|
||||
const hasColumn = await knex.schema.hasColumn('email_configs', 'tls_reject_unauthorized');
|
||||
if (hasColumn) {
|
||||
await knex.schema.alterTable('email_configs', (table) => {
|
||||
table.dropColumn('tls_reject_unauthorized');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -18,7 +18,8 @@ router.get('/config', adminAuth, async (req, res) => {
|
||||
smtp_user: '',
|
||||
smtp_pass: '', // Don't send actual password
|
||||
from_email: '',
|
||||
from_name: ''
|
||||
from_name: '',
|
||||
tls_reject_unauthorized: true
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,7 +54,8 @@ router.post('/config', [
|
||||
smtp_user,
|
||||
smtp_pass,
|
||||
from_email,
|
||||
from_name
|
||||
from_name,
|
||||
tls_reject_unauthorized
|
||||
} = req.body;
|
||||
|
||||
// Check if config exists
|
||||
@@ -66,6 +68,7 @@ router.post('/config', [
|
||||
smtp_user: smtp_user || '',
|
||||
from_email,
|
||||
from_name: from_name || 'Photo Sharing',
|
||||
tls_reject_unauthorized: tls_reject_unauthorized !== false, // Default to true
|
||||
updated_at: new Date()
|
||||
};
|
||||
|
||||
@@ -137,6 +140,10 @@ router.post('/test', adminAuth, async (req, res) => {
|
||||
user: config.smtp_user,
|
||||
pass: config.smtp_pass
|
||||
} : undefined,
|
||||
tls: {
|
||||
// Allow ignoring SSL certificate errors when tls_reject_unauthorized is false
|
||||
rejectUnauthorized: config.tls_reject_unauthorized !== false
|
||||
},
|
||||
logger: process.env.NODE_ENV === 'development',
|
||||
debug: process.env.NODE_ENV === 'development'
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ let lastConfigHash = null;
|
||||
// Generate hash from config for change detection
|
||||
function generateConfigHash(config) {
|
||||
const crypto = require('crypto');
|
||||
const configString = `${config.smtp_host}:${config.smtp_port}:${config.smtp_user}:${config.smtp_pass}:${config.smtp_secure}`;
|
||||
const configString = `${config.smtp_host}:${config.smtp_port}:${config.smtp_user}:${config.smtp_pass}:${config.smtp_secure}:${config.tls_reject_unauthorized}`;
|
||||
return crypto.createHash('md5').update(configString).digest('hex');
|
||||
}
|
||||
|
||||
@@ -40,7 +40,11 @@ async function initializeTransporter(forceReinit = false) {
|
||||
auth: config.smtp_user ? {
|
||||
user: config.smtp_user,
|
||||
pass: config.smtp_pass
|
||||
} : undefined
|
||||
} : undefined,
|
||||
tls: {
|
||||
// Allow ignoring SSL certificate errors when tls_reject_unauthorized is false
|
||||
rejectUnauthorized: config.tls_reject_unauthorized !== false
|
||||
}
|
||||
});
|
||||
|
||||
// Verify configuration
|
||||
|
||||
@@ -1327,7 +1327,34 @@
|
||||
"saveConfiguration": "Konfiguration speichern",
|
||||
"emailTemplates": "E-Mail-Vorlagen",
|
||||
"templateVariables": "Verfügbare Variablen",
|
||||
"previewTemplate": "Vorlage anzeigen"
|
||||
"previewTemplate": "Vorlage anzeigen",
|
||||
"smtpSettings": "SMTP-Einstellungen",
|
||||
"testEmailSuccess": "Test-E-Mail erfolgreich gesendet",
|
||||
"saveSmtpSettings": "SMTP-Einstellungen speichern",
|
||||
"testEmailSection": "E-Mail testen",
|
||||
"beforeTesting": "Vor dem Testen:",
|
||||
"saveSmtpFirst": "Speichern Sie zuerst Ihre SMTP-Einstellungen",
|
||||
"ensureFirewall": "Stellen Sie sicher, dass Ihre Firewall ausgehende SMTP-Verbindungen erlaubt",
|
||||
"gmailAppPassword": "Für Gmail verwenden Sie ein App-spezifisches Passwort",
|
||||
"testEmailAddressLabel": "Test-E-Mail-Adresse",
|
||||
"sendTestEmailButton": "Test-E-Mail senden",
|
||||
"commonSmtpSettings": "Häufige SMTP-Einstellungen:",
|
||||
"editTemplate": "Vorlage bearbeiten",
|
||||
"templateName": "Vorlagenname",
|
||||
"subjectLine": "Betreffzeile",
|
||||
"emailBody": "E-Mail-Text",
|
||||
"preview": "Vorschau",
|
||||
"saveChanges": "Änderungen speichern",
|
||||
"templates": "Vorlagen",
|
||||
"variableHelp": "Verwenden Sie diese Variablen in Ihrer Vorlage. Sie werden beim Senden durch tatsächliche Werte ersetzt.",
|
||||
"port": "Port",
|
||||
"security": "Sicherheit",
|
||||
"username": "Benutzername",
|
||||
"password": "Passwort",
|
||||
"enterPassword": "Passwort eingeben",
|
||||
"required": "erforderlich",
|
||||
"ignoreSslErrors": "SSL/TLS-Zertifikatfehler ignorieren",
|
||||
"ignoreSslWarning": "Warnung: Das Deaktivieren der Zertifikatüberprüfung macht die Verbindung anfällig für Man-in-the-Middle-Angriffe. Aktivieren Sie dies nur, wenn Sie dem SMTP-Server vertrauen und die Sicherheitsrisiken verstehen."
|
||||
},
|
||||
"cms": {
|
||||
"title": "CMS-Seiten",
|
||||
|
||||
@@ -1072,7 +1072,9 @@
|
||||
"enterPassword": "Enter password",
|
||||
"fromEmail": "From Email",
|
||||
"fromName": "From Name",
|
||||
"required": "required"
|
||||
"required": "required",
|
||||
"ignoreSslErrors": "Ignore SSL/TLS certificate errors",
|
||||
"ignoreSslWarning": "Warning: Disabling certificate verification makes the connection vulnerable to man-in-the-middle attacks. Only enable this if you trust the SMTP server and understand the security implications."
|
||||
},
|
||||
"cms": {
|
||||
"title": "CMS Pages",
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
CheckCircle,
|
||||
Eye,
|
||||
EyeOff,
|
||||
ShieldAlert,
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
@@ -107,7 +108,8 @@ export const EmailConfigPage: React.FC = () => {
|
||||
smtp_user: '',
|
||||
smtp_pass: '',
|
||||
from_email: '',
|
||||
from_name: 'Photo Sharing'
|
||||
from_name: 'Photo Sharing',
|
||||
tls_reject_unauthorized: true
|
||||
});
|
||||
|
||||
// Fetch SMTP config
|
||||
@@ -358,6 +360,30 @@ export const EmailConfigPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Ignore SSL Certificate Errors */}
|
||||
<div className="mt-2">
|
||||
<label className="flex items-center gap-3 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!smtpConfig.tls_reject_unauthorized}
|
||||
onChange={(e) => setSmtpConfig(prev => ({ ...prev, tls_reject_unauthorized: !e.target.checked }))}
|
||||
className="w-4 h-4 text-primary-600 border-neutral-300 rounded focus:ring-primary-500"
|
||||
/>
|
||||
<span className="text-sm font-medium text-neutral-700">
|
||||
{t('email.ignoreSslErrors')}
|
||||
</span>
|
||||
</label>
|
||||
{!smtpConfig.tls_reject_unauthorized && (
|
||||
<div className="mt-2 p-3 bg-amber-50 border border-amber-200 rounded-lg">
|
||||
<div className="flex items-start gap-2">
|
||||
<ShieldAlert className="w-4 h-4 text-amber-600 flex-shrink-0 mt-0.5" />
|
||||
<p className="text-xs text-amber-800">
|
||||
{t('email.ignoreSslWarning')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-neutral-700 mb-1">
|
||||
{t('email.username')}
|
||||
|
||||
@@ -8,6 +8,7 @@ export interface EmailConfig {
|
||||
smtp_pass: string;
|
||||
from_email: string;
|
||||
from_name: string;
|
||||
tls_reject_unauthorized: boolean;
|
||||
}
|
||||
|
||||
export interface EmailTemplate {
|
||||
|
||||
Reference in New Issue
Block a user