feat: add BunnyCDN integration
This commit is contained in:
84
lib/api/bunny.ts
Normal file
84
lib/api/bunny.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import apiRequest from './client'
|
||||
|
||||
// ─── Types ──────────────────────────────────────────────────────────
|
||||
|
||||
export interface BunnyStatus {
|
||||
connected: boolean
|
||||
pull_zone_id?: number
|
||||
pull_zone_name?: string
|
||||
status?: 'active' | 'syncing' | 'error'
|
||||
error_message?: string | null
|
||||
last_synced_at?: string | null
|
||||
created_at?: string
|
||||
}
|
||||
|
||||
export interface BunnyOverview {
|
||||
total_bandwidth: number
|
||||
total_requests: number
|
||||
cache_hit_rate: number
|
||||
avg_origin_response: number
|
||||
total_errors: number
|
||||
prev_total_bandwidth: number
|
||||
prev_total_requests: number
|
||||
prev_cache_hit_rate: number
|
||||
prev_avg_origin_response: number
|
||||
prev_total_errors: number
|
||||
}
|
||||
|
||||
export interface BunnyDailyRow {
|
||||
date: string
|
||||
bandwidth_used: number
|
||||
bandwidth_cached: number
|
||||
requests_served: number
|
||||
requests_cached: number
|
||||
error_3xx: number
|
||||
error_4xx: number
|
||||
error_5xx: number
|
||||
origin_response_time_avg: number
|
||||
}
|
||||
|
||||
export interface BunnyPullZone {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface BunnyGeoRow {
|
||||
country_code: string
|
||||
bandwidth: number
|
||||
requests: number
|
||||
}
|
||||
|
||||
// ─── API Functions ──────────────────────────────────────────────────
|
||||
|
||||
export async function getBunnyPullZones(siteId: string, apiKey: string): Promise<{ pull_zones: BunnyPullZone[], message?: string }> {
|
||||
return apiRequest<{ pull_zones: BunnyPullZone[], message?: string }>(
|
||||
`/sites/${siteId}/integrations/bunny/pull-zones?api_key=${encodeURIComponent(apiKey)}`
|
||||
)
|
||||
}
|
||||
|
||||
export async function connectBunny(siteId: string, apiKey: string, pullZoneId: number, pullZoneName: string): Promise<void> {
|
||||
await apiRequest(`/sites/${siteId}/integrations/bunny`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ api_key: apiKey, pull_zone_id: pullZoneId, pull_zone_name: pullZoneName }),
|
||||
})
|
||||
}
|
||||
|
||||
export async function getBunnyStatus(siteId: string): Promise<BunnyStatus> {
|
||||
return apiRequest<BunnyStatus>(`/sites/${siteId}/integrations/bunny/status`)
|
||||
}
|
||||
|
||||
export async function disconnectBunny(siteId: string): Promise<void> {
|
||||
await apiRequest(`/sites/${siteId}/integrations/bunny`, { method: 'DELETE' })
|
||||
}
|
||||
|
||||
export async function getBunnyOverview(siteId: string, startDate: string, endDate: string): Promise<BunnyOverview> {
|
||||
return apiRequest<BunnyOverview>(`/sites/${siteId}/bunny/overview?start_date=${startDate}&end_date=${endDate}`)
|
||||
}
|
||||
|
||||
export async function getBunnyDailyStats(siteId: string, startDate: string, endDate: string): Promise<{ daily_stats: BunnyDailyRow[] }> {
|
||||
return apiRequest<{ daily_stats: BunnyDailyRow[] }>(`/sites/${siteId}/bunny/daily-stats?start_date=${startDate}&end_date=${endDate}`)
|
||||
}
|
||||
|
||||
export async function getBunnyTopCountries(siteId: string, startDate: string, endDate: string, limit = 20): Promise<{ countries: BunnyGeoRow[] }> {
|
||||
return apiRequest<{ countries: BunnyGeoRow[] }>(`/sites/${siteId}/bunny/top-countries?start_date=${startDate}&end_date=${endDate}&limit=${limit}`)
|
||||
}
|
||||
@@ -35,6 +35,8 @@ import { listGoals, type Goal } from '@/lib/api/goals'
|
||||
import { listReportSchedules, type ReportSchedule } from '@/lib/api/report-schedules'
|
||||
import { getGSCStatus, getGSCOverview, getGSCTopQueries, getGSCTopPages, getGSCDailyTotals, getGSCNewQueries } from '@/lib/api/gsc'
|
||||
import type { GSCStatus, GSCOverview, GSCQueryResponse, GSCPageResponse, GSCDailyTotal, GSCNewQueries } from '@/lib/api/gsc'
|
||||
import { getBunnyStatus, getBunnyOverview, getBunnyDailyStats, getBunnyTopCountries } from '@/lib/api/bunny'
|
||||
import type { BunnyStatus, BunnyOverview, BunnyDailyRow, BunnyGeoRow } from '@/lib/api/bunny'
|
||||
import { getSubscription, type SubscriptionDetails } from '@/lib/api/billing'
|
||||
import type {
|
||||
Stats,
|
||||
@@ -86,6 +88,10 @@ const fetchers = {
|
||||
gscTopPages: (siteId: string, start: string, end: string, limit: number, offset: number) => getGSCTopPages(siteId, start, end, limit, offset),
|
||||
gscDailyTotals: (siteId: string, start: string, end: string) => getGSCDailyTotals(siteId, start, end),
|
||||
gscNewQueries: (siteId: string, start: string, end: string) => getGSCNewQueries(siteId, start, end),
|
||||
bunnyStatus: (siteId: string) => getBunnyStatus(siteId),
|
||||
bunnyOverview: (siteId: string, start: string, end: string) => getBunnyOverview(siteId, start, end),
|
||||
bunnyDailyStats: (siteId: string, start: string, end: string) => getBunnyDailyStats(siteId, start, end),
|
||||
bunnyTopCountries: (siteId: string, start: string, end: string) => getBunnyTopCountries(siteId, start, end),
|
||||
subscription: () => getSubscription(),
|
||||
}
|
||||
|
||||
@@ -469,6 +475,42 @@ export function useGSCNewQueries(siteId: string, start: string, end: string) {
|
||||
)
|
||||
}
|
||||
|
||||
// * Hook for BunnyCDN connection status
|
||||
export function useBunnyStatus(siteId: string) {
|
||||
return useSWR<BunnyStatus>(
|
||||
siteId ? ['bunnyStatus', siteId] : null,
|
||||
() => fetchers.bunnyStatus(siteId),
|
||||
{ ...dashboardSWRConfig, refreshInterval: 60 * 1000, dedupingInterval: 30 * 1000 }
|
||||
)
|
||||
}
|
||||
|
||||
// * Hook for BunnyCDN overview metrics (bandwidth, requests, cache hit rate)
|
||||
export function useBunnyOverview(siteId: string, startDate: string, endDate: string) {
|
||||
return useSWR<BunnyOverview>(
|
||||
siteId && startDate && endDate ? ['bunnyOverview', siteId, startDate, endDate] : null,
|
||||
() => fetchers.bunnyOverview(siteId, startDate, endDate),
|
||||
dashboardSWRConfig
|
||||
)
|
||||
}
|
||||
|
||||
// * Hook for BunnyCDN daily stats (bandwidth & requests per day)
|
||||
export function useBunnyDailyStats(siteId: string, startDate: string, endDate: string) {
|
||||
return useSWR<{ daily_stats: BunnyDailyRow[] }>(
|
||||
siteId && startDate && endDate ? ['bunnyDailyStats', siteId, startDate, endDate] : null,
|
||||
() => fetchers.bunnyDailyStats(siteId, startDate, endDate),
|
||||
dashboardSWRConfig
|
||||
)
|
||||
}
|
||||
|
||||
// * Hook for BunnyCDN top countries by bandwidth
|
||||
export function useBunnyTopCountries(siteId: string, startDate: string, endDate: string) {
|
||||
return useSWR<{ countries: BunnyGeoRow[] }>(
|
||||
siteId && startDate && endDate ? ['bunnyTopCountries', siteId, startDate, endDate] : null,
|
||||
() => fetchers.bunnyTopCountries(siteId, startDate, endDate),
|
||||
dashboardSWRConfig
|
||||
)
|
||||
}
|
||||
|
||||
// * Hook for subscription details (changes rarely)
|
||||
export function useSubscription() {
|
||||
return useSWR<SubscriptionDetails>(
|
||||
|
||||
Reference in New Issue
Block a user