feat(portal): refine account identity controls
CI / validate (push) Successful in 4m51s
Release / release (push) Successful in 6m36s

This commit is contained in:
dmg
2026-08-01 18:06:30 -04:00
parent 86c87153b4
commit 19a5d04178
17 changed files with 357 additions and 43 deletions
@@ -3,6 +3,7 @@ import { events, ipObservations, minecraftAccounts, users } from "@minecraft-acc
import { and, desc, eq, isNull, or } from "drizzle-orm";
import Link from "next/link";
import { notFound } from "next/navigation";
import { groupAccessAddresses } from "@/lib/access-address-groups";
import { db } from "@/lib/database";
import { eventIpSummary } from "@/lib/event-ip-summary";
import {
@@ -60,8 +61,11 @@ export default async function AdminUserPage({
.from(ipObservations)
.where(eq(ipObservations.userId, user.id))
.orderBy(desc(ipObservations.observedAt))
.limit(20),
.limit(100),
]);
const addressGroups = groupAccessAddresses(
observations.map((observation) => ({ ...observation, intelligence: null })),
);
const primary = accounts.find((account) => account.isPrimary);
const nickname = user.firstName
? formatManagedDiscordNickname(user.firstName, primary?.username ?? null)
@@ -162,9 +166,19 @@ export default async function AdminUserPage({
<section className="border border-line bg-panel p-6">
<p className="font-mono text-[9px] font-bold uppercase tracking-widest text-muted">Recent addresses</p>
<p className="mt-3 text-[10px] leading-5 text-muted">Grouped by IPv4 /24 or IPv6 /64 network across the 100 most recent observations.</p>
<div className="mt-4 divide-y divide-line">
{observations.map((observation) => <div className="py-3" key={observation.id}><p className="font-mono text-xs">{observation.ipAddress}</p><p className="mt-1 font-mono text-[9px] text-muted">{observation.source} · {observation.observedAt.toISOString()}</p></div>)}
{!observations.length && <p className="py-3 text-xs text-muted">No addresses recorded.</p>}
{addressGroups.map((group) => (
<div className="py-3" key={group.network}>
<div className="flex items-center justify-between gap-3">
<p className="font-mono text-xs font-bold">{group.network}</p>
<span className="font-mono text-[9px] text-muted">×{group.count}</span>
</div>
<p className="mt-1 font-mono text-[9px] text-muted">{group.sources.join(" + ")} · {group.latestObservedAt.toISOString()}</p>
<p className="mt-1 break-all font-mono text-[9px] text-muted">Latest {group.latestAddress}</p>
</div>
))}
{!addressGroups.length && <p className="py-3 text-xs text-muted">No addresses recorded.</p>}
</div>
</section>
</aside>