index.blade.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. @extends('admin.table_layouts')
  2. @section('content')
  3. <div class="page-content container-fluid">
  4. <x-admin.table-panel :title="trans('admin.menu.node.list')" :theads="[
  5. 'ID',
  6. trans('model.common.type'),
  7. trans('model.node.name'),
  8. trans('model.node.domain'),
  9. 'IP',
  10. trans('model.node.static'),
  11. trans('model.node.online_user'),
  12. trans('model.node.data_consume'),
  13. trans('model.node.data_rate'),
  14. trans('model.common.extend'),
  15. trans('common.status.attribute'),
  16. trans('common.action'),
  17. ]" :count="trans('admin.node.counts', ['num' => $nodeList->total()])" :pagination="$nodeList->links()" :delete-config="['url' => route('admin.node.destroy', 'PLACEHOLDER'), 'attribute' => trans('model.node.attribute'), 'nameColumn' => 2]">
  18. @canany(['admin.node.reload', 'admin.node.geo', 'admin.node.create'])
  19. <x-slot:actions>
  20. <div class="btn-group">
  21. @can('admin.node.reload')
  22. @if ($nodeList->where('type', 4)->count())
  23. <button class="btn btn-info" type="button" onclick="reload(0)">
  24. <i class="icon wb-reload" id="reload_0" aria-hidden="true"></i> {{ trans('admin.node.reload_all') }}
  25. </button>
  26. @endif
  27. @endcan
  28. @can('admin.node.geo')
  29. <button class="btn btn-outline-default" type="button" onclick="refreshGeo(0)">
  30. <i class="icon wb-map" id="geo_0" aria-hidden="true"></i> {{ trans('admin.node.refresh_geo_all') }}
  31. </button>
  32. @endcan
  33. @can('admin.node.create')
  34. <a class="btn btn-primary" href="{{ route('admin.node.create') }}">
  35. <i class="icon wb-plus"></i> {{ trans('common.add') }}
  36. </a>
  37. @endcan
  38. </div>
  39. </x-slot:actions>
  40. @endcan
  41. <x-slot:tbody>
  42. @foreach ($nodeList as $node)
  43. <tr>
  44. <td> {{ $node->id }} </td>
  45. <td> {{ $node->type_label }} </td>
  46. <td> {{ $node->name }} </td>
  47. <td> {{ $node->server }} </td>
  48. <td> {{ $node->is_ddns ? trans('model.node.ddns') : $node->ip }} </td>
  49. <td> {{ $node->uptime }} </td>
  50. <td> {{ $node->online_users ?: '-' }} </td>
  51. <td> {{ $node->transfer }} </td>
  52. <td> {{ $node->traffic_rate }} </td>
  53. <td>
  54. @isset($node->profile['passwd'])
  55. {{-- 单端口 --}}
  56. <span class="badge badge-lg badge-info"><i class="fa-solid fa-1" aria-hidden="true"></i></span>
  57. @endisset
  58. @if ($node->is_display === 0)
  59. {{-- 节点完全不可见 --}}
  60. <span class="badge badge-lg badge-danger"><i class="icon wb-eye-close" aria-hidden="true"></i></span>
  61. @elseif($node->is_display === 1)
  62. {{-- 节点只在页面中显示 --}}
  63. <span class="badge badge-lg badge-danger"><i class="fa-solid fa-link-slash" aria-hidden="true"></i></span>
  64. @elseif($node->is_display === 2)
  65. {{-- 节点只可被订阅到 --}}
  66. <span class="badge badge-lg badge-danger"><i class="fa-solid fa-store-slash" aria-hidden="true"></i></span>
  67. @endif
  68. @if ($node->ip)
  69. <span class="badge badge-md badge-info"><i class="fa-solid fa-4" aria-hidden="true"></i></span>
  70. @endif
  71. @if ($node->ipv6)
  72. <span class="badge badge-md badge-info"><i class="fa-solid fa-6" aria-hidden="true"></i></span>
  73. @endif
  74. </td>
  75. <td>
  76. @if ($node->isOnline)
  77. @if ($node->status)
  78. {{ $node->load }}
  79. @else
  80. <i class="yellow-700 icon icon-spin fa-solid fa-gear" aria-hidden="true"></i>
  81. @endif
  82. @else
  83. @if ($node->status)
  84. <i class="red-600 fa-solid fa-gear" aria-hidden="true"></i>
  85. @else
  86. <i class="red-600 fa-solid fa-handshake-simple-slash" aria-hidden="true"></i>
  87. @endif
  88. @endif
  89. </td>
  90. <td>
  91. @canany(['admin.node.edit', 'admin.node.clone', 'admin.node.destroy', 'admin.node.monitor', 'admin.node.geo', 'admin.node.check',
  92. 'admin.node.reload'])
  93. <x-ui.dropdown>
  94. @can('admin.node.edit')
  95. <x-ui.dropdown-item :url="route('admin.node.edit', [$node->id, 'page' => Request::query('page', 1)])" icon="wb-edit" :text="trans('common.edit')" />
  96. @endcan
  97. @can('admin.node.clone')
  98. <x-ui.dropdown-item :url="route('admin.node.clone', $node)" icon="wb-copy" :text="trans('admin.clone')" />
  99. @endcan
  100. @can('admin.node.destroy')
  101. <x-ui.dropdown-item color="red-700" url="javascript:(0)" attribute="data-action=delete" icon="wb-trash" :text="trans('common.delete')" />
  102. @endcan
  103. @can('admin.node.monitor')
  104. <x-ui.dropdown-item :url="route('admin.node.monitor', $node)" icon="wb-stats-bars" :text="trans('admin.node.traffic_monitor')" />
  105. @endcan
  106. <hr />
  107. @can('admin.node.geo')
  108. <x-ui.dropdown-item id="geo{{ $node->id }}" url="javascript:refreshGeo('{{ $node->id }}')" icon="wb-map"
  109. :text="trans('admin.node.refresh_geo')" />
  110. @endcan
  111. @can('admin.node.check')
  112. <x-ui.dropdown-item id="node_{{ $node->id }}" url="javascript:checkNode('{{ $node->id }}')" icon="wb-signal"
  113. :text="trans('admin.node.connection_test')" />
  114. @endcan
  115. @if ($node->type === 4)
  116. @can('admin.node.reload')
  117. <hr />
  118. <x-ui.dropdown-item id="reload_{{ $node->id }}" url="javascript:reload('{{ $node->id }}')" icon="wb-reload"
  119. :text="trans('admin.node.reload')" />
  120. @endcan
  121. @endif
  122. </x-ui.dropdown>
  123. @endcan
  124. </td>
  125. </tr>
  126. @foreach ($node->childNodes as $childNode)
  127. <tr class="bg-blue-grey-200 grey-700 table-borderless">
  128. <td></td>
  129. <td><i class="float-left fa-solid fa-right-left" aria-hidden="true"></i>
  130. <strong>{{ trans('model.node.transfer') }}</strong>
  131. </td>
  132. <td> {{ $childNode->name }} </td>
  133. <td> {{ $childNode->server }} </td>
  134. <td> {{ $childNode->is_ddns ? trans('model.node.ddns') : $childNode->ip }} </td>
  135. <td colspan="2">
  136. @if ($childNode->is_display === 0)
  137. {{-- 节点完全不可见 --}}
  138. <span class="badge badge-lg badge-danger"><i class="icon wb-eye-close" aria-hidden="true"></i></span>
  139. @elseif($childNode->is_display === 1)
  140. {{-- 节点只在页面中显示 --}}
  141. <span class="badge badge-lg badge-danger"><i class="fa-solid fa-link-slash" aria-hidden="true"></i></span>
  142. @elseif($childNode->is_display === 2)
  143. {{-- 节点只可被订阅到 --}}
  144. <span class="badge badge-lg badge-danger"><i class="fa-solid fa-store-slash" aria-hidden="true"></i></span>
  145. @endif
  146. </td>
  147. <td colspan="2">
  148. @if (!$childNode->status || !$node->status)
  149. <i class="red-600 fa-solid fa-handshake-simple-slash" aria-hidden="true"></i>
  150. @endif
  151. </td>
  152. <td colspan="3">
  153. @canany(['admin.node.edit', 'admin.node.clone', 'admin.node.destroy', 'admin.node.monitor', 'admin.node.geo', 'admin.node.check'])
  154. <x-ui.dropdown>
  155. @can('admin.node.edit')
  156. <x-ui.dropdown-item :url="route('admin.node.edit', [$childNode->id, 'page' => Request::query('page', 1)])" icon="wb-edit" :text="trans('common.edit')" />
  157. @endcan
  158. @can('admin.node.clone')
  159. <x-ui.dropdown-item :url="route('admin.node.clone', $childNode)" icon="wb-copy" :text="trans('admin.clone')" />
  160. @endcan
  161. @can('admin.node.destroy')
  162. <x-ui.dropdown-item color="red-700" url="javascript:(0)" attribute="data-action=delete" icon="wb-trash" :text="trans('common.delete')" />
  163. @endcan
  164. @can('admin.node.monitor')
  165. <x-ui.dropdown-item :url="route('admin.node.monitor', $childNode)" icon="wb-stats-bars" :text="trans('admin.node.traffic_monitor')" />
  166. @endcan
  167. <hr />
  168. @can('admin.node.geo')
  169. <x-ui.dropdown-item id="geo_{{ $childNode->id }}" url="javascript:refreshGeo('{{ $childNode->id }}')" icon="wb-map"
  170. :text="trans('admin.node.refresh_geo')" />
  171. @endcan
  172. @can('admin.node.check')
  173. <x-ui.dropdown-item id="node_{{ $childNode->id }}" url="javascript:checkNode('{{ $childNode->id }}')" icon="wb-signal"
  174. :text="trans('admin.node.connection_test')" />
  175. @endcan
  176. </x-ui.dropdown>
  177. @endcan
  178. </td>
  179. </tr>
  180. @endforeach
  181. @endforeach
  182. </x-slot:tbody>
  183. </x-admin.table-panel>
  184. </div>
  185. @endsection
  186. @push('javascript')
  187. <script>
  188. @can('admin.node.check')
  189. function checkNode(id) { // 节点连通性测试
  190. const $element = $(`#node_${id}`);
  191. ajaxPost(jsRoute('{{ route('admin.node.check', 'PLACEHOLDER') }}', id), {}, {
  192. beforeSend: function() {
  193. $element.removeClass("wb-signal").addClass("wb-loop icon-spin");
  194. },
  195. success: function(ret) {
  196. if (ret.status === "success") {
  197. let str = "";
  198. for (let i in ret.message) {
  199. str += "<tr><td>" + i + "</td><td>" + ret.message[i][0] + "</td><td>" + ret.message[i][1] +
  200. "</td></tr>";
  201. }
  202. showMessage({
  203. title: ret.title,
  204. html: "<table class=\"my-20\"><thead class=\"thead-default\"><tr><th> IP </th><th> ICMP </th> <th> TCP </th></thead><tbody>" +
  205. str + "</tbody></table>",
  206. autoClose: false
  207. });
  208. } else {
  209. showMessage({
  210. title: ret.title,
  211. message: ret.message,
  212. icon: "error"
  213. });
  214. }
  215. },
  216. complete: function() {
  217. $element.removeClass("wb-loop icon-spin").addClass("wb-signal");
  218. }
  219. });
  220. }
  221. @endcan
  222. @can('admin.node.reload')
  223. function reload(id) { // 发送节点重载请求
  224. const $element = $(`#reload_${id}`);
  225. showConfirm({
  226. text: '{{ trans('admin.node.reload_confirm') }}',
  227. onConfirm: function() {
  228. ajaxPost(jsRoute('{{ route('admin.node.reload', 'PLACEHOLDER') }}', id), {}, {
  229. beforeSend: function() {
  230. $element.removeClass("wb-reload").addClass("wb-loop icon-spin");
  231. },
  232. complete: function() {
  233. $element.removeClass("wb-loop icon-spin").addClass("wb-reload");
  234. }
  235. });
  236. }
  237. });
  238. }
  239. @endcan
  240. @can('admin.node.geo')
  241. function refreshGeo(id) { // 刷新节点地理信息
  242. const $element = $(`#geo_${id}`);
  243. ajaxGet(jsRoute('{{ route('admin.node.geo', 'PLACEHOLDER') }}', id), {}, {
  244. beforeSend: function() {
  245. $element.removeClass("wb-map").addClass("wb-loop icon-spin");
  246. },
  247. complete: function() {
  248. $element.removeClass("wb-loop icon-spin").addClass("wb-map");
  249. }
  250. });
  251. }
  252. @endcan
  253. </script>
  254. @endpush