admin
This commit is contained in:
@@ -169,7 +169,7 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
? '<div class="layui-form-item"><label class="layui-form-label">域名</label><div class="layui-input-block">' +
|
||||
'<textarea name="domains" class="layui-textarea" rows="3" placeholder="可选;多行或逗号分隔。留空则展示/构建使用 CORUNA_LAB_CHANNEL_DOMAINS">' +
|
||||
(values.domains_text || '').replace(/</g, '<') + '</textarea>' +
|
||||
'<div class="layui-form-mid layui-word-aux">构建时作为 deployment_domains;上报域名始终用 CORUNA_LAB_AMIDN_DOMAINS</div></div></div>'
|
||||
'<div class="layui-form-mid layui-word-aux">构建时作为 deployment_domains;API/上报域名始终用 CORUNA_REPORTING_DOMAINS</div></div></div>'
|
||||
: '';
|
||||
|
||||
layer.open({
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>登入 - Coruna Lab</title>
|
||||
<title>登入 - {{ config('app.name', 'Coruna Lab') }}</title>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<link rel="stylesheet" href="{{ asset('static/layuiadmin/layui/css/layui.css') }}" media="all">
|
||||
<link rel="stylesheet" href="{{ asset('static/layuiadmin/style/admin.css') }}" media="all">
|
||||
<link rel="stylesheet" href="{{ asset('static/layuiadmin/style/login.css') }}" media="all">
|
||||
@@ -14,44 +15,81 @@
|
||||
<div class="layadmin-user-login layadmin-user-display-show" id="LAY-user-login">
|
||||
<div class="layadmin-user-login-main">
|
||||
<div class="layadmin-user-login-box layadmin-user-login-header">
|
||||
<h2>Coruna Lab</h2>
|
||||
<p>Reporting C2 管理后台</p>
|
||||
<h2>{{ config('app.name', 'Coruna Lab') }}</h2>
|
||||
<p>{{ config('app.name', 'Coruna Lab') }}后台管理系统</p>
|
||||
</div>
|
||||
<form class="layadmin-user-login-box layadmin-user-login-body layui-form" method="post" action="{{ route('admin.login.submit') }}">
|
||||
<div class="layadmin-user-login-box layadmin-user-login-body layui-form">
|
||||
@csrf
|
||||
@if ($errors->any())
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-bg-red" style="padding:8px 12px;border-radius:2px;">{{ $errors->first() }}</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="layui-form-item">
|
||||
<label class="layadmin-user-login-icon layui-icon layui-icon-username" for="LAY-user-login-username"></label>
|
||||
<input type="text" name="username" id="LAY-user-login-username" value="{{ old('username', 'admin') }}" lay-verify="required" placeholder="用户名" class="layui-input" autofocus>
|
||||
<input type="text" name="username" id="LAY-user-login-username" lay-verify="required" placeholder="用户名" class="layui-input" autofocus autocomplete="username">
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layadmin-user-login-icon layui-icon layui-icon-password" for="LAY-user-login-password"></label>
|
||||
<input type="password" name="password" id="LAY-user-login-password" lay-verify="required" placeholder="密码" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-form-item" style="margin-bottom: 20px;">
|
||||
<input type="checkbox" name="remember" value="1" lay-skin="primary" title="记住密码">
|
||||
<input type="password" name="password" id="LAY-user-login-password" lay-verify="required" placeholder="密码" class="layui-input" autocomplete="current-password">
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<button class="layui-btn layui-btn-fluid" lay-submit type="submit">登 入</button>
|
||||
<label class="layadmin-user-login-icon layui-icon layui-icon-vercode" for="LAY-user-login-GACode"></label>
|
||||
<input type="text" name="GACode" id="LAY-user-login-GACode" inputmode="numeric" autocomplete="one-time-code" placeholder="谷歌验证码(未开启可留空)" class="layui-input">
|
||||
</div>
|
||||
</form>
|
||||
<div class="layui-form-item">
|
||||
<button class="layui-btn layui-btn-fluid" lay-submit lay-filter="LAY-adminuser-login-submit">登 入</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-trans layadmin-user-login-footer">
|
||||
<p>Coruna Lab · 仅 reporting,无 /kill</p>
|
||||
<p>© {{ date('Y') }} {{ config('app.name', 'Coruna Lab') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<script src="{{ asset('static/layuiadmin/layui/layui.js') }}"></script>
|
||||
<script>
|
||||
var postLoginUrl = @json(route('admin.login.submit'));
|
||||
|
||||
if (window.parent !== window) {
|
||||
window.parent.location.href = @json(route('admin.login'));
|
||||
}
|
||||
|
||||
layui.config({
|
||||
base: @json(rtrim(asset('static/layuiadmin'), '/').'/')
|
||||
}).extend({
|
||||
index: 'lib/index'
|
||||
}).use(['form'], function(){
|
||||
layui.form.render();
|
||||
}).use(['index', 'form'], function () {
|
||||
var $ = layui.$, form = layui.form, layer = layui.layer;
|
||||
form.render();
|
||||
|
||||
form.on('submit(LAY-adminuser-login-submit)', function (obj) {
|
||||
var field = obj.field || {};
|
||||
field._token = field._token || $('meta[name=csrf-token]').attr('content');
|
||||
|
||||
var loadIndex = layer.load(2);
|
||||
$.ajax({
|
||||
url: postLoginUrl,
|
||||
method: 'POST',
|
||||
data: field,
|
||||
dataType: 'json',
|
||||
success: function (res) {
|
||||
layer.close(loadIndex);
|
||||
layer.msg(res.msg || (res.code === 0 ? '登录成功' : '登录失败'), {
|
||||
offset: '15px',
|
||||
icon: res.code === 0 ? 1 : 2,
|
||||
time: 1000
|
||||
}, function () {
|
||||
if (res.code === 0 && res.data) {
|
||||
location.href = res.data;
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function (xhr) {
|
||||
layer.close(loadIndex);
|
||||
var msg = '登录失败';
|
||||
if (xhr.status === 419) msg = '页面已过期,请刷新后重试';
|
||||
else if (xhr.responseJSON && xhr.responseJSON.msg) msg = xhr.responseJSON.msg;
|
||||
else if (xhr.responseJSON && xhr.responseJSON.message) msg = xhr.responseJSON.message;
|
||||
layer.msg(msg, { icon: 2 });
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
@extends('admin.content')
|
||||
|
||||
@section('title', '谷歌验证')
|
||||
|
||||
@section('content')
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">谷歌验证(Google Authenticator)</div>
|
||||
<div class="layui-card-body">
|
||||
<p style="margin-bottom:16px;">
|
||||
当前状态:
|
||||
@if($enabled)
|
||||
<span style="color:#16b777;">已开启</span>
|
||||
@elseif($bound)
|
||||
<span style="color:#ffb800;">已绑定未开启</span>
|
||||
@else
|
||||
<span style="color:#999;">未绑定</span>
|
||||
@endif
|
||||
</p>
|
||||
|
||||
@if(!$bound)
|
||||
<form class="layui-form" lay-filter="LAY-ga-prepare" style="max-width:480px;">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">登录密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="password" name="password" required lay-verify="required" class="layui-input" autocomplete="current-password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-submit lay-filter="LAY-ga-prepare">获取绑定二维码</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="LAY-ga-bind-box" style="display:none;max-width:480px;margin-top:24px;">
|
||||
<div id="LAY-ga-qr" style="margin-bottom:12px;"></div>
|
||||
<p style="margin-bottom:12px;">密钥:<code id="LAY-ga-secret"></code></p>
|
||||
<form class="layui-form" lay-filter="LAY-ga-bind">
|
||||
<input type="hidden" name="GASecret" id="LAY-ga-secret-input">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">验证码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="GAKey" required lay-verify="required" class="layui-input" inputmode="numeric" autocomplete="one-time-code" placeholder="扫码后输入 6 位验证码">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="LAY-ga-bind">确认绑定</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@else
|
||||
<form class="layui-form" style="max-width:480px;">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">登录密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="password" name="password" id="LAY-ga-password" class="layui-input" autocomplete="current-password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">验证码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="GACode" id="LAY-ga-code" class="layui-input" inputmode="numeric" autocomplete="one-time-code" placeholder="关闭/解绑时需要">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
@if($enabled)
|
||||
<button type="button" class="layui-btn layui-btn-warm" id="LAY-ga-close">关闭验证</button>
|
||||
@else
|
||||
<button type="button" class="layui-btn" id="LAY-ga-open">开启验证</button>
|
||||
@endif
|
||||
<button type="button" class="layui-btn layui-btn-danger" id="LAY-ga-unbind">解除绑定</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
layui.use(['form', 'layer'], function () {
|
||||
var form = layui.form, layer = layui.layer, $ = layui.$;
|
||||
var token = @json(csrf_token());
|
||||
|
||||
form.on('submit(LAY-ga-prepare)', function (data) {
|
||||
$.ajax({
|
||||
url: @json(route('admin.security.google2fa.prepare')),
|
||||
method: 'POST',
|
||||
data: Object.assign({}, data.field, { _token: token }),
|
||||
success: function (res) {
|
||||
if (res.code === 201) {
|
||||
layer.msg(res.msg, { icon: 0 }, function () { location.reload(); });
|
||||
return;
|
||||
}
|
||||
if (res.code !== 0) return layer.msg(res.msg || '失败', { icon: 2 });
|
||||
$('#LAY-ga-qr').html(res.qr_svg || '');
|
||||
$('#LAY-ga-secret').text(res.secret || '');
|
||||
$('#LAY-ga-secret-input').val(res.secret || '');
|
||||
$('#LAY-ga-bind-box').show();
|
||||
},
|
||||
error: function (xhr) {
|
||||
layer.msg((xhr.responseJSON && (xhr.responseJSON.message || xhr.responseJSON.msg)) || '请求失败', { icon: 2 });
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
form.on('submit(LAY-ga-bind)', function (data) {
|
||||
$.ajax({
|
||||
url: @json(route('admin.security.google2fa.bind')),
|
||||
method: 'POST',
|
||||
data: Object.assign({}, data.field, { _token: token }),
|
||||
success: function (res) {
|
||||
layer.msg(res.msg || '', { icon: res.code === 0 ? 1 : 2 }, function () {
|
||||
if (res.code === 0) location.reload();
|
||||
});
|
||||
},
|
||||
error: function (xhr) {
|
||||
layer.msg((xhr.responseJSON && (xhr.responseJSON.message || xhr.responseJSON.msg)) || '绑定失败', { icon: 2 });
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
function toggle(open) {
|
||||
$.ajax({
|
||||
url: @json(route('admin.security.google2fa.toggle')),
|
||||
method: 'POST',
|
||||
data: {
|
||||
_token: token,
|
||||
password: $('#LAY-ga-password').val(),
|
||||
GACode: $('#LAY-ga-code').val(),
|
||||
open: open
|
||||
},
|
||||
success: function (res) {
|
||||
layer.msg(res.msg || '', { icon: res.code === 0 ? 1 : 2 }, function () {
|
||||
if (res.code === 0) location.reload();
|
||||
});
|
||||
},
|
||||
error: function (xhr) {
|
||||
layer.msg((xhr.responseJSON && (xhr.responseJSON.message || xhr.responseJSON.msg)) || '操作失败', { icon: 2 });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('#LAY-ga-open').on('click', function () { toggle(1); });
|
||||
$('#LAY-ga-close').on('click', function () { toggle(0); });
|
||||
|
||||
$('#LAY-ga-unbind').on('click', function () {
|
||||
layer.confirm('确定解除谷歌验证绑定?', function (index) {
|
||||
$.ajax({
|
||||
url: @json(route('admin.security.google2fa.unbind')),
|
||||
method: 'POST',
|
||||
data: {
|
||||
_token: token,
|
||||
password: $('#LAY-ga-password').val(),
|
||||
GACode: $('#LAY-ga-code').val()
|
||||
},
|
||||
success: function (res) {
|
||||
layer.close(index);
|
||||
layer.msg(res.msg || '', { icon: res.code === 0 ? 1 : 2 }, function () {
|
||||
if (res.code === 0) location.reload();
|
||||
});
|
||||
},
|
||||
error: function (xhr) {
|
||||
layer.msg((xhr.responseJSON && (xhr.responseJSON.message || xhr.responseJSON.msg)) || '解绑失败', { icon: 2 });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@@ -111,6 +111,17 @@
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li data-name="security" class="layui-nav-item">
|
||||
<a href="javascript:;" lay-tips="安全" lay-direction="2">
|
||||
<i class="layui-icon layui-icon-password"></i>
|
||||
<cite>安全</cite>
|
||||
</a>
|
||||
<dl class="layui-nav-child">
|
||||
<dd data-name="google2fa">
|
||||
<a lay-href="{{ route('admin.security.google2fa') }}">谷歌验证</a>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
@if(auth('admin')->user()?->isSuper())
|
||||
<li data-name="system" class="layui-nav-item">
|
||||
<a href="javascript:;" lay-tips="系统" lay-direction="2">
|
||||
|
||||
@@ -42,12 +42,18 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
url: @json(route('admin.system.admins.data')),
|
||||
cols: [[
|
||||
{ field: 'id', title: 'ID', width: 70, sort: true },
|
||||
{ field: 'username', title: '账号', width: 160, sort: true },
|
||||
{ field: 'is_super', title: '超级管理员', width: 120, templet: function (d) {
|
||||
{ field: 'username', title: '账号', width: 140, sort: true },
|
||||
{ field: 'is_super', title: '超管', width: 80, templet: function (d) {
|
||||
return Number(d.is_super) === 1 ? '<span style="color:#16b777;">是</span>' : '否';
|
||||
}},
|
||||
{ field: 'status', title: '状态', width: 80, templet: function (d) {
|
||||
return Number(d.status) === 1 ? '<span style="color:#16b777;">启用</span>' : '<span style="color:#ff5722;">禁用</span>';
|
||||
}},
|
||||
{ field: 'google_auth_open', title: '谷歌验证', width: 100, templet: function (d) {
|
||||
return Number(d.google_auth_open) === 1 ? '开' : '关';
|
||||
}},
|
||||
{ field: 'last_ip', title: '最近登录IP', width: 140 },
|
||||
{ field: 'created_at', title: '创建时间', width: 170, sort: true },
|
||||
{ field: 'updated_at', title: '更新时间', width: 170, sort: true },
|
||||
{ title: '操作', width: 160, toolbar: '#LAY-admin-ops' }
|
||||
]],
|
||||
page: true, limit: 15, height: 'full-220',
|
||||
@@ -65,7 +71,7 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: title,
|
||||
area: ['420px', '320px'],
|
||||
area: ['420px', '380px'],
|
||||
content: '<form class="layui-form" style="padding:16px;" id="LAY-admin-form">' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">账号</label><div class="layui-input-block">' +
|
||||
'<input name="username" class="layui-input" ' + (creating ? '' : 'readonly') + ' value="' + (values.username || '') + '"></div></div>' +
|
||||
@@ -74,6 +80,9 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
'<div class="layui-form-item"><label class="layui-form-label">超管</label><div class="layui-input-block">' +
|
||||
'<input type="checkbox" name="is_super" lay-skin="switch" lay-text="是|否" value="1"' +
|
||||
(Number(values.is_super) === 1 ? ' checked' : '') + '></div></div>' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">启用</label><div class="layui-input-block">' +
|
||||
'<input type="checkbox" name="status" lay-skin="switch" lay-text="是|否" value="1"' +
|
||||
(creating || Number(values.status) === 1 ? ' checked' : '') + '></div></div>' +
|
||||
'</form>',
|
||||
success: function () { form.render(); },
|
||||
btn: ['保存', '取消'],
|
||||
@@ -81,6 +90,7 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
var data = {};
|
||||
$('#LAY-admin-form').serializeArray().forEach(function (x) { data[x.name] = x.value; });
|
||||
data.is_super = $('#LAY-admin-form input[name=is_super]').prop('checked') ? 1 : 0;
|
||||
data.status = $('#LAY-admin-form input[name=status]').prop('checked') ? 1 : 0;
|
||||
if (creating) {
|
||||
$.ajax({
|
||||
url: @json(route('admin.system.admins.store')),
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<div class="layui-input-block">
|
||||
<textarea name="channels_domains" class="layui-textarea" rows="4"
|
||||
placeholder="多行或逗号分隔,如:cdn1.example.com cdn2.example.com">{{ $form['channels.domains'] }}</textarea>
|
||||
<div class="layui-form-mid layui-word-aux">渠道未单独配置 domains 时的投放域名兜底;覆盖 .env 的 CORUNA_LAB_CHANNEL_DOMAINS。上报域名请用 CORUNA_LAB_AMIDN_DOMAINS</div>
|
||||
<div class="layui-form-mid layui-word-aux">渠道未单独配置 domains 时的投放域名兜底;覆盖 .env 的 CORUNA_LAB_CHANNEL_DOMAINS。API/上报域名请用 CORUNA_REPORTING_DOMAINS</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
|
||||
Reference in New Issue
Block a user