const CESIUM_PODS = [ 'https://g1.data.brussels.ovh/user/profile/_search', 'https://g1.data.le-sou.org/user/profile/_search', 'https://g1.data.e-is.pro/user/profile/_search', ]; const BATCH_SIZE = 500; export type GeoMember = { pubkey: string; title: string; city: string; lat: number; lon: number; }; /** Find the first Cesium+ pod that responds successfully. */ async function findWorkingPod(): Promise { for (const url of CESIUM_PODS) { try { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ size: 0, query: { match_all: {} } }), }); if (res.ok) return url; } catch { // try next pod } } throw new Error('Aucun pod Cesium+ disponible'); } /** * Fetch Cesium+ profiles for a given list of v1 pubkeys. * Uses Elasticsearch `ids` query with batches of 500, filtered to geolocated profiles only. * Pass `null` to skip fetching (e.g. while pubkeys are still loading). */ export function useCesiumProfiles(v1Pubkeys: Ref) { const geoMembers = ref([]); const loading = ref(true); const error = ref(null); watch(v1Pubkeys, async (pubkeys) => { if (pubkeys === null) return; loading.value = true; error.value = null; try { const podUrl = await findWorkingPod(); const allMembers: GeoMember[] = []; for (let i = 0; i < pubkeys.length; i += BATCH_SIZE) { const batch = pubkeys.slice(i, i + BATCH_SIZE); const res = await fetch(podUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ size: batch.length, _source: ['title', 'city', 'geoPoint'], query: { bool: { must: [ { ids: { values: batch } }, { exists: { field: 'geoPoint' } }, ], }, }, }), }); if (!res.ok) throw new Error(`Cesium+ HTTP ${res.status}`); const json = await res.json(); for (const hit of json.hits?.hits ?? []) { const s = hit._source; if (!s?.geoPoint?.lat || !s?.geoPoint?.lon) continue; allMembers.push({ pubkey: hit._id, title: s.title || '', city: s.city || '', lat: s.geoPoint.lat, lon: s.geoPoint.lon, }); } } geoMembers.value = allMembers; } catch (e: any) { error.value = e.message; } finally { loading.value = false; } }, { immediate: true }); return { geoMembers, loading, error }; }