lthn.io/app/Mod/Names/Actions/RegisterName.php
Claude ca11c4ccee
refactor: extract Actions for CheckAvailability, SubmitClaim, RegisterName
CorePHP Actions pattern — single-purpose classes with static ::run().
Controller methods now delegate to Actions. Each Action validates,
executes, and returns typed results. Enables reuse from commands,
jobs, and tests without going through HTTP.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 11:24:27 +01:00

90 lines
2.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Mod\Names\Actions;
use Illuminate\Support\Facades\Cache;
use Illuminate\Validation\ValidationException;
use Mod\Chain\Services\DaemonRpc;
use Mod\Chain\Services\WalletRpc;
/**
* Register a .lthn name on the blockchain.
*
* $result = RegisterName::run('mybrand', '');
* echo $result['ticket_id'];
*/
class RegisterName
{
public function __construct(
private readonly DaemonRpc $rpc,
private readonly WalletRpc $wallet,
) {}
public function handle(string $name, string $address = ''): array
{
$name = strtolower(trim($name));
$this->validate($name);
// Atomic reservation lock (10 min)
$lockKey = "name_lock:{$name}";
if (! Cache::add($lockKey, true, 600)) {
throw ValidationException::withMessages([
'name' => 'Name is being registered by another request.',
]);
}
// Use registrar wallet if no address provided
if (empty($address)) {
$address = $this->wallet->getAddress();
}
$comment = 'v=lthn1;type=user';
$result = $this->wallet->registerAlias($name, $address, $comment);
$ticketId = bin2hex(random_bytes(6));
if (isset($result['tx_id'])) {
return [
'name' => $name,
'fqdn' => "{$name}.lthn",
'ticket_id' => $ticketId,
'tx_id' => $result['tx_id'],
'status' => 'pending',
'address' => $address,
];
}
// Registration queued (chain busy)
Cache::forget($lockKey);
return [
'name' => $name,
'fqdn' => "{$name}.lthn",
'ticket_id' => $ticketId,
'status' => 'queued',
'message' => $result['error'] ?? 'Queued for next block.',
];
}
private function validate(string $name): void
{
if (! preg_match('/^[a-z0-9][a-z0-9.\-]{0,254}$/', $name) || strlen($name) < 6) {
throw ValidationException::withMessages([
'name' => 'Invalid name. Use 6+ lowercase alphanumeric characters.',
]);
}
if ($this->rpc->getAliasByName($name) !== null) {
throw ValidationException::withMessages([
'name' => 'Name already registered.',
]);
}
}
public static function run(string $name, string $address = ''): array
{
return app(static::class)->handle($name, $address);
}
}