feat(settings): add GeoIP provider selection to settings page

- Introduced a new ResourceSelect component for selecting the GeoIP provider.
- Added options for disabling GeoIP, using MaxMind mmdb, and various external GeoIP services.
- Implemented saving functionality for the selected GeoIP provider with a corresponding button.
- Updated default operation fields to include GeoIPProvider with a default value of 'disabled'.
This commit is contained in:
ryan
2026-03-14 15:19:46 +08:00
parent f6dd7df55c
commit 4a50762092
3 changed files with 99 additions and 27 deletions
@@ -9,6 +9,7 @@ import { useEffect, useMemo, useState } from 'react';
import { EmptyState } from '@/components/feedback/empty-state';
import { useTheme } from '@/components/providers/theme-provider';
import worldGeoJson from '@/features/dashboard/data/world-geo.json';
import { StatusBadge } from '@/components/ui/status-badge';
import type {
DashboardConfig,
@@ -71,6 +72,56 @@ type MapNodeDatum = {
};
};
type WorldGeoJson = {
type: string;
features?: unknown[];
};
let worldMapRegistrationAttempted = false;
let worldMapRegistrationSucceeded = false;
let worldMapRegistrationError: Error | null = null;
function ensureWorldMapRegistered() {
if (worldMapRegistrationSucceeded || echarts.getMap('world')) {
worldMapRegistrationSucceeded = true;
worldMapRegistrationError = null;
return true;
}
if (worldMapRegistrationAttempted) {
return false;
}
worldMapRegistrationAttempted = true;
try {
const geoJson = worldGeoJson as WorldGeoJson;
if (geoJson.type !== 'FeatureCollection' || !Array.isArray(geoJson.features)) {
throw new Error('invalid world geojson payload');
}
echarts.registerMap(
'world',
geoJson as unknown as Parameters<typeof echarts.registerMap>[1],
);
if (!echarts.getMap('world')) {
throw new Error('world map registration failed');
}
worldMapRegistrationSucceeded = true;
worldMapRegistrationError = null;
return true;
} catch (error) {
worldMapRegistrationSucceeded = false;
worldMapRegistrationError =
error instanceof Error ? error : new Error('unknown world map registration error');
console.error('Failed to register ECharts world map', worldMapRegistrationError);
return false;
}
}
function formatPercent(value: number) {
if (!Number.isFinite(value)) {
return '0%';
@@ -271,32 +322,9 @@ export function WorldStage({
const [mapFailed, setMapFailed] = useState(false);
useEffect(() => {
let disposed = false;
import('@/features/dashboard/data/world-geo.json')
.then(({ default: worldGeoJson }) => {
if (!echarts.getMap('world')) {
echarts.registerMap('world', worldGeoJson);
}
if (!echarts.getMap('world')) {
throw new Error('world map registration failed');
}
if (!disposed) {
setMapReady(true);
setMapFailed(false);
}
})
.catch((error) => {
console.error('Failed to register ECharts world map', error);
if (!disposed) {
setMapReady(false);
setMapFailed(true);
}
});
return () => {
disposed = true;
};
const ready = ensureWorldMapRegistered();
setMapReady(ready);
setMapFailed(!ready);
}, []);
const onlineRate =