mirror of
https://github.com/saltbo/zpan.git
synced 2026-08-28 15:51:29 +08:00
fix(admin): mount stats under site api group
This commit is contained in:
+1
-1
@@ -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' }))
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user