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>
90 lines
2.5 KiB
PHP
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);
|
|
}
|
|
}
|