mirror of
https://github.com/ajvpot/meshexplorer.git
synced 2026-08-07 17:12:43 +02:00
Migrate the /api/regions endpoint to ConnectRPC
main added a dynamically-discovered regions endpoint (region IATA system). Port it to a RegionsService.GetRegions RPC: new regions.proto (Region / RegionGroup messages), a handler reusing getAvailableRegions()/getRegionGroups(), and useRegions/useRegionGroups switched to connect-query against the generated types. The REST /api/regions route stays for coexistence. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package meshexplorer.v1;
|
||||
|
||||
// A dynamically-discovered region (meshcore_regions MV).
|
||||
message Region {
|
||||
// Canonical IATA region code, e.g. "SEA".
|
||||
string name = 1;
|
||||
string friendly_name = 2;
|
||||
// Distinct nodes seen in this region (last 30 days).
|
||||
int32 node_count = 3;
|
||||
string last_seen = 4;
|
||||
}
|
||||
|
||||
// A named set of IATA region codes (region_groups table).
|
||||
message RegionGroup {
|
||||
string code = 1;
|
||||
string name = 2;
|
||||
repeated string members = 3;
|
||||
}
|
||||
|
||||
message GetRegionsRequest {}
|
||||
|
||||
message GetRegionsResponse {
|
||||
repeated Region regions = 1;
|
||||
repeated RegionGroup groups = 2;
|
||||
}
|
||||
|
||||
service RegionsService {
|
||||
rpc GetRegions(GetRegionsRequest) returns (GetRegionsResponse);
|
||||
}
|
||||
@@ -1,37 +1,16 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { buildApiUrl } from "@/lib/api";
|
||||
import type { RegionGroup } from "@/lib/regionGroups";
|
||||
|
||||
export interface RegionOption {
|
||||
name: string;
|
||||
friendlyName: string;
|
||||
nodeCount?: number;
|
||||
lastSeen?: string;
|
||||
}
|
||||
|
||||
interface RegionsResponse {
|
||||
regions: RegionOption[];
|
||||
groups: RegionGroup[];
|
||||
}
|
||||
import { useQuery } from "@connectrpc/connect-query";
|
||||
import { RegionsService } from "@/gen/meshexplorer/v1/regions_pb";
|
||||
|
||||
const STALE_TIME = 60 * 60 * 1000; // 1h — the region set is slow-moving / MV-backed
|
||||
const GC_TIME = 2 * 60 * 60 * 1000;
|
||||
|
||||
// Single shared query (one network request) feeding both useRegions() and useRegionGroups().
|
||||
function useRegionsQuery() {
|
||||
return useQuery<RegionsResponse>({
|
||||
queryKey: ["regions"],
|
||||
queryFn: async ({ signal }) => {
|
||||
const response = await fetch(buildApiUrl("/api/regions"), { signal });
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch regions: ${response.statusText}`);
|
||||
}
|
||||
return response.json();
|
||||
},
|
||||
staleTime: STALE_TIME,
|
||||
gcTime: GC_TIME,
|
||||
retry: 2,
|
||||
});
|
||||
return useQuery(
|
||||
RegionsService.method.getRegions,
|
||||
{},
|
||||
{ staleTime: STALE_TIME, gcTime: GC_TIME, retry: 2 },
|
||||
);
|
||||
}
|
||||
|
||||
export function useRegions() {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { ServiceImpl } from "@connectrpc/connect";
|
||||
import { RegionsService } from "@/gen/meshexplorer/v1/regions_pb";
|
||||
import { getAvailableRegions, getRegionGroups } from "@/lib/clickhouse/regions";
|
||||
import { num } from "./mappers";
|
||||
|
||||
export const regionsServiceImpl: ServiceImpl<typeof RegionsService> = {
|
||||
async getRegions() {
|
||||
const [regions, groups] = await Promise.all([getAvailableRegions(), getRegionGroups()]);
|
||||
return {
|
||||
regions: regions.map((r) => ({
|
||||
name: r.name,
|
||||
friendlyName: r.friendlyName,
|
||||
nodeCount: num(r.nodeCount),
|
||||
lastSeen: r.lastSeen,
|
||||
})),
|
||||
groups: groups.map((g) => ({
|
||||
code: g.code,
|
||||
name: g.name,
|
||||
members: g.members,
|
||||
})),
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -5,12 +5,14 @@ import { NodeService } from "@/gen/meshexplorer/v1/node_pb";
|
||||
import { StatsService } from "@/gen/meshexplorer/v1/stats_pb";
|
||||
import { ChatService } from "@/gen/meshexplorer/v1/chat_pb";
|
||||
import { PacketsService } from "@/gen/meshexplorer/v1/packets_pb";
|
||||
import { RegionsService } from "@/gen/meshexplorer/v1/regions_pb";
|
||||
import { mapServiceImpl } from "./map";
|
||||
import { neighborsServiceImpl } from "./neighbors";
|
||||
import { nodeServiceImpl } from "./node";
|
||||
import { statsServiceImpl } from "./stats";
|
||||
import { chatServiceImpl } from "./chat";
|
||||
import { packetsServiceImpl } from "./packets";
|
||||
import { regionsServiceImpl } from "./regions";
|
||||
|
||||
export default function routes(router: ConnectRouter) {
|
||||
router.service(MapService, mapServiceImpl);
|
||||
@@ -19,4 +21,5 @@ export default function routes(router: ConnectRouter) {
|
||||
router.service(StatsService, statsServiceImpl);
|
||||
router.service(ChatService, chatServiceImpl);
|
||||
router.service(PacketsService, packetsServiceImpl);
|
||||
router.service(RegionsService, regionsServiceImpl);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user