{unplottedCount} region{unplottedCount === 1 ? '' : 's'} listed
off-map
@@ -373,6 +405,46 @@ function WorldMap({ stats }: { stats: FreebuffLiveStats }) {
)
}
+export function CompactLiveStats({
+ initialStats = EMPTY_LIVE_STATS,
+}: {
+ initialStats?: FreebuffLiveStats
+}) {
+ const stats = useLiveStats(initialStats, { refreshOnMount: true })
+ const isLoading = stats.generatedAt === EMPTY_LIVE_STATS.generatedAt
+
+ return (
+
+
+
+
+
+
+
+
+ Active users
+
+
+
+ {isLoading ? '...' : stats.totalLiveUsers.toLocaleString()}
+
+
+
+
+
+
+
+ )
+}
+
function ModelBars({ stats }: { stats: FreebuffLiveStats }) {
const maxCount = Math.max(1, ...stats.models.map((model) => model.count))
diff --git a/freebuff/web/src/app/page.tsx b/freebuff/web/src/app/page.tsx
index 334631f395..0de8eb7b99 100644
--- a/freebuff/web/src/app/page.tsx
+++ b/freebuff/web/src/app/page.tsx
@@ -8,7 +8,7 @@ import { siteConfig } from '@/lib/constant'
export async function generateMetadata(): Promise
{
const canonicalUrl = env.NEXT_PUBLIC_CODEBUFF_APP_URL
- const title = "Freebuff — the free coding agent"
+ const title = 'Freebuff — the free coding agent'
const description = siteConfig.description
return {
diff --git a/freebuff/web/src/server/live-stats.ts b/freebuff/web/src/server/live-stats.ts
index 359a85ff29..3e41720eeb 100644
--- a/freebuff/web/src/server/live-stats.ts
+++ b/freebuff/web/src/server/live-stats.ts
@@ -21,6 +21,12 @@ export interface FreebuffLiveStats {
generatedAt: string
}
+const LIVE_STATS_CACHE_MS = 60_000
+let cachedLiveStats: {
+ expiresAt: number
+ stats: FreebuffLiveStats
+} | null = null
+
const MODEL_LABELS = Object.fromEntries(
SUPPORTED_FREEBUFF_MODELS.map(
(model) => [model.id, model.displayName] as const,
@@ -48,8 +54,16 @@ function sortCounts(rows: T[]): T[] {
}
export async function getFreebuffLiveStats(
- now = new Date(),
+ now?: Date,
+ options: { cache?: boolean } = {},
): Promise {
+ const useCache = options.cache ?? now === undefined
+ const requestTime = now ?? new Date()
+
+ if (useCache && cachedLiveStats && cachedLiveStats.expiresAt > Date.now()) {
+ return cachedLiveStats.stats
+ }
+
const [countryRows, modelRows] = await Promise.all([
db
.select({
@@ -57,7 +71,7 @@ export async function getFreebuffLiveStats(
count: count(),
})
.from(schema.freeSession)
- .where(liveSessionWhere(now))
+ .where(liveSessionWhere(requestTime))
.groupBy(schema.freeSession.country_code),
db
.select({
@@ -65,7 +79,7 @@ export async function getFreebuffLiveStats(
count: count(),
})
.from(schema.freeSession)
- .where(liveSessionWhere(now))
+ .where(liveSessionWhere(requestTime))
.groupBy(schema.freeSession.model),
])
@@ -84,10 +98,19 @@ export async function getFreebuffLiveStats(
})),
)
- return {
+ const stats = {
totalLiveUsers: models.reduce((sum, row) => sum + row.count, 0),
countries,
models,
- generatedAt: now.toISOString(),
+ generatedAt: requestTime.toISOString(),
}
+
+ if (useCache) {
+ cachedLiveStats = {
+ expiresAt: Date.now() + LIVE_STATS_CACHE_MS,
+ stats,
+ }
+ }
+
+ return stats
}