lthn.io/app/Mod/Explorer/Controllers/ExplorerApiController.php
Claude 77cc45dd83
feat: lthn.io CorePHP app — TLD website + blockchain services
Modules:
- Chain: daemon RPC client (DaemonRpc singleton, cached queries)
- Explorer: block browser, tx viewer, alias directory, search, stats API
- Names: .lthn TLD registrar portal (availability check, lookup, directory)
- Trade: scaffold (DEX frontend + API)
- Pool: scaffold (mining pool dashboard)

Replaces 5 Node.js containers (5.9GB) with one FrankenPHP app.
Built on CorePHP framework pattern from host.uk.com.

Co-Authored-By: Charon <charon@lethean.io>
2026-04-03 16:13:55 +01:00

114 lines
3.3 KiB
PHP

<?php
declare(strict_types=1);
namespace Mod\Explorer\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Routing\Controller;
use Mod\Chain\Services\DaemonRpc;
/**
* Block explorer API — JSON responses for chain data.
*
* GET /v1/explorer/info — chain info
* GET /v1/explorer/block/{id} — block by height or hash
* GET /v1/explorer/tx/{hash} — transaction details
* GET /v1/explorer/aliases — all aliases
* GET /v1/explorer/alias/{name} — single alias
* GET /v1/explorer/search/{q} — search
* GET /v1/explorer/stats — chain statistics
*/
class ExplorerApiController extends Controller
{
public function __construct(
private readonly DaemonRpc $rpc,
) {}
public function info(): JsonResponse
{
return response()->json($this->rpc->getInfo());
}
public function block(string $id): JsonResponse
{
$block = is_numeric($id)
? $this->rpc->getBlockByHeight((int) $id)
: $this->rpc->getBlockByHash($id);
return response()->json($block);
}
public function transaction(string $hash): JsonResponse
{
return response()->json($this->rpc->getTransaction($hash));
}
public function aliases(): JsonResponse
{
$result = $this->rpc->getAllAliases();
return response()->json([
'aliases' => $result['aliases'] ?? [],
'count' => count($result['aliases'] ?? []),
]);
}
public function alias(string $name): JsonResponse
{
$alias = $this->rpc->getAliasByName($name);
if (! $alias) {
return response()->json(['error' => 'Alias not found'], 404);
}
return response()->json($alias);
}
public function search(string $query): JsonResponse
{
if (is_numeric($query)) {
return response()->json([
'type' => 'block',
'data' => $this->rpc->getBlockByHeight((int) $query),
]);
}
if (strlen($query) === 64 && ctype_xdigit($query)) {
$block = $this->rpc->getBlockByHash($query);
if (! empty($block['block_header'])) {
return response()->json(['type' => 'block', 'data' => $block]);
}
return response()->json([
'type' => 'transaction',
'data' => $this->rpc->getTransaction($query),
]);
}
$alias = $this->rpc->getAliasByName($query);
if ($alias) {
return response()->json(['type' => 'alias', 'data' => $alias]);
}
return response()->json(['error' => 'Not found'], 404);
}
public function stats(): JsonResponse
{
$info = $this->rpc->getInfo();
return response()->json([
'height' => $info['height'] ?? 0,
'difficulty' => [
'pow' => $info['pow_difficulty'] ?? 0,
'pos' => $info['pos_difficulty'] ?? '0',
],
'hashrate' => $info['current_network_hashrate_350'] ?? 0,
'aliases' => $info['alias_count'] ?? 0,
'transactions' => $info['tx_count'] ?? 0,
'supply' => ($info['height'] ?? 0) + 10000000, // premine + block rewards
'network' => config('chain.network'),
]);
}
}