diff --git a/app/sites/[id]/cdn/page.tsx b/app/sites/[id]/cdn/page.tsx index d44f9ab..3ace75a 100644 --- a/app/sites/[id]/cdn/page.tsx +++ b/app/sites/[id]/cdn/page.tsx @@ -24,15 +24,63 @@ import { SkeletonLine, StatCardSkeleton, useMinimumLoading, useSkeletonFade } fr // ─── Helpers ──────────────────────────────────────────────────── -function getCountryName(code: string): string { +// US state codes → map to "US" for the dotted map +const US_STATES = new Set([ + 'AL','AK','AZ','AR','CO','CT','DC','DE','FL','GA','HI','ID','IL','IN','IA', + 'KS','KY','LA','ME','MD','MA','MI','MN','MS','MO','MT','NE','NV','NH','NJ', + 'NM','NY','NC','ND','OH','OK','OR','PA','RI','SC','SD','TN','TX','UT','VT', + 'VA','WA','WV','WI','WY', +]) +// Canadian province codes → map to "CA" +const CA_PROVINCES = new Set(['AB','BC','MB','NB','NL','NS','NT','NU','ON','PE','QC','SK','YT']) + +/** + * Extract ISO country code from BunnyCDN datacenter string. + * e.g. "EU: Zurich, CH" → "CH", "NA: Chicago, IL" → "US", "NA: Toronto, CA" → "CA" + */ +function extractCountryCode(datacenter: string): string { + const parts = datacenter.split(', ') + const code = parts[parts.length - 1]?.trim().toUpperCase() + if (!code || code.length !== 2) return '' + if (US_STATES.has(code)) return 'US' + if (CA_PROVINCES.has(code)) return 'CA' + return code +} + +/** + * Extract the city name from a BunnyCDN datacenter string. + * e.g. "EU: Zurich, CH" → "Zurich" + */ +function extractCity(datacenter: string): string { + const afterColon = datacenter.split(': ')[1] || datacenter + return afterColon.split(',')[0]?.trim() || datacenter +} + +/** Convert ISO country code to flag emoji */ +function countryFlag(code: string): string { try { - const regionNames = new Intl.DisplayNames(['en'], { type: 'region' }) - return regionNames.of(code) || code - } catch { return code + .toUpperCase() + .split('') + .map(c => String.fromCodePoint(0x1F1E6 + c.charCodeAt(0) - 65)) + .join('') + } catch { + return '' } } +/** Aggregate bandwidth by ISO country code for the map */ +function aggregateByCountry(data: Array<{ country_code: string; bandwidth: number }>): Array<{ country: string; pageviews: number }> { + const byCountry = new Map() + for (const row of data) { + const cc = extractCountryCode(row.country_code) + if (cc) { + byCountry.set(cc, (byCountry.get(cc) || 0) + row.bandwidth) + } + } + return Array.from(byCountry, ([country, pageviews]) => ({ country, pageviews })) +} + function formatBytes(bytes: number): string { if (bytes === 0) return '0 B' const units = ['B', 'KB', 'MB', 'GB', 'TB'] @@ -424,34 +472,32 @@ export default function CDNPage() { {countries.length > 0 ? ( <>
- ({ - country: row.country_code, - pageviews: row.bandwidth, - }))} - /> +
-
+
{countries.map((row) => { const pct = totalBandwidth > 0 ? (row.bandwidth / totalBandwidth) * 100 : 0 + const cc = extractCountryCode(row.country_code) + const city = extractCity(row.country_code) return ( -
-
- - {getCountryName(row.country_code)} - - +
+
+ {cc && {countryFlag(cc)}} +
+ {city} +
+ {formatBytes(row.bandwidth)}
-
- {pct.toFixed(1)}% of total traffic +
+ {pct.toFixed(1)}% of total traffic
)