Browse Source

Remove ping testing

BrettonYe 1 year ago
parent
commit
8a327f1b8a

+ 0 - 22
app/Http/Controllers/Admin/NodeController.php

@@ -255,26 +255,4 @@ class NodeController extends Controller
     { // 节点流量监控
         return view('admin.node.monitor', array_merge(['nodeName' => $node->name, 'nodeServer' => $node->server], $this->DataFlowChart($node->id, true)));
     }
-
-    public function pingNode(Node $node): JsonResponse
-    { // Ping节点延迟
-        if ($node->is_ddns) {
-            if ($result = (new NetworkDetection)->ping($node->server)) {
-                return Response::json(['status' => 'success', 'message' => $result]);
-            }
-        } else {
-            $msg = null;
-            foreach ($node->ips() as $ip) {
-                $ret = (new NetworkDetection)->ping($ip);
-                if ($ret !== false) {
-                    $msg .= $ret.' <hr>';
-                }
-            }
-            if (isset($msg)) {
-                return Response::json(['status' => 'success', 'message' => $msg]);
-            }
-        }
-
-        return Response::json(['status' => 'fail', 'message' => 'Ping访问失败']);
-    }
 }

+ 4 - 83
app/Utils/NetworkDetection.php

@@ -11,29 +11,6 @@ class NetworkDetection
 {
     private static PendingRequest $basicRequest;
 
-    public function ping(string $ip): ?string
-    { // 用外部API进行Ping检测. TODO: 无权威外部API,功能缺失
-        $testers = ['oiowebPing', 'xiaoapiPing', 'yum6Ping'];
-        self::$basicRequest = Http::timeout(20)->withOptions(['http_errors' => false])->withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36');
-
-        foreach ($testers as $tester) {
-            try {
-                if (method_exists(self::class, $tester)) {
-                    $result = $this->$tester($ip);
-                    if ($result !== null) {
-                        return $result;
-                    }
-                }
-            } catch (Exception $e) {
-                Log::error("[$tester] 网络延迟测试报错: ".$e->getMessage());
-
-                continue;
-            }
-        }
-
-        return null;
-    }
-
     public function networkStatus(string $ip, int $port): ?array
     {
         $status = $this->networkCheck($ip, $port);
@@ -84,66 +61,10 @@ class NetworkDetection
         return null;
     }
 
-    private function oiowebPing(string $ip)
-    {
-        $msg = null;
-        foreach ([1, 6, 14] as $line) {
-            $response = self::$basicRequest->get("https://api.oioweb.cn/api/hostping.php?host=$ip&node=$line"); // https://api.iiwl.cc/api/ping.php?host=
-
-            // 发送成功
-            if ($response->ok()) {
-                $message = $response->json();
-                if ($message && $message['code']) {
-                    $msg .= "{$message['node']}:{$message['data']['Time']}<br>";
-                }
-            } else {
-                return false;
-            }
-        }
-
-        if ($msg) {
-            return $msg;
-        }
-        Log::warning('【PING】检测'.$ip.'时,api.oioweb.cn无结果');
-
-        // 发送错误
-        return false;
-    }
-
-    private function xiaoapiPing(string $ip)
-    { // 开发依据 https://xiaoapi.cn/?action=doc&id=3
-        $response = self::$basicRequest->get("https://xiaoapi.cn/API/sping.php?url=$ip");
-
-        // 发送成功
-        if ($response->ok()) {
-            return $response->body();
-        }
-        Log::warning("【PING】检测{$ip}时,xiaoapi.cn无结果");
-
-        // 发送错误
-        return false;
-    }
-
-    private function yum6Ping(string $ip)
-    { // 来源 https://api.yum6.cn/ping.php?host=api.yum6.cn
-        $response = self::$basicRequest->get("https://api.yum6.cn/ping.php?host=$ip");
-
-        // 发送成功
-        if ($response->ok()) {
-            $msg = $response->json();
-            if ($msg && $msg['state'] === '1000') {
-                return "<h4>{$msg['ip']}</h4>线路【{$msg['node']}】<br> 最小值:{$msg['ping_time_min']}<br> 平均值:{$msg['ping_time_avg']}<br> 最大值:{$msg['ping_time_max']}";
-            }
-        }
-        Log::warning('【PING】检测'.$ip.'时,api.yum6.cn无结果');
-
-        return false; // 发送错误
-    }
-
     private function toolsdaquan(string $ip, int $port): ?array
     { // 开发依据: https://www.toolsdaquan.com/ipcheck/
-        $response_inner = self::$basicRequest->withHeaders(['Referer' => 'https://www.toolsdaquan.com/ipcheck/'])->get("https://www.toolsdaquan.com/toolapi/public/ipchecking/$ip/$port");
-        $response_outer = self::$basicRequest->withHeaders(['Referer' => 'https://www.toolsdaquan.com/ipcheck/'])->get("https://www.toolsdaquan.com/toolapi/public/ipchecking2/$ip/$port");
+        $response_inner = self::$basicRequest->withHeader('Referer', 'https://www.toolsdaquan.com/ipcheck/')->get("https://www.toolsdaquan.com/toolapi/public/ipchecking/$ip/$port");
+        $response_outer = self::$basicRequest->withHeader('Referer', 'https://www.toolsdaquan.com/ipcheck/')->get("https://www.toolsdaquan.com/toolapi/public/ipchecking2/$ip/$port");
 
         if ($response_inner->ok() && $response_outer->ok()) {
             return $this->common_detection($response_inner->json(), $response_outer->json(), $ip);
@@ -343,7 +264,7 @@ class NetworkDetection
 
     private function vps1352(string $ip, int $port): ?array
     { // 开发依据: https://www.51vps.info/ipcheck.html https://www.vps1352.com/ipcheck.html 有缺陷api,查不了海外做判断 备用
-        $response = self::$basicRequest->asForm()->withHeaders(['Referer' => 'https://www.51vps.info'])->post('https://www.vps1352.com/check.php', ['ip' => $ip, 'port' => $port]);
+        $response = self::$basicRequest->asForm()->withHeader('Referer', 'https://www.51vps.info')->post('https://www.vps1352.com/check.php', ['ip' => $ip, 'port' => $port]);
 
         if ($response->ok()) {
             $data = $response->json();
@@ -367,7 +288,7 @@ class NetworkDetection
 
     private function rss(string $ip, int $port): ?array
     { // https://ip.rss.ink/index/check
-        $client = self::$basicRequest->withHeaders(['X-Token' => '5AXfB1xVfuq5hxv4']);
+        $client = self::$basicRequest->withHeader('X-Token', '5AXfB1xVfuq5hxv4');
 
         foreach (['in', 'out'] as $type) {
             foreach (['icmp', 'tcp'] as $protocol) {

+ 0 - 1
database/seeders/RBACSeeder.php

@@ -56,7 +56,6 @@ class RBACSeeder extends Seeder
         'admin.node.geo' => '【线路系统】更新地理',
         'admin.node.index' => '【线路系统】线路列表',
         'admin.node.monitor' => '【线路系统】流量监控',
-        'admin.node.ping' => '【线路系统】测速',
         'admin.node.reload' => '【线路系统】重载',
         'admin.order' => '【商品系统】订单列表',
         'admin.payment.callback' => '【日志系统】回调列表',

+ 334 - 334
resources/views/admin/node/index.blade.php

@@ -19,19 +19,19 @@
                 @canany(['admin.node.geo', 'admin.node.create'])
                     <div class="panel-actions btn-group">
                         @can('admin.node.reload')
-                            @if($nodeList->where('type',4)->count())
-                                <button type="button" onclick="reload(0)" class="btn btn-info">
-                                    <i id="reload_0" class="icon wb-reload" aria-hidden="true"></i> {{ trans('admin.node.reload_all') }}
+                            @if ($nodeList->where('type', 4)->count())
+                                <button class="btn btn-info" type="button" onclick="reload(0)">
+                                    <i class="icon wb-reload" id="reload_0" aria-hidden="true"></i> {{ trans('admin.node.reload_all') }}
                                 </button>
                             @endif
                         @endcan
                         @can('admin.node.geo')
-                            <button type="button" onclick="refreshGeo(0)" class="btn btn-outline-default">
-                                <i id="geo_0" class="icon wb-map" aria-hidden="true"></i> {{ trans('admin.node.refresh_geo_all') }}
+                            <button class="btn btn-outline-default" type="button" onclick="refreshGeo(0)">
+                                <i class="icon wb-map" id="geo_0" aria-hidden="true"></i> {{ trans('admin.node.refresh_geo_all') }}
                             </button>
                         @endcan
                         @can('admin.node.create')
-                            <a href="{{route('admin.node.create')}}" class="btn btn-primary">
+                            <a class="btn btn-primary" href="{{ route('admin.node.create') }}">
                                 <i class="icon wb-plus"></i> {{ trans('common.add') }}
                             </a>
                         @endcan
@@ -41,201 +41,200 @@
             <div class="panel-body">
                 <table class="text-md-center" data-toggle="table" data-mobile-responsive="true">
                     <thead class="thead-default">
-                    <tr>
-                        <th> ID</th>
-                        <th> {{ trans('model.common.type') }}</th>
-                        <th> {{ trans('model.node.name') }}</th>
-                        <th> {{ trans('model.node.domain') }}</th>
-                        <th> IP</th>
-                        <th> {{ trans('model.node.static') }}</th>
-                        <th> {{ trans('model.node.online_user') }}</th>
-                        <th> {{ trans('model.node.data_consume') }}</th>
-                        <th> {{ trans('model.node.data_rate') }}</th>
-                        <th> {{ trans('model.common.extend') }}</th>
-                        <th> {{ trans('common.status.attribute') }}</th>
-                        <th> {{ trans('common.action') }}</th>
-                    </tr>
+                        <tr>
+                            <th> ID</th>
+                            <th> {{ trans('model.common.type') }}</th>
+                            <th> {{ trans('model.node.name') }}</th>
+                            <th> {{ trans('model.node.domain') }}</th>
+                            <th> IP</th>
+                            <th> {{ trans('model.node.static') }}</th>
+                            <th> {{ trans('model.node.online_user') }}</th>
+                            <th> {{ trans('model.node.data_consume') }}</th>
+                            <th> {{ trans('model.node.data_rate') }}</th>
+                            <th> {{ trans('model.common.extend') }}</th>
+                            <th> {{ trans('common.status.attribute') }}</th>
+                            <th> {{ trans('common.action') }}</th>
+                        </tr>
                     </thead>
                     <tbody>
-                    @foreach($nodeList as $node)
-                        <tr>
-                            <td> {{$node->id}} </td>
-                            <td> {{$node->type_label}} </td>
-                            <td> {{$node->name}} </td>
-                            <td> {{$node->server}} </td>
-                            <td> {{$node->is_ddns ? trans('model.node.ddns') : $node->ip}} </td>
-                            <td> {{$node->uptime ?: '-'}} </td>
-                            <td> {{$node->online_users ?: '-'}} </td>
-                            <td> {{$node->transfer}} </td>
-                            <td> {{$node->traffic_rate}} </td>
-                            <td>
-                                @isset($node->profile['passwd'])
-                                    {{-- 单端口 --}}
-                                    <span class="badge badge-lg badge-info"><i class="fa-solid fa-1" aria-hidden="true"></i></span>
-                                @endisset
-                                @if($node->is_display === 0)
-                                    {{-- 节点完全不可见 --}}
-                                    <span class="badge badge-lg badge-danger"><i class="icon wb-eye-close" aria-hidden="true"></i></span>
-                                @elseif($node->is_display === 1)
-                                    {{-- 节点只在页面中显示 --}}
-                                    <span class="badge badge-lg badge-danger"><i class="fa-solid fa-link-slash" aria-hidden="true"></i></span>
-                                @elseif($node->is_display === 2)
-                                    {{-- 节点只可被订阅到 --}}
-                                    <span class="badge badge-lg badge-danger"><i class="fa-solid fa-store-slash" aria-hidden="true"></i></span>
-                                @endif
-                                @if($node->ip)
-                                    <span class="badge badge-md badge-info"><i class="fa-solid fa-4" aria-hidden="true"></i></span>
-                                @endif
-                                @if($node->ipv6)
-                                    <span class="badge badge-md badge-info"><i class="fa-solid fa-6" aria-hidden="true"></i></span>
-                                @endif
-                            </td>
-                            <td>
-                                @if($node->isOnline)
-                                    @if ($node->status)
-                                        {{$node->load}}
-                                    @else
-                                        <i class="yellow-700 icon icon-spin fa-solid fa-gear" aria-hidden="true"></i>
+                        @foreach ($nodeList as $node)
+                            <tr>
+                                <td> {{ $node->id }} </td>
+                                <td> {{ $node->type_label }} </td>
+                                <td> {{ $node->name }} </td>
+                                <td> {{ $node->server }} </td>
+                                <td> {{ $node->is_ddns ? trans('model.node.ddns') : $node->ip }} </td>
+                                <td> {{ $node->uptime ?: '-' }} </td>
+                                <td> {{ $node->online_users ?: '-' }} </td>
+                                <td> {{ $node->transfer }} </td>
+                                <td> {{ $node->traffic_rate }} </td>
+                                <td>
+                                    @isset($node->profile['passwd'])
+                                        {{-- 单端口 --}}
+                                        <span class="badge badge-lg badge-info"><i class="fa-solid fa-1" aria-hidden="true"></i></span>
+                                    @endisset
+                                    @if ($node->is_display === 0)
+                                        {{-- 节点完全不可见 --}}
+                                        <span class="badge badge-lg badge-danger"><i class="icon wb-eye-close" aria-hidden="true"></i></span>
+                                    @elseif($node->is_display === 1)
+                                        {{-- 节点只在页面中显示 --}}
+                                        <span class="badge badge-lg badge-danger"><i class="fa-solid fa-link-slash" aria-hidden="true"></i></span>
+                                    @elseif($node->is_display === 2)
+                                        {{-- 节点只可被订阅到 --}}
+                                        <span class="badge badge-lg badge-danger"><i class="fa-solid fa-store-slash" aria-hidden="true"></i></span>
                                     @endif
-                                @else
-                                    @if ($node->status)
-                                        <i class="red-600 fa-solid fa-gear" aria-hidden="true"></i>
-                                    @else
-                                        <i class="red-600 fa-solid fa-handshake-simple-slash" aria-hidden="true"></i>
+                                    @if ($node->ip)
+                                        <span class="badge badge-md badge-info"><i class="fa-solid fa-4" aria-hidden="true"></i></span>
                                     @endif
-                                @endif
-                            </td>
-                            <td>
-                                @canany(['admin.node.edit', 'admin.node.clone', 'admin.node.destroy', 'admin.node.monitor', 'admin.node.geo', 'admin.node.ping', 'admin.node
-                                .check', 'admin.node.reload'])
-                                    <button type="button" class="btn btn-primary dropdown-toggle" data-boundary="viewport" data-toggle="dropdown" aria-expanded="false">
-                                        <i class="icon wb-wrench" aria-hidden="true"></i>
-                                    </button>
-                                    <div class="dropdown-menu" role="menu">
-                                        @can('admin.node.edit')
-                                            <a class="dropdown-item" href="{{route('admin.node.edit', [$node->id, 'page' => Request::query('page', 1)])}}" role="menuitem">
-                                                <i class="icon wb-edit" aria-hidden="true"></i> {{ trans('common.edit') }}
-                                            </a>
-                                        @endcan
-                                        @can('admin.node.clone')
-                                            <a class="dropdown-item" href="{{route('admin.node.clone', $node)}}" role="menuitem">
-                                                <i class="icon wb-copy" aria-hidden="true"></i> {{ trans('admin.clone') }}
-                                            </a>
-                                        @endcan
-                                        @can('admin.node.destroy')
-                                            <a class="dropdown-item red-700" href="javascript:delNode('{{$node->id}}', '{{$node->name}}')" role="menuitem">
-                                                <i class="icon wb-trash" aria-hidden="true"></i> {{ trans('common.delete') }}
-                                            </a>
-                                        @endcan
-                                        @can('admin.node.monitor')
-                                            <a class="dropdown-item" href="{{route('admin.node.monitor', $node)}}" role="menuitem">
-                                                <i class="icon wb-stats-bars" aria-hidden="true"></i> {{ trans('admin.node.traffic_monitor') }}
-                                            </a>
-                                        @endcan
-                                        <hr/>
-                                        @can('admin.node.geo')
-                                            <a class="dropdown-item" href="javascript:refreshGeo('{{$node->id}}')" role="menuitem">
-                                                <i id="geo{{$node->id}}" class="icon wb-map" aria-hidden="true"></i> {{ trans('admin.node.refresh_geo') }}
-                                            </a>
-                                        @endcan
-                                        @can('admin.node.ping')
-                                            <a class="dropdown-item" href="javascript:pingNode('{{$node->id}}')" role="menuitem">
-                                                <i id="ping_{{$node->id}}" class="icon wb-order" aria-hidden="true"></i> {{ trans('admin.node.ping') }}
-                                            </a>
-                                        @endcan
-                                        @can('admin.node.check')
-                                            <a class="dropdown-item" href="javascript:checkNode('{{$node->id}}')" role="menuitem">
-                                                <i id="node_{{$node->id}}" class="icon wb-signal" aria-hidden="true"></i> {{ trans('admin.node.connection_test') }}
-                                            </a>
-                                        @endcan
-                                        @if($node->type === 4)
-                                            @can('admin.node.reload')
-                                                <hr/>
-                                                <a class="dropdown-item" href="javascript:reload('{{$node->id}}')" role="menuitem">
-                                                    <i id="reload_{{$node->id}}" class="icon wb-reload" aria-hidden="true"></i> {{ trans('admin.node.reload') }}
-                                                </a>
-                                            @endcan
-                                        @endif
-                                    </div>
-                                @endcan
-                            </td>
-                        </tr>
-                        @if (count($node->childNodes))
-                            @foreach($node->childNodes as $childNode)
-                                <tr class="bg-blue-grey-200 grey-700 table-borderless frontlin">
-                                    <td></td>
-                                    <td><i class="float-left fa-solid fa-right-left" aria-hidden="true"></i>
-                                        <strong>{{ trans('model.node.transfer') }}</strong></td>
-                                    <td> {{ $childNode->name }} </td>
-                                    <td> {{ $childNode->server }} </td>
-                                    <td> {{ $childNode->is_ddns ? trans('model.node.ddns') : $childNode->ip }} </td>
-                                    <td colspan="2">
-                                        @if($childNode->is_display === 0)
-                                            {{-- 节点完全不可见 --}}
-                                            <span class="badge badge-lg badge-danger"><i class="icon wb-eye-close" aria-hidden="true"></i></span>
-                                        @elseif($childNode->is_display === 1)
-                                            {{-- 节点只在页面中显示 --}}
-                                            <span class="badge badge-lg badge-danger"><i class="fa-solid fa-link-slash" aria-hidden="true"></i></span>
-                                        @elseif($childNode->is_display === 2)
-                                            {{-- 节点只可被订阅到 --}}
-                                            <span class="badge badge-lg badge-danger"><i class="fa-solid fa-store-slash" aria-hidden="true"></i></span>
+                                    @if ($node->ipv6)
+                                        <span class="badge badge-md badge-info"><i class="fa-solid fa-6" aria-hidden="true"></i></span>
+                                    @endif
+                                </td>
+                                <td>
+                                    @if ($node->isOnline)
+                                        @if ($node->status)
+                                            {{ $node->load }}
+                                        @else
+                                            <i class="yellow-700 icon icon-spin fa-solid fa-gear" aria-hidden="true"></i>
                                         @endif
-                                    </td>
-                                    <td colspan="2">
-                                        @if (!$childNode->status || !$node->status)
+                                    @else
+                                        @if ($node->status)
+                                            <i class="red-600 fa-solid fa-gear" aria-hidden="true"></i>
+                                        @else
                                             <i class="red-600 fa-solid fa-handshake-simple-slash" aria-hidden="true"></i>
                                         @endif
-                                    </td>
-                                    <td colspan="3">
-                                        @canany(['admin.node.edit', 'admin.node.clone', 'admin.node.destroy', 'admin.node.monitor', 'admin.node.geo', 'admin.node.ping', 'admin.node.check'])
-                                            <button type="button" class="btn btn-primary dropdown-toggle" data-boundary="viewport" data-toggle="dropdown" aria-expanded="false">
-                                                <i class="icon wb-wrench" aria-hidden="true"></i>
-                                            </button>
-                                            <div class="dropdown-menu" role="menu">
-                                                @can('admin.node.edit')
-                                                    <a class="dropdown-item" href="{{route('admin.node.edit', [$childNode->id, 'page' => Request::query('page', 1)])}}"
-                                                       role="menuitem">
-                                                        <i class="icon wb-edit" aria-hidden="true"></i> {{ trans('common.edit') }}
-                                                    </a>
-                                                @endcan
-                                                @can('admin.node.clone')
-                                                    <a class="dropdown-item" href="{{route('admin.node.clone', $childNode)}}" role="menuitem">
-                                                        <i class="icon wb-copy" aria-hidden="true"></i> {{ trans('admin.clone') }}
-                                                    </a>
-                                                @endcan
-                                                @can('admin.node.destroy')
-                                                    <a class="dropdown-item red-700" href="javascript:delNode('{{$childNode->id}}', '{{$childNode->name}}')" role="menuitem">
-                                                        <i class="icon wb-trash" aria-hidden="true"></i> {{ trans('common.delete') }}
-                                                    </a>
-                                                @endcan
-                                                @can('admin.node.monitor')
-                                                    <a class="dropdown-item" href="{{route('admin.node.monitor', $childNode)}}" role="menuitem">
-                                                        <i class="icon wb-stats-bars" aria-hidden="true"></i> {{ trans('admin.node.traffic_monitor') }}
-                                                    </a>
-                                                @endcan
-                                                <hr/>
-                                                @can('admin.node.geo')
-                                                    <a class="dropdown-item" href="javascript:refreshGeo('{{$childNode->id}}')" role="menuitem">
-                                                        <i id="geo_{{$childNode->id}}" class="icon wb-map" aria-hidden="true"></i> {{ trans('admin.node.refresh_geo') }}
-                                                    </a>
-                                                @endcan
-                                                @can('admin.node.ping')
-                                                    <a class="dropdown-item" href="javascript:pingNode('{{$childNode->id}}')" role="menuitem">
-                                                        <i id="ping_{{$childNode->id}}" class="icon wb-order" aria-hidden="true"></i> {{ trans('admin.node.ping') }}
-                                                    </a>
-                                                @endcan
-                                                @can('admin.node.check')
-                                                    <a class="dropdown-item" href="javascript:checkNode('{{$childNode->id}}')" role="menuitem">
-                                                        <i id="node_{{$childNode->id}}" class="icon wb-signal" aria-hidden="true"></i> {{ trans('admin.node.connection_test') }}
+                                    @endif
+                                </td>
+                                <td>
+                                    @canany(['admin.node.edit', 'admin.node.clone', 'admin.node.destroy', 'admin.node.monitor', 'admin.node.geo',
+                                        'admin.node.check', 'admin.node.reload'])
+                                        <button class="btn btn-primary dropdown-toggle" data-boundary="viewport" data-toggle="dropdown" type="button"
+                                                aria-expanded="false">
+                                            <i class="icon wb-wrench" aria-hidden="true"></i>
+                                        </button>
+                                        <div class="dropdown-menu" role="menu">
+                                            @can('admin.node.edit')
+                                                <a class="dropdown-item" href="{{ route('admin.node.edit', [$node->id, 'page' => Request::query('page', 1)]) }}"
+                                                   role="menuitem">
+                                                    <i class="icon wb-edit" aria-hidden="true"></i> {{ trans('common.edit') }}
+                                                </a>
+                                            @endcan
+                                            @can('admin.node.clone')
+                                                <a class="dropdown-item" href="{{ route('admin.node.clone', $node) }}" role="menuitem">
+                                                    <i class="icon wb-copy" aria-hidden="true"></i> {{ trans('admin.clone') }}
+                                                </a>
+                                            @endcan
+                                            @can('admin.node.destroy')
+                                                <a class="dropdown-item red-700" href="javascript:delNode('{{ $node->id }}', '{{ $node->name }}')" role="menuitem">
+                                                    <i class="icon wb-trash" aria-hidden="true"></i> {{ trans('common.delete') }}
+                                                </a>
+                                            @endcan
+                                            @can('admin.node.monitor')
+                                                <a class="dropdown-item" href="{{ route('admin.node.monitor', $node) }}" role="menuitem">
+                                                    <i class="icon wb-stats-bars" aria-hidden="true"></i> {{ trans('admin.node.traffic_monitor') }}
+                                                </a>
+                                            @endcan
+                                            <hr />
+                                            @can('admin.node.geo')
+                                                <a class="dropdown-item" href="javascript:refreshGeo('{{ $node->id }}')" role="menuitem">
+                                                    <i class="icon wb-map" id="geo{{ $node->id }}" aria-hidden="true"></i> {{ trans('admin.node.refresh_geo') }}
+                                                </a>
+                                            @endcan
+                                            @can('admin.node.check')
+                                                <a class="dropdown-item" href="javascript:checkNode('{{ $node->id }}')" role="menuitem">
+                                                    <i class="icon wb-signal" id="node_{{ $node->id }}" aria-hidden="true"></i>
+                                                    {{ trans('admin.node.connection_test') }}
+                                                </a>
+                                            @endcan
+                                            @if ($node->type === 4)
+                                                @can('admin.node.reload')
+                                                    <hr />
+                                                    <a class="dropdown-item" href="javascript:reload('{{ $node->id }}')" role="menuitem">
+                                                        <i class="icon wb-reload" id="reload_{{ $node->id }}" aria-hidden="true"></i> {{ trans('admin.node.reload') }}
                                                     </a>
                                                 @endcan
-                                            </div>
-                                        @endcan
-                                    </td>
-                                </tr>
-                            @endforeach
-                        @endif
-                    @endforeach
+                                            @endif
+                                        </div>
+                                    @endcan
+                                </td>
+                            </tr>
+                            @if (count($node->childNodes))
+                                @foreach ($node->childNodes as $childNode)
+                                    <tr class="bg-blue-grey-200 grey-700 table-borderless frontlin">
+                                        <td></td>
+                                        <td><i class="float-left fa-solid fa-right-left" aria-hidden="true"></i>
+                                            <strong>{{ trans('model.node.transfer') }}</strong>
+                                        </td>
+                                        <td> {{ $childNode->name }} </td>
+                                        <td> {{ $childNode->server }} </td>
+                                        <td> {{ $childNode->is_ddns ? trans('model.node.ddns') : $childNode->ip }} </td>
+                                        <td colspan="2">
+                                            @if ($childNode->is_display === 0)
+                                                {{-- 节点完全不可见 --}}
+                                                <span class="badge badge-lg badge-danger"><i class="icon wb-eye-close" aria-hidden="true"></i></span>
+                                            @elseif($childNode->is_display === 1)
+                                                {{-- 节点只在页面中显示 --}}
+                                                <span class="badge badge-lg badge-danger"><i class="fa-solid fa-link-slash" aria-hidden="true"></i></span>
+                                            @elseif($childNode->is_display === 2)
+                                                {{-- 节点只可被订阅到 --}}
+                                                <span class="badge badge-lg badge-danger"><i class="fa-solid fa-store-slash" aria-hidden="true"></i></span>
+                                            @endif
+                                        </td>
+                                        <td colspan="2">
+                                            @if (!$childNode->status || !$node->status)
+                                                <i class="red-600 fa-solid fa-handshake-simple-slash" aria-hidden="true"></i>
+                                            @endif
+                                        </td>
+                                        <td colspan="3">
+                                            @canany(['admin.node.edit', 'admin.node.clone', 'admin.node.destroy', 'admin.node.monitor', 'admin.node.geo',
+                                                'admin.node.check'])
+                                                <button class="btn btn-primary dropdown-toggle" data-boundary="viewport" data-toggle="dropdown" type="button"
+                                                        aria-expanded="false">
+                                                    <i class="icon wb-wrench" aria-hidden="true"></i>
+                                                </button>
+                                                <div class="dropdown-menu" role="menu">
+                                                    @can('admin.node.edit')
+                                                        <a class="dropdown-item"
+                                                           href="{{ route('admin.node.edit', [$childNode->id, 'page' => Request::query('page', 1)]) }}" role="menuitem">
+                                                            <i class="icon wb-edit" aria-hidden="true"></i> {{ trans('common.edit') }}
+                                                        </a>
+                                                    @endcan
+                                                    @can('admin.node.clone')
+                                                        <a class="dropdown-item" href="{{ route('admin.node.clone', $childNode) }}" role="menuitem">
+                                                            <i class="icon wb-copy" aria-hidden="true"></i> {{ trans('admin.clone') }}
+                                                        </a>
+                                                    @endcan
+                                                    @can('admin.node.destroy')
+                                                        <a class="dropdown-item red-700" href="javascript:delNode('{{ $childNode->id }}', '{{ $childNode->name }}')"
+                                                           role="menuitem">
+                                                            <i class="icon wb-trash" aria-hidden="true"></i> {{ trans('common.delete') }}
+                                                        </a>
+                                                    @endcan
+                                                    @can('admin.node.monitor')
+                                                        <a class="dropdown-item" href="{{ route('admin.node.monitor', $childNode) }}" role="menuitem">
+                                                            <i class="icon wb-stats-bars" aria-hidden="true"></i> {{ trans('admin.node.traffic_monitor') }}
+                                                        </a>
+                                                    @endcan
+                                                    <hr />
+                                                    @can('admin.node.geo')
+                                                        <a class="dropdown-item" href="javascript:refreshGeo('{{ $childNode->id }}')" role="menuitem">
+                                                            <i class="icon wb-map" id="geo_{{ $childNode->id }}" aria-hidden="true"></i>
+                                                            {{ trans('admin.node.refresh_geo') }}
+                                                        </a>
+                                                    @endcan
+                                                    @can('admin.node.check')
+                                                        <a class="dropdown-item" href="javascript:checkNode('{{ $childNode->id }}')" role="menuitem">
+                                                            <i class="icon wb-signal" id="node_{{ $childNode->id }}" aria-hidden="true"></i>
+                                                            {{ trans('admin.node.connection_test') }}
+                                                        </a>
+                                                    @endcan
+                                                </div>
+                                            @endcan
+                                        </td>
+                                    </tr>
+                                @endforeach
+                            @endif
+                        @endforeach
                     </tbody>
                 </table>
             </div>
@@ -246,7 +245,7 @@
                     </div>
                     <div class="col-sm-8">
                         <nav class="Page navigation float-right">
-                            {{$nodeList->links()}}
+                            {{ $nodeList->links() }}
                         </nav>
                     </div>
                 </div>
@@ -259,160 +258,161 @@
     <script src="/assets/global/vendor/bootstrap-table/extensions/mobile/bootstrap-table-mobile.min.js"></script>
     <script>
         @can('admin.node.check')
-        // 节点连通性测试
-        function checkNode(id) {
-          $.ajax({
-            method: 'POST',
-            url: '{{route('admin.node.check', '')}}/' + id,
-            data: {_token: '{{csrf_token()}}'},
-            beforeSend: function() {
-              $('#node_' + id).removeClass('wb-signal').addClass('wb-loop icon-spin');
-            },
-            success: function(ret) {
-              if (ret.status === 'success') {
-                let str = '';
-                for (let i in ret.message) {
-                  str += '<tr><td>' + i + '</td><td>' + ret.message[i][0] + '</td><td>' + ret.message[i][1] +
-                      '</td></tr>';
-                }
-                swal.fire({
-                  title: ret.title,
-                  icon: 'info',
-                  html: '<table class="my-20"><thead class="thead-default"><tr><th> IP </th><th> ICMP </th> <th> TCP </th></thead><tbody>' +
-                      str + '</tbody></table>',
-                  showConfirmButton: false,
+            // 节点连通性测试
+            function checkNode(id) {
+                $.ajax({
+                    method: 'POST',
+                    url: '{{ route('admin.node.check', '') }}/' + id,
+                    data: {
+                        _token: '{{ csrf_token() }}'
+                    },
+                    beforeSend: function() {
+                        $('#node_' + id).removeClass('wb-signal').addClass('wb-loop icon-spin');
+                    },
+                    success: function(ret) {
+                        if (ret.status === 'success') {
+                            let str = '';
+                            for (let i in ret.message) {
+                                str += '<tr><td>' + i + '</td><td>' + ret.message[i][0] + '</td><td>' + ret.message[i][1] +
+                                    '</td></tr>';
+                            }
+                            swal.fire({
+                                title: ret.title,
+                                icon: 'info',
+                                html: '<table class="my-20"><thead class="thead-default"><tr><th> IP </th><th> ICMP </th> <th> TCP </th></thead><tbody>' +
+                                    str + '</tbody></table>',
+                                showConfirmButton: false,
+                            });
+                        } else {
+                            swal.fire({
+                                title: ret.title,
+                                text: ret.message,
+                                icon: 'error'
+                            });
+                        }
+                    },
+                    complete: function() {
+                        $('#node_' + id).removeClass('wb-loop icon-spin').addClass('wb-signal');
+                    },
                 });
-              } else {
-                swal.fire({title: ret.title, text: ret.message, icon: 'error'});
-              }
-            },
-            complete: function() {
-              $('#node_' + id).removeClass('wb-loop icon-spin').addClass('wb-signal');
-            },
-          });
-        }
+            }
         @endcan
 
-        @can('admin.node.ping')
-        // Ping节点获取延迟
-        function pingNode(id) {
-          $.ajax({
-            method: 'POST',
-            url: '{{route('admin.node.ping', '')}}/' + id,
-            data: {_token: '{{csrf_token()}}'},
-            beforeSend: function() {
-              $('#ping_' + id).removeClass('wb-order').addClass('wb-loop icon-spin');
-            },
-            success: function(ret) {
-              if (ret.status === 'success') {
+        @can('admin.node.reload')
+            // 发送节点重载请求
+            function reload(id) {
                 swal.fire({
-                  icon: 'info',
-                  html: ret.message,
-                  showConfirmButton: false,
+                    text: '{{ trans('admin.node.reload_confirm') }}',
+                    icon: 'question',
+                    showCancelButton: true,
+                    cancelButtonText: '{{ trans('common.close') }}',
+                    confirmButtonText: '{{ trans('common.confirm') }}',
+                }).then((result) => {
+                    if (result.value) {
+                        $.ajax({
+                            method: 'POST',
+                            url: '{{ route('admin.node.reload', '') }}/' + id,
+                            data: {
+                                _token: '{{ csrf_token() }}'
+                            },
+                            beforeSend: function() {
+                                $('#reload_' + id).removeClass('wb-reload').addClass('wb-loop icon-spin');
+                            },
+                            success: function(ret) {
+                                if (ret.status === 'success') {
+                                    swal.fire({
+                                        title: ret.message,
+                                        icon: 'info',
+                                        showConfirmButton: false
+                                    });
+                                } else {
+                                    swal.fire({
+                                        title: ret.message,
+                                        icon: 'error'
+                                    });
+                                }
+                            },
+                            complete: function() {
+                                $('#reload_' + id).removeClass('wb-loop icon-spin').addClass('wb-reload');
+                            },
+                        });
+                    }
                 });
-              } else {
-                swal.fire({title: ret.message, icon: 'error'});
-              }
-            },
-            complete: function() {
-              $('#ping_' + id).removeClass('wb-loop icon-spin').addClass('wb-order');
-            },
-          });
-        }
-        @endcan
-
-        @can('admin.node.reload')
-        // 发送节点重载请求
-        function reload(id) {
-          swal.fire({
-            text: '{{ trans('admin.node.reload_confirm') }}',
-            icon: 'question',
-            showCancelButton: true,
-            cancelButtonText: '{{trans('common.close')}}',
-            confirmButtonText: '{{trans('common.confirm')}}',
-          }).then((result) => {
-            if (result.value) {
-              $.ajax({
-                method: 'POST',
-                url: '{{route('admin.node.reload', '')}}/' + id,
-                data: {_token: '{{csrf_token()}}'},
-                beforeSend: function() {
-                  $('#reload_' + id).removeClass('wb-reload').addClass('wb-loop icon-spin');
-                },
-                success: function(ret) {
-                  if (ret.status === 'success') {
-                    swal.fire({title: ret.message, icon: 'info', showConfirmButton: false});
-                  } else {
-                    swal.fire({title: ret.message, icon: 'error'});
-                  }
-                },
-                complete: function() {
-                  $('#reload_' + id).removeClass('wb-loop icon-spin').addClass('wb-reload');
-                },
-              });
             }
-          });
-        }
         @endcan
 
         @can('admin.node.geo')
-        // 刷新节点地理信息
-        function refreshGeo(id) {
-          $.ajax({
-            method: 'GET',
-            url: '{{route('admin.node.geo', '')}}/' + id,
-            data: {_token: '{{csrf_token()}}'},
-            beforeSend: function() {
-              $('#geo_' + id).removeClass('wb-map').addClass('wb-loop icon-spin');
-            },
-            success: function(ret) {
-              if (ret.status === 'success') {
-                swal.fire({title: ret.message, icon: 'info', showConfirmButton: false});
-              } else {
-                swal.fire({title: ret.message, icon: 'error'});
-              }
-            },
-            complete: function() {
-              $('#geo_' + id).removeClass('wb-loop icon-spin').addClass('wb-map');
-            },
-          });
-        }
+            // 刷新节点地理信息
+            function refreshGeo(id) {
+                $.ajax({
+                    method: 'GET',
+                    url: '{{ route('admin.node.geo', '') }}/' + id,
+                    data: {
+                        _token: '{{ csrf_token() }}'
+                    },
+                    beforeSend: function() {
+                        $('#geo_' + id).removeClass('wb-map').addClass('wb-loop icon-spin');
+                    },
+                    success: function(ret) {
+                        if (ret.status === 'success') {
+                            swal.fire({
+                                title: ret.message,
+                                icon: 'info',
+                                showConfirmButton: false
+                            });
+                        } else {
+                            swal.fire({
+                                title: ret.message,
+                                icon: 'error'
+                            });
+                        }
+                    },
+                    complete: function() {
+                        $('#geo_' + id).removeClass('wb-loop icon-spin').addClass('wb-map');
+                    },
+                });
+            }
         @endcan
 
         @can('admin.node.destroy')
-        // 删除节点
-        function delNode(id, name) {
-          swal.fire({
-            title: '{{trans('common.warning')}}',
-            text: '{{ trans('admin.confirm.delete.0', ['attribute' => trans('model.node.attribute')]) }}' + name +
-                '{{ trans('admin.confirm.delete.1') }}',
-            icon: 'warning',
-            showCancelButton: true,
-            cancelButtonText: '{{trans('common.close')}}',
-            confirmButtonText: '{{trans('common.confirm')}}',
-          }).then((result) => {
-            if (result.value) {
-              $.ajax({
-                method: 'DELETE',
-                url: '{{route('admin.node.destroy', '')}}/' + id,
-                data: {_token: '{{csrf_token()}}'},
-                dataType: 'json',
-                success: function(ret) {
-                  if (ret.status === 'success') {
-                    swal.fire({
-                      title: ret.message,
-                      icon: 'success',
-                      timer: 1000,
-                      showConfirmButton: false,
-                    }).then(() => window.location.reload());
-                  } else {
-                    swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
-                  }
-                },
-              });
+            // 删除节点
+            function delNode(id, name) {
+                swal.fire({
+                    title: '{{ trans('common.warning') }}',
+                    text: '{{ trans('admin.confirm.delete.0', ['attribute' => trans('model.node.attribute')]) }}' + name +
+                        '{{ trans('admin.confirm.delete.1') }}',
+                    icon: 'warning',
+                    showCancelButton: true,
+                    cancelButtonText: '{{ trans('common.close') }}',
+                    confirmButtonText: '{{ trans('common.confirm') }}',
+                }).then((result) => {
+                    if (result.value) {
+                        $.ajax({
+                            method: 'DELETE',
+                            url: '{{ route('admin.node.destroy', '') }}/' + id,
+                            data: {
+                                _token: '{{ csrf_token() }}'
+                            },
+                            dataType: 'json',
+                            success: function(ret) {
+                                if (ret.status === 'success') {
+                                    swal.fire({
+                                        title: ret.message,
+                                        icon: 'success',
+                                        timer: 1000,
+                                        showConfirmButton: false,
+                                    }).then(() => window.location.reload());
+                                } else {
+                                    swal.fire({
+                                        title: ret.message,
+                                        icon: 'error'
+                                    }).then(() => window.location.reload());
+                                }
+                            },
+                        });
+                    }
+                });
             }
-          });
-        }
         @endcan
     </script>
 @endsection

+ 0 - 1
routes/admin.php

@@ -75,7 +75,6 @@ Route::prefix('admin')->name('admin.')->group(function () {
         Route::get('clone/{node}', 'clone')->name('clone'); // 节点流量监控
         Route::get('monitor/{node}', 'nodeMonitor')->name('monitor'); // 节点流量监控
         Route::post('check/{node}', 'checkNode')->name('check'); // 节点阻断检测
-        Route::post('ping/{node}', 'pingNode')->name('ping'); // 节点ping测速
         Route::get('refreshGeo/{id}', 'refreshGeo')->name('geo'); // 更新节点
         Route::post('reload/{id}', 'reload')->name('reload'); // 更新节点
         Route::resource('auth', NodeAuthController::class)->except(['create', 'show', 'edit']); // 节点授权相关