Просмотр исходного кода

Merge branch 'master' into master

Bruskyii Panda 7 лет назад
Родитель
Сommit
f4718f7237
57 измененных файлов с 634 добавлено и 356 удалено
  1. 0 1
      app/Components/Helpers.php
  2. 0 1
      app/Components/ServerChan.php
  3. 1 1
      app/Console/Commands/AutoCheckNodeStatus.php
  4. 8 4
      app/Console/Commands/AutoClearLog.php
  5. 1 1
      app/Console/Commands/AutoDecGoodsTraffic.php
  6. 22 0
      app/Console/Commands/AutoJob.php
  7. 36 0
      app/Console/Commands/upgradeUserSpeedLimit.php
  8. 2 0
      app/Console/Kernel.php
  9. 92 27
      app/Http/Controllers/AdminController.php
  10. 17 6
      app/Http/Controllers/AuthController.php
  11. 0 1
      app/Http/Controllers/TicketController.php
  12. 13 9
      app/Http/Controllers/UserController.php
  13. 8 8
      app/Http/Models/Device.php
  14. 0 1
      app/Http/Models/EmailLog.php
  15. 0 1
      app/Http/Models/Ticket.php
  16. 0 1
      app/Http/Models/TicketReply.php
  17. 2 2
      app/Http/Models/UserTrafficLog.php
  18. 1 1
      app/Mail/activeUser.php
  19. 1 1
      app/Mail/closeTicket.php
  20. 1 1
      app/Mail/newTicket.php
  21. 2 2
      app/Mail/nodeCrashWarning.php
  22. 1 1
      app/Mail/replyTicket.php
  23. 1 1
      app/Mail/resetPassword.php
  24. 1 1
      app/Mail/sendUserInfo.php
  25. 1 1
      app/Mail/sendVerifyCode.php
  26. 1 1
      app/Mail/userExpireWarning.php
  27. 1 1
      app/Mail/userExpireWarningToday.php
  28. 1 1
      app/Mail/userTrafficWarning.php
  29. 0 35
      database/migrations/2014_10_12_000000_create_users_table.php
  30. 0 32
      database/migrations/2014_10_12_100000_create_password_resets_table.php
  31. 0 102
      database/migrations/2019_01_17_100504_create_permission_tables.php
  32. 9 0
      queue.sh
  33. 6 4
      resources/views/admin/addUser.blade.php
  34. 10 10
      resources/views/admin/config.blade.php
  35. 5 3
      resources/views/admin/editUser.blade.php
  36. 7 1
      resources/views/admin/layouts.blade.php
  37. 1 1
      resources/views/admin/nodeList.blade.php
  38. 123 0
      resources/views/admin/onlineIPMonitor.blade.php
  39. 9 19
      resources/views/admin/system.blade.php
  40. 12 3
      resources/views/admin/trafficLog.blade.php
  41. 11 2
      resources/views/admin/userList.blade.php
  42. 14 26
      resources/views/admin/userOnlineIPList.blade.php
  43. 2 2
      resources/views/emails/nodeCrashWarning.blade.php
  44. 12 6
      resources/views/subscribe/deviceList.blade.php
  45. 3 0
      resources/views/user/buy.blade.php
  46. 1 1
      resources/views/user/invite.blade.php
  47. 1 0
      routes/web.php
  48. 102 26
      sql/db.sql
  49. 2 2
      sql/update/20190129.sql
  50. 8 0
      sql/update/20190130.sql
  51. 30 4
      sql/update/20190131.sql
  52. 33 0
      sql/update/20190210.sql
  53. 13 0
      sql/update/20190213.sql
  54. 3 0
      sql/update/20190215.sql
  55. 1 0
      storage/framework/testing/.gitignore
  56. BIN
      storage/qqwry.dat
  57. 2 1
      update.sh

+ 0 - 1
app/Components/Helpers.php

@@ -141,7 +141,6 @@ class Helpers
         $log->content = $content;
         $log->status = $status;
         $log->error = $error;
-        $log->created_at = date('Y-m-d H:i:s');
 
         return $log->save();
     }

+ 0 - 1
app/Components/ServerChan.php

@@ -52,7 +52,6 @@ class ServerChan
         $log->content = $content;
         $log->status = $status;
         $log->error = $error;
-        $log->created_at = date('Y-m-d H:i:s');
 
         return $log->save();
     }

+ 1 - 1
app/Console/Commands/AutoCheckNodeStatus.php

@@ -99,7 +99,7 @@ class AutoCheckNodeStatus extends Command
                 Log::info("【TCP阻断检测】" . $node->name . ' - ' . $node->ip . ' - ' . $text);
             }
 
-            // 10分钟内无节点负载信息且TCP检测认为不是宕机则认为是SSR(R)后端炸了
+            // 10分钟内无节点负载信息且TCP检测认为不是离线则认为是后端炸了
             $nodeTTL = SsNodeInfo::query()->where('node_id', $node->id)->where('log_time', '>=', strtotime("-10 minutes"))->orderBy('id', 'desc')->first();
             if ($tcpCheck !== 1 && !$nodeTTL) {
                 $this->notifyMaster($title, "节点**{$node->name}【{$node->ip}】**异常:**心跳异常**", $node->name, $node->server);

+ 8 - 4
app/Console/Commands/AutoClearLog.php

@@ -10,6 +10,7 @@ use App\Http\Models\SsNodeTrafficHourly;
 use App\Http\Models\SsNodeTrafficDaily;
 use App\Http\Models\UserBanLog;
 use App\Http\Models\UserLoginLog;
+use App\Http\Models\UserSubscribeLog;
 use App\Http\Models\UserTrafficDaily;
 use App\Http\Models\UserTrafficLog;
 use App\Http\Models\UserTrafficHourly;
@@ -52,8 +53,8 @@ class AutoClearLog extends Command
         // 自动清除1小时以前的节点在线用户数日志
         SsNodeOnlineLog::query()->where('log_time', '<=', strtotime("-1 hour"))->delete();
 
-        // 自动清除1个月以前的用户流量日志
-        UserTrafficLog::query()->where('log_time', '<=', strtotime("-1 month"))->delete();
+        // 自动清除7天以前的用户流量日志
+        UserTrafficLog::query()->where('log_time', '<=', strtotime("-7 days"))->delete();
 
         // 自动清除10天以前的用户每小时流量数据日志
         UserTrafficHourly::query()->where('created_at', '<=', date('Y-m-d H:i:s', strtotime('-10 days')))->delete();
@@ -70,11 +71,14 @@ class AutoClearLog extends Command
         // 自动清除30天以前用户封禁日志
         UserBanLog::query()->where('created_at', '<=', date('Y-m-d H:i:s', strtotime("-1 month")))->delete();
 
-        // 自动清除1个月以前用户连接IP
-        SsNodeIp::query()->where('created_at', '<=', strtotime("-1 month"))->delete();
+        // 自动清除3天前用户连接IP
+        SsNodeIp::query()->where('created_at', '<=', strtotime("-3 days"))->delete();
 
         // 自动清除3个月以前用户登陆日志
         UserLoginLog::query()->where('created_at', '<=', date('Y-m-d H:i:s', strtotime("-3 month")))->delete();
+
+        // 自动清除1个月前的用户订阅记录
+        UserSubscribeLog::query()->where('request_time', '<=', date('Y-m-d H:i:s', strtotime("-1 month")))->delete();
     }
 
 }

+ 1 - 1
app/Console/Commands/AutoDecGoodsTraffic.php

@@ -79,7 +79,7 @@ class AutoDecGoodsTraffic extends Command
                         User::query()->where('id', $order->user_id)->update(['u' => 0, 'd' => 0, 'transfer_enable' => 0]);
                     } else {
                         // 写入用户流量变动记录
-                        Helpers::addUserTrafficModifyLog($order->user_id, $order->oid, $order->user->transfer_enable, ($order->goods->traffic * 1048576), '[定时任务]用户所购商品到期,扣减商品对应的流量(没扣完)');
+                        Helpers::addUserTrafficModifyLog($order->user_id, $order->oid, $order->user->transfer_enable, ($order->user->transfer_enable - $order->goods->traffic * 1048576), '[定时任务]用户所购商品到期,扣减商品对应的流量(没扣完)');
 
                         User::query()->where('id', $order->user_id)->decrement('transfer_enable', $order->goods->traffic * 1048576);
 

+ 22 - 0
app/Console/Commands/AutoJob.php

@@ -3,12 +3,14 @@
 namespace App\Console\Commands;
 
 use App\Components\Helpers;
+use App\Components\ServerChan;
 use App\Components\Yzy;
 use App\Http\Models\Goods;
 use App\Http\Models\GoodsLabel;
 use App\Http\Models\ReferralLog;
 use App\Http\Models\SsNode;
 use App\Http\Models\SsNodeLabel;
+use App\Http\Models\Ticket;
 use App\Http\Models\UserBalanceLog;
 use App\Http\Models\VerifyCode;
 use App\Mail\sendUserInfo;
@@ -73,6 +75,9 @@ class AutoJob extends Command
         // 关闭超时未支付订单
         $this->closeOrders();
 
+        // 关闭超过72小时未处理的工单
+        $this->closeTickets();
+
         $jobEndTime = microtime(true);
         $jobUsedTime = round(($jobEndTime - $jobStartTime), 4);
 
@@ -180,6 +185,11 @@ class AutoJob extends Command
             $userList = User::query()->where('status', '>=', 0)->where('enable', 1)->where('ban_time', 0)->get();
             if (!$userList->isEmpty()) {
                 foreach ($userList as $user) {
+                    // 对管理员豁免
+                    if ($user->is_admin) {
+                        continue;
+                    }
+
                     // 多往前取5分钟,防止数据统计任务执行时间过长导致没有数据
                     $totalTraffic = UserTrafficHourly::query()->where('user_id', $user->id)->where('node_id', 0)->where('created_at', '>=', date('Y-m-d H:i:s', time() - 3900))->sum('total');
                     if ($totalTraffic >= (self::$systemConfig['traffic_ban_value'] * 1024 * 1024 * 1024)) {
@@ -513,6 +523,18 @@ class AutoJob extends Command
         }
     }
 
+    // 关闭超过72小时未处理的工单
+    private function closeTickets()
+    {
+        $ticketList = Ticket::query()->where('updated_at', '<=', date('Y-m-d H:i:s', strtotime("-72 hours")))->where('status', 1)->get();
+        foreach ($ticketList as $ticket) {
+            $ret = Ticket::query()->where('id', $ticket->id)->update(['status' => 2]);
+            if ($ret) {
+                ServerChan::send('工单关闭提醒', '工单:ID' . $ticket->id . '超过72小时未处理,系统已自动关闭');
+            }
+        }
+    }
+
     /**
      * 添加用户封禁日志
      *

+ 36 - 0
app/Console/Commands/upgradeUserSpeedLimit.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Http\Models\User;
+use Illuminate\Console\Command;
+use Log;
+
+class upgradeUserSpeedLimit extends Command
+{
+    protected $signature = 'upgradeUserSpeedLimit';
+    protected $description = '升级用户限速字段,重置初始值';
+
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    public function handle()
+    {
+        Log::info('----------------------------【重置用户限速字段】开始----------------------------');
+
+        $userList = User::query()->get();
+        foreach ($userList as $user) {
+            $data = [
+                'speed_limit_per_con'  => 10737418240,
+                'speed_limit_per_user' => 10737418240
+            ];
+
+            User::query()->where('id', $user->id)->update($data);
+            Log::info('---用户[ID:' . $user->id . ' - ' . $user->username . ']的限速字段值被重置为10G---');
+        }
+
+        Log::info('----------------------------【重置用户限速字段】结束----------------------------');
+    }
+}

+ 2 - 0
app/Console/Kernel.php

@@ -26,6 +26,8 @@ class Kernel extends ConsoleKernel
         \App\Console\Commands\UserExpireAutoWarning::class,
         \App\Console\Commands\UserTrafficAutoWarning::class,
         \App\Console\Commands\upgradeUserPassword::class,
+        \App\Console\Commands\upgradeUserSpeedLimit::class,
+        \App\Console\Commands\upgradeUserSubscribe::class,
         \App\Console\Commands\upgradeUserVmessId::class,
         \App\Console\Commands\AutoReportNode::class,
     ];

+ 92 - 27
app/Http/Controllers/AdminController.php

@@ -3,6 +3,8 @@
 namespace App\Http\Controllers;
 
 use App\Components\Helpers;
+use App\Components\IPIP;
+use App\Components\QQWry;
 use App\Http\Models\Article;
 use App\Http\Models\Config;
 use App\Http\Models\Country;
@@ -116,8 +118,8 @@ class AdminController extends Controller
         $qq = trim($request->get('qq'));
         $port = intval($request->get('port'));
         $pay_way = intval($request->get('pay_way'));
-        $status = intval($request->get('status'));
-        $enable = intval($request->get('enable'));
+        $status = $request->get('status');
+        $enable = $request->get('enable');
         $online = $request->get('online');
         $unActive = $request->get('unActive');
         $flowAbnormal = $request->get('flowAbnormal');
@@ -150,11 +152,11 @@ class AdminController extends Controller
         }
 
         if ($status != '') {
-            $query->where('status', $status);
+            $query->where('status', intval($status));
         }
 
         if ($enable != '') {
-            $query->where('enable', $enable);
+            $query->where('enable', intval($enable));
         }
 
         // 流量超过100G的
@@ -553,8 +555,8 @@ class AdminController extends Controller
 
             // 负载(10分钟以内)
             $node_info = SsNodeInfo::query()->where('node_id', $node->id)->where('log_time', '>=', strtotime("-10 minutes"))->orderBy('id', 'desc')->first();
-            $node->load = empty($node_info) || empty($node_info->load) ? '宕机' : $node_info->load;
-            $node->uptime = empty($online_log) ? 0 : seconds2time($node_info->uptime);
+            $node->load = empty($node_info) || empty($node_info->load) ? '离线' : $node_info->load;
+            $node->uptime = empty($node_info) ? 0 : seconds2time($node_info->uptime);
         }
 
         $view['nodeList'] = $nodeList;
@@ -1108,26 +1110,31 @@ class AdminController extends Controller
     {
         $port = $request->get('port');
         $user_id = $request->get('user_id');
-        $username = $request->get('username');
+        $username = trim($request->get('username'));
+        $nodeId = intval($request->get('nodeId'));
 
-        $query = UserTrafficLog::with(['User', 'SsNode']);
+        $query = UserTrafficLog::query()->with(['user', 'node']);
 
-        if (!empty($port)) {
+        if ($port) {
             $query->whereHas('user', function ($q) use ($port) {
                 $q->where('port', $port);
             });
         }
 
-        if (!empty($user_id)) {
-            $query->where('user_id', $user_id);
+        if ($user_id) {
+            $query->where('user_id', intval($user_id));
         }
 
-        if (!empty($username)) {
+        if ($username) {
             $query->whereHas('user', function ($q) use ($username) {
                 $q->where('username', 'like', '%' . $username . '%');
             });
         }
 
+        if ($nodeId) {
+            $query->where('node_id', $nodeId);
+        }
+
         // 已使用流量
         $view['totalTraffic'] = flowAutoShow($query->sum('u') + $query->sum('d'));
 
@@ -1139,6 +1146,7 @@ class AdminController extends Controller
         }
 
         $view['list'] = $list;
+        $view['nodeList'] = SsNode::query()->where('status', 1)->orderBy('sort', 'desc')->orderBy('id', 'desc')->get();
 
         return Response::view('admin.trafficLog', $view);
     }
@@ -2365,22 +2373,16 @@ EOF;
     public function userOnlineIPList(Request $request)
     {
         $username = trim($request->get('username'));
-        $status = $request->get('status');
         $port = intval($request->get('port'));
         $wechat = trim($request->get('wechat'));
         $qq = trim($request->get('qq'));
-        $enable = intval($request->get('enable'));
 
-        $query = User::query();
+        $query = User::query()->where('status', '>=', 0)->where('enable', 1);
 
         if ($username) {
             $query->where('username', 'like', '%' . $username . '%');
         }
 
-        if ($status != '') {
-            $query->where('status', intval($status));
-        }
-
         if (!empty($wechat)) {
             $query->where('wechat', 'like', '%' . $wechat . '%');
         }
@@ -2389,19 +2391,15 @@ EOF;
             $query->where('qq', 'like', '%' . $qq . '%');
         }
 
-        if (!empty($port)) {
-            $query->where('port', intval($port));
-        }
-
-        if ($enable != '') {
-            $query->where('enable', intval($enable));
+        if ($port) {
+            $query->where('port', $port);
         }
 
         $userList = $query->paginate(15)->appends($request->except('page'));
         if (!$userList->isEmpty()) {
             foreach ($userList as &$user) {
-                // 最近10条在线IP记录,如果后端设置为60秒上报一次,则为10分钟内的在线IP
-                $user->onlineIPList = SsNodeIp::query()->with(['node', 'user'])->where('type', 'tcp')->where('port', $user->port)->where('created_at', '>=', strtotime("-10 minutes"))->orderBy('id', 'desc')->limit(10)->get();
+                // 最近5条在线IP记录,如果后端设置为60秒上报一次,则为10分钟内的在线IP
+                $user->onlineIPList = SsNodeIp::query()->with(['node'])->where('type', 'tcp')->where('port', $user->port)->where('created_at', '>=', strtotime("-10 minutes"))->orderBy('id', 'desc')->limit(5)->get();
             }
         }
 
@@ -2507,6 +2505,73 @@ EOF;
         return Response::view('admin.emailLog', $view);
     }
 
+    // 在线IP监控(实时)
+    public function onlineIPMonitor(Request $request)
+    {
+        $ip = trim($request->get('ip'));
+        $username = trim($request->get('username'));
+        $port = intval($request->get('port'));
+        $nodeId = intval($request->get('nodeId'));
+        $userId = intval($request->get('id'));
+
+        $query = SsNodeIp::query()->with(['node', 'user'])->where('type', 'tcp')->where('created_at', '>=', strtotime("-120 seconds"));
+
+        if ($ip) {
+            $query->where('ip', $ip);
+        }
+
+        if ($username) {
+            $query->whereHas('user', function ($q) use ($username) {
+                $q->where('username', 'like', '%' . $username . '%');
+            });
+        }
+
+        if ($port) {
+            $query->whereHas('user', function ($q) use ($port) {
+                $q->where('port', $port);
+            });
+        }
+
+        if ($nodeId) {
+            $query->whereHas('node', function ($q) use ($nodeId) {
+                $q->where('id', $nodeId);
+            });
+        }
+
+        if ($userId) {
+            $query->whereHas('user', function ($q) use ($userId) {
+                $q->where('id', $userId);
+            });
+        }
+
+        $list = $query->groupBy('port')->orderBy('id', 'desc')->paginate(20)->appends($request->except('page'));
+
+        foreach ($list as $vo) {
+            // 跳过上报多IP的
+            if (strpos($vo->ip, ',') !== false) {
+                continue;
+            }
+
+            $ipInfo = QQWry::ip($vo->ip);
+            if (isset($ipInfo['error'])) {
+                // 用IPIP的库再试一下
+                $ipip = IPIP::ip($vo->ip);
+                $ipInfo = [
+                    'country'  => $ipip['country_name'],
+                    'province' => $ipip['region_name'],
+                    'city'     => $ipip['city_name']
+                ];
+            }
+
+            $vo->ipInfo = $ipInfo['country'] . ' ' . $ipInfo['province'] . ' ' . $ipInfo['city'];
+        }
+
+        $view['list'] = $list;
+        $view['nodeList'] = SsNode::query()->where('status', 1)->orderBy('sort', 'desc')->orderBy('id', 'desc')->get();
+
+        return Response::view('admin.onlineIPMonitor', $view);
+    }
+
     // 生成用户标签
     private function makeUserLabels($userId, $labels)
     {

+ 17 - 6
app/Http/Controllers/AuthController.php

@@ -70,14 +70,25 @@ class AuthController extends Controller
                 Session::flash('errorMsg', '用户名或密码错误');
 
                 return Redirect::back()->withInput();
-            } elseif (!Auth::user()->is_admin && Auth::user()->status < 0) {
-                Session::flash('errorMsg', '账号已禁用');
+            }
 
-                return Redirect::back();
-            } elseif (Auth::user()->status == 0 && self::$systemConfig['is_active_register'] && Auth::user()->is_admin == 0) {
-                Session::flash('errorMsg', '账号未激活,请点击<a href="/activeUser?username=' . Auth::user()->username . '" target="_blank"><span style="color:#000">【激活账号】</span></a>');
+            // 只校验普通用户
+            if (!Auth::user()->is_admin) {
+                if (Auth::user()->status < 0) {
+                    Session::flash('errorMsg', '账号已禁用');
 
-                return Redirect::back()->withInput();
+                    Auth::logout(); // 强制销毁会话,因为Auth::attempt的时候会产生会话
+
+                    return Redirect::back()->withInput();
+                }
+
+                if (Auth::user()->status == 0 && self::$systemConfig['is_active_register']) {
+                    Session::flash('errorMsg', '账号未激活,请点击<a href="/activeUser?username=' . Auth::user()->username . '" target="_blank"><span style="color:#000">【激活账号】</span></a>');
+
+                    Auth::logout(); // 强制销毁会话,因为Auth::attempt的时候会产生会话
+
+                    return Redirect::back()->withInput();
+                }
             }
 
             // 登录送积分

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

@@ -51,7 +51,6 @@ class TicketController extends Controller
             $obj->ticket_id = $id;
             $obj->user_id = Auth::user()->id;
             $obj->content = $content;
-            $obj->created_at = date('Y-m-d H:i:s');
             $obj->save();
 
             if ($obj->id) {

+ 13 - 9
app/Http/Controllers/UserController.php

@@ -381,7 +381,6 @@ class UserController extends Controller
         $obj->title = $title;
         $obj->content = $content;
         $obj->status = 0;
-        $obj->created_at = date('Y-m-d H:i:s');
         $obj->save();
 
         if ($obj->id) {
@@ -411,6 +410,11 @@ class UserController extends Controller
     {
         $id = intval($request->get('id'));
 
+        $ticket = Ticket::query()->with('user')->where('id', $id)->first();
+        if (empty($ticket) || $ticket->user_id != Auth::user()->id) {
+            return Redirect::to('tickets');
+        }
+
         if ($request->method() == 'POST') {
             $content = clean($request->get('content'));
             $content = str_replace("eval", "", str_replace("atob", "", $content));
@@ -424,11 +428,12 @@ class UserController extends Controller
             $obj->ticket_id = $id;
             $obj->user_id = Auth::user()->id;
             $obj->content = $content;
-            $obj->created_at = date('Y-m-d H:i:s');
             $obj->save();
 
             if ($obj->id) {
-                $ticket = Ticket::query()->where('id', $id)->first();
+                // 重新打开工单
+                $ticket->status = 0;
+                $ticket->save();
 
                 $title = "工单回复提醒";
                 $content = "标题:【" . $ticket->title . "】<br>用户回复:" . $content;
@@ -450,11 +455,6 @@ class UserController extends Controller
                 return Response::json(['status' => 'fail', 'data' => '', 'message' => '回复失败']);
             }
         } else {
-            $ticket = Ticket::query()->where('id', $id)->with('user')->first();
-            if (empty($ticket) || $ticket->user_id != Auth::user()->id) {
-                return Redirect::to('tickets');
-            }
-
             $view['ticket'] = $ticket;
             $view['replyList'] = TicketReply::query()->where('ticket_id', $id)->with('user')->orderBy('id', 'asc')->get();
 
@@ -469,6 +469,8 @@ class UserController extends Controller
 
         $ret = Ticket::query()->where('id', $id)->where('user_id', Auth::user()->id)->update(['status' => 2]);
         if ($ret) {
+            ServerChan::send('工单关闭提醒', '工单:ID' . $id . '客户已手动关闭');
+
             return Response::json(['status' => 'success', 'data' => '', 'message' => '关闭成功']);
         } else {
             return Response::json(['status' => 'fail', 'data' => '', 'message' => '关闭失败']);
@@ -525,11 +527,13 @@ class UserController extends Controller
             return Response::json(['status' => 'fail', 'data' => '', 'message' => '该优惠券已使用,请换一个试试']);
         } elseif ($coupon->status == 2) {
             return Response::json(['status' => 'fail', 'data' => '', 'message' => '该优惠券已失效,请换一个试试']);
-        } elseif ($coupon->available_start > time() || $coupon->available_end < time()) {
+        } elseif ($coupon->available_end < time()) {
             $coupon->status = 2;
             $coupon->save();
 
             return Response::json(['status' => 'fail', 'data' => '', 'message' => '该优惠券已失效,请换一个试试']);
+        } elseif ($coupon->available_start > time()) {
+            return Response::json(['status' => 'fail', 'data' => '', 'message' => '该优惠券尚不可用,请换一个试试']);
         }
 
         $data = [

+ 8 - 8
app/Http/Models/Device.php

@@ -21,13 +21,13 @@ class Device extends Model
     {
         switch ($this->attributes['type']) {
             case 1:
-                $type_label = '<span class="label label-info"> Shadowsocks(R) </span>';
+                $type_label = '<span class="label label-danger"> Shadowsocks(R) </span>';
                 break;
             case 2:
-                $type_label = '<span class="label label-info"> V2Ray </span>';
+                $type_label = '<span class="label label-danger"> V2Ray </span>';
                 break;
             default:
-                $type_label = '<span class="label label-info"> 其他 </span>';
+                $type_label = '<span class="label label-default"> 其他 </span>';
         }
 
         return $type_label;
@@ -37,19 +37,19 @@ class Device extends Model
     {
         switch ($this->attributes['platform']) {
             case 1:
-                $platform_label = '<i class="fa fa-apple"></i>';
+                $platform_label = '<i class="fa fa-apple"></i> iOS';
                 break;
             case 2:
-                $platform_label = '<i class="fa fa-android"></i>';
+                $platform_label = '<i class="fa fa-android"></i> Android';
                 break;
             case 3:
-                $platform_label = '<i class="fa fa-apple"></i>';
+                $platform_label = '<i class="fa fa-apple"></i> Mac';
                 break;
             case 4:
-                $platform_label = '<i class="fa fa-windows"></i>';
+                $platform_label = '<i class="fa fa-windows"></i> Windows';
                 break;
             case 5:
-                $platform_label = '<i class="fa fa-linux"></i>';
+                $platform_label = '<i class="fa fa-linux"></i> Linux';
                 break;
             case 0:
             default:

+ 0 - 1
app/Http/Models/EmailLog.php

@@ -16,6 +16,5 @@ class EmailLog extends Model
 {
     protected $table = 'email_log';
     protected $primaryKey = 'id';
-    public $timestamps = false;
 
 }

+ 0 - 1
app/Http/Models/Ticket.php

@@ -16,7 +16,6 @@ class Ticket extends Model
 {
     protected $table = 'ticket';
     protected $primaryKey = 'id';
-    public $timestamps = false;
 
     public function User()
     {

+ 0 - 1
app/Http/Models/TicketReply.php

@@ -16,7 +16,6 @@ class TicketReply extends Model
 {
     protected $table = 'ticket_reply';
     protected $primaryKey = 'id';
-    public $timestamps = false;
 
     public function User()
     {

+ 2 - 2
app/Http/Models/UserTrafficLog.php

@@ -20,13 +20,13 @@ class UserTrafficLog extends Model
     public $timestamps = false;
 
     // 关联账号
-    public function User()
+    public function user()
     {
         return $this->belongsTo(User::class, 'user_id', 'id');
     }
 
     // 关联节点
-    public function SsNode()
+    public function node()
     {
         return $this->belongsTo(SsNode::class, 'node_id', 'id');
     }

+ 1 - 1
app/Mail/activeUser.php

@@ -7,7 +7,7 @@ use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Contracts\Queue\ShouldQueue;
 
-class activeUser extends Mailable
+class activeUser extends Mailable implements ShouldQueue
 {
     use Queueable, SerializesModels;
 

+ 1 - 1
app/Mail/closeTicket.php

@@ -7,7 +7,7 @@ use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Contracts\Queue\ShouldQueue;
 
-class closeTicket extends Mailable
+class closeTicket extends Mailable implements ShouldQueue
 {
     use Queueable, SerializesModels;
 

+ 1 - 1
app/Mail/newTicket.php

@@ -7,7 +7,7 @@ use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Contracts\Queue\ShouldQueue;
 
-class newTicket extends Mailable
+class newTicket extends Mailable implements ShouldQueue
 {
     use Queueable, SerializesModels;
 

+ 2 - 2
app/Mail/nodeCrashWarning.php

@@ -7,7 +7,7 @@ use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Contracts\Queue\ShouldQueue;
 
-class nodeCrashWarning extends Mailable
+class nodeCrashWarning extends Mailable implements ShouldQueue
 {
     use Queueable, SerializesModels;
 
@@ -22,7 +22,7 @@ class nodeCrashWarning extends Mailable
 
     public function build()
     {
-        return $this->view('emails.nodeCrashWarning')->subject('节点宕机警告')->with([
+        return $this->view('emails.nodeCrashWarning')->subject('节点离线警告')->with([
             'nodeName'   => $this->nodeName,
             'nodeServer' => $this->nodeServer
         ]);

+ 1 - 1
app/Mail/replyTicket.php

@@ -7,7 +7,7 @@ use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Contracts\Queue\ShouldQueue;
 
-class replyTicket extends Mailable
+class replyTicket extends Mailable implements ShouldQueue
 {
     use Queueable, SerializesModels;
 

+ 1 - 1
app/Mail/resetPassword.php

@@ -7,7 +7,7 @@ use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Contracts\Queue\ShouldQueue;
 
-class resetPassword extends Mailable
+class resetPassword extends Mailable implements ShouldQueue
 {
     use Queueable, SerializesModels;
 

+ 1 - 1
app/Mail/sendUserInfo.php

@@ -7,7 +7,7 @@ use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Contracts\Queue\ShouldQueue;
 
-class sendUserInfo extends Mailable
+class sendUserInfo extends Mailable implements ShouldQueue
 {
     use Queueable, SerializesModels;
 

+ 1 - 1
app/Mail/sendVerifyCode.php

@@ -7,7 +7,7 @@ use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Contracts\Queue\ShouldQueue;
 
-class sendVerifyCode extends Mailable
+class sendVerifyCode extends Mailable implements ShouldQueue
 {
     use Queueable, SerializesModels;
 

+ 1 - 1
app/Mail/userExpireWarning.php

@@ -7,7 +7,7 @@ use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Contracts\Queue\ShouldQueue;
 
-class userExpireWarning extends Mailable
+class userExpireWarning extends Mailable implements ShouldQueue
 {
     use Queueable, SerializesModels;
 

+ 1 - 1
app/Mail/userExpireWarningToday.php

@@ -7,7 +7,7 @@ use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Contracts\Queue\ShouldQueue;
 
-class userExpireWarningToday extends Mailable
+class userExpireWarningToday extends Mailable implements ShouldQueue
 {
     use Queueable, SerializesModels;
 

+ 1 - 1
app/Mail/userTrafficWarning.php

@@ -7,7 +7,7 @@ use Illuminate\Mail\Mailable;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Contracts\Queue\ShouldQueue;
 
-class userTrafficWarning extends Mailable
+class userTrafficWarning extends Mailable implements ShouldQueue
 {
     use Queueable, SerializesModels;
 

+ 0 - 35
database/migrations/2014_10_12_000000_create_users_table.php

@@ -1,35 +0,0 @@
-<?php
-
-use Illuminate\Support\Facades\Schema;
-use Illuminate\Database\Schema\Blueprint;
-use Illuminate\Database\Migrations\Migration;
-
-class CreateUsersTable extends Migration
-{
-    /**
-     * Run the migrations.
-     *
-     * @return void
-     */
-    public function up()
-    {
-        Schema::create('users', function (Blueprint $table) {
-            $table->increments('id');
-            $table->string('name');
-            $table->string('email')->unique();
-            $table->string('password');
-            $table->rememberToken();
-            $table->timestamps();
-        });
-    }
-
-    /**
-     * Reverse the migrations.
-     *
-     * @return void
-     */
-    public function down()
-    {
-        Schema::dropIfExists('users');
-    }
-}

+ 0 - 32
database/migrations/2014_10_12_100000_create_password_resets_table.php

@@ -1,32 +0,0 @@
-<?php
-
-use Illuminate\Support\Facades\Schema;
-use Illuminate\Database\Schema\Blueprint;
-use Illuminate\Database\Migrations\Migration;
-
-class CreatePasswordResetsTable extends Migration
-{
-    /**
-     * Run the migrations.
-     *
-     * @return void
-     */
-    public function up()
-    {
-        Schema::create('password_resets', function (Blueprint $table) {
-            $table->string('email')->index();
-            $table->string('token');
-            $table->timestamp('created_at')->nullable();
-        });
-    }
-
-    /**
-     * Reverse the migrations.
-     *
-     * @return void
-     */
-    public function down()
-    {
-        Schema::dropIfExists('password_resets');
-    }
-}

+ 0 - 102
database/migrations/2019_01_17_100504_create_permission_tables.php

@@ -1,102 +0,0 @@
-<?php
-
-use Illuminate\Support\Facades\Schema;
-use Illuminate\Database\Schema\Blueprint;
-use Illuminate\Database\Migrations\Migration;
-
-class CreatePermissionTables extends Migration
-{
-    /**
-     * Run the migrations.
-     *
-     * @return void
-     */
-    public function up()
-    {
-        $tableNames = config('permission.table_names');
-        $columnNames = config('permission.column_names');
-
-        Schema::create($tableNames['permissions'], function (Blueprint $table) {
-            $table->increments('id');
-            $table->string('name');
-            $table->string('guard_name');
-            $table->timestamps();
-        });
-
-        Schema::create($tableNames['roles'], function (Blueprint $table) {
-            $table->increments('id');
-            $table->string('name');
-            $table->string('guard_name');
-            $table->timestamps();
-        });
-
-        Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames) {
-            $table->unsignedInteger('permission_id');
-
-            $table->string('model_type');
-            $table->unsignedBigInteger($columnNames['model_morph_key']);
-            $table->index([$columnNames['model_morph_key'], 'model_type', ]);
-
-            $table->foreign('permission_id')
-                ->references('id')
-                ->on($tableNames['permissions'])
-                ->onDelete('cascade');
-
-            $table->primary(['permission_id', $columnNames['model_morph_key'], 'model_type'],
-                    'model_has_permissions_permission_model_type_primary');
-        });
-
-        Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames) {
-            $table->unsignedInteger('role_id');
-
-            $table->string('model_type');
-            $table->unsignedBigInteger($columnNames['model_morph_key']);
-            $table->index([$columnNames['model_morph_key'], 'model_type', ]);
-
-            $table->foreign('role_id')
-                ->references('id')
-                ->on($tableNames['roles'])
-                ->onDelete('cascade');
-
-            $table->primary(['role_id', $columnNames['model_morph_key'], 'model_type'],
-                    'model_has_roles_role_model_type_primary');
-        });
-
-        Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames) {
-            $table->unsignedInteger('permission_id');
-            $table->unsignedInteger('role_id');
-
-            $table->foreign('permission_id')
-                ->references('id')
-                ->on($tableNames['permissions'])
-                ->onDelete('cascade');
-
-            $table->foreign('role_id')
-                ->references('id')
-                ->on($tableNames['roles'])
-                ->onDelete('cascade');
-
-            $table->primary(['permission_id', 'role_id']);
-            
-            app('cache')
-                ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
-                ->forget(config('permission.cache.key'));
-        });
-    }
-
-    /**
-     * Reverse the migrations.
-     *
-     * @return void
-     */
-    public function down()
-    {
-        $tableNames = config('permission.table_names');
-
-        Schema::drop($tableNames['role_has_permissions']);
-        Schema::drop($tableNames['model_has_roles']);
-        Schema::drop($tableNames['model_has_permissions']);
-        Schema::drop($tableNames['roles']);
-        Schema::drop($tableNames['permissions']);
-    }
-}

+ 9 - 0
queue.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+ps -ef | grep queue:work | grep -v grep
+if [ $? -ne 0 ]
+then
+    echo "start queue process successfully....."
+    nohup php artisan queue:work database --queue=default --timeout=60 --sleep=5 --tries=3 >> ./queue.log 2>&1 &
+else
+    echo "queue is running....."
+fi

+ 6 - 4
resources/views/admin/addUser.blade.php

@@ -280,18 +280,20 @@
                                                 <label for="speed_limit_per_con" class="col-md-3 control-label">单连接限速</label>
                                                 <div class="col-md-8">
                                                     <div class="input-group">
-                                                        <input type="text" class="form-control" name="speed_limit_per_con" value="204800" id="speed_limit_per_con" autocomplete="off" />
-                                                        <span class="input-group-addon">KB</span>
+                                                        <input type="text" class="form-control" name="speed_limit_per_con" value="10737418240" id="speed_limit_per_con" autocomplete="off" />
+                                                        <span class="input-group-addon">Byte</span>
                                                     </div>
+                                                    <span class="help-block"> 为 0 时不限速 </span>
                                                 </div>
                                             </div>
                                             <div class="form-group">
                                                 <label for="speed_limit_per_user" class="col-md-3 control-label">单用户限速</label>
                                                 <div class="col-md-8">
                                                     <div class="input-group">
-                                                        <input type="text" class="form-control" name="speed_limit_per_user" value="204800" id="speed_limit_per_user" autocomplete="off" />
-                                                        <span class="input-group-addon">KB</span>
+                                                        <input type="text" class="form-control" name="speed_limit_per_user" value="10737418240" id="speed_limit_per_user" autocomplete="off" />
+                                                        <span class="input-group-addon">Byte</span>
                                                     </div>
+                                                    <span class="help-block"> 为 0 时不限速 </span>
                                                 </div>
                                             </div>
                                             <hr>

+ 10 - 10
resources/views/admin/config.blade.php

@@ -36,8 +36,8 @@
                                         <button class="btn sbold blue" data-toggle="modal" data-target="#add_config_modal"> 新增 <i class="fa fa-plus"></i> </button>
                                     </div>
                                 </div>
-                                <div class="table-scrollable">
-                                    <table class="table table-striped table-bordered table-hover table-checkable order-column">
+                                <div class="table-scrollable table-scrollable-borderless">
+                                    <table class="table table-hover table-light table-checkable">
                                         <thead>
                                             <tr>
                                                 <th style="width: 50%;"> 名称 </th>
@@ -74,8 +74,8 @@
                                         <button class="btn sbold blue" data-toggle="modal" data-target="#add_config_modal"> 新增 <i class="fa fa-plus"></i> </button>
                                     </div>
                                 </div>
-                                <div class="table-scrollable">
-                                    <table class="table table-striped table-bordered table-hover table-checkable order-column">
+                                <div class="table-scrollable table-scrollable-borderless">
+                                    <table class="table table-hover table-light table-checkable">
                                         <thead>
                                             <tr>
                                                 <th style="width: 50%;"> 名称 </th>
@@ -112,8 +112,8 @@
                                         <button class="btn sbold blue" data-toggle="modal" data-target="#add_config_modal"> 新增 <i class="fa fa-plus"></i> </button>
                                     </div>
                                 </div>
-                                <div class="table-scrollable">
-                                    <table class="table table-striped table-bordered table-hover table-checkable order-column">
+                                <div class="table-scrollable table-scrollable-borderless">
+                                    <table class="table table-hover table-light table-checkable">
                                         <thead>
                                         <tr>
                                             <th style="width: 50%;"> 名称 </th>
@@ -150,8 +150,8 @@
                                         <button class="btn sbold blue" data-toggle="modal" data-target="#add_level_modal"> 新增 <i class="fa fa-plus"></i> </button>
                                     </div>
                                 </div>
-                                <div class="table-scrollable">
-                                    <table class="table table-striped table-bordered table-hover table-checkable order-column">
+                                <div class="table-scrollable table-scrollable-borderless">
+                                    <table class="table table-hover table-light table-checkable">
                                         <thead>
                                             <tr>
                                                 <th style="width: 35%;"> 等级 </th>
@@ -188,8 +188,8 @@
                                         <button class="btn sbold blue" data-toggle="modal" data-target="#add_country_modal"> 新增 <i class="fa fa-plus"></i> </button>
                                     </div>
                                 </div>
-                                <div class="table-scrollable">
-                                    <table class="table table-striped table-bordered table-hover table-checkable order-column">
+                                <div class="table-scrollable table-scrollable-borderless">
+                                    <table class="table table-hover table-light table-checkable">
                                         <thead>
                                             <tr>
                                                 <th style="width: 20%;"> 图标 </th>

+ 5 - 3
resources/views/admin/editUser.blade.php

@@ -217,7 +217,7 @@
                                             </div>
                                             <hr>
                                             <div class="form-group">
-                                                <label for="speed_limit_per_user" class="col-md-3 control-label">邀请人</label>
+                                                <label for="referral_uid" class="col-md-3 control-label">邀请人</label>
                                                 <div class="col-md-8">
                                                     <p class="form-control-static"> {{empty($user->referral) ? '无邀请人' : $user->referral->username}} </p>
                                                 </div>
@@ -328,8 +328,9 @@
                                                 <div class="col-md-8">
                                                     <div class="input-group">
                                                         <input type="text" class="form-control" name="speed_limit_per_con" value="{{$user->speed_limit_per_con}}" id="speed_limit_per_con" autocomplete="off">
-                                                        <span class="input-group-addon">KB</span>
+                                                        <span class="input-group-addon">Byte</span>
                                                     </div>
+                                                    <span class="help-block"> 为 0 时不限速 </span>
                                                 </div>
                                             </div>
                                             <div class="form-group">
@@ -337,8 +338,9 @@
                                                 <div class="col-md-8">
                                                     <div class="input-group">
                                                         <input type="text" class="form-control" name="speed_limit_per_user" value="{{$user->speed_limit_per_user}}" id="speed_limit_per_user" autocomplete="off">
-                                                        <span class="input-group-addon">KB</span>
+                                                        <span class="input-group-addon">Byte</span>
                                                     </div>
+                                                    <span class="help-block"> 为 0 时不限速 </span>
                                                 </div>
                                             </div>
                                             <hr>

+ 7 - 1
resources/views/admin/layouts.blade.php

@@ -154,7 +154,7 @@
                         <span class="title">文章管理</span>
                     </a>
                 </li>
-                <li class="nav-item {{in_array(Request::path(), ['admin/userList', 'admin/addUser', 'admin/editUser', 'admin/userOrderList', 'admin/userBalanceLogList', 'admin/userTrafficLogList', 'admin/userRebateList', 'admin/userBanLogList', 'admin/export', 'admin/userMonitor', 'admin/subscribeLog', 'admin/userOnlineIPList']) ? 'active open' : ''}}">
+                <li class="nav-item {{in_array(Request::path(), ['admin/userList', 'admin/addUser', 'admin/editUser', 'admin/userOrderList', 'admin/userBalanceLogList', 'admin/userTrafficLogList', 'admin/userRebateList', 'admin/userBanLogList', 'admin/export', 'admin/userMonitor', 'admin/subscribeLog', 'admin/userOnlineIPList', 'admin/onlineIPMonitor']) ? 'active open' : ''}}">
                     <a href="javascript:;" class="nav-link nav-toggle">
                         <i class="fa fa-users"></i>
                         <span class="title">用户管理</span>
@@ -173,6 +173,12 @@
                                 <span class="title">用户在线IP</span>
                             </a>
                         </li>
+                        <li class="nav-item {{in_array(Request::path(), ['admin/onlineIPMonitor']) ? 'active open' : ''}}">
+                            <a href="{{url('admin/onlineIPMonitor')}}" class="nav-link">
+                                <i class="icon-list"></i>
+                                <span class="title">在线IP监控</span>
+                            </a>
+                        </li>
                         <li class="nav-item {{in_array(Request::path(), ['admin/userBalanceLogList']) ? 'active open' : ''}}">
                             <a href="{{url('admin/userBalanceLogList')}}" class="nav-link ">
                                 <i class="fa fa-money"></i>

+ 1 - 1
resources/views/admin/nodeList.blade.php

@@ -11,7 +11,7 @@
             <div class="col-md-12">
                 <div class="note note-info">
                     <p>节点绑定域名推荐使用<a href="https://www.namesilo.com/?rid=326ec20pa" target="_blank">Namesilo</a>,本面板支持自动更新DNS <a href="https://github.com/ssrpanel/SSRPanel/wiki/%E8%B4%AD%E4%B9%B0%E5%9F%9F%E5%90%8D%EF%BC%88%E8%87%AA%E5%B8%A6%E9%9A%90%E7%A7%81%E4%BF%9D%E6%8A%A4%EF%BC%89" target="_blank" style="color:red;">[购买域名]</a></p>
-                    <p>状态显示为'宕机':1.ShadowsocksR进程挂掉;2.节点和数据库之间的时区不一致或者通信延迟过高;3.服务器真的宕机。<a href="https://github.com/ssrpanel/ssrpanel/wiki/VPS%E6%8E%A8%E8%8D%90&%E8%B4%AD%E4%B9%B0%E7%BB%8F%E9%AA%8C" target="_blank" style="color:red;">[VPS推荐]</a></p>
+                    <p>状态显示为'离线':1.后端进程挂掉;2.节点和数据库之间的时区不一致或者通信延迟过高;3.服务器真的宕机。<a href="https://github.com/ssrpanel/ssrpanel/wiki/VPS%E6%8E%A8%E8%8D%90&%E8%B4%AD%E4%B9%B0%E7%BB%8F%E9%AA%8C" target="_blank" style="color:red;">[VPS推荐]</a></p>
                     <p>务必检查各节点服务器的时间是否同步。<a href="https://github.com/ssrpanel/SSRPanel/wiki/%E5%8D%95%E7%AB%AF%E5%8F%A3%E5%A4%9A%E7%94%A8%E6%88%B7%E7%9A%84%E5%9D%91" target="_blank" style="color:red;">[时间校准]</a></p>
                 </div>
             </div>

+ 123 - 0
resources/views/admin/onlineIPMonitor.blade.php

@@ -0,0 +1,123 @@
+@extends('admin.layouts')
+@section('css')
+    <link href="/assets/global/plugins/datatables/datatables.min.css" rel="stylesheet" type="text/css" />
+    <link href="/assets/global/plugins/datatables/plugins/bootstrap/datatables.bootstrap.css" rel="stylesheet" type="text/css" />
+    <style type="text/css">
+        input,select {
+            margin-bottom: 5px;
+        }
+    </style>
+@endsection
+@section('content')
+    <!-- BEGIN CONTENT BODY -->
+    <div class="page-content" style="padding-top:0;">
+        <!-- BEGIN PAGE BASE CONTENT -->
+        <div class="row">
+            <div class="col-md-12">
+                <!-- BEGIN EXAMPLE TABLE PORTLET-->
+                <div class="portlet light bordered">
+                    <div class="portlet-title">
+                        <div class="caption font-dark">
+                            <span class="caption-subject bold uppercase"> 在线IP监控<small>(实时)</small> </span>
+                        </div>
+                    </div>
+                    <div class="portlet-body">
+                        <div class="row">
+                            <div class="col-md-3 col-sm-4 col-xs-12">
+                                <input type="text" class="col-md-4 col-sm-4 col-xs-12 form-control" name="id" value="{{Request::get('id')}}" id="id" placeholder="用户ID" onkeydown="if(event.keyCode==13){doSearch();}">
+                            </div>
+                            <div class="col-md-3 col-sm-4 col-xs-12">
+                                <input type="text" class="col-md-4 col-sm-4 col-xs-12 form-control" name="ip" value="{{Request::get('ip')}}" id="ip" placeholder="IP" onkeydown="if(event.keyCode==13){doSearch();}">
+                            </div>
+                            <div class="col-md-3 col-sm-4 col-xs-12">
+                                <input type="text" class="col-md-4 col-sm-4 col-xs-12 form-control" name="username" value="{{Request::get('username')}}" id="username" placeholder="用户名" onkeydown="if(event.keyCode==13){doSearch();}">
+                            </div>
+                            <div class="col-md-3 col-sm-4 col-xs-12">
+                                <input type="text" class="col-md-4 col-sm-4 col-xs-12 form-control" name="port" value="{{Request::get('port')}}" id="port" placeholder="端口" onkeydown="if(event.keyCode==13){doSearch();}">
+                            </div>
+                            <div class="col-md-3 col-sm-4 col-xs-12">
+                                <select class="form-control" name="nodeId" id="nodeId" onChange="doSearch()">
+                                    <option value="" @if(Request::get('nodeId') == '') selected @endif>选择节点</option>
+                                    @foreach($nodeList as $node)
+                                        <option value="{{$node->id}}" @if(Request::get('nodeId') == $node->id) selected @endif>{{$node->name}}</option>
+                                    @endforeach
+                                </select>
+                            </div>
+                            <div class="col-md-3 col-sm-4 col-xs-12">
+                                <button type="button" class="btn blue" onclick="doSearch();">查询</button>
+                                <button type="button" class="btn grey" onclick="doReset();">重置</button>
+                            </div>
+                        </div>
+                        <div class="table-scrollable table-scrollable-borderless">
+                            <table class="table table-hover table-light">
+                                <thead>
+                                    <tr>
+                                        <th> # </th>
+                                        <th> 时间 </th>
+                                        <th> 类型 </th>
+                                        <th> 节点 </th>
+                                        <th> 用户 </th>
+                                        <th> IP </th>
+                                        <th> 归属地 </th>
+                                    </tr>
+                                </thead>
+                                <tbody>
+                                    @if ($list->isEmpty())
+                                        <tr>
+                                            <td colspan="7" style="text-align: center;">暂无数据</td>
+                                        </tr>
+                                    @else
+                                        @foreach($list as $vo)
+                                            <tr>
+                                                <td>{{$vo->id}}</td>
+                                                <td>{{$vo->created_at}}</td>
+                                                <td>{{$vo->type}}</td>
+                                                <td>{{$vo->node ? $vo->node->name : '【节点已删除】'}}</td>
+                                                <td>{{$vo->user ? $vo->user->username : '【用户已删除】'}}</td>
+                                                <td><a href="https://www.ipip.net/ip/{{$vo->ip}}.html" target="_blank">{{$vo->ip}}</a></td>
+                                                <td>{{$vo->ipInfo}}</td>
+                                            </tr>
+                                        @endforeach
+                                    @endif
+                                </tbody>
+                            </table>
+                        </div>
+                        <div class="row">
+                            <div class="col-md-4 col-sm-4">
+                                <div class="dataTables_info" role="status" aria-live="polite">共 {{$list->total()}} 个账号</div>
+                            </div>
+                            <div class="col-md-8 col-sm-8">
+                                <div class="dataTables_paginate paging_bootstrap_full_number pull-right">
+                                    {{ $list->links() }}
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <!-- END EXAMPLE TABLE PORTLET-->
+            </div>
+        </div>
+        <!-- END PAGE BASE CONTENT -->
+    </div>
+    <!-- END CONTENT BODY -->
+@endsection
+@section('script')
+    <script src="/assets/global/plugins/clipboardjs/clipboard.min.js" type="text/javascript"></script>
+    <script type="text/javascript">
+        // 搜索
+        function doSearch() {
+            var id = $("#id").val();
+            var ip = $("#ip").val();
+            var username = $("#username").val();
+            var port = $("#port").val();
+            var nodeId = $("#nodeId option:checked").val();
+
+            window.location.href = '{{url('admin/onlineIPMonitor')}}' + '?id=' + id + '&ip=' + ip + '&username=' + username + '&port=' + port + '&nodeId=' + nodeId;
+        }
+
+        // 重置
+        function doReset() {
+            window.location.href = '{{url('admin/onlineIPMonitor')}}';
+        }
+    </script>
+@endsection

+ 9 - 19
resources/views/admin/system.blade.php

@@ -726,15 +726,10 @@
                                                     </div>
                                                     <div class="form-group">
                                                         <div class="col-md-6 col-sm-6 col-xs-12">
-                                                            <label for="is_node_crash_warning"
-                                                                   class="col-md-3 control-label">节点宕机提醒</label>
+                                                            <label for="is_node_crash_warning" class="col-md-3 control-label">节点离线提醒</label>
                                                             <div class="col-md-9">
-                                                                <input type="checkbox" class="make-switch"
-                                                                       @if($is_node_crash_warning) checked
-                                                                       @endif id="is_node_crash_warning"
-                                                                       data-on-color="success" data-off-color="danger"
-                                                                       data-on-text="启用" data-off-text="关闭">
-                                                                <span class="help-block"> 启用后如果节点宕机则发出提醒邮件 </span>
+                                                                <input type="checkbox" class="make-switch" @if($is_node_crash_warning) checked @endif id="is_node_crash_warning" data-on-color="success" data-off-color="danger" data-on-text="启用" data-off-text="关闭">
+                                                                <span class="help-block"> 启用后如果节点离线则发出提醒邮件 </span>
                                                             </div>
                                                         </div>
                                                         <div class="col-md-6 col-sm-6 col-xs-12">
@@ -752,7 +747,7 @@
                                                                                 onclick="setCrashWarningEmail()">修改</button>
                                                                     </span>
                                                                 </div>
-                                                                <span class="help-block"> 填写此值则节点宕机、用户回复工单都会自动提醒 </span>
+                                                                <span class="help-block"> 填写此值则节点离线、用户回复工单都会自动提醒 </span>
                                                             </div>
                                                         </div>
                                                     </div>
@@ -791,13 +786,8 @@
                                                         <div class="col-md-6 col-sm-6 col-xs-12">
                                                             <label for="is_server_chan" class="col-md-3 control-label">ServerChan</label>
                                                             <div class="col-md-9">
-                                                                <input type="checkbox" class="make-switch"
-                                                                       @if($is_server_chan) checked
-                                                                       @endif id="is_server_chan"
-                                                                       data-on-color="success" data-off-color="danger"
-                                                                       data-on-text="启用" data-off-text="关闭">
-                                                                <span class="help-block"> 推送节点宕机提醒、用户流量异常警告、节点使用报告(<a
-                                                                            href="http://sc.ftqq.com" target="_blank">绑定微信</a>) </span>
+                                                                <input type="checkbox" class="make-switch" @if($is_server_chan) checked @endif id="is_server_chan" data-on-color="success" data-off-color="danger" data-on-text="启用" data-off-text="关闭">
+                                                                <span class="help-block"> 推送节点离线提醒、用户流量异常警告、节点使用报告(<a href="http://sc.ftqq.com" target="_blank">绑定微信</a>) </span>
                                                             </div>
                                                         </div>
                                                         <div class="col-md-6 col-sm-6 col-xs-12">
@@ -1727,7 +1717,7 @@
             }
         });
 
-        // 启用、禁用节点宕机发件提醒管理员
+        // 启用、禁用节点离线发件提醒管理员
         $('#is_node_crash_warning').on({
             'switchChange.bootstrapSwitch': function (event, state) {
                 var is_node_crash_warning = state ? 1 : 0;
@@ -1746,7 +1736,7 @@
             }
         });
 
-        // 启用、禁用节点宕机发ServerChan微信消息提醒
+        // 启用、禁用节点离线发ServerChan微信消息提醒
         $('#is_server_chan').on({
             'switchChange.bootstrapSwitch': function (event, state) {
                 var is_server_chan = state ? 1 : 0;
@@ -2151,7 +2141,7 @@
             });
         }
 
-        // 设置节点宕机警告收件地址
+        // 设置节点离线警告收件地址
         function setCrashWarningEmail() {
             var crash_warning_email = $("#crash_warning_email").val();
 

+ 12 - 3
resources/views/admin/trafficLog.blade.php

@@ -18,7 +18,7 @@
                 <div class="portlet light bordered">
                     <div class="portlet-title">
                         <div class="caption font-dark">
-                            <span class="caption-subject bold uppercase"> 流量日志</span>
+                            <span class="caption-subject bold uppercase"> 流量日志 </span>
                         </div>
                     </div>
                     <div class="portlet-body">
@@ -32,6 +32,14 @@
                             <div class="col-md-3 col-sm-4 col-xs-12">
                                 <input type="text" class="col-md-4 form-control" name="username" value="{{Request::get('username')}}" id="username" placeholder="用户名" onkeydown="if(event.keyCode==13){do_search();}">
                             </div>
+                            <div class="col-md-3 col-sm-4 col-xs-12">
+                                <select class="form-control" name="nodeId" id="nodeId" onChange="doSearch()">
+                                    <option value="" @if(Request::get('nodeId') == '') selected @endif>选择节点</option>
+                                    @foreach($nodeList as $node)
+                                        <option value="{{$node->id}}" @if(Request::get('nodeId') == $node->id) selected @endif>{{$node->name}}</option>
+                                    @endforeach
+                                </select>
+                            </div>
                             <div class="col-md-3 col-sm-4 col-xs-12">
                                 <button type="button" class="btn blue" onclick="do_search();">查询</button>
                                 <button type="button" class="btn grey" onclick="do_reset();">重置</button>
@@ -67,7 +75,7 @@
                                                         <a href="{{url('admin/userList?id=') . $vo->user->id}}" target="_blank"> <span class="label label-info"> {{$vo->user->username}} </span> </a>
                                                     @endif
                                                 </td>
-                                                <td> {{$vo->ssnode->name}} </td>
+                                                <td> {{$vo->node->name}} </td>
                                                 <td> {{$vo->rate}} </td>
                                                 <td> {{$vo->u}} </td>
                                                 <td> {{$vo->d}} </td>
@@ -105,8 +113,9 @@
             var port = $("#port").val();
             var user_id = $("#user_id").val();
             var username = $("#username").val();
+            var nodeId = $("#nodeId option:checked").val();
 
-            window.location.href = '{{url('admin/trafficLog')}}' + '?port=' + port + '&user_id=' + user_id + '&username=' + username;
+            window.location.href = '{{url('admin/trafficLog')}}' + '?port=' + port + '&user_id=' + user_id + '&username=' + username + '&nodeId=' + nodeId;
         }
 
         // 重置

+ 11 - 2
resources/views/admin/userList.blade.php

@@ -83,6 +83,7 @@
                                     <th> 订阅码 </th>
                                     <th> 用户名 </th>
                                     <th> 端口 </th>
+                                    <th> 连接密码 </th>
                                     <th> 加密方式 </th>
                                     <!--<th> 协议 </th>
                                     <th> 混淆 </th>-->
@@ -97,7 +98,7 @@
                                 <tbody>
                                     @if ($userList->isEmpty())
                                         <tr>
-                                            <td colspan="13" style="text-align: center;">暂无数据</td>
+                                            <td colspan="14" style="text-align: center;">暂无数据</td>
                                         </tr>
                                     @else
                                         @foreach ($userList as $user)
@@ -106,6 +107,7 @@
                                                 <td> <a href="javascript:;" class="copySubscribeLink" data-clipboard-text="{{$user->link}}" title="点击复制订阅链接">{{$user->subscribe->code}}</a> </td>
                                                 <td> {{$user->username}} </td>
                                                 <td> <span class="label label-danger"> {{$user->port ? $user->port : '未分配'}} </span> </td>
+                                                <td> <span class="label label-default"> {{$user->passwd}} </span> </td>
                                                 <td> <span class="label label-default"> {{$user->method}} </span> </td>
                                                 <!--<td> <span class="label label-default"> {{$user->protocol}} </span> </td>
                                                 <td> <span class="label label-default"> {{$user->obfs}} </span> </td>-->
@@ -156,6 +158,9 @@
                                                             <li>
                                                                 <a href="javascript:doMonitor('{{$user->id}}');"> 流量概况 </a>
                                                             </li>
+                                                            <li>
+                                                                <a href="javascript:ipMonitor('{{$user->id}}');"> 在线巡查 </a>
+                                                            </li>
                                                             <li>
                                                                 <a href="javascript:resetTraffic('{{$user->id}}');"> 流量清零 </a>
                                                             </li>
@@ -221,7 +226,7 @@
 
         // 编辑账号
         function editUser(id) {
-            window.location.href = '{{url('admin/editUser?id=')}}' + id + '&page=' + '{{Request::get('page', 1)}}';
+            window.location.href = '{{url('admin/editUser?id=')}}' + id;
         }
 
         // 删除账号
@@ -268,6 +273,10 @@
             window.location.href = '{{url('admin/userMonitor?id=')}}' + id;
         }
 
+        function ipMonitor(id) {
+            window.location.href = '{{url('admin/onlineIPMonitor?id=')}}' + id;
+        }
+
         // 重置流量
         function resetTraffic(id) {
             layer.confirm('确定重置该用户流量吗?', {icon: 7, title:'警告'}, function(index) {

+ 14 - 26
resources/views/admin/userOnlineIPList.blade.php

@@ -35,21 +35,6 @@
                             <div class="col-md-3 col-sm-4 col-xs-12">
                                 <input type="text" class="col-md-4 form-control" name="port" value="{{Request::get('port')}}" id="port" placeholder="端口" onkeydown="if(event.keyCode==13){doSearch();}">
                             </div>
-                            <div class="col-md-3 col-sm-4 col-xs-12">
-                                <select class="form-control" name="status" id="status" onChange="doSearch()">
-                                    <option value="" @if(Request::get('status') == '') selected @endif>账号状态</option>
-                                    <option value="-1" @if(Request::get('status') == '-1') selected @endif>禁用</option>
-                                    <option value="0" @if(Request::get('status') == '0') selected @endif>未激活</option>
-                                    <option value="1" @if(Request::get('status') == '1') selected @endif>正常</option>
-                                </select>
-                            </div>
-                            <div class="col-md-3 col-sm-4 col-xs-12">
-                                <select class="form-control" name="enable" id="enable" onChange="doSearch()">
-                                    <option value="" @if(Request::get('enable') == '') selected @endif>代理状态</option>
-                                    <option value="1" @if(Request::get('enable') == '1') selected @endif>启用</option>
-                                    <option value="0" @if(Request::get('enable') == '0') selected @endif>禁用</option>
-                                </select>
-                            </div>
                             <div class="col-md-3 col-sm-4 col-xs-12">
                                 <button type="button" class="btn blue" onclick="doSearch();">查询</button>
                                 <button type="button" class="btn grey" onclick="doReset();">重置</button>
@@ -70,7 +55,7 @@
                                 <tbody>
                                     @if ($userList->isEmpty())
                                         <tr>
-                                            <td colspan="13" style="text-align: center;">暂无数据</td>
+                                            <td colspan="5" style="text-align: center;">暂无数据</td>
                                         </tr>
                                     @else
                                         @foreach ($userList as $user)
@@ -98,12 +83,12 @@
                                                     @if(!$user->onlineIPList->isEmpty())
                                                         <table class="table table-hover table-light">
                                                             <thead>
-                                                            <tr>
-                                                                <th> 时间 </th>
-                                                                <th> 节点 </th>
-                                                                <th> 类型 </th>
-                                                                <th> IP </th>
-                                                            </tr>
+                                                                <tr>
+                                                                    <th> 时间 </th>
+                                                                    <th> 节点 </th>
+                                                                    <th> 类型 </th>
+                                                                    <th> IP </th>
+                                                                </tr>
                                                             </thead>
                                                             <tbody>
                                                             @foreach($user->onlineIPList as $vo)
@@ -111,7 +96,7 @@
                                                                     <td>{{$vo->created_at}}</td>
                                                                     <td>{{$vo->node ? $vo->node->name : '【节点已删除】'}}</td>
                                                                     <td>{{$vo->type}}</td>
-                                                                    <td>{{$vo->ip}}</td>
+                                                                    <td><a href="https://www.ipip.net/ip/{{$vo->ip}}.html" target="_blank">{{$vo->ip}}</a></td>
                                                                 </tr>
                                                             @endforeach
                                                             </tbody>
@@ -152,10 +137,13 @@
             var wechat = $("#wechat").val();
             var qq = $("#qq").val();
             var port = $("#port").val();
-            var status = $("#status option:checked").val();
-            var enable = $("#enable option:checked").val();
 
-            window.location.href = '{{url('admin/userOnlineIPList')}}' + '?username=' + username + '&wechat=' + wechat + '&qq=' + qq + '&port=' + port + '&status=' + status + '&enable=' + enable;
+            window.location.href = '{{url('admin/userOnlineIPList')}}' + '?username=' + username + '&wechat=' + wechat + '&qq=' + qq + '&port=' + port;
+        }
+
+        // 重置
+        function doReset() {
+            window.location.href = '{{url('admin/userOnlineIPList')}}';
         }
     </script>
 @endsection

+ 2 - 2
resources/views/emails/nodeCrashWarning.blade.php

@@ -47,7 +47,7 @@
                                                 <th style="Margin:0;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0;padding:0;text-align:left">
                                                     <h6 style="Margin:0;Margin-bottom:10px;color:#f3f3f3;font-family:Helvetica,Arial,sans-serif;font-size:18px;font-weight:400;line-height:1.3;margin:0;margin-bottom:8px;margin-top:8px;padding:0;text-align:left;word-wrap:normal">
                                                         <a href="#" style="Margin:0;color:#f3f3f3;font-family:Helvetica,Arial,sans-serif;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left;text-decoration:none" target="_blank">
-                                                            节点宕机警告
+                                                            节点离线警告
                                                         </a>
                                                     </h6>
                                                 </th>
@@ -68,7 +68,7 @@
                                 <tr style="padding:0;text-align:left;vertical-align:top">
                                     <th style="Margin:0;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0;padding:0;text-align:left">
                                         <div class="release" style="padding-top:5px;padding-left:20px;padding-bottom:20px;">
-                                            <p>节点【{{$nodeName}}】({{$nodeServer}})可能宕机,请及时检查。</p>
+                                            <p>节点【{{$nodeName}}】({{$nodeServer}})离线,请及时检查。</p>
                                         </div>
                                     </th>
                                     <th class="expander" style="Margin:0;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th>

+ 12 - 6
resources/views/subscribe/deviceList.blade.php

@@ -11,10 +11,16 @@
 @section('content')
     <!-- BEGIN CONTENT BODY -->
     <div class="page-content" style="padding-top:0;">
-        <!-- BEGIN PAGE BASE CONTENT -->
         <div class="row">
             <div class="col-md-12">
-                <!-- BEGIN EXAMPLE TABLE PORTLET-->
+                <div class="row">
+                    <div class="col-md-12">
+                        <div class="note note-info">
+                            <p>1.禁止设备订阅功能是根据客户端订阅时请求头信息做判断,禁用相应设备订阅时返回错误信息</p>
+                            <p>以下请求头信息不全或者错误,请各位自行检测,提到Issues</p>
+                        </div>
+                    </div>
+                </div>
                 <div class="portlet light bordered">
                     <div class="portlet-title">
                         <div class="caption font-dark">
@@ -46,9 +52,10 @@
                                 <thead>
                                 <tr>
                                     <th> # </th>
+                                    <th> 名称 </th>
                                     <th> 类型 </th>
                                     <th> 平台 </th>
-                                    <th> 名称 </th>
+                                    <th> 请求头 </th>
                                     <th> 操作 </th>
                                 </tr>
                                 </thead>
@@ -61,9 +68,10 @@
                                         @foreach($deviceList as $vo)
                                             <tr class="odd gradeX">
                                                 <td> {{$vo->id}} </td>
+                                                <td> {{$vo->name}} </td>
                                                 <td> {!! $vo->type_label !!} </td>
                                                 <td> {!! $vo->platform_label !!} </td>
-                                                <td> {{$vo->name}} </td>
+                                                <td> {{$vo->header}} </td>
                                                 <td>
                                                     @if($vo->status == 0)
                                                         <button type="button" class="btn btn-sm green btn-outline" onclick="setDeviceStatus('{{$vo->id}}', 1)">启用</button>
@@ -90,10 +98,8 @@
                         </div>
                     </div>
                 </div>
-                <!-- END EXAMPLE TABLE PORTLET-->
             </div>
         </div>
-        <!-- END PAGE BASE CONTENT -->
     </div>
     <!-- END CONTENT BODY -->
 @endsection

+ 3 - 0
resources/views/user/buy.blade.php

@@ -93,6 +93,7 @@
     <!-- END CONTENT BODY -->
 @endsection
 @section('script')
+    <script src="/js/layer/layer.js" type="text/javascript"></script>
     <script type="text/javascript">
         // 校验优惠券是否可用
         function redeemCoupon() {
@@ -135,6 +136,8 @@
                         $("#coupon_sn").parent().addClass('has-error');
                         $("#coupon_sn").parent().remove('.input-group-addon');
                         $("#coupon_sn").parent().prepend('<span class="input-group-addon"><i class="fa fa-remove fa-fw"></i></span>');
+
+                        layer.msg(ret.message);
                     }
                 }
             });

+ 1 - 1
resources/views/user/invite.blade.php

@@ -71,7 +71,7 @@
                                                             <span class="label label-sm label-default"> {{trans('home.invite_code_table_status_expire')}} </span>
                                                         @endif
                                                     </td>
-                                                    <td> {{empty($invite->user) ? '【账号已删除】' : $invite->user->username}} </td>
+                                                    <td> {{empty($invite->user) ? ($invite->status == 1 ? '【账号已删除】' : '') : $invite->user->username}} </td>
                                                 </tr>
                                             @endforeach
                                         @endif

+ 1 - 0
routes/web.php

@@ -90,6 +90,7 @@ Route::group(['middleware' => ['isForbidden', 'isLogin', 'isAdmin']], function (
     Route::get("marketing/emailList", "MarketingController@emailList"); // 邮件消息列表
     Route::get("marketing/pushList", "MarketingController@pushList"); // 推送消息列表
     Route::post("marketing/addPushMarketing", "MarketingController@addPushMarketing"); // 推送消息
+    Route::get("admin/onlineIPMonitor", "AdminController@onlineIPMonitor"); // 在线IP监控
     Route::any("admin/decompile", "AdminController@decompile"); // SS(R)链接反解析
     Route::get('admin/download', 'AdminController@download'); // 下载转换过的JSON配置
     Route::any('admin/convert', 'AdminController@convert'); // 格式转换

+ 102 - 26
sql/db.sql

@@ -65,8 +65,8 @@ CREATE TABLE `ss_node` (
   `v2_host` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'V2ray伪装的域名',
   `v2_path` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'V2ray WS/H2路径',
   `v2_tls` TINYINT(4) NOT NULL DEFAULT '0' COMMENT 'V2ray底层传输安全 0 未开启 1 开启',
-  `created_at` DATETIME NOT NULL,
-  `updated_at` DATETIME NOT NULL,
+  `created_at` datetime NOT NULL,
+  `updated_at` datetime NOT NULL,
   PRIMARY KEY (`id`),
   INDEX `idx_group` (`group_id`),
 	INDEX `idx_sub` (`is_subscribe`)
@@ -132,8 +132,8 @@ CREATE TABLE `user` (
   `protocol_param` varchar(255) DEFAULT '' COMMENT '协议参数',
   `obfs` varchar(30) NOT NULL DEFAULT 'plain' COMMENT '混淆',
   `obfs_param` varchar(255) DEFAULT '' COMMENT '混淆参数',
-  `speed_limit_per_con` int(255) NOT NULL DEFAULT '204800' COMMENT '单连接限速,默认200M,单位KB',
-  `speed_limit_per_user` int(255) NOT NULL DEFAULT '204800' COMMENT '单用户限速,默认200M,单位KB',
+  `speed_limit_per_con` bigint(20) NOT NULL DEFAULT '10737418240' COMMENT '单连接限速,默认10G,为0表示不限速,单位Byte',
+  `speed_limit_per_user` bigint(20) NOT NULL DEFAULT '10737418240' COMMENT '单用户限速,默认10G,为0表示不限速,单位Byte',
   `gender` tinyint(4) NOT NULL DEFAULT '1' COMMENT '性别:0-女、1-男',
   `wechat` varchar(30) DEFAULT '' COMMENT '微信',
   `qq` varchar(20) DEFAULT '' COMMENT 'QQ',
@@ -597,6 +597,7 @@ CREATE TABLE `ticket` (
   `content` text NOT NULL COMMENT '内容',
   `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态:0-待处理、1-已处理未关闭、2-已关闭',
   `created_at` datetime DEFAULT NULL COMMENT '创建时间',
+  `updated_at` datetime DEFAULT NULL COMMENT '最后更新时间',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工单';
 
@@ -610,6 +611,7 @@ CREATE TABLE `ticket_reply` (
   `user_id` int(11) NOT NULL COMMENT '回复人ID',
   `content` text NOT NULL COMMENT '回复内容',
   `created_at` datetime DEFAULT NULL COMMENT '创建时间',
+  `updated_at` datetime DEFAULT NULL COMMENT '最后更新时间',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工单回复';
 
@@ -650,14 +652,14 @@ CREATE TABLE `user_balance_log` (
 -- Table structure for `user_traffic_modify_log`
 -- ----------------------------
 CREATE TABLE `user_traffic_modify_log` (
-	`id` INT(11) NOT NULL AUTO_INCREMENT,
-	`user_id` INT(11) NOT NULL DEFAULT '0' COMMENT '用户ID',
-	`order_id` INT(11) NOT NULL DEFAULT '0' COMMENT '发生的订单ID',
-	`before` BIGINT(20) NOT NULL DEFAULT '0' COMMENT '操作前流量',
-	`after` BIGINT(20) NOT NULL DEFAULT '0' COMMENT '操作后流量',
-	`desc` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '描述',
-	`created_at` DATETIME NOT NULL,
-	`updated_at` DATETIME NOT NULL,
+	`id` int(11) NOT NULL AUTO_INCREMENT,
+	`user_id` int(11) NOT NULL DEFAULT '0' COMMENT '用户ID',
+	`order_id` int(11) NOT NULL DEFAULT '0' COMMENT '发生的订单ID',
+	`before` bigint(20) NOT NULL DEFAULT '0' COMMENT '操作前流量',
+	`after` bigint(20) NOT NULL DEFAULT '0' COMMENT '操作后流量',
+	`desc` varchar(255) NOT NULL DEFAULT '' COMMENT '描述',
+	`created_at` datetime NOT NULL,
+	`updated_at` datetime NOT NULL,
 	PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户流量变动日志';
 
@@ -708,6 +710,7 @@ CREATE TABLE `email_log` (
   `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1-发送成功、2-发送失败',
   `error` text COMMENT '发送失败抛出的异常信息',
   `created_at` datetime DEFAULT NULL COMMENT '创建时间',
+  `updated_at` datetime DEFAULT NULL COMMENT '最后更新时间',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='邮件投递记录';
 
@@ -835,6 +838,13 @@ INSERT INTO `sensitive_words` (`words`) VALUES ('trashymail.com');
 INSERT INTO `sensitive_words` (`words`) VALUES ('tempemail.net');
 INSERT INTO `sensitive_words` (`words`) VALUES ('slopsbox.com');
 INSERT INTO `sensitive_words` (`words`) VALUES ('mailnesia.com');
+INSERT INTO `sensitive_words` (`words`) VALUES ('ezehe.com');
+INSERT INTO `sensitive_words` (`words`) VALUES ('tempail.com');
+INSERT INTO `sensitive_words` (`words`) VALUES ('newairmail.com');
+INSERT INTO `sensitive_words` (`words`) VALUES ('temp-mail.org');
+INSERT INTO `sensitive_words` (`words`) VALUES ('linshiyouxiang.net');
+INSERT INTO `sensitive_words` (`words`) VALUES ('zwoho.com');
+INSERT INTO `sensitive_words` (`words`) VALUES ('mailboxy.fun');
 
 
 -- ----------------------------
@@ -850,7 +860,9 @@ CREATE TABLE `user_subscribe` (
   `ban_desc` varchar(50) NOT NULL DEFAULT '' COMMENT '封禁理由',
   `created_at` datetime DEFAULT NULL COMMENT '创建时间',
   `updated_at` datetime DEFAULT NULL COMMENT '最后更新时间',
-  PRIMARY KEY (`id`)
+  PRIMARY KEY (`id`),
+  INDEX `user_id` (`user_id`, `status`),
+	INDEX `code` (`code`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户订阅';
 
 
@@ -869,7 +881,8 @@ CREATE TABLE `user_subscribe_log` (
   `request_ip` varchar(20) DEFAULT NULL COMMENT '请求IP',
   `request_time` datetime DEFAULT NULL COMMENT '请求时间',
   `request_header` text COMMENT '请求头部信息',
-  PRIMARY KEY (`id`)
+  PRIMARY KEY (`id`),
+  INDEX `sid` (`sid`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户订阅访问日志';
 
 
@@ -1112,8 +1125,8 @@ CREATE TABLE `marketing` (
   `content` TEXT NOT NULL COMMENT '内容' COLLATE 'utf8mb4_unicode_ci',
   `error` VARCHAR(255) NULL COMMENT '错误信息' COLLATE 'utf8mb4_unicode_ci',
   `status` TINYINT(4) NOT NULL COMMENT '状态:-1-失败、0-待发送、1-成功',
-  `created_at` DATETIME NOT NULL,
-  `updated_at` DATETIME NOT NULL,
+  `created_at` datetime NOT NULL,
+  `updated_at` datetime NOT NULL,
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='营销';
 
@@ -1131,8 +1144,8 @@ CREATE TABLE `user_login_log` (
 	`county` CHAR(20) NOT NULL,
 	`isp` CHAR(20) NOT NULL,
 	`area` CHAR(20) NOT NULL,
-	`created_at` DATETIME NOT NULL,
-	`updated_at` DATETIME NOT NULL,
+	`created_at` datetime NOT NULL,
+	`updated_at` datetime NOT NULL,
 	PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户登录日志';
 
@@ -1150,7 +1163,7 @@ CREATE TABLE `ss_node_ip` (
   PRIMARY KEY (`id`),
   INDEX `idx_node` (`node_id`),
   INDEX `idx_port` (`port`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='在线IP';
 
 
 -- ----------------------------
@@ -1161,7 +1174,7 @@ CREATE TABLE `rule` (
 	`type` CHAR(10) NOT NULL DEFAULT 'domain' COMMENT '类型:domain-域名(单一非通配)、ipv4-IPv4地址、ipv6-IPv6地址、reg-正则表达式',
 	`regular` VARCHAR(255) NOT NULL COMMENT '规则:域名、IP、正则表达式',
 	PRIMARY KEY (`id`)
-) ENGINE=MyISAM COLLATE='utf8_general_ci' COMMENT='规则表';
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='规则表';
 
 
 -- ----------------------------
@@ -1172,7 +1185,7 @@ CREATE TABLE `ss_node_deny` (
 	`node_id` INT(11) NOT NULL DEFAULT '0',
 	`rule_id` INT(11) NOT NULL DEFAULT '0',
 	PRIMARY KEY (`id`)
-) ENGINE=MyISAM COLLATE='utf8_general_ci' COMMENT='节点访问规则关联表';
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='节点访问规则关联表';
 
 
 -- ----------------------------
@@ -1180,20 +1193,83 @@ CREATE TABLE `ss_node_deny` (
 -- ----------------------------
 CREATE TABLE `device` (
 	`id` INT(11) NOT NULL AUTO_INCREMENT,
-	`type` TINYINT(4) NOT NULL DEFAULT '1' COMMENT '类型:1-Shadowsocks(R)、2-V2Ray',
+	`type` TINYINT(4) NOT NULL DEFAULT '1' COMMENT '类型:0-兼容、1-Shadowsocks(R)、2-V2Ray',
 	`platform` TINYINT(4) NOT NULL DEFAULT '1' COMMENT '所属平台:0-其他、1-iOS、2-Android、3-Mac、4-Windows、5-Linux',
 	`name` VARCHAR(50) NOT NULL COMMENT '设备名称',
 	`status` TINYINT(4) NOT NULL DEFAULT '1' COMMENT '状态:0-禁止订阅、1-允许订阅',
+	`header` VARCHAR(100) NOT NULL COMMENT '请求时头部的识别特征码',
 	PRIMARY KEY (`id`)
-) COMMENT='设备型号表' ENGINE=MyISAM;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='设备型号表';
 
 
 -- ----------------------------
 -- Records of `device`
 -- ----------------------------
-INSERT INTO `device` VALUES ('1', '1', '1', 'Quantumult', 1);
-INSERT INTO `device` VALUES ('2', '1', '1', 'Shadowrocket', 1);
-INSERT INTO `device` VALUES ('3', '1', '1', 'ShadowsocksX-NG-R', 1);
+INSERT INTO `device` (`id`, `type`, `platform`, `name`, `status`, `header`) VALUES
+	(1, 1, 1, 'Quantumult', 1, 'Quantumult'),
+	(2, 1, 1, 'Shadowrocket', 1, 'Shadowrocket'),
+	(3, 1, 3, 'ShadowsocksX-NG-R', 1, 'ShadowsocksX-NG-R'),
+	(4, 1, 1, 'Pepi', 1, 'Pepi'),
+	(5, 1, 1, 'Potatso 2', 1, 'Potatso'),
+	(6, 1, 1, 'Potatso Lite', 1, 'Potatso'),
+	(7, 1, 4, 'ShadowsocksR', 1, 'ShadowsocksR'),
+	(8, 2, 4, 'V2RayW', 1, 'V2RayW'),
+	(9, 2, 4, 'V2RayN', 1, 'V2RayN'),
+	(10, 2, 4, 'V2RayS', 1, 'V2RayS'),
+	(11, 2, 4, 'Clash for Windows', 1, 'Clash'),
+	(12, 2, 3, 'V2RayX', 1, 'V2RayX'),
+	(13, 2, 3, 'V2RayU', 1, 'V2RayU'),
+	(14, 2, 3, 'V2RayC', 1, 'V2RayC'),
+	(15, 2, 3, 'ClashX', 1, 'ClashX'),
+	(16, 2, 1, 'Kitsunebi', 1, 'Kitsunebi'),
+	(17, 2, 1, 'Kitsunebi Lite', 1, 'Kitsunebi'),
+	(18, 2, 1, 'i2Ray', 1, 'i2Ray'),
+	(19, 2, 2, 'BifrostV', 1, 'BifrostV'),
+	(20, 2, 2, 'V2RayNG', 1, 'V2RayNG'),
+	(21, 2, 2, 'ShadowsocksR', 1, 'okhttp'),
+	(22, 2, 2, 'SSRR', 1, 'okhttp');
+
+
+-- ----------------------------
+-- Records of `failed_jobs`
+-- ----------------------------
+CREATE TABLE `failed_jobs` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
+  `queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
+  `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
+  `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
+  `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='失败任务';
+
+
+-- ----------------------------
+-- Records of `jobs`
+-- ----------------------------
+CREATE TABLE `jobs` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `queue` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+  `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
+  `attempts` tinyint(3) unsigned NOT NULL,
+  `reserved_at` int(10) unsigned DEFAULT NULL,
+  `available_at` int(10) unsigned NOT NULL,
+  `created_at` int(10) unsigned NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `jobs_queue_index` (`queue`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='任务';
+
+
+-- ----------------------------
+-- Records of `migrations`
+-- ----------------------------
+CREATE TABLE `migrations` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+  `batch` int(11) NOT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='迁移';
+
 
 /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
 /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;

+ 2 - 2
sql/update/20190129.sql

@@ -1,7 +1,7 @@
 -- 加入订阅设备表
 CREATE TABLE `device` (
 	`id` INT(11) NOT NULL AUTO_INCREMENT,
-	`type` TINYINT(4) NOT NULL DEFAULT '1' COMMENT '类型:1-Shadowsocks(R)、2-V2Ray',
+	`type` TINYINT(4) NOT NULL DEFAULT '1' COMMENT '类型:0-兼容、1-Shadowsocks(R)、2-V2Ray',
 	`platform` TINYINT(4) NOT NULL DEFAULT '1' COMMENT '所属平台:0-其他、1-iOS、2-Android、3-Mac、4-Windows、5-Linux',
 	`name` VARCHAR(50) NOT NULL COMMENT '设备名称',
 	`status` TINYINT(4) NOT NULL DEFAULT '1' COMMENT '状态:0-禁止订阅、1-允许订阅',
@@ -10,4 +10,4 @@ CREATE TABLE `device` (
 
 INSERT INTO `device` VALUES ('1', '1', '1', 'Quantumult', 1);
 INSERT INTO `device` VALUES ('2', '1', '1', 'Shadowrocket', 1);
-INSERT INTO `device` VALUES ('3', '1', '1', 'ShadowsocksX-NG-R', 1);
+INSERT INTO `device` VALUES ('3', '1', '3', 'ShadowsocksX-NG-R', 1);

+ 8 - 0
sql/update/20190130.sql

@@ -0,0 +1,8 @@
+-- 工单加入最后更新时间
+ALTER TABLE `ticket`
+	ADD COLUMN `updated_at` DATETIME NULL DEFAULT NULL COMMENT '最后更新时间' AFTER `created_at`;
+
+
+-- 工单回复加入最后更新时间
+ALTER TABLE `ticket_reply`
+	ADD COLUMN `updated_at` DATETIME NULL COMMENT '最后更新时间' AFTER `created_at`;

+ 30 - 4
sql/update/20190131.sql

@@ -1,4 +1,30 @@
-INSERT INTO `config` values ('84', 'is_f2fpay', 0);
-INSERT INTO `config` VALUES ('85', 'f2fpay_app_id', '');
-INSERT INTO `config` VALUES ('86', 'f2fpay_private_key', '');
-INSERT INTO `config` VALUES ('87', 'f2fpay_public_key', '');
+-- 设备表加入 请求时头部的识别特征码 字段
+ALTER TABLE `device`
+	CHANGE COLUMN `platform` `platform` TINYINT(4) NOT NULL DEFAULT '1' COMMENT '所属平台:0-其他、1-iOS、2-Android、3-Mac、4-Windows、5-Linux' AFTER `type`,
+	ADD COLUMN `header` VARCHAR(100) NOT NULL COMMENT '请求时头部的识别特征码' AFTER `status`;
+
+-- 重新初始化设备表数据
+TRUNCATE TABLE `device`;
+INSERT INTO `device` (`id`, `type`, `platform`, `name`, `status`, `header`) VALUES
+	(1, 1, 1, 'Quantumult', 1, 'Quantumult'),
+	(2, 1, 1, 'Shadowrocket', 1, 'Shadowrocket'),
+	(3, 1, 3, 'ShadowsocksX-NG-R', 1, 'ShadowsocksX-NG-R'),
+	(4, 1, 1, 'Pepi', 1, 'Pepi'),
+	(5, 1, 1, 'Potatso 2', 1, 'Potatso'),
+	(6, 1, 1, 'Potatso Lite', 1, 'Potatso'),
+	(7, 1, 4, 'ShadowsocksR', 1, 'ShadowsocksR'),
+	(8, 2, 4, 'V2RayW', 1, 'V2RayW'),
+	(9, 2, 4, 'V2RayN', 1, 'V2RayN'),
+	(10, 2, 4, 'V2RayS', 1, 'V2RayS'),
+	(11, 2, 4, 'Clash for Windows', 1, 'Clash'),
+	(12, 2, 3, 'V2RayX', 1, 'V2RayX'),
+	(13, 2, 3, 'V2RayU', 1, 'V2RayU'),
+	(14, 2, 3, 'V2RayC', 1, 'V2RayC'),
+	(15, 2, 3, 'ClashX', 1, 'ClashX'),
+	(16, 2, 1, 'Kitsunebi', 1, 'Kitsunebi'),
+	(17, 2, 1, 'Kitsunebi Lite', 1, 'Kitsunebi'),
+	(18, 2, 1, 'i2Ray', 1, 'i2Ray'),
+	(19, 2, 2, 'BifrostV', 1, 'BifrostV'),
+	(20, 2, 2, 'V2RayNG', 1, 'V2RayNG'),
+	(21, 2, 2, 'ShadowsocksR', 1, 'okhttp'),
+	(22, 2, 2, 'SSRR', 1, 'okhttp');

+ 33 - 0
sql/update/20190210.sql

@@ -0,0 +1,33 @@
+-- 处理失败的任务
+CREATE TABLE `failed_jobs` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
+  `queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
+  `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
+  `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
+  `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- 任务
+CREATE TABLE `jobs` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `queue` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+  `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
+  `attempts` tinyint(3) unsigned NOT NULL,
+  `reserved_at` int(10) unsigned DEFAULT NULL,
+  `available_at` int(10) unsigned NOT NULL,
+  `created_at` int(10) unsigned NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `jobs_queue_index` (`queue`)
+) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- 迁移记录
+CREATE TABLE `migrations` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+  `batch` int(11) NOT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+

+ 13 - 0
sql/update/20190213.sql

@@ -0,0 +1,13 @@
+-- 适配vnet,修正限速字段,默认限速为10M
+ALTER TABLE `user`
+	CHANGE COLUMN `speed_limit_per_con` `speed_limit_per_con` BIGINT NOT NULL DEFAULT '10737418240' COMMENT '单连接限速,默认10G,为0表示不限速,单位Byte' AFTER `obfs_param`,
+	CHANGE COLUMN `speed_limit_per_user` `speed_limit_per_user` BIGINT NOT NULL DEFAULT '10737418240' COMMENT '单用户限速,默认10G,为0表示不限速,单位Byte' AFTER `speed_limit_per_con`;
+
+
+-- 优化数据库,防止暴库
+ALTER TABLE `user_subscribe_log`
+	ADD INDEX `sid` (`sid`);
+
+ALTER TABLE `user_subscribe`
+	ADD INDEX `user_id` (`user_id`, `status`),
+	ADD INDEX `code` (`code`);

+ 3 - 0
sql/update/20190215.sql

@@ -0,0 +1,3 @@
+-- 邮件投递记录增加最后更新字段
+ALTER TABLE `email_log`
+	ADD COLUMN `updated_at` DATETIME NULL DEFAULT NULL COMMENT '最后更新时间' AFTER `created_at`;

+ 1 - 0
storage/framework/testing/.gitignore

@@ -1,2 +1,3 @@
 *
+*
 !.gitignore

BIN
storage/qqwry.dat


+ 2 - 1
update.sh

@@ -6,4 +6,5 @@ php composer.phar update
 php composer.phar dumpautoload
 php artisan key:generate
 php artisan view:clear
-php artisan cache:clear
+php artisan cache:clear
+chown -R www:www storage