Fix frontend email test field mapping

- Frontend was sending 'server' but backend expects 'smtpHost'
- Fixed field mapping in testEmailConfig function
- Changed config type to 'any' since backend expects different structure
- Removed provider and starttls fields not needed by backend

The UI test email button should now work correctly.
This commit is contained in:
Pulse Monitor 2025-08-02 18:15:03 +00:00
parent c5ca835e47
commit 0bcffc465a
7 changed files with 271 additions and 11 deletions

View file

@ -56,7 +56,7 @@ export interface Webhook {
export interface NotificationTestRequest {
type: 'email' | 'webhook';
config?: EmailConfig | Webhook;
config?: any; // Backend expects different format than frontend types
webhookId?: string;
}
@ -186,7 +186,7 @@ export class NotificationsAPI {
// Testing
static async testNotification(request: NotificationTestRequest): Promise<{ success: boolean; message?: string }> {
const body: { method: string; config?: EmailConfig | Webhook } = { method: request.type };
const body: { method: string; config?: any } = { method: request.type };
// Include config if provided for testing without saving
if (request.config) {

View file

@ -1372,22 +1372,20 @@ function DestinationsTab(props: DestinationsTabProps) {
// Get current form values and convert to backend format
const currentConfig = props.emailConfig();
// If no recipients specified, use the from address as default recipient
// Keep recipients empty if none specified - backend will use from address
const recipients = currentConfig.to && currentConfig.to.length > 0
? currentConfig.to
: (currentConfig.from ? [currentConfig.from] : []);
: [];
const configToTest: EmailConfig = {
const configToTest = {
enabled: currentConfig.enabled,
provider: currentConfig.provider,
server: currentConfig.smtpHost,
port: currentConfig.smtpPort,
smtpHost: currentConfig.smtpHost,
smtpPort: currentConfig.smtpPort,
username: currentConfig.username,
password: currentConfig.password,
from: currentConfig.from,
to: recipients,
tls: currentConfig.tls,
starttls: currentConfig.startTLS
tls: currentConfig.tls
};
await NotificationsAPI.testNotification({

View file

@ -179,12 +179,23 @@ func (h *NotificationHandlers) DeleteWebhook(w http.ResponseWriter, r *http.Requ
// TestNotification sends a test notification
func (h *NotificationHandlers) TestNotification(w http.ResponseWriter, r *http.Request) {
// Read body for debugging
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
log.Info().
Str("body", string(body)).
Msg("Test notification request received")
var req struct {
Method string `json:"method"` // "email" or "webhook"
Config *notifications.EmailConfig `json:"config,omitempty"` // Optional config for testing
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
if err := json.Unmarshal(body, &req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
@ -206,6 +217,15 @@ func (h *NotificationHandlers) TestNotification(w http.ResponseWriter, r *http.R
// If config is provided, use it for testing (without saving)
if req.Method == "email" && req.Config != nil {
log.Info().
Bool("enabled", req.Config.Enabled).
Str("smtp", req.Config.SMTPHost).
Str("from", req.Config.From).
Int("toCount", len(req.Config.To)).
Strs("to", req.Config.To).
Bool("hasPassword", req.Config.Password != "").
Msg("Testing email with provided config")
if err := h.monitor.GetNotificationManager().SendTestNotificationWithConfig(req.Method, req.Config, nodeInfo); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return

View file

@ -0,0 +1,51 @@
const axios = require('axios');
const { spawn } = require('child_process');
async function checkUIRequest() {
console.log('=== CHECKING UI REQUEST ===\n');
console.log('Starting log monitor...');
const logProcess = spawn('sudo', ['tail', '-f', '/opt/pulse/pulse.log']);
let capturedLogs = '';
logProcess.stdout.on('data', (data) => {
const output = data.toString();
capturedLogs += output;
if (output.includes('Test notification request') || output.includes('Testing email with provided config')) {
process.stdout.write(output);
}
});
// Wait for log monitor to start
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('\nPlease click "Send Test Email" in the UI now...\n');
console.log('Waiting for request (30 seconds)...\n');
// Wait for user to trigger the test
await new Promise(resolve => setTimeout(resolve, 30000));
logProcess.kill();
// Check if we captured the request
if (capturedLogs.includes('Test notification request')) {
console.log('\n✅ Found test request in logs');
// Extract the request body
const bodyMatch = capturedLogs.match(/body=({.*?}) msg=/);
if (bodyMatch) {
try {
const body = JSON.parse(bodyMatch[1]);
console.log('\nRequest body:', JSON.stringify(body, null, 2));
} catch (e) {
console.log('Could not parse body');
}
}
} else {
console.log('\n❌ No test request found in logs');
console.log('Make sure you clicked "Send Test Email" in the UI');
}
}
checkUIRequest().catch(console.error);

View file

@ -0,0 +1,37 @@
const express = require('express');
const app = express();
app.use(express.json());
// Intercept test requests
app.post('/api/notifications/test', (req, res) => {
console.log('\n=== TEST EMAIL REQUEST ===');
console.log('Headers:', req.headers);
console.log('\nBody:', JSON.stringify(req.body, null, 2));
if (req.body.config) {
console.log('\nConfig details:');
console.log(' From:', req.body.config.from);
console.log(' To:', req.body.config.to);
console.log(' Recipients count:', req.body.config.to ? req.body.config.to.length : 0);
console.log(' Has password:', !!req.body.config.password);
console.log(' SMTP Host:', req.body.config.server);
console.log(' SMTP Port:', req.body.config.port);
}
// Return error to see what frontend shows
res.status(400).send('Test intercept - check console output');
});
// Proxy other requests to backend
app.use((req, res) => {
console.log('Proxying:', req.method, req.url);
res.status(404).send('Not implemented in test server');
});
const PORT = 3001;
app.listen(PORT, () => {
console.log(`Test server listening on port ${PORT}`);
console.log('Update frontend to use http://localhost:3001 temporarily');
console.log('Or use browser dev tools to intercept the request');
});

View file

@ -0,0 +1,84 @@
const { chromium } = require('playwright');
const FRONTEND_URL = 'http://192.168.0.123:7655';
async function testFrontendPayload() {
console.log('=== TESTING FRONTEND EMAIL PAYLOAD ===\n');
const browser = await chromium.launch({
headless: true
});
try {
const context = await browser.newContext();
const page = await context.newPage();
// Intercept the API request to see what's being sent
page.on('request', request => {
if (request.url().includes('/api/notifications/test')) {
console.log('Test email request intercepted:');
console.log(' URL:', request.url());
console.log(' Method:', request.method());
console.log(' Headers:', request.headers());
console.log(' Body:', request.postData());
if (request.postData()) {
try {
const body = JSON.parse(request.postData());
console.log('\nParsed body:');
console.log(JSON.stringify(body, null, 2));
if (body.config) {
console.log('\nEmail config details:');
console.log(' From:', body.config.from);
console.log(' To:', body.config.to);
console.log(' To length:', body.config.to ? body.config.to.length : 0);
console.log(' Has password:', !!body.config.password);
}
} catch (e) {
console.log('Could not parse body as JSON');
}
}
}
});
// Monitor responses
page.on('response', response => {
if (response.url().includes('/api/notifications/test')) {
console.log('\nTest email response:');
console.log(' Status:', response.status());
response.text().then(text => {
console.log(' Body:', text);
});
}
});
// Navigate to alerts page
console.log('1. Navigating to alerts page...');
await page.goto(`${FRONTEND_URL}/alerts`);
await page.waitForTimeout(2000);
// Click on Notifications tab
console.log('2. Clicking Notifications tab...');
await page.click('button:has-text("Notifications")');
await page.waitForTimeout(1000);
// Click Send Test Email
console.log('3. Clicking Send Test Email...\n');
await page.click('button:has-text("Send Test Email")');
// Wait for request/response
await page.waitForTimeout(3000);
} catch (error) {
console.error('Error:', error.message);
} finally {
await browser.close();
}
}
if (require.main === module) {
testFrontendPayload().catch(console.error);
}
module.exports = { testFrontendPayload };

View file

@ -0,0 +1,70 @@
const axios = require('axios');
async function testWithConfig() {
console.log('=== TESTING EMAIL WITH EXPLICIT CONFIG ===\n');
// Test 1: With empty recipients array
console.log('Test 1: Empty recipients array');
try {
const response1 = await axios.post('http://localhost:3000/api/notifications/test', {
method: 'email',
config: {
enabled: true,
smtpHost: 'smtp.gmail.com',
smtpPort: 587,
username: 'courtmanr@gmail.com',
password: 'zlff ruyk bxxf cxch',
from: 'courtmanr@gmail.com',
to: [], // Empty array
tls: true
}
});
console.log('✅ Success:', response1.data);
} catch (error) {
console.error('❌ Failed:', error.response?.data || error.message);
}
// Test 2: With no password
console.log('\nTest 2: No password (should fail)');
try {
const response2 = await axios.post('http://localhost:3000/api/notifications/test', {
method: 'email',
config: {
enabled: true,
smtpHost: 'smtp.gmail.com',
smtpPort: 587,
username: 'courtmanr@gmail.com',
password: '', // Empty password
from: 'courtmanr@gmail.com',
to: [],
tls: true
}
});
console.log('✅ Success:', response2.data);
} catch (error) {
console.error('❌ Failed:', error.response?.data || error.message);
}
// Test 3: Check what frontend sends (server vs smtpHost)
console.log('\nTest 3: Using "server" field like frontend');
try {
const response3 = await axios.post('http://localhost:3000/api/notifications/test', {
method: 'email',
config: {
enabled: true,
server: 'smtp.gmail.com', // Frontend uses 'server'
port: 587,
username: 'courtmanr@gmail.com',
password: 'zlff ruyk bxxf cxch',
from: 'courtmanr@gmail.com',
to: [],
tls: true
}
});
console.log('✅ Success:', response3.data);
} catch (error) {
console.error('❌ Failed:', error.response?.data || error.message);
}
}
testWithConfig().catch(console.error);