Browse Source

Merge pull request #484 from v2board/dev

1.5.3
tokumeikoi 3 years ago
parent
commit
32c539d2c0
100 changed files with 1208 additions and 750 deletions
  1. 53 13
      app/Console/Commands/CheckCommission.php
  2. 3 14
      app/Console/Commands/CheckOrder.php
  3. 37 16
      app/Console/Commands/ResetTraffic.php
  4. 3 16
      app/Console/Commands/SendRemindMail.php
  5. 3 0
      app/Console/Commands/Test.php
  6. 4 5
      app/Console/Commands/V2boardInstall.php
  7. 4 4
      app/Console/Commands/V2boardStatistics.php
  8. 2 1
      app/Console/Commands/V2boardUpdate.php
  9. 3 1
      app/Console/Kernel.php
  10. 11 11
      app/Exceptions/Handler.php
  11. 6 5
      app/Http/Controllers/Admin/ConfigController.php
  12. 2 37
      app/Http/Controllers/Admin/CouponController.php
  13. 38 27
      app/Http/Controllers/Admin/OrderController.php
  14. 4 1
      app/Http/Controllers/Admin/PaymentController.php
  15. 3 4
      app/Http/Controllers/Admin/Server/GroupController.php
  16. 2 2
      app/Http/Controllers/Admin/Server/ManageController.php
  17. 0 5
      app/Http/Controllers/Admin/Server/ShadowsocksController.php
  18. 0 5
      app/Http/Controllers/Admin/Server/TrojanController.php
  19. 7 35
      app/Http/Controllers/Admin/Server/V2rayController.php
  20. 2 2
      app/Http/Controllers/Admin/StatController.php
  21. 1 1
      app/Http/Controllers/Admin/UserController.php
  22. 1 1
      app/Http/Controllers/Client/AppController.php
  23. 10 4
      app/Http/Controllers/Client/Protocols/AnXray.php
  24. 11 3
      app/Http/Controllers/Client/Protocols/Clash.php
  25. 4 4
      app/Http/Controllers/Client/Protocols/Passwall.php
  26. 2 2
      app/Http/Controllers/Client/Protocols/QuantumultX.php
  27. 4 4
      app/Http/Controllers/Client/Protocols/SSRPlus.php
  28. 3 3
      app/Http/Controllers/Client/Protocols/Shadowrocket.php
  29. 32 4
      app/Http/Controllers/Client/Protocols/Surfboard.php
  30. 4 2
      app/Http/Controllers/Client/Protocols/Surge.php
  31. 4 4
      app/Http/Controllers/Client/Protocols/V2rayN.php
  32. 4 4
      app/Http/Controllers/Client/Protocols/V2rayNG.php
  33. 1 1
      app/Http/Controllers/Guest/PaymentController.php
  34. 8 1
      app/Http/Controllers/Passport/AuthController.php
  35. 8 20
      app/Http/Controllers/Server/DeepbworkController.php
  36. 0 158
      app/Http/Controllers/Server/PoseidonController.php
  37. 5 21
      app/Http/Controllers/Server/ShadowsocksTidalabController.php
  38. 5 17
      app/Http/Controllers/Server/TrojanTidalabController.php
  39. 1 2
      app/Http/Controllers/User/CommController.php
  40. 6 20
      app/Http/Controllers/User/CouponController.php
  41. 0 2
      app/Http/Controllers/User/KnowledgeController.php
  42. 3 4
      app/Http/Controllers/User/OrderController.php
  43. 5 5
      app/Http/Controllers/User/ServerController.php
  44. 1 1
      app/Http/Controllers/User/TicketController.php
  45. 28 7
      app/Http/Controllers/User/UserController.php
  46. 1 0
      app/Http/Kernel.php
  47. 3 2
      app/Http/Middleware/User.php
  48. 6 5
      app/Http/Requests/Admin/ConfigSave.php
  49. 3 1
      app/Http/Requests/Admin/CouponGenerate.php
  50. 0 44
      app/Http/Requests/Admin/CouponSave.php
  51. 5 2
      app/Http/Requests/Admin/PlanSave.php
  52. 9 5
      app/Http/Requests/Admin/ServerV2raySave.php
  53. 2 1
      app/Http/Routes/AdminRoute.php
  54. 1 0
      app/Http/Routes/UserRoute.php
  55. 52 0
      app/Jobs/OrderHandleJob.php
  56. 58 0
      app/Jobs/ServerLogJob.php
  57. 57 0
      app/Jobs/TrafficFetchJob.php
  58. 16 0
      app/Models/CommissionLog.php
  59. 5 0
      app/Models/Coupon.php
  60. 4 0
      app/Models/InviteCode.php
  61. 4 0
      app/Models/Knowledge.php
  62. 4 0
      app/Models/MailLog.php
  63. 4 0
      app/Models/Notice.php
  64. 5 0
      app/Models/Order.php
  65. 5 0
      app/Models/Payment.php
  66. 4 0
      app/Models/Plan.php
  67. 0 12
      app/Models/Server.php
  68. 4 0
      app/Models/ServerGroup.php
  69. 4 0
      app/Models/ServerLog.php
  70. 6 0
      app/Models/ServerShadowsocks.php
  71. 4 0
      app/Models/ServerStat.php
  72. 6 0
      app/Models/ServerTrojan.php
  73. 22 0
      app/Models/ServerV2ray.php
  74. 4 0
      app/Models/StatOrder.php
  75. 4 0
      app/Models/StatServer.php
  76. 4 0
      app/Models/Ticket.php
  77. 4 0
      app/Models/TicketMessage.php
  78. 4 0
      app/Models/User.php
  79. 3 0
      app/Payments/MGate.php
  80. 43 0
      app/Providers/HorizonServiceProvider.php
  81. 60 20
      app/Services/CouponService.php
  82. 22 3
      app/Services/MailService.php
  83. 30 28
      app/Services/OrderService.php
  84. 1 1
      app/Services/PaymentService.php
  85. 31 49
      app/Services/ServerService.php
  86. 1 0
      app/Services/TelegramService.php
  87. 1 0
      app/Services/TicketService.php
  88. 6 28
      app/Services/UserService.php
  89. 1 1
      app/Utils/CacheKey.php
  90. 19 2
      app/Utils/Helper.php
  91. 17 11
      composer.json
  92. 2 1
      config/app.php
  93. 34 0
      config/cors.php
  94. 191 0
      config/horizon.php
  95. 53 33
      database/install.sql
  96. 37 0
      database/update.sql
  97. 1 0
      init.sh
  98. 2 2
      pm2.yaml
  99. 1 0
      public/assets/admin/theme/green.css
  100. 0 0
      public/assets/admin/umi.css

+ 53 - 13
app/Console/Commands/CheckCommission.php

@@ -2,6 +2,7 @@
 
 
 namespace App\Console\Commands;
 namespace App\Console\Commands;
 
 
+use App\Models\CommissionLog;
 use Illuminate\Console\Command;
 use Illuminate\Console\Command;
 use App\Models\Order;
 use App\Models\Order;
 use App\Models\User;
 use App\Models\User;
@@ -59,27 +60,66 @@ class CheckCommission extends Command
 
 
     public function autoPayCommission()
     public function autoPayCommission()
     {
     {
-        $order = Order::where('commission_status', 1)
+        $orders = Order::where('commission_status', 1)
             ->where('invite_user_id', '!=', NULL)
             ->where('invite_user_id', '!=', NULL)
             ->get();
             ->get();
-        foreach ($order as $item) {
-            $inviter = User::find($item->invite_user_id);
+        foreach ($orders as $order) {
+            DB::beginTransaction();
+            if (!$this->payHandle($order->invite_user_id, $order)) {
+                DB::rollBack();
+                continue;
+            }
+            $order->commission_status = 2;
+            if (!$order->save()) {
+                DB::rollBack();
+                continue;
+            }
+            DB::commit();
+        }
+    }
+
+    public function payHandle($inviteUserId, Order $order)
+    {
+        if ((int)config('v2board.commission_distribution_enable', 0)) {
+            $level = 3;
+            $commissionShareLevels = [
+                0 => (int)config('v2board.commission_distribution_l1'),
+                1 => (int)config('v2board.commission_distribution_l2'),
+                2 => (int)config('v2board.commission_distribution_l3')
+            ];
+        } else {
+            $level = 3;
+            $commissionShareLevels = [
+                0 => 100
+            ];
+        }
+        for ($l = 0; $l < $level; $l++) {
+            $inviter = User::find($inviteUserId);
             if (!$inviter) continue;
             if (!$inviter) continue;
+            if (!isset($commissionShareLevels[$l])) continue;
+            $commissionBalance = $order->commission_balance * ($commissionShareLevels[$l] / 100);
             if ((int)config('v2board.withdraw_close_enable', 0)) {
             if ((int)config('v2board.withdraw_close_enable', 0)) {
-                $inviter->balance = $inviter->balance + $item->commission_balance;
+                $inviter->balance = $inviter->balance + $commissionBalance;
             } else {
             } else {
-                $inviter->commission_balance = $inviter->commission_balance + $item->commission_balance;
+                $inviter->commission_balance = $inviter->commission_balance + $commissionBalance;
             }
             }
-            DB::beginTransaction();
-            if ($inviter->save()) {
-                $item->commission_status = 2;
-                if (!$item->save()) {
-                    DB::rollBack();
-                    continue;
-                }
-                DB::commit();
+            if (!$inviter->save()) {
+                DB::rollBack();
+                return false;
+            }
+            if (!CommissionLog::create([
+                'invite_user_id' => $inviteUserId,
+                'user_id' => $order->user_id,
+                'trade_no' => $order->trade_no,
+                'order_amount' => $order->total_amount,
+                'get_amount' => $commissionBalance
+            ])) {
+                DB::rollBack();
+                return false;
             }
             }
+            $inviteUserId = $inviter->invite_user_id;
         }
         }
+        return true;
     }
     }
 
 
 }
 }

+ 3 - 14
app/Console/Commands/CheckOrder.php

@@ -2,6 +2,7 @@
 
 
 namespace App\Console\Commands;
 namespace App\Console\Commands;
 
 
+use App\Jobs\OrderHandleJob;
 use App\Services\OrderService;
 use App\Services\OrderService;
 use Illuminate\Console\Command;
 use Illuminate\Console\Command;
 use App\Models\Order;
 use App\Models\Order;
@@ -45,20 +46,8 @@ class CheckOrder extends Command
         ini_set('memory_limit', -1);
         ini_set('memory_limit', -1);
         $orders = Order::whereIn('status', [0, 1])
         $orders = Order::whereIn('status', [0, 1])
             ->get();
             ->get();
-        foreach ($orders as $item) {
-            $orderService = new OrderService($item);
-            switch ($item->status) {
-                // cancel
-                case 0:
-                    if (strtotime($item->created_at) <= (time() - 1800)) {
-                        $orderService->cancel();
-                    }
-                    break;
-                case 1:
-                    $orderService->open();
-                    break;
-            }
-
+        foreach ($orders as $order) {
+            OrderHandleJob::dispatch($order->trade_no);
         }
         }
     }
     }
 }
 }

+ 37 - 16
app/Console/Commands/ResetTraffic.php

@@ -2,6 +2,7 @@
 
 
 namespace App\Console\Commands;
 namespace App\Console\Commands;
 
 
+use App\Models\Plan;
 use Illuminate\Console\Command;
 use Illuminate\Console\Command;
 use App\Models\User;
 use App\Models\User;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\DB;
@@ -42,24 +43,45 @@ class ResetTraffic extends Command
      */
      */
     public function handle()
     public function handle()
     {
     {
-        DB::beginTransaction();
-        $resetTrafficMethod = config('v2board.reset_traffic_method', 0);
-        switch ((int)$resetTrafficMethod) {
-            // 1 a month
-            case 0:
-                $this->resetByMonthFirstDay();
-                break;
-            // expire day
-            case 1:
-                $this->resetByExpireDay();
-                break;
+        ini_set('memory_limit', -1);
+        foreach (Plan::get() as $plan) {
+            switch ($plan->reset_traffic_method) {
+                case null: {
+                    $resetTrafficMethod = config('v2board.reset_traffic_method', 0);
+                    switch ((int)$resetTrafficMethod) {
+                        // month first day
+                        case 0:
+                            $this->resetByMonthFirstDay($this->builder);
+                            break;
+                        // expire day
+                        case 1:
+                            $this->resetByExpireDay($this->builder);
+                            break;
+                        // no action
+                        case 2:
+                            break;
+                    }
+                    break;
+                }
+                case 0: {
+                    $builder = $this->builder->where('plan_id', $plan->id);
+                    $this->resetByMonthFirstDay($builder);
+                    break;
+                }
+                case 1: {
+                    $builder = $this->builder->where('plan_id', $plan->id);
+                    $this->resetByExpireDay($builder);
+                    break;
+                }
+                case 2: {
+                    break;
+                }
+            }
         }
         }
-        DB::commit();
     }
     }
 
 
-    private function resetByMonthFirstDay():void
+    private function resetByMonthFirstDay($builder):void
     {
     {
-        $builder = $this->builder;
         if ((string)date('d') === '01') {
         if ((string)date('d') === '01') {
             $builder->update([
             $builder->update([
                 'u' => 0,
                 'u' => 0,
@@ -68,9 +90,8 @@ class ResetTraffic extends Command
         }
         }
     }
     }
 
 
-    private function resetByExpireDay():void
+    private function resetByExpireDay($builder):void
     {
     {
-        $builder = $this->builder;
         $lastDay = date('d', strtotime('last day of +0 months'));
         $lastDay = date('d', strtotime('last day of +0 months'));
         $users = [];
         $users = [];
         foreach ($builder->get() as $item) {
         foreach ($builder->get() as $item) {

+ 3 - 16
app/Console/Commands/SendRemindMail.php

@@ -2,6 +2,7 @@
 
 
 namespace App\Console\Commands;
 namespace App\Console\Commands;
 
 
+use App\Services\MailService;
 use Illuminate\Console\Command;
 use Illuminate\Console\Command;
 use App\Models\User;
 use App\Models\User;
 use App\Models\MailLog;
 use App\Models\MailLog;
@@ -41,23 +42,9 @@ class SendRemindMail extends Command
     public function handle()
     public function handle()
     {
     {
         $users = User::all();
         $users = User::all();
+        $mailService = new MailService();
         foreach ($users as $user) {
         foreach ($users as $user) {
-            if ($user->remind_expire) $this->remindExpire($user);
-        }
-    }
-
-    private function remindExpire($user)
-    {
-        if ($user->expired_at !== NULL && ($user->expired_at - 86400) < time() && $user->expired_at > time()) {
-            SendEmailJob::dispatch([
-                'email' => $user->email,
-                'subject' => '在' . config('v2board.app_name', 'V2board') . '的服务即将到期',
-                'template_name' => 'remindExpire',
-                'template_value' => [
-                    'name' => config('v2board.app_name', 'V2Board'),
-                    'url' => config('v2board.app_url')
-                ]
-            ]);
+            if ($user->remind_expire) $mailService->remindExpire($user);
         }
         }
     }
     }
 }
 }

+ 3 - 0
app/Console/Commands/Test.php

@@ -3,7 +3,10 @@
 namespace App\Console\Commands;
 namespace App\Console\Commands;
 
 
 use App\Models\Order;
 use App\Models\Order;
+use App\Models\User;
+use App\Utils\Helper;
 use Illuminate\Console\Command;
 use Illuminate\Console\Command;
+use Matriphe\Larinfo;
 
 
 class Test extends Command
 class Test extends Command
 {
 {

+ 4 - 5
app/Console/Commands/V2boardInstall.php

@@ -47,13 +47,12 @@ class V2boardInstall extends Command
             $this->info(" \ \ / /  __) |  _ \ / _ \ / _` | '__/ _` | ");
             $this->info(" \ \ / /  __) |  _ \ / _ \ / _` | '__/ _` | ");
             $this->info("  \ V /  / __/| |_) | (_) | (_| | | | (_| | ");
             $this->info("  \ V /  / __/| |_) | (_) | (_| | | | (_| | ");
             $this->info("   \_/  |_____|____/ \___/ \__,_|_|  \__,_| ");
             $this->info("   \_/  |_____|____/ \___/ \__,_|_|  \__,_| ");
-            if (\File::exists(base_path() . '/.lock')) {
+            if (\File::exists(base_path() . '/.env')) {
                 abort(500, 'V2board 已安装,如需重新安装请删除目录下.lock文件');
                 abort(500, 'V2board 已安装,如需重新安装请删除目录下.lock文件');
             }
             }
-            if (!\File::exists(base_path() . '/.env')) {
-                if (!copy(base_path() . '/.env.example', base_path() . '/.env')) {
-                    abort(500, '复制环境文件失败,请检查目录权限');
-                }
+
+            if (!copy(base_path() . '/.env.example', base_path() . '/.env')) {
+                abort(500, '复制环境文件失败,请检查目录权限');
             }
             }
             $this->saveToEnv([
             $this->saveToEnv([
                 'APP_KEY' => 'base64:' . base64_encode(Encrypter::generateKey('AES-256-CBC')),
                 'APP_KEY' => 'base64:' . base64_encode(Encrypter::generateKey('AES-256-CBC')),

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

@@ -42,16 +42,16 @@ class V2boardStatistics extends Command
      */
      */
     public function handle()
     public function handle()
     {
     {
-         $this->statOrder();
-         $this->statServer();
+        $this->statOrder();
+        $this->statServer();
     }
     }
 
 
     private function statOrder()
     private function statOrder()
     {
     {
         $endAt = strtotime(date('Y-m-d'));
         $endAt = strtotime(date('Y-m-d'));
         $startAt = strtotime('-1 day', $endAt);
         $startAt = strtotime('-1 day', $endAt);
-        $builder = Order::where('created_at', '>=', $startAt)
-            ->where('created_at', '<', $endAt)
+        $builder = Order::where('paid_at', '>=', $startAt)
+            ->where('paid_at', '<', $endAt)
             ->whereNotIn('status', [0, 2]);
             ->whereNotIn('status', [0, 2]);
         $orderCount = $builder->count();
         $orderCount = $builder->count();
         $orderAmount = $builder->sum('total_amount');
         $orderAmount = $builder->sum('total_amount');

+ 2 - 1
app/Console/Commands/V2boardUpdate.php

@@ -51,11 +51,12 @@ class V2boardUpdate extends Command
         }
         }
         $this->info('正在导入数据库请稍等...');
         $this->info('正在导入数据库请稍等...');
         foreach ($sql as $item) {
         foreach ($sql as $item) {
+            if (!$item) continue;
             try {
             try {
                 DB::select(DB::raw($item));
                 DB::select(DB::raw($item));
             } catch (\Exception $e) {
             } catch (\Exception $e) {
             }
             }
         }
         }
-        $this->info('更新完毕');
+        $this->info('更新完毕,请重新启动队列服务。');
     }
     }
 }
 }

+ 3 - 1
app/Console/Kernel.php

@@ -31,9 +31,11 @@ class Kernel extends ConsoleKernel
         $schedule->command('check:commission')->everyMinute();
         $schedule->command('check:commission')->everyMinute();
         // reset
         // reset
         $schedule->command('reset:traffic')->daily();
         $schedule->command('reset:traffic')->daily();
-        $schedule->command('reset:serverLog')->quarterly();
+        $schedule->command('reset:serverLog')->quarterly()->at('0:15');
         // send
         // send
         $schedule->command('send:remindMail')->dailyAt('11:30');
         $schedule->command('send:remindMail')->dailyAt('11:30');
+        // horizon metrics
+        $schedule->command('horizon:snapshot')->everyFiveMinutes();
     }
     }
 
 
     /**
     /**

+ 11 - 11
app/Exceptions/Handler.php

@@ -2,8 +2,8 @@
 
 
 namespace App\Exceptions;
 namespace App\Exceptions;
 
 
-use Exception;
 use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
 use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
+use Throwable;
 
 
 class Handler extends ExceptionHandler
 class Handler extends ExceptionHandler
 {
 {
@@ -29,10 +29,12 @@ class Handler extends ExceptionHandler
     /**
     /**
      * Report or log an exception.
      * Report or log an exception.
      *
      *
-     * @param \Exception $exception
+     * @param  \Throwable  $exception
      * @return void
      * @return void
+     *
+     * @throws \Throwable
      */
      */
-    public function report(Exception $exception)
+    public function report(Throwable $exception)
     {
     {
         parent::report($exception);
         parent::report($exception);
     }
     }
@@ -40,16 +42,14 @@ class Handler extends ExceptionHandler
     /**
     /**
      * Render an exception into an HTTP response.
      * Render an exception into an HTTP response.
      *
      *
-     * @param \Illuminate\Http\Request $request
-     * @param \Exception $exception
-     * @return \Illuminate\Http\Response
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Throwable  $exception
+     * @return \Symfony\Component\HttpFoundation\Response
+     *
+     * @throws \Throwable
      */
      */
-    public function render($request, Exception $exception)
+    public function render($request, Throwable $exception)
     {
     {
-        if($exception instanceof \Illuminate\Http\Exceptions\ThrottleRequestsException) {
-            abort(429, '请求频繁,请稍后再试');
-        }
         return parent::render($request, $exception);
         return parent::render($request, $exception);
     }
     }
-
 }
 }

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

@@ -60,7 +60,11 @@ class ConfigController extends Controller
                     'commission_auto_check_enable' => config('v2board.commission_auto_check_enable', 1),
                     'commission_auto_check_enable' => config('v2board.commission_auto_check_enable', 1),
                     'commission_withdraw_limit' => config('v2board.commission_withdraw_limit', 100),
                     'commission_withdraw_limit' => config('v2board.commission_withdraw_limit', 100),
                     'commission_withdraw_method' => config('v2board.commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT),
                     'commission_withdraw_method' => config('v2board.commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT),
-                    'withdraw_close_enable' => config('v2board.withdraw_close_enable', 0)
+                    'withdraw_close_enable' => config('v2board.withdraw_close_enable', 0),
+                    'commission_distribution_enable' => config('v2board.commission_distribution_enable', 0),
+                    'commission_distribution_l1' => config('v2board.commission_distribution_l1'),
+                    'commission_distribution_l2' => config('v2board.commission_distribution_l2'),
+                    'commission_distribution_l3' => config('v2board.commission_distribution_l3')
                 ],
                 ],
                 'site' => [
                 'site' => [
                     'safe_mode_enable' => (int)config('v2board.safe_mode_enable', 0),
                     'safe_mode_enable' => (int)config('v2board.safe_mode_enable', 0),
@@ -136,9 +140,6 @@ class ConfigController extends Controller
                     'server_v2ray_domain' => config('v2board.server_v2ray_domain'),
                     'server_v2ray_domain' => config('v2board.server_v2ray_domain'),
                     'server_v2ray_protocol' => config('v2board.server_v2ray_protocol'),
                     'server_v2ray_protocol' => config('v2board.server_v2ray_protocol'),
                 ],
                 ],
-                'tutorial' => [
-                    'apple_id' => config('v2board.apple_id')
-                ],
                 'email' => [
                 'email' => [
                     'email_template' => config('v2board.email_template', 'default'),
                     'email_template' => config('v2board.email_template', 'default'),
                     'email_host' => config('v2board.email_host'),
                     'email_host' => config('v2board.email_host'),
@@ -166,7 +167,7 @@ class ConfigController extends Controller
 
 
     public function save(ConfigSave $request)
     public function save(ConfigSave $request)
     {
     {
-        $data = $request->input();
+        $data = $request->validated();
         $array = \Config::get('v2board');
         $array = \Config::get('v2board');
         foreach ($data as $k => $v) {
         foreach ($data as $k => $v) {
             if (!in_array($k, array_keys($request->validated()))) {
             if (!in_array($k, array_keys($request->validated()))) {

+ 2 - 37
app/Http/Controllers/Admin/CouponController.php

@@ -24,42 +24,12 @@ class CouponController extends Controller
         $total = $builder->count();
         $total = $builder->count();
         $coupons = $builder->forPage($current, $pageSize)
         $coupons = $builder->forPage($current, $pageSize)
             ->get();
             ->get();
-
-        foreach ($coupons as $k => $v) {
-            if ($coupons[$k]['limit_plan_ids']) $coupons[$k]['limit_plan_ids'] = json_decode($coupons[$k]['limit_plan_ids']);
-        }
         return response([
         return response([
             'data' => $coupons,
             'data' => $coupons,
             'total' => $total
             'total' => $total
         ]);
         ]);
     }
     }
 
 
-    public function save(CouponSave $request)
-    {
-        $params = $request->validated();
-        if (isset($params['limit_plan_ids'])) {
-            $params['limit_plan_ids'] = json_encode($params['limit_plan_ids']);
-        }
-        if (!$request->input('id')) {
-            if (!isset($params['code'])) {
-                $params['code'] = Helper::randomChar(8);
-            }
-            if (!Coupon::create($params)) {
-                abort(500, '创建失败');
-            }
-        } else {
-            try {
-                Coupon::find($request->input('id'))->update($params);
-            } catch (\Exception $e) {
-                abort(500, '保存失败');
-            }
-        }
-
-        return response([
-            'data' => true
-        ]);
-    }
-
     public function generate(CouponGenerate $request)
     public function generate(CouponGenerate $request)
     {
     {
         if ($request->input('generate_count')) {
         if ($request->input('generate_count')) {
@@ -68,9 +38,6 @@ class CouponController extends Controller
         }
         }
 
 
         $params = $request->validated();
         $params = $request->validated();
-        if (isset($params['limit_plan_ids'])) {
-            $params['limit_plan_ids'] = json_encode($params['limit_plan_ids']);
-        }
         if (!$request->input('id')) {
         if (!$request->input('id')) {
             if (!isset($params['code'])) {
             if (!isset($params['code'])) {
                 $params['code'] = Helper::randomChar(8);
                 $params['code'] = Helper::randomChar(8);
@@ -95,10 +62,8 @@ class CouponController extends Controller
     {
     {
         $coupons = [];
         $coupons = [];
         $coupon = $request->validated();
         $coupon = $request->validated();
-        if (isset($coupon['limit_plan_ids'])) {
-            $coupon['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']);
-        }
         $coupon['created_at'] = $coupon['updated_at'] = time();
         $coupon['created_at'] = $coupon['updated_at'] = time();
+        $coupon['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']);
         unset($coupon['generate_count']);
         unset($coupon['generate_count']);
         for ($i = 0;$i < $request->input('generate_count');$i++) {
         for ($i = 0;$i < $request->input('generate_count');$i++) {
             $coupon['code'] = Helper::randomChar(8);
             $coupon['code'] = Helper::randomChar(8);
@@ -118,7 +83,7 @@ class CouponController extends Controller
             $endTime = date('Y-m-d H:i:s', $coupon['ended_at']);
             $endTime = date('Y-m-d H:i:s', $coupon['ended_at']);
             $limitUse = $coupon['limit_use'] ?? '不限制';
             $limitUse = $coupon['limit_use'] ?? '不限制';
             $createTime = date('Y-m-d H:i:s', $coupon['created_at']);
             $createTime = date('Y-m-d H:i:s', $coupon['created_at']);
-            $limitPlanIds = $coupon['limit_plan_ids'] ?? '不限制';
+            $limitPlanIds = implode("/", json_decode($coupon['limit_plan_ids'], true)) ?? '不限制';
             $data .= "{$coupon['name']},{$type},{$value},{$startTime},{$endTime},{$limitUse},{$limitPlanIds},{$coupon['code']},{$createTime}\r\n";
             $data .= "{$coupon['name']},{$type},{$value},{$startTime},{$endTime},{$limitUse},{$limitPlanIds},{$coupon['code']},{$createTime}\r\n";
         }
         }
         echo $data;
         echo $data;

+ 38 - 27
app/Http/Controllers/Admin/OrderController.php

@@ -6,6 +6,7 @@ use App\Http\Requests\Admin\OrderAssign;
 use App\Http\Requests\Admin\OrderUpdate;
 use App\Http\Requests\Admin\OrderUpdate;
 use App\Http\Requests\Admin\OrderFetch;
 use App\Http\Requests\Admin\OrderFetch;
 use App\Services\OrderService;
 use App\Services\OrderService;
+use App\Services\UserService;
 use App\Utils\Helper;
 use App\Utils\Helper;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 use App\Http\Controllers\Controller;
 use App\Http\Controllers\Controller;
@@ -63,55 +64,60 @@ class OrderController extends Controller
         ]);
         ]);
     }
     }
 
 
-    public function update(OrderUpdate $request)
+    public function paid(Request $request)
     {
     {
-        $params = $request->only([
-            'status',
-            'commission_status'
-        ]);
-
         $order = Order::where('trade_no', $request->input('trade_no'))
         $order = Order::where('trade_no', $request->input('trade_no'))
             ->first();
             ->first();
         if (!$order) {
         if (!$order) {
             abort(500, '订单不存在');
             abort(500, '订单不存在');
         }
         }
+        if ($order->status !== 0) abort(500, '只能对待支付的订单进行操作');
 
 
-        if (isset($params['status']) && (int)$params['status'] === 2) {
-            $orderService = new OrderService($order);
-            if (!$orderService->cancel()) {
-                abort(500, '更新失败');
-            }
-            return response([
-                'data' => true
-            ]);
+        $orderService = new OrderService($order);
+        if (!$orderService->paid('manual_operation')) {
+            abort(500, '更新失败');
         }
         }
+        return response([
+            'data' => true
+        ]);
+    }
 
 
-        try {
-            $order->update($params);
-        } catch (\Exception $e) {
-            abort(500, '更新失败');
+    public function cancel(Request $request)
+    {
+        $order = Order::where('trade_no', $request->input('trade_no'))
+            ->first();
+        if (!$order) {
+            abort(500, '订单不存在');
         }
         }
+        if ($order->status !== 0) abort(500, '只能对待支付的订单进行操作');
 
 
+        $orderService = new OrderService($order);
+        if (!$orderService->cancel()) {
+            abort(500, '更新失败');
+        }
         return response([
         return response([
             'data' => true
             'data' => true
         ]);
         ]);
     }
     }
 
 
-    public function repair(Request $request)
+    public function update(OrderUpdate $request)
     {
     {
-        if (empty($request->input('trade_no'))) {
-            abort(500, '参数错误');
-        }
+        $params = $request->only([
+            'commission_status'
+        ]);
+
         $order = Order::where('trade_no', $request->input('trade_no'))
         $order = Order::where('trade_no', $request->input('trade_no'))
-            ->where('status', 0)
             ->first();
             ->first();
         if (!$order) {
         if (!$order) {
-            abort(500, '订单不存在或订单已支付');
+            abort(500, '订单不存在');
         }
         }
-        $order->status = 1;
-        if (!$order->save()) {
-            abort(500, '保存失败');
+
+        try {
+            $order->update($params);
+        } catch (\Exception $e) {
+            abort(500, '更新失败');
         }
         }
+
         return response([
         return response([
             'data' => true
             'data' => true
         ]);
         ]);
@@ -130,6 +136,11 @@ class OrderController extends Controller
             abort(500, '该订阅不存在');
             abort(500, '该订阅不存在');
         }
         }
 
 
+        $userService = new UserService();
+        if ($userService->isNotCompleteOrderByUserId($user->id)) {
+            abort(500, '该用户还有待支付的订单,无法分配');
+        }
+
         DB::beginTransaction();
         DB::beginTransaction();
         $order = new Order();
         $order = new Order();
         $orderService = new OrderService($order);
         $orderService = new OrderService($order);

+ 4 - 1
app/Http/Controllers/Admin/PaymentController.php

@@ -42,6 +42,9 @@ class PaymentController extends Controller
 
 
     public function save(Request $request)
     public function save(Request $request)
     {
     {
+        if (!config('v2board.app_url')) {
+            abort(500, '请在站点配置中配置站点地址');
+        }
         if ($request->input('id')) {
         if ($request->input('id')) {
             $payment = Payment::find($request->input('id'));
             $payment = Payment::find($request->input('id'));
             if (!$payment) abort(500, '支付方式不存在');
             if (!$payment) abort(500, '支付方式不存在');
@@ -58,7 +61,7 @@ class PaymentController extends Controller
             'name' => $request->input('name'),
             'name' => $request->input('name'),
             'payment' => $request->input('payment'),
             'payment' => $request->input('payment'),
             'config' => $request->input('config'),
             'config' => $request->input('config'),
-            'uuid' => Helper::guid()
+            'uuid' => Helper::randomChar(8)
         ])) {
         ])) {
             abort(500, '保存失败');
             abort(500, '保存失败');
         }
         }

+ 3 - 4
app/Http/Controllers/Admin/Server/GroupController.php

@@ -3,7 +3,7 @@
 namespace App\Http\Controllers\Admin\Server;
 namespace App\Http\Controllers\Admin\Server;
 
 
 use App\Models\Plan;
 use App\Models\Plan;
-use App\Models\Server;
+use App\Models\ServerV2ray;
 use App\Models\ServerGroup;
 use App\Models\ServerGroup;
 use App\Models\User;
 use App\Models\User;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
@@ -50,10 +50,9 @@ class GroupController extends Controller
             }
             }
         }
         }
 
 
-        $servers = Server::all();
+        $servers = ServerV2ray::all();
         foreach ($servers as $server) {
         foreach ($servers as $server) {
-            $groupId = json_decode($server->group_id);
-            if (in_array($request->input('id'), $groupId)) {
+            if (in_array($request->input('id'), $server->group_id)) {
                 abort(500, '该组已被节点所使用,无法删除');
                 abort(500, '该组已被节点所使用,无法删除');
             }
             }
         }
         }

+ 2 - 2
app/Http/Controllers/Admin/Server/ManageController.php

@@ -2,7 +2,7 @@
 
 
 namespace App\Http\Controllers\Admin\Server;
 namespace App\Http\Controllers\Admin\Server;
 
 
-use App\Models\Server;
+use App\Models\ServerV2ray;
 use App\Models\ServerShadowsocks;
 use App\Models\ServerShadowsocks;
 use App\Models\ServerTrojan;
 use App\Models\ServerTrojan;
 use App\Services\ServerService;
 use App\Services\ServerService;
@@ -32,7 +32,7 @@ class ManageController extends Controller
                     }
                     }
                     break;
                     break;
                 case 'v2ray':
                 case 'v2ray':
-                    if (!Server::find($v['value'])->update(['sort' => $k + 1])) {
+                    if (!ServerV2ray::find($v['value'])->update(['sort' => $k + 1])) {
                         DB::rollBack();
                         DB::rollBack();
                         abort(500, '保存失败');
                         abort(500, '保存失败');
                     }
                     }

+ 0 - 5
app/Http/Controllers/Admin/Server/ShadowsocksController.php

@@ -14,11 +14,6 @@ class ShadowsocksController extends Controller
     public function save(ServerShadowsocksSave $request)
     public function save(ServerShadowsocksSave $request)
     {
     {
         $params = $request->validated();
         $params = $request->validated();
-        $params['group_id'] = json_encode($params['group_id']);
-        if (isset($params['tags'])) {
-            $params['tags'] = json_encode($params['tags']);
-        }
-
         if ($request->input('id')) {
         if ($request->input('id')) {
             $server = ServerShadowsocks::find($request->input('id'));
             $server = ServerShadowsocks::find($request->input('id'));
             if (!$server) {
             if (!$server) {

+ 0 - 5
app/Http/Controllers/Admin/Server/TrojanController.php

@@ -14,11 +14,6 @@ class TrojanController extends Controller
     public function save(ServerTrojanSave $request)
     public function save(ServerTrojanSave $request)
     {
     {
         $params = $request->validated();
         $params = $request->validated();
-        $params['group_id'] = json_encode($params['group_id']);
-        if (isset($params['tags'])) {
-            $params['tags'] = json_encode($params['tags']);
-        }
-
         if ($request->input('id')) {
         if ($request->input('id')) {
             $server = ServerTrojan::find($request->input('id'));
             $server = ServerTrojan::find($request->input('id'));
             if (!$server) {
             if (!$server) {

+ 7 - 35
app/Http/Controllers/Admin/Server/V2rayController.php

@@ -7,44 +7,16 @@ use App\Http\Requests\Admin\ServerV2rayUpdate;
 use App\Services\ServerService;
 use App\Services\ServerService;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 use App\Http\Controllers\Controller;
 use App\Http\Controllers\Controller;
-use App\Models\Server;
+use App\Models\ServerV2ray;
 
 
 class V2rayController extends Controller
 class V2rayController extends Controller
 {
 {
     public function save(ServerV2raySave $request)
     public function save(ServerV2raySave $request)
     {
     {
         $params = $request->validated();
         $params = $request->validated();
-        $params['group_id'] = json_encode($params['group_id']);
-        if (isset($params['tags'])) {
-            $params['tags'] = json_encode($params['tags']);
-        }
-
-        if (isset($params['dnsSettings'])) {
-            if (!is_object(json_decode($params['dnsSettings']))) {
-                abort(500, 'DNS规则配置格式不正确');
-            }
-        }
-
-        if (isset($params['ruleSettings'])) {
-            if (!is_object(json_decode($params['ruleSettings']))) {
-                abort(500, '审计规则配置格式不正确');
-            }
-        }
-
-        if (isset($params['networkSettings'])) {
-            if (!is_object(json_decode($params['networkSettings']))) {
-                abort(500, '传输协议配置格式不正确');
-            }
-        }
-
-        if (isset($params['tlsSettings'])) {
-            if (!is_object(json_decode($params['tlsSettings']))) {
-                abort(500, 'TLS配置格式不正确');
-            }
-        }
 
 
         if ($request->input('id')) {
         if ($request->input('id')) {
-            $server = Server::find($request->input('id'));
+            $server = ServerV2ray::find($request->input('id'));
             if (!$server) {
             if (!$server) {
                 abort(500, '服务器不存在');
                 abort(500, '服务器不存在');
             }
             }
@@ -58,7 +30,7 @@ class V2rayController extends Controller
             ]);
             ]);
         }
         }
 
 
-        if (!Server::create($params)) {
+        if (!ServerV2ray::create($params)) {
             abort(500, '创建失败');
             abort(500, '创建失败');
         }
         }
 
 
@@ -70,7 +42,7 @@ class V2rayController extends Controller
     public function drop(Request $request)
     public function drop(Request $request)
     {
     {
         if ($request->input('id')) {
         if ($request->input('id')) {
-            $server = Server::find($request->input('id'));
+            $server = ServerV2ray::find($request->input('id'));
             if (!$server) {
             if (!$server) {
                 abort(500, '节点ID不存在');
                 abort(500, '节点ID不存在');
             }
             }
@@ -86,7 +58,7 @@ class V2rayController extends Controller
             'show',
             'show',
         ]);
         ]);
 
 
-        $server = Server::find($request->input('id'));
+        $server = ServerV2ray::find($request->input('id'));
 
 
         if (!$server) {
         if (!$server) {
             abort(500, '该服务器不存在');
             abort(500, '该服务器不存在');
@@ -104,12 +76,12 @@ class V2rayController extends Controller
 
 
     public function copy(Request $request)
     public function copy(Request $request)
     {
     {
-        $server = Server::find($request->input('id'));
+        $server = ServerV2ray::find($request->input('id'));
         $server->show = 0;
         $server->show = 0;
         if (!$server) {
         if (!$server) {
             abort(500, '服务器不存在');
             abort(500, '服务器不存在');
         }
         }
-        if (!Server::create($server->toArray())) {
+        if (!ServerV2ray::create($server->toArray())) {
             abort(500, '复制失败');
             abort(500, '复制失败');
         }
         }
 
 

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

@@ -8,7 +8,7 @@ use App\Services\ServerService;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 use App\Http\Controllers\Controller;
 use App\Http\Controllers\Controller;
 use App\Models\ServerGroup;
 use App\Models\ServerGroup;
-use App\Models\Server;
+use App\Models\ServerV2ray;
 use App\Models\Plan;
 use App\Models\Plan;
 use App\Models\User;
 use App\Models\User;
 use App\Models\Ticket;
 use App\Models\Ticket;
@@ -91,7 +91,7 @@ class StatController extends Controller
     {
     {
         $servers = [
         $servers = [
             'shadowsocks' => ServerShadowsocks::where('parent_id', null)->get()->toArray(),
             'shadowsocks' => ServerShadowsocks::where('parent_id', null)->get()->toArray(),
-            'vmess' => Server::where('parent_id', null)->get()->toArray(),
+            'vmess' => ServerV2ray::where('parent_id', null)->get()->toArray(),
             'trojan' => ServerTrojan::where('parent_id', null)->get()->toArray()
             'trojan' => ServerTrojan::where('parent_id', null)->get()->toArray()
         ];
         ];
         $timestamp = strtotime('-1 day', strtotime(date('Y-m-d')));
         $timestamp = strtotime('-1 day', strtotime(date('Y-m-d')));

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

@@ -68,7 +68,7 @@ class UserController extends Controller
                     $res[$i]['plan_name'] = $plan[$k]['name'];
                     $res[$i]['plan_name'] = $plan[$k]['name'];
                 }
                 }
             }
             }
-            $res[$i]['subscribe_url'] = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $res[$i]['token'];
+            $res[$i]['subscribe_url'] = Helper::getSubscribeHost() . '/api/v1/client/subscribe?token=' . $res[$i]['token'];
         }
         }
         return response([
         return response([
             'data' => $res,
             'data' => $res,

+ 1 - 1
app/Http/Controllers/Client/AppController.php

@@ -7,7 +7,7 @@ use App\Services\ServerService;
 use App\Services\UserService;
 use App\Services\UserService;
 use App\Utils\Clash;
 use App\Utils\Clash;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
-use App\Models\Server;
+use App\Models\ServerV2ray;
 use Illuminate\Support\Facades\File;
 use Illuminate\Support\Facades\File;
 use Symfony\Component\Yaml\Yaml;
 use Symfony\Component\Yaml\Yaml;
 
 

+ 10 - 4
app/Http/Controllers/Client/Protocols/AnXray.php

@@ -4,7 +4,7 @@ namespace App\Http\Controllers\Client\Protocols;
 
 
 class AnXray
 class AnXray
 {
 {
-    public $flag = 'anxray';
+    public $flag = 'axxray';
     private $servers;
     private $servers;
     private $user;
     private $user;
 
 
@@ -64,15 +64,21 @@ class AnXray
             "encryption" => "none",
             "encryption" => "none",
             "type" => urlencode($server['network']),
             "type" => urlencode($server['network']),
             "security" => $server['tls'] ? "tls" : "",
             "security" => $server['tls'] ? "tls" : "",
-            "sni" => $server['tls'] ? urlencode(json_decode($server['tlsSettings'], true)['serverName']) : ""
         ];
         ];
+        if ($server['tls']) {
+            if ($server['tlsSettings']) {
+                $tlsSettings = $server['tlsSettings'];
+                if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
+                    $config['sni'] = urlencode($tlsSettings['serverName']);
+            }
+        }
         if ((string)$server['network'] === 'ws') {
         if ((string)$server['network'] === 'ws') {
-            $wsSettings = json_decode($server['networkSettings'], true);
+            $wsSettings = $server['networkSettings'];
             if (isset($wsSettings['path'])) $config['path'] = urlencode($wsSettings['path']);
             if (isset($wsSettings['path'])) $config['path'] = urlencode($wsSettings['path']);
             if (isset($wsSettings['headers']['Host'])) $config['host'] = urlencode($wsSettings['headers']['Host']);
             if (isset($wsSettings['headers']['Host'])) $config['host'] = urlencode($wsSettings['headers']['Host']);
         }
         }
         if ((string)$server['network'] === 'grpc') {
         if ((string)$server['network'] === 'grpc') {
-            $grpcSettings = json_decode($server['networkSettings'], true);
+            $grpcSettings = $server['networkSettings'];
             if (isset($grpcSettings['serviceName'])) $config['serviceName'] = urlencode($grpcSettings['serviceName']);
             if (isset($grpcSettings['serviceName'])) $config['serviceName'] = urlencode($grpcSettings['serviceName']);
         }
         }
         return "vmess://" . $uuid . "@" . $server['host'] . ":" . $server['port'] . "?" . http_build_query($config) . "#" . urlencode($server['name']) . "\r\n";
         return "vmess://" . $uuid . "@" . $server['host'] . ":" . $server['port'] . "?" . http_build_query($config) . "#" . urlencode($server['name']) . "\r\n";

+ 11 - 3
app/Http/Controllers/Client/Protocols/Clash.php

@@ -20,7 +20,10 @@ class Clash
     {
     {
         $servers = $this->servers;
         $servers = $this->servers;
         $user = $this->user;
         $user = $this->user;
+        $appName = config('v2board.app_name', 'V2Board');
         header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
         header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
+        header('profile-update-interval: 24');
+        header("content-disposition: filename={$appName}");
         $defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
         $defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
         $customConfig = base_path() . '/resources/rules/custom.clash.yaml';
         $customConfig = base_path() . '/resources/rules/custom.clash.yaml';
         if (\File::exists($customConfig)) {
         if (\File::exists($customConfig)) {
@@ -51,6 +54,11 @@ class Clash
             if (!is_array($config['proxy-groups'][$k]['proxies'])) continue;
             if (!is_array($config['proxy-groups'][$k]['proxies'])) continue;
             $config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
             $config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
         }
         }
+        // Force the current subscription domain to be a direct rule
+        $subsDomain = $_SERVER['SERVER_NAME'];
+        $subsDomainRule = "DOMAIN,{$subsDomain},DIRECT";
+        array_unshift($config['rules'], $subsDomainRule);
+
         $yaml = Yaml::dump($config);
         $yaml = Yaml::dump($config);
         $yaml = str_replace('$app_name', config('v2board.app_name', 'V2Board'), $yaml);
         $yaml = str_replace('$app_name', config('v2board.app_name', 'V2Board'), $yaml);
         return $yaml;
         return $yaml;
@@ -84,7 +92,7 @@ class Clash
         if ($server['tls']) {
         if ($server['tls']) {
             $array['tls'] = true;
             $array['tls'] = true;
             if ($server['tlsSettings']) {
             if ($server['tlsSettings']) {
-                $tlsSettings = json_decode($server['tlsSettings'], true);
+                $tlsSettings = $server['tlsSettings'];
                 if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
                 if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
                     $array['skip-cert-verify'] = ($tlsSettings['allowInsecure'] ? true : false);
                     $array['skip-cert-verify'] = ($tlsSettings['allowInsecure'] ? true : false);
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
@@ -94,7 +102,7 @@ class Clash
         if ($server['network'] === 'ws') {
         if ($server['network'] === 'ws') {
             $array['network'] = 'ws';
             $array['network'] = 'ws';
             if ($server['networkSettings']) {
             if ($server['networkSettings']) {
-                $wsSettings = json_decode($server['networkSettings'], true);
+                $wsSettings = $server['networkSettings'];
                 if (isset($wsSettings['path']) && !empty($wsSettings['path']))
                 if (isset($wsSettings['path']) && !empty($wsSettings['path']))
                     $array['ws-path'] = $wsSettings['path'];
                     $array['ws-path'] = $wsSettings['path'];
                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
@@ -104,7 +112,7 @@ class Clash
         if ($server['network'] === 'grpc') {
         if ($server['network'] === 'grpc') {
             $array['network'] = 'grpc';
             $array['network'] = 'grpc';
             if ($server['networkSettings']) {
             if ($server['networkSettings']) {
-                $grpcObject = json_decode($server['networkSettings'], true);
+                $grpcObject = $server['networkSettings'];
                 $array['grpc-opts'] = [];
                 $array['grpc-opts'] = [];
                 $array['grpc-opts']['grpc-service-name'] = $grpcObject['serviceName'];
                 $array['grpc-opts']['grpc-service-name'] = $grpcObject['serviceName'];
             }
             }

+ 4 - 4
app/Http/Controllers/Client/Protocols/Passwall.php

@@ -63,19 +63,19 @@ class Passwall
         ];
         ];
         if ($server['tls']) {
         if ($server['tls']) {
             if ($server['tlsSettings']) {
             if ($server['tlsSettings']) {
-                $tlsSettings = json_decode($server['tlsSettings'], true);
+                $tlsSettings = $server['tlsSettings'];
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
                     $config['sni'] = $tlsSettings['serverName'];
                     $config['sni'] = $tlsSettings['serverName'];
             }
             }
         }
         }
         if ((string)$server['network'] === 'ws') {
         if ((string)$server['network'] === 'ws') {
-            $wsSettings = json_decode($server['networkSettings'], true);
+            $wsSettings = $server['networkSettings'];
             if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
             if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
             if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
             if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
         }
         }
         if ((string)$server['network'] === 'grpc') {
         if ((string)$server['network'] === 'grpc') {
-            $grpcSettings = json_decode($server['networkSettings'], true);
-            if (isset($grpcSettings['path'])) $config['path'] = $grpcSettings['serviceName'];
+            $grpcSettings = $server['networkSettings'];
+            if (isset($grpcSettings['serviceName'])) $config['path'] = $grpcSettings['serviceName'];
         }
         }
         return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
         return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
     }
     }

+ 2 - 2
app/Http/Controllers/Client/Protocols/QuantumultX.php

@@ -66,7 +66,7 @@ class QuantumultX
             if ($server['network'] === 'tcp')
             if ($server['network'] === 'tcp')
                 array_push($config, 'obfs=over-tls');
                 array_push($config, 'obfs=over-tls');
             if ($server['tlsSettings']) {
             if ($server['tlsSettings']) {
-                $tlsSettings = json_decode($server['tlsSettings'], true);
+                $tlsSettings = $server['tlsSettings'];
                 if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
                 if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
                     array_push($config, 'tls-verification=' . ($tlsSettings['allowInsecure'] ? 'false' : 'true'));
                     array_push($config, 'tls-verification=' . ($tlsSettings['allowInsecure'] ? 'false' : 'true'));
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
@@ -79,7 +79,7 @@ class QuantumultX
             else
             else
                 array_push($config, 'obfs=ws');
                 array_push($config, 'obfs=ws');
             if ($server['networkSettings']) {
             if ($server['networkSettings']) {
-                $wsSettings = json_decode($server['networkSettings'], true);
+                $wsSettings = $server['networkSettings'];
                 if (isset($wsSettings['path']) && !empty($wsSettings['path']))
                 if (isset($wsSettings['path']) && !empty($wsSettings['path']))
                     array_push($config, "obfs-uri={$wsSettings['path']}");
                     array_push($config, "obfs-uri={$wsSettings['path']}");
                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']) && !isset($host))
                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']) && !isset($host))

+ 4 - 4
app/Http/Controllers/Client/Protocols/SSRPlus.php

@@ -63,19 +63,19 @@ class SSRPlus
         ];
         ];
         if ($server['tls']) {
         if ($server['tls']) {
             if ($server['tlsSettings']) {
             if ($server['tlsSettings']) {
-                $tlsSettings = json_decode($server['tlsSettings'], true);
+                $tlsSettings = $server['tlsSettings'];
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
                     $config['sni'] = $tlsSettings['serverName'];
                     $config['sni'] = $tlsSettings['serverName'];
             }
             }
         }
         }
         if ((string)$server['network'] === 'ws') {
         if ((string)$server['network'] === 'ws') {
-            $wsSettings = json_decode($server['networkSettings'], true);
+            $wsSettings = $server['networkSettings'];
             if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
             if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
             if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
             if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
         }
         }
         if ((string)$server['network'] === 'grpc') {
         if ((string)$server['network'] === 'grpc') {
-            $grpcSettings = json_decode($server['networkSettings'], true);
-            if (isset($grpcSettings['path'])) $config['path'] = $grpcSettings['serviceName'];
+            $grpcSettings = $server['networkSettings'];
+            if (isset($grpcSettings['serviceName'])) $config['path'] = $grpcSettings['serviceName'];
         }
         }
         return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
         return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
     }
     }

+ 3 - 3
app/Http/Controllers/Client/Protocols/Shadowrocket.php

@@ -63,7 +63,7 @@ class Shadowrocket
         if ($server['tls']) {
         if ($server['tls']) {
             $config['tls'] = 1;
             $config['tls'] = 1;
             if ($server['tlsSettings']) {
             if ($server['tlsSettings']) {
-                $tlsSettings = json_decode($server['tlsSettings'], true);
+                $tlsSettings = $server['tlsSettings'];
                 if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
                 if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
                     $config['allowInsecure'] = (int)$tlsSettings['allowInsecure'];
                     $config['allowInsecure'] = (int)$tlsSettings['allowInsecure'];
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
@@ -73,7 +73,7 @@ class Shadowrocket
         if ($server['network'] === 'ws') {
         if ($server['network'] === 'ws') {
             $config['obfs'] = "websocket";
             $config['obfs'] = "websocket";
             if ($server['networkSettings']) {
             if ($server['networkSettings']) {
-                $wsSettings = json_decode($server['networkSettings'], true);
+                $wsSettings = $server['networkSettings'];
                 if (isset($wsSettings['path']) && !empty($wsSettings['path']))
                 if (isset($wsSettings['path']) && !empty($wsSettings['path']))
                     $config['path'] = $wsSettings['path'];
                     $config['path'] = $wsSettings['path'];
                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
@@ -83,7 +83,7 @@ class Shadowrocket
         if ($server['network'] === 'grpc') {
         if ($server['network'] === 'grpc') {
             $config['obfs'] = "grpc";
             $config['obfs'] = "grpc";
             if ($server['networkSettings']) {
             if ($server['networkSettings']) {
-                $grpcSettings = json_decode($server['networkSettings'], true);
+                $grpcSettings = $server['networkSettings'];
                 if (isset($grpcSettings['serviceName']) && !empty($grpcSettings['serviceName']))
                 if (isset($grpcSettings['serviceName']) && !empty($grpcSettings['serviceName']))
                     $config['path'] = $grpcSettings['serviceName'];
                     $config['path'] = $grpcSettings['serviceName'];
             }
             }

+ 32 - 4
app/Http/Controllers/Client/Protocols/Surfboard.php

@@ -26,13 +26,19 @@ class Surfboard
         foreach ($servers as $item) {
         foreach ($servers as $item) {
             if ($item['type'] === 'shadowsocks') {
             if ($item['type'] === 'shadowsocks') {
                 // [Proxy]
                 // [Proxy]
-                $proxies .= Surfboard::buildShadowsocks($user['uuid'], $item);
+                $proxies .= self::buildShadowsocks($user['uuid'], $item);
                 // [Proxy Group]
                 // [Proxy Group]
                 $proxyGroup .= $item['name'] . ', ';
                 $proxyGroup .= $item['name'] . ', ';
             }
             }
             if ($item['type'] === 'v2ray') {
             if ($item['type'] === 'v2ray') {
                 // [Proxy]
                 // [Proxy]
-                $proxies .= Surfboard::buildVmess($user['uuid'], $item);
+                $proxies .= self::buildVmess($user['uuid'], $item);
+                // [Proxy Group]
+                $proxyGroup .= $item['name'] . ', ';
+            }
+            if ($item['type'] === 'trojan') {
+                // [Proxy]
+                $proxies .= self::buildTrojan($user['uuid'], $item);
                 // [Proxy Group]
                 // [Proxy Group]
                 $proxyGroup .= $item['name'] . ', ';
                 $proxyGroup .= $item['name'] . ', ';
             }
             }
@@ -48,8 +54,10 @@ class Surfboard
 
 
         // Subscription link
         // Subscription link
         $subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
         $subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
+        $subsDomain = $_SERVER['SERVER_NAME'];
 
 
         $config = str_replace('$subs_link', $subsURL, $config);
         $config = str_replace('$subs_link', $subsURL, $config);
+        $config = str_replace('$subs_domain', $subsDomain, $config);
         $config = str_replace('$proxies', $proxies, $config);
         $config = str_replace('$proxies', $proxies, $config);
         $config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
         $config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
         return $config;
         return $config;
@@ -88,7 +96,7 @@ class Surfboard
         if ($server['tls']) {
         if ($server['tls']) {
             array_push($config, 'tls=true');
             array_push($config, 'tls=true');
             if ($server['tlsSettings']) {
             if ($server['tlsSettings']) {
-                $tlsSettings = json_decode($server['tlsSettings'], true);
+                $tlsSettings = $server['tlsSettings'];
                 if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
                 if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
                     array_push($config, 'skip-cert-verify=' . ($tlsSettings['allowInsecure'] ? 'true' : 'false'));
                     array_push($config, 'skip-cert-verify=' . ($tlsSettings['allowInsecure'] ? 'true' : 'false'));
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
@@ -98,7 +106,7 @@ class Surfboard
         if ($server['network'] === 'ws') {
         if ($server['network'] === 'ws') {
             array_push($config, 'ws=true');
             array_push($config, 'ws=true');
             if ($server['networkSettings']) {
             if ($server['networkSettings']) {
-                $wsSettings = json_decode($server['networkSettings'], true);
+                $wsSettings = $server['networkSettings'];
                 if (isset($wsSettings['path']) && !empty($wsSettings['path']))
                 if (isset($wsSettings['path']) && !empty($wsSettings['path']))
                     array_push($config, "ws-path={$wsSettings['path']}");
                     array_push($config, "ws-path={$wsSettings['path']}");
                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
@@ -110,4 +118,24 @@ class Surfboard
         $uri .= "\r\n";
         $uri .= "\r\n";
         return $uri;
         return $uri;
     }
     }
+
+    public static function buildTrojan($password, $server)
+    {
+        $config = [
+            "{$server['name']}=trojan",
+            "{$server['host']}",
+            "{$server['port']}",
+            "password={$password}",
+            $server['server_name'] ? "sni={$server['server_name']}" : "",
+            'tfo=true',
+            'udp-relay=true'
+        ];
+        if (!empty($server['allow_insecure'])) {
+            array_push($config, $server['allow_insecure'] ? 'skip-cert-verify=true' : 'skip-cert-verify=false');
+        }
+        $config = array_filter($config);
+        $uri = implode(',', $config);
+        $uri .= "\r\n";
+        return $uri;
+    }
 }
 }

+ 4 - 2
app/Http/Controllers/Client/Protocols/Surge.php

@@ -53,8 +53,10 @@ class Surge
 
 
         // Subscription link
         // Subscription link
         $subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
         $subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
+        $subsDomain = $_SERVER['SERVER_NAME'];
 
 
         $config = str_replace('$subs_link', $subsURL, $config);
         $config = str_replace('$subs_link', $subsURL, $config);
+        $config = str_replace('$subs_domain', $subsDomain, $config);
         $config = str_replace('$proxies', $proxies, $config);
         $config = str_replace('$proxies', $proxies, $config);
         $config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
         $config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
         return $config;
         return $config;
@@ -92,7 +94,7 @@ class Surge
         if ($server['tls']) {
         if ($server['tls']) {
             array_push($config, 'tls=true');
             array_push($config, 'tls=true');
             if ($server['tlsSettings']) {
             if ($server['tlsSettings']) {
-                $tlsSettings = json_decode($server['tlsSettings'], true);
+                $tlsSettings = $server['tlsSettings'];
                 if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
                 if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
                     array_push($config, 'skip-cert-verify=' . ($tlsSettings['allowInsecure'] ? 'true' : 'false'));
                     array_push($config, 'skip-cert-verify=' . ($tlsSettings['allowInsecure'] ? 'true' : 'false'));
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
@@ -102,7 +104,7 @@ class Surge
         if ($server['network'] === 'ws') {
         if ($server['network'] === 'ws') {
             array_push($config, 'ws=true');
             array_push($config, 'ws=true');
             if ($server['networkSettings']) {
             if ($server['networkSettings']) {
-                $wsSettings = json_decode($server['networkSettings'], true);
+                $wsSettings = $server['networkSettings'];
                 if (isset($wsSettings['path']) && !empty($wsSettings['path']))
                 if (isset($wsSettings['path']) && !empty($wsSettings['path']))
                     array_push($config, "ws-path={$wsSettings['path']}");
                     array_push($config, "ws-path={$wsSettings['path']}");
                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))

+ 4 - 4
app/Http/Controllers/Client/Protocols/V2rayN.php

@@ -63,19 +63,19 @@ class V2rayN
         ];
         ];
         if ($server['tls']) {
         if ($server['tls']) {
             if ($server['tlsSettings']) {
             if ($server['tlsSettings']) {
-                $tlsSettings = json_decode($server['tlsSettings'], true);
+                $tlsSettings = $server['tlsSettings'];
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
                     $config['sni'] = $tlsSettings['serverName'];
                     $config['sni'] = $tlsSettings['serverName'];
             }
             }
         }
         }
         if ((string)$server['network'] === 'ws') {
         if ((string)$server['network'] === 'ws') {
-            $wsSettings = json_decode($server['networkSettings'], true);
+            $wsSettings = $server['networkSettings'];
             if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
             if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
             if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
             if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
         }
         }
         if ((string)$server['network'] === 'grpc') {
         if ((string)$server['network'] === 'grpc') {
-            $grpcSettings = json_decode($server['networkSettings'], true);
-            if (isset($grpcSettings['path'])) $config['path'] = $grpcSettings['serviceName'];
+            $grpcSettings = $server['networkSettings'];
+            if (isset($grpcSettings['serviceName'])) $config['path'] = $grpcSettings['serviceName'];
         }
         }
         return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
         return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
     }
     }

+ 4 - 4
app/Http/Controllers/Client/Protocols/V2rayNG.php

@@ -63,19 +63,19 @@ class V2rayNG
         ];
         ];
         if ($server['tls']) {
         if ($server['tls']) {
             if ($server['tlsSettings']) {
             if ($server['tlsSettings']) {
-                $tlsSettings = json_decode($server['tlsSettings'], true);
+                $tlsSettings = $server['tlsSettings'];
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
                 if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
                     $config['sni'] = $tlsSettings['serverName'];
                     $config['sni'] = $tlsSettings['serverName'];
             }
             }
         }
         }
         if ((string)$server['network'] === 'ws') {
         if ((string)$server['network'] === 'ws') {
-            $wsSettings = json_decode($server['networkSettings'], true);
+            $wsSettings = $server['networkSettings'];
             if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
             if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
             if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
             if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
         }
         }
         if ((string)$server['network'] === 'grpc') {
         if ((string)$server['network'] === 'grpc') {
-            $grpcSettings = json_decode($server['networkSettings'], true);
-            if (isset($grpcSettings['path'])) $config['path'] = $grpcSettings['serviceName'];
+            $grpcSettings = $server['networkSettings'];
+            if (isset($grpcSettings['serviceName'])) $config['path'] = $grpcSettings['serviceName'];
         }
         }
         return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
         return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
     }
     }

+ 1 - 1
app/Http/Controllers/Guest/PaymentController.php

@@ -34,7 +34,7 @@ class PaymentController extends Controller
         }
         }
         if ($order->status === 1) return true;
         if ($order->status === 1) return true;
         $orderService = new OrderService($order);
         $orderService = new OrderService($order);
-        if (!$orderService->success($callbackNo)) {
+        if (!$orderService->paid($callbackNo)) {
             return false;
             return false;
         }
         }
         $telegramService = new TelegramService();
         $telegramService = new TelegramService();

+ 8 - 1
app/Http/Controllers/Passport/AuthController.php

@@ -102,10 +102,15 @@ class AuthController extends Controller
         if ((int)config('v2board.email_verify', 0)) {
         if ((int)config('v2board.email_verify', 0)) {
             Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
             Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
         }
         }
+
+        $data = [
+            'token' => $user->token,
+            'auth_data' => base64_encode("{$user->email}:{$user->password}")
+        ];
         $request->session()->put('email', $user->email);
         $request->session()->put('email', $user->email);
         $request->session()->put('id', $user->id);
         $request->session()->put('id', $user->id);
         return response()->json([
         return response()->json([
-            'data' => true
+            'data' => $data
         ]);
         ]);
     }
     }
 
 
@@ -120,6 +125,7 @@ class AuthController extends Controller
         }
         }
         if (!Helper::multiPasswordVerify(
         if (!Helper::multiPasswordVerify(
             $user->password_algo,
             $user->password_algo,
+            $user->password_salt,
             $password,
             $password,
             $user->password)
             $user->password)
         ) {
         ) {
@@ -250,6 +256,7 @@ class AuthController extends Controller
         }
         }
         $user->password = password_hash($request->input('password'), PASSWORD_DEFAULT);
         $user->password = password_hash($request->input('password'), PASSWORD_DEFAULT);
         $user->password_algo = NULL;
         $user->password_algo = NULL;
+        $user->password_salt = NULL;
         if (!$user->save()) {
         if (!$user->save()) {
             abort(500, __('Reset failed'));
             abort(500, __('Reset failed'));
         }
         }

+ 8 - 20
app/Http/Controllers/Server/DeepbworkController.php

@@ -8,7 +8,7 @@ use App\Utils\CacheKey;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 use App\Http\Controllers\Controller;
 use App\Http\Controllers\Controller;
 use App\Models\User;
 use App\Models\User;
-use App\Models\Server;
+use App\Models\ServerV2ray;
 use App\Models\ServerLog;
 use App\Models\ServerLog;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Log;
 use Illuminate\Support\Facades\Log;
@@ -35,13 +35,13 @@ class DeepbworkController extends Controller
     public function user(Request $request)
     public function user(Request $request)
     {
     {
         $nodeId = $request->input('node_id');
         $nodeId = $request->input('node_id');
-        $server = Server::find($nodeId);
+        $server = ServerV2ray::find($nodeId);
         if (!$server) {
         if (!$server) {
             abort(500, 'fail');
             abort(500, 'fail');
         }
         }
         Cache::put(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $server->id), time(), 3600);
         Cache::put(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $server->id), time(), 3600);
         $serverService = new ServerService();
         $serverService = new ServerService();
-        $users = $serverService->getAvailableUsers(json_decode($server->group_id));
+        $users = $serverService->getAvailableUsers($server->group_id);
         $result = [];
         $result = [];
         foreach ($users as $user) {
         foreach ($users as $user) {
             $user->v2ray_user = [
             $user->v2ray_user = [
@@ -64,7 +64,7 @@ class DeepbworkController extends Controller
     public function submit(Request $request)
     public function submit(Request $request)
     {
     {
 //         Log::info('serverSubmitData:' . $request->input('node_id') . ':' . file_get_contents('php://input'));
 //         Log::info('serverSubmitData:' . $request->input('node_id') . ':' . file_get_contents('php://input'));
-        $server = Server::find($request->input('node_id'));
+        $server = ServerV2ray::find($request->input('node_id'));
         if (!$server) {
         if (!$server) {
             return response([
             return response([
                 'ret' => 0,
                 'ret' => 0,
@@ -76,23 +76,11 @@ class DeepbworkController extends Controller
         Cache::put(CacheKey::get('SERVER_V2RAY_ONLINE_USER', $server->id), count($data), 3600);
         Cache::put(CacheKey::get('SERVER_V2RAY_ONLINE_USER', $server->id), count($data), 3600);
         Cache::put(CacheKey::get('SERVER_V2RAY_LAST_PUSH_AT', $server->id), time(), 3600);
         Cache::put(CacheKey::get('SERVER_V2RAY_LAST_PUSH_AT', $server->id), time(), 3600);
         $userService = new UserService();
         $userService = new UserService();
-        DB::beginTransaction();
-        try {
-            foreach ($data as $item) {
-                $u = $item['u'] * $server->rate;
-                $d = $item['d'] * $server->rate;
-                if (!$userService->trafficFetch($u, $d, $item['user_id'], $server, 'vmess')) {
-                    continue;
-                }
-            }
-        } catch (\Exception $e) {
-            DB::rollBack();
-            return response([
-                'ret' => 0,
-                'msg' => 'user fetch fail'
-            ]);
+        foreach ($data as $item) {
+            $u = $item['u'] * $server->rate;
+            $d = $item['d'] * $server->rate;
+            $userService->trafficFetch($u, $d, $item['user_id'], $server, 'vmess');
         }
         }
-        DB::commit();
 
 
         return response([
         return response([
             'ret' => 1,
             'ret' => 1,

+ 0 - 158
app/Http/Controllers/Server/PoseidonController.php

@@ -1,158 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Server;
-
-use App\Services\ServerService;
-use App\Services\UserService;
-use App\Utils\CacheKey;
-use Illuminate\Http\Request;
-use App\Http\Controllers\Controller;
-use App\Models\User;
-use App\Models\Plan;
-use App\Models\Server;
-use App\Models\ServerLog;
-use Illuminate\Support\Facades\Log;
-use Illuminate\Support\Facades\Cache;
-
-/*
- * V2ray Poseidon
- * Github: https://github.com/ColetteContreras/trojan-poseidon
- */
-class PoseidonController extends Controller
-{
-    public $poseidonVersion;
-
-    public function __construct(Request $request)
-    {
-        $this->poseidonVersion = $request->input('poseidon_version');
-    }
-
-    // 后端获取用户
-    public function user(Request $request)
-    {
-        if ($r = $this->verifyToken($request)) { return $r; }
-
-        $nodeId = $request->input('node_id');
-        $server = Server::find($nodeId);
-        if (!$server) {
-            return $this->error("server could not be found", 404);
-        }
-        Cache::put(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $server->id), time(), 3600);
-        $serverService = new ServerService();
-        $users = $serverService->getAvailableUsers(json_decode($server->group_id));
-        $result = [];
-        foreach ($users as $user) {
-            $user->v2ray_user = [
-                "uuid" => $user->uuid,
-                "email" => sprintf("%s@v2board.user", $user->uuid),
-                "alter_id" => $server->alter_id,
-                "level" => 0,
-            ];
-            unset($user['uuid']);
-            unset($user['email']);
-            array_push($result, $user);
-        }
-
-        return $this->success($result);
-    }
-
-    // 后端提交数据
-    public function submit(Request $request)
-    {
-        if ($r = $this->verifyToken($request)) { return $r; }
-        $server = Server::find($request->input('node_id'));
-        if (!$server) {
-            return $this->error("server could not be found", 404);
-        }
-        $data = file_get_contents('php://input');
-        $data = json_decode($data, true);
-        Cache::put(CacheKey::get('SERVER_V2RAY_ONLINE_USER', $server->id), count($data), 3600);
-        Cache::put(CacheKey::get('SERVER_V2RAY_LAST_PUSH_AT', $server->id), time(), 3600);
-        $userService = new UserService();
-        foreach ($data as $item) {
-            $u = $item['u'] * $server->rate;
-            $d = $item['d'] * $server->rate;
-            if (!$userService->trafficFetch($u, $d, $item['user_id'], $server, 'vmess')) {
-                return $this->error("user fetch fail", 500);
-            }
-        }
-
-        return $this->success('');
-    }
-
-    // 后端获取配置
-    public function config(Request $request)
-    {
-        if ($r = $this->verifyToken($request)) { return $r; }
-
-        $nodeId = $request->input('node_id');
-        $localPort = $request->input('local_port');
-        if (empty($nodeId) || empty($localPort)) {
-            return $this->error('invalid parameters', 400);
-        }
-
-        $serverService = new ServerService();
-        try {
-            $json = $serverService->getV2RayConfig($nodeId, $localPort);
-            $json->poseidon = [
-              'license_key' => (string)config('v2board.server_license'),
-            ];
-            if ($this->poseidonVersion >= 'v1.5.0') {
-                // don't need it after v1.5.0
-                unset($json->inboundDetour);
-                unset($json->stats);
-                unset($json->api);
-                array_shift($json->routing->rules);
-            }
-
-            foreach($json->policy->levels as &$level) {
-                $level->handshake = 2;
-                $level->uplinkOnly = 2;
-                $level->downlinkOnly = 2;
-                $level->connIdle = 60;
-            }
-
-            return $this->success($json);
-        } catch (\Exception $e) {
-            return $this->error($e->getMessage(), 500);
-        }
-    }
-
-    protected function verifyToken(Request $request)
-    {
-        $token = $request->input('token');
-        if (empty($token)) {
-            return $this->error("token must be set");
-        }
-        if ($token !== config('v2board.server_token')) {
-            return $this->error("invalid token");
-        }
-    }
-
-    protected function error($msg, int $status = 400) {
-        return response([
-            'msg' => $msg,
-        ], $status);
-    }
-
-    protected function success($data) {
-         $req = request();
-        // Only for "GET" method
-        if (!$req->isMethod('GET') || !$data) {
-            return response([
-                'msg' => 'ok',
-                'data' => $data,
-            ]);
-        }
-
-        $etag = sha1(json_encode($data));
-        if ($etag == $req->header("IF-NONE-MATCH")) {
-            return response(null, 304);
-        }
-
-        return response([
-            'msg' => 'ok',
-            'data' => $data,
-        ])->header('ETAG', $etag);
-    }
-}

+ 5 - 21
app/Http/Controllers/Server/ShadowsocksTidalabController.php

@@ -8,10 +8,6 @@ use App\Services\UserService;
 use App\Utils\CacheKey;
 use App\Utils\CacheKey;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 use App\Http\Controllers\Controller;
 use App\Http\Controllers\Controller;
-use App\Models\User;
-use App\Models\ServerLog;
-use Illuminate\Support\Facades\DB;
-use Illuminate\Support\Facades\Log;
 use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\Cache;
 
 
 /*
 /*
@@ -41,7 +37,7 @@ class ShadowsocksTidalabController extends Controller
         }
         }
         Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $server->id), time(), 3600);
         Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $server->id), time(), 3600);
         $serverService = new ServerService();
         $serverService = new ServerService();
-        $users = $serverService->getAvailableUsers(json_decode($server->group_id));
+        $users = $serverService->getAvailableUsers($server->group_id);
         $result = [];
         $result = [];
         foreach ($users as $user) {
         foreach ($users as $user) {
             array_push($result, [
             array_push($result, [
@@ -72,23 +68,11 @@ class ShadowsocksTidalabController extends Controller
         Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_ONLINE_USER', $server->id), count($data), 3600);
         Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_ONLINE_USER', $server->id), count($data), 3600);
         Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_PUSH_AT', $server->id), time(), 3600);
         Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_PUSH_AT', $server->id), time(), 3600);
         $userService = new UserService();
         $userService = new UserService();
-        DB::beginTransaction();
-        try {
-            foreach ($data as $item) {
-                $u = $item['u'] * $server->rate;
-                $d = $item['d'] * $server->rate;
-                if (!$userService->trafficFetch((float)$u, (float)$d, (int)$item['user_id'], $server, 'shadowsocks')) {
-                    continue;
-                }
-            }
-        } catch (\Exception $e) {
-            DB::rollBack();
-            return response([
-                'ret' => 0,
-                'msg' => 'user fetch fail'
-            ]);
+        foreach ($data as $item) {
+            $u = $item['u'] * $server->rate;
+            $d = $item['d'] * $server->rate;
+            $userService->trafficFetch($u, $d, $item['user_id'], $server, 'shadowsocks');
         }
         }
-        DB::commit();
 
 
         return response([
         return response([
             'ret' => 1,
             'ret' => 1,

+ 5 - 17
app/Http/Controllers/Server/TrojanTidalabController.php

@@ -41,7 +41,7 @@ class TrojanTidalabController extends Controller
         }
         }
         Cache::put(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server->id), time(), 3600);
         Cache::put(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server->id), time(), 3600);
         $serverService = new ServerService();
         $serverService = new ServerService();
-        $users = $serverService->getAvailableUsers(json_decode($server->group_id));
+        $users = $serverService->getAvailableUsers($server->group_id);
         $result = [];
         $result = [];
         foreach ($users as $user) {
         foreach ($users as $user) {
             $user->trojan_user = [
             $user->trojan_user = [
@@ -73,23 +73,11 @@ class TrojanTidalabController extends Controller
         Cache::put(CacheKey::get('SERVER_TROJAN_ONLINE_USER', $server->id), count($data), 3600);
         Cache::put(CacheKey::get('SERVER_TROJAN_ONLINE_USER', $server->id), count($data), 3600);
         Cache::put(CacheKey::get('SERVER_TROJAN_LAST_PUSH_AT', $server->id), time(), 3600);
         Cache::put(CacheKey::get('SERVER_TROJAN_LAST_PUSH_AT', $server->id), time(), 3600);
         $userService = new UserService();
         $userService = new UserService();
-        DB::beginTransaction();
-        try {
-            foreach ($data as $item) {
-                $u = $item['u'] * $server->rate;
-                $d = $item['d'] * $server->rate;
-                if (!$userService->trafficFetch($u, $d, $item['user_id'], $server, 'trojan')) {
-                    continue;
-                }
-            }
-        } catch (\Exception $e) {
-            DB::rollBack();
-            return response([
-                'ret' => 0,
-                'msg' => 'user fetch fail'
-            ]);
+        foreach ($data as $item) {
+            $u = $item['u'] * $server->rate;
+            $d = $item['d'] * $server->rate;
+            $userService->trafficFetch($u, $d, $item['user_id'], $server, 'trojan');
         }
         }
-        DB::commit();
 
 
         return response([
         return response([
             'ret' => 1,
             'ret' => 1,

+ 1 - 2
app/Http/Controllers/User/CommController.php

@@ -27,9 +27,8 @@ class CommController extends Controller
             ->where('payment', 'StripeCredit')
             ->where('payment', 'StripeCredit')
             ->first();
             ->first();
         if (!$payment) abort(500, 'payment is not found');
         if (!$payment) abort(500, 'payment is not found');
-        $config = json_decode($payment->config, true);
         return response([
         return response([
-            'data' => $config['stripe_pk_live']
+            'data' => $payment->config['stripe_pk_live']
         ]);
         ]);
     }
     }
 }
 }

+ 6 - 20
app/Http/Controllers/User/CouponController.php

@@ -3,6 +3,7 @@
 namespace App\Http\Controllers\User;
 namespace App\Http\Controllers\User;
 
 
 use App\Http\Controllers\Controller;
 use App\Http\Controllers\Controller;
+use App\Services\CouponService;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 use App\Models\Coupon;
 use App\Models\Coupon;
 
 
@@ -13,27 +14,12 @@ class CouponController extends Controller
         if (empty($request->input('code'))) {
         if (empty($request->input('code'))) {
             abort(500, __('Coupon cannot be empty'));
             abort(500, __('Coupon cannot be empty'));
         }
         }
-        $coupon = Coupon::where('code', $request->input('code'))->first();
-        if (!$coupon) {
-            abort(500, __('Invalid coupon'));
-        }
-        if ($coupon->limit_use <= 0 && $coupon->limit_use !== NULL) {
-            abort(500, __('This coupon is no longer available'));
-        }
-        if (time() < $coupon->started_at) {
-            abort(500, __('This coupon has not yet started'));
-        }
-        if (time() > $coupon->ended_at) {
-            abort(500, __('This coupon has expired'));
-        }
-        if ($coupon->limit_plan_ids) {
-            $limitPlanIds = json_decode($coupon->limit_plan_ids);
-            if (!in_array($request->input('plan_id'), $limitPlanIds)) {
-                abort(500, __('The coupon code cannot be used for this subscription'));
-            }
-        }
+        $couponService = new CouponService($request->input('code'));
+        $couponService->setPlanId($request->input('plan_id'));
+        $couponService->setUserId($request->session()->get('id'));
+        $couponService->check();
         return response([
         return response([
-            'data' => $coupon
+            'data' => $couponService->getCoupon()
         ]);
         ]);
     }
     }
 }
 }

+ 0 - 2
app/Http/Controllers/User/KnowledgeController.php

@@ -35,8 +35,6 @@ class KnowledgeController extends Controller
             }
             }
             $subscribeUrl = "{$subscribeUrl}/api/v1/client/subscribe?token={$user['token']}";
             $subscribeUrl = "{$subscribeUrl}/api/v1/client/subscribe?token={$user['token']}";
             $knowledge['body'] = str_replace('{{siteName}}', config('v2board.app_name', 'V2Board'), $knowledge['body']);
             $knowledge['body'] = str_replace('{{siteName}}', config('v2board.app_name', 'V2Board'), $knowledge['body']);
-            $knowledge['body'] = str_replace('{{appleId}}', $appleId, $knowledge['body']);
-            $knowledge['body'] = str_replace('{{appleIdPassword}}', $appleIdPassword, $knowledge['body']);
             $knowledge['body'] = str_replace('{{subscribeUrl}}', $subscribeUrl, $knowledge['body']);
             $knowledge['body'] = str_replace('{{subscribeUrl}}', $subscribeUrl, $knowledge['body']);
             $knowledge['body'] = str_replace('{{urlEncodeSubscribeUrl}}', urlencode($subscribeUrl), $knowledge['body']);
             $knowledge['body'] = str_replace('{{urlEncodeSubscribeUrl}}', urlencode($subscribeUrl), $knowledge['body']);
             $knowledge['body'] = str_replace(
             $knowledge['body'] = str_replace(

+ 3 - 4
app/Http/Controllers/User/OrderController.php

@@ -108,7 +108,7 @@ class OrderController extends Controller
         $order->user_id = $request->session()->get('id');
         $order->user_id = $request->session()->get('id');
         $order->plan_id = $plan->id;
         $order->plan_id = $plan->id;
         $order->cycle = $request->input('cycle');
         $order->cycle = $request->input('cycle');
-        $order->trade_no = Helper::guid();
+        $order->trade_no = Helper::generateOrderNo();
         $order->total_amount = $plan[$request->input('cycle')];
         $order->total_amount = $plan[$request->input('cycle')];
 
 
         if ($request->input('coupon_code')) {
         if ($request->input('coupon_code')) {
@@ -169,9 +169,8 @@ class OrderController extends Controller
         }
         }
         // free process
         // free process
         if ($order->total_amount <= 0) {
         if ($order->total_amount <= 0) {
-            $order->total_amount = 0;
-            $order->status = 1;
-            $order->save();
+            $orderService = new OrderService($order);
+            if (!$orderService->paid($order->trade_no)) abort(500, '');
             return response([
             return response([
                 'type' => -1,
                 'type' => -1,
                 'data' => true
                 'data' => true

+ 5 - 5
app/Http/Controllers/User/ServerController.php

@@ -8,7 +8,7 @@ use App\Services\UserService;
 use App\Utils\CacheKey;
 use App\Utils\CacheKey;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\Cache;
-use App\Models\Server;
+use App\Models\ServerV2ray;
 use App\Models\ServerLog;
 use App\Models\ServerLog;
 use App\Models\User;
 use App\Models\User;
 
 
@@ -36,16 +36,16 @@ class ServerController extends Controller
         $current = $request->input('current') ? $request->input('current') : 1;
         $current = $request->input('current') ? $request->input('current') : 1;
         $pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
         $pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
         $serverLogModel = ServerLog::where('user_id', $request->session()->get('id'))
         $serverLogModel = ServerLog::where('user_id', $request->session()->get('id'))
-            ->orderBy('created_at', 'DESC');
+            ->orderBy('log_at', 'DESC');
         switch ($type) {
         switch ($type) {
             case 0:
             case 0:
-                $serverLogModel->where('created_at', '>=', strtotime(date('Y-m-d')));
+                $serverLogModel->where('log_at', '>=', strtotime(date('Y-m-d')));
                 break;
                 break;
             case 1:
             case 1:
-                $serverLogModel->where('created_at', '>=', strtotime(date('Y-m-d')) - 604800);
+                $serverLogModel->where('log_at', '>=', strtotime(date('Y-m-d')) - 604800);
                 break;
                 break;
             case 2:
             case 2:
-                $serverLogModel->where('created_at', '>=', strtotime(date('Y-m-1')));
+                $serverLogModel->where('log_at', '>=', strtotime(date('Y-m-1')));
         }
         }
         $total = $serverLogModel->count();
         $total = $serverLogModel->count();
         $res = $serverLogModel->forPage($current, $pageSize)
         $res = $serverLogModel->forPage($current, $pageSize)

+ 1 - 1
app/Http/Controllers/User/TicketController.php

@@ -168,7 +168,7 @@ class TicketController extends Controller
         $user = User::find($request->session()->get('id'));
         $user = User::find($request->session()->get('id'));
         $limit = config('v2board.commission_withdraw_limit', 100);
         $limit = config('v2board.commission_withdraw_limit', 100);
         if ($limit > ($user->commission_balance / 100)) {
         if ($limit > ($user->commission_balance / 100)) {
-            abort(500, __('The current required minimum withdrawal commission is', ['limit' => $limit]));
+            abort(500, __('The current required minimum withdrawal commission is :limit', ['limit' => $limit]));
         }
         }
         DB::beginTransaction();
         DB::beginTransaction();
         $subject = __('[Commission Withdrawal Request] This ticket is opened by the system');
         $subject = __('[Commission Withdrawal Request] This ticket is opened by the system');

+ 28 - 7
app/Http/Controllers/User/UserController.php

@@ -6,14 +6,16 @@ use App\Http\Controllers\Controller;
 use App\Http\Requests\User\UserTransfer;
 use App\Http\Requests\User\UserTransfer;
 use App\Http\Requests\User\UserUpdate;
 use App\Http\Requests\User\UserUpdate;
 use App\Http\Requests\User\UserChangePassword;
 use App\Http\Requests\User\UserChangePassword;
+use App\Utils\CacheKey;
 use Illuminate\Http\Request;
 use Illuminate\Http\Request;
 use App\Models\User;
 use App\Models\User;
 use App\Models\Plan;
 use App\Models\Plan;
-use App\Models\Server;
+use App\Models\ServerV2ray;
 use App\Models\Ticket;
 use App\Models\Ticket;
 use App\Utils\Helper;
 use App\Utils\Helper;
 use App\Models\Order;
 use App\Models\Order;
 use App\Models\ServerLog;
 use App\Models\ServerLog;
+use Illuminate\Support\Facades\Cache;
 
 
 class UserController extends Controller
 class UserController extends Controller
 {
 {
@@ -33,6 +35,7 @@ class UserController extends Controller
         }
         }
         if (!Helper::multiPasswordVerify(
         if (!Helper::multiPasswordVerify(
             $user->password_algo,
             $user->password_algo,
+            $user->password_salt,
             $request->input('old_password'),
             $request->input('old_password'),
             $user->password)
             $user->password)
         ) {
         ) {
@@ -40,6 +43,7 @@ class UserController extends Controller
         }
         }
         $user->password = password_hash($request->input('new_password'), PASSWORD_DEFAULT);
         $user->password = password_hash($request->input('new_password'), PASSWORD_DEFAULT);
         $user->password_algo = NULL;
         $user->password_algo = NULL;
+        $user->password_salt = NULL;
         if (!$user->save()) {
         if (!$user->save()) {
             abort(500, __('Save failed'));
             abort(500, __('Save failed'));
         }
         }
@@ -118,12 +122,7 @@ class UserController extends Controller
                 abort(500, __('Subscription plan does not exist'));
                 abort(500, __('Subscription plan does not exist'));
             }
             }
         }
         }
-        $subscribeUrl = config('v2board.app_url', env('APP_URL'));
-        $subscribeUrls = explode(',', config('v2board.subscribe_url'));
-        if ($subscribeUrls) {
-            $subscribeUrl = $subscribeUrls[rand(0, count($subscribeUrls) - 1)];
-        }
-        $user['subscribe_url'] = "{$subscribeUrl}/api/v1/client/subscribe?token={$user['token']}";
+        $user['subscribe_url'] = Helper::getSubscribeHost() . "/api/v1/client/subscribe?token={$user['token']}";
         $user['reset_day'] = $this->getResetDay($user);
         $user['reset_day'] = $this->getResetDay($user);
         return response([
         return response([
             'data' => $user
             'data' => $user
@@ -209,4 +208,26 @@ class UserController extends Controller
         }
         }
         return null;
         return null;
     }
     }
+
+
+    public function getQuickLoginUrl(Request $request)
+    {
+        $user = User::find($request->session()->get('id'));
+        if (!$user) {
+            abort(500, __('The user does not exist'));
+        }
+
+        $code = Helper::guid();
+        $key = CacheKey::get('TEMP_TOKEN', $code);
+        Cache::put($key, $user->id, 60);
+        $redirect = '/#/login?verify=' . $code . '&redirect=' . ($request->input('redirect') ? $request->input('redirect') : 'dashboard');
+        if (config('v2board.app_url')) {
+            $url = config('v2board.app_url') . $redirect;
+        } else {
+            $url = url($redirect);
+        }
+        return response([
+            'data' => $url
+        ]);
+    }
 }
 }

+ 1 - 0
app/Http/Kernel.php

@@ -35,6 +35,7 @@ class Kernel extends HttpKernel
             \Illuminate\View\Middleware\ShareErrorsFromSession::class,
             \Illuminate\View\Middleware\ShareErrorsFromSession::class,
             \App\Http\Middleware\VerifyCsrfToken::class,
             \App\Http\Middleware\VerifyCsrfToken::class,
             \Illuminate\Routing\Middleware\SubstituteBindings::class,
             \Illuminate\Routing\Middleware\SubstituteBindings::class,
+            \App\Http\Middleware\CORS::class,
         ],
         ],
 
 
         'api' => [
         'api' => [

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

@@ -15,8 +15,9 @@ class User
      */
      */
     public function handle($request, Closure $next)
     public function handle($request, Closure $next)
     {
     {
-        if ($request->input('auth_data')) {
-            $authData = explode(':', base64_decode($request->input('auth_data')));
+        $authorization = $request->input('auth_data') ?? $request->header('authorization');
+        if ($authorization) {
+            $authData = explode(':', base64_decode($authorization));
             if (!isset($authData[1]) || !isset($authData[0])) abort(403, '鉴权失败,请重新登入');
             if (!isset($authData[1]) || !isset($authData[0])) abort(403, '鉴权失败,请重新登入');
             $user = \App\Models\User::where('password', $authData[1])
             $user = \App\Models\User::where('password', $authData[1])
                 ->where('email', $authData[0])
                 ->where('email', $authData[0])

+ 6 - 5
app/Http/Requests/Admin/ConfigSave.php

@@ -25,6 +25,10 @@ class ConfigSave extends FormRequest
             'commission_withdraw_limit' => 'nullable|numeric',
             'commission_withdraw_limit' => 'nullable|numeric',
             'commission_withdraw_method' => 'nullable|array',
             'commission_withdraw_method' => 'nullable|array',
             'withdraw_close_enable' => 'in:0,1',
             'withdraw_close_enable' => 'in:0,1',
+            'commission_distribution_enable' => 'in:0,1',
+            'commission_distribution_l1' => 'nullable|numeric',
+            'commission_distribution_l2' => 'nullable|numeric',
+            'commission_distribution_l3' => 'nullable|numeric',
             // site
             // site
             'stop_register' => 'in:0,1',
             'stop_register' => 'in:0,1',
             'email_verify' => 'in:0,1',
             'email_verify' => 'in:0,1',
@@ -44,7 +48,7 @@ class ConfigSave extends FormRequest
             'tos_url' => 'nullable|url',
             'tos_url' => 'nullable|url',
             // subscribe
             // subscribe
             'plan_change_enable' => 'in:0,1',
             'plan_change_enable' => 'in:0,1',
-            'reset_traffic_method' => 'in:0,1',
+            'reset_traffic_method' => 'in:0,1,2',
             'surplus_enable' => 'in:0,1',
             'surplus_enable' => 'in:0,1',
             'new_order_event_id' => 'in:0,1',
             'new_order_event_id' => 'in:0,1',
             'renew_order_event_id' => 'in:0,1',
             'renew_order_event_id' => 'in:0,1',
@@ -88,14 +92,11 @@ class ConfigSave extends FormRequest
             'frontend_theme' => '',
             'frontend_theme' => '',
             'frontend_theme_sidebar' => 'in:dark,light',
             'frontend_theme_sidebar' => 'in:dark,light',
             'frontend_theme_header' => 'in:dark,light',
             'frontend_theme_header' => 'in:dark,light',
-            'frontend_theme_color' => 'in:default,darkblue,black',
+            'frontend_theme_color' => 'in:default,darkblue,black,green',
             'frontend_background_url' => 'nullable|url',
             'frontend_background_url' => 'nullable|url',
             'frontend_admin_path' => '',
             'frontend_admin_path' => '',
             'frontend_customer_service_method' => '',
             'frontend_customer_service_method' => '',
             'frontend_customer_service_id' => '',
             'frontend_customer_service_id' => '',
-            // tutorial
-            'apple_id' => 'nullable|email',
-            'apple_id_password' => '',
             // email
             // email
             'email_template' => '',
             'email_template' => '',
             'email_host' => '',
             'email_host' => '',

+ 3 - 1
app/Http/Requests/Admin/CouponGenerate.php

@@ -21,6 +21,7 @@ class CouponGenerate extends FormRequest
             'started_at' => 'required|integer',
             'started_at' => 'required|integer',
             'ended_at' => 'required|integer',
             'ended_at' => 'required|integer',
             'limit_use' => 'nullable|integer',
             'limit_use' => 'nullable|integer',
+            'limit_use_with_user' => 'nullable|integer',
             'limit_plan_ids' => 'nullable|array',
             'limit_plan_ids' => 'nullable|array',
             'code' => ''
             'code' => ''
         ];
         ];
@@ -40,7 +41,8 @@ class CouponGenerate extends FormRequest
             'started_at.integer' => '开始时间格式有误',
             'started_at.integer' => '开始时间格式有误',
             'ended_at.required' => '结束时间不能为空',
             'ended_at.required' => '结束时间不能为空',
             'ended_at.integer' => '结束时间格式有误',
             'ended_at.integer' => '结束时间格式有误',
-            'limit_use.integer' => '使用次数格式有误',
+            'limit_use.integer' => '最大使用次数格式有误',
+            'limit_use_with_user.integer' => '限制用户使用次数格式有误',
             'limit_plan_ids.array' => '指定订阅格式有误'
             'limit_plan_ids.array' => '指定订阅格式有误'
         ];
         ];
     }
     }

+ 0 - 44
app/Http/Requests/Admin/CouponSave.php

@@ -1,44 +0,0 @@
-<?php
-
-namespace App\Http\Requests\Admin;
-
-use Illuminate\Foundation\Http\FormRequest;
-
-class CouponSave extends FormRequest
-{
-    /**
-     * Get the validation rules that apply to the request.
-     *
-     * @return array
-     */
-    public function rules()
-    {
-        return [
-            'name' => 'required',
-            'type' => 'required|in:1,2',
-            'value' => 'required|integer',
-            'started_at' => 'required|integer',
-            'ended_at' => 'required|integer',
-            'limit_use' => 'nullable|integer',
-            'limit_plan_ids' => 'nullable|array',
-            'code' => ''
-        ];
-    }
-
-    public function messages()
-    {
-        return [
-            'name.required' => '名称不能为空',
-            'type.required' => '类型不能为空',
-            'type.in' => '类型格式有误',
-            'value.required' => '金额或比例不能为空',
-            'value.integer' => '金额或比例格式有误',
-            'started_at.required' => '开始时间不能为空',
-            'started_at.integer' => '开始时间格式有误',
-            'ended_at.required' => '结束时间不能为空',
-            'ended_at.integer' => '结束时间格式有误',
-            'limit_use.integer' => '使用次数格式有误',
-            'limit_plan_ids.array' => '指定订阅格式有误'
-        ];
-    }
-}

+ 5 - 2
app/Http/Requests/Admin/PlanSave.php

@@ -25,7 +25,8 @@ class PlanSave extends FormRequest
             'two_year_price' => 'nullable|integer',
             'two_year_price' => 'nullable|integer',
             'three_year_price' => 'nullable|integer',
             'three_year_price' => 'nullable|integer',
             'onetime_price' => 'nullable|integer',
             'onetime_price' => 'nullable|integer',
-            'reset_price' => 'nullable|integer'
+            'reset_price' => 'nullable|integer',
+            'reset_traffic_method' => 'nullable|integer|in:0,1,2'
         ];
         ];
     }
     }
 
 
@@ -44,7 +45,9 @@ class PlanSave extends FormRequest
             'two_year_price.integer' => '两年付金额格式有误',
             'two_year_price.integer' => '两年付金额格式有误',
             'three_year_price.integer' => '三年付金额格式有误',
             'three_year_price.integer' => '三年付金额格式有误',
             'onetime_price.integer' => '一次性金额有误',
             'onetime_price.integer' => '一次性金额有误',
-            'reset_price.integer' => '流量重置包金额有误'
+            'reset_price.integer' => '流量重置包金额有误',
+            'reset_traffic_method.integer' => '流量重置方式格式有误',
+            'reset_traffic_method.in' => '流量重置方式格式有误'
         ];
         ];
     }
     }
 }
 }

+ 9 - 5
app/Http/Requests/Admin/ServerV2raySave.php

@@ -26,10 +26,10 @@ class ServerV2raySave extends FormRequest
             'rate' => 'required|numeric',
             'rate' => 'required|numeric',
             'alter_id' => 'required|integer',
             'alter_id' => 'required|integer',
             'network' => 'required|in:tcp,kcp,ws,http,domainsocket,quic,grpc',
             'network' => 'required|in:tcp,kcp,ws,http,domainsocket,quic,grpc',
-            'networkSettings' => '',
-            'ruleSettings' => '',
-            'tlsSettings' => '',
-            'dnsSettings' => ''
+            'networkSettings' => 'nullable|array',
+            'ruleSettings' => 'nullable|array',
+            'tlsSettings' => 'nullable|array',
+            'dnsSettings' => 'nullable|array'
         ];
         ];
     }
     }
 
 
@@ -48,7 +48,11 @@ class ServerV2raySave extends FormRequest
             'rate.required' => '倍率不能为空',
             'rate.required' => '倍率不能为空',
             'rate.numeric' => '倍率格式不正确',
             'rate.numeric' => '倍率格式不正确',
             'network.required' => '传输协议不能为空',
             'network.required' => '传输协议不能为空',
-            'network.in' => '传输协议格式不正确'
+            'network.in' => '传输协议格式不正确',
+            'networkSettings.array' => '传输协议配置有误',
+            'ruleSettings.array' => '规则配置有误',
+            'tlsSettings.array' => 'tls配置有误',
+            'dnsSettings.array' => 'dns配置有误'
         ];
         ];
     }
     }
 }
 }

+ 2 - 1
app/Http/Routes/AdminRoute.php

@@ -63,9 +63,10 @@ class AdminRoute
             });
             });
             // Order
             // Order
             $router->get ('/order/fetch', 'Admin\\OrderController@fetch');
             $router->get ('/order/fetch', 'Admin\\OrderController@fetch');
-            $router->post('/order/repair', 'Admin\\OrderController@repair');
             $router->post('/order/update', 'Admin\\OrderController@update');
             $router->post('/order/update', 'Admin\\OrderController@update');
             $router->post('/order/assign', 'Admin\\OrderController@assign');
             $router->post('/order/assign', 'Admin\\OrderController@assign');
+            $router->post('/order/paid', 'Admin\\OrderController@paid');
+            $router->post('/order/cancel', 'Admin\\OrderController@cancel');
             // User
             // User
             $router->get ('/user/fetch', 'Admin\\UserController@fetch');
             $router->get ('/user/fetch', 'Admin\\UserController@fetch');
             $router->post('/user/update', 'Admin\\UserController@update');
             $router->post('/user/update', 'Admin\\UserController@update');

+ 1 - 0
app/Http/Routes/UserRoute.php

@@ -20,6 +20,7 @@ class UserRoute
             $router->get ('/getSubscribe', 'User\\UserController@getSubscribe');
             $router->get ('/getSubscribe', 'User\\UserController@getSubscribe');
             $router->get ('/getStat', 'User\\UserController@getStat');
             $router->get ('/getStat', 'User\\UserController@getStat');
             $router->post('/transfer', 'User\\UserController@transfer');
             $router->post('/transfer', 'User\\UserController@transfer');
+            $router->post('/getQuickLoginUrl', 'User\\UserController@getQuickLoginUrl');
             // Order
             // Order
             $router->post('/order/save', 'User\\OrderController@save');
             $router->post('/order/save', 'User\\OrderController@save');
             $router->post('/order/checkout', 'User\\OrderController@checkout');
             $router->post('/order/checkout', 'User\\OrderController@checkout');

+ 52 - 0
app/Jobs/OrderHandleJob.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace App\Jobs;
+
+use App\Models\Order;
+use App\Services\OrderService;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+
+class OrderHandleJob implements ShouldQueue
+{
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+    protected $order;
+
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct($tradeNo)
+    {
+        $this->onQueue('order_handle');
+        $this->order = Order::where('trade_no', $tradeNo)
+            ->lockForUpdate()
+            ->first();
+    }
+
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        if (!$this->order) return;
+        $orderService = new OrderService($this->order);
+        switch ($this->order->status) {
+            // cancel
+            case 0:
+                if ($this->order->created_at <= (time() - 1800)) {
+                    $orderService->cancel();
+                }
+                break;
+            case 1:
+                $orderService->open();
+                break;
+        }
+    }
+}

+ 58 - 0
app/Jobs/ServerLogJob.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace App\Jobs;
+
+use App\Services\ServerService;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+
+class ServerLogJob implements ShouldQueue
+{
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+    protected $u;
+    protected $d;
+    protected $userId;
+    protected $server;
+    protected $protocol;
+
+    public $tries = 3;
+    public $timeout = 3;
+
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct($u, $d, $userId, $server, $protocol)
+    {
+        $this->onQueue('server_log');
+        $this->u = $u;
+        $this->d = $d;
+        $this->userId = $userId;
+        $this->server = $server;
+        $this->protocol = $protocol;
+    }
+
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        $serverService = new ServerService();
+        if (!$serverService->log(
+            $this->userId,
+            $this->server->id,
+            $this->u,
+            $this->d,
+            $this->server->rate,
+            $this->protocol
+        )) {
+            throw new \Exception('日志记录失败');
+        }
+    }
+}

+ 57 - 0
app/Jobs/TrafficFetchJob.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace App\Jobs;
+
+use App\Models\User;
+use App\Services\MailService;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+
+class TrafficFetchJob implements ShouldQueue
+{
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+    protected $u;
+    protected $d;
+    protected $userId;
+    protected $server;
+    protected $protocol;
+
+    public $tries = 3;
+    public $timeout = 3;
+
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct($u, $d, $userId, $server, $protocol)
+    {
+        $this->onQueue('traffic_fetch');
+        $this->u = $u;
+        $this->d = $d;
+        $this->userId = $userId;
+        $this->server = $server;
+        $this->protocol = $protocol;
+    }
+
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        $user = User::lockForUpdate()->find($this->userId);
+        if (!$user) return;
+        
+        $user->t = time();
+        $user->u = $user->u + $this->u;
+        $user->d = $user->d + $this->d;
+        if (!$user->save()) throw new \Exception('流量更新失败');
+        $mailService = new MailService();
+        $mailService->remindTraffic($user);
+    }
+}

+ 16 - 0
app/Models/CommissionLog.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+class CommissionLog extends Model
+{
+    protected $table = 'v2_commission_log';
+    protected $dateFormat = 'U';
+    protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
+}

+ 5 - 0
app/Models/Coupon.php

@@ -9,4 +9,9 @@ class Coupon extends Model
     protected $table = 'v2_coupon';
     protected $table = 'v2_coupon';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp',
+        'limit_plan_ids' => 'array'
+    ];
 }
 }

+ 4 - 0
app/Models/InviteCode.php

@@ -8,4 +8,8 @@ class InviteCode extends Model
 {
 {
     protected $table = 'v2_invite_code';
     protected $table = 'v2_invite_code';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
 }
 }

+ 4 - 0
app/Models/Knowledge.php

@@ -9,4 +9,8 @@ class Knowledge extends Model
     protected $table = 'v2_knowledge';
     protected $table = 'v2_knowledge';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
 }
 }

+ 4 - 0
app/Models/MailLog.php

@@ -9,4 +9,8 @@ class MailLog extends Model
     protected $table = 'v2_mail_log';
     protected $table = 'v2_mail_log';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
 }
 }

+ 4 - 0
app/Models/Notice.php

@@ -9,4 +9,8 @@ class Notice extends Model
     protected $table = 'v2_notice';
     protected $table = 'v2_notice';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
 }
 }

+ 5 - 0
app/Models/Order.php

@@ -9,4 +9,9 @@ class Order extends Model
     protected $table = 'v2_order';
     protected $table = 'v2_order';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp',
+        'surplus_order_ids' => 'array'
+    ];
 }
 }

+ 5 - 0
app/Models/Payment.php

@@ -9,4 +9,9 @@ class Payment extends Model
     protected $table = 'v2_payment';
     protected $table = 'v2_payment';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp',
+        'config' => 'array'
+    ];
 }
 }

+ 4 - 0
app/Models/Plan.php

@@ -9,4 +9,8 @@ class Plan extends Model
     protected $table = 'v2_plan';
     protected $table = 'v2_plan';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
 }
 }

+ 0 - 12
app/Models/Server.php

@@ -1,12 +0,0 @@
-<?php
-
-namespace App\Models;
-
-use Illuminate\Database\Eloquent\Model;
-
-class Server extends Model
-{
-    protected $table = 'v2_server';
-    protected $dateFormat = 'U';
-    protected $guarded = ['id'];
-}

+ 4 - 0
app/Models/ServerGroup.php

@@ -8,4 +8,8 @@ class ServerGroup extends Model
 {
 {
     protected $table = 'v2_server_group';
     protected $table = 'v2_server_group';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
 }
 }

+ 4 - 0
app/Models/ServerLog.php

@@ -9,4 +9,8 @@ class ServerLog extends Model
 {
 {
     protected $table = 'v2_server_log';
     protected $table = 'v2_server_log';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
 }
 }

+ 6 - 0
app/Models/ServerShadowsocks.php

@@ -9,4 +9,10 @@ class ServerShadowsocks extends Model
     protected $table = 'v2_server_shadowsocks';
     protected $table = 'v2_server_shadowsocks';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp',
+        'group_id' => 'array',
+        'tags' => 'array'
+    ];
 }
 }

+ 4 - 0
app/Models/ServerStat.php

@@ -9,4 +9,8 @@ class ServerStat extends Model
     protected $table = 'v2_server_stat';
     protected $table = 'v2_server_stat';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
 }
 }

+ 6 - 0
app/Models/ServerTrojan.php

@@ -9,4 +9,10 @@ class ServerTrojan extends Model
     protected $table = 'v2_server_trojan';
     protected $table = 'v2_server_trojan';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp',
+        'group_id' => 'array',
+        'tags' => 'array'
+    ];
 }
 }

+ 22 - 0
app/Models/ServerV2ray.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+class ServerV2ray extends Model
+{
+    protected $table = 'v2_server_v2ray';
+    protected $dateFormat = 'U';
+    protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp',
+        'group_id' => 'array',
+        'tlsSettings' => 'array',
+        'networkSettings' => 'array',
+        'dnsSettings' => 'array',
+        'ruleSettings' => 'array',
+        'tags' => 'array'
+    ];
+}

+ 4 - 0
app/Models/StatOrder.php

@@ -9,4 +9,8 @@ class StatOrder extends Model
     protected $table = 'v2_stat_order';
     protected $table = 'v2_stat_order';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
 }
 }

+ 4 - 0
app/Models/StatServer.php

@@ -9,4 +9,8 @@ class StatServer extends Model
     protected $table = 'v2_stat_server';
     protected $table = 'v2_stat_server';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
 }
 }

+ 4 - 0
app/Models/Ticket.php

@@ -9,4 +9,8 @@ class Ticket extends Model
     protected $table = 'v2_ticket';
     protected $table = 'v2_ticket';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
 }
 }

+ 4 - 0
app/Models/TicketMessage.php

@@ -9,4 +9,8 @@ class TicketMessage extends Model
     protected $table = 'v2_ticket_message';
     protected $table = 'v2_ticket_message';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
 }
 }

+ 4 - 0
app/Models/User.php

@@ -9,4 +9,8 @@ class User extends Model
     protected $table = 'v2_user';
     protected $table = 'v2_user';
     protected $dateFormat = 'U';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
     protected $guarded = ['id'];
+    protected $casts = [
+        'created_at' => 'timestamp',
+        'updated_at' => 'timestamp'
+    ];
 }
 }

+ 3 - 0
app/Payments/MGate.php

@@ -8,6 +8,8 @@ namespace App\Payments;
 use \Curl\Curl;
 use \Curl\Curl;
 
 
 class MGate {
 class MGate {
+    private $config;
+
     public function __construct($config)
     public function __construct($config)
     {
     {
         $this->config = $config;
         $this->config = $config;
@@ -47,6 +49,7 @@ class MGate {
         $str = http_build_query($params) . $this->config['mgate_app_secret'];
         $str = http_build_query($params) . $this->config['mgate_app_secret'];
         $params['sign'] = md5($str);
         $params['sign'] = md5($str);
         $curl = new Curl();
         $curl = new Curl();
+        $curl->setUserAgent('MGate');
         $curl->post($this->config['mgate_url'] . '/v1/gateway/fetch', http_build_query($params));
         $curl->post($this->config['mgate_url'] . '/v1/gateway/fetch', http_build_query($params));
         $result = $curl->response;
         $result = $curl->response;
         if (!$result) {
         if (!$result) {

+ 43 - 0
app/Providers/HorizonServiceProvider.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace App\Providers;
+
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Gate;
+use Laravel\Horizon\Horizon;
+use Laravel\Horizon\HorizonApplicationServiceProvider;
+
+class HorizonServiceProvider extends HorizonApplicationServiceProvider
+{
+    /**
+     * Bootstrap any application services.
+     *
+     * @return void
+     */
+    public function boot()
+    {
+        parent::boot();
+
+        // Horizon::routeSmsNotificationsTo('15556667777');
+        // Horizon::routeMailNotificationsTo('example@example.com');
+        // Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
+
+        // Horizon::night();
+    }
+
+    /**
+     * Register the Horizon gate.
+     *
+     * This gate determines who can access Horizon in non-local environments.
+     *
+     * @return void
+     */
+    protected function gate()
+    {
+        Gate::define('viewHorizon', function ($user) {
+            return in_array($user->email, [
+                //
+            ]);
+        });
+    }
+}

+ 60 - 20
app/Services/CouponService.php

@@ -8,27 +8,20 @@ use Illuminate\Support\Facades\DB;
 
 
 class CouponService
 class CouponService
 {
 {
-    public $order;
+    public $coupon;
+    public $planId;
+    public $userId;
 
 
     public function __construct($code)
     public function __construct($code)
     {
     {
         $this->coupon = Coupon::where('code', $code)->first();
         $this->coupon = Coupon::where('code', $code)->first();
-        if (!$this->coupon) {
-            abort(500, '优惠券无效');
-        }
-        if ($this->coupon->limit_use <= 0 && $this->coupon->limit_use !== NULL) {
-            abort(500, '优惠券已无可用次数');
-        }
-        if (time() < $this->coupon->started_at) {
-            abort(500, '优惠券还未到可用时间');
-        }
-        if (time() > $this->coupon->ended_at) {
-            abort(500, '优惠券已过期');
-        }
     }
     }
 
 
-    public function use(Order $order)
+    public function use(Order $order):bool
     {
     {
+        $this->setPlanId($order->plan_id);
+        $this->setUserId($order->user_id);
+        $this->check();
         switch ($this->coupon->type) {
         switch ($this->coupon->type) {
             case 1:
             case 1:
                 $order->discount_amount = $this->coupon->value;
                 $order->discount_amount = $this->coupon->value;
@@ -43,12 +36,6 @@ class CouponService
                 return false;
                 return false;
             }
             }
         }
         }
-        if ($this->coupon->limit_plan_ids) {
-            $limitPlanIds = json_decode($this->coupon->limit_plan_ids);
-            if (!in_array($order->plan_id, $limitPlanIds)) {
-                return false;
-            }
-        }
         return true;
         return true;
     }
     }
 
 
@@ -56,4 +43,57 @@ class CouponService
     {
     {
         return $this->coupon->id;
         return $this->coupon->id;
     }
     }
+
+    public function getCoupon()
+    {
+        return $this->coupon;
+    }
+
+    public function setPlanId($planId)
+    {
+        $this->planId = $planId;
+    }
+
+    public function setUserId($userId)
+    {
+        $this->userId = $userId;
+    }
+
+    public function checkLimitUseWithUser():bool
+    {
+        $usedCount = Order::where('coupon_id', $this->coupon->id)
+            ->where('user_id', $this->userId)
+            ->whereNotIn('status', [0, 2])
+            ->count();
+        if ($usedCount >= $this->coupon->limit_use_with_user) return false;
+        return true;
+    }
+
+    public function check()
+    {
+        if (!$this->coupon) {
+            abort(500, __('Invalid coupon'));
+        }
+        if ($this->coupon->limit_use <= 0 && $this->coupon->limit_use !== NULL) {
+            abort(500, __('This coupon is no longer available'));
+        }
+        if (time() < $this->coupon->started_at) {
+            abort(500, __('This coupon has not yet started'));
+        }
+        if (time() > $this->coupon->ended_at) {
+            abort(500, __('This coupon has expired'));
+        }
+        if ($this->coupon->limit_plan_ids && $this->planId) {
+            if (!in_array($this->planId, $this->coupon->limit_plan_ids)) {
+                abort(500, __('The coupon code cannot be used for this subscription'));
+            }
+        }
+        if ($this->coupon->limit_use_with_user !== NULL && $this->userId) {
+            if (!$this->checkLimitUseWithUser()) {
+                abort(500, __('The coupon can only be used :limit_use_with_user per person', [
+                    'limit_use_with_user' => $this->coupon->limit_use_with_user
+                ]));
+            }
+        }
+    }
 }
 }

+ 22 - 3
app/Services/MailService.php

@@ -12,13 +12,15 @@ class MailService
     public function remindTraffic (User $user)
     public function remindTraffic (User $user)
     {
     {
         if (!$user->remind_traffic) return;
         if (!$user->remind_traffic) return;
-        if (!$this->remindTrafficIsWarnValue(($user->u + $user->d), $user->transfer_enable)) return;
+        if (!$this->remindTrafficIsWarnValue($user->u, $user->d, $user->transfer_enable)) return;
         $flag = CacheKey::get('LAST_SEND_EMAIL_REMIND_TRAFFIC', $user->id);
         $flag = CacheKey::get('LAST_SEND_EMAIL_REMIND_TRAFFIC', $user->id);
         if (Cache::get($flag)) return;
         if (Cache::get($flag)) return;
         if (!Cache::put($flag, 1, 24 * 3600)) return;
         if (!Cache::put($flag, 1, 24 * 3600)) return;
         SendEmailJob::dispatch([
         SendEmailJob::dispatch([
             'email' => $user->email,
             'email' => $user->email,
-            'subject' => '在' . config('v2board.app_name', 'V2board') . '的流量使用已达到80%',
+            'subject' => __('The traffic usage in :app_name has reached 80%', [
+                'app_name' => config('v2board.app_name', 'V2board')
+            ]),
             'template_name' => 'remindTraffic',
             'template_name' => 'remindTraffic',
             'template_value' => [
             'template_value' => [
                 'name' => config('v2board.app_name', 'V2Board'),
                 'name' => config('v2board.app_name', 'V2Board'),
@@ -27,8 +29,25 @@ class MailService
         ]);
         ]);
     }
     }
 
 
-    private function remindTrafficIsWarnValue($ud, $transfer_enable)
+    public function remindExpire(User $user)
     {
     {
+        if (!($user->expired_at !== NULL && ($user->expired_at - 86400) < time() && $user->expired_at > time())) return;
+        SendEmailJob::dispatch([
+            'email' => $user->email,
+            'subject' => __('The service in :app_name is about to expire', [
+               'app_name' =>  config('v2board.app_name', 'V2board')
+            ]),
+            'template_name' => 'remindExpire',
+            'template_value' => [
+                'name' => config('v2board.app_name', 'V2Board'),
+                'url' => config('v2board.app_url')
+            ]
+        ]);
+    }
+
+    private function remindTrafficIsWarnValue($u, $d, $transfer_enable)
+    {
+        $ud = $u + $d;
         if (!$ud) return false;
         if (!$ud) return false;
         if (!$transfer_enable) return false;
         if (!$transfer_enable) return false;
         $percentage = ($ud / $transfer_enable) * 100;
         $percentage = ($ud / $transfer_enable) * 100;

+ 30 - 28
app/Services/OrderService.php

@@ -2,6 +2,7 @@
 
 
 namespace App\Services;
 namespace App\Services;
 
 
+use App\Jobs\OrderHandleJob;
 use App\Models\Order;
 use App\Models\Order;
 use App\Models\Plan;
 use App\Models\Plan;
 use App\Models\User;
 use App\Models\User;
@@ -37,7 +38,7 @@ class OrderService
         DB::beginTransaction();
         DB::beginTransaction();
         if ($order->surplus_order_ids) {
         if ($order->surplus_order_ids) {
             try {
             try {
-                Order::whereIn('id', json_decode($order->surplus_order_ids))->update([
+                Order::whereIn('id', $order->surplus_order_ids)->update([
                     'status' => 4
                     'status' => 4
                 ]);
                 ]);
             } catch (\Exception $e) {
             } catch (\Exception $e) {
@@ -81,25 +82,6 @@ class OrderService
         DB::commit();
         DB::commit();
     }
     }
 
 
-    public function cancel():bool
-    {
-        $order = $this->order;
-        DB::beginTransaction();
-        $order->status = 2;
-        if (!$order->save()) {
-            DB::rollBack();
-            return false;
-        }
-        if ($order->balance_amount) {
-            $userService = new UserService();
-            if (!$userService->addBalance($order->user_id, $order->balance_amount)) {
-                DB::rollBack();
-                return false;
-            }
-        }
-        DB::commit();
-        return true;
-    }
 
 
     public function setOrderType(User $user)
     public function setOrderType(User $user)
     {
     {
@@ -190,13 +172,13 @@ class OrderService
         $result = $trafficUnitPrice * $notUsedTraffic;
         $result = $trafficUnitPrice * $notUsedTraffic;
         $orderModel = Order::where('user_id', $user->id)->where('cycle', '!=', 'reset_price')->where('status', 3);
         $orderModel = Order::where('user_id', $user->id)->where('cycle', '!=', 'reset_price')->where('status', 3);
         $order->surplus_amount = $result > 0 ? $result : 0;
         $order->surplus_amount = $result > 0 ? $result : 0;
-        $order->surplus_order_ids = json_encode(array_column($orderModel->get()->toArray(), 'id'));
+        $order->surplus_order_ids = array_column($orderModel->get()->toArray(), 'id');
     }
     }
 
 
     private function orderIsUsed(Order $order):bool
     private function orderIsUsed(Order $order):bool
     {
     {
         $month = self::STR_TO_TIME[$order->cycle];
         $month = self::STR_TO_TIME[$order->cycle];
-        $orderExpireDay = strtotime('+' . $month . ' month', $order->created_at->timestamp);
+        $orderExpireDay = strtotime('+' . $month . ' month', $order->created_at);
         if ($orderExpireDay < time()) return true;
         if ($orderExpireDay < time()) return true;
         return false;
         return false;
     }
     }
@@ -229,20 +211,40 @@ class OrderService
             return;
             return;
         }
         }
         $order->surplus_amount = $orderSurplusAmount > 0 ? $orderSurplusAmount : 0;
         $order->surplus_amount = $orderSurplusAmount > 0 ? $orderSurplusAmount : 0;
-        $order->surplus_order_ids = json_encode(array_column($orders->toArray(), 'id'));
+        $order->surplus_order_ids = array_column($orders->toArray(), 'id');
     }
     }
 
 
-    public function success(string $callbackNo)
+    public function paid(string $callbackNo)
     {
     {
         $order = $this->order;
         $order = $this->order;
-        if ($order->status !== 0) {
-            return true;
-        }
+        if ($order->status !== 0) return true;
         $order->status = 1;
         $order->status = 1;
+        $order->paid_at = time();
         $order->callback_no = $callbackNo;
         $order->callback_no = $callbackNo;
-        return $order->save();
+        if (!$order->save()) return false;
+        OrderHandleJob::dispatch($order->trade_no);
+        return true;
     }
     }
 
 
+    public function cancel():bool
+    {
+        $order = $this->order;
+        DB::beginTransaction();
+        $order->status = 2;
+        if (!$order->save()) {
+            DB::rollBack();
+            return false;
+        }
+        if ($order->balance_amount) {
+            $userService = new UserService();
+            if (!$userService->addBalance($order->user_id, $order->balance_amount)) {
+                DB::rollBack();
+                return false;
+            }
+        }
+        DB::commit();
+        return true;
+    }
 
 
     private function buyByResetTraffic()
     private function buyByResetTraffic()
     {
     {

+ 1 - 1
app/Services/PaymentService.php

@@ -22,7 +22,7 @@ class PaymentService
         if ($uuid) $payment = Payment::where('uuid', $uuid)->first()->toArray();
         if ($uuid) $payment = Payment::where('uuid', $uuid)->first()->toArray();
         $this->config = [];
         $this->config = [];
         if (isset($payment)) {
         if (isset($payment)) {
-            $this->config = json_decode($payment['config'], true);
+            $this->config = $payment['config'];
             $this->config['enable'] = $payment['enable'];
             $this->config['enable'] = $payment['enable'];
             $this->config['id'] = $payment['id'];
             $this->config['id'] = $payment['id'];
             $this->config['uuid'] = $payment['uuid'];
             $this->config['uuid'] = $payment['uuid'];

+ 31 - 49
app/Services/ServerService.php

@@ -5,7 +5,7 @@ namespace App\Services;
 use App\Models\ServerLog;
 use App\Models\ServerLog;
 use App\Models\ServerShadowsocks;
 use App\Models\ServerShadowsocks;
 use App\Models\User;
 use App\Models\User;
-use App\Models\Server;
+use App\Models\ServerV2ray;
 use App\Models\ServerTrojan;
 use App\Models\ServerTrojan;
 use App\Utils\CacheKey;
 use App\Utils\CacheKey;
 use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\Cache;
@@ -18,14 +18,14 @@ class ServerService
     public function getV2ray(User $user, $all = false):array
     public function getV2ray(User $user, $all = false):array
     {
     {
         $servers = [];
         $servers = [];
-        $model = Server::orderBy('sort', 'ASC');
+        $model = ServerV2ray::orderBy('sort', 'ASC');
         if (!$all) {
         if (!$all) {
             $model->where('show', 1);
             $model->where('show', 1);
         }
         }
         $v2ray = $model->get();
         $v2ray = $model->get();
         for ($i = 0; $i < count($v2ray); $i++) {
         for ($i = 0; $i < count($v2ray); $i++) {
             $v2ray[$i]['type'] = 'v2ray';
             $v2ray[$i]['type'] = 'v2ray';
-            $groupId = json_decode($v2ray[$i]['group_id']);
+            $groupId = $v2ray[$i]['group_id'];
             if (in_array($user->group_id, $groupId)) {
             if (in_array($user->group_id, $groupId)) {
                 if ($v2ray[$i]['parent_id']) {
                 if ($v2ray[$i]['parent_id']) {
                     $v2ray[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $v2ray[$i]['parent_id']));
                     $v2ray[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $v2ray[$i]['parent_id']));
@@ -50,7 +50,7 @@ class ServerService
         $trojan = $model->get();
         $trojan = $model->get();
         for ($i = 0; $i < count($trojan); $i++) {
         for ($i = 0; $i < count($trojan); $i++) {
             $trojan[$i]['type'] = 'trojan';
             $trojan[$i]['type'] = 'trojan';
-            $groupId = json_decode($trojan[$i]['group_id']);
+            $groupId = $trojan[$i]['group_id'];
             if (in_array($user->group_id, $groupId)) {
             if (in_array($user->group_id, $groupId)) {
                 if ($trojan[$i]['parent_id']) {
                 if ($trojan[$i]['parent_id']) {
                     $trojan[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $trojan[$i]['parent_id']));
                     $trojan[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $trojan[$i]['parent_id']));
@@ -73,7 +73,7 @@ class ServerService
         $shadowsocks = $model->get();
         $shadowsocks = $model->get();
         for ($i = 0; $i < count($shadowsocks); $i++) {
         for ($i = 0; $i < count($shadowsocks); $i++) {
             $shadowsocks[$i]['type'] = 'shadowsocks';
             $shadowsocks[$i]['type'] = 'shadowsocks';
-            $groupId = json_decode($shadowsocks[$i]['group_id']);
+            $groupId = $shadowsocks[$i]['group_id'];
             if (in_array($user->group_id, $groupId)) {
             if (in_array($user->group_id, $groupId)) {
                 if ($shadowsocks[$i]['parent_id']) {
                 if ($shadowsocks[$i]['parent_id']) {
                     $shadowsocks[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $shadowsocks[$i]['parent_id']));
                     $shadowsocks[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $shadowsocks[$i]['parent_id']));
@@ -123,7 +123,7 @@ class ServerService
 
 
     public function getV2RayConfig(int $nodeId, int $localPort)
     public function getV2RayConfig(int $nodeId, int $localPort)
     {
     {
-        $server = Server::find($nodeId);
+        $server = ServerV2ray::find($nodeId);
         if (!$server) {
         if (!$server) {
             abort(500, '节点不存在');
             abort(500, '节点不存在');
         }
         }
@@ -156,10 +156,10 @@ class ServerService
         return $json;
         return $json;
     }
     }
 
 
-    private function setDns(Server $server, object $json)
+    private function setDns(ServerV2ray $server, object $json)
     {
     {
         if ($server->dnsSettings) {
         if ($server->dnsSettings) {
-            $dns = json_decode($server->dnsSettings);
+            $dns = $server->dnsSettings;
             if (isset($dns->servers)) {
             if (isset($dns->servers)) {
                 array_push($dns->servers, '1.1.1.1');
                 array_push($dns->servers, '1.1.1.1');
                 array_push($dns->servers, 'localhost');
                 array_push($dns->servers, 'localhost');
@@ -169,41 +169,41 @@ class ServerService
         }
         }
     }
     }
 
 
-    private function setNetwork(Server $server, object $json)
+    private function setNetwork(ServerV2ray $server, object $json)
     {
     {
         if ($server->networkSettings) {
         if ($server->networkSettings) {
             switch ($server->network) {
             switch ($server->network) {
                 case 'tcp':
                 case 'tcp':
-                    $json->inbound->streamSettings->tcpSettings = json_decode($server->networkSettings);
+                    $json->inbound->streamSettings->tcpSettings = $server->networkSettings;
                     break;
                     break;
                 case 'kcp':
                 case 'kcp':
-                    $json->inbound->streamSettings->kcpSettings = json_decode($server->networkSettings);
+                    $json->inbound->streamSettings->kcpSettings = $server->networkSettings;
                     break;
                     break;
                 case 'ws':
                 case 'ws':
-                    $json->inbound->streamSettings->wsSettings = json_decode($server->networkSettings);
+                    $json->inbound->streamSettings->wsSettings = $server->networkSettings;
                     break;
                     break;
                 case 'http':
                 case 'http':
-                    $json->inbound->streamSettings->httpSettings = json_decode($server->networkSettings);
+                    $json->inbound->streamSettings->httpSettings = $server->networkSettings;
                     break;
                     break;
                 case 'domainsocket':
                 case 'domainsocket':
-                    $json->inbound->streamSettings->dsSettings = json_decode($server->networkSettings);
+                    $json->inbound->streamSettings->dsSettings = $server->networkSettings;
                     break;
                     break;
                 case 'quic':
                 case 'quic':
-                    $json->inbound->streamSettings->quicSettings = json_decode($server->networkSettings);
+                    $json->inbound->streamSettings->quicSettings = $server->networkSettings;
                     break;
                     break;
                 case 'grpc':
                 case 'grpc':
-                    $json->inbound->streamSettings->grpcSettings = json_decode($server->networkSettings);
+                    $json->inbound->streamSettings->grpcSettings = $server->networkSettings;
                     break;
                     break;
             }
             }
         }
         }
     }
     }
 
 
-    private function setRule(Server $server, object $json)
+    private function setRule(ServerV2ray $server, object $json)
     {
     {
         $domainRules = array_filter(explode(PHP_EOL, config('v2board.server_v2ray_domain')));
         $domainRules = array_filter(explode(PHP_EOL, config('v2board.server_v2ray_domain')));
         $protocolRules = array_filter(explode(PHP_EOL, config('v2board.server_v2ray_protocol')));
         $protocolRules = array_filter(explode(PHP_EOL, config('v2board.server_v2ray_protocol')));
         if ($server->ruleSettings) {
         if ($server->ruleSettings) {
-            $ruleSettings = json_decode($server->ruleSettings);
+            $ruleSettings = $server->ruleSettings;
             // domain
             // domain
             if (isset($ruleSettings->domain)) {
             if (isset($ruleSettings->domain)) {
                 $ruleSettings->domain = array_filter($ruleSettings->domain);
                 $ruleSettings->domain = array_filter($ruleSettings->domain);
@@ -238,10 +238,10 @@ class ServerService
         }
         }
     }
     }
 
 
-    private function setTls(Server $server, object $json)
+    private function setTls(ServerV2ray $server, object $json)
     {
     {
         if ((int)$server->tls) {
         if ((int)$server->tls) {
-            $tlsSettings = json_decode($server->tlsSettings);
+            $tlsSettings = $server->tlsSettings;
             $json->inbound->streamSettings->security = 'tls';
             $json->inbound->streamSettings->security = 'tls';
             $tls = (object)[
             $tls = (object)[
                 'certificateFile' => '/root/.cert/server.crt',
                 'certificateFile' => '/root/.cert/server.crt',
@@ -260,20 +260,23 @@ class ServerService
 
 
     public function log(int $userId, int $serverId, int $u, int $d, float $rate, string $method)
     public function log(int $userId, int $serverId, int $u, int $d, float $rate, string $method)
     {
     {
-        if (($u + $d) <= 10240) return;
-        $timestamp = strtotime(date('Y-m-d H:0'));
+        if (($u + $d) < 10240) return true;
+        $timestamp = strtotime(date('Y-m-d'));
         $serverLog = ServerLog::where('log_at', '>=', $timestamp)
         $serverLog = ServerLog::where('log_at', '>=', $timestamp)
             ->where('log_at', '<', $timestamp + 3600)
             ->where('log_at', '<', $timestamp + 3600)
             ->where('server_id', $serverId)
             ->where('server_id', $serverId)
             ->where('user_id', $userId)
             ->where('user_id', $userId)
             ->where('rate', $rate)
             ->where('rate', $rate)
             ->where('method', $method)
             ->where('method', $method)
-            ->lockForUpdate()
             ->first();
             ->first();
         if ($serverLog) {
         if ($serverLog) {
-            $serverLog->u = $serverLog->u + $u;
-            $serverLog->d = $serverLog->d + $d;
-            $serverLog->save();
+            try {
+                $serverLog->increment('u', $u);
+                $serverLog->increment('d', $d);
+                return true;
+            } catch (\Exception $e) {
+                return false;
+            }
         } else {
         } else {
             $serverLog = new ServerLog();
             $serverLog = new ServerLog();
             $serverLog->user_id = $userId;
             $serverLog->user_id = $userId;
@@ -283,7 +286,7 @@ class ServerService
             $serverLog->rate = $rate;
             $serverLog->rate = $rate;
             $serverLog->log_at = $timestamp;
             $serverLog->log_at = $timestamp;
             $serverLog->method = $method;
             $serverLog->method = $method;
-            $serverLog->save();
+            return $serverLog->save();
         }
         }
     }
     }
 
 
@@ -292,32 +295,15 @@ class ServerService
         $server = ServerShadowsocks::orderBy('sort', 'ASC')->get();
         $server = ServerShadowsocks::orderBy('sort', 'ASC')->get();
         for ($i = 0; $i < count($server); $i++) {
         for ($i = 0; $i < count($server); $i++) {
             $server[$i]['type'] = 'shadowsocks';
             $server[$i]['type'] = 'shadowsocks';
-            if (!empty($server[$i]['tags'])) {
-                $server[$i]['tags'] = json_decode($server[$i]['tags']);
-            }
-            $server[$i]['group_id'] = json_decode($server[$i]['group_id']);
         }
         }
         return $server->toArray();
         return $server->toArray();
     }
     }
 
 
     public function getV2rayServers()
     public function getV2rayServers()
     {
     {
-        $server = Server::orderBy('sort', 'ASC')->get();
+        $server = ServerV2ray::orderBy('sort', 'ASC')->get();
         for ($i = 0; $i < count($server); $i++) {
         for ($i = 0; $i < count($server); $i++) {
             $server[$i]['type'] = 'v2ray';
             $server[$i]['type'] = 'v2ray';
-            if (!empty($server[$i]['tags'])) {
-                $server[$i]['tags'] = json_decode($server[$i]['tags']);
-            }
-            if (!empty($server[$i]['dnsSettings'])) {
-                $server[$i]['dnsSettings'] = json_decode($server[$i]['dnsSettings']);
-            }
-            if (!empty($server[$i]['tlsSettings'])) {
-                $server[$i]['tlsSettings'] = json_decode($server[$i]['tlsSettings']);
-            }
-            if (!empty($server[$i]['ruleSettings'])) {
-                $server[$i]['ruleSettings'] = json_decode($server[$i]['ruleSettings']);
-            }
-            $server[$i]['group_id'] = json_decode($server[$i]['group_id']);
         }
         }
         return $server->toArray();
         return $server->toArray();
     }
     }
@@ -327,10 +313,6 @@ class ServerService
         $server = ServerTrojan::orderBy('sort', 'ASC')->get();
         $server = ServerTrojan::orderBy('sort', 'ASC')->get();
         for ($i = 0; $i < count($server); $i++) {
         for ($i = 0; $i < count($server); $i++) {
             $server[$i]['type'] = 'trojan';
             $server[$i]['type'] = 'trojan';
-            if (!empty($server[$i]['tags'])) {
-                $server[$i]['tags'] = json_decode($server[$i]['tags']);
-            }
-            $server[$i]['group_id'] = json_decode($server[$i]['group_id']);
         }
         }
         return $server->toArray();
         return $server->toArray();
     }
     }

+ 1 - 0
app/Services/TelegramService.php

@@ -40,6 +40,7 @@ class TelegramService {
         $curl->get($this->api . $method . '?' . http_build_query($params));
         $curl->get($this->api . $method . '?' . http_build_query($params));
         $response = $curl->response;
         $response = $curl->response;
         $curl->close();
         $curl->close();
+        if (!isset($response->ok)) abort(500, '请求失败');
         if (!$response->ok) {
         if (!$response->ok) {
             abort(500, '来自TG的错误:' . $response->description);
             abort(500, '来自TG的错误:' . $response->description);
         }
         }

+ 1 - 0
app/Services/TicketService.php

@@ -12,6 +12,7 @@ use Illuminate\Support\Facades\DB;
 class TicketService {
 class TicketService {
     public function replyByAdmin($ticketId, $message, $userId):void
     public function replyByAdmin($ticketId, $message, $userId):void
     {
     {
+        if ($message)
         $ticket = Ticket::where('id', $ticketId)
         $ticket = Ticket::where('id', $ticketId)
             ->first();
             ->first();
         if (!$ticket) {
         if (!$ticket) {

+ 6 - 28
app/Services/UserService.php

@@ -2,9 +2,11 @@
 
 
 namespace App\Services;
 namespace App\Services;
 
 
+use App\Jobs\ServerLogJob;
+use App\Jobs\TrafficFetchJob;
 use App\Models\InviteCode;
 use App\Models\InviteCode;
 use App\Models\Order;
 use App\Models\Order;
-use App\Models\Server;
+use App\Models\ServerV2ray;
 use App\Models\Ticket;
 use App\Models\Ticket;
 use App\Models\User;
 use App\Models\User;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\DB;
@@ -80,33 +82,9 @@ class UserService
         return true;
         return true;
     }
     }
 
 
-    public function trafficFetch(int $u, int $d, int $userId, object $server, string $protocol):bool
+    public function trafficFetch(int $u, int $d, int $userId, object $server, string $protocol)
     {
     {
-        $user = User::lockForUpdate()
-            ->find($userId);
-        if (!$user) {
-            return true;
-        }
-        $user->t = time();
-        $user->u = $user->u + $u;
-        $user->d = $user->d + $d;
-        if (!$user->save()) {
-            return false;
-        }
-        $mailService = new MailService();
-        $serverService = new ServerService();
-        try {
-            $mailService->remindTraffic($user);
-            $serverService->log(
-                $userId,
-                $server->id,
-                $u,
-                $d,
-                $server->rate,
-                $protocol
-            );
-        } catch (\Exception $e) {
-        }
-        return true;
+        TrafficFetchJob::dispatch($u, $d, $userId, $server, $protocol);
+        ServerLogJob::dispatch($u, $d, $userId, $server, $protocol);
     }
     }
 }
 }

+ 1 - 1
app/Utils/CacheKey.php

@@ -17,7 +17,7 @@ class CacheKey
         'SERVER_SHADOWSOCKS_LAST_CHECK_AT' => 'ss节点最后检查时间',
         'SERVER_SHADOWSOCKS_LAST_CHECK_AT' => 'ss节点最后检查时间',
         'SERVER_SHADOWSOCKS_LAST_PUSH_AT' => 'ss节点最后推送时间',
         'SERVER_SHADOWSOCKS_LAST_PUSH_AT' => 'ss节点最后推送时间',
         'TEMP_TOKEN' => '临时令牌',
         'TEMP_TOKEN' => '临时令牌',
-        'LAST_SEND_EMAIL_REMIND_TRAFFIC'
+        'LAST_SEND_EMAIL_REMIND_TRAFFIC' => '最后发送流量邮件提醒'
     ];
     ];
 
 
     public static function get(string $key, $uniqueValue)
     public static function get(string $key, $uniqueValue)

+ 19 - 2
app/Utils/Helper.php

@@ -2,7 +2,7 @@
 
 
 namespace App\Utils;
 namespace App\Utils;
 
 
-use App\Models\Server;
+use App\Models\ServerV2ray;
 use App\Models\ServerShadowsocks;
 use App\Models\ServerShadowsocks;
 use App\Models\ServerTrojan;
 use App\Models\ServerTrojan;
 use App\Models\User;
 use App\Models\User;
@@ -23,6 +23,12 @@ class Helper
         return md5(vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)) . '-' . time());
         return md5(vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)) . '-' . time());
     }
     }
 
 
+    public static function generateOrderNo(): string
+    {
+        $randomChar = rand(10000, 99999);
+        return date('YmdHms') . $randomChar;
+    }
+
     public static function exchange($from, $to)
     public static function exchange($from, $to)
     {
     {
         $result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
         $result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
@@ -58,11 +64,12 @@ class Helper
         return $str;
         return $str;
     }
     }
 
 
-    public static function multiPasswordVerify($algo, $password, $hash)
+    public static function multiPasswordVerify($algo, $salt, $password, $hash)
     {
     {
         switch($algo) {
         switch($algo) {
             case 'md5': return md5($password) === $hash;
             case 'md5': return md5($password) === $hash;
             case 'sha256': return hash('sha256', $password) === $hash;
             case 'sha256': return hash('sha256', $password) === $hash;
+            case 'md5salt': return md5($password . $salt) === $hash;
             default: return password_verify($password, $hash);
             default: return password_verify($password, $hash);
         }
         }
     }
     }
@@ -95,4 +102,14 @@ class Helper
             return round($byte, 2) . ' B';
             return round($byte, 2) . ' B';
         }
         }
     }
     }
+
+    public static function getSubscribeHost()
+    {
+        $subscribeUrl = config('v2board.app_url');
+        $subscribeUrls = explode(',', config('v2board.subscribe_url'));
+        if ($subscribeUrls && $subscribeUrls[0]) {
+            $subscribeUrl = $subscribeUrls[rand(0, count($subscribeUrls) - 1)];
+        }
+        return $subscribeUrl;
+    }
 }
 }

+ 17 - 11
composer.json

@@ -1,31 +1,37 @@
 {
 {
     "name": "v2board/v2board",
     "name": "v2board/v2board",
     "type": "project",
     "type": "project",
-    "description": "v2board is a v2ray manage.",
+    "description": "v2board is a proxy protocol manage.",
     "keywords": [
     "keywords": [
         "v2board",
         "v2board",
         "v2ray",
         "v2ray",
+        "shadowsocks",
+        "trojan",
         "laravel"
         "laravel"
     ],
     ],
     "license": "MIT",
     "license": "MIT",
     "require": {
     "require": {
-        "php": "^7.2",
-        "fideloper/proxy": "^4.0",
+        "php": "^7.2.5|^8.0",
+        "fideloper/proxy": "^4.4",
+        "fruitcake/laravel-cors": "^2.0",
         "google/recaptcha": "^1.2",
         "google/recaptcha": "^1.2",
-        "laravel/framework": "^6.0",
-        "laravel/tinker": "^1.0",
-        "lokielse/omnipay-alipay": "3.0.6",
+        "guzzlehttp/guzzle": "^6.3.1|^7.0.1",
+        "laravel/framework": "^7.29",
+        "laravel/horizon": "^4.3.5",
+        "laravel/tinker": "^2.5",
+        "linfo/linfo": "^4.0",
+        "lokielse/omnipay-alipay": "3.1.2",
         "lokielse/omnipay-wechatpay": "^3.0",
         "lokielse/omnipay-wechatpay": "^3.0",
         "php-curl-class/php-curl-class": "^8.6",
         "php-curl-class/php-curl-class": "^8.6",
         "stripe/stripe-php": "^7.36.1",
         "stripe/stripe-php": "^7.36.1",
         "symfony/yaml": "^4.3"
         "symfony/yaml": "^4.3"
     },
     },
     "require-dev": {
     "require-dev": {
-        "facade/ignition": "^1.4",
-        "fzaninotto/faker": "^1.4",
-        "mockery/mockery": "^1.0",
-        "nunomaduro/collision": "^3.0",
-        "phpunit/phpunit": "^8.0"
+        "facade/ignition": "^2.0",
+        "fakerphp/faker": "^1.9.1",
+        "mockery/mockery": "^1.3.1",
+        "nunomaduro/collision": "^4.3",
+        "phpunit/phpunit": "^8.5.8|^9.3.3"
     },
     },
     "config": {
     "config": {
         "optimize-autoloader": true,
         "optimize-autoloader": true,

+ 2 - 1
config/app.php

@@ -173,6 +173,7 @@ return [
         App\Providers\AuthServiceProvider::class,
         App\Providers\AuthServiceProvider::class,
         // App\Providers\BroadcastServiceProvider::class,
         // App\Providers\BroadcastServiceProvider::class,
         App\Providers\EventServiceProvider::class,
         App\Providers\EventServiceProvider::class,
+        App\Providers\HorizonServiceProvider::class,
         App\Providers\RouteServiceProvider::class,
         App\Providers\RouteServiceProvider::class,
 
 
     ],
     ],
@@ -236,5 +237,5 @@ return [
     | The only modification by laravel config
     | The only modification by laravel config
     |
     |
     */
     */
-    'version' => '1.5.2.1627559775390'
+    'version' => '1.5.3.1628409393360'
 ];
 ];

+ 34 - 0
config/cors.php

@@ -0,0 +1,34 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Cross-Origin Resource Sharing (CORS) Configuration
+    |--------------------------------------------------------------------------
+    |
+    | Here you may configure your settings for cross-origin resource sharing
+    | or "CORS". This determines what cross-origin operations may execute
+    | in web browsers. You are free to adjust these settings as needed.
+    |
+    | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
+    |
+    */
+
+    'paths' => ['api/*'],
+
+    'allowed_methods' => ['*'],
+
+    'allowed_origins' => ['*'],
+
+    'allowed_origins_patterns' => [],
+
+    'allowed_headers' => ['*'],
+
+    'exposed_headers' => [],
+
+    'max_age' => 0,
+
+    'supports_credentials' => false,
+
+];

+ 191 - 0
config/horizon.php

@@ -0,0 +1,191 @@
+<?php
+
+use Illuminate\Support\Str;
+use Linfo\Linfo;
+
+$lInfo = new Linfo();
+$parser = $lInfo->getParser();
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Horizon Domain
+    |--------------------------------------------------------------------------
+    |
+    | This is the subdomain where Horizon will be accessible from. If this
+    | setting is null, Horizon will reside under the same domain as the
+    | application. Otherwise, this value will serve as the subdomain.
+    |
+    */
+
+    'domain' => null,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Horizon Path
+    |--------------------------------------------------------------------------
+    |
+    | This is the URI path where Horizon will be accessible from. Feel free
+    | to change this path to anything you like. Note that the URI will not
+    | affect the paths of its internal API that aren't exposed to users.
+    |
+    */
+
+    'path' => 'monitor',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Horizon Redis Connection
+    |--------------------------------------------------------------------------
+    |
+    | This is the name of the Redis connection where Horizon will store the
+    | meta information required for it to function. It includes the list
+    | of supervisors, failed jobs, job metrics, and other information.
+    |
+    */
+
+    'use' => 'default',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Horizon Redis Prefix
+    |--------------------------------------------------------------------------
+    |
+    | This prefix will be used when storing all Horizon data in Redis. You
+    | may modify the prefix when you are running multiple installations
+    | of Horizon on the same server so that they don't have problems.
+    |
+    */
+
+    'prefix' => env(
+        'HORIZON_PREFIX',
+        Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:'
+    ),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Horizon Route Middleware
+    |--------------------------------------------------------------------------
+    |
+    | These middleware will get attached onto each Horizon route, giving you
+    | the chance to add your own middleware to this list or change any of
+    | the existing middleware. Or, you can simply stick with this list.
+    |
+    */
+
+    'middleware' => ['web', 'admin'],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Queue Wait Time Thresholds
+    |--------------------------------------------------------------------------
+    |
+    | This option allows you to configure when the LongWaitDetected event
+    | will be fired. Every connection / queue combination may have its
+    | own, unique threshold (in seconds) before this event is fired.
+    |
+    */
+
+    'waits' => [
+        'redis:default' => 60,
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Job Trimming Times
+    |--------------------------------------------------------------------------
+    |
+    | Here you can configure for how long (in minutes) you desire Horizon to
+    | persist the recent and failed jobs. Typically, recent jobs are kept
+    | for one hour while all failed jobs are stored for an entire week.
+    |
+    */
+
+    'trim' => [
+        'recent' => 60,
+        'pending' => 60,
+        'completed' => 60,
+        'recent_failed' => 10080,
+        'failed' => 10080,
+        'monitored' => 10080,
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Metrics
+    |--------------------------------------------------------------------------
+    |
+    | Here you can configure how many snapshots should be kept to display in
+    | the metrics graph. This will get used in combination with Horizon's
+    | `horizon:snapshot` schedule to define how long to retain metrics.
+    |
+    */
+
+    'metrics' => [
+        'trim_snapshots' => [
+            'job' => 24,
+            'queue' => 24,
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Fast Termination
+    |--------------------------------------------------------------------------
+    |
+    | When this option is enabled, Horizon's "terminate" command will not
+    | wait on all of the workers to terminate unless the --wait option
+    | is provided. Fast termination can shorten deployment delay by
+    | allowing a new instance of Horizon to start while the last
+    | instance will continue to terminate each of its workers.
+    |
+    */
+
+    'fast_termination' => false,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Memory Limit (MB)
+    |--------------------------------------------------------------------------
+    |
+    | This value describes the maximum amount of memory the Horizon worker
+    | may consume before it is terminated and restarted. You should set
+    | this value according to the resources available to your server.
+    |
+    */
+
+    'memory_limit' => 32,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Queue Worker Configuration
+    |--------------------------------------------------------------------------
+    |
+    | Here you may define the queue worker settings used by your application
+    | in all environments. These supervisors and settings handle all your
+    | queued jobs and will be provisioned by Horizon during deployment.
+    |
+    */
+
+    'environments' => [
+        'local' => [
+            'V2board' => [
+                'connection' => 'redis',
+                'queue' => [
+                    'traffic_fetch',
+                    'server_log',
+                    'send_email',
+                    'send_telegram',
+                    'stat_server',
+                    'order_handle'
+                ],
+                'balance' => 'auto',
+                'minProcesses' => 1,
+                'maxProcesses' => (int)ceil($parser->getRam()['total'] / 1024 / 1024 / 1024 * 6),
+                'tries' => 1,
+                'nice' => 0,
+            ],
+        ],
+    ],
+];

+ 53 - 33
database/install.sql

@@ -19,6 +19,20 @@ CREATE TABLE `failed_jobs` (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
 
 
 
 
+DROP TABLE IF EXISTS `v2_commission_log`;
+CREATE TABLE `v2_commission_log` (
+                                     `id` int(11) NOT NULL AUTO_INCREMENT,
+                                     `invite_user_id` int(11) NOT NULL,
+                                     `user_id` int(11) NOT NULL,
+                                     `trade_no` char(36) NOT NULL,
+                                     `order_amount` int(11) NOT NULL,
+                                     `get_amount` int(11) NOT NULL,
+                                     `created_at` int(11) NOT NULL,
+                                     `updated_at` int(11) NOT NULL,
+                                     PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
 DROP TABLE IF EXISTS `v2_coupon`;
 DROP TABLE IF EXISTS `v2_coupon`;
 CREATE TABLE `v2_coupon` (
 CREATE TABLE `v2_coupon` (
                              `id` int(11) NOT NULL AUTO_INCREMENT,
                              `id` int(11) NOT NULL AUTO_INCREMENT,
@@ -27,6 +41,7 @@ CREATE TABLE `v2_coupon` (
                              `type` tinyint(1) NOT NULL,
                              `type` tinyint(1) NOT NULL,
                              `value` int(11) NOT NULL,
                              `value` int(11) NOT NULL,
                              `limit_use` int(11) DEFAULT NULL,
                              `limit_use` int(11) DEFAULT NULL,
+                             `limit_use_with_user` int(11) DEFAULT NULL,
                              `limit_plan_ids` varchar(255) DEFAULT NULL,
                              `limit_plan_ids` varchar(255) DEFAULT NULL,
                              `started_at` int(11) NOT NULL,
                              `started_at` int(11) NOT NULL,
                              `ended_at` int(11) NOT NULL,
                              `ended_at` int(11) NOT NULL,
@@ -110,6 +125,7 @@ CREATE TABLE `v2_order` (
                             `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待支付1开通中2已取消3已完成4已折抵',
                             `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待支付1开通中2已取消3已完成4已折抵',
                             `commission_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待确认1发放中2有效3无效',
                             `commission_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待确认1发放中2有效3无效',
                             `commission_balance` int(11) NOT NULL DEFAULT '0',
                             `commission_balance` int(11) NOT NULL DEFAULT '0',
+                            `paid_at` int(11) DEFAULT NULL,
                             `created_at` int(11) NOT NULL,
                             `created_at` int(11) NOT NULL,
                             `updated_at` int(11) NOT NULL,
                             `updated_at` int(11) NOT NULL,
                             PRIMARY KEY (`id`)
                             PRIMARY KEY (`id`)
@@ -149,40 +165,13 @@ CREATE TABLE `v2_plan` (
                            `three_year_price` int(11) DEFAULT NULL,
                            `three_year_price` int(11) DEFAULT NULL,
                            `onetime_price` int(11) DEFAULT NULL,
                            `onetime_price` int(11) DEFAULT NULL,
                            `reset_price` int(11) DEFAULT NULL,
                            `reset_price` int(11) DEFAULT NULL,
+                           `reset_traffic_method` tinyint(1) DEFAULT NULL,
                            `created_at` int(11) NOT NULL,
                            `created_at` int(11) NOT NULL,
                            `updated_at` int(11) NOT NULL,
                            `updated_at` int(11) NOT NULL,
                            PRIMARY KEY (`id`)
                            PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
 
 
-DROP TABLE IF EXISTS `v2_server`;
-CREATE TABLE `v2_server` (
-                             `id` int(11) NOT NULL AUTO_INCREMENT,
-                             `group_id` varchar(255) NOT NULL,
-                             `name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
-                             `parent_id` int(11) DEFAULT NULL,
-                             `host` varchar(255) NOT NULL,
-                             `port` int(11) NOT NULL,
-                             `server_port` int(11) NOT NULL,
-                             `tls` tinyint(4) NOT NULL DEFAULT '0',
-                             `tags` varchar(255) DEFAULT NULL,
-                             `rate` varchar(11) NOT NULL,
-                             `network` text NOT NULL,
-                             `alter_id` int(11) NOT NULL DEFAULT '1',
-                             `settings` text,
-                             `rules` text,
-                             `networkSettings` text,
-                             `tlsSettings` text,
-                             `ruleSettings` text,
-                             `dnsSettings` text,
-                             `show` tinyint(1) NOT NULL DEFAULT '0',
-                             `sort` int(11) DEFAULT NULL,
-                             `created_at` int(11) NOT NULL,
-                             `updated_at` int(11) NOT NULL,
-                             PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-
 DROP TABLE IF EXISTS `v2_server_group`;
 DROP TABLE IF EXISTS `v2_server_group`;
 CREATE TABLE `v2_server_group` (
 CREATE TABLE `v2_server_group` (
                                    `id` int(11) NOT NULL AUTO_INCREMENT,
                                    `id` int(11) NOT NULL AUTO_INCREMENT,
@@ -206,7 +195,9 @@ CREATE TABLE `v2_server_log` (
                                  `created_at` int(11) NOT NULL,
                                  `created_at` int(11) NOT NULL,
                                  `updated_at` int(11) NOT NULL,
                                  `updated_at` int(11) NOT NULL,
                                  PRIMARY KEY (`id`),
                                  PRIMARY KEY (`id`),
-                                 KEY `log_at` (`log_at`)
+                                 KEY `log_at` (`log_at`),
+                                 KEY `user_id` (`user_id`),
+                                 KEY `server_id` (`server_id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
 
 
@@ -251,6 +242,34 @@ CREATE TABLE `v2_server_trojan` (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='trojan伺服器表';
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='trojan伺服器表';
 
 
 
 
+DROP TABLE IF EXISTS `v2_server_v2ray`;
+CREATE TABLE `v2_server_v2ray` (
+                                   `id` int(11) NOT NULL AUTO_INCREMENT,
+                                   `group_id` varchar(255) NOT NULL,
+                                   `name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
+                                   `parent_id` int(11) DEFAULT NULL,
+                                   `host` varchar(255) NOT NULL,
+                                   `port` int(11) NOT NULL,
+                                   `server_port` int(11) NOT NULL,
+                                   `tls` tinyint(4) NOT NULL DEFAULT '0',
+                                   `tags` varchar(255) DEFAULT NULL,
+                                   `rate` varchar(11) NOT NULL,
+                                   `network` text NOT NULL,
+                                   `alter_id` int(11) NOT NULL DEFAULT '1',
+                                   `settings` text,
+                                   `rules` text,
+                                   `networkSettings` text,
+                                   `tlsSettings` text,
+                                   `ruleSettings` text,
+                                   `dnsSettings` text,
+                                   `show` tinyint(1) NOT NULL DEFAULT '0',
+                                   `sort` int(11) DEFAULT NULL,
+                                   `created_at` int(11) NOT NULL,
+                                   `updated_at` int(11) NOT NULL,
+                                   PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
 DROP TABLE IF EXISTS `v2_stat_order`;
 DROP TABLE IF EXISTS `v2_stat_order`;
 CREATE TABLE `v2_stat_order` (
 CREATE TABLE `v2_stat_order` (
                                  `id` int(11) NOT NULL AUTO_INCREMENT,
                                  `id` int(11) NOT NULL AUTO_INCREMENT,
@@ -304,7 +323,7 @@ CREATE TABLE `v2_ticket_message` (
                                      `id` int(11) NOT NULL AUTO_INCREMENT,
                                      `id` int(11) NOT NULL AUTO_INCREMENT,
                                      `user_id` int(11) NOT NULL,
                                      `user_id` int(11) NOT NULL,
                                      `ticket_id` int(11) NOT NULL,
                                      `ticket_id` int(11) NOT NULL,
-                                     `message` varchar(255) NOT NULL,
+                                     `message` text CHARACTER SET utf8mb4 NOT NULL,
                                      `created_at` int(11) NOT NULL,
                                      `created_at` int(11) NOT NULL,
                                      `updated_at` int(11) NOT NULL,
                                      `updated_at` int(11) NOT NULL,
                                      PRIMARY KEY (`id`)
                                      PRIMARY KEY (`id`)
@@ -319,6 +338,7 @@ CREATE TABLE `v2_user` (
                            `email` varchar(64) NOT NULL,
                            `email` varchar(64) NOT NULL,
                            `password` varchar(64) NOT NULL,
                            `password` varchar(64) NOT NULL,
                            `password_algo` char(10) DEFAULT NULL,
                            `password_algo` char(10) DEFAULT NULL,
+                           `password_salt` char(10) DEFAULT NULL,
                            `balance` int(11) NOT NULL DEFAULT '0',
                            `balance` int(11) NOT NULL DEFAULT '0',
                            `discount` int(11) DEFAULT NULL,
                            `discount` int(11) DEFAULT NULL,
                            `commission_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0: system 1: cycle 2: onetime',
                            `commission_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0: system 1: cycle 2: onetime',
@@ -336,8 +356,8 @@ CREATE TABLE `v2_user` (
                            `uuid` varchar(36) NOT NULL,
                            `uuid` varchar(36) NOT NULL,
                            `group_id` int(11) DEFAULT NULL,
                            `group_id` int(11) DEFAULT NULL,
                            `plan_id` int(11) DEFAULT NULL,
                            `plan_id` int(11) DEFAULT NULL,
-                           `remind_expire` tinyint(4) DEFAULT '1',
-                           `remind_traffic` tinyint(4) DEFAULT '1',
+                           `remind_expire` tinyint(4) DEFAULT '0',
+                           `remind_traffic` tinyint(4) DEFAULT '0',
                            `token` char(32) NOT NULL,
                            `token` char(32) NOT NULL,
                            `remarks` text,
                            `remarks` text,
                            `expired_at` bigint(20) DEFAULT '0',
                            `expired_at` bigint(20) DEFAULT '0',
@@ -348,4 +368,4 @@ CREATE TABLE `v2_user` (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
 
 
--- 2021-07-13 13:50:52
+-- 2021-09-21 10:07:22

+ 37 - 0
database/update.sql

@@ -425,3 +425,40 @@ DROP INDEX `email_deleted_at`;
 
 
 ALTER TABLE `v2_user`
 ALTER TABLE `v2_user`
     ADD `commission_type` tinyint NOT NULL DEFAULT '0' COMMENT '0: system 1: cycle 2: onetime' AFTER `discount`;
     ADD `commission_type` tinyint NOT NULL DEFAULT '0' COMMENT '0: system 1: cycle 2: onetime' AFTER `discount`;
+
+ALTER TABLE `v2_order`
+    ADD `paid_at` int(11) NULL AFTER `commission_balance`;
+
+ALTER TABLE `v2_server_log`
+    ADD INDEX `user_id` (`user_id`),
+ADD INDEX `server_id` (`server_id`);
+
+ALTER TABLE `v2_ticket_message`
+    CHANGE `message` `message` text COLLATE 'utf8mb4_general_ci' NOT NULL AFTER `ticket_id`;
+
+ALTER TABLE `v2_coupon`
+    ADD `limit_use_with_user` int(11) NULL AFTER `limit_use`;
+
+ALTER TABLE `v2_user`
+    ADD `password_salt` char(10) COLLATE 'utf8_general_ci' NULL AFTER `password_algo`;
+
+CREATE TABLE `v2_commission_log` (
+                                     `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
+                                     `invite_user_id` int(11) NOT NULL,
+                                     `user_id` int(11) NOT NULL,
+                                     `trade_no` char(36) NOT NULL,
+                                     `order_amount` int(11) NOT NULL,
+                                     `get_amount` int(11) NOT NULL,
+                                     `created_at` int(11) NOT NULL,
+                                     `updated_at` int(11) NOT NULL
+) COLLATE 'utf8mb4_general_ci';
+
+ALTER TABLE `v2_plan`
+    ADD `reset_traffic_method` tinyint(1) NULL AFTER `reset_price`;
+
+ALTER TABLE `v2_server`
+    RENAME TO `v2_server_v2ray`;
+
+ALTER TABLE `v2_user`
+    CHANGE `remind_expire` `remind_expire` tinyint(4) NULL DEFAULT '0' AFTER `plan_id`,
+    CHANGE `remind_traffic` `remind_traffic` tinyint(4) NULL DEFAULT '0' AFTER `remind_expire`;

+ 1 - 0
init.sh

@@ -1,3 +1,4 @@
+rm -rf composer.phar
 wget https://getcomposer.org/download/2.0.13/composer.phar
 wget https://getcomposer.org/download/2.0.13/composer.phar
 php composer.phar install -vvv
 php composer.phar install -vvv
 php artisan v2board:install
 php artisan v2board:install

+ 2 - 2
pm2.yaml

@@ -1,5 +1,5 @@
 apps:
 apps:
   - name     : 'V2Board'
   - name     : 'V2Board'
-    script   : 'php artisan queue:work --queue=send_email,send_telegram,stat_server'
-    instances: 4
+    script   : 'php artisan horizon'
+    instances: 1
     out_file : './storage/logs/queue/queue.log'
     out_file : './storage/logs/queue/queue.log'

File diff suppressed because it is too large
+ 1 - 0
public/assets/admin/theme/green.css


File diff suppressed because it is too large
+ 0 - 0
public/assets/admin/umi.css


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