Files
zpan/server/http/admin-stats.ts
T
saltbo 60f8f64481 refactor(analytics): enforce offline result boundaries
Build versioned UTC hourly rollups, validate analytics facts, and keep dashboard requests result-only with explicit coverage semantics.

Move derived metrics to the server and simplify the admin dashboard around one reconcilable UTC range without export.
2026-07-18 01:58:34 -04:00

68 lines
3.0 KiB
TypeScript

import { zValidator } from '@hono/zod-validator'
import { Hono } from 'hono'
import { z } from 'zod'
import { addCalendarDays, utcDateStart } from '../domain/admin-stats-time'
import { requireAdmin } from '../middleware/auth'
import type { Env } from '../middleware/platform'
import { requireFeature } from '../middleware/require-feature'
import {
getAdminDashboardGrowthStats,
getAdminDashboardOperationsStats,
getAdminDashboardOverviewStats,
getAdminDashboardSharingStats,
getAdminDashboardStorageStats,
getAdminDashboardTrafficStats,
} from '../usecases/admin-stats'
const dashboardDateSchema = z
.string()
.refine(isValidDashboardDate, { message: 'Expected valid yyyy-MM-dd or ISO datetime with offset' })
const rangeQuerySchema = z.object({
from: dashboardDateSchema.optional(),
to: dashboardDateSchema.optional(),
timeZone: z.literal('UTC').optional(),
})
function parseRange(query: z.infer<typeof rangeQuerySchema>): { from?: Date; to?: Date; timeZone: 'UTC' } {
return {
from: query.from ? parseDashboardDate(query.from, 'start') : undefined,
to: query.to ? parseDashboardDate(query.to, 'end') : undefined,
timeZone: 'UTC',
}
}
function isValidDashboardDate(value: string): boolean {
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return z.string().datetime({ offset: true }).safeParse(value).success
const date = new Date(`${value}T00:00:00.000Z`)
return !Number.isNaN(date.getTime()) && date.toISOString().startsWith(value)
}
function parseDashboardDate(value: string, boundary: 'start' | 'end'): Date {
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
if (boundary === 'start') return utcDateStart(value)
return new Date(utcDateStart(addCalendarDays(value, 1)).getTime() - 1)
}
return new Date(value)
}
export const adminStats = new Hono<Env>()
.get('/overview', requireAdmin, zValidator('query', rangeQuerySchema), async (c) =>
c.json(await getAdminDashboardOverviewStats(c.get('deps'), parseRange(c.req.valid('query'))), 200),
)
.get('/operations', requireAdmin, requireFeature('analytics'), zValidator('query', rangeQuerySchema), async (c) =>
c.json(await getAdminDashboardOperationsStats(c.get('deps'), parseRange(c.req.valid('query'))), 200),
)
.get('/growth', requireAdmin, requireFeature('analytics'), zValidator('query', rangeQuerySchema), async (c) =>
c.json(await getAdminDashboardGrowthStats(c.get('deps'), parseRange(c.req.valid('query'))), 200),
)
.get('/storage', requireAdmin, requireFeature('analytics'), zValidator('query', rangeQuerySchema), async (c) =>
c.json(await getAdminDashboardStorageStats(c.get('deps'), parseRange(c.req.valid('query'))), 200),
)
.get('/traffic', requireAdmin, requireFeature('analytics'), zValidator('query', rangeQuerySchema), async (c) =>
c.json(await getAdminDashboardTrafficStats(c.get('deps'), parseRange(c.req.valid('query'))), 200),
)
.get('/sharing', requireAdmin, requireFeature('analytics'), zValidator('query', rangeQuerySchema), async (c) =>
c.json(await getAdminDashboardSharingStats(c.get('deps'), parseRange(c.req.valid('query'))), 200),
)