Преглед на файлове

1.账号邮件激活功能
2.节点分组(未完成)
3.代码优化

zhangjiangbin преди 8 години
родител
ревизия
91b98b5e8d

+ 168 - 8
app/Http/Controllers/AdminController.php

@@ -6,6 +6,7 @@ use App\Http\Models\Article;
 use App\Http\Models\Config;
 use App\Http\Models\Invite;
 use App\Http\Models\SsConfig;
+use App\Http\Models\SsGroup;
 use App\Http\Models\SsNode;
 use App\Http\Models\SsNodeInfo;
 use App\Http\Models\SsNodeOnlineLog;
@@ -479,6 +480,26 @@ class AdminController extends BaseController
         }
     }
 
+    // 删除节点
+    public function delNode(Request $request)
+    {
+        if (!$request->session()->has('user')) {
+            return Redirect::to('login');
+        }
+
+        if (!$request->session()->get('user')['is_admin']) {
+            return Redirect::to('login');
+        }
+
+        $id = $request->get('id');
+        $user = SsNode::where('id', $id)->delete();
+        if ($user) {
+            return Response::json(['status' => 'success', 'data' => '', 'message' => '删除成功']);
+        } else {
+            return Response::json(['status' => 'fail', 'data' => '', 'message' => '删除失败']);
+        }
+    }
+
     // 文章列表
     public function articleList(Request $request)
     {
@@ -582,8 +603,52 @@ class AdminController extends BaseController
         }
     }
 
-    // 删除文章
-    public function delNode(Request $request)
+    // 节点分组列表
+    public function groupList(Request $request)
+    {
+        if (!$request->session()->has('user')) {
+            return Redirect::to('login');
+        }
+
+        if (!$request->session()->get('user')['is_admin']) {
+            return Redirect::to('login');
+        }
+
+        $view['groupList'] = SsGroup::paginate(10);
+
+        return Response::view('admin/groupList', $view);
+    }
+
+    // 添加节点分组
+    public function addGroup(Request $request)
+    {
+        if (!$request->session()->has('user')) {
+            return Redirect::to('login');
+        }
+
+        if (!$request->session()->get('user')['is_admin']) {
+            return Redirect::to('login');
+        }
+
+        if ($request->method() == 'POST') {
+            $name = $request->get('name');
+            $server = $request->get('server');
+            $method = $request->get('method');
+
+            SsNode::create([
+                'name' => $name,
+                'server' => $server,
+                'method' => $method
+            ]);
+
+            return Response::json(['status' => 'success', 'data' => '', 'message' => '添加成功']);
+        } else {
+            return Response::view('admin/addGroup');
+        }
+    }
+
+    // 编辑节点分组
+    public function editGroup(Request $request)
     {
         if (!$request->session()->has('user')) {
             return Redirect::to('login');
@@ -594,7 +659,43 @@ class AdminController extends BaseController
         }
 
         $id = $request->get('id');
-        $user = SsNode::where('id', $id)->delete();
+        if ($request->method() == 'POST') {
+            $name = $request->get('name');
+            $server = $request->get('server');
+            $method = $request->get('method');
+
+            $data = [
+                'name' => $name,
+                'server' => $server,
+                'method' => $method
+            ];
+
+            $ret = SsNode::where('id', $id)->update($data);
+            if ($ret) {
+                return Response::json(['status' => 'success', 'data' => '', 'message' => '编辑成功']);
+            } else {
+                return Response::json(['status' => 'fail', 'data' => '', 'message' => '编辑失败']);
+            }
+        } else {
+            $view['group'] = SsNode::where('id', $id)->first();
+
+            return Response::view('admin/editNode', $view);
+        }
+    }
+
+    // 删除节点分组
+    public function delGroup(Request $request)
+    {
+        if (!$request->session()->has('user')) {
+            return Redirect::to('login');
+        }
+
+        if (!$request->session()->get('user')['is_admin']) {
+            return Redirect::to('login');
+        }
+
+        $id = $request->get('id');
+        $user = SsGroup::where('id', $id)->delete();
         if ($user) {
             return Response::json(['status' => 'success', 'data' => '', 'message' => '删除成功']);
         } else {
@@ -1130,6 +1231,14 @@ TXT;
     // 设置某个配置项
     public function setConfig(Request $request)
     {
+        if (!$request->session()->has('user')) {
+            return Redirect::to('login');
+        }
+
+        if (!$request->session()->get('user')['is_admin']) {
+            return Redirect::to('login');
+        }
+
         $name = trim($request->get('name'));
         $value = trim($request->get('value'));
 
@@ -1143,7 +1252,7 @@ TXT;
         }
 
         // 如果开启用户邮件重置密码,则先设置网站名称和网址
-        if ($name == 'is_reset_password' && $value == '1') {
+        if (($name == 'is_reset_password' || $name == 'is_active_register') && $value == '1') {
             $config = Config::where('name', 'website_name')->first();
             if ($config->value == '') {
                 return Response::json(['status' => 'fail', 'data' => '', 'message' => '设置失败:开启重置密码需要先设置【网站名称】']);
@@ -1166,9 +1275,17 @@ TXT;
     // 设置可生成邀请码数
     public function setInviteNum(Request $request)
     {
+        if (!$request->session()->has('user')) {
+            return Redirect::to('login');
+        }
+
+        if (!$request->session()->get('user')['is_admin']) {
+            return Redirect::to('login');
+        }
+
         $value = intval($request->get('value'));
 
-        Config::where('id', 3)->update(['value' => $value]);
+        Config::where('name', 'invite_num')->update(['value' => $value]);
 
         return Response::json(['status' => 'success', 'data' => '', 'message' => '设置成功']);
     }
@@ -1176,9 +1293,17 @@ TXT;
     // 设置网站名称
     public function setWebsiteName(Request $request)
     {
+        if (!$request->session()->has('user')) {
+            return Redirect::to('login');
+        }
+
+        if (!$request->session()->get('user')['is_admin']) {
+            return Redirect::to('login');
+        }
+
         $value = trim($request->get('value'));
 
-        Config::where('id', 6)->update(['value' => $value]);
+        Config::where('name', 'website_name')->update(['value' => $value]);
 
         return Response::json(['status' => 'success', 'data' => '', 'message' => '设置成功']);
     }
@@ -1186,9 +1311,17 @@ TXT;
     // 设置网站地址
     public function setWebsiteUrl(Request $request)
     {
+        if (!$request->session()->has('user')) {
+            return Redirect::to('login');
+        }
+
+        if (!$request->session()->get('user')['is_admin']) {
+            return Redirect::to('login');
+        }
+
         $value = trim($request->get('value'));
 
-        Config::where('id', 9)->update(['value' => $value]);
+        Config::where('name', 'website_url')->update(['value' => $value]);
 
         return Response::json(['status' => 'success', 'data' => '', 'message' => '设置成功']);
     }
@@ -1196,9 +1329,35 @@ TXT;
     // 设置重置密码次数
     public function setResetPasswordTimes(Request $request)
     {
+        if (!$request->session()->has('user')) {
+            return Redirect::to('login');
+        }
+
+        if (!$request->session()->get('user')['is_admin']) {
+            return Redirect::to('login');
+        }
+
+        $value = intval($request->get('value'));
+
+        Config::where('name', 'reset_password_times')->update(['value' => $value]);
+
+        return Response::json(['status' => 'success', 'data' => '', 'message' => '设置成功']);
+    }
+
+    // 设置激活账号次数
+    public function setActiveTimes(Request $request)
+    {
+        if (!$request->session()->has('user')) {
+            return Redirect::to('login');
+        }
+
+        if (!$request->session()->get('user')['is_admin']) {
+            return Redirect::to('login');
+        }
+
         $value = intval($request->get('value'));
 
-        Config::where('id', 8)->update(['value' => $value]);
+        Config::where('name', 'active_times')->update(['value' => $value]);
 
         return Response::json(['status' => 'success', 'data' => '', 'message' => '设置成功']);
     }
@@ -1244,4 +1403,5 @@ TXT;
 
         return Response::json(['status' => 'success', 'data' => '', 'message' => '生成成功']);
     }
+
 }

+ 13 - 2
app/Http/Controllers/LoginController.php

@@ -12,8 +12,15 @@ use Redirect;
  * Class LoginController
  * @package App\Http\Controllers
  */
-class LoginController extends Controller
+class LoginController extends BaseController
 {
+    protected static $config;
+
+    function __construct()
+    {
+        self::$config = $this->systemConfig();
+    }
+
     // 登录页
     public function index(Request $request)
     {
@@ -32,10 +39,14 @@ class LoginController extends Controller
                 $request->session()->flash('errorMsg', '用户名或密码错误');
 
                 return Redirect::back()->withInput();
-            } else if (!$user->enable) {
+            } else if ($user->status < 0) {
                 $request->session()->flash('errorMsg', '账号已禁用');
 
                 return Redirect::back();
+            } else if ($user->status == 0 && self::$config['is_active_register'] && $user->is_admin == 0) {
+                $request->session()->flash('errorMsg', '账号未激活,请先<a href="/activeUser?username=' . $user->username . '" target="_blank">【激活账号】</a>');
+
+                return Redirect::back()->withInput();
             }
 
             $request->session()->put('user', $user->toArray());

+ 121 - 3
app/Http/Controllers/UserController.php

@@ -11,6 +11,7 @@ use App\Http\Models\SsNodeOnlineLog;
 use App\Http\Models\User;
 use App\Http\Models\UserTrafficLog;
 use App\Http\Models\Verify;
+use App\Mail\activeUser;
 use App\Mail\resetPassword;
 use Illuminate\Http\Request;
 use Redirect;
@@ -272,7 +273,116 @@ TXT;
         return Response::json(['status' => 'success', 'data' => '', 'message' => '生成成功']);
     }
 
-    // 重设密码
+    // 激活账号页
+    public function activeUser(Request $request)
+    {
+        if ($request->method() == 'POST') {
+            $username = trim($request->get('username'));
+
+            // 是否开启账号激活
+            if (!self::$config['is_active_register']) {
+                $request->session()->flash('errorMsg', '系统未开启账号激活功能,请联系管理员');
+
+                return Redirect::back()->withInput();
+            }
+
+            // 查找账号
+            $user = User::where('username', $username)->first();
+            if (!$user) {
+                $request->session()->flash('errorMsg', '账号不存在,请重试');
+
+                return Redirect::back();
+            } else if ($user->status < 0) {
+                $request->session()->flash('errorMsg', '账号已禁止登陆,无需激活');
+
+                return Redirect::back();
+            } else if ($user->status > 0) {
+                $request->session()->flash('errorMsg', '账号无需激活');
+
+                return Redirect::back();
+            }
+
+            // 24小时内激活次数限制
+            $activeTimes = 0;
+            if (Cache::has('activeUser_' . md5($username))) {
+                $activeTimes = Cache::get('activeUser_' . md5($username));
+                if ($activeTimes >= self::$config['active_times']) {
+                    $request->session()->flash('errorMsg', '同一个账号24小时内只能请求激活' . self::$config['active_times'] . '次,请勿频繁操作');
+
+                    return Redirect::back();
+                }
+            }
+
+            // 生成激活账号的地址
+            $token = md5(self::$config['website_name'] . $username . microtime());
+            $verify = new Verify();
+            $verify->user_id = $user->id;
+            $verify->username = $username;
+            $verify->token = $token;
+            $verify->status = 0;
+            $verify->save();
+
+            // 发送邮件
+            $activeUserUrl = self::$config['website_url'] . '/active/' . $token;
+            Mail::to($user->username)->send(new activeUser(self::$config['website_name'], $activeUserUrl));
+
+            Cache::put('activeUser_' . md5($username), $activeTimes + 1, 1440);
+            $request->session()->flash('successMsg', '邮件已发送,请查看邮箱');
+
+            return Redirect::back();
+        } else {
+            $view['is_active_register'] = self::$config['is_active_register'];
+
+            return Response::view('user/activeUser', $view);
+        }
+    }
+
+    // 激活账号
+    public function active(Request $request, $token)
+    {
+        if (empty($token)) {
+            return Redirect::to('login');
+        }
+
+        $verify = Verify::where('token', $token)->with('user')->first();
+        if (empty($verify)) {
+            return Redirect::to('login');
+        } else if ($verify->status == 1) {
+            $request->session()->flash('errorMsg', '该链接已失效');
+
+            return Response::view('user/active');
+        } else if ($verify->user->status != 0) {
+            $request->session()->flash('errorMsg', '该账号无需激活.');
+
+            return Response::view('user/active');
+        } else if (time() - strtotime($verify->created_at) >= 1800) {
+            $request->session()->flash('errorMsg', '该链接已过期');
+
+            // 置为已失效
+            $verify->status = 2;
+            $verify->save();
+
+            return Response::view('user/active');
+        }
+
+        // 更新账号状态
+        $ret = User::where('id', $verify->user_id)->update(['status' => 1]);
+        if (!$ret) {
+            $request->session()->flash('errorMsg', '账号激活失败');
+
+            return Redirect::back();
+        }
+
+        // 置为已使用
+        $verify->status = 1;
+        $verify->save();
+
+        $request->session()->flash('successMsg', '账号激活成功');
+
+        return Response::view('user/active');
+    }
+
+    // 重设密码页
     public function resetPassword(Request $request)
     {
         if ($request->method() == 'POST') {
@@ -355,7 +465,7 @@ TXT;
                 $request->session()->flash('errorMsg', '该链接已失效');
 
                 return Redirect::back();
-            } else if (!$verify->user->enable) {
+            } else if ($verify->user->status < 0) {
                 $request->session()->flash('errorMsg', '账号已被禁用');
 
                 return Redirect::back();
@@ -373,7 +483,7 @@ TXT;
                 return Redirect::back();
             }
 
-            // 更新verify
+            // 置为已使用
             $verify->status = 1;
             $verify->save();
 
@@ -388,6 +498,14 @@ TXT;
             $verify = Verify::where('token', $token)->with('user')->first();
             if (empty($verify)) {
                 return Redirect::to('login');
+            } else if (time() - strtotime($verify->created_at) >= 1800) {
+                $request->session()->flash('errorMsg', '该链接已过期');
+
+                // 置为已失效
+                $verify->status = 2;
+                $verify->save();
+
+                return Response::view('user/reset');
             }
 
             $view['verify'] = $verify;

+ 21 - 0
app/Http/Models/SsGroup.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace App\Http\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+/**
+ * SS节点分组
+ * Class SsNodeGroup
+ * @package App\Http\Models
+ */
+class SsGroup extends Model
+{
+    protected $table = 'ss_group';
+    protected $primaryKey = 'id';
+    protected $fillable = [
+        'name',
+        'level'
+    ];
+
+}

+ 22 - 0
app/Http/Models/SsGroupNode.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Http\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+/**
+ * SS分组和节点关联表
+ * Class SsNodeGroup
+ * @package App\Http\Models
+ */
+class SsGroupNode extends Model
+{
+    protected $table = 'ss_group_node';
+    protected $primaryKey = 'id';
+    public $timestamps = false;
+    protected $fillable = [
+        'group_id',
+        'node_id'
+    ];
+
+}

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

@@ -14,6 +14,7 @@ class Verify extends Model
     protected $table = 'verify';
     protected $primaryKey = 'id';
     protected $fillable = [
+        'user_id',
         'username',
         'token',
         'status'

+ 30 - 0
app/Mail/activeUser.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Mail;
+
+use Illuminate\Bus\Queueable;
+use Illuminate\Mail\Mailable;
+use Illuminate\Queue\SerializesModels;
+use Illuminate\Contracts\Queue\ShouldQueue;
+
+class activeUser extends Mailable
+{
+    use Queueable, SerializesModels;
+
+    protected $websiteName;
+    protected $activeUserUrl;
+
+    public function __construct($websiteName, $activeUserUrl)
+    {
+        $this->websiteName = $websiteName;
+        $this->activeUserUrl = $activeUserUrl;
+    }
+
+    public function build()
+    {
+        return $this->view('emails.activeUser')->subject('激活账号')->with([
+            'websiteName' => $this->websiteName,
+            'activeUserUrl' => $this->activeUserUrl
+        ]);
+    }
+}

+ 109 - 0
resources/views/admin/addGroup.blade.php

@@ -0,0 +1,109 @@
+@extends('admin.layouts')
+
+@section('css')
+    <link href="/assets/global/plugins/bootstrap-datepicker/css/bootstrap-datepicker3.min.css" rel="stylesheet" type="text/css" />
+@endsection
+@section('title', '控制面板')
+@section('content')
+    <!-- BEGIN CONTENT BODY -->
+    <div class="page-content">
+        <!-- BEGIN PAGE BREADCRUMB -->
+        <ul class="page-breadcrumb breadcrumb">
+            <li>
+                <a href="javascript:;">节点管理</a>
+                <i class="fa fa-circle"></i>
+            </li>
+            <li>
+                <a href="{{url('admin/groupList')}}">节点分组</a>
+                <i class="fa fa-circle"></i>
+            </li>
+            <li>
+                <a href="{{url('admin/addGroup')}}">添加节点分组</a>
+            </li>
+        </ul>
+        <!-- END PAGE BREADCRUMB -->
+        <!-- BEGIN PAGE BASE CONTENT -->
+        <div class="row">
+            <div class="col-md-6">
+                <div class="tab-pane active">
+                    <div class="portlet light bordered">
+                        <div class="portlet-body form">
+                            <!-- BEGIN FORM-->
+                            <form action="{{url('admin/addGroup')}}" method="post" class="form-horizontal" onsubmit="return do_submit();">
+                                <div class="form-body">
+                                    <div class="form-group">
+                                        <label for="name" class="col-md-3 control-label"> 分组名称 </label>
+                                        <div class="col-md-8">
+                                            <input type="text" class="form-control" name="name" id="name" placeholder="" autofocus required>
+                                            <input type="hidden" name="_token" value="{{csrf_token()}}">
+                                        </div>
+                                    </div>
+                                    <div class="form-group">
+                                        <label for="server" class="col-md-3 control-label"> 级别 </label>
+                                        <div class="col-md-8">
+                                            <input type="text" class="form-control" name="level" id="level" placeholder="" required>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="form-actions">
+                                    <div class="row">
+                                        <div class="col-md-offset-6 col-md-4">
+                                            <button type="submit" class="btn green"> 提 交 </button>
+                                        </div>
+                                    </div>
+                                </div>
+                            </form>
+                            <!-- END FORM-->
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <!-- END PAGE BASE CONTENT -->
+    </div>
+    <!-- END CONTENT BODY -->
+@endsection
+@section('script')
+    <script src="/assets/global/plugins/bootbox/bootbox.min.js" type="text/javascript"></script>
+
+    <script type="text/javascript">
+        // ajax同步提交
+        function do_submit() {
+            var _token = '{{csrf_token()}}';
+            var name = $('#name').val();
+            var server = $('#server').val();
+            var method = $('#method').val();
+            var custom_method = $('#custom_method').val();
+            var traffic_rate = $('#traffic_rate').val();
+            var protocol = $('#protocol').val();
+            var protocol_param = $('#protocol_param').val();
+            var obfs = $('#obfs').val();
+            var obfs_param = $('#obfs_param').val();
+            var bandwidth = $('#bandwidth').val();
+            var traffic = $('#traffic').val();
+            var monitor_url = $('#monitor_url').val();
+            var compatible = $('#compatible').val();
+            var sort = $('#sort').val();
+            var status = $('#status').val();
+
+            $.ajax({
+                type: "POST",
+                url: "{{url('admin/addNode')}}",
+                async: false,
+                data: {_token:_token, name: name, server:server, method:method, custom_method:custom_method, traffic_rate:traffic_rate, protocol:protocol, protocol_param:protocol_param, obfs:obfs, obfs_param:obfs_param, bandwidth:bandwidth, traffic:traffic, monitor_url:monitor_url, compatible:compatible, sort:sort, status:status},
+                dataType: 'json',
+                success: function (ret) {
+                    if (ret.status == 'success') {
+                        bootbox.alert(ret.message, function () {
+                            window.location.href = '{{url('admin/nodeList')}}';
+                        });
+                    } else {
+                        bootbox.alert(ret.message);
+                    }
+                }
+            });
+
+            return false;
+        }
+    </script>
+@endsection

+ 5 - 7
resources/views/admin/config.blade.php

@@ -70,7 +70,7 @@
                                                     <td> {{$method->name}} @if($method->is_default) <small><span class='label label-info label-sm'>默认</span></small> @endif </td>
                                                     <td>
                                                         @if(!$method->is_default)
-                                                            <button type="button" class="btn btn-sm blue btn-outline" onclick="setDefault('{{$method->id}}')">默认</button>
+                                                            <button type="button" class="btn btn-sm blue btn-outline" onclick="setDefault('1', '{{$method->id}}')">默认</button>
                                                             <button type="button" class="btn btn-sm red btn-outline" onclick="delConfig('1', '{{$method->id}}')">删除</button>
                                                         @endif
                                                     </td>
@@ -103,7 +103,7 @@
                                                     <td> {{$protocol->name}} @if($protocol->is_default) <small><span class='label label-info label-sm'>默认</span></small> @endif </td>
                                                     <td>
                                                         @if(!$protocol->is_default)
-                                                            <button type="button" class="btn btn-sm blue btn-outline" onclick="setDefault('{{$protocol->id}}')">默认</button>
+                                                            <button type="button" class="btn btn-sm blue btn-outline" onclick="setDefault('2', '{{$protocol->id}}')">默认</button>
                                                             <button type="button" class="btn btn-sm red btn-outline" onclick="delConfig('2', '{{$protocol->id}}')">删除</button>
                                                         @endif
                                                     </td>
@@ -136,7 +136,7 @@
                                                     <td> {{$obfs->name}} @if($obfs->is_default) <small><span class='label label-info label-sm'>默认</span></small> @endif </td>
                                                     <td>
                                                         @if(!$obfs->is_default)
-                                                            <button type="button" class="btn btn-sm blue btn-outline" onclick="setDefault('{{$obfs->id}}')">默认</button>
+                                                            <button type="button" class="btn btn-sm blue btn-outline" onclick="setDefault('3', '{{$obfs->id}}')">默认</button>
                                                             <button type="button" class="btn btn-sm red btn-outline" onclick="delConfig('3', '{{$obfs->id}}')">删除</button>
                                                         @endif
                                                     </td>
@@ -273,7 +273,7 @@
         }
 
         // 置为默认
-        function setDefault(id) {
+        function setDefault(tabId, id) {
             var _token = '{{csrf_token()}}';
 
             $.ajax({
@@ -284,9 +284,7 @@
                 dataType: 'json',
                 success: function (ret) {
                     if (ret.status == 'success') {
-                        bootbox.alert(ret.message, function () {
-                            window.location.reload();
-                        });
+                        window.location.href = '{{url('admin/config?tab=')}}' + tabId;
                     } else {
                         bootbox.alert(ret.message);
                     }

+ 137 - 0
resources/views/admin/groupList.blade.php

@@ -0,0 +1,137 @@
+@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" />
+@endsection
+@section('title', '控制面板')
+@section('content')
+    <!-- BEGIN CONTENT BODY -->
+    <div class="page-content">
+        <!-- BEGIN PAGE BREADCRUMB -->
+        <ul class="page-breadcrumb breadcrumb">
+            <li>
+                <a href="javascript:;">节点管理</a>
+                <i class="fa fa-circle"></i>
+            </li>
+            <li>
+                <a href="{{url('admin/groupList')}}">节点分组列表</a>
+            </li>
+        </ul>
+        <!-- END PAGE BREADCRUMB -->
+        <!-- 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">
+                            <i class="icon-list font-dark"></i>
+                            <span class="caption-subject bold uppercase"> 节点分组列表 </span>
+                        </div>
+                        <div class="actions">
+                            <div class="btn-group">
+                                <button id="sample_editable_1_new" class="btn sbold blue" onclick="addGroup()"> 新增
+                                    <i class="fa fa-plus"></i>
+                                </button>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="portlet-body">
+                        <div class="table-scrollable">
+                            <table class="table table-striped table-bordered table-hover table-checkable order-column">
+                                <thead>
+                                <tr>
+                                    <th> ID </th>
+                                    <th> 分组名称 </th>
+                                    <th> 限制 </th>
+                                    <th> 操作 </th>
+                                </tr>
+                                </thead>
+                                <tbody>
+                                    @if($groupList->isEmpty())
+                                        <tr>
+                                            <td colspan="4">暂无数据</td>
+                                        </tr>
+                                    @else
+                                        @foreach($groupList as $group)
+                                            <tr class="odd gradeX">
+                                                <td> {{$group->id}} </td>
+                                                <td> {{$group->name}} </td>
+                                                <td> {{$node->level}} </td>
+                                                <td>
+                                                    <button type="button" class="btn btn-sm blue btn-outline" onclick="editNode('{{$node->id}}')">编辑</button>
+                                                    <button type="button" class="btn btn-sm red btn-outline" onclick="delNode('{{$node->id}}')">删除</button>
+                                                </td>
+                                            </tr>
+                                        @endforeach
+                                    @endif
+                                </tbody>
+                            </table>
+                        </div>
+                        <div class="row">
+                            <div class="col-md-5 col-sm-5">
+                                <div class="dataTables_info" role="status" aria-live="polite">共 {{$groupList->total()}} 个节点</div>
+                            </div>
+                            <div class="col-md-7 col-sm-7">
+                                <div class="dataTables_paginate paging_bootstrap_full_number pull-right">
+                                    {{$groupList->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/bootbox/bootbox.min.js" type="text/javascript"></script>
+
+    <script type="text/javascript">
+        // 添加节点分组
+        function addGroup() {
+            window.location.href = '{{url('admin/addGroup')}}';
+        }
+
+        // 编辑节点分组
+        function editGroup(id) {
+            window.location.href = '{{url('admin/editGroup?id=')}}' + id + '&page=' + '{{Request::get('page', 1)}}';
+        }
+
+        // 删除节点分组
+        function delGroup(id) {
+            var _token = '{{csrf_token()}}';
+
+            bootbox.confirm({
+                message: "确定删除节点?",
+                buttons: {
+                    confirm: {
+                        label: '确定',
+                        className: 'btn-success'
+                    },
+                    cancel: {
+                        label: '取消',
+                        className: 'btn-danger'
+                    }
+                },
+                callback: function (result) {
+                    if (result) {
+                        $.post("{{url('admin/delGroup')}}", {id:id, _token:_token}, function(ret){
+                            if (ret.status == 'success') {
+                                bootbox.alert(ret.message, function(){
+                                    window.location.reload();
+                                });
+                            } else {
+                                bootbox.alert(ret.message);
+                            }
+                        });
+                    }
+                }
+            });
+        }
+    </script>
+@endsection

+ 2 - 1
resources/views/admin/inviteList.blade.php

@@ -26,7 +26,8 @@
                         </div>
                         <div class="portlet-body">
                             <div class="alert alert-info">
-                                注意:每次仅生成 <strong> 5 </strong> 个邀请码
+                                <i class="fa fa-warning"></i>
+                                每次仅生成 <strong> 5 </strong> 个邀请码
                             </div>
                             <button type="submit" class="btn blue" onclick="makeInvite()"> 生 成 </button>
                         </div>

+ 23 - 8
resources/views/admin/layouts.blade.php

@@ -114,12 +114,6 @@
                         <span class="title">账号管理</span>
                     </a>
                 </li>
-                <li class="nav-item {{Request::getRequestUri() == '/admin/nodeList' ? 'active open' : ''}}">
-                    <a href="{{url('admin/nodeList')}}" class="nav-link nav-toggle">
-                        <i class="icon-list"></i>
-                        <span class="title">节点管理</span>
-                    </a>
-                </li>
                 <li class="nav-item {{Request::getRequestUri() == '/admin/inviteList' ? 'active open' : ''}}">
                     <a href="{{url('admin/inviteList')}}" class="nav-link nav-toggle">
                         <i class="icon-puzzle"></i>
@@ -138,9 +132,30 @@
                         <span class="title">流量日志</span>
                     </a>
                 </li>
-                <li class="nav-item {{Request::getRequestUri() == '/admin/convert' || Request::getRequestUri() == '/admin/import' || Request::getRequestUri() == '/admin/analysis' ? 'active open' : ''}}">
+                <li class="nav-item {{in_array(Request::getRequestUri(), ['/admin/nodeList', '/admin/addNode', '/admin/editNode', '/admin/groupList', '/admin/addGroup', '/admin/editGroup']) ? 'active open' : ''}}">
                     <a href="javascript:;" class="nav-link nav-toggle">
                         <i class="icon-layers"></i>
+                        <span class="title">节点管理</span>
+                        <span class="arrow"></span>
+                    </a>
+                    <ul class="sub-menu">
+                        <li class="nav-item {{Request::getRequestUri() == '/admin/nodeList' ? 'active open' : ''}}">
+                            <a href="{{url('admin/nodeList')}}" class="nav-link ">
+                                <i class="icon-list"></i>
+                                <span class="title">节点列表</span>
+                            </a>
+                        </li>
+                        <li class="nav-item {{Request::getRequestUri() == '/admin/groupList' ? 'active open' : ''}}">
+                            <a href="{{url('admin/groupList')}}" class="nav-link ">
+                                <i class="icon-grid"></i>
+                                <span class="title">节点分组列表</span>
+                            </a>
+                        </li>
+                    </ul>
+                </li>
+                <li class="nav-item {{Request::getRequestUri() == '/admin/convert' || Request::getRequestUri() == '/admin/import' || Request::getRequestUri() == '/admin/analysis' ? 'active open' : ''}}">
+                    <a href="javascript:;" class="nav-link nav-toggle">
+                        <i class="icon-wrench"></i>
                         <span class="title">工具箱</span>
                         <span class="arrow"></span>
                     </a>
@@ -180,7 +195,7 @@
                         </li>
                         <li class="nav-item {{Request::getRequestUri() == '/admin/system' ? 'active open' : ''}}">
                             <a href="{{url('admin/system')}}" class="nav-link ">
-                                <i class="icon-wrench"></i>
+                                <i class="icon-settings"></i>
                                 <span class="title">系统设置</span>
                             </a>
                         </li>

+ 5 - 2
resources/views/admin/nodeList.blade.php

@@ -11,9 +11,12 @@
         <!-- BEGIN PAGE BREADCRUMB -->
         <ul class="page-breadcrumb breadcrumb">
             <li>
-                <a href="{{url('admin/nodeList')}}">节点管理</a>
+                <a href="javascript:;">节点管理</a>
                 <i class="fa fa-circle"></i>
             </li>
+            <li>
+                <a href="{{url('admin/nodeList')}}">节点列表</a>
+            </li>
         </ul>
         <!-- END PAGE BREADCRUMB -->
         <!-- BEGIN PAGE BASE CONTENT -->
@@ -24,7 +27,7 @@
                     <div class="portlet-title">
                         <div class="caption font-dark">
                             <i class="icon-list font-dark"></i>
-                            <span class="caption-subject bold uppercase"> 节点管理</span>
+                            <span class="caption-subject bold uppercase"> 节点列表 </span>
                         </div>
                         <div class="actions">
                             <div class="btn-group">

+ 78 - 51
resources/views/admin/system.blade.php

@@ -48,8 +48,8 @@
                                                             <div class="input-group">
                                                                 <input class="form-control" type="text" name="website_name" value="{{$website_name}}" id="website_name" />
                                                                 <span class="input-group-btn">
-                                                        <button class="btn btn-success" type="button" onclick="setWebsiteName()">修改</button>
-                                                    </span>
+                                                                    <button class="btn btn-success" type="button" onclick="setWebsiteName()">修改</button>
+                                                                </span>
                                                             </div>
                                                             <span class="help-block"> 发邮件时展示 </span>
                                                         </div>
@@ -60,8 +60,8 @@
                                                             <div class="input-group">
                                                                 <input class="form-control" type="text" name="website_url" value="{{$website_url}}" id="website_url" />
                                                                 <span class="input-group-btn">
-                                                        <button class="btn btn-success" type="button" onclick="setWebsiteUrl()">修改</button>
-                                                    </span>
+                                                                    <button class="btn btn-success" type="button" onclick="setWebsiteUrl()">修改</button>
+                                                                </span>
                                                             </div>
                                                             <span class="help-block"> 生成重置密码必备,示例:https://github.com </span>
                                                         </div>
@@ -74,22 +74,24 @@
                                                         </div>
                                                     </div>
                                                     <div class="form-group">
-                                                <label for="is_invite_register" class="col-md-2 control-label">邀请注册</label>
-                                                <div class="col-md-6">
-                                                    <input type="checkbox" class="make-switch" @if($is_invite_register) checked @endif id="is_invite_register" data-on-color="success" data-off-color="danger" data-on-text="启用" data-off-text="关闭">
-                                                    <span class="help-block"> 启用后必须使用邀请码进行注册 </span>
-                                                </div>
-                                            </div>
+                                                        <label for="is_invite_register" class="col-md-2 control-label">邀请注册</label>
+                                                        <div class="col-md-6">
+                                                            <input type="checkbox" class="make-switch" @if($is_invite_register) checked @endif id="is_invite_register" data-on-color="success" data-off-color="danger" data-on-text="启用" data-off-text="关闭">
+                                                            <span class="help-block"> 启用后必须使用邀请码进行注册 </span>
+                                                        </div>
+                                                    </div>
                                                     <div class="form-group">
-                                                        <label for="invite_num" class="col-md-2 control-label">可生成邀请码数</label>
-                                                        <div class="col-md-2">
-                                                            <div class="input-group">
-                                                                <input class="form-control" type="text" name="invite_num" value="{{$invite_num}}" id="invite_num" />
-                                                                <span class="input-group-btn">
-                                                        <button class="btn btn-success" type="button" onclick="setInviteNum()">修改</button>
-                                                    </span>
-                                                            </div>
-                                                            <span class="help-block"> 用户可以生成的邀请码数 </span>
+                                                        <label for="is_active_register" class="col-md-2 control-label">激活账号</label>
+                                                        <div class="col-md-6">
+                                                            <input type="checkbox" class="make-switch" @if($is_active_register) checked @endif id="is_active_register" data-on-color="success" data-off-color="danger" data-on-text="启用" data-off-text="关闭">
+                                                            <span class="help-block"> 开启后用户需要通过邮件来激活账号 </span>
+                                                        </div>
+                                                    </div>
+                                                    <div class="form-group">
+                                                        <label for="is_reset_password" class="col-md-2 control-label">重置密码</label>
+                                                        <div class="col-md-6">
+                                                            <input type="checkbox" class="make-switch" @if($is_reset_password) checked @endif id="is_reset_password" data-on-color="success" data-off-color="danger" data-on-text="启用" data-off-text="关闭">
+                                                            <span class="help-block"> 开启后不允许用户通过邮件重置密码 </span>
                                                         </div>
                                                     </div>
                                                 </div>
@@ -113,10 +115,15 @@
                                                         </div>
                                                     </div>
                                                     <div class="form-group">
-                                                        <label for="is_reset_password" class="col-md-2 control-label">重置密码</label>
-                                                        <div class="col-md-6">
-                                                            <input type="checkbox" class="make-switch" @if($is_reset_password) checked @endif id="is_reset_password" data-on-color="success" data-off-color="danger" data-on-text="启用" data-off-text="关闭">
-                                                            <span class="help-block"> 开启后不允许用户重置密码 </span>
+                                                        <label for="invite_num" class="col-md-2 control-label">可生成邀请码数</label>
+                                                        <div class="col-md-2">
+                                                            <div class="input-group">
+                                                                <input class="form-control" type="text" name="invite_num" value="{{$invite_num}}" id="invite_num" />
+                                                                <span class="input-group-btn">
+                                                                    <button class="btn btn-success" type="button" onclick="setInviteNum()">修改</button>
+                                                                </span>
+                                                            </div>
+                                                            <span class="help-block"> 用户可以生成的邀请码数 </span>
                                                         </div>
                                                     </div>
                                                     <div class="form-group">
@@ -125,10 +132,22 @@
                                                             <div class="input-group">
                                                                 <input class="form-control" type="text" name="reset_password_times" value="{{$reset_password_times}}" id="reset_password_times" />
                                                                 <span class="input-group-btn">
-                                                        <button class="btn btn-success" type="button" onclick="setResetPasswordTimes()">修改</button>
-                                                    </span>
+                                                                    <button class="btn btn-success" type="button" onclick="setResetPasswordTimes()">修改</button>
+                                                                </span>
                                                             </div>
-                                                            <span class="help-block"> 用户可以重置密码次数 </span>
+                                                            <span class="help-block"> 24小时内可以通过邮件重置密码次数 </span>
+                                                        </div>
+                                                    </div>
+                                                    <div class="form-group">
+                                                        <label for="active_times" class="col-md-2 control-label">激活账号次数</label>
+                                                        <div class="col-md-2">
+                                                            <div class="input-group">
+                                                                <input class="form-control" type="text" name="active_times" value="{{$active_times}}" id="active_times" />
+                                                                <span class="input-group-btn">
+                                                                    <button class="btn btn-success" type="button" onclick="setActiveTimes()">修改</button>
+                                                                </span>
+                                                            </div>
+                                                            <span class="help-block"> 24小时内可以通过邮件激活账号次数 </span>
                                                         </div>
                                                     </div>
                                                 </div>
@@ -155,11 +174,7 @@
         // 启用、禁用随机端口
         $('#is_rand_port').on({
             'switchChange.bootstrapSwitch': function(event, state) {
-                var is_rand_port = 0;
-
-                if (state) {
-                    is_rand_port = 1;
-                }
+                var is_rand_port = state ? 1 : 0;
 
                 $.post("{{url('admin/setConfig')}}", {_token:'{{csrf_token()}}', name:'is_rand_port', value:is_rand_port}, function (ret) {
                     console.log(ret);
@@ -170,11 +185,7 @@
         // 启用、禁用自定义端口
         $('#is_user_rand_port').on({
             'switchChange.bootstrapSwitch': function(event, state) {
-                var is_user_rand_port = 0;
-
-                if (state) {
-                    is_user_rand_port = 1;
-                }
+                var is_user_rand_port = state ? 1 : 0;
 
                 $.post("{{url('admin/setConfig')}}", {_token:'{{csrf_token()}}', name:'is_user_rand_port', value:is_user_rand_port}, function (ret) {
                     console.log(ret);
@@ -185,11 +196,7 @@
         // 启用、禁用注册
         $('#is_register').on({
             'switchChange.bootstrapSwitch': function(event, state) {
-                var is_register = 0;
-
-                if (state) {
-                    is_register = 1;
-                }
+                var is_register = state ? 1 : 0;
 
                 $.post("{{url('admin/setConfig')}}", {_token:'{{csrf_token()}}', name:'is_register', value:is_register}, function (ret) {
                     console.log(ret);
@@ -200,11 +207,7 @@
         // 启用、禁用邀请注册
         $('#is_invite_register').on({
             'switchChange.bootstrapSwitch': function(event, state) {
-                var is_invite_register = 0;
-
-                if (state) {
-                    is_invite_register = 1;
-                }
+                var is_invite_register = state ? 1 : 0;
 
                 $.post("{{url('admin/setConfig')}}", {_token:'{{csrf_token()}}', name:'is_invite_register', value:is_invite_register}, function (ret) {
                     console.log(ret);
@@ -215,11 +218,7 @@
         // 启用、禁用用户重置密码
         $('#is_reset_password').on({
             'switchChange.bootstrapSwitch': function(event, state) {
-                var is_reset_password = 0;
-
-                if (state) {
-                    is_reset_password = 1;
-                }
+                var is_reset_password = state ? 1 : 0;
 
                 $.post("{{url('admin/setConfig')}}", {_token:'{{csrf_token()}}', name:'is_reset_password', value:is_reset_password}, function (ret) {
                     if (ret.status == 'fail') {
@@ -231,6 +230,21 @@
             }
         });
 
+        // 启用、禁用用户激活账号
+        $('#is_active_register').on({
+            'switchChange.bootstrapSwitch': function(event, state) {
+                var is_active_register = state ? 1 : 0;
+
+                $.post("{{url('admin/setConfig')}}", {_token:'{{csrf_token()}}', name:'is_active_register', value:is_active_register}, function (ret) {
+                    if (ret.status == 'fail') {
+                        bootbox.alert(ret.message, function() {
+                            window.location.reload();
+                        });
+                    }
+                });
+            }
+        });
+
         // 设置可生成邀请码数量
         function setInviteNum() {
             var invite_num = $("#invite_num").val();
@@ -257,6 +271,19 @@
             });
         }
 
+        // 设置激活账号次数
+        function setActiveTimes() {
+            var active_times = $("#active_times").val();
+
+            $.post("{{url('admin/setActiveTimes')}}", {_token:'{{csrf_token()}}', value:active_times}, function (ret) {
+                if (ret.status == 'success') {
+                    bootbox.alert(ret.message, function() {
+                        window.location.reload();
+                    });
+                }
+            });
+        }
+
         // 设置网站名称
         function setWebsiteName() {
             var website_name = $("#website_name").val();

+ 91 - 0
resources/views/emails/activeUser.blade.php

@@ -0,0 +1,91 @@
+<table class="body" style="Margin:0;background:#FAFAFA;border-collapse:collapse;border-spacing: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;vertical-align:top;width:100%">
+    <tbody>
+    <tr style="padding:0;text-align:left;vertical-align:top">
+        <td class="center" align="center" valign="top" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:19px;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+            <center data-parsed="" style="min-width:580px;width:100%">
+                <table align="center" class="container no-bg float-center" style="Margin:0 auto;background:0 0;border:0;border-collapse:collapse;border-radius:3px;border-spacing:0;box-shadow:none;float:none;margin:0 auto;margin-top:20px;padding:0;text-align:center;vertical-align:top;width:580px">
+                    <tbody>
+                    <tr style="padding:0;text-align:left;vertical-align:top">
+                        <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:19px;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+                            <table class="row" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%">
+                                <tbody>
+                                <tr style="padding:0;text-align:left;vertical-align:top">
+
+                                    <th class="small-11 large-11 columns last" style="Margin:0 auto;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0 auto;padding:0;padding-bottom:0;padding-left:8px;padding-right:16px;text-align:left;width:515.67px">
+                                        <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+                                            <tbody>
+                                            <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">
+                                                    <h3 style="Margin:0;Margin-bottom:10px;color:inherit;font-family:Helvetica,Arial,sans-serif;font-size:28px;font-weight:400;line-height:1.3;margin:0;margin-bottom:0;padding:0;text-align:left;word-wrap:normal">
+                                                        <a href="#" style="Margin:0;color:#40253b;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">
+                                                            SSRPanel
+                                                        </a>
+                                                    </h3>
+                                                </th>
+                                            </tr>
+                                            </tbody>
+                                        </table>
+                                    </th>
+                                </tr>
+                                </tbody>
+                            </table>
+                        </td>
+                    </tr>
+                    </tbody>
+                </table>
+                <table align="center" class="container float-center" style="Margin:0 auto;background:#fefefe;border:1px solid #cdcdcd;border-collapse:collapse;border-radius:3px;border-spacing:0;float:none;margin:0 auto;margin-top:20px;padding:0;text-align:center;vertical-align:top;width:580px">
+                    <tbody>
+                    <tr style="padding:0;text-align:left;vertical-align:top">
+                        <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:19px;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+                            <table class="row container-header-row" style="background-color:#5c97bd;border-collapse:collapse;border-spacing:0;color:#f3f3f3;display:table;padding:0;padding-bottom:8px;padding-top:8px;position:relative;text-align:left;vertical-align:top;width:100%">
+                                <tbody>
+                                <tr style="padding:0;text-align:left;vertical-align:top">
+                                    <th class="small-12 large-12 columns first last" style="Margin:0 auto;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0 auto;padding:0;padding-bottom:0;padding-left:16px;padding-right:16px;text-align:left;width:564px">
+                                        <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+                                            <tbody>
+                                            <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">
+                                                    <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>
+                                                <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>
+                                            </tr>
+                                            </tbody>
+                                        </table>
+                                    </th>
+                                </tr>
+                                </tbody>
+                            </table>
+                            <table class="row" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%">
+                                <tbody>
+                                <tr style="padding:0;text-align:left;vertical-align:top">
+                                    <th class="small-12 large-12 columns first last" style="Margin:0 auto;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0 auto;padding:0;padding-bottom:0;padding-left:16px;padding-right:16px;text-align:left;width:564px">
+                                        <p style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"></p>
+                                <tbody>
+                                <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>如果您并没有访问过 {{$websiteName}} 或者没有进行上述操作,请忽略这封邮件。</p>
+                                            <p>点击这里去<a href="{{$activeUserUrl}}" target="_blank">【激活账号】</a>,或者点击下面的链接(30分钟内有效)。</p>
+                                            <p>
+                                                <a href="{{$activeUserUrl}}" target="_blank">{{$activeUserUrl}}</a>
+                                            </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>
+                                </tr>
+                                </tbody>
+                                </th></tr></tbody>
+                            </table>
+                        </td>
+                    </tr>
+                    </tbody>
+                </table>
+            </center>
+        </td>
+    </tr>
+    </tbody>
+</table>

+ 3 - 3
resources/views/login.blade.php

@@ -46,17 +46,17 @@
         @if (Session::get('errorMsg'))
             <div class="alert alert-danger">
                 <button class="close" data-close="alert"></button>
-                <span> {{Session::get('errorMsg')}} </span>
+                <span> {!! Session::get('errorMsg') !!} </span>
             </div>
         @endif
         <div class="form-group">
             <!--ie8, ie9 does not support html5 placeholder, so we just show field title for that-->
-            <label class="control-label visible-ie8 visible-ie9">E-mail</label>
+            <label class="control-label visible-ie8 visible-ie9">用户名</label>
             <input class="form-control form-control-solid placeholder-no-fix" type="text" autocomplete="off" placeholder="用户名" name="username" value="{{Request::old('username')}}" /> </div>
         <div class="form-group">
             <label class="control-label visible-ie8 visible-ie9">密码</label>
             <input class="form-control form-control-solid placeholder-no-fix" type="password" autocomplete="off" placeholder="密码" name="password" value="{{Request::old('password')}}" />
-            <input type="hidden" name="_token" value="{{ csrf_token() }}" />
+            <input type="hidden" name="_token" value="{{csrf_token()}}" />
         </div>
         <div class="form-actions">
             <div class="pull-left">

+ 85 - 0
resources/views/user/active.blade.php

@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<!--[if IE 8]> <html lang="en" class="ie8 no-js"> <![endif]-->
+<!--[if IE 9]> <html lang="en" class="ie9 no-js"> <![endif]-->
+<!--[if !IE]><!-->
+<html lang="{{app()->getLocale()}}">
+<!--<![endif]-->
+
+<head>
+    <meta charset="utf-8" />
+    <title>激活账号</title>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta content="width=device-width, initial-scale=1" name="viewport" />
+    <meta content="" name="description" />
+    <meta content="" name="author" />
+    <!-- BEGIN GLOBAL MANDATORY STYLES -->
+    <link href="/assets/global/plugins/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
+    <link href="/assets/global/plugins/simple-line-icons/simple-line-icons.min.css" rel="stylesheet" type="text/css" />
+    <link href="/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
+    <link href="/assets/global/plugins/bootstrap-switch/css/bootstrap-switch.min.css" rel="stylesheet" type="text/css" />
+    <!-- END GLOBAL MANDATORY STYLES -->
+    <!-- BEGIN THEME GLOBAL STYLES -->
+    <link href="/assets/global/css/components-rounded.min.css" rel="stylesheet" id="style_components" type="text/css" />
+    <!-- END THEME GLOBAL STYLES -->
+    <!-- BEGIN PAGE LEVEL STYLES -->
+    <link href="/assets/pages/css/login-2.min.css" rel="stylesheet" type="text/css" />
+    <!-- END PAGE LEVEL STYLES -->
+    <!-- BEGIN THEME LAYOUT STYLES -->
+    <!-- END THEME LAYOUT STYLES -->
+    <link rel="shortcut icon" href="favicon.ico" />
+</head>
+
+<body class=" login">
+<!-- BEGIN LOGO -->
+<div class="logo">
+    <a href="javascript:;"> <img src="/assets/images/home_logo.png" alt="" /> </a>
+</div>
+<!-- END LOGO -->
+<!-- BEGIN LOGIN -->
+<div class="content">
+    @if(Session::get('errorMsg'))
+        <div class="alert alert-danger">
+            <span> {{Session::get('errorMsg')}} </span>
+        </div>
+    @endif
+    @if(Session::get('successMsg'))
+        <div class="alert alert-success">
+            <span> {{Session::get('successMsg')}} </span>
+        </div>
+    @endif
+    <!-- BEGIN REGISTRATION FORM -->
+    <form class="register-form" action="{{url(Request::getRequestUri())}}" method="post" style="display: block;">
+        <div class="form-actions">
+            <button type="button" class="btn btn-default" onclick="login()">登 录</button>
+        </div>
+    </form>
+    <!-- END REGISTRATION FORM -->
+</div>
+
+<!-- END LOGIN -->
+<!--[if lt IE 9]>
+<script src="/assets/global/plugins/respond.min.js"></script>
+<script src="/assets/global/plugins/excanvas.min.js"></script>
+<script src="/assets/global/plugins/ie8.fix.min.js"></script>
+<![endif]-->
+<!-- BEGIN CORE PLUGINS -->
+<script src="/assets/global/plugins/jquery.min.js" type="text/javascript"></script>
+<script src="/assets/global/plugins/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
+<!-- END CORE PLUGINS -->
+<!-- BEGIN PAGE LEVEL PLUGINS -->
+<script src="/assets/global/plugins/jquery-validation/js/jquery.validate.min.js" type="text/javascript"></script>
+<script src="/assets/global/plugins/jquery-validation/js/additional-methods.min.js" type="text/javascript"></script>
+<script src="/assets/global/plugins/jquery-validation/js/localization/messages_zh.min.js" type="text/javascript"></script>
+<!-- END PAGE LEVEL PLUGINS -->
+<script type="text/javascript">
+    // 登录
+    function login() {
+        window.location.href = '{{url('login')}}';
+    }
+</script>
+<!-- BEGIN THEME GLOBAL SCRIPTS -->
+<script src="/assets/global/scripts/app.min.js" type="text/javascript"></script>
+<!-- END THEME GLOBAL SCRIPTS -->
+</body>
+
+</html>

+ 98 - 0
resources/views/user/activeUser.blade.php

@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<!--[if IE 8]> <html lang="en" class="ie8 no-js"> <![endif]-->
+<!--[if IE 9]> <html lang="en" class="ie9 no-js"> <![endif]-->
+<!--[if !IE]><!-->
+<html lang="{{app()->getLocale()}}">
+<!--<![endif]-->
+
+<head>
+    <meta charset="utf-8" />
+    <title>激活账号</title>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta content="width=device-width, initial-scale=1" name="viewport" />
+    <meta content="" name="description" />
+    <meta content="" name="author" />
+    <!-- BEGIN GLOBAL MANDATORY STYLES -->
+    <link href="/assets/global/plugins/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
+    <link href="/assets/global/plugins/simple-line-icons/simple-line-icons.min.css" rel="stylesheet" type="text/css" />
+    <link href="/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
+    <link href="/assets/global/plugins/bootstrap-switch/css/bootstrap-switch.min.css" rel="stylesheet" type="text/css" />
+    <!-- END GLOBAL MANDATORY STYLES -->
+    <!-- BEGIN THEME GLOBAL STYLES -->
+    <link href="/assets/global/css/components-rounded.min.css" rel="stylesheet" id="style_components" type="text/css" />
+    <!-- END THEME GLOBAL STYLES -->
+    <!-- BEGIN PAGE LEVEL STYLES -->
+    <link href="/assets/pages/css/login-2.min.css" rel="stylesheet" type="text/css" />
+    <!-- END PAGE LEVEL STYLES -->
+    <!-- BEGIN THEME LAYOUT STYLES -->
+    <!-- END THEME LAYOUT STYLES -->
+    <link rel="shortcut icon" href="favicon.ico" />
+</head>
+
+<body class=" login">
+<!-- BEGIN LOGO -->
+<div class="logo">
+    <a href="javascript:;"> <img src="/assets/images/home_logo.png" alt="" /> </a>
+</div>
+<!-- END LOGO -->
+<!-- BEGIN LOGIN -->
+<div class="content">
+    @if (Session::get('errorMsg'))
+        <div class="alert alert-danger">
+            <button class="close" data-close="alert"></button>
+            <span> {{Session::get('errorMsg')}} </span>
+        </div>
+    @endif
+    @if (Session::get('successMsg'))
+        <div class="alert alert-success">
+            <button class="close" data-close="alert"></button>
+            <span> {{Session::get('successMsg')}} </span>
+        </div>
+    @endif
+    <!-- BEGIN FORGOT PASSWORD FORM -->
+    <form class="forget-form" action="{{url('activeUser')}}" method="post" style="display: block;">
+        @if($is_active_register)
+            <div class="form-title">
+                <span class="form-title">激活账号</span>
+            </div>
+            <div class="form-group">
+                <input class="form-control placeholder-no-fix" type="text" autocomplete="off" placeholder="请输入用户名" name="username" value="{{Request::get('username')}}" required />
+                <input type="hidden" name="_token" value="{{csrf_token()}}" />
+            </div>
+        @else
+            <div class="alert alert-danger">
+                <span> 系统维护中,如需激活账号请联系管理员 </span>
+            </div>
+        @endif
+        <div class="form-actions">
+            <button type="button" class="btn btn-default" onclick="login()">返 回</button>
+            @if($is_active_register)
+                <button type="submit" class="btn red uppercase pull-right">激 活</button>
+            @endif
+        </div>
+    </form>
+    <!-- END FORGOT PASSWORD FORM -->
+</div>
+
+<!-- END LOGIN -->
+<!--[if lt IE 9]>
+<script src="/assets/global/plugins/respond.min.js"></script>
+<script src="/assets/global/plugins/excanvas.min.js"></script>
+<script src="/assets/global/plugins/ie8.fix.min.js"></script>
+<![endif]-->
+<!-- BEGIN CORE PLUGINS -->
+<script src="/assets/global/plugins/jquery.min.js" type="text/javascript"></script>
+<script src="/assets/global/plugins/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
+<!-- END CORE PLUGINS -->
+<script type="text/javascript">
+    // 登录
+    function login() {
+        window.location.href = '{{url('login')}}';
+    }
+</script>
+<!-- BEGIN THEME GLOBAL SCRIPTS -->
+<script src="/assets/global/scripts/app.min.js" type="text/javascript"></script>
+<!-- END THEME GLOBAL SCRIPTS -->
+</body>
+
+</html>

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

@@ -26,7 +26,8 @@
                         </div>
                         <div class="portlet-body">
                             <div class="alert alert-info">
-                                共计可以生成 <strong> {{$num}} </strong> 个邀请码
+                                <i class="fa fa-warning"></i>
+                                可生成 <strong> {{$num}} </strong> 个邀请码
                             </div>
                             <button type="submit" class="btn blue" onclick="makeInvite()" @if(!$num) disabled @endif> 生 成 </button>
                         </div>

+ 7 - 0
routes/web.php

@@ -18,6 +18,10 @@ Route::get('admin/articleList', 'AdminController@articleList'); // 文章列表
 Route::any('admin/addArticle', 'AdminController@addArticle'); // 添加文章
 Route::any('admin/editArticle', 'AdminController@editArticle'); // 编辑文章
 Route::post('admin/delArticle', 'AdminController@delArticle'); // 删除文章
+Route::get('admin/groupList', 'AdminController@groupList'); // 文章列表
+Route::any('admin/addGroup', 'AdminController@addGroup'); // 添加文章
+Route::any('admin/editGroup', 'AdminController@editGroup'); // 编辑文章
+Route::post('admin/delGroup', 'AdminController@delGroup'); // 删除文章
 Route::get('admin/inviteList', 'AdminController@inviteList'); // 邀请码列表
 Route::post('admin/makeInvite', 'AdminController@makeInvite'); // 生成邀请码
 Route::any('admin/config', 'AdminController@config'); // 配置列表
@@ -37,6 +41,7 @@ Route::post('admin/setInviteNum', 'AdminController@setInviteNum'); // 可生成
 Route::post('admin/setWebsiteName', 'AdminController@setWebsiteName'); // 设置网站名称
 Route::post('admin/setWebsiteUrl', 'AdminController@setWebsiteUrl'); // 设置网站地址
 Route::post('admin/setResetPasswordTimes', 'AdminController@setResetPasswordTimes'); // 设置重置密码次数
+Route::post('admin/setActiveTimes', 'AdminController@setActiveTimes'); // 设置激活账号次数
 Route::get('makePasswd', 'AdminController@makePasswd'); // 获取随机密码
 Route::get('download', 'AdminController@download'); // 下载转换过的JSON配置
 
@@ -49,4 +54,6 @@ Route::any('user/invite', 'UserController@invite'); // 邀请码
 Route::any('user/makeInvite', 'UserController@makeInvite'); // 生成邀请码
 Route::any('resetPassword', 'UserController@resetPassword'); // 重设密码
 Route::any('reset/{token}', 'UserController@reset'); // 重设密码
+Route::any('activeUser', 'UserController@activeUser'); // 激活账号
+Route::any('active/{token}', 'UserController@active'); // 激活账号
 

+ 27 - 1
sql/db.sql

@@ -91,7 +91,7 @@ CREATE TABLE `user` (
   `u` bigint(20) NOT NULL DEFAULT '0' COMMENT '已上传流量,单位字节',
   `d` bigint(20) NOT NULL DEFAULT '0' COMMENT '已下载流量,单位字节',
   `t` int(11) NOT NULL DEFAULT '0' COMMENT '最后使用时间',
-  `enable` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态',
+  `enable` tinyint(4) NOT NULL DEFAULT '1' COMMENT 'SS状态',
   `method` varchar(30) NOT NULL DEFAULT 'aes-192-ctr' COMMENT '加密方式',
   `custom_method` varchar(30) DEFAULT 'aes-192-ctr' COMMENT '自定义加密方式',
   `protocol` varchar(30) NOT NULL DEFAULT 'auth_chain_a' COMMENT '协议',
@@ -110,6 +110,7 @@ CREATE TABLE `user` (
   `remark` text COMMENT '备注',
   `is_admin` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否管理员:0-否、1-是',
   `reg_ip` varchar(20) NOT NULL DEFAULT '127.0.0.1' COMMENT '注册IP',
+  `status` tinyint(11) DEFAULT '0' COMMENT '状态:-1-禁止登陆、0-未激活、1-正常',
   `created_at` datetime DEFAULT NULL,
   `updated_at` datetime DEFAULT NULL,
   PRIMARY KEY (`id`),
@@ -219,6 +220,8 @@ INSERT INTO `config` VALUES ('6', 'website_name', 'SSRPanel');
 INSERT INTO `config` VALUES ('7', 'is_reset_password', 1);
 INSERT INTO `config` VALUES ('8', 'reset_password_times', 3);
 INSERT INTO `config` VALUES ('9', 'website_url', 'http://baidu.com');
+INSERT INTO `config` VALUES ('10', 'is_active_register', 1);
+INSERT INTO `config` VALUES ('11', 'active_times', 3);
 
 
 -- ----------------------------
@@ -267,6 +270,29 @@ CREATE TABLE `verify` (
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
 
 
+-- ----------------------------
+-- Table structure for `ss_group`
+-- ----------------------------
+CREATE TABLE `ss_group` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分组名称',
+  `level` tinyint(4) NOT NULL DEFAULT '1' COMMENT '分组级别',
+  `created_at` datetime DEFAULT NULL,
+  `updated_at` datetime DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+
+-- ----------------------------
+-- Table structure for `ss_group_node`
+-- ----------------------------
+CREATE TABLE `ss_group_node` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `group_id` int(11) NOT NULL DEFAULT '0' COMMENT '分组ID',
+  `node_id` int(11) NOT NULL DEFAULT '0' COMMENT '节点ID',
+  PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='分组节点关系表';
+
 
 /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
 /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;