- Removed the local old() function definition from views/servers/create.php
which conflicted with the global old() in src/Helpers/functions.php:37
- Replaced all old() calls with direct htmlspecialchars([...]) lookups
- Wrap ServerPermission queries in try-catch so show/edit don't 500
when the migration hasn't been run yet
- Same protection for save calls in store/update (both web and API)
- New server_permissions table (migration 007) stores required SSH permissions per server
- PermissionValidator service connects via SSH and validates each permission before save
- ServerPermission model for CRUD on server_permissions table
- Web flow: create/edit forms include dynamic permission rows with quick-add presets
- API flow: serverAdd/serverUpdate accept permissions array, return 400 on failure
- Show view displays required permissions on server detail page
- Form input preserved on validation failure (credentials excluded)
- AGENTS.md updated with new conventions
Scaffold's innerPadding reserved NavigationBar height even when the
bar was hidden (on detail screens). Content like the services list
had extra empty space at the bottom.
Fix: build custom PaddingValues that only includes bottom padding
when inLoggedInArea is true.
Root cause: LoginViewModel.logout() was async (viewModelScope.launch),
so _uiState.isConnected remained true after navigating to login.
LoginScreen's LaunchedEffect immediately navigated back to dashboard,
where APIs failed with 401 again → infinite loop.
Fixes:
- SessionManager: add _invalidating flag to prevent concurrent 401 handling
- AuthInterceptor: check isSessionValid before invalidating
- LoginViewModel: update _uiState synchronously in logout(), clear token
before launching coroutine for prefs.clear()
- LoginViewModel: call SessionManager.restoreSession() after successful login
- MainActivity: remove restoreSession() from logout handler (was incorrectly
re-validating session right after invalidation)
Bump to v1.7.1 (versionCode 10)
Some locales use comma as decimal separator (e.g., es_DO.UTF-8).
This caused invalid JSON like "cpu": 2,4 instead of "cpu": 2.4.
Prefixed CPU and RAM commands with LC_ALL=C to force dot decimals.
Also removed debug logging from pushMetrics endpoint.
The inner PAYLOAD heredoc had a quoted delimiter ('PAYLOAD') which
prevented runtime variable expansion. Changed to unquoted (PAYLOAD)
so $AGENT_KEY, $CPU, etc. are correctly expanded at runtime.
The double-escaping works as:
install time (outer EOF): $AGENT_KEY → (in file)
runtime (inner PAYLOAD): → abc123... (actual value)
The unquoted EOF heredoc expands all $ at install time. Runtime
variables inside functions (CPU, RAM, AGENT_KEY, etc.) must be
escaped with $ to prevent premature expansion.
Also removed the fragile sed replace step entirely — values are now
embedded directly when the heredoc is expanded.
AgentService now replaces 'localhost' in the server URL with
sysadmin.lan before sending to the remote agent. Also added
URL/hostname debug output to the agent startup log.
- Removed 'set -e' so a failing metric collection command doesn't kill
the agent process
- Added '|| true' to every command in collect_metrics() to prevent
pipeline failures
- Fixed push_metrics() to capture curl exit code with 'local code; code=$(...) || code=0'
- The agent now keeps running even if individual metrics or push fail
The values SERVER_URL, AGENT_KEY, INTERVAL are now written directly
into the agent bash script using @placeholder@ substitution with sed.
No config file lookup needed — eliminates the 'not configured' error.
Config file directory (/etc/servermanager, ~/.config/servermanager)
is no longer created or referenced.
The systemd service runs as root so /root is /root, but config is
written to /etc/servermanager/agent.conf. The agent now tries:
1. Path from command-line argument
2. /etc/servermanager/agent.conf (system mode)
3. $HOME/.config/servermanager/agent.conf (user mode)
Service ExecStart and cron job both pass the config path explicitly.
Installer now detects at runtime if it can get root access:
- Root/NOPASSWD sudo: installs to system directories with systemd
- No root: installs to ~/.local/bin with @reboot cron job
Self-elevation uses 'script -qc sudo bash' for requiretty, or falls
back to user mode without hanging on password prompts.
Uninstaller checks both system and user paths.
- agent-install.sh and agent-uninstall.sh now detect if running as root
at startup and re-execute via 'script -qc sudo bash' if needed
- AgentService commands simplified: just write to /tmp and run bash,
the scripts handle privilege escalation internally
- Fixes exit code propagation: the script's exit code now correctly
flows back through exec -> script -> sudo -> bash -> SSH
- Revert SSHService exec() back to original (remove PTY parameter)
- AgentService now writes installer to /tmp/ first, then runs via
script -q -c to create a PTY for sudo (handles requiretty)
- Falls back to direct sudo bash if script is unavailable
- Cleans up temp files after execution
- Added mkdir -p for /usr/local/bin in installer
- Added file verification and byte-count after writing agent binary
The remote server has 'Defaults requiretty' in sudoers, which prevents
sudo from running without a TTY. Modified SSHService::exec() to accept
an optional parameter that allocates a pseudo-terminal, and
extracts the exit status from output when PTY is used (since phpseclib
doesn't return exit status in PTY mode).
The sudo -n check was incorrectly detecting that sudo required a
password, causing fallback to plain bash without root permissions.
Now we always run the installer via sudo bash, consistent with how
all other SSH commands work (systemctl, reboot, etc.).
- AgentService: try sudo -n first, fall back to bash without sudo
- AgentService: add sudo to uninstall command
- AgentService: trim error output to last 20 lines for readability
- View: show SSH output inline in error toast
- Fix undefined $path variable in success path
- Add database migration 006 for agent_key, agent_installed, agent_install_path, agent_last_seen_at columns
- Add Server model methods: findByAgentKey, generateAgentKey, regenerateAgentKey, updateAgentLastSeen, setAgentInstalled
- Auto-generate agent_key on server creation
- Create AgentService for SSH-based install/uninstall of remote agent
- Create bin/agent.sh - bash agent script for remote servers
- Create install/agent-install.sh and install/agent-uninstall.sh
- Create install/servermanager-agent.service systemd unit
- Add POST /api/metrics/push endpoint (auth via agent_key)
- Add web routes for agent install/uninstall/regenerate-key
- Add agent status section to server detail view with install/uninstall modals
- Make MonitoringService::saveMetrics() public for reuse