diff --git a/Xboard/plugins/UserOnlineDevices/Controllers/UserOnlineDevicesController.php b/Xboard/plugins/UserOnlineDevices/Controllers/UserOnlineDevicesController.php index b11dee5..7d390ef 100644 --- a/Xboard/plugins/UserOnlineDevices/Controllers/UserOnlineDevicesController.php +++ b/Xboard/plugins/UserOnlineDevices/Controllers/UserOnlineDevicesController.php @@ -7,6 +7,7 @@ use App\Models\User; use App\Services\DeviceStateService; use Illuminate\Contracts\View\View; use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Illuminate\Support\Facades\URL; @@ -46,11 +47,46 @@ class UserOnlineDevicesController extends PluginController ]); } - public function userStatus(): View + public function adminUsersPage(): View { abort_unless($this->isPluginEnabled(), 404); - return view('UserOnlineDevices::userstatus'); + $securePath = admin_setting('secure_path', admin_setting('frontend_admin_path', hash('crc32b', config('app.key')))); + + return view('UserOnlineDevices::admin-users', [ + 'apiEndpoint' => url('/api/v2/' . $securePath . '/user-online-devices/users'), + 'adminHomeUrl' => url('/' . $securePath), + ]); + } + + public function adminUsers(Request $request) + { + if ($error = $this->beforePluginAction()) { + return $this->fail($error); + } + + $current = max(1, (int) $request->input('current', 1)); + $pageSize = min(100, max(10, (int) $request->input('pageSize', 20))); + $keyword = trim((string) $request->input('keyword', '')); + + $query = User::query() + ->select(['id', 'email', 'last_online_at', 'created_at']); + + if ($keyword !== '') { + $query->where(function ($builder) use ($keyword) { + $builder->where('email', 'like', '%' . $keyword . '%'); + if (is_numeric($keyword)) { + $builder->orWhere('id', (int) $keyword); + } + }); + } + + $users = $query + ->orderByDesc('last_online_at') + ->orderByDesc('id') + ->paginate($pageSize, ['*'], 'page', $current); + + return $this->success($this->buildAdminUsersPayload($users)); } public function panel(Request $request, int $user): View @@ -111,6 +147,43 @@ class UserOnlineDevicesController extends PluginController ]; } + private function buildAdminUsersPayload(LengthAwarePaginator $users): array + { + $items = $users->getCollection(); + $devicesByUser = $this->deviceStateService->getUsersDevices($items->pluck('id')->all()); + + $users->setCollection($items->map(function (User $user) use ($devicesByUser) { + $ips = collect($devicesByUser[$user->id] ?? []) + ->filter(fn ($ip) => is_string($ip) && $ip !== '') + ->unique() + ->values(); + + return [ + 'id' => $user->id, + 'email' => $user->email, + 'online_count' => $ips->count(), + 'online_devices' => $this->formatOnlineDevices($ips), + 'last_online_at' => $user->last_online_at?->timestamp, + 'created_at' => $user->created_at?->timestamp, + ]; + })); + + return [ + 'list' => $users->items(), + 'pagination' => [ + 'current' => $users->currentPage(), + 'pageSize' => $users->perPage(), + 'total' => $users->total(), + 'lastPage' => $users->lastPage(), + ], + 'meta' => [ + 'generated_at' => time(), + 'refresh_interval_seconds' => max(5, (int) $this->getConfig('refresh_interval_seconds', 15)), + 'mask_ip' => (bool) $this->getConfig('mask_ip', false), + ], + ]; + } + private function formatOnlineDevices(Collection $ips): array { return $ips->values()->map(function (string $ip, int $index) { diff --git a/Xboard/plugins/UserOnlineDevices/README.md b/Xboard/plugins/UserOnlineDevices/README.md index f00a5be..4abcf73 100644 --- a/Xboard/plugins/UserOnlineDevices/README.md +++ b/Xboard/plugins/UserOnlineDevices/README.md @@ -1,26 +1,25 @@ # User Online Devices Plugin -This plugin adds a user-facing online device dashboard for Xboard. +This plugin provides online IP monitoring for Xboard users and administrators. ## Features - Shows the current online device count based on Xboard's built-in `DeviceStateService` -- Shows the current online IP list for the logged-in user -- Optionally shows Sanctum login sessions -- Provides an authenticated API endpoint for the frontend to fetch the dashboard URL -- Provides a temporary signed dashboard page that auto-refreshes +- Provides an administrator page to inspect all users' online IP counts +- Provides an administrator API endpoint with paginated user online IP data +- Provides a temporary signed dashboard page for a single user snapshot ## Routes - `GET /api/v1/user-online-devices/summary` - `GET /api/v1/user-online-devices/panel-url` -- `GET /userstatus` -- `GET /user-online-devices/userstatus` +- `GET /api/v2/{secure_path}/user-online-devices/users` +- `GET /{secure_path}/user-online-devices` - `GET /user-online-devices/panel/{user}` (temporary signed URL) ## Notes - This plugin reuses Xboard's existing real-time device state data from Redis. -- In current Xboard releases, plugin development is primarily backend-oriented, so this plugin ships a standalone user-side page instead of patching the compiled SPA bundle directly. +- In current Xboard releases, plugin development is primarily backend-oriented, so this plugin ships standalone pages instead of patching the compiled SPA bundle directly. - The "online device" count is effectively the number of unique online IPs reported by nodes. -- The easiest user entry is now `/userstatus`, which reads the current Xboard login token and shows the current user's own status. +- The administrator entry is `/{secure_path}/user-online-devices`, which reads the current admin login token and shows paginated user online IP data. diff --git a/Xboard/plugins/UserOnlineDevices/resources/views/admin-users.blade.php b/Xboard/plugins/UserOnlineDevices/resources/views/admin-users.blade.php new file mode 100644 index 0000000..5531ad5 --- /dev/null +++ b/Xboard/plugins/UserOnlineDevices/resources/views/admin-users.blade.php @@ -0,0 +1,612 @@ + + + +
+ + +| ID | +Online IP Count | +Online IP List | +Last Online | +Created | +
|---|