Browse Source

1.节点防墙监测
2.月流量展示优化
3.语言包更新
4.开发标准化
(本次更新不在直接使用config/database.php和config/mail.php,请cp .env.example .env,然后更改.env文件)

bingo 7 years ago
parent
commit
31fbd3cb14

+ 0 - 33
.env

@@ -1,33 +0,0 @@
-APP_NAME=Laravel
-APP_ENV=local
-APP_KEY=
-APP_DEBUG=true
-APP_LOG_LEVEL=debug
-APP_URL=http://localhost
-
-DB_CONNECTION=mysql
-DB_HOST=127.0.0.1
-DB_PORT=3306
-DB_DATABASE=ssrpanel
-DB_USERNAME=root
-DB_PASSWORD=
-
-BROADCAST_DRIVER=log
-CACHE_DRIVER=file
-SESSION_DRIVER=file
-QUEUE_DRIVER=sync
-
-REDIS_HOST=127.0.0.1
-REDIS_PASSWORD=null
-REDIS_PORT=6379
-
-MAIL_DRIVER=smtp
-MAIL_HOST=smtp.mailtrap.io
-MAIL_PORT=2525
-MAIL_USERNAME=null
-MAIL_PASSWORD=null
-MAIL_ENCRYPTION=null
-
-PUSHER_APP_ID=
-PUSHER_APP_KEY=
-PUSHER_APP_SECRET=

+ 8 - 2
.env.example

@@ -1,16 +1,22 @@
-APP_NAME=Laravel
+APP_NAME=SSRPanel
 APP_ENV=local
 APP_KEY=
 APP_DEBUG=true
 APP_LOG_LEVEL=debug
 APP_URL=http://localhost
+APP_TIMEZONE=Asia/Shanghai
+APP_LOCALE=zh-CN
+APP_FALLBACK_LOCALE=en
+APP_LOG=daily
+APP_LOG_LEVEL=debug
 
 DB_CONNECTION=mysql
 DB_HOST=127.0.0.1
 DB_PORT=3306
 DB_DATABASE=ssrpanel
 DB_USERNAME=root
-DB_PASSWORD=
+DB_PASSWORD=root
+DB_STRICT=false
 
 BROADCAST_DRIVER=log
 CACHE_DRIVER=file

+ 4 - 2
.gitignore

@@ -1,14 +1,16 @@
-/config/database.php
+/node_modules
 /public/upload
 /storage/framework
 /storage/logs
 /storage/app/public
+/storage/*.key
 /vendor
 /.idea
 /.vagrant
-/.env.example
 Homestead.json
 Homestead.yaml
 npm-debug.log
 .DS_Store
 .phpstorm.meta.php
+yarn-error.log
+.env

+ 284 - 47
app/Console/Commands/AutoJob.php

@@ -5,6 +5,7 @@ namespace App\Console\Commands;
 use Illuminate\Console\Command;
 use App\Components\ServerChan;
 use App\Http\Models\Coupon;
+use App\Http\Models\CouponLog;
 use App\Http\Models\EmailLog;
 use App\Http\Models\Invite;
 use App\Http\Models\Order;
@@ -28,11 +29,14 @@ class AutoJob extends Command
 {
     protected $signature = 'autoJob';
     protected $description = '自动化任务';
-    protected $cacheKey = 'node_shutdown_warning_';
+    protected $ssrCheckCacheKey = 'ssr_check_warning_';
+    protected $serverCheckCacheKey = 'server_check_warning_';
+    protected static $config;
 
     public function __construct()
     {
         parent::__construct();
+        self::$config = $this->systemConfig();
     }
 
     /*
@@ -42,32 +46,80 @@ class AutoJob extends Command
     {
         $jobStartTime = microtime(true);
 
-        $config = $this->systemConfig();
+        // SSR(R)被启用说明用户购买了流量,需要重置ban_time,防止异常
+        $this->resetBantime();
 
         // 优惠券到期自动置无效
+        $this->expireCoupon();
+
+        // 邀请码到期自动置无效
+        $this->exipreInvite();
+
+        // 封禁访问异常的订阅链接
+        $this->blockSubscribe();
+
+        // 封禁账号
+        $this->blockUsers();
+
+        // 自动移除被封禁账号的标签
+        $this->removeUserLabels();
+
+        // 解封被临时封禁的账号(ban_time > 0)
+        $this->unblockUsers();
+
+        // 端口回收与分配
+        $this->dispatchPort();
+
+        // 关闭超时未支付订单
+        $this->closeOrder();
+
+        // 监测节点状态
+        $this->checkNode();
+
+        $jobEndTime = microtime(true);
+        $jobUsedTime = round(($jobEndTime - $jobStartTime), 4);
+
+        Log::info('执行定时任务【' . $this->description . '】,耗时' . $jobUsedTime . '秒');
+    }
+
+    // 重置ban_time
+    private function resetBantime()
+    {
+        User::query()->where('enable', 1)->where('ban_time', -1)->update(['ban_time' => 0]);
+    }
+
+    // 优惠券到期自动置无效
+    private function expireCoupon()
+    {
         $couponList = Coupon::query()->where('status', 0)->where('available_end', '<=', time())->get();
         if (!$couponList->isEmpty()) {
             foreach ($couponList as $coupon) {
                 Coupon::query()->where('id', $coupon->id)->update(['status' => 2]);
             }
         }
+    }
 
-        // 邀请码到期自动置无效
+    // 邀请码到期自动置无效
+    private function exipreInvite()
+    {
         $inviteList = Invite::query()->where('status', 0)->where('dateline', '<=', date('Y-m-d H:i:s'))->get();
         if (!$inviteList->isEmpty()) {
             foreach ($inviteList as $invite) {
                 Invite::query()->where('id', $invite->id)->update(['status' => 2]);
             }
         }
+    }
 
-        // 封禁24小时访问异常的订阅链接
-        if ($config['is_subscribe_ban']) {
+    // 封禁访问异常的订阅链接
+    private function blockSubscribe()
+    {
+        if (self::$config['is_subscribe_ban']) {
             $subscribeList = UserSubscribe::query()->where('status', 1)->get();
             if (!$subscribeList->isEmpty()) {
                 foreach ($subscribeList as $subscribe) {
                     // 24小时内不同IP的请求次数
                     $request_times = UserSubscribeLog::query()->where('sid', $subscribe->id)->where('request_time', '>=', date("Y-m-d H:i:s", strtotime("-24 hours")))->distinct('request_ip')->count('request_ip');
-                    if ($request_times >= $config['subscribe_ban_times']) {
+                    if ($request_times >= self::$config['subscribe_ban_times']) {
                         UserSubscribe::query()->where('id', $subscribe->id)->update(['status' => 0, 'ban_time' => time(), 'ban_desc' => '存在异常,自动封禁']);
 
                         // 记录封禁日志
@@ -76,28 +128,29 @@ class AutoJob extends Command
                 }
             }
         }
+    }
 
+    // 封禁账号
+    private function blockUsers()
+    {
         // 封禁24小时内流量异常账号
-        if ($config['is_traffic_ban']) {
+        if (self::$config['is_traffic_ban']) {
             $userList = User::query()->where('status', '>=', 0)->where('enable', 1)->where('ban_time', '>=', 0)->get();
             if (!$userList->isEmpty()) {
                 foreach ($userList as $user) {
                     $time = date('Y-m-d H:i:s', time() - 24 * 60 * 60);
                     $totalTraffic = UserTrafficHourly::query()->where('user_id', $user->id)->where('node_id', 0)->where('created_at', '>=', $time)->sum('total');
-                    if ($totalTraffic >= ($config['traffic_ban_value'] * 1024 * 1024 * 1024)) {
-                        $ban_time = strtotime(date('Y-m-d H:i:s', strtotime("+" . $config['traffic_ban_time'] . " minutes")));
+                    if ($totalTraffic >= (self::$config['traffic_ban_value'] * 1024 * 1024 * 1024)) {
+                        $ban_time = strtotime(date('Y-m-d H:i:s', strtotime("+" . self::$config['traffic_ban_time'] . " minutes")));
                         User::query()->where('id', $user->id)->update(['enable' => 0, 'ban_time' => $ban_time]);
 
                         // 写入日志
-                        $this->addUserBanLog($user->id, $config['traffic_ban_time'], '【临时封禁代理】-24小时内流量异常');
+                        $this->addUserBanLog($user->id, self::$config['traffic_ban_time'], '【临时封禁代理】-24小时内流量异常');
                     }
                 }
             }
         }
 
-        // SSR(R)被启用说明用户购买了流量,需要重置ban_time,防止异常
-        User::query()->where('enable', 1)->where('ban_time', -1)->update(['ban_time' => 0]);
-
         // 禁用流量超限用户
         $userList = User::query()->where('enable', 1)->whereRaw("u + d >= transfer_enable")->get();
         if (!$userList->isEmpty()) {
@@ -113,7 +166,7 @@ class AutoJob extends Command
         $userList = User::query()->where('enable', 1)->where('expire_time', '<=', date('Y-m-d'))->get();
         if (!$userList->isEmpty()) {
             foreach ($userList as $user) {
-                if ($config['is_ban_status']) {
+                if (self::$config['is_ban_status']) {
                     User::query()->where('id', $user->id)->update(['enable' => 0, 'status' => -1, 'ban_time' => -1]);
 
                     $this->addUserBanLog($user->id, 0, '【完全封禁账号及代理】-账号已过期');
@@ -124,16 +177,22 @@ class AutoJob extends Command
                 }
             }
         }
+    }
 
-        // 自动移除被封禁账号的标签
+    // 自动移除被封禁账号的标签
+    private function removeUserLabels()
+    {
         $userList = User::query()->where('enable', 0)->where('ban_time', -1)->get();
         if (!$userList->isEmpty()) {
             foreach ($userList as $user) {
                 UserLabel::query()->where('user_id', $user->id)->delete();
             }
         }
+    }
 
-        // 解封被临时封禁的账号(ban_time > 0)
+    // 解封被临时封禁的账号(ban_time > 0)
+    private function unblockUsers()
+    {
         $userList = User::query()->where('status', '>=', 0)->where('ban_time', '>', 0)->get();
         foreach ($userList as $user) {
             if ($user->ban_time < time()) {
@@ -154,13 +213,17 @@ class AutoJob extends Command
                 $this->addUserBanLog($user->id, 0, '【自动解封】-有流量解封');
             }
         }
+    }
 
+    // 端口回收与分配
+    private function dispatchPort()
+    {
         // 自动分配端口
-        if ($config['auto_release_port']) {
+        if (self::$config['auto_release_port']) {
             $userList = User::query()->where('status', '>=', 0)->where('enable', 1)->where('port', 0)->get();
             if (!$userList->isEmpty()) {
                 foreach ($userList as $user) {
-                    $port = $config['is_rand_port'] ? $this->getRandPort() : $this->getOnlyPort();
+                    $port = self::$config['is_rand_port'] ? $this->getRandPort() : $this->getOnlyPort();
 
                     User::query()->where('id', $user->id)->update(['port' => $port]);
                 }
@@ -168,7 +231,7 @@ class AutoJob extends Command
         }
 
         // 被封禁账号自动释放端口
-        if ($config['auto_release_port']) {
+        if (self::$config['auto_release_port']) {
             $userList = User::query()->where('enable', 0)->where('ban_time', -1)->get();
             if (!$userList->isEmpty()) {
                 foreach ($userList as $user) {
@@ -178,9 +241,13 @@ class AutoJob extends Command
                 }
             }
         }
+    }
 
-        // 自动关闭超时未支付订单(有赞云收款二维码超过60分钟自动关闭,我们限制30分钟内必须付款)
-        $paymentList = Payment::query()->with(['order'])->where('status', 0)->where('created_at', '<=', date("Y-m-d H:i:s", strtotime("-30 minutes")))->get();
+    // 自动关闭超时未支付订单
+    private function closeOrder()
+    {
+        // 自动关闭超时未支付的有赞云订单(有赞云收款二维码超过60分钟自动关闭,我们限制30分钟内必须付款)
+        $paymentList = Payment::query()->with(['order', 'order.coupon'])->where('status', 0)->where('created_at', '<=', date("Y-m-d H:i:s", strtotime("-30 minutes")))->get();
         if (!$paymentList->isEmpty()) {
             DB::beginTransaction();
             try {
@@ -194,6 +261,8 @@ class AutoJob extends Command
                     // 退回优惠券
                     if ($payment->order->coupon_id) {
                         Coupon::query()->where('id', $payment->order->coupon_id)->update(['status' => 0]);
+
+                        $this->addCouponLog($payment->order->coupon_id, $payment->order->goods_id, $payment->oid, '订单超时未支付,自动退回');
                     }
                 }
 
@@ -204,49 +273,150 @@ class AutoJob extends Command
                 DB::rollBack();
             }
         }
+    }
+
+    // 监测节点状态 TODO:需要改进,否则curl请求时间超长
+    private function checkNode()
+    {
+        $title = "节点异常警告";
 
-        // 自动监测节点状态
         $nodeList = SsNode::query()->where('status', 1)->get();
         foreach ($nodeList as $node) {
-            // 10分钟内无节点负载信息则认为是宕机
-            $node_info = SsNodeInfo::query()->where('node_id', $node->id)->where('log_time', '>=', strtotime("-10 minutes"))->orderBy('id', 'desc')->first();
-            if (empty($node_info) || empty($node_info->load)) {
+            // TCP检测
+            $tcpCheck = $this->tcpCheck($node->ip);
+            if (false !== $tcpCheck && $tcpCheck) {
                 // 10分钟内已发警告,则不再发
-                if (Cache::has($this->cacheKey . $node->id)) {
+                if (Cache::has($this->ssrCheckCacheKey . $node->id)) {
                     continue;
                 }
 
-                $title = "节点宕机警告";
-                $content = "节点**{$node->name}【{$node->server}】({$node->ip})**可能宕机,请及时检查。";
-
-                // 发邮件通知管理员
-                if ($config['is_node_crash_warning'] && $config['crash_warning_email']) {
-                    try {
-                        Mail::to($config['crash_warning_email'])->send(new nodeCrashWarning($config['website_name'], $node->name, $node->server));
-                        $this->addEmailLog(1, $title, $content);
-                    } catch (\Exception $e) {
-                        $this->addEmailLog(1, $title, $content, 0, $e->getMessage());
-                    }
+                $content = '节点无异常';
+                if ($tcpCheck === 1) {
+                    $content = "节点**{$node->name}【{$node->ip}】**异常:**宕机**";
+                } else if ($tcpCheck === 2) {
+                    $content = "节点**{$node->name}【{$node->ip}】**异常:**海外不通**";
+                } else if ($tcpCheck === 3) {
+                    $content = "节点**{$node->name}【{$node->ip}】**异常:**TCP阻断**";
                 }
 
-                // 通过ServerChan发微信消息提醒管理员
-                if ($config['is_server_chan'] && $config['server_chan_key']) {
-                    $serverChan = new ServerChan();
-                    $serverChan->send($title, $content);
+                // 通知管理员
+                $this->notifyMaster($title, $content, $node->name, $node->server);
+
+                // 写入发信缓存
+                Cache::put($this->ssrCheckCacheKey . $node->id, $node->name . '(' . $node->server . ')', 10);
+            }
+
+            // 10分钟内无节点负载信息且TCP检测认为不是宕机则认为是SSR(R)后端炸了
+            $node_info = SsNodeInfo::query()->where('node_id', $node->id)->where('log_time', '>=', strtotime("-10 minutes"))->orderBy('id', 'desc')->first();
+            if ($tcpCheck !== 1 && (empty($node_info) || empty($node_info->load))) {
+                // 10分钟内已发警告,则不再发
+                if (Cache::has($this->serverCheckCacheKey . $node->id)) {
+                    continue;
                 }
 
+                $content = "节点**{$node->name}【{$node->ip}】**异常:**心跳异常**";
+
+                // 通知管理员
+                $this->notifyMaster($title, $content, $node->name, $node->server);
+
                 // 写入发信缓存
-                Cache::put($this->cacheKey . $node->id, $node->name . '(' . $node->server . ')', 10);
+                Cache::put($this->serverCheckCacheKey . $node->id, $node->name . '(' . $node->server . ')', 10);
             }
         }
+    }
 
-        $jobEndTime = microtime(true);
-        $jobUsedTime = round(($jobEndTime - $jobStartTime) , 4);
+    // TCP检测
+    private function tcpCheck($ip)
+    {
+        try {
+            $tcpCN = $this->curlRequest("https://ipcheck.need.sh/api.php?location=cn&ip={$ip}&type=tcp");
+            $tcpUS = $this->curlRequest("https://ipcheck.need.sh/api.php?location=us&ip={$ip}&type=tcp");
 
-        Log::info('执行定时任务【' . $this->description . '】,耗时' . $jobUsedTime . '秒');
+            if ($tcpCN['result'] != 'success' && $tcpUS['result'] != 'success') {
+                throw new \Exception("节点监测探测接口异常");
+            }
+
+            if (!$tcpCN['alive'] && !$tcpUS['alive']) {
+                return 1; // 中美都不通,服务器宕机
+            } else if ($tcpCN['alive'] && !$tcpUS['alive']) {
+                return 2; // 中通美不通,无法出国,可能是安全组策略限制(例如:阿里云、腾讯云)
+            } else if (!$tcpCN['alive'] && $tcpUS['alive']) {
+                return 3; // 美通中不通,说明被墙进行TCP阻断
+            } else {
+                return 0; // 正常
+            }
+        } catch (\Exception $e) {
+            Log::error('节点监测请求失败:' . $e);
+
+            return false;
+        }
+    }
+
+    // 检测服务
+//    private function checkHost()
+//    {
+//        $data =
+//        $header = [
+//            'Content-Type: application/json',
+//            'Content-Length: ' . strlen($data)
+//        ];
+//    }
+
+    /**
+     * 通知管理员
+     *
+     * @param string $title      消息标题
+     * @param string $content    消息内容
+     * @param string $nodeName   节点名称
+     * @param string $nodeServer 节点域名
+     */
+    private function notifyMaster($title, $content, $nodeName, $nodeServer)
+    {
+        $this->notifyMasterByEmail($title, $content, $nodeName, $nodeServer);
+        $this->notifyMasterByServerchan($title, $content);
+    }
+
+    /**
+     * 发邮件通知管理员
+     *
+     * @param string $title      消息标题
+     * @param string $content    消息内容
+     * @param string $nodeName   节点名称
+     * @param string $nodeServer 节点域名
+     */
+    private function notifyMasterByEmail($title, $content, $nodeName, $nodeServer)
+    {
+        if (self::$config['is_node_crash_warning'] && self::$config['crash_warning_email']) {
+            try {
+                Mail::to(self::$config['crash_warning_email'])->send(new nodeCrashWarning(self::$config['website_name'], $nodeName, $nodeServer));
+                $this->addEmailLog(1, $title, $content);
+            } catch (\Exception $e) {
+                $this->addEmailLog(1, $title, $content, 0, $e->getMessage());
+            }
+        }
     }
 
-    // 添加用户封禁日志
+    /**
+     * 通过ServerChan发微信消息提醒管理员
+     *
+     * @param string $title   消息标题
+     * @param string $content 消息内容
+     */
+    private function notifyMasterByServerchan($title, $content)
+    {
+        if (self::$config['is_server_chan'] && self::$config['server_chan_key']) {
+            $serverChan = new ServerChan();
+            $serverChan->send($title, $content);
+        }
+    }
+
+    /**
+     * 添加用户封禁日志
+     *
+     * @param int    $userId  用户ID
+     * @param int    $minutes 封禁时长,单位分钟
+     * @param string $desc    封禁理由
+     */
     private function addUserBanLog($userId, $minutes, $desc)
     {
         $log = new UserBanLog();
@@ -257,7 +427,7 @@ class AutoJob extends Command
     }
 
     /**
-     * 写入邮件发送日志
+     * 添加邮件发送日志
      *
      * @param int    $user_id 接收者用户ID
      * @param string $title   标题
@@ -277,6 +447,24 @@ class AutoJob extends Command
         $emailLogObj->save();
     }
 
+    /**
+     * 添加优惠券操作日志
+     *
+     * @param int    $couponId 优惠券ID
+     * @param int    $goodsId  商品ID
+     * @param int    $orderId  订单ID
+     * @param string $desc     备注
+     */
+    private function addCouponLog($couponId, $goodsId, $orderId, $desc = '')
+    {
+        $couponLog = new CouponLog();
+        $couponLog->coupon_id = $couponId;
+        $couponLog->goods_id = $goodsId;
+        $couponLog->order_id = $orderId;
+        $couponLog->desc = $desc;
+        $couponLog->save();
+    }
+
     // 系统配置
     private function systemConfig()
     {
@@ -320,4 +508,53 @@ class AutoJob extends Command
 
         return $port;
     }
+
+    /**
+     * 发起一个CURL请求
+     *
+     * @param string $url
+     * @param array  $data
+     * @param array  $header
+     *
+     * @return mixed
+     */
+    private function curlRequest($url, $data = [], $header = [])
+    {
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($ch, CURLOPT_TIMEOUT, 500);
+        // 为保证第三方服务器与微信服务器之间数据传输的安全性,所有微信接口采用https方式调用,必须使用下面2行代码打开ssl安全校验。
+        // 如果在部署过程中代码在此处验证失败,请到 http://curl.haxx.se/ca/cacert.pem 下载新的证书判别文件。
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+        curl_setopt($ch, CURLOPT_URL, $url);
+
+        // 如果设置了header
+        if ($header) {
+//            $header = [
+//                'Content-Type: application/json',
+//                'Content-Length: ' . strlen($data)
+//            ];
+//            $header = [
+//                'Content-Type: text/xml; charset=utf-8',
+//                'Content-Length: ' . strlen($data)
+//            ];
+//            $header = [
+//                'Content-type: text/plain',
+//                'Content-Length: ' . strlen($data)
+//            ];
+            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
+        }
+
+        // 如果data有数据,则用POST请求
+        if ($data) {
+            curl_setopt($ch, CURLOPT_POST, 1);
+            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
+        }
+
+        $result = curl_exec($ch);
+        curl_close($ch);
+
+        return json_decode($result, JSON_OBJECT_AS_ARRAY);
+    }
 }

+ 20 - 21
app/Http/Controllers/AdminController.php

@@ -185,18 +185,9 @@ class AdminController extends Controller
                 return Response::json(['status' => 'fail', 'data' => '', 'message' => '用户名已存在,请重新输入']);
             }
 
-            // 密码为空时则生成随机密码
-            $password = $request->get('password');
-            if (empty($password)) {
-                $str = makeRandStr();
-                $password = md5($str);
-            } else {
-                $password = md5($password);
-            }
-
             $user = new User();
             $user->username = trim($request->get('username'));
-            $user->password = $password;
+            $user->password = trim($request->get('password')) ? md5(trim($request->get('password'))) : md5(makeRandStr()); // 密码为空时则生成随机密码
             $user->port = $request->get('port');
             $user->passwd = empty($request->get('passwd')) ? makeRandStr() : $request->get('passwd'); // SS密码为空时生成默认密码
             $user->transfer_enable = toGB($request->get('transfer_enable', 0));
@@ -239,7 +230,6 @@ class AdminController extends Controller
             }
         } else {
             // 生成一个可用端口
-            $last_user = User::query()->orderBy('id', 'desc')->first();
             $view['last_port'] = $this->systemConfig['is_rand_port'] ? $this->getRandPort() : $this->getOnlyPort();
             $view['is_rand_port'] = $this->systemConfig['is_rand_port'];
             $view['method_list'] = $this->methodList();
@@ -259,7 +249,6 @@ class AdminController extends Controller
         try {
             for ($i = 0; $i < 5; $i++) {
                 // 生成一个可用端口
-                $last_user = User::query()->orderBy('id', 'desc')->first();
                 $port = $this->systemConfig['is_rand_port'] ? $this->getRandPort() : $this->getOnlyPort();
 
                 $user = new User();
@@ -379,11 +368,11 @@ class AdminController extends Controller
 
                 User::query()->where('id', $id)->update($data);
 
-                // 先删除该用户所有的标签
-                UserLabel::query()->where('user_id', $id)->delete();
-
-                // 生成用户标签
+                // 重新生成用户标签
                 if (!empty($labels)) {
+                    // 先删除该用户所有的标签
+                    UserLabel::query()->where('user_id', $id)->delete();
+
                     foreach ($labels as $label) {
                         $userLabel = new UserLabel();
                         $userLabel->user_id = $id;
@@ -433,7 +422,7 @@ class AdminController extends Controller
     {
         $id = $request->get('id');
 
-        if ($id == 1) {
+        if ($id === 1) {
             return Response::json(['status' => 'fail', 'data' => '', 'message' => '系统管理员不可删除']);
         }
 
@@ -467,7 +456,7 @@ class AdminController extends Controller
             $totalTraffic = SsNodeTrafficDaily::query()->where('node_id', $node->id)->sum('total');
             $node->transfer = flowAutoShow($totalTraffic);
 
-            // 负载(10分钟以内)
+            // 负载(10分钟以内) TODO:待改造
             $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;
         }
@@ -646,8 +635,10 @@ class AdminController extends Controller
 
                 // 生成节点标签
                 $labels = $request->get('labels');
-                SsNodeLabel::query()->where('node_id', $id)->delete(); // 删除所有该节点的标签
                 if (!empty($labels)) {
+                    // 删除所有该节点的标签
+                    SsNodeLabel::query()->where('node_id', $id)->delete();
+
                     foreach ($labels as $label) {
                         $ssNodeLabel = new SsNodeLabel();
                         $ssNodeLabel->node_id = $id;
@@ -704,9 +695,9 @@ class AdminController extends Controller
         DB::beginTransaction();
         try {
             // 删除分组关联、节点标签、节点相关日志
+            SsNode::query()->where('id', $id)->delete();
             SsGroupNode::query()->where('node_id', $id)->delete();
             SsNodeLabel::query()->where('node_id', $id)->delete();
-            SsNode::query()->where('id', $id)->delete();
             SsNodeInfo::query()->where('node_id', $id)->delete();
             SsNodeOnlineLog::query()->where('node_id', $id)->delete();
             SsNodeTrafficDaily::query()->where('node_id', $id)->delete();
@@ -774,8 +765,16 @@ class AdminController extends Controller
             'hourlyData' => "'" . implode("','", $hourlyData) . "'"
         ];
 
+        // 本月天数数据
+        $monthDays = [];
+        $monthHasDays = date("t");
+        for ($i = 1; $i <= $monthHasDays; $i++) {
+            $monthDays[] = $i;
+        }
+
         $view['nodeName'] = $node->name;
         $view['nodeServer'] = $node->server;
+        $view['monthDays'] = "'" . implode("','", $monthDays) . "'";
 
         return Response::view('admin/nodeMonitor', $view);
     }
@@ -1857,7 +1856,7 @@ EOF;
         return Response::json(['status' => 'success', 'data' => '', 'message' => '设置成功']);
     }
 
-    // 设置微信、支付宝二维码
+    // 设置微信、支付宝二维码(已废弃)
     public function setQrcode(Request $request)
     {
         // 微信二维码

+ 3 - 21
app/Http/Controllers/Api/YzyController.php

@@ -4,8 +4,6 @@ namespace App\Http\Controllers\Api;
 
 use App\Components\Yzy;
 use App\Http\Controllers\Controller;
-use App\Http\Models\Coupon;
-use App\Http\Models\CouponLog;
 use App\Http\Models\Goods;
 use App\Http\Models\GoodsLabel;
 use App\Http\Models\Order;
@@ -29,7 +27,7 @@ class YzyController extends Controller
     // 接收GET请求
     public function index(Request $request)
     {
-        \Log::info("YZY-GET:" . var_export($request->all()));
+        \Log::info("YZY-GET:" . var_export($request->all()) . '[' . $request->getClientIp() . ']');
     }
 
     // 接收POST请求
@@ -40,7 +38,7 @@ class YzyController extends Controller
         $json = file_get_contents('php://input');
         $data = json_decode($json, true);
         if (!$data) {
-            Log::info('YZY-POST:回调数据无法解析,可能是非法请求');
+            Log::info('YZY-POST:回调数据无法解析,可能是非法请求[' . $request->getClientIp() . ']');
             exit();
         }
 
@@ -49,7 +47,7 @@ class YzyController extends Controller
         $sign_string = $this->systemConfig['youzan_client_id'] . "" . $msg . "" . $this->systemConfig['youzan_client_secret'];
         $sign = md5($sign_string);
         if ($sign != $data['sign']) {
-            Log::info('YZY-POST:回调数据签名错误,可能是非法请求');
+            Log::info('YZY-POST:回调数据签名错误,可能是非法请求[' . $request->getClientIp() . ']');
             exit();
         } else {
             // 返回请求成功标识给有赞
@@ -103,22 +101,6 @@ class YzyController extends Controller
                     $order->status = 2;
                     $order->save();
 
-                    // 优惠券置为已使用
-                    $coupon = Coupon::query()->where('id', $order->coupon_id)->first();
-                    if ($coupon) {
-                        if ($coupon->usage == 1) {
-                            $coupon->status = 1;
-                            $coupon->save();
-                        }
-
-                        // 写入日志
-                        $couponLog = new CouponLog();
-                        $couponLog->coupon_id = $coupon->id;
-                        $couponLog->goods_id = $order->goods_id;
-                        $couponLog->order_id = $order->oid;
-                        $couponLog->save();
-                    }
-
                     // 如果买的是套餐,则先将之前购买的所有套餐置都无效,并扣掉之前所有套餐的流量
                     $goods = Goods::query()->where('id', $order->goods_id)->first();
                     if ($goods->type == 2) {

+ 19 - 0
app/Http/Controllers/Controller.php

@@ -2,6 +2,7 @@
 
 namespace App\Http\Controllers;
 
+use App\Http\Models\CouponLog;
 use App\Http\Models\UserSubscribe;
 use Illuminate\Foundation\Bus\DispatchesJobs;
 use Illuminate\Routing\Controller as BaseController;
@@ -198,6 +199,24 @@ class Controller extends BaseController
         $emailLogObj->save();
     }
 
+    /**
+     * 添加优惠券操作日志
+     *
+     * @param int    $couponId 优惠券ID
+     * @param int    $goodsId  商品ID
+     * @param int    $orderId  订单ID
+     * @param string $desc     备注
+     */
+    public function addCouponLog($couponId, $goodsId, $orderId, $desc = '')
+    {
+        $couponLog = new CouponLog();
+        $couponLog->coupon_id = $couponId;
+        $couponLog->goods_id = $goodsId;
+        $couponLog->order_id = $orderId;
+        $couponLog->desc = $desc;
+        $couponLog->save();
+    }
+
     // 将Base64图片转换为本地图片并保存
     function base64ImageSaver($base64_image_content)
     {

+ 4 - 2
app/Http/Controllers/PaymentController.php

@@ -43,8 +43,8 @@ class PaymentController extends Controller
         $strategy = $this->systemConfig['goods_purchase_limit_strategy'];
         if ($strategy == 'all' || ($strategy == 'free' && $goods->price == 0)) {
             // 判断是否已经购买过该商品
-            $none_expire_good_exist = Order::query()->where('user_id', $user['id'])->where('goods_id', $goods_id)->where('is_expire', 0)->exists();
-            if ($none_expire_good_exist) {
+            $noneExpireOrderExist = Order::query()->where('user_id', $user['id'])->where('goods_id', $goods_id)->where('status', '>=', 0)->where('is_expire', 0)->exists();
+            if ($noneExpireOrderExist) {
                 return Response::json(['status' => 'fail', 'data' => '', 'message' => '创建支付单失败:商品不可重复购买']);
             }
         }
@@ -114,6 +114,8 @@ class PaymentController extends Controller
             // 优惠券置为已使用
             if (!empty($coupon)) {
                 Coupon::query()->where('id', $coupon->id)->update(['status' => 1]);
+
+                $this->addCouponLog($coupon->id, $goods_id, $order->oid, '在线支付使用');
             }
 
             DB::commit();

+ 4 - 9
app/Http/Controllers/RegisterController.php

@@ -3,7 +3,6 @@
 namespace App\Http\Controllers;
 
 use App\Http\Models\Invite;
-use App\Http\Models\SsConfig;
 use App\Http\Models\User;
 use App\Http\Models\UserLabel;
 use App\Http\Models\Verify;
@@ -150,22 +149,18 @@ class RegisterController extends Controller
                 return Redirect::back()->withInput();
             }
 
-            // 默认加密方式、协议、混淆
-            $method = SsConfig::query()->where('type', 1)->where('is_default', 1)->first();
-            $protocol = SsConfig::query()->where('type', 2)->where('is_default', 1)->first();
-            $obfs = SsConfig::query()->where('type', 3)->where('is_default', 1)->first();
+            $transfer_enable = $referral_uid ? ($this->systemConfig['default_traffic'] + $this->systemConfig['referral_traffic']) * 1048576 : $this->systemConfig['default_traffic'] * 1048576;
 
             // 创建新用户
-            $transfer_enable = $referral_uid ? ($this->systemConfig['default_traffic'] + $this->systemConfig['referral_traffic']) * 1048576 : $this->systemConfig['default_traffic'] * 1048576;
             $user = new User();
             $user->username = $username;
             $user->password = md5($password);
             $user->port = $port;
             $user->passwd = makeRandStr();
             $user->transfer_enable = $transfer_enable;
-            $user->method = $method ? $method->name : 'aes-192-ctr';
-            $user->protocol = $protocol ? $protocol->name : 'auth_chain_a';
-            $user->obfs = $obfs ? $obfs->name : 'tls1.2_ticket_auth';
+            $user->method = $this->getDefaultMethod();
+            $user->protocol = $this->getDefaultProtocol();
+            $user->obfs = $this->getDefaultObfs();
             $user->enable_time = date('Y-m-d H:i:s');
             $user->expire_time = date('Y-m-d H:i:s', strtotime("+" . $this->systemConfig['default_days'] . " days"));
             $user->reg_ip = $request->getClientIp();

+ 10 - 11
app/Http/Controllers/UserController.php

@@ -306,8 +306,16 @@ class UserController extends Controller
             $hourlyData[$x] = round($userTrafficHourly[$x - ($hourlyTotal - $hourlyCount)] / (1024 * 1024 * 1024), 3);
         }
 
+        // 本月天数数据
+        $monthDays = [];
+        $monthHasDays = date("t");
+        for ($i = 1; $i <= $monthHasDays; $i++) {
+            $monthDays[] = $i;
+        }
+
         $view['trafficDaily'] = "'" . implode("','", $dailyData) . "'";
         $view['trafficHourly'] = "'" . implode("','", $hourlyData) . "'";
+        $view['monthDays'] = "'" . implode("','", $monthDays) . "'";
 
         $view['website_logo'] = $this->systemConfig['website_logo'];
         $view['website_analytics'] = $this->systemConfig['website_analytics'];
@@ -929,11 +937,7 @@ class UserController extends Controller
                     }
 
                     // 写入日志
-                    $couponLog = new CouponLog();
-                    $couponLog->coupon_id = $coupon->id;
-                    $couponLog->goods_id = $goods_id;
-                    $couponLog->order_id = $order->oid;
-                    $couponLog->save();
+                    $this->addCouponLog($coupon->id, $goods_id, $order->oid, '余额支付订单使用');
                 }
 
                 // 如果买的是套餐,则先将之前购买的所有套餐置都无效,并扣掉之前所有套餐的流量,并移除之前所有套餐的标签
@@ -1237,11 +1241,7 @@ class UserController extends Controller
             $coupon->save();
 
             // 写入卡券日志
-            $couponLog = new CouponLog();
-            $couponLog->coupon_id = $coupon->id;
-            $couponLog->goods_id = 0;
-            $couponLog->order_id = 0;
-            $couponLog->save();
+            $this->addCouponLog($coupon->id, 0, 0, '账户余额充值使用');
 
             DB::commit();
 
@@ -1260,5 +1260,4 @@ class UserController extends Controller
 
         return Redirect::back();
     }
-
 }

+ 0 - 6
composer.json

@@ -60,11 +60,5 @@
         "preferred-install": "dist",
         "sort-packages": true,
         "optimize-autoloader": true
-    },
-    "repositories": {
-        "packagist": {
-            "type": "composer",
-            "url": "https://packagist.phpcomposer.com"
-        }
     }
 }

+ 10 - 12
config/app.php

@@ -12,7 +12,7 @@ return [
     | any other location as required by the application or its packages.
     */
 
-    'name' => 'SSRPanel',
+    'name' => env('APP_NAME', 'Laravel'),
 
     /*
     |--------------------------------------------------------------------------
@@ -25,7 +25,7 @@ return [
     |
     */
 
-    'env' => 'production',
+    'env' => env('APP_ENV', 'production'),
 
     /*
     |--------------------------------------------------------------------------
@@ -38,7 +38,7 @@ return [
     |
     */
 
-    'debug' => true,
+    'debug' => env('APP_DEBUG', false),
 
     /*
     |--------------------------------------------------------------------------
@@ -51,7 +51,7 @@ return [
     |
     */
 
-    'url' => 'http://localhost',
+    'url' => env('APP_URL', 'http://localhost'),
 
     /*
     |--------------------------------------------------------------------------
@@ -64,7 +64,7 @@ return [
     |
     */
 
-    'timezone' => 'Asia/Shanghai',
+    'timezone' => env('APP_TIMEZONE', 'UTC'),
 
     /*
     |--------------------------------------------------------------------------
@@ -77,8 +77,7 @@ return [
     |
     */
 
-    'locale' => 'zh-CN',
-    //'locale' => 'en',
+    'locale' => env('APP_LOCALE', 'en'),
 
     /*
     |--------------------------------------------------------------------------
@@ -91,8 +90,7 @@ return [
     |
     */
 
-    'fallback_locale' => 'en',
-    //'fallback_locale' => 'zh-CN',
+    'fallback_locale' => env('APP_FALLBACK_LOCALE', 'zh-CN'),
 
     /*
     |--------------------------------------------------------------------------
@@ -105,7 +103,7 @@ return [
     |
     */
 
-    'key' => 'base64:6nNq+XYCwjwi/FQ/x9tK40ETErxtRUzLWk8Jny9bRa8=',
+    'key' => env('APP_KEY'),
 
     'cipher' => 'AES-256-CBC',
 
@@ -122,9 +120,9 @@ return [
     |
     */
 
-    'log' => 'daily',
+    'log' => env('APP_LOG', 'single'),
 
-    'log_level' => 'debug',
+    'log_level' => env('APP_LOG_LEVEL', 'debug'),
 
     'log_max_files' => 30,
 

+ 1 - 1
config/cache.php

@@ -86,6 +86,6 @@ return [
     |
     */
 
-    'prefix' => 'laravel',
+    'prefix' => 'ssrpanel',
 
 ];

+ 8 - 8
config/database.php

@@ -13,7 +13,7 @@ return [
     |
     */
 
-    'default' => 'mysql',
+    'default' => env('DB_CONNECTION', 'mysql'),
 
     /*
     |--------------------------------------------------------------------------
@@ -41,16 +41,16 @@ return [
 
         'mysql' => [
             'driver' => 'mysql',
-            'host' => '127.0.0.1',
-            'port' => '3306',
-            'database' => 'ssrpanel',
-            'username' => 'root',
-            'password' => 'root',
-            'unix_socket' => '',
+            'host' => env('DB_HOST', '127.0.0.1'),
+            'port' => env('DB_PORT', '3306'),
+            'database' => env('DB_DATABASE', 'forge'),
+            'username' => env('DB_USERNAME', 'forge'),
+            'password' => env('DB_PASSWORD', ''),
+            'unix_socket' => env('DB_SOCKET', ''),
             'charset' => 'utf8mb4',
             'collation' => 'utf8mb4_unicode_ci',
             'prefix' => '',
-            'strict' => false,
+            'strict' => env('DB_STRICT', true),
             'engine' => null,
         ],
 

+ 16 - 16
config/domains.php

@@ -3,24 +3,24 @@
 // 常见域名后缀
 return [
     'com', 'net', 'org', 'couk', 'io', 'computer', 'ac', 'academy', 'actor', 'ae', 'aero', 'af', 'ag', 'agency',
-    'ai', 'am', 'archi', 'arpa', 'as', 'asia', 'associates', 'at', 'au', 'aw', 'ax', 'az', 'bar', 'bargains',
+    'ai', 'am', 'archi', 'arpa', 'as', 'asia', 'associates', 'at', 'au', 'aw', 'ax', 'az', 'bar', 'bargains', 'ltd',
     'bayern', 'be', 'berlin', 'bg', 'bi', 'bike', 'biz', 'bj', 'blackfriday', 'bn', 'boutique', 'build', 'builders',
-    'bw', 'by', 'ca', 'cab', 'camera', 'camp', 'capital', 'cards', 'careers', 'cat', 'catering', 'cc', 'center',
-    'ceo', 'cf', 'ch', 'cheap', 'christmas', 'ci', 'cl', 'cleaning', 'clothing', 'club', 'cn', 'co', 'codes',
-    'coffee', 'college', 'cologne', 'community', 'company', 'construction', 'contractors', 'cooking', 'cool',
-    'coop', 'country', 'cruises', 'cx', 'cz', 'dating', 'de', 'democrat', 'desi', 'diamonds', 'directory', 'dk',
-    'dm', 'domains', 'dz', 'ec', 'edu', 'education', 'ee', 'email', 'engineering', 'enterprises', 'equipment',
-    'es', 'estate', 'eu', 'eus', 'events', 'expert', 'exposed', 'farm', 'feedback', 'fi', 'fish', 'fishing',
-    'flights', 'florist', 'fo', 'foo', 'foundation', 'fr', 'frogans', 'futbol', 'ga', 'gal', 'gd', 'gg', 'gi',
-    'gift', 'gl', 'glass', 'gop', 'gov', 'graphics', 'gripe', 'gs', 'guitars', 'guru', 'gy', 'haus', 'hk', 'hn',
-    'holiday', 'horse', 'house', 'hr', 'ht', 'hu', 'id', 'ie', 'il', 'im', 'immobilien', 'in', 'industries',
-    'institute', 'int', 'international', 'iq', 'ir', 'is', 'it', 'je', 'jobs', 'jp', 'kaufen', 'ke', 'kg', 'ki',
-    'kitchen', 'kiwi', 'koeln', 'kr', 'kz', 'la', 'land', 'lease', 'li', 'lighting', 'limo', 'link', 'london',
-    'lt', 'lu', 'luxury', 'lv', 'ly', 'ma', 'management', 'mango', 'marketing', 'md', 'me', 'media', 'menu', 'mg',
+    'bw', 'by', 'ca', 'cab', 'camera', 'camp', 'capital', 'cards', 'careers', 'cat', 'catering', 'cc', 'center', 'style',
+    'ceo', 'cf', 'ch', 'cheap', 'christmas', 'ci', 'cl', 'cleaning', 'clothing', 'club', 'cn', 'co', 'codes', 'store',
+    'coffee', 'college', 'cologne', 'community', 'company', 'construction', 'contractors', 'cooking', 'cool', 'online',
+    'coop', 'country', 'cruises', 'cx', 'cz', 'dating', 'de', 'democrat', 'desi', 'diamonds', 'directory', 'dk', 'vip',
+    'dm', 'domains', 'dz', 'ec', 'edu', 'education', 'ee', 'email', 'engineering', 'enterprises', 'equipment', 'top',
+    'es', 'estate', 'eu', 'eus', 'events', 'expert', 'exposed', 'farm', 'feedback', 'fi', 'fish', 'fishing', 'dog',
+    'flights', 'florist', 'fo', 'foo', 'foundation', 'fr', 'frogans', 'futbol', 'ga', 'gal', 'gd', 'gg', 'gi', 'fun',
+    'gift', 'gl', 'glass', 'gop', 'gov', 'graphics', 'gripe', 'gs', 'guitars', 'guru', 'gy', 'haus', 'hk', 'hn', 'loans',
+    'holiday', 'horse', 'house', 'hr', 'ht', 'hu', 'id', 'ie', 'il', 'im', 'immobilien', 'in', 'industries', 'holdings',
+    'institute', 'int', 'international', 'iq', 'ir', 'is', 'it', 'je', 'jobs', 'jp', 'kaufen', 'ke', 'kg', 'ki', 'care',
+    'kitchen', 'kiwi', 'koeln', 'kr', 'kz', 'la', 'land', 'lease', 'li', 'lighting', 'limo', 'link', 'london', 'football',
+    'lt', 'lu', 'luxury', 'lv', 'ly', 'ma', 'management', 'mango', 'marketing', 'md', 'me', 'media', 'menu', 'mg', 'tours',
     'miami', 'mk', 'ml', 'mn', 'mo', 'mobi', 'moda', 'monash', 'mp', 'ms', 'mu', 'museum', 'mx', 'my', 'na', 'name',
-    'nc', 'nf', 'ng', 'ninja', 'nl', 'no', 'nu', 'nz', 'om', 'onl', 'paris', 'partners', 'parts', 'pe', 'pf',
+    'nc', 'nf', 'ng', 'ninja', 'nl', 'no', 'nu', 'nz', 'om', 'onl', 'paris', 'partners', 'parts', 'pe', 'pf', 'financial',
     'photo', 'photography', 'photos', 'pics', 'pictures', 'pl', 'plumbing', 'pm', 'post', 'pr', 'pro', 'productions',
-    'properties', 'pt', 'pub', 'pw', 'qa', 'quebec', 're', 'recipes', 'reisen', 'rentals', 'repair', 'report',
+    'properties', 'pt', 'pub', 'pw', 'qa', 'quebec', 're', 'recipes', 'reisen', 'rentals', 'repair', 'report', 'app',
     'rest', 'reviews', 'rich', 'ro', 'rocks', 'rodeo', 'rs', 'ru', 'ruhr', 'sa', 'saarland', 'sb', 'sc', 'se',
     'services', 'sexy', 'sg', 'sh', 'shoes', 'si', 'singles', 'sk', 'sm', 'sn', 'so', 'social', 'solar', 'solutions',
     'soy', 'st', 'su', 'supplies', 'supply', 'support', 'sx', 'sy', 'systems', 'tattoo', 'tc', 'technology', 'tel',
@@ -33,5 +33,5 @@ return [
     'lb', 'lk', 'lr', 'ls', 'mc', 'mf', 'mh', 'mil', 'mm', 'moe', 'mq', 'mr', 'mt', 'mv', 'mw', 'mz', 'nagoya', 'ne',
     'neustar', 'ni', 'np', 'nr', 'nyc', 'okinawa', 'pa', 'pg', 'ph', 'pk', 'pn', 'ps', 'py', 'qpon', 'ren', 'rw',
     'sd', 'sj', 'sl', 'sohu', 'sr', 'ss', 'sv', 'sz', 'td', 'tg', 'tj', 'tokyo', 'tp', 'trade', 'tt', 'um', 'uno',
-    'va', 'vi', 'vi', 'vn', 'webcam', 'ye', 'yokohoma', 'ryukyu', 'meet', 'vote', 'lc', 'voto', 'wed', 'zw'
+    'va', 'vi', 'vi', 'vn', 'webcam', 'ye', 'yokohoma', 'ryukyu', 'meet', 'vote', 'lc', 'voto', 'wed', 'zw', 'ooo'
 ];

+ 8 - 8
config/mail.php

@@ -16,7 +16,7 @@ return [
     |
     */
 
-    'driver' => 'smtp',
+    'driver' => env('MAIL_DRIVER', 'smtp'),
 
     /*
     |--------------------------------------------------------------------------
@@ -29,7 +29,7 @@ return [
     |
     */
 
-    'host' => 'smtp.exmail.qq.com',
+    'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
 
     /*
     |--------------------------------------------------------------------------
@@ -42,7 +42,7 @@ return [
     |
     */
 
-    'port' => 465,
+    'port' => env('MAIL_PORT', 587),
 
     /*
     |--------------------------------------------------------------------------
@@ -56,8 +56,8 @@ return [
     */
 
     'from' => [
-        'address' => '[email protected]',
-        'name' => '重置密码',
+        'address' => env('MAIL_FROM_ADDRESS', '[email protected]'),
+        'name'    => env('MAIL_FROM_NAME', 'Example'),
     ],
 
     /*
@@ -71,7 +71,7 @@ return [
     |
     */
 
-    'encryption' => 'ssl',
+    'encryption' => env('MAIL_ENCRYPTION', 'tls'),
 
     /*
     |--------------------------------------------------------------------------
@@ -84,9 +84,9 @@ return [
     |
     */
 
-    'username' => '[email protected]',
+    'username' => env('MAIL_USERNAME'),
 
-    'password' => 'xxxxxx',
+    'password' => env('MAIL_PASSWORD'),
 
     /*
     |--------------------------------------------------------------------------

+ 4 - 0
config/muv2.php

@@ -0,0 +1,4 @@
+<?php
+
+// V2Ray配置
+return [];

+ 17 - 24
readme.md

@@ -34,10 +34,6 @@ telegram千人讨论群已解散,有问题提issues,或者进小群讨论(
 199是设定的门槛,因为进群后如果捣乱就踢,这样的成本会拦住较多无聊的捣乱者
 ````
 
-## 开发支持金
-支持金最高的功能,将再下次版本更新中优先开发。
-![开发支持金](https://github.com/ssrpanel/ssrpanel/blob/master/public/assets/images/vote.jpg?raw=true)
-
 ## 捐赠
 **ETH** : 0x968f797f194fcec05ea571723199748b58de38ba
 
@@ -65,13 +61,14 @@ PHP必须开启curl、gd、fileinfo、openssl、mbstring组件
 #### 拉取代码
 ````
 cd /home/wwwroot/
-git clone https://github.com/ssrpanel/ssrpanel.git
+git clone https://github.com/ssrpanel/SSRPanel.git
 ````
 
 #### 安装面板
 ````
-cd ssrpanel/
+cd SSRPanel/
 php composer.phar install
+cp .env.example .env
 php artisan key:generate
 chown -R www:www storage/
 chmod -R 777 storage/
@@ -82,7 +79,7 @@ chmod -R 777 storage/
 ##### 连接数据库
 ````
 1.创建一个utf8mb4的数据库
-2.编辑config/database.php,编辑mysql选项中如下配置值:host、port、database、username、password
+2.编辑 .env 文件,修改 DB_ 开头的值
 ````
 
 ##### 自动部署
@@ -103,7 +100,7 @@ php artisan db:seed --class=UserTableSeeder
 
 ##### 手工部署
 ````
-迁移未经完整测试,存在BUG,可以手动将sql/db.sql导入到创建好的数据库
+注意:迁移未经完整测试,存在BUG(主要是我懒得用),可以手动将sql/db.sql导入到创建好的数据库
 ````
 
 #### 加入NGINX的URL重写规则
@@ -148,27 +145,15 @@ crontab -e -u www
 ## 邮件配置
 ###### SMTP
 ````
-编辑 config\mail.php
-
-请自行配置如下内容
-'driver' => 'smtp',
-'host' => 'smtp.exmail.qq.com',
-'port' => 465,
-'from' => [
-    'address' => '[email protected]',
-    'name' => 'SSRPanel',
-],
-'encryption' => 'ssl',
-'username' => '[email protected]',
-'password' => 'xxxxxx',
+编辑 .env 文件,修改 MAIL_ 开头的配置
 ````
 
 ###### Mailgun
 ````
-编辑 config\mail.php
-将 driver 值改为 mailgun
+编辑 .env 文件
+将 MAIL_DRIVER 值改为 mailgun
 
-编辑 config/services.php
+然后编辑 config/services.php
 
 请自行配置如下内容
 'mailgun' => [
@@ -287,6 +272,14 @@ vim user-config.json
 警告:经实测单端口下如果用锐速没有效果,很可能是VPS供应商限制了这两个端口
 提示:配置单端口最好先看下这个WIKI,防止才踩坑: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
 
+````
+## 代码更新
+````
+进入到SSRPanel目录下
+1.手动更新: git pull
+2.强制更新: sh ./update.sh 
+
+如果你更改了本地文件,手动更新会提示错误需要合并代码(自己搞定),强制更新会直接覆盖你本地所有更改过的文件
 ````
 
 ## 校时

+ 4 - 4
resources/lang/en/home.php

@@ -5,7 +5,7 @@ return [
     'subscribe_address'               => 'My Subscribe Address',
     'copy_subscribe_address'          => 'Copy Address',
     'exchange_subscribe'              => 'Exchange Address',
-    'subscribe_warning'               => 'Warning: this subscription address is for personal use only. Do not propagate the address.',
+    'subscribe_warning'               => 'Warning: This subscription address is for personal use only. Do not propagate the address.',
     'reset_password_title'            => 'Reset Password',
     'system_down'                     => 'The system is being maintained',
     'subscribe_baned'                 => 'Your subscription function has been banned. Please contact the administrator.',
@@ -49,7 +49,7 @@ return [
     'close'                           => 'Close',
     'redeem_score'                    => 'Redeem for bandwidth',
     'redeem'                          => 'Redeem',
-    'redeem_info'                     => 'You have got :score points and can redeem :scoreM free bandwidth.',
+    'redeem_info'                     => 'You have got :score points and can redeem :score M free bandwidth.',
     'coupon_code'                     => 'Coupon Code',
     'please_input_coupon'             => 'Please input the coupon code',
     'scan_qrcode'                     => 'Scan qrcode with your client',
@@ -96,11 +96,11 @@ return [
     'service_type_2'                  => 'Flow Plans',
 
     // 流量日志
-    'traffic_log_tips'                => 'Tips: The update of the flow chart will be delayed. The chart is updated on the following day, and the hourly chart is updated at the next hour.',
+    'traffic_log_tips'                => 'Tips: The update of the flow chart will be delayed.',
     'traffic_log_30days'              => '30 Days Statistics',
     'traffic_log_24hours'             => '24 Hours Statistics',
     'traffic_log_keywords'            => 'Bandwidth',
-    'traffic_log_unit'                => 'unit: G',
+    'traffic_log_unit'                => 'Unit: G',
     'traffic_log_max'                 => 'Max',
 
     // 邀请码

+ 3 - 3
resources/lang/ja/home.php

@@ -35,7 +35,7 @@ return [
     'account_bandwidth_usage'         => 'データ通信量の使用状況',
     'account_total_traffic'           => 'データ通信量の上限',
     'account_usage_traffic'           => '使用済み',
-    'account_reset_notice'            => '毎月:reset_day日にデータ通信量をリセットする',
+    'account_reset_notice'            => '毎月 :reset_day 日にデータ通信量をリセットする',
     'article_title'                   => '文章',
     'recharge'                        => 'チャージ',
     'enabled'                         => '有効',
@@ -49,7 +49,7 @@ return [
     'close'                           => 'キャンセル',
     'redeem_score'                    => 'データ通信量を兌換する',
     'redeem'                          => '今すぐ兌換',
-    'redeem_info'                     => 'あなたは :score ポイントを持っており、 :scoreM 無料のデータ通信量を利用できます。',
+    'redeem_info'                     => 'あなたは :score ポイントを持っており、 :score M 無料のデータ通信量を利用できます。',
     'coupon_code'                     => 'クーポンコード',
     'please_input_coupon'             => 'クーポンコードを入力してください',
     'scan_qrcode'                     => 'QRコードをスキャンするクライアントを使用してください',
@@ -96,7 +96,7 @@ return [
     'service_type_2'                  => 'コース',
 
     // 流量日志
-    'traffic_log_tips'                => 'ヒント:流量統計図更新は遅延がある。日の統計図を押すと翌日に更新され、時間統計図で次の時間に更新されます。',
+    'traffic_log_tips'                => '注意:データの更新が遅れている',
     'traffic_log_30days'              => '30 日内のデータ通信量消耗',
     'traffic_log_24hours'             => '24 時間内のデータ通信量消耗',
     'traffic_log_keywords'            => 'データ通信量消耗',

+ 3 - 3
resources/lang/ko/home.php

@@ -35,7 +35,7 @@ return [
     'account_bandwidth_usage'         => '트래픽 사용량',
     'account_total_traffic'           => '총',
     'account_usage_traffic'           => '이미사용',
-    'account_reset_notice'            => '매월:reset_day일에 트래픽 초기화',
+    'account_reset_notice'            => '매월 :reset_day 일에 트래픽 초기화',
     'article_title'                   => '공지사항',
     'recharge'                        => '충전',
     'enabled'                         => '정상',
@@ -49,7 +49,7 @@ return [
     'close'                           => '취소',
     'redeem_score'                    => '트래픽 ',
     'redeem'                          => '환전하기',
-    'redeem_info'                     => '총 :score 포인트,:scoreM 의 트레픽 화전가능。',
+    'redeem_info'                     => '총 :score 포인트,:score M 의 트레픽 화전가능。',
     'coupon_code'                     => '충전카드',
     'please_input_coupon'             => '충전 카드 번호를 입력하세요',
     'scan_qrcode'                     => '앱 으로 QR코드를 스켄해주시면 서버가 추가 됩니다',
@@ -94,7 +94,7 @@ return [
     'service_type_2'                  => '세트',
 
     // 流量日志
-    'traffic_log_tips'                => '주의하다:유량 통계도 갱신 지연.천인 통계도로는 이튿날 갱신하며 시간별로 통계도가 1 시간에 갱신한다.',
+    'traffic_log_tips'                => '메모:데이터 갱신 지연.',
     'traffic_log_30days'              => '30 일트래픽 사용현황',
     'traffic_log_24hours'             => '24 간트패픽 사용현환',
     'traffic_log_keywords'            => '사용된트래픽',

+ 3 - 3
resources/lang/zh-CN/home.php

@@ -35,7 +35,7 @@ return [
     'account_bandwidth_usage'         => '已用流量',
     'account_total_traffic'           => '共计',
     'account_usage_traffic'           => '已使用',
-    'account_reset_notice'            => '每月:reset_day日重置',
+    'account_reset_notice'            => '每月 :reset_day 日重置',
     'article_title'                   => '文章',
     'recharge'                        => '充值',
     'enabled'                         => '正常',
@@ -49,7 +49,7 @@ return [
     'close'                           => '取消',
     'redeem_score'                    => '兑换流量',
     'redeem'                          => '立即兑换',
-    'redeem_info'                     => '您有 :score 积分,可兑换 :scoreM 免费流量。',
+    'redeem_info'                     => '您有 :score 积分,可兑换 :score M 免费流量。',
     'coupon_code'                     => '充值券码',
     'please_input_coupon'             => '请输入充值券码',
     'scan_qrcode'                     => '请使用客户端扫描二维码',
@@ -96,7 +96,7 @@ return [
     'service_type_2'                  => '套餐',
 
     // 流量日志
-    'traffic_log_tips'                => '提示:流量统计更新会有延迟。按天统计于次日更新,按小时统计于次小时更新。(每月1日不显示月流量统计数据)',
+    'traffic_log_tips'                => '提示:流量统计更新会有延迟。按天统计于次日更新,按小时统计于次小时更新。',
     'traffic_log_30days'              => '本月流量使用情况',
     'traffic_log_24hours'             => '今日流量使用情况',
     'traffic_log_keywords'            => '使用流量',

+ 3 - 3
resources/lang/zh-tw/home.php

@@ -35,7 +35,7 @@ return [
     'account_bandwidth_usage'         => '已用流量',
     'account_total_traffic'           => '共計',
     'account_usage_traffic'           => '已使用',
-    'account_reset_notice'            => '每月:reset_day日重置',
+    'account_reset_notice'            => '每月 :reset_day 日重置',
     'article_title'                   => '文章',
     'recharge'                        => '充值',
     'enabled'                         => '正常',
@@ -49,7 +49,7 @@ return [
     'close'                           => '取消',
     'redeem_score'                    => '兌換流量',
     'redeem'                          => '立即兌換',
-    'redeem_info'                     => '您有 :score積分,可兌換 :scoreM免費流量。',
+    'redeem_info'                     => '您有 :score 積分,可兌換 :score M 免費流量。',
     'coupon_code'                     => '優惠券碼',
     'please_input_coupon'             => '請輸入優惠券碼',
     'scan_qrcode'                     => '請使用用戶端掃描二維碼',
@@ -96,7 +96,7 @@ return [
     'service_type_2'                  => '套餐',
 
     // 流量日志
-    'traffic_log_tips'                => '提示:流量統計更新會有延遲。按天統計于次日更新,按小時統計圖于次小時更新。(每月1日不顯示月流量統計數據)',
+    'traffic_log_tips'                => '提示:流量統計更新會有延遲。按天統計于次日更新,按小時統計圖于次小時更新。',
     'traffic_log_30days'              => '最近 30 天的流量使用情况',
     'traffic_log_24hours'             => '最近 24 小時的流量使用情况',
     'traffic_log_keywords'            => '使用流量',

+ 1 - 1
resources/views/404.blade.php

@@ -38,7 +38,7 @@
         <p><br></p>
         <div class="number font-red"> <img src="{{asset('assets/images/404.gif')}}" /> </div>
         <p><br></p>
-        <a href="{{url('admin')}}" class="btn default"> {{trans('404.back')}} </a>
+        <a href="{{url('/')}}" class="btn default"> {{trans('404.back')}} </a>
     </div>
 </div>
 <!--[if lt IE 9]>

+ 4 - 4
resources/views/admin/nodeMonitor.blade.php

@@ -12,7 +12,7 @@
             <div class="col-md-12">
                 <div class="note note-info">
                     <h3 class="block">{{$nodeName}}<small style="padding-left:10px;">{{$nodeServer}}</small></h3>
-                    <p> 提示:月流量统计不会统计当天,日流量统计不会统计当前小时;如果无统计数据,请检查定时任务是否正常。(每月1日和每日0时因为没有统计流量,不显示流量) </p>
+                    <p> 提示:月流量统计不会统计当天,日流量统计不会统计当前小时;如果无统计数据,请检查定时任务是否正常。 </p>
                 </div>
             </div>
         </div>
@@ -47,7 +47,7 @@
         option = {
             title: {
                 text: '今日流量',
-                subtext: '单位G'
+                subtext: '单位 / G'
             },
             tooltip: {
                 trigger: 'axis'
@@ -94,7 +94,7 @@
         option = {
             title: {
                 text: '本月流量',
-                subtext: '单位G'
+                subtext: '单位 / G'
             },
             tooltip: {
                 trigger: 'axis'
@@ -108,7 +108,7 @@
             xAxis: {
                 type: 'category',
                 boundaryGap: false,
-                data: ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','30','31']
+                data: [{!! $monthDays !!}]
             },
             yAxis: {
                 type: 'value',

+ 2 - 2
resources/views/admin/userMonitor.blade.php

@@ -47,7 +47,7 @@
         option = {
             title: {
                 text: '今日流量',
-                subtext: '单位G'
+                subtext: '单位 / G'
             },
             tooltip: {
                 trigger: 'axis'
@@ -96,7 +96,7 @@
         option = {
             title: {
                 text: '本月流量',
-                subtext: '单位G'
+                subtext: '单位 / G'
             },
             tooltip: {
                 trigger: 'axis'

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

@@ -60,7 +60,7 @@
             xAxis: {
                 type: 'category',
                 boundaryGap: false,
-                data: ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31']
+                data: [{!! $monthDays !!}]
             },
             yAxis: {
                 type: 'value',

+ 1 - 1
routes/web.php

@@ -72,7 +72,7 @@ Route::group(['middleware' => ['forbidden', 'user', 'admin']], function () {
     Route::get('admin/system', 'AdminController@system'); // 系统设置
     Route::post('admin/setConfig', 'AdminController@setConfig'); // 设置某个配置项
     Route::post('admin/setReferralPercent', 'AdminController@setReferralPercent'); // 设置返利比例
-    Route::post('admin/setQrcode', 'AdminController@setQrcode'); // 设置充值二维码
+    Route::post('admin/setQrcode', 'AdminController@setQrcode'); // 设置充值二维码(已废弃)
     Route::post('admin/resetUserTraffic', 'AdminController@resetUserTraffic'); // 重置用户流量
     Route::post('admin/handleUserBalance', 'AdminController@handleUserBalance'); // 余额充值
     Route::get('admin/userBalanceLogList', 'AdminController@userBalanceLogList'); // 余额变动日志

+ 5 - 0
sql/db.sql

@@ -42,6 +42,10 @@ CREATE TABLE `ss_node` (
   `traffic` BIGINT(20) NOT NULL DEFAULT '1000' COMMENT '每月可用流量,单位G',
   `monitor_url` VARCHAR(255) NULL DEFAULT NULL COMMENT '监控地址',
   `is_subscribe` TINYINT(4) NULL DEFAULT '1' COMMENT '是否允许用户订阅该节点:0-否、1-是',
+  `ssh_port` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '22' COMMENT 'SSH端口',
+  `icmp` TINYINT(4) NOT NULL DEFAULT '1' COMMENT 'ICMP检测:-2-内外都不通、-1-内不通外通、0-外不通内通、1-内外都通',
+  `tcp` TINYINT(4) NOT NULL DEFAULT '1' COMMENT 'TCP检测:-2-内外都不通、-1-内不通外通、0-外不通内通、1-内外都通',
+  `udp` TINYINT(4) NOT NULL DEFAULT '1' COMMENT 'ICMP检测:-2-内外都不通、-1-内不通外通、0-外不通内通、1-内外都通',
   `compatible` TINYINT(4) NULL DEFAULT '0' COMMENT '兼容SS',
   `single` TINYINT(4) NULL DEFAULT '0' COMMENT '单端口多用户:0-否、1-是',
   `single_force` TINYINT(4) NULL DEFAULT NULL COMMENT '模式:0-兼容模式、1-严格模式',
@@ -485,6 +489,7 @@ CREATE TABLE `coupon_log` (
   `coupon_id` int(11) NOT NULL DEFAULT '0' COMMENT '优惠券ID',
   `goods_id` int(11) NOT NULL DEFAULT '0' COMMENT '商品ID',
   `order_id` int(11) NOT NULL DEFAULT '0' COMMENT '订单ID',
+  `desc` varchar(50) NOT NULL DEFAULT '' COMMENT '备注',
   `created_at` datetime DEFAULT NULL COMMENT '创建时间',
   `updated_at` datetime DEFAULT NULL COMMENT '最后更新时间',
   PRIMARY KEY (`id`)

+ 8 - 0
sql/update/20180724.sql

@@ -0,0 +1,8 @@
+-- 节点表增加防墙监测字段
+ALTER TABLE `ss_node` ADD COLUMN `ssh_port` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '22' COMMENT 'SSH端口' AFTER `is_subscribe`;
+ALTER TABLE `ss_node` ADD COLUMN `icmp` TINYINT(4) NOT NULL DEFAULT '1' COMMENT 'ICMP检测:-2-内外都不通、-1-内不通外通、0-外不通内通、1-内外都通'  AFTER `ssh_port`;
+ALTER TABLE `ss_node` ADD COLUMN `tcp` TINYINT(4) NOT NULL DEFAULT '1' COMMENT 'TCP检测:-2-内外都不通、-1-内不通外通、0-外不通内通、1-内外都通' AFTER `icmp`;
+ALTER TABLE `ss_node` ADD COLUMN `udp` TINYINT(4) NOT NULL DEFAULT '1' COMMENT 'ICMP检测:-2-内外都不通、-1-内不通外通、0-外不通内通、1-内外都通' AFTER `tcp`;
+
+-- 优惠券操作日志加备注字段
+ALTER TABLE `coupon_log` ADD COLUMN `desc` varchar(50) NOT NULL DEFAULT '' COMMENT '备注' AFTER `order_id`;