feat: add SMTP connection testing functionality (#103)

This commit is contained in:
Daniel Luiz Alves
2025-06-23 18:28:54 -03:00
committed by GitHub
22 changed files with 475 additions and 57 deletions

View File

@@ -1,3 +1,4 @@
import { EmailService } from "../email/service";
import { LogoService } from "./logo.service";
import { AppService } from "./service";
import { FastifyReply, FastifyRequest } from "fastify";
@@ -26,6 +27,7 @@ if (!fs.existsSync(uploadsDir)) {
export class AppController {
private appService = new AppService();
private logoService = new LogoService();
private emailService = new EmailService();
async getAppInfo(request: FastifyRequest, reply: FastifyReply) {
try {
@@ -70,6 +72,24 @@ export class AppController {
}
}
async testSmtpConnection(request: FastifyRequest, reply: FastifyReply) {
try {
await request.jwtVerify();
if (!(request as any).user?.isAdmin) {
return reply.status(403).send({ error: "Access restricted to administrators" });
}
const body = request.body as any;
const smtpConfig = body.smtpConfig || undefined;
const result = await this.emailService.testConnection(smtpConfig);
return reply.send(result);
} catch (error: any) {
return reply.status(400).send({ error: error.message });
}
}
async uploadLogo(request: FastifyRequest, reply: FastifyReply) {
try {
const file = await request.file();

View File

@@ -126,6 +126,54 @@ export async function appRoutes(app: FastifyInstance) {
appController.bulkUpdateConfigs.bind(appController)
);
app.post(
"/app/test-smtp",
{
preValidation: adminPreValidation,
schema: {
tags: ["App"],
operationId: "testSmtpConnection",
summary: "Test SMTP connection with provided or saved configuration",
description:
"Validates SMTP connectivity using either provided configuration parameters or the currently saved settings. This endpoint allows testing SMTP settings before saving them permanently. Requires admin privileges.",
body: z
.object({
smtpConfig: z
.object({
smtpEnabled: z.string().describe("Whether SMTP is enabled ('true' or 'false')"),
smtpHost: z.string().describe("SMTP server hostname or IP address (e.g., 'smtp.gmail.com')"),
smtpPort: z
.union([z.string(), z.number()])
.transform(String)
.describe("SMTP server port (typically 587 for TLS, 25 for non-secure)"),
smtpUser: z.string().describe("Username for SMTP authentication (e.g., email address)"),
smtpPass: z.string().describe("Password for SMTP authentication (for Gmail, use App Password)"),
})
.optional()
.describe("SMTP configuration to test. If not provided, uses currently saved configuration"),
})
.optional()
.describe("Request body containing SMTP configuration to test. Send empty body to test saved configuration"),
response: {
200: z.object({
success: z.boolean().describe("Whether the SMTP connection test was successful"),
message: z.string().describe("Descriptive message about the test result"),
}),
400: z.object({
error: z.string().describe("Error message describing what went wrong with the test"),
}),
401: z.object({
error: z.string().describe("Authentication error - invalid or missing JWT token"),
}),
403: z.object({
error: z.string().describe("Authorization error - user does not have admin privileges"),
}),
},
},
},
appController.testSmtpConnection.bind(appController)
);
app.post(
"/app/logo",
{

View File

@@ -1,6 +1,14 @@
import { ConfigService } from "../config/service";
import nodemailer from "nodemailer";
interface SmtpConfig {
smtpEnabled: string;
smtpHost: string;
smtpPort: string;
smtpUser: string;
smtpPass: string;
}
export class EmailService {
private configService = new ConfigService();
@@ -21,6 +29,45 @@ export class EmailService {
});
}
async testConnection(config?: SmtpConfig) {
let smtpConfig: SmtpConfig;
if (config) {
// Use provided configuration
smtpConfig = config;
} else {
// Fallback to saved configuration
smtpConfig = {
smtpEnabled: await this.configService.getValue("smtpEnabled"),
smtpHost: await this.configService.getValue("smtpHost"),
smtpPort: await this.configService.getValue("smtpPort"),
smtpUser: await this.configService.getValue("smtpUser"),
smtpPass: await this.configService.getValue("smtpPass"),
};
}
if (smtpConfig.smtpEnabled !== "true") {
throw new Error("SMTP is not enabled");
}
const transporter = nodemailer.createTransport({
host: smtpConfig.smtpHost,
port: Number(smtpConfig.smtpPort),
secure: false,
auth: {
user: smtpConfig.smtpUser,
pass: smtpConfig.smtpPass,
},
});
try {
await transporter.verify();
return { success: true, message: "SMTP connection successful" };
} catch (error: any) {
throw new Error(`SMTP connection failed: ${error.message}`);
}
}
async sendPasswordResetEmail(to: string, resetToken: string, origin: string) {
const transporter = await this.createTransporter();
if (!transporter) {

View File

@@ -528,10 +528,16 @@
"oidcAdminEmailDomains": {
"title": "نطاقات البريد الإلكتروني للمشرف",
"description": "نطاقات البريد الإلكتروني التي تحصل على صلاحيات المشرف تلقائياً"
},
"testSmtp": {
"title": "اختبار اتصال SMTP",
"description": "اختبار ما إذا كان تكوين SMTP صالحًا"
}
},
"buttons": {
"save": "احفظ {group}"
"save": "احفظ {group}",
"testSmtp": "اختبار الاتصال",
"testing": "جاري الاختبار..."
},
"errors": {
"loadFailed": "فشل في تحميل الإعدادات",
@@ -539,14 +545,18 @@
},
"messages": {
"noChanges": "لا توجد تغييرات للحفظ",
"updateSuccess": "تم تحديث إعدادات {group} بنجاح"
"updateSuccess": "تم تحديث إعدادات {group} بنجاح",
"smtpTestFailed": "فشل اتصال SMTP: {error}",
"smtpTestGenericError": "فشل اختبار اتصال SMTP. يرجى التحقق من إعداداتك والمحاولة مرة أخرى.",
"smtpTestSuccess": "تم اتصال SMTP بنجاح! تكوين البريد الإلكتروني الخاص بك يعمل بشكل صحيح."
},
"title": "الإعدادات",
"breadcrumb": "الإعدادات",
"pageTitle": "الإعدادات",
"tooltips": {
"oidcScope": "أدخل نطاقاً واضغط Enter للإضافة",
"oidcAdminEmailDomains": "أدخل نطاقاً واضغط Enter للإضافة"
"oidcAdminEmailDomains": "أدخل نطاقاً واضغط Enter للإضافة",
"testSmtp": "يختبر اتصال SMTP بالقيم المدخلة حاليًا في النموذج. لجعل التغييرات دائمة، تذكر حفظ إعداداتك بعد الاختبار."
},
"redirectUri": {
"placeholder": "https://mysite.com",

View File

@@ -528,10 +528,16 @@
"oidcAdminEmailDomains": {
"title": "Admin-E-Mail-Domains",
"description": "E-Mail-Domains, die automatisch Administratorrechte erhalten"
},
"testSmtp": {
"title": "SMTP-Verbindung testen",
"description": "Testen Sie, ob die SMTP-Konfiguration gültig ist"
}
},
"buttons": {
"save": "{group} speichern"
"save": "{group} speichern",
"testSmtp": "Verbindung testen",
"testing": "Teste..."
},
"errors": {
"loadFailed": "Fehler beim Laden der Einstellungen",
@@ -539,14 +545,18 @@
},
"messages": {
"noChanges": "Keine Änderungen zum Speichern",
"updateSuccess": "{group}-Einstellungen erfolgreich aktualisiert"
"updateSuccess": "{group}-Einstellungen erfolgreich aktualisiert",
"smtpTestFailed": "SMTP-Verbindung fehlgeschlagen: {error}",
"smtpTestGenericError": "SMTP-Verbindungstest fehlgeschlagen. Bitte überprüfen Sie Ihre Einstellungen und versuchen Sie es erneut.",
"smtpTestSuccess": "SMTP-Verbindung erfolgreich! Ihre E-Mail-Konfiguration funktioniert korrekt."
},
"title": "Einstellungen",
"breadcrumb": "Einstellungen",
"pageTitle": "Einstellungen",
"tooltips": {
"oidcScope": "Geben Sie einen Scope ein und drücken Sie Enter zum Hinzufügen",
"oidcAdminEmailDomains": "Geben Sie eine Domain ein und drücken Sie Enter zum Hinzufügen"
"oidcAdminEmailDomains": "Geben Sie eine Domain ein und drücken Sie Enter zum Hinzufügen",
"testSmtp": "Testet die SMTP-Verbindung mit den aktuell im Formular eingegebenen Werten. Um die Änderungen dauerhaft zu machen, denken Sie daran, Ihre Einstellungen nach dem Test zu speichern."
},
"redirectUri": {
"placeholder": "https://meineseite.com",

View File

@@ -465,6 +465,10 @@
"title": "Sender Email",
"description": "Sender email address"
},
"testSmtp": {
"title": "Test SMTP Connection",
"description": "Test if the SMTP configuration is valid"
},
"maxLoginAttempts": {
"title": "Maximum Login Attempts",
"description": "Maximum number of login attempts before blocking"
@@ -531,7 +535,9 @@
}
},
"buttons": {
"save": "Save {group}"
"save": "Save {group}",
"testSmtp": "Test Connection",
"testing": "Testing..."
},
"errors": {
"loadFailed": "Failed to load settings",
@@ -539,14 +545,18 @@
},
"messages": {
"noChanges": "No changes to save",
"updateSuccess": "{group} settings updated successfully"
"updateSuccess": "{group} settings updated successfully",
"smtpTestSuccess": "SMTP connection successful! Your email configuration is working correctly.",
"smtpTestFailed": "SMTP connection failed: {error}",
"smtpTestGenericError": "Failed to test SMTP connection. Please check your settings and try again."
},
"title": "Settings",
"breadcrumb": "Settings",
"pageTitle": "Settings",
"tooltips": {
"oidcScope": "Enter a scope and press Enter to add",
"oidcAdminEmailDomains": "Enter a domain and press Enter to add"
"oidcAdminEmailDomains": "Enter a domain and press Enter to add",
"testSmtp": "Tests the SMTP connection with the values currently entered in the form. To make changes permanent, remember to save your settings after testing."
},
"redirectUri": {
"placeholder": "https://mysite.com",

View File

@@ -528,10 +528,16 @@
"oidcAdminEmailDomains": {
"title": "Dominios de Correo Admin",
"description": "Dominios de correo que reciben privilegios de administrador automáticamente"
},
"testSmtp": {
"title": "Probar Conexión SMTP",
"description": "Probar si la configuración SMTP es válida"
}
},
"buttons": {
"save": "Guardar {group}"
"save": "Guardar {group}",
"testSmtp": "Probar Conexión",
"testing": "Probando..."
},
"errors": {
"loadFailed": "Error al cargar la configuración",
@@ -539,14 +545,18 @@
},
"messages": {
"noChanges": "No hay cambios para guardar",
"updateSuccess": "Configuración de {group} actualizada exitosamente"
"updateSuccess": "Configuración de {group} actualizada exitosamente",
"smtpTestSuccess": "¡Conexión SMTP exitosa! Tu configuración de correo funciona correctamente.",
"smtpTestFailed": "Falló la conexión SMTP: {error}",
"smtpTestGenericError": "Error al probar la conexión SMTP. Por favor revisa tu configuración e inténtalo de nuevo."
},
"title": "Configuración",
"breadcrumb": "Configuración",
"pageTitle": "Configuración",
"tooltips": {
"oidcScope": "Ingrese un ámbito y presione Enter para agregar",
"oidcAdminEmailDomains": "Ingrese un dominio y presione Enter para agregar"
"oidcAdminEmailDomains": "Ingrese un dominio y presione Enter para agregar",
"testSmtp": "Prueba la conexión SMTP con los valores actualmente ingresados en el formulario. Para hacer los cambios permanentes, recuerda guardar la configuración después de probar."
},
"redirectUri": {
"placeholder": "https://misitio.com",

View File

@@ -531,10 +531,16 @@
"oidcAdminEmailDomains": {
"title": "Domaines Email Admin",
"description": "Domaines email qui reçoivent automatiquement les privilèges d'administrateur"
},
"testSmtp": {
"title": "[TO_TRANSLATE] Test SMTP Connection",
"description": "[TO_TRANSLATE] Test if the SMTP configuration is valid"
}
},
"buttons": {
"save": "Enregistrer {group}"
"save": "Enregistrer {group}",
"testSmtp": "Tester la Connexion",
"testing": "Test en cours..."
},
"errors": {
"loadFailed": "Échec du chargement des paramètres",
@@ -542,11 +548,15 @@
},
"messages": {
"noChanges": "Aucun changement à enregistrer",
"updateSuccess": "Paramètres {group} mis à jour avec succès"
"updateSuccess": "Paramètres {group} mis à jour avec succès",
"smtpTestFailed": "La connexion SMTP a échoué : {error}",
"smtpTestGenericError": "Échec du test de connexion SMTP. Veuillez vérifier vos paramètres et réessayer.",
"smtpTestSuccess": "Connexion SMTP réussie ! Votre configuration email fonctionne correctement."
},
"tooltips": {
"oidcScope": "Entrez une portée et appuyez sur Entrée pour l'ajouter",
"oidcAdminEmailDomains": "Entrez un domaine et appuyez sur Entrée pour l'ajouter"
"oidcAdminEmailDomains": "Entrez un domaine et appuyez sur Entrée pour l'ajouter",
"testSmtp": "Teste la connexion SMTP avec les valeurs actuellement saisies dans le formulaire. Pour rendre les modifications permanentes, n'oubliez pas d'enregistrer vos paramètres après le test."
},
"redirectUri": {
"placeholder": "https://monsite.com",

View File

@@ -528,10 +528,16 @@
"oidcAdminEmailDomains": {
"title": "एडमिन ईमेल डोमेन",
"description": "जिन ईमेल डोमेन को स्वचालित रूप से व्यवस्थापक विशेषाधिकार प्राप्त होंगे"
},
"testSmtp": {
"title": "SMTP कनेक्शन का परीक्षण करें",
"description": "जांचें कि SMTP कॉन्फ़िगरेशन मान्य है"
}
},
"buttons": {
"save": "{group} सहेजें"
"save": "{group} सहेजें",
"testSmtp": "कनेक्शन का परीक्षण करें",
"testing": "परीक्षण किया जा रहा है..."
},
"errors": {
"loadFailed": "सेटिंग्स लोड करने में विफल",
@@ -539,14 +545,18 @@
},
"messages": {
"noChanges": "सहेजने के लिए कोई परिवर्तन नहीं",
"updateSuccess": "{group} सेटिंग्स सफलतापूर्वक अपडेट हुईं"
"updateSuccess": "{group} सेटिंग्स सफलतापूर्वक अपडेट हुईं",
"smtpTestFailed": "SMTP कनेक्शन विफल: {error}",
"smtpTestGenericError": "SMTP कनेक्शन का परीक्षण करने में विफल। कृपया अपनी सेटिंग्स जांचें और पुनः प्रयास करें।",
"smtpTestSuccess": "SMTP कनेक्शन सफल! आपका ईमेल कॉन्फ़िगरेशन सही ढंग से काम कर रहा है।"
},
"title": "सेटिंग्स",
"breadcrumb": "सेटिंग्स",
"pageTitle": "सेटिंग्स",
"tooltips": {
"oidcScope": "स्कोप जोड़ने के लिए एक स्कोप दर्ज करें और Enter दबाएं",
"oidcAdminEmailDomains": "डोमेन जोड़ने के लिए एक डोमेन दर्ज करें और Enter दबाएं"
"oidcAdminEmailDomains": "डोमेन जोड़ने के लिए एक डोमेन दर्ज करें और Enter दबाएं",
"testSmtp": "फॉर्म में वर्तमान में दर्ज मानों के साथ SMTP कनेक्शन का परीक्षण करता है। परिवर्तनों को स्थायी बनाने के लिए, परीक्षण के बाद अपनी सेटिंग्स को सहेजना याद रखें।"
},
"redirectUri": {
"placeholder": "https://mysite.com",

View File

@@ -528,10 +528,16 @@
"oidcAdminEmailDomains": {
"title": "Domini Email Admin",
"description": "Domini email che ricevono automaticamente i privilegi di amministratore"
},
"testSmtp": {
"title": "Test Connessione SMTP",
"description": "Verifica se la configurazione SMTP è valida"
}
},
"buttons": {
"save": "Salva {group}"
"save": "Salva {group}",
"testSmtp": "Prova Connessione",
"testing": "Verifica in corso..."
},
"errors": {
"loadFailed": "Errore durante il caricamento delle impostazioni",
@@ -539,14 +545,18 @@
},
"messages": {
"noChanges": "Nessuna modifica da salvare",
"updateSuccess": "Impostazioni {group} aggiornate con successo"
"updateSuccess": "Impostazioni {group} aggiornate con successo",
"smtpTestFailed": "Connessione SMTP fallita: {error}",
"smtpTestGenericError": "Impossibile testare la connessione SMTP. Controlla le impostazioni e riprova.",
"smtpTestSuccess": "Connessione SMTP riuscita! La configurazione email funziona correttamente."
},
"title": "Impostazioni",
"breadcrumb": "Impostazioni",
"pageTitle": "Impostazioni",
"tooltips": {
"oidcScope": "Inserisci uno scope e premi Invio per aggiungerlo",
"oidcAdminEmailDomains": "Inserisci un dominio e premi Invio per aggiungerlo"
"oidcAdminEmailDomains": "Inserisci un dominio e premi Invio per aggiungerlo",
"testSmtp": "Verifica la connessione SMTP con i valori attualmente inseriti nel modulo. Per rendere permanenti le modifiche, ricordati di salvare le impostazioni dopo il test."
},
"redirectUri": {
"placeholder": "https://miosito.com",

View File

@@ -528,10 +528,16 @@
"oidcAdminEmailDomains": {
"title": "管理者メールドメイン",
"description": "自動的に管理者権限が付与されるメールドメイン"
},
"testSmtp": {
"title": "SMTP接続テスト",
"description": "SMTP設定が有効かどうかをテストします"
}
},
"buttons": {
"save": "{group}を保存"
"save": "{group}を保存",
"testSmtp": "接続テスト",
"testing": "テスト中..."
},
"errors": {
"loadFailed": "設定の読み込みに失敗しました",
@@ -539,14 +545,18 @@
},
"messages": {
"noChanges": "保存する変更はありません",
"updateSuccess": "{group}の設定が正常に更新されました"
"updateSuccess": "{group}の設定が正常に更新されました",
"smtpTestFailed": "SMTP接続に失敗しました: {error}",
"smtpTestGenericError": "SMTP接続のテストに失敗しました。設定を確認して再度お試しください。",
"smtpTestSuccess": "SMTP接続に成功しましたメール設定は正常に動作しています。"
},
"title": "設定",
"breadcrumb": "設定",
"pageTitle": "設定",
"tooltips": {
"oidcScope": "スコープを入力してEnterキーを押して追加",
"oidcAdminEmailDomains": "ドメインを入力してEnterキーを押して追加"
"oidcAdminEmailDomains": "ドメインを入力してEnterキーを押して追加",
"testSmtp": "フォームに現在入力されている値でSMTP接続をテストします。変更を永続化するには、テスト後に設定を保存することを忘れないでください。"
},
"redirectUri": {
"placeholder": "https://mysite.com",

View File

@@ -528,10 +528,16 @@
"oidcAdminEmailDomains": {
"title": "관리자 이메일 도메인",
"description": "자동으로 관리자 권한이 부여되는 이메일 도메인"
},
"testSmtp": {
"title": "SMTP 연결 테스트",
"description": "SMTP 구성이 유효한지 테스트합니다"
}
},
"buttons": {
"save": "{group} 저장"
"save": "{group} 저장",
"testSmtp": "연결 테스트",
"testing": "테스트 중..."
},
"errors": {
"loadFailed": "설정을 불러오는데 실패했습니다",
@@ -539,14 +545,18 @@
},
"messages": {
"noChanges": "저장할 변경 사항이 없습니다",
"updateSuccess": "{group} 설정이 성공적으로 업데이트되었습니다"
"updateSuccess": "{group} 설정이 성공적으로 업데이트되었습니다",
"smtpTestFailed": "SMTP 연결 실패: {error}",
"smtpTestGenericError": "SMTP 연결 테스트에 실패했습니다. 설정을 확인하고 다시 시도해주세요.",
"smtpTestSuccess": "SMTP 연결 성공! 이메일 구성이 올바르게 작동합니다."
},
"title": "설정",
"breadcrumb": "설정",
"pageTitle": "설정",
"tooltips": {
"oidcScope": "스코프를 입력하고 Enter 키를 눌러 추가",
"oidcAdminEmailDomains": "도메인을 입력하고 Enter 키를 눌러 추가"
"oidcAdminEmailDomains": "도메인을 입력하고 Enter 키를 눌러 추가",
"testSmtp": "현재 입력된 값으로 SMTP 연결을 테스트합니다. 변경 사항을 영구적으로 적용하려면 테스트 후 설정을 저장하는 것을 잊지 마세요."
},
"redirectUri": {
"placeholder": "https://mysite.com",

View File

@@ -528,10 +528,16 @@
"oidcAdminEmailDomains": {
"title": "Admin E-mail Domeinen",
"description": "E-mail domeinen die automatisch admin rechten krijgen"
},
"testSmtp": {
"title": "Test SMTP Verbinding",
"description": "Test of de SMTP configuratie geldig is"
}
},
"buttons": {
"save": "{group} Opslaan"
"save": "{group} Opslaan",
"testSmtp": "Test Verbinding",
"testing": "Testen..."
},
"errors": {
"loadFailed": "Fout bij het laden van instellingen",
@@ -539,14 +545,18 @@
},
"messages": {
"noChanges": "Geen wijzigingen om op te slaan",
"updateSuccess": "{group} instellingen succesvol bijgewerkt"
"updateSuccess": "{group} instellingen succesvol bijgewerkt",
"smtpTestFailed": "SMTP verbinding mislukt: {error}",
"smtpTestGenericError": "SMTP verbinding testen mislukt. Controleer uw instellingen en probeer het opnieuw.",
"smtpTestSuccess": "SMTP verbinding succesvol! Uw e-mailconfiguratie werkt correct."
},
"title": "Instellingen",
"breadcrumb": "Instellingen",
"pageTitle": "Instellingen",
"tooltips": {
"oidcScope": "Voer een scope in en druk op Enter om toe te voegen",
"oidcAdminEmailDomains": "Voer een domein in en druk op Enter om toe te voegen"
"oidcAdminEmailDomains": "Voer een domein in en druk op Enter om toe te voegen",
"testSmtp": "Test de SMTP verbinding met de waarden die momenteel in het formulier zijn ingevoerd. Om wijzigingen permanent te maken, vergeet niet om uw instellingen op te slaan na het testen."
},
"redirectUri": {
"placeholder": "https://mijnsite.com",

View File

@@ -528,10 +528,16 @@
"oidcAdminEmailDomains": {
"title": "Domeny e-mail administratora",
"description": "Domeny e-mail, które automatycznie otrzymują uprawnienia administratora"
},
"testSmtp": {
"title": "Test połączenia SMTP",
"description": "Sprawdź, czy konfiguracja SMTP jest prawidłowa"
}
},
"buttons": {
"save": "Zapisz {group}"
"save": "Zapisz {group}",
"testSmtp": "Testuj połączenie",
"testing": "Testowanie..."
},
"errors": {
"loadFailed": "Nie udało się załadować ustawień",
@@ -539,14 +545,18 @@
},
"messages": {
"noChanges": "Brak zmian do zapisania",
"updateSuccess": "Ustawienia {group} zaktualizowane pomyślnie"
"updateSuccess": "Ustawienia {group} zaktualizowane pomyślnie",
"smtpTestFailed": "Połączenie SMTP nie powiodło się: {error}",
"smtpTestGenericError": "Nie udało się przetestować połączenia SMTP. Sprawdź ustawienia i spróbuj ponownie.",
"smtpTestSuccess": "Połączenie SMTP udane! Twoja konfiguracja poczty e-mail działa poprawnie."
},
"title": "Ustawienia",
"breadcrumb": "Ustawienia",
"pageTitle": "Ustawienia",
"tooltips": {
"oidcScope": "Wprowadź zakres i naciśnij Enter, aby dodać",
"oidcAdminEmailDomains": "Wprowadź domenę i naciśnij Enter, aby dodać"
"oidcAdminEmailDomains": "Wprowadź domenę i naciśnij Enter, aby dodać",
"testSmtp": "Testuje połączenie SMTP z obecnie wprowadzonymi wartościami w formularzu. Aby wprowadzić trwałe zmiany, pamiętaj o zapisaniu ustawień po testowaniu."
},
"redirectUri": {
"placeholder": "https://mysite.com",

View File

@@ -443,7 +443,8 @@
},
"tooltips": {
"oidcScope": "Digite um escopo e pressione Enter para adicionar",
"oidcAdminEmailDomains": "Digite um domínio e pressione Enter para adicionar"
"oidcAdminEmailDomains": "Digite um domínio e pressione Enter para adicionar",
"testSmtp": "Testa a conexão SMTP com os valores atualmente inseridos no formulário. Para tornar as alterações permanentes, lembre-se de salvar suas configurações após o teste."
},
"redirectUri": {
"placeholder": "https://meusite.com",
@@ -558,10 +559,16 @@
"oidcAdminEmailDomains": {
"title": "Domínios de E-mail Admin",
"description": "Domínios de e-mail que recebem privilégios de administrador automaticamente"
},
"testSmtp": {
"title": "Testar Conexão SMTP",
"description": "Testa se a configuração SMTP é válida"
}
},
"buttons": {
"save": "Salvar {group}"
"save": "Salvar {group}",
"testSmtp": "Testar Conexão",
"testing": "Testando..."
},
"errors": {
"loadFailed": "Falha ao carregar configurações",
@@ -569,7 +576,10 @@
},
"messages": {
"noChanges": "Nenhuma alteração para salvar",
"updateSuccess": "Configurações de {group} atualizadas com sucesso"
"updateSuccess": "Configurações de {group} atualizadas com sucesso",
"smtpTestFailed": "Falha na conexão SMTP: {error}",
"smtpTestGenericError": "Falha ao testar conexão SMTP. Por favor, verifique suas configurações e tente novamente.",
"smtpTestSuccess": "Conexão SMTP bem-sucedida! Sua configuração de e-mail está funcionando corretamente."
},
"title": "Configurações",
"breadcrumb": "Configurações",

View File

@@ -528,10 +528,16 @@
"oidcAdminEmailDomains": {
"title": "Домены Email Администратора",
"description": "Домены электронной почты, которые автоматически получают права администратора"
},
"testSmtp": {
"title": "Проверить SMTP-соединение",
"description": "Проверить правильность настройки SMTP"
}
},
"buttons": {
"save": "Сохранить {group}"
"save": "Сохранить {group}",
"testSmtp": "Проверить соединение",
"testing": "Проверка..."
},
"errors": {
"loadFailed": "Ошибка загрузки настроек",
@@ -539,14 +545,18 @@
},
"messages": {
"noChanges": "Изменений для сохранения нет",
"updateSuccess": "Настройки {group} успешно обновлены"
"updateSuccess": "Настройки {group} успешно обновлены",
"smtpTestFailed": "Ошибка SMTP-соединения: {error}",
"smtpTestGenericError": "Не удалось проверить SMTP-соединение. Пожалуйста, проверьте настройки и попробуйте снова.",
"smtpTestSuccess": "SMTP-соединение успешно установлено! Ваша конфигурация электронной почты работает корректно."
},
"title": "Настройки",
"breadcrumb": "Настройки",
"pageTitle": "Настройки",
"tooltips": {
"oidcScope": "Digite um escopo e pressione Enter para adicionar",
"oidcAdminEmailDomains": "Digite um domínio e pressione Enter para adicionar"
"oidcAdminEmailDomains": "Digite um domínio e pressione Enter para adicionar",
"testSmtp": "Проверяет SMTP-соединение с текущими значениями в форме. Чтобы сохранить изменения, не забудьте сохранить настройки после тестирования."
},
"redirectUri": {
"placeholder": "https://meusite.com",

View File

@@ -528,10 +528,16 @@
"oidcAdminEmailDomains": {
"title": "Yönetici E-posta Alanları",
"description": "Otomatik olarak yönetici ayrıcalıkları alan e-posta alanları"
},
"testSmtp": {
"title": "SMTP Bağlantısını Test Et",
"description": "SMTP yapılandırmasının geçerli olup olmadığını test et"
}
},
"buttons": {
"save": "{group} Kaydet"
"save": "{group} Kaydet",
"testSmtp": "Bağlantıyı Test Et",
"testing": "Test Ediliyor..."
},
"errors": {
"loadFailed": "Ayarlar yüklenemedi",
@@ -539,14 +545,18 @@
},
"messages": {
"noChanges": "Kaydedilecek değişiklik yok",
"updateSuccess": "{group} ayarları başarıyla güncellendi"
"updateSuccess": "{group} ayarları başarıyla güncellendi",
"smtpTestFailed": "SMTP bağlantısı başarısız: {error}",
"smtpTestGenericError": "SMTP bağlantısı test edilemedi. Lütfen ayarlarınızı kontrol edin ve tekrar deneyin.",
"smtpTestSuccess": "SMTP bağlantısı başarılı! E-posta yapılandırmanız doğru çalışıyor."
},
"title": "Ayarlar",
"breadcrumb": "Ayarlar",
"pageTitle": "Ayarlar",
"tooltips": {
"oidcScope": "Bir kapsam girin ve eklemek için Enter'a basın",
"oidcAdminEmailDomains": "Bir alan girin ve eklemek için Enter'a basın"
"oidcAdminEmailDomains": "Bir alan girin ve eklemek için Enter'a basın",
"testSmtp": "Formda şu anda girilen değerlerle SMTP bağlantısını test eder. Değişiklikleri kalıcı yapmak için test ettikten sonra ayarlarınızı kaydetmeyi unutmayın."
},
"redirectUri": {
"placeholder": "https://sitem.com",

View File

@@ -528,10 +528,16 @@
"oidcAdminEmailDomains": {
"title": "管理员电子邮件域名",
"description": "自动获得管理员权限的电子邮件域名"
},
"testSmtp": {
"title": "测试SMTP连接",
"description": "测试SMTP配置是否有效"
}
},
"buttons": {
"save": "保存 {group}"
"save": "保存 {group}",
"testSmtp": "测试连接",
"testing": "测试中..."
},
"errors": {
"loadFailed": "加载设置失败",
@@ -539,14 +545,18 @@
},
"messages": {
"noChanges": "没有需要保存的更改",
"updateSuccess": "{group}设置更新成功"
"updateSuccess": "{group}设置更新成功",
"smtpTestFailed": "SMTP连接失败{error}",
"smtpTestGenericError": "SMTP连接测试失败。请检查您的设置并重试。",
"smtpTestSuccess": "SMTP连接成功您的电子邮件配置工作正常。"
},
"title": "设置",
"breadcrumb": "设置",
"pageTitle": "设置",
"tooltips": {
"oidcScope": "Digite um escopo e pressione Enter para adicionar",
"oidcAdminEmailDomains": "Digite um domínio e pressione Enter para adicionar"
"oidcAdminEmailDomains": "Digite um domínio e pressione Enter para adicionar",
"testSmtp": "使用当前在表单中输入的值测试SMTP连接。要使更改永久生效请记得在测试后保存您的设置。"
},
"redirectUri": {
"placeholder": "https://meusite.com",

View File

@@ -0,0 +1,31 @@
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
const cookieHeader = req.headers.get("cookie");
const body = await req.text();
const apiRes = await fetch(`${process.env.API_BASE_URL}/app/test-smtp`, {
method: "POST",
headers: {
cookie: cookieHeader || "",
"Content-Type": "application/json",
},
body: body || "{}",
redirect: "manual",
});
const resBody = await apiRes.text();
const res = new NextResponse(resBody, {
status: apiRes.status,
headers: {
"Content-Type": "application/json",
},
});
const setCookie = apiRes.headers.getSetCookie?.() || [];
if (setCookie.length > 0) {
res.headers.set("Set-Cookie", setCookie.join(","));
}
return res;
}

View File

@@ -8,6 +8,7 @@ import { Separator } from "@/components/ui/separator";
import { createFieldDescriptions, createGroupMetadata } from "../constants";
import { SettingsGroupProps } from "../types";
import { isFieldHidden, SettingsInput } from "./settings-input";
import { SmtpTestButton } from "./smtp-test-button";
export function SettingsGroup({ group, configs, form, isCollapsed, onToggleCollapse, onSubmit }: SettingsGroupProps) {
const t = useTranslations();
@@ -19,6 +20,8 @@ export function SettingsGroup({ group, configs, form, isCollapsed, onToggleColla
description: t("settings.groups.defaultDescription"),
};
const isEmailGroup = group === "email";
return (
<form onSubmit={form.handleSubmit(onSubmit)}>
<Card className="p-6 gap-0">
@@ -70,18 +73,32 @@ export function SettingsGroup({ group, configs, form, isCollapsed, onToggleColla
</div>
))}
</div>
<div className="flex justify-end mt-4">
<Button
variant="default"
disabled={form.formState.isSubmitting}
className="flex items-center gap-2"
type="submit"
>
{!form.formState.isSubmitting && <IconDeviceFloppy className="h-4 w-4" />}
{t("settings.buttons.save", {
group: t(`settings.groups.${group}.title`, { defaultValue: metadata.title }),
})}
</Button>
<div className="flex justify-between items-center mt-4">
{isEmailGroup && (
<SmtpTestButton
smtpEnabled={form.watch("configs.smtpEnabled") || "false"}
getFormValues={() => ({
smtpEnabled: form.getValues("configs.smtpEnabled") || "false",
smtpHost: form.getValues("configs.smtpHost") || "",
smtpPort: form.getValues("configs.smtpPort") || "",
smtpUser: form.getValues("configs.smtpUser") || "",
smtpPass: form.getValues("configs.smtpPass") || "",
})}
/>
)}
<div className={`flex ${isEmailGroup ? "ml-auto" : ""}`}>
<Button
variant="default"
disabled={form.formState.isSubmitting}
className="flex items-center gap-2"
type="submit"
>
{!form.formState.isSubmitting && <IconDeviceFloppy className="h-4 w-4" />}
{t("settings.buttons.save", {
group: t(`settings.groups.${group}.title`, { defaultValue: metadata.title }),
})}
</Button>
</div>
</div>
</CardContent>
</Card>

View File

@@ -0,0 +1,86 @@
"use client";
import { useState } from "react";
import { IconFlask, IconInfoCircle, IconLoader } from "@tabler/icons-react";
import { useTranslations } from "next-intl";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { testSmtpConnection } from "@/http/endpoints/app";
interface SmtpTestButtonProps {
smtpEnabled: string;
getFormValues: () => {
smtpEnabled: string;
smtpHost: string;
smtpPort: string;
smtpUser: string;
smtpPass: string;
};
}
export function SmtpTestButton({ smtpEnabled, getFormValues }: SmtpTestButtonProps) {
const [isLoading, setIsLoading] = useState(false);
const t = useTranslations();
const handleTestConnection = async () => {
const formValues = getFormValues();
if (formValues.smtpEnabled !== "true") {
toast.error("SMTP is not enabled. Please enable SMTP first.");
return;
}
// Check if required fields are filled
if (!formValues.smtpHost || !formValues.smtpPort || !formValues.smtpUser || !formValues.smtpPass) {
toast.error("Please fill in all SMTP configuration fields before testing.");
return;
}
setIsLoading(true);
try {
const response = await testSmtpConnection({
smtpConfig: {
smtpEnabled: formValues.smtpEnabled,
smtpHost: formValues.smtpHost,
smtpPort: formValues.smtpPort,
smtpUser: formValues.smtpUser,
smtpPass: formValues.smtpPass,
},
});
if (response.data.success) {
toast.success(t("settings.messages.smtpTestSuccess"));
} else {
toast.error(t("settings.messages.smtpTestGenericError"));
}
} catch (error: any) {
const errorMessage = error?.response?.data?.error || error?.message || "Unknown error";
toast.error(t("settings.messages.smtpTestFailed", { error: errorMessage }));
} finally {
setIsLoading(false);
}
};
return (
<div className="flex items-center gap-2">
<Button
type="button"
variant="outline"
onClick={handleTestConnection}
disabled={isLoading || smtpEnabled !== "true"}
className="flex items-center gap-2"
>
{isLoading ? <IconLoader className="h-4 w-4 animate-spin" /> : <IconFlask className="h-4 w-4" />}
{isLoading ? t("settings.buttons.testing") : t("settings.buttons.testSmtp")}
</Button>
<div className="relative group">
<IconInfoCircle className="h-4 w-4 text-muted-foreground hover:text-foreground cursor-help transition-colors" />
<div className="absolute left-1/2 -translate-x-1/2 bottom-full mb-2 px-3 py-2 bg-popover text-popover-foreground text-xs rounded-md border shadow-md opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-50 w-72 text-wrap">
{t("settings.tooltips.testSmtp")}
<div className="absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-popover"></div>
</div>
</div>
</div>
);
}

View File

@@ -80,3 +80,22 @@ export const checkUploadAllowed = <TData = CheckUploadAllowedResult>(
params: { ...params, ...options?.params },
});
};
export type TestSmtpConnectionResult = { success: boolean; message: string };
export interface TestSmtpConnectionBody {
smtpConfig?: {
smtpEnabled: string;
smtpHost: string;
smtpPort: string;
smtpUser: string;
smtpPass: string;
};
}
export const testSmtpConnection = (
body?: TestSmtpConnectionBody,
options?: AxiosRequestConfig
): Promise<{ data: TestSmtpConnectionResult }> => {
return apiInstance.post(`/api/app/test-smtp`, body || {}, options);
};