Commit Graph

132 Commits

Author SHA1 Message Date
395c84f75e 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 13:23:29 -04:00
f1f263225f 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 13:11:12 -04:00
988ea118b0 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 13:00:54 -04:00
136eebc4c3 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 12:37:40 -04:00
bdc4d73d89 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 12:20:09 -04:00
80f5893d92 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 11:57:56 -04:00
12c3e23d9c 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 11:48:05 -04:00
b94ae16f69 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 11:31:52 -04:00
bb1192c164 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 10:55:14 -04:00
0cfed5bf27 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 10:41:18 -04:00
8f3b5bd7ce feat: add aggregated resource usage chart to dashboard 2026-06-11 21:46:33 -04:00
ae09af3e02 Bump app version to 1.7.2 (versionCode 11) and rebuild APK 2026-06-11 21:31:39 -04:00
c88b46ec80 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-11 21:26:34 -04:00
1885693f8d 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-11 21:12:39 -04:00
4bb383f127 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-11 20:55:15 -04:00
552e30751d 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-11 20:49:14 -04:00
2340fce8f8 fix: use warning level for push metrics log 2026-06-11 20:39:28 -04:00
ef23dd0b08 fix: add logging to pushMetrics endpoint for debugging 401 2026-06-11 20:39:14 -04:00
ddf4d744c6 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-11 20:35:24 -04:00
a1e3723a97 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-11 20:33:11 -04:00
26e35c6bc7 fix: use ['HTTP_HOST'] dynamically for agent URL 2026-06-11 20:28:35 -04:00
4d3a7e4d65 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-11 20:26:37 -04:00
cc0a0c0644 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-11 20:21:24 -04:00
b27e1392ca 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-11 19:44:30 -04:00
93d5f24d0f 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-11 19:38:10 -04:00
4ae9443016 Revert "fix: agent now searches both /etc/servermanager and ~/.config for config"
This reverts commit fe997750ff.
2026-06-11 19:33:29 -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
92bc12355c fix: show stopped services in list + loading bar on action
- API: --all flag in systemctl list-units to show inactive services
- API: removed head -50 limit
- Android: LinearProgressIndicator while service action is running
2026-06-09 19:29:36 -04:00