Browse Source

🚀 Update Laravel Framework to 10

BrettonYe 2 years ago
parent
commit
72c66e199c
100 changed files with 847 additions and 2539 deletions
  1. 5 2
      .editorconfig
  2. 11 6
      .env.example
  3. 10 4
      .gitattributes
  4. 13 19
      .gitignore
  5. 53 72
      app/Channels/Library/WeChat.php
  6. 6 8
      app/Channels/WeChatChannel.php
  7. 0 211
      app/Components/CurrencyExchange.php
  8. 0 80
      app/Components/DDNS.php
  9. 0 153
      app/Components/DDNS/Aliyun.php
  10. 0 129
      app/Components/DDNS/CloudFlare.php
  11. 0 152
      app/Components/DDNS/DNSPod.php
  12. 0 148
      app/Components/DDNS/Namesilo.php
  13. 0 238
      app/Components/IP.php
  14. 0 17
      app/Components/MigrationToolBox.php
  15. 0 386
      app/Components/NetworkDetection.php
  16. 0 56
      app/Components/QQInfo.php
  17. 3 3
      app/Console/Commands/AutoClearLogs.php
  18. 6 6
      app/Console/Commands/DailyNodeReport.php
  19. 11 12
      app/Console/Commands/NodeStatusDetection.php
  20. 8 11
      app/Console/Commands/PanelInstallation.php
  21. 1 28
      app/Console/Commands/PanelUpdate.php
  22. 2 2
      app/Console/Commands/ServiceTimer.php
  23. 7 7
      app/Console/Commands/TaskAuto.php
  24. 15 16
      app/Console/Commands/TaskDaily.php
  25. 6 6
      app/Console/Commands/TaskHourly.php
  26. 2 2
      app/Console/Commands/TaskMonthly.php
  27. 2 2
      app/Console/Commands/UserExpireWarning.php
  28. 4 4
      app/Console/Commands/UserTrafficWarning.php
  29. 2 2
      app/Console/Commands/VNetReload.php
  30. 0 32
      app/Console/Kernel.php
  31. 3 8
      app/Exceptions/Handler.php
  32. 6 6
      app/Helpers/ClientApiResponse.php
  33. 61 64
      app/Helpers/ClientConfig.php
  34. 21 16
      app/Helpers/DataChart.php
  35. 29 29
      app/Helpers/ResponseEnum.php
  36. 5 5
      app/Helpers/WebApiResponse.php
  37. 5 4
      app/Http/Controllers/Admin/AffiliateController.php
  38. 4 2
      app/Http/Controllers/Admin/ArticleController.php
  39. 9 11
      app/Http/Controllers/Admin/CertController.php
  40. 4 3
      app/Http/Controllers/Admin/Config/CategoryController.php
  41. 4 3
      app/Http/Controllers/Admin/Config/CountryController.php
  42. 3 2
      app/Http/Controllers/Admin/Config/EmailFilterController.php
  43. 4 3
      app/Http/Controllers/Admin/Config/LabelController.php
  44. 4 3
      app/Http/Controllers/Admin/Config/LevelController.php
  45. 4 3
      app/Http/Controllers/Admin/Config/SsConfigController.php
  46. 23 44
      app/Http/Controllers/Admin/CouponController.php
  47. 17 16
      app/Http/Controllers/Admin/LogsController.php
  48. 2 1
      app/Http/Controllers/Admin/MarketingController.php
  49. 4 3
      app/Http/Controllers/Admin/NodeAuthController.php
  50. 72 72
      app/Http/Controllers/Admin/NodeController.php
  51. 11 9
      app/Http/Controllers/Admin/PermissionController.php
  52. 2 2
      app/Http/Controllers/Admin/ReportController.php
  53. 10 8
      app/Http/Controllers/Admin/RoleController.php
  54. 1 1
      app/Http/Controllers/Admin/RuleController.php
  55. 9 13
      app/Http/Controllers/Admin/RuleGroupController.php
  56. 7 14
      app/Http/Controllers/Admin/ShopController.php
  57. 6 5
      app/Http/Controllers/Admin/SubscribeController.php
  58. 46 46
      app/Http/Controllers/Admin/SystemController.php
  59. 5 4
      app/Http/Controllers/Admin/TicketController.php
  60. 1 1
      app/Http/Controllers/Admin/ToolsController.php
  61. 9 9
      app/Http/Controllers/Admin/UserController.php
  62. 10 14
      app/Http/Controllers/Admin/UserGroupController.php
  63. 5 5
      app/Http/Controllers/AdminController.php
  64. 2 4
      app/Http/Controllers/Api/Client/AuthController.php
  65. 28 26
      app/Http/Controllers/Api/Client/ClientController.php
  66. 1 1
      app/Http/Controllers/Api/WebApi/CoreController.php
  67. 1 1
      app/Http/Controllers/Api/WebApi/V2RayController.php
  68. 21 32
      app/Http/Controllers/AuthController.php
  69. 1 4
      app/Http/Controllers/Controller.php
  70. 42 74
      app/Http/Controllers/OAuthController.php
  71. 16 15
      app/Http/Controllers/PaymentController.php
  72. 26 25
      app/Http/Controllers/TelegramController.php
  73. 3 3
      app/Http/Controllers/User/AffiliateController.php
  74. 6 6
      app/Http/Controllers/User/SubscribeController.php
  75. 13 15
      app/Http/Controllers/UserController.php
  76. 12 12
      app/Http/Kernel.php
  77. 2 7
      app/Http/Middleware/Affiliate.php
  78. 2 7
      app/Http/Middleware/Authenticate.php
  79. 1 1
      app/Http/Middleware/EncryptCookies.php
  80. 3 3
      app/Http/Middleware/PreventRequestsDuringMaintenance.php
  81. 9 6
      app/Http/Middleware/RedirectIfAuthenticated.php
  82. 2 2
      app/Http/Middleware/SetLocale.php
  83. 2 1
      app/Http/Middleware/TrimStrings.php
  84. 2 2
      app/Http/Middleware/TrustHosts.php
  85. 8 3
      app/Http/Middleware/TrustProxies.php
  86. 22 0
      app/Http/Middleware/ValidateSignature.php
  87. 1 1
      app/Http/Middleware/VerifyCsrfToken.php
  88. 3 2
      app/Http/Middleware/WebApi.php
  89. 33 31
      app/Http/Middleware/isForbidden.php
  90. 1 1
      app/Http/Middleware/isSecurity.php
  91. 1 1
      app/Http/Requests/Admin/CertRequest.php
  92. 1 1
      app/Http/Requests/Admin/NodeRequest.php
  93. 1 1
      app/Http/Requests/Admin/PermissionRequest.php
  94. 1 1
      app/Http/Requests/Admin/RbacRequest.php
  95. 1 1
      app/Http/Requests/Admin/RoleRequest.php
  96. 1 1
      app/Http/Requests/Admin/RuleGroupRequest.php
  97. 1 1
      app/Http/Requests/Admin/RuleRequest.php
  98. 2 2
      app/Http/Requests/Admin/SystemRequest.php
  99. 1 1
      app/Http/Requests/Admin/TicketRequest.php
  100. 1 1
      app/Http/Requests/Admin/UserGroupRequest.php

+ 5 - 2
.editorconfig

@@ -3,9 +3,9 @@ root = true
 [*]
 charset = utf-8
 end_of_line = lf
-insert_final_newline = true
-indent_style = space
 indent_size = 4
+indent_style = space
+insert_final_newline = true
 trim_trailing_whitespace = true
 
 [*.md]
@@ -13,3 +13,6 @@ trim_trailing_whitespace = false
 
 [*.{yml,yaml}]
 indent_size = 2
+
+[docker-compose.yml]
+indent_size = 4

+ 11 - 6
.env.example

@@ -8,6 +8,8 @@ APP_TIMEZONE=Asia/Shanghai
 APP_LOCALE=zh_CN
 APP_FALLBACK_LOCALE=en
 LOG_CHANNEL=daily
+LOG_DEPRECATIONS_CHANNEL=null
+LOG_LEVEL=debug
 
 # 数据库
 DB_CONNECTION=mysql
@@ -16,10 +18,10 @@ DB_PORT=3306
 DB_DATABASE=ProxyPanel
 DB_USERNAME=root
 DB_PASSWORD=root
-DB_STRICT=false
 
 BROADCAST_DRIVER=redis
 CACHE_DRIVER=redis
+FILESYSTEM_DISK=local
 QUEUE_CONNECTION=redis
 SESSION_DRIVER=redis
 SESSION_LIFETIME=120
@@ -34,16 +36,19 @@ MAIL_MAILER=smtp
 # SMTP设置
 MAIL_HOST=smtp.exmail.qq.com
 MAIL_PORT=465
-MAIL_ENCRYPTION=ssl
 [email protected]
 MAIL_PASSWORD=password
-# Mailgun设置
-MAILGUN_DOMAIN=
-MAILGUN_SECRET=
+MAIL_ENCRYPTION=ssl
 # 发信方信息
 [email protected]
 MAIL_FROM_NAME=ProxyPanel
 
-REDIRECT_HTTPS=true
+SESSION_SECURE_COOKIE=true
+#IP查询相关
 BAIDU_APP_AK=
+IPINFO_ACCESS_TOKEN=
+IP2LOCATION_API_KEY=
+IPDATA_API_KEY=
+IP_API_KEY=ipapiq9SFY1Ic4
+
 API_LAYER_API_KEY=

+ 10 - 4
.gitattributes

@@ -1,5 +1,11 @@
-* text=auto
-*.css linguist-vendored
-*.scss linguist-vendored
-*.js linguist-vendored
+* text=auto eol=lf
+
+*.blade.php diff=html
+*.css diff=css
+*.html diff=html
+*.md diff=markdown
+*.php diff=php
+
+/.github export-ignore
 CHANGELOG.md export-ignore
+.styleci.yml export-ignore

+ 13 - 19
.gitignore

@@ -1,29 +1,23 @@
-/public/upload
-/storage/framework/cache/*
-/storage/framework/cookies/*
-/storage/framework/sessions/*
-/storage/framework/testing/*
-/storage/framework/views/*
-/storage/logs/*
-/storage/app/public
+/.phpunit.cache
+/node_modules
+/public/build
+/public/hot
+/public/storage
 /storage/*.key
 /vendor
 .env
+.env.backup
+.env.production
 .phpunit.result.cache
-/.idea
-/.vagrant
-/.vscode
-/phpunit.xml
 Homestead.json
 Homestead.yaml
+auth.json
 npm-debug.log
-.DS_Store
-.phpstorm.meta.php
 yarn-error.log
-_ide_helper_models.php
+/.fleet
+/.idea
+/.vscode
+.phpstorm.meta.php
 _ide_helper.php
-.php_cs.cache
-node_modules/*
-composer.phar
-public/uploads/*
+_ide_helper_models.php
 composer.lock

+ 53 - 72
app/Channels/Library/WeChat.php

@@ -9,46 +9,7 @@ use Str;
 
 class WeChat
 {
-    public function VerifyURL($sMsgSignature, $sTimeStamp, $sNonce, $sEchoStr, &$sReplyEchoStr)
-    { // 验证URL
-        //verify msg_signature
-        $array = $this->getSHA1($sTimeStamp, $sNonce, $sEchoStr);
-        $ret = $array[0];
-
-        if ($ret !== 0) {
-            return $ret;
-        }
-
-        $signature = $array[1];
-        if ($signature !== $sMsgSignature) {
-            return -40001; // ValidateSignatureError
-        }
-
-        $result = (new Prpcrypt())->decrypt($sEchoStr);
-        if ($result[0] !== 0) {
-            return $result[0];
-        }
-        $sReplyEchoStr = $result[1];
-
-        return 0;
-    }
-
-    public function getSHA1($timestamp, $nonce, $encrypt_msg)
-    {
-        //排序
-        try {
-            $array = [$encrypt_msg, sysConfig('wechat_token'), $timestamp, $nonce];
-            sort($array, SORT_STRING);
-
-            return [0, sha1(implode($array))];
-        } catch (Exception $e) {
-            Log::critical('企业微信消息推送异常:'.var_export($e->getMessage(), true));
-
-            return [-40003, null]; // ComputeSignatureError
-        }
-    }
-
-    public function EncryptMsg($sReplyMsg, $sTimeStamp, $sNonce, &$sEncryptMsg)
+    public function EncryptMsg(string $sReplyMsg, int|null $sTimeStamp, string $sNonce, string &$sEncryptMsg)
     { //将公众平台回复用户的消息加密打包.
         //加密
         $array = (new Prpcrypt())->encrypt($sReplyMsg);
@@ -76,6 +37,21 @@ class WeChat
         return 0;
     }
 
+    public function getSHA1(string $timestamp, string $nonce, string $encrypt_msg): array
+    {
+        //排序
+        try {
+            $array = [$encrypt_msg, sysConfig('wechat_token'), $timestamp, $nonce];
+            sort($array, SORT_STRING);
+
+            return [0, sha1(implode($array))];
+        } catch (Exception $e) {
+            Log::critical('企业微信消息推送异常:'.var_export($e->getMessage(), true));
+
+            return [-40003, null]; // ComputeSignatureError
+        }
+    }
+
     /**
      * 生成xml消息.
      *
@@ -84,7 +60,7 @@ class WeChat
      * @param  string  $timestamp  时间戳
      * @param  string  $nonce  随机字符串
      */
-    public function generate($encrypt, $signature, $timestamp, $nonce)
+    public function generate(string $encrypt, string $signature, string $timestamp, string $nonce): string
     {
         $format = '<xml>
 <Encrypt><![CDATA[%s]]></Encrypt>
@@ -96,7 +72,7 @@ class WeChat
         return sprintf($format, $encrypt, $signature, $timestamp, $nonce);
     }
 
-    public function DecryptMsg($sMsgSignature, $sTimeStamp, $sNonce, $sPostData, &$sMsg)
+    public function DecryptMsg(string $sMsgSignature, int|null $sTimeStamp, string $sNonce, string $sPostData, string &$sMsg)
     { // 检验消息的真实性,并且获取解密后的明文.
         //提取密文
         $array = $this->extract($sPostData);
@@ -113,37 +89,20 @@ class WeChat
         $encrypt = $array[1];
 
         //验证安全签名
-        $array = $this->getSHA1($sTimeStamp, $sNonce, $encrypt);
-        $ret = $array[0];
-
-        if ($ret !== 0) {
-            return $ret;
-        }
-
-        $signature = $array[1];
-        if ($signature !== $sMsgSignature) {
-            return -40001; // ValidateSignatureError
-        }
-        $result = (new Prpcrypt())->decrypt($encrypt);
-        if ($result[0] !== 0) {
-            return $result[0];
-        }
-        $sMsg = $result[1];
-
-        return 0;
+        $this->verifyURL($sMsgSignature, $sTimeStamp, $sNonce, $encrypt, $sMsg);
     }
 
     /**
      * 提取出xml数据包中的加密消息.
      *
-     * @param  string  $xmltext  待提取的xml字符串
+     * @param  string  $xmlText  待提取的xml字符串
      * @return array 提取出的加密消息字符串
      */
-    public function extract($xmltext)
+    public function extract(string $xmlText): array
     {
         try {
             $xml = new DOMDocument();
-            $xml->loadXML($xmltext);
+            $xml->loadXML($xmlText);
             $array_e = $xml->getElementsByTagName('Encrypt');
             $encrypt = $array_e->item(0)->nodeValue;
 
@@ -154,6 +113,30 @@ class WeChat
             return [-40002, null]; // ParseXmlError
         }
     }
+
+    public function verifyURL(string $sMsgSignature, string $sTimeStamp, string $sNonce, string $sEchoStr, string &$sReplyEchoStr)
+    { // 验证URL
+        //verify msg_signature
+        $array = $this->getSHA1($sTimeStamp, $sNonce, $sEchoStr);
+        $ret = $array[0];
+
+        if ($ret !== 0) {
+            return $ret;
+        }
+
+        $signature = $array[1];
+        if ($signature !== $sMsgSignature) {
+            return -40001; // ValidateSignatureError
+        }
+
+        $result = (new Prpcrypt())->decrypt($sEchoStr);
+        if ($result[0] !== 0) {
+            return $result[0];
+        }
+        $sReplyEchoStr = $result[1];
+
+        return 0;
+    }
 }
 
 /**
@@ -163,9 +146,9 @@ class WeChat
  */
 class PKCS7Encoder
 {
-    public static $block_size = 32;
+    public static int $block_size = 32;
 
-    public function encode($text)
+    public function encode(string $text): string
     { // 对需要加密的明文进行填充补位
         //计算需要填充的位数
         $amount_to_pad = self::$block_size - (strlen($text) % self::$block_size);
@@ -176,7 +159,7 @@ class PKCS7Encoder
         return $text.str_repeat(chr($amount_to_pad), $amount_to_pad); // 获得补位所用的字符
     }
 
-    public function decode($text)
+    public function decode(string $text): string
     { // 对解密后的明文进行补位删除
         $pad = ord(substr($text, -1));
         if ($pad < 1 || $pad > self::$block_size) {
@@ -194,9 +177,9 @@ class PKCS7Encoder
  */
 class Prpcrypt
 {
-    public $key;
+    public string $key;
 
-    public $iv;
+    public string $iv;
 
     public function __construct()
     {
@@ -206,10 +189,8 @@ class Prpcrypt
 
     /**
      * 加密.
-     *
-     * @return array
      */
-    public function encrypt($text)
+    public function encrypt(string $text): array
     {
         try {
             //拼接
@@ -227,7 +208,7 @@ class Prpcrypt
         }
     }
 
-    public function decrypt($encrypted): array
+    public function decrypt(string $encrypted): array
     { // 解密
         try {
             //解密

+ 6 - 8
app/Channels/WeChatChannel.php

@@ -13,7 +13,7 @@ use Str;
 
 class WeChatChannel
 {
-    public function send($notifiable, Notification $notification)
+    public function send($notifiable, Notification $notification): false|array
     { // route('message.show', ['type' => 'markdownMsg', 'msgId' => ''])
         $message = $notification->toCustom($notifiable);
 
@@ -83,16 +83,14 @@ class WeChatChannel
             }
             // 发送失败
             Helpers::addNotificationLog($message['title'], $message['content'] ?? var_export($message['body'], true), 5, -1, $ret ? $ret['errmsg'] : '未知');
-
-            return false;
+        } else {
+            Log::critical('[企业微信] 消息推送异常:'.var_export($response, true)); // 发送错误
         }
-        // 发送错误
-        Log::critical('[企业微信] 消息推送异常:'.var_export($response, true));
 
         return false;
     }
 
-    private function getAccessToken()
+    private function getAccessToken(): ?string
     {
         if (Cache::has('wechat_access_token')) {
             $access_token = Cache::get('wechat_access_token');
@@ -111,9 +109,9 @@ class WeChatChannel
         return $access_token ?? null;
     }
 
-    public function verify(Request $request)
+    public function verify(Request $request): void
     {
-        $errCode = (new WeChat())->VerifyURL($request->input('msg_signature'), $request->input('timestamp'), $request->input('nonce'), $request->input('echostr'), $sEchoStr);
+        $errCode = (new WeChat())->verifyURL($request->input('msg_signature'), $request->input('timestamp'), $request->input('nonce'), $request->input('echostr'), $sEchoStr);
         if ($errCode === 0) {
             exit($sEchoStr);
         }

+ 0 - 211
app/Components/CurrencyExchange.php

@@ -1,211 +0,0 @@
-<?php
-
-namespace App\Components;
-
-use Cache;
-use Http;
-use Log;
-
-class CurrencyExchange
-{
-    /**
-     * @param  string  $target  target Currency
-     * @param  int|float|false  $amount  exchange amount
-     * @param  string  $base  Base Currency
-     * @return false|float amount in target currency
-     */
-    public static function convert(string $target, $amount = false, string $base = 'default')
-    {
-        if ($base === 'default') {
-            $base = sysConfig('standard_currency', 'CNY');
-        }
-        $cacheKey = "Currency_{$base}_{$target}_ExRate";
-        $isStored = Cache::has($cacheKey);
-        $rate = $isStored ? Cache::get($cacheKey) : false;
-        $case = 0;
-
-        while ($case < 7 && $rate === false) {
-            switch ($case) {
-                case 0:
-                    $rate = self::exchangerateApi($base, $target);
-                    break;
-                case 1:
-                    $rate = self::k780($base, $target);
-                    break;
-                case 2:
-                    $rate = self::it120($base, $target);
-                    break;
-                case 3:
-                    $rate = self::exchangerate($base, $target);
-                    break;
-                case 4:
-                    $rate = self::fixer($base, $target);
-                    break;
-                case 5:
-                    $rate = self::currencyData($base, $target);
-                    break;
-                case 6:
-                    $rate = self::exchangeRatesData($base, $target);
-                    break;
-                default:
-                    break;
-            }
-            $case++;
-        }
-
-        if ($rate !== false) {
-            if (! $isStored) {
-                Cache::put($cacheKey, $rate, Day);
-            }
-            if ($amount === false) {
-                return $rate;
-            }
-
-            return round($amount * $rate, 2);
-        }
-
-        return false;
-    }
-
-    private static function exchangerateApi($base, $target)
-    { // Reference: https://www.exchangerate-api.com/docs/php-currency-api
-        $response = Http::retry(3)->get('https://open.er-api.com/v6/latest/'.$base); // https://v6.exchangerate-api.com/v6/{{your token}}/latest/USD
-        if ($response->ok()) {
-            $response = $response->json();
-
-            if ($response['result'] === 'success') {
-                return $response['rates'][$target];
-            }
-            Log::emergency('[CurrencyExchange]exchangerateApi exchange failed with following message: '.$response['error-type']);
-        } else {
-            Log::emergency('[CurrencyExchange]exchangerateApi request failed '.var_export($response, true));
-        }
-
-        return false;
-    }
-
-    private static function k780($base, $target)
-    { // Reference: https://www.nowapi.com/api/finance.rate
-        $response = Http::retry(3)->get("https://sapi.k780.com/?app=finance.rate&scur={$base}&tcur={$target}&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json");
-        if ($response->ok()) {
-            $response = $response->json();
-
-            if ($response['success'] === '1') {
-                return $response['result']['rate'];
-            }
-            Log::emergency('[CurrencyExchange]Nowapi exchange failed with following message: '.$response['msg']);
-        } else {
-            Log::emergency('[CurrencyExchange]Nowapi request failed'.var_export($response, true));
-        }
-
-        return false;
-    }
-
-    private static function it120($base, $target)
-    { // Reference: https://www.it120.cc/help/fnun8g.html
-        $response = Http::retry(3)->get("https://api.it120.cc/gooking/forex/rate?fromCode={$target}&toCode={$base}");
-        if ($response->ok()) {
-            $response = $response->json();
-
-            if ($response['code'] === 0) {
-                return $response['data']['rate'];
-            }
-            Log::emergency('[CurrencyExchange]it120 exchange failed with following message: '.$response['msg']);
-        } else {
-            Log::emergency('[CurrencyExchange]it120 request failed'.var_export($response, true));
-        }
-
-        return false;
-    }
-
-    private static function exchangerate($base, $target)
-    { // Reference: https://exchangerate.host/#/
-        $response = file_get_contents("https://api.exchangerate.host/latest?symbols={$target}&base={$base}");
-        if (false !== $response) {
-            $response = json_decode($response, true);
-
-            if ($response['success'] === true) {
-                if ($response['base'] === $base) {
-                    return $response['rates'][$target];
-                }
-
-                Log::emergency('[CurrencyExchange]exchangerate exchange failed with following message: Get un-supported base currency '.$response['base']);
-            } else {
-                Log::emergency('[CurrencyExchange]exchangerate exchange failed with following message: '.$response['error-type']);
-            }
-        } else {
-            Log::emergency('[CurrencyExchange]exchangerate request failed');
-        }
-
-        return false;
-    }
-
-    private static function fixer($base, $target)
-    { // Reference: https://apilayer.com/marketplace/fixer-api
-        // RATE LIMIT: 100 Requests / Monthly
-        if (! config('services.apiLayer')) {
-            return false;
-        }
-
-        $response = Http::retry(3)->withHeaders(['apikey' => config('services.apiLayer')])->get("https://api.apilayer.com/fixer/latest?symbols={$target}&base={$base}");
-        if ($response->ok()) {
-            $response = $response->json();
-
-            if ($response['success'] === true) {
-                return $response['rates'][$target];
-            }
-
-            Log::emergency('[CurrencyExchange]Fixer exchange failed with following message: '.$response['error']['type'] ?? '');
-        } else {
-            Log::emergency('[CurrencyExchange]Fixer request failed'.var_export($response, true));
-        }
-
-        return false;
-    }
-
-    private static function currencyData($base, $target)
-    { // Reference: https://apilayer.com/marketplace/currency_data-api
-        // RATE LIMIT: 100 Requests / Monthly
-        if (! config('services.apiLayer')) {
-            return false;
-        }
-
-        $response = Http::retry(3)->withHeaders(['apikey' => config('services.apiLayer')])
-            ->get("https://api.apilayer.com/currency_data/live?source={$base}&currencies={$target}");
-        if ($response->ok()) {
-            $response = $response->json();
-
-            if ($response['success'] === true) {
-                return $response['quotes'][$base.$target];
-            }
-            Log::emergency('[CurrencyExchange]Currency Data exchange failed with following message: '.$response['error']['info'] ?? '');
-        } else {
-            Log::emergency('[CurrencyExchange]Currency Data request failed'.var_export($response, true));
-        }
-
-        return false;
-    }
-
-    private static function exchangeRatesData($base, $target)
-    { // Reference: https://apilayer.com/marketplace/exchangerates_data-api
-        // RATE LIMIT: 250 Requests / Monthly
-        if (! config('services.apiLayer')) {
-            return false;
-        }
-
-        $response = Http::retry(3)->withHeaders(['apikey' => config('services.apiLayer')])
-            ->get("https://api.apilayer.com/exchangerates_data/latest?symbols={$target}&base={$base}");
-        if ($response->ok()) {
-            $response = $response->json();
-
-            if ($response['success'] === true) {
-                return $response['rates'][$target];
-            }
-            Log::emergency('[CurrencyExchange]Exchange Rates Data exchange failed with following message: '.$response['error']['message'] ?? '');
-        } else {
-            Log::emergency('[CurrencyExchange]Exchange Rates Data request failed'.var_export($response, true));
-        }
-
-        return false;
-    }
-}

+ 0 - 80
app/Components/DDNS.php

@@ -1,80 +0,0 @@
-<?php
-
-namespace App\Components;
-
-use App\Components\DDNS\Aliyun;
-use App\Components\DDNS\CloudFlare;
-use App\Components\DDNS\DNSPod;
-use App\Components\DDNS\Namesilo;
-use Log;
-
-/**
- * Class DDNS 域名解析.
- */
-class DDNS
-{
-    /**
-     * 删除解析记录.
-     *
-     * @param  string  $domain  域名
-     * @param  string|null  $type
-     */
-    public static function destroy(string $domain, $type = null)
-    {
-        if (self::dnsProvider($domain)->destroy($type)) {
-            Log::notice("【DDNS】删除:{$domain} 成功");
-        } else {
-            Log::alert("【DDNS】删除:{$domain} 失败,请手动删除!");
-        }
-    }
-
-    private static function dnsProvider($domain)
-    {
-        switch (sysConfig('ddns_mode')) {
-            case 'aliyun':
-                return new Aliyun($domain);
-            case 'namesilo':
-                return new Namesilo($domain);
-            case 'dnspod':
-                return new DNSPod($domain);
-            case 'cloudflare':
-                return new CloudFlare($domain);
-            default:
-                Log::emergency('【DDNS】未知渠道:'.sysConfig('ddns_mode'));
-
-                return false;
-        }
-    }
-
-    /**
-     * 修改解析记录.
-     *
-     * @param  string  $domain  域名
-     * @param  string  $ip  ip地址
-     * @param  string  $type  记录类型,默认为 A
-     */
-    public static function update(string $domain, string $ip, string $type = 'A')
-    {
-        if (self::dnsProvider($domain)->update($ip, $type)) {
-            Log::info("【DDNS】更新:{$ip} => {$domain} 类型:{$type} 成功");
-        } else {
-            Log::warning("【DDNS】更新:{$ip} => {$domain} 类型:{$type} 失败,请手动设置!");
-        }
-    }
-
-    /**
-     * 添加解析记录.
-     *
-     * @param  string  $domain  域名
-     * @param  string  $ip  ip地址
-     * @param  string  $type  记录类型,默认为 A
-     */
-    public static function store(string $domain, string $ip, string $type = 'A')
-    {
-        if (self::dnsProvider($domain)->store($ip, $type)) {
-            Log::info("【DDNS】添加:{$ip} => {$domain} 类型:{$type} 成功");
-        } else {
-            Log::warning("【DDNS】添加:{$ip} => {$domain} 类型:{$type} 失败,请手动设置!");
-        }
-    }
-}

+ 0 - 153
app/Components/DDNS/Aliyun.php

@@ -1,153 +0,0 @@
-<?php
-
-namespace App\Components\DDNS;
-
-use Arr;
-use Http;
-use Log;
-
-class Aliyun
-{
-    private static $apiHost = 'https://alidns.aliyuncs.com/';
-
-    private static $subDomain;
-
-    public function __construct($subDomain)
-    {
-        self::$subDomain = $subDomain;
-    }
-
-    public function store($ip, $type)
-    {
-        $domainInfo = $this->analysisDomain();
-
-        if ($domainInfo) {
-            return $this->send('AddDomainRecord', ['DomainName' => $domainInfo[0], 'RR' => $domainInfo[1], 'Type' => $type, 'Value' => $ip]);
-        }
-
-        return false;
-    }
-
-    private function analysisDomain()
-    {
-        $domainList = $this->domainList();
-        if ($domainList) {
-            foreach ($domainList as $domain) {
-                if (str_contains(self::$subDomain, $domain)) {
-                    return [$domain, rtrim(substr(self::$subDomain, 0, -strlen($domain)), '.')];
-                }
-            }
-        }
-
-        return false;
-    }
-
-    public function domainList()
-    {
-        $result = $this->send('DescribeDomains');
-        if ($result) {
-            $result = $result['Domains']['Domain'];
-            if ($result) {
-                return Arr::pluck($result, 'DomainName');
-            }
-        }
-
-        return false;
-    }
-
-    private function send($action, $data = [])
-    {
-        $public = [
-            'Format' => 'JSON',
-            'Version' => '2015-01-09',
-            'AccessKeyId' => sysConfig('ddns_key'),
-            'SignatureMethod' => 'HMAC-SHA1',
-            'Timestamp' => gmdate("Y-m-d\TH:i:s\Z"), //公共参数Timestamp GMT时间
-            'SignatureVersion' => '1.0',
-            'SignatureNonce' => str_replace('.', '', microtime(true)), //唯一数,用于防止网络重放攻击
-        ];
-        $parameters = array_merge(['Action' => $action], $data, $public);
-        $parameters['Signature'] = $this->computeSignature($parameters);
-
-        $response = Http::asForm()->timeout(15)->post(self::$apiHost, $parameters);
-        $message = $response->json();
-
-        if ($response->failed()) {
-            if ($message && $message['Code']) {
-                $error = $message['Message'];
-            } else {
-                $error = $response->body();
-            }
-            Log::error('[Aliyun - '.$action.'] 请求失败:'.$error);
-
-            return false;
-        }
-
-        return $message;
-    }
-
-    // 签名
-    private function computeSignature($parameters): string
-    {
-        ksort($parameters, SORT_STRING);
-
-        $stringToBeSigned = 'POST&%2F&'.urlencode(http_build_query($parameters));
-
-        return base64_encode(hash_hmac('sha1', $stringToBeSigned, sysConfig('ddns_secret').'&', true));
-    }
-
-    public function update($ip, $type)
-    {
-        $recordId = $this->getRecordId($type);
-        $domainInfo = $this->analysisDomain();
-        if ($recordId && $domainInfo) {
-            return $this->send('UpdateDomainRecord', ['RR' => $domainInfo[1], 'RecordId' => $recordId[0], 'Type' => $type, 'Value' => $ip]);
-        }
-
-        return false;
-    }
-
-    /**
-     * 域名信息.
-     *
-     * @param  string|null  $type  记录类型,默认为 null
-     * @return array|false
-     */
-    private function getRecordId($type = null)
-    {
-        $parameters = ['SubDomain' => self::$subDomain];
-        if ($type) {
-            $parameters['Type'] = $type;
-        }
-        $records = $this->send('DescribeSubDomainRecords', $parameters);
-
-        if ($records && Arr::has($records, 'DomainRecords.Record')) {
-            $records = $records['DomainRecords']['Record'];
-            $data = null;
-            foreach ($records as $record) {
-                $data[] = $record['RecordId'];
-            }
-
-            return $data ?: false;
-        }
-
-        return false;
-    }
-
-    public function destroy($type)
-    {
-        $records = $this->getRecordId($type);
-        if ($records) {
-            $count = 0;
-            foreach ($records as $record) {
-                if ($this->send('DeleteDomainRecord', ['RecordId' => $record])) {
-                    $count++;
-                }
-            }
-
-            return $count;
-        }
-
-        return false;
-    }
-}

+ 0 - 129
app/Components/DDNS/CloudFlare.php

@@ -1,129 +0,0 @@
-<?php
-
-namespace App\Components\DDNS;
-
-use Arr;
-use Http;
-use Log;
-
-class CloudFlare
-{
-    private static $apiHost = 'https://api.cloudflare.com/client/v4/';
-
-    private static $subDomain;
-
-    private $zoneIdentifier;
-
-    private $client;
-
-    public function __construct($subDomain)
-    {
-        self::$subDomain = $subDomain;
-        $this->zoneIdentifier = $this->getZone();
-        $this->client = Http::withHeaders(['X-Auth-Key' => sysConfig('ddns_secret'), 'X-Auth-Email' => sysConfig('ddns_key')]);
-    }
-
-    private function getZone()
-    {
-        $zoneInfo = $this->client->get(self::$apiHost.'zones')->json();
-        if ($zoneInfo && Arr::has($zoneInfo, 'result.0.id')) {
-            foreach ($zoneInfo['result'] as $zone) {
-                if (str_contains(self::$subDomain, $zone['name'])) {
-                    return [$zone['name'], rtrim(substr(self::$subDomain, 0, -strlen($zone['name'])), '.'), $zone['id']];
-                }
-            }
-        }
-
-        return [];
-    }
-
-    public function store($ip, $type)
-    {
-        if ($this->zoneIdentifier) {
-            return $this->send('create', ['type' => $type, 'name' => self::$subDomain, 'content' => $ip, 'ttl' => 120]);
-        }
-
-        return false;
-    }
-
-    private function send($action, $data = [], $id = null)
-    {
-        if ($this->zoneIdentifier) {
-            switch ($action) {
-                case 'get':
-                    $response = $this->client->get(self::$apiHost.'zones/'.$this->zoneIdentifier[2].'/dns_records', $data);
-                    break;
-                case 'create':
-                    $response = $this->client->post(self::$apiHost.'zones/'.$this->zoneIdentifier[2].'/dns_records', $data);
-                    break;
-                case 'update':
-                    $response = $this->client->put(self::$apiHost.'zones/'.$this->zoneIdentifier[2].'/dns_records/'.$id, $data);
-                    break;
-                case 'delete':
-                    $response = $this->client->delete(self::$apiHost.'zones/'.$this->zoneIdentifier[2].'/dns_records/'.$id);
-                    break;
-                default:
-                    return false;
-            }
-
-            $message = $response->json();
-            if ($message && ! $response->failed()) {
-                return $message;
-            }
-
-            Log::error('[CloudFlare API] - ['.$action.'] 请求失败:'.var_export($message, true));
-        }
-
-        return false;
-    }
-
-    public function update($ip, $type)
-    {
-        $recordId = $this->getRecordId($type);
-
-        if ($this->zoneIdentifier && $recordId) {
-            return $this->send('update', ['type' => $type, 'name' => self::$subDomain, 'content' => $ip, 'ttl' => 120], $recordId[0]);
-        }
-
-        return false;
-    }
-
-    private function getRecordId($type = null)
-    {
-        $parameters['name'] = self::$subDomain;
-        if ($type) {
-            $parameters['type'] = $type;
-        }
-        $dnsList = $this->send('get', $parameters);
-
-        if ($dnsList && Arr::has($dnsList, 'result.0.id')) {
-            $dnsRecords = $dnsList['result'];
-            $data = null;
-            foreach ($dnsRecords as $record) {
-                $data[] = $record['id'];
-            }
-
-            return $data ?: false;
-        }
-
-        return false;
-    }
-
-    public function destroy($type)
-    {
-        $records = $this->getRecordId($type);
-        if ($records && $this->zoneIdentifier) {
-            $count = 0;
-            foreach ($records as $record) {
-                $result = $this->send('delete', [], $record);
-                if ($result && Arr::has($result, 'result.id')) {
-                    $count++;
-                }
-            }
-
-            return $count;
-        }
-
-        return false;
-    }
-}

+ 0 - 152
app/Components/DDNS/DNSPod.php

@@ -1,152 +0,0 @@
-<?php
-
-namespace App\Components\DDNS;
-
-use Arr;
-use Http;
-use Log;
-
-class DNSPod
-{
-    private static $apiHost = 'https://dnsapi.cn/';
-
-    private static $subDomain;
-
-    public function __construct($subDomain)
-    {
-        self::$subDomain = $subDomain;
-    }
-
-    public function store($ip, $type)
-    {
-        $domainInfo = $this->analysisDomain();
-
-        if ($domainInfo) {
-            return $this->send('Record.Create', [
-                'domain_id' => $domainInfo[2],
-                'sub_domain' => $domainInfo[1],
-                'record_type' => $type,
-                'record_line_id' => 0,
-                'value' => $ip,
-            ]);
-        }
-
-        return false;
-    }
-
-    private function analysisDomain()
-    {
-        $domainList = $this->domainList();
-        if ($domainList) {
-            foreach ($domainList as $key => $domain) {
-                if (str_contains(self::$subDomain, $domain)) {
-                    return [$domain, rtrim(substr(self::$subDomain, 0, -strlen($domain)), '.'), $key];
-                }
-            }
-        }
-
-        return [];
-    }
-
-    public function domainList()
-    {
-        $domainList = $this->send('Domain.List', ['type' => 'mine']);
-        if ($domainList) {
-            return Arr::pluck($domainList['domains'], 'name', 'id');
-        }
-
-        return false;
-    }
-
-    private function send($action, $data = null)
-    {
-        $parameters = [
-            'login_token' => sysConfig('ddns_key').','.sysConfig('ddns_secret'),
-            'format' => 'json',
-        ];
-
-        if ($data) {
-            $parameters = array_merge($data, $parameters);
-        }
-
-        $response = Http::timeout(15)->asForm()->post(self::$apiHost.$action, $parameters);
-        $message = $response->json();
-
-        if ($response->failed() || ($message && Arr::has($message, 'status.code') && $message['status']['code'] !== '1')) {
-            if ($message && Arr::has($message, 'status.code') && $message['status']['code'] !== '1') {
-                $error = $message['status']['message'];
-            } else {
-                $error = $response->body();
-            }
-            Log::error('[DNSPod - '.$action.'] 请求失败:'.$error);
-
-            return false;
-        }
-
-        return $message;
-    }
-
-    public function update($ip, $type)
-    {
-        $recordId = $this->getRecordId($type);
-        $domainInfo = $this->analysisDomain();
-        if ($recordId && $domainInfo) {
-            return $this->send('Record.Modify', [
-                'domain_id' => $domainInfo[2],
-                'record_id' => $recordId[0],
-                'sub_domain' => $domainInfo[1],
-                'record_type' => $type,
-                'record_line_id' => 0,
-                'value' => $ip,
-            ]);
-        }
-
-        return false;
-    }
-
-    private function getRecordId($type = null)
-    {
-        $domainInfo = $this->analysisDomain();
-        if ($domainInfo) {
-            $parameters = ['domain_id' => $domainInfo[2], 'sub_domain' => $domainInfo[1]];
-            if ($type) {
-                $parameters['record_type'] = $type;
-            }
-            $records = $this->send('Record.List', $parameters);
-            if ($records && Arr::has($records, 'records')) {
-                return Arr::pluck($records['records'], 'id');
-            }
-        }
-
-        return false;
-    }
-
-    public function destroy($type)
-    {
-        $records = $this->getRecordId($type);
-        $domainInfo = $this->analysisDomain();
-        if ($records && $domainInfo) {
-            $count = 0;
-            foreach ($records as $record) {
-                $result = $this->send('Record.Remove', ['domain_id' => $domainInfo[2], 'record_id' => $record]);
-                if ($result) {
-                    $count++;
-                }
-            }
-
-            return $count;
-        }
-
-        return false;
-    }
-
-    public function version()
-    {
-        $version = $this->send('Info.Version');
-        if ($version) {
-            return $version['status']['message'];
-        }
-
-        return false;
-    }
-}

+ 0 - 148
app/Components/DDNS/Namesilo.php

@@ -1,148 +0,0 @@
-<?php
-
-namespace App\Components\DDNS;
-
-use Arr;
-use Log;
-
-class Namesilo
-{
-    private static $apiHost = 'https://www.namesilo.com/api/';
-
-    private static $subDomain;
-
-    public function __construct($subDomain)
-    {
-        self::$subDomain = $subDomain;
-    }
-
-    public function store($ip, $type)
-    {
-        $domainInfo = $this->analysisDomain();
-        if ($domainInfo) {
-            return $this->send('dnsAddRecord', [
-                'domain' => $domainInfo[0],
-                'rrtype' => $type,
-                'rrhost' => $domainInfo[1],
-                'rrvalue' => $ip,
-                'rrttl' => 3600,
-            ]);
-        }
-
-        return false;
-    }
-
-    private function analysisDomain()
-    {
-        $domainList = $this->domainList();
-        if ($domainList) {
-            if (is_array($domainList)) {
-                foreach ($domainList as $domain) {
-                    if (str_contains(self::$subDomain, $domain)) {
-                        return [$domain, rtrim(substr(self::$subDomain, 0, -strlen($domain)), '.')];
-                    }
-                }
-            } elseif (str_contains(self::$subDomain, $domainList)) {
-                return [$domainList, rtrim(substr(self::$subDomain, 0, -strlen($domainList)), '.')];
-            }
-        }
-
-        return [];
-    }
-
-    public function domainList()
-    {
-        $data = $this->send('listDomains');
-        if ($data) {
-            return $data['domains']['domain'];
-        }
-
-        return false;
-    }
-
-    private function send($action, $data = [])
-    {
-        $params = [
-            'version' => 1,
-            'type' => 'xml',
-            'key' => sysConfig('ddns_key'),
-        ];
-        $query = array_merge($params, $data);
-
-        $result = file_get_contents(self::$apiHost.$action.'?'.http_build_query($query));
-        $result = json_decode(json_encode(simplexml_load_string(trim($result))), true);
-
-        if ($result && $result['reply']['code'] === '300' && $result['reply']['detail'] === 'success') {
-            return $result['reply'];
-        }
-        Log::error('[Namesilo API] - ['.$action.'] 请求失败:'.var_export($result, true));
-
-        return false;
-    }
-
-    public function update($ip, $type)
-    {
-        $recordId = $this->getRecordId($type);
-        $domainInfo = $this->analysisDomain();
-
-        if ($domainInfo && $recordId) {
-            return $this->send('dnsUpdateRecord', [
-                'domain' => $domainInfo[0],
-                'rrid' => $recordId[0],
-                'rrhost' => $domainInfo[1],
-                'rrvalue' => $ip,
-                'rrttl' => 3600,
-            ]);
-        }
-        Log::error('[Namesilo API] - [更新] 处理失败:'.var_export($recordId, true).var_export($domainInfo, true));
-
-        return false;
-    }
-
-    private function getRecordId($type = null)
-    {
-        $domainInfo = $this->analysisDomain();
-        if ($domainInfo) {
-            $records = $this->send('dnsListRecords', ['domain' => $domainInfo[0]]);
-
-            if ($records && Arr::has($records, 'resource_record')) {
-                $records = $records['resource_record'];
-                $data = null;
-                foreach ($records as $record) {
-                    if (Arr::has($record, ['host', 'type', 'record_id']) && $record['host'] === self::$subDomain) {
-                        if ($type) {
-                            if ($type === $record['type']) {
-                                $data[] = $record['record_id'];
-                            }
-                        } else {
-                            $data[] = $record['record_id'];
-                        }
-                    }
-                }
-
-                return $data ?: false;
-            }
-        }
-
-        return false;
-    }
-
-    public function destroy($type)
-    {
-        $records = $this->getRecordId($type);
-        $domainInfo = $this->analysisDomain();
-        if ($records && $domainInfo) {
-            $count = 0;
-            foreach ($records as $record) {
-                $result = $this->send('dnsDeleteRecord', ['domain' => $domainInfo[0], 'rrid' => $record]);
-                if ($result) {
-                    $count++;
-                }
-            }
-
-            return $count;
-        }
-
-        return false;
-    }
-}

+ 0 - 238
app/Components/IP.php

@@ -1,238 +0,0 @@
-<?php
-
-namespace App\Components;
-
-use Exception;
-use GeoIp2\Database\Reader;
-use GeoIp2\Exception\AddressNotFoundException;
-use Http;
-use IP2Location\Database;
-use Ip2Region;
-use ipip\db\City;
-use Log;
-use MaxMind\Db\Reader\InvalidDatabaseException;
-
-class IP
-{
-    public static function getIPInfo($ip) // 获取IP地址信息
-    {
-        // IPv6 推荐使用ip.sb
-        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
-            Log::notice('识别到IPv6,尝试解析:'.$ip);
-            $ipInfo = self::IPSB($ip);
-        } else {
-            $ipInfo = self::ip2Region($ip);
-            if (! $ipInfo) {
-                Log::warning('无法识别,尝试使用【IPIP库】库解析:'.$ip);
-                $ipInfo = self::ip2Location($ip);
-            }
-        }
-
-        return $ipInfo;
-    }
-
-    public static function IPSB($ip) // 通过api.ip.sb查询IP地址的详细信息
-    {
-        try {
-            $response = Http::withHeaders(['User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'])->timeout(15)->post('https://api.ip.sb/geoip/'.$ip);
-
-            if ($response->ok()) {
-                return $response->json();
-            }
-
-            Log::warning('[IPSB] 解析'.$ip.'异常: '.$response->body());
-
-            return false;
-        } catch (Exception $e) {
-            Log::error('[IPSB] 解析'.$ip.'错误: '.var_export($e->getMessage(), true));
-
-            return false;
-        }
-    }
-
-    public static function ip2Region(string $ip) // 通过ip2Region查询IP地址的详细信息 ← 聚合 淘宝IP库,GeoIP,纯真IP库
-    {
-        $ipInfo = false;
-        try {
-            $ipInfo = (new Ip2Region())->memorySearch($ip);
-        } catch (Exception $e) {
-            Log::error('【ip2Region】错误信息:'.$e->getMessage());
-        }
-
-        if ($ipInfo) {
-            $location = explode('|', $ipInfo['region']);
-            if ($location) {
-                return [
-                    'country' => $location[0] ?: '',
-                    'province' => $location[2] ?: '',
-                    'city' => $location[3] ?: '',
-                    'isp' => $location[4] ?: '',
-                    'area' => $location[1] ?: '',
-                ];
-            }
-        }
-
-        return $ipInfo;
-    }
-
-    public static function ip2Location(string $ip) // 通过ip2Location查询IP地址的详细信息
-    {
-        $filePath = database_path('IP2LOCATION-LITE-DB3.IPV6.BIN'); // 来源: https://lite.ip2location.com/database-download IP-COUNTRY-REGION-CITY的BIN
-        try {
-            $location = (new Database($filePath, Database::FILE_IO))
-                ->lookup($ip, [Database::CITY_NAME, Database::REGION_NAME, Database::COUNTRY_NAME]);
-
-            return [
-                'country' => $location['countryName'],
-                'province' => $location['regionName'],
-                'city' => $location['cityName'],
-            ];
-        } catch (Exception $e) {
-            Log::error('【ip2Location】错误信息:'.$e->getMessage());
-        }
-
-        return false;
-    }
-
-    public static function IPIP(string $ip): array // 通过IPIP离线数据查询IP地址的详细信息
-    {
-        $filePath = database_path('ipipfree.ipdb'); // 来源: https://www.ipip.net/free_download/
-        $location = (new City($filePath))->findMap($ip, 'CN');
-
-        return [
-            'country' => $location['country_name'],
-            'province' => $location['region_name'],
-            'city' => $location['city_name'],
-        ];
-    }
-
-    public static function IPIPOnline(string $ip) // 通过IPIP在线查询IP地址的详细信息
-    { // https://freeapi.ipip.net
-        $response = Http::timeout(15)->get('https://freeapi.ipip.net/'.$ip);
-
-        if ($response->ok()) {
-            $message = $response->json();
-            if ($message) {
-                return [
-                    'country' => $message[0],
-                    'province' => $message[1],
-                    'city' => $message[2],
-                ];
-            }
-
-            Log::warning('【IPIP在线】返回错误信息:'.$ip.PHP_EOL.$message['msg']);
-        } else {
-            Log::error('【IPIP在线】解析异常:'.$ip);
-        }
-
-        return false;
-    }
-
-    public static function TaoBao(string $ip) // 通过ip.taobao.com查询IP地址的详细信息
-    {
-        // 依据 https://ip.taobao.com/instructions 开发
-        $response = Http::timeout(15)->post('https://ip.taobao.com/outGetIpInfo?ip='.$ip.'&accessKey=alibaba-inc');
-
-        if ($response->ok()) {
-            $message = $response->json();
-            if ($message['code'] === 0) {
-                return [
-                    'country' => $message['data']['country'] === 'XX' ? '' : $message['data']['country'],
-                    'province' => $message['data']['region'] === 'XX' ? '' : $message['data']['region'],
-                    'city' => $message['data']['city'] === 'XX' ? '' : $message['data']['city'],
-                    'isp' => $message['data']['isp'] === 'XX' ? '' : $message['data']['isp'],
-                ];
-            }
-
-            Log::warning('【淘宝IP库】返回错误信息:'.$ip.PHP_EOL.$message['msg']);
-        } else {
-            Log::error('【淘宝IP库】解析异常:'.$ip);
-        }
-
-        return false;
-    }
-
-    public static function Baidu(string $ip) // 通过api.map.baidu.com查询IP地址的详细信息
-    {
-        if (! config('services.baidu.app_ak')) {
-            Log::error('【百度IP库】AK信息缺失');
-
-            return false;
-        }
-        // 依据 http://lbsyun.baidu.com/index.php?title=webapi/ip-api 开发
-        $response = Http::timeout(15)->get('https://api.map.baidu.com/location/ip?ak='.config('services.baidu.app_ak').'&'.$ip.'&coor=bd09ll');
-
-        if ($response->ok()) {
-            $message = $response->json();
-            if ($message['status'] === 0) {
-                return [
-                    'country' => $message['content']['address_detail']['country'],
-                    'province' => $message['content']['address_detail']['province'],
-                    'city' => $message['content']['address_detail']['city'],
-                    'area' => $message['address'],
-                ];
-            }
-
-            Log::warning('【百度IP库】返回错误信息:'.$ip.PHP_EOL.var_export($message['message'], true));
-        } else {
-            Log::error('【百度IP库】解析异常:'.$ip);
-        }
-
-        return false;
-    }
-
-    public static function GeoIP2(string $ip) // 通过GeoIP2查询IP地址的详细信息
-    {
-        $filePath = database_path('GeoLite2-City.mmdb'); // 来源:https://github.com/PrxyHunter/GeoLite2/releases
-        try {
-            $location = (new Reader($filePath))->city($ip);
-
-            return [
-                'country' => $location->country->name ?? '',
-                'province' => $location->mostSpecificSubdivision->name ?? '',
-                'city' => $location->city->name ?? '',
-            ];
-        } catch (AddressNotFoundException $e) {
-            Log::error('【GeoIP2】查询失败:'.$ip);
-        } catch (InvalidDatabaseException $e) {
-            Log::error('【GeoIP2】数据库无效:'.$ip);
-        }
-
-        return false;
-    }
-
-    public static function getClientIP() // 获取访客真实IP
-    {
-        /*
-         * 访问时用localhost访问的,读出来的是“::1”是正常情况
-         * ::1说明开启了IPv6支持,这是IPv6下的本地回环地址的表示
-         * 使用IPv4地址访问或者关闭IPv6支持都可以不显示这个
-         */
-        if (isset($_SERVER)) {
-            if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
-                $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
-                $ip = $_SERVER['REMOTE_ADDR'];
-            } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
-                $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
-            } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
-                $ip = $_SERVER['HTTP_CLIENT_IP'];
-            } elseif (isset($_SERVER['REMOTE_ADDR'])) {
-                $ip = $_SERVER['REMOTE_ADDR'];
-            } else {
-                $ip = 'unknown';
-            }
-        } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
-            $ip = getenv('HTTP_X_FORWARDED_FOR');
-        } elseif (getenv('HTTP_CLIENT_IP')) {
-            $ip = getenv('HTTP_CLIENT_IP');
-        } else {
-            $ip = getenv('REMOTE_ADDR');
-        }
-
-        if (trim($ip) === '::1') {
-            $ip = '127.0.0.1';
-        }
-
-        return $ip;
-    }
-}

+ 0 - 17
app/Components/MigrationToolBox.php

@@ -1,17 +0,0 @@
-<?php
-
-namespace App\Components;
-
-use DB;
-
-class MigrationToolBox
-{
-    public function versionCheck(): bool
-    {
-        $dbVersion = DB::select('select version()')[0]->{'version()'};
-        $dbType = strpos($dbVersion, 'Maria');
-        $dbVersion = mb_substr($dbVersion, 0, 6);
-
-        return ($dbType !== false && version_compare($dbVersion, '10.2.7', '>=')) || ($dbType === false && version_compare($dbVersion, '5.7.8', '>='));
-    }
-}

+ 0 - 386
app/Components/NetworkDetection.php

@@ -1,386 +0,0 @@
-<?php
-
-namespace App\Components;
-
-use Http;
-use Log;
-
-class NetworkDetection
-{
-    /**
-     * 用外部API进行Ping检测.
-     *
-     * @param  string  $ip  被检测的IP或者域名
-     * @return bool
-     */
-    public function ping(string $ip)
-    {
-        $round = 1;
-        // 依次尝试接口
-        while (true) {
-            switch ($round) {
-                case 0:
-                    $ret = $this->oiowebPing($ip);
-                    break;
-                case 1:
-                    $ret = $this->xiaoapiPing($ip);
-                    break;
-                case 2:
-                    $ret = $this->yum6Ping($ip);
-                    break;
-                default:
-                    return false;
-            }
-            if ($ret !== false) {
-                return $ret;
-            }
-            $round++;
-        }
-    }
-
-    private function oiowebPing(string $ip)
-    {
-        $msg = null;
-        foreach ([1, 6, 14] as $line) {
-            $url = "https://api.oioweb.cn/api/hostping.php?host={$ip}&node={$line}"; // https://api.iiwl.cc/api/ping.php?host=
-            $response = Http::timeout(20)->get($url);
-
-            // 发送成功
-            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
-        $msg = null;
-
-        $url = "https://xiaoapi.cn/API/sping.php?url={$ip}";
-        $response = Http::timeout(20)->get($url);
-
-        // 发送成功
-        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
-        $url = "https://api.yum6.cn/ping.php?host={$ip}";
-        $response = Http::timeout(20)->get($url);
-
-        // 发送成功
-        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;
-    }
-
-    /**
-     * 通过众多API进行节点阻断检测.
-     *
-     * @param  string  $ip  被检测的IP
-     * @param  bool  $is_icmp  TRUE 为ICMP,FALSE 为tcp
-     * @param  int|null  $port  检测端口,默认为空
-     * @return bool
-     */
-    public function networkCheck(string $ip, bool $is_icmp, int $port = null)
-    {
-        $round = 1;
-        // 依次尝试接口
-        while (true) {
-            switch ($round) {
-                case 0:
-                    $ret = $this->idcWiki($ip, $is_icmp, $port);
-                    break;
-                case 1:
-                    $ret = $this->flyzy2005($ip, $is_icmp, $port);
-                    break;
-                case 2:
-                    $ret = $this->vps234($ip, $is_icmp);
-                    break;
-                case 3:
-                    $ret = $this->idcoffer($ip, $is_icmp, $port);
-                    break;
-                case 4:
-                    $ret = $this->gd($ip, $is_icmp, $port);
-                    break;
-                case 5:
-                    $ret = $this->ip112($ip, $is_icmp, $port);
-                    break;
-                default:
-                    return false;
-            }
-            if ($ret !== false) {
-                return $ret;
-            }
-            $round++;
-        }
-    }
-
-    private function idcWiki(string $ip, bool $is_icmp, int $port = null)
-    {
-        if ($is_icmp) {
-            $type_string = 'icmp/';
-            $checkName = 'ICMP';
-        } else {
-            $type_string = 'tcp_ack/';
-            $checkName = 'TCP';
-        }
-        if ($port) {
-            $port = '/'.$port;
-            $type_string = 'tcp_port/';
-        }
-
-        $url = "https://api.50network.com/china-firewall/check/ip/{$type_string}{$ip}{$port}";
-
-        $response = Http::timeout(20)->get($url);
-
-        if ($response->ok()) {
-            $message = $response->json();
-            if (! $message) {
-                Log::warning('【'.$checkName.'阻断检测】检测'.$ip.'时,接口返回异常访问链接:'.$url);
-
-                return false;
-            }
-
-            if (! $message['success']) {
-                if (isset($message['error']) && $message['error'] === 'execute timeout (3s)') {
-                    return false;
-                }
-
-                Log::warning('【'.$checkName.'阻断检测】检测'.$ip.$port.'时,返回'.var_export($message, true));
-
-                return false;
-            }
-
-            if ($message['firewall-enable'] && $message['firewall-disable']) {
-                return 1; // 正常
-            }
-
-            if ($message['firewall-enable'] && ! $message['firewall-disable']) {
-                return 2; // 国外访问异常
-            }
-
-            if (! $message['firewall-enable'] && $message['firewall-disable']) {
-                return 3; // 被墙
-            }
-
-            return 4; // 服务器宕机
-        }
-
-        return false;
-    }
-
-    private function flyzy2005(string $ip, bool $is_icmp, int $port = null)
-    {
-        $cn = "https://mini.flyzy2005.cn/ip_check.php?ip={$ip}&port={$port}";
-        $us = "https://mini.flyzy2005.cn/ip_check_outside.php?ip={$ip}&port={$port}";
-
-        $checkName = $is_icmp ? 'icmp' : 'tcp';
-
-        $response_cn = Http::timeout(20)->get($cn);
-        $response_us = Http::timeout(20)->get($us);
-
-        if ($response_cn->ok() && $response_us->ok()) {
-            $cn = $response_cn->json();
-            $us = $response_us->json();
-            if (! $cn || ! $us) {
-                Log::warning("【{$checkName}阻断检测】检测{$ip}时,接口返回异常访问链接:{$cn} | {$us}");
-
-                return false;
-            }
-
-            if ($cn[$checkName] === 'success' && $us['outside_'.$checkName] === 'success') {
-                return 1; // 正常
-            }
-
-            if ($cn[$checkName] === 'success' && $us['outside_'.$checkName] !== 'success') {
-                return 2; // 国外访问异常
-            }
-
-            if ($cn[$checkName] !== 'success' && $us['outside_'.$checkName] === 'success') {
-                return 3; // 被墙
-            }
-
-            return 4; // 服务器宕机
-        }
-
-        return false;
-    }
-
-    private function vps234(string $ip, bool $is_icmp)
-    {
-        $url = 'https://www.vps234.com/ipcheck/getdata/';
-
-        $checkName = $is_icmp ? 'ICMP' : 'TCP';
-
-        $response = Http::timeout(20)->withoutVerifying()->asForm()->post($url, ['ip' => $ip]);
-        if ($response->ok()) {
-            $message = $response->json();
-            if (! $message) {
-                Log::warning('【'.$checkName.'阻断检测】检测'.$ip.'时,接口返回异常访问链接:'.$url);
-
-                return false;
-            }
-
-            if (! $message['data']['success']) {
-                Log::warning('【'.$checkName.'阻断检测】检测'.$ip.'时,返回'.var_export($message, true));
-
-                return false;
-            }
-
-            if ($message['data']['data']['inner'.$checkName] && $message['data']['data']['out'.$checkName]) {
-                return 1; // 正常
-            }
-
-            if ($message['data']['data']['inner'.$checkName] && ! $message['data']['data']['out'.$checkName]) {
-                return 2; // 国外访问异常
-            }
-
-            if (! $message['data']['data']['inner'.$checkName] && $message['data']['data']['out'.$checkName]) {
-                return 3; // 被墙
-            }
-
-            return 4; // 服务器宕机
-        }
-
-        return false;
-    }
-
-    private function idcoffer(string $ip, bool $is_icmp, int $port = null)
-    { // 来源:https://www.idcoffer.com/ipcheck
-        $cn = "https://api.24kplus.com/ipcheck?host={$ip}&port={$port}";
-        $us = "https://api.idcoffer.com/ipcheck?host={$ip}&port={$port}";
-        $checkName = $is_icmp ? 'ping' : 'tcp';
-
-        $response_cn = Http::timeout(20)->get($cn);
-        $response_us = Http::timeout(20)->get($us);
-
-        if ($response_cn->ok() && $response_us->ok()) {
-            $cn = $response_cn->json();
-            $us = $response_us->json();
-            if (! $cn || ! $us) {
-                Log::warning("【{$checkName}阻断检测】检测{$ip}时,接口返回异常访问链接:{$cn} | {$us}");
-
-                return false;
-            }
-
-            if (! $cn['code'] || ! $us['code']) {
-                Log::warning('【'.$checkName.'阻断检测】检测'.$ip.$port.'时,返回'.var_export($cn, true).var_export($us, true));
-
-                return false;
-            }
-
-            if ($cn['data'][$checkName] && $us['data'][$checkName]) {
-                return 1; // 正常
-            }
-
-            if ($cn['data'][$checkName] && ! $us['data'][$checkName]) {
-                return 2; // 国外访问异常
-            }
-
-            if (! $cn['data'][$checkName] && $us['data'][$checkName]) {
-                return 3; // 被墙
-            }
-
-            return 4; // 服务器宕机
-        }
-
-        return false;
-    }
-
-    private function gd(string $ip, bool $is_icmp, int $port = 443)
-    { // 来源:https://ping.gd/
-        $url = "https://ping.gd/api/ip-test/{$ip}:".($port ?? 443);
-
-        $checkName = $is_icmp ? 'ping_alive' : 'telnet_alive';
-
-        $response = Http::timeout(20)->get($url);
-
-        if ($response->ok()) {
-            $message = $response->json();
-            if (! $message) {
-                Log::warning("【{$checkName}阻断检测】检测{$ip}时,接口返回异常访问链接:{$url}");
-
-                return false;
-            }
-
-            if ($message[0]['result'][$checkName] && $message[1]['result'][$checkName]) {
-                return 1; // 正常
-            }
-
-            if ($message[0]['result'][$checkName] && ! $message[1]['result'][$checkName]) {
-                return 2; // 国外访问异常
-            }
-
-            if (! $message[0]['result'][$checkName] && $message[1]['result'][$checkName]) {
-                return 3; // 被墙
-            }
-
-            return 4; // 服务器宕机
-        }
-
-        return false;
-    }
-
-    private function ip112(string $ip, bool $is_icmp, int $port = 443)
-    { // 来源:https://ip112.cn/
-        $cn = 'https://api.zhujiquanzi.com/ipcheck/ipcheck.php';
-        $us = 'https://api.52bwg.com/ipcheck/ipcheck.php';
-        $checkName = $is_icmp ? 'icmp' : 'tcp';
-
-        $response_cn = Http::asForm()->post($cn, ['ip' => $ip, 'port' => $port]);
-        $response_us = Http::asForm()->post($us, ['ip' => $ip, 'port' => $port]);
-
-        if ($response_cn->ok() && $response_us->ok()) {
-            $cn = $response_cn->json();
-            $us = $response_us->json();
-            if (! $cn || ! $us) {
-                Log::warning("【{$checkName}阻断检测】检测{$ip}时,接口返回异常访问链接:{$cn} | {$us}");
-
-                return false;
-            }
-
-            if (str_contains($cn[$checkName], 'green') && str_contains($us[$checkName], 'green')) {
-                return 1; // 正常
-            }
-
-            if (str_contains($cn[$checkName], 'green') && ! str_contains($us[$checkName], 'green')) {
-                return 2; // 国外访问异常
-            }
-
-            if (! str_contains($cn[$checkName], 'green') && str_contains($us[$checkName], 'green')) {
-                return 3; // 被墙
-            }
-
-            return 4; // 服务器宕机
-        }
-
-        return false;
-    }
-}

+ 0 - 56
app/Components/QQInfo.php

@@ -1,56 +0,0 @@
-<?php
-
-namespace App\Components;
-
-use Http;
-
-class QQInfo
-{
-    public static function getName(string $qq): string
-    {
-        //向接口发起请求获取json数据
-        $url = 'https://r.qzone.qq.com/fcg-bin/cgi_get_portrait.fcg?get_nick=1&uins='.$qq;
-        $response = Http::timeout(15)->retry(2)->get($url);
-        $message = mb_convert_encoding($response->body(), 'UTF-8', 'GBK');
-
-        // 接口是否异常
-        if ($response->ok() && str_contains($message, $qq)) {
-            //对获取的json数据进行截取并解析成数组
-            $message = json_decode(substr($message, 17, -1), true);
-
-            return stripslashes($message[$qq][6]);
-        }
-
-        return $qq;
-    }
-
-    public static function getName2(string $qq): string
-    {
-        //向接口发起请求获取json数据
-        $url = 'https://api.qqder.com/qqxt/api.php?qq='.$qq;
-        $response = Http::timeout(15)->get($url);
-        $message = $response->json();
-
-        // 接口是否异常
-        if ($message && $message['code'] === 1 && $response->ok()) {
-            return $message['name'];
-        }
-
-        return $qq;
-    }
-
-    public static function getName3(string $qq): string
-    {
-        //向接口发起请求获取json数据
-        $url = 'https://api.unipay.qq.com/v1/r/1450000186/wechat_query?cmd=1&pf=mds_storeopen_qb-__mds_qqclub_tab_-html5&pfkey=pfkey&from_h5=1&from_https=1&openid=openid&openkey=openkey&session_id=hy_gameid&session_type=st_dummy&qq_appid=&offerId=1450000186&sandbox=&provide_uin='.$qq;
-        $response = Http::timeout(15)->get($url);
-        $message = $response->json();
-
-        // 接口是否异常
-        if ($message && $message['ret'] === 0 && $response->ok()) {
-            return urldecode($message['nick']);
-        }
-
-        return $qq;
-    }
-}

+ 3 - 3
app/Console/Commands/AutoClearLogs.php

@@ -22,7 +22,7 @@ class AutoClearLogs extends Command
 
     protected $description = '自动清除日志';
 
-    public function handle()
+    public function handle(): void
     {
         $jobTime = microtime(true);
 
@@ -35,7 +35,7 @@ class AutoClearLogs extends Command
     }
 
     // 清除日志
-    private function clearLog()
+    private function clearLog(): void
     {
         try {
             NodeDailyDataFlow::where('created_at', '<=', date('Y-m-d H:i:s', strtotime(config('tasks.clean.node_daily_logs'))))->delete(); // 清除节点每天流量数据日志
@@ -50,7 +50,7 @@ class AutoClearLogs extends Command
 
             NodeOnlineIp::where('created_at', '<=', strtotime(config('tasks.clean.node_online_ips')))->delete(); // 清除用户连接IP
 
-            UserDailyDataFlow::where('node_id', '<>', null)
+            UserDailyDataFlow::where('node_id', '<>')
                 ->where('created_at', '<=', date('Y-m-d H:i:s', strtotime(config('tasks.clean.user_daily_logs_nodes'))))
                 ->orWhere('created_at', '<=', date('Y-m-d H:i:s', strtotime(config('tasks.clean.user_daily_logs_total'))))
                 ->delete(); // 清除用户各节点 / 节点总计的每天流量数据日志

+ 6 - 6
app/Console/Commands/DailyNodeReport.php

@@ -16,7 +16,7 @@ class DailyNodeReport extends Command
 
     protected $description = '自动报告节点昨日使用情况';
 
-    public function handle()
+    public function handle(): void
     {
         $jobTime = microtime(true);
 
@@ -33,8 +33,8 @@ class DailyNodeReport extends Command
                     $log = $node->dailyDataFlows()->whereDate('created_at', $date)->first();
                     $data[] = [
                         'name' => $node->name,
-                        'upload' => flowAutoShow($log->u ?? 0),
-                        'download' => flowAutoShow($log->d ?? 0),
+                        'upload' => formatBytes($log->u ?? 0),
+                        'download' => formatBytes($log->d ?? 0),
                         'total' => $log->traffic ?? '',
                     ];
                     $upload += $log->u ?? 0;
@@ -43,9 +43,9 @@ class DailyNodeReport extends Command
                 if ($data) {
                     $data[] = [
                         'name' => trans('notification.node.total'),
-                        'total' => flowAutoShow($upload + $download),
-                        'upload' => flowAutoShow($upload),
-                        'download' => flowAutoShow($download),
+                        'total' => formatBytes($upload + $download),
+                        'upload' => formatBytes($upload),
+                        'download' => formatBytes($download),
                     ];
 
                     Notification::send(User::role('Super Admin')->get(), new NodeDailyReport($data));

+ 11 - 12
app/Console/Commands/NodeStatusDetection.php

@@ -2,12 +2,12 @@
 
 namespace App\Console\Commands;
 
-use App\Components\NetworkDetection;
 use App\Models\Node;
 use App\Models\NodeHeartbeat;
 use App\Models\User;
 use App\Notifications\NodeBlocked;
 use App\Notifications\NodeOffline;
+use App\Utils\NetworkDetection;
 use Cache;
 use Illuminate\Console\Command;
 use Log;
@@ -19,7 +19,7 @@ class NodeStatusDetection extends Command
 
     protected $description = '节点状态检测';
 
-    public function handle()
+    public function handle(): void
     {
         $jobTime = microtime(true);
 
@@ -39,7 +39,7 @@ class NodeStatusDetection extends Command
         Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
-    private function checkNodeStatus()
+    private function checkNodeStatus(): void
     {
         $offlineCheckTimes = sysConfig('offline_check_times');
         $onlineNode = NodeHeartbeat::recently()->distinct()->pluck('node_id')->toArray();
@@ -78,16 +78,15 @@ class NodeStatusDetection extends Command
             $node_id = $node->id;
             // 使用DDNS的node先通过gethostbyname获取ipv4地址
             foreach ($node->ips() as $ip) {
-                if ($node->detection_type !== 1) {
-                    $icmpCheck = (new NetworkDetection)->networkCheck($ip, true, $node->port ?? 22);
-                    if ($icmpCheck !== false && $icmpCheck !== 1) {
-                        $data[$node_id][$ip]['icmp'] = config('common.network_status')[$icmpCheck];
+                if ($node->detection_type) {
+                    $status = (new NetworkDetection)->networkStatus($ip, $node->port ?? 22);
+
+                    if ($node->detection_type !== 1 && $status['icmp'] !== 1) {
+                        $data[$node_id][$ip]['icmp'] = config('common.network_status')[$status['icmp']];
                     }
-                }
-                if ($node->detection_type !== 2) {
-                    $tcpCheck = (new NetworkDetection)->networkCheck($ip, false, $node->port ?? 22);
-                    if ($tcpCheck !== false && $tcpCheck !== 1) {
-                        $data[$node_id][$ip]['tcp'] = config('common.network_status')[$tcpCheck];
+
+                    if ($node->detection_type !== 2 && $status['tcp'] !== 1) {
+                        $data[$node_id][$ip]['tcp'] = config('common.network_status')[$status['tcp']];
                     }
                 }
             }

+ 8 - 11
app/Console/Commands/PanelInstallation.php

@@ -15,7 +15,7 @@ class PanelInstallation extends Command
 
     protected $description = 'ProxyPanel Installation (面板自主安装)';
 
-    public function handle()
+    public function handle(): int
     {
         try {
             $bar = $this->output->createProgressBar(7);
@@ -102,9 +102,9 @@ class PanelInstallation extends Command
         return 0;
     }
 
-    private function saveToEnv($data = [])
+    private function saveToEnv($data = []): void
     {
-        function set_env_var($key, $value): bool
+        function set_env_var($key, $value): void
         {
             if (! is_bool(strpos($value, ' '))) {
                 $value = '"'.$value.'"';
@@ -114,30 +114,27 @@ class PanelInstallation extends Command
             $envPath = app()->environmentFilePath();
             $contents = file_get_contents($envPath);
 
-            preg_match("/^{$key}=[^\r\n]*/m", $contents, $matches);
+            preg_match("/^$key=[^\r\n]*/m", $contents, $matches);
 
             $oldValue = count($matches) ? $matches[0] : '';
 
             if ($oldValue) {
-                $contents = str_replace((string) $oldValue, "{$key}={$value}", $contents);
+                $contents = str_replace($oldValue, "$key=$value", $contents);
             } else {
-                $contents .= "\n{$key}={$value}\n";
+                $contents .= "\n$key=$value\n";
             }
 
             $file = fopen($envPath, 'wb');
             fwrite($file, $contents);
-
-            return fclose($file);
+            fclose($file);
         }
 
         foreach ($data as $key => $value) {
             set_env_var($key, $value);
         }
-
-        return true;
     }
 
-    private function editAdmin($email, $password)
+    private function editAdmin($email, $password): bool
     {
         $user = User::find(1);
         $user->username = $email;

+ 1 - 28
app/Console/Commands/PanelUpdate.php

@@ -8,36 +8,11 @@ use Illuminate\Console\Command;
 
 class PanelUpdate extends Command
 {
-    /**
-     * The name and signature of the console command.
-     *
-     * @var string
-     */
     protected $signature = 'panel:update';
 
-    /**
-     * The console command description.
-     *
-     * @var string
-     */
     protected $description = 'ProxyPanel Version Update (面板更新)';
 
-    /**
-     * Create a new command instance.
-     *
-     * @return void
-     */
-    public function __construct()
-    {
-        parent::__construct();
-    }
-
-    /**
-     * Execute the console command.
-     *
-     * @return mixed
-     */
-    public function handle()
+    public function handle(): void
     {
         try {
             $bar = $this->output->createProgressBar(2);
@@ -59,7 +34,5 @@ class PanelUpdate extends Command
         } catch (Exception $e) {
             $this->error($e->getMessage());
         }
-
-        return 0;
     }
 }

+ 2 - 2
app/Console/Commands/ServiceTimer.php

@@ -12,7 +12,7 @@ class ServiceTimer extends Command
 
     protected $description = '服务计时器';
 
-    public function handle()
+    public function handle(): void
     {
         $jobTime = microtime(true);
 
@@ -22,7 +22,7 @@ class ServiceTimer extends Command
         Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
-    private function expiredPlan()
+    private function expiredPlan(): void
     {
         Order::activePlan()
             ->where('expired_at', '<=', date('Y-m-d H:i:s'))

+ 7 - 7
app/Console/Commands/TaskAuto.php

@@ -22,7 +22,7 @@ class TaskAuto extends Command
     /*
      * 警告:除非熟悉业务流程,否则不推荐更改以下执行顺序,随意变更以下顺序可能导致系统异常
      */
-    public function handle()
+    public function handle(): void
     {
         $jobTime = microtime(true);
 
@@ -43,7 +43,7 @@ class TaskAuto extends Command
         Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
-    private function orderTimer()
+    private function orderTimer(): void
     {
         Order::recentUnPay()->chunk(config('tasks.chunk'), function ($orders) {
             $orders->each->close();
@@ -54,7 +54,7 @@ class TaskAuto extends Command
         }); // 关闭未处理的人工支付订单
     }
 
-    private function expireCode()
+    private function expireCode(): void
     { // 注册验证码自动置无效 & 优惠券无效化
         // 注册验证码自动置无效
         VerifyCode::recentUnused()->update(['status' => 2]);
@@ -73,7 +73,7 @@ class TaskAuto extends Command
             ->update(['status' => 2]);
     }
 
-    private function blockSubscribes()
+    private function blockSubscribes(): void
     { // 封禁访问异常的订阅链接
         User::activeUser()
             ->with(['subscribe', 'subscribeLogs'])
@@ -95,7 +95,7 @@ class TaskAuto extends Command
             });
     }
 
-    private function unblockSubscribes()
+    private function unblockSubscribes(): void
     {
         UserSubscribe::whereStatus(0)->where('ban_time', '<=', time())->chunk(config('tasks.chunk'), function ($subscribes) {
             foreach ($subscribes as $subscribe) {
@@ -104,7 +104,7 @@ class TaskAuto extends Command
         });
     }
 
-    private function blockUsers()
+    private function blockUsers(): void
     { // 封禁账号
         // 禁用流量超限用户
         User::activeUser()
@@ -134,7 +134,7 @@ class TaskAuto extends Command
         }
     }
 
-    private function unblockUsers()
+    private function unblockUsers(): void
     { // 解封账号
         // 解封被临时封禁的账号
         User::bannedUser()

+ 15 - 16
app/Console/Commands/TaskDaily.php

@@ -2,12 +2,12 @@
 
 namespace App\Console\Commands;
 
-use App\Components\Helpers;
 use App\Models\Node;
 use App\Models\Ticket;
 use App\Models\User;
 use App\Notifications\TicketClosed;
 use App\Services\OrderService;
+use App\Utils\Helpers;
 use Illuminate\Console\Command;
 use Illuminate\Database\Eloquent\Builder;
 use Log;
@@ -18,7 +18,7 @@ class TaskDaily extends Command
 
     protected $description = '每日任务';
 
-    public function handle()
+    public function handle(): void
     {
         $jobTime = microtime(true);
 
@@ -37,8 +37,8 @@ class TaskDaily extends Command
         Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
-    private function expireUser()// 过期用户处理
-    {
+    private function expireUser(): void
+    { // 过期用户处理
         $isBanStatus = sysConfig('is_ban_status');
         User::activeUser()
             ->where('expired_at', '<', date('Y-m-d')) // 过期
@@ -68,8 +68,8 @@ class TaskDaily extends Command
             });
     }
 
-    private function closeTickets()// 关闭用户超时未处理的工单
-    {
+    private function closeTickets(): void
+    { // 关闭用户超时未处理的工单
         Ticket::whereStatus(1)
             ->whereHas('reply', function ($q) {
                 $q->where('admin_id', '<>', null);
@@ -87,8 +87,8 @@ class TaskDaily extends Command
             });
     }
 
-    private function resetUserTraffic()// 重置用户流量
-    {
+    private function resetUserTraffic(): void
+    { // 重置用户流量
         User::where('status', '<>', -1)
             ->where('expired_at', '>', date('Y-m-d'))
             ->where('reset_time', '<=', date('Y-m-d'))
@@ -116,9 +116,8 @@ class TaskDaily extends Command
             });
     }
 
-    private function releaseAccountPort()
-    {
-        // 被封禁 / 过期N天 的账号自动释放端口
+    private function releaseAccountPort(): void
+    { // 被封禁 / 过期N天 的账号自动释放端口
         User::where('port', '<>', 0)
             ->where(function ($query) {
                 $query->whereStatus(-1)->orWhere('expired_at', '<=', date('Y-m-d', strtotime('-'.config('tasks.release_port').' days')));
@@ -126,7 +125,7 @@ class TaskDaily extends Command
             ->update(['port' => 0]);
     }
 
-    private function userTrafficStatistics()
+    private function userTrafficStatistics(): void
     {
         $created_at = date('Y-m-d 23:59:59', strtotime('-1 days'));
         $end = strtotime($created_at);
@@ -144,7 +143,7 @@ class TaskDaily extends Command
 
                 $data = $logs->each(function ($log) use ($created_at) {
                     $log->total = $log->u + $log->d;
-                    $log->traffic = flowAutoShow($log->total);
+                    $log->traffic = formatBytes($log->total);
                     $log->created_at = $created_at;
                 })->flatten()->toArray();
 
@@ -152,7 +151,7 @@ class TaskDaily extends Command
                     'u' => $logs->sum('u'),
                     'd' => $logs->sum('d'),
                     'total' => $logs->sum('total'),
-                    'traffic' => flowAutoShow($logs->sum('total')),
+                    'traffic' => formatBytes($logs->sum('total')),
                     'created_at' => $created_at,
                 ];
 
@@ -161,7 +160,7 @@ class TaskDaily extends Command
         });
     }
 
-    private function nodeTrafficStatistics()
+    private function nodeTrafficStatistics(): void
     {
         $created_at = date('Y-m-d 23:59:59', strtotime('-1 day'));
         $end = strtotime($created_at);
@@ -179,7 +178,7 @@ class TaskDaily extends Command
                     'u' => $traffic->u,
                     'd' => $traffic->d,
                     'total' => $total,
-                    'traffic' => flowAutoShow($total),
+                    'traffic' => formatBytes($total),
                     'created_at' => $created_at,
                 ]);
             }

+ 6 - 6
app/Console/Commands/TaskHourly.php

@@ -27,7 +27,7 @@ class TaskHourly extends Command
         Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
-    private function userTrafficStatistics()
+    private function userTrafficStatistics(): void
     {
         $created_at = date('Y-m-d H:59:59', strtotime('-1 hour'));
         $end = strtotime($created_at);
@@ -46,14 +46,14 @@ class TaskHourly extends Command
 
                 $data = $logs->each(function ($log) use ($created_at) {
                     $log->total = $log->u + $log->d;
-                    $log->traffic = flowAutoShow($log->total);
+                    $log->traffic = formatBytes($log->total);
                     $log->created_at = $created_at;
                 })->flatten()->toArray();
                 $overall = [ // 每小时节点流量合计
                     'u' => $logs->sum('u'),
                     'd' => $logs->sum('d'),
                     'total' => $logs->sum('total'),
-                    'traffic' => flowAutoShow($logs->sum('total')),
+                    'traffic' => formatBytes($logs->sum('total')),
                     'created_at' => $created_at,
                 ];
                 $data[] = $overall;
@@ -61,13 +61,13 @@ class TaskHourly extends Command
 
                 // 用户流量异常警告
                 if ($data_anomaly_notification && $overall['total'] >= $traffic_ban_value) {
-                    Notification::send(User::find(1), new DataAnomaly($user->username, flowAutoShow($overall['u']), flowAutoShow($overall['d']), $overall['traffic']));
+                    Notification::send(User::find(1), new DataAnomaly($user->username, formatBytes($overall['u']), formatBytes($overall['d']), $overall['traffic']));
                 }
             }
         });
     }
 
-    private function nodeTrafficStatistics()
+    private function nodeTrafficStatistics(): void
     {
         $created_at = date('Y-m-d H:59:59', strtotime('-1 hour'));
         $end = strtotime($created_at);
@@ -85,7 +85,7 @@ class TaskHourly extends Command
                     'u' => $traffic->u,
                     'd' => $traffic->d,
                     'total' => $total,
-                    'traffic' => flowAutoShow($total),
+                    'traffic' => formatBytes($total),
                     'created_at' => $created_at,
                 ]);
             }

+ 2 - 2
app/Console/Commands/TaskMonthly.php

@@ -32,13 +32,13 @@ class TaskMonthly extends Command
         Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
-    private function cleanAccounts()
+    private function cleanAccounts(): void
     {
         User::where('expired_at', '<', date('Y-m-d'))->where('transfer_enable', '==', 0)->whereEnable(0)
             ->whereRaw('u + d > transfer_enable')->update(['u' => 0, 'd' => 0]);
     }
 
-    private function clearLog()
+    private function clearLog(): void
     {
         try {
             NotificationLog::where('updated_at', '<=', date('Y-m-d H:i:s', strtotime(config('tasks.clean.notification_logs'))))->delete(); // 清理通知日志

+ 2 - 2
app/Console/Commands/UserExpireWarning.php

@@ -13,7 +13,7 @@ class UserExpireWarning extends Command
 
     protected $description = '用户临近到期自动提醒';
 
-    public function handle()
+    public function handle(): void
     {
         $jobTime = microtime(true);
 
@@ -25,7 +25,7 @@ class UserExpireWarning extends Command
         Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
-    private function userExpireWarning()
+    private function userExpireWarning(): void
     {
         // 只取没被禁用的用户,其他不用管
         User::whereEnable(1)

+ 4 - 4
app/Console/Commands/UserTrafficWarning.php

@@ -13,11 +13,11 @@ class UserTrafficWarning extends Command
 
     protected $description = '用户流量超过警告阈值自动发邮件提醒';
 
-    public function handle()
+    public function handle(): void
     {
         $jobTime = microtime(true);
 
-        if (sysConfig('data_exhaust_notification')) {// 用户流量超过警告阈值提醒
+        if (sysConfig('data_exhaust_notification')) { // 用户流量超过警告阈值提醒
             $this->userTrafficWarning();
         }
 
@@ -25,8 +25,8 @@ class UserTrafficWarning extends Command
         Log::info(__('----「:job」Completed, Used :time seconds ----', ['job' => $this->description, 'time' => $jobTime]));
     }
 
-    private function userTrafficWarning()// 用户流量超过警告阈值提醒
-    {
+    private function userTrafficWarning(): void
+    { // 用户流量超过警告阈值提醒
         $trafficWarningPercent = sysConfig('traffic_warning_percent');
         User::activeUser()->where('transfer_enable', '>', 0)->chunk(config('tasks.chunk'), function ($users) use ($trafficWarningPercent) {
             foreach ($users as $user) {

+ 2 - 2
app/Console/Commands/VNetReload.php

@@ -13,13 +13,13 @@ class VNetReload extends Command
 
     protected $description = 'VNet线路重置';
 
-    public function handle()
+    public function handle(): void
     {
         $startTime = microtime(true);
 
         $nodes = Node::whereStatus(1)->whereType(4)->get();
         if ($nodes->isNotEmpty()) {
-            reloadNode::dispatchNow($nodes);
+            reloadNode::dispatchSync($nodes);
         }
 
         $jobTime = round(microtime(true) - $startTime, 4);

+ 0 - 32
app/Console/Kernel.php

@@ -2,43 +2,13 @@
 
 namespace App\Console;
 
-use App\Console\Commands\AutoClearLogs;
-use App\Console\Commands\DailyNodeReport;
-use App\Console\Commands\NodeStatusDetection;
-use App\Console\Commands\ServiceTimer;
-use App\Console\Commands\TaskAuto;
-use App\Console\Commands\TaskDaily;
-use App\Console\Commands\TaskHourly;
-use App\Console\Commands\TaskMonthly;
-use App\Console\Commands\UserExpireWarning;
-use App\Console\Commands\UserTrafficWarning;
 use Illuminate\Console\Scheduling\Schedule;
 use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
 
 class Kernel extends ConsoleKernel
 {
-    /**
-     * The Artisan commands provided by your application.
-     *
-     * @var array
-     */
-    protected $commands = [
-        AutoClearLogs::class,
-        DailyNodeReport::class,
-        NodeStatusDetection::class,
-        ServiceTimer::class,
-        TaskAuto::class,
-        TaskDaily::class,
-        TaskHourly::class,
-        TaskMonthly::class,
-        UserExpireWarning::class,
-        UserTrafficWarning::class,
-    ];
-
     /**
      * Define the application's command schedule.
-     *
-     * @return void
      */
     protected function schedule(Schedule $schedule): void
     {
@@ -56,8 +26,6 @@ class Kernel extends ConsoleKernel
 
     /**
      * Register the commands for the application.
-     *
-     * @return void
      */
     protected function commands(): void
     {

+ 3 - 8
app/Exceptions/Handler.php

@@ -2,7 +2,7 @@
 
 namespace App\Exceptions;
 
-use App\Components\IP;
+use App\Utils\IP;
 use ErrorException;
 use Illuminate\Auth\AuthenticationException;
 use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
@@ -19,20 +19,15 @@ use Throwable;
 
 class Handler extends ExceptionHandler
 {
-    /**
-     * A list of the exception types that are not reported.
-     *
-     * @var array
-     */
     protected $dontReport = [
         //        HttpException::class,
         ValidationException::class,
     ];
 
     /**
-     * A list of the inputs that are never flashed for validation exceptions.
+     * The list of the inputs that are never flashed to the session on validation exceptions.
      *
-     * @var array
+     * @var array<int, string>
      */
     protected $dontFlash = [
         'current_password',

+ 6 - 6
app/Helpers/ClientApiResponse.php

@@ -7,7 +7,7 @@ use Illuminate\Http\Request;
 
 trait ClientApiResponse
 {
-    private static $client;
+    private static string $client;
 
     public function __construct(Request $request)
     {
@@ -16,17 +16,17 @@ trait ClientApiResponse
         }
     }
 
-    public function setClient($client)
+    public function setClient(string $client): void
     {
         self::$client = $client;
     }
 
-    public function succeed($data = null, $addition = null, $codeResponse = ResponseEnum::HTTP_OK): JsonResponse
+    public function succeed(array|null $data = null, array|null $addition = null, array $codeResponse = ResponseEnum::HTTP_OK): JsonResponse
     {
         return $this->jsonResponse(1, $codeResponse, $data, $addition);
     }
 
-    private function jsonResponse($status, $codeResponse, $data = null, $addition = null): JsonResponse
+    private function jsonResponse(int $status, array $codeResponse, array|string|null $data = null, array|null $addition = null): JsonResponse
     {
         [$code, $message] = $codeResponse;
         $code = $code > 1000 ? (int) ($code / 1000) : $code;
@@ -47,8 +47,8 @@ trait ClientApiResponse
         return response()->json($result, $code, ['content-type' => 'application/json']);
     }
 
-    public function failed($codeResponse = ResponseEnum::HTTP_ERROR, $data = null, $addition = null): JsonResponse
+    public function failed(array $codeResponse = ResponseEnum::HTTP_ERROR, array|string|null $data = null): JsonResponse
     {
-        return $this->jsonResponse(0, $codeResponse, is_array($data) ? $data[0] : $data, $addition);
+        return $this->jsonResponse(0, $codeResponse, is_array($data) ? $data[0] : $data);
     }
 }

+ 61 - 64
app/Helpers/ClientConfig.php

@@ -2,16 +2,16 @@
 
 namespace App\Helpers;
 
-use App\Components\Client\Clash;
-use App\Components\Client\QuantumultX;
-use App\Components\Client\Surge;
-use App\Components\Client\URLSchemes;
+use App\Utils\Clients\Clash;
+use App\Utils\Clients\QuantumultX;
+use App\Utils\Clients\Surge;
+use App\Utils\Clients\URLSchemes;
 use File;
 use Symfony\Component\Yaml\Yaml;
 
 trait ClientConfig
 {
-    private function clientConfig(string $target)
+    private function clientConfig(string $target): string
     {
         if (str_contains($target, 'quantumult%20x')) {
             return $this->quantumultX();
@@ -37,9 +37,9 @@ trait ClientConfig
         if (str_contains($target, 'v2rayn') || str_contains($target, 'v2rayng') || str_contains($target, 'v2rayu')) {
             return $this->v2rayN();
         }
-        //            if (strpos($target, 'shadowsocks') !== false) {
-        //                exit($this->shaodowsocksSIP008());
-        //            }
+        //        if (str_contains($target, 'shadowsocks')) {
+        //            exit($this->shaodowsocksSIP008());
+        //        }
         return $this->origin();
     }
 
@@ -99,7 +99,7 @@ trait ClientConfig
         return $encode ? base64_encode($uri) : $uri;
     }
 
-    private function clash($client = false)
+    private function clash(string $client = ''): string
     {
         $user = $this->getUser();
         $webName = sysConfig('website_name');
@@ -152,7 +152,7 @@ trait ClientConfig
         return str_replace('$app_name', $webName, Yaml::dump($config, 2, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE));
     }
 
-    private function surfboard()
+    private function surfboard(): string
     {
         $defaultConfig = base_path().'/resources/rules/default.surfboard.conf';
         $customConfig = base_path().'/resources/rules/custom.surfboard.conf';
@@ -160,7 +160,52 @@ trait ClientConfig
         return $this->sugerLike($customConfig, $defaultConfig);
     }
 
-    private function surge(string $target)
+    private function sugerLike(string $customConfig, string $defaultConfig, string $target = ''): string
+    {
+        if (File::exists($customConfig)) {
+            $config = file_get_contents($customConfig);
+        } else {
+            $config = file_get_contents($defaultConfig);
+        }
+
+        $proxies = '';
+        $proxyGroup = '';
+        $user = $this->getUser();
+        $webName = sysConfig('website_name');
+        header("content-disposition:attachment;filename*=UTF-8''".rawurlencode($webName).'.conf');
+        foreach ($this->getServers() as $server) {
+            if ($server['type'] === 'shadowsocks') {
+                $proxies .= Surge::buildShadowsocks($server);
+                $proxyGroup .= $server['name'].', ';
+            }
+            if ($server['type'] === 'v2ray') {
+                $proxies .= Surge::buildVmess($server);
+                $proxyGroup .= $server['name'].', ';
+            }
+            if ($server['type'] === 'trojan') {
+                $proxies .= Surge::buildTrojan($server);
+                $proxyGroup .= $server['name'].', ';
+            }
+        }
+
+        if (str_contains($target, 'list')) {
+            return $proxies;
+        }
+
+        if (sysConfig('is_custom_subscribe')) {
+            $upload = formatBytes($user->u);
+            $download = formatBytes($user->d);
+            $totalTraffic = formatBytes($user->transfer_enable);
+            $subscribeInfo = "title=$webName".trans('user.subscribe.info.title').', content='.trans('user.subscribe.info.upload').": $upload\\n".trans('user.subscribe.info.download').": $download\\n".trans('user.subscribe.info.total').": $totalTraffic\\n".trans('model.user.expired_date').": $user->expired_at";
+            $config = str_replace('$subscribe_info', $subscribeInfo, $config);
+        }
+
+        return str_replace(['$subs_link', '$subs_domain', '$proxies', '$proxy_group'],
+            [route('sub', $user->subscribe->code), $_SERVER['HTTP_HOST'], $proxies, rtrim($proxyGroup, ', ')],
+            $config);
+    }
+
+    private function surge(string $target): string
     {
         $defaultConfig = base_path().'/resources/rules/default.surge.conf';
         $customConfig = base_path().'/resources/rules/custom.surge.conf';
@@ -174,9 +219,9 @@ trait ClientConfig
         $uri = '';
         $user = $this->getUser();
         if (sysConfig('is_custom_subscribe')) {
-            $upload = flowAutoShow($user->u);
-            $download = flowAutoShow($user->d);
-            $totalTraffic = flowAutoShow($user->transfer_enable);
+            $upload = formatBytes($user->u);
+            $download = formatBytes($user->d);
+            $totalTraffic = formatBytes($user->transfer_enable);
             $uri = "STATUS=📤:{$upload}📥:{$download}⏳:{$totalTraffic}📅:$user->expiration_date\r\n";
         }
         $uri .= $this->origin(false);
@@ -184,7 +229,7 @@ trait ClientConfig
         return base64_encode($uri);
     }
 
-    private function v2rayN()
+    private function v2rayN(): string
     {
         $uri = '';
         $user = $this->getUser();
@@ -194,7 +239,7 @@ trait ClientConfig
                 if ($user->transfer_enable === 0) {
                     $text .= trans('user.account.remain').': 0';
                 } else {
-                    $text .= trans('user.account.remain').': '.flowAutoShow($user->transfer_enable);
+                    $text .= trans('user.account.remain').': '.formatBytes($user->transfer_enable);
                 }
                 $text .= ', '.trans('model.user.expired_date').": $user->expiration_date";
             } else {
@@ -216,52 +261,4 @@ trait ClientConfig
 
         return json_encode(['version' => 1, 'remark' => sysConfig('website_name'), 'servers' => $configs ?? []], JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
     }
-
-    /**
-     * @return array|false|string|string[]
-     */
-    private function sugerLike(string $customConfig, string $defaultConfig, string $target = '')
-    {
-        if (File::exists($customConfig)) {
-            $config = file_get_contents($customConfig);
-        } else {
-            $config = file_get_contents($defaultConfig);
-        }
-
-        $proxies = '';
-        $proxyGroup = '';
-        $user = $this->getUser();
-        $webName = sysConfig('website_name');
-        header("content-disposition:attachment;filename*=UTF-8''".rawurlencode($webName).'.conf');
-        foreach ($this->getServers() as $server) {
-            if ($server['type'] === 'shadowsocks') {
-                $proxies .= Surge::buildShadowsocks($server);
-                $proxyGroup .= $server['name'].', ';
-            }
-            if ($server['type'] === 'v2ray') {
-                $proxies .= Surge::buildVmess($server);
-                $proxyGroup .= $server['name'].', ';
-            }
-            if ($server['type'] === 'trojan') {
-                $proxies .= Surge::buildTrojan($server);
-                $proxyGroup .= $server['name'].', ';
-            }
-        }
-
-        if (str_contains($target, 'list')) {
-            return $proxies;
-        }
-
-        if (sysConfig('is_custom_subscribe')) {
-            $upload = flowAutoShow($user->u);
-            $download = flowAutoShow($user->d);
-            $totalTraffic = flowAutoShow($user->transfer_enable);
-            $subscribeInfo = "title=$webName".trans('user.subscribe.info.title').', content='.trans('user.subscribe.info.upload').": $upload\\n".trans('user.subscribe.info.download').": $download\\n".trans('user.subscribe.info.total').": $totalTraffic\\n".trans('model.user.expired_date').": $user->expired_at";
-            $config = str_replace('$subscribe_info', $subscribeInfo, $config);
-        }
-
-        return str_replace(['$subs_link', '$subs_domain', '$proxies', '$proxy_group'],
-            [route('sub', $user->subscribe->code), $_SERVER['HTTP_HOST'], $proxies, rtrim($proxyGroup, ', ')],
-            $config);
-    }
 }

+ 21 - 16
app/Helpers/DataChart.php

@@ -11,44 +11,49 @@ use DB;
 
 trait DataChart
 {
-    public function dataFlowChart($id, $is_node = false): array // 流量使用图表
+    /**
+     * 流量统计
+     *
+     * @param  int  $id  用户ID 或者 节点ID
+     * @param  bool  $is_node  决定 id 是否为节点ID
+     * @return array 用户/节点 流量统计
+     */
+    public function dataFlowChart(int $id, bool $is_node = false): array // 流量使用图表
     {
+        $lastHour = (int) date('G') + 1;
+        $lastDay = date('j');
+        $hourlyData = array_fill(0, $lastHour, 0);
+        $dailyData = array_fill(0, $lastDay - 1, 0);
+
         if ($is_node) {
             $currentFlow = UserDataFlowLog::whereNodeId($id);
-            $hourlyFlow = NodeHourlyDataFlow::whereNodeId($id)->whereDate('created_at',
-                date('Y-m-d'))->selectRaw('(DATE_FORMAT(node_hourly_data_flow.created_at, "%k")) as date, total')->pluck('total', 'date');
-            $dailyFlow = NodeDailyDataFlow::whereNodeId($id)->whereMonth('created_at',
-                date('n'))->selectRaw('(DATE_FORMAT(node_daily_data_flow.created_at, "%e")) as date, total')->pluck('total', 'date');
+            $hourlyFlow = NodeHourlyDataFlow::whereNodeId($id)->whereDate('created_at', date('Y-m-d'))->selectRaw('(DATE_FORMAT(node_hourly_data_flow.created_at, "%k")) as date, total')->pluck('total', 'date');
+            $dailyFlow = NodeDailyDataFlow::whereNodeId($id)->whereMonth('created_at', date('n'))->selectRaw('(DATE_FORMAT(node_daily_data_flow.created_at, "%e")) as date, total')->pluck('total', 'date');
         } else {
             $currentFlow = UserDataFlowLog::whereUserId($id);
-            $hourlyFlow = UserHourlyDataFlow::userHourly($id)->whereDate('created_at',
-                date('Y-m-d'))->selectRaw('(DATE_FORMAT(user_hourly_data_flow.created_at, "%k")) as date, total')->pluck('total', 'date');
-            $dailyFlow = UserDailyDataFlow::userDaily($id)->whereMonth('created_at',
-                date('n'))->selectRaw('(DATE_FORMAT(user_daily_data_flow.created_at, "%e")) as date, total')->pluck('total', 'date');
+            $hourlyFlow = UserHourlyDataFlow::userHourly($id)->whereDate('created_at', date('Y-m-d'))->selectRaw('(DATE_FORMAT(user_hourly_data_flow.created_at, "%k")) as date, total')->pluck('total', 'date');
+            $dailyFlow = UserDailyDataFlow::userDaily($id)->whereMonth('created_at', date('n'))->selectRaw('(DATE_FORMAT(user_daily_data_flow.created_at, "%e")) as date, total')->pluck('total', 'date');
         }
         $currentFlow = $currentFlow->where('log_time', '>=', strtotime(date('Y-m-d H:0')))->sum(DB::raw('u + d'));
 
         // 节点一天内的流量
-        $hourlyData = array_fill(0, date('G') + 1, 0);
         foreach ($hourlyFlow as $date => $dataFlow) {
             $hourlyData[$date] = round($dataFlow / GB, 3);
         }
-        $hourlyData[date('G') + 1] = round($currentFlow / GB, 3);
+        $hourlyData[$lastHour] = round($currentFlow / GB, 3);
 
         // 节点一个月内的流量
-        $dailyData = array_fill(0, date('j') - 1, 0);
-
         foreach ($dailyFlow as $date => $dataFlow) {
             $dailyData[$date - 1] = round($dataFlow / GB, 3);
         }
 
-        $dailyData[date('j', strtotime(now())) - 1] = round(array_sum($hourlyData) + $currentFlow / GB, 3);
+        $dailyData[$lastDay - 1] = round(array_sum($hourlyData) + $currentFlow / GB, 3);
 
         return [
             'trafficDaily' => $dailyData,
             'trafficHourly' => $hourlyData,
-            'monthDays' => range(1, date('j')), // 本月天数
-            'dayHours' => range(0, date('G') + 1), // 本日小时
+            'monthDays' => range(1, $lastDay), // 本月天数
+            'dayHours' => range(0, $lastHour), // 本日小时
         ];
     }
 }

+ 29 - 29
app/Helpers/ResponseEnum.php

@@ -13,25 +13,25 @@ class ResponseEnum
 
     /*-------------------------------------------------------------------------------------------*/
     // 200表示服务器成功地接受了客户端请求
-    const HTTP_OK = [200001, '操作成功'];
+    public const HTTP_OK = [200001, '操作成功'];
 
-    const HTTP_ERROR = [200002, '操作失败'];
+    public const HTTP_ERROR = [200002, '操作失败'];
 
-    const HTTP_ACTION_COUNT_ERROR = [200302, '操作频繁'];
+    public const HTTP_ACTION_COUNT_ERROR = [200302, '操作频繁'];
 
-    const USER_SERVICE_LOGIN_SUCCESS = [200200, '登录成功'];
+    public const USER_SERVICE_LOGIN_SUCCESS = [200200, '登录成功'];
 
-    const USER_SERVICE_LOGIN_ERROR = [200201, '登录失败'];
+    public const USER_SERVICE_LOGIN_ERROR = [200201, '登录失败'];
 
-    const USER_SERVICE_LOGOUT_SUCCESS = [200202, '退出登录成功'];
+    public const USER_SERVICE_LOGOUT_SUCCESS = [200202, '退出登录成功'];
 
-    const USER_SERVICE_LOGOUT_ERROR = [200203, '退出登录失败'];
+    public const USER_SERVICE_LOGOUT_ERROR = [200203, '退出登录失败'];
 
-    const USER_SERVICE_REGISTER_SUCCESS = [200104, '注册成功'];
+    public const USER_SERVICE_REGISTER_SUCCESS = [200104, '注册成功'];
 
-    const USER_SERVICE_REGISTER_ERROR = [200105, '注册失败'];
+    public const USER_SERVICE_REGISTER_ERROR = [200105, '注册失败'];
 
-    const USER_ACCOUNT_REGISTERED = [23001, '账号已注册'];
+    public const USER_ACCOUNT_REGISTERED = [23001, '账号已注册'];
 
     /*-------------------------------------------------------------------------------------------*/
     // 300开头的表示服务器重定向,指向的别的地方,客户端浏览器必须采取更多操作来实现请求
@@ -42,29 +42,29 @@ class ResponseEnum
     /*-------------------------------------------------------------------------------------------*/
     // 400开头的表示客户端错误请求错误,请求不到数据,或者找不到等等
     // 400 - 错误的请求
-    const CLIENT_NOT_FOUND_HTTP_ERROR = [400001, '请求失败'];
+    public const CLIENT_NOT_FOUND_HTTP_ERROR = [400001, '请求失败'];
 
-    const CLIENT_PARAMETER_ERROR = [400200, '参数错误'];
+    public const CLIENT_PARAMETER_ERROR = [400200, '参数错误'];
 
-    const CLIENT_CREATED_ERROR = [400201, '数据已存在'];
+    public const CLIENT_CREATED_ERROR = [400201, '数据已存在'];
 
-    const CLIENT_DELETED_ERROR = [400202, '数据不存在'];
+    public const CLIENT_DELETED_ERROR = [400202, '数据不存在'];
 
     // 401 - 访问被拒绝
-    const CLIENT_HTTP_UNSYNCHRONIZE_TIMER = [401001, '通信双方时钟不同步,或通信超时'];
+    public const CLIENT_HTTP_UNSYNCHRONIZE_TIMER = [401001, '通信双方时钟不同步,或通信超时'];
 
-    const CLIENT_HTTP_UNAUTHORIZED = [401200, '授权失败,请先登录'];
+    public const CLIENT_HTTP_UNAUTHORIZED = [401200, '授权失败,请先登录'];
 
-    const CLIENT_HTTP_UNAUTHORIZED_EXPIRED = [401201, '账号信息已过期,请重新登录'];
+    public const CLIENT_HTTP_UNAUTHORIZED_EXPIRED = [401201, '账号信息已过期,请重新登录'];
 
-    const CLIENT_HTTP_UNAUTHORIZED_BLACKLISTED = [401202, '账号已被禁止登录'];
+    public const CLIENT_HTTP_UNAUTHORIZED_BLACKLISTED = [401202, '账号已被禁止登录'];
 
     // 403 - 禁止访问
     // 404 - 没有找到文件或目录
-    const CLIENT_NOT_FOUND_ERROR = [404001, '没有找到该页面'];
+    public const CLIENT_NOT_FOUND_ERROR = [404001, '没有找到该页面'];
 
     // 405 - 用来访问本页面的 HTTP 谓词不被允许(方法不被允许)
-    const CLIENT_METHOD_HTTP_TYPE_ERROR = [405001, 'HTTP请求类型错误'];
+    public const CLIENT_METHOD_HTTP_TYPE_ERROR = [405001, 'HTTP请求类型错误'];
     // 406 - 客户端浏览器不接受所请求页面的 MIME 类型
     // 407 - 要求进行代理身份验证
     // 412 - 前提条件失败
@@ -79,24 +79,24 @@ class ResponseEnum
     // 500开头的表示服务器错误,服务器因为代码,或者什么原因终止运行
     // 服务端操作错误码:500 ~ 599 开头,后拼接 3 位
     // 500 - 内部服务器错误
-    const SYSTEM_ERROR = [500001, '服务器错误'];
+    public const SYSTEM_ERROR = [500001, '服务器错误'];
 
-    const SYSTEM_UNAVAILABLE = [500002, '服务器正在维护,暂不可用'];
+    public const SYSTEM_UNAVAILABLE = [500002, '服务器正在维护,暂不可用'];
 
-    const SYSTEM_CACHE_CONFIG_ERROR = [500003, '缓存配置错误'];
+    public const SYSTEM_CACHE_CONFIG_ERROR = [500003, '缓存配置错误'];
 
-    const SYSTEM_CACHE_MISSED_ERROR = [500004, '缓存未命中'];
+    public const SYSTEM_CACHE_MISSED_ERROR = [500004, '缓存未命中'];
 
-    const SYSTEM_CONFIG_ERROR = [500005, '系统配置错误'];
+    public const SYSTEM_CONFIG_ERROR = [500005, '系统配置错误'];
 
     // 业务操作错误码(外部服务或内部服务调用)
-    const SERVICE_REGISTER_ERROR = [500101, '注册失败'];
+    public const SERVICE_REGISTER_ERROR = [500101, '注册失败'];
 
-    const SERVICE_LOGIN_ERROR = [500102, '登录失败'];
+    public const SERVICE_LOGIN_ERROR = [500102, '登录失败'];
 
-    const SERVICE_LOGIN_ACCOUNT_ERROR = [500103, '账号或密码错误'];
+    public const SERVICE_LOGIN_ACCOUNT_ERROR = [500103, '账号或密码错误'];
 
-    const SERVICE_USER_INTEGRAL_ERROR = [500200, '积分不足'];
+    public const SERVICE_USER_INTEGRAL_ERROR = [500200, '积分不足'];
 
     //501 - 页眉值指定了未实现的配置
     //502 - Web 服务器用作网关或代理服务器时收到了无效响应

+ 5 - 5
app/Helpers/WebApiResponse.php

@@ -6,12 +6,12 @@ use Illuminate\Http\JsonResponse;
 
 trait WebApiResponse
 {
-    public function succeed($data = null, $addition = null, $codeResponse = ResponseEnum::HTTP_OK): JsonResponse // 成功
+    public function succeed(array|null $data = null, array|null $addition = null, array $codeResponse = ResponseEnum::HTTP_OK): JsonResponse // 成功
     {
         return $this->jsonResponse('success', $codeResponse, $data, $addition);
     }
 
-    private function jsonResponse($status, $codeResponse, $data, $addition): JsonResponse // 返回数据
+    private function jsonResponse(string $status, array $codeResponse, array|null $data = null, array|null $addition = null): JsonResponse // 返回数据
     {
         [$code, $message] = $codeResponse;
         if ($status === 'success') {
@@ -26,7 +26,7 @@ trait WebApiResponse
         return response()->json($data, $code, ['ETAG' => $etag ?? '']);
     }
 
-    private static function abortIfNotModified($data): string // 检查数据是否有变动
+    private static function abortIfNotModified(array|null $data): string // 检查数据是否有变动
     {
         $req = request();
 
@@ -42,8 +42,8 @@ trait WebApiResponse
         return $etag;
     }
 
-    public function failed($codeResponse = ResponseEnum::HTTP_ERROR, $data = null, $addition = null): JsonResponse // 失败
+    public function failed(array $codeResponse = ResponseEnum::HTTP_ERROR, array|null $data = null): JsonResponse // 失败
     {
-        return $this->jsonResponse('fail', $codeResponse, $data, $addition);
+        return $this->jsonResponse('fail', $codeResponse, $data);
     }
 }

+ 5 - 4
app/Http/Controllers/Admin/AffiliateController.php

@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
 use App\Http\Controllers\Controller;
 use App\Models\ReferralApply;
 use App\Models\ReferralLog;
+use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 
 class AffiliateController extends Controller
@@ -15,7 +16,7 @@ class AffiliateController extends Controller
         $query = ReferralApply::with('user:id,username');
         $request->whenFilled('username', function ($username) use ($query) {
             $query->whereHas('user', function ($query) use ($username) {
-                $query->where('username', 'like', "%{$username}%");
+                $query->where('username', 'like', "%$username%");
             });
         });
 
@@ -36,7 +37,7 @@ class AffiliateController extends Controller
     }
 
     // 设置提现申请状态
-    public function setStatus(Request $request, ReferralApply $aff)
+    public function setStatus(Request $request, ReferralApply $aff): JsonResponse
     {
         $status = (int) $request->input('status');
 
@@ -61,13 +62,13 @@ class AffiliateController extends Controller
 
         $request->whenFilled('invitee_username', function ($username) use ($query) {
             $query->whereHas('invitee', function ($query) use ($username) {
-                $query->where('username', 'like', "%{$username}%");
+                $query->where('username', 'like', "%$username%");
             });
         });
 
         $request->whenFilled('inviter_username', function ($username) use ($query) {
             $query->whereHas('inviter', function ($query) use ($username) {
-                $query->where('username', 'like', "%{$username}%");
+                $query->where('username', 'like', "%$username%");
             });
         });
 

+ 4 - 2
app/Http/Controllers/Admin/ArticleController.php

@@ -7,6 +7,8 @@ use App\Http\Requests\Admin\ArticleRequest;
 use App\Models\Article;
 use App\Services\ArticleService;
 use Exception;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\RedirectResponse;
 use Illuminate\Http\Request;
 use Illuminate\Http\UploadedFile;
 use Log;
@@ -86,7 +88,7 @@ class ArticleController extends Controller
         return view('admin.article.info', compact('article', 'categories'));
     }
 
-    public function update(ArticleRequest $request, Article $article)
+    public function update(ArticleRequest $request, Article $article): RedirectResponse
     { // 编辑文章
         $data = $request->validated();
         $data['logo'] = $data['logo'] ?? null;
@@ -107,7 +109,7 @@ class ArticleController extends Controller
         return redirect()->back()->withInput()->withErrors('编辑失败');
     }
 
-    public function destroy(Article $article)
+    public function destroy(Article $article): JsonResponse
     { // 删除文章
         try {
             $article->delete();

+ 9 - 11
app/Http/Controllers/Admin/CertController.php

@@ -6,11 +6,12 @@ use App\Http\Controllers\Controller;
 use App\Http\Requests\Admin\CertRequest;
 use App\Models\NodeCertificate;
 use Exception;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\RedirectResponse;
 use Log;
 
 class CertController extends Controller
 {
-    // 域名证书列表
     public function index()
     {
         $certs = NodeCertificate::orderBy('id')->paginate()->appends(request('page'));
@@ -28,12 +29,6 @@ class CertController extends Controller
         return view('admin.node.cert.index', ['certs' => $certs]);
     }
 
-    public function create()
-    {
-        return view('admin.node.cert.info');
-    }
-
-    // 添加域名证书
     public function store(CertRequest $request)
     {
         if ($cert = NodeCertificate::create($request->validated())) {
@@ -43,13 +38,17 @@ class CertController extends Controller
         return redirect()->back()->withInput()->withErrors('生成失败');
     }
 
-    // 编辑域名证书
+    public function create()
+    {
+        return view('admin.node.cert.info');
+    }
+
     public function edit(NodeCertificate $cert)
     {
         return view('admin.node.cert.info', compact('cert'));
     }
 
-    public function update(CertRequest $request, NodeCertificate $cert)
+    public function update(CertRequest $request, NodeCertificate $cert): RedirectResponse
     {
         if ($cert->update($request->validated())) {
             return redirect()->back()->with('successMsg', trans('common.update_action', ['action' => trans('common.success')]));
@@ -58,8 +57,7 @@ class CertController extends Controller
         return redirect()->back()->withInput()->withErrors(trans('common.update_action', ['action' => trans('common.failed')]));
     }
 
-    // 删除域名证书
-    public function destroy(NodeCertificate $cert)
+    public function destroy(NodeCertificate $cert): JsonResponse
     {
         try {
             if ($cert->delete()) {

+ 4 - 3
app/Http/Controllers/Admin/Config/CategoryController.php

@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin\Config;
 use App\Http\Controllers\Controller;
 use App\Models\GoodsCategory;
 use Exception;
+use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Log;
 use Response;
@@ -13,7 +14,7 @@ use Validator;
 class CategoryController extends Controller
 {
     // 添加等级
-    public function store(Request $request)
+    public function store(Request $request): JsonResponse
     {
         $validator = Validator::make($request->all(), ['name' => 'required']);
 
@@ -29,7 +30,7 @@ class CategoryController extends Controller
     }
 
     // 编辑等级
-    public function update(Request $request, GoodsCategory $category)
+    public function update(Request $request, GoodsCategory $category): JsonResponse
     {
         $validator = Validator::make($request->all(), [
             'name' => 'required',
@@ -47,7 +48,7 @@ class CategoryController extends Controller
     }
 
     // 删除等级
-    public function destroy(GoodsCategory $category)
+    public function destroy(GoodsCategory $category): JsonResponse
     {
         // 校验该等级下是否存在关联账号
         if ($category->goods()->exists()) {

+ 4 - 3
app/Http/Controllers/Admin/Config/CountryController.php

@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin\Config;
 use App\Http\Controllers\Controller;
 use App\Models\Country;
 use Exception;
+use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Log;
 use Response;
@@ -13,7 +14,7 @@ use Validator;
 class CountryController extends Controller
 {
     // 添加国家/地区
-    public function store(Request $request)
+    public function store(Request $request): JsonResponse
     {
         $validator = Validator::make($request->all(), [
             'code' => 'required|string|unique:country,code',
@@ -32,7 +33,7 @@ class CountryController extends Controller
     }
 
     // 编辑国家/地区
-    public function update(Request $request, Country $country)
+    public function update(Request $request, Country $country): JsonResponse
     {
         $validator = Validator::make($request->all(), ['name' => 'required']);
 
@@ -54,7 +55,7 @@ class CountryController extends Controller
     }
 
     // 删除国家/地区
-    public function destroy(Country $country)
+    public function destroy(Country $country): JsonResponse
     {
         // 校验该国家/地区下是否存在关联节点
         if ($country->nodes()->exists()) {

+ 3 - 2
app/Http/Controllers/Admin/Config/EmailFilterController.php

@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin\Config;
 use App\Http\Controllers\Controller;
 use App\Models\EmailFilter;
 use Exception;
+use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Log;
 use Response;
@@ -19,7 +20,7 @@ class EmailFilterController extends Controller
     }
 
     // 添加邮箱后缀
-    public function store(Request $request)
+    public function store(Request $request): JsonResponse
     {
         $validator = Validator::make($request->all(), [
             'type' => 'required|numeric|between:1,2',
@@ -44,7 +45,7 @@ class EmailFilterController extends Controller
     }
 
     // 删除邮箱后缀
-    public function destroy(EmailFilter $filter)
+    public function destroy(EmailFilter $filter): JsonResponse
     {
         try {
             if ($filter->delete()) {

+ 4 - 3
app/Http/Controllers/Admin/Config/LabelController.php

@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin\Config;
 use App\Http\Controllers\Controller;
 use App\Models\Label;
 use Exception;
+use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Log;
 use Response;
@@ -13,7 +14,7 @@ use Validator;
 class LabelController extends Controller
 {
     // 添加标签
-    public function store(Request $request)
+    public function store(Request $request): JsonResponse
     {
         $validator = Validator::make($request->all(), [
             'name' => 'required|string|unique:label,name',
@@ -32,7 +33,7 @@ class LabelController extends Controller
     }
 
     // 编辑标签
-    public function update(Request $request, Label $label)
+    public function update(Request $request, Label $label): JsonResponse
     {
         $validator = Validator::make($request->all(), [
             'name' => 'required|string|unique:label,name,'.$label->id,
@@ -51,7 +52,7 @@ class LabelController extends Controller
     }
 
     // 删除标签
-    public function destroy(Label $label)
+    public function destroy(Label $label): ?JsonResponse
     {
         try {
             $label->delete();

+ 4 - 3
app/Http/Controllers/Admin/Config/LevelController.php

@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin\Config;
 use App\Http\Controllers\Controller;
 use App\Models\Level;
 use Exception;
+use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Log;
 use Response;
@@ -13,7 +14,7 @@ use Validator;
 class LevelController extends Controller
 {
     // 添加等级
-    public function store(Request $request)
+    public function store(Request $request): JsonResponse
     {
         $validator = Validator::make($request->all(), [
             'level' => 'required|numeric|unique:level,level',
@@ -32,7 +33,7 @@ class LevelController extends Controller
     }
 
     // 编辑等级
-    public function update(Request $request, Level $level)
+    public function update(Request $request, Level $level): JsonResponse
     {
         $validator = Validator::make($request->all(), [
             'level' => 'required|numeric|unique:level,level,'.$level->id,
@@ -51,7 +52,7 @@ class LevelController extends Controller
     }
 
     // 删除等级
-    public function destroy(Level $level)
+    public function destroy(Level $level): JsonResponse
     {
         // 校验该等级下是否存在关联账号
         if ($level->users()->exists()) {

+ 4 - 3
app/Http/Controllers/Admin/Config/SsConfigController.php

@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin\Config;
 use App\Http\Controllers\Controller;
 use App\Models\SsConfig;
 use Exception;
+use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Log;
 use Response;
@@ -13,7 +14,7 @@ use Validator;
 class SsConfigController extends Controller
 {
     // 添加SS配置
-    public function store(Request $request)
+    public function store(Request $request): JsonResponse
     {
         $validator = Validator::make($request->all(), [
             'name' => 'required|string|unique:ss_config,name',
@@ -32,7 +33,7 @@ class SsConfigController extends Controller
     }
 
     // 设置SS默认配置
-    public function update(SsConfig $ss)
+    public function update(SsConfig $ss): JsonResponse
     {
         // 去除该配置所属类型的默认值
         SsConfig::default()->type($ss->type)->update(['is_default' => 0]);
@@ -44,7 +45,7 @@ class SsConfigController extends Controller
     }
 
     // 删除SS配置
-    public function destroy(SsConfig $ss)
+    public function destroy(SsConfig $ss): JsonResponse
     {
         try {
             if ($ss->delete()) {

+ 23 - 44
app/Http/Controllers/Admin/CouponController.php

@@ -7,12 +7,14 @@ use App\Http\Requests\Admin\CouponRequest;
 use App\Models\Coupon;
 use App\Models\Level;
 use App\Models\UserGroup;
+use App\Utils\Helpers;
 use Exception;
 use Illuminate\Http\JsonResponse;
+use Illuminate\Http\RedirectResponse;
 use Illuminate\Http\Request;
 use Log;
+use PhpOffice\PhpSpreadsheet\IOFactory;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
-use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
 use Redirect;
 use Response;
 use Str;
@@ -25,7 +27,7 @@ class CouponController extends Controller
         $query = Coupon::query();
 
         $request->whenFilled('sn', function ($sn) use ($query) {
-            $query->where('sn', 'like', "%{$sn}%");
+            $query->where('sn', 'like', "%$sn%");
         });
 
         foreach (['type', 'status'] as $field) {
@@ -48,7 +50,7 @@ class CouponController extends Controller
     }
 
     // 添加优惠券
-    public function store(CouponRequest $request)
+    public function store(CouponRequest $request): ?RedirectResponse
     {
         // 优惠卷LOGO
         $logo = null;
@@ -127,59 +129,36 @@ class CouponController extends Controller
     // 导出卡券
     public function exportCoupon(): void
     {
-        $voucherList = Coupon::type(1)->whereStatus(0)->get();
-        $discountCouponList = Coupon::type(2)->whereStatus(0)->get();
-        $refillList = Coupon::type(3)->whereStatus(0)->get();
+        $couponList = Coupon::whereStatus(0)->get();
 
         try {
-            $filename = '卡券'.date('Ymd').'.xlsx';
+            $filename = '卡券_Coupon_'.date('Ymd').'.xlsx';
             $spreadsheet = new Spreadsheet();
             $spreadsheet->getProperties()
                 ->setCreator('ProxyPanel')
                 ->setLastModifiedBy('ProxyPanel')
-                ->setTitle('邀请码')
-                ->setSubject('邀请码');
+                ->setTitle('卡券')
+                ->setSubject('卡券');
 
-            // 抵用券
-            $spreadsheet->setActiveSheetIndex(0);
             $sheet = $spreadsheet->getActiveSheet();
-            $sheet->setTitle('抵用券');
-            $sheet->fromArray(['名称', '使用次数', '有效期', '券码', '金额('.array_column(config('common.currency'), 'symbol', 'code')[sysConfig('standard_currency')].')', '权重', '使用限制']);
-            foreach ($voucherList as $k => $vo) {
-                $dateRange = $vo->start_time.' ~ '.$vo->end_time;
-                $sheet->fromArray([$vo->name, $vo->usable_times ?? trans('common.unlimited'), $dateRange, $vo->sn, $vo->value, $vo->priority, json_encode($vo->limit)], null, 'A'.($k + 2));
+            $sheet->setTitle('卡券');
+            $sheet->fromArray([
+                trans('model.common.type'), trans('model.coupon.name'), trans('model.coupon.usable_times'), trans('common.available_date'), trans('common.expired_at'), trans('model.coupon.sn'), trans('admin.coupon.discount'),
+                trans('model.coupon.priority'), trans('model.rule.attribute'),
+            ]);
+
+            foreach ($couponList as $index => $coupon) {
+                $sheet->fromArray([
+                    [trans('common.status.unknown'), trans('admin.coupon.type.voucher'), trans('admin.coupon.type.discount'), trans('admin.coupon.type.charge')][$coupon->type], $coupon->name,
+                    $coupon->type === 3 ? trans('admin.coupon.single_use') : ($coupon->usable_times ?? trans('common.unlimited')), $coupon->start_time, $coupon->end_time, $coupon->sn,
+                    $coupon->type === 2 ? $coupon->value : Helpers::getPriceTag($coupon->value), $coupon->priority, json_encode($coupon->limit),
+                ], null, 'A'.($index + 2));
             }
 
-            // 折扣券
-            $spreadsheet->createSheet(1);
-            $spreadsheet->setActiveSheetIndex(1);
-            $sheet = $spreadsheet->getActiveSheet();
-            $sheet->setTitle('折扣券');
-            $sheet->fromArray(['名称', '使用次数', '有效期', '券码', '折扣(折)', '权重', '使用限制']);
-            foreach ($discountCouponList as $k => $vo) {
-                $dateRange = $vo->start_time.' ~ '.$vo->end_time;
-                $sheet->fromArray([$vo->name, $vo->usable_times ?? trans('common.unlimited'), $dateRange, $vo->sn, $vo->value, $vo->priority, json_encode($vo->limit)], null, 'A'.($k + 2));
-            }
-
-            // 充值券
-            $spreadsheet->createSheet(2);
-            $spreadsheet->setActiveSheetIndex(2);
-            $sheet = $spreadsheet->getActiveSheet();
-            $sheet->setTitle('充值券');
-            $sheet->fromArray(['名称', '有效期', '券码', '金额('.array_column(config('common.currency'), 'symbol', 'code')[sysConfig('standard_currency')].')']);
-            foreach ($refillList as $k => $vo) {
-                $dateRange = $vo->start_time.' ~ '.$vo->end_time;
-                $sheet->fromArray([$vo->name, $dateRange, $vo->sn, $vo->value], null, 'A'.($k + 2));
-            }
-
-            // 指针切换回第一个sheet
-            $spreadsheet->setActiveSheetIndex(0);
-
-            header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); // 输出07Excel文件
-            //header('Content-Type:application/vnd.ms-excel'); // 输出Excel03版本文件
+            header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
             header('Content-Disposition: attachment;filename="'.$filename.'"');
             header('Cache-Control: max-age=0');
-            $writer = new Xlsx($spreadsheet);
+            $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
             $writer->save('php://output');
         } catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
             Log::error('导出优惠券时报错:'.$e->getMessage());

+ 17 - 16
app/Http/Controllers/Admin/LogsController.php

@@ -2,7 +2,6 @@
 
 namespace App\Http\Controllers\Admin;
 
-use App\Components\IP;
 use App\Helpers\DataChart;
 use App\Http\Controllers\Controller;
 use App\Models\Node;
@@ -15,6 +14,8 @@ use App\Models\UserBanedLog;
 use App\Models\UserCreditLog;
 use App\Models\UserDataFlowLog;
 use App\Models\UserDataModifyLog;
+use App\Utils\IP;
+use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Response;
 
@@ -28,12 +29,12 @@ class LogsController extends Controller
 
         $request->whenFilled('username', function ($username) use ($query) {
             $query->whereHas('user', function ($query) use ($username) {
-                $query->where('username', 'like', "%{$username}%");
+                $query->where('username', 'like', "%$username%");
             });
         });
 
         $request->whenFilled('sn', function ($value) use ($query) {
-            $query->where('sn', 'like', "%{$value}%");
+            $query->where('sn', 'like', "%$value%");
         });
 
         $request->whenFilled('start', function ($value) use ($query) {
@@ -65,7 +66,7 @@ class LogsController extends Controller
         return view('admin.logs.order', ['orders' => $query->sortable(['id' => 'desc'])->paginate(15)->appends($request->except('page'))]);
     }
 
-    public function changeOrderStatus(Request $request)
+    public function changeOrderStatus(Request $request): JsonResponse
     {
         $order = Order::findOrFail($request->input('oid'));
         $status = (int) $request->input('status');
@@ -100,7 +101,7 @@ class LogsController extends Controller
 
         $request->whenFilled('username', function ($username) use ($query) {
             $query->whereHas('user', function ($query) use ($username) {
-                $query->where('username', 'like', "%{$username}%");
+                $query->where('username', 'like', "%$username%");
             });
         });
 
@@ -112,11 +113,11 @@ class LogsController extends Controller
             $query->where('log_time', '<=', strtotime($value) + 86399);
         });
 
-        $totalTraffic = flowAutoShow($query->sum('u') + $query->sum('d')); // 在分页前,计算总使用流量
+        $totalTraffic = formatBytes($query->sum('u') + $query->sum('d')); // 在分页前,计算总使用流量
         $dataFlowLogs = $query->latest('log_time')->paginate(20)->appends($request->except('page'));
         foreach ($dataFlowLogs as $log) {
-            $log->u = flowAutoShow($log->u);
-            $log->d = flowAutoShow($log->d);
+            $log->u = formatBytes($log->u);
+            $log->d = formatBytes($log->d);
             $log->log_time = date('Y-m-d H:i:s', $log->log_time);
         }
         $nodes = Node::whereStatus(1)->orderByDesc('sort')->latest()->get();
@@ -130,7 +131,7 @@ class LogsController extends Controller
         $query = NotificationLog::query();
 
         $request->whenFilled('username', function ($username) use ($query) {
-            $query->where('address', 'like', "%{$username}%");
+            $query->where('address', 'like', "%$username%");
         });
 
         $request->whenFilled('type', function ($type) use ($query) {
@@ -157,7 +158,7 @@ class LogsController extends Controller
 
         $request->whenFilled('username', function ($username) use ($query) {
             $query->whereHas('user', function ($query) use ($username) {
-                $query->where('username', 'like', "%{$username}%");
+                $query->where('username', 'like', "%$username%");
             });
         });
 
@@ -173,7 +174,7 @@ class LogsController extends Controller
             });
         });
 
-        $onlineIPLogs = $query->groupBy('user_id', 'node_id')->latest()->paginate(20)->appends($request->except('page'));
+        $onlineIPLogs = $query->groupBy(['user_id', 'node_id'])->latest()->paginate(20)->appends($request->except('page'));
         foreach ($onlineIPLogs as $log) {
             // 跳过上报多IP的
             if ($log->ip === null || str_contains($log->ip, ',')) {
@@ -181,7 +182,7 @@ class LogsController extends Controller
             }
             $ipInfo = IP::getIPInfo($log->ip);
 
-            $log->ipInfo = implode(' ', $ipInfo);
+            $log->ipInfo = $ipInfo['address'].' '.$ipInfo['isp'];
         }
 
         return view('admin.logs.onlineIPMonitor', [
@@ -197,7 +198,7 @@ class LogsController extends Controller
 
         $request->whenFilled('username', function ($username) use ($query) {
             $query->whereHas('user', function ($query) use ($username) {
-                $query->where('username', 'like', "%{$username}%");
+                $query->where('username', 'like', "%$username%");
             });
         });
 
@@ -211,7 +212,7 @@ class LogsController extends Controller
 
         $request->whenFilled('username', function ($username) use ($query) {
             $query->whereHas('user', function ($query) use ($username) {
-                $query->where('username', 'like', "%{$username}%");
+                $query->where('username', 'like', "%$username%");
             });
         });
 
@@ -225,7 +226,7 @@ class LogsController extends Controller
 
         $request->whenFilled('username', function ($username) use ($query) {
             $query->whereHas('user', function ($query) use ($username) {
-                $query->where('username', 'like', "%{$username}%");
+                $query->where('username', 'like', "%$username%");
             });
         });
 
@@ -239,7 +240,7 @@ class LogsController extends Controller
 
         foreach (['username', 'wechat', 'qq'] as $field) {
             $request->whenFilled($field, function ($value) use ($query, $field) {
-                $query->where($field, 'like', "%{$value}%");
+                $query->where($field, 'like', "%$value%");
             });
         }
 

+ 2 - 1
app/Http/Controllers/Admin/MarketingController.php

@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Admin;
 
 use App\Http\Controllers\Controller;
 use App\Models\Marketing;
+use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Response;
 
@@ -34,7 +35,7 @@ class MarketingController extends Controller
     }
 
     // 添加推送消息
-    public function addPushMarketing(Request $request)
+    public function addPushMarketing(Request $request): JsonResponse
     {
         $title = $request->input('title');
         $content = $request->input('content');

+ 4 - 3
app/Http/Controllers/Admin/NodeAuthController.php

@@ -6,6 +6,7 @@ use App\Http\Controllers\Controller;
 use App\Models\Node;
 use App\Models\NodeAuth;
 use Exception;
+use Illuminate\Http\JsonResponse;
 use Response;
 use Str;
 
@@ -18,7 +19,7 @@ class NodeAuthController extends Controller
     }
 
     // 添加节点授权
-    public function store()
+    public function store(): JsonResponse
     {
         $nodes = Node::whereStatus(1)->doesntHave('auth')->orderBy('id')->get();
 
@@ -33,7 +34,7 @@ class NodeAuthController extends Controller
     }
 
     // 重置节点授权
-    public function update(NodeAuth $auth)
+    public function update(NodeAuth $auth): JsonResponse
     {
         if ($auth->update(['key' => Str::random(), 'secret' => Str::random(8)])) {
             return Response::json(['status' => 'success', 'message' => '操作成功']);
@@ -43,7 +44,7 @@ class NodeAuthController extends Controller
     }
 
     // 删除节点授权
-    public function destroy(NodeAuth $auth)
+    public function destroy(NodeAuth $auth): JsonResponse
     {
         try {
             $auth->delete();

+ 72 - 72
app/Http/Controllers/Admin/NodeController.php

@@ -2,7 +2,6 @@
 
 namespace App\Http\Controllers\Admin;
 
-use App\Components\NetworkDetection;
 use App\Helpers\DataChart;
 use App\Http\Controllers\Controller;
 use App\Http\Requests\Admin\NodeRequest;
@@ -13,8 +12,10 @@ use App\Models\Level;
 use App\Models\Node;
 use App\Models\NodeCertificate;
 use App\Models\RuleGroup;
+use App\Utils\NetworkDetection;
 use Exception;
 use Illuminate\Http\JsonResponse;
+use Illuminate\Http\RedirectResponse;
 use Illuminate\Http\Request;
 use Log;
 use Response;
@@ -37,11 +38,11 @@ class NodeController extends Controller
         foreach ($nodeList as $node) {
             $online_log = $node->onlineLogs->where('log_time', '>=', strtotime('-5 minutes'))->sortBy('log_time')->first(); // 在线人数
             $node->online_users = $online_log->online_user ?? 0;
-            $node->transfer = flowAutoShow($node->dailyDataFlows->sum('total')); // 已产生流量
+            $node->transfer = formatBytes($node->dailyDataFlows->sum('total')); // 已产生流量
             $node_info = $node->heartbeats->where('log_time', '>=', strtotime(config('tasks.recently_heartbeat')))->sortBy('log_time')->first(); // 近期负载
-            $node->isOnline = ! empty($node_info) && ! empty($node_info->load);
+            $node->isOnline = $node_info !== null && ! empty($node_info->load);
             $node->load = $node_info->load ?? false;
-            $node->uptime = empty($node_info) ? 0 : seconds2time($node_info->uptime);
+            $node->uptime = $node_info === null ? 0 : formatTime($node_info->uptime);
         }
 
         return view('admin.node.index', ['nodeList' => $nodeList]);
@@ -79,7 +80,69 @@ class NodeController extends Controller
         ]);
     }
 
-    public function clone(Node $node)
+    private function nodeStore(array $info): array
+    { // 添加节点信息
+        switch ($info['type']) {
+            case 0:
+                $profile = ['method' => $info['method']];
+                break;
+            case 2:
+                $profile = [
+                    'method' => $info['v2_method'],
+                    'v2_alter_id' => $info['v2_alter_id'],
+                    'v2_net' => $info['v2_net'],
+                    'v2_type' => $info['v2_type'],
+                    'v2_host' => $info['v2_host'],
+                    'v2_path' => $info['v2_path'],
+                    'v2_tls' => $info['v2_tls'] ? 'tls' : '',
+                    'v2_sni' => $info['v2_sni'],
+                ];
+                break;
+            case 3:
+                $profile = [
+                    'allow_insecure' => false,
+                ];
+                break;
+            case 1:
+            case 4:
+                $profile = [
+                    'method' => $info['method'],
+                    'protocol' => $info['protocol'],
+                    'obfs' => $info['obfs'],
+                    'obfs_param' => $info['obfs_param'],
+                    'protocol_param' => $info['protocol_param'],
+                    'passwd' => $info['passwd'],
+                ];
+                break;
+        }
+
+        return [
+            'type' => $info['type'],
+            'name' => $info['name'],
+            'country_code' => $info['country_code'],
+            'server' => $info['server'],
+            'ip' => $info['ip'],
+            'ipv6' => $info['ipv6'],
+            'level' => $info['level'],
+            'rule_group_id' => $info['rule_group_id'],
+            'speed_limit' => $info['speed_limit'],
+            'client_limit' => $info['client_limit'],
+            'description' => $info['description'],
+            'profile' => $profile ?? [],
+            'traffic_rate' => $info['traffic_rate'],
+            'is_udp' => $info['is_udp'],
+            'is_display' => $info['is_display'],
+            'is_ddns' => $info['is_ddns'],
+            'relay_node_id' => $info['relay_node_id'],
+            'port' => $info['port'] ?? 0,
+            'push_port' => $info['push_port'],
+            'detection_type' => $info['detection_type'],
+            'sort' => $info['sort'],
+            'status' => $info['status'],
+        ];
+    }
+
+    public function clone(Node $node): RedirectResponse
     { // 克隆节点
         $new = $node->replicate()->fill([
             'name' => $node->name.'_克隆',
@@ -139,9 +202,8 @@ class NodeController extends Controller
     public function checkNode(Node $node): JsonResponse
     { // 节点IP阻断检测
         foreach ($node->ips() as $ip) {
-            $icmp = (new NetworkDetection)->networkCheck($ip, true, $node->port ?? 22); // ICMP
-            $tcp = (new NetworkDetection)->networkCheck($ip, false, $node->port ?? 22); // TCP
-            $data[$ip] = [$icmp ? config('common.network_status')[$icmp] : ' ', $tcp ? config('common.network_status')[$tcp] : ' '];
+            $status = (new NetworkDetection)->networkStatus($ip, $node->port ?? 22);
+            $data[$ip] = [config('common.network_status')[$status['icmp']], config('common.network_status')[$status['tcp']]];
         }
 
         return Response::json(['status' => 'success', 'title' => '['.$node->name.']阻断信息', 'message' => $data ?? []]);
@@ -173,10 +235,10 @@ class NodeController extends Controller
         $ret = false;
         if ($id) {
             $node = Node::findOrFail($id);
-            $ret = reloadNode::dispatchNow($node);
+            $ret = reloadNode::dispatchSync($node);
         } else {
             foreach (Node::whereStatus(1)->whereType(4)->get() as $node) {
-                $result = reloadNode::dispatchNow($node);
+                $result = reloadNode::dispatchSync($node);
                 if ($result && ! $ret) {
                     $ret = true;
                 }
@@ -216,66 +278,4 @@ class NodeController extends Controller
 
         return Response::json(['status' => 'fail', 'message' => 'Ping访问失败']);
     }
-
-    private function nodeStore(array $info): array
-    { // 添加节点信息
-        switch ($info['type']) {
-            case 0:
-                $profile = ['method' => $info['method']];
-                break;
-            case 2:
-                $profile = [
-                    'method' => $info['v2_method'],
-                    'v2_alter_id' => $info['v2_alter_id'],
-                    'v2_net' => $info['v2_net'],
-                    'v2_type' => $info['v2_type'],
-                    'v2_host' => $info['v2_host'],
-                    'v2_path' => $info['v2_path'],
-                    'v2_tls' => $info['v2_tls'] ? 'tls' : '',
-                    'v2_sni' => $info['v2_sni'],
-                ];
-                break;
-            case 3:
-                $profile = [
-                    'allow_insecure' => false,
-                ];
-                break;
-            case 1:
-            case 4:
-                $profile = [
-                    'method' => $info['method'],
-                    'protocol' => $info['protocol'],
-                    'obfs' => $info['obfs'],
-                    'obfs_param' => $info['obfs_param'],
-                    'protocol_param' => $info['protocol_param'],
-                    'passwd' => $info['passwd'],
-                ];
-                break;
-        }
-
-        return [
-            'type' => $info['type'],
-            'name' => $info['name'],
-            'country_code' => $info['country_code'],
-            'server' => $info['server'],
-            'ip' => $info['ip'],
-            'ipv6' => $info['ipv6'],
-            'level' => $info['level'],
-            'rule_group_id' => $info['rule_group_id'],
-            'speed_limit' => $info['speed_limit'],
-            'client_limit' => $info['client_limit'],
-            'description' => $info['description'],
-            'profile' => $profile ?? [],
-            'traffic_rate' => $info['traffic_rate'],
-            'is_udp' => $info['is_udp'],
-            'is_display' => $info['is_display'],
-            'is_ddns' => $info['is_ddns'],
-            'relay_node_id' => $info['relay_node_id'],
-            'port' => $info['port'] ?? 0,
-            'push_port' => $info['push_port'],
-            'detection_type' => $info['detection_type'],
-            'sort' => $info['sort'],
-            'status' => $info['status'],
-        ];
-    }
 }

+ 11 - 9
app/Http/Controllers/Admin/PermissionController.php

@@ -5,6 +5,8 @@ namespace App\Http\Controllers\Admin;
 use App\Http\Controllers\Controller;
 use App\Http\Requests\Admin\PermissionRequest;
 use Exception;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\RedirectResponse;
 use Illuminate\Http\Request;
 use Spatie\Permission\Models\Permission;
 
@@ -16,19 +18,14 @@ class PermissionController extends Controller
 
         foreach (['name', 'description'] as $field) {
             $request->whenFilled($field, function ($value) use ($query, $field) {
-                $query->where($field, 'like', "%{$value}%");
+                $query->where($field, 'like', "%$value%");
             });
         }
 
         return view('admin.permission.index', ['permissions' => $query->paginate(20)->appends($request->except('page'))]);
     }
 
-    public function create()
-    {
-        return view('admin.permission.info');
-    }
-
-    public function store(PermissionRequest $request)
+    public function store(PermissionRequest $request): RedirectResponse
     {
         if ($permission = Permission::create($request->validated())) {
             return redirect()->route('admin.permission.edit', $permission)->with('successMsg', '操作成功');
@@ -37,12 +34,17 @@ class PermissionController extends Controller
         return redirect()->back()->withInput()->withErrors('操作失败');
     }
 
+    public function create()
+    {
+        return view('admin.permission.info');
+    }
+
     public function edit(Permission $permission)
     {
         return view('admin.permission.info', compact('permission'));
     }
 
-    public function update(PermissionRequest $request, Permission $permission)
+    public function update(PermissionRequest $request, Permission $permission): RedirectResponse
     {
         if ($permission->update($request->validated())) {
             return redirect()->back()->with('successMsg', '操作成功');
@@ -51,7 +53,7 @@ class PermissionController extends Controller
         return redirect()->back()->withInput()->withErrors('操作失败');
     }
 
-    public function destroy(Permission $permission)
+    public function destroy(Permission $permission): JsonResponse
     {
         try {
             $permission->delete();

+ 2 - 2
app/Http/Controllers/Admin/ReportController.php

@@ -81,7 +81,7 @@ class ReportController extends Controller
             $data['hourlyFlow'] = $user->hourlyDataFlows()->whereNotNull('node_id')
                 ->where('created_at', '>=', date('Y-m-d H:i:s', strtotime('-1 days')))
                 ->selectRaw('node_id, (DATE_FORMAT(user_hourly_data_flow.created_at, "%k")) as date, total')
-                ->get()->transform(function ($item, $key) {
+                ->get()->transform(function ($item) {
                     return [
                         'node_id' => $item->node_id,
                         'date' => (int) $item->date,
@@ -95,7 +95,7 @@ class ReportController extends Controller
                 ->whereMonth('created_at', date('n'))
                 ->where('total', '>', 6000000)
                 ->selectRaw('node_id, (DATE_FORMAT(user_daily_data_flow.created_at, "%e")) as date, total')
-                ->get()->transform(function ($item, $key) {
+                ->get()->transform(function ($item) {
                     return [
                         'node_id' => $item->node_id,
                         'date' => (int) $item->date,

+ 10 - 8
app/Http/Controllers/Admin/RoleController.php

@@ -5,6 +5,8 @@ namespace App\Http\Controllers\Admin;
 use App\Http\Controllers\Controller;
 use App\Http\Requests\Admin\RoleRequest;
 use Exception;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\RedirectResponse;
 use Spatie\Permission\Models\Permission;
 use Spatie\Permission\Models\Role;
 
@@ -15,12 +17,7 @@ class RoleController extends Controller
         return view('admin.role.index', ['roles' => Role::with('permissions')->paginate(15)]);
     }
 
-    public function create()
-    {
-        return view('admin.role.info', ['permissions' => Permission::all()->pluck('description', 'name')]);
-    }
-
-    public function store(RoleRequest $request)
+    public function store(RoleRequest $request): RedirectResponse
     {
         if ($role = Role::create($request->only(['name', 'description']))) {
             $role->givePermissionTo($request->input('permissions') ?? []);
@@ -31,6 +28,11 @@ class RoleController extends Controller
         return redirect()->back()->withInput()->withErrors('操作失败');
     }
 
+    public function create()
+    {
+        return view('admin.role.info', ['permissions' => Permission::all()->pluck('description', 'name')]);
+    }
+
     public function edit(Role $role)
     {
         return view('admin.role.info', [
@@ -39,7 +41,7 @@ class RoleController extends Controller
         ]);
     }
 
-    public function update(RoleRequest $request, Role $role)
+    public function update(RoleRequest $request, Role $role): RedirectResponse
     {
         if ($role->name === 'Super Admin') {
             return redirect()->back()->withInput()->withErrors('请勿修改超级管理员');
@@ -54,7 +56,7 @@ class RoleController extends Controller
         return redirect()->back()->withInput()->withErrors('操作失败');
     }
 
-    public function destroy(Role $role)
+    public function destroy(Role $role): JsonResponse
     {
         try {
             if ($role->name === 'Super Admin') {

+ 1 - 1
app/Http/Controllers/Admin/RuleController.php

@@ -71,7 +71,7 @@ class RuleController extends Controller
 
         $request->whenFilled('username', function ($username) use ($query) {
             $query->whereHas('user', function ($query) use ($username) {
-                $query->where('username', 'like', "%{$username}%");
+                $query->where('username', 'like', "%$username%");
             });
         });
 

+ 9 - 13
app/Http/Controllers/Admin/RuleGroupController.php

@@ -7,22 +7,16 @@ use App\Http\Requests\Admin\RuleGroupRequest;
 use App\Models\Rule;
 use App\Models\RuleGroup;
 use Exception;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\RedirectResponse;
 
 class RuleGroupController extends Controller
 {
-    // 审计规则分组列表
     public function index()
     {
         return view('admin.rule.group.index', ['ruleGroups' => RuleGroup::paginate(15)->appends(request('page'))]);
     }
 
-    // 添加审计规则分组页面
-    public function create()
-    {
-        return view('admin.rule.group.info', ['rules' => Rule::all()]);
-    }
-
-    // 添加审计规则分组
     public function store(RuleGroupRequest $request)
     {
         if ($group = RuleGroup::create($request->only('name', 'type'))) {
@@ -34,7 +28,11 @@ class RuleGroupController extends Controller
         return redirect()->back()->withInput()->withErrors('操作失败');
     }
 
-    // 编辑审计规则分组页面
+    public function create()
+    {
+        return view('admin.rule.group.info', ['rules' => Rule::all()]);
+    }
+
     public function edit(RuleGroup $group)
     {
         return view('admin.rule.group.info', [
@@ -43,8 +41,7 @@ class RuleGroupController extends Controller
         ]);
     }
 
-    // 编辑审计规则分组
-    public function update(RuleGroupRequest $request, RuleGroup $group)
+    public function update(RuleGroupRequest $request, RuleGroup $group): RedirectResponse
     {
         if ($group->update($request->only(['name', 'type']))) {
             $group->rules()->sync($request->input('rules'));
@@ -55,8 +52,7 @@ class RuleGroupController extends Controller
         return redirect()->back()->withInput()->withErrors('操作失败');
     }
 
-    // 删除审计规则分组
-    public function destroy(RuleGroup $group)
+    public function destroy(RuleGroup $group): JsonResponse
     {
         try {
             $group->delete();

+ 7 - 14
app/Http/Controllers/Admin/ShopController.php

@@ -21,7 +21,6 @@ use Str;
 
 class ShopController extends Controller
 {
-    // 商品列表
     public function index(Request $request)
     {
         $query = Goods::query();
@@ -42,13 +41,6 @@ class ShopController extends Controller
         return view('admin.shop.index', ['goodsList' => $goodsList]);
     }
 
-    // 添加商品页面
-    public function create()
-    {
-        return view('admin.shop.info', ['levels' => Level::orderBy('level')->get(), 'categories' => GoodsCategory::all()]);
-    }
-
-    // 添加商品
     public function store(ShopStoreRequest $request): RedirectResponse
     {
         $data = $request->validated();
@@ -81,9 +73,8 @@ class ShopController extends Controller
         return Redirect::back()->withInput()->withErrors('添加商品信息失败');
     }
 
-    // 图片上传
     public function fileUpload(UploadedFile $file)
-    {
+    { // 图片上传
         $fileName = Str::random(8).time().'.'.$file->getClientOriginalExtension();
         if (! $file->storeAs('public', $fileName)) {
             return Redirect::back()->withInput()->withErrors('Logo存储失败');
@@ -92,7 +83,11 @@ class ShopController extends Controller
         return 'upload/'.$fileName;
     }
 
-    // 编辑商品页面
+    public function create()
+    {
+        return view('admin.shop.info', ['levels' => Level::orderBy('level')->get(), 'categories' => GoodsCategory::all()]);
+    }
+
     public function edit(Goods $good)
     {
         return view('admin.shop.info', [
@@ -102,8 +97,7 @@ class ShopController extends Controller
         ]);
     }
 
-    // 编辑商品
-    public function update(ShopUpdateRequest $request, Goods $good)
+    public function update(ShopUpdateRequest $request, Goods $good): RedirectResponse
     {
         $data = $request->validated();
 
@@ -132,7 +126,6 @@ class ShopController extends Controller
         return Redirect::back()->withInput()->withErrors('编辑失败');
     }
 
-    // 删除商品
     public function destroy(Goods $good): JsonResponse
     {
         try {

+ 6 - 5
app/Http/Controllers/Admin/SubscribeController.php

@@ -2,10 +2,11 @@
 
 namespace App\Http\Controllers\Admin;
 
-use App\Components\IP;
 use App\Http\Controllers\Controller;
 use App\Models\UserSubscribe;
 use App\Models\UserSubscribeLog;
+use App\Utils\IP;
+use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Response;
 
@@ -23,7 +24,7 @@ class SubscribeController extends Controller
 
         $request->whenFilled('username', function ($username) use ($query) {
             $query->whereHas('user', function ($query) use ($username) {
-                $query->where('username', 'like', "%{$username}%");
+                $query->where('username', 'like', "%$username%");
             });
         });
 
@@ -46,7 +47,7 @@ class SubscribeController extends Controller
         });
 
         $request->whenFilled('ip', function ($value) use ($query) {
-            $query->where('request_ip', 'like', "%{$value}%");
+            $query->where('request_ip', 'like', "%$value%");
         });
 
         if ($request->filled('start')) {
@@ -57,7 +58,7 @@ class SubscribeController extends Controller
         foreach ($subscribeLogs as $log) {
             // 跳过上报多IP的
             if ($log->request_ip) {
-                $log->ipInfo = implode(' ', IP::getIPInfo($log->request_ip));
+                $log->ipInfo = IP::getIPInfo($log->request_ip)['address'] ?? null;
             }
         }
 
@@ -65,7 +66,7 @@ class SubscribeController extends Controller
     }
 
     // 设置用户的订阅的状态
-    public function setSubscribeStatus(UserSubscribe $subscribe)
+    public function setSubscribeStatus(UserSubscribe $subscribe): JsonResponse
     {
         if ($subscribe->status) {
             $subscribe->update(['status' => 0, 'ban_time' => strtotime(sysConfig('traffic_ban_time').' minutes'), 'ban_desc' => 'Your subscription has been disabled by the administrator, please contact the administrator to restore it']);

+ 46 - 46
app/Http/Controllers/Admin/SystemController.php

@@ -31,6 +31,44 @@ class SystemController extends Controller
         return view('admin.config.system', array_merge(['payments' => $this->getPayment(), 'captcha' => $this->getCaptcha()], Config::pluck('value', 'name')->toArray()));
     }
 
+    private function getPayment(): array
+    { // 获取已经完成配置的支付渠道
+        if (sysConfig('f2fpay_app_id') && sysConfig('f2fpay_private_key') && sysConfig('f2fpay_public_key')) {
+            $payment[] = 'f2fpay';
+        }
+        if (sysConfig('codepay_url') && sysConfig('codepay_id') && sysConfig('codepay_key')) {
+            $payment[] = 'codepay';
+        }
+        if (sysConfig('epay_url') && sysConfig('epay_mch_id') && sysConfig('epay_key')) {
+            $payment[] = 'epay';
+        }
+        if (sysConfig('payjs_mch_id') && sysConfig('payjs_key')) {
+            $payment[] = 'payjs';
+        }
+        if (sysConfig('bitpay_secret')) {
+            $payment[] = 'bitpayx';
+        }
+        if (sysConfig('paypal_client_id') && sysConfig('paypal_client_secret') && sysConfig('paypal_app_id')) {
+            $payment[] = 'paypal';
+        }
+        if (sysConfig('stripe_public_key') && sysConfig('stripe_secret_key')) {
+            $payment[] = 'stripe';
+        }
+        if (sysConfig('paybeaver_app_id') && sysConfig('paybeaver_app_secret')) {
+            $payment[] = 'paybeaver';
+        }
+        if (sysConfig('theadpay_mchid') && sysConfig('theadpay_key')) {
+            $payment[] = 'theadpay';
+        }
+
+        return $payment ?? [];
+    }
+
+    private function getCaptcha(): bool
+    {
+        return sysConfig('captcha_secret') && sysConfig('captcha_key');
+    }
+
     public function setExtend(Request $request): RedirectResponse  // 设置涉及到上传的设置
     {
         if ($request->hasAny(['website_home_logo', 'website_home_logo'])) { // 首页LOGO
@@ -41,8 +79,8 @@ class SystemController extends Controller
                     return redirect()->route('admin.system.index', '#other')->withErrors($validator->errors());
                 }
                 $file = $request->file('website_home_logo');
-                $ret = $file->move('uploads/logo', $file->getClientOriginalName());
-                if ($ret && Config::find('website_home_logo')->update(['value' => 'uploads/logo/'.$file->getClientOriginalName()])) {
+                $file->move('uploads/logo', $file->getClientOriginalName());
+                if (Config::find('website_home_logo')->update(['value' => 'uploads/logo/'.$file->getClientOriginalName()])) {
                     return redirect()->route('admin.system.index', '#other')->with('successMsg', '更新成功');
                 }
             }
@@ -53,8 +91,8 @@ class SystemController extends Controller
                     return redirect()->route('admin.system.index', '#other')->withErrors($validator->errors());
                 }
                 $file = $request->file('website_logo');
-                $ret = $file->move('uploads/logo', $file->getClientOriginalName());
-                if ($ret && Config::findOrFail('website_logo')->update(['value' => 'uploads/logo/'.$file->getClientOriginalName()])) {
+                $file->move('uploads/logo', $file->getClientOriginalName());
+                if (Config::findOrFail('website_logo')->update(['value' => 'uploads/logo/'.$file->getClientOriginalName()])) {
                     return redirect()->route('admin.system.index', '#other')->with('successMsg', '更新成功');
                 }
             }
@@ -70,8 +108,8 @@ class SystemController extends Controller
                     return redirect()->route('admin.system.index', '#payment')->withErrors($validator->errors());
                 }
                 $file = $request->file('alipay_qrcode');
-                $ret = $file->move('uploads/images', $file->getClientOriginalName());
-                if ($ret && Config::find('alipay_qrcode')->update(['value' => 'uploads/images/'.$file->getClientOriginalName()])) {
+                $file->move('uploads/images', $file->getClientOriginalName());
+                if (Config::find('alipay_qrcode')->update(['value' => 'uploads/images/'.$file->getClientOriginalName()])) {
                     return redirect()->route('admin.system.index', '#payment')->with('successMsg', '更新成功');
                 }
             }
@@ -83,8 +121,8 @@ class SystemController extends Controller
                     return redirect()->route('admin.system.index', '#payment')->withErrors($validator->errors());
                 }
                 $file = $request->file('wechat_qrcode');
-                $ret = $file->move('uploads/images', $file->getClientOriginalName());
-                if ($ret && Config::findOrFail('wechat_qrcode')->update(['value' => 'uploads/images/'.$file->getClientOriginalName()])) {
+                $file->move('uploads/images', $file->getClientOriginalName());
+                if (Config::findOrFail('wechat_qrcode')->update(['value' => 'uploads/images/'.$file->getClientOriginalName()])) {
                     return redirect()->route('admin.system.index', '#payment')->with('successMsg', '更新成功');
                 }
             }
@@ -188,42 +226,4 @@ class SystemController extends Controller
 
         return Response::json(['status' => 'success', 'message' => '发送成功,请查看手机是否收到推送消息']);
     }
-
-    private function getPayment() // 获取已经完成配置的支付渠道
-    {
-        if (sysConfig('f2fpay_app_id') && sysConfig('f2fpay_private_key') && sysConfig('f2fpay_public_key')) {
-            $payment[] = 'f2fpay';
-        }
-        if (sysConfig('codepay_url') && sysConfig('codepay_id') && sysConfig('codepay_key')) {
-            $payment[] = 'codepay';
-        }
-        if (sysConfig('epay_url') && sysConfig('epay_mch_id') && sysConfig('epay_key')) {
-            $payment[] = 'epay';
-        }
-        if (sysConfig('payjs_mch_id') && sysConfig('payjs_key')) {
-            $payment[] = 'payjs';
-        }
-        if (sysConfig('bitpay_secret')) {
-            $payment[] = 'bitpayx';
-        }
-        if (sysConfig('paypal_username') && sysConfig('paypal_password') && sysConfig('paypal_secret')) {
-            $payment[] = 'paypal';
-        }
-        if (sysConfig('stripe_public_key') && sysConfig('stripe_secret_key')) {
-            $payment[] = 'stripe';
-        }
-        if (sysConfig('paybeaver_app_id') && sysConfig('paybeaver_app_secret')) {
-            $payment[] = 'paybeaver';
-        }
-        if (sysConfig('theadpay_mchid') && sysConfig('theadpay_key')) {
-            $payment[] = 'theadpay';
-        }
-
-        return $payment ?? [];
-    }
-
-    private function getCaptcha()
-    {
-        return sysConfig('captcha_secret') && sysConfig('captcha_key');
-    }
 }

+ 5 - 4
app/Http/Controllers/Admin/TicketController.php

@@ -10,6 +10,7 @@ use App\Notifications\TicketClosed;
 use App\Notifications\TicketCreated;
 use App\Notifications\TicketReplied;
 use Auth;
+use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Response;
 
@@ -24,7 +25,7 @@ class TicketController extends Controller
 
         $request->whenFilled('username', function ($username) use ($query) {
             $query->whereHas('user', function ($query) use ($username) {
-                $query->where('username', 'like', "%{$username}%");
+                $query->where('username', 'like', "%$username%");
             });
         });
 
@@ -32,7 +33,7 @@ class TicketController extends Controller
     }
 
     // 创建工单
-    public function store(TicketRequest $request)
+    public function store(TicketRequest $request): JsonResponse
     {
         $data = $request->validated();
         $user = User::find($data['uid']) ?: User::whereUsername($data['username'])->first();
@@ -61,7 +62,7 @@ class TicketController extends Controller
     }
 
     // 回复工单
-    public function update(Request $request, Ticket $ticket)
+    public function update(Request $request, Ticket $ticket): JsonResponse
     {
         $content = substr(str_replace(['atob', 'eval'], '', clean($request->input('content'))), 0, 300);
 
@@ -82,7 +83,7 @@ class TicketController extends Controller
     }
 
     // 关闭工单
-    public function destroy(Ticket $ticket)
+    public function destroy(Ticket $ticket): JsonResponse
     {
         if (! $ticket->close()) {
             return Response::json(['status' => 'fail', 'message' => '关闭失败']);

+ 1 - 1
app/Http/Controllers/Admin/ToolsController.php

@@ -2,9 +2,9 @@
 
 namespace App\Http\Controllers\Admin;
 
-use App\Components\IP;
 use App\Http\Controllers\Controller;
 use App\Models\User;
+use App\Utils\IP;
 use DB;
 use Exception;
 use Illuminate\Http\Request;

+ 9 - 9
app/Http/Controllers/Admin/UserController.php

@@ -2,8 +2,6 @@
 
 namespace App\Http\Controllers\Admin;
 
-use App\Components\Helpers;
-use App\Components\IP;
 use App\Http\Controllers\Controller;
 use App\Http\Requests\Admin\UserStoreRequest;
 use App\Http\Requests\Admin\UserUpdateRequest;
@@ -16,6 +14,8 @@ use App\Models\UserGroup;
 use App\Models\UserHourlyDataFlow;
 use App\Models\UserOauth;
 use App\Services\ProxyService;
+use App\Utils\Helpers;
+use App\Utils\IP;
 use Arr;
 use Auth;
 use Exception;
@@ -41,7 +41,7 @@ class UserController extends Controller
 
         foreach (['username', 'wechat', 'qq'] as $field) {
             $request->whenFilled($field, function ($value) use ($query, $field) {
-                $query->where($field, 'like', "%{$value}%");
+                $query->where($field, 'like', "%$value%");
             });
         }
 
@@ -67,7 +67,7 @@ class UserController extends Controller
 
         // 不活跃用户
         $request->whenFilled('paying', function () use ($query) {
-            $payingUser = Order::whereStatus(2)->where('goods_id', '<>', null)->whereIsExpire(0)->where('amount', '>', 0)->pluck('user_id')->unique();
+            $payingUser = Order::whereStatus(2)->where('goods_id', '<>')->whereIsExpire(0)->where('amount', '>', 0)->pluck('user_id')->unique();
             $query->whereIn('id', $payingUser);
         });
 
@@ -153,7 +153,7 @@ class UserController extends Controller
         ]);
     }
 
-    public function destroy(User $user)
+    public function destroy(User $user): JsonResponse
     {
         if ($user->id === 1) {
             return Response::json(['status' => 'fail', 'message' => '系统管理员不可删除']);
@@ -172,7 +172,7 @@ class UserController extends Controller
         return Response::json(['status' => 'fail', 'message' => '删除失败']);
     }
 
-    public function batchAddUsers()
+    public function batchAddUsers(): ?JsonResponse
     {
         try {
             for ($i = 0; $i < (int) request('amount', 1); $i++) {
@@ -208,7 +208,7 @@ class UserController extends Controller
         return Response::json(['status' => 'success', 'message' => '流量重置成功']);
     }
 
-    public function update(UserUpdateRequest $request, User $user)
+    public function update(UserUpdateRequest $request, User $user): JsonResponse
     {
         $data = $request->validated();
         $data['passwd'] = $request->input('passwd') ?? Str::random();
@@ -289,7 +289,7 @@ class UserController extends Controller
 
     public function exportProxyConfig(Request $request, User $user): JsonResponse
     {
-        $proxyServer = ProxyService::getInstance();
+        $proxyServer = new ProxyService;
         $proxyServer->setUser($user);
         $server = $proxyServer->getProxyConfig(Node::findOrFail($request->input('id')));
 
@@ -303,7 +303,7 @@ class UserController extends Controller
         return view('admin.user.oauth', compact('list'));
     }
 
-    public function VNetInfo(User $user)
+    public function VNetInfo(User $user): JsonResponse
     {
         $nodes = $user->nodes()->whereType(4)->get(['node.id', 'node.name']);
         $nodeList = (new getUser())->existsinVNet($user);

+ 10 - 14
app/Http/Controllers/Admin/UserGroupController.php

@@ -7,6 +7,8 @@ use App\Http\Requests\Admin\UserGroupRequest;
 use App\Models\Node;
 use App\Models\UserGroup;
 use Exception;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\RedirectResponse;
 
 class UserGroupController extends Controller
 {
@@ -15,13 +17,6 @@ class UserGroupController extends Controller
         return view('admin.user.group.index', ['groups' => UserGroup::paginate(15)->appends(request('page'))]);
     }
 
-    // 添加用户分组页面
-    public function create()
-    {
-        return view('admin.user.group.info', ['nodes' => Node::whereStatus(1)->pluck('name', 'id')]);
-    }
-
-    // 添加用户分组
     public function store(UserGroupRequest $request)
     {
         if ($userGroup = UserGroup::create($request->only(['name']))) {
@@ -33,7 +28,11 @@ class UserGroupController extends Controller
         return redirect()->back()->withInput()->withErrors('操作失败');
     }
 
-    // 编辑用户分组页面
+    public function create()
+    {
+        return view('admin.user.group.info', ['nodes' => Node::whereStatus(1)->pluck('name', 'id')]);
+    }
+
     public function edit(UserGroup $group)
     {
         return view('admin.user.group.info', [
@@ -42,8 +41,7 @@ class UserGroupController extends Controller
         ]);
     }
 
-    // 编辑用户分组
-    public function update(UserGroupRequest $request, UserGroup $group)
+    public function update(UserGroupRequest $request, UserGroup $group): RedirectResponse
     {
         if ($group->update($request->only(['name']))) {
             $group->nodes()->sync($request->input('nodes'));
@@ -54,11 +52,9 @@ class UserGroupController extends Controller
         return redirect()->back()->withInput()->withErrors('操作失败');
     }
 
-    // 删除用户分组
-    public function destroy(UserGroup $group)
+    public function destroy(UserGroup $group): JsonResponse
     {
-        // 校验该分组下是否存在关联账号
-        if ($group->users->isNotEmpty()) {
+        if ($group->users->isNotEmpty()) { // 校验该分组下是否存在关联账号
             return response()->json(['status' => 'fail', 'message' => '该分组下存在关联账号,请先取消关联!']);
         }
 

+ 5 - 5
app/Http/Controllers/AdminController.php

@@ -35,7 +35,7 @@ class AdminController extends Controller
             'todayRegister' => User::whereDate('created_at', date('Y-m-d'))->count(), // 今日注册用户
             'enableUserCount' => User::whereEnable(1)->count(), // 有效用户数
             'activeUserCount' => User::where('t', '>=', $past)->count(), // 活跃用户数,
-            'payingUserCount' => Order::whereStatus(2)->where('goods_id', '<>', null)->whereIsExpire(0)->where('amount', '>', 0)->pluck('user_id')->unique()->count(), // 付费用户数
+            'payingUserCount' => Order::whereStatus(2)->where('goods_id', '<>')->whereIsExpire(0)->where('amount', '>', 0)->pluck('user_id')->unique()->count(), // 付费用户数
             'inactiveUserCount' => User::whereEnable(1)->whereBetween('t', [1, $past])->count(), // 不活跃用户数
             'onlineUserCount' => User::where('t', '>=', strtotime('-10 minutes'))->count(), // 10分钟内在线用户数
             'expireWarningUserCount' => User::whereBetween('expired_at', [date('Y-m-d'), date('Y-m-d', strtotime(sysConfig('expire_days').' days'))])->count(), // 临近过期用户数
@@ -43,9 +43,9 @@ class AdminController extends Controller
             'flowAbnormalUserCount' => count((new UserHourlyDataFlow)->trafficAbnormal()), // 1小时内流量异常用户
             'nodeCount' => Node::count(),
             'unnormalNodeCount' => Node::whereStatus(0)->count(),
-            'flowCount' => flowAutoShow(NodeDailyDataFlow::where('created_at', '>=', date('Y-m-d', strtotime('-30 days')))->sum('total')),
-            'todayFlowCount' => flowAutoShow(NodeDailyDataFlow::where('created_at', '>=', date('Y-m-d'))->sum('total')),
-            'totalFlowCount' => flowAutoShow(NodeDailyDataFlow::sum('total')),
+            'flowCount' => formatBytes(NodeDailyDataFlow::where('created_at', '>=', date('Y-m-d', strtotime('-30 days')))->sum('total')),
+            'todayFlowCount' => formatBytes(NodeDailyDataFlow::where('created_at', '>=', date('Y-m-d'))->sum('total')),
+            'totalFlowCount' => formatBytes(NodeDailyDataFlow::sum('total')),
             'totalCredit' => User::where('credit', '<>', 0)->sum('credit') / 100,
             'totalWaitRefAmount' => ReferralLog::whereIn('status', [0, 1])->sum('commission') / 100,
             'todayWaitRefAmount' => ReferralLog::whereIn('status', [0, 1])->whereDate('created_at', date('Y-m-d'))->sum('commission') / 100,
@@ -81,7 +81,7 @@ class AdminController extends Controller
     }
 
     // 导出邀请码
-    public function exportInvite()
+    public function exportInvite(): void
     {
         $inviteList = Invite::whereStatus(0)->orderBy('id')->get();
         $filename = '邀请码'.date('Ymd').'.xlsx';

+ 2 - 4
app/Http/Controllers/Api/Client/AuthController.php

@@ -2,10 +2,10 @@
 
 namespace App\Http\Controllers\Api\Client;
 
-use App\Components\Helpers;
 use App\Helpers\ClientApiResponse;
 use App\Helpers\ResponseEnum;
 use App\Services\UserService;
+use App\Utils\Helpers;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Illuminate\Routing\Controller;
@@ -83,12 +83,10 @@ class AuthController extends Controller
                 $request->session()->put('uid', $user->id);
             }
 
-            $userService = UserService::getInstance();
-
             return $this->succeed([
                 'token' => $user->createToken('client')->plainTextToken,
                 'expire_in' => time() + config('session.lifetime') * Minute,
-                'user' => $userService->getProfile(),
+                'user' => (new UserService)->getProfile(),
             ], null, ResponseEnum::USER_SERVICE_LOGIN_SUCCESS);
         }
 

+ 28 - 26
app/Http/Controllers/Api/Client/ClientController.php

@@ -2,7 +2,6 @@
 
 namespace App\Http\Controllers\Api\Client;
 
-use App\Components\Helpers;
 use App\Helpers\ClientApiResponse;
 use App\Helpers\ResponseEnum;
 use App\Models\Article;
@@ -12,6 +11,7 @@ use App\Models\ReferralLog;
 use App\Models\Ticket;
 use App\Services\ProxyService;
 use App\Services\UserService;
+use App\Utils\Helpers;
 use Arr;
 use Artisan;
 use Illuminate\Database\Eloquent\Builder;
@@ -43,7 +43,7 @@ class ClientController extends Controller
             return false;
         }
 
-        $userInfo = UserService::getInstance()->getProfile();
+        $userInfo = (new UserService)->getProfile();
         $userInfo['user_name'] = $user->nickname;
         $userInfo['email'] = $user->username;
         $userInfo['class_expire'] = $user->expiration_date;
@@ -69,9 +69,9 @@ class ClientController extends Controller
             'baseUrl' => sysConfig('subscribe_domain') ?? sysConfig('website_url'),
             'ann' => $ann,
             'avatar' => $user->avatar,
-            'usedTraffic' => flowAutoShow($total),
-            'enableTraffic' => flowAutoShow($transfer_enable),
-            'unUsedTraffic' => flowAutoShow($transfer_enable - $total),
+            'usedTraffic' => formatBytes($total),
+            'enableTraffic' => formatBytes($transfer_enable),
+            'unUsedTraffic' => formatBytes($transfer_enable - $total),
             'reset_time' => now()->diffInDays($user->reset_time, false),
             'android_index_button' => config('client.android_index_button'),
         ];
@@ -79,7 +79,7 @@ class ClientController extends Controller
         return $this->succeed(null, $data);
     }
 
-    public function getOrders(Request $request)
+    public function getOrders(Request $request): JsonResponse
     {
         $user = $request->user();
         $orders = $user->orders()->orderByDesc('id')->limit(8)->get();
@@ -97,20 +97,20 @@ class ClientController extends Controller
         return $this->succeed($data);
     }
 
-    public function getUserTransfer()
+    public function getUserTransfer(): JsonResponse
     {
         $user = auth()->user();
 
         return $this->succeed(null, [
             'arr' => [
-                'todayUsedTraffic' => flowAutoShow($user->d),
-                'lastUsedTraffic' => flowAutoShow($user->u),
-                'unUsedTraffic' => flowAutoShow($user->transfer_enable - $user->d - $user->u),
+                'todayUsedTraffic' => formatBytes($user->d),
+                'lastUsedTraffic' => formatBytes($user->u),
+                'unUsedTraffic' => formatBytes($user->transfer_enable - $user->d - $user->u),
             ],
         ]);
     }
 
-    public function shop()
+    public function shop(): JsonResponse
     {
         $shops = [
             'keys' => [],
@@ -124,11 +124,11 @@ class ClientController extends Controller
         return $this->succeed($shops);
     }
 
-    public function getInvite()
+    public function getInvite(): JsonResponse
     {
         $user = auth()->user();
 
-        $referral_traffic = flowAutoShow(sysConfig('referral_traffic') * MB);
+        $referral_traffic = formatBytes(sysConfig('referral_traffic') * MB);
         $referral_percent = sysConfig('referral_percent');
         // 邀请码
         $code = $user->invites()->whereStatus(0)->value('code');
@@ -138,8 +138,10 @@ class ClientController extends Controller
             'referral_percent' => $referral_percent * 100,
         ]);
 
-        $data['invite_code'] = $code ?? UserService::getInstance()->inviteURI(true);
-        $data['invite_url'] = UserService::getInstance()->inviteURI();
+        $userService = new UserService;
+
+        $data['invite_code'] = $code ?? $userService->inviteURI(true);
+        $data['invite_url'] = $userService->inviteURI();
         $data['invite_text'] = $data['invite_url'].'&(复制整段文字到浏览器打开即可访问),找梯子最重要的就是稳定,这个已经上线三年了,一直稳定没有被封过,赶紧下载备用吧!'.($code ? '安装后打开填写我的邀请码【'.$code.'】,你还能多得3天会员.' : '');
         // 累计数据
         $data['back_sum'] = ReferralLog::uid()->sum('commission') / 100;
@@ -178,14 +180,14 @@ class ClientController extends Controller
         $ttl = sysConfig('traffic_limit_time') ? sysConfig('traffic_limit_time') * Minute : Day;
         Cache::put('userCheckIn_'.$user->id, '1', $ttl);
 
-        return $this->succeed(null, null, [200, trans('user.home.attendance.success', ['data' => flowAutoShow($traffic)])]);
+        return $this->succeed(null, null, [200, trans('user.home.attendance.success', ['data' => formatBytes($traffic)])]);
     }
 
-    public function proxyCheck(Request $request)
+    public function proxyCheck(Request $request): JsonResponse
     {
         $md5 = $request->get('md5', '');
 
-        $proxy = ProxyService::getInstance()->getProxyCode('clash');
+        $proxy = (new ProxyService)->getProxyCode('clash');
         if (strtolower(md5(json_encode($proxy))) === strtolower($md5)) {
             return $this->succeed(false);
         }
@@ -197,12 +199,12 @@ class ClientController extends Controller
     {
         $flag = strtolower($request->input('flag') ?? ($request->userAgent() ?? ''));
 
-        return ProxyService::getInstance()->getProxyText($flag === 'v2rayng' ? 'v2rayng' : 'clash', $request->input('type'));
+        return (new ProxyService)->getProxyText($flag === 'v2rayng' ? 'v2rayng' : 'clash', $request->input('type'));
     }
 
-    public function getProxyList()
+    public function getProxyList(): JsonResponse
     {
-        $proxyServer = ProxyService::getInstance();
+        $proxyServer = new ProxyService;
 
         $servers = [];
         foreach ($proxyServer->getNodeList(null, false) as $node) {
@@ -221,7 +223,7 @@ class ClientController extends Controller
         return $this->succeed($servers);
     }
 
-    private function getOnlineCount(&$node, int $online)
+    private function getOnlineCount(&$node, int $online): void
     {
         $node['flag'] = $node['area'];
 
@@ -237,7 +239,7 @@ class ClientController extends Controller
         }
     }
 
-    public function getconfig()
+    public function getconfig(): JsonResponse
     {
         $config = $this->clientConfig();
         Arr::forget($config, ['read', 'configured']);
@@ -258,7 +260,7 @@ class ClientController extends Controller
         return $key ? config('client.'.$key) : config('client');
     }
 
-    private function setClientConfig() //
+    private function setClientConfig(): void
     {
         $ann = Article::type(2)->latest()->first();
 
@@ -277,7 +279,7 @@ class ClientController extends Controller
         ]);
     }
 
-    public function checkClientVersion(Request $request)
+    public function checkClientVersion(Request $request): JsonResponse
     {
         $version = $request->input('version');
         $type = $request->input('type');
@@ -294,7 +296,7 @@ class ClientController extends Controller
         return $this->succeed(null, ['enable' => true, 'download_url' => $vpn['download_url'], 'must' => $vpn['must'], 'message' => $vpn['message']], [200, $vpn['message']]);
     }
 
-    public function ticketList()
+    public function ticketList(): JsonResponse
     {
         $ticket = Ticket::where('user_id', auth()->user()->id)->selectRaw('id, title, UNIX_TIMESTAMP(created_at) as datetime, status')->orderBy('created_at', 'DESC')->get();
 

+ 1 - 1
app/Http/Controllers/Api/WebApi/CoreController.php

@@ -77,7 +77,7 @@ class CoreController extends Controller
             $u = $input['upload'] * $rate;
             $d = $input['download'] * $rate;
 
-            $formattedData[] = ['user_id' => $input['uid'], 'u' => $u, 'd' => $d, 'rate' => $rate, 'traffic' => flowAutoShow($u + $d), 'log_time' => time()];
+            $formattedData[] = ['user_id' => $input['uid'], 'u' => $u, 'd' => $d, 'rate' => $rate, 'traffic' => formatBytes($u + $d), 'log_time' => time()];
         }
 
         if (isset($formattedData) && $logs = $node->userDataFlowLogs()->createMany($formattedData)) { // 生成用户流量数据

+ 1 - 1
app/Http/Controllers/Api/WebApi/V2RayController.php

@@ -23,7 +23,7 @@ class V2RayController extends Controller
             'speed_limit' => $node->getRawOriginal('speed_limit'),
             'client_limit' => $node->client_limit,
             'push_port' => $node->push_port,
-            'redirect_url' => (string) sysConfig('redirect_url', ''),
+            'redirect_url' => (string) sysConfig('redirect_url'),
             'secret' => $node->auth->secret,
             'key' => $cert ? $cert->key : '',
             'pem' => $cert ? $cert->pem : '',

+ 21 - 32
app/Http/Controllers/AuthController.php

@@ -2,8 +2,6 @@
 
 namespace App\Http\Controllers;
 
-use App\Components\Helpers;
-use App\Components\IP;
 use App\Http\Requests\Auth\LoginRequest;
 use App\Http\Requests\Auth\RegisterRequest;
 use App\Models\EmailFilter;
@@ -14,6 +12,8 @@ use App\Models\VerifyCode;
 use App\Notifications\AccountActivation;
 use App\Notifications\PasswordReset;
 use App\Notifications\Verification;
+use App\Utils\Helpers;
+use App\Utils\IP;
 use Auth;
 use Cache;
 use Captcha;
@@ -46,7 +46,7 @@ class AuthController extends Controller
         return view('auth.login');
     }
 
-    public function login(LoginRequest $request)
+    public function login(LoginRequest $request): RedirectResponse
     {
         $data = $request->validated();
 
@@ -87,7 +87,8 @@ class AuthController extends Controller
         if ($user->status === 0 && sysConfig('is_activate_account')) {
             Auth::logout(); // 强制销毁会话,因为Auth::attempt的时候会产生会话
 
-            return Redirect::back()->withInput()->withErrors(trans('auth.active.promotion', ['action' => '<a href="'.route('active', ['username' => $user->username]).'" target="_blank">'.trans('common.active_item', ['attribute' => trans('common.account')]).'</a>']));
+            return Redirect::back()->withInput()->withErrors(trans('auth.active.promotion',
+                ['action' => '<a href="'.route('active', ['username' => $user->username]).'" target="_blank">'.trans('common.active_item', ['attribute' => trans('common.account')]).'</a>']));
         }
 
         Helpers::userLoginAction($user, IP::getClientIp()); // 用户登录后操作
@@ -158,12 +159,10 @@ class AuthController extends Controller
         $aff = $request->input('aff');
 
         // 防止重复提交
-        if ($register_token !== Session::get('register_token')) {
+        if ($register_token !== Session::pull('register_token')) {
             return Redirect::back()->withInput()->withErrors(trans('auth.error.repeat_request'));
         }
 
-        Session::forget('register_token');
-
         // 是否开启注册
         if (! sysConfig('is_register')) {
             return Redirect::back()->withErrors(trans('auth.register.error.disable'));
@@ -319,14 +318,8 @@ class AuthController extends Controller
         return false;
     }
 
-    /**
-     * 获取AFF.
-     *
-     * @param  string|null  $code  邀请码
-     * @param  int|null  $aff  URL中的aff参数
-     */
-    private function getAff($code = null, $aff = null): array
-    {
+    private function getAff(string $code = '', int $aff = 0): array
+    { // 获取AFF
         $data = ['inviter_id' => null, 'code_id' => 0]; // 邀请人ID 与 邀请码ID
 
         // 有邀请码先用邀请码,用谁的邀请码就给谁返利
@@ -340,35 +333,31 @@ class AuthController extends Controller
 
         // 没有用邀请码或者邀请码是管理员生成的,则检查cookie或者url链接
         if (! $data['inviter_id']) {
-            // 检查一下cookie里有没有aff
-            $cookieAff = \request()->cookie('register_aff');
-            if ($cookieAff) {
-                $cookieAff = $this->affConvert($cookieAff);
-                $data['inviter_id'] = $cookieAff && User::find($cookieAff) ? $cookieAff : null;
-            } elseif ($aff) { // 如果cookie里没有aff,就再检查一下请求的url里有没有aff,因为有些人的浏览器会禁用了cookie,比如chrome开了隐私模式
-                $aff = $this->affConvert($aff);
-                $data['inviter_id'] = $aff && User::find($aff) ? $aff : null;
+            $cookieAff = \request()?->cookie('register_aff'); // 检查一下cookie里有没有aff
+            if ($cookieAff || $aff) {
+                $data['inviter_id'] = $this->setInviter($aff ?: $cookieAff);
             }
         }
 
         return $data;
     }
 
-    private function affConvert($aff)
+    private function setInviter(string|int $aff): int|null
     {
+        $uid = 0;
         if (is_numeric($aff)) {
-            return $aff;
-        }
-
-        $decode = (new Hashids(sysConfig('aff_salt'), 8))->decode($aff);
-        if ($decode) {
-            return $decode[0];
+            $uid = (int) $aff;
+        } else {
+            $decode = (new Hashids(sysConfig('aff_salt'), 8))->decode($aff);
+            if ($decode) {
+                $uid = $decode[0];
+            }
         }
 
-        return false;
+        return $uid && User::whereId($uid)->exists() ? $uid : null;
     }
 
-    private function addVerifyUrl($uid, $email)
+    private function addVerifyUrl($uid, $email): string
     { // 生成申请的请求地址
         $token = md5(sysConfig('website_name').$email.microtime());
         $verify = new Verify();

+ 1 - 4
app/Http/Controllers/Controller.php

@@ -3,13 +3,10 @@
 namespace App\Http\Controllers;
 
 use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
-use Illuminate\Foundation\Bus\DispatchesJobs;
 use Illuminate\Foundation\Validation\ValidatesRequests;
 use Illuminate\Routing\Controller as BaseController;
 
 class Controller extends BaseController
 {
-    use AuthorizesRequests;
-    use DispatchesJobs;
-    use ValidatesRequests;
+    use AuthorizesRequests, ValidatesRequests;
 }

+ 42 - 74
app/Http/Controllers/OAuthController.php

@@ -2,68 +2,48 @@
 
 namespace App\Http\Controllers;
 
-use App\Components\Helpers;
-use App\Components\IP;
 use App\Models\User;
 use App\Models\UserOauth;
+use App\Utils\Helpers;
+use App\Utils\IP;
 use Auth;
-use Illuminate\Http\Request;
+use Illuminate\Http\RedirectResponse;
 use Laravel\Socialite\Facades\Socialite;
 use Str;
 
 class OAuthController extends Controller
 {
-    public function simple(string $type)
+    public function simple(string $type): RedirectResponse
     {
         $info = Socialite::driver($type)->stateless()->user();
         if ($info) {
             $user = Auth::user();
 
             if ($user) {
-                return $this->bind($type, $user, $info);
+                return $this->binding($type, $user, $info);
             }
 
-            return $this->login($type, $info);
+            return $this->logging($type, $info);
         }
 
         return redirect()->route('login')->withErrors(trans('auth.oauth.login_failed'));
     }
 
-    private function bind(string $type, $user, $info)
+    private function binding(string $type, User $user, \Laravel\Socialite\Contracts\User $OauthUser): RedirectResponse
     {
-        $auth = $user->userAuths()->whereType($type)->first();
-        $data = ['type' => $type, 'identifier' => $info->getId(), 'credential' => $info->token];
-        if ($auth) {
-            $user->userAuths()->whereType($type)->update($data);
-
-            return redirect()->route('profile')->with('successMsg', trans('auth.oauth.rebind_success'));
-        }
-
-        $user->userAuths()->create($data);
-
-        return redirect()->route('profile')->with('successMsg', trans('auth.oauth.bind_success'));
-    }
-
-    public function route(Request $request, string $type)
-    {
-        $action = $request->input('action');
-        $key = "services.{$type}.redirect";
-        if ($action === 'binding') {
-            config([$key => route('oauth.bind', ['type' => $type])]);
-        } elseif ($action === 'register') {
-            config([$key => route('oauth.register', ['type' => $type])]);
-        } else {
-            config([$key => route('oauth.login', ['type' => $type])]);
+        $data = ['type' => $type, 'identifier' => $OauthUser->getId(), 'credential' => $OauthUser->token];
+        if ($user->userAuths()->whereType($type)->updateOrCreate($data)) {
+            return redirect()->route('profile')->with('successMsg', trans('auth.oauth.bind_success'));
         }
 
-        return Socialite::driver($type)->redirect();
+        return redirect()->route('profile')->withErrors(trans('auth.oauth.bind_failed'));
     }
 
-    private function login(string $type, $info)
+    private function logging(string $type, \Laravel\Socialite\Contracts\User $OauthUser): RedirectResponse
     {
-        $user = User::whereUsername($info->getEmail())->first();
+        $user = User::whereUsername($OauthUser->getEmail())->first();
         if (! isset($user)) {
-            $auth = UserOauth::whereType($type)->whereIdentifier($info->getId())->first();
+            $auth = UserOauth::whereType($type)->whereIdentifier($OauthUser->getId())->first();
             if (isset($auth)) {
                 $user = $auth->user;
             }
@@ -79,7 +59,17 @@ class OAuthController extends Controller
         return redirect()->route('login')->withErrors(trans('auth.error.not_found_user'));
     }
 
-    public function unsubscribe(string $type)
+    public function login(string $type): RedirectResponse
+    {
+        $info = Socialite::driver($type)->stateless()->user();
+        if ($info) {
+            return $this->logging($type, $info);
+        }
+
+        return redirect()->route('login')->withErrors(trans('auth.oauth.login_failed'));
+    }
+
+    public function unbind(string $type): RedirectResponse
     {
         $user = Auth::user();
         if ($user && $user->userAuths()->whereType($type)->delete()) {
@@ -89,15 +79,14 @@ class OAuthController extends Controller
         return redirect()->route('profile')->with('successMsg', trans('auth.oauth.unbind_failed'));
     }
 
-    public function binding($type)
+    public function bind(string $type): RedirectResponse
     {
-        config(["services.{$type}.redirect" => route('oauth.bind', ['type' => $type])]);
         $info = Socialite::driver($type)->stateless()->user();
 
         if ($info) {
             $user = Auth::user();
             if ($user) {
-                return $this->bind($type, $user, $info);
+                return $this->binding($type, $user, $info);
             }
 
             return redirect()->route('profile')->withErrors(trans('auth.oauth.bind_failed'));
@@ -106,18 +95,7 @@ class OAuthController extends Controller
         return redirect()->route('login')->withErrors(trans('auth.oauth.login_failed'));
     }
 
-    public function logining($type)
-    {
-        config(["services.{$type}.redirect" => route('oauth.login', ['type' => $type])]);
-        $info = Socialite::driver($type)->stateless()->user();
-        if ($info) {
-            return $this->login($type, $info);
-        }
-
-        return redirect()->route('login')->withErrors(trans('auth.oauth.login_failed'));
-    }
-
-    public function register($type)
+    public function register(string $type): RedirectResponse
     {
         if (! sysConfig('is_register')) {
             return redirect()->route('register')->withErrors(trans('auth.register.error.disable'));
@@ -127,31 +105,21 @@ class OAuthController extends Controller
             return redirect()->route('register')->withErrors(trans('validation.required', ['attribute' => trans('auth.invite.attribute')]));
         }
 
-        config(["services.{$type}.redirect" => route('oauth.register', ['type' => $type])]);
-        $info = Socialite::driver($type)->stateless()->user();
+        $OauthUser = Socialite::driver($type)->stateless()->user();
 
-        // 排除重复用户注册
-        if ($info) {
-            $user = User::whereUsername($info->getEmail())->first();
-            if (! $user) {
-                $user = UserOauth::whereIdentifier($info->getId())->first();
-                if (! $user) {
-                    $user = Helpers::addUser($info->getEmail(), Str::random(), MB * ((int) sysConfig('default_traffic')), null, $info->getNickname());
-
-                    if ($user) {
-                        $user->userAuths()->create([
-                            'type' => $type,
-                            'identifier' => $info->getId(),
-                            'credential' => $info->token,
-                        ]);
-
-                        Auth::login($user);
-
-                        return redirect()->route('login');
-                    }
-
-                    return redirect()->route('register')->withErrors(trans('auth.oauth.register_failed'));
-                }
+        if ($OauthUser) {
+            if (User::whereUsername($OauthUser->getEmail())->doesntExist() && UserOauth::whereIdentifier($OauthUser->getId())->doesntExist()) { // 排除重复用户注册
+                $user = Helpers::addUser($OauthUser->getEmail(), Str::random(), MB * ((int) sysConfig('default_traffic')), null, $OauthUser->getNickname());
+
+                $user->userAuths()->create([
+                    'type' => $type,
+                    'identifier' => $OauthUser->getId(),
+                    'credential' => $OauthUser->token,
+                ]);
+
+                Auth::login($user);
+
+                return redirect()->route('login');
             }
 
             return redirect()->route('login')->withErrors(trans('auth.oauth.registered'));

+ 16 - 15
app/Http/Controllers/PaymentController.php

@@ -2,22 +2,23 @@
 
 namespace App\Http\Controllers;
 
-use App\Components\Helpers;
 use App\Models\Coupon;
 use App\Models\Goods;
 use App\Models\Order;
 use App\Models\Payment;
-use App\Payments\CodePay;
-use App\Payments\EPay;
-use App\Payments\F2Fpay;
-use App\Payments\Local;
-use App\Payments\Manual;
-use App\Payments\PayBeaver;
-use App\Payments\PayJs;
-use App\Payments\PayPal;
-use App\Payments\Stripe;
-use App\Payments\THeadPay;
 use App\Services\CouponService;
+use App\Utils\Helpers;
+use App\Utils\Library\Templates\Gateway;
+use App\Utils\Payments\CodePay;
+use App\Utils\Payments\EPay;
+use App\Utils\Payments\F2Fpay;
+use App\Utils\Payments\Local;
+use App\Utils\Payments\Manual;
+use App\Utils\Payments\PayBeaver;
+use App\Utils\Payments\PayJs;
+use App\Utils\Payments\PayPal;
+use App\Utils\Payments\Stripe;
+use App\Utils\Payments\THeadPay;
 use Auth;
 use Exception;
 use Illuminate\Http\JsonResponse;
@@ -27,7 +28,7 @@ use Response;
 
 class PaymentController extends Controller
 {
-    public static $method;
+    public static string $method;
 
     public static function notify(Request $request)
     {
@@ -38,7 +39,7 @@ class PaymentController extends Controller
         return self::getClient()->notify($request);
     }
 
-    public static function getClient()
+    public static function getClient(): Gateway
     {
         switch (self::$method) {
             case 'credit':
@@ -63,7 +64,7 @@ class PaymentController extends Controller
                 return new Manual();
             default:
                 Log::emergency('未知支付:'.self::$method);
-                exit;
+                exit(404);
         }
     }
 
@@ -176,7 +177,7 @@ class PaymentController extends Controller
             ]);
 
             // 使用优惠券,减少可使用次数
-            if (! empty($coupon)) {
+            if ($coupon !== null) {
                 if ($coupon->usable_times > 0) {
                     $coupon->decrement('usable_times');
                 }

+ 26 - 25
app/Http/Controllers/TelegramController.php

@@ -14,7 +14,7 @@ class TelegramController extends Controller
 {
     protected $msg;
 
-    public function webhook(Request $request)
+    public function webhook(Request $request): void
     {
         $this->msg = $this->getMessage($request->input());
         if (! $this->msg) {
@@ -59,7 +59,7 @@ class TelegramController extends Controller
         return $obj;
     }
 
-    private function fromSend()
+    private function fromSend(): void
     {
         switch ($this->msg->command) {
             case '/bind':
@@ -79,7 +79,7 @@ class TelegramController extends Controller
         }
     }
 
-    private function bind()
+    private function bind(): void
     {
         $msg = $this->msg;
         if (! $msg->is_private) {
@@ -103,7 +103,7 @@ class TelegramController extends Controller
         $telegramService->sendMessage($msg->chat_id, '绑定成功');
     }
 
-    private function traffic()
+    private function traffic(): void
     {
         $msg = $this->msg;
         if (! $msg->is_private) {
@@ -120,21 +120,21 @@ class TelegramController extends Controller
             return;
         }
         $user = $oauth->user;
-        $transferEnable = flowAutoShow($user->transfer_enable);
-        $up = flowAutoShow($user->u);
-        $down = flowAutoShow($user->d);
-        $remaining = flowAutoShow($user->transfer_enable - ($user->u + $user->d));
-        $text = "🚥流量查询\n———————————————\n计划流量:`{$transferEnable}`\n已用上行:`{$up}`\n已用下行:`{$down}`\n剩余流量:`{$remaining}`";
+        $transferEnable = formatBytes($user->transfer_enable);
+        $up = formatBytes($user->u);
+        $down = formatBytes($user->d);
+        $remaining = formatBytes($user->transfer_enable - ($user->u + $user->d));
+        $text = "🚥流量查询\n———————————————\n计划流量:`$transferEnable`\n已用上行:`$up`\n已用下行:`$down`\n剩余流量:`$remaining`";
         $telegramService->sendMessage($msg->chat_id, $text, 'markdown');
     }
 
-    private function help()
+    private function help(): void
     {
         $msg = $this->msg;
         if (! $msg->is_private) {
             return;
         }
-        $telegramService = new TelegramService();
+        $telegramService = new TelegramService;
         $commands = [
             '/bind 订阅地址 - 绑定你的'.sysConfig('website_name').'账号',
             '/traffic - 查询流量信息',
@@ -145,10 +145,10 @@ class TelegramController extends Controller
         $telegramService->sendMessage($msg->chat_id, "你可以使用以下命令进行操作:\n\n$text", 'markdown');
     }
 
-    private function getLatestUrl()
+    private function getLatestUrl(): void
     {
         $msg = $this->msg;
-        $telegramService = new TelegramService();
+        $telegramService = new TelegramService;
         $text = sprintf(
             '%s的最新网址是:%s',
             sysConfig('website_name'),
@@ -157,18 +157,19 @@ class TelegramController extends Controller
         $telegramService->sendMessage($msg->chat_id, $text, 'markdown');
     }
 
-    private function unbind()
+    private function unbind(): void
     {
         $msg = $this->msg;
         if (! $msg->is_private) {
             return;
         }
-        $user = User::with(['userAuths' => function ($query) use ($msg) {
-            $query->whereType('telegram')->whereIdentifier($msg->chat_id);
-        },
+        $user = User::with([
+            'userAuths' => function ($query) use ($msg) {
+                $query->whereType('telegram')->whereIdentifier($msg->chat_id);
+            },
         ])->first();
 
-        $telegramService = new TelegramService();
+        $telegramService = new TelegramService;
         if (! $user) {
             $this->help();
             $telegramService->sendMessage($msg->chat_id, '没有查询到您的用户信息,请先绑定账号', 'markdown');
@@ -181,7 +182,7 @@ class TelegramController extends Controller
         $telegramService->sendMessage($msg->chat_id, '解绑成功', 'markdown');
     }
 
-    private function fromReply()
+    private function fromReply(): void
     {
         // ticket
         if (preg_match('/[#](.*)/', $this->msg->reply_text, $match)) {
@@ -189,15 +190,16 @@ class TelegramController extends Controller
         }
     }
 
-    private function replayTicket($ticketId)
+    private function replayTicket($ticketId): void
     {
         $msg = $this->msg;
         if (! $msg->is_private) {
             return;
         }
-        $user = User::with(['userAuths' => function ($query) use ($msg) {
-            $query->whereType('telegram')->whereIdentifier($msg->chat_id);
-        },
+        $user = User::with([
+            'userAuths' => function ($query) use ($msg) {
+                $query->whereType('telegram')->whereIdentifier($msg->chat_id);
+            },
         ])->first();
 
         if (! $user) {
@@ -214,7 +216,6 @@ class TelegramController extends Controller
             }
             $ticket->reply()->create(['admin_id' => $admin->id, 'content' => $msg->text]);
         }
-        $telegramService = new TelegramService();
-        $telegramService->sendMessage($msg->chat_id, "#`{$ticketId}` 的工单已回复成功", 'markdown');
+        (new TelegramService)->sendMessage($msg->chat_id, "#`$ticketId` 的工单已回复成功", 'markdown');
     }
 }

+ 3 - 3
app/Http/Controllers/User/AffiliateController.php

@@ -2,12 +2,12 @@
 
 namespace App\Http\Controllers\User;
 
-use App\Components\Helpers;
 use App\Http\Controllers\Controller;
 use App\Models\Order;
 use App\Models\ReferralApply;
 use App\Models\ReferralLog;
 use App\Services\UserService;
+use App\Utils\Helpers;
 use Auth;
 use Illuminate\Http\JsonResponse;
 use Response;
@@ -22,12 +22,12 @@ class AffiliateController extends Controller
         }
 
         return view('user.referral', [
-            'referral_traffic' => flowAutoShow(sysConfig('referral_traffic') * MB),
+            'referral_traffic' => formatBytes(sysConfig('referral_traffic') * MB),
             'referral_percent' => sysConfig('referral_percent'),
             'referral_money' => Helpers::getPriceTag(sysConfig('referral_money')),
             'totalAmount' => ReferralLog::uid()->sum('commission') / 100,
             'canAmount' => Helpers::getPriceTag(ReferralLog::uid()->whereStatus(0)->sum('commission') / 100),
-            'aff_link' => UserService::getInstance()->inviteURI(),
+            'aff_link' => (new UserService)->inviteURI(),
             'referralLogList' => ReferralLog::uid()->with('invitee:id,username')->latest()->paginate(10, ['*'], 'log_page'),
             'referralApplyList' => ReferralApply::uid()->latest()->paginate(10, ['*'], 'apply_page'),
             'referralUserList' => Auth::getUser()->invitees()->select(['username', 'created_at'])->latest()->paginate(10, ['*'], 'user_page'),

+ 6 - 6
app/Http/Controllers/User/SubscribeController.php

@@ -2,20 +2,20 @@
 
 namespace App\Http\Controllers\User;
 
-use App\Components\IP;
 use App\Http\Controllers\Controller;
 use App\Models\UserSubscribe;
 use App\Models\UserSubscribeLog;
 use App\Services\ProxyService;
+use App\Utils\IP;
 use Illuminate\Http\Request;
 use Redirect;
 use Response;
 
 class SubscribeController extends Controller
 {
-    private static $subType;
+    private static int|null $subType;
 
-    private $proxyServer;
+    private ProxyService $proxyServer;
 
     // 通过订阅码获取订阅信息
     public function getSubscribeByCode(Request $request, string $code)
@@ -26,7 +26,7 @@ class SubscribeController extends Controller
             return Redirect::route('login');
         }
         $code = $matches[0];
-        $this->proxyServer = ProxyService::getInstance();
+        $this->proxyServer = new ProxyService;
         self::$subType = is_numeric($request->input('type')) ? $request->input('type') : null;
 
         // 检查订阅码是否有效
@@ -69,10 +69,10 @@ class SubscribeController extends Controller
         $subscribe->increment('times'); // 更新访问次数
         $this->subscribeLog($subscribe->id, IP::getClientIp(), json_encode(['Host' => $request->getHost(), 'User-Agent' => $request->userAgent()])); // 记录每次请求
 
-        return ProxyService::getInstance()->getProxyText(strtolower($request->input('target') ?? ($request->userAgent() ?? '')), self::$subType);
+        return $this->proxyServer->getProxyText(strtolower($request->input('target') ?? ($request->userAgent() ?? '')), self::$subType);
     }
 
-    private function failed(string $text)
+    private function failed(string $text): \Illuminate\Http\Response
     { // 抛出错误的节点信息,用于兼容防止客户端订阅失败
         return Response::make(base64url_encode($this->proxyServer->failedProxyReturn($text, self::$subType ?? 1)));
     }

+ 13 - 15
app/Http/Controllers/UserController.php

@@ -2,7 +2,6 @@
 
 namespace App\Http\Controllers;
 
-use App\Components\Helpers;
 use App\Helpers\DataChart;
 use App\Models\Article;
 use App\Models\Coupon;
@@ -19,11 +18,13 @@ use App\Services\ArticleService;
 use App\Services\CouponService;
 use App\Services\ProxyService;
 use App\Services\UserService;
+use App\Utils\Helpers;
 use Cache;
 use DB;
 use Exception;
 use Hash;
 use Illuminate\Http\JsonResponse;
+use Illuminate\Http\RedirectResponse;
 use Illuminate\Http\Request;
 use Illuminate\Validation\Rule;
 use Log;
@@ -42,10 +43,8 @@ class UserController extends Controller
     {
         // 用户转换
         if (Session::has('user')) {
-            auth()->loginUsingId(Session::get('user'));
-            Session::forget('user');
+            auth()->loginUsingId(Session::pull('user'));
         }
-        $userService = UserService::getInstance();
         $user = auth()->user();
         $totalTransfer = $user->transfer_enable;
         $usedTransfer = $user->used_traffic;
@@ -66,13 +65,13 @@ class UserController extends Controller
         return view('user.index', array_merge([
             'remainDays' => now()->diffInDays($user->expired_at, false),
             'resetDays' => $user->reset_time ? now()->diffInDays($user->reset_time, false) : null,
-            'unusedTraffic' => flowAutoShow($unusedTraffic),
+            'unusedTraffic' => formatBytes($unusedTraffic),
             'expireTime' => $user->expiration_date,
             'banedTime' => $user->ban_time,
             'unusedPercent' => $totalTransfer > 0 ? round($unusedTraffic / $totalTransfer, 2) * 100 : 0,
             'announcements' => Article::type(2)->lang()->latest()->simplePaginate(1), // 公告
             'isTrafficWarning' => $user->isTrafficWarning(), // 流量异常判断
-            'paying_user' => $userService->isActivePaying(), // 付费用户判断
+            'paying_user' => (new UserService)->isActivePaying(), // 付费用户判断
             'userLoginLog' => $user->loginLogs()->latest()->first(), // 近期登录日志
             'subscribe_status' => $user->subscribe->status,
             'subMsg' => $user->subscribe->ban_desc,
@@ -106,7 +105,7 @@ class UserController extends Controller
         $ttl = sysConfig('traffic_limit_time') ? sysConfig('traffic_limit_time') * Minute : Day;
         Cache::put('userCheckIn_'.$user->id, '1', $ttl);
 
-        return Response::json(['status' => 'success', 'message' => trans('user.home.attendance.success', ['data' => flowAutoShow($traffic)])]);
+        return Response::json(['status' => 'success', 'message' => trans('user.home.attendance.success', ['data' => formatBytes($traffic)])]);
     }
 
     // 节点列表
@@ -114,7 +113,7 @@ class UserController extends Controller
     {
         $user = auth()->user();
         if ($request->isMethod('POST')) {
-            $proxyServer = ProxyService::getInstance();
+            $proxyServer = new ProxyService;
             $server = $proxyServer->getProxyConfig(Node::findOrFail($request->input('id')));
 
             return Response::json(['status' => 'success', 'data' => $proxyServer->getUserProxyConfig($server, $request->input('type') !== 'text'), 'title' => $server['type']]);
@@ -134,7 +133,7 @@ class UserController extends Controller
         ]);
     }
 
-    public function article(Article $article)
+    public function article(Article $article): JsonResponse
     { // 公告详情
         $articleService = new ArticleService($article);
 
@@ -200,7 +199,7 @@ class UserController extends Controller
     }
 
     // 商品列表
-    public function services(Request $request)
+    public function services()
     {
         $user = auth()->user();
         // 余额充值商品,只取10个
@@ -383,7 +382,7 @@ class UserController extends Controller
         return view('user.invite', [
             'num' => auth()->user()->invite_num, // 还可以生成的邀请码数量
             'inviteList' => Invite::uid()->with(['invitee', 'inviter'])->paginate(10), // 邀请码列表
-            'referral_traffic' => flowAutoShow(sysConfig('referral_traffic') * MB),
+            'referral_traffic' => formatBytes(sysConfig('referral_traffic') * MB),
             'referral_percent' => sysConfig('referral_percent'),
         ]);
     }
@@ -505,8 +504,7 @@ class UserController extends Controller
         }
 
         // 管理员信息重新写入user
-        $user = auth()->loginUsingId(Session::get('admin'));
-        Session::forget('admin');
+        $user = auth()->loginUsingId(Session::pull('admin'));
         if ($user) {
             return Response::json(['status' => 'success', 'message' => trans('common.toggle_action', ['action' => trans('common.success')])]);
         }
@@ -535,8 +533,8 @@ class UserController extends Controller
         return Response::json(['status' => 'fail', 'message' => trans('common.failed_item', ['attribute' => trans('user.recharge')])]);
     }
 
-    public function switchCurrency(string $code)// 切换语言
-    {
+    public function switchCurrency(string $code): RedirectResponse
+    { // 切换语言
         Session::put('currency', $code);
 
         return Redirect::back();

+ 12 - 12
app/Http/Kernel.php

@@ -5,7 +5,6 @@ namespace App\Http;
 use App\Http\Middleware\AcceptHeader;
 use App\Http\Middleware\Affiliate;
 use App\Http\Middleware\Authenticate;
-use App\Http\Middleware\CheckForMaintenanceMode;
 use App\Http\Middleware\ClientAuthenticate;
 use App\Http\Middleware\EncryptCookies;
 use App\Http\Middleware\isForbidden;
@@ -13,6 +12,7 @@ use App\Http\Middleware\isLogin;
 use App\Http\Middleware\isMaintenance;
 use App\Http\Middleware\isSecurity;
 use App\Http\Middleware\Permission;
+use App\Http\Middleware\PreventRequestsDuringMaintenance;
 use App\Http\Middleware\RedirectIfAuthenticated;
 use App\Http\Middleware\SetLocale;
 use App\Http\Middleware\Telegram;
@@ -21,7 +21,6 @@ use App\Http\Middleware\TrimStrings;
 use App\Http\Middleware\TrustProxies;
 use App\Http\Middleware\VerifyCsrfToken;
 use App\Http\Middleware\WebApi;
-use Fruitcake\Cors\HandleCors;
 use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
 use Illuminate\Auth\Middleware\Authorize;
 use Illuminate\Auth\Middleware\EnsureEmailIsVerified;
@@ -30,10 +29,12 @@ use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
 use Illuminate\Foundation\Http\Kernel as HttpKernel;
 use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
 use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
+use Illuminate\Http\Middleware\HandleCors;
 use Illuminate\Http\Middleware\SetCacheHeaders;
 use Illuminate\Routing\Middleware\SubstituteBindings;
 use Illuminate\Routing\Middleware\ThrottleRequests;
 use Illuminate\Routing\Middleware\ValidateSignature;
+use Illuminate\Session\Middleware\AuthenticateSession;
 use Illuminate\Session\Middleware\StartSession;
 use Illuminate\View\Middleware\ShareErrorsFromSession;
 
@@ -44,13 +45,13 @@ class Kernel extends HttpKernel
      *
      * These middleware are run during every request to your application.
      *
-     * @var array
+     * @var array<int, class-string|string>
      */
     protected $middleware = [
         // \App\Http\Middleware\TrustHosts::class,
         TrustProxies::class,
         HandleCors::class,
-        CheckForMaintenanceMode::class,
+        PreventRequestsDuringMaintenance::class,
         ValidatePostSize::class,
         TrimStrings::class,
         ConvertEmptyStringsToNull::class,
@@ -59,14 +60,13 @@ class Kernel extends HttpKernel
     /**
      * The application's route middleware groups.
      *
-     * @var array
+     * @var array<string, array<int, class-string|string>>
      */
     protected $middlewareGroups = [
         'web' => [
             EncryptCookies::class,
             AddQueuedCookiesToResponse::class,
             StartSession::class,
-            // \Illuminate\Session\Middleware\AuthenticateSession::class,
             SetLocale::class,
             ShareErrorsFromSession::class,
             VerifyCsrfToken::class,
@@ -92,23 +92,23 @@ class Kernel extends HttpKernel
             StartSession::class,
             tmpCORS::class,
             SetLocale::class,
-            'throttle:60,1',
+            ThrottleRequests::class.':api',
             SubstituteBindings::class,
         ],
     ];
 
     /**
-     * The application's route middleware.
+     * The application's middleware aliases.
      *
-     * These middleware may be assigned to groups or used individually.
+     * Aliases may be used instead of class names to conveniently assign middleware to routes and groups.
      *
-     * @var array
+     * @var array<string, class-string|string>
      */
-    protected $routeMiddleware = [
+    protected $middlewareAliases = [
         'auth' => Authenticate::class,
         'auth.basic' => AuthenticateWithBasicAuth::class,
+        'auth.session' => AuthenticateSession::class,
         'auth.client' => ClientAuthenticate::class,
-        'bindings' => SubstituteBindings::class,
         'cache.headers' => SetCacheHeaders::class,
         'can' => Authorize::class,
         'guest' => RedirectIfAuthenticated::class,

+ 2 - 7
app/Http/Middleware/Affiliate.php

@@ -8,13 +8,8 @@ use Illuminate\Http\Request;
 
 class Affiliate
 {
-    /**
-     * 返利识别.
-     *
-     * @return mixed
-     */
-    public function handle(Request $request, Closure $next)
-    {
+    public function handle(Request $request, Closure $next): mixed
+    { // 返利识别
         $aff = $request->input('aff');
         if ($aff) {
             Cookie::queue('register_aff', $aff, 129600);

+ 2 - 7
app/Http/Middleware/Authenticate.php

@@ -9,14 +9,9 @@ class Authenticate extends Middleware
 {
     /**
      * Get the path the user should be redirected to when they are not authenticated.
-     *
-     * @param  Request  $request
-     * @return string|null
      */
-    protected function redirectTo($request)
+    protected function redirectTo(Request $request): ?string
     {
-        if (! $request->expectsJson()) {
-            return route('login');
-        }
+        return $request->expectsJson() ? null : route('login');
     }
 }

+ 1 - 1
app/Http/Middleware/EncryptCookies.php

@@ -9,7 +9,7 @@ class EncryptCookies extends Middleware
     /**
      * The names of the cookies that should not be encrypted.
      *
-     * @var array
+     * @var array<int, string>
      */
     protected $except = [
         //

+ 3 - 3
app/Http/Middleware/CheckForMaintenanceMode.php → app/Http/Middleware/PreventRequestsDuringMaintenance.php

@@ -2,14 +2,14 @@
 
 namespace App\Http\Middleware;
 
-use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as Middleware;
+use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
 
-class CheckForMaintenanceMode extends Middleware
+class PreventRequestsDuringMaintenance extends Middleware
 {
     /**
      * The URIs that should be reachable while maintenance mode is enabled.
      *
-     * @var array
+     * @var array<int, string>
      */
     protected $except = [
         //

+ 9 - 6
app/Http/Middleware/RedirectIfAuthenticated.php

@@ -6,20 +6,23 @@ use App\Providers\RouteServiceProvider;
 use Closure;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Auth;
+use Symfony\Component\HttpFoundation\Response;
 
 class RedirectIfAuthenticated
 {
     /**
      * Handle an incoming request.
      *
-     * @param  Request  $request
-     * @param  string|null  $guard
-     * @return mixed
+     * @param  Closure(Request): (Response)  $next
      */
-    public function handle($request, Closure $next, $guard = null)
+    public function handle(Request $request, Closure $next, string ...$guards): Response
     {
-        if (Auth::guard($guard)->check()) {
-            return redirect(RouteServiceProvider::HOME);
+        $guards = empty($guards) ? [null] : $guards;
+
+        foreach ($guards as $guard) {
+            if (Auth::guard($guard)->check()) {
+                return redirect(RouteServiceProvider::HOME);
+            }
         }
 
         return $next($request);

+ 2 - 2
app/Http/Middleware/SetLocale.php

@@ -17,8 +17,8 @@ class SetLocale
      */
     public function handle(Request $request, Closure $next)
     {
-        if (session()->has('locale')) {
-            $lang = session()->get('locale');
+        if ($request->session()->has('locale')) {
+            $lang = $request->session()->get('locale');
         } elseif ($request->query('locale')) {
             $lang = $request->query('locale');
         } elseif (Agent::languages()) {

+ 2 - 1
app/Http/Middleware/TrimStrings.php

@@ -9,9 +9,10 @@ class TrimStrings extends Middleware
     /**
      * The names of the attributes that should not be trimmed.
      *
-     * @var array
+     * @var array<int, string>
      */
     protected $except = [
+        'current_password',
         'password',
         'password_confirmation',
     ];

+ 2 - 2
app/Http/Middleware/TrustHosts.php

@@ -9,9 +9,9 @@ class TrustHosts extends Middleware
     /**
      * Get the host patterns that should be trusted.
      *
-     * @return array
+     * @return array<int, string|null>
      */
-    public function hosts()
+    public function hosts(): array
     {
         return [
             $this->allSubdomainsOfApplicationUrl(),

+ 8 - 3
app/Http/Middleware/TrustProxies.php

@@ -2,7 +2,7 @@
 
 namespace App\Http\Middleware;
 
-use Fideloper\Proxy\TrustProxies as Middleware;
+use Illuminate\Http\Middleware\TrustProxies as Middleware;
 use Illuminate\Http\Request;
 
 class TrustProxies extends Middleware
@@ -10,7 +10,7 @@ class TrustProxies extends Middleware
     /**
      * The trusted proxies for this application.
      *
-     * @var array|string|null
+     * @var array<int, string>|string|null
      */
     protected $proxies;
 
@@ -19,5 +19,10 @@ class TrustProxies extends Middleware
      *
      * @var int
      */
-    protected $headers = Request::HEADER_X_FORWARDED_ALL;
+    protected $headers =
+        Request::HEADER_X_FORWARDED_FOR |
+        Request::HEADER_X_FORWARDED_HOST |
+        Request::HEADER_X_FORWARDED_PORT |
+        Request::HEADER_X_FORWARDED_PROTO |
+        Request::HEADER_X_FORWARDED_AWS_ELB;
 }

+ 22 - 0
app/Http/Middleware/ValidateSignature.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
+
+class ValidateSignature extends Middleware
+{
+    /**
+     * The names of the query string parameters that should be ignored.
+     *
+     * @var array<int, string>
+     */
+    protected $except = [
+        // 'fbclid',
+        // 'utm_campaign',
+        // 'utm_content',
+        // 'utm_medium',
+        // 'utm_source',
+        // 'utm_term',
+    ];
+}

+ 1 - 1
app/Http/Middleware/VerifyCsrfToken.php

@@ -9,7 +9,7 @@ class VerifyCsrfToken extends Middleware
     /**
      * The URIs that should be excluded from CSRF verification.
      *
-     * @var array
+     * @var array<int, string>
      */
     protected $except = [
         'callback/notify',

+ 3 - 2
app/Http/Middleware/WebApi.php

@@ -5,18 +5,19 @@ namespace App\Http\Middleware;
 use App\Helpers\ResponseEnum;
 use App\Helpers\WebApiResponse;
 use Closure;
-use Illuminate\Http\Request;
 
 class WebApi
 {
     /**
      * Handle an incoming request.
      *
+     * @param  $request
+     * @param  Closure  $next
      * @return mixed
      */
     use WebApiResponse;
 
-    public function handle(Request $request, Closure $next)
+    public function handle($request, Closure $next)
     {
         $node = $request->node;
         $key = $request->header('key');

+ 33 - 31
app/Http/Middleware/isForbidden.php

@@ -3,7 +3,7 @@
 namespace App\Http\Middleware;
 
 use Agent;
-use App\Components\IP;
+use App\Utils\IP;
 use Closure;
 use Illuminate\Http\Request;
 use Log;
@@ -17,7 +17,7 @@ class isForbidden
      * @return mixed
      */
     public function handle(Request $request, Closure $next)
-    { // 限制机器人、指定IP访问.
+    {
         // 拒绝机器人访问
         if (sysConfig('is_forbid_robot') && Agent::isRobot()) {
             Log::warning('识别到机器人('.IP::getClientIp().')访问');
@@ -33,40 +33,42 @@ class isForbidden
         }
 
         $ip = IP::getClientIP();
-        $ipLocation = IP::getIPInfo($ip);
+        if ($ip !== '::1') {
+            $ipLocation = IP::getIPInfo($ip);
 
-        // 拒绝无IP请求
-        if (! $ipLocation || empty(array_filter($ipLocation))) {
-            return Response::view('auth.error', ['message' => trans('errors.forbidden.access')], 403);
-        }
+            // 拒绝无IP请求
+            if (! $ipLocation || empty(array_filter($ipLocation))) {
+                return Response::view('auth.error', ['message' => trans('errors.forbidden.access')], 403);
+            }
 
-        if (! in_array($ipLocation['country'], ['本机地址', '局域网']) && sysConfig('forbid_mode')) {
-            // 拒绝大陆IP访问
-            switch (sysConfig('forbid_mode')) {
-                case 'ban_mainland':
-                    if (in_array($ipLocation['country'], ['China', '中国']) && ! in_array($ipLocation['province'], ['香港', '澳门', '台湾', '台湾省'])) {
-                        Log::warning('识别到大陆IP,拒绝访问:'.$ip);
+            if (! in_array($ipLocation['country'], ['本机地址', '局域网']) && sysConfig('forbid_mode')) {
+                // 拒绝大陆IP访问
+                switch (sysConfig('forbid_mode')) {
+                    case 'ban_mainland':
+                        if (in_array($ipLocation['country'], ['China', '中国', 'CN']) && ! in_array($ipLocation['region'], ['香港', '澳门', '台湾', '台湾省'])) {
+                            Log::warning('识别到大陆IP,拒绝访问:'.$ip);
 
-                        return Response::view('auth.error', ['message' => trans('errors.forbidden.china')], 403);
-                    }
-                    break;
-                case 'ban_china':
-                    if (in_array($ipLocation['country'], ['China', '中国', 'Taiwan', 'Hong Kong', 'Macao'])) {
-                        Log::warning('识别到中国IP,拒绝访问:'.$ip);
+                            return Response::view('auth.error', ['message' => trans('errors.forbidden.china')], 403);
+                        }
+                        break;
+                    case 'ban_china':
+                        if (in_array($ipLocation['country'], ['China', '中国', 'Taiwan', 'Hong Kong', 'Macao'])) {
+                            Log::warning('识别到中国IP,拒绝访问:'.$ip);
 
-                        return Response::view('auth.error', ['message' => trans('errors.forbidden.china')], 403);
-                    }
-                    break;
-                case 'ban_oversea':
-                    if (! in_array($ipLocation['country'], ['China', '中国', 'Taiwan', 'Hong Kong', 'Macao'])) {
-                        Log::warning('识别到海外IP,拒绝访问:'.$ip.' - '.$ipLocation['country']);
+                            return Response::view('auth.error', ['message' => trans('errors.forbidden.china')], 403);
+                        }
+                        break;
+                    case 'ban_oversea':
+                        if (! in_array($ipLocation['country'], ['China', '中国', 'Taiwan', 'Hong Kong', 'Macao'])) {
+                            Log::warning('识别到海外IP,拒绝访问:'.$ip.' - '.$ipLocation['country']);
 
-                        return Response::view('auth.error', ['message' => trans('errors.forbidden.oversea')], 403);
-                    }
-                    break;
-                default:
-                    Log::emergency('未知禁止访问模式!请在系统设置中修改【禁止访问模式】!');
-                    break;
+                            return Response::view('auth.error', ['message' => trans('errors.forbidden.oversea')], 403);
+                        }
+                        break;
+                    default:
+                        Log::emergency('未知禁止访问模式!请在系统设置中修改【禁止访问模式】!');
+                        break;
+                }
             }
         }
 

+ 1 - 1
app/Http/Middleware/isSecurity.php

@@ -2,7 +2,7 @@
 
 namespace App\Http\Middleware;
 
-use App\Components\IP;
+use App\Utils\IP;
 use Cache;
 use Closure;
 use Log;

+ 1 - 1
app/Http/Requests/Admin/CertRequest.php

@@ -6,7 +6,7 @@ use Illuminate\Foundation\Http\FormRequest;
 
 class CertRequest extends FormRequest
 {
-    public function rules()
+    public function rules(): array
     {
         $unq_domain = '';
         if (in_array($this->method(), ['PATCH', 'PUT'], true)) {

+ 1 - 1
app/Http/Requests/Admin/NodeRequest.php

@@ -50,7 +50,7 @@ class NodeRequest extends FormRequest
         ];
     }
 
-    public function messages()
+    public function messages(): array
     {
         return [
             'server.required_if' => '开启DDNS, 域名不能为空',

+ 1 - 1
app/Http/Requests/Admin/PermissionRequest.php

@@ -6,7 +6,7 @@ use Illuminate\Foundation\Http\FormRequest;
 
 class PermissionRequest extends FormRequest
 {
-    public function rules()
+    public function rules(): array
     {
         return [
             'name' => 'required|string',

+ 1 - 1
app/Http/Requests/Admin/RbacRequest.php

@@ -6,7 +6,7 @@ use Illuminate\Foundation\Http\FormRequest;
 
 class RbacRequest extends FormRequest
 {
-    public function rules()
+    public function rules(): array
     {
         return [
             'name' => 'required|string',

+ 1 - 1
app/Http/Requests/Admin/RoleRequest.php

@@ -6,7 +6,7 @@ use Illuminate\Foundation\Http\FormRequest;
 
 class RoleRequest extends FormRequest
 {
-    public function rules()
+    public function rules(): array
     {
         $unq_name = '';
         if (in_array($this->method(), ['PATCH', 'PUT'], true)) {

+ 1 - 1
app/Http/Requests/Admin/RuleGroupRequest.php

@@ -6,7 +6,7 @@ use Illuminate\Foundation\Http\FormRequest;
 
 class RuleGroupRequest extends FormRequest
 {
-    public function rules()
+    public function rules(): array
     {
         return [
             'name' => 'required|string',

+ 1 - 1
app/Http/Requests/Admin/RuleRequest.php

@@ -6,7 +6,7 @@ use Illuminate\Foundation\Http\FormRequest;
 
 class RuleRequest extends FormRequest
 {
-    public function rules()
+    public function rules(): array
     {
         $rules = [
             'name' => 'required|string',

+ 2 - 2
app/Http/Requests/Admin/SystemRequest.php

@@ -6,7 +6,7 @@ use Illuminate\Foundation\Http\FormRequest;
 
 class SystemRequest extends FormRequest
 {
-    public function rules()
+    public function rules(): array
     {
         return [
             'name' => 'required|exists:config,name',
@@ -14,7 +14,7 @@ class SystemRequest extends FormRequest
         ];
     }
 
-    public function messages()
+    public function messages(): array
     {
         return [
             'name.exists' => '设置项目不存在于数据库',

+ 1 - 1
app/Http/Requests/Admin/TicketRequest.php

@@ -6,7 +6,7 @@ use Illuminate\Foundation\Http\FormRequest;
 
 class TicketRequest extends FormRequest
 {
-    public function rules()
+    public function rules(): array
     {
         return [
             'uid' => 'required_without:username|exists:user,id|numeric|nullable',

+ 1 - 1
app/Http/Requests/Admin/UserGroupRequest.php

@@ -6,7 +6,7 @@ use Illuminate\Foundation\Http\FormRequest;
 
 class UserGroupRequest extends FormRequest
 {
-    public function rules()
+    public function rules(): array
     {
         return [
             'name' => 'required|string',

Some files were not shown because too many files changed in this diff