Commit Graph

140 Commits

Author SHA1 Message Date
taylanbakircioglu 36fdba52bc fix: ACME setup guide accuracy, reject rollback, and UX improvements
- Step 3 (Enable ACME on Cluster) now shows a process icon instead of
  a misleading green checkmark when ACME is enabled but not yet applied.
  Per-cluster "(pending apply)" annotation for multi-cluster setups.
- Step 4 button and all /apply-management navigation buttons now say
  "Apply Changes" instead of "Configure" for clearer guidance.
- Setup Guide auto-selects the correct cluster before navigating to
  Apply Management, showing pending cluster names in alerts.
- Pending ACME disable changes are now correctly detected in Step 4
  even when acme_enabled is already FALSE in the database.
- Entity snapshot rollback for cluster ACME settings: reject correctly
  restores acme_enabled/acme_backend_url to pre-change values.
- Deduplication logic prevents "last wins" bug when multiple ACME
  toggles are rejected in sequence.
- Connection leak prevention with try/finally around conn2 in ACME
  config version creation.
- Step 4 branching uses boolean has_enabled instead of fragile string
  truthiness check.

Made-with: Cursor
2026-04-04 16:57:57 +03:00
taylanbakircioglu deb784bb65 fix: ACME certificate issuance improvements, null-safe hardening, guided setup UX, and order list enhancements (Issue #9)
- Fix critical cascading NULL status bug in ACME challenge flow that could cause 404s
- Add defense-in-depth NULL handling across all ACME service methods
- Add new GET /api/letsencrypt/prerequisites endpoint for configuration checks
- Add interactive ACME Setup Guide with step-by-step navigation links
- Add URL-based tab navigation in Settings and SSL Management pages
- Harden retry flow: return clear 409 errors for invalid/cancelled orders
- Allow cancellation of invalid orders (backend + frontend)
- Improve error message extraction with consistent getErrorMsg helper
- Add status filter tabs (Active/Completed/Failed/All) for order list
- Add visual dimming for cancelled/invalid orders
- Add enhanced pagination with size changer and total count
- Add comprehensive diagnostic logging with ACME: prefix
- Update README with ACME architecture docs, quick start guide, and troubleshooting

Resolves #9

Made-with: Cursor
2026-04-04 15:15:05 +03:00
taylanbakircioglu c2766eadbd feat: dark mode support, UI improvements, and version bump to v1.2.0
- Full dark mode support across all pages with lightbulb toggle in header
- Theme preference persisted in localStorage across sessions
- Ant Design 5 token-based theming (40+ components updated)
- Recharts dark mode: axes, grids, tooltips adapt to theme
- Login page redesigned with product-consistent blue-gray palette
- Overscroll bounce background matches dark theme
- All Servers search with multi-field filtering
- Bulk Config Import UI streamlined with collapsible guidelines
- ConfigProvider moved above AppContent for correct token resolution

Made-with: Cursor
2026-04-03 01:16:05 +03:00
taylanbakircioglu fabe7aba4c fix: prevent empty HAPROXY_CONFIG_PATH when get_cluster_paths() fails
Agent startup left HAPROXY_CONFIG_PATH empty when the /api/clusters jq
select() returned no output (e.g. transient API failure or cluster_id
mismatch). This caused "No existing HAProxy config found at: " errors
and partial config merge failures for newly added agents.

Three-layer fix:
1. Load HAProxy paths from config.json as baseline before get_cluster_paths()
2. Use local variables in get_cluster_paths() - only override globals when
   API returns non-empty values (defensive against jq select() empty output)
3. Dynamically update paths from /api/agents/{name}/config response in
   check_config_updates() - allows cluster path changes without reinstall

Applied to both linux_install.sh and macos_install.sh.

Made-with: Cursor
2026-04-03 00:42:58 +03:00
taylanbakircioglu 2fc2603d1a fix: ACME account removal, status display, and timeline icon clipping
- Add permanent delete endpoint for deactivated ACME accounts (DELETE /accounts/{id}/permanent)
- Add "Remove from database" button in ACME Accounts modal for deactivated accounts
- Fix ACME Account stat card showing "Active" for deactivated accounts
- Fix Timeline dot/icon clipping in Applied/Rejected/Pending sections

Made-with: Cursor
2026-04-02 00:36:56 +03:00
taylanbakircioglu 93d7ad8fdb feat: add ACME Auto SSL with Let's Encrypt integration (v1.1.0)
Add automated SSL certificate management via ACME protocol (RFC 8555):
- Full ACME client implementation (account registration, HTTP-01 challenges, certificate issuance/renewal)
- Configurable ACME providers (Let's Encrypt, ZeroSSL, custom CA) via Settings UI
- Auto-renewal scheduler with PENDING -> Apply -> APPLIED flow alignment
- ACME account management (register, deactivate) from UI
- Certificate request wizard with domain validation and cluster targeting
- Zero changes to HAProxy agent scripts - challenges routed through existing architecture
- Comprehensive security hardening (no private key exposure in API responses)
- Full backward compatibility with existing SSL, Apply, Rollback, and Restore workflows
- Updated README, API documentation, and Kubernetes deployment notes
- Version management embedded in code (v1.1.0)
- UI messaging improvements for agent-pull architecture accuracy

Made-with: Cursor
2026-04-02 00:25:50 +03:00
taylanbakircioglu 77bd3ecead fix: add missing REJECTED and DELETION values to config_status enum
Root cause: config_status enum was created with only PENDING and APPLIED
values. The REJECTED value was never added due to a silent duplicate_object
exception in create_essential_tables(). This caused SSL certificate listing
to crash with "invalid input value for enum config_status: REJECTED" on
fresh installations.

Also adds scrollable containers to Apply Management page to prevent
Agent Sync Status card from being pushed off-screen.

Closes #7

Made-with: Cursor
2026-04-01 13:18:24 +03:00
taylanbakircioglu 922bc2ce6d improve: SSL certificate listing reliability, error visibility, and ARM64 support
Fixes #6

- Fix NameError in soft-deleted certificate reactivation path by
  reordering variable extraction before DB operations
- Replace silent empty-array returns with HTTP 500 on SQL errors,
  making failures visible in both API responses and server logs
- Add primary_domain migration for schema consistency across fresh
  and upgraded installations (backfill from legacy domain column)
- Use primary_domain in non-cluster SSL query branch for schema
  compatibility
- Surface SSL fetch errors in frontend via toast notifications
- Harden connection cleanup in error handlers with try/except
- Add QEMU + Buildx for linux/amd64,linux/arm64 multi-platform
  Docker image builds
- Update GitHub Actions to latest versions (checkout v4, login v3,
  build-push v6)

Made-with: Cursor
2026-03-30 10:42:28 +03:00
taylanbakircioglu 5b59accd51 fix: resolve asyncpg AmbiguousParameterError on keepalive params
asyncpg cannot infer the PostgreSQL type for $20/$21 when the Python
value is None in CASE WHEN $20 IS NOT NULL expressions. Add explicit
::text cast so asyncpg can prepare the statement regardless of whether
the agent sends keepalive data or not. This was causing ALL heartbeats
to fail with 500 error, making all agents appear offline.

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 0be3c299dd fix: serialize new_values in entity snapshot to prevent silent JSON failure
Root cause: SSL update passes expiry_date as Python datetime object in
new_values. json.dumps() fails on datetime, causing save_entity_snapshot
to return {} (empty). Entity snapshot is never saved in config_version
metadata, so reject/rollback can never find it to restore old values.

Fix: Apply same JSON serialization to new_values as old_values. Only
affects SSL certificates (only entity with datetime in new_values).
All other entity types (frontend, backend, server, waf) are unaffected
as their new_values contain only JSON-serializable types.

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 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 7ca036ef3c fix: add missing is_active field to SSL certificate API responses
The detail endpoint (GET /api/ssl/certificates/{id}) was missing is_active
in its SQL SELECT/GROUP BY, causing the frontend details modal to always
show "Inactive". The list endpoint included is_active in its SQL query but
omitted it from the response dict, causing the Active counter card to
always show 0.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 16:36:27 +03:00
taylanbakircioglu c19f0b5275 fix: SSL certificate updates now trigger HAProxy reload after self-upgrade
Root cause: After agent self-upgrade, the embedded daemon (SKIP_TO_DAEMON
block) runs instead of run_daemon(). HAPROXY_BIN and HAPROXY_CONFIG
variables were uninitialized before the daemon loop, causing SSL-triggered
HAProxy reloads to silently fail with empty path validation.

Changes:
- Initialize HAPROXY_BIN/HAPROXY_CONFIG before embedded daemon loop
- Add md5 checksum comparison in deploy_ssl_certificates() to detect
  actual cert file changes (avoid unnecessary writes and reloads)
- Add check_ssl_updates() for independent SSL sync every ~2.5 min
  in run_daemon(), independent of config version changes
- Add SSL-aware reload in check_config_updates(): if config validation
  fails but SSL certs changed, reload HAProxy with existing config
- Add "full" fetch mode to fetch_and_deploy_ssl_certificates() to
  bypass incremental timestamp filter for standalone SSL checks

Applied to both linux_install.sh and macos_install.sh.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 16:36:27 +03:00
taylanbakircioglu dc978f847d fix: prevent daemon crash-loop on exit 1 with retry-backoff mechanism
- Replace immediate exit 1 with retry-with-backoff in all daemon mode paths
  (dependency checks: 5 retries, 30/60/90/120/150s; config file: 5 retries, 15/30/45/60/75s)
- Make socat missing non-fatal in daemon mode (agent continues without stats)
- Add SystemD StartLimitBurst=5/StartLimitIntervalSec=120 for new installs
- Add macOS launchd ThrottleInterval=30 for new installs
- Fix Linux daemon fallback to installer mode (would hang on interactive read)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 20:04:42 +03:00
taylanbakircioglu 93602fa65d fix: check and install jq before cluster validation in agent scripts
Move jq dependency check to run before cluster validation which requires
it. Auto-install jq via the detected package manager (apt, dnf, yum,
zypper, apk for Linux; brew for macOS). Exit with clear manual install
instructions if automatic installation fails.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 20:04:42 +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 cc10015952 fix: prevent install script from killing itself during cleanup
Same pgrep self-kill bug as uninstall scripts. When user downloads
install script as install-haproxy-agent.sh, pgrep -f "haproxy-agent"
matches the installer's own bash process and kills it before
installation starts. Now filters out INSTALLER_PID and PPID.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 16:45:29 +03:00
taylanbakircioglu 2d5c863735 fix: replace static protected paths with dynamic safety in uninstall
Instead of a hardcoded PROTECTED_PATHS list (varies per environment):
- safe_rm() blocks any path not containing "haproxy-agent"
- Process killer verifies ps output contains "haproxy-agent"
- Pre/post HAProxy integrity check via md5 hash comparison of config
  and service status diff (was running -> still running?)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 16:45:29 +03:00
taylanbakircioglu 8351a6439b fix: add HAProxy safety guards to uninstall scripts
- PROTECTED_PATHS array prevents accidental removal of haproxy.cfg,
  haproxy binary, haproxy.service, haproxy logs
- safe_rm() blocks any path not containing "haproxy-agent"
- Process killer verifies command line contains "haproxy-agent" and
  skips HAProxy master PID
- Binary removal validates filename is exactly "haproxy-agent"
- Pre-flight safety check reports HAProxy status before starting
- Post-uninstall HAProxy integrity check confirms nothing was touched

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 16:45:29 +03:00
taylanbakircioglu 08c500855e fix: rewrite uninstall scripts - simple and reliable
Replaced overcomplicated 380-line scripts with clean ~125-line versions.
Steps: stop service, kill processes (excluding self), remove binary,
remove config/logs/temps, verify. No emojis, no fragile helpers.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 16:45:29 +03:00
taylanbakircioglu b7dde62972 fix: prevent uninstall script from killing itself via pgrep
pgrep -f "haproxy-agent" was matching the uninstall script's own
process (uninstall-haproxy-agent-linux.sh contains "haproxy-agent"),
causing the script to terminate itself at step 1/7 before reaching
file cleanup. Now filters out $$ (own PID) and $PPID from kill lists.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 16:45:29 +03:00
taylanbakircioglu 5cec6cf85a fix: remove emojis from all agent scripts and fix install/uninstall bugs
- Remove all emoji characters from install scripts (linux, macos)
- Rewrite uninstall scripts: remove set -e, fix safe_remove to always
  return 0, replace ((var++)) with $((var + 1)), fix local keyword
  usage, add comprehensive cleanup including config.json
- Fix install script QUIET_MODE/DAEMON_MODE detection to correctly
  handle interactive runs when old config.json exists

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 eac00b3e39 fix: consistent section header detection in listen extraction
Additional fix for listen block extraction:
- Section boundary check now uses ([[:space:]]|$) pattern
- Previously only checked [[:space:]], missing 'defaults' at end of line
- Ensures listen extraction stops correctly at any section header

Pattern consistency verified across all 4 locations:
- Linux install mode: global, defaults, listen extraction ✓
- Linux daemon mode: global, defaults, listen extraction ✓
- macOS install mode: global, defaults, listen extraction ✓
- macOS daemon mode: global, defaults, listen extraction ✓

Edge cases tested:
- Named defaults (defaults http): works correctly
- Multiple listen blocks: all preserved
- Tab characters: handled by [[:space:]]
- User's actual config structure: produces clean merge

No impact on other agent functions (self-upgrade, metrics, etc.)
2026-01-26 15:25:16 +03:00
taylanbakircioglu 25408a618e fix: awk patterns now match keywords at end of line
CRITICAL BUG FIX:
- Previous pattern `/^defaults[[:space:]]/` required whitespace after keyword
- HAProxy allows `defaults` without a name (no trailing whitespace)
- Pattern failed to match, causing global extraction to include defaults section

New pattern uses `([[:space:]]|$)`:
- Matches keyword followed by whitespace OR end of line
- `defaults` (no name) now correctly triggers exit
- `defaults http` (named) also correctly triggers exit

Test results:
- Old pattern: extracted 8 lines (included defaults) 
- New pattern: extracted 4 lines (stopped at defaults) 
2026-01-26 15:25:16 +03:00
taylanbakircioglu 995d87df43 fix: defaults extraction captures only first defaults section
CRITICAL BUG FIX:
- Previous awk pattern allowed multiple defaults sections to be captured
- Pattern `!/^defaults/` meant "don't exit if line IS defaults"
- This caused duplicate defaults when config had listen before defaults

New pattern uses `started` flag:
- First `defaults` line: set started=1, begin capturing
- Second `defaults` line: started is set, EXIT immediately
- Any `frontend/backend/listen`: started is set, EXIT

Also includes: debug logging for validation error storage with fallback

Tested scenarios:
- Normal config (defaults → listen → frontend): ✓
- Listen before defaults: ✓
- Two defaults sections: ✓ Only first captured
- No defaults section: ✓ Empty output
- Defaults at end of file: ✓
- Empty defaults section: ✓
2026-01-26 15:25:15 +03:00
taylanbakircioglu 4a6bc1b139 fix: global section extraction includes listen blocks when listen comes before defaults
Bug: When haproxy.cfg has 'listen stats' BEFORE 'defaults' section,
the global extraction incorrectly included listen stats because it
only stopped at 'defaults', not at any section header.

Fix: Changed extraction to stop at ANY section (defaults/listen/frontend/backend)
This prevents duplicate listen blocks in merged config.

Root cause of 'proxy stats has same name as proxy stats' HAProxy validation error.
2026-01-26 15:25:15 +03:00
taylanbakircioglu 83204c86a8 fix: critical security and UX improvements for config management
SECURITY FIX (agent.py):
- Ensure ONLY APPLIED versions are sent to agents
- Fixed fallback query that could return PENDING versions
- Agents will never receive unapproved configurations

UX FIX (cluster.py):
- Clear validation_error when creating new version via Apply
- Prevents stale validation errors from showing after re-apply

UX FIX (agent.py):
- Clear validation_error when agent successfully applies config
- Clear last_validation_error on agent record after success
2026-01-26 15:25:15 +03:00
taylanbakircioglu 1dcf45b1bb fix: dynamic collision detection in config generation
Query agent preserved_listen_blocks dynamically and skip
conflicting entities to prevent HAProxy validation errors.

Falls back to static reserved list if no agent data available.
2026-01-26 15:25:15 +03:00
taylanbakircioglu e75bfdf706 feat: dynamic HAProxy binary path from cluster configuration
- /api/agents/{name}/config now returns haproxy_bin_path, haproxy_config_path, stats_socket_path from cluster
- Agent daemon uses these dynamic paths from API response for validation
- Fallback to local config file if API values not present
- Allows cluster admin to change paths without reinstalling agents

This fixes validation when HAProxy binary is in non-standard location
2026-01-26 15:25:15 +03:00
taylanbakircioglu 97e32114e0 fix: translate Turkish error messages to English
- Updated SUGGESTION_TEMPLATES in haproxy_error_parser.py
- Fixed fallback error messages in cluster.py
- Product language should be English throughout
2026-01-26 15:25:15 +03:00
taylanbakircioglu 095c682fa3 fix: handle soft-deleted entities in bulk import and frontend deletion
Bulk Import Fix (config.py):
- Check for existing servers (including soft-deleted) before INSERT
- If server exists: UPDATE and reactivate instead of INSERT
- Create snapshot for ALL updated servers (enables reject/rollback)
- Add backend_name to UPDATE for field consistency
- Track updated/reactivated servers in response
- Prevents duplicate key constraint violation on re-import

Frontend Deletion Fix (frontend.py):
- Add hard delete support for already soft-deleted frontends
- Follows same pattern as backend.py hard delete
- Deletes WAF associations and config versions before frontend
- Works with existing orphan cleanup in Apply/Reject flows

Both fixes are safe because:
- Orphan version cleanup handles hard-deleted entities in Apply/Reject
- Consistent with existing backend.py patterns
- Snapshot-based rollback fully supported
2026-01-26 15:25:15 +03:00
taylanbakircioglu ecbaebf451 feat: report invalid config format errors to backend for UI display
When config content doesn't look like valid HAProxy config (missing
global/defaults/frontend/backend/listen keywords), agent now reports
this to backend via config-validation-failed endpoint.

This catches backend-side config generation errors (like Python
exceptions) that prevented actual HAProxy config from being generated.

Changes:
- Add invalid config format detection and reporting in daemon mode
- Use same curl pattern as existing HAProxy validation failure reporting
- Same endpoint, headers, error handling, and spam prevention
- Follows exact existing pattern for consistency
- Does not affect self-upgrade flow (runs before upgrade check)

Both linux and macos scripts updated identically.
2026-01-26 15:25:15 +03:00
taylanbakircioglu d40c701272 fix: resolve Python scoping error in config generation
Remove redundant local 'import json' statements that caused:
"cannot access local variable 'json' where it is not associated with a value"

Root cause: Local import inside try block made 'json' a local variable,
but except clause referenced json.JSONDecodeError before assignment.

Changes:
- Line 197: Remove local import, use global json (line 5)
- Line 514: Remove 'import json as _json', use global json
- Use ValueError instead of json.JSONDecodeError (equivalent, JSONDecodeError
  is a subclass of ValueError)

This was causing config generation to fail completely, returning error
message as config content instead of actual HAProxy configuration.
2026-01-26 15:25:15 +03:00
taylanbakircioglu 5947caa46d fix: move uninstall scripts to backend/utils/agent_scripts for deployment
- Copy uninstall-agent-linux.sh and uninstall-agent-macos.sh to
  backend/utils/agent_scripts/ (same location as install scripts)
- Update generate-uninstall-script endpoint to use the same path
  pattern as working install script endpoint
- Add container-specific fallback paths for robustness
- Add debug logging to track which path is used

Fixes 404 error when fetching uninstall script in production
where /utils/ directory at project root is not deployed.
2026-01-26 15:25:15 +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 851377aedf feat: Add HAProxy proxy name collision prevention system
- Add preserved_listen_blocks column to agents table for storing agent's local listen block names
- Implement reserved names check (stats, monitoring, admin, etc.) for frontend/backend creation
- Add dynamic collision detection against agent's preserved listen blocks
- Apply collision checks to CREATE, UPDATE endpoints and bulk import
- Add debug mode for failed config validation (saves to /tmp/haproxy-failed-*.cfg)
- Fix JSON character stripping for ACL and use_backend rules
- Remove collision protection from agent scripts (now handled by backend)
- All collision checks wrapped in try-except for backwards compatibility
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 183b4fa9e2 fix: Auto-add 'verify none' for SSL backend servers without CA file
When ssl_enabled is true for a backend server but:
- No ssl_verify option is explicitly set AND
- No CA file (ssl_certificate_id) is specified

HAProxy 2.8+ defaults to 'verify required' which fails without a CA.
Now auto-adding 'verify none' in this case with a warning log.

Users can override this in UI by setting SSL Verification explicitly.

This fix is safe for:
- Bulk Import: Parser already sets ssl_verify='none' when removing ca-file
- Version Diff: Generated config correctly shows 'verify none'
- Restore: DB values unchanged, config regeneration applies same fix

Fixes: 'verify is enabled by default but no CA file specified' error
2025-12-23 13:28:49 +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 378710db3f fix(haproxy-config): ACL rules must come before http-request directives
CRITICAL HAProxy Validation Error Fix

Problem:
Generated HAProxy config failed validation with error:
[ALERT] error detected while parsing an 'http-request deny' condition:
no such ACL: 'waf_demo-rule1_path'.

Root Cause:
Config generator was writing http-request directives BEFORE ACL definitions.
HAProxy requires ACLs to be defined before they are referenced.

Generated Config (WRONG ORDER):
  http-request deny if waf_demo-rule1_path   ACL not defined yet!
  acl waf_demo-rule1_path path_reg ^/admin   Too late!

Fix:
Reordered frontend config generation:
1. ACL Rules (Line 339-362) - Define ACLs FIRST
2. HTTP Request Headers (Line 364-371) - Use ACLs AFTER

Generated Config (CORRECT ORDER):
  acl waf_demo-rule1_path path_reg ^/admin         Define first
  acl waf_demo-rule1_method method POST            Define first
  http-request deny if waf_demo-rule1_path waf_demo-rule1_method   Use after

Impact:
- Parsing logic: UNCHANGED (no breaking changes)
- Database storage: UNCHANGED (no schema changes)
- Config generation: FIXED (correct HAProxy syntax)
- Bulk import: Works correctly now
- Agent config apply: Validation passes

Testing:
1. Bulk import config with ACLs and http-request rules
2. Agent applies config successfully
3. HAProxy validation passes

Files Changed:
- backend/services/haproxy_config.py: Reordered ACL and request_headers generation
2025-11-18 21:58:05 +03:00
Taylan Bakırcıoğlu 1d17dd8fd5 fix(reject): prevent deletion of existing entities on bulk import reject
CRITICAL DATA LOSS BUG FIX

Problem:
- User updates existing entity via bulk import
- User clicks Reject
- EXPECTED: Entity reverts to old values
- ACTUAL: Entity is COMPLETELY DELETED!

Root Cause:
Reject logic tracked ALL bulk import entities for force deletion.
Did not distinguish between CREATE and UPDATE operations.

Fix:
Added operation check - only track CREATE operations for force deletion.
UPDATE operations are rolled back, not deleted.

Impact:
- CREATE scenarios: No change in behavior
- UPDATE scenarios: Data loss prevented
- Mixed CREATE+UPDATE: Only CREATE entities deleted
- Rollback failures: Safety mechanism preserved

Files Changed:
- backend/routers/cluster.py: Line 4494 added operation check
2025-11-18 21:58:05 +03:00
Taylan Bakırcıoğlu 589f865f59 fix(ssl): rollback support + improved ALPN validation error message
PART 1: Rollback Support for SSL Advanced Options
===================================================
Problem: SSL advanced options lost on reject/rollback operations

Root Cause:
- Snapshot creation uses SELECT * (includes all fields) 
- old_values contains SSL advanced options 
- Rollback UPDATE did NOT restore SSL fields 

Impact - Frontend:
- User changes ssl_alpn, then clicks Reject
- Rollback skipped: ssl_alpn, ssl_npn, ssl_ciphers, ssl_ciphersuites, ssl_min_ver, ssl_max_ver, ssl_strict_sni
- Result: All SSL advanced options lost (set to NULL)

Impact - Backend Server:
- User changes ssl_min_ver='TLSv1.2', then clicks Reject
- Rollback skipped: ssl_sni, ssl_min_ver, ssl_max_ver, ssl_ciphers
- Result: Security issue - TLS version constraints removed!

Fix:
- backend/utils/entity_snapshot.py line 285-286: Added 7 frontend SSL fields to rollback UPDATE
- backend/utils/entity_snapshot.py line 458: Added 4 server SSL fields to rollback UPDATE

PART 2: Improved ALPN Validation Error Message
===============================================
Problem: User enters 'http/2' in ALPN field, gets generic error

User feedback: Tried 'h2,http/1.1,http/2' → validation error not clear

Root Cause:
- ALPN standard uses 'h2' for HTTP/2 (not 'http/2')
- Validator rejected 'http/2' but didn't explain the correct format

Fix:
- backend/models/frontend.py line 255-260: Detect common mistakes (http/2, http2, http-2)
- Provide helpful error: 'For HTTP/2, use "h2" (not "http/2")'

Before:
  "Invalid ALPN protocol: http/2. Valid protocols: h2, http/1.1, ..."

After:
  "Invalid ALPN protocol: http/2. For HTTP/2, use 'h2' (not 'http/2'). Valid protocols: ..."

Testing:
1. Rollback test: Edit frontend SSL, reject, verify SSL fields restored
2. Validation test: Enter 'http/2', verify friendly error message

Files Changed:
- backend/utils/entity_snapshot.py: _rollback_update() for frontend and server
- backend/models/frontend.py: validate_alpn() with better error messages
2025-11-18 21:58:05 +03:00
Taylan Bakırcıoğlu 8e1a93f873 fix(ssl): add backend server SSL advanced options to haproxy config generation
CRITICAL BUG FIX: Backend server SSL advanced options not in generated HAProxy config

Problem:
- Database has ssl_sni, ssl_min_ver, ssl_max_ver, ssl_ciphers columns 
- Config generation code tries to write them to HAProxy config (line 680-687) 
- BUT SELECT statement did NOT include these fields 
- Result: server.get('ssl_sni') always returned None

Impact:
- User sets 'TLS Min Version: TLSv1.2' for backend server in UI
- Value saved to database correctly
- BUT generated HAProxy config missing 'ssl-min-ver TLSv1.2'
- Agent deploys incomplete config, SSL settings lost!

Solution:
- Added 4 server SSL advanced options to SELECT query (line 616):
  - ssl_sni (for SNI hostname)
  - ssl_min_ver (minimum TLS version)
  - ssl_max_ver (maximum TLS version)
  - ssl_ciphers (cipher suite override)

Config Generation Logic:
- Line 680-687: Code already writes these fields to HAProxy config
- Line 616: Now SELECT actually retrieves the values from database
- Example output: 'server backend1 10.0.0.1:443 ssl sni example.com ssl-min-ver TLSv1.2'

Testing:
- After deployment, bulk import a config with backend server SSL options
- Check generated HAProxy config on agent
- Should now see: 'server X ssl ssl-min-ver TLSv1.2 ciphers ...'

Files Changed:
- backend/services/haproxy_config.py: Line 616 SELECT statement
2025-11-18 21:58:05 +03:00