Browse Source

Merge pull request #125 from v2board/dev

1.2.3
tokumeikoi 5 years ago
parent
commit
56fd3f5b99
52 changed files with 1315 additions and 338 deletions
  1. 7 6
      app/Console/Commands/CheckOrder.php
  2. 12 15
      app/Console/Commands/ResetTraffic.php
  3. 5 5
      app/Console/Commands/SendRemindMail.php
  4. 16 1
      app/Http/Controllers/Admin/ConfigController.php
  5. 3 1
      app/Http/Controllers/Admin/CouponController.php
  6. 4 4
      app/Http/Controllers/Admin/MailController.php
  7. 3 1
      app/Http/Controllers/Admin/NoticeController.php
  8. 15 2
      app/Http/Controllers/Admin/OrderController.php
  9. 8 4
      app/Http/Controllers/Admin/PlanController.php
  10. 32 6
      app/Http/Controllers/Admin/ServerController.php
  11. 3 1
      app/Http/Controllers/Admin/TutorialController.php
  12. 4 1
      app/Http/Controllers/Admin/UserController.php
  13. 8 8
      app/Http/Controllers/Client/AppController.php
  14. 8 9
      app/Http/Controllers/Client/ClientController.php
  15. 6 7
      app/Http/Controllers/Passport/AuthController.php
  16. 9 8
      app/Http/Controllers/Passport/CommController.php
  17. 5 59
      app/Http/Controllers/Server/DeepbworkController.php
  18. 9 63
      app/Http/Controllers/Server/PoseidonController.php
  19. 1 1
      app/Http/Controllers/User/InviteController.php
  20. 36 11
      app/Http/Controllers/User/OrderController.php
  21. 3 1
      app/Http/Controllers/User/UserController.php
  22. 6 1
      app/Http/Requests/Admin/ConfigSave.php
  23. 3 2
      app/Http/Requests/Admin/ServerSave.php
  24. 2 0
      app/Http/Routes/AdminRoute.php
  25. 3 1
      app/Jobs/SendEmailJob.php
  26. 36 0
      app/Services/OrderService.php
  27. 77 0
      app/Services/ServerService.php
  28. 16 0
      app/Services/UserService.php
  29. 11 0
      app/Utils/CacheKey.php
  30. 11 8
      app/Utils/Helper.php
  31. 1 1
      composer.json
  32. 2 2
      config/app.php
  33. 5 1
      database/install.sql
  34. 15 0
      database/update.sql
  35. 0 2
      init.sh
  36. 2 2
      pm2.yaml
  37. 0 0
      public/assets/admin/umi.js
  38. 0 0
      public/assets/user/umi.css
  39. 0 0
      public/assets/user/umi.js
  40. 187 0
      resources/views/mail/classic/notify.blade.php
  41. 187 0
      resources/views/mail/classic/remindExpire.blade.php
  42. 187 0
      resources/views/mail/classic/remindTraffic.blade.php
  43. 195 0
      resources/views/mail/classic/verify.blade.php
  44. 42 0
      resources/views/mail/default/notify.blade.php
  45. 42 0
      resources/views/mail/default/remindExpire.blade.php
  46. 42 0
      resources/views/mail/default/remindTraffic.blade.php
  47. 42 0
      resources/views/mail/default/verify.blade.php
  48. 0 26
      resources/views/mail/sendEmailCustom.blade.php
  49. 0 26
      resources/views/mail/sendEmailVerify.blade.php
  50. 0 26
      resources/views/mail/sendRemindExpire.blade.php
  51. 0 26
      resources/views/mail/sendRemindTraffic.blade.php
  52. 4 0
      update.sh

+ 7 - 6
app/Console/Commands/CheckOrder.php

@@ -2,6 +2,7 @@
 
 namespace App\Console\Commands;
 
+use App\Services\OrderService;
 use Illuminate\Console\Command;
 use App\Models\Order;
 use App\Models\User;
@@ -42,14 +43,14 @@ class CheckOrder extends Command
      */
     public function handle()
     {
-        $order = Order::get();
-        foreach ($order as $item) {
+        $orders = Order::get();
+        foreach ($orders as $item) {
             switch ($item->status) {
                 // cancel
                 case 0:
                     if (strtotime($item->created_at) <= (time() - 1800)) {
-                        $item->status = 2;
-                        $item->save();
+                        $orderService = new OrderService($item);
+                        $orderService->cancel();
                     }
                     break;
                 case 1:
@@ -64,7 +65,7 @@ class CheckOrder extends Command
     {
         $user = User::find($order->user_id);
         $plan = Plan::find($order->plan_id);
-        if ($order->cycle === 'onetime_price') {
+        if ((string)$order->cycle === 'onetime_price') {
             return $this->buyByOneTime($order, $user, $plan);
         }
         return $this->buyByCycle($order, $user, $plan);
@@ -73,7 +74,7 @@ class CheckOrder extends Command
     private function buyByCycle(Order $order, User $user, Plan $plan)
     {
         // change plan process
-        if ($order->type == 3) {
+        if ((int)$order->type === 3) {
             $user->expired_at = time();
         }
         if ($order->refund_amount) {

+ 12 - 15
app/Console/Commands/ResetTraffic.php

@@ -38,7 +38,8 @@ class ResetTraffic extends Command
      */
     public function handle()
     {
-        $user = User::where('expired_at', '!=', NULL);
+        $user = User::where('expired_at', '!=', NULL)
+            ->where('expired_at', '>', time());
         $resetTrafficMethod = config('v2board.reset_traffic_method', 0);
         switch ((int)$resetTrafficMethod) {
             // 1 a month
@@ -64,21 +65,17 @@ class ResetTraffic extends Command
 
     private function resetByExpireDay($user):void
     {
-        $date = date('Y-m-d', time());
-        $startAt = strtotime((string)$date);
-        $endAt = (int)$startAt + 24 * 3600;
         $lastDay = date('d', strtotime('last day of +0 months'));
-        if ((string)$lastDay === '29') {
-            $endAt = (int)$startAt + 72 * 3600;
+        $users = [];
+        foreach ($user->get() as $item) {
+            $expireDay = date('d', $item->expired_at);
+            if ($expireDay === date('d') || (string)$lastDay === '29' || (string)$lastDay === '30') {
+                array_push($users, $item->id);
+            }
         }
-        if ((string)$lastDay === '30') {
-            $endAt = (int)$startAt + 48 * 3600;
-        }
-        $user->where('expired_at', '>=', (int)$startAt)
-            ->where('expired_at', '<', (int)$endAt)
-            ->update([
-                'u' => 0,
-                'd' => 0
-            ]);
+        $user->whereIn('id', $users)->update([
+            'u' => 0,
+            'd' => 0
+        ]);
     }
 }

+ 5 - 5
app/Console/Commands/SendRemindMail.php

@@ -5,7 +5,7 @@ namespace App\Console\Commands;
 use Illuminate\Console\Command;
 use App\Models\User;
 use App\Models\MailLog;
-use App\Jobs\SendEmail;
+use App\Jobs\SendEmailJob;
 
 class SendRemindMail extends Command
 {
@@ -50,10 +50,10 @@ class SendRemindMail extends Command
     private function remindExpire($user)
     {
         if (($user->expired_at - 86400) < time() && $user->expired_at > time()) {
-            SendEmail::dispatch([
+            SendEmailJob::dispatch([
                 'email' => $user->email,
                 'subject' => '在' . config('v2board.app_name', 'V2board') . '的服务即将到期',
-                'template_name' => 'mail.sendRemindExpire',
+                'template_name' => 'remindExpire',
                 'template_value' => [
                     'name' => config('v2board.app_name', 'V2Board'),
                     'url' => config('v2board.app_url')
@@ -69,10 +69,10 @@ class SendRemindMail extends Command
                 ->where('template_name', 'mail.sendRemindTraffic')
                 ->count();
             if ($sendCount > 0) return;
-            SendEmail::dispatch([
+            SendEmailJob::dispatch([
                 'email' => $user->email,
                 'subject' => '在' . config('v2board.app_name', 'V2board') . '的流量使用已达到80%',
-                'template_name' => 'mail.sendRemindTraffic',
+                'template_name' => 'remindTraffic',
                 'template_value' => [
                     'name' => config('v2board.app_name', 'V2Board'),
                     'url' => config('v2board.app_url')

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

@@ -9,6 +9,17 @@ use App\Http\Controllers\Controller;
 
 class ConfigController extends Controller
 {
+    public function getEmailTemplate()
+    {
+        $path = resource_path('views/mail/');
+        $files = array_map(function ($item) use ($path) {
+            return str_replace($path, '', $item);
+        }, glob($path . '*'));
+        return response([
+            'data' => $files
+        ]);
+    }
+
     public function fetch()
     {
         // TODO: default should be in Dict
@@ -18,7 +29,8 @@ class ConfigController extends Controller
                     'invite_force' => (int)config('v2board.invite_force', 0),
                     'invite_commission' => config('v2board.invite_commission', 10),
                     'invite_gen_limit' => config('v2board.invite_gen_limit', 5),
-                    'invite_never_expire' => config('v2board.invite_never_expire', 0)
+                    'invite_never_expire' => config('v2board.invite_never_expire', 0),
+                    'commission_first_time_enable' => config('v2board.commission_first_time_enable', 1)
                 ],
                 'site' => [
                     'safe_mode_enable' => (int)config('v2board.safe_mode_enable', 0),
@@ -71,6 +83,9 @@ class ConfigController extends Controller
                 ],
                 'tutorial' => [
                     'apple_id' => config('v2board.apple_id')
+                ],
+                'email' => [
+                    'email_template' => config('v2board.email_template', 'default')
                 ]
             ]
         ]);

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

@@ -34,7 +34,9 @@ class CouponController extends Controller
                 abort(500, '创建失败');
             }
         } else {
-            if (!Coupon::find($request->input('id'))->update($params)) {
+            try {
+                Coupon::find($request->input('id'))->update($params);
+            } catch (\Exception $e) {
                 abort(500, '保存失败');
             }
         }

+ 4 - 4
app/Http/Controllers/Admin/MailController.php

@@ -6,7 +6,7 @@ use App\Http\Requests\Admin\MailSend;
 use App\Services\UserService;
 use Illuminate\Http\Request;
 use App\Http\Controllers\Controller;
-use App\Jobs\SendEmail;
+use App\Jobs\SendEmailJob;
 
 class MailController extends Controller
 {
@@ -28,16 +28,16 @@ class MailController extends Controller
         }
 
         foreach ($users as $user) {
-            SendEmail::dispatch([
+            SendEmailJob::dispatch([
                 'email' => $user->email,
                 'subject' => $request->input('subject'),
-                'template_name' => 'mail.sendEmailCustom',
+                'template_name' => 'notify',
                 'template_value' => [
                     'name' => config('v2board.app_name', 'V2Board'),
                     'url' => config('v2board.app_url'),
                     'content' => $request->input('content')
                 ]
-            ])->onQueue('other_mail');
+            ]);
         }
 
         return response([

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

@@ -29,7 +29,9 @@ class NoticeController extends Controller
                 abort(500, '保存失败');
             }
         } else {
-            if (!Notice::find($request->input('id'))->update($data)) {
+            try {
+                Notice::find($request->input('id'))->update($data);
+            } catch (\Exception $e) {
                 abort(500, '保存失败');
             }
         }

+ 15 - 2
app/Http/Controllers/Admin/OrderController.php

@@ -3,6 +3,7 @@
 namespace App\Http\Controllers\Admin;
 
 use App\Http\Requests\Admin\OrderUpdate;
+use App\Services\OrderService;
 use Illuminate\Http\Request;
 use App\Http\Controllers\Controller;
 use App\Models\Order;
@@ -48,7 +49,7 @@ class OrderController extends Controller
 
     public function update(OrderUpdate $request)
     {
-        $updateData = $request->only([
+        $params = $request->only([
             'status',
             'commission_status'
         ]);
@@ -59,7 +60,19 @@ class OrderController extends Controller
             abort(500, '订单不存在');
         }
 
-        if (!$order->update($updateData)) {
+        if (isset($params['status']) && (int)$params['status'] === 2) {
+            $orderService = new OrderService($order);
+            if (!$orderService->cancel()) {
+                abort(500, '更新失败');
+            }
+            return response([
+                'data' => true
+            ]);
+        }
+
+        try {
+            $order->update($params);
+        } catch (\Exception $e) {
             abort(500, '更新失败');
         }
 

+ 8 - 4
app/Http/Controllers/Admin/PlanController.php

@@ -30,9 +30,10 @@ class PlanController extends Controller
             }
             DB::beginTransaction();
             // update user group id
-            User::where('plan_id', $plan->id)
-                ->update(['group_id' => $plan->group_id]);
-            if (!$plan->update($params)) {
+            try {
+                User::where('plan_id', $plan->id)->update(['group_id' => $plan->group_id]);
+                $plan->update($params);
+            } catch (\Exception $e) {
                 DB::rollBack();
                 abort(500, '保存失败');
             }
@@ -79,7 +80,10 @@ class PlanController extends Controller
         if (!$plan) {
             abort(500, '该订阅不存在');
         }
-        if (!$plan->update($updateData)) {
+
+        try {
+            $plan->update($updateData);
+        } catch (\Exception $e) {
             abort(500, '保存失败');
         }
 

+ 32 - 6
app/Http/Controllers/Admin/ServerController.php

@@ -40,24 +40,33 @@ class ServerController extends Controller
         if (isset($params['tags'])) {
             $params['tags'] = json_encode($params['tags']);
         }
-        if (isset($params['rules'])) {
-            if (!is_object(json_decode($params['rules']))) {
+
+        if (isset($params['ruleSettings'])) {
+            if (!is_object(json_decode($params['ruleSettings']))) {
                 abort(500, '审计规则配置格式不正确');
             }
         }
 
-        if (isset($params['settings'])) {
-            if (!is_object(json_decode($params['settings']))) {
+        if (isset($params['networkSettings'])) {
+            if (!is_object(json_decode($params['networkSettings']))) {
                 abort(500, '传输协议配置格式不正确');
             }
         }
 
+        if (isset($params['tlsSettings'])) {
+            if (!is_object(json_decode($params['tlsSettings']))) {
+                abort(500, 'TLS配置格式不正确');
+            }
+        }
+
         if ($request->input('id')) {
             $server = Server::find($request->input('id'));
             if (!$server) {
                 abort(500, '服务器不存在');
             }
-            if (!$server->update($params)) {
+            try {
+                $server->update($params);
+            } catch (\Exception $e) {
                 abort(500, '保存失败');
             }
             return response([
@@ -156,7 +165,9 @@ class ServerController extends Controller
         if (!$server) {
             abort(500, '该服务器不存在');
         }
-        if (!$server->update($params)) {
+        try {
+            $server->update($params);
+        } catch (\Exception $e) {
             abort(500, '保存失败');
         }
 
@@ -164,4 +175,19 @@ class ServerController extends Controller
             'data' => true
         ]);
     }
+
+    public function copy(Request $request)
+    {
+        $server = Server::find($request->input('id'));
+        if (!$server) {
+            abort(500, '服务器不存在');
+        }
+        if (!Server::create($server->toArray())) {
+            abort(500, '复制失败');
+        }
+
+        return response([
+            'data' => true
+        ]);
+    }
 }

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

@@ -25,7 +25,9 @@ class TutorialController extends Controller
                 abort(500, '创建失败');
             }
         } else {
-            if (!Tutorial::find($request->input('id'))->update($params)) {
+            try {
+                Tutorial::find($request->input('id'))->update($params);
+            } catch (\Exception $e) {
                 abort(500, '保存失败');
             }
         }

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

@@ -79,7 +79,10 @@ class UserController extends Controller
             }
             $params['group_id'] = $plan->group_id;
         }
-        if (!$user->update($params)) {
+
+        try {
+            $user->update($params);
+        } catch (\Exception $e) {
             abort(500, '保存失败');
         }
         return response([

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

@@ -57,7 +57,7 @@ class AppController extends Controller
             abort(500, '参数错误');
         }
         $user = $request->user;
-        if ($user->expired_at < time()) {
+        if ($user->expired_at < time() && $user->expired_at !== NULL) {
             abort(500, '订阅计划已过期');
         }
         $server = Server::where('show', 1)
@@ -78,25 +78,25 @@ class AppController extends Controller
         $json->outbound->settings->vnext[0]->users[0]->alterId = (int)$user->v2ray_alter_id;
         $json->outbound->settings->vnext[0]->remark = (string)$server->name;
         $json->outbound->streamSettings->network = $server->network;
-        if ($server->settings) {
+        if ($server->networkSettings) {
             switch ($server->network) {
                 case 'tcp':
-                    $json->outbound->streamSettings->tcpSettings = json_decode($server->settings);
+                    $json->outbound->streamSettings->tcpSettings = json_decode($server->networkSettings);
                     break;
                 case 'kcp':
-                    $json->outbound->streamSettings->kcpSettings = json_decode($server->settings);
+                    $json->outbound->streamSettings->kcpSettings = json_decode($server->networkSettings);
                     break;
                 case 'ws':
-                    $json->outbound->streamSettings->wsSettings = json_decode($server->settings);
+                    $json->outbound->streamSettings->wsSettings = json_decode($server->networkSettings);
                     break;
                 case 'http':
-                    $json->outbound->streamSettings->httpSettings = json_decode($server->settings);
+                    $json->outbound->streamSettings->httpSettings = json_decode($server->networkSettings);
                     break;
                 case 'domainsocket':
-                    $json->outbound->streamSettings->dsSettings = json_decode($server->settings);
+                    $json->outbound->streamSettings->dsSettings = json_decode($server->networkSettings);
                     break;
                 case 'quic':
-                    $json->outbound->streamSettings->quicSettings = json_decode($server->settings);
+                    $json->outbound->streamSettings->quicSettings = json_decode($server->networkSettings);
                     break;
             }
         }

+ 8 - 9
app/Http/Controllers/Client/ClientController.php

@@ -3,7 +3,6 @@
 namespace App\Http\Controllers\Client;
 
 use App\Http\Controllers\Controller;
-use App\Http\Middleware\User;
 use Illuminate\Http\Request;
 use App\Models\Server;
 use App\Utils\Helper;
@@ -49,9 +48,9 @@ class ClientController extends Controller
         foreach ($server as $item) {
             $uri .= "vmess=" . $item->host . ":" . $item->port . ", method=none, password=" . $user->v2ray_uuid . ", fast-open=false, udp-relay=false, tag=" . $item->name;
             if ($item->network == 'ws') {
-                $uri .= ', obfs=ws';
-                if ($item->settings) {
-                    $wsSettings = json_decode($item->settings);
+                $uri .= ', obfs=' . ($item->tls ? 'wss' : 'ws');
+                if ($item->networkSettings) {
+                    $wsSettings = json_decode($item->networkSettings);
                     if (isset($wsSettings->path)) $uri .= ', obfs-uri=' . $wsSettings->path;
                     if (isset($wsSettings->headers->Host)) $uri .= ', obfs-host=' . $wsSettings->headers->Host;
                 }
@@ -69,9 +68,9 @@ class ClientController extends Controller
             $str = '';
             $str .= $item->name . '= vmess, ' . $item->host . ', ' . $item->port . ', chacha20-ietf-poly1305, "' . $user->v2ray_uuid . '", over-tls=' . ($item->tls ? "true" : "false") . ', certificate=0, group=' . config('v2board.app_name', 'V2Board');
             if ($item->network === 'ws') {
-                $str .= ', obfs=ws';
-                if ($item->settings) {
-                    $wsSettings = json_decode($item->settings);
+                $uri .= ', obfs=' . ($item->tls ? 'wss' : 'ws');
+                if ($item->networkSettings) {
+                    $wsSettings = json_decode($item->networkSettings);
                     if (isset($wsSettings->path)) $str .= ', obfs-path="' . $wsSettings->path . '"';
                     if (isset($wsSettings->headers->Host)) $str .= ', obfs-header="Host:' . $wsSettings->headers->Host . '"';
                 }
@@ -111,8 +110,8 @@ class ClientController extends Controller
             }
             if ($item->network == 'ws') {
                 $array['network'] = $item->network;
-                if ($item->settings) {
-                    $wsSettings = json_decode($item->settings);
+                if ($item->networkSettings) {
+                    $wsSettings = json_decode($item->networkSettings);
                     if (isset($wsSettings->path)) $array['ws-path'] = $wsSettings->path;
                     if (isset($wsSettings->headers->Host)) $array['ws-headers'] = [
                         'Host' => $wsSettings->headers->Host

+ 6 - 7
app/Http/Controllers/Passport/AuthController.php

@@ -13,6 +13,7 @@ use App\Models\User;
 use App\Models\InviteCode;
 use App\Utils\Helper;
 use App\Utils\Dict;
+use App\Utils\CacheKey;
 
 class AuthController extends Controller
 {
@@ -35,11 +36,10 @@ class AuthController extends Controller
             }
         }
         if ((int)config('v2board.email_verify', 0)) {
-            $redisKey = 'sendEmailVerify:' . $request->input('email');
             if (empty($request->input('email_code'))) {
                 abort(500, '邮箱验证码不能为空');
             }
-            if (Cache::get($redisKey) !== $request->input('email_code')) {
+            if (Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))) !== $request->input('email_code')) {
                 abort(500, '邮箱验证码有误');
             }
         }
@@ -64,7 +64,7 @@ class AuthController extends Controller
                 }
             } else {
                 $user->invite_user_id = $inviteCode->user_id ? $inviteCode->user_id : null;
-                if (!(int)config('v2board.invite_never_expire', env('V2BOARD_INVITE_NEVER_EXPIRE'))) {
+                if (!(int)config('v2board.invite_never_expire', 0)) {
                     $inviteCode->status = 1;
                     $inviteCode->save();
                 }
@@ -86,7 +86,7 @@ class AuthController extends Controller
             abort(500, '注册失败');
         }
         if ((int)config('v2board.email_verify', 0)) {
-            Cache::forget($redisKey);
+            Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
         }
         $request->session()->put('email', $user->email);
         $request->session()->put('id', $user->id);
@@ -189,8 +189,7 @@ class AuthController extends Controller
 
     public function forget(AuthForget $request)
     {
-        $redisKey = 'sendEmailVerify:' . $request->input('email');
-        if (Cache::get($redisKey) !== $request->input('email_code')) {
+        if (Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))) !== $request->input('email_code')) {
             abort(500, '邮箱验证码有误');
         }
         $user = User::where('email', $request->input('email'))->first();
@@ -202,7 +201,7 @@ class AuthController extends Controller
         if (!$user->save()) {
             abort(500, '重置失败');
         }
-        Cache::forget($redisKey);
+        Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
         return response([
             'data' => true
         ]);

+ 9 - 8
app/Http/Controllers/Passport/CommController.php

@@ -9,9 +9,10 @@ use Illuminate\Http\Exceptions\HttpResponseException;
 use Illuminate\Support\Facades\Mail;
 use App\Utils\Helper;
 use Illuminate\Support\Facades\Cache;
-use App\Jobs\SendEmail;
+use App\Jobs\SendEmailJob;
 use App\Models\InviteCode;
 use App\Utils\Dict;
+use App\Utils\CacheKey;
 
 class CommController extends Controller
 {
@@ -38,25 +39,25 @@ class CommController extends Controller
     public function sendEmailVerify(CommSendEmailVerify $request)
     {
         $email = $request->input('email');
-        $cacheKey = 'sendEmailVerify:' . $email;
-        if (Cache::get($cacheKey)) {
+        if (Cache::get(CacheKey::get('LAST_SEND_EMAIL_VERIFY_TIMESTAMP', $email))) {
             abort(500, '验证码已发送,请过一会再请求');
         }
-        $code = Helper::randomChar(6);
+        $code = rand(100000, 999999);
         $subject = config('v2board.app_name', 'V2Board') . '邮箱验证码';
 
-        SendEmail::dispatch([
+        SendEmailJob::dispatch([
             'email' => $email,
             'subject' => $subject,
-            'template_name' => 'mail.sendEmailVerify',
+            'template_name' => 'verify',
             'template_value' => [
                 'name' => config('v2board.app_name', 'V2Board'),
                 'code' => $code,
                 'url' => config('v2board.app_url')
             ]
-        ])->onQueue('verify_mail');
+        ]);
 
-        Cache::put($cacheKey, $code, 60);
+        Cache::put(CacheKey::get('EMAIL_VERIFY_CODE', $email), $code, 300);
+        Cache::put(CacheKey::get('LAST_SEND_EMAIL_VERIFY_TIMESTAMP', $email), time(), 60);
         return response([
             'data' => true
         ]);

+ 5 - 59
app/Http/Controllers/Server/DeepbworkController.php

@@ -101,65 +101,11 @@ class DeepbworkController extends Controller
         if (empty($nodeId) || empty($localPort)) {
             abort(500, '参数错误');
         }
-        $server = Server::find($nodeId);
-        if (!$server) {
-            abort(500, '节点不存在');
-        }
-        $json = json_decode(self::SERVER_CONFIG);
-        $json->inboundDetour[0]->port = (int)$localPort;
-        $json->inbound->port = (int)$server->server_port;
-        $json->inbound->streamSettings->network = $server->network;
-        if ($server->settings) {
-            switch ($server->network) {
-                case 'tcp':
-                    $json->inbound->streamSettings->tcpSettings = json_decode($server->settings);
-                    break;
-                case 'kcp':
-                    $json->inbound->streamSettings->kcpSettings = json_decode($server->settings);
-                    break;
-                case 'ws':
-                    $json->inbound->streamSettings->wsSettings = json_decode($server->settings);
-                    break;
-                case 'http':
-                    $json->inbound->streamSettings->httpSettings = json_decode($server->settings);
-                    break;
-                case 'domainsocket':
-                    $json->inbound->streamSettings->dsSettings = json_decode($server->settings);
-                    break;
-                case 'quic':
-                    $json->inbound->streamSettings->quicSettings = json_decode($server->settings);
-                    break;
-            }
-        }
-
-        if ($server->rules) {
-            $rules = json_decode($server->rules);
-            // domain
-            if (isset($rules->domain) && !empty($rules->domain)) {
-                $domainObj = new \StdClass();
-                $domainObj->type = 'field';
-                $domainObj->domain = $rules->domain;
-                $domainObj->outboundTag = 'block';
-                array_push($json->routing->rules, $domainObj);
-            }
-            // protocol
-            if (isset($rules->protocol) && !empty($rules->protocol)) {
-                $protocolObj = new \StdClass();
-                $protocolObj->type = 'field';
-                $protocolObj->protocol = $rules->protocol;
-                $protocolObj->outboundTag = 'block';
-                array_push($json->routing->rules, $protocolObj);
-            }
-        }
-
-        if ((int)$server->tls) {
-            $json->inbound->streamSettings->security = 'tls';
-            $tls = (object)[
-                'certificateFile' => '/home/v2ray.crt',
-                'keyFile' => '/home/v2ray.key'
-            ];
-            $json->inbound->streamSettings->tlsSettings = new \StdClass();
-            $json->inbound->streamSettings->tlsSettings->certificates[0] = $tls;
+        $serverService = new ServerService();
+        try {
+            $json = $serverService->getConfig($nodeId, $localPort);
+        } catch (\Exception $e) {
+            abort(500, $e->getMessage());
         }
 
         die(json_encode($json, JSON_UNESCAPED_UNICODE));

+ 9 - 63
app/Http/Controllers/Server/PoseidonController.php

@@ -89,72 +89,18 @@ class PoseidonController extends Controller
         if (empty($nodeId) || empty($localPort)) {
             return $this->error('invalid parameters', 400);
         }
-        $server = Server::find($nodeId);
-        if (!$server) {
-            return $this->error("server could not be found", 404);
-        }
-        $json = json_decode(self::SERVER_CONFIG);
-        $json->inboundDetour[0]->port = (int)$localPort;
-        $json->inbound->port = (int)$server->server_port;
-        $json->inbound->streamSettings->network = $server->network;
-        if ($server->settings) {
-            switch ($server->network) {
-                case 'tcp':
-                    $json->inbound->streamSettings->tcpSettings = json_decode($server->settings);
-                    break;
-                case 'kcp':
-                    $json->inbound->streamSettings->kcpSettings = json_decode($server->settings);
-                    break;
-                case 'ws':
-                    $json->inbound->streamSettings->wsSettings = json_decode($server->settings);
-                    break;
-                case 'http':
-                    $json->inbound->streamSettings->httpSettings = json_decode($server->settings);
-                    break;
-                case 'domainsocket':
-                    $json->inbound->streamSettings->dsSettings = json_decode($server->settings);
-                    break;
-                case 'quic':
-                    $json->inbound->streamSettings->quicSettings = json_decode($server->settings);
-                    break;
-            }
-        }
 
-        if ($server->rules) {
-            $rules = json_decode($server->rules);
-            // domain
-            if (isset($rules->domain) && !empty($rules->domain)) {
-                $domainObj = new \StdClass();
-                $domainObj->type = 'field';
-                $domainObj->domain = $rules->domain;
-                $domainObj->outboundTag = 'block';
-                array_push($json->routing->rules, $domainObj);
-            }
-            // protocol
-            if (isset($rules->protocol) && !empty($rules->protocol)) {
-                $protocolObj = new \StdClass();
-                $protocolObj->type = 'field';
-                $protocolObj->protocol = $rules->protocol;
-                $protocolObj->outboundTag = 'block';
-                array_push($json->routing->rules, $protocolObj);
-            }
-        }
-
-        if ((int)$server->tls) {
-            $json->inbound->streamSettings->security = 'tls';
-            $tls = (object)[
-                'certificateFile' => '/home/v2ray.crt',
-                'keyFile' => '/home/v2ray.key'
+        $serverService = new ServerService();
+        try {
+            $json = $serverService->getConfig($nodeId, $localPort);
+            $json->poseidon = [
+              'license_key' => (string)config('v2board.server_license'),
             ];
-            $json->inbound->streamSettings->tlsSettings = new \StdClass();
-            $json->inbound->streamSettings->tlsSettings->certificates[0] = $tls;
-        }
 
-        $json->poseidon = [
-          'license_key' => (string)config('v2board.server_license'),
-        ];
-
-        return $this->success($json);
+            return $this->success($json);
+        } catch (\Exception $e) {
+            return $this->error($e->getMessage(), 500);
+        }
     }
 
     protected function verifyToken(Request $request)

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

@@ -45,7 +45,7 @@ class InviteController extends Controller
         $codes = InviteCode::where('user_id', $request->session()->get('id'))
             ->where('status', 0)
             ->get();
-        $commission_rate = config('v2board.invite_commission');
+        $commission_rate = config('v2board.invite_commission', 10);
         $user = User::find($request->session()->get('id'));
         if ($user->commission_rate) {
             $commission_rate = $user->commission_rate;

+ 36 - 11
app/Http/Controllers/User/OrderController.php

@@ -4,6 +4,8 @@ namespace App\Http\Controllers\User;
 
 use App\Http\Controllers\Controller;
 use App\Http\Requests\User\OrderSave;
+use App\Services\OrderService;
+use App\Services\UserService;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\Log;
@@ -162,8 +164,7 @@ class OrderController extends Controller
         $order->cycle = $request->input('cycle');
         $order->trade_no = Helper::guid();
         $order->total_amount = $plan[$request->input('cycle')];
-        // discount start
-        // coupon
+        // coupon start
         if (isset($coupon)) {
             switch ($coupon->type) {
                 case 1:
@@ -181,13 +182,13 @@ class OrderController extends Controller
                 }
             }
         }
-        // user
+        // coupon complete
+        // discount start
         if ($user->discount) {
             $order->discount_amount = $order->discount_amount + ($order->total_amount * ($user->discount / 100));
         }
-        // discount complete
-        $order->total_amount = $order->total_amount - $order->discount_amount;
         // discount end
+        $order->total_amount = $order->total_amount - $order->discount_amount;
         // renew and change subscribe process
         if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id) {
             if (!(int)config('v2board.plan_change_enable', 1)) abort(500, '目前不允许更改订阅,请联系客服或提交工单');
@@ -207,13 +208,37 @@ class OrderController extends Controller
         // invite process
         if ($user->invite_user_id && $order->total_amount > 0) {
             $order->invite_user_id = $user->invite_user_id;
-            $inviter = User::find($user->invite_user_id);
-            if ($inviter && $inviter->commission_rate) {
-                $order->commission_balance = $order->total_amount * ($inviter->commission_rate / 100);
+            $commissionFirstTime = (int)config('v2board.commission_first_time_enable', 1);
+            if (!$commissionFirstTime || ($commissionFirstTime && !Order::where('user_id', $user->id)->where('status', 3)->first())) {
+                $inviter = User::find($user->invite_user_id);
+                if ($inviter && $inviter->commission_rate) {
+                    $order->commission_balance = $order->total_amount * ($inviter->commission_rate / 100);
+                } else {
+                    $order->commission_balance = $order->total_amount * (config('v2board.invite_commission', 10) / 100);
+                }
+            }
+        }
+        // use balance
+        if ($user->balance && $order->total_amount > 0) {
+            $remainingBalance = $user->balance - $order->total_amount;
+            $userService = new UserService();
+            if ($remainingBalance > 0) {
+                if (!$userService->addBalance($order->user_id, - $order->total_amount)) {
+                    DB::rollBack();
+                    abort(500, '余额不足');
+                }
+                $order->balance_amount = $order->total_amount;
+                $order->total_amount = 0;
             } else {
-                $order->commission_balance = $order->total_amount * (config('v2board.invite_commission', 10) / 100);
+                if (!$userService->addBalance($order->user_id, - $user->balance)) {
+                    DB::rollBack();
+                    abort(500, '余额不足');
+                }
+                $order->balance_amount = $user->balance;
+                $order->total_amount = $order->total_amount - $user->balance;
             }
         }
+
         if (!$order->save()) {
             DB::rollback();
             abort(500, '订单创建失败');
@@ -371,8 +396,8 @@ class OrderController extends Controller
         if ($order->status !== 0) {
             abort(500, '只可以取消待支付订单');
         }
-        $order->status = 2;
-        if (!$order->save()) {
+        $orderService = new OrderService($order);
+        if (!$orderService->cancel()) {
             abort(500, '取消失败');
         }
         return response([

+ 3 - 1
app/Http/Controllers/User/UserController.php

@@ -132,7 +132,9 @@ class UserController extends Controller
         if (!$user) {
             abort(500, '该用户不存在');
         }
-        if (!$user->update($updateData)) {
+        try {
+            $user->update($updateData);
+        } catch (\Exception $e) {
             abort(500, '保存失败');
         }
 

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

@@ -7,11 +7,14 @@ use Illuminate\Foundation\Http\FormRequest;
 class ConfigSave extends FormRequest
 {
     CONST RULES = [
+        // invite & commission
         'safe_mode_enable' => 'in:0,1',
         'invite_force' => 'in:0,1',
         'invite_commission' => 'integer',
         'invite_gen_limit' => 'integer',
         'invite_never_expire' => 'in:0,1',
+        'commission_first_time_enable' => 'in:0,1',
+        // site
         'stop_register' => 'in:0,1',
         'email_verify' => 'in:0,1',
         'app_name' => '',
@@ -56,7 +59,9 @@ class ConfigSave extends FormRequest
         'frontend_background_url' => 'nullable|url',
         // tutorial
         'apple_id' => 'email',
-        'apple_id_password' => ''
+        'apple_id_password' => '',
+        // email
+        'email_template' => ''
     ];
 
     /**

+ 3 - 2
app/Http/Requests/Admin/ServerSave.php

@@ -7,7 +7,6 @@ use Illuminate\Foundation\Http\FormRequest;
 class ServerSave extends FormRequest
 {
     CONST RULES = [
-        'rules' => '',
         'show' => '',
         'name' => 'required',
         'group_id' => 'required|array',
@@ -19,7 +18,9 @@ class ServerSave extends FormRequest
         'tags' => 'nullable|array',
         'rate' => 'required|numeric',
         'network' => 'required|in:tcp,kcp,ws,http,domainsocket,quic',
-        'settings' => ''
+        'networkSettings' => '',
+        'ruleSettings' => '',
+        'tlsSettings' => ''
     ];
     /**
      * Get the validation rules that apply to the request.

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

@@ -14,6 +14,7 @@ class AdminRoute
             // Config
             $router->get ('/config/fetch', 'Admin\\ConfigController@fetch');
             $router->post('/config/save', 'Admin\\ConfigController@save');
+            $router->get ('/config/getEmailTemplate', 'Admin\\ConfigController@getEmailTemplate');
             // Plan
             $router->get ('/plan/fetch', 'Admin\\PlanController@fetch');
             $router->post('/plan/save', 'Admin\\PlanController@save');
@@ -27,6 +28,7 @@ class AdminRoute
             $router->post('/server/group/drop', 'Admin\\ServerController@groupDrop');
             $router->post('/server/drop', 'Admin\\ServerController@drop');
             $router->post('/server/update', 'Admin\\ServerController@update');
+            $router->post('/server/copy', 'Admin\\ServerController@copy');
             // Order
             $router->get ('/order/fetch', 'Admin\\OrderController@fetch');
             $router->post('/order/repair', 'Admin\\OrderController@repair');

+ 3 - 1
app/Jobs/SendEmail.php → app/Jobs/SendEmailJob.php

@@ -10,7 +10,7 @@ use Illuminate\Queue\SerializesModels;
 use Illuminate\Support\Facades\Mail;
 use App\Models\MailLog;
 
-class SendEmail implements ShouldQueue
+class SendEmailJob implements ShouldQueue
 {
     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
     protected $params;
@@ -22,6 +22,7 @@ class SendEmail implements ShouldQueue
      */
     public function __construct($params)
     {
+        $this->onQueue('send_email');
         $this->params = $params;
     }
 
@@ -35,6 +36,7 @@ class SendEmail implements ShouldQueue
         $params = $this->params;
         $email = $params['email'];
         $subject = $params['subject'];
+        $params['template_name'] = 'mail.' . config('v2board.email_template', 'default') . '.' . $params['template_name'];
         try {
             Mail::send(
                 $params['template_name'],

+ 36 - 0
app/Services/OrderService.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Services;
+
+use App\Models\Order;
+use Illuminate\Support\Facades\DB;
+
+class OrderService
+{
+    public $order;
+
+    public function __construct(Order $order)
+    {
+        $this->order = $order;
+    }
+
+    public function cancel():bool
+    {
+        $order = $this->order;
+        DB::beginTransaction();
+        $order->status = 2;
+        if (!$order->save()) {
+            DB::rollBack();
+            return false;
+        }
+        if ($order->balance_amount) {
+            $userService = new UserService();
+            if (!$userService->addBalance($order->user_id, $order->balance_amount)) {
+                DB::rollBack();
+                return false;
+            }
+        }
+        DB::commit();
+        return true;
+    }
+}

+ 77 - 0
app/Services/ServerService.php

@@ -3,9 +3,13 @@
 namespace App\Services;
 
 use App\Models\User;
+use App\Models\Server;
 
 class ServerService
 {
+
+    CONST SERVER_CONFIG = '{"api":{"services":["HandlerService","StatsService"],"tag":"api"},"stats":{},"inbound":{"port":443,"protocol":"vmess","settings":{"clients":[]},"sniffing":{"enabled": true,"destOverride": ["http","tls"]},"streamSettings":{"network":"tcp"},"tag":"proxy"},"inboundDetour":[{"listen":"0.0.0.0","port":23333,"protocol":"dokodemo-door","settings":{"address":"0.0.0.0"},"tag":"api"}],"log":{"loglevel":"debug","access":"access.log","error":"error.log"},"outbound":{"protocol":"freedom","settings":{}},"outboundDetour":[{"protocol":"blackhole","settings":{},"tag":"block"}],"routing":{"rules":[{"inboundTag":"api","outboundTag":"api","type":"field"}]},"policy":{"levels":{"0":{"handshake":4,"connIdle":300,"uplinkOnly":5,"downlinkOnly":30,"statsUserUplink":true,"statsUserDownlink":true}}}}';
+
     public function getAvailableUsers($groupId)
     {
         return User::whereIn('group_id', $groupId)
@@ -28,4 +32,77 @@ class ServerService
             ])
             ->get();
     }
+
+    public function getConfig(int $nodeId, int $localPort)
+    {
+        $server = Server::find($nodeId);
+        if (!$server) {
+            abort(500, '节点不存在');
+        }
+        $json = json_decode(self::SERVER_CONFIG);
+        $json->inboundDetour[0]->port = (int)$localPort;
+        $json->inbound->port = (int)$server->server_port;
+        $json->inbound->streamSettings->network = $server->network;
+        if ($server->networkSettings) {
+            switch ($server->network) {
+                case 'tcp':
+                    $json->inbound->streamSettings->tcpSettings = json_decode($server->networkSettings);
+                    break;
+                case 'kcp':
+                    $json->inbound->streamSettings->kcpSettings = json_decode($server->networkSettings);
+                    break;
+                case 'ws':
+                    $json->inbound->streamSettings->wsSettings = json_decode($server->networkSettings);
+                    break;
+                case 'http':
+                    $json->inbound->streamSettings->httpSettings = json_decode($server->networkSettings);
+                    break;
+                case 'domainsocket':
+                    $json->inbound->streamSettings->dsSettings = json_decode($server->networkSettings);
+                    break;
+                case 'quic':
+                    $json->inbound->streamSettings->quicSettings = json_decode($server->networkSettings);
+                    break;
+            }
+        }
+
+        if ($server->ruleSettings) {
+            $rules = json_decode($server->ruleSettings);
+            // domain
+            if (isset($rules->domain) && !empty($rules->domain)) {
+                $domainObj = new \StdClass();
+                $domainObj->type = 'field';
+                $domainObj->domain = $rules->domain;
+                $domainObj->outboundTag = 'block';
+                array_push($json->routing->rules, $domainObj);
+            }
+            // protocol
+            if (isset($rules->protocol) && !empty($rules->protocol)) {
+                $protocolObj = new \StdClass();
+                $protocolObj->type = 'field';
+                $protocolObj->protocol = $rules->protocol;
+                $protocolObj->outboundTag = 'block';
+                array_push($json->routing->rules, $protocolObj);
+            }
+        }
+
+        if ((int)$server->tls) {
+            $tlsSettings = json_decode($server->tlsSettings);
+            $json->inbound->streamSettings->security = 'tls';
+            $tls = (object)[
+                'certificateFile' => '/home/v2ray.crt',
+                'keyFile' => '/home/v2ray.key'
+            ];
+            $json->inbound->streamSettings->tlsSettings = new \StdClass();
+            if (isset($tlsSettings->serverName)) {
+                $json->inbound->streamSettings->tlsSettings->serverName = (string)$tlsSettings->serverName;
+            }
+            if (isset($tlsSettings->allowInsecure)) {
+                $json->inbound->streamSettings->tlsSettings->allowInsecure = (int)$tlsSettings->allowInsecure ? true : false;
+            }
+            $json->inbound->streamSettings->tlsSettings->certificates[0] = $tls;
+        }
+
+        return $json;
+    }
 }

+ 16 - 0
app/Services/UserService.php

@@ -47,4 +47,20 @@ class UserService
     {
         return User::all();
     }
+
+    public function addBalance(int $userId, int $balance):bool
+    {
+        $user = User::find($userId);
+        if (!$user) {
+            return false;
+        }
+        $user->balance = $user->balance + $balance;
+        if ($user->balance < 0) {
+            return false;
+        }
+        if (!$user->save()) {
+            return false;
+        }
+        return true;
+    }
 }

+ 11 - 0
app/Utils/CacheKey.php

@@ -4,5 +4,16 @@ namespace App\Utils;
 
 class CacheKey
 {
+    CONST KEYS = [
+        'EMAIL_VERIFY_CODE' => '邮箱验证吗',
+        'LAST_SEND_EMAIL_VERIFY_TIMESTAMP' => '最后一次发送邮箱验证码时间'
+    ];
 
+    public static function get(string $key, $uniqueValue)
+    {
+        if (!in_array($key, array_keys(self::KEYS))) {
+            abort(500, 'key is not in cache key list');
+        }
+        return $key . '_' . $uniqueValue;
+    }
 }

+ 11 - 8
app/Utils/Helper.php

@@ -2,6 +2,9 @@
 
 namespace App\Utils;
 
+use App\Models\Server;
+use App\Models\User;
+
 class Helper
 {
     public static function guid($format = false)
@@ -53,23 +56,23 @@ class Helper
         return $str;
     }
 
-    public static function buildVmessLink($item, $user)
+    public static function buildVmessLink(Server $server, User $user)
     {
         $config = [
             "v" => "2",
-            "ps" => $item->name,
-            "add" => $item->host,
-            "port" => $item->port,
+            "ps" => $server->name,
+            "add" => $server->host,
+            "port" => $server->port,
             "id" => $user->v2ray_uuid,
             "aid" => "2",
-            "net" => $item->network,
+            "net" => $server->network,
             "type" => "none",
             "host" => "",
             "path" => "",
-            "tls" => $item->tls ? "tls" : ""
+            "tls" => $server->tls ? "tls" : ""
         ];
-        if ($item->network == 'ws') {
-            $wsSettings = json_decode($item->settings);
+        if ((string)$server->network === 'ws') {
+            $wsSettings = json_decode($server->networkSettings);
             if (isset($wsSettings->path)) $config['path'] = $wsSettings->path;
             if (isset($wsSettings->headers->Host)) $config['host'] = $wsSettings->headers->Host;
         }

+ 1 - 1
composer.json

@@ -13,7 +13,7 @@
         "fideloper/proxy": "^4.0",
         "laravel/framework": "^6.0",
         "laravel/tinker": "^1.0",
-        "lokielse/omnipay-alipay": "^3.0",
+        "lokielse/omnipay-alipay": "3.0.6",
         "php-curl-class/php-curl-class": "^8.6",
         "stripe/stripe-php": "^7.5",
         "symfony/yaml": "^4.3"

+ 2 - 2
config/app.php

@@ -67,7 +67,7 @@ return [
     |
     */
 
-    'timezone' => 'UTC',
+    'timezone' => 'Asia/Shanghai',
 
     /*
     |--------------------------------------------------------------------------
@@ -236,5 +236,5 @@ return [
     | The only modification by laravel config
     |
     */
-    'version' => '1.2.2'
+    'version' => '1.2.3'
 ];

+ 5 - 1
database/install.sql

@@ -87,6 +87,7 @@ CREATE TABLE `v2_order` (
   `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 '使用余额',
   `status` tinyint(1) NOT NULL DEFAULT '0',
   `commission_status` tinyint(1) NOT NULL DEFAULT '0',
   `commission_balance` int(11) NOT NULL DEFAULT '0',
@@ -131,6 +132,9 @@ CREATE TABLE `v2_server` (
   `network` varchar(11) NOT NULL,
   `settings` text,
   `rules` text,
+  `networkSettings` text,
+  `tlsSettings` text,
+  `ruleSettings` text,
   `show` tinyint(1) NOT NULL DEFAULT '0',
   `created_at` int(11) NOT NULL,
   `updated_at` int(11) NOT NULL,
@@ -239,4 +243,4 @@ CREATE TABLE `v2_user` (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
--- 2020-03-05 14:10:26
+-- 2020-03-17 14:16:01

+ 15 - 0
database/update.sql

@@ -185,3 +185,18 @@ CHANGE `expired_at` `expired_at` bigint(20) NULL DEFAULT '0' AFTER `token`;
 
 ALTER TABLE `v2_tutorial`
 DROP `icon`;
+
+ALTER TABLE `v2_server`
+CHANGE `settings` `networkSettings` text COLLATE 'utf8_general_ci' NULL AFTER `network`,
+CHANGE `rules` `ruleSettings` text COLLATE 'utf8_general_ci' NULL AFTER `networkSettings`;
+
+ALTER TABLE `v2_server`
+CHANGE `tags` `tags` varchar(255) COLLATE 'utf8_general_ci' NULL AFTER `server_port`,
+CHANGE `rate` `rate` varchar(11) COLLATE 'utf8_general_ci' NOT NULL AFTER `tags`,
+CHANGE `network` `network` varchar(11) COLLATE 'utf8_general_ci' NOT NULL AFTER `rate`,
+CHANGE `networkSettings` `networkSettings` text COLLATE 'utf8_general_ci' NULL AFTER `network`,
+CHANGE `tls` `tls` tinyint(4) NOT NULL DEFAULT '0' AFTER `networkSettings`,
+ADD `tlsSettings` text COLLATE 'utf8_general_ci' NULL AFTER `tls`;
+
+ALTER TABLE `v2_order`
+ADD `balance_amount` int(11) NULL COMMENT '使用余额' AFTER `refund_amount`;

+ 0 - 2
init.sh

@@ -1,2 +0,0 @@
-php artisan key:generate
-php artisan config:cache

+ 2 - 2
pm2.yaml

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

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


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


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


+ 187 - 0
resources/views/mail/classic/notify.blade.php

@@ -0,0 +1,187 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+
+<head>
+    <meta charset="UTF-8">
+    <title>网站公告</title>
+    <style type="text/css">
+        img {
+            max-width: 100%;
+        }
+
+        body {
+            -webkit-font-smoothing: antialiased;
+            -webkit-text-size-adjust: none;
+            width: 100% !important;
+            height: 100%;
+            line-height: 1.6em;
+        }
+
+        body {
+            background-color: #f6f6f6;
+        }
+
+        @media only screen and (max-width: 640px) {
+            body {
+                padding: 0 !important;
+            }
+
+            h1 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h2 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h3 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h4 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h1 {
+                font-size: 22px !important;
+            }
+
+            h2 {
+                font-size: 18px !important;
+            }
+
+            h3 {
+                font-size: 16px !important;
+            }
+
+            .container {
+                padding: 0 !important;
+                width: 100% !important;
+            }
+
+            .content {
+                padding: 0 !important;
+            }
+
+            .content-wrap {
+                padding: 10px !important;
+            }
+
+            .invoice {
+                width: 100% !important;
+            }
+        }
+    </style>
+</head>
+
+<body itemscope itemtype="http://schema.org/EmailMessage"
+    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;"
+    bgcolor="#f6f6f6">
+    <table class="body-wrap"
+        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;"
+        bgcolor="#f6f6f6">
+        <tr
+            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+            <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"
+                valign="top">
+            </td>
+            <td class="container" width="600"
+                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;"
+                valign="top">
+                <div class="content"
+                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;">
+                    <table class="main" width="100%" cellpadding="0" cellspacing="0"
+                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;"
+                        bgcolor="#fff">
+                        <tr
+                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                            <td class="alert alert-warning"
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 22px; font-weight: bold; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: #0073ba; margin: 0; padding: 20px;"
+                                align="center" bgcolor="#0073ba" valign="top">
+                                网站公告
+                            </td>
+                        </tr>
+                        <tr
+                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                            <td class="content-wrap"
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;"
+                                valign="top">
+                                <table width="100%" cellpadding="0" cellspacing="0"
+                                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 34px; vertical-align: top; line-height: 1em; margin: 0; padding: 20px 0 30px;"
+                                            valign="top">
+                                            Dear Customer
+                                        </td>
+                                    </tr>
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; color: #4a4a4a; vertical-align: top; margin: 0; padding: 0 10px 20px;"
+                                            valign="top">
+                                            {!! nl2br($content) !!}
+                                        </td>
+                                    </tr>
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #757575; vertical-align: top; margin: 0; padding: 0 0 20px;"
+                                            valign="top">
+                                            (本邮件由系统自动发出,请勿直接回复)
+                                        </td>
+                                    </tr>
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; text-align: center; vertical-align: top; margin: 0; padding: 0 0 20px;"
+                                            valign="top">
+                                            <a href="{{$url}}"
+                                                class="btn-primary"
+                                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #fff; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #0073ba; margin: 0; border-color: #0073ba; border-style: solid; border-width: 8px 20px;">登录 {{$name}}</a>
+                                        </td>
+                                    </tr>
+                                </table>
+                            </td>
+                        </tr>
+                    </table>
+                    <div class="footer"
+                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
+                        <table width="100%"
+                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                            <tr
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                <td class="aligncenter content-block"
+                                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0;"
+                                    align="center" valign="top">
+                                    &copy; {{$name}}. All Rights Reserved.
+                                </td>
+                            </tr>
+                            <tr
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                <td class="aligncenter content-block"
+                                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;"
+                                    align="center" valign="top">
+                                    <a href="{{$url}}/#/subscribe"
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: none; margin: 0;">我的订阅</a> |
+                                    <a href="{{$url}}/#/tutorial"
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: none; margin: 0;">使用教程</a>
+                                </td>
+                            </tr>
+                        </table>
+                    </div>
+                </div>
+            </td>
+            <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"
+                valign="top">
+            </td>
+        </tr>
+    </table>
+</body>
+
+</html>

+ 187 - 0
resources/views/mail/classic/remindExpire.blade.php

@@ -0,0 +1,187 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+
+<head>
+    <meta charset="UTF-8">
+    <title>到期提示</title>
+    <style type="text/css">
+        img {
+            max-width: 100%;
+        }
+
+        body {
+            -webkit-font-smoothing: antialiased;
+            -webkit-text-size-adjust: none;
+            width: 100% !important;
+            height: 100%;
+            line-height: 1.6em;
+        }
+
+        body {
+            background-color: #f6f6f6;
+        }
+
+        @media only screen and (max-width: 640px) {
+            body {
+                padding: 0 !important;
+            }
+
+            h1 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h2 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h3 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h4 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h1 {
+                font-size: 22px !important;
+            }
+
+            h2 {
+                font-size: 18px !important;
+            }
+
+            h3 {
+                font-size: 16px !important;
+            }
+
+            .container {
+                padding: 0 !important;
+                width: 100% !important;
+            }
+
+            .content {
+                padding: 0 !important;
+            }
+
+            .content-wrap {
+                padding: 10px !important;
+            }
+
+            .invoice {
+                width: 100% !important;
+            }
+        }
+    </style>
+</head>
+
+<body itemscope itemtype="http://schema.org/EmailMessage"
+    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;"
+    bgcolor="#f6f6f6">
+    <table class="body-wrap"
+        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;"
+        bgcolor="#f6f6f6">
+        <tr
+            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+            <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"
+                valign="top">
+            </td>
+            <td class="container" width="600"
+                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;"
+                valign="top">
+                <div class="content"
+                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;">
+                    <table class="main" width="100%" cellpadding="0" cellspacing="0"
+                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;"
+                        bgcolor="#fff">
+                        <tr
+                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                            <td class="alert alert-warning"
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 22px; font-weight: bold; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: #0073ba; margin: 0; padding: 20px;"
+                                align="center" bgcolor="#0073ba" valign="top">
+                                到期提示
+                            </td>
+                        </tr>
+                        <tr
+                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                            <td class="content-wrap"
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;"
+                                valign="top">
+                                <table width="100%" cellpadding="0" cellspacing="0"
+                                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 34px; vertical-align: top; line-height: 1em; margin: 0; padding: 20px 0 30px;"
+                                            valign="top">
+                                            Dear Customer
+                                        </td>
+                                    </tr>
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; color: #4a4a4a; vertical-align: top; margin: 0; padding: 0 0 20px;"
+                                            valign="top">
+                                            您的订阅套餐将于 <strong>24</strong> 小时后到期,请及时续费
+                                        </td>
+                                    </tr>
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #757575; vertical-align: top; margin: 0; padding: 0 0 20px;"
+                                            valign="top">
+                                            (本邮件由系统自动发出,请勿直接回复)
+                                        </td>
+                                    </tr>
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; text-align: center; vertical-align: top; margin: 0; padding: 0 0 20px;"
+                                            valign="top">
+                                            <a href="{{$url}}"
+                                                class="btn-primary"
+                                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #fff; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #0073ba; margin: 0; border-color: #0073ba; border-style: solid; border-width: 8px 20px;">登录 {{$name}}</a>
+                                        </td>
+                                    </tr>
+                                </table>
+                            </td>
+                        </tr>
+                    </table>
+                    <div class="footer"
+                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
+                        <table width="100%"
+                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                            <tr
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                <td class="aligncenter content-block"
+                                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0;"
+                                    align="center" valign="top">
+                                    &copy; {{$name}}. All Rights Reserved.
+                                </td>
+                            </tr>
+                            <tr
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                <td class="aligncenter content-block"
+                                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;"
+                                    align="center" valign="top">
+                                    <a href="{{$url}}/#/subscribe"
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: none; margin: 0;">我的订阅</a> |
+                                    <a href="{{$url}}/#/tutorial"
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: none; margin: 0;">使用教程</a>
+                                </td>
+                            </tr>
+                        </table>
+                    </div>
+                </div>
+            </td>
+            <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"
+                valign="top">
+            </td>
+        </tr>
+    </table>
+</body>
+
+</html>

+ 187 - 0
resources/views/mail/classic/remindTraffic.blade.php

@@ -0,0 +1,187 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+
+<head>
+    <meta charset="UTF-8">
+    <title>流量提示</title>
+    <style type="text/css">
+        img {
+            max-width: 100%;
+        }
+
+        body {
+            -webkit-font-smoothing: antialiased;
+            -webkit-text-size-adjust: none;
+            width: 100% !important;
+            height: 100%;
+            line-height: 1.6em;
+        }
+
+        body {
+            background-color: #f6f6f6;
+        }
+
+        @media only screen and (max-width: 640px) {
+            body {
+                padding: 0 !important;
+            }
+
+            h1 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h2 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h3 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h4 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h1 {
+                font-size: 22px !important;
+            }
+
+            h2 {
+                font-size: 18px !important;
+            }
+
+            h3 {
+                font-size: 16px !important;
+            }
+
+            .container {
+                padding: 0 !important;
+                width: 100% !important;
+            }
+
+            .content {
+                padding: 0 !important;
+            }
+
+            .content-wrap {
+                padding: 10px !important;
+            }
+
+            .invoice {
+                width: 100% !important;
+            }
+        }
+    </style>
+</head>
+
+<body itemscope itemtype="http://schema.org/EmailMessage"
+    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;"
+    bgcolor="#f6f6f6">
+    <table class="body-wrap"
+        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;"
+        bgcolor="#f6f6f6">
+        <tr
+            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+            <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"
+                valign="top">
+            </td>
+            <td class="container" width="600"
+                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;"
+                valign="top">
+                <div class="content"
+                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;">
+                    <table class="main" width="100%" cellpadding="0" cellspacing="0"
+                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;"
+                        bgcolor="#fff">
+                        <tr
+                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                            <td class="alert alert-warning"
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 22px; font-weight: bold; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: #0073ba; margin: 0; padding: 20px;"
+                                align="center" bgcolor="#0073ba" valign="top">
+                                流量提示
+                            </td>
+                        </tr>
+                        <tr
+                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                            <td class="content-wrap"
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;"
+                                valign="top">
+                                <table width="100%" cellpadding="0" cellspacing="0"
+                                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 34px; vertical-align: top; line-height: 1em; margin: 0; padding: 20px 0 30px;"
+                                            valign="top">
+                                            Dear Customer
+                                        </td>
+                                    </tr>
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; color: #4a4a4a; vertical-align: top; margin: 0; padding: 0 0 20px;"
+                                            valign="top">
+                                            您本月的套餐流量已使用 <strong>80%</strong>,请合理安排使用,避免提前耗尽
+                                        </td>
+                                    </tr>
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #757575; vertical-align: top; margin: 0; padding: 0 0 20px;"
+                                            valign="top">
+                                            (本邮件由系统自动发出,请勿直接回复)
+                                        </td>
+                                    </tr>
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; text-align: center; vertical-align: top; margin: 0; padding: 0 0 20px;"
+                                            valign="top">
+                                            <a href="{{$url}}"
+                                                class="btn-primary"
+                                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #fff; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #0073ba; margin: 0; border-color: #0073ba; border-style: solid; border-width: 8px 20px;">登录 {{$name}}</a>
+                                        </td>
+                                    </tr>
+                                </table>
+                            </td>
+                        </tr>
+                    </table>
+                    <div class="footer"
+                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
+                        <table width="100%"
+                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                            <tr
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                <td class="aligncenter content-block"
+                                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0;"
+                                    align="center" valign="top">
+                                    &copy; {{$name}}. All Rights Reserved.
+                                </td>
+                            </tr>
+                            <tr
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                <td class="aligncenter content-block"
+                                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;"
+                                    align="center" valign="top">
+                                    <a href="{{$url}}/#/subscribe"
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: none; margin: 0;">我的订阅</a> |
+                                    <a href="{{$url}}/#/tutorial"
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: none; margin: 0;">使用教程</a>
+                                </td>
+                            </tr>
+                        </table>
+                    </div>
+                </div>
+            </td>
+            <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"
+                valign="top">
+            </td>
+        </tr>
+    </table>
+</body>
+
+</html>

+ 195 - 0
resources/views/mail/classic/verify.blade.php

@@ -0,0 +1,195 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+
+<head>
+    <meta charset="UTF-8">
+    <title>邮箱验证码</title>
+    <style type="text/css">
+        img {
+            max-width: 100%;
+        }
+
+        body {
+            -webkit-font-smoothing: antialiased;
+            -webkit-text-size-adjust: none;
+            width: 100% !important;
+            height: 100%;
+            line-height: 1.6em;
+        }
+
+        body {
+            background-color: #f6f6f6;
+        }
+
+        @media only screen and (max-width: 640px) {
+            body {
+                padding: 0 !important;
+            }
+
+            h1 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h2 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h3 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h4 {
+                font-weight: 800 !important;
+                margin: 20px 0 5px !important;
+            }
+
+            h1 {
+                font-size: 22px !important;
+            }
+
+            h2 {
+                font-size: 18px !important;
+            }
+
+            h3 {
+                font-size: 16px !important;
+            }
+
+            .container {
+                padding: 0 !important;
+                width: 100% !important;
+            }
+
+            .content {
+                padding: 0 !important;
+            }
+
+            .content-wrap {
+                padding: 10px !important;
+            }
+
+            .invoice {
+                width: 100% !important;
+            }
+        }
+    </style>
+</head>
+
+<body itemscope itemtype="http://schema.org/EmailMessage"
+    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;"
+    bgcolor="#f6f6f6">
+    <table class="body-wrap"
+        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;"
+        bgcolor="#f6f6f6">
+        <tr
+            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+            <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"
+                valign="top">
+            </td>
+            <td class="container" width="600"
+                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;"
+                valign="top">
+                <div class="content"
+                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;">
+                    <table class="main" width="100%" cellpadding="0" cellspacing="0"
+                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;"
+                        bgcolor="#fff">
+                        <tr
+                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                            <td class="alert alert-warning"
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 22px; font-weight: bold; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: #0073ba; margin: 0; padding: 20px;"
+                                align="center" bgcolor="#0073ba" valign="top">
+                                邮箱验证码
+                            </td>
+                        </tr>
+                        <tr
+                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                            <td class="content-wrap"
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;"
+                                valign="top">
+                                <table width="100%" cellpadding="0" cellspacing="0"
+                                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 34px; vertical-align: top; line-height: 1em; margin: 0; padding: 20px 0 30px;"
+                                            valign="top">
+                                            Dear Customer
+                                        </td>
+                                    </tr>
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; color: #4a4a4a; vertical-align: top; margin: 0; padding: 0 0 20px;"
+                                            valign="top">
+                                            请填写以下验证码完成邮箱验证 (1分钟内有效)
+                                        </td>
+                                    </tr>
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 36px; font-weight: bold; text-align: center; color: #4a4a4a; vertical-align: top; line-height: 1.6em; margin: 0; padding: 0 0 20px;"
+                                            valign="top">
+                                            {{$code}}
+                                        </td>
+                                    </tr>
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #757575; vertical-align: top; margin: 0; padding: 0 0 20px;"
+                                            valign="top">
+                                            (本邮件由系统自动发出,请勿直接回复)
+                                        </td>
+                                    </tr>
+                                    <tr
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                        <td class="content-block"
+                                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; text-align: center; vertical-align: top; margin: 0; padding: 0 0 20px;"
+                                            valign="top">
+                                            <a href="{{$url}}"
+                                                class="btn-primary"
+                                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #fff; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #0073ba; margin: 0; border-color: #0073ba; border-style: solid; border-width: 8px 20px;">登录 {{$name}}</a>
+                                        </td>
+                                    </tr>
+                                </table>
+                            </td>
+                        </tr>
+                    </table>
+                    <div class="footer"
+                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
+                        <table width="100%"
+                            style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                            <tr
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                <td class="aligncenter content-block"
+                                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0;"
+                                    align="center" valign="top">
+                                    &copy; {{$name}}. All Rights Reserved.
+                                </td>
+                            </tr>
+                            <tr
+                                style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
+                                <td class="aligncenter content-block"
+                                    style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;"
+                                    align="center" valign="top">
+                                    <a href="{{$url}}/#/subscribe"
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: none; margin: 0;">我的订阅</a> |
+                                    <a href="{{$url}}/#/tutorial"
+                                        style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: none; margin: 0;">使用教程</a>
+                                </td>
+                            </tr>
+                        </table>
+                    </div>
+                </div>
+            </td>
+            <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;"
+                valign="top">
+            </td>
+        </tr>
+    </table>
+</body>
+
+</html>

+ 42 - 0
resources/views/mail/default/notify.blade.php

@@ -0,0 +1,42 @@
+<div style="background: #eee">
+    <table width="600" border="0" align="center" cellpadding="0" cellspacing="0">
+        <tbody>
+        <tr>
+            <td>
+                <div style="background:#fff">
+                    <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                        <thead>
+                        <tr>
+                            <td valign="middle" style="padding-left:30px;background-color:#415A94;color:#fff;padding:20px 40px;font-size: 21px;">{{$name}}</td>
+                        </tr>
+                        </thead>
+                        <tbody>
+                        <tr style="padding:40px 40px 0 40px;display:table-cell">
+                            <td style="font-size:24px;line-height:1.5;color:#000;margin-top:40px">公告通知</td>
+                        </tr>
+                        <tr>
+                            <td style="font-size:14px;color:#333;padding:24px 40px 0 40px">
+                                尊敬的用户您好!
+                                <br />
+                                <br />
+                                {!! nl2br($content) !!}
+                            </td>
+                        </tr>
+                        <tr style="padding:40px;display:table-cell">
+                        </tr>
+                        </tbody>
+                    </table>
+                </div>
+                <div>
+                    <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                        <tbody>
+                        <tr>
+                            <td style="padding:20px 40px;font-size:12px;color:#999;line-height:20px;background:#f7f7f7"><a href="{{$url}}" style="font-size:14px;color:#929292">返回{{$name}}</a></td>
+                        </tr>
+                        </tbody>
+                    </table>
+                </div></td>
+        </tr>
+        </tbody>
+    </table>
+</div>

+ 42 - 0
resources/views/mail/default/remindExpire.blade.php

@@ -0,0 +1,42 @@
+<div style="background: #eee">
+    <table width="600" border="0" align="center" cellpadding="0" cellspacing="0">
+        <tbody>
+        <tr>
+            <td>
+                <div style="background:#fff">
+                    <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                        <thead>
+                        <tr>
+                            <td valign="middle" style="padding-left:30px;background-color:#415A94;color:#fff;padding:20px 40px;font-size: 21px;">{{$name}}</td>
+                        </tr>
+                        </thead>
+                        <tbody>
+                        <tr style="padding:40px 40px 0 40px;display:table-cell">
+                            <td style="font-size:24px;line-height:1.5;color:#000;margin-top:40px">到期通知</td>
+                        </tr>
+                        <tr>
+                            <td style="font-size:14px;color:#333;padding:24px 40px 0 40px">
+                                尊敬的用户您好!
+                                <br />
+                                <br />
+                                你的服务将在24小时内到期。为了不造成使用上的影响请尽快续费。如果你已续费请忽略此邮件。
+                            </td>
+                        </tr>
+                        <tr style="padding:40px;display:table-cell">
+                        </tr>
+                        </tbody>
+                    </table>
+                </div>
+                <div>
+                    <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                        <tbody>
+                        <tr>
+                            <td style="padding:20px 40px;font-size:12px;color:#999;line-height:20px;background:#f7f7f7"><a href="{{$url}}" style="font-size:14px;color:#929292">返回{{$name}}</a></td>
+                        </tr>
+                        </tbody>
+                    </table>
+                </div></td>
+        </tr>
+        </tbody>
+    </table>
+</div>

+ 42 - 0
resources/views/mail/default/remindTraffic.blade.php

@@ -0,0 +1,42 @@
+<div style="background: #eee">
+    <table width="600" border="0" align="center" cellpadding="0" cellspacing="0">
+        <tbody>
+        <tr>
+            <td>
+                <div style="background:#fff">
+                    <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                        <thead>
+                        <tr>
+                            <td valign="middle" style="padding-left:30px;background-color:#415A94;color:#fff;padding:20px 40px;font-size: 21px;">{{$name}}</td>
+                        </tr>
+                        </thead>
+                        <tbody>
+                        <tr style="padding:40px 40px 0 40px;display:table-cell">
+                            <td style="font-size:24px;line-height:1.5;color:#000;margin-top:40px">流量通知</td>
+                        </tr>
+                        <tr>
+                            <td style="font-size:14px;color:#333;padding:24px 40px 0 40px">
+                                尊敬的用户您好!
+                                <br />
+                                <br />
+                                你的流量已经使用80%。为了不造成使用上的影响请合理安排流量的使用。
+                            </td>
+                        </tr>
+                        <tr style="padding:40px;display:table-cell">
+                        </tr>
+                        </tbody>
+                    </table>
+                </div>
+                <div>
+                    <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                        <tbody>
+                        <tr>
+                            <td style="padding:20px 40px;font-size:12px;color:#999;line-height:20px;background:#f7f7f7"><a href="{{$url}}" style="font-size:14px;color:#929292">返回{{$name}}</a></td>
+                        </tr>
+                        </tbody>
+                    </table>
+                </div></td>
+        </tr>
+        </tbody>
+    </table>
+</div>

+ 42 - 0
resources/views/mail/default/verify.blade.php

@@ -0,0 +1,42 @@
+<div style="background: #eee">
+    <table width="600" border="0" align="center" cellpadding="0" cellspacing="0">
+        <tbody>
+        <tr>
+            <td>
+                <div style="background:#fff">
+                    <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                        <thead>
+                        <tr>
+                            <td valign="middle" style="padding-left:30px;background-color:#415A94;color:#fff;padding:20px 40px;font-size: 21px;">{{$name}}</td>
+                        </tr>
+                        </thead>
+                        <tbody>
+                        <tr style="padding:40px 40px 0 40px;display:table-cell">
+                            <td style="font-size:24px;line-height:1.5;color:#000;margin-top:40px">邮箱验证码</td>
+                        </tr>
+                        <tr>
+                            <td style="font-size:14px;color:#333;padding:24px 40px 0 40px">
+                                尊敬的用户您好!
+                                <br />
+                                <br />
+                                您的验证码是:{{$code}},请在 5 分钟内进行验证。如果该验证码不为您本人申请,请无视。
+                            </td>
+                        </tr>
+                        <tr style="padding:40px;display:table-cell">
+                        </tr>
+                        </tbody>
+                    </table>
+                </div>
+                <div>
+                    <table width="100%" border="0" cellspacing="0" cellpadding="0">
+                        <tbody>
+                        <tr>
+                            <td style="padding:20px 40px;font-size:12px;color:#999;line-height:20px;background:#f7f7f7"><a href="{{$url}}" style="font-size:14px;color:#929292">返回{{$name}}</a></td>
+                        </tr>
+                        </tbody>
+                    </table>
+                </div></td>
+        </tr>
+        </tbody>
+    </table>
+</div>

File diff suppressed because it is too large
+ 0 - 26
resources/views/mail/sendEmailCustom.blade.php


File diff suppressed because it is too large
+ 0 - 26
resources/views/mail/sendEmailVerify.blade.php


File diff suppressed because it is too large
+ 0 - 26
resources/views/mail/sendRemindExpire.blade.php


File diff suppressed because it is too large
+ 0 - 26
resources/views/mail/sendRemindTraffic.blade.php


+ 4 - 0
update.sh

@@ -0,0 +1,4 @@
+git fetch --all && git reset --hard origin/master && git pull origin master
+php artisan v2board:update
+php artisan config:cache
+pm2 restart pm2.yaml

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