Root cause: docker compose down killed the nohup script inside the
container before rebuild could complete — only git pull was visible
(updated VERSION file).
Changes:
- update_host.sh: new host-side watchdog (poll .update_trigger every 3s)
runs: git pull → docker compose down → build --no-cache app → up -d
PROJECT_DIR and User are dynamic (set at install time, not hardcoded)
- update_service.py: perform_update() now writes .update_trigger instead
of running git/docker commands inside the container
- deploy.sh: added install_update_watchdog() + --watchdog CLI option;
generates /etc/systemd/system/pvemanager-update.service dynamically
with correct User= and WorkingDirectory= for any install path/user
- .gitignore: ignore .update_trigger and generated systemd/ files
- CHANGELOG, VERSION, WIKI: bump to v1.1.1, document new update flow
String comparison caused local 1.0.3 to show "update available"
when GitHub had 1.0.2, since "1.0.3" != "1.0.2".
Added _is_newer() with proper semver tuple comparison so update_available
is only set when the remote version is strictly greater than local.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace hardcoded legacy Gitea URL in settings.py fallback
- Skip legacy Gitea URL even if saved in DB (treat as unset)
- Track last_response in branch loop so errors report the real HTTP
status code instead of always showing "HTTP unknown"
- Unify 401/403/404 into one "private or not accessible" message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The fallback repo URL was pointing to the old Gitea instance
(git.tzim.uz/dilshod/pve_manager), causing "Failed to fetch VERSION
file: HTTP unknown" because both main/master branches returned non-200.
Updated to the current GitHub repository.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously seenNodes used only the node name as the dedup key, causing
independent servers that happen to share a default node name (e.g. "pve")
to hide each other's charts.
Fix: /api/{server_id}/nodes now also returns cluster_id — the actual
Proxmox cluster name for clustered servers, or "standalone-{server_id}"
for single-node servers. The dashboard deduplicates by
"cluster_id::node_name", so:
- nodes in the same cluster → deduped correctly (shown once)
- nodes on independent servers with the same name → both shown
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The /api/auto-import/{server_id} endpoint ran as a background task
and returned {status: "queued"} with no templates field, while the
frontend's executeAutoImport() immediately called data.templates.forEach()
— causing TypeError: can't access property "forEach", data.templates
is undefined.
Replaced the background task with a synchronous handler that imports
templates in-request and returns {templates: [...], imported_count: N},
matching what the frontend expects.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix all `from ..` → `from ...` in app/api/proxmox/*.py after the
proxmox.py split (PR #6) placed modules one level deeper, causing
ModuleNotFoundError on startup
- Broaden Content-Security-Policy to allow CDN sources used in templates:
cdnjs.cloudflare.com (Font Awesome), cdn.jsdelivr.net (Bootstrap,
Chart.js, xterm), fonts.googleapis.com/gstatic.com (Google Fonts),
esm.sh (noVNC) — the overly-strict CSP was blocking all icons and
charts from loading in the browser
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- api/ipam.py: convert 30 async def handlers that had no await to plain def,
letting FastAPI run them in its thread pool (correct for sync SQLAlchemy I/O);
cleanup_orphan_allocations kept async (it has await)
- api/proxmox.py: add _get_proxmox_client(server) helper that encapsulates the
use_password/token branching, replace 58 duplicated 10-line blocks with a
single call; get_proxmox_resources() call sites left unchanged (different sig)
https://claude.ai/code/session_019rK2wvaWg5zXFwmAPPEB7R
- proxmox_client.py: add ThreadPoolExecutor(max_workers=20) + async wrapper
methods (async_get_nodes, async_get_vms, async_get_containers,
async_get_node_status, async_get_vm_status, async_get_container_status,
async_get_storages, async_get_all_resources) to avoid blocking event loop
- ipam_service.py: replace full-network allocation scan + Python IP loop with
per-pool targeted SQL query — only loads IPs for the specific pool being searched
- api/ipam.py: fix N+1 full-table scans on GET /api/allocations — use IN-filtered
queries on only the network/pool IDs present in the result set
- monitoring_worker.py: wire LoggingService.cleanup_old_logs(days=30) into the
existing run_cleanup_expired() job so audit logs don't grow unboundedly
- api/templates.py: convert auto_import_templates to async endpoint using
BackgroundTasks — returns immediately, heavy Proxmox I/O runs in background
https://claude.ai/code/session_019rK2wvaWg5zXFwmAPPEB7R
Encrypt Proxmox server passwords and VM cloud-init passwords in the database:
- Add backend/app/crypto.py: Fernet-based encrypt/decrypt helpers + EncryptedString
SQLAlchemy TypeDecorator (transparent to all existing code, no API changes)
- Add FERNET_KEY config field (env var); without it values stay plaintext for
backward compatibility, with a logged warning
- Update models.py: ProxmoxServer.password and VMInstance.cloud_init_password
now use EncryptedString(255) instead of String(255)
- Add migration 14 in migrations/migrations.py: idempotent re-encryption of
existing plaintext rows (skipped when FERNET_KEY is not set)
CSRF analysis:
- /api/auth/change-password is protected by JWT Bearer token (Authorization header)
- Browsers cannot auto-include Authorization headers cross-site → CSRF is not
applicable here; added explanatory comment
Improve migration loading:
- Add migrations/__init__.py to make it a proper Python package
- Replace fragile importlib dynamic loading in main.py with a standard import
https://claude.ai/code/session_019rK2wvaWg5zXFwmAPPEB7R
Critical fixes:
- init_admin.py: replace hardcoded admin123 password with ADMIN_PASSWORD env var
- proxmox_client.py, models.py: change verify_ssl default from False to True
- api/proxmox.py: use server.verify_ssl instead of hardcoded verify=False in websocket handler
- ssh_client.py: replace AutoAddPolicy (MITM-vulnerable) with WarningPolicy + load_system_host_keys
Security hardening:
- main.py: add security headers middleware (CSP, X-Frame-Options, HSTS, X-XSS-Protection, Referrer-Policy)
- main.py: fix CORS — allow_credentials=True is incompatible with allow_origins=["*"]; read origins from CORS_ORIGINS env var
- auth.py, logging_middleware.py: validate X-Forwarded-For/X-Real-IP to prevent IP spoofing
Performance:
- monitoring_worker.py: wire up cleanup_expired_connections() as hourly scheduled job (was defined but never called)
Code quality:
- logging_service.py: move `import json` from inside function to module level
- ssh_client.py, proxmox_client.py, api/proxmox.py, rbac/migration.py, task_queue_service.py: replace all bare `except:` with `except Exception:`
- .env.example, config.py: document ADMIN_PASSWORD and CORS_ORIGINS settings
https://claude.ai/code/session_019rK2wvaWg5zXFwmAPPEB7R
- Updated `backend/app/main.py`: Added automatic execution of `/app/init_admin.py` during application startup to create default admin account
- Modified `.gitignore`: Removed `.env` from ignored files to ensure environment configuration is available during deployment
- Enhanced `lifespan` context manager: Integrated admin initialization with proper error handling and logging
- Improved startup reliability: Ensures default credentials are provisioned regardless of deployment environment
The fix ensures that new deployments automatically set up the default admin user, resolving widespread issues with missing login credentials. The change is robust, idempotent, and logs all initialization steps for debugging.
- Add repository URL setting to panel_settings table
- Support for multiple git hosting providers (GitHub, Gitea, GitLab, etc.)
- UI selector for predefined repositories (git.tzim.uz and github.com) or custom URL
- Update service now uses repository from database settings
- API endpoints: GET/PUT /api/updates/repository
- Migration for default repository setting
- Translations for repository selection UI
- Add notify_update_available() method to NotificationService
- Add translations for update notifications (ru/en)
- Add 'update' type to default notification types
- Add check_for_panel_updates() background task (runs every 6 hours)
- Notifications include link to settings page for easy update access
- Prevent duplicate notifications for same version
- Added IPAM release in sync_vm_cache when VM is no longer found on Proxmox
- Works when VMs deleted through Proxmox UI (not through panel)
- Logs released IPs count in sync summary
- Also includes server availability monitoring with notifications
- Removed complex cluster detection logic
- Simple global deduplication by node name is sufficient
- Works correctly even when nodes temporarily disconnect/reconnect
- Each node name appears only once regardless of cluster state
- Changed cluster detection to use sorted node names as cluster key
- Global deduplication by node name prevents duplicates
- Works correctly when cluster servers have different IPs
- Changed logic to process ALL nodes from cluster, not just the first one
- Added processedClusterIPs set to avoid duplicate processing of cluster servers
- Now clusters show all nodes (pve1, pve2, pve3) instead of duplicating first node
- Fixed issue where node names were incorrect after node reconnection
- Added color: var(--text-primary) to unlinked allocations IP in ipam_dashboard.html
- Added color: var(--text-primary) to IP address column in logs.html
- Prevents black text on dark theme
- Modified save_vm_instance to check for soft-deleted records before insert
- If a soft-deleted record exists with same server_id+vmid, restore and update it
- Prevents 'duplicate key value violates unique constraint' errors
- Changed textContent to innerHTML for proper HTML icon rendering
- Fixed VNC modal titles in instance_detail.html, proxmox_vms.html, proxmox_server_detail.html
- Fixed command execution output icons in instance_detail.html
- Fixed settings and reinstall modal titles in proxmox_server_detail.html
- Added color styles to IPv4/IPv6 network interface icons
- Fix field names in frontend: ipam_ip -> ip_address, ipam_gateway -> gateway
- Pass ipam_network_id to deploy API for proper network mask lookup
- Extract network mask from IPAM network CIDR (e.g., /24 from 10.10.10.0/24)
- Auto-fetch gateway from IPAM network if not provided
- Add detailed logging for VM IP configuration
- Remove hardcoded /24 mask, use actual network mask from IPAM
This fixes the issue where VMs were getting DHCP instead of the
static IP allocated from IPAM.
- Add ssh_public_key column to User model for storing SSH keys
- Add database migration for ssh_public_key column
- Add SSH key section in user profile settings with validation
- Auto-inject user's SSH key when deploying VMs from templates
- Auto-inject user's SSH key when creating LXC containers
- Support for both regular and smart LXC creation endpoints
- Add i18n translations for SSH key UI (ru/en)
- Validate SSH key format (ssh-rsa, ssh-ed25519, ecdsa-sha2, ssh-dss)
Users can now save their SSH public key in Settings -> Account,
and it will be automatically added to any VM or LXC container
they create, enabling passwordless SSH access.
- Add password field to UserUpdate schema for password change on edit
- Implement password validation in update_user endpoint
- Add i18n translations for password validation errors (ru/en)
- Update SecurityService.validate_password to support language parameter
- Add GitHub icon and repository name 'pvemanager' to header button
- Fix internal server error when updating user password
- Clean up hardcoded translations in frontend