fix(notifications): fix SMTP error surfacing, webhook button label, backup timestamp

- testSmtp now surfaces real nodemailer error instead of generic 'SMTP not configured' on send failure
- admin webhook test button uses correct i18n key (was showing 'Test-E-Mail senden' in all languages)
- backup created_at uses stat.mtime instead of unreliable stat.birthtime on Linux
This commit is contained in:
jubnl
2026-04-14 16:14:58 +02:00
parent bae24ad4af
commit 6a23118342
4 changed files with 23 additions and 8 deletions
+1 -1
View File
@@ -117,7 +117,7 @@ export function listBackups(): BackupInfo[] {
filename,
size: stat.size,
sizeText: formatSize(stat.size),
created_at: stat.birthtime.toISOString(),
created_at: stat.mtime.toISOString(),
};
})
.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
+17 -2
View File
@@ -399,9 +399,24 @@ export async function sendWebhook(url: string, payload: { event: string; title:
}
export async function testSmtp(to: string): Promise<{ success: boolean; error?: string }> {
if (!getSmtpConfig()) return { success: false, error: 'SMTP not configured' };
try {
const sent = await sendEmail(to, 'Test Notification', 'This is a test email from TREK. If you received this, your SMTP configuration is working correctly.');
return sent ? { success: true } : { success: false, error: 'SMTP not configured' };
const config = getSmtpConfig()!;
const skipTls = process.env.SMTP_SKIP_TLS_VERIFY === 'true' || getAppSetting('smtp_skip_tls_verify') === 'true';
const transporter = nodemailer.createTransport({
host: config.host,
port: config.port,
secure: config.secure,
auth: config.user ? { user: config.user, pass: config.pass } : undefined,
...(skipTls ? { tls: { rejectUnauthorized: false } } : {}),
});
await transporter.sendMail({
from: config.from,
to,
subject: 'TREK — Test Notification',
text: 'This is a test email from TREK. If you received this, your SMTP configuration is working correctly.',
});
return { success: true };
} catch (err) {
return { success: false, error: err instanceof Error ? err.message : 'Unknown error' };
}
@@ -580,7 +580,7 @@ describe('BACKUP-041 listBackups', () => {
fsMock.readdirSync.mockReturnValue(['backup-2026-01-01T00-00-00.zip']);
fsMock.statSync.mockReturnValue({
size: 1024,
birthtime: new Date('2026-01-01T00:00:00Z'),
mtime: new Date('2026-01-01T00:00:00Z'),
});
const result = listBackups();
@@ -599,9 +599,9 @@ describe('BACKUP-041 listBackups', () => {
]);
fsMock.statSync.mockImplementation((p: string) => {
if (String(p).includes('2026-01-01')) {
return { size: 512, birthtime: new Date('2026-01-01T00:00:00Z') };
return { size: 512, mtime: new Date('2026-01-01T00:00:00Z') };
}
return { size: 2048, birthtime: new Date('2026-06-01T00:00:00Z') };
return { size: 2048, mtime: new Date('2026-06-01T00:00:00Z') };
});
const result = listBackups();
@@ -619,7 +619,7 @@ describe('BACKUP-041 listBackups', () => {
]);
fsMock.statSync.mockReturnValue({
size: 1024,
birthtime: new Date('2026-01-01T00:00:00Z'),
mtime: new Date('2026-01-01T00:00:00Z'),
});
const result = listBackups();