feat: indicateurs de statut et configuration des endpoints SubSquid/Cesium+
ci/woodpecker/push/woodpecker Pipeline was successful

- Dots de statut en temps réel dans le StatsPanel (ok/slow/error + latence)
- Bannière d'alerte si un service est inaccessible
- EndpointPopover : sélection parmi nœuds connus, test de latence live, URL custom
- Rechargement automatique des données après changement d'endpoint
- SubsquidAdapter et CesiumAdapter lisent l'URL active depuis EndpointConfig
- InfoPanel mis à jour (overlay DU + statut des services)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
syoul
2026-04-21 20:43:33 +02:00
parent 7c9d626b98
commit 0d9415ae6a
9 changed files with 448 additions and 10 deletions
+35
View File
@@ -0,0 +1,35 @@
const STORAGE_KEY = {
subsquid: 'geoflux-ep-subsquid',
cesium: 'geoflux-ep-cesium',
} as const;
export const DEFAULT_ENDPOINTS = {
subsquid: 'https://squidv2s.syoul.fr/v1/graphql',
cesium: 'https://g1.data.e-is.pro',
} as const;
export const KNOWN_SUBSQUID_NODES: { label: string; url: string }[] = [
{ label: 'squidv2s.syoul.fr (défaut)', url: 'https://squidv2s.syoul.fr/v1/graphql' },
];
export const KNOWN_CESIUM_NODES: { label: string; url: string }[] = [
{ label: 'g1.data.e-is.pro (défaut)', url: 'https://g1.data.e-is.pro' },
];
export function getSubsquidUrl(): string {
return localStorage.getItem(STORAGE_KEY.subsquid) ?? DEFAULT_ENDPOINTS.subsquid;
}
export function getCesiumUrl(): string {
return localStorage.getItem(STORAGE_KEY.cesium) ?? DEFAULT_ENDPOINTS.cesium;
}
export function setSubsquidUrl(url: string): void {
if (url === DEFAULT_ENDPOINTS.subsquid) localStorage.removeItem(STORAGE_KEY.subsquid);
else localStorage.setItem(STORAGE_KEY.subsquid, url);
}
export function setCesiumUrl(url: string): void {
if (url === DEFAULT_ENDPOINTS.cesium) localStorage.removeItem(STORAGE_KEY.cesium);
else localStorage.setItem(STORAGE_KEY.cesium, url);
}
+3 -2
View File
@@ -12,6 +12,7 @@
*/
import { z } from 'zod';
import { getCesiumUrl } from '../EndpointConfig';
export const CESIUM_ENDPOINT = 'https://g1.data.e-is.pro';
@@ -136,7 +137,7 @@ export async function resolveGeoByKeys(
_source: ['title', 'city', 'geoPoint'],
};
const response = await fetch(`${CESIUM_ENDPOINT}/user/profile/_search`, {
const response = await fetch(`${getCesiumUrl()}/user/profile/_search`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(query),
@@ -210,7 +211,7 @@ export async function resolveGeoByNames(
_source: ['title', 'city', 'geoPoint'],
};
const response = await fetch(`${CESIUM_ENDPOINT}/user/profile/_search`, {
const response = await fetch(`${getCesiumUrl()}/user/profile/_search`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(query),
+5 -4
View File
@@ -12,6 +12,7 @@
*/
import { z } from 'zod';
import { getSubsquidUrl } from '../EndpointConfig';
export const SUBSQUID_ENDPOINT = 'https://squidv2s.syoul.fr/v1/graphql';
@@ -136,7 +137,7 @@ const IDENTITY_KEY_MAP_QUERY = `
* car previousId = clé génesis = clé Ed25519 v1 = _id dans Cesium+
*/
export async function buildIdentityKeyMap(): Promise<Map<string, string>> {
const response = await fetch(SUBSQUID_ENDPOINT, {
const response = await fetch(getSubsquidUrl(), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: IDENTITY_KEY_MAP_QUERY }),
@@ -157,7 +158,7 @@ export async function buildIdentityKeyMap(): Promise<Map<string, string>> {
export async function fetchCurrentUD(): Promise<number> {
const UD_FALLBACK = 11.78; // valeur au bloc 225874 — mis à jour si la requête échoue
try {
const response = await fetch(SUBSQUID_ENDPOINT, {
const response = await fetch(getSubsquidUrl(), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -193,7 +194,7 @@ const ACTIVE_MEMBERS_QUERY = `
/** Retourne la liste des clés SS58 de tous les membres WoT actifs. */
export async function fetchActiveMemberKeys(): Promise<string[]> {
const res = await fetch(SUBSQUID_ENDPOINT, {
const res = await fetch(getSubsquidUrl(), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: ACTIVE_MEMBERS_QUERY }),
@@ -222,7 +223,7 @@ export async function fetchTransfers(
Date.now() - periodDays * 24 * 60 * 60 * 1000
).toISOString();
const response = await fetch(SUBSQUID_ENDPOINT, {
const response = await fetch(getSubsquidUrl(), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({