Browse Source

Merge pull request #2092 from sspanel-uim/dev

Dev 20230731
M1Screw 2 years ago
parent
commit
2f334c016d
71 changed files with 662 additions and 708 deletions
  1. 9 6
      app/routes.php
  2. 104 104
      composer.lock
  3. 4 0
      resources/views/tabler/admin/header.tpl
  4. 5 14
      resources/views/tabler/admin/index.tpl
  5. 64 0
      resources/views/tabler/admin/system.tpl
  6. 1 1
      resources/views/tabler/auth/login.tpl
  7. 2 2
      resources/views/tabler/user/announcement.tpl
  8. 0 9
      resources/views/tabler/user/detect/index.tpl
  9. 11 11
      resources/views/tabler/user/detect/log.tpl
  10. 7 7
      resources/views/tabler/user/edit.tpl
  11. 45 45
      resources/views/tabler/user/index.tpl
  12. 1 1
      resources/views/tabler/user/invite.tpl
  13. 19 28
      resources/views/tabler/user/money.tpl
  14. 1 1
      resources/views/tabler/user/profile.tpl
  15. 1 11
      resources/views/tabler/user/server.tpl
  16. 1 1
      src/Command/Cron.php
  17. 2 1
      src/Command/Migration.php
  18. 1 1
      src/Command/Tool.php
  19. 73 0
      src/Controllers/Admin/LoginLogController.php
  20. 1 1
      src/Controllers/Admin/MoneyLogController.php
  21. 5 2
      src/Controllers/Admin/NodeController.php
  22. 5 62
      src/Controllers/Admin/OnlineIpController.php
  23. 1 1
      src/Controllers/Admin/PaylistController.php
  24. 3 3
      src/Controllers/Admin/Setting/BillingController.php
  25. 54 0
      src/Controllers/Admin/SystemController.php
  26. 4 2
      src/Controllers/Admin/TicketController.php
  27. 2 2
      src/Controllers/Admin/UserController.php
  28. 22 20
      src/Controllers/AuthController.php
  29. 4 0
      src/Controllers/HomeController.php
  30. 2 1
      src/Controllers/LinkController.php
  31. 1 1
      src/Controllers/PasswordController.php
  32. 4 2
      src/Controllers/SubController.php
  33. 4 6
      src/Controllers/User/InfoController.php
  34. 1 1
      src/Controllers/User/InvoiceController.php
  35. 1 1
      src/Controllers/User/MoneyController.php
  36. 4 5
      src/Controllers/UserController.php
  37. 2 1
      src/Middleware/NodeToken.php
  38. 3 1
      src/Models/DetectBanLog.php
  39. 9 25
      src/Models/DetectLog.php
  40. 3 1
      src/Models/InviteCode.php
  41. 1 4
      src/Models/LoginIp.php
  42. 14 16
      src/Models/Node.php
  43. 2 0
      src/Models/OnlineLog.php
  44. 3 1
      src/Models/Payback.php
  45. 1 4
      src/Models/Product.php
  46. 6 9
      src/Models/Setting.php
  47. 1 2
      src/Models/SubscribeLog.php
  48. 39 183
      src/Models/User.php
  49. 3 7
      src/Models/UserCoupon.php
  50. 1 1
      src/Models/UserMoneyLog.php
  51. 2 2
      src/Services/Analytics.php
  52. 17 11
      src/Services/Auth/Cookie.php
  53. 19 11
      src/Services/Captcha.php
  54. 2 2
      src/Services/CronDetect.php
  55. 14 12
      src/Services/CronJob.php
  56. 1 1
      src/Services/Gateway/AbstractPayment.php
  57. 2 1
      src/Services/Gateway/Epay/EpaySubmit.php
  58. 2 1
      src/Services/Gateway/StripeCard.php
  59. 1 2
      src/Utils/ClassHelper.php
  60. 1 1
      src/Utils/Telegram.php
  61. 2 2
      src/Utils/Telegram/Callback.php
  62. 2 1
      src/Utils/Telegram/Commands/HelpCommand.php
  63. 6 3
      src/Utils/Telegram/Commands/StartCommand.php
  64. 1 2
      src/Utils/Telegram/Message.php
  65. 4 0
      src/Utils/Telegram/Process.php
  66. 2 1
      src/Utils/Telegram/Reply.php
  67. 4 36
      src/Utils/Telegram/TelegramTools.php
  68. 23 8
      src/Utils/Tools.php
  69. 0 1
      tests/App/Services/ConfigTest.php
  70. 0 1
      tests/App/Utils/HashTest.php
  71. 0 1
      tests/App/Utils/ToolsTest.php

+ 9 - 6
app/routes.php

@@ -189,11 +189,11 @@ return static function (Slim\App $app): void {
         $group->delete('/coupon/{id}', App\Controllers\Admin\CouponController::class . ':delete');
         $group->post('/coupon/{id}/disable', App\Controllers\Admin\CouponController::class . ':disable');
         // 登录日志
-        $group->get('/login', App\Controllers\Admin\IpController::class . ':login');
-        $group->post('/login/ajax', App\Controllers\Admin\IpController::class . ':ajaxLogin');
+        $group->get('/login', App\Controllers\Admin\LoginLogController::class . ':index');
+        $group->post('/login/ajax', App\Controllers\Admin\LoginLogController::class . ':ajax');
         // 在线IP
-        $group->get('/online', App\Controllers\Admin\IpController::class . ':online');
-        $group->post('/online/ajax', App\Controllers\Admin\IpController::class . ':ajaxOnline');
+        $group->get('/online', App\Controllers\Admin\OnlineIpController::class . ':index');
+        $group->post('/online/ajax', App\Controllers\Admin\OnlineIpController::class . ':ajax');
         // 订阅日志
         $group->get('/subscribe', App\Controllers\Admin\SubscribeLogController::class . ':index');
         $group->post('/subscribe/ajax', App\Controllers\Admin\SubscribeLogController::class . ':ajax');
@@ -206,11 +206,14 @@ return static function (Slim\App $app): void {
         $group->get('/trafficlog', App\Controllers\Admin\TrafficLogController::class . ':index');
         $group->post('/trafficlog/ajax', App\Controllers\Admin\TrafficLogController::class . ':ajax');
         // 用户余额日志
-        $group->get('/moneylog', App\Controllers\Admin\MoneyLogController::class . ':log');
+        $group->get('/moneylog', App\Controllers\Admin\MoneyLogController::class . ':index');
         $group->post('/moneylog/ajax', App\Controllers\Admin\MoneyLogController::class . ':ajax');
         // 支付网关日志
-        $group->get('/gateway', App\Controllers\Admin\PaylistController::class . ':gateway');
+        $group->get('/gateway', App\Controllers\Admin\PaylistController::class . ':index');
         $group->post('/gateway/ajax', App\Controllers\Admin\PaylistController::class . ':ajax');
+        // 系统状态
+        $group->get('/system', App\Controllers\Admin\SystemController::class . ':index');
+        $group->post('/system/check_update', App\Controllers\Admin\SystemController::class . ':checkUpdate');
         // 设置中心
         $group->get('/setting/billing', App\Controllers\Admin\Setting\BillingController::class . ':billing');
         $group->post('/setting/billing', App\Controllers\Admin\Setting\BillingController::class . ':saveBilling');

+ 104 - 104
composer.lock

@@ -69,16 +69,16 @@
         },
         {
             "name": "aws/aws-crt-php",
-            "version": "v1.2.1",
+            "version": "v1.2.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/awslabs/aws-crt-php.git",
-                "reference": "1926277fc71d253dfa820271ac5987bdb193ccf5"
+                "reference": "2f1dc7b7eda080498be96a4a6d683a41583030e9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/1926277fc71d253dfa820271ac5987bdb193ccf5",
-                "reference": "1926277fc71d253dfa820271ac5987bdb193ccf5",
+                "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/2f1dc7b7eda080498be96a4a6d683a41583030e9",
+                "reference": "2f1dc7b7eda080498be96a4a6d683a41583030e9",
                 "shasum": ""
             },
             "require": {
@@ -117,22 +117,22 @@
             ],
             "support": {
                 "issues": "https://github.com/awslabs/aws-crt-php/issues",
-                "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.1"
+                "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.2"
             },
-            "time": "2023-03-24T20:22:19+00:00"
+            "time": "2023-07-20T16:49:55+00:00"
         },
         {
             "name": "aws/aws-sdk-php",
-            "version": "3.276.4",
+            "version": "3.277.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "80509b932c7e8e917a5026a89bd7af1f2b34ed59"
+                "reference": "9f36e76a3e1f8c7eba8cacbfa1d5f1257cd04241"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/80509b932c7e8e917a5026a89bd7af1f2b34ed59",
-                "reference": "80509b932c7e8e917a5026a89bd7af1f2b34ed59",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/9f36e76a3e1f8c7eba8cacbfa1d5f1257cd04241",
+                "reference": "9f36e76a3e1f8c7eba8cacbfa1d5f1257cd04241",
                 "shasum": ""
             },
             "require": {
@@ -212,9 +212,9 @@
             "support": {
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.276.4"
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.277.4"
             },
-            "time": "2023-07-24T18:15:58+00:00"
+            "time": "2023-07-28T22:10:53+00:00"
         },
         {
             "name": "bacon/bacon-qr-code",
@@ -1233,16 +1233,16 @@
         },
         {
             "name": "illuminate/collections",
-            "version": "v10.15.0",
+            "version": "v10.16.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/collections.git",
-                "reference": "fba281693edb2caf429c872d59770a28a4e01595"
+                "reference": "07ec467c42b90ec6e7e2faec381e3457dcfd5571"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/collections/zipball/fba281693edb2caf429c872d59770a28a4e01595",
-                "reference": "fba281693edb2caf429c872d59770a28a4e01595",
+                "url": "https://api.github.com/repos/illuminate/collections/zipball/07ec467c42b90ec6e7e2faec381e3457dcfd5571",
+                "reference": "07ec467c42b90ec6e7e2faec381e3457dcfd5571",
                 "shasum": ""
             },
             "require": {
@@ -1284,11 +1284,11 @@
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2023-07-08T21:20:28+00:00"
+            "time": "2023-07-24T14:56:19+00:00"
         },
         {
             "name": "illuminate/conditionable",
-            "version": "v10.15.0",
+            "version": "v10.16.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/conditionable.git",
@@ -1334,7 +1334,7 @@
         },
         {
             "name": "illuminate/container",
-            "version": "v10.15.0",
+            "version": "v10.16.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/container.git",
@@ -1385,7 +1385,7 @@
         },
         {
             "name": "illuminate/contracts",
-            "version": "v10.15.0",
+            "version": "v10.16.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/contracts.git",
@@ -1433,16 +1433,16 @@
         },
         {
             "name": "illuminate/database",
-            "version": "v10.15.0",
+            "version": "v10.16.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/database.git",
-                "reference": "f40768ccf945a494c005000f348a13e8828a2621"
+                "reference": "2d4d2c0e3c728124c6d2b8e419c455d43902c2f7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/database/zipball/f40768ccf945a494c005000f348a13e8828a2621",
-                "reference": "f40768ccf945a494c005000f348a13e8828a2621",
+                "url": "https://api.github.com/repos/illuminate/database/zipball/2d4d2c0e3c728124c6d2b8e419c455d43902c2f7",
+                "reference": "2d4d2c0e3c728124c6d2b8e419c455d43902c2f7",
                 "shasum": ""
             },
             "require": {
@@ -1498,11 +1498,11 @@
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2023-07-08T21:13:43+00:00"
+            "time": "2023-07-24T14:38:25+00:00"
         },
         {
             "name": "illuminate/macroable",
-            "version": "v10.15.0",
+            "version": "v10.16.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/macroable.git",
@@ -1548,7 +1548,7 @@
         },
         {
             "name": "illuminate/pagination",
-            "version": "v10.15.0",
+            "version": "v10.16.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/pagination.git",
@@ -1598,16 +1598,16 @@
         },
         {
             "name": "illuminate/support",
-            "version": "v10.15.0",
+            "version": "v10.16.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/support.git",
-                "reference": "aa6c012527c05d1c2f11df0b84d9ba8742d2b88f"
+                "reference": "40b87e80d150339bc186256576e11ea3be3d0ff5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/support/zipball/aa6c012527c05d1c2f11df0b84d9ba8742d2b88f",
-                "reference": "aa6c012527c05d1c2f11df0b84d9ba8742d2b88f",
+                "url": "https://api.github.com/repos/illuminate/support/zipball/40b87e80d150339bc186256576e11ea3be3d0ff5",
+                "reference": "40b87e80d150339bc186256576e11ea3be3d0ff5",
                 "shasum": ""
             },
             "require": {
@@ -1665,7 +1665,7 @@
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2023-07-09T12:11:55+00:00"
+            "time": "2023-07-25T19:33:34+00:00"
         },
         {
             "name": "irazasyed/telegram-bot-sdk",
@@ -4765,16 +4765,16 @@
         },
         {
             "name": "stripe/stripe-php",
-            "version": "v10.18.0",
+            "version": "v10.19.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/stripe/stripe-php.git",
-                "reference": "c32549e443a619d1b885b99d624568ecae82d6a0"
+                "reference": "9ea3ba13791217bd697e896bb839d905d170cba6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/stripe/stripe-php/zipball/c32549e443a619d1b885b99d624568ecae82d6a0",
-                "reference": "c32549e443a619d1b885b99d624568ecae82d6a0",
+                "url": "https://api.github.com/repos/stripe/stripe-php/zipball/9ea3ba13791217bd697e896bb839d905d170cba6",
+                "reference": "9ea3ba13791217bd697e896bb839d905d170cba6",
                 "shasum": ""
             },
             "require": {
@@ -4820,9 +4820,9 @@
             ],
             "support": {
                 "issues": "https://github.com/stripe/stripe-php/issues",
-                "source": "https://github.com/stripe/stripe-php/tree/v10.18.0"
+                "source": "https://github.com/stripe/stripe-php/tree/v10.19.0"
             },
-            "time": "2023-07-20T16:59:49+00:00"
+            "time": "2023-07-27T23:18:52+00:00"
         },
         {
             "name": "symfony/deprecation-contracts",
@@ -4893,16 +4893,16 @@
         },
         {
             "name": "symfony/http-client",
-            "version": "v6.3.1",
+            "version": "v6.3.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-client.git",
-                "reference": "1c828a06aef2f5eeba42026dfc532d4fc5406123"
+                "reference": "15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-client/zipball/1c828a06aef2f5eeba42026dfc532d4fc5406123",
-                "reference": "1c828a06aef2f5eeba42026dfc532d4fc5406123",
+                "url": "https://api.github.com/repos/symfony/http-client/zipball/15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00",
+                "reference": "15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00",
                 "shasum": ""
             },
             "require": {
@@ -4965,7 +4965,7 @@
                 "http"
             ],
             "support": {
-                "source": "https://github.com/symfony/http-client/tree/v6.3.1"
+                "source": "https://github.com/symfony/http-client/tree/v6.3.2"
             },
             "funding": [
                 {
@@ -4981,7 +4981,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-06-24T11:51:27+00:00"
+            "time": "2023-07-05T08:41:27+00:00"
         },
         {
             "name": "symfony/http-client-contracts",
@@ -5063,16 +5063,16 @@
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v6.3.1",
+            "version": "v6.3.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
-                "reference": "e0ad0d153e1c20069250986cd9e9dd1ccebb0d66"
+                "reference": "43ed99d30f5f466ffa00bdac3f5f7aa9cd7617c3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e0ad0d153e1c20069250986cd9e9dd1ccebb0d66",
-                "reference": "e0ad0d153e1c20069250986cd9e9dd1ccebb0d66",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/43ed99d30f5f466ffa00bdac3f5f7aa9cd7617c3",
+                "reference": "43ed99d30f5f466ffa00bdac3f5f7aa9cd7617c3",
                 "shasum": ""
             },
             "require": {
@@ -5120,7 +5120,7 @@
             "description": "Defines an object-oriented layer for the HTTP specification",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/http-foundation/tree/v6.3.1"
+                "source": "https://github.com/symfony/http-foundation/tree/v6.3.2"
             },
             "funding": [
                 {
@@ -5136,7 +5136,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-06-24T11:51:27+00:00"
+            "time": "2023-07-23T21:58:39+00:00"
         },
         {
             "name": "symfony/options-resolver",
@@ -7820,16 +7820,16 @@
         },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "10.1.2",
+            "version": "10.1.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e"
+                "reference": "be1fe461fdc917de2a29a452ccf2657d325b443d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/db1497ec8dd382e82c962f7abbe0320e4882ee4e",
-                "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/be1fe461fdc917de2a29a452ccf2657d325b443d",
+                "reference": "be1fe461fdc917de2a29a452ccf2657d325b443d",
                 "shasum": ""
             },
             "require": {
@@ -7886,7 +7886,7 @@
             "support": {
                 "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
                 "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
-                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.2"
+                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.3"
             },
             "funding": [
                 {
@@ -7894,7 +7894,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2023-05-22T09:04:27+00:00"
+            "time": "2023-07-26T13:45:28+00:00"
         },
         {
             "name": "phpunit/php-file-iterator",
@@ -9201,16 +9201,16 @@
         },
         {
             "name": "slevomat/coding-standard",
-            "version": "8.13.3",
+            "version": "8.13.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/slevomat/coding-standard.git",
-                "reference": "daa7ae48cca0e19e320b02c3f3f1176538b6a5c4"
+                "reference": "4b2af2fb17773656d02fbfb5d18024ebd19fe322"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/daa7ae48cca0e19e320b02c3f3f1176538b6a5c4",
-                "reference": "daa7ae48cca0e19e320b02c3f3f1176538b6a5c4",
+                "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/4b2af2fb17773656d02fbfb5d18024ebd19fe322",
+                "reference": "4b2af2fb17773656d02fbfb5d18024ebd19fe322",
                 "shasum": ""
             },
             "require": {
@@ -9250,7 +9250,7 @@
             ],
             "support": {
                 "issues": "https://github.com/slevomat/coding-standard/issues",
-                "source": "https://github.com/slevomat/coding-standard/tree/8.13.3"
+                "source": "https://github.com/slevomat/coding-standard/tree/8.13.4"
             },
             "funding": [
                 {
@@ -9262,7 +9262,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-07-24T15:24:58+00:00"
+            "time": "2023-07-25T10:28:55+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",
@@ -9323,16 +9323,16 @@
         },
         {
             "name": "symfony/cache",
-            "version": "v6.3.1",
+            "version": "v6.3.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/cache.git",
-                "reference": "52cff7608ef6e38376ac11bd1fbb0a220107f066"
+                "reference": "d176b97600860df13e851538c2df2ad88e9e5ca9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/cache/zipball/52cff7608ef6e38376ac11bd1fbb0a220107f066",
-                "reference": "52cff7608ef6e38376ac11bd1fbb0a220107f066",
+                "url": "https://api.github.com/repos/symfony/cache/zipball/d176b97600860df13e851538c2df2ad88e9e5ca9",
+                "reference": "d176b97600860df13e851538c2df2ad88e9e5ca9",
                 "shasum": ""
             },
             "require": {
@@ -9399,7 +9399,7 @@
                 "psr6"
             ],
             "support": {
-                "source": "https://github.com/symfony/cache/tree/v6.3.1"
+                "source": "https://github.com/symfony/cache/tree/v6.3.2"
             },
             "funding": [
                 {
@@ -9415,7 +9415,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-06-24T11:51:27+00:00"
+            "time": "2023-07-27T16:19:14+00:00"
         },
         {
             "name": "symfony/cache-contracts",
@@ -9495,16 +9495,16 @@
         },
         {
             "name": "symfony/console",
-            "version": "v6.3.0",
+            "version": "v6.3.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7"
+                "reference": "aa5d64ad3f63f2e48964fc81ee45cb318a723898"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7",
-                "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7",
+                "url": "https://api.github.com/repos/symfony/console/zipball/aa5d64ad3f63f2e48964fc81ee45cb318a723898",
+                "reference": "aa5d64ad3f63f2e48964fc81ee45cb318a723898",
                 "shasum": ""
             },
             "require": {
@@ -9565,7 +9565,7 @@
                 "terminal"
             ],
             "support": {
-                "source": "https://github.com/symfony/console/tree/v6.3.0"
+                "source": "https://github.com/symfony/console/tree/v6.3.2"
             },
             "funding": [
                 {
@@ -9581,20 +9581,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-05-29T12:49:39+00:00"
+            "time": "2023-07-19T20:17:28+00:00"
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v6.3.0",
+            "version": "v6.3.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
-                "reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa"
+                "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa",
-                "reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/adb01fe097a4ee930db9258a3cc906b5beb5cf2e",
+                "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e",
                 "shasum": ""
             },
             "require": {
@@ -9645,7 +9645,7 @@
             "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.0"
+                "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.2"
             },
             "funding": [
                 {
@@ -9661,7 +9661,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-04-21T14:41:17+00:00"
+            "time": "2023-07-06T06:56:43+00:00"
         },
         {
             "name": "symfony/event-dispatcher-contracts",
@@ -9804,16 +9804,16 @@
         },
         {
             "name": "symfony/finder",
-            "version": "v6.3.0",
+            "version": "v6.3.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
-                "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2"
+                "reference": "78ce4c29757d657d2b41a91c328923b9a0d6b43d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/finder/zipball/d9b01ba073c44cef617c7907ce2419f8d00d75e2",
-                "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/78ce4c29757d657d2b41a91c328923b9a0d6b43d",
+                "reference": "78ce4c29757d657d2b41a91c328923b9a0d6b43d",
                 "shasum": ""
             },
             "require": {
@@ -9848,7 +9848,7 @@
             "description": "Finds files and directories via an intuitive fluent interface",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/finder/tree/v6.3.0"
+                "source": "https://github.com/symfony/finder/tree/v6.3.2"
             },
             "funding": [
                 {
@@ -9864,7 +9864,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-04-02T01:25:41+00:00"
+            "time": "2023-07-13T14:29:38+00:00"
         },
         {
             "name": "symfony/polyfill-php81",
@@ -9947,16 +9947,16 @@
         },
         {
             "name": "symfony/process",
-            "version": "v6.3.0",
+            "version": "v6.3.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
-                "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628"
+                "reference": "c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/8741e3ed7fe2e91ec099e02446fb86667a0f1628",
-                "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628",
+                "url": "https://api.github.com/repos/symfony/process/zipball/c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d",
+                "reference": "c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d",
                 "shasum": ""
             },
             "require": {
@@ -9988,7 +9988,7 @@
             "description": "Executes commands in sub-processes",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/process/tree/v6.3.0"
+                "source": "https://github.com/symfony/process/tree/v6.3.2"
             },
             "funding": [
                 {
@@ -10004,7 +10004,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-05-19T08:06:44+00:00"
+            "time": "2023-07-12T16:00:22+00:00"
         },
         {
             "name": "symfony/stopwatch",
@@ -10070,16 +10070,16 @@
         },
         {
             "name": "symfony/string",
-            "version": "v6.3.0",
+            "version": "v6.3.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/string.git",
-                "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f"
+                "reference": "53d1a83225002635bca3482fcbf963001313fb68"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/string/zipball/f2e190ee75ff0f5eced645ec0be5c66fac81f51f",
-                "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f",
+                "url": "https://api.github.com/repos/symfony/string/zipball/53d1a83225002635bca3482fcbf963001313fb68",
+                "reference": "53d1a83225002635bca3482fcbf963001313fb68",
                 "shasum": ""
             },
             "require": {
@@ -10136,7 +10136,7 @@
                 "utf8"
             ],
             "support": {
-                "source": "https://github.com/symfony/string/tree/v6.3.0"
+                "source": "https://github.com/symfony/string/tree/v6.3.2"
             },
             "funding": [
                 {
@@ -10152,20 +10152,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-03-21T21:06:29+00:00"
+            "time": "2023-07-05T08:41:27+00:00"
         },
         {
             "name": "symfony/var-exporter",
-            "version": "v6.3.0",
+            "version": "v6.3.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-exporter.git",
-                "reference": "db5416d04269f2827d8c54331ba4cfa42620d350"
+                "reference": "3400949782c0cb5b3e73aa64cfd71dde000beccc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-exporter/zipball/db5416d04269f2827d8c54331ba4cfa42620d350",
-                "reference": "db5416d04269f2827d8c54331ba4cfa42620d350",
+                "url": "https://api.github.com/repos/symfony/var-exporter/zipball/3400949782c0cb5b3e73aa64cfd71dde000beccc",
+                "reference": "3400949782c0cb5b3e73aa64cfd71dde000beccc",
                 "shasum": ""
             },
             "require": {
@@ -10210,7 +10210,7 @@
                 "serialize"
             ],
             "support": {
-                "source": "https://github.com/symfony/var-exporter/tree/v6.3.0"
+                "source": "https://github.com/symfony/var-exporter/tree/v6.3.2"
             },
             "funding": [
                 {
@@ -10226,7 +10226,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-04-21T08:48:44+00:00"
+            "time": "2023-07-26T17:39:03+00:00"
         },
         {
             "name": "theseer/tokenizer",

+ 4 - 0
resources/views/tabler/admin/header.tpl

@@ -135,6 +135,10 @@
                                                 <i class="ti ti-server-2"></i>&nbsp;
                                                 节点
                                             </a>
+                                            <a class="dropdown-item" href="/admin/system">
+                                                <i class="ti ti-tool"></i>&nbsp;
+                                                系统
+                                            </a>
                                         </div>
                                     </div>
                                 </div>

+ 5 - 14
resources/views/tabler/admin/index.tpl

@@ -6,10 +6,10 @@
             <div class="row align-items-center">
                 <div class="col">
                     <h2 class="page-title">
-                        <span class="home-title">系统概况</span>
+                        <span class="home-title">站点概况</span>
                     </h2>
                     <div class="page-pretitle my-3">
-                        <span class="home-subtitle">在这里查看系统的的各项运营指标</span>
+                        <span class="home-subtitle">站点运营状态总览</span>
                     </div>
                 </div>
             </div>
@@ -191,10 +191,7 @@
                     fillSeriesColor: false
                 },
             })).render();
-        });
-    </script>
-    <script>
-        document.addEventListener("DOMContentLoaded", function() {
+
             window.ApexCharts && (new ApexCharts(document.getElementById('node-online'), {
                 chart: {
                     type: "donut",
@@ -234,10 +231,7 @@
                     fillSeriesColor: false
                 },
             })).render();
-        });
-    </script>
-    <script>
-        document.addEventListener("DOMContentLoaded", function() {
+
             window.ApexCharts && (new ApexCharts(document.getElementById('user-inactive'), {
                 chart: {
                     type: "donut",
@@ -277,10 +271,7 @@
                     fillSeriesColor: false
                 },
             })).render();
-        });
-    </script>
-    <script>
-        document.addEventListener("DOMContentLoaded", function() {
+
             window.ApexCharts && (new ApexCharts(document.getElementById('traffic-usage'), {
                 chart: {
                     type: "donut",

+ 64 - 0
resources/views/tabler/admin/system.tpl

@@ -0,0 +1,64 @@
+{include file='admin/header.tpl'}
+
+<div class="page-wrapper">
+    <div class="container-xl">
+        <div class="page-header d-print-none text-white">
+            <div class="row align-items-center">
+                <div class="col">
+                    <h2 class="page-title">
+                        <span class="home-title">系统状态</span>
+                    </h2>
+                    <div class="page-pretitle my-3">
+                        <span class="home-subtitle">查看系统的运行状态</span>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="page-body">
+        <div class="container-xl">
+            <div class="row row-deck row-cards">
+                 <div class="col-sm-12 col-lg-12">
+                     <div class="card">
+                         <div class="card-body">
+                             <table class="table table-transparent table-responsive">
+                                 <tr>
+                                     <td>SSPanel-UIM 版本</td>
+                                     <td class="text-end" id="version"><a href="#" id="version_check">{$version} </a></td>
+                                 </tr>
+                                 <tr>
+                                     <td>数据库版本</td>
+                                     <td class="text-end">{$db_version}</td>
+                                 </tr>
+                                 <tr>
+                                     <td>最后一次每日任务执行时间</td>
+                                     <td class="text-end">{$last_daily_job_time}</td>
+                                 </tr>
+                             </table>
+                         </div>
+                     </div>
+                 </div>
+            </div>
+        </div>
+    </div>
+
+    <script>
+        $('#version_check').click(function() {
+            $.ajax({
+                url: '/admin/system/check_update',
+                type: 'POST',
+                dataType: "json",
+                success: function(data) {
+                    if (data.is_upto_date === true) {
+                        $('.badge').remove();
+                        $('#version').append('<span class="badge bg-green">已是最新版本</span>');
+                    } else {
+                        $('.badge').remove();
+                        $('#version').append('<span class="badge bg-red">有新版本 ' + data.latest_version + ' 可用</span>');
+                    }
+                }
+            })
+        });
+    </script>
+
+{include file='admin/footer.tpl'}

+ 1 - 1
resources/views/tabler/auth/login.tpl

@@ -73,7 +73,7 @@
                     code: $('#code').val(),
                     email: $('#email').val(),
                     passwd: $('#passwd').val(),
-                    remember_me: $('#remember_me').val(),
+                    remember_me: $('#remember_me').is(":checked"),
                     {if $public_setting['enable_login_captcha']}
                         {if $public_setting['captcha_provider'] === 'turnstile'}
                             turnstile: $('input[name=cf-turnstile-response]').val(),

+ 2 - 2
resources/views/tabler/user/announcement.tpl

@@ -18,13 +18,13 @@
     <div class="page-body">
         <div class="container-xl">
             <div class="row row-deck row-cards">
-                <div class="col-12">
+                <div class="col-sm-12 col-lg-12">
                     <div class="card">
                         <div class="table-responsive">
                             <table class="table table-vcenter card-table">
                                 <thead>
                                     <tr>
-                                        <th>ID</th>
+                                        <th>公告ID</th>
                                         <th>发布日期</th>
                                         <th>公告内容</th>
                                     </tr>

+ 0 - 9
resources/views/tabler/user/detect/index.tpl

@@ -19,15 +19,6 @@
     <div class="page-body">
         <div class="container-xl">
             <div class="row row-deck row-cards">
-                <div class="col-12">
-                    <div class="card">
-                        <div class="card-body">
-                            <div class="m-0 my-2">
-                                <p>为了防止滥用与确保站点可以稳定运行,特制定了如下过滤规则,当你使用节点执行这些动作时,你的通信就会被截断。</p>
-                            </div>
-                        </div>
-                    </div>
-                </div>
                 <div class="col-12">
                     <div class="card">
                         <div class="table-responsive">

+ 11 - 11
resources/views/tabler/user/detect/log.tpl

@@ -37,17 +37,17 @@
                                 </thead>
                                 <tbody>
                                 {foreach $logs as $log}
-                                <tr>
-                                    <td>#{$log->id}</td>
-                                    <td>{$log->node_id}</td>
-                                    <td>{$log->node_name}</td>
-                                    <td>{$log->list_id}</td>
-                                    <td>{$log->rule->name}</td>
-                                    <td>{$log->rule->text}</td>
-                                    <td>{$log->rule->regex}</td>
-                                    <td>{$log->rule->type}</td>
-                                    <td>{$log->datetime}</td>
-                                </tr>
+                                    <tr>
+                                        <td>#{$log->id}</td>
+                                        <td>{$log->node_id}</td>
+                                        <td>{$log->node_name}</td>
+                                        <td>{$log->list_id}</td>
+                                        <td>{$log->rule->name}</td>
+                                        <td>{$log->rule->text}</td>
+                                        <td>{$log->rule->regex}</td>
+                                        <td>{$log->rule->type}</td>
+                                        <td>{$log->datetime}</td>
+                                    </tr>
                                 {/foreach}
                                 </tbody>
                             </table>

+ 7 - 7
resources/views/tabler/user/edit.tpl

@@ -17,7 +17,7 @@
     </div>
     <div class="page-body">
         <div class="container-xl">
-            <div class="row row-cards">
+            <div class="row row-deck row-cards">
                 <div class="col-12">
                     <div class="card">
                         <ul class="nav nav-tabs nav-fill" data-bs-toggle="tabs" role="tablist">
@@ -53,7 +53,7 @@
                         <div class="card-body">
                             <div class="tab-content">
                                 <div class="tab-pane active show" id="personal_information" role="tabpanel">
-                                    <div class="row row-cards">
+                                    <div class="row row-deck row-cards">
                                         <div class="col-sm-12 col-md-6">
                                             <div class="card">
                                                 <div class="card-body">
@@ -194,7 +194,7 @@
                                     </div>
                                 </div>
                                 <div class="tab-pane" id="login_security" role="tabpanel">
-                                    <div class="row row-cards">
+                                    <div class="row row-deck row-cards">
                                         <div class="col-sm-12 col-md-6">
                                             <div class="card">
                                                 <div class="card-body">
@@ -283,7 +283,7 @@
                                     </div>
                                 </div>
                                 <div class="tab-pane" id="use_safety" role="tabpanel">
-                                    <div class="row row-cards">
+                                    <div class="row row-deck row-cards">
                                         <div class="col-sm-12 col-md-6">
                                             <div class="card">
                                                 <div class="card-body">
@@ -340,15 +340,15 @@
                                     </div>
                                 </div>
                                 <div class="tab-pane" id="other_settings" role="tabpanel">
-                                    <div class="row row-cards">
+                                    <div class="row row-deck row-cards">
                                         <div class="col-sm-12 col-md-6">
                                             <div class="card">
                                                 <div class="card-body">
-                                                    <h3 class="card-title">每日用量推送</h3>
+                                                    <h3 class="card-title">每日流量报告</h3>
                                                     <div class="mb-3">
                                                         <select id="daily-report" class="form-select">
                                                             <option value="0" {if $user->daily_mail_enable === 0}selected{/if}>
-                                                                不发送
+                                                                不接收
                                                             </option>
                                                             <option value="1" {if $user->daily_mail_enable === 1}selected{/if}>
                                                                 邮件接收

+ 45 - 45
resources/views/tabler/user/index.tpl

@@ -463,10 +463,11 @@
                             <i class="ti ti-bell-ringing icon"></i>
                         </div>
                         <div class="card-body">
-                            <h3 class="card-title">最新公告
-                            {if $ann !== null}
-                            <span class="card-subtitle">{$ann->date}</span>
-                            {/if}
+                            <h3 class="card-title">
+                                最新公告
+                                {if $ann !== null}
+                                <span class="card-subtitle">{$ann->date}</span>
+                                {/if}
                             </h3>
                             <p class="text-secondary">
                             {if $ann !== null}
@@ -479,55 +480,54 @@
                     </div>
                 </div>
                 {if $config['enable_checkin']}
-                    <div class="col-lg-6 col-sm-12">
-                        <div class="card">
-                            <div class="card-stamp">
-                                <div class="card-stamp-icon bg-green">
-                                    <i class="ti ti-check"></i>
-                                </div>
-                            </div>
-                            <div class="card-body">
-                                <h3 class="card-title">每日签到</h3>
-                                <p>
-                                    签到可领取
-                                    {if $config['checkinMin'] !== $config['checkinMax']}
-                                        &nbsp;<code>{$config['checkinMin']} MB</code> 至 <code>{$config['checkinMax']} MB</code>
-                                        范围内的流量
-                                    {else}
-                                        <code>{$config['checkinMin']} MB</code>
-                                    {/if}
-                                </p>
-                                <p>
-                                    上次签到时间:<code>{$user->lastCheckInTime()}</code>
-                                </p>
+                <div class="col-lg-6 col-sm-12">
+                    <div class="card">
+                        <div class="card-stamp">
+                            <div class="card-stamp-icon bg-green">
+                                <i class="ti ti-check"></i>
                             </div>
-                            <div class="card-footer">
-                                <div class="d-flex">
-                                    {if ! $user->isAbleToCheckin()}
-                                    <button id="check-in" class="btn btn-primary ms-auto" disabled>已签到</button>
-                                    {else}
-                                    {if $public_setting['enable_checkin_captcha']}
-                                        {if $public_setting['captcha_provider'] === 'turnstile'}
-                                            <div id="cf-turnstile" class="cf-turnstile" data-sitekey="{$captcha['turnstile_sitekey']}"
-                                            {if $user->is_dark_mode}
-                                                 data-theme="dark"
-                                            {else}
-                                                 data-theme="light"
-                                            {/if}
-                                            ></div>
-                                        {/if}
-                                        {if $public_setting['captcha_provider'] === 'geetest'}
-                                            <div id="geetest"></div>
+                        </div>
+                        <div class="card-body">
+                            <h3 class="card-title">每日签到</h3>
+                            <p>
+                                签到可领取
+                                {if $config['checkinMin'] !== $config['checkinMax']}
+                                    &nbsp;<code>{$config['checkinMin']} MB</code> 至 <code>{$config['checkinMax']} MB</code>
+                                    范围内的流量
+                                {else}
+                                    <code>{$config['checkinMin']} MB</code>
+                                {/if}
+                            </p>
+                            <p>
+                                上次签到时间:<code>{$user->lastCheckInTime()}</code>
+                            </p>
+                        </div>
+                        <div class="card-footer">
+                            <div class="d-flex">
+                                {if ! $user->isAbleToCheckin()}
+                                <button id="check-in" class="btn btn-primary ms-auto" disabled>已签到</button>
+                                {else}
+                                {if $public_setting['enable_checkin_captcha']}
+                                    {if $public_setting['captcha_provider'] === 'turnstile'}
+                                        <div id="cf-turnstile" class="cf-turnstile" data-sitekey="{$captcha['turnstile_sitekey']}"
+                                        {if $user->is_dark_mode}
+                                             data-theme="dark"
+                                        {else}
+                                             data-theme="light"
                                         {/if}
+                                        ></div>
                                     {/if}
-                                    <button id="check-in" class="btn btn-primary ms-auto">签到</button>
+                                    {if $public_setting['captcha_provider'] === 'geetest'}
+                                        <div id="geetest"></div>
                                     {/if}
-                                </div>
+                                {/if}
+                                <button id="check-in" class="btn btn-primary ms-auto">签到</button>
+                                {/if}
                             </div>
                         </div>
                     </div>
+                </div>
                 {/if}
-
             </div>
         </div>
     </div>

+ 1 - 1
resources/views/tabler/user/invite.tpl

@@ -17,7 +17,7 @@
     </div>
     <div class="page-body">
         <div class="container-xl">
-            <div class="row row-cards">
+            <div class="row row-deck row-cards">
                 <div class="col-12">
                     <div class="row row-deck row-cards">
                         <div class="col-sm-12 col-lg-6">

+ 19 - 28
resources/views/tabler/user/money.tpl

@@ -26,16 +26,12 @@
     </div>
     <div class="page-body">
         <div class="container-xl">
-            <div class="row row-deck">
+            <div class="row row-deck row-cards">
                 <div class="col-sm-12 col-lg-12">
                     <div class="card">
-                        <div class="card-header">
-                            <h3 class="card-title">账户余额记录</h3>
-                        </div>
-                        {if $moneylog_count !== 0}
-                            <div class="table-responsive">
-                                <table class="table card-table table-vcenter text-nowrap datatable">
-                                    <thead>
+                        <div class="table-responsive">
+                            <table class="table card-table table-vcenter text-nowrap datatable">
+                                <thead>
                                     <tr>
                                         <th>事件ID</th>
                                         <th>变动前余额</th>
@@ -44,26 +40,21 @@
                                         <th>备注</th>
                                         <th>变动时间</th>
                                     </tr>
-                                    </thead>
-                                    <tbody>
-                                    {foreach $moneylogs as $moneylog}
-                                        <tr>
-                                            <td>{$moneylog->id}</td>
-                                            <td>{$moneylog->before}</td>
-                                            <td>{$moneylog->after}</td>
-                                            <td>{$moneylog->amount}</td>
-                                            <td>{$moneylog->remark}</td>
-                                            <td>{$moneylog->create_time}</td>
-                                        </tr>
-                                    {/foreach}
-                                    </tbody>
-                                </table>
-                            </div>
-                        {else}
-                            <div class="card-body">
-                                <p>没有找到记录</p>
-                            </div>
-                        {/if}
+                                </thead>
+                                <tbody>
+                                {foreach $moneylogs as $moneylog}
+                                    <tr>
+                                        <td>{$moneylog->id}</td>
+                                        <td>{$moneylog->before}</td>
+                                        <td>{$moneylog->after}</td>
+                                        <td>{$moneylog->amount}</td>
+                                        <td>{$moneylog->remark}</td>
+                                        <td>{$moneylog->create_time}</td>
+                                    </tr>
+                                {/foreach}
+                                </tbody>
+                            </table>
+                        </div>
                     </div>
                 </div>
             </div>

+ 1 - 1
resources/views/tabler/user/profile.tpl

@@ -17,7 +17,7 @@
     </div>
     <div class="page-body">
         <div class="container-xl">
-            <div class="row row-deck">
+            <div class="row row-deck row-cards">
                 <div class="col-sm-6 col-lg-3">
                     <div class="card">
                         <div class="card-body">

+ 1 - 11
resources/views/tabler/user/server.tpl

@@ -17,17 +17,7 @@
     </div>
     <div class="page-body">
         <div class="container-xl">
-            <div class="row row-cards">
-                <div class="col-12">
-                    <div class="card">
-                        <div class="card-body">
-                            <div class="m-0 my-2">
-                                <p>描述中分别表述为:该节点的在线人数,该节点的流量倍率,该节点的类型</p>
-                                <p>指示灯为绿色表示正常运行;为黄色表示当月流量用尽;为橙色表示未配置成功;为红色表示已离线,不可使用</p>
-                            </div>
-                        </div>
-                    </div>
-                </div>
+            <div class="row row-deck row-cards">
                 <div class="col-12">
                     <div class="card">
                         <div class="card-body">

+ 1 - 1
src/Command/Cron.php

@@ -78,7 +78,7 @@ EOL;
                 $jobs->sendTelegramDailyJob();
             }
 
-            Setting::where('item', '=', 'last_daily_job_time')->update([
+            Setting::where('item', 'last_daily_job_time')->update([
                 'value' => mktime(
                     Setting::obtain('daily_job_hour'),
                     Setting::obtain('daily_job_minute'),

+ 2 - 1
src/Command/Migration.php

@@ -105,7 +105,8 @@ END;
         }
 
         $sql = match ($target) {
-            'new' => 'INSERT INTO `config` (`item`, `value`, `type`, `default`) VALUES("db_version", ?, "int", "2023020100")',
+            'new' => 'INSERT INTO `config` (`item`, `value`, `type`, `default`)
+                        VALUES("db_version", ?, "int", "2023020100")',
             default => 'UPDATE `config` SET `value` = ? WHERE `item` = "db_version"'
         };
         $stat = DB::getPdo()->prepare($sql);

+ 1 - 1
src/Command/Tool.php

@@ -125,7 +125,7 @@ EOL;
         foreach ($settings as $item) {
             $config[] = $item['item'];
             $item_name = $item['item'];
-            $query = Setting::where('item', '=', $item['item'])->first();
+            $query = Setting::where('item', $item['item'])->first();
 
             if ($query === null) {
                 $new_item = new Setting();

+ 73 - 0
src/Controllers/Admin/LoginLogController.php

@@ -0,0 +1,73 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Controllers\Admin;
+
+use App\Controllers\BaseController;
+use App\Models\LoginIp;
+use App\Utils\Tools;
+use Exception;
+use MaxMind\Db\Reader\InvalidDatabaseException;
+use Psr\Http\Message\ResponseInterface;
+use Slim\Http\Response;
+use Slim\Http\ServerRequest;
+
+final class LoginLogController extends BaseController
+{
+    public static array $details =
+        [
+            'field' => [
+                'id' => '事件ID',
+                'userid' => '用户ID',
+                'user_name' => '用户名',
+                'ip' => 'IP',
+                'location' => 'IP归属地',
+                'datetime' => '时间',
+                'type' => '类型',
+            ],
+        ];
+
+    /**
+     * 后台登录记录页面
+     *
+     * @throws Exception
+     */
+    public function index(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    {
+        return $response->write(
+            $this->view()
+                ->assign('details', self::$details)
+                ->fetch('admin/log/login.tpl')
+        );
+    }
+
+    /**
+     * 后台登录记录页面 AJAX
+     *
+     * @throws InvalidDatabaseException
+     */
+    public function ajax(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    {
+        $length = $request->getParam('length');
+        $page = $request->getParam('start') / $length + 1;
+        $draw = $request->getParam('draw');
+
+        $logins = LoginIp::orderBy('id', 'desc')->paginate($length, '*', '', $page);
+        $total = LoginIp::count();
+
+        foreach ($logins as $login) {
+            $login->user_name = $login->userName();
+            $login->location = Tools::getIpLocation($login->ip);
+            $login->datetime = Tools::toDateTime((int) $login->datetime);
+            $login->type = $login->type();
+        }
+
+        return $response->withJson([
+            'draw' => $draw,
+            'recordsTotal' => $total,
+            'recordsFiltered' => $total,
+            'logins' => $logins,
+        ]);
+    }
+}

+ 1 - 1
src/Controllers/Admin/MoneyLogController.php

@@ -32,7 +32,7 @@ final class MoneyLogController extends BaseController
      *
      * @throws Exception
      */
-    public function log(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    public function index(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     {
         return $response->write(
             $this->view()

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

@@ -266,8 +266,11 @@ final class NodeController extends BaseController
         ]);
     }
 
-    public function resetNodePassword(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
-    {
+    public function resetNodePassword(
+        ServerRequest $request,
+        Response $response,
+        array $args
+    ): Response|ResponseInterface {
         $id = $args['id'];
         $node = Node::find($id);
         $password = Tools::genRandomChar(32);

+ 5 - 62
src/Controllers/Admin/IpController.php → src/Controllers/Admin/OnlineIpController.php

@@ -5,7 +5,6 @@ declare(strict_types=1);
 namespace App\Controllers\Admin;
 
 use App\Controllers\BaseController;
-use App\Models\LoginIp;
 use App\Services\DB;
 use App\Utils\Tools;
 use Exception;
@@ -18,22 +17,9 @@ use function array_slice;
 use function count;
 use function str_replace;
 
-final class IpController extends BaseController
+final class OnlineIpController extends BaseController
 {
-    public static array $login_details =
-        [
-            'field' => [
-                'id' => '事件ID',
-                'userid' => '用户ID',
-                'user_name' => '用户名',
-                'ip' => 'IP',
-                'location' => 'IP归属地',
-                'datetime' => '时间',
-                'type' => '类型',
-            ],
-        ];
-
-    public static array $ip_details =
+    public static array $details =
         [
             'field' => [
                 'id' => '事件ID',
@@ -48,59 +34,16 @@ final class IpController extends BaseController
             ],
         ];
 
-    /**
-     * 后台登录记录页面
-     *
-     * @throws Exception
-     */
-    public function login(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
-    {
-        return $response->write(
-            $this->view()
-                ->assign('details', self::$login_details)
-                ->fetch('admin/log/login.tpl')
-        );
-    }
-
-    /**
-     * 后台登录记录页面 AJAX
-     *
-     * @throws InvalidDatabaseException
-     */
-    public function ajaxLogin(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
-    {
-        $length = $request->getParam('length');
-        $page = $request->getParam('start') / $length + 1;
-        $draw = $request->getParam('draw');
-
-        $logins = LoginIp::orderBy('id', 'desc')->paginate($length, '*', '', $page);
-        $total = LoginIp::count();
-
-        foreach ($logins as $login) {
-            $login->user_name = $login->userName();
-            $login->location = Tools::getIpLocation($login->ip);
-            $login->datetime = Tools::toDateTime((int) $login->datetime);
-            $login->type = $login->type();
-        }
-
-        return $response->withJson([
-            'draw' => $draw,
-            'recordsTotal' => $total,
-            'recordsFiltered' => $total,
-            'logins' => $logins,
-        ]);
-    }
-
     /**
      * 后台在线 IP 页面
      *
      * @throws Exception
      */
-    public function online(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    public function index(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     {
         return $response->write(
             $this->view()
-                ->assign('details', self::$ip_details)
+                ->assign('details', self::$details)
                 ->fetch('admin/log/online.tpl')
         );
     }
@@ -110,7 +53,7 @@ final class IpController extends BaseController
      *
      * @throws InvalidDatabaseException
      */
-    public function ajaxOnline(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    public function ajax(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     {
         $data = $request->getParsedBody();
         $length = (int) ($data['length'] ?? 0);

+ 1 - 1
src/Controllers/Admin/PaylistController.php

@@ -33,7 +33,7 @@ final class PaylistController extends BaseController
      *
      * @throws Exception
      */
-    public function gateway(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    public function index(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     {
         return $response->write(
             $this->view()

+ 3 - 3
src/Controllers/Admin/Setting/BillingController.php

@@ -83,7 +83,7 @@ final class BillingController extends BaseController
             }
         }
 
-        $gateway = Setting::where('item', '=', 'payment_gateway')->first();
+        $gateway = Setting::where('item', 'payment_gateway')->first();
         $gateway->value = json_encode($gateway_in_use);
 
         if (! $gateway->save()) {
@@ -96,7 +96,7 @@ final class BillingController extends BaseController
         $list = self::$update_field;
 
         foreach ($list as $item) {
-            $setting = Setting::where('item', '=', $item)->first();
+            $setting = Setting::where('item', $item)->first();
 
             if ($setting->type === 'array') {
                 $setting->value = json_encode($request->getParam($item));
@@ -131,7 +131,7 @@ final class BillingController extends BaseController
 
     public function returnActiveGateways()
     {
-        $payment_gateways = Setting::where('item', '=', 'payment_gateway')->first();
+        $payment_gateways = Setting::where('item', 'payment_gateway')->first();
         return json_decode($payment_gateways->value);
     }
 }

+ 54 - 0
src/Controllers/Admin/SystemController.php

@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Controllers\Admin;
+
+use App\Controllers\BaseController;
+use App\Models\Setting;
+use App\Utils\Tools;
+use Exception;
+use Psr\Http\Message\ResponseInterface;
+use Slim\Http\Response;
+use Slim\Http\ServerRequest;
+use const VERSION;
+
+final class SystemController extends BaseController
+{
+    /**
+     * 后台系统状态页面
+     *
+     * @throws Exception
+     */
+    public function index(ServerRequest $request, Response $response, array $args): ResponseInterface
+    {
+        $last_daily_job_time = Tools::toDateTime(Setting::obtain('last_daily_job_time'));
+        $db_version = Setting::obtain('db_version');
+
+        return $response->write(
+            $this->view()
+                ->assign('version', VERSION)
+                ->assign('last_daily_job_time', $last_daily_job_time)
+                ->assign('db_version', $db_version)
+                ->fetch('admin/system.tpl')
+        );
+    }
+
+    /**
+     * 检查版本更新
+     */
+    public function checkUpdate(ServerRequest $request, Response $response, array $args): ResponseInterface
+    {
+        $latest_version = file_get_contents('https://ota.sspanel.org/get-latest-version', false, stream_context_create([
+            'http' => [
+                'timeout' => 3,
+            ],
+        ]));
+        $is_upto_date = version_compare($latest_version, VERSION, '<=');
+
+        return $response->withJson([
+            'is_upto_date' => $is_upto_date,
+            'latest_version' => $latest_version,
+        ]);
+    }
+}

+ 4 - 2
src/Controllers/Admin/TicketController.php

@@ -87,7 +87,8 @@ final class TicketController extends BaseController
             $_ENV['appName'] . '-工单被回复',
             'warn.tpl',
             [
-                'text' => '你好,有人回复了<a href="' . $_ENV['baseUrl'] . '/user/ticket/' . $ticket->id . '/view">工单</a>,请你查看。',
+                'text' => '你好,有人回复了<a href="' .
+                    $_ENV['baseUrl'] . '/user/ticket/' . $ticket->id . '/view">工单</a>,请你查看。',
             ],
             []
         );
@@ -136,7 +137,8 @@ final class TicketController extends BaseController
             $_ENV['appName'] . '-工单被回复',
             'warn.tpl',
             [
-                'text' => '你好,AI 回复了<a href="' . $_ENV['baseUrl'] . '/user/ticket/' . $ticket->id . '/view">工单</a>,请你查看。',
+                'text' => '你好,AI 回复了<a href="' .
+                    $_ENV['baseUrl'] . '/user/ticket/' . $ticket->id . '/view">工单</a>,请你查看。',
             ],
             []
         );

+ 2 - 2
src/Controllers/Admin/UserController.php

@@ -181,7 +181,7 @@ final class UserController extends BaseController
             $money = (float) $request->getParam('money');
             $diff = $money - $user->money;
             $remark = ($diff > 0 ? '管理员添加余额' : '管理员扣除余额');
-            (new UserMoneyLog())->addMoneyLog($id, (float) $user->money, $money, $diff, $remark);
+            (new UserMoneyLog())->add($id, (float) $user->money, $money, $diff, $remark);
             $user->money = $money;
         }
 
@@ -227,7 +227,7 @@ final class UserController extends BaseController
         $id = $args['id'];
         $user = User::find((int) $id);
 
-        if (! $user->killUser()) {
+        if (! $user->kill()) {
             return $response->withJson([
                 'ret' => 0,
                 'msg' => '删除失败',

+ 22 - 20
src/Controllers/AuthController.php

@@ -59,6 +59,7 @@ final class AuthController extends BaseController
     {
         if (Setting::obtain('enable_login_captcha')) {
             $ret = Captcha::verify($request->getParams());
+
             if (! $ret) {
                 return $response->withJson([
                     'ret' => 0,
@@ -67,11 +68,13 @@ final class AuthController extends BaseController
             }
         }
 
-        $code = $request->getParam('code');
+        $antiXss = new AntiXSS();
+
+        $code = $antiXss->xss_clean($request->getParam('code'));
         $passwd = $request->getParam('passwd');
-        $rememberMe = $request->getParam('remember_me');
-        $email = strtolower(trim($request->getParam('email')));
-        $redir = Cookie::get('redir') ?? '/user';
+        $rememberMe = $request->getParam('remember_me') === 'true' ? 1 : 0;
+        $email = strtolower(trim($antiXss->xss_clean($request->getParam('email'))));
+        $redir = Cookie::get('redir') === '' ? $antiXss->xss_clean(Cookie::get('redir')) : '/user';
 
         $user = User::where('email', $email)->first();
 
@@ -92,7 +95,7 @@ final class AuthController extends BaseController
             ]);
         }
 
-        if ($user->ga_enable === 1) {
+        if ($user->ga_enable) {
             if (strlen($code) !== 6) {
                 // 记录登录失败
                 $user->collectLoginIP($_SERVER['REMOTE_ADDR'], 1);
@@ -117,9 +120,10 @@ final class AuthController extends BaseController
             }
         }
 
-        $time = 3600 * 24;
+        $time = 3600;
+
         if ($rememberMe) {
-            $time = 3600 * 24 * ($_ENV['rememberMeDuration'] ?: 7);
+            $time = 86400 * ($_ENV['rememberMeDuration'] ?: 7);
         }
 
         Auth::login($user->id, $time);
@@ -144,18 +148,16 @@ final class AuthController extends BaseController
             $captcha = Captcha::generate();
         }
 
-        $ary = $request->getQueryParams();
-        $code = '';
-        if (isset($ary['code'])) {
-            $antiXss = new AntiXSS();
-            $code = $antiXss->xss_clean($ary['code']);
-        }
-
-        return $response->write($this->view()
-            ->assign('code', $code)
-            ->assign('base_url', $_ENV['baseUrl'])
-            ->assign('captcha', $captcha)
-            ->fetch('auth/register.tpl'));
+        $antiXss = new AntiXSS();
+        $code = $antiXss->xss_clean($request->getParam('code'));
+
+        return $response->write(
+            $this->view()
+                ->assign('code', $code)
+                ->assign('base_url', $_ENV['baseUrl'])
+                ->assign('captcha', $captcha)
+                ->fetch('auth/register.tpl')
+        );
     }
 
     /**
@@ -413,7 +415,7 @@ final class AuthController extends BaseController
                 return ResponseHelper::error($response, '你的邮箱验证码不正确');
             }
 
-            $redis->del($email_verify_code);
+            $redis->del('email_verify:' . $email_verify_code);
         }
 
         return $this->registerHelper($response, $name, $email, $passwd, $code, $imtype, $imvalue, 0, 0, 0);

+ 4 - 0
src/Controllers/HomeController.php

@@ -7,7 +7,9 @@ namespace App\Controllers;
 use App\Services\Auth;
 use App\Utils\Telegram\Process;
 use Exception;
+use MaxMind\Db\Reader\InvalidDatabaseException;
 use Psr\Http\Message\ResponseInterface;
+use RedisException;
 use Slim\Http\Response;
 use Slim\Http\ServerRequest;
 use Telegram\Bot\Exceptions\TelegramSDKException;
@@ -72,6 +74,8 @@ final class HomeController extends BaseController
 
     /**
      * @throws TelegramSDKException
+     * @throws RedisException
+     * @throws InvalidDatabaseException
      */
     public function telegram(ServerRequest $request, Response $response, array $args): ResponseInterface
     {

+ 2 - 1
src/Controllers/LinkController.php

@@ -199,7 +199,8 @@ final class LinkController extends BaseController
                 $network = $node_custom_config['network'] ?? '';
                 $header = $node_custom_config['header'] ?? ['type' => 'none'];
                 $header_type = $header['type'] ?? '';
-                $host = $node_custom_config['header']['request']['headers']['Host'][0] ?? $node_custom_config['host'] ?? '';
+                $host = $node_custom_config['header']['request']['headers']['Host'][0] ??
+                    $node_custom_config['host'] ?? '';
                 $path = $node_custom_config['header']['request']['path'][0] ?? $node_custom_config['path'] ?? '/';
 
                 $v2rayn_array = [

+ 1 - 1
src/Controllers/PasswordController.php

@@ -148,7 +148,7 @@ final class PasswordController extends BaseController
             $user->cleanLink();
         }
 
-        $redis->del($token);
+        $redis->del('password_reset:' . $token);
 
         return ResponseHelper::success($response, '重置成功');
     }

+ 4 - 2
src/Controllers/SubController.php

@@ -135,7 +135,8 @@ final class SubController extends BaseController
                     $network = $node_custom_config['network'] ?? '';
                     $header = $node_custom_config['header'] ?? ['type' => 'none'];
                     $header_type = $header['type'] ?? '';
-                    $host = $node_custom_config['header']['request']['headers']['Host'][0] ?? $node_custom_config['host'] ?? '';
+                    $host = $node_custom_config['header']['request']['headers']['Host'][0] ??
+                        $node_custom_config['host'] ?? '';
                     $servicename = $node_custom_config['servicename'] ?? '';
                     $path = $node_custom_config['header']['request']['path'][0] ?? $node_custom_config['path'] ?? '/';
                     $tls = in_array($security, ['tls', 'xtls']) ? '1' : '0';
@@ -278,7 +279,8 @@ final class SubController extends BaseController
                     $security = $node_custom_config['security'] ?? 'none';
                     $encryption = $node_custom_config['encryption'] ?? 'auto';
                     $network = $node_custom_config['header']['type'] ?? $node_custom_config['network'] ?? '';
-                    $host = $node_custom_config['header']['request']['headers']['Host'][0] ?? $node_custom_config['host'] ?? '';
+                    $host = $node_custom_config['header']['request']['headers']['Host'][0] ??
+                        $node_custom_config['host'] ?? '';
                     $allow_insecure = $node_custom_config['allow_insecure'] ?? false;
                     $tls = in_array($security, ['tls', 'xtls']);
                     // Clash 特定配置

+ 4 - 6
src/Controllers/User/InfoController.php

@@ -94,7 +94,7 @@ final class InfoController extends BaseController
                 return ResponseHelper::error($response, '你的邮箱验证码不正确');
             }
 
-            $redis->del($email_verify_code);
+            $redis->del('email_verify:' . $email_verify_code);
         }
 
         $user->email = $new_email;
@@ -148,10 +148,7 @@ final class InfoController extends BaseController
             return ResponseHelper::error($response, '密码太短啦');
         }
 
-        $hashPwd = Hash::passwordHash($pwd);
-        $user->pass = $hashPwd;
-
-        if (! $user->save()) {
+        if (! $user->updatePassword($pwd)) {
             return ResponseHelper::error($response, '修改失败');
         }
 
@@ -315,7 +312,8 @@ final class InfoController extends BaseController
 
         if ($_ENV['enable_kill']) {
             Auth::logout();
-            $user->killUser();
+            $user->kill();
+
             return ResponseHelper::success($response, '你的帐号已被送去古拉格劳动改造,再见');
         }
 

+ 1 - 1
src/Controllers/User/InvoiceController.php

@@ -115,7 +115,7 @@ final class InvoiceController extends BaseController
         $user->money -= $invoice->price;
         $user->save();
 
-        (new UserMoneyLog())->addMoneyLog(
+        (new UserMoneyLog())->add(
             $user->id,
             (float) $money_before,
             (float) $user->money,

+ 1 - 1
src/Controllers/User/MoneyController.php

@@ -73,7 +73,7 @@ final class MoneyController extends BaseController
         $user->money += $giftcard->balance;
         $user->save();
 
-        (new UserMoneyLog())->addMoneyLog(
+        (new UserMoneyLog())->add(
             $user->id,
             (float) $money_before,
             (float) $user->money,

+ 4 - 5
src/Controllers/UserController.php

@@ -58,9 +58,9 @@ final class UserController extends BaseController
     public function profile(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     {
         // 登录IP
-        $logins = LoginIp::where('userid', '=', $this->user->id)
+        $logins = LoginIp::where('userid', $this->user->id)
             ->where('type', '=', 0)->orderBy('datetime', 'desc')->take(10)->get();
-        $ips = OnlineLog::where('user_id', '=', $this->user->id)
+        $ips = OnlineLog::where('user_id', $this->user->id)
             ->where('last_time', '>', time() - 90)->orderByDesc('last_time')->get();
 
         foreach ($logins as $login) {
@@ -105,8 +105,7 @@ final class UserController extends BaseController
         $code = InviteCode::where('user_id', $this->user->id)->first();
 
         if ($code === null) {
-            $this->user->addInviteCode();
-            $code = InviteCode::where('user_id', $this->user->id)->first();
+            $code = $this->user->addInviteCode();
         }
 
         $paybacks = Payback::where('ref_by', $this->user->id)
@@ -123,7 +122,7 @@ final class UserController extends BaseController
             $paybacks_sum = 0;
         }
 
-        $invite_url = $_ENV['baseUrl'] . '/auth/register?code=' . $code->code;
+        $invite_url = $_ENV['baseUrl'] . '/auth/register?code=' . $code;
         $rebate_ratio_per = Setting::obtain('rebate_ratio') * 100;
         $payback_count = $paybacks->count();
 

+ 2 - 1
src/Middleware/NodeToken.php

@@ -51,7 +51,8 @@ final class NodeToken implements MiddlewareInterface
 
         if ($_ENV['checkNodeIp']) {
             $ip = $request->getServerParam('REMOTE_ADDR');
-            if ($ip !== '127.0.0.1' && ! Node::where('node_ip', 'LIKE', "{$ip}%")->exists()) {
+
+            if ($ip !== '127.0.0.1' && ! Node::where('node_ip', $ip)->exists()) {
                 return AppFactory::determineResponseFactory()->createResponse(401)->withJson([
                     'ret' => 0,
                     'data' => 'Invalid request IP.',

+ 3 - 1
src/Models/DetectBanLog.php

@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace App\Models;
 
+use App\Utils\Tools;
+
 final class DetectBanLog extends Model
 {
     protected $connection = 'default';
@@ -22,6 +24,6 @@ final class DetectBanLog extends Model
      */
     public function banEndTime(): string
     {
-        return date('Y-m-d H:i:s', $this->end_time + $this->ban_time * 60);
+        return Tools::toDateTime($this->end_time + $this->ban_time * 60);
     }
 }

+ 9 - 25
src/Models/DetectLog.php

@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace App\Models;
 
+use App\Utils\Tools;
+
 final class DetectLog extends Model
 {
     protected $connection = 'default';
@@ -22,10 +24,7 @@ final class DetectLog extends Model
      */
     public function userName(): string
     {
-        if ($this->user() === null) {
-            return '用户已不存在';
-        }
-        return $this->user()->user_name;
+        return $this->user() === null ? '用户不存在' : $this->user()->user_name;
     }
 
     /**
@@ -41,10 +40,7 @@ final class DetectLog extends Model
      */
     public function nodeName(): string
     {
-        if ($this->node() === null) {
-            return '节点已不存在';
-        }
-        return $this->node()->name;
+        return $this->node() === null ? '节点不存在' : $this->node()->name;
     }
 
     /**
@@ -60,10 +56,7 @@ final class DetectLog extends Model
      */
     public function ruleName(): string
     {
-        if ($this->rule() === null) {
-            return '规则已不存在';
-        }
-        return $this->rule()->name;
+        return $this->rule() === null ? '规则不存在' : $this->rule()->name;
     }
 
     /**
@@ -71,10 +64,7 @@ final class DetectLog extends Model
      */
     public function ruleText(): string
     {
-        if ($this->rule() === null) {
-            return '规则已不存在';
-        }
-        return $this->rule()->text;
+        return $this->rule() === null ? '规则不存在' : $this->rule()->text;
     }
 
     /**
@@ -82,10 +72,7 @@ final class DetectLog extends Model
      */
     public function ruleRegex(): string
     {
-        if ($this->rule() === null) {
-            return '规则已不存在';
-        }
-        return $this->rule()->regex;
+        return $this->rule() === null ? '规则已不存在' : $this->rule()->regex;
     }
 
     /**
@@ -93,10 +80,7 @@ final class DetectLog extends Model
      */
     public function ruleType(): string
     {
-        if ($this->rule() === null) {
-            return '规则已不存在';
-        }
-        return $this->rule()->type();
+        return $this->rule() === null ? '规则已不存在' : $this->rule()->type();
     }
 
     /**
@@ -104,6 +88,6 @@ final class DetectLog extends Model
      */
     public function datetime(): string
     {
-        return date('Y-m-d H:i:s', $this->datetime);
+        return Tools::toDateTime($this->datetime);
     }
 }

+ 3 - 1
src/Models/InviteCode.php

@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace App\Models;
 
+use App\Utils\Tools;
+
 /**
  * InviteCode Model
  */
@@ -15,7 +17,7 @@ final class InviteCode extends Model
     public function reward(): void
     {
         $user = User::where('id', $this->user_id)->first();
-        $user->transfer_enable += Setting::obtain('invitation_to_register_traffic_reward') * 1024 * 1024 * 1024;
+        $user->transfer_enable += Tools::toGB(Setting::obtain('invitation_to_register_traffic_reward'));
 
         if ($user->invite_num > 0) {
             --$user->invite_num;

+ 1 - 4
src/Models/LoginIp.php

@@ -25,10 +25,7 @@ final class LoginIp extends Model
      */
     public function userName(): string
     {
-        if ($this->user() === null) {
-            return '用户已不存在';
-        }
-        return $this->user()->user_name;
+        return $this->user() === null ? '用户不存在' : $this->user()->user_name;
     }
 
     /**

+ 14 - 16
src/Models/Node.php

@@ -4,9 +4,14 @@ declare(strict_types=1);
 
 namespace App\Models;
 
+use App\Utils\Tools;
+use Exception;
 use function array_key_exists;
 use function count;
+use function dns_get_record;
 use function time;
+use const DNS_A;
+use const DNS_AAAA;
 
 final class Node extends Model
 {
@@ -44,7 +49,7 @@ final class Node extends Model
      */
     public function nodeHeartbeat(): string
     {
-        return date('Y-m-d H:i:s', $this->node_heartbeat);
+        return Tools::toDateTime($this->node_heartbeat);
     }
 
     /**
@@ -54,12 +59,7 @@ final class Node extends Model
      */
     public function getNodeOnlineStatus(): int
     {
-        // 类型 9 或者心跳为 0
-        if ($this->node_heartbeat === 0) {
-            return 0;
-        }
-
-        return $this->node_heartbeat + 600 > time() ? 1 : -1;
+        return $this->node_heartbeat === 0 ? 0 : ($this->node_heartbeat + 600 > time() ? 1 : -1);
     }
 
     /**
@@ -67,14 +67,7 @@ final class Node extends Model
      */
     public function getNodeSpeedlimit(): string
     {
-        if ($this->node_speedlimit === 0.0) {
-            return '0';
-        }
-        if ($this->node_speedlimit >= 1024.00) {
-            return round($this->node_speedlimit / 1024.00, 1) . 'Gbps';
-        }
-
-        return $this->node_speedlimit . 'Mbps';
+        return Tools::autoMbps($this->node_speedlimit);
     }
 
     /**
@@ -90,7 +83,12 @@ final class Node extends Model
      */
     public function changeNodeIp(string $server_name): void
     {
-        $result = dns_get_record($server_name, DNS_A + DNS_AAAA);
+        try {
+            $result = dns_get_record($server_name, DNS_A + DNS_AAAA);
+        } catch (Exception $e) {
+            $result = false;
+        }
+
         $dns = [];
 
         if ($result !== false && count($result) > 0) {

+ 2 - 0
src/Models/OnlineLog.php

@@ -42,6 +42,7 @@ final class OnlineLog extends Model
     public function ip(): string
     {
         $ip = $this->attributes['ip'];
+
         if (str_starts_with($ip, '::ffff:')) {
             $v4 = substr($ip, 7);
             // Mix hexadecimal and dot decimal notations: https://www.rfc-editor.org/rfc/rfc5952#section-5
@@ -51,6 +52,7 @@ final class OnlineLog extends Model
                 return $v4;
             }
         }
+
         return $ip;
     }
 }

+ 3 - 1
src/Models/Payback.php

@@ -41,6 +41,7 @@ final class Payback extends Model
 
         // 判断
         $invite_rebate_mode = (string) $configs['invite_rebate_mode'];
+
         if ($invite_rebate_mode === 'continued') {
             // 不设限制
             self::executeRebate($user_id, $gift_user_id, $order_amount);
@@ -76,6 +77,7 @@ final class Payback extends Model
     public static function executeRebate($user_id, $gift_user_id, $order_amount, $adjust_rebate = null): void
     {
         $gift_user = User::where('id', $gift_user_id)->first();
+
         if ($gift_user !== null) {
             $rebate_amount = $order_amount * Setting::obtain('rebate_ratio');
             // 返利
@@ -83,7 +85,7 @@ final class Payback extends Model
             $gift_user->money += $adjust_rebate ?? $rebate_amount;
             $gift_user->save();
             // 余额变动记录
-            (new UserMoneyLog())->addMoneyLog(
+            (new UserMoneyLog())->add(
                 $gift_user->id,
                 (float) $money_before,
                 (float) $gift_user->money,

+ 1 - 4
src/Models/Product.php

@@ -35,9 +35,6 @@ final class Product extends Model
      */
     public function stock(): string|int
     {
-        if ($this->stock < 0) {
-            return '无限制';
-        }
-        return $this->stock;
+        return $this->stock < 0 ? '无限制' : $this->stock;
     }
 }

+ 6 - 9
src/Models/Setting.php

@@ -11,16 +11,13 @@ final class Setting extends Model
 
     public static function obtain($item): bool|int|string
     {
-        $config = self::where('item', '=', $item)->first();
+        $config = self::where('item', $item)->first();
 
-        if ($config->type === 'bool') {
-            return (bool) $config->value;
-        }
-        if ($config->type === 'int') {
-            return (int) $config->value;
-        }
-
-        return (string) $config->value;
+        return match ($config->type) {
+            'bool' => (bool) $config->value,
+            'int' => (int) $config->value,
+            default => (string) $config->value,
+        };
     }
 
     public static function getClass($class): array

+ 1 - 2
src/Models/SubscribeLog.php

@@ -5,7 +5,6 @@ declare(strict_types=1);
 namespace App\Models;
 
 use App\Utils\Tools;
-use GeoIp2\Exception\AddressNotFoundException;
 use MaxMind\Db\Reader\InvalidDatabaseException;
 use voku\helper\AntiXSS;
 use function time;
@@ -30,7 +29,7 @@ final class SubscribeLog extends Model
     {
         try {
             return Tools::getIpLocation($this->request_ip);
-        } catch (AddressNotFoundException | InvalidDatabaseException $e) {
+        } catch (InvalidDatabaseException $e) {
             return '未知';
         }
     }

+ 39 - 183
src/Models/User.php

@@ -15,13 +15,11 @@ use Psr\Http\Client\ClientExceptionInterface;
 use Ramsey\Uuid\Uuid;
 use function array_merge;
 use function date;
-use function in_array;
 use function is_null;
 use function json_encode;
 use function md5;
 use function random_int;
 use function round;
-use function str_replace;
 use function time;
 use const PHP_EOL;
 
@@ -103,17 +101,8 @@ final class User extends Model
     public function updatePassword(string $pwd): bool
     {
         $this->pass = Hash::passwordHash($pwd);
-        return $this->save();
-    }
-
-    public function getForbiddenIp(): array|string
-    {
-        return str_replace(',', PHP_EOL, $this->forbidden_ip);
-    }
 
-    public function getForbiddenPort(): array|string
-    {
-        return str_replace(',', PHP_EOL, $this->forbidden_port);
+        return $this->save();
     }
 
     /**
@@ -121,19 +110,12 @@ final class User extends Model
      */
     public function addInviteCode(): string
     {
-        while (true) {
-            $temp_code = Tools::genRandomChar(10);
-            if (InviteCode::where('code', $temp_code)->first() === null) {
-                if (InviteCode::where('user_id', $this->id)->count() === 0) {
-                    $code = new InviteCode();
-                    $code->code = $temp_code;
-                    $code->user_id = $this->id;
-                    $code->save();
-                    return $temp_code;
-                }
-                return InviteCode::where('user_id', $this->id)->first()->code;
-            }
-        }
+        $code = new InviteCode();
+        $code->code = Tools::genRandomChar(10);
+        $code->user_id = $this->id;
+        $code->save();
+
+        return $code->code;
     }
 
     /**
@@ -142,6 +124,7 @@ final class User extends Model
     public function addInviteNum(int $num): bool
     {
         $this->invite_num += $num;
+
         return $this->save();
     }
 
@@ -150,17 +133,9 @@ final class User extends Model
      */
     public function generateUUID(): bool
     {
-        for ($i = 0; $i < 10; $i++) {
-            $uuid = Uuid::uuid4();
-            $is_uuid_used = User::where('uuid', $uuid)->first();
+        $this->uuid = Uuid::uuid4();
 
-            if ($is_uuid_used === null) {
-                $this->uuid = Uuid::uuid4();
-                return $this->save();
-            }
-        }
-
-        return false;
+        return $this->save();
     }
 
     /**
@@ -168,17 +143,9 @@ final class User extends Model
      */
     public function generateApiToken(): bool
     {
-        for ($i = 0; $i < 10; $i++) {
-            $api_token = Uuid::uuid4();
-            $is_api_token_used = User::where('api_token', $api_token)->first();
+        $this->api_token = Uuid::uuid4();
 
-            if ($is_api_token_used === null) {
-                $this->api_token = Uuid::uuid4();
-                return $this->save();
-            }
-        }
-
-        return false;
+        return $this->save();
     }
 
     /*
@@ -189,14 +156,6 @@ final class User extends Model
         return Tools::autoBytes($this->transfer_enable);
     }
 
-    /*
-     * 总流量[GB],不含单位
-     */
-    public function enableTrafficInGB(): float
-    {
-        return Tools::flowToGB($this->transfer_enable);
-    }
-
     /*
      * 当期用量[自动单位]
      */
@@ -216,14 +175,12 @@ final class User extends Model
     /*
      * 已用流量占总流量的百分比
      */
-    public function trafficUsagePercent(): int
+    public function trafficUsagePercent(): float
     {
-        if ($this->transfer_enable === 0) {
-            return 0;
-        }
-        $percent = ($this->u + $this->d) / $this->transfer_enable;
-        $percent = round($percent, 2);
-        return (int) $percent * 100;
+        return $this->transfer_enable === 0 ?
+            0
+            :
+            round(($this->u + $this->d) / $this->transfer_enable, 2) * 100;
     }
 
     /*
@@ -239,13 +196,10 @@ final class User extends Model
      */
     public function unusedTrafficPercent(): float
     {
-        if ($this->transfer_enable === 0) {
-            return 0;
-        }
-        $unused = $this->transfer_enable - ($this->u + $this->d);
-        $percent = $unused / $this->transfer_enable;
-        $percent = round($percent, 4);
-        return $percent * 100;
+        return $this->transfer_enable === 0 ?
+            0
+            :
+            round(($this->transfer_enable - ($this->u + $this->d)) / $this->transfer_enable, 2) * 100;
     }
 
     /*
@@ -261,12 +215,10 @@ final class User extends Model
      */
     public function todayUsedTrafficPercent(): float
     {
-        if ($this->transfer_enable === 0 || $this->transfer_enable === null) {
-            return 0;
-        }
-        $percent = $this->transfer_today / $this->transfer_enable;
-        $percent = round($percent, 4);
-        return $percent * 100;
+        return $this->transfer_enable === 0 ?
+            0
+            :
+            round($this->transfer_today / $this->transfer_enable, 2) * 100;
     }
 
     /*
@@ -282,12 +234,10 @@ final class User extends Model
      */
     public function lastUsedTrafficPercent(): float
     {
-        if ($this->transfer_enable === 0 || $this->transfer_enable === null) {
-            return 0;
-        }
-        $percent = ($this->u + $this->d - $this->transfer_today) / $this->transfer_enable;
-        $percent = round($percent, 4);
-        return $percent * 100;
+        return $this->transfer_enable === 0 ?
+            0
+            :
+            round(($this->u + $this->d - $this->transfer_today) / $this->transfer_enable, 2) * 100;
     }
 
     /*
@@ -298,39 +248,6 @@ final class User extends Model
         return date('Ymd') !== date('Ymd', $this->last_check_in_time);
     }
 
-    /**
-     * 获取用户的邀请码
-     */
-    public function getInviteCodes(): ?InviteCode
-    {
-        return InviteCode::where('user_id', $this->id)->first();
-    }
-
-    /**
-     * 用户的邀请人
-     */
-    public function refByUser(): ?User
-    {
-        return self::find($this->ref_by);
-    }
-
-    /**
-     * 用户邀请人的用户名
-     */
-    public function refByUserName(): string
-    {
-        if ($this->ref_by === 0) {
-            return '系统邀请';
-        }
-
-        $refUser = $this->refByUser();
-
-        if ($refUser === null) {
-            return '邀请人已经被删除';
-        }
-        return $refUser->user_name;
-    }
-
     /**
      * 删除用户的订阅链接
      */
@@ -377,59 +294,19 @@ final class User extends Model
     /**
      * 销户
      */
-    public function killUser(): bool
+    public function kill(): bool
     {
         $uid = $this->id;
 
-        DetectBanLog::where('user_id', '=', $uid)->delete();
-        DetectLog::where('user_id', '=', $uid)->delete();
-        InviteCode::where('user_id', '=', $uid)->delete();
-        OnlineLog::where('user_id', '=', $uid)->delete();
-        Link::where('userid', '=', $uid)->delete();
-        LoginIp::where('userid', '=', $uid)->delete();
-        SubscribeLog::where('user_id', '=', $uid)->delete();
-
-        $this->delete();
-
-        return true;
-    }
-
-    /**
-     * 最后一次被封禁的时间
-     */
-    public function lastDetectBanTime(): string
-    {
-        return $this->last_detect_ban_time === '1989-06-04 00:05:00' ? '未被封禁过' : $this->last_detect_ban_time;
-    }
-
-    /**
-     * 当前解封时间
-     */
-    public function relieveTime(): string
-    {
-        $logs = DetectBanLog::where('user_id', $this->id)->orderBy('id', 'desc')->first();
-        if ($this->enable === 0 && $logs !== null) {
-            $time = $logs->end_time + $logs->ban_time * 60;
-            return date('Y-m-d H:i:s', $time);
-        }
-        return '当前未被封禁';
-    }
-
-    /**
-     * 累计被封禁的次数
-     */
-    public function detectBanNumber(): int
-    {
-        return DetectBanLog::where('user_id', $this->id)->count();
-    }
+        DetectBanLog::where('user_id', $uid)->delete();
+        DetectLog::where('user_id', $uid)->delete();
+        InviteCode::where('user_id', $uid)->delete();
+        OnlineLog::where('user_id', $uid)->delete();
+        Link::where('userid', $uid)->delete();
+        LoginIp::where('userid', $uid)->delete();
+        SubscribeLog::where('user_id', $uid)->delete();
 
-    /**
-     * 最后一次封禁的违规次数
-     */
-    public function userDetectBanNumber(): int
-    {
-        $logs = DetectBanLog::where('user_id', $this->id)->orderBy('id', 'desc')->first();
-        return $logs->detect_number;
+        return $this->delete();
     }
 
     /**
@@ -472,8 +349,7 @@ final class User extends Model
         $this->telegram_id = 0;
 
         if ($this->save()) {
-            if (
-                $_ENV['enable_telegram']
+            if ($_ENV['enable_telegram']
                 &&
                 Setting::obtain('telegram_group_bound_user')
                 &&
@@ -499,26 +375,6 @@ final class User extends Model
         return $return;
     }
 
-    /**
-     * 更新端口
-     */
-    public function setPort(int $Port): array
-    {
-        $PortOccupied = User::pluck('port')->toArray();
-        if (in_array($Port, $PortOccupied)) {
-            return [
-                'ok' => false,
-                'msg' => '端口已被占用',
-            ];
-        }
-        $this->port = $Port;
-        $this->save();
-        return [
-            'ok' => true,
-            'msg' => $this->port,
-        ];
-    }
-
     /**
      * 发送邮件
      */

+ 3 - 7
src/Models/UserCoupon.php

@@ -5,6 +5,7 @@ declare(strict_types=1);
 namespace App\Models;
 
 use function json_decode;
+use function time;
 
 final class UserCoupon extends Model
 {
@@ -16,9 +17,7 @@ final class UserCoupon extends Model
      */
     public function type(): string
     {
-        $content = json_decode($this->content);
-
-        return match ($content->type) {
+        return match (json_decode($this->content)) {
             'percentage' => '百分比',
             'fixed' => '固定金额',
             default => '未知',
@@ -30,9 +29,6 @@ final class UserCoupon extends Model
      */
     public function status(): string
     {
-        if ($this->expire_time < time()) {
-            return '已过期';
-        }
-        return '激活';
+        return $this->expire_time < time() ? '已过期' : '激活';
     }
 }

+ 1 - 1
src/Models/UserMoneyLog.php

@@ -11,7 +11,7 @@ final class UserMoneyLog extends Model
     protected $connection = 'default';
     protected $table = 'user_money_log';
 
-    public function addMoneyLog(int $user_id, float $before, float $after, float $amount, string $remark): void
+    public function add(int $user_id, float $before, float $after, float $amount, string $remark): void
     {
         $this->user_id = $user_id;
         $this->before = $before;

+ 2 - 2
src/Services/Analytics.php

@@ -104,11 +104,11 @@ final class Analytics
 
     public function getInactiveUser()
     {
-        return User::where('is_inactive', '=', 1)->count();
+        return User::where('is_inactive', 1)->count();
     }
 
     public function getActiveUser()
     {
-        return User::where('is_inactive', '=', 0)->count();
+        return User::where('is_inactive', 0)->count();
     }
 }

+ 17 - 11
src/Services/Auth/Cookie.php

@@ -6,7 +6,7 @@ namespace App\Services\Auth;
 
 use App\Models\Node;
 use App\Models\User;
-use App\Utils;
+use App\Utils\Cookie as CookieUtils;
 use App\Utils\Hash;
 use function strval;
 use function time;
@@ -17,11 +17,11 @@ final class Cookie extends Base
     {
         $user = User::find($uid);
         $expire_in = $time + time();
-        $key = Hash::cookieHash($user->pass, $expire_in);
-        Utils\Cookie::set([
+
+        CookieUtils::set([
             'uid' => strval($uid),
             'email' => $user->email,
-            'key' => $key,
+            'key' => Hash::cookieHash($user->pass, $expire_in),
             'ip' => Hash::ipHash($_SERVER['REMOTE_ADDR'], $uid, $expire_in),
             'expire_in' => strval($expire_in),
         ], $expire_in);
@@ -29,11 +29,11 @@ final class Cookie extends Base
 
     public function getUser(): User
     {
-        $uid = Utils\Cookie::get('uid');
-        $email = Utils\Cookie::get('email');
-        $key = Utils\Cookie::get('key');
-        $ipHash = Utils\Cookie::get('ip');
-        $expire_in = Utils\Cookie::get('expire_in');
+        $uid = CookieUtils::get('uid');
+        $email = CookieUtils::get('email');
+        $key = CookieUtils::get('key');
+        $ipHash = CookieUtils::get('ip');
+        $expire_in = CookieUtils::get('expire_in');
 
         $user = new User();
         $user->isLogin = false;
@@ -47,13 +47,15 @@ final class Cookie extends Base
         }
 
         if ($_ENV['enable_login_bind_ip']) {
-            $nodes = Node::where('node_ip', '=', $_SERVER['REMOTE_ADDR'])->first();
+            $nodes = Node::where('node_ip', $_SERVER['REMOTE_ADDR'])->first();
+
             if (($nodes === null) && $ipHash !== Hash::ipHash($_SERVER['REMOTE_ADDR'], $uid, $expire_in)) {
                 return $user;
             }
         }
 
         $user = User::find($uid);
+
         if ($user === null) {
             $user = new User();
             $user->isLogin = false;
@@ -72,16 +74,20 @@ final class Cookie extends Base
         }
 
         $user->isLogin = true;
+
         return $user;
     }
 
     public function logout(): void
     {
         $time = time() - 1000;
-        Utils\Cookie::set([
+
+        CookieUtils::set([
             'uid' => '',
             'email' => '',
             'key' => '',
+            'ip' => '',
+            'expire_in' => '',
         ], $time);
     }
 }

+ 19 - 11
src/Services/Captcha.php

@@ -40,18 +40,22 @@ final class Captcha
                             'response' => $turnstile,
                         ]
                     );
-                    $opts = ['http' => [
-                        'method' => 'POST',
-                        'header' => 'Content-Type: application/x-www-form-urlencoded',
-                        'content' => $postdata,
-                    ],
+
+                    $opts = [
+                        'http' => [
+                            'method' => 'POST',
+                            'header' => 'Content-Type: application/x-www-form-urlencoded',
+                            'content' => $postdata,
+                        ],
                     ];
-                    $json = file_get_contents(
+
+                    $json = json_decode(file_get_contents(
                         'https://challenges.cloudflare.com/turnstile/v0/siteverify',
                         false,
                         stream_context_create($opts)
-                    );
-                    $result = json_decode($json)->success;
+                    ));
+
+                    $result = $json->success;
                 }
                 break;
             case 'geetest':
@@ -64,6 +68,7 @@ final class Captcha
                     $pass_token = $geetest['pass_token'];
                     $gen_time = $geetest['gen_time'];
                     $sign_token = hash_hmac('sha256', $lot_number, $captcha_key);
+
                     $postdata = http_build_query(
                         [
                             'lot_number' => $lot_number,
@@ -73,6 +78,7 @@ final class Captcha
                             'sign_token' => $sign_token,
                         ]
                     );
+
                     $opts = [
                         'http' => [
                             'method' => 'POST',
@@ -81,12 +87,14 @@ final class Captcha
                             'timeout' => 5,
                         ],
                     ];
-                    $json = file_get_contents(
+
+                    $json = json_decode(file_get_contents(
                         'https://gcaptcha4.geetest.com/validate?captcha_id=' . $captcha_id,
                         false,
                         stream_context_create($opts)
-                    );
-                    if (json_decode($json)->result === 'success') {
+                    ));
+
+                    if ($json->result === 'success') {
                         $result = true;
                     }
                 }

+ 2 - 2
src/Services/CronDetect.php

@@ -27,8 +27,8 @@ final class CronDetect
      */
     public static function gfw(): void
     {
-        $nodes = Node::where('type', '=', 1)->where('node_ip', '!=', '')->where('online', '=', 1)->get();
-        $adminUser = User::where('is_admin', '=', '1')->get();
+        $nodes = Node::where('type', 1)->where('node_ip', '!=', '')->where('online', 1)->get();
+        $adminUser = User::where('is_admin', '1')->get();
 
         foreach ($nodes as $node) {
             $api_url = str_replace(

+ 14 - 12
src/Services/CronJob.php

@@ -79,22 +79,22 @@ final class CronJob
         $login_days = Setting::obtain('detect_inactive_user_login_days');
         $use_days = Setting::obtain('detect_inactive_user_use_days');
 
-        User::where('is_admin', '=', '0')
-            ->where('is_inactive', '=', '0')
+        User::where('is_admin', 0)
+            ->where('is_inactive', 0)
             ->where('last_check_in_time', '<', time() - 86400 * $checkin_days)
             ->where('last_login_time', '<', time() - 86400 * $login_days)
             ->where('last_use_time', '<', time() - 86400 * $use_days)
             ->update(['is_inactive' => 1]);
 
-        User::where('is_admin', '=', '0')
-            ->where('is_inactive', '=', '1')
+        User::where('is_admin', 0)
+            ->where('is_inactive', 1)
             ->where('last_check_in_time', '>', time() - 86400 * $checkin_days)
             ->where('last_login_time', '>', time() - 86400 * $login_days)
             ->where('last_use_time', '>', time() - 86400 * $use_days)
             ->update(['is_inactive' => 0]);
 
         echo Tools::toDateTime(time()) .
-            ' 检测到 ' . User::where('is_inactive', '=', '1')->count() . ' 个账户处于闲置状态' . PHP_EOL;
+            ' 检测到 ' . User::where('is_inactive', 1)->count() . ' 个账户处于闲置状态' . PHP_EOL;
     }
 
     /**
@@ -103,7 +103,7 @@ final class CronJob
     public static function detectNodeOffline(): void
     {
         $nodes = Node::where('type', 1)->get();
-        $adminUsers = User::where('is_admin', '=', '1')->get();
+        $adminUsers = User::where('is_admin', 1)->get();
 
         foreach ($nodes as $node) {
             if ($node->getNodeOnlineStatus() >= 0 && $node->online === 1) {
@@ -491,7 +491,9 @@ final class CronJob
     public static function sendWeeklyFinanceMail(): void
     {
         $today = strtotime('00:00:00');
-        $paylists = Paylist::where('status', 1)->whereBetween('datetime', [strtotime('-1 week', $today), $today])->get();
+        $paylists = Paylist::where('status', 1)
+            ->whereBetween('datetime', [strtotime('-1 week', $today), $today])
+            ->get();
 
         $text_html = '<br>上周总收入笔数:' . count($paylists) . '<br>上周总收入金额:' . $paylists->sum('total');
         $adminUser = User::where('is_admin', '=', '1')->get();
@@ -515,7 +517,9 @@ final class CronJob
     public static function sendMonthlyFinanceMail(): void
     {
         $today = strtotime('00:00:00');
-        $paylists = Paylist::where('status', 1)->whereBetween('datetime', [strtotime('-1 month', $today), $today])->get();
+        $paylists = Paylist::where('status', 1)
+            ->whereBetween('datetime', [strtotime('-1 month', $today), $today])
+            ->get();
 
         $text_html = '<br>上月总收入笔数:' . count($paylists) . '<br>上月总收入金额:' . $paylists->sum('total');
         $adminUser = User::where('is_admin', '=', '1')->get();
@@ -545,14 +549,12 @@ final class CronJob
             $under_limit = false;
             $unit_text = '';
 
-            if (
-                $_ENV['notify_limit_mode'] === 'per' &&
+            if ($_ENV['notify_limit_mode'] === 'per' &&
                 $user_traffic_left / $user->transfer_enable * 100 < $_ENV['notify_limit_value']
             ) {
                 $under_limit = true;
                 $unit_text = '%';
-            } elseif (
-                $_ENV['notify_limit_mode'] === 'mb' &&
+            } elseif ($_ENV['notify_limit_mode'] === 'mb' &&
                 Tools::flowToMB($user_traffic_left) < $_ENV['notify_limit_value']
             ) {
                 $under_limit = true;

+ 1 - 1
src/Services/Gateway/AbstractPayment.php

@@ -114,7 +114,7 @@ abstract class AbstractPayment
 
     protected static function getActiveGateway($key): bool
     {
-        $payment_gateways = Setting::where('item', '=', 'payment_gateway')->first();
+        $payment_gateways = Setting::where('item', 'payment_gateway')->first();
         $active_gateways = json_decode($payment_gateways->value);
         if (in_array($key, $active_gateways)) {
             return true;

+ 2 - 1
src/Services/Gateway/Epay/EpaySubmit.php

@@ -55,7 +55,8 @@ final class EpaySubmit
         //待请求参数数组
         $para = $this->buildRequestPara($para_temp);
 
-        $sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='".$this->alipay_gateway_new."' method='".$method."'>";
+        $sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='".
+            $this->alipay_gateway_new . "' method='" . $method . "'>";
         foreach ($para as $key => $val) {
             $sHtml .= "<input type='hidden' name='".$key."' value='".$val."'/>";
         }

+ 2 - 1
src/Services/Gateway/StripeCard.php

@@ -72,7 +72,8 @@ final class StripeCard extends AbstractPayment
                 ],
                 ],
                 'mode' => 'payment',
-                'success_url' => self::getUserReturnUrl() . '?session_id={CHECKOUT_SESSION_ID}&' . http_build_query($params),
+                'success_url' => self::getUserReturnUrl() .
+                    '?session_id={CHECKOUT_SESSION_ID}&' . http_build_query($params),
                 'cancel_url' => $_ENV['baseUrl'] . '/user/invoice',
             ]);
         } catch (ApiErrorException $e) {

+ 1 - 2
src/Utils/ClassHelper.php

@@ -45,8 +45,7 @@ final class ClassHelper
         $termUpper = strtoupper($namespace);
         return array_filter($this->getClasses(), static function ($class) use ($termUpper) {
             $className = strtoupper($class);
-            if (
-                str_starts_with($className, $termUpper) &&
+            if (str_starts_with($className, $termUpper) &&
                 ! str_contains($className, strtoupper('Abstract')) &&
                 ! str_contains($className, strtoupper('Interface'))
             ) {

+ 1 - 1
src/Utils/Telegram.php

@@ -160,7 +160,7 @@ final class Telegram
             return 0;
         }
 
-        $redis->del($token);
+        $redis->del('telegram_bind:' . $token);
 
         return (int) $uid;
     }

+ 2 - 2
src/Utils/Telegram/Callback.php

@@ -407,7 +407,7 @@ final class Callback
         switch ($OpEnd) {
             case 'login_log':
                 // 登录记录
-                $total = LoginIp::where('userid', '=', $this->User->id)
+                $total = LoginIp::where('userid', $this->User->id)
                     ->where('type', '=', 0)
                     ->orderBy('datetime', 'desc')
                     ->take(10)
@@ -436,7 +436,7 @@ final class Callback
                 break;
             case 'usage_log':
                 // 使用记录
-                $logs = OnlineLog::where('user_id', '=', $this->User->id)
+                $logs = OnlineLog::where('user_id', $this->User->id)
                     ->where('last_time', '>', time() - 90)->orderByDesc('last_time')->get('ip');
                 $text = '<strong>以下是你账户在线 IP 和地理位置:</strong>' . PHP_EOL;
                 $text .= PHP_EOL;

+ 2 - 1
src/Utils/Telegram/Commands/HelpCommand.php

@@ -30,7 +30,8 @@ final class HelpCommand extends Command
         if ($Message->getChat()->getId() < 0 && Setting::obtain('telegram_group_quiet')) {
             return;
         }
-        if (! preg_match('/^\/help\s?(@' . $_ENV['telegram_bot'] . ')?.*/i', $Message->getText()) && Setting::obtain('help_any_command') === false) {
+        if (! preg_match('/^\/help\s?(@' . $_ENV['telegram_bot'] . ')?.*/i', $Message->getText()) &&
+            Setting::obtain('help_any_command') === false) {
             return;
         }
         $this->replyWithChatAction(['action' => Actions::TYPING]);

+ 6 - 3
src/Utils/Telegram/Commands/StartCommand.php

@@ -28,6 +28,9 @@ final class StartCommand extends Command
      */
     protected string $description = '[群组/私聊] Bot 初始命令.';
 
+    /**
+     * @throws RedisException
+     */
     public function handle(): void
     {
         $Update = $this->getUpdate();
@@ -51,8 +54,7 @@ final class StartCommand extends Command
             // 消息内容
             $MessageText = explode(' ', trim($Message->getText()));
             $MessageKey = array_splice($MessageText, -1)[0];
-            if (
-                $MessageKey !== ''
+            if ($MessageKey !== ''
                 && TelegramTools::getUser($SendUser['id']) === null
                 && strlen($MessageKey) === 16
             ) {
@@ -104,7 +106,8 @@ final class StartCommand extends Command
                 $text = '尊敬的 **管理员** 你好,恭喜绑定成功。' . PHP_EOL . '当前绑定邮箱为: ' . $BinsUser->email;
             } else {
                 if ($BinsUser->class >= 1) {
-                    $text = '尊敬的 **VIP ' . $BinsUser->class . '** 用户你好.' . PHP_EOL . '恭喜你绑定成功,当前绑定邮箱为: ' . $BinsUser->email;
+                    $text = '尊敬的 **VIP ' . $BinsUser->class . '** 用户你好.' .
+                        PHP_EOL . '恭喜你绑定成功,当前绑定邮箱为: ' . $BinsUser->email;
                 } else {
                     $text = '绑定成功了,你的邮箱为:' . $BinsUser->email;
                 }

+ 1 - 2
src/Utils/Telegram/Message.php

@@ -182,8 +182,7 @@ final class Message
             // 新成员加入群组
             $NewUser = TelegramTools::getUser($Member['id']);
 
-            if (
-                Setting::obtain('telegram_group_bound_user')
+            if (Setting::obtain('telegram_group_bound_user')
                 &&
                 $this->ChatID === $_ENV['telegram_chatid']
                 &&

+ 4 - 0
src/Utils/Telegram/Process.php

@@ -4,7 +4,9 @@ declare(strict_types=1);
 
 namespace App\Utils\Telegram;
 
+use MaxMind\Db\Reader\InvalidDatabaseException;
 use Psr\Http\Message\RequestInterface;
+use RedisException;
 use Telegram\Bot\Api;
 use Telegram\Bot\Exceptions\TelegramSDKException;
 
@@ -12,6 +14,8 @@ final class Process
 {
     /**
      * @throws TelegramSDKException
+     * @throws InvalidDatabaseException
+     * @throws RedisException
      */
     public static function index(RequestInterface $request): void
     {

+ 2 - 1
src/Utils/Telegram/Reply.php

@@ -30,7 +30,8 @@ final class Reply
     {
         $text = [
             '当前余额:' . $user->money,
-            '在线 IP 数:' . ($user->node_iplimit !== 0 ? $user->onlineIpCount() . ' / ' . $user->node_iplimit : $user->onlineIpCount() . ' / 不限制'),
+            '在线 IP 数:' . ($user->node_iplimit !== 0 ? $user->onlineIpCount() .
+                ' / ' . $user->node_iplimit : $user->onlineIpCount() . ' / 不限制'),
             '端口速率:' . ($user->node_speedlimit > 0 ? $user->node_speedlimit . 'Mbps' : '不限制'),
             '上次使用:' . $user->lastUseTime(),
             '过期时间:' . $user->class_expire,

+ 4 - 36
src/Utils/Telegram/TelegramTools.php

@@ -154,35 +154,6 @@ final class TelegramTools
                 $old = ($old ? '启用' : '禁用');
                 break;
                 // ##############
-            case 'port':
-                // 支持正整数或 0 随机选择
-                if (! is_numeric($value) || str_starts_with((string) $value, '-')) {
-                    return [
-                        'ok' => false,
-                        'msg' => '提供的端口非数值,如要随机重置请指定为 0.',
-                    ];
-                }
-                if ((int) $value === 0) {
-                    $value = Tools::getAvPort();
-                }
-                $temp = $User->setPort($value);
-                if ($temp['ok'] === false) {
-                    $strArray = [
-                        '目标用户:' . $Email,
-                        '欲修改项:' . $useOptionMethodName . '[' . $useOptionMethod . ']',
-                        '当前值为:' . $old,
-                        '欲修改为:' . $value,
-                        '错误详情:' . $temp['msg'],
-                    ];
-                    return [
-                        'ok' => false,
-                        'msg' => self::strArrayToCode($strArray),
-                    ];
-                }
-                $new = $User->$useOptionMethod;
-                $User->$useOptionMethod = $new;
-                break;
-                // ##############
             case 'transfer_enable':
                 $strArray = [
                     '// 支持的写法,不支持单位 b,不区分大小写',
@@ -223,8 +194,7 @@ final class TelegramTools
                     '// 2020-02-30 —— 指定日期',
                     '// 2020-02-30 08:00:00 —— 指定日期精确到秒',
                 ];
-                if (
-                    str_starts_with($value, '+')
+                if (str_starts_with($value, '+')
                     ||
                     str_starts_with($value, '-')
                 ) {
@@ -319,7 +289,7 @@ final class TelegramTools
             if ($useOptionMethod === 'money') {
                 $diff = $new - $old;
                 $remark = ($diff > 0 ? '管理员添加余额' : '管理员扣除余额');
-                (new UserMoneyLog())->addMoneyLog($User->id, (float) $old, (float) $new, (float) $diff, $remark);
+                (new UserMoneyLog())->add($User->id, (float) $old, (float) $new, (float) $diff, $remark);
             }
             $strArray = [
                 '目标用户:' . $Email,
@@ -418,8 +388,7 @@ final class TelegramTools
      */
     public static function computingMethod(string $Source, string $Value, bool $FloatingNumber = false): ?string
     {
-        if (
-            (str_starts_with($Value, '+')
+        if ((str_starts_with($Value, '+')
                 ||
                 str_starts_with($Value, '-')
                 ||
@@ -459,8 +428,7 @@ final class TelegramTools
      */
     public static function trafficMethod(string $Source, string $Value): ?int
     {
-        if (
-            str_starts_with($Value, '+')
+        if (str_starts_with($Value, '+')
             ||
             str_starts_with($Value, '-')
             ||

+ 23 - 8
src/Utils/Tools.php

@@ -123,30 +123,45 @@ final class Tools
         return (int) ($base * pow(1024, array_flip($units)[$suffix]));
     }
 
+    /**
+     * 根据速率值自动转换单位输出
+     */
+    public static function autoMbps($size, $precision = 2): string
+    {
+        if ($size <= 0) {
+            return '0Bbps';
+        }
+
+        if ($size > 1000000000) {
+            return '∞';
+        }
+
+        $base = log((float) $size, 1000);
+        $units = ['Mbps', 'Gbps', 'Tbps'];
+
+        return round(pow(1000, $base - floor($base)), $precision) . $units[floor($base)];
+    }
+
     //虽然名字是toMB,但是实际上功能是from MB to B
     public static function toMB($traffic): float|int
     {
-        $mb = 1048576;
-        return $traffic * $mb;
+        return $traffic * 1048576;
     }
 
     //虽然名字是toGB,但是实际上功能是from GB to B
     public static function toGB($traffic): float|int
     {
-        $gb = 1048576 * 1024;
-        return $traffic * $gb;
+        return $traffic * 1073741824;
     }
 
     public static function flowToGB($traffic): float
     {
-        $gb = 1048576 * 1024;
-        return $traffic / $gb;
+        return $traffic / 1073741824;
     }
 
     public static function flowToMB($traffic): float
     {
-        $gb = 1048576;
-        return $traffic / $gb;
+        return $traffic / 1048576;
     }
 
     public static function genRandomChar($length = 8): string

+ 0 - 1
tests/App/Services/ConfigTest.php

@@ -158,4 +158,3 @@ class ConfigTest extends TestCase
         $this->assertContains('xchacha20-ietf-poly1305', $params);
     }
 }
-

+ 0 - 1
tests/App/Utils/HashTest.php

@@ -55,4 +55,3 @@ class HashTest extends TestCase
         $this->assertIsString(Hash::sha256WithSalt($password));
     }
 }
-

+ 0 - 1
tests/App/Utils/ToolsTest.php

@@ -147,4 +147,3 @@ class ToolsTest extends TestCase
         $this->assertFalse(Tools::isInt('abc'));
     }
 }
-