Files
coruna-lab/app/Console/Commands/ImportNotesLogCommand.php
T
2026-09-08 04:53:35 +08:00

95 lines
2.9 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\Device;
use App\Models\Note;
use App\Services\IngestService;
use Illuminate\Console\Command;
class ImportNotesLogCommand extends Command
{
protected $signature = 'notes:import-log
{file? : C2 traffic log containing /nb lines}
{--fresh : Delete all notes before import}';
protected $description = 'Import /nb note payloads from a C2 log into the local notes table';
public function handle(IngestService $ingest): int
{
$path = $this->argument('file') ?: '/Users/toad/Downloads/20260908 (1).log';
if (! is_file($path)) {
$this->error('Log file not found: '.$path);
return self::FAILURE;
}
if ($this->option('fresh')) {
Note::query()->delete();
$this->info('Cleared notes table.');
}
$handle = fopen($path, 'r');
if ($handle === false) {
$this->error('Cannot read log file.');
return self::FAILURE;
}
$posts = 0;
$devices = [];
$skipped = 0;
while (($line = fgets($handle)) !== false) {
if (! str_contains($line, ' /nb ') && ! str_contains($line, '"path":"/nb"')) {
continue;
}
$jsonStart = strpos($line, '{');
if ($jsonStart === false) {
continue;
}
$rec = json_decode(substr($line, $jsonStart), true);
if (! is_array($rec) || ($rec['path'] ?? '') !== '/nb') {
continue;
}
$payload = $rec['payload'] ?? null;
if (! is_array($payload) || ! array_key_exists('list', $payload)) {
$skipped++;
continue;
}
$deviceKey = (string) ($rec['device_key'] ?? $payload['d'] ?? $payload['f'] ?? '');
$deviceKey = IngestService::normalizeDeviceKey(substr($deviceKey, 0, 64)) ?? '';
if ($deviceKey === '') {
$skipped++;
continue;
}
$channel = trim((string) ($payload['c'] ?? ''));
$device = Device::query()->firstOrCreate(
['device_id' => $deviceKey],
[
'channel_id' => $channel !== '' ? substr($channel, 0, 64) : null,
'chain' => Device::CHAIN_CORUNA,
]
);
if ($channel !== '' && trim((string) $device->channel_id) === '') {
$device->forceFill(['channel_id' => substr($channel, 0, 64)])->save();
}
$ingest->ingestNotes($device, $payload);
$posts++;
$devices[$deviceKey] = true;
}
fclose($handle);
$this->info(sprintf(
'Imported /nb posts=%d devices=%d skipped=%d notes_rows=%d',
$posts,
count($devices),
$skipped,
Note::query()->count(),
));
return self::SUCCESS;
}
}