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.
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.
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
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
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
- Add configuration guide for environment variables
- Add agent upgrade guide
- Use example.com instead of company-specific URLs
- No sensitive information included
This is a comprehensive update that adds SSL certificate differentiation
for frontend (HAProxy bind) and server (backend verification) use cases.
FEATURES:
- SSL certificates can be marked as 'frontend' or 'server' usage type
- Frontend SSL: Private key REQUIRED (for HAProxy bind ssl crt)
- Server SSL: Private key OPTIONAL (CA cert only for backend verification)
- UI dropdown for usage type selection
- Dynamic form validation based on usage type
- Filtering: Frontends see only Frontend SSL, Backends see only Server SSL
DATABASE:
- Added usage_type column to ssl_certificates (default: 'frontend')
- Made private_key_content nullable for server SSL support
- Migration automatically runs on pod restart
BACKEND:
- Pydantic v2 compatibility (@field_validator, @model_validator)
- SSL router: usage_type filtering support
- Agent endpoint: usage_type field included
- Improved migration robustness with better error handling
- Fixed duplicate ensure_agents_table() function
- Fixed JSONB permissions insert with json.dumps()
- Fixed ON CONFLICT constraints with explicit checks
FRONTEND:
- SSL Management: Usage Type dropdown with visual feedback
- Frontend Management: Filters only Frontend SSL certificates
- Backend Servers: Filters only Server SSL certificates
- Dynamic private key validation (required for Frontend, optional for Server)
- Improved form UX with color-coded hints
AGENT SCRIPTS (Linux & macOS):
- Support for Server SSL without private key
- Conditional PEM file creation (cert+key vs cert-only)
- usage_type awareness in SSL deployment
- Backward compatible with existing Frontend SSL certificates
DOCKER:
- Increased npm timeout for slow networks (300s → 600s)
- Increased fetch-retries (5 → 10)
- Reduced maxsockets for stability (3 → 1)
All changes are backward compatible. Existing SSL certificates
default to 'frontend' type and continue working unchanged.
Tested with: HAProxy 2.8+, PostgreSQL 15, React 18
Trigger GitHub Actions workflow for:
- Building backend and frontend images
- Publishing to burganbank/* on Docker Hub
- Version tagging with timestamp
This will make latest changes available publicly
Updated image registry references for public deployment
Changed:
- image: haproxy-openmanager-backend:latest
- image: haproxy-openmanager-frontend:latest
To:
- image: burganbank/haproxy-openmanager-backend:latest
- image: burganbank/haproxy-openmanager-frontend:latest
Docker Hub Registry: burganbank/*
GitHub Actions workflow automatically publishes to Docker Hub on push to main
Users can now deploy directly from public Docker Hub images
CRITICAL BUG - Duplicate Key Constraint:
Error: duplicate key violates unique constraint backends_name_cluster_id_key
Cause: Soft-deleted entity exists, bulk import tries to create with same name
Fix:
- Check both active AND inactive entities
- Skip with helpful message showing status
- Prevent 500 errors
User feedback: Backend 'X' (deleted/inactive) instead of error 500
Terminology Fix:
Changed: "Upload SSL certificates"
To: "Create SSL certificates by entering PEM content"
SSL Management uses certificate creation form with PEM content input, not file upload.
Updated in two places:
1. Frontend UI Alert (BulkConfigImport.js Line 355)
2. Backend warning message (config.py Line 931-933)
Accurate workflow now:
1. Go to SSL Management
2. Create certificate (enter PEM content + private key)
3. Give it exact name from config
4. Apply and wait for SYNCED
5. Bulk import with auto-assignment
Code cleanup - removed duplicate import statements
Duplicate imports removed:
Line 778-779: import os, import re (already imported at top)
Line 835: import re (already imported at top)
Top-level imports (Line 10-11):
import os
import re
These are now used throughout the file without re-importing
Clean code practices:
- All imports at file top
- No duplicate imports
- Better code organization
Three Final Fixes Combined:
1. Backend GET Response (backend/routers/frontend.py Line 273):
- use_backend_rules now uses parse_jsonb_field()
- Consistent with acl_rules and redirect_rules
- Returns array instead of raw JSONB string
2. Bulk Import UI Row Expandability (BulkConfigImport.js Line 730):
- Added use_backend_rules to rowExpandable check
- Frontends with routing rules now show expand icon
3. Bulk Import Details Tag (BulkConfigImport.js Line 207):
- Added Routes tag showing use_backend count
- Cyan color to distinguish from ACL orange
Complete use_backend_rules Implementation:
Parser ✓
Parse Response ✓
UI Display ✓
Bulk Create ✓
Model Validator ✓
Frontend Create/Update ✓
GET Response ✓ (FIXED)
Config Generation ✓
JSONB Migration ✓
All components verified and working
Performance Fix - Dashboard Cleanup:
Dashboard makes 13 API calls on load and auto-refreshes every 60 seconds
When user navigates away, these operations need proper cleanup
Issue:
- User visits Dashboard → 13 API calls start loading
- User quickly navigates to Backend Management
- Dashboard cleanup incomplete, API calls still pending
- Backend Management loads slower due to backend busy with Dashboard requests
Fix Applied:
1. Added loading state reset in useEffect cleanup (Line 467-472)
- setLoading(false)
- setInitialLoad(false)
- Only runs on component unmount
- Does NOT affect Dashboard performance while in use
2. Enhanced interval cleanup documentation (Line 559-563)
- Already clears auto-refresh interval
- Added comment about preventing background fetches
Dashboard API Calls (13 total):
Sequential: 7 calls (overview, agents, frontends, backends, stats, health, slowest)
Parallel: 5 timeseries calls
Separate: 1 heatmap (24h data)
Performance Impact Analysis:
Dashboard in use: ZERO impact (cleanup only runs on unmount)
Dashboard to other pages: FASTER (loading states cleared)
Other pages: FASTER (Dashboard not blocking backend)
Risk: NONE - Only cleanup code, doesn't change functionality
Code cleanup - removed emoji from form field
Changed: extra="🆕 Select one or more..."
To: extra="Select one or more..."
Note: Backend Server SSL is single select (correct)
Frontend SSL is multiple select (correct - supports SNI)
CRITICAL RACE CONDITION FIX - FrontendManagement:
Same race condition pattern found and fixed
Component Analysis:
BackendServers: FIXED (guard clause added)
FrontendManagement: FIXED (guard clause added)
SSLManagement: Already has guard clause
WAFManagement: Already has guard clause
FrontendManagement Issues Fixed:
1. fetchFrontends() - Added guard clause
if (!selectedCluster) → Clear state and return
2. fetchBackends() - Added guard clause
if (!selectedCluster) → Clear state and return
Race Condition Pattern:
Mount → selectedCluster=undefined → fetch() → API returns ALL
Load → selectedCluster=1 → fetch() → API returns filtered
Problem: First response arrives late and overwrites correct data
Solution - Guard Clauses:
if (!selectedCluster) {
setEntities([]);
setFilteredEntities([]);
return; // Don't call API
}
Risk Assessment - SAFE:
- Only adds early return if no cluster selected
- Doesn't change existing logic when cluster IS selected
- Same pattern already used in SSLManagement and WAFManagement
- No breaking changes to other functions
Impact:
- Prevents race condition on component mount
- Prevents all entities appearing briefly
- Consistent behavior across all management pages
Tested Components:
Backend/Frontend/SSL/WAF Management all now protected
Added debug logging to fetchBackends:
- Log selectedCluster info
- Log params object being sent to API
- Log API response data (total count, IDs, cluster_ids)
This will help identify why wrong cluster backends are appearing:
- If params shows cluster_id: undefined → selectedCluster issue
- If params correct but response wrong → backend API issue
- If response correct but UI wrong → state/filter issue
Logs will appear in browser console with prefix:
FETCH BACKENDS DEBUG
FETCH BACKENDS RESPONSE
After testing, these logs can be removed or converted to conditional debug mode
Database Migration:
- Added ssl_certificate_id column to backend_servers table
- Added FK constraint to ssl_certificates table
- ON DELETE SET NULL behavior
- Idempotent migration (safe to run multiple times)
Column Details:
Name: ssl_certificate_id
Type: INTEGER
Nullable: YES
Foreign Key: ssl_certificates(id)
On Delete: SET NULL
Migration Function:
add_ssl_certificate_id_to_backend_servers()
Called in run_migrations() at Line 1523
Code Cleanup:
- Removed emojis from migration logs
- Removed emojis from SSL dropdown status icons
- Changed to text: Valid, Expiring, Expired
- Changed to text: Global, Cluster
Error Fixed:
GET /api/backends - 500
column "ssl_certificate_id" does not exist
After migration runs on startup, column will exist and API will work
🐛 Bug Fix:
- Fixed SSL certificate dropdown showing empty list in Backend Server edit
- Backend Server SSL dropdown now loads certificates correctly
🔧 Technical Details:
- Wrong API endpoint: /api/ssl-certificates (incorrect)
- Correct endpoint: /api/ssl/certificates (same as Frontend)
- Added cluster_id filtering and Authorization header
- Added debug logging for troubleshooting
✅ Now Shows (Verified with Query Analysis):
- Global SSL certificates (available to all clusters)
- Cluster-specific SSL certificates for SELECTED cluster only
- Other clusters' specific SSLs are NOT shown (correct behavior)
💡 SSL Enable Logic (HAProxy Standard):
Current implementation is CORRECT per HAProxy syntax:
server name addr:port ssl [verify required]
The 'ssl' flag MUST be present before 'verify' can be used.
Therefore: SSL Enable switch → SSL dropdown (correct behavior)
Example HAProxy syntax:
✅ server es1 10.0.0.1:443 ssl verify required ca-file /path/cert.pem
❌ server es1 10.0.0.1:443 verify required (invalid - missing ssl flag)
Query Logic (Line 173-176 backend/routers/ssl.py):
Global: NOT EXISTS in ssl_certificate_clusters
Cluster-specific: scc.cluster_id = selected_cluster_id
🐛 Critical Bug Fix:
- Fixed 'list' object has no attribute 'strip' error
- Error occurred in _validate_parsed_config() at line 847
- use_backend_rules is now a list, not a string
🔧 Technical Details:
- Changed from: frontend.use_backend_rules.strip()
- Changed to: bool(frontend.use_backend_rules)
- Simple boolean check works for both list and None types
✅ Impact:
- Bulk import parsing now works without errors
- Config validation properly handles list-based use_backend_rules
- All warning messages display correctly
Error was:
'list' object has no attribute 'strip'
at _validate_parsed_config line 847
Fix applied:
Line 847-848: Use bool() instead of .strip() for list validation
✨ New Features:
- Added use_backend_rules validator to Frontend model
- UI now supports editing use_backend rules from Frontend Management page
- Array to string conversion for use_backend rules in edit modal
🔧 Model Improvements:
- Changed use_backend_rules field type from Optional[str] to Any (list support)
- Added parse_use_backend_rules validator (same logic as ACL/redirect rules)
- Handles 3 formats: Array, Textarea string (newline-separated), JSON string
💡 UI Improvements:
- Frontend edit modal automatically converts use_backend array to multi-line text
- Users can edit routing rules line by line in textarea
- Format: 'use_backend BackendName if condition'
✅ Complete Workflow:
1. Bulk Import: Config parsed → ACL + use_backend stored as array
2. Frontend Edit: Arrays converted to multi-line string in textarea
3. User edits ACL/use_backend rules in UI
4. Save: Textarea string → validator → array → database
5. Config Generation: Array → HAProxy config format
Example workflow:
Parse: ['use_backend API if is_api']
→ Edit UI: 'use_backend API if is_api' (textarea)
→ User edits: 'use_backend API_v2 if is_api_v2'
→ Save: ['use_backend API_v2 if is_api_v2']
→ Generate: 'use_backend API_v2 if is_api_v2' (HAProxy config)
🐛 Critical Bug Fixes:
- Fixed duplicate 'acl' prefix in generated config (was: 'acl acl Name ...')
- Fixed duplicate 'use_backend' prefix in generated config
- Added use_backend directive parsing from bulk import configs
- Fixed redirect_rules list handling (was causing .strip() error)
🔧 Parser Improvements:
- Added use_backend rules parsing (stored as list like ACL rules)
- Changed use_backend_rules field from str to list for consistency
- Parser now captures all use_backend directives with conditions
🎯 Config Generation Improvements:
- Smart prefix detection: only add 'acl' if not already present
- Smart prefix detection: only add 'use_backend' if not already present
- Support both legacy (string) and new (list) format for rules
- Proper JSON parsing with fallback to newline-separated format
✅ HAProxy Validation:
- Generated config now passes HAProxy validation (haproxy -c -f)
- ACL and use_backend directives in correct HAProxy format
- Routing rules properly linked with ACL conditions
Example parsed config:
acl Elasticsearch hdr(host) -i baremetal-elastic.burgan.com.tr
use_backend Elasticsearch if Elasticsearch
Tested with full config including multiple ACLs and routing rules.
Critical security fixes:
- Add admin-only checks for user CRUD operations
- Add admin-only checks for role CRUD operations
- Add admin-only checks for role assignment operations
- Add permission check for agent script generation
- Fix auth_middleware to include is_admin flag in user context
- Hide user/role management buttons from non-admin users in UI
- Add 'View Only' labels for viewer users
Security improvements:
- Prevent viewer users from creating/editing/deleting users
- Prevent viewer users from creating/editing/deleting roles
- Prevent viewer users from assigning roles to users
- Backend API endpoints now properly check admin status
- Frontend UI now hides admin-only actions from viewers
Public release changes:
- Remove company-specific registry URLs from build-images.sh
- Update registry to generic example: your-registry.example.com
Affected endpoints:
- POST /api/users (create user) - admin only
- PUT /api/users/{id} (update user) - admin only
- DELETE /api/users/{id} (delete user) - admin only
- POST /api/roles (create role) - admin only
- PUT /api/roles/{id} (update role) - admin only
- DELETE /api/roles/{id} (delete role) - admin only
- POST /api/users/{id}/roles (assign roles) - admin only
- POST /api/agents/generate-install-script - permission check
- Remove duplicate Statistics section (already covered in Dashboard)
- Update Table of Contents to remove Statistics link
- Keep Statistics & Monitoring in API Reference (different context)
Major improvements:
- Add comprehensive Table of Contents (15 sections with clickable links)
- Reorganize structure: Screenshots after Features (better UX)
- Modernize System Architecture diagram (3-layer design, dark mode optimized)
- Simplify Agent Version Update Flow (user vs automatic actions)
- Add visual Installation selection panel (Docker vs Kubernetes)
- Expand Project Structure (150+ files documented)
- Restructure sections: 'Getting Started' (usage) vs 'Installation' (setup)
- Add Role-Based User Management to features
- Remove redundant sections (Advanced Setup, Coming Soon notes)
- Improve all diagrams for readability and modern look
- Fix all Table of Contents anchor links
- Add 18 production screenshots with descriptions
- Add cluster_id column to agent_config_requests table
- Update config request endpoint to accept and validate cluster_id
- Frontend now sends cluster_id with config requests
- Fixes issue where agents in pools with multiple clusters get wrong config requests
Technical Details:
- Database migration adds cluster_id as nullable foreign key for backward compatibility
- Backend validates that agent belongs to requested cluster via pool_id check
- Improved logging includes cluster name for better traceability
- No impact on existing features (apply management, sync status, entity CRUD)