feat: chain:stop command + DNS update activity logging

chain:stop sends SIGTERM and waits for graceful shutdown.
DNS updateRecords now logs 'dns_updated' activity with ticket_id
and records. Claim approve/reject also logged in previous commit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude 2026-04-04 12:09:19 +01:00
parent 3f82c497fd
commit ad29a45507
No known key found for this signature in database
GPG key ID: AF404715446AEB41
3 changed files with 60 additions and 0 deletions

View file

@ -37,6 +37,7 @@ class Boot
public function onConsole(ConsoleBooting $event): void
{
$event->command(Commands\ChainStart::class);
$event->command(Commands\ChainStop::class);
$event->command(Commands\ChainStatus::class);
}
}

View file

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Mod\Chain\Commands;
use Illuminate\Console\Command;
/**
* Gracefully stop chain daemon and wallet.
*
* php artisan chain:stop
*/
class ChainStop extends Command
{
protected $signature = 'chain:stop';
protected $description = 'Stop testnet chain daemon and wallet';
public function handle(): int
{
$this->stopProcess('lethean-wallet-cli', 'Wallet');
$this->stopProcess('lethean-testnet-chain-node', 'Daemon');
return self::SUCCESS;
}
private function stopProcess(string $name, string $label): void
{
// Safe: escapeshellarg prevents injection, process names are hardcoded constants
exec("pgrep -f " . escapeshellarg($name), $pids); // @codeCoverageIgnore
if (empty($pids)) {
$this->info("{$label}: not running.");
return;
}
foreach ($pids as $pid) {
posix_kill((int) $pid, SIGTERM);
}
$this->info("{$label}: SIGTERM sent to " . count($pids) . " process(es).");
// Wait up to 10s for graceful shutdown
for ($i = 0; $i < 10; $i++) {
exec("pgrep -f " . escapeshellarg($name), $check); // @codeCoverageIgnore
if (empty($check)) {
$this->info("{$label}: stopped.");
return;
}
sleep(1);
}
$this->warn("{$label}: still running after 10s.");
}
}

View file

@ -282,6 +282,7 @@ class NamesController extends Controller
if (isset($result['tx_id'])) {
Cache::put($editLock, true, 300);
$ticket = Models\DnsTicket::open($name, $records, $result['tx_id'], $address, $comment);
Models\NameActivity::log($name, 'dns_updated', ['ticket_id' => $ticket->ticket_id, 'records' => $records], request()?->ip());
return response()->json([
'name' => $name,