diff --git a/frontend-modern/src/components/Settings/NodeModal.tsx b/frontend-modern/src/components/Settings/NodeModal.tsx index 434dd7f44..725ea39e3 100644 --- a/frontend-modern/src/components/Settings/NodeModal.tsx +++ b/frontend-modern/src/components/Settings/NodeModal.tsx @@ -57,6 +57,7 @@ export const NodeModal: Component = (props) => { status: string; message: string; isCluster?: boolean; + warnings?: string[]; } | null>(null); const [isTesting, setIsTesting] = createSignal(false); @@ -362,9 +363,10 @@ export const NodeModal: Component = (props) => { try { const result = await NodesAPI.testConnection(testData as NodeConfig); setTestResult({ - status: 'success', + status: result.warnings && result.warnings.length > 0 ? 'warning' : 'success', message: result.message || 'Connection successful', isCluster: result.isCluster, + warnings: result.warnings, }); } catch (error) { logger.error('Test connection error:', error); @@ -1863,7 +1865,9 @@ export const NodeModal: Component = (props) => { class={`mx-6 p-3 rounded-lg text-sm ${ testResult()?.status === 'success' ? 'bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 text-green-800 dark:text-green-200' - : 'bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 text-red-800 dark:text-red-200' + : testResult()?.status === 'warning' + ? 'bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 text-amber-800 dark:text-amber-200' + : 'bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 text-red-800 dark:text-red-200' }`} >
@@ -1881,6 +1885,19 @@ export const NodeModal: Component = (props) => { + + + + + = (props) => { -
+

{testResult()?.message}

✨ Cluster detected! All cluster nodes will be automatically added.

+ 0}> +
+

Warnings:

+
    + + {(warning) =>
  • • {warning}
  • } +
    +
+
+
diff --git a/frontend-modern/src/components/Settings/Settings.tsx b/frontend-modern/src/components/Settings/Settings.tsx index c248b219e..2c9bb4a51 100644 --- a/frontend-modern/src/components/Settings/Settings.tsx +++ b/frontend-modern/src/components/Settings/Settings.tsx @@ -12,7 +12,7 @@ import { import type { JSX } from 'solid-js'; import { useNavigate, useLocation } from '@solidjs/router'; import { useWebSocket } from '@/App'; -import { showSuccess, showError } from '@/utils/toast'; +import { showSuccess, showError, showWarning } from '@/utils/toast'; import { copyToClipboard } from '@/utils/clipboard'; import { getPulsePort, getPulseWebSocketUrl } from '@/utils/url'; import { logger } from '@/utils/logger'; @@ -1843,7 +1843,13 @@ const Settings: Component = (props) => { // Use the existing node test endpoint which uses stored credentials const result = await NodesAPI.testExistingNode(nodeId); if (result.status === 'success') { - showSuccess(result.message || 'Connection successful'); + // Check for warnings in the response + if (result.warnings && Array.isArray(result.warnings) && result.warnings.length > 0) { + const warningMessage = result.message + '\n\nWarnings:\n' + result.warnings.map((w: string) => '• ' + w).join('\n'); + showWarning(warningMessage); + } else { + showSuccess(result.message || 'Connection successful'); + } } else { throw new Error(result.message || 'Connection failed'); } diff --git a/frontend-modern/src/utils/toast.ts b/frontend-modern/src/utils/toast.ts index b8aadff04..12aa16859 100644 --- a/frontend-modern/src/utils/toast.ts +++ b/frontend-modern/src/utils/toast.ts @@ -23,3 +23,5 @@ export const showSuccess = (title: string, message?: string, duration?: number) showToast('success', title, message, duration); export const showError = (title: string, message?: string, duration?: number) => showToast('error', title, message, duration); +export const showWarning = (title: string, message?: string, duration?: number) => + showToast('warning', title, message, duration); diff --git a/internal/api/config_handlers.go b/internal/api/config_handlers.go index d4d9f2ae1..a7a6e6a62 100644 --- a/internal/api/config_handlers.go +++ b/internal/api/config_handlers.go @@ -1675,7 +1675,7 @@ func (h *ConfigHandlers) HandleTestConnection(w http.ResponseWriter, r *http.Req return } - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() version, err := tempClient.GetVersion(ctx) @@ -1692,10 +1692,34 @@ func (h *ConfigHandlers) HandleTestConnection(w http.ResponseWriter, r *http.Req } } + // Test actual metrics endpoints to ensure monitoring will work + warnings := []string{} + + // Test mail statistics endpoint (core PMG functionality) + if _, err := tempClient.GetMailStatistics(ctx, "day"); err != nil { + warnings = append(warnings, "Mail statistics endpoint unavailable - check user permissions") + log.Warn().Err(err).Msg("PMG connection test: mail statistics check failed") + } + + // Test cluster status endpoint + if _, err := tempClient.GetClusterStatus(ctx, true); err != nil { + warnings = append(warnings, "Cluster status endpoint unavailable") + log.Warn().Err(err).Msg("PMG connection test: cluster status check failed") + } + + // Test quarantine endpoint + if _, err := tempClient.GetQuarantineStatus(ctx, "spam"); err != nil { + warnings = append(warnings, "Quarantine endpoint unavailable") + log.Warn().Err(err).Msg("PMG connection test: quarantine check failed") + } + message := "Connected to PMG instance" if versionLabel != "" { message = fmt.Sprintf("Connected to PMG instance (version %s)", versionLabel) } + if len(warnings) > 0 { + message += " (some metrics may be unavailable - check logs for details)" + } response := map[string]interface{}{ "status": "success", @@ -1711,6 +1735,10 @@ func (h *ConfigHandlers) HandleTestConnection(w http.ResponseWriter, r *http.Req } } + if len(warnings) > 0 { + response["warnings"] = warnings + } + w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) }