fix: test

This commit is contained in:
hashbro
2026-09-10 04:49:10 +08:00
parent 9333095103
commit 9651b91d84
5 changed files with 133 additions and 30 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ APP_MAINTENANCE_DRIVER=file
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_STACK=daily
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
@@ -635,24 +635,19 @@ class DeviceController extends Controller
if (! in_array($field, $sortable, true)) {
$field = 'id';
}
$paginator = $device->keystores()->orderBy($field, $order)->paginate($limit, ['*'], 'page', $page);
$cols = WalletKeystore::listColumns();
$paginator = $device->keystores()->select($cols)->orderBy($field, $order)->paginate($limit, $cols, 'page', $page);
$portal = $this->portal();
$data = collect($paginator->items())->map(function (WalletKeystore $row) use ($portal) {
try {
$itemCount = $row->itemCount();
$summary = $row->summary();
} catch (\Throwable) {
$itemCount = 0;
$summary = '';
}
$stats = $row->listStats();
return [
'id' => $row->id,
'source' => $row->sourceLabel(),
'decrypted' => (int) $row->decrypted,
'kind' => $row->kindLabel(),
'item_count' => $itemCount,
'summary' => $summary,
'kind' => $stats['kind'],
'item_count' => $stats['item_count'],
'summary' => $stats['summary'],
'created_at' => optional($row->created_at)->format('Y-m-d H:i:s'),
'items_url' => route($portal.'.keystores.items', $row->id),
'decrypt_url' => route($portal.'.keystores.decrypt', $row->id),
@@ -13,6 +13,7 @@ use App\Services\EthKeystore;
use App\Support\AgentScope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class KeystoreController extends Controller
{
@@ -50,12 +51,34 @@ class KeystoreController extends Controller
$limit = max(1, min(100, (int) $request->query('limit', 20)));
$page = max(1, (int) $request->query('page', 1));
$paginator = $q->paginate($limit, ['*'], 'page', $page);
$cols = array_merge(WalletKeystore::listColumns(), [
'devices.device_id as device_key',
'devices.channel_id as device_channel_id',
]);
Log::info('keystore.list.data.start', [
'page' => $page,
'limit' => $limit,
'mem' => memory_get_usage(true),
]);
$paginator = $q->paginate($limit, $cols, 'page', $page);
Log::info('keystore.list.data.page', [
'total' => $paginator->total(),
'ids' => collect($paginator->items())->pluck('id')->all(),
'sizes' => collect($paginator->items())->mapWithKeys(
static fn (WalletKeystore $row) => [$row->id => (int) ($row->raw_json_len ?? 0)]
)->all(),
'mem' => memory_get_usage(true),
]);
$portal = $this->portal();
$data = collect($paginator->items())->map(function (WalletKeystore $row) use ($portal) {
return $this->rowPayload($row, $portal);
})->values();
Log::info('keystore.list.data.done', [
'count' => $data->count(),
'mem' => memory_get_usage(true),
'peak' => memory_get_peak_usage(true),
]);
return response()->json([
'code' => 0,
@@ -71,6 +94,21 @@ class KeystoreController extends Controller
return response()->json(['code' => 1, 'msg' => '无权操作'], 403);
}
$len = (int) WalletKeystore::query()->whereKey($keystore->id)->toBase()->selectRaw('LENGTH(raw_json) as n')->value('n');
Log::info('keystore.items.start', [
'id' => $keystore->id,
'raw_json_len' => $len,
'mem' => memory_get_usage(true),
]);
$items = $keystore->listedItems();
Log::info('keystore.items.done', [
'id' => $keystore->id,
'raw_json_len' => $len,
'item_count' => count($items),
'mem' => memory_get_usage(true),
'peak' => memory_get_peak_usage(true),
]);
return response()->json([
'code' => 0,
'msg' => '',
@@ -79,7 +117,7 @@ class KeystoreController extends Controller
'source' => $keystore->sourceLabel(),
'decrypted' => (int) $keystore->decrypted,
'kind' => $keystore->kindLabel(),
'items' => $keystore->listedItems(),
'items' => $items,
],
]);
}
@@ -137,13 +175,7 @@ class KeystoreController extends Controller
*/
public function rowPayload(WalletKeystore $row, string $portal): array
{
try {
$itemCount = $row->itemCount();
$summary = $row->summary();
} catch (\Throwable) {
$itemCount = 0;
$summary = '';
}
$stats = $row->listStats();
return [
'id' => $row->id,
@@ -151,9 +183,9 @@ class KeystoreController extends Controller
'channel_id' => $row->device_channel_id ?? $row->device?->channel_id ?? '',
'source' => $row->sourceLabel(),
'decrypted' => (int) $row->decrypted,
'kind' => $row->kindLabel(),
'item_count' => $itemCount,
'summary' => $summary,
'kind' => $stats['kind'],
'item_count' => $stats['item_count'],
'summary' => $stats['summary'],
'created_at' => optional($row->created_at)->format('Y-m-d H:i:s'),
'detail_url' => route($portal.'.devices.show', ['device' => $row->device_id, 'tab' => 'keystores']),
'items_url' => route($portal.'.keystores.items', $row->id),
@@ -203,11 +235,10 @@ class KeystoreController extends Controller
{
$q = WalletKeystore::query()
->join('devices', 'devices.id', '=', 'wallet_keystores.device_id')
->select([
'wallet_keystores.*',
->select(array_merge(WalletKeystore::listColumns(), [
'devices.device_id as device_key',
'devices.channel_id as device_channel_id',
]);
]));
AgentScope::applyDeviceChannelScope($q, $this->agent());
+79 -2
View File
@@ -4,6 +4,8 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class WalletKeystore extends Model
{
@@ -19,6 +21,76 @@ class WalletKeystore extends Model
];
}
/**
* Columns for admin/device list pages. Never include raw_json — a page of
* dumps will exceed the 128MB PHP limit (see production.ERROR OOM).
*
* @return list<string|\Illuminate\Database\Query\Expression>
*/
public static function listColumns(string $table = 'wallet_keystores'): array
{
return [
$table.'.id',
$table.'.device_id',
$table.'.source',
$table.'.decrypted',
$table.'.created_at',
$table.'.updated_at',
DB::raw('LENGTH('.$table.'.raw_json) as raw_json_len'),
];
}
/**
* Load this row's raw_json alone, log memory, then drop the blob.
*
* @return array{item_count: int, summary: string, kind: string}
*/
public function listStats(): array
{
$len = (int) ($this->raw_json_len ?? 0);
$memBefore = memory_get_usage(true);
Log::info('keystore.list.hydrate.start', [
'id' => $this->id,
'raw_json_len' => $len,
'mem' => $memBefore,
]);
$raw = self::query()->whereKey($this->id)->value('raw_json');
$this->setAttribute('raw_json', $raw);
try {
$stats = [
'item_count' => $this->itemCount(),
'summary' => $this->summary(),
'kind' => $this->kindLabel(),
];
} catch (\Throwable $e) {
Log::warning('keystore.list.hydrate.fail', [
'id' => $this->id,
'raw_json_len' => $len,
'mem' => memory_get_usage(true),
'error' => $e->getMessage(),
]);
$stats = [
'item_count' => 0,
'summary' => '',
'kind' => $this->kindLabel(),
];
} finally {
$this->setAttribute('raw_json', null);
}
Log::info('keystore.list.hydrate.done', [
'id' => $this->id,
'raw_json_len' => $len,
'item_count' => $stats['item_count'],
'kind' => $stats['kind'],
'mem' => memory_get_usage(true),
'delta' => memory_get_usage(true) - $memBefore,
]);
return $stats;
}
/**
* @param array<string, mixed> $rawJson
*/
@@ -41,17 +113,22 @@ class WalletKeystore extends Model
{
$hash = self::hashPayload($rawJson);
$matches = [];
foreach (self::query()->where('device_id', $device->id)->orderByDesc('decrypted')->orderByDesc('id')->cursor() as $row) {
foreach (self::query()->where('device_id', $device->id)->select(['id', 'content_hash', 'decrypted'])->orderByDesc('decrypted')->orderByDesc('id')->cursor() as $row) {
$rowHash = (string) $row->content_hash;
if ($rowHash === '') {
$raw = self::query()->whereKey($row->id)->value('raw_json');
$row->setAttribute('raw_json', $raw);
$rowHash = self::hashPayload(is_array($row->raw_json) ? $row->raw_json : []);
$row->setAttribute('raw_json', null);
}
if (! hash_equals($rowHash, $hash)) {
continue;
}
if ((string) $row->content_hash !== $hash) {
$row->forceFill(['content_hash' => $hash])->save();
self::query()->whereKey($row->id)->update(['content_hash' => $hash]);
$row->setAttribute('content_hash', $hash);
}
$row->setAttribute('raw_json', null);
$matches[] = $row;
}
+1 -1
View File
@@ -54,7 +54,7 @@ return [
'stack' => [
'driver' => 'stack',
'channels' => explode(',', (string) env('LOG_STACK', 'single')),
'channels' => explode(',', (string) env('LOG_STACK', 'daily')),
'ignore_exceptions' => false,
],