Przeglądaj źródła

用户注册功能
用户自己可以生成邀请码

zhangjiangbin 8 lat temu
rodzic
commit
e60adcb2da

+ 56 - 2
app/Http/Controllers/AdminController.php

@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
 
 use App\Http\Models\Article;
 use App\Http\Models\Config;
+use App\Http\Models\Invite;
 use App\Http\Models\SsConfig;
 use App\Http\Models\SsNode;
 use App\Http\Models\SsNodeInfo;
@@ -140,6 +141,12 @@ class AdminController extends BaseController
             $remark = $request->get('remark');
             $is_admin = $request->get('is_admin');
 
+            // 校验username是否已存在
+            $exists = User::where('username', $username)->first();
+            if ($exists) {
+                return Response::json(['status' => 'fail', 'data' => '', 'message' => '用户名已存在,请重新输入']);
+            }
+
             // 密码为空时生成默认密码
             if (empty($password)) {
                 $str = $this->makeRandStr();
@@ -179,9 +186,8 @@ class AdminController extends BaseController
                 return Response::json(['status' => 'fail', 'data' => '', 'message' => '添加失败']);
             }
         } else {
-            $config = $this->systemConfig();
-
             // 最后一个可用端口
+            $config = $this->systemConfig();
             $last_user = User::orderBy('id', 'desc')->first();
             $view['last_port'] = $config['is_rand_port'] ? $this->getRandPort() : $last_user->port + 1;
 
@@ -231,6 +237,12 @@ class AdminController extends BaseController
             $remark = $request->get('remark');
             $is_admin = $request->get('is_admin');
 
+            // 校验username是否已存在
+            $exists = User::where('username', $username)->first();
+            if ($exists) {
+                return Response::json(['status' => 'fail', 'data' => '', 'message' => '用户名已存在,请重新输入']);
+            }
+
             $data = [
                 'username' => $username,
                 'port' => $port,
@@ -1125,4 +1137,46 @@ TXT;
 
         return Response::json(['status' => 'success', 'data' => '', 'message' => '操作成功']);
     }
+
+    // 邀请码列表
+    public function inviteList(Request $request)
+    {
+        if (!$request->session()->has('user')) {
+            return Redirect::to('login');
+        }
+
+        if (!$request->session()->get('user')['is_admin']) {
+            return Redirect::to('login');
+        }
+
+        $view['inviteList'] = Invite::with(['generator', 'user'])->paginate(10);
+
+        return Response::view('admin/inviteList', $view);
+    }
+
+    // 生成邀请码
+    public function makeInvite(Request $request)
+    {
+        if (!$request->session()->has('user')) {
+            return Redirect::to('login');
+        }
+
+        if (!$request->session()->get('user')['is_admin']) {
+            return Redirect::to('login');
+        }
+
+        $user = $request->session()->get('user');
+
+        for ($i = 0; $i < 10; $i++) {
+            $obj = new Invite();
+            $obj->uid = $user['id'];
+            $obj->fuid = 0;
+            $obj->code = strtoupper(md5(microtime() . $this->makeRandStr(6)));
+            $obj->status = 0;
+            $obj->dateline = date('Y-m-d H:i:s', strtotime("+ 7days"));
+            $obj->save();
+        }
+
+        return Response::json(['status' => 'success', 'data' => '', 'message' => '生成成功']);
+    }
 }

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

@@ -22,19 +22,24 @@ class LoginController extends Controller
             $password = trim($request->get('password'));
 
             if (empty($username) || empty($password)) {
-                $request->session()->flash('error_msg', '请输入用户名和密码');
+                $request->session()->flash('errorMsg', '请输入用户名和密码');
                 return Redirect::to('login');
             }
 
-            $user = User::where('username', $username)->where('password', md5($password))->where('is_admin', 1)->first();
+            $user = User::where('username', $username)->where('password', md5($password))->first();
             if (!$user) {
-                $request->session()->flash('error_msg', '用户名或密码错误');
+                $request->session()->flash('errorMsg', '用户名或密码错误');
                 return Redirect::to('login');
             }
 
             $request->session()->put('user', $user->toArray());
 
-            return Redirect::to('admin');
+            // 根据权限跳转
+            if ($user['is_admin']) {
+                return Redirect::to('admin');
+            }
+
+            return Redirect::to('user');
         } else {
             return Response::view('login');
         }

+ 94 - 0
app/Http/Controllers/RegisterController.php

@@ -0,0 +1,94 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Http\Models\Invite;
+use App\Http\Models\User;
+use Illuminate\Http\Request;
+use Response;
+use Redirect;
+
+/**
+ * 注册控制器
+ * Class LoginController
+ * @package App\Http\Controllers
+ */
+class RegisterController extends BaseController
+{
+    // 注册页
+    public function index(Request $request)
+    {
+        if ($request->method() == 'POST') {
+            $username = trim($request->get('username'));
+            $password = trim($request->get('password'));
+            $repassword = trim($request->get('repassword'));
+            $code = trim($request->get('code'));
+
+            if (empty($username)) {
+                $request->session()->flash('errorMsg', '请输入用户名');
+
+                return Redirect::back()->withInput();
+            } else if (empty($password)) {
+                $request->session()->flash('errorMsg', '请输入密码');
+
+                return Redirect::back()->withInput();
+            } else if (empty($repassword)) {
+                $request->session()->flash('errorMsg', '请重新输入密码');
+
+                return Redirect::back()->withInput();
+            } else if (empty($code)) {
+                $request->session()->flash('errorMsg', '请输入邀请码');
+
+                return Redirect::back()->withInput();
+            } else if (md5($password) != md5($repassword)) {
+                $request->session()->flash('errorMsg', '两次输入密码不一致,请重新输入');
+
+                return Redirect::back()->withInput($request->except(['password', 'repassword']));
+            }
+
+            // 校验邀请码合法性
+            $code = Invite::where('code', $code)->where('status', 0)->first();
+            if (empty($code)) {
+                $request->session()->flash('errorMsg', '邀请码不可用,请更换邀请码后重试');
+
+                return Redirect::back()->withInput($request->except(['code']));
+            }
+
+            // 校验用户名是否已存在
+            $exists = User::where('username', $username)->first();
+            if ($exists) {
+                $request->session()->flash('errorMsg', '用户名已存在,请更换用户名');
+
+                return Redirect::back()->withInput();
+            }
+
+            // 最后一个可用端口
+            $config = $this->systemConfig();
+            $last_user = User::orderBy('id', 'desc')->first();
+            $port = $config['is_rand_port'] ? $this->getRandPort() : $last_user->port + 1;
+
+            // 创建新用户
+            $obj = new User();
+            $obj->username = $username;
+            $obj->password = md5($password);
+            $obj->port = $port;
+            $obj->passwd = $this->makeRandStr();
+            $obj->transfer_enable = $this->toGB(1);
+            $obj->enable_time = date('Y-m-d H:i:s');
+            $obj->expire_time = date('Y-m-d H:i:s', strtotime("+30 days"));
+            $obj->reg_ip = $request->getClientIp();
+            $obj->save();
+
+            // 更新邀请码
+            if ($obj->id) {
+                Invite::where('id', $code->id)->update(['status' => 1]);
+            }
+
+            return Redirect::to('login');
+        } else {
+            return Response::view('register');
+        }
+    }
+
+
+}

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

@@ -3,6 +3,8 @@
 namespace App\Http\Controllers;
 
 use App\Http\Models\Article;
+use App\Http\Models\Config;
+use App\Http\Models\Invite;
 use App\Http\Models\SsNode;
 use App\Http\Models\SsNodeInfo;
 use App\Http\Models\SsNodeOnlineLog;
@@ -97,10 +99,10 @@ class UserController extends BaseController
 
             $data = [
                 //'port' => $port,
-                'passwd' => $passwd,
-                'method' => $method,
+                'passwd'   => $passwd,
+                'method'   => $method,
                 'protocol' => $protocol,
-                'obfs' => $obfs
+                'obfs'     => $obfs
             ];
 
             $ret = User::where('id', $user['id'])->update($data);
@@ -120,9 +122,9 @@ class UserController extends BaseController
             }
         } else {
             // 加密方式、协议、混淆
-            $view['method_list'] =  $this->methodList();
-            $view['protocol_list'] =  $this->protocolList();
-            $view['obfs_list'] =  $this->obfsList();
+            $view['method_list'] = $this->methodList();
+            $view['protocol_list'] = $this->protocolList();
+            $view['obfs_list'] = $this->obfsList();
             $view['info'] = User::where('id', $user['id'])->first();
 
             return Response::view('user/profile', $view);
@@ -166,7 +168,7 @@ class UserController extends BaseController
 
             // 生成ss scheme
             $ss_str = '';
-            $ss_str .= $user['method']. ':' . $user['passwd'] . '@';
+            $ss_str .= $user['method'] . ':' . $user['passwd'] . '@';
             $ss_str .= $node->server . ':' . $user['port'];
             $ss_str = $this->base64url_encode($ss_str) . '#' . 'VPN';
             $ss_scheme = 'ss://' . $ss_str;
@@ -222,11 +224,41 @@ TXT;
 
         $user = $request->session()->get('user');
 
+        // 已生成的邀请码数量
+        $num = Invite::where('uid', $user['id'])->count();
+        $inviteNum = Config::where('id', 3)->pluck('value');
 
-
-        $view = [];
+        $view['num'] = $inviteNum[0] - $num; // 还可以生成的邀请码数量
+        $view['inviteList'] = Invite::where('uid', $user['id'])->with(['generator', 'user'])->paginate(10); // 邀请码列表
 
         return Response::view('user/invite', $view);
     }
 
+    // 生成邀请码
+    public function makeInvite(Request $request)
+    {
+        if (!$request->session()->has('user')) {
+            return Redirect::to('login');
+        }
+
+        $user = $request->session()->get('user');
+
+        // 已生成的邀请码数量
+        $num = Invite::where('uid', $user['id'])->count();
+        $inviteNum = Config::where('id', 3)->pluck('value');
+        if ($num >= $inviteNum[0]) {
+            return Response::json(['status' => 'fail', 'data' => '', 'message' => '生成失败:最多只能生成' . $inviteNum[0] . '个邀请码']);
+        }
+
+        $obj = new Invite();
+        $obj->uid = $user['id'];
+        $obj->fuid = 0;
+        $obj->code = strtoupper(md5(microtime() . $this->makeRandStr(6)));
+        $obj->status = 0;
+        $obj->dateline = date('Y-m-d H:i:s', strtotime("+ 7days"));
+        $obj->save();
+
+        return Response::json(['status' => 'success', 'data' => '', 'message' => '生成成功']);
+    }
+
 }

+ 34 - 0
app/Http/Models/Invite.php

@@ -0,0 +1,34 @@
+<?php
+
+namespace App\Http\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+/**
+ * 邀请码
+ * Class Node
+ * @package App\Http\Models
+ */
+class Invite extends Model
+{
+    protected $table = 'invite';
+    protected $primaryKey = 'id';
+    protected $fillable = [
+        'uid',
+        'fuid',
+        'code',
+        'status',
+        'dateline'
+    ];
+
+    public function Generator()
+    {
+        return $this->hasOne(User::class, 'id', 'uid');
+    }
+
+    public function User()
+    {
+        return $this->hasOne(User::class, 'id', 'fuid');
+    }
+
+}

+ 2 - 20
public/assets/pages/scripts/login.js

@@ -157,31 +157,13 @@ var Login = function() {
             focusInvalid: false, // do not focus the last invalid input
             ignore: "",
             rules: {
-
-                fullname: {
-                    required: true
-                },
-                email: {
-                    required: true,
-                    email: true
-                },
-                address: {
-                    required: true
-                },
-                city: {
-                    required: true
-                },
-                country: {
-                    required: true
-                },
-
                 username: {
                     required: true
                 },
                 password: {
                     required: true
                 },
-                rpassword: {
+                repassword: {
                     equalTo: "#register_password"
                 },
 
@@ -192,7 +174,7 @@ var Login = function() {
 
             messages: { // custom messages for radio buttons and checkboxes
                 tnc: {
-                    required: "同意“服务条款”和“隐私权相关政策”"
+                    required: "同意"
                 }
             },
 

+ 18 - 22
readme.md

@@ -15,12 +15,21 @@ telegram:https://t.me/ssrpanel
 用户名:admin 密码:123456
 ````
 
-#### PHP7环境配置
+#### 请我吃巨无霸
+````
+哈哈,如果你觉得这套代码好用,可以请我吃一个🍔,我最喜欢吃麦当劳的巨无霸。
+
+微信扫一扫,请我吃巨无霸🍔,功能排期请看开发计划
+
+![请我吃巨无霸](https://raw.githubusercontent.com/ssrpanel/ssrpanel/723d6f9d35d10db7c57dab962c972035099a733f/public/assets/images/donate.jpeg)
+
 ````
-建议小白用LNMP先傻瓜安装出php5.6 + mysql(5.5以上)
-然后再编译安装PHP7.1,搭建版本环境
 
-请看WIKI [编译安装PHP7.1.7环境(CentOS)]
+#### PHP7环境配置
+````
+Laravel 5 + Metronic 4.7.1 + PHP 7.1 + Mysql 5.6
+建议小白LNMP傻瓜安装出php7.1 + mysql(5.5以上)
+手动编译请看WIKI [编译安装PHP7.1.7环境(CentOS)]
 ````
 
 #### 拉取代码
@@ -55,18 +64,6 @@ location / {
 service nginx reload
 ````
 
-## 代码解释
-````
-\app\Http\Controllers 控制器文件
-\app\Http\Models 模型文件
-\config 配置信息
-\public 公共文件
-\resources\views 视图文件
-\storage 临时文件(页面缓存、日志),文件夹一个都不能少,少了必报错
-\vendor 组件
-\routes 路由
-````
-
 ## SSR服务端
 ````
 把userapiconfig.py里的 API_INTERFACE 设置为 glzjinmod
@@ -84,12 +81,11 @@ chown www:www ssserver.log
 
 ## 说明
 ````
-1.纯账号管理后台
-2.需要配合SSR后端使用,[请作者吃巨无霸,获取SSR后端最新版,详情请看WIKI]
-3.用户端开发中
-4.支持SS多用户json文件一键转换成SSR多用户json文件
-5.支持SSR多用户json文件一键导入数据库
-6.基础文章系统
+1.账号管理面板
+2.需配合SSR后端使用
+3.强大的管理后台
+4.美观的界面
+5.支持手机自适应,方便管理账号
 ````
 
 ![Markdown](http://i4.bvimg.com/1949/aac73bf589fbd785.png)

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

@@ -50,7 +50,7 @@
                                         <div class="form-group">
                                             <label for="content" class="col-md-3 control-label">内容</label>
                                             <div class="col-md-8">
-                                                <script id="editor" type="text/plain" style="width:680px;height:400px;"></script>
+                                                <script id="editor" type="text/plain" style="width:800px;height:400px;"></script>
                                             </div>
                                         </div>
                                     </div>
@@ -86,7 +86,7 @@
             elementPathEnabled : false,    //是否启用元素路径
             maximumWords:300,              //允许的最大字符数
             initialContent:'',             //初始化编辑器的内容
-            initialFrameWidth:960,         //初始化宽度
+            initialFrameWidth:800,         //初始化宽度
             autoClearinitialContent:false, //是否自动清除编辑器初始内容
         });
 

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

@@ -44,7 +44,7 @@
                                         <div class="form-group">
                                             <label for="content" class="col-md-3 control-label">内容</label>
                                             <div class="col-md-8">
-                                                <script id="editor" type="text/plain" style="width:680px;height:400px;">
+                                                <script id="editor" type="text/plain" style="width:800px;height:400px;">
                                                     {!! $article->content !!}
                                                 </script>
                                             </div>
@@ -82,7 +82,7 @@
             elementPathEnabled : false,    //是否启用元素路径
             maximumWords:300,              //允许的最大字符数
             initialContent:'',             //初始化编辑器的内容
-            initialFrameWidth:960,         //初始化宽度
+            initialFrameWidth:800,         //初始化宽度
             autoClearinitialContent:false, //是否自动清除编辑器初始内容
         });
 

+ 132 - 0
resources/views/admin/inviteList.blade.php

@@ -0,0 +1,132 @@
+@extends('admin.layouts')
+
+@section('css')
+@endsection
+@section('title', '控制面板')
+@section('content')
+    <!-- BEGIN CONTENT BODY -->
+    <div class="page-content">
+        <!-- BEGIN PAGE BREADCRUMB -->
+        <ul class="page-breadcrumb breadcrumb">
+            <li>
+                <a href="{{url('admin/inviteList')}}">邀请码管理</a>
+                <i class="fa fa-circle"></i>
+            </li>
+        </ul>
+        <!-- END PAGE BREADCRUMB -->
+        <!-- BEGIN PAGE BASE CONTENT -->
+        <div class="row">
+            <div class="col-md-4">
+                <div class="tab-pane active" id="tab_0">
+                    <div class="portlet light bordered">
+                        <div class="portlet-title">
+                            <div class="caption">
+                                <span class="caption-subject font-dark bold uppercase">生成邀请码</span>
+                            </div>
+                        </div>
+                        <div class="portlet-body">
+                            <div class="alert alert-info">
+                                注意:每次仅生成 <strong> 10 </strong> 个邀请码
+                            </div>
+                            <button type="submit" class="btn blue" onclick="makeInvite()"> 生 成 </button>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="col-md-8">
+                <div class="tab-pane active" id="tab_0">
+                    <div class="portlet light bordered">
+                        <div class="portlet-title">
+                            <div class="caption">
+                                <span class="caption-subject font-dark bold uppercase">邀请码列表</span>
+                            </div>
+                        </div>
+                        <div class="portlet-body">
+                            <div class="table-scrollable table-scrollable-borderless">
+                                <table class="table table-hover table-light">
+                                    <thead>
+                                        <tr class="uppercase">
+                                            <th> # </th>
+                                            <th> 邀请码 </th>
+                                            <th> 有效期 </th>
+                                            <th> 生成者 </th>
+                                            <th> 使用者 </th>
+                                            <th> 状态 </th>
+                                        </tr>
+                                    </thead>
+                                    <tbody>
+                                        @if($inviteList->isEmpty())
+                                            <tr>
+                                                <td colspan="6" style="text-align: center;">暂无数据</td>
+                                            </tr>
+                                        @else
+                                            @foreach($inviteList as $invite)
+                                                <tr>
+                                                    <td> {{$invite->id}} </td>
+                                                    <td> {{$invite->code}} </td>
+                                                    <td> {{$invite->dateline}} </td>
+                                                    <td> {{$invite->generator->username}} </td>
+                                                    <td> {{empty($invite->user) ? '' : $invite->user->username}} </td>
+                                                    <td>
+                                                        @if($invite->status == '0')
+                                                            <span class="label label-sm label-success"> 未使用 </span>
+                                                        @elseif($invite->status == '1')
+                                                            <span class="label label-sm label-danger"> 已使用 </span>
+                                                        @else
+                                                            <span class="label label-sm label-default"> 已过期 </span>
+                                                        @endif
+                                                    </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">共 {{$inviteList->total()}} 个邀请码</div>
+                                </div>
+                                <div class="col-md-7 col-sm-7">
+                                    <div class="dataTables_paginate paging_bootstrap_full_number pull-right">
+                                        {{ $inviteList->links() }}
+                                    </div>
+                                </div>
+                            </div>
+                        </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">
+        // 生成邀请码
+        function makeInvite() {
+            var _token = '{{csrf_token()}}';
+
+            $.ajax({
+                type: "POST",
+                url: "{{url('admin/makeInvite')}}",
+                async: false,
+                data: {_token:_token},
+                dataType: 'json',
+                success: function (ret) {
+                    if (ret.status == 'success') {
+                        bootbox.alert(ret.message, function () {
+                            window.location.reload();
+                        });
+                    } else {
+                        bootbox.alert(ret.message);
+                    }
+                }
+            });
+
+            return false;
+        }
+    </script>
+@endsection

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

@@ -120,6 +120,12 @@
                         <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>
+                        <span class="title">邀请码管理</span>
+                    </a>
+                </li>
                 <li class="nav-item {{Request::getRequestUri() == '/admin/articleList' ? 'active open' : ''}}">
                     <a href="{{url('admin/articleList')}}" class="nav-link nav-toggle">
                         <i class="icon-docs"></i>
@@ -174,7 +180,7 @@
                         </li>
                         <li class="nav-item {{Request::getRequestUri() == '/admin/system' ? 'active open' : ''}}">
                             <a href="{{url('admin/system')}}" class="nav-link ">
-                                <i class="icon-grid"></i>
+                                <i class="icon-wrench"></i>
                                 <span class="title">系统配置</span>
                             </a>
                         </li>

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

@@ -26,7 +26,7 @@
                 <div class="portlet light bordered">
                     <div class="portlet-title">
                         <div class="caption font-dark">
-                            <i class="icon-grid font-dark"></i>
+                            <i class="icon-wrench font-dark"></i>
                             <span class="caption-subject bold uppercase"> 系统配置 </span>
                         </div>
                     </div>

+ 18 - 2
resources/views/login.blade.php

@@ -48,10 +48,10 @@
             <button class="close" data-close="alert"></button>
             <span> 请输入用户名和密码 </span>
         </div>
-        @if (Session::get('error_msg'))
+        @if (Session::get('errorMsg'))
         <div class="alert alert-danger">
             <button class="close" data-close="alert"></button>
-            <span> {{Session::get('error_msg')}} </span>
+            <span> {{Session::get('errorMsg')}} </span>
         </div>
         @endif
         <div class="form-group">
@@ -63,9 +63,25 @@
             <input class="form-control form-control-solid placeholder-no-fix" type="password" autocomplete="off" placeholder="密码" name="password" />
             <input type="hidden" name="_token" value="{{ csrf_token() }}" />
         </div>
+        <div class="form-actions">
+            <div class="pull-left">
+                <label class="rememberme mt-checkbox mt-checkbox-outline">
+                    <input type="checkbox" name="remember" value="1"> 记住我
+                    <span></span>
+                </label>
+            </div>
+            <div class="pull-right forget-password-block">
+                <a href="javascript:;" class="forget-password">找回密码</a>
+            </div>
+        </div>
         <div class="form-actions">
             <button type="submit" class="btn red btn-block uppercase">登录</button>
         </div>
+        <div class="create-account">
+            <p>
+                <a href="{{url('register')}}" class="btn-primary btn">注册</a>
+            </p>
+        </div>
     </form>
     <!-- END LOGIN FORM -->
 </div>

+ 116 - 0
resources/views/register.blade.php

@@ -0,0 +1,116 @@
+<!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 PAGE LEVEL PLUGINS -->
+    <link href="/assets/global/plugins/select2/css/select2.min.css" rel="stylesheet" type="text/css" />
+    <link href="/assets/global/plugins/select2/css/select2-bootstrap.min.css" rel="stylesheet" type="text/css" />
+    <!-- END PAGE LEVEL PLUGINS -->
+    <!-- BEGIN THEME GLOBAL STYLES -->
+    <link href="/assets/global/css/components-rounded.min.css" rel="stylesheet" id="style_components" type="text/css" />
+    <link href="/assets/global/css/plugins.min.css" rel="stylesheet" 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">
+    <!-- BEGIN REGISTRATION FORM -->
+    <form class="register-form" action="{{url('register')}}" method="post" style="display: block;">
+        @if (Session::get('errorMsg'))
+            <div class="alert alert-danger">
+                <button class="close" data-close="alert"></button>
+                <span> {{Session::get('errorMsg')}} </span>
+            </div>
+        @endif
+        <div class="form-group">
+            <label class="control-label visible-ie8 visible-ie9">用户名</label>
+            <input class="form-control placeholder-no-fix" type="text" autocomplete="off" placeholder="用户名" name="username" value="{{Request::old('username')}}" required />
+            <input type="hidden" name="_token" value="{{csrf_token()}}" />
+        </div>
+        <div class="form-group">
+            <label class="control-label visible-ie8 visible-ie9">密码</label>
+            <input class="form-control placeholder-no-fix" type="password" autocomplete="off" placeholder="密码" name="password" value="{{Request::old('password')}}" required />
+        </div>
+        <div class="form-group">
+            <label class="control-label visible-ie8 visible-ie9">重复密码</label>
+            <input class="form-control placeholder-no-fix" type="password" autocomplete="off" placeholder="重复密码" name="repassword" value="{{Request::old('repassword')}}" required />
+        </div>
+        <div class="form-group">
+            <label class="control-label visible-ie8 visible-ie9">邀请码</label>
+            <input class="form-control placeholder-no-fix" type="text" autocomplete="off" placeholder="邀请码" name="code" value="{{Request::old('code')}}" required />
+        </div>
+        <div class="form-group margin-top-20 margin-bottom-20">
+            <label class="mt-checkbox mt-checkbox-outline">
+                <input type="checkbox" name="tnc" checked disabled /> 我已阅读并同意遵守
+                <a href="javascript:;"> 服务条款 </a>
+                <span></span>
+            </label>
+        </div>
+        <div class="form-actions">
+            <button type="button" class="btn btn-default" onclick="login()">返回</button>
+            <button type="submit" class="btn red uppercase pull-right">提交</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>
+<script src="/assets/global/plugins/js.cookie.min.js" type="text/javascript"></script>
+<script src="/assets/global/plugins/jquery-slimscroll/jquery.slimscroll.min.js" type="text/javascript"></script>
+<script src="/assets/global/plugins/jquery.blockui.min.js" type="text/javascript"></script>
+<script src="/assets/global/plugins/bootstrap-switch/js/bootstrap-switch.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>
+<script src="/assets/global/plugins/select2/js/select2.full.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 - 12
resources/views/user/invite.blade.php

@@ -1,8 +1,6 @@
 @extends('user.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')
@@ -18,20 +16,83 @@
         <!-- 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-user-follow font-dark"></i>
-                            <span class="caption-subject bold uppercase"> 邀请码</span>
+            <div class="col-md-4">
+                <div class="tab-pane active" id="tab_0">
+                    <div class="portlet light bordered">
+                        <div class="portlet-title">
+                            <div class="caption">
+                                <span class="caption-subject font-dark bold uppercase">生成邀请码</span>
+                            </div>
+                        </div>
+                        <div class="portlet-body">
+                            <div class="alert alert-info">
+                                共计可以生成 <strong> {{$num}} </strong> 个邀请码
+                            </div>
+                            <button type="submit" class="btn blue" onclick="makeInvite()"> 生 成 </button>
                         </div>
                     </div>
-                    <div class="portlet-body">
-                        、、
+                </div>
+            </div>
+            <div class="col-md-8">
+                <div class="tab-pane active" id="tab_0">
+                    <div class="portlet light bordered">
+                        <div class="portlet-title">
+                            <div class="caption">
+                                <span class="caption-subject font-dark bold uppercase">我的邀请码</span>
+                            </div>
+                        </div>
+                        <div class="portlet-body">
+                            <div class="table-scrollable table-scrollable-borderless">
+                                <table class="table table-hover table-light">
+                                    <thead>
+                                        <tr class="uppercase">
+                                            <th> # </th>
+                                            <th> 邀请码 </th>
+                                            <th> 有效期 </th>
+                                            <th> 使用者 </th>
+                                            <th> 状态 </th>
+                                        </tr>
+                                    </thead>
+                                    <tbody>
+                                        @if($inviteList->isEmpty())
+                                            <tr>
+                                                <td colspan="5" style="text-align: center;">暂无数据</td>
+                                            </tr>
+                                        @else
+                                            @foreach($inviteList as $key => $invite)
+                                                <tr>
+                                                    <td> {{$key + 1}} </td>
+                                                    <td> {{$invite->code}} </td>
+                                                    <td> {{$invite->dateline}} </td>
+                                                    <td> {{empty($invite->user) ? '' : $invite->user->username}} </td>
+                                                    <td>
+                                                        @if($invite->status == '0')
+                                                            <span class="label label-sm label-success"> 未使用 </span>
+                                                        @elseif($invite->status == '1')
+                                                            <span class="label label-sm label-danger"> 已使用 </span>
+                                                        @else
+                                                            <span class="label label-sm label-default"> 已过期 </span>
+                                                        @endif
+                                                    </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">共 {{$inviteList->total()}} 个邀请码</div>
+                                </div>
+                                <div class="col-md-7 col-sm-7">
+                                    <div class="dataTables_paginate paging_bootstrap_full_number pull-right">
+                                        {{ $inviteList->links() }}
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
                     </div>
                 </div>
-                <!-- END EXAMPLE TABLE PORTLET-->
             </div>
         </div>
         <!-- END PAGE BASE CONTENT -->
@@ -41,4 +102,29 @@
 @section('script')
     <script src="/assets/global/plugins/bootbox/bootbox.min.js" type="text/javascript"></script>
 
+    <script type="text/javascript">
+        // 生成邀请码
+        function makeInvite() {
+            var _token = '{{csrf_token()}}';
+
+            $.ajax({
+                type: "POST",
+                url: "{{url('user/makeInvite')}}",
+                async: false,
+                data: {_token:_token},
+                dataType: 'json',
+                success: function (ret) {
+                    if (ret.status == 'success') {
+                        //bootbox.alert(ret.message, function () {
+                            window.location.reload();
+                        //});
+                    } else {
+                        bootbox.alert(ret.message);
+                    }
+                }
+            });
+
+            return false;
+        }
+    </script>
 @endsection

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

@@ -46,7 +46,7 @@
                                 </thead>
                                 <tbody>
                                 <div class="alert alert-danger">
-                                    <strong>流量比例:</strong> 1表示用100M就结算100M,0.1表示用100M结算10M,5表示用100M结算500M,以此类推。目的是在于限制优质节点频繁使用,请大家珍惜自己的流量,选择适合自己的节点
+                                    <strong>流量比例:</strong> 1表示用100M就结算100M,0.1表示用100M结算10M,5表示用100M结算500M,以此类推,越是优质节点则比例越高
                                 </div>
                                 @if($nodeList->isEmpty())
                                     <tr>

+ 3 - 0
routes/web.php

@@ -18,6 +18,8 @@ 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/inviteList', 'AdminController@inviteList'); // 邀请码列表
+Route::post('admin/makeInvite', 'AdminController@makeInvite'); // 生成邀请码
 Route::any('admin/config', 'AdminController@config'); // 配置列表
 Route::any('admin/addConfig', 'AdminController@addConfig'); // 添加配置
 Route::post('admin/delConfig', 'AdminController@delConfig'); // 删除配置
@@ -41,3 +43,4 @@ Route::any('user/nodeList', 'UserController@nodeList'); // 节点列表
 Route::any('user/profile', 'UserController@profile'); // 修改个人信息
 Route::any('user/trafficLog', 'UserController@trafficLog'); // 流量日志
 Route::any('user/invite', 'UserController@invite'); // 邀请码
+Route::any('user/makeInvite', 'UserController@makeInvite'); // 生成邀请码

+ 19 - 1
sql/db.sql

@@ -212,6 +212,7 @@ CREATE TABLE `config` (
 -- ----------------------------
 INSERT INTO `config` VALUES ('1', 'is_rand_port', 0);
 INSERT INTO `config` VALUES ('2', 'is_user_rand_port', 0);
+INSERT INTO `config` VALUES ('3', 'invite_num', 3);
 
 
 -- ----------------------------
@@ -226,7 +227,24 @@ CREATE TABLE `article` (
   `created_at` datetime DEFAULT NULL,
   `updated_at` datetime DEFAULT NULL,
   PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
+) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COMMENT='文章表';
+
+
+-- ----------------------------
+-- Table structure for `invite`
+-- ----------------------------
+CREATE TABLE `invite` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `uid` int(11) NOT NULL DEFAULT '0' COMMENT '邀请人ID',
+  `fuid` int(11) NOT NULL DEFAULT '0' COMMENT '受邀人ID',
+  `code` char(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邀请码',
+  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '邀请码状态:0-未使用、1-已使用、2-已过期',
+  `dateline` datetime DEFAULT NULL COMMENT '有效期至',
+  `created_at` datetime DEFAULT NULL,
+  `updated_at` datetime DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='邀请码表';
+