feat: add intelligent HAProxy validation error display in UI

- Add haproxy_error_parser.py: Parses HAProxy validation errors with
  confidence scoring, extracts entity type/name, line number, error type
- Add ValidationErrorModal.js: Rich modal with parsed error summary,
  quick fix suggestions, and manual troubleshooting guide
- Update cluster.py: Integrate error parser into agent-sync and
  config-versions endpoints with graceful fallback
- Update ApplyManagement.js: Add validation error banner with quick
  navigation buttons and error detail modal
- Update FrontendManagement.js & BackendServers.js: Handle URL params
  for deep-linking to entity edit forms with field highlighting

Enables users to see actionable validation failure details directly
in the UI without needing server access for debugging.
This commit is contained in:
taylanbakircioglu
2026-01-26 15:25:15 +03:00
parent 0c1d68eb01
commit 8076f3fcfd
6 changed files with 1212 additions and 37 deletions
+33 -1
View File
@@ -12,7 +12,7 @@ import {
WarningOutlined, SearchOutlined, HistoryOutlined, PlayCircleOutlined, LoadingOutlined
} from '@ant-design/icons';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import { useNavigate, useLocation } from 'react-router-dom';
import { useCluster } from '../contexts/ClusterContext';
import { VersionHistory } from './VersionHistory';
@@ -60,6 +60,7 @@ const { TextArea } = Input;
const FrontendManagement = () => {
const { selectedCluster } = useCluster();
const navigate = useNavigate();
const location = useLocation();
const [frontends, setFrontends] = useState([]);
const [backends, setBackends] = useState([]);
const [sslCertificates, setSslCertificates] = useState([]);
@@ -162,6 +163,37 @@ const FrontendManagement = () => {
checkPendingChanges();
}, [selectedCluster]);
// Handle URL parameters for quick edit navigation (from validation error modal)
useEffect(() => {
const params = new URLSearchParams(location.search);
const editId = params.get('edit');
const highlightField = params.get('highlight');
if (editId && frontends.length > 0) {
const frontendToEdit = frontends.find(f => f.id === parseInt(editId));
if (frontendToEdit) {
// Open edit modal for the frontend
setEditingFrontend(frontendToEdit);
form.setFieldsValue({
...frontendToEdit,
ssl_enabled: frontendToEdit.ssl_enabled || false,
ssl_certificate_ids: frontendToEdit.ssl_certificate_ids || [],
acl_rules: frontendToEdit.acl_rules || [],
use_backend_rules: frontendToEdit.use_backend_rules || [],
});
setModalVisible(true);
// Clear URL params after opening modal
navigate(location.pathname, { replace: true });
// Show notification about which field to check
if (highlightField) {
message.info(`Please check the "${highlightField}" field - it may have caused a validation error.`, 5);
}
}
}
}, [location.search, frontends]);
const fetchFrontends = async () => {
// CRITICAL FIX: Don't fetch if no cluster selected to prevent race condition
// Same race condition as BackendServers - prevents all frontends from appearing