Преглед изворни кода

Merge pull request #399 from v2board/dev

1.4.3
tokumeikoi пре 4 година
родитељ
комит
f81426b2c4
54 измењених фајлова са 1111 додато и 634 уклоњено
  1. 12 2
      app/Console/Commands/CheckCommission.php
  2. 0 68
      app/Console/Commands/V2boardCache.php
  3. 101 0
      app/Console/Commands/V2boardStatistics.php
  4. 1 1
      app/Console/Kernel.php
  5. 3 1
      app/Http/Controllers/Admin/ConfigController.php
  6. 5 2
      app/Http/Controllers/Admin/CouponController.php
  7. 1 1
      app/Http/Controllers/Admin/Server/V2rayController.php
  8. 78 0
      app/Http/Controllers/Admin/StatController.php
  9. 10 0
      app/Http/Controllers/Admin/UserController.php
  10. 13 7
      app/Http/Controllers/Client/ClientController.php
  11. 1 1
      app/Http/Controllers/Server/DeepbworkController.php
  12. 1 1
      app/Http/Controllers/Server/PoseidonController.php
  13. 5 2
      app/Http/Controllers/User/CommController.php
  14. 6 6
      app/Http/Controllers/User/CouponController.php
  15. 1 1
      app/Http/Controllers/User/InviteController.php
  16. 3 3
      app/Http/Controllers/User/KnowledgeController.php
  17. 34 34
      app/Http/Controllers/User/OrderController.php
  18. 1 1
      app/Http/Controllers/User/PlanController.php
  19. 35 25
      app/Http/Controllers/User/TicketController.php
  20. 37 11
      app/Http/Controllers/User/UserController.php
  21. 1 0
      app/Http/Kernel.php
  22. 17 0
      app/Http/Middleware/Language.php
  23. 6 0
      app/Http/Middleware/User.php
  24. 3 1
      app/Http/Requests/Admin/ConfigSave.php
  25. 1 1
      app/Http/Requests/Admin/UserFetch.php
  26. 2 1
      app/Http/Requests/Admin/UserUpdate.php
  27. 1 1
      app/Http/Requests/User/TicketWithdraw.php
  28. 4 1
      app/Http/Routes/AdminRoute.php
  29. 54 0
      app/Jobs/StatServerJob.php
  30. 2 2
      app/Models/StatOrder.php
  31. 12 0
      app/Models/StatServer.php
  32. 1 1
      app/Services/ServerService.php
  33. 5 0
      app/Utils/Dict.php
  34. 11 12
      app/Utils/QuantumultX.php
  35. 1 1
      app/Utils/URLSchemes.php
  36. 4 4
      config/app.php
  37. 246 225
      database/install.sql
  38. 42 0
      database/update.sql
  39. 1 1
      pm2.yaml
  40. 0 0
      public/assets/admin/umi.js
  41. 0 0
      public/assets/admin/vendors.async.js
  42. 0 0
      public/assets/user/components.async.js
  43. 0 0
      public/assets/user/components.chunk.css
  44. 0 0
      public/assets/user/umi.css
  45. 0 0
      public/assets/user/umi.js
  46. 37 0
      resources/lang/en-US/passport.php
  47. 132 0
      resources/lang/en-US/user.php
  48. 0 19
      resources/lang/en/auth.php
  49. 0 19
      resources/lang/en/pagination.php
  50. 0 21
      resources/lang/en/passwords.php
  51. 0 150
      resources/lang/en/validation.php
  52. 37 0
      resources/lang/zh-CN/passport.php
  53. 132 0
      resources/lang/zh-CN/user.php
  54. 11 7
      resources/rules/app.clash.yaml

+ 12 - 2
app/Console/Commands/CheckCommission.php

@@ -5,6 +5,7 @@ namespace App\Console\Commands;
 use Illuminate\Console\Command;
 use App\Models\Order;
 use App\Models\User;
+use Illuminate\Support\Facades\DB;
 
 class CheckCommission extends Command
 {
@@ -64,10 +65,19 @@ class CheckCommission extends Command
         foreach ($order as $item) {
             $inviter = User::find($item->invite_user_id);
             if (!$inviter) continue;
-            $inviter->commission_balance = $inviter->commission_balance + $item->commission_balance;
+            if ((int)config('v2board.withdraw_close_enable', 0)) {
+                $inviter->balance = $inviter->balance + $item->commission_balance;
+            } else {
+                $inviter->commission_balance = $inviter->commission_balance + $item->commission_balance;
+            }
+            DB::beginTransaction();
             if ($inviter->save()) {
                 $item->commission_status = 2;
-                $item->save();
+                if (!$item->save()) {
+                    DB::rollBack();
+                    continue;
+                }
+                DB::commit();
             }
         }
     }

+ 0 - 68
app/Console/Commands/V2boardCache.php

@@ -1,68 +0,0 @@
-<?php
-
-namespace App\Console\Commands;
-
-use App\Utils\CacheKey;
-use Illuminate\Console\Command;
-use App\Models\ServerLog;
-use App\Models\ServerStat;
-use Illuminate\Support\Facades\Cache;
-use Illuminate\Support\Facades\DB;
-
-class V2boardCache extends Command
-{
-    /**
-     * The name and signature of the console command.
-     *
-     * @var string
-     */
-    protected $signature = 'v2board:cache';
-
-    /**
-     * The console command description.
-     *
-     * @var string
-     */
-    protected $description = '缓存任务';
-
-    /**
-     * Create a new command instance.
-     *
-     * @return void
-     */
-    public function __construct()
-    {
-        parent::__construct();
-    }
-
-    /**
-     * Execute the console command.
-     *
-     * @return mixed
-     */
-    public function handle()
-    {
-    }
-
-    private function cacheServerStat()
-    {
-        $serverLogs = ServerLog::select(
-            'server_id',
-            DB::raw("sum(u) as u"),
-            DB::raw("sum(d) as d"),
-            DB::raw("count(*) as online")
-        )
-            ->where('updated_at', '>=', time() - 3600)
-            ->groupBy('server_id')
-            ->get();
-        foreach ($serverLogs as $serverLog) {
-            $data = [
-                'server_id' => $serverLog->server_id,
-                'u' => $serverLog->u,
-                'd' => $serverLog->d,
-                'online' => $serverLog->online
-            ];
-//            ServerStat::create($data);
-        }
-    }
-}

+ 101 - 0
app/Console/Commands/V2boardStatistics.php

@@ -0,0 +1,101 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Jobs\StatServerJob;
+use Illuminate\Console\Command;
+use App\Models\Order;
+use App\Models\StatOrder;
+use App\Models\ServerLog;
+use Illuminate\Support\Facades\DB;
+
+class V2BoardStatistics extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'v2board:statistics';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '统计任务';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+         $this->statOrder();
+         $this->statServer();
+    }
+
+    private function statOrder()
+    {
+        $endAt = strtotime(date('Y-m-d'));
+        $startAt = strtotime('-1 day', $endAt);
+        $builder = Order::where('created_at', '>=', $startAt)
+            ->where('created_at', '<', $endAt)
+            ->whereIn('status', [3, 4]);
+        $orderCount = $builder->count();
+        $orderAmount = $builder->sum('total_amount');
+        $builder = $builder->where('commission_balance', '!=', NULL)
+            ->whereIn('commission_status', [1, 2]);
+        $commissionCount = $builder->count();
+        $commissionAmount = $builder->sum('commission_balance');
+        $data = [
+            'order_count' => $orderCount,
+            'order_amount' => $orderAmount,
+            'commission_count' => $commissionCount,
+            'commission_amount' => $commissionAmount,
+            'record_type' => 'd',
+            'record_at' => $startAt
+        ];
+        $statistic = StatOrder::where('record_at', $startAt)
+            ->where('record_type', 'd')
+            ->first();
+        if ($statistic) {
+            $statistic->update($data);
+            return;
+        }
+        StatOrder::create($data);
+    }
+
+    private function statServer()
+    {
+        $endAt = strtotime(date('Y-m-d'));
+        $startAt = strtotime('-1 day', $endAt);
+        $statistics = ServerLog::select([
+            'server_id',
+            'method as server_type',
+            DB::raw("sum(u) as u"),
+            DB::raw("sum(d) as d"),
+        ])
+            ->where('log_at', '>=', $startAt)
+            ->where('log_at', '<', $endAt)
+            ->groupBy('server_id', 'method')
+            ->get()
+            ->toArray();
+        foreach ($statistics as $statistic) {
+            $statistic['record_type'] = 'd';
+            $statistic['record_at'] = $startAt;
+            StatServerJob::dispatch($statistic);
+        }
+    }
+}

+ 1 - 1
app/Console/Kernel.php

@@ -25,7 +25,7 @@ class Kernel extends ConsoleKernel
     protected function schedule(Schedule $schedule)
     {
         // v2board
-        $schedule->command('v2board:cache')->hourly();
+        $schedule->command('v2board:statistics')->daily();
         // check
         $schedule->command('check:order')->everyMinute();
         $schedule->command('check:commission')->everyMinute();

+ 3 - 1
app/Http/Controllers/Admin/ConfigController.php

@@ -47,7 +47,9 @@ class ConfigController extends Controller
                     'invite_never_expire' => config('v2board.invite_never_expire', 0),
                     'commission_first_time_enable' => config('v2board.commission_first_time_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),
+                    'withdraw_close_enable' => config('v2board.withdraw_close_enable', 0)
                 ],
                 'site' => [
                     'safe_mode_enable' => (int)config('v2board.safe_mode_enable', 0),

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

@@ -95,7 +95,9 @@ class CouponController extends Controller
     {
         $coupons = [];
         $coupon = $request->validated();
-        $coupon['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']);
+        if (isset($coupon['limit_plan_ids'])) {
+            $coupon['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']);
+        }
         $coupon['created_at'] = $coupon['updated_at'] = time();
         unset($coupon['generate_count']);
         for ($i = 0;$i < $request->input('generate_count');$i++) {
@@ -116,7 +118,8 @@ class CouponController extends Controller
             $endTime = date('Y-m-d H:i:s', $coupon['ended_at']);
             $limitUse = $coupon['limit_use'] ?? '不限制';
             $createTime = date('Y-m-d H:i:s', $coupon['created_at']);
-            $data .= "{$coupon['name']},{$type},{$value},{$startTime},{$endTime},{$limitUse},{$coupon['limit_plan_ids']},{$coupon['code']},{$createTime}\r\n";
+            $limitPlanIds = $coupon['limit_plan_ids'] ?? '不限制';
+            $data .= "{$coupon['name']},{$type},{$value},{$startTime},{$endTime},{$limitUse},{$limitPlanIds},{$coupon['code']},{$createTime}\r\n";
         }
         echo $data;
     }

+ 1 - 1
app/Http/Controllers/Admin/Server/V2rayController.php

@@ -125,7 +125,7 @@ class V2rayController extends Controller
     public function viewConfig(Request $request)
     {
         $serverService = new ServerService();
-        $config = $serverService->getVmessConfig($request->input('node_id'), 23333);
+        $config = $serverService->getV2RayConfig($request->input('node_id'), 23333);
         return response([
             'data' => $config
         ]);

+ 78 - 0
app/Http/Controllers/Admin/StatController.php

@@ -2,6 +2,9 @@
 
 namespace App\Http\Controllers\Admin;
 
+use App\Models\ServerShadowsocks;
+use App\Models\ServerTrojan;
+use App\Services\ServerService;
 use Illuminate\Http\Request;
 use App\Http\Controllers\Controller;
 use App\Models\ServerGroup;
@@ -10,7 +13,10 @@ use App\Models\Plan;
 use App\Models\User;
 use App\Models\Ticket;
 use App\Models\Order;
+use App\Models\StatOrder;
+use App\Models\StatServer;
 use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
 
 class StatController extends Controller
 {
@@ -43,4 +49,76 @@ class StatController extends Controller
             ]
         ]);
     }
+
+    public function getOrder(Request $request)
+    {
+        $statistics = StatOrder::where('record_type', 'd')
+            ->limit(31)
+            ->orderBy('record_at', 'DESC')
+            ->get()
+            ->toArray();
+        $result = [];
+        foreach ($statistics as $statistic) {
+            $date = date('m-d', $statistic['record_at']);
+            array_push($result, [
+                'type' => '收款金额',
+                'date' => $date,
+                'value' => $statistic['order_amount'] / 100
+            ]);
+            array_push($result, [
+                'type' => '收款笔数',
+                'date' => $date,
+                'value' => $statistic['order_count']
+            ]);
+            array_push($result, [
+                'type' => '佣金金额',
+                'date' => $date,
+                'value' => $statistic['commission_amount'] / 100
+            ]);
+            array_push($result, [
+                'type' => '佣金笔数',
+                'date' => $date,
+                'value' => $statistic['commission_count']
+            ]);
+        }
+        $result = array_reverse($result);
+        return response([
+            'data' => $result
+        ]);
+    }
+
+    public function getServerLastRank()
+    {
+        $servers = [
+            'shadowsocks' => ServerShadowsocks::where('parent_id', null)->get()->toArray(),
+            'vmess' => Server::where('parent_id', null)->get()->toArray(),
+            'trojan' => ServerTrojan::where('parent_id', null)->get()->toArray()
+        ];
+        $timestamp = strtotime('-1 day', strtotime(date('Y-m-d')));
+        $statistics = StatServer::select([
+                'server_id',
+                'server_type',
+                'u',
+                'd'
+            ])
+            ->where('record_at', '>=', $timestamp)
+            ->where('record_type', 'd')
+            ->limit(10)
+            ->orderBy('record_at', 'DESC')
+            ->get()
+            ->toArray();
+        foreach ($statistics as $k => $v) {
+            foreach ($servers[$v['server_type']] as $server) {
+                if ($server['id'] === $v['server_id']) {
+                    $statistics[$k]['server_name'] = $server['name'];
+                }
+            }
+            $statistics[$k]['total'] = ($v['u'] + $v['d']) / 1073741824;
+        }
+        array_multisort(array_column($statistics, 'total'), SORT_DESC, $statistics);
+        return response([
+            'data' => $statistics
+        ]);
+    }
 }
+

+ 10 - 0
app/Http/Controllers/Admin/UserController.php

@@ -17,6 +17,16 @@ use Illuminate\Support\Facades\DB;
 
 class UserController extends Controller
 {
+    public function resetSecret(Request $request)
+    {
+        $user = User::find($request->input('id'));
+        if (!$user) abort(500, '用户不存在');
+        $user->token = Helper::guid();
+        $user->uuid = Helper::guid(true);
+        return response([
+            'data' => $user->save()
+        ]);
+    }
 
     private function filter(Request $request, $builder)
     {

+ 13 - 7
app/Http/Controllers/Client/ClientController.php

@@ -61,7 +61,7 @@ class ClientController extends Controller
     private function quantumult($user, $servers = [])
     {
         $uri = '';
-        header('subscription-userinfo: upload=' . $user->u . '; download=' . $user->d . ';total=' . $user->transfer_enable);
+        header('subscription-userinfo: upload=' . $user['u'] . '; download=' . $user['d'] . ';total=' . $user['transfer_enable']);
         foreach ($servers as $item) {
             if ($item['type'] === 'v2ray') {
                 $str = '';
@@ -84,10 +84,10 @@ class ClientController extends Controller
     {
         $uri = '';
         //display remaining traffic and expire date
-        $upload = round($user->u / (1024*1024*1024), 2);
-        $download = round($user->d / (1024*1024*1024), 2);
-        $totalTraffic = round($user->transfer_enable / (1024*1024*1024), 2);
-        $expiredDate = date('Y-m-d', $user->expired_at);
+        $upload = round($user['u'] / (1024*1024*1024), 2);
+        $download = round($user['d'] / (1024*1024*1024), 2);
+        $totalTraffic = round($user['transfer_enable'] / (1024*1024*1024), 2);
+        $expiredDate = date('Y-m-d', $user['expired_at']);
         $uri .= "STATUS=🚀↑:{$upload}GB,↓:{$download}GB,TOT:{$totalTraffic}GB💡Expires:{$expiredDate}\r\n";
         foreach ($servers as $item) {
             if ($item['type'] === 'shadowsocks') {
@@ -106,7 +106,7 @@ class ClientController extends Controller
     private function quantumultX($user, $servers = [])
     {
         $uri = '';
-        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']}");
         foreach ($servers as $item) {
             if ($item['type'] === 'shadowsocks') {
                 $uri .= QuantumultX::buildShadowsocks($user['uuid'], $item);
@@ -143,6 +143,11 @@ class ClientController extends Controller
         $configs = [];
         $subs = [];
         $subs['servers'] = [];
+        $subs['bytes_used'] = '';
+        $subs['bytes_remaining'] = '';
+
+        $bytesUsed = $user['u'] + $user['d'];
+        $bytesRemaining = $user['transfer_enable'] - $bytesUsed;
 
         foreach ($servers as $item) {
             if ($item['type'] === 'shadowsocks') {
@@ -151,7 +156,8 @@ class ClientController extends Controller
         }
 
         $subs['version'] = 1;
-        $subs['remark'] = config('v2board.app_name', 'V2Board');
+        $subs['bytes_used'] = $bytesUsed;
+        $subs['bytes_remaining'] = $bytesRemaining;
         $subs['servers'] = array_merge($subs['servers'] ? $subs['servers'] : [], $configs);
 
         return json_encode($subs, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT);

+ 1 - 1
app/Http/Controllers/Server/DeepbworkController.php

@@ -109,7 +109,7 @@ class DeepbworkController extends Controller
         }
         $serverService = new ServerService();
         try {
-            $json = $serverService->getVmessConfig($nodeId, $localPort);
+            $json = $serverService->getV2RayConfig($nodeId, $localPort);
         } catch (\Exception $e) {
             abort(500, $e->getMessage());
         }

+ 1 - 1
app/Http/Controllers/Server/PoseidonController.php

@@ -92,7 +92,7 @@ class PoseidonController extends Controller
 
         $serverService = new ServerService();
         try {
-            $json = $serverService->getVmessConfig($nodeId, $localPort);
+            $json = $serverService->getV2RayConfig($nodeId, $localPort);
             $json->poseidon = [
               'license_key' => (string)config('v2board.server_license'),
             ];

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

@@ -2,6 +2,7 @@
 
 namespace App\Http\Controllers\User;
 
+use App\Utils\Dict;
 use Illuminate\Http\Request;
 use App\Http\Controllers\Controller;
 
@@ -11,8 +12,10 @@ class CommController extends Controller
     {
         return response([
             'data' => [
-                'isTelegram' => (int)config('v2board.telegram_bot_enable', 0),
-                'stripePk' => config('v2board.stripe_pk_live')
+                'is_telegram' => (int)config('v2board.telegram_bot_enable', 0),
+                'stripe_pk' => config('v2board.stripe_pk_live'),
+                'withdraw_methods' => config('v2board.commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT),
+                'withdraw_close' => (int)config('v2board.withdraw_close_enable', 0)
             ]
         ]);
     }

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

@@ -11,25 +11,25 @@ class CouponController extends Controller
     public function check(Request $request)
     {
         if (empty($request->input('code'))) {
-            abort(500, '优惠券码不能为空');
+            abort(500, __('user.coupon.check.coupon_not_empty'));
         }
         $coupon = Coupon::where('code', $request->input('code'))->first();
         if (!$coupon) {
-            abort(500, '优惠券无效');
+            abort(500, __('user.coupon.check.coupon_invalid'));
         }
         if ($coupon->limit_use <= 0 && $coupon->limit_use !== NULL) {
-            abort(500, '优惠券已无可用次数');
+            abort(500, __('user.coupon.check.coupon_not_available_by_number'));
         }
         if (time() < $coupon->started_at) {
-            abort(500, '优惠券还未到可用时间');
+            abort(500, __('user.coupon.check.coupon_not_available_by_time'));
         }
         if (time() > $coupon->ended_at) {
-            abort(500, '优惠券已过期');
+            abort(500, __('user.coupon.check.coupon_expired'));
         }
         if ($coupon->limit_plan_ids) {
             $limitPlanIds = json_decode($coupon->limit_plan_ids);
             if (!in_array($request->input('plan_id'), $limitPlanIds)) {
-                abort(500, '这个计划无法使用该优惠码');
+                abort(500, __('user.coupon.check.coupon_limit_plan'));
             }
         }
         return response([

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

@@ -14,7 +14,7 @@ class InviteController extends Controller
     public function save(Request $request)
     {
         if (InviteCode::where('user_id', $request->session()->get('id'))->where('status', 0)->count() >= config('v2board.invite_gen_limit', 5)) {
-            abort(500, '已达到创建数量上限');
+            abort(500, __('user.invite.save.invite_create_limit'));
         }
         $inviteCode = new InviteCode();
         $inviteCode->user_id = $request->session()->get('id');

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

@@ -17,11 +17,11 @@ class KnowledgeController extends Controller
                 ->where('show', 1)
                 ->first()
                 ->toArray();
-            if (!$knowledge) abort(500, '知识不存在');
+            if (!$knowledge) abort(500, __('user.knowledge.fetch.knowledge_not_exist'));
             $user = User::find($request->session()->get('id'));
             $userService = new UserService();
-            $appleId = $userService->isAvailable($user) ? config('v2board.apple_id') : '没有有效订阅无法使用本站提供的AppleID';
-            $appleIdPassword = $userService->isAvailable($user) ? config('v2board.apple_id_password') : '没有有效订阅无法使用本站提供的AppleID';
+            $appleId = $userService->isAvailable($user) ? config('v2board.apple_id') : __('user.knowledge.fetch.apple_id_must_be_plan');
+            $appleIdPassword = $userService->isAvailable($user) ? config('v2board.apple_id_password') : __('user.knowledge.fetch.apple_id_must_be_plan');
             $subscribeUrl = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
             $knowledge['body'] = str_replace('{{siteName}}', config('v2board.app_name', 'V2Board'), $knowledge['body']);
             $knowledge['body'] = str_replace('{{appleId}}', $appleId, $knowledge['body']);

+ 34 - 34
app/Http/Controllers/User/OrderController.php

@@ -50,12 +50,12 @@ class OrderController extends Controller
             ->where('trade_no', $request->input('trade_no'))
             ->first();
         if (!$order) {
-            abort(500, '订单不存在');
+            abort(500, __('user.order.details.order_not_exist'));
         }
         $order['plan'] = Plan::find($order->plan_id);
         $order['try_out_plan_id'] = (int)config('v2board.try_out_plan_id');
         if (!$order['plan']) {
-            abort(500, '订阅不存在');
+            abort(500, __('user.order.details.plan_not_exist'));
         }
         return response([
             'data' => $order
@@ -66,38 +66,38 @@ class OrderController extends Controller
     {
         $userService = new UserService();
         if ($userService->isNotCompleteOrderByUserId($request->session()->get('id'))) {
-            abort(500, '您有未付款或开通中的订单,请稍后或取消再试');
+            abort(500, __('user.order.save.exist_open_order'));
         }
 
         $plan = Plan::find($request->input('plan_id'));
         $user = User::find($request->session()->get('id'));
 
         if (!$plan) {
-            abort(500, '该订阅不存在');
+            abort(500, __('user.order.save.plan_not_exist'));
         }
 
         if ((!$plan->show && !$plan->renew) || (!$plan->show && $user->plan_id !== $plan->id)) {
             if ($request->input('cycle') !== 'reset_price') {
-                abort(500, '该订阅已售罄,请更换其他订阅');
+                abort(500, __('user.order.save.plan_stop_sell'));
             }
         }
 
         if (!$plan->renew && $user->plan_id == $plan->id && $request->input('cycle') !== 'reset_price') {
-            abort(500, '该订阅无法续费,请更换其他订阅');
+            abort(500, __('user.order.save.plan_stop_renew'));
         }
 
         if ($plan[$request->input('cycle')] === NULL) {
-            abort(500, '该订阅周期无法进行购买,请选择其他周期');
+            abort(500, __('user.order.save.plan_stop'));
         }
 
         if ($request->input('cycle') === 'reset_price') {
             if ($user->expired_at <= time() || !$user->plan_id) {
-                abort(500, '订阅已过期或无有效订阅,无法购买重置包');
+                abort(500, __('user.order.save.plan_exist_not_buy_package'));
             }
         }
 
         if (!$plan->show && $plan->renew && !$userService->isAvailable($user)) {
-            abort(500, '订阅已过期,请更换其他订阅');
+            abort(500, __('user.order.save.plan_expired'));
         }
 
         DB::beginTransaction();
@@ -113,7 +113,7 @@ class OrderController extends Controller
             $couponService = new CouponService($request->input('coupon_code'));
             if (!$couponService->use($order)) {
                 DB::rollBack();
-                abort(500, '优惠券使用失败');
+                abort(500, __('user.order.save.coupon_use_failed'));
             }
             $order->coupon_id = $couponService->getId();
         }
@@ -128,14 +128,14 @@ class OrderController extends Controller
             if ($remainingBalance > 0) {
                 if (!$userService->addBalance($order->user_id, - $order->total_amount)) {
                     DB::rollBack();
-                    abort(500, '余额不足');
+                    abort(500, __('user.order.save.insufficient_balance'));
                 }
                 $order->balance_amount = $order->total_amount;
                 $order->total_amount = 0;
             } else {
                 if (!$userService->addBalance($order->user_id, - $user->balance)) {
                     DB::rollBack();
-                    abort(500, '余额不足');
+                    abort(500, __('user.order.save.insufficient_balance'));
                 }
                 $order->balance_amount = $user->balance;
                 $order->total_amount = $order->total_amount - $user->balance;
@@ -144,7 +144,7 @@ class OrderController extends Controller
 
         if (!$order->save()) {
             DB::rollback();
-            abort(500, '订单创建失败');
+            abort(500, __('user.order.save.order_create_failed'));
         }
 
         DB::commit();
@@ -163,7 +163,7 @@ class OrderController extends Controller
             ->where('status', 0)
             ->first();
         if (!$order) {
-            abort(500, '订单不存在或已支付');
+            abort(500, __('user.order.checkout.order_not_exist_or_paid'));
         }
         // free process
         if ($order->total_amount <= 0) {
@@ -180,7 +180,7 @@ class OrderController extends Controller
             case 0:
                 // alipayF2F
                 if (!(int)config('v2board.alipay_enable')) {
-                    abort(500, '支付方式不可用');
+                    abort(500, __('user.order.checkout.pay_method_not_use'));
                 }
                 return response([
                     'type' => 0,
@@ -189,7 +189,7 @@ class OrderController extends Controller
             case 2:
                 // stripeAlipay
                 if (!(int)config('v2board.stripe_alipay_enable')) {
-                    abort(500, '支付方式不可用');
+                    abort(500, __('user.order.checkout.pay_method_not_use'));
                 }
                 return response([
                     'type' => 1,
@@ -198,7 +198,7 @@ class OrderController extends Controller
             case 3:
                 // stripeWepay
                 if (!(int)config('v2board.stripe_wepay_enable')) {
-                    abort(500, '支付方式不可用');
+                    abort(500, __('user.order.checkout.pay_method_not_use'));
                 }
                 return response([
                     'type' => 0,
@@ -207,7 +207,7 @@ class OrderController extends Controller
             case 4:
                 // bitpayX
                 if (!(int)config('v2board.bitpayx_enable')) {
-                    abort(500, '支付方式不可用');
+                    abort(500, __('user.order.checkout.pay_method_not_use'));
                 }
                 return response([
                     'type' => 1,
@@ -215,7 +215,7 @@ class OrderController extends Controller
                 ]);
             case 5:
                 if (!(int)config('v2board.mgate_enable')) {
-                    abort(500, '支付方式不可用');
+                    abort(500, __('user.order.checkout.pay_method_not_use'));
                 }
                 return response([
                     'type' => 1,
@@ -223,7 +223,7 @@ class OrderController extends Controller
                 ]);
             case 6:
                 if (!(int)config('v2board.stripe_card_enable')) {
-                    abort(500, '支付方式不可用');
+                    abort(500, __('user.order.checkout.pay_method_not_use'));
                 }
                 return response([
                     'type' => 2,
@@ -231,14 +231,14 @@ class OrderController extends Controller
                 ]);
             case 7:
                 if (!(int)config('v2board.epay_enable')) {
-                    abort(500, '支付方式不可用');
+                    abort(500, __('user.order.checkout.pay_method_not_use'));
                 }
                 return response([
                     'type' => 1,
                     'data' => $this->epay($order)
                 ]);
             default:
-                abort(500, '支付方式不存在');
+                abort(500, __('user.order.checkout.pay_method_not_use'));
         }
     }
 
@@ -249,7 +249,7 @@ class OrderController extends Controller
             ->where('user_id', $request->session()->get('id'))
             ->first();
         if (!$order) {
-            abort(500, '订单不存在');
+            abort(500, __('user.order.check.order_not_exist'));
         }
         return response([
             'data' => $order->status
@@ -323,20 +323,20 @@ class OrderController extends Controller
     public function cancel(Request $request)
     {
         if (empty($request->input('trade_no'))) {
-            abort(500, '参数有误');
+            abort(500, __('user.order.cancel.params_wrong'));
         }
         $order = Order::where('trade_no', $request->input('trade_no'))
             ->where('user_id', $request->session()->get('id'))
             ->first();
         if (!$order) {
-            abort(500, '订单不存在');
+            abort(500, __('user.order.cancel.order_not_exist'));
         }
         if ($order->status !== 0) {
-            abort(500, '只可以取消待支付订单');
+            abort(500, __('user.order.cancel.only_cancel_pending_order'));
         }
         $orderService = new OrderService($order);
         if (!$orderService->cancel()) {
-            abort(500, '取消失败');
+            abort(500, __('user.order.cancel.cancel_failed'));
         }
         return response([
             'data' => true
@@ -372,7 +372,7 @@ class OrderController extends Controller
         $currency = config('v2board.stripe_currency', 'hkd');
         $exchange = Helper::exchange('CNY', strtoupper($currency));
         if (!$exchange) {
-            abort(500, '货币转换超时,请稍后再试');
+            abort(500, __('user.order.stripeAlipay.currency_convert_timeout'));
         }
         Stripe::setApiKey(config('v2board.stripe_sk_live'));
         $source = Source::create([
@@ -390,7 +390,7 @@ class OrderController extends Controller
             ]
         ]);
         if (!$source['redirect']['url']) {
-            abort(500, '支付网关请求失败');
+            abort(500, __('user.order.stripeAlipay.gateway_request_failed'));
         }
         return $source['redirect']['url'];
     }
@@ -400,7 +400,7 @@ class OrderController extends Controller
         $currency = config('v2board.stripe_currency', 'hkd');
         $exchange = Helper::exchange('CNY', strtoupper($currency));
         if (!$exchange) {
-            abort(500, '货币转换超时,请稍后再试');
+            abort(500, __('user.order.stripeWepay.currency_convert_timeout'));
         }
         Stripe::setApiKey(config('v2board.stripe_sk_live'));
         $source = Source::create([
@@ -417,7 +417,7 @@ class OrderController extends Controller
             ]
         ]);
         if (!$source['wechat']['qr_code_url']) {
-            abort(500, '支付网关请求失败');
+            abort(500, __('user.order.stripeWepay.gateway_request_failed'));
         }
         return $source['wechat']['qr_code_url'];
     }
@@ -427,7 +427,7 @@ class OrderController extends Controller
         $currency = config('v2board.stripe_currency', 'hkd');
         $exchange = Helper::exchange('CNY', strtoupper($currency));
         if (!$exchange) {
-            abort(500, '货币转换超时,请稍后再试');
+            abort(500, __('user.order.stripeCard.currency_convert_timeout'));
         }
         Stripe::setApiKey(config('v2board.stripe_sk_live'));
         try {
@@ -442,11 +442,11 @@ class OrderController extends Controller
                 ]
             ]);
         } catch (\Exception $e) {
-            abort(500, '遇到了点问题,请刷新页面稍后再试');
+            abort(500, __('user.order.stripeCard.was_problem'));
         }
         info($charge);
         if (!$charge->paid) {
-            abort(500, '扣款失败,请检查信用卡信息');
+            abort(500, __('user.order.stripeCard.deduction_failed'));
         }
         return $charge->paid;
     }

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

@@ -14,7 +14,7 @@ class PlanController extends Controller
             $plan = Plan::where('id', $request->input('id'))
                 ->first();
             if (!$plan) {
-                abort(500, '该订阅不存在');
+                abort(500, __('user.plan.fetch.plan_not_exist'));
             }
             return response([
                 'data' => $plan

+ 35 - 25
app/Http/Controllers/User/TicketController.php

@@ -8,6 +8,7 @@ use App\Http\Requests\User\TicketWithdraw;
 use App\Jobs\SendTelegramJob;
 use App\Models\User;
 use App\Services\TelegramService;
+use App\Utils\Dict;
 use Illuminate\Http\Request;
 use App\Models\Ticket;
 use App\Models\TicketMessage;
@@ -22,7 +23,7 @@ class TicketController extends Controller
                 ->where('user_id', $request->session()->get('id'))
                 ->first();
             if (!$ticket) {
-                abort(500, '工单不存在');
+                abort(500, __('user.ticket.fetch.ticket_not_exist'));
             }
             $ticket['message'] = TicketMessage::where('ticket_id', $ticket->id)->get();
             for ($i = 0; $i < count($ticket['message']); $i++) {
@@ -55,7 +56,7 @@ class TicketController extends Controller
     {
         DB::beginTransaction();
         if ((int)Ticket::where('status', 0)->where('user_id', $request->session()->get('id'))->count()) {
-            abort(500, '存在其他工单尚未处理');
+            abort(500, __('user.ticket.save.exist_other_open_ticket'));
         }
         $ticket = Ticket::create(array_merge($request->only([
             'subject',
@@ -66,7 +67,7 @@ class TicketController extends Controller
         ]));
         if (!$ticket) {
             DB::rollback();
-            abort(500, '工单创建失败');
+            abort(500, __('user.ticket.save.ticket_create_failed'));
         }
         $ticketMessage = TicketMessage::create([
             'user_id' => $request->session()->get('id'),
@@ -75,7 +76,7 @@ class TicketController extends Controller
         ]);
         if (!$ticketMessage) {
             DB::rollback();
-            abort(500, '工单创建失败');
+            abort(500, __('user.ticket.save.ticket_create_failed'));
         }
         DB::commit();
         $this->sendNotify($ticket, $ticketMessage);
@@ -87,22 +88,22 @@ class TicketController extends Controller
     public function reply(Request $request)
     {
         if (empty($request->input('id'))) {
-            abort(500, '参数错误');
+            abort(500, __('user.ticket.reply.params_wrong'));
         }
         if (empty($request->input('message'))) {
-            abort(500, '消息不能为空');
+            abort(500, __('user.ticket.reply.message_not_empty'));
         }
         $ticket = Ticket::where('id', $request->input('id'))
             ->where('user_id', $request->session()->get('id'))
             ->first();
         if (!$ticket) {
-            abort(500, '工单不存在');
+            abort(500, __('user.ticket.reply.ticket_not_exist'));
         }
         if ($ticket->status) {
-            abort(500, '工单已关闭,无法回复');
+            abort(500, __('user.ticket.reply.ticket_close_not_reply'));
         }
         if ($request->session()->get('id') == $this->getLastMessage($ticket->id)->user_id) {
-            abort(500, '请等待技术支持回复');
+            abort(500, __('user.ticket.reply.wait_reply'));
         }
         DB::beginTransaction();
         $ticketMessage = TicketMessage::create([
@@ -113,7 +114,7 @@ class TicketController extends Controller
         $ticket->last_reply_user_id = $request->session()->get('id');
         if (!$ticketMessage || !$ticket->save()) {
             DB::rollback();
-            abort(500, '工单回复失败');
+            abort(500, __('user.ticket.reply.ticket_reply_failed'));
         }
         DB::commit();
         $this->sendNotify($ticket, $ticketMessage);
@@ -126,17 +127,17 @@ class TicketController extends Controller
     public function close(Request $request)
     {
         if (empty($request->input('id'))) {
-            abort(500, '参数错误');
+            abort(500, __('user.ticket.close.params_wrong'));
         }
         $ticket = Ticket::where('id', $request->input('id'))
             ->where('user_id', $request->session()->get('id'))
             ->first();
         if (!$ticket) {
-            abort(500, '工单不存在');
+            abort(500, __('user.ticket.close.ticket_not_exist'));
         }
         $ticket->status = 1;
         if (!$ticket->save()) {
-            abort(500, '关闭失败');
+            abort(500, __('user.ticket.close.close_failed'));
         }
         return response([
             'data' => true
@@ -152,13 +153,25 @@ class TicketController extends Controller
 
     public function withdraw(TicketWithdraw $request)
     {
+        if ((int)config('v2board.withdraw_close_enable', 0)) {
+            abort(500, 'user.ticket.withdraw.not_support_withdraw');
+        }
+        if (!in_array(
+            $request->input('withdraw_method'),
+            config(
+                'v2board.commission_withdraw_method',
+                Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT
+            )
+        )) {
+            abort(500, __('user.ticket.withdraw.not_support_withdraw_method'));
+        }
         $user = User::find($request->session()->get('id'));
         $limit = config('v2board.commission_withdraw_limit', 100);
         if ($limit > ($user->commission_balance / 100)) {
-            abort(500, "当前系统要求的提现门槛佣金需为{$limit}CNY");
+            abort(500, __('user.ticket.withdraw.system_require_withdraw_limit', ['limit' => $limit]));
         }
         DB::beginTransaction();
-        $subject = '[提现申请]本工单由系统发出';
+        $subject = __('user.ticket.withdraw.ticket_subject');
         $ticket = Ticket::create([
             'subject' => $subject,
             'level' => 2,
@@ -167,15 +180,12 @@ class TicketController extends Controller
         ]);
         if (!$ticket) {
             DB::rollback();
-            abort(500, '工单创建失败');
-        }
-        $methodText = [
-            'alipay' => '支付宝',
-            'paypal' => '贝宝(Paypal)',
-            'usdt' => 'USDT',
-            'btc' => '比特币'
-        ];
-        $message = "提现方式:{$methodText[$request->input('withdraw_method')]}\r\n提现账号:{$request->input('withdraw_account')}\r\n";
+            abort(500, __('user.ticket.withdraw.ticket_create_failed'));
+        }
+        $message = __('user.ticket.withdraw.ticket_message', [
+            'method' => $request->input('withdraw_method'),
+            'account' => $request->input('withdraw_account')
+        ]);
         $ticketMessage = TicketMessage::create([
             'user_id' => $request->session()->get('id'),
             'ticket_id' => $ticket->id,
@@ -183,7 +193,7 @@ class TicketController extends Controller
         ]);
         if (!$ticketMessage) {
             DB::rollback();
-            abort(500, '工单创建失败');
+            abort(500, __('user.ticket.withdraw.ticket_create_failed'));
         }
         DB::commit();
         $this->sendNotify($ticket, $ticketMessage);

+ 37 - 11
app/Http/Controllers/User/UserController.php

@@ -27,17 +27,20 @@ class UserController extends Controller
     public function changePassword(UserChangePassword $request)
     {
         $user = User::find($request->session()->get('id'));
+        if (!$user) {
+            abort(500, __('user.user.changePassword.user_not_exist'));
+        }
         if (!Helper::multiPasswordVerify(
             $user->password_algo,
             $request->input('old_password'),
             $user->password)
         ) {
-            abort(500, '旧密码有误');
+            abort(500, __('user.user.changePassword.old_password_wrong'));
         }
         $user->password = password_hash($request->input('new_password'), PASSWORD_DEFAULT);
         $user->password_algo = NULL;
         if (!$user->save()) {
-            abort(500, '保存失败');
+            abort(500, __('user.user.changePassword.save_failed'));
         }
         $request->session()->flush();
         return response([
@@ -65,6 +68,9 @@ class UserController extends Controller
                 'telegram_id'
             ])
             ->first();
+        if (!$user) {
+            abort(500, __('user.user.info.user_not_exist'));
+        }
         $user['avatar_url'] = 'https://cdn.v2ex.com/gravatar/' . md5($user->email) . '?s=64&d=identicon';
         return response([
             'data' => $user
@@ -90,11 +96,25 @@ class UserController extends Controller
 
     public function getSubscribe(Request $request)
     {
-        $user = User::find($request->session()->get('id'));
+        $user = User::where('id', $request->session()->get('id'))
+            ->select([
+                'id',
+                'plan_id',
+                'token',
+                'expired_at',
+                'u',
+                'd',
+                'transfer_enable',
+                'email'
+            ])
+            ->first();
+        if (!$user) {
+            abort(500, __('user.user.getSubscribe.user_not_exist'));
+        }
         if ($user->plan_id) {
             $user['plan'] = Plan::find($user->plan_id);
             if (!$user['plan']) {
-                abort(500, '订阅计划不存在');
+                abort(500, __('user.user.getSubscribe.plan_not_exist'));
             }
         }
         $user['subscribe_url'] = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
@@ -107,10 +127,13 @@ class UserController extends Controller
     public function resetSecurity(Request $request)
     {
         $user = User::find($request->session()->get('id'));
+        if (!$user) {
+            abort(500, __('user.user.resetSecurity.user_not_exist'));
+        }
         $user->uuid = Helper::guid(true);
         $user->token = Helper::guid();
         if (!$user->save()) {
-            abort(500, '重置失败');
+            abort(500, __('user.user.resetSecurity.reset_failed'));
         }
         return response([
             'data' => config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user->token
@@ -126,12 +149,12 @@ class UserController extends Controller
 
         $user = User::find($request->session()->get('id'));
         if (!$user) {
-            abort(500, '该用户不存在');
+            abort(500, __('user.user.update.user_not_exist'));
         }
         try {
             $user->update($updateData);
         } catch (\Exception $e) {
-            abort(500, '保存失败');
+            abort(500, __('user.user.update.save_failed'));
         }
 
         return response([
@@ -143,18 +166,18 @@ class UserController extends Controller
     {
         $user = User::find($request->session()->get('id'));
         if (!$user) {
-            abort(500, '该用户不存在');
+            abort(500, __('user.user.transfer.user_not_exist'));
         }
         if ($request->input('transfer_amount') <= 0) {
-            abort(500, '参数错误');
+            abort(500, __('user.user.transfer.params_wrong'));
         }
         if ($request->input('transfer_amount') > $user->commission_balance) {
-            abort(500, '推广佣金余额不足');
+            abort(500, __('user.user.transfer.insufficient_commission_balance'));
         }
         $user->commission_balance = $user->commission_balance - $request->input('transfer_amount');
         $user->balance = $user->balance + $request->input('transfer_amount');
         if (!$user->save()) {
-            abort(500, '划转失败');
+            abort(500, __('user.user.transfer.transfer_failed'));
         }
         return response([
             'data' => true
@@ -172,6 +195,9 @@ class UserController extends Controller
             return $lastDay - $today;
         }
         if ((int)config('v2board.reset_traffic_method') === 1) {
+            if ((int)$day >= (int)$today && (int)$day >= (int)$lastDay) {
+                return $lastDay - $today;
+            }
             if ((int)$day >= (int)$today) {
                 return $day - $today;
             } else {

+ 1 - 0
app/Http/Kernel.php

@@ -43,6 +43,7 @@ class Kernel extends HttpKernel
             \Illuminate\Session\Middleware\StartSession::class,
             \App\Http\Middleware\ForceJson::class,
             \App\Http\Middleware\CORS::class,
+            \App\Http\Middleware\Language::class,
             'bindings',
         ],
     ];

+ 17 - 0
app/Http/Middleware/Language.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Support\Facades\App;
+
+class Language
+{
+    public function handle($request, Closure $next)
+    {
+        if ($request->header('content-language')) {
+            App::setLocale($request->header('content-language'));
+        }
+        return $next($request);
+    }
+}

+ 6 - 0
app/Http/Middleware/User.php

@@ -22,6 +22,12 @@ class User
                 $request->session()->put('id', $user->id);
             }
         }
+//        if ($request->input('lang')) {
+//            $request->session()->put('lang', $request->input('lang'));
+//        }
+//        if ($request->session()->get('lang')) {
+//            App::setLocale($request->session()->get('lang'));
+//        }
         if (!$request->session()->get('id')) {
             abort(403, '未登录或登陆已过期');
         }

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

@@ -23,6 +23,8 @@ class ConfigSave extends FormRequest
             'commission_first_time_enable' => 'in:0,1',
             'commission_auto_check_enable' => 'in:0,1',
             'commission_withdraw_limit' => 'nullable|numeric',
+            'commission_withdraw_method' => 'nullable|array',
+            'withdraw_close_enable' => 'in:0,1',
             // site
             'stop_register' => 'in:0,1',
             'email_verify' => 'in:0,1',
@@ -34,7 +36,7 @@ class ConfigSave extends FormRequest
             'try_out_plan_id' => 'integer',
             'try_out_hour' => 'numeric',
             'email_whitelist_enable' => 'in:0,1',
-            'email_whitelist_suffix' => '',
+            'email_whitelist_suffix' => 'nullable|array',
             'email_gmail_limit_enable' => 'in:0,1',
             'recaptcha_enable' => 'in:0,1',
             'recaptcha_key' => '',

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

@@ -14,7 +14,7 @@ class UserFetch extends FormRequest
     public function rules()
     {
         return [
-            'filter.*.key' => 'required|in:id,email,transfer_enable,d,expired_at,uuid,token,invite_by_email,invite_user_id,plan_id',
+            'filter.*.key' => 'required|in:id,email,transfer_enable,d,expired_at,uuid,token,invite_by_email,invite_user_id,plan_id,banned',
             'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊',
             'filter.*.value' => 'required'
         ];

+ 2 - 1
app/Http/Requests/Admin/UserUpdate.php

@@ -27,7 +27,8 @@ class UserUpdate extends FormRequest
             'u' => 'integer',
             'd' => 'integer',
             'balance' => 'integer',
-            'commission_balance' => 'integer'
+            'commission_balance' => 'integer',
+            'remarks' => 'nullable'
         ];
     }
 

+ 1 - 1
app/Http/Requests/User/TicketWithdraw.php

@@ -14,7 +14,7 @@ class TicketWithdraw  extends FormRequest
     public function rules()
     {
         return [
-            'withdraw_method' => 'required|in:alipay,paypal,usdt,btc',
+            'withdraw_method' => 'required',
             'withdraw_account' => 'required'
         ];
     }

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

@@ -73,8 +73,11 @@ class AdminRoute
             $router->post('/user/dumpCSV', 'Admin\\UserController@dumpCSV');
             $router->post('/user/sendMail', 'Admin\\UserController@sendMail');
             $router->post('/user/ban', 'Admin\\UserController@ban');
-            // Stat
+            $router->post('/user/resetSecret', 'Admin\\UserController@resetSecret');
+            // StatOrder
             $router->get ('/stat/getOverride', 'Admin\\StatController@getOverride');
+            $router->get ('/stat/getServerLastRank', 'Admin\\StatController@getServerLastRank');
+            $router->get ('/stat/getOrder', 'Admin\\StatController@getOrder');
             // Notice
             $router->get ('/notice/fetch', 'Admin\\NoticeController@fetch');
             $router->post('/notice/save', 'Admin\\NoticeController@save');

+ 54 - 0
app/Jobs/StatServerJob.php

@@ -0,0 +1,54 @@
+<?php
+
+namespace App\Jobs;
+
+use App\Models\StatServer;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+
+class StatServerJob implements ShouldQueue
+{
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+    protected $statistic;
+
+    public $tries = 3;
+    public $timeout = 5;
+
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct(array $statistic)
+    {
+        $this->onQueue('stat_server');
+        $this->statistic = $statistic;
+    }
+
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        $statistic = $this->statistic;
+        $data = StatServer::where('record_at', $statistic['record_at'])
+            ->where('server_id', $statistic['server_id'])
+            ->first();
+        if ($data) {
+            try {
+                $data->update($statistic);
+            } catch (\Exception $e) {
+                abort(500, '节点统计数据更新失败');
+            }
+        } else {
+            if (!StatServer::create($statistic)) {
+                abort(500, '节点统计数据创建失败');
+            }
+        }
+    }
+}

+ 2 - 2
app/Models/Tutorial.php → app/Models/StatOrder.php

@@ -4,9 +4,9 @@ namespace App\Models;
 
 use Illuminate\Database\Eloquent\Model;
 
-class Tutorial extends Model
+class StatOrder extends Model
 {
-    protected $table = 'v2_tutorial';
+    protected $table = 'v2_stat_order';
     protected $dateFormat = 'U';
     protected $guarded = ['id'];
 }

+ 12 - 0
app/Models/StatServer.php

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

+ 1 - 1
app/Services/ServerService.php

@@ -126,7 +126,7 @@ class ServerService
             ->get();
     }
 
-    public function getVmessConfig(int $nodeId, int $localPort)
+    public function getV2RayConfig(int $nodeId, int $localPort)
     {
         $server = Server::find($nodeId);
         if (!$server) {

+ 5 - 0
app/Utils/Dict.php

@@ -15,4 +15,9 @@ class Dict
         'yeah.net',
         'foxmail.com'
     ];
+    CONST WITHDRAW_METHOD_WHITELIST_DEFAULT = [
+        '支付宝',
+        'USDT',
+        'Paypal'
+    ];
 }

+ 11 - 12
app/Utils/QuantumultX.php

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

+ 1 - 1
app/Utils/URLSchemes.php

@@ -21,7 +21,7 @@ class URLSchemes
     {
         $config = [
             "id" => $server['id'],
-            "remark" => $server['name'],
+            "remarks" => $server['name'],
             "server" => $server['host'],
             "server_port" => $server['port'],
             "password" => $user['uuid'],

+ 4 - 4
config/app.php

@@ -80,7 +80,7 @@ return [
     |
     */
 
-    'locale' => 'en',
+    'locale' => 'zh-CN',
 
     /*
     |--------------------------------------------------------------------------
@@ -93,7 +93,7 @@ return [
     |
     */
 
-    'fallback_locale' => 'en',
+    'fallback_locale' => 'zh-CN',
 
     /*
     |--------------------------------------------------------------------------
@@ -106,7 +106,7 @@ return [
     |
     */
 
-    'faker_locale' => 'en_US',
+    'faker_locale' => 'zh-CN',
 
     /*
     |--------------------------------------------------------------------------
@@ -236,5 +236,5 @@ return [
     | The only modification by laravel config
     |
     */
-    'version' => '1.4.2.1607914998'
+    'version' => '1.4.3.1612347430'
 ];

+ 246 - 225
database/install.sql

@@ -1,4 +1,4 @@
--- Adminer 4.7.6 MySQL dump
+-- Adminer 4.7.8 MySQL dump
 
 SET NAMES utf8;
 SET time_zone = '+00:00';
@@ -9,305 +9,326 @@ SET NAMES utf8mb4;
 
 DROP TABLE IF EXISTS `failed_jobs`;
 CREATE TABLE `failed_jobs` (
-  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
-  `connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
-  `queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
-  `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
-  `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
-  `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
-  PRIMARY KEY (`id`)
+                               `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+                               `connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
+                               `queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
+                               `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
+                               `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
+                               `failed_at` timestamp NOT NULL DEFAULT current_timestamp(),
+                               PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
 
 
 DROP TABLE IF EXISTS `v2_coupon`;
 CREATE TABLE `v2_coupon` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `code` varchar(255) NOT NULL,
-  `name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
-  `type` tinyint(1) NOT NULL,
-  `value` int(11) NOT NULL,
-  `limit_use` int(11) DEFAULT NULL,
-  `limit_plan_ids` varchar(255) DEFAULT NULL,
-  `started_at` int(11) NOT NULL,
-  `ended_at` int(11) NOT NULL,
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`)
+                             `id` int(11) NOT NULL AUTO_INCREMENT,
+                             `code` varchar(255) NOT NULL,
+                             `name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
+                             `type` tinyint(1) NOT NULL,
+                             `value` int(11) NOT NULL,
+                             `limit_use` int(11) DEFAULT NULL,
+                             `limit_plan_ids` varchar(255) DEFAULT NULL,
+                             `started_at` int(11) NOT NULL,
+                             `ended_at` int(11) NOT 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_invite_code`;
 CREATE TABLE `v2_invite_code` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `user_id` int(11) NOT NULL,
-  `code` char(32) NOT NULL,
-  `status` tinyint(1) NOT NULL DEFAULT '0',
-  `pv` int(11) NOT NULL DEFAULT '0',
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`)
+                                  `id` int(11) NOT NULL AUTO_INCREMENT,
+                                  `user_id` int(11) NOT NULL,
+                                  `code` char(32) NOT NULL,
+                                  `status` tinyint(1) NOT NULL DEFAULT 0,
+                                  `pv` int(11) NOT NULL DEFAULT 0,
+                                  `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_knowledge`;
 CREATE TABLE `v2_knowledge` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `language` char(5) NOT NULL COMMENT '語言',
-  `category` varchar(255) NOT NULL COMMENT '分類名',
-  `title` varchar(255) NOT NULL COMMENT '標題',
-  `body` text NOT NULL COMMENT '內容',
-  `sort` int(11) DEFAULT NULL COMMENT '排序',
-  `show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '顯示',
-  `created_at` int(11) NOT NULL COMMENT '創建時間',
-  `updated_at` int(11) NOT NULL COMMENT '更新時間',
-  PRIMARY KEY (`id`)
+                                `id` int(11) NOT NULL AUTO_INCREMENT,
+                                `language` char(5) NOT NULL COMMENT '語言',
+                                `category` varchar(255) NOT NULL COMMENT '分類名',
+                                `title` varchar(255) NOT NULL COMMENT '標題',
+                                `body` text NOT NULL COMMENT '內容',
+                                `sort` int(11) DEFAULT NULL COMMENT '排序',
+                                `show` tinyint(1) NOT NULL DEFAULT 0 COMMENT '顯示',
+                                `created_at` int(11) NOT NULL COMMENT '創建時間',
+                                `updated_at` int(11) NOT NULL COMMENT '更新時間',
+                                PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知識庫';
 
 
 DROP TABLE IF EXISTS `v2_mail_log`;
 CREATE TABLE `v2_mail_log` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `email` varchar(64) NOT NULL,
-  `subject` varchar(255) NOT NULL,
-  `template_name` varchar(255) NOT NULL,
-  `error` text,
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`)
+                               `id` int(11) NOT NULL AUTO_INCREMENT,
+                               `email` varchar(64) NOT NULL,
+                               `subject` varchar(255) NOT NULL,
+                               `template_name` varchar(255) NOT NULL,
+                               `error` text 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_notice`;
 CREATE TABLE `v2_notice` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `title` varchar(255) NOT NULL,
-  `content` text NOT NULL,
-  `img_url` varchar(255) DEFAULT NULL,
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`)
+                             `id` int(11) NOT NULL AUTO_INCREMENT,
+                             `title` varchar(255) NOT NULL,
+                             `content` text NOT NULL,
+                             `img_url` varchar(255) 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_order`;
 CREATE TABLE `v2_order` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `invite_user_id` int(11) DEFAULT NULL,
-  `user_id` int(11) NOT NULL,
-  `plan_id` int(11) NOT NULL,
-  `coupon_id` int(11) DEFAULT NULL,
-  `type` int(11) NOT NULL COMMENT '1新购2续费3升级',
-  `cycle` varchar(255) NOT NULL,
-  `trade_no` varchar(36) NOT NULL,
-  `callback_no` varchar(255) DEFAULT NULL,
-  `total_amount` int(11) NOT NULL,
-  `discount_amount` int(11) DEFAULT NULL,
-  `surplus_amount` int(11) DEFAULT NULL COMMENT '剩余价值',
-  `refund_amount` int(11) DEFAULT NULL COMMENT '退款金额',
-  `balance_amount` int(11) DEFAULT NULL COMMENT '使用余额',
-  `surplus_order_ids` text COMMENT '折抵订单',
-  `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_balance` int(11) NOT NULL DEFAULT '0',
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`)
+                            `id` int(11) NOT NULL AUTO_INCREMENT,
+                            `invite_user_id` int(11) DEFAULT NULL,
+                            `user_id` int(11) NOT NULL,
+                            `plan_id` int(11) NOT NULL,
+                            `coupon_id` int(11) DEFAULT NULL,
+                            `type` int(11) NOT NULL COMMENT '1新购2续费3升级',
+                            `cycle` varchar(255) NOT NULL,
+                            `trade_no` varchar(36) NOT NULL,
+                            `callback_no` varchar(255) DEFAULT NULL,
+                            `total_amount` int(11) NOT NULL,
+                            `discount_amount` int(11) DEFAULT NULL,
+                            `surplus_amount` int(11) DEFAULT NULL COMMENT '剩余价值',
+                            `refund_amount` int(11) DEFAULT NULL COMMENT '退款金额',
+                            `balance_amount` int(11) DEFAULT NULL COMMENT '使用余额',
+                            `surplus_order_ids` text DEFAULT NULL COMMENT '折抵订单',
+                            `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_balance` int(11) NOT NULL DEFAULT 0,
+                            `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_plan`;
 CREATE TABLE `v2_plan` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `group_id` int(11) NOT NULL,
-  `transfer_enable` int(11) NOT NULL,
-  `name` varchar(255) NOT NULL,
-  `show` tinyint(1) NOT NULL DEFAULT '0',
-  `sort` int(11) DEFAULT NULL,
-  `renew` tinyint(1) NOT NULL DEFAULT '1',
-  `content` text,
-  `month_price` int(11) DEFAULT NULL,
-  `quarter_price` int(11) DEFAULT NULL,
-  `half_year_price` int(11) DEFAULT NULL,
-  `year_price` int(11) DEFAULT NULL,
-  `two_year_price` int(11) DEFAULT NULL,
-  `three_year_price` int(11) DEFAULT NULL,
-  `onetime_price` int(11) DEFAULT NULL,
-  `reset_price` int(11) DEFAULT NULL,
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`)
+                           `id` int(11) NOT NULL AUTO_INCREMENT,
+                           `group_id` int(11) NOT NULL,
+                           `transfer_enable` int(11) NOT NULL,
+                           `name` varchar(255) NOT NULL,
+                           `show` tinyint(1) NOT NULL DEFAULT 0,
+                           `sort` int(11) DEFAULT NULL,
+                           `renew` tinyint(1) NOT NULL DEFAULT 1,
+                           `content` text DEFAULT NULL,
+                           `month_price` int(11) DEFAULT NULL,
+                           `quarter_price` int(11) DEFAULT NULL,
+                           `half_year_price` int(11) DEFAULT NULL,
+                           `year_price` int(11) DEFAULT NULL,
+                           `two_year_price` int(11) DEFAULT NULL,
+                           `three_year_price` int(11) DEFAULT NULL,
+                           `onetime_price` int(11) DEFAULT NULL,
+                           `reset_price` 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`;
 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`)
+                             `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 DEFAULT NULL,
+                             `rules` text DEFAULT NULL,
+                             `networkSettings` text DEFAULT NULL,
+                             `tlsSettings` text DEFAULT NULL,
+                             `ruleSettings` text DEFAULT NULL,
+                             `dnsSettings` text DEFAULT NULL,
+                             `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`;
 CREATE TABLE `v2_server_group` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `name` varchar(255) NOT NULL,
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`)
+                                   `id` int(11) NOT NULL AUTO_INCREMENT,
+                                   `name` varchar(255) NOT 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_log`;
 CREATE TABLE `v2_server_log` (
-  `id` bigint(20) NOT NULL AUTO_INCREMENT,
-  `user_id` int(11) NOT NULL,
-  `server_id` int(11) NOT NULL,
-  `u` varchar(255) NOT NULL,
-  `d` varchar(255) NOT NULL,
-  `rate` decimal(10,2) NOT NULL,
-  `method` varchar(255) NOT NULL,
-  `log_at` int(11) NOT NULL,
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`),
-  KEY `log_at` (`log_at`)
+                                 `id` bigint(20) NOT NULL AUTO_INCREMENT,
+                                 `user_id` int(11) NOT NULL,
+                                 `server_id` int(11) NOT NULL,
+                                 `u` varchar(255) NOT NULL,
+                                 `d` varchar(255) NOT NULL,
+                                 `rate` decimal(10,2) NOT NULL,
+                                 `method` varchar(255) NOT NULL,
+                                 `log_at` int(11) NOT NULL,
+                                 `created_at` int(11) NOT NULL,
+                                 `updated_at` int(11) NOT NULL,
+                                 PRIMARY KEY (`id`),
+                                 KEY `log_at` (`log_at`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
 DROP TABLE IF EXISTS `v2_server_shadowsocks`;
 CREATE TABLE `v2_server_shadowsocks` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `group_id` varchar(255) NOT NULL,
-  `parent_id` int(11) DEFAULT NULL,
-  `tags` varchar(255) DEFAULT NULL,
-  `name` varchar(255) NOT NULL,
-  `rate` varchar(11) NOT NULL,
-  `host` varchar(255) NOT NULL,
-  `port` int(11) NOT NULL,
-  `server_port` int(11) NOT NULL,
-  `cipher` varchar(255) NOT NULL,
-  `show` tinyint(4) NOT NULL DEFAULT '0',
-  `sort` int(11) DEFAULT NULL,
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`)
+                                         `id` int(11) NOT NULL AUTO_INCREMENT,
+                                         `group_id` varchar(255) NOT NULL,
+                                         `parent_id` int(11) DEFAULT NULL,
+                                         `tags` varchar(255) DEFAULT NULL,
+                                         `name` varchar(255) NOT NULL,
+                                         `rate` varchar(11) NOT NULL,
+                                         `host` varchar(255) NOT NULL,
+                                         `port` int(11) NOT NULL,
+                                         `server_port` int(11) NOT NULL,
+                                         `cipher` varchar(255) NOT NULL,
+                                         `show` tinyint(4) 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=utf8mb4;
 
 
-DROP TABLE IF EXISTS `v2_server_stat`;
-CREATE TABLE `v2_server_stat` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `server_id` int(11) NOT NULL,
-  `method` varchar(255) NOT NULL,
-  `u` varchar(255) NOT NULL,
-  `d` varchar(255) NOT NULL,
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`),
-  KEY `created_at` (`created_at`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-
 DROP TABLE IF EXISTS `v2_server_trojan`;
 CREATE TABLE `v2_server_trojan` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '节点ID',
-  `group_id` varchar(255) NOT NULL COMMENT '节点组',
-  `parent_id` int(11) DEFAULT NULL COMMENT '父节点',
-  `tags` varchar(255) DEFAULT NULL COMMENT '节点标签',
-  `name` varchar(255) NOT NULL COMMENT '节点名称',
-  `rate` varchar(11) NOT NULL COMMENT '倍率',
-  `host` varchar(255) NOT NULL COMMENT '主机名',
-  `port` int(11) NOT NULL COMMENT '连接端口',
-  `server_port` int(11) NOT NULL COMMENT '服务端口',
-  `allow_insecure` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否允许不安全',
-  `server_name` varchar(255) DEFAULT NULL,
-  `show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否显示',
-  `sort` int(11) DEFAULT NULL,
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`)
+                                    `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '节点ID',
+                                    `group_id` varchar(255) NOT NULL COMMENT '节点组',
+                                    `parent_id` int(11) DEFAULT NULL COMMENT '父节点',
+                                    `tags` varchar(255) DEFAULT NULL COMMENT '节点标签',
+                                    `name` varchar(255) NOT NULL COMMENT '节点名称',
+                                    `rate` varchar(11) NOT NULL COMMENT '倍率',
+                                    `host` varchar(255) NOT NULL COMMENT '主机名',
+                                    `port` int(11) NOT NULL COMMENT '连接端口',
+                                    `server_port` int(11) NOT NULL COMMENT '服务端口',
+                                    `allow_insecure` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否允许不安全',
+                                    `server_name` varchar(255) DEFAULT NULL,
+                                    `show` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否显示',
+                                    `sort` int(11) DEFAULT NULL,
+                                    `created_at` int(11) NOT NULL,
+                                    `updated_at` int(11) NOT NULL,
+                                    PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='trojan伺服器表';
 
 
+DROP TABLE IF EXISTS `v2_stat_order`;
+CREATE TABLE `v2_stat_order` (
+                                 `id` int(11) NOT NULL AUTO_INCREMENT,
+                                 `order_count` int(11) NOT NULL COMMENT '订单数量',
+                                 `order_amount` int(11) NOT NULL COMMENT '订单合计',
+                                 `commission_count` int(11) NOT NULL,
+                                 `commission_amount` int(11) NOT NULL COMMENT '佣金合计',
+                                 `record_type` char(1) NOT NULL,
+                                 `record_at` int(11) NOT NULL,
+                                 `created_at` int(11) NOT NULL,
+                                 `updated_at` int(11) NOT NULL,
+                                 PRIMARY KEY (`id`),
+                                 UNIQUE KEY `record_at` (`record_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单统计';
+
+
+DROP TABLE IF EXISTS `v2_stat_server`;
+CREATE TABLE `v2_stat_server` (
+                                  `id` int(11) NOT NULL AUTO_INCREMENT,
+                                  `server_id` int(11) NOT NULL COMMENT '节点id',
+                                  `server_type` char(11) NOT NULL COMMENT '节点类型',
+                                  `u` varchar(255) NOT NULL,
+                                  `d` varchar(255) NOT NULL,
+                                  `record_type` char(1) NOT NULL COMMENT 'd day m month',
+                                  `record_at` int(11) NOT NULL COMMENT '记录时间',
+                                  `created_at` int(11) NOT NULL,
+                                  `updated_at` int(11) NOT NULL,
+                                  PRIMARY KEY (`id`),
+                                  UNIQUE KEY `server_id_server_type_record_at` (`server_id`,`server_type`,`record_at`),
+                                  KEY `record_at` (`record_at`),
+                                  KEY `server_id` (`server_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='节点数据统计';
+
+
 DROP TABLE IF EXISTS `v2_ticket`;
 CREATE TABLE `v2_ticket` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `user_id` int(11) NOT NULL,
-  `last_reply_user_id` int(11) NOT NULL,
-  `subject` varchar(255) NOT NULL,
-  `level` tinyint(1) NOT NULL,
-  `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:已开启 1:已关闭',
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`)
+                             `id` int(11) NOT NULL AUTO_INCREMENT,
+                             `user_id` int(11) NOT NULL,
+                             `last_reply_user_id` int(11) NOT NULL,
+                             `subject` varchar(255) NOT NULL,
+                             `level` tinyint(1) NOT NULL,
+                             `status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0:已开启 1:已关闭',
+                             `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_ticket_message`;
 CREATE TABLE `v2_ticket_message` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `user_id` int(11) NOT NULL,
-  `ticket_id` int(11) NOT NULL,
-  `message` varchar(255) NOT NULL,
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`)
+                                     `id` int(11) NOT NULL AUTO_INCREMENT,
+                                     `user_id` int(11) NOT NULL,
+                                     `ticket_id` int(11) NOT NULL,
+                                     `message` varchar(255) NOT 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_user`;
 CREATE TABLE `v2_user` (
-  `id` int(11) NOT NULL AUTO_INCREMENT,
-  `invite_user_id` int(11) DEFAULT NULL,
-  `telegram_id` bigint(20) DEFAULT NULL,
-  `email` varchar(64) NOT NULL,
-  `password` varchar(64) NOT NULL,
-  `password_algo` char(10) DEFAULT NULL,
-  `balance` int(11) NOT NULL DEFAULT '0',
-  `discount` int(11) DEFAULT NULL,
-  `commission_rate` int(11) DEFAULT NULL,
-  `commission_balance` int(11) NOT NULL DEFAULT '0',
-  `t` int(11) NOT NULL DEFAULT '0',
-  `u` bigint(20) NOT NULL DEFAULT '0',
-  `d` bigint(20) NOT NULL DEFAULT '0',
-  `transfer_enable` bigint(20) NOT NULL DEFAULT '0',
-  `banned` tinyint(1) NOT NULL DEFAULT '0',
-  `is_admin` tinyint(1) NOT NULL DEFAULT '0',
-  `is_staff` tinyint(1) NOT NULL DEFAULT '0',
-  `last_login_at` int(11) DEFAULT NULL,
-  `last_login_ip` int(11) DEFAULT NULL,
-  `uuid` varchar(36) NOT NULL,
-  `group_id` int(11) DEFAULT NULL,
-  `plan_id` int(11) DEFAULT NULL,
-  `remind_expire` tinyint(4) DEFAULT '1',
-  `remind_traffic` tinyint(4) DEFAULT '1',
-  `token` char(32) NOT NULL,
-  `expired_at` bigint(20) DEFAULT '0',
-  `created_at` int(11) NOT NULL,
-  `updated_at` int(11) NOT NULL,
-  PRIMARY KEY (`id`),
-  UNIQUE KEY `email` (`email`)
+                           `id` int(11) NOT NULL AUTO_INCREMENT,
+                           `invite_user_id` int(11) DEFAULT NULL,
+                           `telegram_id` bigint(20) DEFAULT NULL,
+                           `email` varchar(64) NOT NULL,
+                           `password` varchar(64) NOT NULL,
+                           `password_algo` char(10) DEFAULT NULL,
+                           `balance` int(11) NOT NULL DEFAULT 0,
+                           `discount` int(11) DEFAULT NULL,
+                           `commission_rate` int(11) DEFAULT NULL,
+                           `commission_balance` int(11) NOT NULL DEFAULT 0,
+                           `t` int(11) NOT NULL DEFAULT 0,
+                           `u` bigint(20) NOT NULL DEFAULT 0,
+                           `d` bigint(20) NOT NULL DEFAULT 0,
+                           `transfer_enable` bigint(20) NOT NULL DEFAULT 0,
+                           `banned` tinyint(1) NOT NULL DEFAULT 0,
+                           `is_admin` tinyint(1) NOT NULL DEFAULT 0,
+                           `is_staff` tinyint(1) NOT NULL DEFAULT 0,
+                           `last_login_at` int(11) DEFAULT NULL,
+                           `last_login_ip` int(11) DEFAULT NULL,
+                           `uuid` varchar(36) NOT NULL,
+                           `group_id` int(11) DEFAULT NULL,
+                           `plan_id` int(11) DEFAULT NULL,
+                           `remind_expire` tinyint(4) DEFAULT 1,
+                           `remind_traffic` tinyint(4) DEFAULT 1,
+                           `token` char(32) NOT NULL,
+                           `remarks` text DEFAULT NULL,
+                           `expired_at` bigint(20) DEFAULT 0,
+                           `created_at` int(11) NOT NULL,
+                           `updated_at` int(11) NOT NULL,
+                           PRIMARY KEY (`id`),
+                           UNIQUE KEY `email` (`email`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
--- 2020-11-17 10:46:49
+-- 2021-01-21 14:25:17

+ 42 - 0
database/update.sql

@@ -352,3 +352,45 @@ ADD `alter_id` int(11) NOT NULL DEFAULT '1' AFTER `network`;
 ALTER TABLE `v2_user`
 DROP `v2ray_alter_id`,
 DROP `v2ray_level`;
+
+DROP TABLE `v2_server_stat`;
+
+CREATE TABLE `v2_stat_server` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `server_id` int(11) NOT NULL COMMENT '节点id',
+  `server_type` char(11) NOT NULL COMMENT '节点类型',
+  `u` varchar(255) NOT NULL,
+  `d` varchar(255) NOT NULL,
+  `record_type` char(1) NOT NULL COMMENT 'd day m month',
+  `record_at` int(11) NOT NULL COMMENT '记录时间',
+  `created_at` int(11) NOT NULL,
+  `updated_at` int(11) NOT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='节点数据统计';
+
+ALTER TABLE `v2_stat_server`
+ADD UNIQUE `server_id_server_type_record_at` (`server_id`, `server_type`, `record_at`);
+
+ALTER TABLE `v2_stat_server`
+ADD INDEX `record_at` (`record_at`),
+ADD INDEX `server_id` (`server_id`);
+
+CREATE TABLE `v2_stat_order` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `order_count` int(11) NOT NULL COMMENT '订单数量',
+  `order_amount` int(11) NOT NULL COMMENT '订单合计',
+  `commission_count` int(11) NOT NULL,
+  `commission_amount` int(11) NOT NULL COMMENT '佣金合计',
+  `record_type` char(1) NOT NULL,
+  `record_at` int(11) NOT NULL,
+  `created_at` int(11) NOT NULL,
+  `updated_at` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `record_at` (`record_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单统计';
+
+ALTER TABLE `v2_user`
+DROP `enable`;
+
+ALTER TABLE `v2_user`
+    ADD `remarks` text COLLATE 'utf8_general_ci' NULL AFTER `token`;

+ 1 - 1
pm2.yaml

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

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
public/assets/admin/umi.js


Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
public/assets/admin/vendors.async.js


Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
public/assets/user/components.async.js


Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
public/assets/user/components.chunk.css


Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
public/assets/user/umi.css


Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
public/assets/user/umi.js


+ 37 - 0
resources/lang/en-US/passport.php

@@ -0,0 +1,37 @@
+<?php
+
+return [
+    'auth' => [
+        'register' => [
+            'verify_incorrect' => 'Invalid code is incorrect',
+            'email_suffix_not_in_whitelist' => 'Email suffix is not in the Whitelist',
+            'no_support_gmail_alias' => 'Gmail alias is not supported',
+            'close_register' => 'Registration has closed',
+            'must_use_invite_code' => 'You must use the invitation code to register',
+            'email_code_not_empty' => 'Email verification code cannot be empty',
+            'email_code_incorrect' => 'Incorrect email verification code',
+            'email_exist_system' => 'Email already exists',
+            'invalid_invite_code' => 'Invalid invitation code',
+            'register_failed' => 'Register failed'
+        ],
+        'login' => [
+            'wrong_email_or_password' => 'Incorrect email or password',
+            'account_been_discontinued' => 'Your account has been suspended'
+        ],
+        'getQuickLoginUrl' => [
+            'wrong_token' => 'Token error'
+        ],
+        'forget' => [
+            'email_verification_code_incorrect' => 'Incorrect email verification code',
+            'email_not_exist_system' => 'This email is not registered in the system',
+            'reset_failed' => 'Reset failed'
+        ]
+    ],
+    'comm' => [
+        'sendEmailVerify' => [
+            'verification_code_incorrect' => 'Incorrect email verification code',
+            'code_sent_request_later' => 'Email verification code has been sent, please request again later',
+            'email_verification_code' => 'Email verification code'
+        ]
+    ]
+];

+ 132 - 0
resources/lang/en-US/user.php

@@ -0,0 +1,132 @@
+<?php
+
+return [
+    'user' => [
+        'changePassword' => [
+            'user_not_exist' => 'The user does not exist',
+            'old_password_wrong' => 'The old password is wrong',
+            'save_failed' => 'Save failed'
+        ],
+        'info' => [
+            'user_not_exist' => 'The user does not exist'
+        ],
+        'getSubscribe' => [
+            'user_not_exist' => 'The user does not exist',
+            'plan_not_exist' => 'Subscription plan does not exist',
+        ],
+        'resetSecurity' => [
+            'user_not_exist' => 'The user does not exist',
+            'reset_failed' => 'Reset failed'
+        ],
+        'update' => [
+            'user_not_exist' => 'The user does not exist',
+            'save_failed' => 'Save failed',
+        ],
+        'transfer' => [
+            'user_not_exist' => 'The user does not exist',
+            'params_wrong' => 'Invalid parameter',
+            'insufficient_commission_balance' => 'Insufficient commission balance',
+            'transfer_failed' => 'Transfer failed'
+        ]
+    ],
+    'ticket' => [
+        'fetch' => [
+            'ticket_not_exist' => 'Ticket does not exist',
+        ],
+        'save' => [
+            'exist_other_open_ticket' => 'There are other unresolved tickets',
+            'ticket_create_failed' => 'Failed to open ticket',
+        ],
+        'reply' => [
+            'params_wrong' => 'Invalid parameter',
+            'message_not_empty' => 'Message cannot be empty',
+            'ticket_not_exist' => 'Ticket does not exist',
+            'ticket_close_not_reply' => 'The ticket is closed and cannot be replied',
+            'wait_reply' => 'Please wait for the technical enginneer to reply',
+            'ticket_reply_failed' => 'Ticket reply failed',
+        ],
+        'close' => [
+            'params_wrong' => 'Invalid parameter',
+            'ticket_not_exist' => 'Ticket does not exist',
+            'close_failed' => 'Close failed',
+        ],
+        'withdraw' => [
+            'not_support_withdraw_method' => 'Unsupported withdrawal method',
+            'system_require_withdraw_limit' => 'The current required minimum withdrawal commission is: ¥:limitCNY',
+            'ticket_subject' => '[Commission Withdrawal Request] This ticket is opened by the system',
+            'ticket_create_failed' => 'Failed to open ticket',
+            'ticket_message' => "Withdrawal method: :method\r\nPayment account: :account\r\n",
+            'not_support_withdraw' => 'Unsupported withdrawal'
+        ]
+    ],
+    'plan' => [
+        'fetch' => [
+            'plan_not_exist' => 'Subscription plan does not exist'
+        ]
+    ],
+    'order' => [
+        'details' => [
+            'order_not_exist' => 'Order does not exist',
+            'plan_not_exist' => 'Subscription plan does not exist',
+        ],
+        'save' => [
+            'plan_not_exist' => 'Subscription plan does not exist',
+            'exist_open_order' => 'You have an unpaid or pending order, please try again later or cancel it',
+            'plan_stop_sell' => 'This subscription has been sold out, please choose another subscription',
+            'plan_stop_renew' => 'This subscription cannot be renewed, please change to another subscription',
+            'plan_stop' => 'This payment cycle cannot be purchased, please choose another cycle',
+            'plan_exist_not_buy_package' => 'Subscription has expired or no active subscription, unable to purchase Data Reset Package',
+            'plan_expired' => 'This subscription has expired, please change to another subscription',
+            'coupon_use_failed' => 'Invalid coupon',
+            'insufficient_balance' => 'Insufficient balance',
+            'order_create_failed' => 'Failed to create order'
+        ],
+        'checkout' => [
+            'order_not_exist_or_paid' => 'Order does not exist or has been paid',
+            'pay_method_not_use' => 'Payment method is not available',
+        ],
+        'check' => [
+            'order_not_exist' => 'Order does not exist'
+        ],
+        'cancel' => [
+            'params_wrong' => 'Invalid parameter',
+            'order_not_exist' => 'Order does not exist',
+            'only_cancel_pending_order' => 'You can only cancel pending orders',
+            'cancel_failed' => 'Cancel failed',
+        ],
+        'stripeAlipay' => [
+            'currency_convert_timeout' => 'Currency conversion has timed out, please try again later',
+            'gateway_request_failed' => 'Payment gateway request failed',
+        ],
+        'stripeWepay' => [
+            'currency_convert_timeout' => 'Currency conversion has timed out, please try again later',
+            'gateway_request_failed' => 'Payment gateway request failed',
+        ],
+        'stripeCard' => [
+            'currency_convert_timeout' => 'Currency conversion has timed out, please try again later',
+            'was_problem' => 'Oops, there's a problem... Please refresh the page and try again later',
+            'deduction_failed' => 'Payment failed. Please check your credit card information'
+        ]
+    ],
+    'knowledge' => [
+        'fetch' => [
+            'knowledge_not_exist' => 'Article does not exist',
+            'apple_id_must_be_plan' => 'No active subscription. Unable to use our provided Apple ID'
+        ]
+    ],
+    'invite' => [
+        'save' => [
+            'invite_create_limit' => 'The maximum number of creations has been reached'
+        ]
+    ],
+    'coupon' => [
+        'check' => [
+            'coupon_not_empty' => 'Coupon cannot be empty',
+            'coupon_invalid' => 'Invalid coupon',
+            'coupon_not_available_by_number' => 'This coupon is no longer available',
+            'coupon_not_available_by_time' => 'This coupon has not yet started',
+            'coupon_expired' => 'This coupon has expired',
+            'coupon_limit_plan' => 'The coupon code cannot be used for this subscription'
+        ]
+    ]
+];

+ 0 - 19
resources/lang/en/auth.php

@@ -1,19 +0,0 @@
-<?php
-
-return [
-
-    /*
-    |--------------------------------------------------------------------------
-    | Authentication Language Lines
-    |--------------------------------------------------------------------------
-    |
-    | The following language lines are used during authentication for various
-    | messages that we need to display to the user. You are free to modify
-    | these language lines according to your application's requirements.
-    |
-    */
-
-    'failed' => 'These credentials do not match our records.',
-    'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
-
-];

+ 0 - 19
resources/lang/en/pagination.php

@@ -1,19 +0,0 @@
-<?php
-
-return [
-
-    /*
-    |--------------------------------------------------------------------------
-    | Pagination Language Lines
-    |--------------------------------------------------------------------------
-    |
-    | The following language lines are used by the paginator library to build
-    | the simple pagination links. You are free to change them to anything
-    | you want to customize your views to better match your application.
-    |
-    */
-
-    'previous' => '&laquo; Previous',
-    'next' => 'Next &raquo;',
-
-];

+ 0 - 21
resources/lang/en/passwords.php

@@ -1,21 +0,0 @@
-<?php
-
-return [
-
-    /*
-    |--------------------------------------------------------------------------
-    | Password Reset Language Lines
-    |--------------------------------------------------------------------------
-    |
-    | The following language lines are the default lines which match reasons
-    | that are given by the password broker for a password update attempt
-    | has failed, such as for an invalid token or invalid new password.
-    |
-    */
-
-    'reset' => 'Your password has been reset!',
-    'sent' => 'We have e-mailed your password reset link!',
-    'token' => 'This password reset token is invalid.',
-    'user' => "We can't find a user with that e-mail address.",
-
-];

+ 0 - 150
resources/lang/en/validation.php

@@ -1,150 +0,0 @@
-<?php
-
-return [
-
-    /*
-    |--------------------------------------------------------------------------
-    | Validation Language Lines
-    |--------------------------------------------------------------------------
-    |
-    | The following language lines contain the default error messages used by
-    | the validator class. Some of these rules have multiple versions such
-    | as the size rules. Feel free to tweak each of these messages here.
-    |
-    */
-
-    'accepted' => 'The :attribute must be accepted.',
-    'active_url' => 'The :attribute is not a valid URL.',
-    'after' => 'The :attribute must be a date after :date.',
-    'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
-    'alpha' => 'The :attribute may only contain letters.',
-    'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.',
-    'alpha_num' => 'The :attribute may only contain letters and numbers.',
-    'array' => 'The :attribute must be an array.',
-    'before' => 'The :attribute must be a date before :date.',
-    'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
-    'between' => [
-        'numeric' => 'The :attribute must be between :min and :max.',
-        'file' => 'The :attribute must be between :min and :max kilobytes.',
-        'string' => 'The :attribute must be between :min and :max characters.',
-        'array' => 'The :attribute must have between :min and :max items.',
-    ],
-    'boolean' => 'The :attribute field must be true or false.',
-    'confirmed' => 'The :attribute confirmation does not match.',
-    'date' => 'The :attribute is not a valid date.',
-    'date_equals' => 'The :attribute must be a date equal to :date.',
-    'date_format' => 'The :attribute does not match the format :format.',
-    'different' => 'The :attribute and :other must be different.',
-    'digits' => 'The :attribute must be :digits digits.',
-    'digits_between' => 'The :attribute must be between :min and :max digits.',
-    'dimensions' => 'The :attribute has invalid image dimensions.',
-    'distinct' => 'The :attribute field has a duplicate value.',
-    'email' => 'The :attribute must be a valid email address.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
-    'exists' => 'The selected :attribute is invalid.',
-    'file' => 'The :attribute must be a file.',
-    'filled' => 'The :attribute field must have a value.',
-    'gt' => [
-        'numeric' => 'The :attribute must be greater than :value.',
-        'file' => 'The :attribute must be greater than :value kilobytes.',
-        'string' => 'The :attribute must be greater than :value characters.',
-        'array' => 'The :attribute must have more than :value items.',
-    ],
-    'gte' => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file' => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string' => 'The :attribute must be greater than or equal :value characters.',
-        'array' => 'The :attribute must have :value items or more.',
-    ],
-    'image' => 'The :attribute must be an image.',
-    'in' => 'The selected :attribute is invalid.',
-    'in_array' => 'The :attribute field does not exist in :other.',
-    'integer' => 'The :attribute must be an integer.',
-    'ip' => 'The :attribute must be a valid IP address.',
-    'ipv4' => 'The :attribute must be a valid IPv4 address.',
-    'ipv6' => 'The :attribute must be a valid IPv6 address.',
-    'json' => 'The :attribute must be a valid JSON string.',
-    'lt' => [
-        'numeric' => 'The :attribute must be less than :value.',
-        'file' => 'The :attribute must be less than :value kilobytes.',
-        'string' => 'The :attribute must be less than :value characters.',
-        'array' => 'The :attribute must have less than :value items.',
-    ],
-    'lte' => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file' => 'The :attribute must be less than or equal :value kilobytes.',
-        'string' => 'The :attribute must be less than or equal :value characters.',
-        'array' => 'The :attribute must not have more than :value items.',
-    ],
-    'max' => [
-        'numeric' => 'The :attribute may not be greater than :max.',
-        'file' => 'The :attribute may not be greater than :max kilobytes.',
-        'string' => 'The :attribute may not be greater than :max characters.',
-        'array' => 'The :attribute may not have more than :max items.',
-    ],
-    'mimes' => 'The :attribute must be a file of type: :values.',
-    'mimetypes' => 'The :attribute must be a file of type: :values.',
-    'min' => [
-        'numeric' => 'The :attribute must be at least :min.',
-        'file' => 'The :attribute must be at least :min kilobytes.',
-        'string' => 'The :attribute must be at least :min characters.',
-        'array' => 'The :attribute must have at least :min items.',
-    ],
-    'not_in' => 'The selected :attribute is invalid.',
-    'not_regex' => 'The :attribute format is invalid.',
-    'numeric' => 'The :attribute must be a number.',
-    'present' => 'The :attribute field must be present.',
-    'regex' => 'The :attribute format is invalid.',
-    'required' => 'The :attribute field is required.',
-    'required_if' => 'The :attribute field is required when :other is :value.',
-    'required_unless' => 'The :attribute field is required unless :other is in :values.',
-    'required_with' => 'The :attribute field is required when :values is present.',
-    'required_with_all' => 'The :attribute field is required when :values are present.',
-    'required_without' => 'The :attribute field is required when :values is not present.',
-    'required_without_all' => 'The :attribute field is required when none of :values are present.',
-    'same' => 'The :attribute and :other must match.',
-    'size' => [
-        'numeric' => 'The :attribute must be :size.',
-        'file' => 'The :attribute must be :size kilobytes.',
-        'string' => 'The :attribute must be :size characters.',
-        'array' => 'The :attribute must contain :size items.',
-    ],
-    'starts_with' => 'The :attribute must start with one of the following: :values',
-    'string' => 'The :attribute must be a string.',
-    'timezone' => 'The :attribute must be a valid zone.',
-    'unique' => 'The :attribute has already been taken.',
-    'uploaded' => 'The :attribute failed to upload.',
-    'url' => 'The :attribute format is invalid.',
-    'uuid' => 'The :attribute must be a valid UUID.',
-
-    /*
-    |--------------------------------------------------------------------------
-    | Custom Validation Language Lines
-    |--------------------------------------------------------------------------
-    |
-    | Here you may specify custom validation messages for attributes using the
-    | convention "attribute.rule" to name the lines. This makes it quick to
-    | specify a specific custom language line for a given attribute rule.
-    |
-    */
-
-    'custom' => [
-        'attribute-name' => [
-            'rule-name' => 'custom-message',
-        ],
-    ],
-
-    /*
-    |--------------------------------------------------------------------------
-    | Custom Validation Attributes
-    |--------------------------------------------------------------------------
-    |
-    | The following language lines are used to swap our attribute placeholder
-    | with something more reader friendly such as "E-Mail Address" instead
-    | of "email". This simply helps us make our message more expressive.
-    |
-    */
-
-    'attributes' => [],
-
-];

+ 37 - 0
resources/lang/zh-CN/passport.php

@@ -0,0 +1,37 @@
+<?php
+
+return [
+    'auth' => [
+        'register' => [
+            'verify_incorrect' => '验证码有误',
+            'email_suffix_not_in_whitelist' => '邮箱后缀不处于白名单中',
+            'no_support_gmail_alias' => '不支持 Gmail 别名邮箱',
+            'close_register' => '本站已关闭注册',
+            'must_use_invite_code' => '必须使用邀请码才可以注册',
+            'email_code_not_empty' => '邮箱验证码不能为空',
+            'email_code_incorrect' => '邮箱验证码有误',
+            'email_exist_system' => '邮箱已存在系统中',
+            'invalid_invite_code' => '邀请码无效',
+            'register_failed' => '注册失败'
+        ],
+        'login' => [
+            'wrong_email_or_password' => '邮箱或密码错误',
+            'account_been_discontinued' => '该账户已被停止使用'
+        ],
+        'getQuickLoginUrl' => [
+            'wrong_token' => '令牌有误'
+        ],
+        'forget' => [
+            'email_verification_code_incorrect' => '邮箱验证码有误',
+            'email_not_exist_system' => '该邮箱不存在系统中',
+            'reset_failed' => '重置失败'
+        ]
+    ],
+    'comm' => [
+        'sendEmailVerify' => [
+            'verification_code_incorrect' => '验证码有误',
+            'code_sent_request_later' => '验证码已发送,请过一会再请求',
+            'email_verification_code' => '邮箱验证码'
+        ]
+    ]
+];

+ 132 - 0
resources/lang/zh-CN/user.php

@@ -0,0 +1,132 @@
+<?php
+
+return [
+    'user' => [
+        'changePassword' => [
+            'user_not_exist' => '该用户不存在',
+            'old_password_wrong' => '旧密码有误',
+            'save_failed' => '保存失败'
+        ],
+        'info' => [
+            'user_not_exist' => '该用户不存在'
+        ],
+        'getSubscribe' => [
+            'user_not_exist' => '该用户不存在',
+            'plan_not_exist' => '订阅计划不存在',
+        ],
+        'resetSecurity' => [
+            'user_not_exist' => '该用户不存在',
+            'reset_failed' => '重置失败'
+        ],
+        'update' => [
+            'user_not_exist' => '该用户不存在',
+            'save_failed' => '保存失败',
+        ],
+        'transfer' => [
+            'user_not_exist' => '该用户不存在',
+            'params_wrong' => '参数错误',
+            'insufficient_commission_balance' => '推广佣金余额不足',
+            'transfer_failed' => '划转失败'
+        ]
+    ],
+    'ticket' => [
+        'fetch' => [
+            'ticket_not_exist' => '工单不存在',
+        ],
+        'save' => [
+            'exist_other_open_ticket' => '存在其它工单尚未处理',
+            'ticket_create_failed' => '工单创建失败',
+        ],
+        'reply' => [
+            'params_wrong' => '参数错误',
+            'message_not_empty' => '消息不能为空',
+            'ticket_not_exist' => '工单不存在',
+            'ticket_close_not_reply' => '工单已关闭,无法回复',
+            'wait_reply' => '请等待技术支持回复',
+            'ticket_reply_failed' => '工单回复失败',
+        ],
+        'close' => [
+            'params_wrong' => '参数错误',
+            'ticket_not_exist' => '工单不存在',
+            'close_failed' => '关闭失败',
+        ],
+        'withdraw' => [
+            'not_support_withdraw_method' => '不支持的提现方式',
+            'system_require_withdraw_limit' => '当前系统要求的最少提现佣金为:¥:limitCNY',
+            'ticket_subject' => '[提现申请] 本工单由系统发出',
+            'ticket_create_failed' => '工单创建失败',
+            'ticket_message' => "提现方式::method\r\n提现账号::account\r\n",
+            'not_support_withdraw' => '不支持提现'
+        ]
+    ],
+    'plan' => [
+        'fetch' => [
+            'plan_not_exist' => '订阅计划不存在'
+        ]
+    ],
+    'order' => [
+        'details' => [
+            'order_not_exist' => '订单不存在',
+            'plan_not_exist' => '订阅计划不存在',
+        ],
+        'save' => [
+            'plan_not_exist' => '订阅计划不存在',
+            'exist_open_order' => '您有未付款或开通中的订单,请稍后再试或将其取消',
+            'plan_stop_sell' => '该订阅已售罄,请更换其它订阅',
+            'plan_stop_renew' => '该订阅无法续费,请更换其它订阅',
+            'plan_stop' => '该订阅周期无法进行购买,请选择其它周期',
+            'plan_exist_not_buy_package' => '订阅已过期或无有效订阅,无法购买重置包',
+            'plan_expired' => '订阅已过期,请更换其它订阅',
+            'coupon_use_failed' => '优惠券使用失败',
+            'insufficient_balance' => '余额不足',
+            'order_create_failed' => '订单创建失败'
+        ],
+        'checkout' => [
+            'order_not_exist_or_paid' => '订单不存在或已支付',
+            'pay_method_not_use' => '支付方式不可用',
+        ],
+        'check' => [
+            'order_not_exist' => '订单不存在'
+        ],
+        'cancel' => [
+            'params_wrong' => '参数有误',
+            'order_not_exist' => '订单不存在',
+            'only_cancel_pending_order' => '只可以取消待支付订单',
+            'cancel_failed' => '取消失败',
+        ],
+        'stripeAlipay' => [
+            'currency_convert_timeout' => '货币转换超时,请稍后再试',
+            'gateway_request_failed' => '支付网关请求失败',
+        ],
+        'stripeWepay' => [
+            'currency_convert_timeout' => '货币转换超时,请稍后再试',
+            'gateway_request_failed' => '支付网关请求失败',
+        ],
+        'stripeCard' => [
+            'currency_convert_timeout' => '货币转换超时,请稍后再试',
+            'was_problem' => '遇到了点问题,请刷新页面稍后再试',
+            'deduction_failed' => '扣款失败,请检查信用卡信息'
+        ]
+    ],
+    'knowledge' => [
+        'fetch' => [
+            'knowledge_not_exist' => '文章不存在',
+            'apple_id_must_be_plan' => '无有效订阅,无法使用本站提供的 AppleID'
+        ]
+    ],
+    'invite' => [
+        'save' => [
+            'invite_create_limit' => '已达到创建数量上限'
+        ]
+    ],
+    'coupon' => [
+        'check' => [
+            'coupon_not_empty' => '优惠券不能为空',
+            'coupon_invalid' => '优惠券无效',
+            'coupon_not_available_by_number' => '优惠券已无可用次数',
+            'coupon_not_available_by_time' => '优惠券还未到可用时间',
+            'coupon_expired' => '优惠券已过期',
+            'coupon_limit_plan' => '该订阅无法使用此优惠码'
+        ]
+    ]
+];

+ 11 - 7
resources/rules/app.clash.yaml

@@ -1,11 +1,14 @@
-port: 8890
-socks-port: 8891
-allow-lan: false
+port: 7890
+socks-port: 7891
+# redir-port: 7892
+# tproxy-port: 7893
+# mixed-port: 7890
+allow-lan: true
+bind-address: "*"
 mode: rule
 log-level: info
-external-controller: 127.0.0.1:9091
-experimental:
-  ignore-resolve-fail: true
+external-controller: 127.0.0.1:9090
+
 dns:
   enable: true
   # listen: 0.0.0.0:53
@@ -18,15 +21,16 @@ dns:
   fake-ip-range: 198.18.0.1/16
   use-hosts: true
   nameserver:
-    - https://dns.alidns.com/dns-query
     - https://doh.pub/dns-query
   fallback:
     - tls://1.0.0.1:853
+    - https://cloudflare-dns.com/dns-query
     - https://dns.google/dns-query
   fallback-filter:
     geoip: true
     ipcidr:
       - 240.0.0.0/4
+      - 0.0.0.0/32
 
 proxies:
 

Неке датотеке нису приказане због велике количине промена