Commit Graph

73 Commits

Author SHA1 Message Date
taylanbakircioglu 5796d3d1fd feat: rebrand to HAProxy OpenManager, remove demo accounts from login
- Replace login icon with ClusterOutlined, title "HAProxy OpenManager"
- Add subtitle "Multi-Cluster Load Balancer Management" on login page
- Remove demo account list from login page
- Update sidebar logo: emoji → ClusterOutlined icon
- Update header and browser tab title
- Clean up unused demo-account CSS styles

Made-with: Cursor
2026-03-19 13:12:24 +03:00
taylanbakircioglu bb6cb58db3 fix: auto-correct redirect target when switching type to scheme
When user changes redirect type from location/prefix to scheme, the
target field (e.g. "https://example.com") stays as-is but the UI shows
a blank Select. If saved without re-selecting, invalid config would be
sent. Now auto-sets target to "https" when switching to scheme type.

Made-with: Cursor
2026-03-19 12:07:56 +03:00
taylanbakircioglu 7105213ba5 feat: add UI-level validation to prevent invalid HAProxy ACL configurations
- Enforce mutual exclusivity for -m flags (only one match method at a time)
- Show error status on value field when -f flag is used without absolute
  file path, with tooltip explaining the requirement
- Replace free-text input with Select dropdown for redirect scheme type,
  restricting to valid values (http/https) only
- Fix flag serialization order (-i → -m → -f) to prevent HAProxy parse errors

Made-with: Cursor
2026-03-19 12:02:07 +03:00
taylanbakircioglu 4780f0c4d6 feat: add visual ACL Rule Builder for frontend management
Replace plain TextArea inputs with an interactive card-based visual builder
for ACL rules, backend routing rules, and redirect rules. Includes:

- Structured ACL definition cards with match type, flags, and value fields
- Backend routing cards with operator (if/unless) and ACL condition selector
- Redirect rule cards with type, target, code, and condition fields
- Visual ↔ Raw mode toggle for each section
- HAProxy config preview panel
- Safe flag serialization order (-i → -m → -f) to prevent parse errors
- Guard against empty/incomplete rules in serializers
- Context-aware flag placeholder hints per match type category

Made-with: Cursor
2026-03-18 18:45:35 +03:00
taylanbakircioglu d4b58d62c7 fix: upgrade react-scripts to 5.0.1 for frontend Docker build compatibility
- react-scripts 4.0.3 -> 5.0.1 (Webpack 5, supports modern JS in node_modules)
- @monaco-editor/react 3.7.5 -> ^4.6.0 (resolves peer dep conflict with monaco-editor)
- Removed --legacy-peer-deps from Dockerfile (no longer needed, was causing ajv resolution issues)
- Added @babel/plugin-proposal-private-property-in-object as explicit devDependency
- Removed overrides section (not needed with react-scripts 5)

Made-with: Cursor
2026-03-16 13:25:03 +03:00
taylanbakircioglu 68a4d1ac23 fix: resolve Babel version conflict in frontend Docker build
Add npm overrides to pin @babel/core and @babel/plugin-proposal-private-property-in-object
to versions compatible with react-scripts 4.0.3. Fixes build failure caused by transitive
dependency resolution pulling incompatible Babel 7.12.3.

Made-with: Cursor
2026-03-16 12:56:37 +03:00
taylanbakircioglu efb466e6af ui: softer cluster selector highlight without container border
Remove the blue background/border container that looked disconnected
from the page. Instead highlight the Select itself with a subtle blue
border glow when no cluster is selected.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-18 20:41:17 +03:00
taylanbakircioglu 16c502dc17 ui: improve backend servers column and cluster selector visibility
- Replace wrapping "active" text in Servers column with compact tag
  showing active/total count with tooltip for details
- Add subtle pulse animation and dashed border to cluster selector
  when no cluster is selected to guide new users

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-18 20:41:17 +03:00
taylanbakircioglu f526c3b6b6 feat: add IP Inventory page for cross-cluster IP search
New page that provides a unified view of all IPs across all clusters.
Users can search any IP to identify which cluster/agent/backend it
belongs to, and whether it is an Agent IP, VIP (keepalive), or
Backend Server address.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-18 20:41:17 +03:00
taylanbakircioglu 833c551929 ui: compact Clusters table layout to avoid horizontal scroll
- Description: constrained to 200px width, 2-line clamp with tooltip
  for full text on hover. Prevents long descriptions from stretching
  the table.
- Connection Type: shortened labels ("Agent"/"Sidecar" instead of
  "Agent Connection"/"Sidecar Agent"), reduced column width to 110px.
- HAProxy Version: reduced width from 220px to 170px.
- Keepalive: reduced width from 180px to 155px.
- Table scroll: changed from max-content to 1100px fixed minimum,
  so the table fits on normal screens without scrolling.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-18 20:41:17 +03:00
taylanbakircioglu 9ddc495e4f fix: keepalive UI edge cases and dashboard auto-refresh
- ClusterManagement: show all unique VIPs from all agents (not just
  first agent's VIP) to prevent misleading display in multi-agent
  clusters with different virtual IPs.
- ClusterManagement: limit displayed agents to 4 with "+N more"
  indicator to prevent row height explosion in large clusters.
- ClusterManagement: make keepalive IP search case-insensitive.
- Dashboard: add fetchAgentsStatus to overview tab auto-refresh
  (60s interval) so keepalive state changes are reflected without
  manual refresh.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-18 20:41:17 +03:00
taylanbakircioglu 4082152312 fix: resolve stale keepalive state when keepalived stops
- Fix DB: COALESCE(NULLIF) prevented clearing stale MASTER/BACKUP state.
  Use CASE WHEN IS NOT NULL to distinguish absent fields (old agents)
  from empty fields (keepalived stopped) and properly clear to NULL.
- Fix Redis: explicitly delete cache key when keepalived stops instead
  of relying on 90s TTL expiry, preventing stale dashboard data.
- Fix agent scripts: SKIP_TO_DAEMON send_heartbeat now always sends
  keepalive_state/keepalive_ip fields (even empty) so backend can
  detect stopped keepalived and clear stale data.
- Fix legacy heartbeat endpoint: POST /{agent_id}/heartbeat now also
  updates keepalive_state and keepalive_ip fields.
- UI: add horizontal scroll to ClusterManagement table to prevent
  column overflow with new Keepalive column.
- UI: add VIP tooltip to Dashboard AgentStatusCard keepalive tag.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-18 20:41:17 +03:00
taylanbakircioglu b0feca3c2e feat: add keepalived VRRP state (MASTER/BACKUP) detection to agent heartbeat
- Add keepalive_state and keepalive_ip columns to agents table (migration + schema)
- Add keepalive fields to AgentHeartbeat Pydantic model (backward compatible)
- Update heartbeat endpoint to persist keepalive data to DB and cache in Redis
- Add multi-method keepalived detection in agent scripts (journalctl, log files, VIP check)
- Update dashboard-stats agents/status API with Redis-first keepalive lookup
- Update GET /api/agents to include keepalive_state and keepalive_ip
- Show MASTER/BACKUP tag in Dashboard AgentStatusCard
- Show keepalive info in Agent Management registered agents table
- Add Keepalive column to Cluster Management table with VIP search support

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-18 20:41:17 +03:00
taylanbakircioglu eabbb2a987 fix: SSL page header layout - prevent Add Certificate button wrapping
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 16:36:27 +03:00
taylanbakircioglu 0b40d7b8a2 refactor: replace Expiring Soon alert banner with hover tooltip
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 16:36:27 +03:00
taylanbakircioglu 1f100d01e7 fix: SSL reject rollback, expiry calculation, and UI improvements
- SSL Reject: Remove conditional has_same_update_applied check, always
  rollback since Auto-Reject handles cross-cluster consistency
- SSL Rollback: Restore expiry_date by parsing ISO string back to datetime
- SSL UI: Add In Use filter toggle, fix Expiring Soon count using actual
  date calculation, smart Private Key content validation messages
- Remove emojis from log messages

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 16:36:27 +03:00
taylanbakircioglu 6c8d771c3a fix: restrict INCOMPLETE status to SSL only, remove emojis from code
- Timeout/INCOMPLETE logic now applies only to ssl_certificates entity type
- Non-SSL entities (frontend, backend, waf) retain original APPLYING behavior
- Remove all emoji characters from console.log and UI strings

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 16:36:27 +03:00
taylanbakircioglu 7dedd0f573 feat: SSL sync status tracking and smart reload optimization
- Add per-cluster SSL sync status tracking with APPLYING/SYNCED/PARTIAL states
- Add timeout detection (10min) for stalled SSL deployments showing PARTIAL status
- Add SSL-specific info in Apply Management dialog (cluster count, auto-apply notice)
- Add Deployment Status tab in SSL details showing per-cluster agent sync progress
- Implement SSL auto-reject (reject propagates to all clusters) and auto-undo
- Add conditional entity rollback for SSL reject (time-window based safety check)
- Filter REJECTED status from SSL last_config_status display
- Add pending_cluster_names to SSL list API response
- Optimize agent SSL reload: only trigger HAProxy reload when changed cert is
  actually referenced in the cluster's HAProxy config (grep -qF check), preventing
  unnecessary reloads on clusters that don't use the updated certificate
- Applied to all 4 SSL deploy paths: Linux/macOS standalone and daemon embedded modes

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 16:36:27 +03:00
taylanbakircioglu 9b8016588c feat: show SSL certificate usage status (In Use / Not In Use)
Replace the always-Active status badge with actual usage information by
querying which frontends and backend servers reference each certificate.

Backend: detail endpoint returns used_by_frontends, used_by_servers and
usage_count; list endpoint includes usage_count via scalar subqueries
checking both ssl_certificate_id and ssl_certificate_ids JSONB fields.

Frontend: General Info badge shows In Use/Not In Use, new Usage tab with
searchable frontend and server tables, summary card shows In Use count.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 16:36:27 +03:00
taylanbakircioglu 45d5919dc2 fix: resolve stale closure in agent actions after cluster change
- Remove useMemo from table columns to prevent stale closures for
  upgradeAgent, toggleAgent, deleteAgent after cluster switches
- Add selectedClusterRef and fetchAgentsRef for safe async access
  in setTimeout callbacks (upgrade delayed refresh)
- Force-fetch (bypass throttle) after all user actions: toggle,
  refresh button, reset scripts, update version
- Remove unused useMemo import

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 20:37:49 +03:00
taylanbakircioglu 4002c89d58 fix: agent list not loading on cluster change due to throttle race condition
- Combine cluster change clear + fetch into single useEffect to prevent
  race between clearing agents and throttled fetch being skipped
- Force-fetch agents on cluster change (bypass throttle and loading guard)
- Use loadingRef to avoid stale closure in fetchAgents useCallback
- Reset throttle timer on cluster change so fetch is never blocked

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 20:37:49 +03:00
taylanbakircioglu e6142a9a9c feat: add search capability to cluster selector dropdown
Enable showSearch on the header cluster selector so users can quickly
filter clusters by name, pool, connection type or description.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 16:49:59 +03:00
taylanbakircioglu 4c7a6d79f3 fix: apply sync for empty config, UI responsiveness improvements
- Fix backend cleanup order: delete backend_servers before backends
  to prevent orphan records and foreign key constraint violations
- Fix Apply progress getting stuck at "0/55" when all entities deleted
  by using verifyRealAgentSync result for accurate syncedCount
- Add completion condition for "all entities deleted" scenario
- Fix cluster selector truncation with dynamic width calculation
- Fix sidebar menu label truncation: increase sider width to 240px,
  use concise menu labels, add CSS overflow handling
- Add responsive breakpoints for header title and content padding
- Auto-collapse/expand sidebar on breakpoint change

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 16:45:29 +03:00
taylanbakircioglu 6349f1ad7c fix: exclude disabled (OFF) agents from sync calculations
Disabled agents were blocking apply sync progress indefinitely because
they were counted in total_agents but could never report as synced.

Backend:
- agent-sync endpoint: disabled agents excluded from total/synced/unsynced
  counts, added disabled_agents and total_agents_including_disabled fields
- SSL cert agent-sync endpoint: same disabled agent exclusion
- Both endpoints still return disabled agents in the list with
  sync_excluded: true for UI display

Frontend:
- ApplyManagement: cluster sync complete logic handles 0 enabled agents,
  all updateEntityCounts calls pass disabled count, agent table shows
  OFF/Excluded tags for disabled agents
- GlobalProgress: shows "(X off)" indicator, visible even when all
  agents are disabled
- ProgressContext: agentCounts state supports disabled field
- agentSync utility: verifyRealAgentSync treats null/0-agent sync_status
  as synced (nothing to wait for)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 16:45:28 +03:00
taylanbakircioglu 946b1a8e84 fix: Default Backend select filterOption crash on search
Fixed TypeError "t.children.toLowerCase is not a function" when searching
in Default Backend dropdown after editing a frontend.

Root cause: option.children was a React element array (multiple JSX parts),
not a string, so toLowerCase() failed.

Solution: Added label prop to Option and use optionLabelProp="label" pattern
(consistent with SSL Certificates Select in same file).

Changes:
- Added optionLabelProp="label" to Select component
- Added label={backendLabel} to each Option
- Changed filterOption to use option.label instead of option.children
- Users can now search by backend name, server count, or "No servers"
2026-01-28 16:04:42 +03:00
taylanbakircioglu 8076f3fcfd 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.
2026-01-26 15:25:15 +03:00
taylanbakircioglu 0c1d68eb01 feat: add uninstall script UI with modern design
- Add new API endpoint to serve uninstall scripts by platform
- Display uninstall script alongside install script in setup wizard
- Add dedicated delete agent modal with 2-step workflow
- Modern UI with gradient banners, platform icons, and info cards
- Enhanced uninstall scripts to clean all agent temp/backup files
- HAProxy service and config remain untouched during uninstall
2026-01-26 15:25:15 +03:00
taylanbakircioglu 5d054f3426 feat: Add random and first balance methods support
- Add 'random' and 'first' options to backend balance method selector
- Add balance method validation in config parser with warning for unknown methods
- Update API documentation with all supported balance algorithms
2026-01-26 15:25:15 +03:00
taylanbakircioglu bc8563d455 fix: Update agent token association on config change and improve Security UI
- Fix token-agent relationship not updating when agent config changes
- Agent's api_key now syncs with DB on heartbeat when using different token
- Change Security page badge color from red to blue for better UX
2026-01-26 15:25:15 +03:00
taylanbakircioglu 86c7b3a57f feat: Display HAProxy versions with agent names in Cluster Management
- Add HAProxy Version column to cluster list table
- Show version number with agent names directly (no hover required)
- Fetch agents for each cluster to get haproxy_version info
- Show warning icon (yellow) when agents have different HAProxy versions
- Green color for consistent versions, yellow for mismatched versions
- Agent names displayed below each version in smaller gray text
- UI-only change, no backend modifications
- Safe implementation using existing /api/agents endpoint
2025-12-26 15:26:46 +03:00
taylanbakircioglu 81d2b3b8d5 feat: Display HAProxy version in Agent Management page
- Add haproxy_version field to heartbeat payload in Linux agent script
- Add haproxy_version field to heartbeat payload in macOS agent script
- Display HAProxy version below IP address in Registered Agents list
- Safe extraction with fallback to 'unknown' if haproxy command fails
- Version is updated on every heartbeat (30s interval)
- Green color styling for easy visibility

Backend already supports haproxy_version field in AgentHeartbeat model
and saves it to database on each heartbeat.
2025-12-23 13:28:29 +03:00
Taylan Bakırcıoğlu ac5ba4dc58 fix(ssl): add SSL advanced options to GET /api/frontends SELECT queries
CRITICAL BUG FIX: SSL advanced options were not being returned by GET API

Problem:
- Database has ssl_alpn, ssl_npn, ssl_ciphers, etc. columns 
- Response builder tries to access them (Line 341-347) 
- BUT SELECT statements did NOT include these fields 
- Result: f.get('ssl_alpn') returned None for all frontends

Impact:
- Frontend Edit modal always showed empty SSL advanced options fields
- User edits would overwrite existing values with NULL
- Data loss on every frontend edit!

Solution:
- Added all 7 SSL advanced options to ALL 6 SELECT queries:
  1. cluster_id filter (line 174)
  2. cluster_id fallback (line 190)
  3. global include_inactive=True (line 219)
  4. global include_inactive=False (line 231)
  5. global fallback include_inactive=True (line 246)
  6. global fallback include_inactive=False (line 258)

Testing:
- Added debug console.log for SSL advanced options (line 551-559)
- After deployment, check browser console for 'SSL ADVANCED OPTIONS DEBUG'
- Should now show: ssl_alpn: 'h2,http/1.1' etc.

Files Changed:
- backend/routers/frontend.py: All 6 SELECT statements
- frontend/src/components/FrontendManagement.js: Debug logging
2025-11-18 21:58:05 +03:00
Taylan Bakırcıoğlu cb645b5ef9 CRITICAL FIX: Agent Offline Issue - Backend Tolerance for Legacy Agents
🔴 PRODUCTION CRITICAL FIX - Agent HTTP 422 Validation Error

PROBLEM:
- Production agents sending heartbeat with flat system_info fields
- Backend Pydantic model was strict and rejecting unknown fields
- Agents going offline with 'Validation error in request data' (HTTP 422)

ROOT CAUSE:
- Legacy agents embed system_info as flat key-value pairs in heartbeat JSON
- Backend expected only defined fields, rejected extra fields
- No backward compatibility for agent format variations

SOLUTION - BACKEND ONLY (NO AGENT CHANGES):
 Added 'extra = "allow"' to AgentHeartbeat Pydantic Config
 Backend now accepts both formats:
   - Flat format: operating_system, kernel_version, etc. (legacy agents)
   - Nested format: system_info: {...} (future agents)
 Updated comments to clarify backward compatibility

IMPACT:
-  ZERO CHANGES to production agent scripts
-  Existing agents will work immediately after backend deploy
-  Forward compatible with future agent upgrades
-  Tolerant to agent format variations

SAFETY:
- Minimal change (3 lines)
- Pydantic still validates required fields
- Extra fields ignored silently (no breaking changes)
- Production agents continue without restart or upgrade

DEPLOYMENT:
1. Deploy backend (this commit)
2. Agents come online automatically (no action needed)
3. Agent upgrades can happen later (when convenient)

This fix ensures production stability without touching agent scripts.
2025-11-18 21:58:05 +03:00
taylanbakircioglu e0fb7180ae fix: Agent heartbeat cluster-pool auto-healing + global token support
MAIN BUG FIX:
- Agent offline issue resolved (cluster created before pool scenario)
- 2-method cluster lookup: pool_id -> cluster_id fallback
- Auto-healing: pool_id NULL automatically corrected on first heartbeat

SECURITY & VALIDATION:
- Removed pool-based security check (token is globally usable)
- Pool-cluster validation for new agents (frontend + backend)
- Relaxed validation for agent upgrades (fallback pool_id tolerated)

AGENT IMPROVEMENTS:
- HTTP error logging in agent scripts (curl status code check)
- Detailed backend error response logging
- Better troubleshooting capabilities

PRODUCTION SAFE:
- Backward compatible (no breaking changes)
- Existing agents unaffected (Method 1 priority)
- Agent upgrades work (relaxed validation)
- Global token model preserved (cross-pool usage OK)
2025-11-17 20:20:09 +03:00
Taylan Bakırcıoğlu c75b5572d9 fix: Change validation strategy - soft warnings instead of hard blocks
PHILOSOPHY: Don't block what HAProxy allows - guide users instead

Changes:
1. Frontend → Backend (server-less): REMOVED validation, ADDED warning
2. WAF → Frontend: KEPT validation (risk of global apply)

Problem with Previous Approach:
- Backend validation too strict
- Blocked valid HAProxy configs
- Bulk import would fail
- Users confused: "Why can't I do this?"

HAProxy Validation Standards:
- Frontend → Backend without servers: VALID (backend DOWN, 503 errors)
- Frontend without default_backend: VALID (ACL-only routing)
- WAF without frontend: INVALID (applies to ALL frontends - dangerous!)

Solution Part 1: Frontend Validation REMOVED

frontend.py CREATE + UPDATE:
- REMOVED: Backend server check validation
- Reason: HAProxy allows this (valid syntax)
- User can: Create frontend, assign backend, add servers later
- Production: Traffic gets 503 until servers added (acceptable)

frontend.py Changes:
- Lines 408-428: REMOVED backend server validation (CREATE)
- Lines 650-670: REMOVED backend server validation (UPDATE)
- Result: No API blocking, user has full control

Solution Part 2: Frontend UI Warning ADDED

FrontendManagement.js (lines 1402-1443):
BEFORE:
- Backend dropdown: Shows server count
- No warning when selecting server-less backend
- User unaware of consequences

AFTER:
- Tooltip: "Optional: Leave empty for ACL-only routing"
- Placeholder: "Select default backend (optional)"
- onChange handler: Checks server count
- Warning message if no servers:
  "Warning: Backend 'X' has no active servers. Traffic will receive 503 errors until you add servers."
- Visual indicator: Red text for backends without servers
- Display: "backend-name (0 servers) - No servers"

Solution Part 3: WAF Validation KEPT

waf.py CREATE (lines 418-426):
- KEPT: Frontend selection required
- Reason: WAF without frontend = applies to ALL frontends
- Risk: Unintentional global WAF application
- Too dangerous to allow without explicit user action

waf.py UPDATE (lines 676-683):
- KEPT: Cannot remove all frontend assignments
- Reason: Same risk as CREATE

User Experience:
FRONTEND:
- Before: HTTP 400 error, blocked
- After: Warning message, allowed
- Message: Clear consequences, user decides

WAF:
- Before: HTTP 400 error, blocked
- After: Same (still blocked - too risky)
- Reason: Global apply risk too high

Bulk Import Impact:
- Before: Would FAIL on frontends with server-less backends
- After: SUCCESS - no validation blocking
- Result: Bulk import works smoothly

Test Scenarios:
1. Create frontend with server-less backend:
   - API: SUCCESS (no validation)
   - UI: WARNING shown (6 seconds)
   - User: Can proceed with awareness

2. Select backend without servers:
   - Dropdown: Shows "backend (0 servers) - No servers" in RED
   - OnChange: Warning message appears
   - Submit: Allowed

3. Create WAF without frontend:
   - API: HTTP 400 (blocked)
   - UI: Required field validation
   - Reason: Too risky to allow

4. Bulk import with server-less backends:
   - API: SUCCESS (no blocking)
   - Frontends created successfully
   - Users can add servers later

Philosophy:
- HAProxy validation = source of truth
- Backend validation = guidance, not blocking
- Dangerous operations = strict validation (WAF global apply)
- User empowerment = soft warnings with clear info

Related: bcb8ef0 (backend without servers), 595d981 (WAF validation)
Refs: #validation-strategy #soft-warnings #bulk-import #ux
2025-11-17 14:15:44 +03:00
Taylan Bakırcıoğlu 7d3eeebcb7 fix(waf): Require frontend selection with backend + frontend validation
COMPLETE UX FIX: Backend validation + Frontend required field

Changes Summary:
1. Backend API validation (waf.py)
2. Frontend API validation (frontend.py)
3. Frontend UI required field (WAFManagement.js)

Problem:
- User creates WAF without selecting frontends
- Backend applies WAF to ALL frontends (unintentional)
- No visual indication that frontend selection is required
- User confused about where WAF is applied

Solution - Part 1: Backend API Validation

waf.py CREATE (lines 418-426):
- Validate frontend_ids not empty
- HTTP 400 if no frontends selected
- Error: "At least one frontend must be selected"

waf.py UPDATE (lines 686-693):
- Validate if frontend_ids explicitly provided
- HTTP 400 if trying to clear all frontends
- Allow config-only updates (preserve frontends)

Solution - Part 2: Frontend Validation

frontend.py CREATE (lines 408-428):
- Validate backend has active servers
- HTTP 400 if backend has no servers
- Error: "Backend has no active servers. Add servers first."

frontend.py UPDATE (lines 650-670):
- Same validation when changing default_backend
- Prevent routing to DOWN backends

Solution - Part 3: Frontend UI (User Experience)

WAFManagement.js (lines 1473-1508):
BEFORE:
- Label: "Target Frontends"
- Tooltip: "Can be left empty for globally available WAF"
- Placeholder: "Select frontends"
- No validation
- Optional field appearance

AFTER:
- Label: "Target Frontends" (with red asterisk)
- Required validation rules:
  * Antd required: true
  * Custom validator: at least 1 frontend
- Placeholder: "Select frontends (Required *)"
- Tooltip: "At least one frontend is required"
- Search enabled for easy filtering
- Error messages:
  * "Please select at least one frontend"
  * "At least one frontend must be selected for WAF rule"

User Experience Improvements:
1. Visual indication: Red asterisk on label
2. Clear placeholder text: "(Required *)"
3. Helpful tooltip: Explains requirement
4. Client-side validation: Immediate feedback
5. Server-side validation: Safety net
6. Searchable dropdown: Easy to find frontends
7. Clear error messages: User knows what to do

Test Scenarios:
1. Create WAF without selecting frontend:
   - UI: Red error "Please select at least one frontend"
   - Submit blocked (client-side)

2. Bypass client-side, try API:
   - API: HTTP 400 "At least one frontend must be selected"

3. Create frontend with server-less backend:
   - UI: Can select backend
   - API: HTTP 400 "Backend has no active servers"

4. Update WAF remove all frontends:
   - UI: Red error message
   - API: HTTP 400 if bypassed

Related: bcb8ef0 (backend without servers)
Refs: #waf-validation #frontend-validation #ux-improvement
2025-11-17 14:15:44 +03:00
Taylan Bakırcıoğlu 60b77734a5 debug(frontend): Add comprehensive debug logs for Apply/Reject operations
CRITICAL DEBUG: Track down why backend stays in pending after Apply/Reject

Problem:
- User reports backend 'deneme-sil' remains in Apply Management
- Apply All and Reject All both fail to remove it
- Console shows 'Filtered pending backends: 1' but backend not visible

Debug Logs Added:
1. fetchPendingChanges():
   - Log ALL backends from API (not just 3 specific ones)
   - Show: id, name, cluster_id, last_config_status, has_pending_config, is_active
   - Log ALL pending backends after filtering

2. executeApplyAll():
   - Log pending changes state before apply
   - Log backend details being applied
   - Log apply API response
   - Log data refresh events

3. executeRejectAll():
   - Log pending changes state before reject
   - Log backend details being rejected
   - Log reject API response
   - Log data refresh events

Expected Output:
- [ALL BACKENDS FROM API]: Shows all 4 backends including hidden one
- [PENDING BACKENDS DETAILS]: Shows which backend has has_pending_config=true
- [Backend Details Being Applied/Rejected]: Shows backend state during operation
- [APPLY/REJECT RESPONSE]: Shows API response
- [DATA REFRESHED]: Confirms data reload completed

This will help identify:
- Is backend in API response? (hidden or missing)
- What is backend's actual state? (last_config_status, has_pending_config, is_active)
- Does Apply/Reject API call succeed?
- Does backend state change after apply/reject?

Refs: #debug #apply-management #pending-backend
2025-11-17 14:15:44 +03:00
Taylan Bakırcıoğlu 8131b0b18c fix(frontend): Handle null/undefined servers array in BackendServers component
CRITICAL FIX: Backends without servers were not visible in UI due to missing null/undefined checks

Problem:
- Backend tanımı yaparken server eklenmezse, frontend'de görünmüyordu
- servers.filter() ve servers.length çağrıları undefined/null servers array'inde crash veriyordu
- Bu crash nedeniyle backend satırı render edilmiyordu

Solution:
- Servers kolonu render: servers = [] default parameter + null/Array.isArray kontrolü
- Expandable row render: const servers = record.servers || [] defensive check
- renderServerList: flatMap içinde servers || [] kontrolü
- Boş backend'ler için ⚠️ Empty tag ve tooltip eklendi

Impact Analysis:
 Sync Status: ETKİLENMEDİ (EntitySyncStatus props'ları servers'a bağımlı değil)
 Config Status: ETKİLENMEDİ (has_pending_config backend'de hesaplanıyor, servers'a bağımsız)
 Tüm diğer kolonlar: ETKİLENMEDİ (servers field'ına bağımlı değil)
 Backend API: DEĞİŞMEDİ (servers her zaman array döndürüyor)

Testing:
- Backend API'den servers her zaman array geliyor (boş olabilir)
- Defensive programming ile cache/race condition/parse error durumlarına karşı korundu
- UI artık boş backend'leri 0/0 active ve ⚠️ Empty tag ile gösterecek

Refs: #backend-visibility-bug #defensive-programming
2025-11-17 14:14:26 +03:00
taylanbakircioglu 8a24d22e5d debug: Add console logs for tcp_request_rules in frontend edit modal
Added debug logging to track tcp_request_rules value when frontend edit modal opens.
This will help diagnose why tcp_request_rules from bulk import are not appearing in the edit form.
2025-11-14 01:06:38 +03:00
taylanbakircioglu 1276725f45 fix: Frontend modal - preserve tcp_request_rules, redirect_rules and use-service on update
🐛 Bug Düzeltmeleri:

1. tcp_request_rules field'ı edit modal'da gösterilmiyordu
   - handleEdit() setFieldsValue'ya tcp_request_rules eklendi
   - Artık DB'deki TCP rules edit modal'da görünüyor

2. use-service header'ları (prometheus-exporter gibi) bulk import'ta siliniyordu
   - config.py bulk_create_entities'e merge stratejisi eklendi
   - Mevcut use-service direktifleri parse edilip yeni header'larla birleştiriliyor
   - Manuel eklenmiş servisler korunuyor

3. redirect_rules field'ı form'da hiç yoktu (yeni bulgu!)
   - ACL Rules panel'ına redirect_rules textarea eklendi
   - HTTP to HTTPS redirect kuralları için kullanılabilir

 Diğer modaller kontrol edildi:
   - Backend modal: Tam ✓
   - Server modal: Tam ✓
   - SSL modal: Tam ✓
   - WAF modal: Tam ✓
2025-11-14 01:06:38 +03:00
taylanbakircioglu 01ae508d39 feat: Line-by-line diff for multi-line fields + rollback merge strategy
USER FEEDBACK SUMMARY:
1. "Old/New görünümü karışık, sadece yeni eklenen görünsün"
2. "Merge logic silme işlemini engelliyor, geri al"

CHANGES OVERVIEW:
 Backend: Rolled back merge strategy (prevents deletion issue)
 Frontend: Added line-by-line diff renderer (cleaner UX)

═══════════════════════════════════════════════════════════

PART 1: BACKEND ROLLBACK (config.py)
═══════════════════════════════════════════════════════════

REMOVED: merge_multiline_field() function
REASON: Merge strategy was additive-only, prevented deletion

OLD BEHAVIOR (MERGE):
DB:     "option http-keep-alive"
Config: "option forwardfor"
Result: "option http-keep-alive\noption forwardfor"  ← BOTH kept!
Issue:  User CANNOT delete http-keep-alive 

NEW BEHAVIOR (REPLACE):
DB:     "option http-keep-alive"
Config: "option forwardfor"
Result: "option forwardfor"  ← Old deleted! ✓
Works:  Deletion and addition both work ✓

ROLLBACK DETAILS:
- Removed merge_multiline_field() function (lines 30-71)
- Restored simple comparison for frontend fields:
  * request_headers: simple != comparison
  * response_headers: simple != comparison
  * options: simple != comparison
  * tcp_request_rules: simple != comparison

- Restored simple comparison for backend fields:
  * request_headers: simple != comparison
  * response_headers: simple != comparison
  * options: simple != comparison

USER CONFIRMATION:
"aslında bu senaryoyu denedim. entitiy'de option http-keep-alive
varken option forwardfor'ı da ekledim. Confirm & Create Entitiy
dedikten sonra oluşan versiyon sadece yeni eklenen option forwardfor'u
dahil edecek şekilde oluştu. Yani sağlıklı çalıştı ve önceki option'u
kaldırmadı."

ANALYSIS: User's config ALREADY HAD both options!
Parser extracted: "option http-keep-alive\noption forwardfor"
No data loss occurred because BOTH were in the imported config.

═══════════════════════════════════════════════════════════

PART 2: FRONTEND LINE-BY-LINE DIFF (BulkConfigImport.js)
═══════════════════════════════════════════════════════════

NEW FEATURE: Smart line-by-line diff rendering

PROBLEM (User's Example):
DB:     "option http-keep-alive"
Config: "option http-keep-alive\noption forwardfor"

Old Preview (Confusing):
  Old: option http-keep-alive
  New: option http-keep-alive
       option forwardfor
  → User can't see what changed! 

New Preview (Clear):
  Added:
  + option forwardfor
  → Only changes shown! ✓

IMPLEMENTATION:

1. calculateLineDiff() Helper Function:
   - Splits old/new into lines
   - Trims whitespace and filters empty lines
   - Uses Set comparison for efficient diff
   - Returns: { added: [], removed: [], unchanged: [] }

   Algorithm:
   oldSet = Set(oldLines)
   newSet = Set(newLines)
   added = newLines.filter(line => !oldSet.has(line))
   removed = oldLines.filter(line => !newSet.has(line))

2. MultiLineDiffRenderer Component:
   - No changes: Shows plain text
   - Has changes: Shows diff boxes
   - Removed lines: Red box with - prefix
   - Added lines: Green box with + prefix
   - Unchanged lines: HIDDEN (per user request!)

APPLIED TO 7 FIELDS:

Frontend Entity (4 fields):
✓ frontend.options               (line 1097)
✓ frontend.request_headers        (line 1116)
✓ frontend.response_headers       (line 1135)
✓ frontend.tcp_request_rules      (line 1154)

Backend Entity (3 fields):
✓ backend.options                 (line 1228)
✓ backend.request_headers         (line 1248)
✓ backend.response_headers        (line 1268)

EDGE CASES HANDLED:
✓ Both null: No crash, returns empty arrays
✓ Old null: Shows all as added (green)
✓ New null: Shows all as removed (red)
✓ Empty lines: Filtered out before comparison
✓ Whitespace: Trimmed before comparison
✓ Duplicates: Set ensures unique comparison

VISUAL DESIGN:
- Removed box: #fff1f0 bg, #ff4d4f border, red text
- Added box: #f6ffed bg, #52c41a border, green text
- Spacing: 8px margin between boxes
- Font: 11px code font for readability
- Labels: 10px secondary text ("Removed:", "Added:")

USER FEEDBACK:
"neden old gösteriyor ki aslında sadece New: option forwardfor
gösterse daha doğru olmaz mı?"

RESPONSE: Absolutely! Removed unchanged section completely.

═══════════════════════════════════════════════════════════

TEST SCENARIOS:
═══════════════════════════════════════════════════════════

Scenario 1: Addition Only
Old: "option http-keep-alive"
New: "option http-keep-alive\noption forwardfor"
Result: Shows "+ option forwardfor" (green) ✓

Scenario 2: Deletion Only
Old: "option http-keep-alive\noption forwardfor"
New: "option http-keep-alive"
Result: Shows "- option forwardfor" (red) ✓

Scenario 3: Replacement
Old: "option http-keep-alive"
New: "option forwardfor"
Result: Shows "- option http-keep-alive" (red)
        and "+ option forwardfor" (green) ✓

Scenario 4: No Change
Old: "option http-keep-alive"
New: "option http-keep-alive"
Result: Plain text, no diff boxes ✓

Scenario 5: Empty Lines & Whitespace
Old: "  option http-keep-alive  \n\noption forwardfor"
New: "option http-keep-alive\noption forwardfor"
Result: No diff (trimmed and filtered) ✓

═══════════════════════════════════════════════════════════

SYSTEMATIC VERIFICATION COMPLETED:
═══════════════════════════════════════════════════════════

Backend Analysis:
✓ merge_multiline_field removed
✓ No merge logic in frontend comparison
✓ No merge logic in backend comparison
✓ Simple != comparison restored
✓ Deletion works correctly

Frontend Analysis:
✓ calculateLineDiff helper correct
✓ MultiLineDiffRenderer renders correctly
✓ Unchanged section removed
✓ 7 fields updated (3 backend + 4 frontend)
✓ Props passed correctly
✓ Edge cases handled
✓ No debug code left
✓ Badge colors consistent

No Side Effects:
✓ Backend edit modal: Still works
✓ Version diff: No impact
✓ Apply management: No impact
✓ Bulk create endpoint: No changes
✓ Other fields: Unaffected

BACKWARD COMPATIBILITY: ✓
PERFORMANCE: No performance impact (Set operations O(n))
CODE QUALITY: Clean, DRY, maintainable
2025-11-13 10:12:30 +03:00
taylanbakircioglu 82a88ee244 fix: Apply inline rendering to frontend fields (same empty string issue)
USER QUESTION: "backend içinde aynı durum olabilir mi?"
ANSWER: Yes! Frontend entity has the same issue.

ISSUE SCOPE EXPANDED:
The empty string oldValue problem affects ALL multi-line text fields:
- Backend entity: ✓ Already fixed (options, request_headers, response_headers)
- Frontend entity:  Still using FieldChange component (same bug!)

FRONTEND FIELDS AFFECTED:
- Frontend Options (line 984)
- Request Headers (line 1001)
- Response Headers (line 1018)
- TCP Request Rules (line 1035)

All use FieldChange component → All fail with oldValue = '' (empty string)

SOLUTION: Replace FieldChange with inline rendering for frontend

Applied same pattern as backend:
1. Frontend Options:
   - Inline diff rendering
   - Badge: NEW/CHANGED based on old value
   - Old box: Only if old has content (skips empty string)
   - New box: Always shown with green styling

2. Request Headers, Response Headers, TCP Request Rules:
   - Same inline pattern
   - Badge: CHANGED (orange)
   - Graceful empty string handling

CONSISTENCY:
✓ Backend fields: inline rendering
✓ Frontend fields: inline rendering
✓ All text fields handle empty string correctly
✓ No more FieldChange component issues

EDGE CASE HANDLING:
- oldValue = '' → Old box not shown, NEW badge
- oldValue = null → Old box not shown, NEW badge
- oldValue = 'content' → Old box shown, CHANGED badge

RESULT: Complete fix across both backend and frontend entities
2025-11-13 10:12:30 +03:00
taylanbakircioglu 969259b7de debug: Add systematic debugging for empty Backend Options field
ISSUE: Backend Options field appears EMPTY even though:
- ✓ Confirm & Create generates correct version with option
- ✓ Cookie Options field works (same component type)
- ✓ Request Headers field works (same ternary pattern)
-  Backend Options field: Label visible, content empty

SYSTEMATIC ROOT CAUSE ANALYSIS:

Checkpoint 1: Parser adds options field ✓
- Line 906 in config.py: 'options': backend.options
- Parser correctly extracts 'option http-keep-alive' from config

Checkpoint 2: _changes object created ✓
- Line 1125-1127: Compares with existing backend
- Line 1138: backend['_changes'] = changes
- If options changed: changes['options'] = {old: None, new: 'option...'}

Checkpoint 3: API response includes _changes ✓
- Line 1189: return {'backends': backends_data}
- backends_data contains backend dicts with _changes key

Checkpoint 4: Frontend render logic
- HYPOTHESIS: backend.options exists but falsy?
- HYPOTHESIS: _changes object not reaching frontend?
- HYPOTHESIS: Ternary operator short-circuit issue?

SIMULATION:
Scenario A (_changes exists):
  backend._changes.options = {old: null, new: 'option...'}
  → backend._changes?.options → truthy
  → FieldChange renders
  → hasOldValue = true, hasChange = true
  → Should display with NEW badge ✓

Scenario B (_changes missing):
  backend._changes = undefined
  → backend._changes?.options → undefined (falsy)
  → Descriptions.Item with NEW badge renders
  → Should display green text ✓

Both scenarios SHOULD work!

SOLUTION: Detailed Debug Logging + Robust Fallback

Changed from ternary to IIFE (Immediately Invoked Function Expression):
1. console.group() for organized logging
2. Logs: field value, type, length, _changes object, render decision
3. Clear if-else (no ternary ambiguity)
4. Early return if no options field
5. Explicit FieldChange vs Descriptions.Item branches

NEXT STEP: Test and inspect browser console
- Check: Is backend.options present? What's its value/type?
- Check: Is backend._changes present? What's its structure?
- Check: Which branch is rendered?
- Result: Pinpoint exact cause (data or rendering)

RATIONALE FOR THIS APPROACH:
- Avoids trial-and-error (user's concern)
- Provides observable data points
- Guarantees one of two render paths executes
- Maintains fallback for both _changes presence/absence scenarios
2025-11-13 10:12:30 +03:00
taylanbakircioglu d07388dc97 fix: Fallback rendering for fields without change tracking + TODO for reject bug
PRIMARY FIX: Empty Backend Options / Request Headers display issue

ISSUE:
- Backend Options field: Empty (even though data exists)
- Request Headers field: Empty (even though data exists)
- Cookie Options field: Works  (using old Descriptions.Item)

ROOT CAUSE:
FieldChange component requires _changes object to render.
If _changes is undefined/null, component shows nothing.

SOLUTION: Dual render pattern
1. IF _changes exists → Show FieldChange (with diff visualization)
2. IF _changes missing → Show plain Descriptions.Item (fallback)

Pattern:
{field && entity._changes?.field && (
  <FieldChange ... />  // Show diff
)}
{field && !entity._changes?.field && (
  <Descriptions.Item ... />  // Fallback display
)}

Applied to:
- Backend: options, request_headers, response_headers
- Frontend: options, request_headers, response_headers, tcp_request_rules

RESULT:
 Fields always visible (even without change tracking)
 Diff shown when _changes available
 Graceful degradation when _changes missing
2025-11-13 10:12:30 +03:00
taylanbakircioglu 4c9ee88549 feat: Add field-level change visualization in bulk import preview
UX IMPROVEMENT: Users can now see exactly what changed in existing entities.

BACKEND CHANGES (config.py):
- Parse endpoint now tracks field-level changes for frontends and backends
- Added '_changes' object to each entity containing old vs new values
- Format: { 'field_name': { 'old': value, 'new': value } }
- Applied to ALL updatable fields:
  * Frontend: bind_address, bind_port, mode, timeouts, headers, options, etc.
  * Backend: balance_method, mode, health_check, timeouts, headers, options, etc.
- Only includes fields that actually changed (empty object if no changes)

FRONTEND CHANGES (BulkConfigImport.js):
- Created FieldChange component for visual diff display
- Shows old value (strikethrough, red) vs new value (green highlight)
- Badge indicators: 'NEW' (green) or 'CHANGED' (orange)
- Applied to multi-line text fields:
  * Backend Options
  * Request Headers
  * Response Headers
  * Frontend Options
  * TCP Request Rules

UI DESIGN:
┌─────────────────────────────────────────────────┐
│ Backend Options              [NEW] ← Badge      │
├─────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────┐ │
│ │ Old: (crossed out, red background)          │ │
│ │ null                                        │ │
│ └─────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────┐ │
│ │ New: (green background)                     │ │
│ │ option http-keep-alive                      │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘

EXAMPLE SCENARIOS:

Scenario 1: New field added (like user's example)
- Backend: Elasticsearch
- Field: options
- Old: null → shown as red box with 'null' (crossed out)
- New: 'option http-keep-alive' → shown in green box
- Badge: 'NEW' (green)

Scenario 2: Existing field changed
- Backend: Elasticsearch
- Field: timeout_server
- Old: 30000 → shown in red box (crossed out)
- New: 60000 → shown in green box
- Badge: 'CHANGED' (orange)

Scenario 3: Multi-line text modified
- Backend: Elasticsearch
- Field: request_headers
- Old: 3 lines → shown in red box (all 3 lines crossed out)
- New: 5 lines → shown in green box (all 5 lines)
- Badge: 'CHANGED' (orange)
- Diff is clearly visible line by line

USER EXPERIENCE:
 Clear visual feedback: What was there before
 Clear visual feedback: What will be applied
 Color coding: Red (removed) → Green (added)
 Badge indicators: NEW vs CHANGED
 Works for multi-line content (preserves formatting)
 Only shows diff for fields that actually changed
 Parse preview now matches Apply Management diff view

Impact: Users can confidently review and approve bulk imports with full visibility into changes.
2025-11-13 10:12:30 +03:00
taylanbakircioglu 70b838a0c5 fix: Bulk import preview now shows accurate status (NEW/UPDATE/NO CHANGES)
CRITICAL FIX: Parse endpoint was marking ALL existing entities as UPDATE, even when no field values changed.

ROOT CAUSE:
- /parse-bulk endpoint only checked if entity exists in database
- If exists → _isUpdate = true (ALWAYS)
- Never compared field values to detect actual changes

SOLUTION:
Backend (config.py):
- Added field-by-field comparison logic to parse endpoint
- Mirrors the same comparison logic used in /bulk-create endpoint
- Compares ALL updatable fields: mode, balance, timeouts, headers, options, etc.
- Only sets _isUpdate = true if at least one field has changed
- Inactive entities being reactivated also count as changes

Frontend (BulkConfigImport.js):
- Changed status render for better UX clarity
- Before: _isUpdate=false showed '-' (confusing)
- After: Shows 'NO CHANGES' tag with tooltip explanation
- Applied to both frontend and backend tables

BEHAVIOR NOW:
1. Parse config → Compare with DB
2. If identical → Status: NO CHANGES (gray tag)
3. If different → Status: UPDATE (orange tag)
4. If new → Status: NEW (green tag)
5. Confirm & Create → Only applies actual changes

USER EXPERIENCE:
 First bulk import: Shows NEW or UPDATE correctly
 Apply changes
 Re-import same config: Shows NO CHANGES (not UPDATE)
 Clear visual feedback on what will actually change

Impact: Users can now trust bulk import preview. No more false positives for updates.
2025-11-13 10:12:30 +03:00
taylanbakircioglu 4272dbb4ef feat: Add 'option httpchk' validation and auto-filtering across all layers
CRITICAL FIX: Prevent 'option httpchk' duplication in HAProxy config by implementing 3-layer validation:

1. BULK IMPORT PARSER:
   - Frontend: Filter out 'option httpchk' with warning (not applicable to frontends)
   - Backend: Already filtering 'option httpchk' (handled by health_check_uri field)

2. BACKEND API:
   - Backend create/update: Auto-filter 'option httpchk' from options field
   - Frontend create/update: Auto-filter 'option httpchk' from options field
   - Added filter_httpchk_from_options() helper function in both routers

3. FRONTEND UI:
   - Backend modal: Real-time warning when 'option httpchk' is typed
   - Frontend modal: Real-time warning when 'option httpchk' is typed
   - Warning messages guide users to use proper fields instead

Changes:
- backend/utils/haproxy_config_parser.py: Added httpchk filtering for frontend parsing
- backend/routers/backend.py: Added filter function + applied to create/update
- backend/routers/frontend.py: Added filter function + applied to create/update
- frontend/src/components/BackendServers.js: Added dynamic warning for httpchk
- frontend/src/components/FrontendManagement.js: Added dynamic warning for httpchk

User Experience:
 Bulk Import: Automatically filters httpchk, shows warning in preview
 Manual Entry: Shows real-time warning, auto-filters on save
 No Config Duplication: 'option httpchk' never appears twice in generated config

Impact: Users can safely paste or type 'option httpchk' without breaking HAProxy config. System automatically filters it and guides users to use the Health Check URI field instead.
2025-11-13 10:12:30 +03:00
taylanbakircioglu 0cf038a428 fix: Improve backend edit modal options field and bulk import UX
Two key improvements for options field implementation:

1. Backend Edit Modal - Options Field Display:
   - Added explicit options field handling in handleEditBackend
   - Set options to empty string if null/undefined (prevents form field issues)
   - Added debug logging to track options field value
   - Now properly displays existing options value when editing backend

2. Bulk Import UX - Status Badge Enhancement:
   - Changed 'Existing' badge to 'UPDATE' with orange color (more visible)
   - Changed 'New' badge to 'NEW' (uppercase, consistent)
   - Updated tooltip text for better clarity
   - Frontend and Backend tables now use consistent status indicators

Technical Details:
- handleEditBackend now explicitly sets options field: options: backend.options || ''
- Status badges: NEW (green) vs UPDATE (orange) for better visual distinction
- Console debug logs added for troubleshooting options field issues
- SSL verify behavior preserved (none when certificate not in database)

Files Modified:
- frontend/src/components/BackendServers.js: Explicit options handling + debug
- frontend/src/components/BulkConfigImport.js: Enhanced status badges
- backend/routers/config.py: SSL handling cleanup
2025-11-13 10:12:30 +03:00
taylanbakircioglu 3e22776b8c fix: Improve options field UX and HAProxy config ordering
Fixed three critical issues with options field implementation:

1. Bulk Import Preview UI:
   - Added options field to frontend expandedRowRender display
   - Added options field to backend expandedRowRender display
   - Options now visible in preview before import confirmation

2. HAProxy Config Generator - Best Practice Ordering:
   Backend:
   - Moved options to position #2 (after mode/balance, before health checks)
   - New order: balance → mode → OPTIONS → httpchk → timeouts → cookie → headers

   Frontend:
   - Moved options to position #2 (after mode, before default_backend)
   - New order: bind → mode → OPTIONS → default_backend → timeouts → headers

3. UI Display Improvements:
   - Options now prominently displayed in bulk import preview
   - Better visual hierarchy with numbered comments in config generator
   - Consistent code style with proper whitespace handling

Technical Details:
- Frontend options placed after mode directive per HAProxy standards
- Backend options placed before health checks for better readability
- All options rendered as separate lines in preview
- hasDetails check updated to include options field

Files Modified:
- backend/services/haproxy_config.py: Config generation order optimized
- frontend/src/components/BulkConfigImport.js: Preview display enhanced
2025-11-13 10:12:30 +03:00
taylanbakircioglu 0fc18fde38 feat: Add HAProxy options support for backends and frontends
Implemented comprehensive HAProxy options field support for both backend and frontend entities to enable standard HAProxy directives like 'option http-keep-alive', 'option httplog', 'option forwardfor', etc.

Changes:
- Database: Added 'options' TEXT column to backends and frontends tables
- Models: Added options field to BackendConfig, BackendConfigUpdate, and FrontendConfig
- API Endpoints: Updated CREATE, UPDATE, and GET endpoints to handle options field
  * Backend: CREATE/UPDATE/GET with options support
  * Frontend: CREATE/UPDATE/GET with options support (fixed 5 SELECT queries)
- Config Generator: Added options block generation for both backends and frontends
- Bulk Import Parser:
  * Added options field to ParsedBackend and ParsedFrontend dataclasses
  * Implemented option directive parsing with validation
  * Added unknown option warnings
  * Fixed bulk parse response to include options field
- Bulk Import Merge: Added options field comparison in UPDATE logic
- UI Components:
  * BackendServers.js: Added options TextArea form field
  * FrontendManagement.js: Added options TextArea form field

Features:
- Multi-line options support (newline-separated format)
- Option validation with known HAProxy options list
- Backward compatible (NULL options for existing entities)
- Bulk import support with merge strategy
- Full CRUD support for both manual and bulk operations

Technical Details:
- Format: Newline-separated TEXT field for multiple options
- Validation: Warns about unknown options but allows them
- Config Generation: Each option written as separate directive
- Agent: Standard HAProxy config validation applies

Total: 10 files modified, ~195 lines added, 26 integration points verified
2025-11-13 10:12:30 +03:00