128 lines
3.7 KiB
PHP
128 lines
3.7 KiB
PHP
|
|
<?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);
|
||
|
|
}
|
||
|
|
}
|