ServerService::buildNodeConfig($node)]); } /** * Push all users to all nodes in the group */ public static function notifyUsersUpdatedByGroup(int $groupId): void { $servers = Server::whereJsonContains('group_ids', (string) $groupId) ->get(); foreach ($servers as $server) { if (!self::isNodeOnline($server->id)) continue; $users = ServerService::getAvailableUsers($server)->toArray(); self::push($server->id, 'sync.users', ['users' => $users]); } } /** * Push user changes (add/remove) to affected nodes */ public static function notifyUserChanged(User $user): void { if (!$user->group_id) return; $servers = Server::whereJsonContains('group_ids', (string) $user->group_id)->get(); foreach ($servers as $server) { if (!self::isNodeOnline($server->id)) continue; if ($user->isAvailable()) { self::push($server->id, 'sync.user.delta', [ 'action' => 'add', 'users' => [ [ 'id' => $user->id, 'uuid' => $user->uuid, 'speed_limit' => $user->speed_limit, 'device_limit' => $user->device_limit, ] ], ]); } else { self::push($server->id, 'sync.user.delta', [ 'action' => 'remove', 'users' => [['id' => $user->id]], ]); } } } /** * Push user removal from a specific group's nodes */ public static function notifyUserRemovedFromGroup(int $userId, int $groupId): void { $servers = Server::whereJsonContains('group_ids', (string) $groupId) ->get(); foreach ($servers as $server) { if (!self::isNodeOnline($server->id)) continue; self::push($server->id, 'sync.user.delta', [ 'action' => 'remove', 'users' => [['id' => $userId]], ]); } } /** * Full sync: push config + users to a node */ public static function notifyFullSync(int $nodeId): void { if (!self::isNodeOnline($nodeId)) return; $node = Server::find($nodeId); if (!$node) return; self::push($nodeId, 'sync.config', ['config' => ServerService::buildNodeConfig($node)]); $users = ServerService::getAvailableUsers($node)->toArray(); self::push($nodeId, 'sync.users', ['users' => $users]); } /** * Publish a push command to Redis — picked up by the Workerman WS server */ private static function push(int $nodeId, string $event, array $data): void { try { Redis::publish('node:push', json_encode([ 'node_id' => $nodeId, 'event' => $event, 'data' => $data, ])); } catch (\Throwable $e) { Log::warning("[NodePush] Redis publish failed: {$e->getMessage()}", [ 'node_id' => $nodeId, 'event' => $event, ]); } } }