Commit Graph

133 Commits

Author SHA1 Message Date
e37ff8aa9f fix: register FCM token on app startup and improve error logging 2026-06-13 14:08:07 -04:00
17d565a25b fix: actively fetch FCM token on login if not cached
- LoginViewModel now calls Tasks.await(FirebaseMessaging.getInstance().token)
  to actively fetch the FCM token if not yet saved in prefs
- Previous code relied solely on onNewToken callback, which may not fire
  before the user logs in on a fresh install
2026-06-13 14:08:07 -04:00
83b780f66a fix: FCM push not sending — add v1 API + better diagnostics
- FCMService now supports both HTTP v1 API (service account) and legacy API (server key)
- v1 API uses OAuth2 JWT auth with Firebase service account; tries this first
- Falls back to legacy HTTP API if only server key is configured
- Removes invalid tokens from DB automatically (NotRegistered, INVALID_ARGUMENT, etc.)
- AdminController now logs push result status and shows in UI response
- .env updated with FCM_SERVER_KEY placeholder and docs
- Migration 010 run in dev
2026-06-13 14:08:07 -04:00
26e1e1155e chore: add real Firebase config and messaging dependency
- google-services.json now has real Firebase project values
- Added firebase-messaging-ktx dependency (was missing)
- Updated google-services plugin to 4.4.4
2026-06-13 14:08:07 -04:00
51e40d22f5 feat: Firebase Cloud Messaging push notifications
Backend:
- New user_fcm_tokens table (migration 010) for storing device tokens
- FCMService sends push via Firebase HTTP legacy API (server key from .env)
- POST /api/fcm/register endpoint for Android to register its FCM token
- AdminController::sendNotification() triggers FCM push after DB insert
- FCM config added to config.php

Android:
- Firebase Cloud Messaging service (ServerManagerFirebaseService)
  receives push notifications and shows them via NotificationHelper
- FCM token registered with backend on new token and on login
- google-services.json template (must be replaced with real Firebase config)
- firebase-setup.sh script with configuration instructions
- FCM dependency + google-services plugin added to Gradle
- Bump to v1.8.0 (versionCode 12)
2026-06-13 14:08:07 -04:00
297a0153e0 feat(android): foreground service for real-time notification polling
- New NotificationForegroundService: persistent foreground coroutine-based
  polling every 30s, keeps app alive in background
- Manifest: FOREGROUND_SERVICE + FOREGROUND_SERVICE_DATA_SYNC permissions,
  service declaration with foregroundServiceType=dataSync
- NotificationHelper: added silent channel for persistent service notification
- ServerManagerApp: starts foreground service on app launch
- MainActivity: removed redundant NotificationWorker.checkNow (service handles
  background polling; NotificationPoller still handles in-app refresh)
- Bump to v1.8.0 (versionCode 12)
2026-06-13 14:08:07 -04:00
71d6078329 refactor: drop is_read and read_at from notifications table
- is_read and read_at columns are now obsolete since notification_reads
  tracks per-user read receipts
- Removed UPDATE statements in markAsRead/markAllAsRead that touched
  the old columns
- API response still includes computed is_read field via SQL CASE in
  getForUser() JOIN with notification_reads, maintaining Android
  compatibility
- Migration 009 drops both columns
2026-06-13 14:08:07 -04:00
5cc5303826 feat: track notification read receipts per user
- New notification_reads table (migration 008) with unique (notification_id, user_id)
- Notification model: markAsRead inserts into notification_reads; getForUser JOINs
  to show per-user read status; getUnreadCount checks NOT EXISTS in reads table
- getAll() includes read_count subquery for admin view
- Admin view shows 'X / Y' read count for broadcast notifications,
  'Read'/'Unread' badge for user-targeted ones
- Migrates existing read notifications into notification_reads
- AGENTS.md updated with new conventions
2026-06-13 14:08:07 -04:00
1e01961462 fix: remove duplicate old() function causing 500 on create page
- 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
2026-06-13 14:08:07 -04:00
e99615c162 fix: handle missing server_permissions table gracefully
- 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)
2026-06-13 14:08:06 -04:00
c4118c6e6a feat: validate required SSH permissions before creating server
- 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
2026-06-13 14:08:06 -04:00
42f3f64b69 feat: add aggregated resource usage chart to dashboard 2026-06-13 14:08:06 -04:00
6885f643fa Bump app version to 1.7.2 (versionCode 11) and rebuild APK 2026-06-13 14:08:06 -04:00
84d3592bec fix: remove bottom nav padding on server detail screen
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.
2026-06-13 14:08:06 -04:00
d7953727a6 fix: 401 logout loop — sync state update, prevent re-navigation
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)
2026-06-13 14:08:06 -04:00
b8298024fb feat: start agent immediately in background after user-mode install
After installing cron, the agent now starts right away via
nohup ... & disown instead of waiting for the next boot.
2026-06-13 14:08:06 -04:00
38ac2dc891 fix: force LC_ALL=C for metric commands to avoid comma decimal separator
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.
2026-06-13 14:08:06 -04:00
23dc96ae64 fix: use warning level for push metrics log 2026-06-13 14:08:06 -04:00
a3270877dd fix: add logging to pushMetrics endpoint for debugging 401 2026-06-13 14:08:06 -04:00
7eaeb5f08b fix: unquote PAYLOAD heredoc so runtime vars are expanded
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)
2026-06-13 14:08:06 -04:00
4b8fa1948a fix: escape runtime vars in unquoted heredoc, eliminate sed dependency
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.
2026-06-13 14:08:06 -04:00
25caf2b596 fix: use ['HTTP_HOST'] dynamically for agent URL 2026-06-13 14:08:05 -04:00
8abeb9a2d8 fix: use correct server URL instead of localhost
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.
2026-06-13 14:08:05 -04:00
1c98d7aa35 fix: remove set -e from agent, guard all commands with || true
- 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
2026-06-13 14:08:05 -04:00
1a72b3ba50 fix: embed config directly in agent script, remove external config file
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.
2026-06-13 14:08:05 -04:00
8697f37862 fix: agent config search order — 1st arg > /etc/ > ~/.config/
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.
2026-06-13 14:08:05 -04:00
aae01b0a1e Revert "fix: agent now searches both /etc/servermanager and ~/.config for config"
This reverts commit fe997750ff.
2026-06-13 14:08:05 -04:00
3a263a692f Merge pull request 'fix: agent now searches both /etc/servermanager and ~/.config for config' (#42) from feat/push-monitoring-agent into master
Reviewed-on: #42
2026-06-11 19:27:27 -04:00
fe997750ff fix: agent now searches both /etc/servermanager and ~/.config for config
The embedded agent binary checks /etc/servermanager/agent.conf first
(system mode), then falls back to ~/.config/servermanager/agent.conf
(user mode).
2026-06-11 19:24:25 -04:00
20e3a6926f Merge pull request 'fix: dual-mode installer — works with or without sudo' (#41) from feat/push-monitoring-agent into master
Reviewed-on: #41
2026-06-11 19:14:59 -04:00
fd3cc6825c fix: dual-mode installer — works with or without sudo
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.
2026-06-11 19:12:57 -04:00
76180271ce Merge pull request 'feat: push-based monitoring with remote agent (self-elevating installer)' (#40) from feat/push-monitoring-agent into master
Reviewed-on: #40
Reviewed-by: rafaga21 <rafaelminaya20@hotmail.com>
2026-06-11 19:09:09 -04:00
0edc882f2e fix: installer self-elevates with script PTY instead of external sudo chain
- 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
2026-06-11 19:07:14 -04:00
08ee59663b Merge pull request 'fix: use script command for PTY instead of modifying SSHService' (#39) from feat/push-monitoring-agent into master
Reviewed-on: #39
2026-06-11 19:02:34 -04:00
0ce24c4b8f fix: use script command for PTY instead of modifying SSHService
- 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
2026-06-11 19:01:27 -04:00
00062e4d4a Merge pull request 'fix: allocate PTY for agent install to work with sudo requiretty' (#38) from feat/push-monitoring-agent into master
Reviewed-on: #38
2026-06-11 18:53:19 -04:00
63bf125420 fix: allocate PTY for agent install to work with sudo requiretty
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).
2026-06-11 18:52:40 -04:00
5f6788970f Merge pull request 'feat: push-based monitoring with remote agent' (#37) from feat/push-monitoring-agent into master
Reviewed-on: #37
Reviewed-by: rafaga21 <rafaelminaya20@hotmail.com>
2026-06-11 18:50:21 -04:00
f4c534c020 fix: always use sudo for agent install (remove fallback check)
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.).
2026-06-11 18:48:23 -04:00
28d2dfecce Merge pull request 'fix: add sudo fallback to agent installer + improve error output' (#36) from feat/push-monitoring-agent into master
Reviewed-on: #36
2026-06-11 18:46:13 -04:00
85bb70fe0f fix: add sudo fallback to agent installer + improve error output
- 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
2026-06-11 18:45:14 -04:00
16044445d4 Merge pull request 'feat: push-based monitoring with remote agent' (#35) from feat/push-monitoring-agent into master
Reviewed-on: #35
2026-06-11 18:31:47 -04:00
bfa102cf97 feat: push-based monitoring with remote agent
- 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
2026-06-11 18:28:22 -04:00
fcd8565890 Merge pull request 'fix: filtrar servidores por usuario/equipo segun rol' (#34) from fix/server-access-filtering into master
Reviewed-on: #34
Reviewed-by: rafaga21 <rafaelminaya20@hotmail.com>
2026-06-11 17:27:23 -04:00
6e4bf2ad1d build: compile APK v1.7.0 (versionCode 9) 2026-06-11 17:26:54 -04:00
1bc8236d4d fix: filtrar servidores por usuario/equipo según rol
- Server::getAccessibleServerIds(): super_admin ve todos los servidores;
  admin y operator solo ven propios, asignados directos o por equipo
- DashboardController: unifica lógica usando getAccessibleServerIds()
- ServerController::metrics(): agrega control de acceso requireServerAccess()
- ApiController::status(): filtra estadísticas por servidores accesibles
- Android: bump versionCode 9, versionName 1.7.0
2026-06-11 13:09:53 -04:00
830161ad87 Merge pull request 'docs: update AGENTS.md — verified against current codebase' (#33) from feat/android-v1.6.0 into master
Reviewed-on: #33
2026-06-09 19:49:58 -04:00
dec6aa3527 docs: update AGENTS.md — verified against current codebase 2026-06-09 19:49:15 -04:00
d52cb9ae8b Merge pull request 'fix: show stopped services in list + loading bar on action' (#32) from feat/android-v1.6.0 into master
Reviewed-on: #32
2026-06-09 19:32:00 -04:00
4d00f0d8b1 bump to 1.6.1 2026-06-09 19:31:42 -04:00