import { events, ipIntelligence, ipObservations, minecraftAccounts, users } from "@minecraft-account-manager/database"; import { formatManagedDiscordNickname } from "@minecraft-account-manager/minecraft"; import { and, count, countDistinct, desc, eq, gte, inArray, isNotNull, isNull, sql } from "drizzle-orm"; import Link from "next/link"; import { UserWorldMap, type UserMapLocation } from "@/components/user-world-map"; import { db } from "@/lib/database"; import { fillDailySeries, mergeRiskActivity, type DailyCount } from "@/lib/admin-metrics"; import { MAP_LOCATION_CLASSIFICATIONS, parseUserLocation, parseUserNetwork } from "@/lib/user-location-map"; export const dynamic = "force-dynamic"; export default async function AdminDashboardPage() { const now = new Date(); const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1_000); const fourteenDaysAgo = new Date(now.getTime() - 13 * 24 * 60 * 60 * 1_000); fourteenDaysAgo.setUTCHours(0, 0, 0, 0); const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1_000); const [dailyActiveRows, [totals], [monthlyActive], [monthlyAccounts], locationRows, riskyLatestRows, riskySummaryRows, [recentDenials]] = await Promise.all([ db .select({ day: sql`to_char(date_trunc('day', ${ipObservations.observedAt} at time zone 'UTC'), 'YYYY-MM-DD')`, count: countDistinct(ipObservations.userId), }) .from(ipObservations) .where(and(gte(ipObservations.observedAt, fourteenDaysAgo), isNotNull(ipObservations.userId))) .groupBy(sql`date_trunc('day', ${ipObservations.observedAt} at time zone 'UTC')`) .orderBy(sql`date_trunc('day', ${ipObservations.observedAt} at time zone 'UTC')`), db.select({ users: count(users.id) }).from(users), db.select({ users: countDistinct(ipObservations.userId), }).from(ipObservations).where(and( gte(ipObservations.observedAt, thirtyDaysAgo), isNotNull(ipObservations.userId), )), db.select({ accounts: countDistinct(events.subject) }).from(events).where(and( eq(events.type, "games.minecraft.account-manager.game.player.connected"), gte(events.time, thirtyDaysAgo), )), db .selectDistinctOn([ipObservations.userId], { userId: ipObservations.userId, name: users.firstName, discordUsername: users.discordUsername, primaryUsername: minecraftAccounts.username, classification: ipIntelligence.classification, source: ipObservations.source, observedAt: ipObservations.observedAt, intelligence: ipIntelligence.rawResponse, }) .from(ipObservations) .innerJoin(users, eq(users.id, ipObservations.userId)) .innerJoin(ipIntelligence, eq(ipIntelligence.ipAddress, ipObservations.ipAddress)) .leftJoin(minecraftAccounts, and( eq(minecraftAccounts.userId, users.id), eq(minecraftAccounts.isPrimary, true), isNull(minecraftAccounts.deletedAt), )) .where(and( isNotNull(ipObservations.userId), inArray(ipIntelligence.classification, MAP_LOCATION_CLASSIFICATIONS), sql`case when jsonb_typeof(${ipIntelligence.rawResponse}->'location'->'latitude') = 'number' then (${ipIntelligence.rawResponse}->'location'->>'latitude')::double precision between -90 and 90 else false end`, sql`case when jsonb_typeof(${ipIntelligence.rawResponse}->'location'->'longitude') = 'number' then (${ipIntelligence.rawResponse}->'location'->>'longitude')::double precision between -180 and 180 else false end`, )) .orderBy(ipObservations.userId, desc(ipObservations.observedAt), desc(ipObservations.id)), db .selectDistinctOn([ipObservations.userId], { id: ipObservations.id, classification: ipIntelligence.classification, observedAt: ipObservations.observedAt, source: ipObservations.source, userId: users.id, firstName: users.firstName, discordUsername: users.discordUsername, accountUsername: minecraftAccounts.username, }) .from(ipObservations) .innerJoin(users, eq(users.id, ipObservations.userId)) .leftJoin(minecraftAccounts, eq(minecraftAccounts.id, ipObservations.minecraftAccountId)) .innerJoin(ipIntelligence, eq(ipIntelligence.ipAddress, ipObservations.ipAddress)) .where(and( isNotNull(ipObservations.userId), gte(ipObservations.observedAt, thirtyDaysAgo), inArray(ipIntelligence.classification, ["vpn", "proxy", "tor"]), )) .orderBy(ipObservations.userId, desc(ipObservations.observedAt), desc(ipObservations.id)), db .select({ userId: ipObservations.userId, count: count(), classifications: sql`array_agg(distinct ${ipIntelligence.classification}::text order by ${ipIntelligence.classification}::text)`, sources: sql`array_agg(distinct ${ipObservations.source}::text order by ${ipObservations.source}::text)`, }) .from(ipObservations) .innerJoin(ipIntelligence, eq(ipIntelligence.ipAddress, ipObservations.ipAddress)) .where(and( isNotNull(ipObservations.userId), gte(ipObservations.observedAt, thirtyDaysAgo), inArray(ipIntelligence.classification, ["vpn", "proxy", "tor"]), )) .groupBy(ipObservations.userId), db.select({ count: count() }).from(events).where(and( eq(events.type, "games.minecraft.account-manager.game.login.denied"), gte(events.time, oneDayAgo), )), ]); const dailyActive = fillDailySeries(dailyActiveRows as DailyCount[], now, 14); const riskyActivity = mergeRiskActivity(riskyLatestRows, riskySummaryRows).slice(0, 10); const locations = locationRows.flatMap((row): UserMapLocation[] => { const parsed = parseUserLocation(row.intelligence); if (!parsed || !row.userId) return []; const network = parseUserNetwork(row.intelligence); return [{ userId: row.userId, name: row.name ?? row.discordUsername, discordUsername: row.discordUsername, nickname: formatManagedDiscordNickname(row.name ?? row.discordUsername, row.primaryUsername ?? null), latitude: parsed.latitude, longitude: parsed.longitude, location: parsed.label, classification: row.classification, networkProvider: network.provider, networkAsn: network.asn, connectionType: network.connectionType, proxy: network.proxy, source: row.source, observedAt: row.observedAt, }]; }); return (

Operations overview

Dashboard

Live, server-rendered registration, activity, and network-risk signals from the account registry.

Network review

Recent risky network activity

Collapsed per user across VPN, proxy, and Tor observations from the past 30 days.

All security events
{riskyActivity.map((activity) => (
{activity.userId ? {activity.firstName ?? activity.discordUsername ?? "Unknown user"} : Unknown user}

{activity.accountUsername ?? "No Minecraft account"} · {activity.sources.join(" + ")} · {activity.count} {activity.count === 1 ? "observation" : "observations"}

{activity.classifications.join(" + ")}
))} {!riskyActivity.length &&

No recent VPN, proxy, or Tor observations.

}
); } function Metric({ label, value, detail, accent = false }: { label: string; value: number; detail: string; accent?: boolean }) { return (

{label}

{value}

{detail}

); } function DailyActiveChart({ data }: { data: DailyCount[] }) { const width = 720; const height = 260; const padding = 32; const maximum = Math.max(1, ...data.map((entry) => entry.count)); const points = data.map((entry, index) => { const x = padding + index * ((width - padding * 2) / Math.max(1, data.length - 1)); const y = height - padding - (entry.count / maximum) * (height - padding * 2); return `${x},${y}`; }).join(" "); return (

Activity signal

Daily active users

Daily active users over the last 14 days Distinct daily users range from zero to {maximum}. Date-labelled values follow the chart. {data.map((entry, index) => { const [x, y] = points.split(" ")[index]!.split(","); return {entry.day}: {entry.count} active users; })}
{data.map((entry) =>
{entry.count}
)}
); }