Sfoglia il codice sorgente

feat: remove phinx & new migrator

Irohaede 2 anni fa
parent
commit
0a68f46504
91 ha cambiato i file con 997 aggiunte e 2099 eliminazioni
  1. 77 44
      app/routes.php
  2. 0 1
      composer.json
  3. 10 0
      config/settings.json
  4. 0 404
      db/migrations/20000101000000_init_database.php.new
  5. 0 22
      db/migrations/20220109130532_create_stream_media_table.php
  6. 0 15
      db/migrations/20220514172426_adjust_configuration_table_value_field_length.php
  7. 0 17
      db/migrations/20220522184308_fix_user_money_field.php
  8. 0 32
      db/migrations/20220814175532_drop_g_config_table.php
  9. 0 24
      db/migrations/20220902062145_add_node_password.php
  10. 0 28
      db/migrations/20220909202811_merge_node_info.php
  11. 0 23
      db/migrations/20220920012728_email_queue_use_longtext.php
  12. 0 37
      db/migrations/20220926175600_add_traffic_log.php
  13. 0 24
      db/migrations/20221016073516_add_user_dark_mode.php
  14. 0 26
      db/migrations/20221024101400_add_docs.php
  15. 0 34
      db/migrations/20221026130700_add_ban_user.php
  16. 0 24
      db/migrations/20221026143200_remove_user_enable.php
  17. 0 22
      db/migrations/20221031185300_custom_config_json.php
  18. 0 30
      db/migrations/20221112115300_update_ticket.php
  19. 0 35
      db/migrations/20221124113800_remove_block.php
  20. 0 24
      db/migrations/20221130005800_add_user_node_iplimit.php
  21. 0 30
      db/migrations/20221203142200_clean_node.php
  22. 0 24
      db/migrations/20221204134100_remove_ann_markdown.php
  23. 0 37
      db/migrations/20221205162200_change_number_type.php
  24. 0 24
      db/migrations/20221208132600_add_user_api_token.php
  25. 0 35
      db/migrations/20221211223600_merge_node_online_log.php
  26. 0 30
      db/migrations/20221211230900_gift_card_table.php
  27. 0 19
      db/migrations/20221212141700_fix_node_default_value.php
  28. 0 24
      db/migrations/20221217033500_add_user_use_new_shop.php
  29. 0 74
      db/migrations/20221230010900_add_product_order_invoice.php
  30. 0 30
      db/migrations/20230115090200_add_user_coupon.php
  31. 445 0
      db/migrations/20230201-init.php
  32. 0 0
      db/seeds/readme.md
  33. 12 0
      db/update.sql
  34. 1 1
      install.sh
  35. 0 44
      phinx.php
  36. 2 5
      phpinsights.php
  37. 0 1
      public/index.php
  38. 4 1
      resources/views/tabler/staff.tpl
  39. 3 3
      src/Command/FinanceMail.php
  40. 3 3
      src/Command/Job.php
  41. 107 0
      src/Command/Migration.php
  42. 22 29
      src/Command/Tool.php
  43. 5 5
      src/Command/Update.php
  44. 1 1
      src/Controllers/Admin/AnnController.php
  45. 1 1
      src/Controllers/Admin/ApiController.php
  46. 1 1
      src/Controllers/Admin/CodeController.php
  47. 1 1
      src/Controllers/Admin/CouponController.php
  48. 1 1
      src/Controllers/Admin/DetectBanLogController.php
  49. 1 1
      src/Controllers/Admin/DetectController.php
  50. 1 1
      src/Controllers/Admin/GiftCardController.php
  51. 2 2
      src/Controllers/Admin/InvoiceController.php
  52. 1 1
      src/Controllers/Admin/IpController.php
  53. 1 1
      src/Controllers/Admin/NodeController.php
  54. 2 2
      src/Controllers/Admin/OrderController.php
  55. 3 3
      src/Controllers/Admin/ProductController.php
  56. 0 1
      src/Controllers/Admin/SettingController.php
  57. 1 1
      src/Controllers/Admin/ShopController.php
  58. 1 1
      src/Controllers/Admin/SubscribeLogController.php
  59. 1 1
      src/Controllers/Admin/TicketController.php
  60. 1 1
      src/Controllers/Admin/TrafficLogController.php
  61. 1 1
      src/Controllers/Admin/UserController.php
  62. 1 1
      src/Controllers/AdminController.php
  63. 1 1
      src/Controllers/AuthController.php
  64. 1 1
      src/Controllers/HomeController.php
  65. 197 740
      src/Controllers/LinkController.php
  66. 1 1
      src/Controllers/PasswordController.php
  67. 1 1
      src/Controllers/User/DetectController.php
  68. 1 1
      src/Controllers/User/ServerController.php
  69. 1 1
      src/Controllers/User/ShopController.php
  70. 1 1
      src/Controllers/User/TicketController.php
  71. 1 2
      src/Controllers/UserController.php
  72. 1 1
      src/Controllers/WebAPI/FuncController.php
  73. 1 1
      src/Controllers/WebAPI/NodeController.php
  74. 1 1
      src/Controllers/WebAPI/UserController.php
  75. 12 0
      src/Interfaces/MigrationInterface.php
  76. 1 1
      src/Middleware/Admin.php
  77. 0 3
      src/Middleware/Auth.php
  78. 1 4
      src/Middleware/AuthorizationBearer.php
  79. 4 7
      src/Middleware/ErrorHandler.php
  80. 0 3
      src/Middleware/Guest.php
  81. 6 8
      src/Middleware/NodeToken.php
  82. 2 2
      src/Models/Model.php
  83. 1 1
      src/Services/Gateway/AbstractPayment.php
  84. 1 1
      src/Services/Gateway/AopF2F.php
  85. 1 1
      src/Services/Gateway/Epay.php
  86. 1 1
      src/Services/Gateway/PAYJS.php
  87. 1 1
      src/Services/Gateway/PaymentWall.php
  88. 1 1
      src/Services/Gateway/StripeCard.php
  89. 1 1
      src/Services/Gateway/THeadPay.php
  90. 1 1
      src/Services/Gateway/Vmqpay.php
  91. 42 3
      update.sh

+ 77 - 44
app/routes.php

@@ -4,12 +4,11 @@ declare(strict_types=1);
 
 use App\Middleware\Admin;
 use App\Middleware\Auth;
-use App\Middleware\AuthorizationBearer;
 use App\Middleware\Guest;
 use App\Middleware\NodeToken;
 use Slim\Routing\RouteCollectorProxy;
 
-return function (Slim\App $app): void {
+return static function (Slim\App $app): void {
     // Home
     $app->get('/', App\Controllers\HomeController::class . ':index');
     $app->get('/404', App\Controllers\HomeController::class . ':page404');
@@ -25,31 +24,41 @@ return function (Slim\App $app): void {
     $app->post('/telegram_callback', App\Controllers\HomeController::class . ':telegram');
 
     // User Center
-    $app->group('/user', function (RouteCollectorProxy $group): void {
+    $app->group('/user', static function (RouteCollectorProxy $group): void {
         $group->get('', App\Controllers\UserController::class . ':index');
         $group->get('/', App\Controllers\UserController::class . ':index');
 
+        // 签到
         $group->post('/checkin', App\Controllers\UserController::class . ':doCheckin');
 
+        // 公告
         $group->get('/announcement', App\Controllers\UserController::class . ':announcement');
+
+        // 文档
         $group->get('/docs', App\Controllers\UserController::class . ':docs');
 
+        //流媒体解锁
         $group->get('/media', App\Controllers\UserController::class . ':media');
 
         $group->get('/profile', App\Controllers\UserController::class . ':profile');
         $group->get('/invite', App\Controllers\UserController::class . ':invite');
+
+        // 封禁
         $group->get('/banned', App\Controllers\UserController::class . ':banned');
 
+        // 节点
         $group->get('/server', App\Controllers\User\ServerController::class . ':userServerPage');
 
-        $group->get('/detect', App\Controllers\User\DetectController::class . ':detectIndex');
-        $group->get('/detect/log', App\Controllers\User\DetectController::class . ':detectLog');
+        // 审计
+        $group->get('/detect', App\Controllers\User\DetectController::class . ':index');
+        $group->get('/detect/log', App\Controllers\User\DetectController::class . ':log');
 
         $group->get('/shop', App\Controllers\User\ShopController::class . ':shop');
         $group->post('/coupon_check', App\Controllers\User\ShopController::class . ':couponCheck');
         $group->post('/buy', App\Controllers\User\ShopController::class . ':buy');
         $group->post('/buy_traffic_package', App\Controllers\User\ShopController::class . ':buyTrafficPackage');
 
+        // 工单
         $group->get('/ticket', App\Controllers\User\TicketController::class . ':ticket');
         $group->get('/ticket/create', App\Controllers\User\TicketController::class . ':ticketCreate');
         $group->post('/ticket', App\Controllers\User\TicketController::class . ':ticketAdd');
@@ -64,12 +73,10 @@ return function (Slim\App $app): void {
         $group->post('/password', App\Controllers\UserController::class . ':updatePassword');
         $group->post('/send', App\Controllers\AuthController::class . ':sendVerify');
         $group->post('/contact_update', App\Controllers\UserController::class . ':updateContact');
-        $group->post('/ssr', App\Controllers\UserController::class . ':updateSSR');
         $group->post('/theme', App\Controllers\UserController::class . ':updateTheme');
         $group->post('/mail', App\Controllers\UserController::class . ':updateMail');
         $group->post('/passwd_reset', App\Controllers\UserController::class . ':resetPasswd');
         $group->post('/method', App\Controllers\UserController::class . ':updateMethod');
-        $group->get('/trafficlog', App\Controllers\UserController::class . ':trafficLog');
         $group->get('/kill', App\Controllers\UserController::class . ':kill');
         $group->post('/kill', App\Controllers\UserController::class . ':handleKill');
         $group->get('/logout', App\Controllers\UserController::class . ':logout');
@@ -97,20 +104,20 @@ return function (Slim\App $app): void {
         // getUserAllURL
         $group->get('/getUserAllURL', App\Controllers\UserController::class . ':getUserAllURL');
 
-        //Reconstructed Payment System
+        // 支付
         $group->post('/payment/purchase/{type}', App\Services\Payment::class . ':purchase');
         $group->get('/payment/purchase/{type}', App\Services\Payment::class . ':purchase');
         $group->get('/payment/return/{type}', App\Services\Payment::class . ':returnHTML');
     })->add(new Auth());
 
-    $app->group('/payment', function (RouteCollectorProxy $group): void {
+    $app->group('/payment', static function (RouteCollectorProxy $group): void {
         $group->get('/notify/{type}', App\Services\Payment::class . ':notify');
         $group->post('/notify/{type}', App\Services\Payment::class . ':notify');
         $group->post('/status/{type}', App\Services\Payment::class . ':getStatus');
     });
 
     // Auth
-    $app->group('/auth', function (RouteCollectorProxy $group): void {
+    $app->group('/auth', static function (RouteCollectorProxy $group): void {
         $group->get('/login', App\Controllers\AuthController::class . ':login');
         $group->post('/qrcode_check', App\Controllers\AuthController::class . ':qrcodeCheck');
         $group->post('/login', App\Controllers\AuthController::class . ':loginHandle');
@@ -123,7 +130,7 @@ return function (Slim\App $app): void {
     })->add(new Guest());
 
     // Password
-    $app->group('/password', function (RouteCollectorProxy $group): void {
+    $app->group('/password', static function (RouteCollectorProxy $group): void {
         $group->get('/reset', App\Controllers\PasswordController::class . ':reset');
         $group->post('/reset', App\Controllers\PasswordController::class . ':handleReset');
         $group->get('/token/{token}', App\Controllers\PasswordController::class . ':token');
@@ -131,7 +138,7 @@ return function (Slim\App $app): void {
     })->add(new Guest());
 
     // Admin
-    $app->group('/admin', function (RouteCollectorProxy $group): void {
+    $app->group('/admin', static function (RouteCollectorProxy $group): void {
         $group->get('', App\Controllers\AdminController::class . ':index');
         $group->get('/', App\Controllers\AdminController::class . ':index');
 
@@ -217,9 +224,10 @@ return function (Slim\App $app): void {
         $group->post('/user/ajax', App\Controllers\Admin\UserController::class . ':ajax');
 
         // Coupon Mange
-        $group->get('/coupon', App\Controllers\AdminController::class . ':coupon');
-        $group->post('/coupon', App\Controllers\AdminController::class . ':addCoupon');
-        $group->post('/coupon/ajax', App\Controllers\AdminController::class . ':ajaxCoupon');
+        $group->get('/coupon', App\Controllers\Admin\CouponController::class . ':index');
+        $group->post('/coupon', App\Controllers\Admin\CouponController::class . ':add');
+        $group->post('/coupon/ajax', App\Controllers\Admin\CouponController::class . ':ajax');
+        $group->delete('/coupon/{id}', App\Controllers\Admin\CouponController::class . ':delete');
 
         // Subscribe Log Mange
         $group->get('/subscribe', App\Controllers\Admin\SubscribeLogController::class . ':index');
@@ -238,45 +246,70 @@ return function (Slim\App $app): void {
         $group->post('/setting', App\Controllers\Admin\SettingController::class . ':save');
         $group->post('/setting/email', App\Controllers\Admin\SettingController::class . ':test');
         $group->post('/setting/payment', App\Controllers\Admin\SettingController::class . ':payment');
+
+        // 礼品卡
+        $group->get('/giftcard', App\Controllers\Admin\GiftCardController::class . ':index');
+        $group->post('/giftcard', App\Controllers\Admin\GiftCardController::class . ':add');
+        $group->post('/giftcard/ajax', App\Controllers\Admin\GiftCardController::class . ':ajax');
+        $group->delete('/giftcard/{id}', App\Controllers\Admin\GiftCardController::class . ':delete');
+
+        // 商品
+        $group->get('/product', App\Controllers\Admin\ProductController::class . ':index');
+        $group->get('/product/create', App\Controllers\Admin\ProductController::class . ':create');
+        $group->post('/product', App\Controllers\Admin\ProductController::class . ':add');
+        $group->get('/product/{id}/edit', App\Controllers\Admin\ProductController::class . ':edit');
+        $group->post('/product/{id}/copy', App\Controllers\Admin\ProductController::class . ':copy');
+        $group->put('/product/{id}', App\Controllers\Admin\ProductController::class . ':update');
+        $group->delete('/product/{id}', App\Controllers\Admin\ProductController::class . ':delete');
+        $group->post('/product/ajax', App\Controllers\Admin\ProductController::class . ':ajax');
+
+        // 订单
+        $group->get('/order', App\Controllers\Admin\OrderController::class . ':index');
+        $group->get('/order/{id}/view', App\Controllers\Admin\OrderController::class . ':detail');
+        $group->post('/order/{id}/cancel', App\Controllers\Admin\OrderController::class . ':cancel');
+        $group->delete('/order/{id}', App\Controllers\Admin\OrderController::class . ':delete');
+        $group->post('/order/ajax', App\Controllers\Admin\OrderController::class . ':ajax');
+
+        // 账单
+        $group->get('/invoice', App\Controllers\Admin\InvoiceController::class . ':index');
+        $group->get('/invoice/{id}/view', App\Controllers\Admin\InvoiceController::class . ':detail');
+        $group->post('/invoice/{id}/mark_paid', App\Controllers\Admin\InvoiceController::class . ':markPaid');
+        $group->post('/invoice/ajax', App\Controllers\Admin\InvoiceController::class . ':ajax');
     })->add(new Admin());
 
-    if ($_ENV['enableAdminApi']) {
-        $app->group('/admin/api', function (RouteCollectorProxy $group): void {
-            $group->get('/nodes', App\Controllers\Admin\ApiController::class . ':getNodeList');
-            $group->get('/node/{id}', App\Controllers\Admin\ApiController::class . ':getNodeInfo');
-            $group->get('/ping', App\Controllers\Admin\ApiController::class . ':ping');
-
-            // Re-bind controller, bypass admin token require
-            $group->post('/node', App\Controllers\Admin\NodeController::class . ':add');
-            $group->put('/node/{id}', App\Controllers\Admin\NodeController::class . ':update');
-            $group->delete('/node', App\Controllers\Admin\NodeController::class . ':delete');
-        })->add(new AuthorizationBearer($_ENV['adminApiToken']));
-    }
-
-    // mu
-    $app->group('/mod_mu', function (RouteCollectorProxy $group): void {
+    //$app->group('/admin/api', function (RouteCollectorProxy $group): void {
+    //    $group->post('/{action}', App\Controllers\Api\AdminApiController::class . ':actionHandler');
+    //})->add(new AdminApiToken());
+
+    //$app->group('/user/api', function (RouteCollectorProxy $group): void {
+    //    $group->post('/{action}', App\Controllers\Api\UserApiController::class . ':actionHandler');
+    //})->add(new UserApiToken());
+
+    // WebAPI
+    $app->group('/mod_mu', static function (RouteCollectorProxy $group): void {
         // 流媒体检测
-        $group->post('/media/saveReport', App\Controllers\Node\NodeController::class . ':saveReport');
+        $group->post('/media/save_report', App\Controllers\WebAPI\NodeController::class . ':saveReport');
         // 节点
-        $group->get('/nodes', App\Controllers\Node\NodeController::class . ':getAllInfo');
-        $group->get('/nodes/{id}/info', App\Controllers\Node\NodeController::class . ':getInfo');
-        $group->post('/nodes/{id}/info', App\Controllers\Node\NodeController::class . ':info');
+        $group->get('/nodes/{id}/info', App\Controllers\WebAPI\NodeController::class . ':getInfo');
         // 用户
-        $group->get('/users', App\Controllers\Node\UserController::class . ':index');
-        $group->post('/users/traffic', App\Controllers\Node\UserController::class . ':addTraffic');
-        $group->post('/users/aliveip', App\Controllers\Node\UserController::class . ':addAliveIp');
-        $group->post('/users/detectlog', App\Controllers\Node\UserController::class . ':addDetectLog');
+        $group->get('/users', App\Controllers\WebAPI\UserController::class . ':index');
+        $group->post('/users/traffic', App\Controllers\WebAPI\UserController::class . ':addTraffic');
+        $group->post('/users/aliveip', App\Controllers\WebAPI\UserController::class . ':addAliveIp');
+        $group->post('/users/detectlog', App\Controllers\WebAPI\UserController::class . ':addDetectLog');
         // 审计 & 杂七杂八的功能
-        $group->get('/func/detect_rules', App\Controllers\Node\FuncController::class . ':getDetectLogs');
-        $group->get('/func/ping', App\Controllers\Node\FuncController::class . ':ping');
+        $group->get('/func/detect_rules', App\Controllers\WebAPI\FuncController::class . ':getDetectLogs');
+        $group->get('/func/ping', App\Controllers\WebAPI\FuncController::class . ':ping');
         // Dummy API for old version
-        $group->post('/func/block_ip', App\Controllers\Node\FuncController::class . ':addBlockIp');
-        $group->get('/func/block_ip', App\Controllers\Node\FuncController::class . ':getBlockip');
-        $group->get('/func/unblock_ip', App\Controllers\Node\FuncController::class . ':getUnblockip');
+        $group->get('/nodes', App\Controllers\WebAPI\NodeController::class . ':getAllInfo');
+        $group->post('/func/block_ip', App\Controllers\WebAPI\FuncController::class . ':addBlockIp');
+        $group->get('/func/block_ip', App\Controllers\WebAPI\FuncController::class . ':getBlockip');
+        $group->get('/func/unblock_ip', App\Controllers\WebAPI\FuncController::class . ':getUnblockip');
+        $group->post('/nodes/{id}/info', App\Controllers\WebAPI\NodeController::class . ':info');
     })->add(new NodeToken());
 
+    // 传统订阅(SS/V2Ray/Trojan etc.)
     $app->get('/link/{token}', App\Controllers\LinkController::class . ':getContent');
 
-    //通用訂閲
+    // 通用订阅(Json/Clash)
     $app->get('/sub/{token}/{subtype}', App\Controllers\SubController::class . ':getContent');
 };

+ 0 - 1
composer.json

@@ -21,7 +21,6 @@
         "paymentwall/paymentwall-php": "^2",
         "phpmailer/phpmailer": "^6",
         "ramsey/uuid": "^4",
-        "robmorgan/phinx": "^0.13",
         "sendgrid/sendgrid": "^8",
         "sentry/sentry": "^3",
         "slim/http": "^1.3",

+ 10 - 0
config/settings.json

@@ -1248,5 +1248,15 @@
         "type": "string",
         "default": "https://t.me/joinchat/XXXXX",
         "mark": "用户群组链接"
+    },
+    {
+        "id": null,
+        "item": "db_version",
+        "value": "20230201000",
+        "class": "db",
+        "is_public": 0,
+        "type": "int",
+        "default": "20230201000",
+        "mark": "当前数据库版本"
     }
 ]

+ 0 - 404
db/migrations/20000101000000_init_database.php.new

@@ -1,404 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Db\Adapter\MysqlAdapter;
-use Phinx\Migration\AbstractMigration;
-
-final class InitDatabase extends AbstractMigration
-{
-    public function up(): void
-    {
-        $this->table('user', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'comment' => '用户ID', 'identity' => true, 'signed' => false, 'null' => false ])
-            ->addColumn('user_name', 'string', [ 'comment' => '用户名' ])
-            ->addColumn('email', 'string', [ 'comment' => 'E-Mail' ])
-            ->addIndex([ 'email' ], [ 'unique' => true ])
-            ->addColumn('pass', 'string', [ 'comment' => '登录密码' ])
-            ->addColumn('passwd', 'string', [ 'comment' => '节点密码' ])
-            ->addColumn('uuid', 'uuid', [ 'comment' => '用户UUID', 'null' => false ])
-            ->addIndex([ 'uuid' ], [ 'unique' => true ])
-            ->addColumn('t', 'biginteger', [ 'comment' => '最后使用时间', 'default' => 0, 'signed' => false])
-            ->addColumn('u', 'biginteger', [ 'comment' => '账户当前上传流量', 'default' => 0, 'signed' => false])
-            ->addColumn('d', 'biginteger', [ 'comment' => '账户当前下载流量', 'default' => 0, 'signed' => false])
-            ->addColumn('transfer_total', 'biginteger', [ 'comment' => '账户累计使用流量', 'default' => 0, 'signed' => false])
-            ->addColumn('transfer_enable', 'biginteger', [ 'comment' => '账户当前可用流量', 'default' => 0, 'signed' => false ])
-            ->addColumn('port', 'smallinteger', [ 'comment' => '用户端口', 'null' => false ])
-            ->addColumn('last_detect_ban_time', 'datetime', [ 'comment' => '最后一次被封禁的时间', 'default' => '1989-06-04 00:05:00' ])
-            ->addColumn('all_detect_number', 'integer', [ 'comment' => '累计违规次数', 'default' => 0 ])
-            ->addColumn('last_check_in_time', 'biginteger', [ 'comment' => '最后签到时间', 'default' => 0, 'signed' => false ])
-            ->addColumn('reg_date', 'datetime', [ 'comment' => '注册时间' ])
-            ->addColumn('invite_num', 'integer', [ 'comment' => '可用邀请次数', 'default' => 0 ])
-            ->addColumn('money', 'decimal', [ 'comment' => '钱包余额', 'default' => 0 ])
-            ->addColumn('ref_by', 'biginteger', [ 'comment' => '邀请人ID', 'default' => '0', 'signed' => false ])
-            ->addColumn('method', 'string', [ 'comment' => 'SS/SSR加密方式', 'default' => 'rc4-md5' ])
-            ->addColumn('reg_ip', 'string', [ 'comment' => '注册IP', 'default' => '127.0.0.1' ])
-            ->addColumn('node_speedlimit', 'double', [ 'comment' => '用户节点限速', 'default' => 0, 'null' => false ])
-            ->addColumn('node_iplimit', 'smallinteger', [ 'comment' => '同时可连接IP数', 'default' => 0, 'null' => false ])
-            ->addColumn('node_connector', 'integer', [ 'comment' => '同时可使用连接数', 'default' => 0 ])
-            ->addColumn('is_admin', 'boolean', [ 'comment' => '是否管理员', 'default' => false ])
-            ->addColumn('im_type', 'integer', [ 'comment' => '联系方式类型', 'default' => 1 ])
-            ->addColumn('im_value', 'string', [ 'comment' => '联系方式', 'default' => '' ])
-            ->addColumn('last_day_t', 'biginteger', [ 'comment' => '今天之前已使用的流量', 'default' => 0 ])
-            ->addColumn('sendDailyMail', 'boolean', [ 'comment' => '每日报告开关', 'default' => 0 ])
-            ->addColumn('class', 'integer', [ 'comment' => '用户等级', 'default' => 0, 'signed' => false, 'null' => false ])
-            ->addColumn('class_expire', 'datetime', [ 'comment' => '等级过期时间', 'default' => '1989-06-04 00:05:00' ])
-            ->addColumn('expire_in', 'datetime', [ 'default' => '2099-06-04 00:05:00' ])
-            ->addColumn('theme', 'string', [ 'comment' => '网站主题' ])
-            ->addColumn('ga_token', 'string', [ ])
-            ->addIndex([ 'ga_token' ], [ 'unique' => true ])
-            ->addColumn('ga_enable', 'integer', [ 'default' => '0' ])
-            ->addColumn('remark', 'text', [ 'comment' => '备注', 'default' => '' ])
-            ->addColumn('node_group', 'integer', [ 'comment' => '用户等级', 'default' => 0, 'signed' => false, 'null' => false ])
-            ->addColumn('protocol', 'string', [ 'comment' => 'SS/SSR协议方式', 'default' => 'origin' ])
-            ->addColumn('protocol_param', 'string', [ 'default' => '' ])
-            ->addColumn('obfs', 'string', [ 'comment' => 'SS/SSR混淆方式', 'default' => 'plain' ])
-            ->addColumn('obfs_param', 'string', [ 'default' => '' ])
-            ->addColumn('is_banned', 'integer', [ 'comment' => '是否封禁', 'default' => 0 ])
-            ->addColumn('banned_reason', 'string', [ 'comment' => '封禁理由', 'default' => '' ])
-            ->addColumn('is_multi_user', 'integer', [ 'default' => 0 ])
-            ->addColumn('telegram_id', 'biginteger', [ 'default' => 0 ])
-            ->addColumn('expire_notified', 'boolean', [ 'default' => false])
-            ->addColumn('traffic_notified', 'boolean', [ 'default' => false])
-            ->addColumn('forbidden_ip', 'string', [ 'default' => '' ])
-            ->addColumn('forbidden_port', 'string', [ 'default' => '' ])
-            ->addColumn('auto_reset_day', 'integer', [ 'default' => 0])
-            ->addColumn('auto_reset_bandwidth', 'decimal', [ 'default' => '0.00', 'precision' => 12, 'scale' => 2 ])
-            ->addColumn('api_token', 'uuid', [ 'comment' => 'API 密钥', 'null' => false, 'default' => '' ])
-            ->addColumn('use_new_shop', 'smallinteger', [ 'comment' => '是否启用新商店', 'null' => false, 'default' => 0 ])
-            ->addIndex([ 'user_name' ])
-            ->create();
-
-        $this->table('node', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'integer', [ 'identity' => true ])
-            ->addColumn('name', 'string', [])
-            ->addColumn('type', 'integer', [])
-            ->addColumn('server', 'string', [])
-            ->addColumn('custom_config', 'json', [ 'comment' => '自定义配置', 'default' => '{}' ])
-            ->addColumn('info', 'text', [ 'default' => '' ])
-            ->addColumn('status', 'string', [ 'default' => '' ])
-            ->addColumn('sort', 'integer', [])
-            ->addColumn('traffic_rate', 'float', [ 'default' => 1 ])
-            ->addColumn('node_class', 'integer', [ 'default' => 0 ])
-            ->addColumn('node_speedlimit', 'double', [ 'comment' => '节点限速', 'default' => 0, 'null' => false ])
-            ->addColumn('node_connector', 'integer', [ 'default' => 0 ])
-            ->addColumn('node_bandwidth', 'biginteger', [ 'default' => 0 ])
-            ->addColumn('node_bandwidth_limit', 'biginteger', [ 'default' => 0 ])
-            ->addColumn('bandwidthlimit_resetday', 'integer', [ 'default' => 0 ])
-            ->addColumn('node_heartbeat', 'biginteger', [ 'default' => 0 ])
-            ->addColumn('online_user', 'integer', [ 'comment' => '节点在线用户', 'default' => 0 ])
-            ->addColumn('node_ip', 'string', [ 'default' => null ])
-            ->addColumn('node_group', 'integer', [ 'default' => 0 ])
-            ->addColumn('mu_only', 'boolean', [ 'default' => false ])
-            ->addColumn('online', 'boolean', [ 'default' => true ])
-            ->addColumn('gfw_block', 'boolean', [ 'default' => false ])
-            ->addColumn('password', 'string', [])
-            ->create();
-
-        $this->table('alive_ip', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('nodeid', 'integer', [])
-            ->addColumn('userid', 'integer', [])
-            ->addColumn('ip', 'string', [])
-            ->addColumn('datetime', 'biginteger', [])
-            ->create();
-
-        $this->table('announcement', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'integer', [ 'identity' => true ])
-            ->addColumn('date', 'datetime', [])
-            ->addColumn('content', 'text', [])
-            ->create();
-
-        $this->table('bought', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('userid', 'biginteger', [])
-            ->addColumn('shopid', 'biginteger', [])
-            ->addColumn('datetime', 'biginteger', [])
-            ->addColumn('renew', 'biginteger', [])
-            ->addColumn('coupon', 'string', [])
-            ->addColumn('price', 'decimal', [ 'precision' => 12, 'scale' => 2 ])
-            ->addColumn('is_notified', 'boolean', [ 'default' => false ])
-            ->create();
-
-        $this->table('code', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('code', 'string', [])
-            ->addColumn('type', 'integer', [])
-            ->addColumn('number', 'decimal', [ 'precision' => 12, 'scale' => 2 ])
-            ->addColumn('isused', 'integer', [ 'default' => 0 ])
-            ->addColumn('userid', 'biginteger', [])
-            ->addColumn('usedatetime', 'datetime', [])
-            ->create();
-
-        $this->table('config', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'integer', [ 'comment' => '主键','identity' => true ])
-            ->addColumn('item', 'string', [ 'comment' => '项' ])
-            ->addColumn('value', 'string', [ 'comment' => '值' ])
-            ->addColumn('class', 'string', [ 'comment' => '配置分类','default' => 'default' ])
-            ->addColumn('is_public', 'integer', [ 'comment' => '是否为公共参数','default' => 0 ])
-            ->addColumn('type', 'string', [ 'comment' => '值类型' ])
-            ->addColumn('default', 'string', [ 'comment' => '默认值' ])
-            ->addColumn('mark', 'string', [ 'comment' => '备注' ])
-            ->create();
-
-        $this->table('coupon', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('code', 'string', [])
-            ->addColumn('onetime', 'integer', [])
-            ->addColumn('expire', 'biginteger', [])
-            ->addColumn('shop', 'string', [])
-            ->addColumn('credit', 'integer', [])
-            ->create();
-
-        $this->table('detect_ban_log', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'integer', [ 'identity' => true, 'signed' => false ])
-            ->addColumn('user_name', 'string', [ 'comment' => '用户名' ])
-            ->addColumn('user_id', 'biginteger', [ 'comment' => '用户 ID', 'signed' => false ])
-            ->addColumn('email', 'string', [ 'comment' => '用户邮箱' ])
-            ->addColumn('detect_number', 'integer', [ 'comment' => '本次违规次数' ])
-            ->addColumn('ban_time', 'integer', [ 'comment' => '本次封禁时长' ])
-            ->addColumn('start_time', 'biginteger', [ 'comment' => '统计开始时间' ])
-            ->addColumn('end_time', 'biginteger', [ 'comment' => '统计结束时间' ])
-            ->addColumn('all_detect_number', 'integer', [ 'comment' => '累计违规次数' ])
-            ->addIndex([ 'user_id' ])
-            ->addForeignKey('user_id', 'user', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-            ->create();
-
-        $this->table('detect_list', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true,'signed' => false ])
-            ->addColumn('name', 'string', [])
-            ->addColumn('text', 'string', [])
-            ->addColumn('regex', 'string', [])
-            ->addColumn('type', 'integer', [])
-            ->create();
-
-        $this->table('detect_log', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true,'signed' => false ])
-            ->addColumn('user_id', 'biginteger', [ 'signed' => false ])
-            ->addColumn('list_id', 'biginteger', [ 'signed' => false ])
-            ->addColumn('datetime', 'biginteger', [ 'signed' => false ])
-            ->addColumn('node_id', 'integer', [])
-            ->addColumn('status', 'integer', [ 'default' => 0 ])
-            ->addIndex([ 'user_id' ])
-            ->addIndex([ 'node_id' ])
-            ->addIndex([ 'list_id' ])
-            ->addForeignKey('user_id', 'user', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-            ->addForeignKey('node_id', 'node', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-            ->addForeignKey('list_id', 'detect_list', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-            ->create();
-
-        $this->table('email_queue', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('to_email', 'string', [])
-            ->addColumn('subject', 'string', [])
-            ->addColumn('template', 'string', [])
-            ->addColumn('array', 'text', ['limit' => MysqlAdapter::TEXT_LONG])
-            ->addColumn('time', 'integer', [])
-            ->create();
-
-        $this->table('email_verify', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('email', 'string', [])
-            ->addColumn('ip', 'string', [])
-            ->addColumn('code', 'string', [])
-            ->addColumn('expire_in', 'biginteger', [])
-            ->create();
-
-        $this->table('link', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('token', 'string', [ ])
-            ->addIndex([ 'token' ], [ 'unique' => true ])
-            ->addColumn('userid', 'biginteger', [ 'signed' => false ])
-            ->addIndex([ 'userid' ], [ 'unique' => true ])
-            ->addForeignKey('userid', 'user', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-            ->create();
-
-        $this->table('login_ip', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('userid', 'biginteger', [ 'signed' => false ])
-            ->addColumn('ip', 'string', [])
-            ->addColumn('datetime', 'biginteger', [])
-            ->addColumn('type', 'integer', [])
-            ->addIndex([ 'userid' ])
-            ->addForeignKey('userid', 'user', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-            ->create();
-
-        $this->table('payback', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('total', 'decimal', [ 'precision' => 12, 'scale' => 2 ])
-            ->addColumn('userid', 'biginteger', [])
-            ->addColumn('ref_by', 'biginteger', [])
-            ->addColumn('ref_get', 'decimal', [ 'precision' => 12, 'scale' => 2 ])
-            ->addColumn('datetime', 'biginteger', [])
-            ->create();
-
-        $this->table('paylist', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('userid', 'biginteger', [ 'signed' => false ])
-            ->addColumn('total', 'decimal', [ 'precision' => 12, 'scale' => 2 ])
-            ->addColumn('status', 'integer', [ 'default' => 0 ])
-            ->addColumn('tradeno', 'string', [ 'default' => null ])
-            ->addColumn('datetime', 'biginteger', [ 'default' => 0 ])
-            ->addIndex([ 'userid' ])
-            ->addForeignKey('userid', 'user', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-            ->create();
-
-        $this->table('shop', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('name', 'string', [])
-            ->addColumn('price', 'decimal', [ 'precision' => 12, 'scale' => 2 ])
-            ->addColumn('content', 'text', [])
-            ->addColumn('auto_renew', 'integer', [])
-            ->addColumn('auto_reset_bandwidth', 'integer', [ 'default' => 0 ])
-            ->addColumn('status', 'integer', [ 'default' => 1 ])
-            ->create();
-
-        $this->table('user_invite_code', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true, 'signed' => false ])
-            ->addColumn('code', 'string', [  ])
-            ->addIndex([ 'code' ], [ 'unique' => true ])
-            ->addColumn('user_id', 'biginteger', [ 'signed' => false ])
-            ->addIndex([ 'user_id' ], [ 'unique' => true ])
-            ->addColumn('created_at', 'timestamp', [ 'default' => 'CURRENT_TIMESTAMP', 'update' => 'CURRENT_TIMESTAMP' ])
-            ->addColumn('updated_at', 'timestamp', [ 'default' => '2016-06-01 00:00:00' ])
-            ->addForeignKey('user_id', 'user', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-            ->create();
-
-        $this->table('user_password_reset', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'integer', [ 'identity' => true ])
-            ->addColumn('email', 'string', [])
-            ->addColumn('token', 'string', [])
-            ->addColumn('init_time', 'integer', [])
-            ->addColumn('expire_time', 'integer', [])
-            ->create();
-
-        $this->table('telegram_session', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('user_id', 'biginteger', [])
-            ->addColumn('type', 'integer', [])
-            ->addColumn('session_content', 'string', [])
-            ->addColumn('datetime', 'biginteger', [])
-            ->create();
-
-        $this->table('ticket', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('title', 'string', [])
-            ->addColumn('content', 'json', [ 'comment' => '工单内容', 'default' => '' ])
-            ->addColumn('userid', 'biginteger', [])
-            ->addColumn('datetime', 'biginteger', [])
-            ->addColumn('status', 'string', [ 'comment' => '工单状态', 'default' => '' ])
-            ->addColumn('type', 'string', [ 'comment' => '工单类型', 'default' => 'other' ])
-            ->create();
-
-        $this->table('user_hourly_usage', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true, 'signed' => false ])
-            ->addColumn('user_id', 'biginteger', [ 'signed' => false ])
-            ->addColumn('traffic', 'biginteger', [])
-            ->addColumn('hourly_usage', 'biginteger', [])
-            ->addColumn('datetime', 'integer', [])
-            ->addIndex([ 'user_id' ])
-            ->addForeignKey('user_id', 'user', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-            ->create();
-
-        $this->table('user_subscribe_log', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true, 'signed' => false ])
-            ->addColumn('user_name', 'string', [ 'comment' => '用户名' ])
-            ->addColumn('user_id', 'biginteger', [ 'comment' => '用户 ID','signed' => false ])
-            ->addColumn('email', 'string', [ 'comment' => '用户邮箱' ])
-            ->addColumn('subscribe_type', 'string', [ 'comment' => '获取的订阅类型' ])
-            ->addColumn('request_ip', 'string', [ 'comment' => '请求 IP' ])
-            ->addColumn('request_time', 'datetime', [ 'comment' => '请求时间' ])
-            ->addColumn('request_user_agent', 'text', [ 'comment' => '请求 UA 信息','default' => null ])
-            ->addIndex([ 'user_id' ])
-            ->addForeignKey('user_id', 'user', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-            ->create();
-
-        $this->table('user_token', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true, 'signed' => false ])
-            ->addColumn('token', 'string', [])
-            ->addColumn('user_id', 'biginteger', [ 'signed' => false ])
-            ->addColumn('create_time', 'biginteger', [ 'signed' => false ])
-            ->addColumn('expire_time', 'biginteger', [])
-            ->addIndex([ 'user_id' ])
-            ->addForeignKey('user_id', 'user', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-            ->create();
-
-        $this->table('gift_card', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true,'signed' => false ])
-            ->addColumn('card', 'text', ['comment' => '卡号'])
-            ->addColumn('balance', 'integer', ['comment' => '余额'])
-            ->addColumn('create_time', 'integer', ['comment' => '创建时间'])
-            ->addColumn('status', 'integer', ['comment' => '使用状态'])
-            ->addColumn('use_time', 'integer', ['comment' => '使用时间'])
-            ->addColumn('use_user', 'integer', ['comment' => '使用用户'])
-            ->addIndex([ 'id' ])
-            ->addIndex([ 'status' ])
-            ->create();
-
-        $this->table('product', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'integer', [ 'comment' => '商品ID', 'identity' => true ])
-            ->addColumn('type', 'string', [ 'comment' => '类型' ])
-            ->addColumn('name', 'string', [ 'comment' => '名称' ])
-            ->addColumn('price', 'double', [ 'comment' => '售价' ])
-            ->addColumn('content', 'json', [ 'comment' => '内容' ])
-            ->addColumn('limit', 'json', [ 'comment' => '购买限制'])
-            ->addColumn('status', 'integer', [ 'comment' => '销售状态' ])
-            ->addColumn('create_time', 'integer', [ 'comment' => '创建时间' ])
-            ->addColumn('update_time', 'integer', [ 'comment' => '更新时间' ])
-            ->addColumn('sale_count', 'integer', [ 'comment' => '累计销售数'])
-            ->addColumn('stock', 'integer', [ 'comment' => '库存'])
-            ->addIndex([ 'id' ])
-            ->addIndex([ 'type' ])
-            ->create();
-
-        $this->table('order', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'integer', [ 'comment' => '订单ID', 'identity' => true ])
-            ->addColumn('user_id', 'integer', [ 'comment' => '提交用户' ])
-            ->addColumn('product_id', 'integer', [ 'comment' => '商品ID' ])
-            ->addColumn('product_type', 'string', [ 'comment' => '商品类型' ])
-            ->addColumn('product_name', 'string', [ 'comment' => '商品名称' ])
-            ->addColumn('product_content', 'json', [ 'comment' => '商品内容' ])
-            ->addColumn('coupon', 'string', [ 'comment' => '订单优惠码'])
-            ->addColumn('price', 'double', [ 'comment' => '订单金额' ])
-            ->addColumn('status', 'string', [ 'comment' => '订单状态' ])
-            ->addColumn('create_time', 'integer', [ 'comment' => '创建时间' ])
-            ->addColumn('update_time', 'integer', [ 'comment' => '更新时间' ])
-            ->addIndex([ 'id' ])
-            ->addIndex([ 'user_id' ])
-            ->addIndex([ 'product_id' ])
-            ->addIndex([ 'status' ])
-            ->create();
-
-        $this->table('invoice', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'integer', [ 'comment' => '账单ID', 'identity' => true ])
-            ->addColumn('user_id', 'integer', [ 'comment' => '归属用户' ])
-            ->addColumn('order_id', 'integer', [ 'comment' => '订单ID' ])
-            ->addColumn('content', 'json', [ 'comment' => '账单内容' ])
-            ->addColumn('price', 'double', [ 'comment' => '账单金额' ])
-            ->addColumn('status', 'string', [ 'comment' => '账单状态' ])
-            ->addColumn('create_time', 'integer', [ 'comment' => '创建时间' ])
-            ->addColumn('update_time', 'integer', [ 'comment' => '更新时间' ])
-            ->addColumn('pay_time', 'integer', [ 'comment' => '支付时间' ])
-            ->addIndex([ 'id' ])
-            ->addIndex([ 'user_id' ])
-            ->addIndex([ 'order_id' ])
-            ->addIndex([ 'status' ])
-            ->create();
-
-        $this->table('user_coupon', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'integer', [ 'comment' => '优惠码ID', 'identity' => true ])
-            ->addColumn('code', 'string', [ 'comment' => '优惠码' ])
-            ->addColumn('content', 'json', [ 'comment' => '优惠码内容' ])
-            ->addColumn('limit', 'json', [ 'comment' => '优惠码限制' ])
-            ->addColumn('create_time', 'integer', [ 'comment' => '创建时间' ])
-            ->addColumn('expire_time', 'integer', [ 'comment' => '过期时间' ])
-            ->addIndex([ 'id' ])
-            ->addIndex([ 'code' ])
-            ->addIndex([ 'expire_time' ])
-            ->create();
-    }
-
-    public function down(): void
-    {
-    }
-}

+ 0 - 22
db/migrations/20220109130532_create_stream_media_table.php

@@ -1,22 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class CreateStreamMediaTable extends AbstractMigration
-{
-    public function up(): void
-    {
-        $table = $this->table('stream_media');
-        $table->addColumn('node_id', 'integer', ['comment' => '节点id'])
-            ->addColumn('result', 'text', ['comment' => '检测结果'])
-            ->addColumn('created_at', 'integer', ['comment' => '创建时间'])
-            ->create();
-    }
-
-    public function down(): void
-    {
-        $this->table('stream_media')->drop()->update();
-    }
-}

+ 0 - 15
db/migrations/20220514172426_adjust_configuration_table_value_field_length.php

@@ -1,15 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class AdjustConfigurationTableValueFieldLength extends AbstractMigration
-{
-    public function change(): void
-    {
-        $table = $this->table('config');
-        $table->changeColumn('value', 'string', ['limit' => 2048])
-            ->save();
-    }
-}

+ 0 - 17
db/migrations/20220522184308_fix_user_money_field.php

@@ -1,17 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class FixUserMoneyField extends AbstractMigration
-{
-    public function change(): void
-    {
-        // https://github.com/lucasjkr/opencommerce/blob/2a7c6851a61b5078842c6353393b7fea2b818b09/db/migrations/20171111111123_change_price_precision.php
-
-        $table = $this->table('user');
-        $table->changeColumn('money', 'decimal', ['precision' => 10, 'scale' => 2, 'null' => false, 'default' => '0.00'])
-            ->save();
-    }
-}

+ 0 - 32
db/migrations/20220814175532_drop_g_config_table.php

@@ -1,32 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class DropGConfigTable extends AbstractMigration
-{
-    public function up(): void
-    {
-        if ($this->hasTable('gconfig')) {
-            $this->table('gconfig')->drop()->update();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('gconfig', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'integer', [ 'identity' => true,'signed' => false ])
-            ->addColumn('key', 'string', [ 'comment' => '配置键名' ])
-            ->addColumn('type', 'string', [ 'comment' => '值类型' ])
-            ->addColumn('value', 'string', [ 'comment' => '配置值' ])
-            ->addColumn('oldvalue', 'string', [ 'comment' => '之前的配置值' ])
-            ->addColumn('name', 'string', [ 'comment' => '配置名称' ])
-            ->addColumn('comment', 'string', [ 'comment' => '配置描述' ])
-            ->addColumn('operator_id', 'integer', [ 'comment' => '操作员 ID' ])
-            ->addColumn('operator_name', 'string', [ 'comment' => '操作员名称' ])
-            ->addColumn('operator_email', 'string', [ 'comment' => '操作员邮箱' ])
-            ->addColumn('last_update', 'biginteger', [ 'comment' => '修改时间' ])
-            ->create();
-    }
-}

+ 0 - 24
db/migrations/20220902062145_add_node_password.php

@@ -1,24 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class AddNodePassword extends AbstractMigration
-{
-    public function up(): void
-    {
-        if (! $this->table('node')->hasColumn('password')) {
-            $this->table('node')
-                ->addColumn('password', 'string')
-                ->save();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('node')
-            ->removeColumn('password')
-            ->save();
-    }
-}

+ 0 - 28
db/migrations/20220909202811_merge_node_info.php

@@ -1,28 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class MergeNodeInfo extends AbstractMigration
-{
-    public function up(): void
-    {
-        if ($this->hasTable('node_info')) {
-            $this->table('node_info')->drop()->update();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('node_info', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true,'signed' => false ])
-            ->addColumn('node_id', 'integer', [])
-            ->addColumn('uptime', 'float', [])
-            ->addColumn('load', 'string', [])
-            ->addColumn('log_time', 'integer', [])
-            ->addIndex([ 'node_id' ])
-            ->addForeignKey('node_id', 'node', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-            ->create();
-    }
-}

+ 0 - 23
db/migrations/20220920012728_email_queue_use_longtext.php

@@ -1,23 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Db\Adapter\MysqlAdapter;
-use Phinx\Migration\AbstractMigration;
-
-final class EmailQueueUseLongtext extends AbstractMigration
-{
-    public function up(): void
-    {
-        $this->table('email_queue')
-            ->changeColumn('array', 'text', ['limit' => MysqlAdapter::TEXT_LONG])
-            ->save();
-    }
-
-    public function down(): void
-    {
-        $this->table('email_queue')
-            ->changeColumn('array', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR])
-            ->save();
-    }
-}

+ 0 - 37
db/migrations/20220926175600_add_traffic_log.php

@@ -1,37 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class AddTrafficLog extends AbstractMigration
-{
-    public function up(): void
-    {
-        if (! $this->table('user')->hasColumn('transfer_total')) {
-            $this->table('user')
-                ->addColumn('transfer_total', 'biginteger', [ 'comment' => '账户累计使用流量', 'default' => 0, 'signed' => false])
-                ->save();
-        }
-
-        if (! $this->hasTable('user_hourly_usage')) {
-            $this->table('user_hourly_usage', [ 'id' => false, 'primary_key' => [ 'id' ]])
-                ->addColumn('id', 'biginteger', [ 'identity' => true,'signed' => false ])
-                ->addColumn('user_id', 'biginteger', [ 'signed' => false ])
-                ->addColumn('traffic', 'biginteger', [])
-                ->addColumn('hourly_usage', 'biginteger', [])
-                ->addColumn('datetime', 'integer', [])
-                ->addIndex([ 'user_id' ])
-                ->addForeignKey('user_id', 'user', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-                ->create();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('user')
-            ->removeColumn('transfer_total')
-            ->save();
-        $this->table('user_hourly_usage')->drop()->update();
-    }
-}

+ 0 - 24
db/migrations/20221016073516_add_user_dark_mode.php

@@ -1,24 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class AddUserDarkMode extends AbstractMigration
-{
-    public function up(): void
-    {
-        if (! $this->table('user')->hasColumn('is_dark_mode')) {
-            $this->table('user')
-                ->addColumn('is_dark_mode', 'integer', ['default' => 0])
-                ->save();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('user')
-            ->removeColumn('is_dark_mode')
-            ->save();
-    }
-}

+ 0 - 26
db/migrations/20221024101400_add_docs.php

@@ -1,26 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class AddDocs extends AbstractMigration
-{
-    public function up(): void
-    {
-        if (! $this->hasTable('docs')) {
-            $this->table('docs', [ 'id' => false, 'primary_key' => [ 'id' ]])
-                ->addColumn('id', 'biginteger', [ 'identity' => true,'signed' => false ])
-                ->addColumn('date', 'datetime', [])
-                ->addColumn('title', 'string', [])
-                ->addColumn('content', 'string', [])
-                ->addColumn('markdown', 'string', [])
-                ->create();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('docs')->drop()->update();
-    }
-}

+ 0 - 34
db/migrations/20221026130700_add_ban_user.php

@@ -1,34 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class AddBanUser extends AbstractMigration
-{
-    public function up(): void
-    {
-        if (! $this->table('user')->hasColumn('is_banned')) {
-            $this->table('user')
-                ->addColumn('is_banned', 'integer', [ 'comment' => '是否封禁', 'default' => 0 ])
-                ->save();
-        }
-        if (! $this->table('user')->hasColumn('banned_reason')) {
-            $this->table('user')
-                ->addColumn('banned_reason', 'string', [ 'comment' => '封禁理由', 'default' => '' ])
-                ->save();
-        }
-        if ($this->table('user')->hasColumn('is_hide')) {
-            $this->table('user')
-                ->removeColumn('is_hide')
-                ->save();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('user')
-            ->addColumn('is_hide', 'integer', [ 'default' => 0 ])
-            ->save();
-    }
-}

+ 0 - 24
db/migrations/20221026143200_remove_user_enable.php

@@ -1,24 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class RemoveUserEnable extends AbstractMigration
-{
-    public function up(): void
-    {
-        if ($this->table('user')->hasColumn('enable')) {
-            $this->table('user')
-                ->removeColumn('enable')
-                ->save();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('user')
-            ->addColumn('enable', 'boolean', [ 'comment' => '是否启用', 'default' => true ])
-            ->save();
-    }
-}

+ 0 - 22
db/migrations/20221031185300_custom_config_json.php

@@ -1,22 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class CustomConfigJson extends AbstractMigration
-{
-    public function up(): void
-    {
-        $this->table('node')
-            ->changeColumn('custom_config', 'json', [ 'comment' => '自定义配置', 'default' => '{}' ])
-            ->save();
-    }
-
-    public function down(): void
-    {
-        $this->table('node')
-            ->changeColumn('custom_config', 'text')
-            ->save();
-    }
-}

+ 0 - 30
db/migrations/20221112115300_update_ticket.php

@@ -1,30 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class UpdateTicket extends AbstractMigration
-{
-    public function up(): void
-    {
-        if ($this->table('ticket')->hasColumn('rootid')) {
-            $this->table('ticket')
-                ->changeColumn('content', 'json', [ 'comment' => '工单内容', 'default' => '' ])
-                ->changeColumn('status', 'string', [ 'comment' => '工单状态', 'default' => '' ])
-                ->addColumn('type', 'string', [ 'comment' => '工单类型', 'default' => 'other' ])
-                ->removeColumn('rootid')
-                ->save();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('ticket')
-            ->changeColumn('content', 'text', [])
-            ->changeColumn('status', 'integer', [ 'default' => 1 ])
-            ->removeColumn('type')
-            ->addColumn('rootid', 'biginteger', [])
-            ->save();
-    }
-}

+ 0 - 35
db/migrations/20221124113800_remove_block.php

@@ -1,35 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class RemoveBlock extends AbstractMigration
-{
-    public function up(): void
-    {
-        if ($this->hasTable('blockip')) {
-            $this->table('blockip')->drop()->update();
-        }
-        if ($this->hasTable('unblockip')) {
-            $this->table('unblockip')->drop()->update();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('blockip', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('nodeid', 'integer', [])
-            ->addColumn('ip', 'string', [])
-            ->addColumn('datetime', 'biginteger', [])
-            ->create();
-
-        $this->table('unblockip', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'biginteger', [ 'identity' => true ])
-            ->addColumn('ip', 'string', [])
-            ->addColumn('datetime', 'biginteger', [])
-            ->addColumn('userid', 'biginteger', [])
-            ->create();
-    }
-}

+ 0 - 24
db/migrations/20221130005800_add_user_node_iplimit.php

@@ -1,24 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class AddUserNodeIplimit extends AbstractMigration
-{
-    public function up(): void
-    {
-        if (! $this->table('user')->hasColumn('node_iplimit')) {
-            $this->table('user')
-                ->addColumn('node_iplimit', 'smallinteger', [ 'comment' => '同时可连接IP数', 'default' => 0, 'signed' => false, 'null' => false ])
-                ->save();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('user')
-            ->removeColumn('node_iplimit')
-            ->save();
-    }
-}

+ 0 - 30
db/migrations/20221203142200_clean_node.php

@@ -1,30 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class CleanNode extends AbstractMigration
-{
-    public function up(): void
-    {
-        if ($this->table('node')->hasColumn('load')) {
-            $this->table('node')
-                ->removeColumn('load')
-                ->save();
-        }
-        if ($this->table('node')->hasColumn('uptime')) {
-            $this->table('node')
-                ->removeColumn('uptime')
-                ->save();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('node')
-            ->addColumn('load', 'string', ['default' => ''])
-            ->addColumn('uptime', 'integer', ['default' => 0])
-            ->save();
-    }
-}

+ 0 - 24
db/migrations/20221204134100_remove_ann_markdown.php

@@ -1,24 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class RemoveAnnMarkdown extends AbstractMigration
-{
-    public function up(): void
-    {
-        if ($this->table('announcement')->hasColumn('markdown')) {
-            $this->table('announcement')
-                ->removeColumn('markdown')
-                ->save();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('announcement')
-            ->addColumn('markdown', 'text', [])
-            ->save();
-    }
-}

+ 0 - 37
db/migrations/20221205162200_change_number_type.php

@@ -1,37 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class ChangeNumberType extends AbstractMigration
-{
-    public function up(): void
-    {
-        $this->table('user')
-            ->changeColumn('port', 'smallinteger', [ 'comment' => '端口', 'signed' => false, 'null' => false ])
-            ->changeColumn('class', 'integer', [ 'comment' => '等级', 'default' => 0, 'signed' => false, 'null' => false ])
-            ->changeColumn('node_group', 'integer', [ 'comment' => '节点分组', 'default' => 0, 'signed' => false, 'null' => false ])
-            ->changeColumn('node_speedlimit', 'double', [ 'comment' => '用户限速', 'default' => 0, 'null' => false ])
-            ->changeColumn('node_iplimit', 'smallinteger', [ 'comment' => '同时可连接IP数', 'default' => 0, 'signed' => false, 'null' => false ])
-            ->changeColumn('uuid', 'uuid', [ 'comment' => 'UUID', 'null' => false ])
-            ->save();
-        $this->table('node')
-            ->changeColumn('node_speedlimit', 'double', [ 'comment' => '节点限速', 'default' => 0, 'null' => false ])
-            ->save();
-    }
-
-    public function down(): void
-    {
-        $this->table('user')
-            ->changeColumn('port', 'integer', [ 'comment' => '用户端口' ])
-            ->changeColumn('class', 'integer', [ 'comment' => '用户等级', 'default' => 0 ])
-            ->changeColumn('node_group', 'integer', [ 'comment' => '节点分组', 'default' => 0 ])
-            ->changeColumn('node_speedlimit', 'decimal', [ 'comment' => '每个连接限速', 'default' => 0 ])
-            ->changeColumn('uuid', 'string', [ 'comment' => 'UUID' ])
-            ->save();
-        $this->table('node')
-            ->changeColumn('node_speedlimit', 'decimal', [ 'default' => 0.00,'precision' => 12, 'scale' => 2 ])
-            ->save();
-    }
-}

+ 0 - 24
db/migrations/20221208132600_add_user_api_token.php

@@ -1,24 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class AddUserApiToken extends AbstractMigration
-{
-    public function up(): void
-    {
-        if (! $this->table('user')->hasColumn('api_token')) {
-            $this->table('user')
-                ->addColumn('api_token', 'uuid', [ 'comment' => 'API 密钥', 'null' => false, 'default' => '' ])
-                ->save();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('user')
-            ->removeColumn('api_token')
-            ->save();
-    }
-}

+ 0 - 35
db/migrations/20221211223600_merge_node_online_log.php

@@ -1,35 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class MergeNodeOnlineLog extends AbstractMigration
-{
-    public function up(): void
-    {
-        if ($this->hasTable('node_online_log')) {
-            $this->table('node_online_log')->drop()->update();
-        }
-        if (! $this->table('node')->hasColumn('online_user')) {
-            $this->table('node')
-                ->addColumn('online_user', 'integer', [ 'comment' => '节点在线用户', 'default' => 0 ])
-                ->save();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('node_online_log', [ 'id' => false, 'primary_key' => [ 'id' ]])
-            ->addColumn('id', 'integer', [ 'identity' => true ])
-            ->addColumn('node_id', 'integer', [])
-            ->addColumn('online_user', 'integer', [])
-            ->addColumn('log_time', 'integer', [])
-            ->addIndex([ 'node_id' ])
-            ->addForeignKey('node_id', 'node', 'id', [ 'delete' => 'CASCADE', 'update' => 'CASCADE' ])
-            ->create();
-        $this->table('node')
-            ->removeColumn('online_user')
-            ->save();
-    }
-}

+ 0 - 30
db/migrations/20221211230900_gift_card_table.php

@@ -1,30 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class GiftCardTable extends AbstractMigration
-{
-    public function up(): void
-    {
-        if (! $this->hasTable('gift_card')) {
-            $this->table('gift_card', [ 'id' => false, 'primary_key' => [ 'id' ]])
-                ->addColumn('id', 'biginteger', [ 'identity' => true,'signed' => false ])
-                ->addColumn('card', 'text', ['comment' => '卡号'])
-                ->addColumn('balance', 'integer', ['comment' => '余额'])
-                ->addColumn('create_time', 'integer', ['comment' => '创建时间'])
-                ->addColumn('status', 'integer', ['comment' => '使用状态'])
-                ->addColumn('use_time', 'integer', ['comment' => '使用时间'])
-                ->addColumn('use_user', 'integer', ['comment' => '使用用户'])
-                ->addIndex([ 'id' ])
-                ->addIndex([ 'status' ])
-                ->create();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('gift_card')->drop()->update();
-    }
-}

+ 0 - 19
db/migrations/20221212141700_fix_node_default_value.php

@@ -1,19 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class FixNodeDefaultValue extends AbstractMigration
-{
-    public function up(): void
-    {
-        $this->table('node')
-            ->changeColumn('node_speedlimit', 'double', [ 'comment' => '节点限速', 'default' => 0, 'null' => false ])
-            ->save();
-    }
-
-    public function down(): void
-    {
-    }
-}

+ 0 - 24
db/migrations/20221217033500_add_user_use_new_shop.php

@@ -1,24 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class AddUserUseNewShop extends AbstractMigration
-{
-    public function up(): void
-    {
-        if (! $this->table('user')->hasColumn('use_new_shop')) {
-            $this->table('user')
-                ->addColumn('use_new_shop', 'smallinteger', [ 'comment' => '是否启用新商店', 'null' => false, 'default' => 0 ])
-                ->save();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('user')
-            ->removeColumn('use_new_shop')
-            ->save();
-    }
-}

+ 0 - 74
db/migrations/20221230010900_add_product_order_invoice.php

@@ -1,74 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class AddProductOrderInvoice extends AbstractMigration
-{
-    public function up(): void
-    {
-        if (! $this->hasTable('product')) {
-            $this->table('product', [ 'id' => false, 'primary_key' => [ 'id' ]])
-                ->addColumn('id', 'integer', [ 'comment' => '商品ID', 'identity' => true ])
-                ->addColumn('type', 'string', [ 'comment' => '类型' ])
-                ->addColumn('name', 'string', [ 'comment' => '名称' ])
-                ->addColumn('price', 'double', [ 'comment' => '售价' ])
-                ->addColumn('content', 'json', [ 'comment' => '内容' ])
-                ->addColumn('limit', 'json', [ 'comment' => '购买限制'])
-                ->addColumn('status', 'integer', [ 'comment' => '销售状态' ])
-                ->addColumn('create_time', 'integer', [ 'comment' => '创建时间' ])
-                ->addColumn('update_time', 'integer', [ 'comment' => '更新时间' ])
-                ->addColumn('sale_count', 'integer', [ 'comment' => '累计销售数'])
-                ->addColumn('stock', 'integer', [ 'comment' => '库存'])
-                ->addIndex([ 'id' ])
-                ->addIndex([ 'type' ])
-                ->create();
-        }
-
-        if (! $this->hasTable('order')) {
-            $this->table('order', [ 'id' => false, 'primary_key' => [ 'id' ]])
-                ->addColumn('id', 'integer', [ 'comment' => '订单ID', 'identity' => true ])
-                ->addColumn('user_id', 'integer', [ 'comment' => '提交用户' ])
-                ->addColumn('product_id', 'integer', [ 'comment' => '商品ID' ])
-                ->addColumn('product_type', 'string', [ 'comment' => '商品类型' ])
-                ->addColumn('product_name', 'string', [ 'comment' => '商品名称' ])
-                ->addColumn('product_content', 'json', [ 'comment' => '商品内容' ])
-                ->addColumn('coupon', 'string', [ 'comment' => '订单优惠码'])
-                ->addColumn('price', 'double', [ 'comment' => '订单金额' ])
-                ->addColumn('status', 'string', [ 'comment' => '订单状态' ])
-                ->addColumn('create_time', 'integer', [ 'comment' => '创建时间' ])
-                ->addColumn('update_time', 'integer', [ 'comment' => '更新时间' ])
-                ->addIndex([ 'id' ])
-                ->addIndex([ 'user_id' ])
-                ->addIndex([ 'product_id' ])
-                ->addIndex([ 'status' ])
-                ->create();
-        }
-
-        if (! $this->hasTable('invoice')) {
-            $this->table('invoice', [ 'id' => false, 'primary_key' => [ 'id' ]])
-                ->addColumn('id', 'integer', [ 'comment' => '账单ID', 'identity' => true ])
-                ->addColumn('user_id', 'integer', [ 'comment' => '归属用户' ])
-                ->addColumn('order_id', 'integer', [ 'comment' => '订单ID' ])
-                ->addColumn('content', 'json', [ 'comment' => '账单内容' ])
-                ->addColumn('price', 'double', [ 'comment' => '账单金额' ])
-                ->addColumn('status', 'string', [ 'comment' => '账单状态' ])
-                ->addColumn('create_time', 'integer', [ 'comment' => '创建时间' ])
-                ->addColumn('update_time', 'integer', [ 'comment' => '更新时间' ])
-                ->addColumn('pay_time', 'integer', [ 'comment' => '支付时间' ])
-                ->addIndex([ 'id' ])
-                ->addIndex([ 'user_id' ])
-                ->addIndex([ 'order_id' ])
-                ->addIndex([ 'status' ])
-                ->create();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('product')->drop()->update();
-        $this->table('order')->drop()->update();
-        $this->table('invoice')->drop()->update();
-    }
-}

+ 0 - 30
db/migrations/20230115090200_add_user_coupon.php

@@ -1,30 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Phinx\Migration\AbstractMigration;
-
-final class AddUserCoupon extends AbstractMigration
-{
-    public function up(): void
-    {
-        if (! $this->hasTable('user_coupon')) {
-            $this->table('user_coupon', [ 'id' => false, 'primary_key' => [ 'id' ]])
-                ->addColumn('id', 'integer', [ 'comment' => '优惠码ID', 'identity' => true ])
-                ->addColumn('code', 'string', [ 'comment' => '优惠码' ])
-                ->addColumn('content', 'json', [ 'comment' => '优惠码内容' ])
-                ->addColumn('limit', 'json', [ 'comment' => '优惠码限制' ])
-                ->addColumn('create_time', 'integer', [ 'comment' => '创建时间' ])
-                ->addColumn('expire_time', 'integer', [ 'comment' => '过期时间' ])
-                ->addIndex([ 'id' ])
-                ->addIndex([ 'code' ])
-                ->addIndex([ 'expire_time' ])
-                ->create();
-        }
-    }
-
-    public function down(): void
-    {
-        $this->table('user_coupon')->drop()->update();
-    }
-}

+ 445 - 0
db/migrations/20230201-init.php

@@ -0,0 +1,445 @@
+<?php
+
+declare(strict_types=1);
+
+use App\Interfaces\MigrationInterface;
+use App\Services\DB;
+
+return new class() implements MigrationInterface {
+    private const INIT = <<< END
+      CREATE TABLE `alive_ip` (
+        `id` bigint(20) NOT NULL AUTO_INCREMENT,
+        `nodeid` int(11) DEFAULT NULL,
+        `userid` int(11) DEFAULT NULL,
+        `ip` varchar(255) DEFAULT NULL,
+        `datetime` bigint(20) DEFAULT NULL,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `announcement` (
+        `id` int(11) NOT NULL AUTO_INCREMENT,
+        `date` datetime DEFAULT NULL,
+        `content` text DEFAULT NULL,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `bought` (
+        `id` bigint(20) NOT NULL AUTO_INCREMENT,
+        `userid` bigint(20) DEFAULT NULL,
+        `shopid` bigint(20) DEFAULT NULL,
+        `datetime` bigint(20) DEFAULT NULL,
+        `renew` bigint(20) DEFAULT NULL,
+        `coupon` varchar(255) DEFAULT NULL,
+        `price` decimal(12,2) DEFAULT NULL,
+        `is_notified` tinyint(1) DEFAULT 0,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `code` (
+        `id` bigint(20) NOT NULL AUTO_INCREMENT,
+        `code` varchar(255) DEFAULT NULL,
+        `type` int(11) DEFAULT NULL,
+        `number` decimal(12,2) DEFAULT NULL,
+        `isused` int(11) DEFAULT 0,
+        `userid` bigint(20) DEFAULT NULL,
+        `usedatetime` datetime DEFAULT NULL,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `config` (
+        `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+        `item` varchar(255) DEFAULT NULL COMMENT '项',
+        `value` varchar(2048) DEFAULT NULL,
+        `class` varchar(255) DEFAULT 'default' COMMENT '配置分类',
+        `is_public` int(11) DEFAULT 0 COMMENT '是否为公共参数',
+        `type` varchar(255) DEFAULT NULL COMMENT '值类型',
+        `default` varchar(255) DEFAULT NULL COMMENT '默认值',
+        `mark` varchar(255) DEFAULT NULL COMMENT '备注',
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `coupon` (
+        `id` bigint(20) NOT NULL AUTO_INCREMENT,
+        `code` varchar(255) DEFAULT NULL,
+        `onetime` int(11) DEFAULT NULL,
+        `expire` bigint(20) DEFAULT NULL,
+        `shop` varchar(255) DEFAULT NULL,
+        `credit` int(11) DEFAULT NULL,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `detect_ban_log` (
+        `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+        `user_name` varchar(255) DEFAULT NULL COMMENT '用户名',
+        `user_id` bigint(20) unsigned DEFAULT NULL COMMENT '用户 ID',
+        `email` varchar(255) DEFAULT NULL COMMENT '用户邮箱',
+        `detect_number` int(11) DEFAULT NULL COMMENT '本次违规次数',
+        `ban_time` int(11) DEFAULT NULL COMMENT '本次封禁时长',
+        `start_time` bigint(20) DEFAULT NULL COMMENT '统计开始时间',
+        `end_time` bigint(20) DEFAULT NULL COMMENT '统计结束时间',
+        `all_detect_number` int(11) DEFAULT NULL COMMENT '累计违规次数',
+        PRIMARY KEY (`id`),
+        KEY `user_id` (`user_id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `detect_list` (
+        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+        `name` varchar(255) DEFAULT NULL,
+        `text` varchar(255) DEFAULT NULL,
+        `regex` varchar(255) DEFAULT NULL,
+        `type` int(11) DEFAULT NULL,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `detect_log` (
+        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+        `user_id` bigint(20) unsigned DEFAULT NULL,
+        `list_id` bigint(20) unsigned DEFAULT NULL,
+        `datetime` bigint(20) unsigned DEFAULT NULL,
+        `node_id` int(11) DEFAULT NULL,
+        `status` int(11) DEFAULT 0,
+        PRIMARY KEY (`id`),
+        KEY `user_id` (`user_id`),
+        KEY `node_id` (`node_id`),
+        KEY `list_id` (`list_id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `docs` (
+        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+        `date` datetime DEFAULT NULL,
+        `title` varchar(255) DEFAULT NULL,
+        `content` varchar(255) DEFAULT NULL,
+        `markdown` varchar(255) DEFAULT NULL,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `email_queue` (
+        `id` bigint(20) NOT NULL AUTO_INCREMENT,
+        `to_email` varchar(255) DEFAULT NULL,
+        `subject` varchar(255) DEFAULT NULL,
+        `template` varchar(255) DEFAULT NULL,
+        `array` longtext DEFAULT NULL,
+        `time` int(11) DEFAULT NULL,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `email_verify` (
+        `id` bigint(20) NOT NULL AUTO_INCREMENT,
+        `email` varchar(255) DEFAULT NULL,
+        `ip` varchar(255) DEFAULT NULL,
+        `code` varchar(255) DEFAULT NULL,
+        `expire_in` bigint(20) DEFAULT NULL,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `gift_card` (
+        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+        `card` text DEFAULT NULL COMMENT '卡号',
+        `balance` int(11) DEFAULT NULL COMMENT '余额',
+        `create_time` int(11) DEFAULT NULL COMMENT '创建时间',
+        `status` int(11) DEFAULT NULL COMMENT '使用状态',
+        `use_time` int(11) DEFAULT NULL COMMENT '使用时间',
+        `use_user` int(11) DEFAULT NULL COMMENT '使用用户',
+        PRIMARY KEY (`id`),
+        KEY `id` (`id`),
+        KEY `status` (`status`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `invoice` (
+        `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '账单ID',
+        `user_id` int(11) DEFAULT NULL COMMENT '归属用户',
+        `order_id` int(11) DEFAULT NULL COMMENT '订单ID',
+        `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账单内容' CHECK (json_valid(`content`)),
+        `price` double DEFAULT NULL COMMENT '账单金额',
+        `status` varchar(255) DEFAULT NULL COMMENT '账单状态',
+        `create_time` int(11) DEFAULT NULL COMMENT '创建时间',
+        `update_time` int(11) DEFAULT NULL COMMENT '更新时间',
+        `pay_time` int(11) DEFAULT NULL COMMENT '支付时间',
+        PRIMARY KEY (`id`),
+        KEY `id` (`id`),
+        KEY `user_id` (`user_id`),
+        KEY `order_id` (`order_id`),
+        KEY `status` (`status`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `link` (
+        `id` bigint(20) NOT NULL AUTO_INCREMENT,
+        `token` varchar(255) DEFAULT NULL,
+        `userid` bigint(20) unsigned DEFAULT NULL,
+        PRIMARY KEY (`id`),
+        UNIQUE KEY `token` (`token`),
+        UNIQUE KEY `userid` (`userid`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `login_ip` (
+        `id` bigint(20) NOT NULL AUTO_INCREMENT,
+        `userid` bigint(20) unsigned DEFAULT NULL,
+        `ip` varchar(255) DEFAULT NULL,
+        `datetime` bigint(20) DEFAULT NULL,
+        `type` int(11) DEFAULT NULL,
+        PRIMARY KEY (`id`),
+        KEY `userid` (`userid`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `node` (
+        `id` int(11) NOT NULL AUTO_INCREMENT,
+        `name` varchar(255) DEFAULT NULL,
+        `type` int(11) DEFAULT NULL,
+        `server` varchar(255) DEFAULT NULL,
+        `custom_config` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT '{}' COMMENT '自定义配置' CHECK (json_valid(`custom_config`)),
+        `info` text DEFAULT '',
+        `status` varchar(255) DEFAULT '',
+        `sort` int(11) DEFAULT NULL,
+        `traffic_rate` float DEFAULT 1,
+        `node_class` int(11) DEFAULT 0,
+        `node_speedlimit` double NOT NULL DEFAULT 0 COMMENT '节点限速',
+        `node_connector` int(11) DEFAULT 0,
+        `node_bandwidth` bigint(20) DEFAULT 0,
+        `node_bandwidth_limit` bigint(20) DEFAULT 0,
+        `bandwidthlimit_resetday` int(11) DEFAULT 0,
+        `node_heartbeat` bigint(20) DEFAULT 0,
+        `online_user` int(11) DEFAULT 0 COMMENT '节点在线用户',
+        `node_ip` varchar(255) DEFAULT NULL,
+        `node_group` int(11) DEFAULT 0,
+        `mu_only` tinyint(1) DEFAULT 0,
+        `online` tinyint(1) DEFAULT 1,
+        `gfw_block` tinyint(1) DEFAULT 0,
+        `password` varchar(255) DEFAULT NULL,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `order` (
+        `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单ID',
+        `user_id` int(11) DEFAULT NULL COMMENT '提交用户',
+        `product_id` int(11) DEFAULT NULL COMMENT '商品ID',
+        `product_type` varchar(255) DEFAULT NULL COMMENT '商品类型',
+        `product_name` varchar(255) DEFAULT NULL COMMENT '商品名称',
+        `product_content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '商品内容' CHECK (json_valid(`product_content`)),
+        `coupon` varchar(255) DEFAULT NULL COMMENT '订单优惠码',
+        `price` double DEFAULT NULL COMMENT '订单金额',
+        `status` varchar(255) DEFAULT NULL COMMENT '订单状态',
+        `create_time` int(11) DEFAULT NULL COMMENT '创建时间',
+        `update_time` int(11) DEFAULT NULL COMMENT '更新时间',
+        PRIMARY KEY (`id`),
+        KEY `id` (`id`),
+        KEY `user_id` (`user_id`),
+        KEY `product_id` (`product_id`),
+        KEY `status` (`status`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `payback` (
+        `id` bigint(20) NOT NULL AUTO_INCREMENT,
+        `total` decimal(12,2) DEFAULT NULL,
+        `userid` bigint(20) DEFAULT NULL,
+        `ref_by` bigint(20) DEFAULT NULL,
+        `ref_get` decimal(12,2) DEFAULT NULL,
+        `datetime` bigint(20) DEFAULT NULL,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `paylist` (
+        `id` bigint(20) NOT NULL AUTO_INCREMENT,
+        `userid` bigint(20) unsigned DEFAULT NULL,
+        `total` decimal(12,2) DEFAULT NULL,
+        `status` int(11) DEFAULT 0,
+        `tradeno` varchar(255) DEFAULT NULL,
+        `datetime` bigint(20) DEFAULT 0,
+        PRIMARY KEY (`id`),
+        KEY `userid` (`userid`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `product` (
+        `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品ID',
+        `type` varchar(255) DEFAULT NULL COMMENT '类型',
+        `name` varchar(255) DEFAULT NULL COMMENT '名称',
+        `price` double DEFAULT NULL COMMENT '售价',
+        `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '内容' CHECK (json_valid(`content`)),
+        `limit` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '购买限制' CHECK (json_valid(`limit`)),
+        `status` int(11) DEFAULT NULL COMMENT '销售状态',
+        `create_time` int(11) DEFAULT NULL COMMENT '创建时间',
+        `update_time` int(11) DEFAULT NULL COMMENT '更新时间',
+        `sale_count` int(11) DEFAULT NULL COMMENT '累计销售数',
+        `stock` int(11) DEFAULT NULL COMMENT '库存',
+        PRIMARY KEY (`id`),
+        KEY `id` (`id`),
+        KEY `type` (`type`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `shop` (
+        `id` bigint(20) NOT NULL AUTO_INCREMENT,
+        `name` varchar(255) DEFAULT NULL,
+        `price` decimal(12,2) DEFAULT NULL,
+        `content` text DEFAULT NULL,
+        `auto_renew` int(11) DEFAULT NULL,
+        `auto_reset_bandwidth` int(11) DEFAULT 0,
+        `status` int(11) DEFAULT 1,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `stream_media` (
+        `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+        `node_id` int(11) DEFAULT NULL COMMENT '节点id',
+        `result` text DEFAULT NULL COMMENT '检测结果',
+        `created_at` int(11) DEFAULT NULL COMMENT '创建时间',
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `telegram_session` (
+        `id` bigint(20) NOT NULL AUTO_INCREMENT,
+        `user_id` bigint(20) DEFAULT NULL,
+        `type` int(11) DEFAULT NULL,
+        `session_content` varchar(255) DEFAULT NULL,
+        `datetime` bigint(20) DEFAULT NULL,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `ticket` (
+        `id` bigint(20) NOT NULL AUTO_INCREMENT,
+        `title` varchar(255) DEFAULT NULL,
+        `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT '' COMMENT '工单内容' CHECK (json_valid(`content`)),
+        `userid` bigint(20) DEFAULT NULL,
+        `datetime` bigint(20) DEFAULT NULL,
+        `status` varchar(255) DEFAULT '' COMMENT '工单状态',
+        `type` varchar(255) DEFAULT 'other' COMMENT '工单类型',
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `user` (
+        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
+        `user_name` varchar(255) DEFAULT NULL COMMENT '用户名',
+        `email` varchar(255) DEFAULT NULL COMMENT 'E-Mail',
+        `pass` varchar(255) DEFAULT NULL COMMENT '登录密码',
+        `passwd` varchar(255) DEFAULT NULL COMMENT '节点密码',
+        `uuid` char(36) NOT NULL COMMENT 'UUID',
+        `t` bigint(20) unsigned DEFAULT 0 COMMENT '最后使用时间',
+        `u` bigint(20) unsigned DEFAULT 0 COMMENT '账户当前上传流量',
+        `d` bigint(20) unsigned DEFAULT 0 COMMENT '账户当前下载流量',
+        `transfer_total` bigint(20) unsigned DEFAULT 0 COMMENT '账户累计使用流量',
+        `transfer_enable` bigint(20) unsigned DEFAULT 0 COMMENT '账户当前可用流量',
+        `port` smallint(6) unsigned NOT NULL COMMENT '端口',
+        `last_detect_ban_time` datetime DEFAULT '1989-06-04 00:05:00' COMMENT '最后一次被封禁的时间',
+        `all_detect_number` int(11) DEFAULT 0 COMMENT '累计违规次数',
+        `last_check_in_time` bigint(20) unsigned DEFAULT 0 COMMENT '最后签到时间',
+        `reg_date` datetime DEFAULT NULL COMMENT '注册时间',
+        `invite_num` int(11) DEFAULT 0 COMMENT '可用邀请次数',
+        `money` decimal(10,2) NOT NULL DEFAULT 0.00,
+        `ref_by` bigint(20) unsigned DEFAULT 0 COMMENT '邀请人ID',
+        `method` varchar(255) DEFAULT 'rc4-md5' COMMENT 'SS/SSR加密方式',
+        `reg_ip` varchar(255) DEFAULT '127.0.0.1' COMMENT '注册IP',
+        `node_speedlimit` double NOT NULL DEFAULT 0 COMMENT '用户限速',
+        `node_iplimit` smallint(6) unsigned NOT NULL DEFAULT 0 COMMENT '同时可连接IP数',
+        `node_connector` int(11) DEFAULT 0 COMMENT '同时可使用连接数',
+        `is_admin` tinyint(1) DEFAULT 0 COMMENT '是否管理员',
+        `im_type` int(11) DEFAULT 1 COMMENT '联系方式类型',
+        `im_value` varchar(255) DEFAULT '' COMMENT '联系方式',
+        `last_day_t` bigint(20) DEFAULT 0 COMMENT '今天之前已使用的流量',
+        `sendDailyMail` tinyint(1) DEFAULT 0 COMMENT '每日报告开关',
+        `class` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '等级',
+        `class_expire` datetime DEFAULT '1989-06-04 00:05:00' COMMENT '等级过期时间',
+        `expire_in` datetime DEFAULT '2099-06-04 00:05:00',
+        `theme` varchar(255) DEFAULT NULL COMMENT '网站主题',
+        `ga_token` varchar(255) DEFAULT NULL,
+        `ga_enable` int(11) DEFAULT 0,
+        `remark` text DEFAULT '' COMMENT '备注',
+        `node_group` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '节点分组',
+        `protocol` varchar(255) DEFAULT 'origin' COMMENT 'SS/SSR协议方式',
+        `protocol_param` varchar(255) DEFAULT '',
+        `obfs` varchar(255) DEFAULT 'plain' COMMENT 'SS/SSR混淆方式',
+        `obfs_param` varchar(255) DEFAULT '',
+        `is_banned` int(11) DEFAULT 0 COMMENT '是否封禁',
+        `banned_reason` varchar(255) DEFAULT '' COMMENT '封禁理由',
+        `is_multi_user` int(11) DEFAULT 0,
+        `telegram_id` bigint(20) DEFAULT 0,
+        `expire_notified` tinyint(1) DEFAULT 0,
+        `traffic_notified` tinyint(1) DEFAULT 0,
+        `forbidden_ip` varchar(255) DEFAULT '',
+        `forbidden_port` varchar(255) DEFAULT '',
+        `auto_reset_day` int(11) DEFAULT 0,
+        `auto_reset_bandwidth` decimal(12,2) DEFAULT 0.00,
+        `api_token` char(36) NOT NULL DEFAULT '' COMMENT 'API 密钥',
+        `use_new_shop` smallint(6) NOT NULL DEFAULT 0 COMMENT '是否启用新商店',
+        `is_dark_mode` int(11) DEFAULT 0,
+        PRIMARY KEY (`id`),
+        UNIQUE KEY `uuid` (`uuid`),
+        UNIQUE KEY `email` (`email`),
+        UNIQUE KEY `ga_token` (`ga_token`),
+        KEY `user_name` (`user_name`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `user_coupon` (
+        `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '优惠码ID',
+        `code` varchar(255) DEFAULT NULL COMMENT '优惠码',
+        `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '优惠码内容' CHECK (json_valid(`content`)),
+        `limit` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '优惠码限制' CHECK (json_valid(`limit`)),
+        `create_time` int(11) DEFAULT NULL COMMENT '创建时间',
+        `expire_time` int(11) DEFAULT NULL COMMENT '过期时间',
+        PRIMARY KEY (`id`),
+        KEY `id` (`id`),
+        KEY `code` (`code`),
+        KEY `expire_time` (`expire_time`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `user_hourly_usage` (
+        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+        `user_id` bigint(20) unsigned DEFAULT NULL,
+        `traffic` bigint(20) DEFAULT NULL,
+        `hourly_usage` bigint(20) DEFAULT NULL,
+        `datetime` int(11) DEFAULT NULL,
+        PRIMARY KEY (`id`),
+        KEY `user_id` (`user_id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `user_invite_code` (
+        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+        `code` varchar(255) DEFAULT NULL,
+        `user_id` bigint(20) unsigned DEFAULT NULL,
+        `created_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
+        `updated_at` timestamp NULL DEFAULT '2016-05-31 15:00:00',
+        PRIMARY KEY (`id`),
+        UNIQUE KEY `code` (`code`),
+        UNIQUE KEY `user_id` (`user_id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `user_password_reset` (
+        `id` int(11) NOT NULL AUTO_INCREMENT,
+        `email` varchar(255) DEFAULT NULL,
+        `token` varchar(255) DEFAULT NULL,
+        `init_time` int(11) DEFAULT NULL,
+        `expire_time` int(11) DEFAULT NULL,
+        PRIMARY KEY (`id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `user_subscribe_log` (
+        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+        `user_name` varchar(255) DEFAULT NULL COMMENT '用户名',
+        `user_id` bigint(20) unsigned DEFAULT NULL COMMENT '用户 ID',
+        `email` varchar(255) DEFAULT NULL COMMENT '用户邮箱',
+        `subscribe_type` varchar(255) DEFAULT NULL COMMENT '获取的订阅类型',
+        `request_ip` varchar(255) DEFAULT NULL COMMENT '请求 IP',
+        `request_time` datetime DEFAULT NULL COMMENT '请求时间',
+        `request_user_agent` text DEFAULT NULL COMMENT '请求 UA 信息',
+        PRIMARY KEY (`id`),
+        KEY `user_id` (`user_id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+      
+      CREATE TABLE `user_token` (
+        `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+        `token` varchar(255) DEFAULT NULL,
+        `user_id` bigint(20) unsigned DEFAULT NULL,
+        `create_time` bigint(20) unsigned DEFAULT NULL,
+        `expire_time` bigint(20) DEFAULT NULL,
+        PRIMARY KEY (`id`),
+        KEY `user_id` (`user_id`)
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;      
+END;
+
+    public function up(): void
+    {
+        DB::getPdo()->exec(self::INIT);
+    }
+
+    public function down(): void
+    {
+        echo "No reverse operation for initial migration\n";
+    }
+};

+ 0 - 0
db/seeds/readme.md


+ 12 - 0
db/update.sql

@@ -0,0 +1,12 @@
+-- Update from phinx migration
+ALTER TABLE detect_ban_log DROP FOREIGN KEY detect_ban_log_ibfk_1;
+ALTER TABLE detect_log DROP FOREIGN KEY detect_log_ibfk_1;
+ALTER TABLE detect_log DROP FOREIGN KEY detect_log_ibfk_2;
+ALTER TABLE detect_log DROP FOREIGN KEY detect_log_ibfk_3;
+ALTER TABLE link DROP FOREIGN KEY link_ibfk_1;
+ALTER TABLE login_ip DROP FOREIGN KEY login_ip_ibfk_1;
+ALTER TABLE paylist DROP FOREIGN KEY paylist_ibfk_1;
+ALTER TABLE user_hourly_usage DROP FOREIGN KEY user_hourly_usage_ibfk_1;
+ALTER TABLE user_invite_code DROP FOREIGN KEY user_invite_code_ibfk_1;
+ALTER TABLE user_subscribe_log DROP FOREIGN KEY user_subscribe_log_ibfk_1;
+ALTER TABLE user_token DROP FOREIGN KEY user_token_ibfk_1;

+ 1 - 1
install.sh

@@ -38,7 +38,7 @@ do_install_sspanel() {
     GRANT ALL PRIVILEGES ON $db_database.* TO '$db_username'@'localhost' IDENTIFIED BY '$db_password';
     FLUSH PRIVILEGES;"
     echo "Importing config to database..."
-    php vendor/bin/phinx migrate
+    php xcat Migration new
     php xcat Tool importAllSettings
     wget https://cdn.jsdelivr.net/gh/sspanel-uim/qqwry.dat@latest/qqwry.dat -O storage/qqwry.dat
     current_dir=$(pwd)

+ 0 - 44
phinx.php

@@ -1,44 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-include './config/.config.php';
-
-return [
-    'paths' => [
-        'migrations' => '%%PHINX_CONFIG_DIR%%/db/migrations',
-        'seeds' => '%%PHINX_CONFIG_DIR%%/db/seeds',
-    ],
-    'environments' => [
-        'default_migration_table' => 'phinxlog',
-        'default_environment' => 'production',
-        'production' => [
-            'adapter' => 'mysql',
-            'host' => $_ENV['db_host'],
-            'name' => $_ENV['db_database'],
-            'user' => $_ENV['db_username'],
-            'pass' => $_ENV['db_password'],
-            'port' => '3306',
-            'charset' => $_ENV['db_charset'],
-        ],
-        'development' => [
-            'adapter' => 'mysql',
-            'host' => 'localhost',
-            'name' => 'development_db',
-            'user' => 'root',
-            'pass' => '',
-            'port' => '3306',
-            'charset' => 'utf8',
-        ],
-        'testing' => [
-            'adapter' => 'mysql',
-            'host' => 'localhost',
-            'name' => 'testing_db',
-            'user' => 'root',
-            'pass' => '',
-            'port' => '3306',
-            'charset' => 'utf8',
-        ],
-    ],
-    'version_order' => 'creation',
-];

+ 2 - 5
phpinsights.php

@@ -7,6 +7,7 @@ return [
     'remove' => [
         NunoMaduro\PhpInsights\Domain\Sniffs\ForbiddenSetterSniff::class,
         NunoMaduro\PhpInsights\Domain\Insights\CyclomaticComplexityIsHigh::class,
+        NunoMaduro\PhpInsights\Domain\Insights\ForbiddenDefineFunctions::class,
         NunoMaduro\PhpInsights\Domain\Insights\ForbiddenDefineGlobalConstants::class,
         NunoMaduro\PhpInsights\Domain\Insights\ForbiddenGlobals::class,
         PHP_CodeSniffer\Standards\Generic\Sniffs\Commenting\TodoSniff::class,
@@ -21,6 +22,7 @@ return [
         SlevomatCodingStandard\Sniffs\Classes\ModernClassNameReferenceSniff::class,
         SlevomatCodingStandard\Sniffs\Classes\SuperfluousExceptionNamingSniff::class,
         SlevomatCodingStandard\Sniffs\Classes\SuperfluousAbstractClassNamingSniff::class,
+        SlevomatCodingStandard\Sniffs\Classes\SuperfluousInterfaceNamingSniff::class,
         SlevomatCodingStandard\Sniffs\Functions\FunctionLengthSniff::class,
         SlevomatCodingStandard\Sniffs\Functions\UnusedParameterSniff::class,
         SlevomatCodingStandard\Sniffs\Namespaces\UseSpacingSniff::class,
@@ -36,11 +38,6 @@ return [
                 'src/Command/Job.php',
             ],
         ],
-        // Db migration should not have a class declaration
-        // If they have, phinx will unable to found them
-        PHP_CodeSniffer\Standards\PSR1\Sniffs\Classes\ClassDeclarationSniff::class => [
-            'exclude' => ['db/migrations'],
-        ],
     ],
 
     'exclude' => [

+ 0 - 1
public/index.php

@@ -33,7 +33,6 @@ $app = AppFactory::create($response_factory);
 
 $app->add(new ErrorHandler());
 
-/** @var callable */
 $routes = require __DIR__ . '/../app/routes.php';
 $routes($app);
 

+ 4 - 1
resources/views/tabler/staff.tpl

@@ -31,17 +31,20 @@
         OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         SOFTWARE.</p>
         <br>
+        <br>
         <h1><a href="https://wiki.sspanel.org/#/contributors">贡献者清单</a></h1>
         <br>
+        <br>
         <h1><a href="https://github.com/Anankke/SSPanel-Uim">GitHub</h1>
         <br>
+        <br>
         <h1>SSPanel-UIM 中所使用的开源项目</h1>
         <p><a href="https://github.com/smarty-php/smarty">Smarty template engine</a></p>
         <p><a href="https://github.com/slimphp/Slim">Slim Framework</a></p>
-        <p><a href="https://github.com/cakephp/phinx">Phinx</a></p>
         <p><a href="https://github.com/orvice/ss-panel">ss-panel</a></p>
         <p><a href="https://github.com/tabler/tabler">tabler</a></p>
         <br>
+        <br>
         <h1>鸣谢</h1>
         <p>所有被引用过代码的开发者,以及所有提交过 PR 的贡献者。当然,还有在使用这份程序的你我Ta。</p>
     </div>

+ 3 - 3
src/Command/FinanceMail.php

@@ -13,9 +13,9 @@ final class FinanceMail extends Command
 {
     public $description = <<<EOL
 ├─=: php xcat FinanceMail [选项]
-│├─ day                     - 日报
-│├─ week                    - 周报
-│├─ month                   - 月报
+│ ├─ day                     - 日报
+│ ├─ week                    - 周报
+│ ├─ month                   - 月报
 EOL;
 
     public function boot(): void

+ 3 - 3
src/Command/Job.php

@@ -33,9 +33,9 @@ final class Job extends Command
 {
     public $description = <<<EOL
 ├─=: php xcat Job [选项]
-│├─ DailyJob                - 每日任务,每天
-│├─ CheckJob                - 检查任务,每分钟
-│├─ UserJob                 - 用户账户相关任务,每小时
+│ ├─ DailyJob                - 每日任务,每天
+│ ├─ CheckJob                - 检查任务,每分钟
+│ ├─ UserJob                 - 用户账户相关任务,每小时
 EOL;
 
     public function boot(): void

+ 107 - 0
src/Command/Migration.php

@@ -0,0 +1,107 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Command;
+
+use App\Interfaces\MigrationInterface;
+use App\Models\Setting;
+use App\Services\DB;
+use function count;
+use function explode;
+use function is_numeric;
+use function krsort;
+use function ksort;
+use function scandir;
+use function substr;
+use const PHP_INT_MAX;
+use const SCANDIR_SORT_NONE;
+
+final class Migration extends Command
+{
+    public $description = <<< END
+├─=: php xcat Migration [版本]
+│ ├─ <version>               - 迁移至指定版本(前进/退回)
+│ ├─ latest                  - 迁移至最新版本
+│ ├─ new                     - 导入全新数据库至最新版本
+END;
+
+    public function boot(): void
+    {
+        $reverse = false;
+        // (min_version, max_version]
+        $min_version = 0;
+        $max_version = 0;
+        $target = $this->argv[2] ?? 0;
+        if ($target === 'latest') {
+            $min_version = Setting::obtain('db_version');
+            $max_version = PHP_INT_MAX;
+        } elseif ($target === 'new') {
+            $tables = []; //DB::select('SHOW TABLES');
+            if ($tables === []) {
+                $min_version = 0;
+                $max_version = PHP_INT_MAX;
+            } else {
+                echo "Table exists in database!!! Do not use 'new' version.\n";
+                return;
+            }
+        } elseif (is_numeric($target)) {
+            $target = (int) $target;
+            $current = Setting::obtain('db_version');
+            if ($target < $current) {
+                $reverse = true;
+                $min_version = $target;
+                $max_version = $current;
+            } else {
+                $min_version = $current;
+                $max_version = $target;
+            }
+        } else {
+            echo "Illegle version argument\n";
+            return;
+        }
+
+        $queue = [];
+        $files = scandir(BASE_PATH . '/db/migrations/', SCANDIR_SORT_NONE);
+        if ($files) {
+            foreach ($files as $file) {
+                if ($file === '.' || $file === '..' || substr($file, -4) !== '.php') {
+                    continue;
+                }
+                $version = (int) (explode('-', $file, 1)[0] ?? 0);
+                if (!($version > $min_version && $version <= $max_version)) {
+                    continue;
+                }
+                $object = require BASE_PATH . '/db/migrations/' . $file;
+                if ($object instanceof MigrationInterface) {
+                    $queue[$version] = $object;
+                }
+            }
+        }
+
+        if ($reverse) {
+            krsort($queue);
+            foreach ($queue as $version => $object) {
+                echo "Reverse to {$version}\n";
+                $object->down();
+            }
+            $current = $min_version;
+        } else {
+            ksort($queue);
+            foreach ($queue as $version => $object) {
+                echo "Forward to {$version}\n";
+                $object->up();
+            }
+            $current = $max_version;
+        }
+        if ($target === 'new') {
+            $sql = 'INSERT INTO `config` (`item`, `value`, `type`, `default`) VALUES("db_version", ?, "int", "20230201000")';
+        } else {
+            $sql = 'UPDATE `config` SET `value` = ? WHERE `item` = "db_version"';
+        }
+        DB::insert($sql, [$current]);
+
+        $count = count($queue);
+        echo "Imigration complete. {$count} items processed.\n";
+    }
+}

+ 22 - 29
src/Command/Tool.php

@@ -17,21 +17,20 @@ final class Tool extends Command
 {
     public $description = <<<EOL
 ├─=: php xcat Tool [选项]
-│ ├─ setTelegram             - 设置 Telegram 机器人
-│ ├─ resetAllSettings        - 使用默认值覆盖设置中心设置
-│ ├─ exportAllSettings       - 导出所有设置
-│ ├─ importAllSettings       - 导入所有设置
-│ ├─ upgradeDatabase         - 升级(如果不存在的话初始化) 数据库
-│ ├─ resetNodePassword       - 重置所有节点通讯密钥
-│ ├─ getCookie               - 获取指定用户的 Cookie
-│ ├─ resetPort               - 重置单个用户端口
-│ ├─ createAdmin             - 创建管理员帐号
-│ ├─ resetAllPort            - 重置所有用户端口
-│ ├─ resetTraffic            - 重置所有用户流量
-│ ├─ generateUUID            - 为所有用户生成新的 UUID
-│ ├─ generateGa              - 为所有用户生成新的 Ga Secret
+│ ├─ setTelegram             - 设置 Telegram 机器人
+│ ├─ resetAllSettings        - 使用默认值覆盖设置中心设置
+│ ├─ exportAllSettings       - 导出所有设置
+│ ├─ importAllSettings       - 导入所有设置
+│ ├─ resetNodePassword       - 重置所有节点通讯密钥
+│ ├─ getCookie               - 获取指定用户的 Cookie
+│ ├─ resetPort               - 重置单个用户端口
+│ ├─ createAdmin             - 创建管理员帐号
+│ ├─ resetAllPort            - 重置所有用户端口
+│ ├─ resetTraffic            - 重置所有用户流量
+│ ├─ generateUUID            - 为所有用户生成新的 UUID
+│ ├─ generateGa              - 为所有用户生成新的 Ga Secret
 | ├─ generateApiToken        - 为所有用户生成新的 API Token
-│├─ setTheme                - 为所有用户设置新的主题
+│ ├─ setTheme                - 为所有用户设置新的主题
 
 EOL;
 
@@ -44,7 +43,7 @@ EOL;
             if (method_exists($this, $methodName)) {
                 $this->$methodName();
             } else {
-                echo '方法不存在.' . PHP_EOL;
+                echo '方法不存在' . PHP_EOL;
             }
         }
     }
@@ -70,7 +69,7 @@ EOL;
             $setting->save();
         }
 
-        echo '已使用默认值覆盖所有设置.' . PHP_EOL;
+        echo '已使用默认值覆盖所有数据库设置' . PHP_EOL;
     }
 
     public function exportAllSettings(): void
@@ -87,7 +86,7 @@ EOL;
         $json_settings = \json_encode($settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
         file_put_contents('./config/settings.json', $json_settings);
 
-        echo '已导出所有设置.' . PHP_EOL;
+        echo '已导出所有数据库设置' . PHP_EOL;
     }
 
     public function importAllSettings(): void
@@ -116,7 +115,7 @@ EOL;
                 $new_item->mark = $item['mark'];
                 $new_item->save();
 
-                echo "添加新设置:${item_name}" . PHP_EOL;
+                echo "添加新数据库设置:${item_name}" . PHP_EOL;
                 $add_counter += 1;
             }
         }
@@ -130,21 +129,15 @@ EOL;
         }
 
         if ($add_counter !== 0) {
-            echo "总计添加了 ${add_counter} 条新设置." . PHP_EOL;
+            echo "总计添加了 ${add_counter} 项新数据库设置" . PHP_EOL;
         } else {
-            echo '没有任何新设置需要添加.' . PHP_EOL;
+            echo '没有任何新数据库设置需要添加' . PHP_EOL;
         }
         if ($del_counter !== 0) {
-            echo "总计移除了 ${del_counter} 条设置." . PHP_EOL;
+            echo "总计移除了 ${del_counter} 项数据库设置" . PHP_EOL;
         }
     }
 
-    public function upgradeDatabase(): void
-    {
-        $phinx = new \Phinx\Console\PhinxApplication();
-        $phinx->run();
-    }
-
     public function resetNodePassword(): void
     {
         $nodes = Node::all();
@@ -152,7 +145,7 @@ EOL;
             $node->password = Tools::genRandomChar(32);
             $node->save();
         }
-        echo '已重置所有节点密码.' . PHP_EOL;
+        echo '已重置所有节点密码' . PHP_EOL;
     }
 
     /**
@@ -168,7 +161,7 @@ EOL;
                 echo '重置成功!';
             }
         } else {
-            echo 'not found user.';
+            echo '用户不存在';
         }
     }
 

+ 5 - 5
src/Command/Update.php

@@ -13,9 +13,9 @@ final class Update extends Command
         global $_ENV;
         $copy_result = copy(BASE_PATH . '/config/.config.php', BASE_PATH . '/config/.config.php.bak');
         if ($copy_result === true) {
-            echo '备份成功' . PHP_EOL;
+            echo '.config.php 文件备份成功' . PHP_EOL;
         } else {
-            echo '备份失败,迁移终止' . PHP_EOL;
+            echo '.config.php 文件备份失败,迁移终止' . PHP_EOL;
         }
 
         echo PHP_EOL;
@@ -30,7 +30,7 @@ final class Update extends Command
             $matches_new = [];
             preg_match($regex, $config_new, $matches_new);
             if (isset($matches_new[0]) === false) {
-                echo '未找到配置项:' . $key . ' 未能在新config文件中找到,可能已被更名或废弃' . PHP_EOL;
+                echo '未找到配置项:' . $key . ' 未能在新版本 .config.php 文件中找到,可能已被更名或废弃' . PHP_EOL;
                 continue;
             }
 
@@ -69,9 +69,9 @@ final class Update extends Command
             $difference = substr($difference, 15);
             $difference = substr($difference, 0, -2);
 
-            echo '新增配置项:' . $difference . ':' . $comment . PHP_EOL;
+            echo '新增 .config.php 配置项:' . $difference . ':' . $comment . PHP_EOL;
         }
-        echo '新增配置项通常带有默认值,因此通常即使不作任何改动网站也可以正常运行' . PHP_EOL;
+        echo '没有任何新.config.php 配置项需要添加' . PHP_EOL;
 
         file_put_contents(BASE_PATH . '/config/.config.php', $config_new);
         echo PHP_EOL . '迁移完成' . PHP_EOL;

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

@@ -9,8 +9,8 @@ use App\Models\Ann;
 use App\Models\User;
 use App\Utils\Telegram;
 use League\HTMLToMarkdown\HtmlConverter;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class AnnController extends BaseController
 {

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

@@ -7,8 +7,8 @@ namespace App\Controllers\Admin;
 use App\Controllers\BaseController;
 use App\Models\Node;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class ApiController extends BaseController
 {

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

@@ -11,8 +11,8 @@ use App\Services\Auth;
 use App\Services\Mail;
 use App\Utils\ResponseHelper;
 use App\Utils\Tools;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class CodeController extends BaseController
 {

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

@@ -95,7 +95,7 @@ final class CouponController extends BaseController
         return $response->write(
             $this->view()
                 ->assign('details', self::$details)
-                ->display('admin/coupon.tpl')
+                ->fetch('admin/coupon.tpl')
         );
     }
 

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

@@ -8,8 +8,8 @@ use App\Controllers\BaseController;
 use App\Models\DetectBanLog;
 use App\Utils\ResponseHelper;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class DetectBanLogController extends BaseController
 {

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

@@ -9,8 +9,8 @@ use App\Models\DetectLog;
 use App\Models\DetectRule;
 use App\Utils\ResponseHelper;
 use App\Utils\Telegram;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class DetectController extends BaseController
 {

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

@@ -57,7 +57,7 @@ final class GiftCardController extends BaseController
         return $response->write(
             $this->view()
                 ->assign('details', self::$details)
-                ->display('admin/giftcard.tpl')
+                ->fetch('admin/giftcard.tpl')
         );
     }
 

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

@@ -33,7 +33,7 @@ final class InvoiceController extends BaseController
         return $response->write(
             $this->view()
                 ->assign('details', self::$details)
-                ->display('admin/invoice/index.tpl')
+                ->fetch('admin/invoice/index.tpl')
         );
     }
 
@@ -52,7 +52,7 @@ final class InvoiceController extends BaseController
             $this->view()
                 ->assign('invoice', $invoice)
                 ->assign('invoice_content', $invoice_content)
-                ->display('admin/invoice/view.tpl')
+                ->fetch('admin/invoice/view.tpl')
         );
     }
 

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

@@ -9,8 +9,8 @@ use App\Models\Ip;
 use App\Models\LoginIp;
 use App\Utils\QQWry;
 use App\Utils\Tools;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class IpController extends BaseController
 {

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

@@ -12,8 +12,8 @@ use App\Utils\Telegram;
 use App\Utils\Tools;
 use Exception;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class NodeController extends BaseController
 {

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

@@ -35,7 +35,7 @@ final class OrderController extends BaseController
         return $response->write(
             $this->view()
                 ->assign('details', self::$details)
-                ->display('admin/order/index.tpl')
+                ->fetch('admin/order/index.tpl')
         );
     }
 
@@ -64,7 +64,7 @@ final class OrderController extends BaseController
                 ->assign('invoice', $invoice)
                 ->assign('product_content', $product_content)
                 ->assign('invoice_content', $invoice_content)
-                ->display('admin/order/view.tpl')
+                ->fetch('admin/order/view.tpl')
         );
     }
 

+ 3 - 3
src/Controllers/Admin/ProductController.php

@@ -50,7 +50,7 @@ final class ProductController extends BaseController
         return $response->write(
             $this->view()
                 ->assign('details', self::$details)
-                ->display('admin/product/index.tpl')
+                ->fetch('admin/product/index.tpl')
         );
     }
 
@@ -59,7 +59,7 @@ final class ProductController extends BaseController
         return $response->write(
             $this->view()
                 ->assign('update_field', self::$update_field)
-                ->display('admin/product/create.tpl')
+                ->fetch('admin/product/create.tpl')
         );
     }
 
@@ -181,7 +181,7 @@ final class ProductController extends BaseController
                 ->assign('content', $content)
                 ->assign('limit', $limit)
                 ->assign('update_field', self::$update_field)
-                ->display('admin/product/edit.tpl')
+                ->fetch('admin/product/edit.tpl')
         );
     }
 

+ 0 - 1
src/Controllers/Admin/SettingController.php

@@ -8,7 +8,6 @@ use App\Controllers\BaseController;
 use App\Models\Setting;
 use App\Services\Mail;
 use App\Services\Payment;
-use Exception;
 
 final class SettingController extends BaseController
 {

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

@@ -8,8 +8,8 @@ use App\Controllers\BaseController;
 use App\Models\Bought;
 use App\Models\Shop;
 use App\Utils\ResponseHelper;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class ShopController extends BaseController
 {

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

@@ -8,8 +8,8 @@ use App\Controllers\BaseController;
 use App\Models\UserSubscribeLog;
 use App\Utils\QQWry;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class SubscribeLogController extends BaseController
 {

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

@@ -8,8 +8,8 @@ use App\Controllers\BaseController;
 use App\Models\Ticket;
 use App\Models\User;
 use App\Utils\Tools;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 use voku\helper\AntiXSS;
 
 final class TicketController extends BaseController

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

@@ -8,8 +8,8 @@ use App\Controllers\BaseController;
 use App\Models\UserHourlyUsage;
 use App\Utils\Tools;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class TrafficLogController extends BaseController
 {

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

@@ -14,8 +14,8 @@ use App\Utils\Check;
 use App\Utils\Cookie;
 use App\Utils\Hash;
 use App\Utils\Tools;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class UserController extends BaseController
 {

+ 1 - 1
src/Controllers/AdminController.php

@@ -11,8 +11,8 @@ use App\Utils\DatatablesHelper;
 use App\Utils\ResponseHelper;
 use App\Utils\Tools;
 use Ozdemir\Datatables\Datatables;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 /*
  *  Admin Controller

+ 1 - 1
src/Controllers/AuthController.php

@@ -20,8 +20,8 @@ use App\Utils\Tools;
 use Exception;
 use Psr\Http\Message\ResponseInterface;
 use Ramsey\Uuid\Uuid;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 use voku\helper\AntiXSS;
 
 /**

+ 1 - 1
src/Controllers/HomeController.php

@@ -8,8 +8,8 @@ use App\Models\InviteCode;
 use App\Services\Auth;
 use App\Utils\Telegram\Process;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 /**
  *  HomeController

+ 197 - 740
src/Controllers/LinkController.php

@@ -2,826 +2,283 @@
 
 declare(strict_types=1);
 
-//Thanks to http://blog.csdn.net/jollyjumper/article/details/9823047
-
 namespace App\Controllers;
 
 use App\Models\Link;
-use App\Models\User;
-
+use App\Models\Node;
 use App\Models\UserSubscribeLog;
-use App\Utils\AppURI;
-use App\Utils\ConfGenerate;
-use App\Utils\ConfRender;
-use App\Utils\Tools;
-use App\Utils\URL;
-use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
-use voku\helper\AntiXSS;
+use Slim\Http\ServerRequest;
 
 /**
  *  LinkController
  */
 final class LinkController extends BaseController
 {
-    public static function generateRandomLink()
-    {
-        for ($i = 0; $i < 10; $i++) {
-            $token = Tools::genRandomChar(16);
-            $Elink = Link::where('token', $token)->first();
-            if ($Elink === null) {
-                return $token;
-            }
-        }
-
-        return "couldn't alloc token";
-    }
-
-    public static function generateSSRSubCode(int $userid): string
-    {
-        $Elink = Link::where('userid', $userid)->first();
-        if ($Elink !== null) {
-            return $Elink->token;
-        }
-        $NLink = new Link();
-        $NLink->userid = $userid;
-        $NLink->token = self::generateRandomLink();
-        $NLink->save();
-
-        return $NLink->token;
-    }
-
-    /**
-     * @param array     $args
-     */
     public static function getContent(ServerRequest $request, Response $response, array $args)
     {
         if (! $_ENV['Subscribe']) {
-            return null;
+            return $response->withJson([
+                'ret' => 0,
+            ]);
         }
 
         $token = $args['token'];
+        $params = $request->getQueryParams();
 
         $Elink = Link::where('token', $token)->first();
         if ($Elink === null) {
-            return null;
+            return $response->withJson([
+                'ret' => 0,
+            ]);
         }
 
         $user = $Elink->getUser();
         if ($user === null) {
-            return null;
+            return $response->withJson([
+                'ret' => 0,
+            ]);
         }
 
-        $opts = $request->getQueryParams();
+        $sub_info = [];
 
-        // 筛选节点部分
-        $Rule = [];
-        $Rule['type'] = (isset($opts['type']) ? trim($opts['type']) : 'all');
-        $Rule['is_mu'] = ($_ENV['mergeSub'] === true ? 1 : 0);
-        if (isset($opts['mu'])) {
-            $Rule['is_mu'] = (int) $opts['mu'];
+        if (isset($params['clash']) && $params['clash'] === '1') {
+            $sub_type = 'clash';
+            $sub_info = SubController::getClash($user);
         }
 
-        if (isset($opts['class'])) {
-            $class = trim(urldecode($opts['class']));
-            $Rule['content']['class'] = array_map(
-                static function ($item) {
-                    return (int) $item;
-                },
-                explode('-', $class)
-            );
+        if (isset($params['sip002']) && $params['sip002'] === '1') {
+            $sub_type = 'sip002';
+            $sub_info = self::getSIP002($user);
         }
 
-        if (isset($opts['noclass'])) {
-            $noclass = trim(urldecode($opts['noclass']));
-            $Rule['content']['noclass'] = array_map(
-                static function ($item) {
-                    return (int) $item;
-                },
-                explode('-', $noclass)
-            );
+        if (isset($params['ss']) && $params['ss'] === '1') {
+            $sub_type = 'ss';
+            $sub_info = self::getSS($user);
         }
 
-        if (isset($opts['regex'])) {
-            $Rule['content']['regex'] = trim(urldecode($opts['regex']));
+        if (isset($params['v2ray']) && $params['v2ray'] === '1') {
+            $sub_type = 'v2ray';
+            $sub_info = self::getV2Ray($user);
         }
 
-        // 显示流量以及到期时间等
-        $Rule['extend'] = $_ENV['enable_sub_extend'];
-        if (isset($opts['extend'])) {
-            $Rule['extend'] = (bool) $opts['extend'];
+        if (isset($params['trojan']) && $params['trojan'] === '1') {
+            $sub_type = 'trojan';
+            $sub_info = self::getTrojan($user);
         }
 
-        // 兼容原版
-        if (isset($opts['mu'])) {
-            $mu = (int) $opts['mu'];
-            switch ($mu) {
-                case 0:
-                    $opts['sub'] = 1;
+        if (isset($params['sub'])) {
+            switch ($params['sub']) {
+                case '2':
+                    $sub_type = 'ss';
+                    $sub_info = self::getSS($user);
                     break;
-                case 1:
-                    $opts['sub'] = 1;
+                case '3':
+                    $sub_type = 'v2ray';
+                    $sub_info = self::getV2Ray($user);
                     break;
-                case 2:
-                    $opts['sub'] = 3;
+                case '4':
+                    $sub_type = 'trojan';
+                    $sub_info = self::getTrojan($user);
                     break;
-                case 3:
-                    $opts['ssd'] = 1; //deprecated
-                    break;
-                case 4:
-                    $opts['clash'] = 1;
-                    break;
-            }
-        }
-
-        // 订阅类型
-        $subscribe_type = '';
-
-        $getBody = '';
-
-        $sub_type_array = ['list', 'clash', 'surge', 'surfboard','anxray', 'quantumult', 'quantumultx', 'sub'];
-        foreach ($sub_type_array as $key) {
-            if (isset($opts[$key])) {
-                $query_value = $opts[$key];
-                if ($query_value !== '0' && $query_value !== '') {
-                    // 兼容代码开始
-                    if ($key === 'sub' && $query_value > 4) {
-                        $query_value = 1;
-                    }
-                    // 兼容代码结束
-
-                    if ($key === 'list') {
-                        $SubscribeExtend = self::getSubscribeExtend($query_value);
-                    } else {
-                        $SubscribeExtend = self::getSubscribeExtend($key, $query_value);
-                    }
-                    $filename = $SubscribeExtend['filename'] . '_' . \time() . '.' . $SubscribeExtend['suffix'];
-                    $subscribe_type = $SubscribeExtend['filename'];
-
-                    $class = 'get' . $SubscribeExtend['class'];
-                    $content = self::$class($user, $query_value, $opts, $Rule);
-                    $getBody = self::getBody(
-                        $user,
-                        $response,
-                        $content,
-                        $filename
-                    );
+                default:
+                    $sub_type = 'ss';
+                    $sub_info = self::getSS($user);
                     break;
-                }
-                continue;
             }
         }
 
         // 记录订阅日志
         if ($_ENV['subscribeLog'] === true) {
-            self::subscribeLog($user, $subscribe_type, $request->getHeaderLine('User-Agent'));
+            UserSubscribeLog::addSubscribeLog($user, $sub_type, $request->getHeaderLine('User-Agent'));
         }
 
-        return $response->write($getBody);
-    }
-
-    /**
-     * 获取订阅类型的文件名
-     *
-     * @param string      $type  订阅类型
-     * @param string|null $value 值
-     *
-     * @return array
-     */
-    public static function getSubscribeExtend(string $type, ?string $value = null): array
-    {
-        switch ($type) {
-            case 'ss':
-                $return = [
-                    'filename' => 'SS',
-                    'suffix' => 'txt',
-                    'class' => 'Sub',
-                ];
-                break;
-            case 'ssa':
-                $return = [
-                    'filename' => 'SSA',
-                    'suffix' => 'json',
-                    'class' => 'Lists',
-                ];
-                break;
-            case 'ssr':
-                $return = [
-                    'filename' => 'SSR',
-                    'suffix' => 'txt',
-                    'class' => 'Sub',
-                ];
-                break;
-            case 'sub':
-                $strArray = [
-                    1 => 'ssr',
-                    2 => 'ss',
-                    3 => 'v2rayn',
-                    4 => 'trojan',
-                ];
-                $str = (! \in_array($value, $strArray) ? $strArray[$value] : $strArray[1]);
-                $return = self::getSubscribeExtend($str);
-                break;
-            case 'clash':
-                if ($value !== null) {
-                    $return = self::getSubscribeExtend('clash');
-                    $return['class'] = 'Clash';
-                } else {
-                    $return = [
-                        'filename' => 'Clash',
-                        'suffix' => 'yaml',
-                        'class' => 'Lists',
-                    ];
-                }
-                break;
-            case 'surge':
-                if ($value !== null) {
-                    $return = [
-                        'filename' => 'Surge',
-                        'suffix' => 'conf',
-                        'class' => 'Surge',
-                    ];
-                    $return['filename'] .= $value;
-                } else {
-                    $return = [
-                        'filename' => 'SurgeList',
-                        'suffix' => 'list',
-                        'class' => 'Lists',
-                    ];
-                }
-                break;
-            case 'v2rayn':
-                $return = [
-                    'filename' => 'V2RayN',
-                    'suffix' => 'txt',
-                    'class' => 'Sub',
-                ];
-                break;
-            case 'trojan':
-                $return = [
-                    'filename' => 'Trojan',
-                    'suffix' => 'txt',
-                    'class' => 'Sub',
-                ];
-                break;
-            case 'kitsunebi':
-                $return = [
-                    'filename' => 'Kitsunebi',
-                    'suffix' => 'txt',
-                    'class' => 'Lists',
-                ];
-                break;
-            case 'anxray':
-                $return = [
-                    'filename' => 'AnXray',
-                    'suffix' => 'txt',
-                    'class' => 'AnXray',
-                ];
-                break;
-            case 'surfboard':
-                $return = [
-                    'filename' => 'Surfboard',
-                    'suffix' => 'conf',
-                    'class' => 'Surfboard',
-                ];
-                break;
-            case 'quantumult':
-                if ($value !== null) {
-                    if ((int) $value === 2) {
-                        $return = self::getSubscribeExtend('quantumult_sub');
-                    } else {
-                        $return = self::getSubscribeExtend('quantumult_conf');
-                    }
-                } else {
-                    $return = [
-                        'filename' => 'Quantumult',
-                        'suffix' => 'conf',
-                        'class' => 'Lists',
-                    ];
-                }
-                break;
-            case 'quantumultx':
-                $return = [
-                    'filename' => 'QuantumultX',
-                    'suffix' => 'txt',
-                    'class' => 'Lists',
-                ];
-                if ($value !== null) {
-                    $return['class'] = 'QuantumultX';
-                }
-                break;
-            case 'shadowrocket':
-                $return = [
-                    'filename' => 'Shadowrocket',
-                    'suffix' => 'txt',
-                    'class' => 'Lists',
-                ];
-                break;
-            case 'clash_provider':
-                $return = [
-                    'filename' => 'ClashProvider',
-                    'suffix' => 'yaml',
-                    'class' => 'Lists',
-                ];
-                break;
-            case 'quantumult_sub':
-                $return = [
-                    'filename' => 'QuantumultSub',
-                    'suffix' => 'conf',
-                    'class' => 'Quantumult',
-                ];
-                break;
-            case 'quantumult_conf':
-                $return = [
-                    'filename' => 'QuantumultConf',
-                    'suffix' => 'conf',
-                    'class' => 'Quantumult',
-                ];
-                break;
-            default:
-                $return = [
-                    'filename' => 'UndefinedNode',
-                    'suffix' => 'txt',
-                    'class' => 'Sub',
-                ];
-                break;
-        }
-        return $return;
-    }
+        $sub_details = ' upload=' . $user->u
+            . '; download=' . $user->d
+            . '; total=' . $user->transfer_enable
+            . '; expire=' . strtotime($user->class_expire);
 
-    /**
-     * 响应内容
-     *
-     * @param string $content  订阅内容
-     * @param string $filename 文件名
-     */
-    public static function getBody(User $user, object $response, string $content, string $filename): ResponseInterface
-    {
-        $response = $response
-            ->withHeader(
-                'Content-type',
-                ' application/octet-stream; charset=utf-8'
-            )
-            ->withHeader(
-                'Cache-Control',
-                'no-store, no-cache, must-revalidate'
-            )
-            ->withHeader(
-                'Content-Disposition',
-                ' attachment; filename=' . $filename
-            )
-            ->withHeader(
-                'Subscription-Userinfo',
-                (' upload=' . $user->u
-                    . '; download=' . $user->d
-                    . '; total=' . $user->transfer_enable
-                    . '; expire=' . strtotime($user->class_expire))
-            );
-
-        return $response->write($content);
-    }
-
-    /**
-     * 订阅链接汇总
-     *
-     * @param User $user 用户
-     * @param int  $int  当前用户访问的订阅类型
-     *
-     * @return array
-     */
-    public static function getSubinfo(User $user, int $int = 0): array
-    {
-        if ($int === 0) {
-            $int = '';
-        }
-        $userapiUrl = $_ENV['subUrl'] . '/link/' . self::generateSSRSubCode($user->id);
-        $return_info = [
-            'link' => '',
-            // sub
-            'ss' => '?sub=2',
-            'ssr' => '?sub=1',
-            'v2ray' => '?sub=3',
-            'trojan' => '?sub=4',
-            // apps
-            'ssa' => '?list=ssa',
-            'anxray' => '?anxray=1',
-            'clash' => '?clash=1',
-            'clash_provider' => '?list=clash',
-            'surge' => '?surge=' . $int,
-            'surge_node' => '?list=surge',
-            'surge2' => '?surge=2',
-            'surge3' => '?surge=3',
-            'surge4' => '?surge=4',
-            'surfboard' => '?surfboard=1',
-            'quantumult' => '?quantumult=' . $int,
-            'quantumult_v2' => '?list=quantumult',
-            'quantumult_sub' => '?quantumult=2',
-            'quantumult_conf' => '?quantumult=3',
-            'quantumultx' => '?list=quantumultx',
-            'shadowrocket' => '?list=shadowrocket',
-            'kitsunebi' => '?list=kitsunebi',
-        ];
-
-        return array_map(
-            static function ($item) use ($userapiUrl) {
-                return $userapiUrl . $item;
-            },
-            $return_info
+        return $response->withHeader('Subscription-Userinfo', $sub_details)->write(
+            $sub_info
         );
     }
 
-    public static function getListItem($item, $list)
+    // 传统 SS 订阅
+    public static function getSS($user): string
     {
-        $return = null;
-        switch ($list) {
-            case 'ss':
-                $return = AppURI::getItemUrl($item, 1);
-                break;
-            case 'ssr':
-                $return = AppURI::getItemUrl($item, 0);
-                break;
-            case 'ssa':
-                $return = AppURI::getSSJSON($item);
-                break;
-            case 'anxray':
-                $return = AppURI::getAnXrayURI($item);
-                break;
-            case 'surge':
-                $return = AppURI::getSurgeURI($item, 3);
-                break;
-            case 'clash':
-                $return = AppURI::getClashURI($item);
-                break;
-            case 'v2rayn':
-                $return = AppURI::getV2RayNURI($item);
-                break;
-            case 'trojan':
-                $return = AppURI::getTrojanURI($item);
-                break;
-            case 'kitsunebi':
-                $return = AppURI::getKitsunebiURI($item);
-                break;
-            case 'quantumult':
-                $return = AppURI::getQuantumultURI($item, true);
-                break;
-            case 'quantumultx':
-                $return = AppURI::getQuantumultXURI($item);
-                break;
-            case 'shadowrocket':
-                $return = AppURI::getShadowrocketURI($item);
-                break;
-        }
-        return $return;
-    }
-
-    public static function getLists($user, $list, $opts, $Rule)
-    {
-        $list = strtolower($list);
-        if ($list === 'ssa') {
-            $Rule['type'] = 'ss';
-        }
-        if ($list === 'quantumult') {
-            $Rule['type'] = 'vmess';
-        }
-        $items = URL::getNewAllItems($user, $Rule);
-        $return = [];
-        if ($Rule['extend'] === true) {
-            switch ($list) {
-                case 'ssa':
-                case 'clash':
-                    $return = array_merge($return, self::getListExtend($user, $list));
-                    break;
-                default:
-                    $return[] = implode(PHP_EOL, self::getListExtend($user, $list));
-                    break;
+        $links = '';
+        //篩選出用戶能連接的節點
+        $nodes_raw = Node::where('type', 1)
+            ->where('node_class', '<=', $user->class)
+            ->whereIn('node_group', [0, $user->node_group])
+            ->where(static function ($query): void {
+                $query->where('node_bandwidth_limit', '=', 0)->orWhereRaw('node_bandwidth < node_bandwidth_limit');
+            })
+            ->get();
+
+        foreach ($nodes_raw as $node_raw) {
+            $node_custom_config = \json_decode($node_raw->custom_config, true);
+            //檢查是否配置“前端/订阅中下发的服务器地址”
+            if (! \array_key_exists('server_user', $node_custom_config)) {
+                $server = $node_raw->server;
+            } else {
+                $server = $node_custom_config['server_user'];
             }
-        }
-        foreach ($items as $item) {
-            $out = self::getListItem($item, $list);
-            if ($out !== null) {
-                $return[] = $out;
+            switch ($node_raw->sort) {
+                case '0':
+                    $links .= \base64_encode($user->method . ':' . $user->passwd . '@' . $server . ':' . $user->port) . '#' .
+                    $node_raw->name . PHP_EOL;
+                    break;
             }
         }
-        switch ($list) {
-            case 'ssa':
-                return \json_encode($return, 320);
-                break;
-            case 'clash':
-                return \Symfony\Component\Yaml\Yaml::dump(['proxies' => $return], 4, 2);
-            case 'kitsunebi':
-            case 'quantumult':
-            case 'shadowrocket':
-                return base64_encode(implode(PHP_EOL, $return));
-            default:
-                return implode(PHP_EOL, $return);
-        }
+
+        return $links;
     }
 
-    public static function getListExtend($user, $list)
+    // SIP002 SS 订阅
+    public static function getSIP002($user): string
     {
-        $return = [];
-        $info_array = (count($_ENV['sub_message']) !== 0 ? (array) $_ENV['sub_message'] : []);
-        if (strtotime($user->expire_in) > \time()) {
-            if ($user->transfer_enable === 0) {
-                $unusedTraffic = '剩余流量:0';
-            } else {
-                $unusedTraffic = '剩余流量:' . $user->unusedTraffic();
-            }
-            $expire_in = '过期时间:';
-            if ($user->class_expire !== '1989-06-04 00:05:00') {
-                $userClassExpire = explode(' ', $user->class_expire);
-                $expire_in .= $userClassExpire[0];
-            } else {
-                $expire_in .= '无限期';
-            }
-        } else {
-            $unusedTraffic = '账户已过期,请续费后使用';
-            $expire_in = '账户已过期,请续费后使用';
-        }
-        if (! \in_array($list, ['quantumult', 'quantumultx', 'shadowrocket'])) {
-            $info_array[] = $unusedTraffic;
-            $info_array[] = $expire_in;
-        }
-        $baseUrl = explode('//', $_ENV['baseUrl'])[1];
-        $baseUrl = explode('/', $baseUrl)[0];
-        $Extend = [
-            'remark' => '',
-            'type' => '',
-            'add' => $baseUrl,
-            'address' => $baseUrl,
-            'port' => 10086,
-            'method' => 'chacha20-ietf',
-            'passwd' => $user->passwd,
-            'id' => $user->uuid,
-            'aid' => 0,
-            'net' => 'tcp',
-            'headerType' => 'none',
-            'host' => '',
-            'path' => '/',
-            'tls' => '',
-            'protocol' => 'origin',
-            'protocol_param' => '',
-            'obfs' => 'plain',
-            'obfs_param' => '',
-            'group' => $_ENV['appName'],
-        ];
-        if ($list === 'shadowrocket') {
-            $return[] = 'STATUS=' . $unusedTraffic . '.♥.' . $expire_in . PHP_EOL . 'REMARKS=' . $_ENV['appName'];
-        }
-        foreach ($info_array as $remark) {
-            $Extend['remark'] = $remark;
-            if (\in_array($list, ['kitsunebi', 'quantumult', 'v2rayn'])) {
-                $Extend['type'] = 'vmess';
-                $out = self::getListItem($Extend, $list);
-            } elseif ($list === 'trojan') {
-                $Extend['type'] = 'trojan';
-                $out = self::getListItem($Extend, $list);
-            } elseif ($list === 'ssr') {
-                $Extend['type'] = 'ssr';
-                $out = self::getListItem($Extend, $list);
+        $links = '';
+        //篩選出用戶能連接的節點
+        $nodes_raw = Node::where('type', 1)
+            ->where('node_class', '<=', $user->class)
+            ->whereIn('node_group', [0, $user->node_group])
+            ->where(static function ($query): void {
+                $query->where('node_bandwidth_limit', '=', 0)->orWhereRaw('node_bandwidth < node_bandwidth_limit');
+            })
+            ->get();
+
+        foreach ($nodes_raw as $node_raw) {
+            $node_custom_config = \json_decode($node_raw->custom_config, true);
+            //檢查是否配置“前端/订阅中下发的服务器地址”
+            if (! \array_key_exists('server_user', $node_custom_config)) {
+                $server = $node_raw->server;
             } else {
-                $Extend['type'] = 'ss';
-                $out = self::getListItem($Extend, $list);
+                $server = $node_custom_config['server_user'];
             }
-            if ($out !== null) {
-                $return[] = $out;
+            switch ($node_raw->sort) {
+                case '0':
+                    $plugin = $node_custom_config['plugin'] ?? '';
+                    $plugin_option = $node_custom_config['plugin_option'] ?? '';
+
+                    $links .= $user->method . ':' . $user->passwd . '@' . $server . ':' .
+                    $user->port . '/?plugin=' . $plugin . '&' . $plugin_option . '#' .
+                    $node_raw->name . PHP_EOL;
+                    break;
             }
         }
-        return $return;
-    }
 
-    /**
-     * Surge 配置
-     *
-     * @param User  $user  用户
-     * @param int   $surge 订阅类型
-     * @param array $opts  request
-     * @param array $Rule  节点筛选规则
-     */
-    public static function getSurge(User $user, $surge, array $opts, array $Rule): string
-    {
-        if ($surge !== 4) {
-            $Rule['type'] = 'ss';
-        }
-        $items = URL::getNewAllItems($user, $Rule);
-        $Nodes = [];
-        $All_Proxy = '';
-        foreach ($items as $item) {
-            $out = AppURI::getSurgeURI($item, $surge);
-            if ($out !== null) {
-                $Nodes[] = $item;
-                $All_Proxy .= $out . PHP_EOL;
-            }
-        }
-        $variable = ($surge === 2 ? 'Surge2_Profiles' : 'Surge_Profiles');
-        if (isset($opts['profiles']) && \in_array($opts['profiles'], array_keys($_ENV[$variable]))) {
-            $Profiles = $opts['profiles'];
-        } else {
-            $Profiles = ($surge === 2 ? $_ENV['Surge2_DefaultProfiles'] : $_ENV['Surge_DefaultProfiles']);
-        }
-        return ConfGenerate::getSurgeConfs($user, $All_Proxy, $Nodes, $_ENV[$variable][$Profiles]);
+        return $links;
     }
 
-    /**
-     * Quantumult 配置
-     *
-     * @param User  $user       用户
-     * @param int   $quantumult 订阅类型
-     * @param array $opts       request
-     * @param array $Rule       节点筛选规则
-     */
-    public static function getQuantumult(User $user, $quantumult, array $opts, array $Rule): string
+    public static function getV2Ray($user): string
     {
-        switch ($quantumult) {
-            case 2:
-                $subUrl = self::getSubinfo($user, 0);
-                $str = [
-                    '[SERVER]',
-                    '',
-                    '[SOURCE]',
-                    $_ENV['appName'] . ', server ,' . $subUrl['ssr'] . ', false, true, false',
-                    $_ENV['appName'] . '_ss, server ,' . $subUrl['ss'] . ', false, true, false',
-                    $_ENV['appName'] . '_VMess, server ,' . $subUrl['quantumult_v2'] . ', false, true, false',
-                    'Hackl0us Rules, filter, https://raw.githubusercontent.com/Hackl0us/Surge-Rule-Snippets/master/LAZY_RULES/Quantumult.conf, true',
-                    '',
-                    '[DNS]',
-                    'system, 119.29.29.29, 223.6.6.6, 114.114.114.114',
-                    '',
-                    '[STATE]',
-                    'STATE,AUTO',
-                ];
-                return implode(PHP_EOL, $str);
-                break;
-            case 3:
-                $items = URL::getNewAllItems($user, $Rule);
-                break;
-            default:
-                return self::getLists($user, 'quantumult', $opts, $Rule);
-                break;
-        }
-
-        $All_Proxy = '';
-        $All_Proxy_name = '';
-        $BackChina_name = '';
-        foreach ($items as $item) {
-            $out = AppURI::getQuantumultURI($item);
-            if ($out !== null) {
-                $All_Proxy .= $out . PHP_EOL;
-                if (strpos($item['remark'], '回国') || strpos($item['remark'], 'China')) {
-                    $BackChina_name .= "\n" . $item['remark'];
-                } else {
-                    $All_Proxy_name .= "\n" . $item['remark'];
-                }
+        $links = '';
+        //篩選出用戶能連接的節點
+        $nodes_raw = Node::where('type', 1)
+            ->where('node_class', '<=', $user->class)
+            ->whereIn('node_group', [0, $user->node_group])
+            ->where(static function ($query): void {
+                $query->where('node_bandwidth_limit', '=', 0)->orWhereRaw('node_bandwidth < node_bandwidth_limit');
+            })
+            ->get();
+
+        foreach ($nodes_raw as $node_raw) {
+            $node_custom_config = \json_decode($node_raw->custom_config, true);
+            //檢查是否配置“前端/订阅中下发的服务器地址”
+            if (! \array_key_exists('server_user', $node_custom_config)) {
+                $server = $node_raw->server;
+            } else {
+                $server = $node_custom_config['server_user'];
             }
-        }
-        $ProxyGroups = [
-            'proxy_group' => base64_encode("🍃 Proxy  :  static, 🏃 Auto\n🏃 Auto\n🚀 Direct\n" . $All_Proxy_name),
-            'domestic_group' => base64_encode("🍂 Domestic  :  static, 🚀 Direct\n🚀 Direct\n🍃 Proxy\n" . $BackChina_name),
-            'others_group' => base64_encode("☁️ Others  :   static, 🍃 Proxy\n🚀 Direct\n🍃 Proxy"),
-            'direct_group' => base64_encode("🚀 Direct : static, DIRECT\nDIRECT"),
-            'apple_group' => base64_encode("🍎 Only  :  static, 🚀 Direct\n🚀 Direct\n🍃 Proxy"),
-            'auto_group' => base64_encode("🏃 Auto  :  auto\n" . $All_Proxy_name),
-        ];
-        $render = ConfRender::getTemplateRender();
-        $render->assign('All_Proxy', $All_Proxy)->assign('ProxyGroups', $ProxyGroups);
-        return $render->fetch('quantumult/quantumult.tpl');
-    }
-
-    /**
-     * QuantumultX 配置
-     *
-     * @param User  $user        用户
-     * @param int   $quantumultx 订阅类型
-     * @param array $opts        request
-     * @param array $Rule        节点筛选规则
-     */
-    public static function getQuantumultX(User $user, $quantumultx, array $opts, array $Rule): string
-    {
-        return '';
-    }
+            switch ($node_raw->sort) {
+                case '11':
+                    $v2_port = $node_custom_config['v2_port'] ?? ($node_custom_config['offset_port_user'] ?? ($node_custom_config['offset_port_node'] ?? 443));
+                    //默認值有問題的請懂 V2 怎麽用的人來改一改。
+                    $alter_id = $node_custom_config['alter_id'] ?? '0';
+                    $security = $node_custom_config['security'] ?? 'none';
+                    $network = $node_custom_config['network'] ?? '';
+                    $header = $node_custom_config['header'] ?? ['type' => 'none'];
+                    $header_type = $header['type'] ?? '';
+                    $host = $node_custom_config['host'] ?? '';
+                    $path = $node_custom_config['path'] ?? '/';
+                    $enable_vless = $node_custom_config['enable_vless'] ?? '0';
+
+                    $v2rayn_array = [
+                        'v' => '2',
+                        'ps' => $node_raw->name,
+                        'add' => $server,
+                        'port' => $v2_port,
+                        'id' => $user->uuid,
+                        'aid' => $alter_id,
+                        'net' => $network,
+                        'type' => $header_type,
+                        'host' => $host,
+                        'path' => $path,
+                        'tls' => $security,
+                    ];
 
-    /**
-     * Surfboard 配置
-     *
-     * @param User  $user      用户
-     * @param int   $surfboard 订阅类型
-     * @param array $opts      request
-     * @param array $Rule      节点筛选规则
-     */
-    public static function getSurfboard(User $user, int $surfboard, array $opts, array $Rule): string
-    {
-        $Nodes = [];
-        $All_Proxy = '';
-        $items = URL::getNewAllItems($user, $Rule);
-        foreach ($items as $item) {
-            $out = AppURI::getSurfboardURI($item);
-            if ($out !== null) {
-                $Nodes[] = $item;
-                $All_Proxy .= $out . PHP_EOL;
+                    if ($enable_vless === '1' || $enable_vless === 1) {
+                        $links .= 'vless://' . \base64_encode(\json_encode($v2rayn_array)) . PHP_EOL;
+                        break;
+                    }
+                    $links .= 'vmess://' . \base64_encode(\json_encode($v2rayn_array)) . PHP_EOL;
+                    break;
             }
         }
-        if (isset($opts['profiles']) && \in_array($opts['profiles'], array_keys($_ENV['Surfboard_Profiles']))) {
-            $Profiles = $opts['profiles'];
-        } else {
-            $Profiles = $_ENV['Surfboard_DefaultProfiles']; // 默认策略组
-        }
 
-        return ConfGenerate::getSurgeConfs($user, $All_Proxy, $Nodes, $_ENV['Surfboard_Profiles'][$Profiles]);
+        return $links;
     }
 
-    /**
-     * Clash 配置
-     *
-     * @param User  $user  用户
-     * @param int   $clash 订阅类型
-     * @param array $opts  request
-     * @param array $Rule  节点筛选规则
-     */
-    public static function getClash(User $user, $clash, array $opts, array $Rule): string
+    public static function getTrojan($user): string
     {
-        $items = URL::getNewAllItems($user, $Rule);
-        $Proxys = [];
-        foreach ($items as $item) {
-            $Proxy = AppURI::getClashURI($item);
-            if ($Proxy !== null) {
-                $Proxys[] = $Proxy;
+        $links = '';
+        //篩選出用戶能連接的節點
+        $nodes_raw = Node::where('type', 1)
+            ->where('node_class', '<=', $user->class)
+            ->whereIn('node_group', [0, $user->node_group])
+            ->where(static function ($query): void {
+                $query->where('node_bandwidth_limit', '=', 0)->orWhereRaw('node_bandwidth < node_bandwidth_limit');
+            })
+            ->get();
+
+        foreach ($nodes_raw as $node_raw) {
+            $node_custom_config = \json_decode($node_raw->custom_config, true);
+            //檢查是否配置“前端/订阅中下发的服务器地址”
+            if (! \array_key_exists('server_user', $node_custom_config)) {
+                $server = $node_raw->server;
+            } else {
+                $server = $node_custom_config['server_user'];
             }
-        }
-        if (isset($opts['profiles']) && \in_array($opts['profiles'], array_keys($_ENV['Clash_Profiles']))) {
-            $Profiles = $opts['profiles'];
-        } else {
-            $Profiles = $_ENV['Clash_DefaultProfiles']; // 默认策略组
-        }
-        return ConfGenerate::getClashConfs($user, $Proxys, $_ENV['Clash_Profiles'][$Profiles]);
-    }
-
-    public static function getAnXray($user, $anxray, $opts, $Rule)
-    {
-        $All_Proxy = '';
-        $items = URL::getNewAllItems($user, $Rule);
-        foreach ($items as $item) {
-            $out = AppURI::getAnXrayURI($item);
-            if ($out !== null) {
-                $All_Proxy .= $out . PHP_EOL;
+            switch ($node_raw->sort) {
+                case '14':
+                    $trojan_port = $node_custom_config['trojan_port'] ?? ($node_custom_config['offset_port_user'] ?? ($node_custom_config['offset_port_node'] ?? 443));
+                    $host = $node_custom_config['host'] ?? '';
+                    $allow_insecure = $node_custom_config['allow_insecure'] ?? '0';
+                    $security = $node_custom_config['security'] ?? \array_key_exists('enable_xtls', $node_custom_config) && $node_custom_config['enable_xtls'] === '1' ? 'xtls' : 'tls';
+                    $mux = $node_custom_config['mux'] ?? '';
+                    $transport = $node_custom_config['transport'] ?? \array_key_exists('grpc', $node_custom_config) && $node_custom_config['grpc'] === '1' ? 'grpc' : 'tcp';
+
+                    $transport_plugin = $node_custom_config['transport_plugin'] ?? '';
+                    $transport_method = $node_custom_config['transport_method'] ?? '';
+                    $servicename = $node_custom_config['servicename'] ?? '';
+                    $path = $node_custom_config['path'] ?? '';
+
+                    $links .= 'trojan://' . $user->uuid . '@' . $server . ':' . $trojan_port . '?peer=' . $host . '&sni=' . $host .
+                    '&obfs=' . $transport_plugin . '&path=' . $path . '&mux=' . $mux . '&allowInsecure=' . $allow_insecure .
+                    '&obfsParam=' . $transport_method . '&type=' . $transport . '&security=' . $security . '&serviceName=' . $servicename . '#' .
+                    $node_raw->name . PHP_EOL;
+                    break;
             }
         }
-        return base64_encode($All_Proxy);
-    }
-    /**
-     * 通用订阅,ssr & v2rayn
-     *
-     * @param User   $user 用户
-     * @param int    $sub  订阅类型
-     * @param array  $opts request
-     * @param array  $Rule 节点筛选规则
-     */
-    public static function getSub(User $user, $sub, array $opts, array $Rule): string
-    {
-        $return_url = '';
-        switch (((int) $sub)) {
-            case 2: // SS
-                $Rule['type'] = 'ss';
-                $getListExtend = $Rule['extend'] ? self::getListExtend($user, 'ss') : [];
-                break;
-            case 3: // V2
-                $Rule['type'] = 'vmess';
-                $getListExtend = $Rule['extend'] ? self::getListExtend($user, 'v2rayn') : [];
-                break;
-            case 4: // Trojan
-                $Rule['type'] = 'trojan';
-                $getListExtend = $Rule['extend'] ? self::getListExtend($user, 'trojan') : [];
-                break;
-            default: // SSR
-                $Rule['type'] = 'ssr';
-                $getListExtend = $Rule['extend'] ? self::getListExtend($user, 'ssr') : [];
-                break;
-        }
-        if ($Rule['extend']) {
-            $return_url .= implode(PHP_EOL, $getListExtend) . PHP_EOL;
-        }
-        $return_url .= URL::getNewAllUrl($user, $Rule);
-        return base64_encode($return_url);
+
+        return $links;
     }
 
-    /**
-     * 记录订阅日志
-     *
-     * @param User   $user 用户
-     * @param string $type 订阅类型
-     * @param string $ua   UA
-     */
-    private static function subscribeLog(User $user, string $type, string $ua): void
+    public static function getTraditionalSub($user)
     {
-        $log = new UserSubscribeLog();
-        $log->user_name = $user->user_name;
-        $log->user_id = $user->id;
-        $log->email = $user->email;
-        $log->subscribe_type = $type;
-        $log->request_ip = $_SERVER['REMOTE_ADDR'];
-        $log->request_time = date('Y-m-d H:i:s');
-        $antiXss = new AntiXSS();
-        $log->request_user_agent = $antiXss->xss_clean($ua);
-        $log->save();
+        $userid = $user->id;
+        $token = Link::where('userid', $userid)->first();
+        return $_ENV['subUrl'] . '/link/' . $token->token;
     }
 }

+ 1 - 1
src/Controllers/PasswordController.php

@@ -11,8 +11,8 @@ use App\Services\Captcha;
 use App\Services\Password;
 use App\Utils\Hash;
 use App\Utils\ResponseHelper;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 /*
  * Class Password

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

@@ -8,8 +8,8 @@ use App\Controllers\BaseController;
 use App\Models\DetectLog;
 use App\Models\DetectRule;
 use App\Utils\Tools;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class DetectController extends BaseController
 {

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

@@ -8,8 +8,8 @@ use App\Controllers\BaseController;
 use App\Models\Node;
 use App\Utils\Tools;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 /**
  *  User ServerController

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

@@ -11,8 +11,8 @@ use App\Models\Payback;
 use App\Models\Setting;
 use App\Models\Shop;
 use App\Utils\ResponseHelper;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class ShopController extends BaseController
 {

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

@@ -9,8 +9,8 @@ use App\Models\Ticket;
 use App\Models\User;
 use App\Utils\Tools;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 use voku\helper\AntiXSS;
 
 /**

+ 1 - 2
src/Controllers/UserController.php

@@ -34,8 +34,8 @@ use App\Utils\TelegramSessionManager;
 use App\Utils\Tools;
 use App\Utils\URL;
 use Ramsey\Uuid\Uuid;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 use voku\helper\AntiXSS;
 
 /**
@@ -461,7 +461,6 @@ final class UserController extends BaseController
             ->assign('methods', $methods)
             ->assign('telegram_bot', $_ENV['telegram_bot'])
             ->registerClass('Config', Config::class)
-            ->registerClass('URL', URL::class)
             ->fetch('user/edit.tpl'));
     }
 

+ 1 - 1
src/Controllers/WebAPI/FuncController.php

@@ -8,8 +8,8 @@ use App\Controllers\BaseController;
 use App\Models\DetectRule;
 use App\Utils\ResponseHelper;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class FuncController extends BaseController
 {

+ 1 - 1
src/Controllers/WebAPI/NodeController.php

@@ -9,8 +9,8 @@ use App\Models\Node;
 use App\Models\StreamMedia;
 use App\Utils\ResponseHelper;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class NodeController extends BaseController
 {

+ 1 - 1
src/Controllers/WebAPI/UserController.php

@@ -14,8 +14,8 @@ use App\Utils\ResponseHelper;
 use App\Utils\Tools;
 use Illuminate\Database\Eloquent\Builder;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class UserController extends BaseController
 {

+ 12 - 0
src/Interfaces/MigrationInterface.php

@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Interfaces;
+
+interface MigrationInterface
+{
+    public function up(): void;
+
+    public function down(): void;
+}

+ 1 - 1
src/Middleware/Admin.php

@@ -5,8 +5,8 @@ declare(strict_types=1);
 namespace App\Middleware;
 
 use App\Services\Auth as AuthService;
-use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Server\MiddlewareInterface;
 use Psr\Http\Server\RequestHandlerInterface;
 use Slim\Factory\AppFactory;

+ 0 - 3
src/Middleware/Auth.php

@@ -15,9 +15,6 @@ use function in_array;
 
 final class Auth implements MiddlewareInterface
 {
-    /**
-     * @inheritdoc
-     */
     public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
     {
         $user = AuthService::getUser();

+ 1 - 4
src/Middleware/AuthorizationBearer.php

@@ -21,9 +21,6 @@ final class AuthorizationBearer implements MiddlewareInterface
         $this->token = $token;
     }
 
-    /**
-     * @inheritdoc
-     */
     public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
     {
         // The OAuth 2.0 Authorization Framework: Bearer Token Usage
@@ -35,7 +32,7 @@ final class AuthorizationBearer implements MiddlewareInterface
             substr($authHeader, 0, 7) !== 'Bearer ' ||
             substr($authHeader, 8) !== $this->token
         ) {
-            /** @var \Slim\Http\Response */
+            /** @var \Slim\Http\Response $response */
             $response = AppFactory::determineResponseFactory()->createResponse(401);
             return $response->withJson([
                 'ret' => 0,

+ 4 - 7
src/Middleware/ErrorHandler.php

@@ -5,9 +5,9 @@ declare(strict_types=1);
 namespace App\Middleware;
 
 use App\Services\View;
-use Psr\Http\Server\MiddlewareInterface;
-use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\MiddlewareInterface;
 use Psr\Http\Server\RequestHandlerInterface;
 use Slim\CallableResolver;
 use Slim\Exception\HttpMethodNotAllowedException;
@@ -18,9 +18,6 @@ use Throwable;
 
 final class ErrorHandler implements MiddlewareInterface
 {
-    /**
-     * @inheritdoc
-     */
     public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
     {
         try {
@@ -29,7 +26,7 @@ final class ErrorHandler implements MiddlewareInterface
             // 404 or 405 throwed by router
             $smarty = View::getSmarty();
             $code = $e->getCode();
-            $response->getBody()->write($smarty->fetch("$code.tpl"));
+            $response->getBody()->write($smarty->fetch("{$code}.tpl"));
             $response = $response->withStatus($code);
         } catch (Throwable $e) {
             $response_factory = AppFactory::determineResponseFactory();
@@ -40,7 +37,7 @@ final class ErrorHandler implements MiddlewareInterface
             } else {
                 $response = $response_factory->createResponse(500);
                 $smarty = View::getSmarty();
-                $response->getBody()->write($smarty->fetch("500.tpl"));
+                $response->getBody()->write($smarty->fetch('500.tpl'));
             }
         }
         return $response;

+ 0 - 3
src/Middleware/Guest.php

@@ -13,9 +13,6 @@ use Slim\Factory\AppFactory;
 
 final class Guest implements MiddlewareInterface
 {
-    /**
-     * @inheritdoc
-     */
     public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
     {
         $user = AuthService::getUser();

+ 6 - 8
src/Middleware/NodeToken.php

@@ -9,18 +9,16 @@ use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Server\MiddlewareInterface;
 use Psr\Http\Server\RequestHandlerInterface;
+use Slim\Factory\AppFactory;
 
 final class NodeToken implements MiddlewareInterface
 {
-    /**
-     * @inheritdoc
-     */
     public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
     {
         $key = $request->getQueryParams()['key'] ?? null;
         if ($key === null) {
             // 未提供 key
-            return $response->withJson([
+            return AppFactory::determineResponseFactory()->createResponse(401)->withJson([
                 'ret' => 0,
                 'data' => 'Invalid request.',
             ]);
@@ -28,7 +26,7 @@ final class NodeToken implements MiddlewareInterface
 
         if ($key !== $_ENV['muKey']) {
             // key 不存在
-            return $response->withJson([
+            return AppFactory::determineResponseFactory()->createResponse(401)->withJson([
                 'ret' => 0,
                 'data' => 'Invalid request.',
             ]);
@@ -36,7 +34,7 @@ final class NodeToken implements MiddlewareInterface
 
         if ($_ENV['WebAPI'] === false) {
             // 主站不提供 WebAPI
-            return $response->withJson([
+            return AppFactory::determineResponseFactory()->createResponse(401)->withJson([
                 'ret' => 0,
                 'data' => 'Invalid request.',
             ]);
@@ -46,7 +44,7 @@ final class NodeToken implements MiddlewareInterface
             $ip = $request->getServerParam('REMOTE_ADDR');
             if ($ip !== '127.0.0.1') {
                 if (! Node::where('node_ip', 'LIKE', "${ip}%")->exists()) {
-                    return $response->withJson([
+                    return AppFactory::determineResponseFactory()->createResponse(401)->withJson([
                         'ret' => 0,
                         'data' => 'Invalid request IP.',
                     ]);
@@ -54,6 +52,6 @@ final class NodeToken implements MiddlewareInterface
             }
         }
 
-        return $next($request, $response);
+        return $handler->handle($request);
     }
 }

+ 2 - 2
src/Models/Model.php

@@ -53,11 +53,11 @@ abstract class Model extends EloquentModel
         if ($search) {
             $query->where(
                 static function ($query) use ($search): void {
-                    $query->where('id', 'LIKE binary', "%${search}%");
+                    $query->where('id', 'LIKE binary', "%{$search}%");
                     $attributes = Capsule::schema()->getColumnListing(self::getTableName());
                     foreach ($attributes as $s) {
                         if ($s !== 'id') {
-                            $query->orwhere($s, 'LIKE binary', "%${search}%");
+                            $query->orwhere($s, 'LIKE binary', "%{$search}%");
                         }
                     }
                 }

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

@@ -11,8 +11,8 @@ use App\Models\Setting;
 use App\Models\User;
 use Psr\Http\Message\ResponseInterface;
 use Ramsey\Uuid\Uuid;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 abstract class AbstractPayment
 {

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

@@ -13,8 +13,8 @@ use Omnipay\Alipay\AbstractAopGateway;
 use Omnipay\Alipay\AopF2FGateway;
 use Omnipay\Omnipay;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class AopF2F extends AbstractPayment
 {

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

@@ -17,8 +17,8 @@ use App\Services\Gateway\Epay\EpayNotify;
 use App\Services\Gateway\Epay\EpaySubmit;
 use App\Services\View;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class Epay extends AbstractPayment
 {

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

@@ -9,8 +9,8 @@ use App\Models\Setting;
 use App\Services\Auth;
 use App\Services\View;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class PAYJS extends AbstractPayment
 {

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

@@ -13,8 +13,8 @@ use Paymentwall_Config;
 use Paymentwall_Pingback;
 use Paymentwall_Widget;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class PaymentWall extends AbstractPayment
 {

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

@@ -9,8 +9,8 @@ use App\Models\Setting;
 use App\Services\Auth;
 use App\Services\View;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class StripeCard extends AbstractPayment
 {

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

@@ -10,8 +10,8 @@ use App\Services\Auth;
 use App\Services\View;
 use Exception;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class THeadPay extends AbstractPayment
 {

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

@@ -8,8 +8,8 @@ use App\Models\Paylist;
 use App\Models\Setting;
 use App\Services\Auth;
 use Psr\Http\Message\ResponseInterface;
-use Slim\Http\ServerRequest;
 use Slim\Http\Response;
+use Slim\Http\ServerRequest;
 
 final class Vmqpay extends AbstractPayment
 {

+ 42 - 3
update.sh

@@ -1,18 +1,57 @@
 #!/usr/bin/bash
 
+cat << "EOF"
+SSPanel-UIM update script
+Author: M1Screw
+Github: https://github.com/sspanel-uim/SSPanel-Uim-Dev
+Usage: 
+./update.sh dev --> Upgrade to the latest development version
+./update.sh release release_version db_version --> Upgrade to the specified release version
+EOF
+
 [ $(id -u) != "0" ] && { echo "Error: You must be root to run this script!"; exit 1; }
 
-do_update_sspanel(){
+do_update_sspanel_dev(){
     git pull
     git reset --hard origin/dev
     git fetch --prune --prune-tags
     rm -r storage/framework/smarty/compile/*
     php composer.phar update
     php composer.phar selfupdate
-    php vendor/bin/phinx migrate
+    php xcat Migration latest
     php xcat Update
     php xcat Tool importAllSettings
     wget https://cdn.jsdelivr.net/gh/sspanel-uim/qqwry.dat@latest/qqwry.dat -O storage/qqwry.dat 
 }
 
-do_update_sspanel
+do_update_sspanel_release(){
+    tag=$1
+    db_version=$2
+    git pull
+    git reset --hard $tag
+    rm -r storage/framework/smarty/compile/*
+    php composer.phar update
+    php composer.phar selfupdate
+    php xcat Migration $db_version
+    php xcat Update
+    php xcat Tool importAllSettings
+    wget https://cdn.jsdelivr.net/gh/sspanel-uim/qqwry.dat@latest/qqwry.dat -O storage/qqwry.dat 
+}
+
+if [[ $1 == "dev" ]]; then
+    do_update_sspanel_dev
+    exit 0
+fi
+
+if [[ $1 == "release" ]]; then
+    if [[ $2 == "" ]]; then
+        echo "Error: The release version cannot be empty!"
+        exit 1
+    fi
+    if [[ $3 == "" ]]; then
+        echo "Error: The database version cannot be empty!"
+        exit 1
+    fi
+    do_update_sspanel_release $2 $3
+    exit 0
+fi