This commit is contained in:
Saxon Fletcher committed 2025-08-29 15:36:31 +10:00
1 parent 0a5eae6aaf
commit b2bb62ccaa
1 file changed
+188 -163
@@ -60,6 +60,121 @@ const CHART_INTERVALS: ChartIntervals[] = [
}, },
] ]
type LogsBarChartDatum = {
timestamp: string
error_count: number
ok_count: number
warning_count: number
}
type ServiceKey = 'db' | 'functions' | 'auth' | 'storage' | 'realtime'
type ServiceEntry = {
key: ServiceKey
title: string
icon: React.ReactNode
href?: string
route: string
enabled: boolean
}
type ServiceComputed = ServiceEntry & {
data: LogsBarChartDatum[]
total: number
warn: number
err: number
stats: ReturnType<typeof useProjectUsageStats>
}
type ServiceStatsMap = Record<
ServiceKey,
{
current: ReturnType<typeof useProjectUsageStats>
previous: ReturnType<typeof useProjectUsageStats>
}
>
const useServiceStats = (
projectRef: string,
timestampStart: string,
timestampEnd: string,
previousStart: string,
previousEnd: string
): ServiceStatsMap => {
const dbCurrent = useProjectUsageStats({
projectRef,
table: LogsTableName.EDGE,
timestampStart,
timestampEnd,
})
const dbPrevious = useProjectUsageStats({
projectRef,
table: LogsTableName.EDGE,
timestampStart: previousStart,
timestampEnd: previousEnd,
})
const fnCurrent = useProjectUsageStats({
projectRef,
table: LogsTableName.FN_EDGE,
timestampStart,
timestampEnd,
})
const fnPrevious = useProjectUsageStats({
projectRef,
table: LogsTableName.FN_EDGE,
timestampStart: previousStart,
timestampEnd: previousEnd,
})
const authCurrent = useProjectUsageStats({
projectRef,
table: LogsTableName.AUTH,
timestampStart,
timestampEnd,
})
const authPrevious = useProjectUsageStats({
projectRef,
table: LogsTableName.AUTH,
timestampStart: previousStart,
timestampEnd: previousEnd,
})
const storageCurrent = useProjectUsageStats({
projectRef,
table: LogsTableName.STORAGE,
timestampStart,
timestampEnd,
})
const storagePrevious = useProjectUsageStats({
projectRef,
table: LogsTableName.STORAGE,
timestampStart: previousStart,
timestampEnd: previousEnd,
})
const realtimeCurrent = useProjectUsageStats({
projectRef,
table: LogsTableName.REALTIME,
timestampStart,
timestampEnd,
})
const realtimePrevious = useProjectUsageStats({
projectRef,
table: LogsTableName.REALTIME,
timestampStart: previousStart,
timestampEnd: previousEnd,
})
return {
db: { current: dbCurrent, previous: dbPrevious },
functions: { current: fnCurrent, previous: fnPrevious },
auth: { current: authCurrent, previous: authPrevious },
storage: { current: storageCurrent, previous: storagePrevious },
realtime: { current: realtimeCurrent, previous: realtimePrevious },
}
}
const ProjectUsage = () => { const ProjectUsage = () => {
const router = useRouter() const router = useRouter()
const { ref: projectRef } = useParams() const { ref: projectRef } = useParams()
@@ -90,7 +205,6 @@ const ProjectUsage = () => {
} }
}, [selectedInterval]) // Only recalculate when interval changes }, [selectedInterval]) // Only recalculate when interval changes
// Compute the previous period range for deltas
const { previousStart, previousEnd } = useMemo(() => { const { previousStart, previousEnd } = useMemo(() => {
const currentStart = dayjs(timestampStart) const currentStart = dayjs(timestampStart)
const currentEnd = dayjs(timestampEnd) const currentEnd = dayjs(timestampEnd)
@@ -100,102 +214,13 @@ const ProjectUsage = () => {
return { previousStart: prevStart.toISOString(), previousEnd: prevEnd.toISOString() } return { previousStart: prevStart.toISOString(), previousEnd: prevEnd.toISOString() }
}, [timestampStart, timestampEnd]) }, [timestampStart, timestampEnd])
// Fetch per-service event chart data const statsByService = useServiceStats(
const dbStats = useProjectUsageStats({ projectRef as string,
projectRef: projectRef as string,
table: LogsTableName.EDGE,
timestampStart, timestampStart,
timestampEnd, timestampEnd,
}) previousStart,
previousEnd
const fnEdgeStats = useProjectUsageStats({ )
projectRef: projectRef as string,
table: LogsTableName.FN_EDGE,
timestampStart,
timestampEnd,
})
const authStats = useProjectUsageStats({
projectRef: projectRef as string,
table: LogsTableName.AUTH,
timestampStart,
timestampEnd,
})
const storageStats = useProjectUsageStats({
projectRef: projectRef as string,
table: LogsTableName.STORAGE,
timestampStart,
timestampEnd,
})
const realtimeStats = useProjectUsageStats({
projectRef: projectRef as string,
table: LogsTableName.REALTIME,
timestampStart,
timestampEnd,
})
// Fetch previous period data for deltas
const dbStatsPrev = useProjectUsageStats({
projectRef: projectRef as string,
table: LogsTableName.EDGE,
timestampStart: previousStart,
timestampEnd: previousEnd,
})
const fnEdgeStatsPrev = useProjectUsageStats({
projectRef: projectRef as string,
table: LogsTableName.FN_EDGE,
timestampStart: previousStart,
timestampEnd: previousEnd,
})
const authStatsPrev = useProjectUsageStats({
projectRef: projectRef as string,
table: LogsTableName.AUTH,
timestampStart: previousStart,
timestampEnd: previousEnd,
})
const storageStatsPrev = useProjectUsageStats({
projectRef: projectRef as string,
table: LogsTableName.STORAGE,
timestampStart: previousStart,
timestampEnd: previousEnd,
})
const realtimeStatsPrev = useProjectUsageStats({
projectRef: projectRef as string,
table: LogsTableName.REALTIME,
timestampStart: previousStart,
timestampEnd: previousEnd,
})
// Transform eventChartData into LogsBarChart format
type LogsBarChartDatum = {
timestamp: string
error_count: number
ok_count: number
warning_count: number
}
type ServiceEntry = {
key: 'db' | 'functions' | 'auth' | 'storage' | 'realtime'
title: string
icon: React.ReactNode
href?: string
route: string
stats: ReturnType<typeof useProjectUsageStats>
enabled: boolean
}
type ServiceComputed = ServiceEntry & {
data: LogsBarChartDatum[]
total: number
warn: number
err: number
}
const toLogsBarChartData = (rows: any[] = []): LogsBarChartDatum[] => { const toLogsBarChartData = (rows: any[] = []): LogsBarChartDatum[] => {
return rows.map((r) => ({ return rows.map((r) => ({
@@ -212,63 +237,65 @@ const ProjectUsage = () => {
data.reduce((acc, r) => acc + r.warning_count, 0) data.reduce((acc, r) => acc + r.warning_count, 0)
const sumErrors = (data: LogsBarChartDatum[]) => data.reduce((acc, r) => acc + r.error_count, 0) const sumErrors = (data: LogsBarChartDatum[]) => data.reduce((acc, r) => acc + r.error_count, 0)
const serviceEntries: ServiceEntry[] = [ const serviceBase: ServiceEntry[] = useMemo(
{ () => [
key: 'db', {
title: 'Database requests', key: 'db',
icon: <Database strokeWidth={1.5} size={16} className="text-foreground-lighter" />, title: 'Database requests',
href: `/project/${projectRef}/editor`, icon: <Database strokeWidth={1.5} size={16} className="text-foreground-lighter" />,
route: '/logs/postgres-logs', href: `/project/${projectRef}/editor`,
stats: dbStats, route: '/logs/postgres-logs',
enabled: true, enabled: true,
}, },
{ {
key: 'functions', key: 'functions',
title: 'Functions requests', title: 'Functions requests',
icon: <Code strokeWidth={1.5} size={16} className="text-foreground-lighter" />, icon: <Code strokeWidth={1.5} size={16} className="text-foreground-lighter" />,
route: '/logs/edge-functions-logs', route: '/logs/edge-functions-logs',
stats: fnEdgeStats, enabled: true,
enabled: true, },
}, {
{ key: 'auth',
key: 'auth', title: 'Auth requests',
title: 'Auth requests', icon: <Key strokeWidth={1.5} size={16} className="text-foreground-lighter" />,
icon: <Key strokeWidth={1.5} size={16} className="text-foreground-lighter" />, href: `/project/${projectRef}/auth/users`,
href: `/project/${projectRef}/auth/users`, route: '/logs/auth-logs',
route: '/logs/auth-logs', enabled: authEnabled,
stats: authStats, },
enabled: authEnabled, {
}, key: 'storage',
{ title: 'Storage requests',
key: 'storage', icon: <Archive strokeWidth={1.5} size={16} className="text-foreground-lighter" />,
title: 'Storage requests', href: `/project/${projectRef}/storage/buckets`,
icon: <Archive strokeWidth={1.5} size={16} className="text-foreground-lighter" />, route: '/logs/storage-logs',
href: `/project/${projectRef}/storage/buckets`, enabled: storageEnabled,
route: '/logs/storage-logs', },
stats: storageStats, {
enabled: storageEnabled, key: 'realtime',
}, title: 'Realtime requests',
{ icon: <Zap strokeWidth={1.5} size={16} className="text-foreground-lighter" />,
key: 'realtime', route: '/logs/realtime-logs',
title: 'Realtime requests', enabled: true,
icon: <Zap strokeWidth={1.5} size={16} className="text-foreground-lighter" />, },
route: '/logs/realtime-logs', ],
stats: realtimeStats, [projectRef, authEnabled, storageEnabled]
enabled: true, )
},
]
const services: ServiceComputed[] = serviceEntries.map((s) => { const services: ServiceComputed[] = useMemo(
const data = toLogsBarChartData(s.stats.eventChartData) () =>
const total = sumTotal(data || []) serviceBase.map((s) => {
const warn = sumWarnings(data || []) const currentStats = statsByService[s.key].current
const err = sumErrors(data || []) const data = toLogsBarChartData(currentStats.eventChartData)
return { ...s, data, total, warn, err } const total = sumTotal(data)
}) const warn = sumWarnings(data)
const err = sumErrors(data)
return { ...s, stats: currentStats, data, total, warn, err }
}),
[serviceBase, statsByService]
)
const isLoading = services.some((s) => s.stats.isLoading) const isLoading = services.some((s) => s.stats.isLoading)
// Handle bar click navigation to logs pages
const handleBarClick = (logRoute: string) => (datum: any) => { const handleBarClick = (logRoute: string) => (datum: any) => {
if (!datum?.timestamp) return if (!datum?.timestamp) return
@@ -289,21 +316,19 @@ const ProjectUsage = () => {
const totalErrors = enabledServices.reduce((sum, s) => sum + (s.err || 0), 0) const totalErrors = enabledServices.reduce((sum, s) => sum + (s.err || 0), 0)
const errorRate = totalRequests > 0 ? (totalErrors / totalRequests) * 100 : 0 const errorRate = totalRequests > 0 ? (totalErrors / totalRequests) * 100 : 0
// Compute previous totals for enabled services const prevServiceTotals = useMemo(
const prevServiceTotals = [ () =>
{ enabled: true, stats: dbStatsPrev }, serviceBase.map((s) => {
{ enabled: true, stats: fnEdgeStatsPrev }, const previousStats = statsByService[s.key].previous
{ enabled: authEnabled, stats: authStatsPrev }, const data = toLogsBarChartData(previousStats.eventChartData)
{ enabled: storageEnabled, stats: storageStatsPrev }, return {
{ enabled: true, stats: realtimeStatsPrev }, enabled: s.enabled,
].map((entry) => { total: sumTotal(data),
const data = toLogsBarChartData(entry.stats.eventChartData) err: sumErrors(data),
return { }
enabled: entry.enabled, }),
total: sumTotal(data || []), [serviceBase, statsByService]
err: sumErrors(data || []), )
}
})
const enabledPrev = prevServiceTotals.filter((s) => s.enabled) const enabledPrev = prevServiceTotals.filter((s) => s.enabled)
const prevTotalRequests = enabledPrev.reduce((sum, s) => sum + (s.total || 0), 0) const prevTotalRequests = enabledPrev.reduce((sum, s) => sum + (s.total || 0), 0)