fix(admin): mount stats under site api group

This commit is contained in:
saltbo
2026-07-09 22:15:05 -04:00
committed by Jasper Van
parent b540956fb8
commit 337db89a4a
5 changed files with 41 additions and 32 deletions
+1 -1
View File
@@ -223,7 +223,7 @@ export function createApp(platform: Platform, auth: Auth, deps: Deps = createDep
app.route('/api/site/licensing', licensingAdmin)
app.route('/api/site/branding', brandingAdmin)
app.route('/api/site/audit-events', adminAudit)
app.route('/api/admin/stats', adminStats)
app.route('/api/site/stats', adminStats)
app.route('/api/downloads/downloaders', downloaders)
app.get('/api/health', (c) => c.json({ status: 'ok' }))
+25 -16
View File
@@ -3,13 +3,22 @@ import { describe, expect, it } from 'vitest'
import { currentTrafficPeriod } from '../domain/quota'
import { adminHeaders, createTestApp, seedProLicense } from '../test/setup.js'
describe('admin stats routes', () => {
describe('site stats routes', () => {
it('does not expose stats under the removed admin API group', async () => {
const { app } = await createTestApp()
const headers = await adminHeaders(app)
const res = await app.request('/api/admin/stats/overview', { headers })
expect(res.status).toBe(404)
})
it('does not expose legacy core or details stats endpoints', async () => {
const { app } = await createTestApp()
const headers = await adminHeaders(app)
const coreRes = await app.request('/api/admin/stats/core', { headers })
const detailsRes = await app.request('/api/admin/stats/details', { headers })
const coreRes = await app.request('/api/site/stats/core', { headers })
const detailsRes = await app.request('/api/site/stats/details', { headers })
expect(coreRes.status).toBe(404)
expect(detailsRes.status).toBe(404)
@@ -20,7 +29,7 @@ describe('admin stats routes', () => {
const headers = await adminHeaders(app)
await seedStatsFixture(db)
const res = await app.request('/api/admin/stats/storage', { headers })
const res = await app.request('/api/site/stats/storage', { headers })
const body = (await res.json()) as { error: { details: Array<{ metadata: Record<string, string> }> } }
expect(res.status).toBe(402)
@@ -31,7 +40,7 @@ describe('admin stats routes', () => {
const { app } = await createTestApp()
const headers = await adminHeaders(app)
const res = await app.request('/api/admin/stats/overview?from=2026-01-01&to=2026-01-07', { headers })
const res = await app.request('/api/site/stats/overview?from=2026-01-01&to=2026-01-07', { headers })
const body = (await res.json()) as { from: string; to: string; trends: Array<{ date: string }> }
expect(res.status).toBe(200)
@@ -52,19 +61,19 @@ describe('admin stats routes', () => {
const { app } = await createTestApp()
const headers = await adminHeaders(app)
const res = await app.request('/api/admin/stats/overview?from=2025-01-01&to=2026-01-02', { headers })
const res = await app.request('/api/site/stats/overview?from=2025-01-01&to=2026-01-02', { headers })
expect(res.status).toBe(400)
})
it('does not publish admin stats routes in the OpenAPI document', async () => {
it('does not publish site stats routes in the OpenAPI document', async () => {
const { app } = await createTestApp()
const res = await app.request('/api/openapi.json')
const body = (await res.json()) as { paths: Record<string, unknown> }
expect(res.status).toBe(200)
expect(Object.keys(body.paths).some((path) => path.startsWith('/api/admin/stats'))).toBe(false)
expect(Object.keys(body.paths).some((path) => path.startsWith('/api/site/stats'))).toBe(false)
})
it('reads storage waterline trends from daily rollups when present', async () => {
@@ -84,7 +93,7 @@ describe('admin stats routes', () => {
)
`)
const res = await app.request('/api/admin/stats/storage?from=2026-01-01&to=2026-01-01', { headers })
const res = await app.request('/api/site/stats/storage?from=2026-01-01&to=2026-01-01', { headers })
const body = (await res.json()) as {
storageTrend: Array<{ date: string; usedBytes: number; newBytes: number; newFiles: number }>
}
@@ -100,7 +109,7 @@ describe('admin stats routes', () => {
await seedStatsFixture(db)
const before = await db.all<{ count: number }>(sql`SELECT COUNT(*) AS count FROM stats_rollups_daily`)
const res = await app.request('/api/admin/stats/storage?from=2000-01-01&to=2000-01-02', { headers })
const res = await app.request('/api/site/stats/storage?from=2000-01-01&to=2000-01-02', { headers })
const after = await db.all<{ count: number }>(sql`SELECT COUNT(*) AS count FROM stats_rollups_daily`)
expect(res.status).toBe(200)
@@ -114,7 +123,7 @@ describe('admin stats routes', () => {
await seedProLicense(db)
await seedStatsFixture(db)
const res = await app.request('/api/admin/stats/traffic', { headers })
const res = await app.request('/api/site/stats/traffic', { headers })
const body = (await res.json()) as {
summary: {
totalBytes: { value: number }
@@ -151,7 +160,7 @@ describe('admin stats routes', () => {
await seedProLicense(db)
await seedStatsFixture(db)
const currentSharingRes = await app.request('/api/admin/stats/sharing', { headers })
const currentSharingRes = await app.request('/api/site/stats/sharing', { headers })
const currentSharing = (await currentSharingRes.json()) as {
summary: { views: { value: number }; downloads: { value: number } }
topShares: Array<{
@@ -162,18 +171,18 @@ describe('admin stats routes', () => {
downloadPercent: number
}>
}
const oldSharingRes = await app.request('/api/admin/stats/sharing?from=2000-01-01&to=2000-01-02', { headers })
const oldSharingRes = await app.request('/api/site/stats/sharing?from=2000-01-01&to=2000-01-02', { headers })
const oldSharing = (await oldSharingRes.json()) as {
summary: { views: { value: number }; downloads: { value: number } }
topShares: unknown[]
}
const currentRankingRes = await app.request('/api/admin/stats/ranking', { headers })
const currentRankingRes = await app.request('/api/site/stats/ranking', { headers })
const currentRanking = (await currentRankingRes.json()) as {
topSpaces: unknown[]
storageByType: unknown[]
topShares: unknown[]
}
const oldRankingRes = await app.request('/api/admin/stats/ranking?from=2000-01-01&to=2000-01-02', { headers })
const oldRankingRes = await app.request('/api/site/stats/ranking?from=2000-01-01&to=2000-01-02', { headers })
const oldRanking = (await oldRankingRes.json()) as {
topSpaces: unknown[]
storageByType: unknown[]
@@ -221,7 +230,7 @@ describe('admin stats routes', () => {
('activity-download-heavy-3', ${orgId}, NULL, 'anonymous', 'share_download', 'share', 'share-download-heavy', 'report.pdf', '{"bytes":512,"source":"landing_share","anonymous":true}', ${nowSec})
`)
const res = await app.request('/api/admin/stats/ranking', { headers })
const res = await app.request('/api/site/stats/ranking', { headers })
const body = (await res.json()) as { topShares: Array<{ token: string; views: number; downloads: number }> }
expect(res.status).toBe(200)
+7 -7
View File
@@ -3815,7 +3815,7 @@ describe('api', () => {
})
})
describe('admin stats API', () => {
describe('site stats API', () => {
const dashboardPayload = {
generatedAt: '2026-07-09T00:00:00.000Z',
from: '2026-07-01T00:00:00.000Z',
@@ -3823,12 +3823,12 @@ describe('api', () => {
}
const range = { from: '2026-07-01T00:00:00.000Z', to: '2026-07-09T00:00:00.000Z' }
const dashboardEndpoints = [
{ name: 'getAdminDashboardOverviewStats', path: '/api/admin/stats/overview', fn: getAdminDashboardOverviewStats },
{ name: 'getAdminDashboardGrowthStats', path: '/api/admin/stats/growth', fn: getAdminDashboardGrowthStats },
{ name: 'getAdminDashboardStorageStats', path: '/api/admin/stats/storage', fn: getAdminDashboardStorageStats },
{ name: 'getAdminDashboardTrafficStats', path: '/api/admin/stats/traffic', fn: getAdminDashboardTrafficStats },
{ name: 'getAdminDashboardSharingStats', path: '/api/admin/stats/sharing', fn: getAdminDashboardSharingStats },
{ name: 'getAdminDashboardRankingStats', path: '/api/admin/stats/ranking', fn: getAdminDashboardRankingStats },
{ name: 'getAdminDashboardOverviewStats', path: '/api/site/stats/overview', fn: getAdminDashboardOverviewStats },
{ name: 'getAdminDashboardGrowthStats', path: '/api/site/stats/growth', fn: getAdminDashboardGrowthStats },
{ name: 'getAdminDashboardStorageStats', path: '/api/site/stats/storage', fn: getAdminDashboardStorageStats },
{ name: 'getAdminDashboardTrafficStats', path: '/api/site/stats/traffic', fn: getAdminDashboardTrafficStats },
{ name: 'getAdminDashboardSharingStats', path: '/api/site/stats/sharing', fn: getAdminDashboardSharingStats },
{ name: 'getAdminDashboardRankingStats', path: '/api/site/stats/ranking', fn: getAdminDashboardRankingStats },
] as const
for (const endpoint of dashboardEndpoints) {
+7 -7
View File
@@ -68,7 +68,6 @@ import {
adminDownloadersApi,
adminQuotas,
adminSiteInvitations,
adminStatsApi,
adminTeams,
announcementsApi,
authedSharesApi,
@@ -90,6 +89,7 @@ import {
publicBrandingApi,
publicSharesApi,
publicSiteInvitations,
siteStatsApi,
storages,
system,
teamsApi,
@@ -447,27 +447,27 @@ function statsRangeQuery(filter: AdminStatsRangeFilter = {}): Record<string, str
}
export function getAdminDashboardOverviewStats(filter: AdminStatsRangeFilter = {}) {
return unwrap<AdminDashboardOverviewStats>(adminStatsApi.overview.$get({ query: statsRangeQuery(filter) }))
return unwrap<AdminDashboardOverviewStats>(siteStatsApi.overview.$get({ query: statsRangeQuery(filter) }))
}
export function getAdminDashboardGrowthStats(filter: AdminStatsRangeFilter = {}) {
return unwrap<AdminDashboardGrowthStats>(adminStatsApi.growth.$get({ query: statsRangeQuery(filter) }))
return unwrap<AdminDashboardGrowthStats>(siteStatsApi.growth.$get({ query: statsRangeQuery(filter) }))
}
export function getAdminDashboardStorageStats(filter: AdminStatsRangeFilter = {}) {
return unwrap<AdminDashboardStorageStats>(adminStatsApi.storage.$get({ query: statsRangeQuery(filter) }))
return unwrap<AdminDashboardStorageStats>(siteStatsApi.storage.$get({ query: statsRangeQuery(filter) }))
}
export function getAdminDashboardTrafficStats(filter: AdminStatsRangeFilter = {}) {
return unwrap<AdminDashboardTrafficStats>(adminStatsApi.traffic.$get({ query: statsRangeQuery(filter) }))
return unwrap<AdminDashboardTrafficStats>(siteStatsApi.traffic.$get({ query: statsRangeQuery(filter) }))
}
export function getAdminDashboardSharingStats(filter: AdminStatsRangeFilter = {}) {
return unwrap<AdminDashboardSharingStats>(adminStatsApi.sharing.$get({ query: statsRangeQuery(filter) }))
return unwrap<AdminDashboardSharingStats>(siteStatsApi.sharing.$get({ query: statsRangeQuery(filter) }))
}
export function getAdminDashboardRankingStats(filter: AdminStatsRangeFilter = {}) {
return unwrap<AdminDashboardRankingStats>(adminStatsApi.ranking.$get({ query: statsRangeQuery(filter) }))
return unwrap<AdminDashboardRankingStats>(siteStatsApi.ranking.$get({ query: statsRangeQuery(filter) }))
}
// Admin Storages API
+1 -1
View File
@@ -79,5 +79,5 @@ export const licensingAdminApi = hc<LicensingAdminRoute>('/api/site/licensing',
export const publicBrandingApi = hc<PublicBrandingRoute>('/api/site/branding', opts)
export const brandingAdminApi = hc<BrandingAdminRoute>('/api/site/branding', opts)
export const adminAuditApi = hc<AdminAuditRoute>('/api/site/audit-events', opts)
export const adminStatsApi = hc<AdminStatsRoute>('/api/admin/stats', opts)
export const siteStatsApi = hc<AdminStatsRoute>('/api/site/stats', opts)
export const publicSiteInvitations = hc<PublicSiteInvitationsRoute>('/api/site/invitations', opts)