Commit Graph

52 Commits

Author SHA1 Message Date
d231af1c91 fix: remove invalid priority field from AndroidNotification in FCM v1 payload 2026-06-13 14:41:56 -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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
96a73384c4 fix: allow dots in service names for systemd (nginx.service, ssh.service) 2026-06-09 19:25:14 -04:00
3e13ac60f1 feat: v1.6.0 — fix service 500, search bar, modal redesign
- Fix 500 error: cast to string before preg_replace
- Search bar to filter services by name/description
- Redesigned service action modal with status badge + colored buttons
- Version 1.6.0
2026-06-09 19:20:18 -04:00
c4277c937f feat: v1.5.0 + service actions + remove duplicate apk
- Services tab: start/stop/restart/reload via dialog
- Backend: POST /api/servers/:id/service-action
- Removed duplicate APK download from dashboard view
- Version bump to 1.5.0
2026-06-09 19:02:41 -04:00
af3315e6ab feat: dashboard version local, bottom nav simplificado, services+actions tabs
- Dashboard muestra version del gradle (no API)
- Header sin iconos duplicados (notifications solo en bottom nav)
- Bottom nav: Dashboard, Servers, Alerts (sin Profile ni Logout)
- ServerDetail: tab Services con lista de systemd services
- ServerDetail: tab Actions con Test/Reboot/Shutdown
- Backend: API endpoints para services, reboot, shutdown, test
2026-06-09 18:15:04 -04:00
13f14416a4 feat: teams redesign — cards, filter, user autocomplete
- Teams index: cards with animation, search filter
- Team detail: user autocomplete via AJAX (search on type)
- CSS: card grid styles, autocomplete dropdown
- Backend: searchUsers endpoint, filter support
2026-06-09 18:11:20 -04:00
a43e0ae3a4 feat: instant notifications + Notifications screen
- NotificationPoller polls every 30s while app is in foreground
- Poller auto-starts on resume, stops on pause
- New NotificationsScreen with mark-as-read, mark-all-read, infinite scroll
- Bottom nav: Alerts tab with unread badge
- Dashboard: bell icon with unread badge
- Backend: GET /api/notifications/unread-count endpoint
- App version 1.1.0
2026-06-09 17:51:55 -04:00
d1c36d4642 fix: AuditService::log() requires 3 args (action, entityType, entityId) 2026-06-09 17:40:22 -04:00
ddec27ca5c fix: notification worker reliability + version bump to 1.1.0
- Bump android versionCode=2 versionName=1.1.0
- NotificationWorker: use UPDATE policy to reschedule after login
- Immediate notification check on login via LaunchedEffect
- Added logging for debugging notification delivery
- DB already has test notification inserted
- Fixed admin app version form (POST redirect)
2026-06-09 17:31:26 -04:00
086bbda6e8 fix: app version form now uses POST redirect with flash message instead of AJAX 2026-06-09 17:15:47 -04:00
53e9b92a8d feat: admin app version mgmt + notification system
- New tables: app_config, notifications
- Admin views: App Version editor, Notification sender
- API: GET /api/notifications, POST /api/notifications/:id/read, POST /api/notifications/read-all
- App version now stored in DB, editable by super admin
- Notifications: send to all users or specific user
- Android notification models + API client
- Sidebar: App Version + Notifications links for super_admin
2026-06-09 17:06:22 -04:00
3a4e8eb52c feat: add auto-update mechanism with in-app APK download 2026-06-09 16:59:08 -04:00
a424120770 fix: remove leftover conflict marker in ApiController 2026-06-09 16:46:03 -04:00
1241d50a4a Merge branch 'master' into feat/android-app-redesign 2026-06-09 16:41:23 -04:00
b613e43ab3 feat: redesign Android app with animations, charts, profile, and registration
- Register screen with validation (username, email, password, confirm)
- Profile screen with email editing and password change
- User greeting on dashboard showing logged-in username
- 401 auto-logout via SessionManager + AuthInterceptor
- MetricsChart with Canvas line graphs (CPU, RAM, Disk)
- Animated StatCard with counters and icons
- ServerCard with progress bars, pulsating status dot, dynamic elevation
- Shimmer loading placeholders
- Dark mode support with Material3
- Navigation transitions (slide + fade)
- New server rack launcher icon
- Splash screen
- Backend API: register, profile (GET/PUT) endpoints
2026-06-09 16:38:04 -04:00
d77d340fe0 Merge remote-tracking branch 'origin/master' into feature/login-endpoint
# Conflicts:
#	public/sysadmin.apk
2026-06-07 18:10:16 -04:00
470f2e9404 Change Android login to username/password
- Add POST /api/auth/login endpoint on server side
- Update Android login screen to username/password fields
- Hardcode production URL https://servermanager.devlab.lat
- Rebuild APK with login changes
2026-06-07 18:05:00 -04:00
1741545318 Add soft deletes via status column
- Add status ENUM('active','disabled','deleted') to 8 tables
- Change all physical DELETE to UPDATE status='deleted'
- All SELECT queries filter by status='active'
- toggleActive now alternates between active/disabled status
- Exceptions: monitoring_history, rate_limits, sessions keep physical purge
- audit_logs remains immutable
2026-06-07 17:26:29 -04:00
799df8d6c8 Fix MySQL timezone — sync session timezone with app config 2026-06-07 13:14:07 -04:00
cd24cc53a0 Add per-column filters, smart pagination (25/page), and date range filter to audit logs 2026-06-07 12:43:50 -04:00
4448dec60d Filter recent activity to show only current user's activity 2026-06-07 12:25:04 -04:00
b105b27124 Dashboard stats now scoped to user's accessible servers; super_admin sees all 2026-06-07 07:43:37 -04:00
016584e907 Restrict Users, Audit Logs, Security routes to super_admin only
- Sidebar: Users, Audit Logs and Security now only visible to super_admin
- AdminController: requireSuperAdmin() check on users() and auditLogs()
- AdminController: super_admin check on createUser, updateUser, deleteUser JSON endpoints
- securitySettings() already had the check in place
2026-06-07 07:34:16 -04:00
e721a34d8f Filter teams by creator: users only see and manage their own teams 2026-06-07 07:23:47 -04:00
5607a3e507 Replace old team system with work teams (equipos de trabajo)
- New migration 003_teams.sql: teams, team_user, team_server tables
- New Team model with CRUD, members, and server assignment
- New TeamController with full team management
- New views: teams/index, teams/create, teams/show
- Server model: getUserRole and getAccessibleServerIds now include team-based access
- Sidebar: Teams link under Administration (admin+ only)
- Routes: old /team routes removed, new /teams routes added
- Removed old ServerController team methods and views
- Fixed sidebar empty accessible IDs returning all servers
- Fixed dashboard and API to show empty when no accessible servers
2026-06-07 07:08:26 -04:00
5f5de248c6 Add server ownership, team access, role-based UI, and default admin role
- New migration 002_server_access.sql: created_by column + server_user table
- Server model: ownership, permission checks, team management methods
- Routes: /team routes, RoleMiddleware on /servers/create and /admin
- ServerController: permission checks via server_user, team CRUD methods
- TerminalController: server access checks
- DashboardController: filter servers by accessible ones
- ApiController: server access checks
- Views: team.php, team_server.php, sidebar Team link, hide actions by role
- Default user role changed from operator to admin on registration
- Admin user form defaults to admin role
2026-06-07 06:49:12 -04:00
516ffbb579 Add user registration page with default operator role 2026-06-06 19:41:03 -04:00
7a8792a1e9 Add services management with pagination and conditional buttons 2026-06-06 18:56:42 -04:00
e0c4ec7a53 Fix system users: sudoers and nested sudo 2026-06-06 18:43:24 -04:00