Bläddra i källkod

Trojan support

GeekQuerxy 5 år sedan
förälder
incheckning
5f913443aa

+ 2 - 1
resources/views/material/admin/node/create.tpl

@@ -117,7 +117,8 @@
                                             <option value="10">Shadowsocks 中转</option>
                                             <option value="10">Shadowsocks 中转</option>
                                             <option value="11">V2Ray</option>
                                             <option value="11">V2Ray</option>
                                             <option value="12">V2Ray 中转</option>
                                             <option value="12">V2Ray 中转</option>
-                                            <option value="13">Shadowsocks V2Ray-Plugin</option>
+                                            <option value="13">Shadowsocks V2Ray-Plugin&Obfs</option>
+                                            <option value="14">Trojan</option>
                                         </select>
                                         </select>
                                     </div>
                                     </div>
                                 </div>
                                 </div>

+ 3 - 5
resources/views/material/admin/node/edit.tpl

@@ -117,14 +117,12 @@
                                             <option value="1" {if $node->sort==1}selected{/if}>VPN/Radius基础</option>
                                             <option value="1" {if $node->sort==1}selected{/if}>VPN/Radius基础</option>
                                             <option value="2" {if $node->sort==2}selected{/if}>SSH</option>
                                             <option value="2" {if $node->sort==2}selected{/if}>SSH</option>
                                             <option value="5" {if $node->sort==5}selected{/if}>Anyconnect</option>
                                             <option value="5" {if $node->sort==5}selected{/if}>Anyconnect</option>
-                                            <option value="9" {if $node->sort==9}selected{/if}>Shadowsocks 单端口多用户
-                                            </option>
+                                            <option value="9" {if $node->sort==9}selected{/if}>Shadowsocks 单端口多用户</option>
                                             <option value="10" {if $node->sort==10}selected{/if}>Shadowsocks 中转</option>
                                             <option value="10" {if $node->sort==10}selected{/if}>Shadowsocks 中转</option>
                                             <option value="11" {if $node->sort==11}selected{/if}>V2Ray</option>
                                             <option value="11" {if $node->sort==11}selected{/if}>V2Ray</option>
                                             <option value="12" {if $node->sort==12}selected{/if}>V2Ray 中转</option>
                                             <option value="12" {if $node->sort==12}selected{/if}>V2Ray 中转</option>
-                                            <option value="13" {if $node->sort==13}selected{/if}>Shadowsocks
-                                                V2Ray-Plugin
-                                            </option>
+                                            <option value="13" {if $node->sort==13}selected{/if}>Shadowsocks V2Ray-Plugin&Obfs</option>
+                                            <option value="14" {if $node->sort==14}selected{/if}>Trojan</option>
                                         </select>
                                         </select>
                                     </div>
                                     </div>
                                 </div>
                                 </div>

+ 24 - 6
resources/views/material/user/node.tpl

@@ -50,6 +50,16 @@
     {/if}
     {/if}
 {/function}
 {/function}
 
 
+{function displaySort14Node node=null}
+    {$sort14Node = URL::getTrojanItem($user, $node['raw_node'], false)}
+    <p>节点地址:<span class="card-tag tag-blue">{$sort14Node['address']}</span></p>
+    <p>节点端口:<span class="card-tag tag-volcano">{$sort14Node['port']}</span></p>
+    <p>连接密码:<span class="card-tag tag-geekblue">{$sort14Node['passwd']}</span></p>
+    {if $sort14Node['host'] != $sort14Node['address']}
+        <p>HOST&PEER:<span class="card-tag tag-green">{$sort14Node['host']}</span></p>
+    {/if}
+{/function}
+
 {function displayNodeLink node=null}
 {function displayNodeLink node=null}
     <div class="tiptitle">
     <div class="tiptitle">
         <a href="javascript:void(0);">{$node['name']}</a>
         <a href="javascript:void(0);">{$node['name']}</a>
@@ -172,7 +182,7 @@
                                         {$relay_rule = $tools->pick_out_relay_rule($node['id'], $user->port, $relay_rules)}
                                         {$relay_rule = $tools->pick_out_relay_rule($node['id'], $user->port, $relay_rules)}
                                     {/if}
                                     {/if}
 
 
-                                    {if $node['mu_only'] != 1 && ($node['sort'] != 11 || $node['sort'] != 12 || $node['sort'] != 13)}
+                                    {if $node['mu_only'] != 1 && !in_array($node['sort'], [11, 12, 13, 14])}
                                         <div class="tiptitle">
                                         <div class="tiptitle">
                                             <a href="javascript:void(0);" onClick="urlChange('{$node['id']}',0,{if $relay_rule != null}{$relay_rule->id}{else}0{/if})">
                                             <a href="javascript:void(0);" onClick="urlChange('{$node['id']}',0,{if $relay_rule != null}{$relay_rule->id}{else}0{/if})">
                                                 {$node['name']}{if $relay_rule != null} - {$relay_rule->dist_node()->name}{/if}
                                                 {$node['name']}{if $relay_rule != null} - {$relay_rule->dist_node()->name}{/if}
@@ -185,11 +195,11 @@
                                                 </div>
                                                 </div>
                                             </div>
                                             </div>
                                         </div>
                                         </div>
-                                    {elseif $node['sort'] == 11 || $node['sort'] == 12 || $node['sort'] == 13}
+                                    {elseif in_array($node['sort'], [11, 12, 13, 14])}
                                         {displayNodeLink node=$node}
                                         {displayNodeLink node=$node}
                                     {/if}
                                     {/if}
 
 
-                                    {if $node['sort'] == 0 || $node['sort'] == 10 || $node['sort'] == 11 || $node['sort'] == 12 || $node['sort'] == 13}
+                                    {if in_array($node['sort'], [0, 10, 11, 12, 13, 14])}
                                         {$point_node=$node}
                                         {$point_node=$node}
                                     {/if}
                                     {/if}
 
 
@@ -226,6 +236,10 @@
                                         {displaySort13Node node=$node}
                                         {displaySort13Node node=$node}
                                     {/if}
                                     {/if}
 
 
+                                    {if $node['sort'] == 14}
+                                        {displaySort14Node node=$node}
+                                    {/if}
+
                                 {/if}
                                 {/if}
 
 
                             </div>
                             </div>
@@ -315,18 +329,18 @@
                                                         <div class="card nodetip-table">
                                                         <div class="card nodetip-table">
                                                             <div class="card-main">
                                                             <div class="card-main">
                                                                 <div class="card-inner">
                                                                 <div class="card-inner">
-                                                                    {if $node['mu_only'] != 1 && ($node['sort'] != 11 || $node['sort'] != 12 || $node['sort'] != 13)}
+                                                                    {if $node['mu_only'] != 1 && !in_array($node['sort'], [11, 12, 13, 14])}
                                                                         <p class="card-heading">
                                                                         <p class="card-heading">
                                                                             <a href="javascript:void(0);"
                                                                             <a href="javascript:void(0);"
                                                                                onClick="urlChange('{$node['id']}',0,{if $relay_rule != null}{$relay_rule->id}{else}0{/if})">{$node['name']}
                                                                                onClick="urlChange('{$node['id']}',0,{if $relay_rule != null}{$relay_rule->id}{else}0{/if})">{$node['name']}
                                                                                 {if $relay_rule != null} - {$relay_rule->dist_node()->name}{/if}</a>
                                                                                 {if $relay_rule != null} - {$relay_rule->dist_node()->name}{/if}</a>
                                                                             <span class="label label-brand-accent">←点击节点查看配置信息</span>
                                                                             <span class="label label-brand-accent">←点击节点查看配置信息</span>
                                                                         </p>
                                                                         </p>
-                                                                    {elseif $node['sort'] == 11 || $node['sort'] == 12 || $node['sort'] == 13}
+                                                                    {elseif in_array($node['sort'], [11, 12, 13, 14])}
                                                                         {displayNodeLink node=$node}
                                                                         {displayNodeLink node=$node}
                                                                     {/if}
                                                                     {/if}
 
 
-                                                                    {if $node['sort'] == 0 || $node['sort'] == 10 || $node['sort'] == 11 || $node['sort'] == 12 || $node['sort'] == 13}
+                                                                    {if in_array($node['sort'], [0, 10, 11, 12, 13, 14])}
                                                                         {$point_node = $node}
                                                                         {$point_node = $node}
                                                                     {/if}
                                                                     {/if}
 
 
@@ -366,6 +380,10 @@
                                                                         {displaySort13Node node=$node}
                                                                         {displaySort13Node node=$node}
                                                                     {/if}
                                                                     {/if}
 
 
+                                                                    {if $node['sort'] == 14}
+                                                                        {displaySort14Node node=$node}
+                                                                    {/if}
+
                                                                 </div>
                                                                 </div>
                                                             </div>
                                                             </div>
 
 

+ 5 - 2
src/Controllers/Admin/NodeController.php

@@ -259,7 +259,7 @@ class NodeController extends AdminController
         });
         });
 
 
         $datatables->edit('outaddress', static function ($data) {
         $datatables->edit('outaddress', static function ($data) {
-            return (in_array($data['sort'], [0, 10, 11, 12, 13]) ? explode(';', $data['server'])[0] : '');
+            return (in_array($data['sort'], [0, 10, 11, 12, 13, 14]) ? explode(';', $data['server'])[0] : '');
         });
         });
 
 
         $datatables->edit('node_bandwidth', static function ($data) {
         $datatables->edit('node_bandwidth', static function ($data) {
@@ -298,7 +298,10 @@ class NodeController extends AdminController
                     $sort = 'V2Ray - 中转';
                     $sort = 'V2Ray - 中转';
                     break;
                     break;
                 case 13:
                 case 13:
-                    $sort = 'Shadowsocks - V2Ray-Plugin';
+                    $sort = 'Shadowsocks - V2Ray-Plugin&Obfs';
+                    break;
+                case 14:
+                    $sort = 'Trojan';
                     break;
                     break;
                 default:
                 default:
                     $sort = '系统保留';
                     $sort = '系统保留';

+ 21 - 11
src/Controllers/LinkController.php

@@ -152,7 +152,7 @@ class LinkController extends BaseController
                 if ($query_value != '0' && $query_value != '') {
                 if ($query_value != '0' && $query_value != '') {
 
 
                     // 兼容代码开始
                     // 兼容代码开始
-                    if ($key == 'sub' && $query_value > 3) {
+                    if ($key == 'sub' && $query_value > 4) {
                         $query_value = 1;
                         $query_value = 1;
                     }
                     }
                     // 兼容代码结束
                     // 兼容代码结束
@@ -227,13 +227,14 @@ class LinkController extends BaseController
                 ];
                 ];
                 break;
                 break;
             case 'sub':
             case 'sub':
-                if ($value == 3) {
-                    $return = self::getSubscribeExtend('v2rayn');
-                } elseif ($value == 2) {
-                    $return = self::getSubscribeExtend('ss');
-                } else {
-                    $return = self::getSubscribeExtend('ssr');
-                }
+                $strArray = [
+                    1 => 'ssr',
+                    2 => 'ss',
+                    3 => 'v2rayn',
+                    4 => 'trojan',
+                ];
+                $str = (!in_array($value, $strArray) ? $strArray[$value] : $strArray[1]);
+                $return = self::getSubscribeExtend($str);
                 break;
                 break;
             case 'clash':
             case 'clash':
                 if ($value !== null) {
                 if ($value !== null) {
@@ -444,6 +445,7 @@ class LinkController extends BaseController
             'ss'              => '?sub=2',
             'ss'              => '?sub=2',
             'ssr'             => '?sub=1',
             'ssr'             => '?sub=1',
             'v2ray'           => '?sub=3',
             'v2ray'           => '?sub=3',
+            'trojan'          => '?sub=4',
             // apps
             // apps
             'ssa'             => '?list=ssa',
             'ssa'             => '?list=ssa',
             'ssd'             => '?ssd=1',
             'ssd'             => '?ssd=1',
@@ -497,9 +499,10 @@ class LinkController extends BaseController
                 $return = AppURI::getClashURI($item, true);
                 $return = AppURI::getClashURI($item, true);
                 break;
                 break;
             case 'v2rayn':
             case 'v2rayn':
-                $item['ps'] = $item['remark'];
-                $item['type'] = $item['headerType'];
-                $return = 'vmess://' . base64_encode(json_encode($item, 320));
+                $return = AppURI::getV2RayNURI($item);
+                break;
+            case 'trojan':
+                $return = AppURI::getTrojanURI($item);
                 break;
                 break;
             case 'kitsunebi':
             case 'kitsunebi':
                 $return = AppURI::getKitsunebiURI($item);
                 $return = AppURI::getKitsunebiURI($item);
@@ -618,6 +621,9 @@ class LinkController extends BaseController
             if (in_array($list, ['kitsunebi', 'quantumult', 'v2rayn'])) {
             if (in_array($list, ['kitsunebi', 'quantumult', 'v2rayn'])) {
                 $Extend['type'] = 'vmess';
                 $Extend['type'] = 'vmess';
                 $out = self::getListItem($Extend, $list);
                 $out = self::getListItem($Extend, $list);
+            } elseif ($list == 'trojan') {
+                $Extend['type'] = 'trojan';
+                $out = self::getListItem($Extend, $list);
             } elseif ($list == 'ssr') {
             } elseif ($list == 'ssr') {
                 $Extend['type'] = 'ssr';
                 $Extend['type'] = 'ssr';
                 $out = self::getListItem($Extend, $list);
                 $out = self::getListItem($Extend, $list);
@@ -897,6 +903,10 @@ class LinkController extends BaseController
                 $Rule['type'] = 'vmess';
                 $Rule['type'] = 'vmess';
                 $getListExtend = $Rule['extend'] ? self::getListExtend($user, 'v2rayn') : [];
                 $getListExtend = $Rule['extend'] ? self::getListExtend($user, 'v2rayn') : [];
                 break;
                 break;
+            case 4: // Trojan
+                $Rule['type'] = 'trojan';
+                $getListExtend = $Rule['extend'] ? self::getListExtend($user, 'trojan') : [];
+                break;
             default: // SSR
             default: // SSR
                 $Rule['type'] = 'ssr';
                 $Rule['type'] = 'ssr';
                 $getListExtend = $Rule['extend'] ? self::getListExtend($user, 'ssr') : [];
                 $getListExtend = $Rule['extend'] ? self::getListExtend($user, 'ssr') : [];

+ 10 - 0
src/Models/User.php

@@ -931,4 +931,14 @@ class User extends Model
                 break;
                 break;
         }
         }
     }
     }
+
+    /**
+     * 获取转发规则
+     */
+    public function getRelays()
+    {
+        return (!Tools::is_protocol_relay($this)
+            ? []
+            : Relay::where('user_id', $this->id)->orwhere('user_id', 0)->orderBy('id', 'asc')->get());
+    }
 }
 }

+ 109 - 49
src/Utils/AppURI.php

@@ -6,7 +6,7 @@ use App\Services\Config;
 
 
 class AppURI
 class AppURI
 {
 {
-    public static function getItemUrl($item, $is_ss)
+    public static function getItemUrl(array $item, int $is_ss)
     {
     {
         $ss_obfs_list = Config::getSupportParam('ss_obfs');
         $ss_obfs_list = Config::getSupportParam('ss_obfs');
         if (!$is_ss) {
         if (!$is_ss) {
@@ -51,7 +51,33 @@ class AppURI
         return $ssurl;
         return $ssurl;
     }
     }
 
 
-    public static function getSurgeURI($item, $version)
+    public static function getV2RayNURI(array $item)
+    {
+        $return = null;
+        switch ($item['type']) {
+            case 'vmess':
+                $node = [
+                    'v'     => 2,
+                    'ps'    => $item['remark'],
+                    'add'   => $item['add'],
+                    'port'  => $item['port'],
+                    'id'    => $item['id'],
+                    'aid'   => $item['aid'],
+                    'net'   => $item['net'],
+                    'type'  => $item['headerType'],
+                    'host'  => $item['host'],
+                    'path'  => $item['path'],
+                    'tls'   => $item['tls']
+                ];
+                $return = ('vmess://' . base64_encode(
+                    json_encode($node, 320)
+                ));
+                break;
+        }
+        return $return;
+    }
+
+    public static function getSurgeURI(array $item, int $version)
     {
     {
         $return = null;
         $return = null;
         switch ($version) {
         switch ($version) {
@@ -83,13 +109,16 @@ class AppURI
                             : '');
                             : '');
                         $return = $item['remark'] . ' = vmess, ' . $item['add'] . ', ' . $item['port'] . ', username = ' . $item['id'] . $ws . $tls;
                         $return = $item['remark'] . ' = vmess, ' . $item['add'] . ', ' . $item['port'] . ', username = ' . $item['id'] . $ws . $tls;
                         break;
                         break;
+                    case 'trojan':
+                        $return = ($item['remark'] . ' = trojan, ' . $item['address'] . ', ' . $item['port'] . ', password=' . $item['passwd']);
+                        break;
                 }
                 }
                 break;
                 break;
         }
         }
         return $return;
         return $return;
     }
     }
 
 
-    public static function getQuantumultURI($item, $base64_encode = false)
+    public static function getQuantumultURI(array $item, bool $base64_encode = false)
     {
     {
         $return = null;
         $return = null;
         switch ($item['type']) {
         switch ($item['type']) {
@@ -128,7 +157,7 @@ class AppURI
         return $return;
         return $return;
     }
     }
 
 
-    public static function getQuantumultXURI($item)
+    public static function getQuantumultXURI(array $item)
     {
     {
         $return = null;
         $return = null;
         switch ($item['type']) {
         switch ($item['type']) {
@@ -188,11 +217,17 @@ class AppURI
                 }
                 }
                 $return .= (', tag=' . $item['remark']);
                 $return .= (', tag=' . $item['remark']);
                 break;
                 break;
+            case 'trojan':
+                // ;trojan=example.com:443, password=pwd, over-tls=true, tls-verification=true, fast-open=false, udp-relay=false, tag=trojan-tls-01
+                $return  = ('trojan=' . $item['address'] . ':' . $item['port'] . ', password=' . $item['passwd']);
+                $return .= ', over-tls=true, tls-verification=true';
+                $return .= (', tag=' . $item['remark']);
+                break;
         }
         }
         return $return;
         return $return;
     }
     }
 
 
-    public static function getSurfboardURI($item)
+    public static function getSurfboardURI(array $item)
     {
     {
         $return = null;
         $return = null;
         switch ($item['type']) {
         switch ($item['type']) {
@@ -218,7 +253,7 @@ class AppURI
         return $return;
         return $return;
     }
     }
 
 
-    public static function getClashURI($item, $ssr_support = false)
+    public static function getClashURI(array $item, bool $ssr_support = false)
     {
     {
         $return = null;
         $return = null;
         if ($item['type'] == 'ssr' && $ssr_support === false) {
         if ($item['type'] == 'ssr' && $ssr_support === false) {
@@ -232,13 +267,13 @@ class AppURI
                     break;
                     break;
                 }
                 }
                 $return = [
                 $return = [
-                    'name' => $item['remark'],
-                    'type' => 'ss',
-                    'server' => $item['address'],
-                    'port' => $item['port'],
-                    'cipher' => $item['method'],
-                    'password' => $item['passwd'],
-                    'udp' => true
+                    'name'        => $item['remark'],
+                    'type'        => 'ss',
+                    'server'      => $item['address'],
+                    'port'        => $item['port'],
+                    'cipher'      => $item['method'],
+                    'password'    => $item['passwd'],
+                    'udp'         => true
                 ];
                 ];
                 if ($item['obfs'] != 'plain') {
                 if ($item['obfs'] != 'plain') {
                     switch ($item['obfs']) {
                     switch ($item['obfs']) {
@@ -273,27 +308,27 @@ class AppURI
                 }
                 }
                 break;
                 break;
             case 'ssr':
             case 'ssr':
-                if (
-                    in_array($item['method'], ['rc4-md5-6', 'des-ede3-cfb', 'xsalsa20', 'none'])
-                    ||
-                    in_array($item['protocol'], array_merge(Config::getSupportParam('allow_none_protocol'), ['verify_deflate']))
-                    ||
-                    in_array($item['obfs'], ['tls1.2_ticket_fastauth'])
-                ) {
-                    // 不支持的
-                    break;
-                }
+                // if (
+                //     in_array($item['method'], ['rc4-md5-6', 'des-ede3-cfb', 'xsalsa20', 'none'])
+                //     ||
+                //     in_array($item['protocol'], array_merge(Config::getSupportParam('allow_none_protocol'), ['verify_deflate']))
+                //     ||
+                //     in_array($item['obfs'], ['tls1.2_ticket_fastauth'])
+                // ) {
+                //     // 不支持的
+                //     break;
+                // }
                 $return = [
                 $return = [
-                    'name' => $item['remark'],
-                    'type' => 'ssr',
-                    'server' => $item['address'],
-                    'port' => $item['port'],
-                    'cipher' => $item['method'],
-                    'password' => $item['passwd'],
-                    'protocol' => $item['protocol'],
-                    'protocolparam' => $item['protocol_param'],
-                    'obfs' => $item['obfs'],
-                    'obfsparam' => $item['obfs_param']
+                    'name'            => $item['remark'],
+                    'type'            => 'ssr',
+                    'server'          => $item['address'],
+                    'port'            => $item['port'],
+                    'cipher'          => $item['method'],
+                    'password'        => $item['passwd'],
+                    'protocol'        => $item['protocol'],
+                    'protocolparam'   => $item['protocol_param'],
+                    'obfs'            => $item['obfs'],
+                    'obfsparam'       => $item['obfs_param']
                 ];
                 ];
                 break;
                 break;
             case 'vmess':
             case 'vmess':
@@ -301,14 +336,14 @@ class AppURI
                     break;
                     break;
                 }
                 }
                 $return = [
                 $return = [
-                    'name' => $item['remark'],
-                    'type' => 'vmess',
-                    'server' => $item['add'],
-                    'port' => $item['port'],
-                    'uuid' => $item['id'],
+                    'name'    => $item['remark'],
+                    'type'    => 'vmess',
+                    'server'  => $item['add'],
+                    'port'    => $item['port'],
+                    'uuid'    => $item['id'],
                     'alterId' => $item['aid'],
                     'alterId' => $item['aid'],
-                    'cipher' => 'auto',
-                    'udp' => true
+                    'cipher'  => 'auto',
+                    'udp'     => true
                 ];
                 ];
                 if ($item['net'] == 'ws') {
                 if ($item['net'] == 'ws') {
                     $return['network'] = 'ws';
                     $return['network'] = 'ws';
@@ -322,11 +357,20 @@ class AppURI
                     }
                     }
                 }
                 }
                 break;
                 break;
+            case 'trojan':
+                $return = [
+                    'name'        => $item['remark'],
+                    'type'        => 'trojan',
+                    'server'      => $item['address'],
+                    'port'        => $item['port'],
+                    'password'    => $item['passwd']
+                ];
+                break;
         }
         }
         return $return;
         return $return;
     }
     }
 
 
-    public static function getShadowrocketURI($item)
+    public static function getShadowrocketURI(array $item)
     {
     {
         $return = null;
         $return = null;
         switch ($item['type']) {
         switch ($item['type']) {
@@ -336,11 +380,11 @@ class AppURI
                 } else {
                 } else {
                     if ($item['obfs'] == 'v2ray') {
                     if ($item['obfs'] == 'v2ray') {
                         $v2rayplugin = [
                         $v2rayplugin = [
-                            'address' => $item['address'],
-                            'port' => (string) $item['port'],
-                            'path' => $item['path'],
-                            'host' => $item['host'],
-                            'mode' => 'websocket',
+                            'address'   => $item['address'],
+                            'port'      => (string) $item['port'],
+                            'path'      => $item['path'],
+                            'host'      => $item['host'],
+                            'mode'      => 'websocket',
                         ];
                         ];
                         $v2rayplugin['tls'] = $item['tls'] == 'tls' ? true : false;
                         $v2rayplugin['tls'] = $item['tls'] == 'tls' ? true : false;
                         $return = ('ss://' . Tools::base64_url_encode($item['method'] . ':' . $item['passwd'] . '@' . $item['address'] . ':' . $item['port']) . '?v2ray-plugin=' . base64_encode(json_encode($v2rayplugin)) . '#' . rawurlencode($item['remark']));
                         $return = ('ss://' . Tools::base64_url_encode($item['method'] . ':' . $item['passwd'] . '@' . $item['address'] . ':' . $item['port']) . '?v2ray-plugin=' . base64_encode(json_encode($v2rayplugin)) . '#' . rawurlencode($item['remark']));
@@ -391,11 +435,15 @@ class AppURI
                 }
                 }
                 $return = ('vmess://' . Tools::base64_url_encode('chacha20-poly1305:' . $item['id'] . '@' . $item['add'] . ':' . $item['port']) . '?remarks=' . rawurlencode($item['remark']) . $obfs . $tls);
                 $return = ('vmess://' . Tools::base64_url_encode('chacha20-poly1305:' . $item['id'] . '@' . $item['add'] . ':' . $item['port']) . '?remarks=' . rawurlencode($item['remark']) . $obfs . $tls);
                 break;
                 break;
+            case 'trojan':
+                $return  = ('trojan://' . $item['passwd'] . '@' . $item['address'] . ':' . $item['port']);
+                $return .= ('#' . rawurlencode($item['remark']));
+                break;
         }
         }
         return $return;
         return $return;
     }
     }
 
 
-    public static function getKitsunebiURI($item)
+    public static function getKitsunebiURI(array $item)
     {
     {
         $return = null;
         $return = null;
         switch ($item['type']) {
         switch ($item['type']) {
@@ -434,7 +482,7 @@ class AppURI
         return $return;
         return $return;
     }
     }
 
 
-    public static function getSSDURI($item)
+    public static function getSSDURI(array $item)
     {
     {
         $return = null;
         $return = null;
         switch ($item['type']) {
         switch ($item['type']) {
@@ -482,7 +530,7 @@ class AppURI
         return $return;
         return $return;
     }
     }
 
 
-    public static function getSSJSON($item)
+    public static function getSSJSON(array $item)
     {
     {
         $return = null;
         $return = null;
         switch ($item['type']) {
         switch ($item['type']) {
@@ -528,4 +576,16 @@ class AppURI
         }
         }
         return $return;
         return $return;
     }
     }
+
+    public static function getTrojanURI(array $item)
+    {
+        $return = null;
+        switch ($item['type']) {
+            case 'trojan':
+                $return  = ('trojan://' . $item['passwd'] . '@' . $item['address'] . ':' . $item['port']);
+                $return .= ('#' . rawurlencode($item['remark']));
+                break;
+        }
+        return $return;
+    }
 }
 }

+ 241 - 529
src/Utils/URL.php

@@ -91,7 +91,7 @@ class URL
         return $return_array;
         return $return_array;
     }
     }
 
 
-    public static function SSCanConnect($user, $mu_port = 0)
+    public static function SSCanConnect(User $user, $mu_port = 0)
     {
     {
         if ($mu_port != 0) {
         if ($mu_port != 0) {
             $mu_user = User::where('port', '=', $mu_port)->where('is_multi_user', '<>', 0)->first();
             $mu_user = User::where('port', '=', $mu_port)->where('is_multi_user', '<>', 0)->first();
@@ -103,7 +103,7 @@ class URL
         return self::CanMethodConnect($user->method) >= 2 && self::CanProtocolConnect($user->protocol) >= 2 && self::CanObfsConnect($user->obfs) >= 2;
         return self::CanMethodConnect($user->method) >= 2 && self::CanProtocolConnect($user->protocol) >= 2 && self::CanObfsConnect($user->obfs) >= 2;
     }
     }
 
 
-    public static function SSRCanConnect($user, $mu_port = 0)
+    public static function SSRCanConnect(User $user, $mu_port = 0)
     {
     {
         if ($mu_port != 0) {
         if ($mu_port != 0) {
             $mu_user = User::where('port', '=', $mu_port)->where('is_multi_user', '<>', 0)->first();
             $mu_user = User::where('port', '=', $mu_port)->where('is_multi_user', '<>', 0)->first();
@@ -115,7 +115,7 @@ class URL
         return self::CanMethodConnect($user->method) != 2 && self::CanProtocolConnect($user->protocol) != 2 && self::CanObfsConnect($user->obfs) != 2;
         return self::CanMethodConnect($user->method) != 2 && self::CanProtocolConnect($user->protocol) != 2 && self::CanObfsConnect($user->obfs) != 2;
     }
     }
 
 
-    public static function getSSConnectInfo($user)
+    public static function getSSConnectInfo(User $user)
     {
     {
         $new_user = clone $user;
         $new_user = clone $user;
         if (self::CanObfsConnect($new_user->obfs) == 5) {
         if (self::CanObfsConnect($new_user->obfs) == 5) {
@@ -131,7 +131,7 @@ class URL
         return $new_user;
         return $new_user;
     }
     }
 
 
-    public static function getSSRConnectInfo($user)
+    public static function getSSRConnectInfo(User $user)
     {
     {
         $new_user = clone $user;
         $new_user = clone $user;
         if (self::CanObfsConnect($new_user->obfs) == 4) {
         if (self::CanObfsConnect($new_user->obfs) == 4) {
@@ -143,113 +143,46 @@ class URL
         return $new_user;
         return $new_user;
     }
     }
 
 
-    public static function getAllItems($user, $is_mu = 0, $is_ss = 0)
+    /**
+     * 获取全部节点对象
+     *
+     * @param User  $user
+     * @param mixed $sort  数值或数组
+     * @param array $rules 节点筛选规则
+     */
+    public static function getNodes(User $user, $sort, array $rules = []): \Illuminate\Database\Eloquent\Collection
     {
     {
-        $return_array = array();
-        if ($user->is_admin) {
-            $nodes = Node::where(
-                static function ($query) {
-                    $query->where('sort', 0)->orwhere('sort', 10);
-                }
-            )
-                ->where('type', '1')
-                ->orderBy('name')
-                ->get();
+        $query = Node::query();
+        if (is_array($sort)) {
+            $query->whereIn('sort', $sort);
         } else {
         } else {
-            $nodes = Node::where(
-                static function ($query) {
-                    $query->where('sort', 0)->orwhere('sort', 10);
-                }
-            )
-                ->where(
-                    static function ($query) use ($user) {
-                        $query->where('node_group', '=', $user->node_group)
-                            ->orWhere('node_group', '=', 0);
-                    }
-                )
-                ->where('type', '1')
-                ->where('node_class', '<=', $user->class)
-                ->orderBy('name')
-                ->get();
-        }
-        if ($is_mu) {
-            if ($user->is_admin) {
-                if ($is_mu != 1) {
-                    $mu_nodes = Node::where('sort', 9)->where('server', '=', $is_mu)->where('type', '1')->get();
-                } else {
-                    $mu_nodes = Node::where('sort', 9)->where('type', '1')->get();
-                }
-            } elseif ($is_mu != 1) {
-                $mu_nodes = Node::where('sort', 9)->where('server', '=', $is_mu)->where('node_class', '<=', $user->class)->where('type', '1')->where(
-                    static function ($query) use ($user) {
-                        $query->where('node_group', '=', $user->node_group)
-                            ->orWhere('node_group', '=', 0);
-                    }
-                )->get();
-            } else {
-                $mu_nodes = Node::where('sort', 9)->where('node_class', '<=', $user->class)->where('type', '1')->where(
-                    static function ($query) use ($user) {
-                        $query->where('node_group', '=', $user->node_group)
-                            ->orWhere('node_group', '=', 0);
-                    }
-                )->get();
-            }
+            $query->where('sort', $sort);
         }
         }
-        $relay_rules = Relay::where('user_id', $user->id)->orwhere('user_id', 0)->orderBy('id', 'asc')->get();
-        if (!Tools::is_protocol_relay($user)) {
-            $relay_rules = array();
+        if (!$user->is_admin) {
+            $group = ($user->node_group != 0 ? [0, $user->node_group] : [0]);
+            $query->whereIn('node_group', $group)
+                ->where('node_class', '<=', $user->class);
         }
         }
-        foreach ($nodes as $node) {
-            if ($node->mu_only != 1 && $is_mu == 0) {
-                if ($node->sort == 10) {
-                    $relay_rule_id = 0;
-                    $relay_rule = Tools::pick_out_relay_rule($node->id, $user->port, $relay_rules);
-                    if (($relay_rule != null) && $relay_rule->dist_node() != null) {
-                        $relay_rule_id = $relay_rule->id;
-                    }
-                    $item = self::getItem($user, $node, 0, $relay_rule_id, $is_ss);
-                    if ($item != null) {
-                        $return_array[] = $item;
-                    }
-                } else {
-                    $item = self::getItem($user, $node, 0, 0, $is_ss);
-                    if ($item != null) {
-                        $return_array[] = $item;
-                    }
-                }
-            }
-            if ($node->custom_rss == 1 && $node->mu_only != -1 && $is_mu != 0) {
-                foreach ($mu_nodes as $mu_node) {
-                    if ($node->sort == 10) {
-                        $relay_rule_id = 0;
-                        $relay_rule = Tools::pick_out_relay_rule($node->id, $mu_node->server, $relay_rules);
-                        if (($relay_rule != null) && $relay_rule->dist_node() != null) {
-                            $relay_rule_id = $relay_rule->id;
-                        }
-                        $item = self::getItem($user, $node, $mu_node->server, $relay_rule_id, $is_ss);
-                        if ($item != null) {
-                            $return_array[] = $item;
-                        }
-                    } else {
-                        $item = self::getItem($user, $node, $mu_node->server, 0, $is_ss);
-                        if ($item != null) {
-                            $return_array[] = $item;
-                        }
-                    }
-                }
-            }
+        // 等级筛选
+        if (isset($rules['content']['class']) && count($rules['content']['class']) > 0) {
+            $query->whereIn('node_class', $rules['content']['class']);
+        }
+        if (isset($rules['content']['noclass']) && count($rules['content']['noclass']) > 0) {
+            $query->whereNotIn('node_class', $rules['content']['noclass']);
         }
         }
+        // 等级筛选 end
+        $nodes = $query->where('type', '1')
+            ->orderBy('name')->get();
 
 
-        return $return_array;
+        return $nodes;
     }
     }
 
 
-
     /**
     /**
      * 获取全部节点
      * 获取全部节点
      * 
      * 
-     *  <code>
-     *  $Rule = [
-     *      'type'    => 'ss | ssr | vmess',
+     * ```
+     * $Rule = [
+     *      'type'    => 'all | ss | ssr | vmess | trojan',
      *      'emoji'   => false,
      *      'emoji'   => false,
      *      'is_mu'   => 1,
      *      'is_mu'   => 1,
      *      'content' => [
      *      'content' => [
@@ -257,27 +190,22 @@ class URL
      *          'class'   => [0, 1, 2],
      *          'class'   => [0, 1, 2],
      *          'regex'   => '.*香港.*HKBN.*',
      *          'regex'   => '.*香港.*HKBN.*',
      *      ]
      *      ]
-     *  ]
-     *  </code>
+     * ]
+     * ```
      *
      *
      * @param User  $user 用户
      * @param User  $user 用户
      * @param array $Rule 节点筛选规则
      * @param array $Rule 节点筛选规则
-     *
-     * @return array
      */
      */
-    public static function getNew_AllItems($user, $Rule): array
+    public static function getNew_AllItems(User $user, array $Rule): array
     {
     {
-        if (isset($Rule['is_mu'])) {
-            $is_mu = $Rule['is_mu'];
-        } else {
-            $is_mu = ($_ENV['mergeSub'] === true ? 1 : 0);
-        }
-        $is_ss = 0;
+        $is_ss = [0];
+        $is_mu = (isset($Rule['is_mu']) ? $Rule['is_mu'] : (int) $_ENV['mergeSub']);
         $emoji = (isset($Rule['emoji']) ? $Rule['emoji'] : false);
         $emoji = (isset($Rule['emoji']) ? $Rule['emoji'] : false);
+
         switch ($Rule['type']) {
         switch ($Rule['type']) {
             case 'ss':
             case 'ss':
                 $sort = [0, 10, 13];
                 $sort = [0, 10, 13];
-                $is_ss = 1;
+                $is_ss = [1];
                 break;
                 break;
             case 'ssr':
             case 'ssr':
                 $sort = [0, 10];
                 $sort = [0, 10];
@@ -285,206 +213,134 @@ class URL
             case 'vmess':
             case 'vmess':
                 $sort = [11, 12];
                 $sort = [11, 12];
                 break;
                 break;
+            case 'trojan':
+                $sort = [14];
+                break;
             default:
             default:
                 $Rule['type'] = 'all';
                 $Rule['type'] = 'all';
-                $sort = [0, 10, 11, 12, 13];
+                $sort = [0, 10, 11, 12, 13, 14];
+                $is_ss = [0, 1];
                 break;
                 break;
         }
         }
-        if ($user->is_admin) {
-            $nodes = Node::whereIn('sort', $sort)->where('type', '1')->orderBy('name')->get();
-        } else {
-            $node_query = Node::query();
-            $node_query->whereIn('sort', $sort)->where('type', '1')->where(
-                static function ($query) use ($user) {
-                    $query->where('node_group', '=', $user->node_group)
-                        ->orWhere('node_group', '=', 0);
-                }
-            );
-            $class = [];
-            if (isset($Rule['content']['class']) && count($Rule['content']['class']) > 0) {
-                foreach ($Rule['content']['class'] as $x) {
-                    if ($x <= $user->class && $x >= 0 && !in_array($x, $class)) {
-                        $class[] = $x;
-                    }
-                }
-            }
-            if (count($class) > 0) {
-                $node_query->whereIn('node_class', $class);
-            } else {
-                $node_query->where('node_class', '<=', $user->class);
-            }
-            $nodes = $node_query->orderBy('name')->get();
-        }
-        $return_array = array();
-        if ($is_mu != 0 && $Rule['type'] != 'vmess') {
+
+        // 获取节点
+        $nodes = self::getNodes($user, $sort, $Rule);
+
+        // 单端口 sort = 9
+        $mu_nodes = [];
+        if ($is_mu != 0 && in_array($Rule['type'], ['all', 'ss', 'ssr'])) {
             $mu_node_query = Node::query();
             $mu_node_query = Node::query();
             $mu_node_query->where('sort', 9)->where('type', '1');
             $mu_node_query->where('sort', 9)->where('type', '1');
-            if ($user->is_admin) {
-                if ($is_mu != 1) {
-                    $mu_node_query->where('server', $is_mu);
-                }
-            } elseif ($is_mu != 1) {
-                $mu_node_query->where('server', $is_mu)
-                    ->where('node_class', '<=', $user->class)
-                    ->where(
-                        static function ($query) use ($user) {
-                            $query->where('node_group', '=', $user->node_group)
-                                ->orWhere('node_group', '=', 0);
-                        }
-                    );
-            } else {
+            if ($is_mu != 1) {
+                $mu_node_query->where('server', $is_mu);
+            }
+            if (!$user->is_admin) {
+                $group = ($user->node_group != 0 ? [0, $user->node_group] : [0]);
                 $mu_node_query->where('node_class', '<=', $user->class)
                 $mu_node_query->where('node_class', '<=', $user->class)
-                    ->where(
-                        static function ($query) use ($user) {
-                            $query->where('node_group', '=', $user->node_group)
-                                ->orWhere('node_group', '=', 0);
-                        }
-                    );
+                    ->whereIn('node_group', $group);
             }
             }
             $mu_nodes = $mu_node_query->get();
             $mu_nodes = $mu_node_query->get();
         }
         }
-        if (!Tools::is_protocol_relay($user)) {
-            $relay_rules = array();
-        } else {
-            $relay_rules = Relay::where('user_id', $user->id)->orwhere('user_id', 0)->orderBy('id', 'asc')->get();
-        }
-        $foreachss = [];
-        if ($Rule['type'] == 'all') {
-            $foreachss = [0, 1];
-        } else {
-            $foreachss[] = $is_ss;
-        }
-        foreach ($foreachss as $x) {
-            // all is_ss *2
-            foreach ($nodes as $node) {
-                if (in_array($node->sort, [13]) && (($Rule['type'] == 'all' && $x == 0) || ($Rule['type'] != 'all'))) {
-                    // Rico SS (V2RayPlugin && obfs)
-                    $item = self::getV2RayPluginItem($user, $node, $emoji);
-                    if ($item != null) {
-                        $find = (isset($Rule['content']['regex']) && $Rule['content']['regex'] != '' ? ConfController::getMatchProxy($item, ['content' => ['regex' => $Rule['content']['regex']]]) : true);
-                        if ($find) {
-                            $return_array[] = $item;
-                        }
-                    }
-                    continue;
-                }
-                if (in_array($node->sort, [11, 12]) && (($Rule['type'] == 'all' && $x == 0) || ($Rule['type'] != 'all'))) {
-                    // V2Ray
-                    $item = self::getV2Url($user, $node, true, $emoji);
-                    if ($item != null) {
-                        $find = (isset($Rule['content']['regex']) && $Rule['content']['regex'] != '' ? ConfController::getMatchProxy($item, ['content' => ['regex' => $Rule['content']['regex']]]) : true);
-                        if ($find) {
-                            $return_array[] = $item;
-                        }
-                    }
+
+        // 获取适用于用户的中转规则
+        $relay_rules = $user->getRelays();
+
+        $return_array = [];
+        foreach ($nodes as $node) {
+            if (isset($Rule['content']['regex']) && $Rule['content']['regex'] != '') {
+                // 节点名称筛选
+                if (
+                    ConfController::getMatchProxy(
+                        [
+                            'remark' => $node->name
+                        ],
+                        [
+                            'content' => [
+                                'regex' => $Rule['content']['regex']
+                            ]
+                        ]
+                    ) === null
+                ) {
                     continue;
                     continue;
                 }
                 }
-                if (in_array($node->sort, [0, 10]) && $node->mu_only != 1 && ($is_mu == 0 || ($is_mu != 0 && $_ENV['mergeSub'] === true))) {
-                    // 节点非只启用单端口 && 只获取普通端口
-                    if ($node->sort == 10) {
-                        // SS 中转
-                        $relay_rule_id = 0;
-                        $relay_rule = Tools::pick_out_relay_rule($node->id, $user->port, $relay_rules);
-                        if ($relay_rule != null && $relay_rule->dist_node() != null) {
-                            $relay_rule_id = $relay_rule->id;
-                        }
-                        $item = self::getItem($user, $node, 0, $relay_rule_id, $x, $emoji);
-                        if ($item != null) {
-                            $find = (isset($Rule['content']['regex']) && $Rule['content']['regex'] != '' ? ConfController::getMatchProxy($item, ['content' => ['regex' => $Rule['content']['regex']]]) : true);
-                            if ($find) {
-                                $return_array[] = $item;
-                            }
-                        }
-                    } else {
-                        // SS 非中转
-                        $item = self::getItem($user, $node, 0, 0, $x, $emoji);
-                        if ($item != null) {
-                            $find = (isset($Rule['content']['regex']) && $Rule['content']['regex'] != '' ? ConfController::getMatchProxy($item, ['content' => ['regex' => $Rule['content']['regex']]]) : true);
-                            if ($find) {
-                                $return_array[] = $item;
-                            }
-                        }
-                    }
+            }
+            // 筛选 End
+
+            // 其他类型单端口节点
+            if (in_array($node->sort, [11, 12, 13, 14])) {
+                $node_class = [
+                    11 => 'getV2RayItem',           // V2Ray
+                    12 => 'getV2RayItem',           // V2Ray
+                    13 => 'getV2RayPluginItem',     // Rico SS (V2RayPlugin && obfs)
+                    14 => 'getTrojanItem',          // Trojan
+                ];
+                $class = $node_class[$node->sort];
+                $item = self::$class($user, $node, $emoji);
+                if ($item != null) {
+                    $return_array[] = $item;
                 }
                 }
-                if (in_array($node->sort, [0, 10]) && $node->custom_rss == 1 && $node->mu_only != -1 && $is_mu != 0) {
-                    // 非只启用普通端口 && 获取单端口
-                    foreach ($mu_nodes as $mu_node) {
+                continue;
+            }
+            // 其他类型单端口节点 End
+
+            // SS 节点
+            if (in_array($node->sort, [0, 10])) {
+                // 节点非只启用单端口 && 只获取普通端口
+                if ($node->mu_only != 1 && ($is_mu == 0 || ($is_mu != 0 && $_ENV['mergeSub'] === true))) {
+                    foreach ($is_ss as $ss) {
                         if ($node->sort == 10) {
                         if ($node->sort == 10) {
                             // SS 中转
                             // SS 中转
                             $relay_rule_id = 0;
                             $relay_rule_id = 0;
-                            $relay_rule = Tools::pick_out_relay_rule($node->id, $mu_node->server, $relay_rules);
+                            $relay_rule = Tools::pick_out_relay_rule($node->id, $user->port, $relay_rules);
                             if ($relay_rule != null && $relay_rule->dist_node() != null) {
                             if ($relay_rule != null && $relay_rule->dist_node() != null) {
                                 $relay_rule_id = $relay_rule->id;
                                 $relay_rule_id = $relay_rule->id;
                             }
                             }
-                            $item = self::getItem($user, $node, $mu_node->server, $relay_rule_id, $x, $emoji);
-                            if ($item != null) {
-                                $find = (isset($Rule['content']['regex']) && $Rule['content']['regex'] != '' ? ConfController::getMatchProxy($item, ['content' => ['regex' => $Rule['content']['regex']]]) : true);
-                                if ($find) {
-                                    $return_array[] = $item;
-                                }
-                            }
+                            $item = self::getItem($user, $node, 0, $relay_rule_id, $ss, $emoji);
                         } else {
                         } else {
                             // SS 非中转
                             // SS 非中转
-                            $item = self::getItem($user, $node, $mu_node->server, 0, $x, $emoji);
-                            if ($item != null) {
-                                $find = (isset($Rule['content']['regex']) && $Rule['content']['regex'] != '' ? ConfController::getMatchProxy($item, ['content' => ['regex' => $Rule['content']['regex']]]) : true);
-                                if ($find) {
-                                    $return_array[] = $item;
+                            $item = self::getItem($user, $node, 0, 0, $ss, $emoji);
+                        }
+                        if ($item != null) {
+                            $return_array[] = $item;
+                        }
+                    }
+                }
+                // 获取 SS 普通端口 End
+
+                // 非只启用普通端口 && 获取单端口
+                if ($node->mu_only != -1 && $is_mu != 0) {
+                    foreach ($is_ss as $ss) {
+                        foreach ($mu_nodes as $mu_node) {
+                            if ($node->sort == 10) {
+                                // SS 中转
+                                $relay_rule_id = 0;
+                                $relay_rule = Tools::pick_out_relay_rule($node->id, $mu_node->server, $relay_rules);
+                                if ($relay_rule != null && $relay_rule->dist_node() != null) {
+                                    $relay_rule_id = $relay_rule->id;
                                 }
                                 }
+                                $item = self::getItem($user, $node, $mu_node->server, $relay_rule_id, $ss, $emoji);
+                            } else {
+                                // SS 非中转
+                                $item = self::getItem($user, $node, $mu_node->server, 0, $ss, $emoji);
+                            }
+                            if ($item != null) {
+                                $return_array[] = $item;
                             }
                             }
                         }
                         }
                     }
                     }
                 }
                 }
+                // 获取 SS 单端口 End
             }
             }
-        }
-        if (!isset($Rule['content']['class']) && isset($Rule['content']['noclass']) && count($Rule['content']['noclass']) > 0) {
-            // 如果设置不需要的等级的节点
-            $tmp = [];
-            foreach ($return_array as $outnode) {
-                if (!in_array($outnode['class'], $Rule['content']['noclass'])) {
-                    // 放行节点等级不存在数组内的
-                    $tmp[] = $outnode;
-                }
-            }
-            $return_array = $tmp;
-        }
-        if ($Rule['type'] != 'all') {
-            // 如果获取节点的类型不是 all
-            $tmp = [];
-            foreach ($return_array as $outnode) {
-                if ($outnode['type'] == $Rule['type']) {
-                    // 放行类型相同
-                    $tmp[] = $outnode;
-                }
-            }
-            $return_array = $tmp;
-        }
-        return $return_array;
-    }
-
-    public static function getAllUrl($user, $is_mu, $is_ss = 0)
-    {
-        $return_url = '';
-        if (strtotime($user->expire_in) < time()) {
-            return $return_url;
-        }
-        $items = self::getAllItems($user, $is_mu, $is_ss);
-        foreach ($items as $item) {
-            $return_url .= self::getItemUrl($item, $is_ss) . PHP_EOL;
-        }
-        $is_mu = $is_mu == 0 ? 1 : 0;
-        $items = self::getAllItems($user, $is_mu, $is_ss);
-        foreach ($items as $item) {
-            $return_url .= self::getItemUrl($item, $is_ss) . PHP_EOL;
+            // SS 节点 End
         }
         }
 
 
-        return $return_url;
+        return $return_array;
     }
     }
 
 
     /**
     /**
      * 获取全部节点 Url
      * 获取全部节点 Url
      * 
      * 
-     *  <code>
+     * ```
      *  $Rule = [
      *  $Rule = [
      *      'type'    => 'ss | ssr | vmess',
      *      'type'    => 'ss | ssr | vmess',
      *      'emoji'   => false,
      *      'emoji'   => false,
@@ -495,14 +351,12 @@ class URL
      *          'regex'   => '.*香港.*HKBN.*',
      *          'regex'   => '.*香港.*HKBN.*',
      *      ]
      *      ]
      *  ]
      *  ]
-     *  </code>
+     * ```
      *
      *
      * @param User  $user 用户
      * @param User  $user 用户
      * @param array $Rule 节点筛选规则
      * @param array $Rule 节点筛选规则
-     *
-     * @return string
      */
      */
-    public static function get_NewAllUrl($user, $Rule)
+    public static function get_NewAllUrl(User $user, array $Rule): string
     {
     {
         $return_url = '';
         $return_url = '';
         if (strtotime($user->expire_in) < time()) {
         if (strtotime($user->expire_in) < time()) {
@@ -527,6 +381,19 @@ class URL
         return AppURI::getItemUrl($item, $is_ss);
         return AppURI::getItemUrl($item, $is_ss);
     }
     }
 
 
+    /**
+     * 获取 SS && SSR 全部节点
+     *
+     * @param User $user 用户
+     * @param bool $emoji
+     *
+     * @return array
+     */
+    public static function getAllSSItems(User $user, $emoji = false)
+    {
+        return self::getNodes($user, [0, 10]);
+    }
+
     /**
     /**
      * 获取 V2RayPlugin 全部节点
      * 获取 V2RayPlugin 全部节点
      *
      *
@@ -535,27 +402,10 @@ class URL
      *
      *
      * @return array
      * @return array
      */
      */
-    public static function getAllV2RayPluginItems($user, $emoji = false)
+    public static function getAllV2RayPluginItems(User $user, $emoji = false)
     {
     {
         $return_array = array();
         $return_array = array();
-        if ($user->is_admin) {
-            $nodes = Node::where('sort', 13)
-                ->where('type', '1')
-                ->orderBy('name')
-                ->get();
-        } else {
-            $nodes = Node::where('sort', 13)
-                ->where(
-                    static function ($query) use ($user) {
-                        $query->where('node_group', '=', $user->node_group)
-                            ->orWhere('node_group', '=', 0);
-                    }
-                )
-                ->where('type', '1')
-                ->where('node_class', '<=', $user->class)
-                ->orderBy('name')
-                ->get();
-        }
+        $nodes = self::getNodes($user, 13);
         foreach ($nodes as $node) {
         foreach ($nodes as $node) {
             $item = self::getV2RayPluginItem($user, $node, $emoji);
             $item = self::getV2RayPluginItem($user, $node, $emoji);
             if ($item != null) {
             if ($item != null) {
@@ -575,20 +425,18 @@ class URL
      *
      *
      * @return array|null
      * @return array|null
      */
      */
-    public static function getV2RayPluginItem($user, $node, $emoji = false)
+    public static function getV2RayPluginItem(User $user, Node $node, bool $emoji = false)
     {
     {
         $return_array = Tools::ssv2Array($node->server);
         $return_array = Tools::ssv2Array($node->server);
         // 非 AEAD 加密无法使用
         // 非 AEAD 加密无法使用
         if ($return_array['net'] != 'obfs' && !in_array($user->method, Config::getSupportParam('ss_aead_method'))) {
         if ($return_array['net'] != 'obfs' && !in_array($user->method, Config::getSupportParam('ss_aead_method'))) {
             return null;
             return null;
         }
         }
-        $return_array['remark'] = ($emoji == true
-            ? Tools::addEmoji($node->name)
-            : $node->name);
-        $return_array['address'] = $return_array['add'];
-        $return_array['method'] = $user->method;
-        $return_array['passwd'] = $user->passwd;
-        $return_array['protocol'] = 'origin';
+        $return_array['remark']         = ($emoji ? Tools::addEmoji($node->name) : $node->name);
+        $return_array['address']        = $return_array['add'];
+        $return_array['method']         = $user->method;
+        $return_array['passwd']         = $user->passwd;
+        $return_array['protocol']       = 'origin';
         $return_array['protocol_param'] = '';
         $return_array['protocol_param'] = '';
         if ($return_array['net'] == 'obfs') {
         if ($return_array['net'] == 'obfs') {
             $return_array['obfs_param'] = $user->getMuMd5();
             $return_array['obfs_param'] = $user->getMuMd5();
@@ -626,9 +474,7 @@ class URL
         $item = Tools::v2Array($node->server);
         $item = Tools::v2Array($node->server);
         $item['v'] = '2';
         $item['v'] = '2';
         $item['type'] = 'vmess';
         $item['type'] = 'vmess';
-        $item['ps'] = ($emoji == true
-            ? Tools::addEmoji($node->name)
-            : $node->name);
+        $item['ps'] = ($emoji ? Tools::addEmoji($node->name) : $node->name);
         $item['remark'] = $item['ps'];
         $item['remark'] = $item['ps'];
         $item['id'] = $user->getUuid();
         $item['id'] = $user->getUuid();
         $item['class'] = $node->node_class;
         $item['class'] = $node->node_class;
@@ -637,7 +483,23 @@ class URL
                 json_encode($item, 320)
                 json_encode($item, 320)
             );
             );
         }
         }
+        return $item;
+    }
 
 
+    /**
+     * 获取 V2Ray 节点
+     *
+     * @param User $user
+     * @param Node $node
+     * @param bool $emoji
+     */
+    public static function getV2RayItem(User $user, Node $node, bool $emoji = false): array
+    {
+        $item           = Tools::v2Array($node->server);
+        $item['type']   = 'vmess';
+        $item['remark'] = ($emoji ? Tools::addEmoji($node->name) : $node->name);
+        $item['id']     = $user->getUuid();
+        $item['class']  = $node->node_class;
         return $item;
         return $item;
     }
     }
 
 
@@ -647,38 +509,10 @@ class URL
      * @param User $user
      * @param User $user
      * @param bool $arrout
      * @param bool $arrout
      * @param bool $emoji
      * @param bool $emoji
-     *
-     * @return array|string
      */
      */
-    public static function getAllVMessUrl($user, $arrout = false, $emoji = false)
+    public static function getAllVMessUrl(User $user, $arrout = false, $emoji = false)
     {
     {
-        if ($user->is_admin) {
-            $nodes = Node::where(
-                static function ($query) {
-                    $query->where('sort', 11)
-                        ->orwhere('sort', 12);
-                }
-            )
-                ->where('type', '1')
-                ->orderBy('name')
-                ->get();
-        } else {
-            $nodes = Node::where(
-                static function ($query) {
-                    $query->where('sort', 11)
-                        ->orwhere('sort', 12);
-                }
-            )->where(
-                static function ($query) use ($user) {
-                    $query->where('node_group', '=', $user->node_group)
-                        ->orWhere('node_group', '=', 0);
-                }
-            )
-                ->where('type', '1')
-                ->where('node_class', '<=', $user->class)
-                ->orderBy('name')
-                ->get();
-        }
+        $nodes = self::getNodes($user, [11, 12]);
         # 增加中转配置,后台目前配置user=0的话是自由门直接中转
         # 增加中转配置,后台目前配置user=0的话是自由门直接中转
         $tmp_nodes = array();
         $tmp_nodes = array();
         foreach ($nodes as $node) {
         foreach ($nodes as $node) {
@@ -718,172 +552,54 @@ class URL
         return $result;
         return $result;
     }
     }
 
 
-    // public static function getAllSSDUrl($user)
-    // {
-    //     if (!self::SSCanConnect($user)) {
-    //         return null;
-    //     }
-    //     $array_all = array();
-    //     $array_all['airport'] = $_ENV['appName'];
-    //     $array_all['port'] = $user->port;
-    //     $array_all['encryption'] = $user->method;
-    //     $array_all['password'] = $user->passwd;
-    //     $array_all['traffic_used'] = Tools::flowToGB($user->u + $user->d);
-    //     $array_all['traffic_total'] = Tools::flowToGB($user->transfer_enable);
-    //     $array_all['expiry'] = $user->class_expire;
-    //     $array_all['url'] = $_ENV['subUrl'] . LinkController::GenerateSSRSubCode($user->id, 0) . '?ssd=1';
-    //     $plugin_options = '';
-    //     if (strpos($user->obfs, 'http') != false) {
-    //         $plugin_options = 'obfs=http';
-    //     }
-    //     if (strpos($user->obfs, 'tls') != false) {
-    //         $plugin_options = 'obfs=tls';
-    //     }
-    //     if ($plugin_options != '') {
-    //         $array_all['plugin'] = 'simple-obfs';
-    //         $array_all['plugin_options'] = $plugin_options;
-    //         if ($user->obfs_param != '') {
-    //             $array_all['plugin_options'] .= ';obfs-host=' . $user->obfs_param;
-    //         }
-    //     }
-
-    //     $nodes_muport = Node::where('type', 1)
-    //         ->where('sort', '=', 9)
-    //         ->orderBy('name')
-    //         ->get();
-    //     $array_server = array();
-    //     $nodes = Node::where('type', 1)
-    //         ->where('node_class', '<=', $user->class)
-    //         ->where(
-    //             static function ($func) {
-    //                 $func->where('sort', '=', 0)
-    //                     ->orwhere('sort', '=', 10)
-    //                     ->orwhere('sort', '=', 13);
-    //             }
-    //         )
-    //         ->where(
-    //             static function ($func) use ($user) {
-    //                 $func->where('node_group', '=', $user->node_group)
-    //                     ->orwhere('node_group', '=', 0);
-    //             }
-    //         )
-    //         ->orderBy('name')
-    //         ->get();
-    //     $server_index = 1;
-    //     foreach ($nodes as $node) {
-    //         $server = array();
-    //         if ($node->sort == 13) {
-    //             if (self::CanMethodConnect($user->method) != 2) {
-    //                 continue;
-    //             }
-    //             $server = Tools::ssv2Array($node->server);
-    //             $server['server'] = $server['add'];
-    //             $server['id'] = $server_index;
-    //             $server['remarks'] = $node->name . ' - 单多' . $server['port'] . '端口';
-    //             $server['encryption'] = $user->method;
-    //             $server['password'] = $user->passwd;
-    //             $server['path'] = '';
-    //             $plugin_options = '';
-    //             if ($server['net'] == 'obfs') {
-    //                 $array_all['plugin'] = 'simple-obfs'; //目前只支持这个
-    //                 if (strpos($server['obfs'], 'http') != false) {
-    //                     $plugin_options .= 'obfs=http';
-    //                 }
-    //                 if (strpos($server['obfs'], 'tls') != false) {
-    //                     $plugin_options .= 'obfs=tls';
-    //                 }
-    //                 $plugin_options .= ';obfs-host=' . $user->getMuMd5();
-    //             } else {
-    //                 $server['plugin'] = 'v2ray';
-    //                 $server['path'] = ($server['path'] . '?redirect=' . $user->getMuMd5());
-    //                 if ($server['tls'] == 'tls' && $server['net'] == 'ws') {
-    //                     $plugin_options .= ('mode=ws;security=tls;path=' . $server['path'] .
-    //                         ';host=' . $server['host']);
-    //                 } else {
-    //                     $plugin_options .= ('mode=ws;security=none;path=' . $server['path'] .
-    //                         ';host=' . $server['host']);
-    //                 }
-    //             }
-    //             $server['plugin_options'] = $plugin_options;
-    //             $array_server[] = $server;
-    //             $server_index++;
-    //             continue;
-    //         } else {
-    //             $server['server'] = $node->getServer();
-    //         }
-    //         $server['id'] = $server_index;
-    //         //判断是否是中转起源节点
-    //         $relay_rule = Relay::where('source_node_id', $node->id)->where(
-    //             static function ($query) use ($user) {
-    //                 $query->Where('user_id', '=', $user->id)
-    //                     ->orWhere('user_id', '=', 0);
-    //             }
-    //         )->orderBy('priority', 'DESC')->orderBy('id')->first();
-    //         if ($relay_rule != null) {
-    //             //是中转起源节点
-    //             $server['remarks'] = $node->name . ' => ' . $relay_rule->dist_node()->name;
-    //             $server['ratio'] = $node->traffic_rate + $relay_rule->dist_node()->traffic_rate;
-    //             $array_server[] = $server;
-    //             $server_index++;
-    //             continue;
-    //         }
-
-    //         //不是中转起源节点
-
-    //         $server['ratio'] = $node->traffic_rate;
-    //         //包含普通
-    //         if (($node->mu_only == 0 || $node->mu_only == -1) && $node->sort != 13) {
-    //             $server['remarks'] = $node->name;
-    //             $array_server[] = $server;
-    //             $server_index++;
-    //         }
-    //         //包含单多
-    //         if (($node->mu_only == 0 || $node->mu_only == 1) && $node->sort != 13) {
-    //             $nodes_muport = Node::where('type', '1')->where('sort', '=', 9)
-    //                 ->where(static function ($query) use ($user) {
-    //                     $query->Where('node_group', '=', $user->group)
-    //                         ->orWhere('node_group', '=', 0);
-    //                 })
-    //                 ->where('node_class', '<=', $user->class)
-    //                 ->orderBy('server')->get();
-    //             foreach ($nodes_muport as $node_muport) {
-    //                 $muport_user = User::where('port', '=', $node_muport->server)->first();
-    //                 if (!self::SSCanConnect($muport_user)) {
-    //                     continue;
-    //                 }
-    //                 $server['id'] = $server_index;
-    //                 $server['remarks'] = $node->name . ' - 单多' . $node_muport->server . '端口';
-    //                 $server['port'] = $node_muport->server;
-    //                 // 端口偏移
-    //                 if (strpos($node->server, ';') !== false) {
-    //                     $node_tmp = Tools::OutPort($node->server, $node->name, $node_muport->server);
-    //                     $server['port'] = $node_tmp['port'];
-    //                     $server['remarks'] = $node->name . ' - 单多' . $node_tmp['port'] . '端口';
-    //                 }
-    //                 $server['encryption'] = $muport_user->method;
-    //                 $server['password'] = $muport_user->passwd;
-    //                 $server['plugin'] = 'simple-obfs'; //目前只支持这个
-    //                 $plugin_options = '';
-    //                 if (strpos($muport_user->obfs, 'http') != false) {
-    //                     $plugin_options = 'obfs=http';
-    //                 }
-    //                 if (strpos($muport_user->obfs, 'tls') != false) {
-    //                     $plugin_options = 'obfs=tls';
-    //                 }
-    //                 $server['plugin_options'] = $plugin_options . ';obfs-host=' . $user->getMuMd5();
-    //                 $array_server[] = $server;
-    //                 $server_index++;
-    //             }
-    //         }
-    //     }
-
-    //     $array_all['servers'] = $array_server;
-    //     $json_all = json_encode($array_all);
-
-    //     return 'ssd://' . Tools::base64_url_encode($json_all);
-    // }
-
-    public static function getJsonObfs($item)
+    /**
+     * 获取 Trojan 全部节点
+     *
+     * @param User $user 用户
+     * @param bool $emoji
+     */
+    public static function getAllTrojan($user, $emoji = false): array
+    {
+        $return_array = array();
+        $nodes = self::getNodes($user, 14);
+        foreach ($nodes as $node) {
+            $item = self::getTrojanItem($user, $node, $emoji);
+            if ($item != null) {
+                $return_array[] = $item;
+            }
+        }
+
+        return $return_array;
+    }
+
+    /**
+     * Trojan 节点
+     *
+     * @param User $user 用户
+     * @param Node $node
+     * @param bool $emoji
+     */
+    public static function getTrojanItem(User $user, Node $node, bool $emoji = false): array
+    {
+        $server = explode(';', $node->server);
+        $opt    = [];
+        if (isset($server[1])) {
+            $opt = self::parse_args($server[1]);
+        }
+        $item['remark']   = ($emoji ? Tools::addEmoji($node->name) : $node->name);
+        $item['type']     = 'trojan';
+        $item['address']  = $server[0];
+        $item['port']     = (isset($opt['port']) ? (int) $opt['port'] : 443);
+        $item['passwd']   = $user->getUuid();
+        $item['host']     = $item['address'];
+        if (isset($opt['host'])) {
+            $item['host'] = $opt['address'];
+        }
+
+        return $item;
+    }
+
+    public static function getJsonObfs(array $item): string
     {
     {
         $ss_obfs_list = Config::getSupportParam('ss_obfs');
         $ss_obfs_list = Config::getSupportParam('ss_obfs');
         $plugin = '';
         $plugin = '';
@@ -900,7 +616,7 @@ class URL
         return $plugin;
         return $plugin;
     }
     }
 
 
-    public static function getSurgeObfs($item)
+    public static function getSurgeObfs(array $item): string
     {
     {
         $ss_obfs_list = Config::getSupportParam('ss_obfs');
         $ss_obfs_list = Config::getSupportParam('ss_obfs');
         $plugin = '';
         $plugin = '';
@@ -931,7 +647,7 @@ class URL
     * obfs
     * obfs
     * obfs_param
     * obfs_param
     */
     */
-    public static function getItem($user, $node, $mu_port = 0, $relay_rule_id = 0, $is_ss = 0, $emoji = false)
+    public static function getItem(User $user, Node $node, int $mu_port = 0, int $relay_rule_id = 0, int $is_ss = 0, bool $emoji = false):? array
     {
     {
         $relay_rule = Relay::where('id', $relay_rule_id)->where(
         $relay_rule = Relay::where('id', $relay_rule_id)->where(
             static function ($query) use ($user) {
             static function ($query) use ($user) {
@@ -946,7 +662,7 @@ class URL
         if ($mu_port != 0) {
         if ($mu_port != 0) {
             $mu_user = User::where('port', '=', $mu_port)->where('is_multi_user', '<>', 0)->first();
             $mu_user = User::where('port', '=', $mu_port)->where('is_multi_user', '<>', 0)->first();
             if ($mu_user == null) {
             if ($mu_user == null) {
-                return;
+                return null;
             }
             }
             $mu_user->obfs_param = $user->getMuMd5();
             $mu_user->obfs_param = $user->getMuMd5();
             $mu_user->protocol_param = $user->id . ':' . $user->passwd;
             $mu_user->protocol_param = $user->id . ':' . $user->passwd;
@@ -955,43 +671,39 @@ class URL
         }
         }
         if ($is_ss) {
         if ($is_ss) {
             if (!self::SSCanConnect($user)) {
             if (!self::SSCanConnect($user)) {
-                return;
+                return null;
             }
             }
             $user = self::getSSConnectInfo($user);
             $user = self::getSSConnectInfo($user);
             $return_array['type'] = 'ss';
             $return_array['type'] = 'ss';
         } else {
         } else {
             if (!self::SSRCanConnect($user)) {
             if (!self::SSRCanConnect($user)) {
-                return;
+                return null;
             }
             }
             $user = self::getSSRConnectInfo($user);
             $user = self::getSSRConnectInfo($user);
             $return_array['type'] = 'ssr';
             $return_array['type'] = 'ssr';
         }
         }
-        $return_array['address'] = $node->getServer();
-        $return_array['port'] = $user->port;
-        $return_array['protocol'] = $user->protocol;
+        $return_array['address']        = $node->getServer();
+        $return_array['port']           = $user->port;
+        $return_array['protocol']       = $user->protocol;
         $return_array['protocol_param'] = $user->protocol_param;
         $return_array['protocol_param'] = $user->protocol_param;
-        $return_array['obfs'] = $user->obfs;
-        $return_array['obfs_param'] = $user->obfs_param;
+        $return_array['obfs']           = $user->obfs;
+        $return_array['obfs_param']     = $user->obfs_param;
         if ($mu_port != 0 && strpos($node->server, ';') !== false) {
         if ($mu_port != 0 && strpos($node->server, ';') !== false) {
-            $node_tmp = Tools::OutPort($node->server, $node->name, $mu_port);
+            $node_tmp             = Tools::OutPort($node->server, $node->name, $mu_port);
             $return_array['port'] = $node_tmp['port'];
             $return_array['port'] = $node_tmp['port'];
-            $node_name = $node_tmp['name'];
+            $node_name            = $node_tmp['name'];
         }
         }
         $return_array['passwd'] = $user->passwd;
         $return_array['passwd'] = $user->passwd;
         $return_array['method'] = $user->method;
         $return_array['method'] = $user->method;
-        $return_array['remark'] = ($emoji == true
-            ? Tools::addEmoji($node_name)
-            : $node_name);
-        $return_array['class'] = $node->node_class;
-        $return_array['group'] = $_ENV['appName'];
-        $return_array['ratio'] = ($relay_rule != null
-            ? $node->traffic_rate + $relay_rule->dist_node()->traffic_rate
-            : $node->traffic_rate);
+        $return_array['remark'] = ($emoji ? Tools::addEmoji($node_name) : $node_name);
+        $return_array['class']  = $node->node_class;
+        $return_array['group']  = $_ENV['appName'];
+        $return_array['ratio']  = ($relay_rule != null ? $node->traffic_rate + $relay_rule->dist_node()->traffic_rate : $node->traffic_rate);
 
 
         return $return_array;
         return $return_array;
     }
     }
 
 
-    public static function cloneUser($user)
+    public static function cloneUser(User $user): User
     {
     {
         return clone $user;
         return clone $user;
     }
     }