lthn.io/app/Mod/Names/Controllers/NamesController.php

128 lines
3.7 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
namespace Mod\Names\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Mod\Chain\Services\DaemonRpc;
/**
* .lthn TLD registrar API.
*
* GET /v1/names/available/{name} check if name is available
* GET /v1/names/lookup/{name} look up a registered name
* GET /v1/names/search?q={query} search names
* POST /v1/names/register request name registration (requires auth)
* GET /v1/names/reserved list reserved/protected names
*/
class NamesController extends Controller
{
public function __construct(
private readonly DaemonRpc $rpc,
) {}
/**
* GET /v1/names/available/myname
*/
public function available(string $name): JsonResponse
{
$name = strtolower(trim($name));
if (! $this->isValidName($name)) {
return response()->json([
'available' => false,
'reason' => 'Invalid name. Use 1-63 lowercase alphanumeric characters.',
]);
}
$alias = $this->rpc->getAliasByName($name);
return response()->json([
'name' => $name,
'available' => $alias === null,
'fqdn' => "{$name}.lthn",
]);
}
/**
* GET /v1/names/lookup/charon
*/
public function lookup(string $name): JsonResponse
{
$alias = $this->rpc->getAliasByName(strtolower(trim($name)));
if (! $alias) {
return response()->json(['error' => 'Name not registered'], 404);
}
return response()->json([
'name' => $name,
'fqdn' => "{$name}.lthn",
'address' => $alias['address'] ?? '',
'comment' => $alias['comment'] ?? '',
'registered' => true,
]);
}
/**
* GET /v1/names/search?q=gate
*/
public function search(Request $request): JsonResponse
{
$query = strtolower(trim((string) $request->get('q')));
$result = $this->rpc->getAllAliases();
$aliases = $result['aliases'] ?? [];
$matches = array_filter($aliases, function ($alias) use ($query) {
return str_contains($alias['alias'] ?? '', $query)
|| str_contains($alias['comment'] ?? '', $query);
});
return response()->json([
'query' => $query,
'results' => array_values($matches),
'count' => count($matches),
]);
}
/**
* GET /v1/names/directory
*/
public function directory(): JsonResponse
{
$result = $this->rpc->getAllAliases();
$aliases = $result['aliases'] ?? [];
// Group by type from comment metadata
$grouped = ['gateway' => [], 'service' => [], 'exit' => [], 'reserved' => [], 'user' => []];
foreach ($aliases as $alias) {
$comment = $alias['comment'] ?? '';
if (str_contains($comment, 'type=gateway')) {
$grouped['gateway'][] = $alias;
} elseif (str_contains($comment, 'type=exit')) {
$grouped['exit'][] = $alias;
} elseif (str_contains($comment, 'type=service')) {
$grouped['service'][] = $alias;
} elseif (str_contains($comment, 'type=reserved')) {
$grouped['reserved'][] = $alias;
} else {
$grouped['user'][] = $alias;
}
}
return response()->json([
'total' => count($aliases),
'directory' => $grouped,
]);
}
private function isValidName(string $name): bool
{
return (bool) preg_match('/^[a-z0-9][a-z0-9-]{0,62}$/', $name);
}
}