整理文件
This commit is contained in:
@@ -5,11 +5,10 @@ namespace Plugin\UserOnlineDevices\Controllers;
|
||||
use App\Http\Controllers\PluginController;
|
||||
use App\Models\User;
|
||||
use App\Services\DeviceStateService;
|
||||
use DateTimeInterface;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
||||
class UserOnlineDevicesController extends PluginController
|
||||
{
|
||||
@@ -18,231 +17,107 @@ class UserOnlineDevicesController extends PluginController
|
||||
) {
|
||||
}
|
||||
|
||||
public function summary(Request $request)
|
||||
public function index(Request $request): View
|
||||
{
|
||||
if ($error = $this->beforePluginAction()) {
|
||||
return $this->fail($error);
|
||||
abort($error[0], $error[1]);
|
||||
}
|
||||
|
||||
return $this->success($this->buildPayload($request->user()));
|
||||
}
|
||||
$defaultPageSize = (int) $this->getConfig('default_page_size', 20);
|
||||
$pageSize = (int) $request->integer('per_page', $defaultPageSize);
|
||||
$pageSize = in_array($pageSize, [20, 50, 100], true) ? $pageSize : $defaultPageSize;
|
||||
|
||||
public function panelUrl(Request $request)
|
||||
{
|
||||
if ($error = $this->beforePluginAction()) {
|
||||
return $this->fail($error);
|
||||
}
|
||||
|
||||
$user = $request->user();
|
||||
$ttlMinutes = max(1, (int) $this->getConfig('signed_url_ttl_minutes', 60));
|
||||
$expiresAt = now()->addMinutes($ttlMinutes);
|
||||
|
||||
return $this->success([
|
||||
'url' => URL::temporarySignedRoute(
|
||||
'user-online-devices.panel',
|
||||
$expiresAt,
|
||||
['user' => $user->id]
|
||||
),
|
||||
'expires_at' => $expiresAt->timestamp,
|
||||
]);
|
||||
}
|
||||
|
||||
public function adminUsersPage(): View
|
||||
{
|
||||
abort_unless($this->isPluginEnabled(), 404);
|
||||
|
||||
$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', ''));
|
||||
$keyword = trim((string) $request->query('keyword', ''));
|
||||
|
||||
$query = User::query()
|
||||
->select(['id', 'email', 'last_online_at', 'created_at']);
|
||||
->select([
|
||||
'id',
|
||||
'email',
|
||||
'plan_id',
|
||||
'online_count',
|
||||
'last_online_at',
|
||||
'created_at',
|
||||
])
|
||||
->with(['plan:id,name'])
|
||||
->orderByDesc('id');
|
||||
|
||||
if ($keyword !== '') {
|
||||
$query->where(function ($builder) use ($keyword) {
|
||||
$builder->where('email', 'like', '%' . $keyword . '%');
|
||||
if (is_numeric($keyword)) {
|
||||
|
||||
if (ctype_digit($keyword)) {
|
||||
$builder->orWhere('id', (int) $keyword);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$users = $query
|
||||
->orderByDesc('last_online_at')
|
||||
->orderByDesc('id')
|
||||
->paginate($pageSize, ['*'], 'page', $current);
|
||||
/** @var LengthAwarePaginator $paginator */
|
||||
$paginator = $query->paginate($pageSize)->appends($request->query());
|
||||
|
||||
return $this->success($this->buildAdminUsersPayload($users));
|
||||
}
|
||||
$userIds = $paginator->getCollection()->pluck('id')->all();
|
||||
$devicesByUser = $this->deviceStateService->getUsersDevices($userIds);
|
||||
|
||||
public function panel(Request $request, int $user): View
|
||||
{
|
||||
abort_unless($this->isPluginEnabled(), 404);
|
||||
$paginator->setCollection(
|
||||
$paginator->getCollection()->map(function (User $user) use ($devicesByUser) {
|
||||
$ips = array_values($devicesByUser[$user->id] ?? []);
|
||||
sort($ips);
|
||||
|
||||
$targetUser = User::query()->findOrFail($user);
|
||||
$payload = $this->buildPayload($targetUser);
|
||||
$ttlMinutes = max(1, (int) $this->getConfig('signed_url_ttl_minutes', 60));
|
||||
$user->online_devices = $ips;
|
||||
$user->online_count_live = count($ips);
|
||||
$user->subscription_name = $user->plan?->name ?: 'No subscription';
|
||||
$user->last_online_text = $this->formatDateTime($user->last_online_at);
|
||||
$user->created_text = $this->formatDateTime($user->created_at);
|
||||
|
||||
return view('UserOnlineDevices::panel', [
|
||||
'payload' => $payload,
|
||||
'snapshotUrl' => URL::temporarySignedRoute(
|
||||
'user-online-devices.snapshot',
|
||||
now()->addMinutes($ttlMinutes),
|
||||
['user' => $targetUser->id]
|
||||
),
|
||||
'refreshInterval' => max(5, (int) $this->getConfig('refresh_interval_seconds', 15)),
|
||||
return $user;
|
||||
})
|
||||
);
|
||||
|
||||
$pageUsers = $paginator->getCollection()->count();
|
||||
$usersWithOnlineIp = $paginator->getCollection()->filter(function (User $user) {
|
||||
return ($user->online_count_live ?? 0) > 0;
|
||||
})->count();
|
||||
$totalOnlineIps = $paginator->getCollection()->sum(function (User $user) {
|
||||
return (int) ($user->online_count_live ?? 0);
|
||||
});
|
||||
|
||||
$securePath = admin_setting('secure_path', admin_setting('frontend_admin_path', hash('crc32b', config('app.key'))));
|
||||
|
||||
return view('UserOnlineDevices::admin-index', [
|
||||
'users' => $paginator,
|
||||
'filters' => [
|
||||
'keyword' => $keyword,
|
||||
'per_page' => $pageSize,
|
||||
],
|
||||
'summary' => [
|
||||
'page_users' => $pageUsers,
|
||||
'users_with_online_ip' => $usersWithOnlineIp,
|
||||
'total_online_ips' => $totalOnlineIps,
|
||||
'current_page' => $paginator->currentPage(),
|
||||
],
|
||||
'adminHomeUrl' => url('/' . $securePath),
|
||||
]);
|
||||
}
|
||||
|
||||
public function snapshot(Request $request, int $user)
|
||||
private function formatDateTime(mixed $value): string
|
||||
{
|
||||
abort_unless($this->isPluginEnabled(), 404);
|
||||
|
||||
$targetUser = User::query()->findOrFail($user);
|
||||
|
||||
return response()->json([
|
||||
'data' => $this->buildPayload($targetUser),
|
||||
]);
|
||||
}
|
||||
|
||||
private function buildPayload(User $user): array
|
||||
{
|
||||
$devices = $this->deviceStateService->getUsersDevices([$user->id]);
|
||||
$ips = collect($devices[$user->id] ?? [])
|
||||
->filter(fn ($ip) => is_string($ip) && $ip !== '')
|
||||
->unique()
|
||||
->values();
|
||||
|
||||
return [
|
||||
'user' => [
|
||||
'id' => $user->id,
|
||||
'email' => $user->email,
|
||||
'online_count' => $ips->count(),
|
||||
'last_online_at' => $user->last_online_at,
|
||||
],
|
||||
'online_devices' => $this->formatOnlineDevices($ips),
|
||||
'active_sessions' => $this->shouldShowActiveSessions()
|
||||
? $this->formatActiveSessions($user)
|
||||
: [],
|
||||
'meta' => [
|
||||
'generated_at' => time(),
|
||||
'refresh_interval_seconds' => max(5, (int) $this->getConfig('refresh_interval_seconds', 15)),
|
||||
'mask_ip' => (bool) $this->getConfig('mask_ip', false),
|
||||
'show_active_sessions' => $this->shouldShowActiveSessions(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
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) {
|
||||
return [
|
||||
'name' => 'Device ' . ($index + 1),
|
||||
'ip' => $this->maskIp($ip),
|
||||
'raw_ip' => $ip,
|
||||
];
|
||||
})->all();
|
||||
}
|
||||
|
||||
private function formatActiveSessions(User $user): array
|
||||
{
|
||||
return $user->tokens()
|
||||
->where(function ($query) {
|
||||
$query->whereNull('expires_at')
|
||||
->orWhere('expires_at', '>', now());
|
||||
})
|
||||
->orderByDesc('last_used_at')
|
||||
->get(['id', 'name', 'last_used_at', 'created_at', 'expires_at'])
|
||||
->map(function ($token) {
|
||||
return [
|
||||
'id' => $token->id,
|
||||
'device' => $token->name ?: 'Unknown Session',
|
||||
'last_used_at' => $token->last_used_at?->timestamp,
|
||||
'created_at' => $token->created_at?->timestamp,
|
||||
'expires_at' => $token->expires_at?->timestamp,
|
||||
];
|
||||
})
|
||||
->all();
|
||||
}
|
||||
|
||||
private function shouldShowActiveSessions(): bool
|
||||
{
|
||||
return (bool) $this->getConfig('show_active_sessions', true);
|
||||
}
|
||||
|
||||
private function maskIp(string $ip): string
|
||||
{
|
||||
if (!(bool) $this->getConfig('mask_ip', false)) {
|
||||
return $ip;
|
||||
if ($value instanceof DateTimeInterface) {
|
||||
return $value->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
$parts = explode('.', $ip);
|
||||
$parts[3] = '*';
|
||||
return implode('.', $parts);
|
||||
}
|
||||
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
$parts = explode(':', $ip);
|
||||
$count = count($parts);
|
||||
if ($count > 2) {
|
||||
$parts[$count - 1] = '****';
|
||||
$parts[$count - 2] = '****';
|
||||
if (is_numeric($value)) {
|
||||
$timestamp = (int) $value;
|
||||
if ($timestamp > 0) {
|
||||
return date('Y-m-d H:i:s', $timestamp);
|
||||
}
|
||||
return implode(':', $parts);
|
||||
}
|
||||
|
||||
return $ip;
|
||||
if (is_string($value) && trim($value) !== '') {
|
||||
$timestamp = strtotime($value);
|
||||
if ($timestamp !== false) {
|
||||
return date('Y-m-d H:i:s', $timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user