fix: test
This commit is contained in:
+1
-1
@@ -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());
|
||||
|
||||
|
||||
@@ -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
@@ -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,
|
||||
],
|
||||
|
||||
|
||||
Reference in New Issue
Block a user