42 lines
1.2 KiB
TypeScript
42 lines
1.2 KiB
TypeScript
"use server";
|
|
|
|
import { requireStaff } from "@/lib/admin/guard";
|
|
import { prisma } from "@/lib/prisma";
|
|
|
|
export interface MultiAccountCluster {
|
|
key: string;
|
|
label: string;
|
|
accountCount: number;
|
|
accounts: Array<{ id: number; username: string; rank: number; online: string }>;
|
|
}
|
|
|
|
export async function detectMultiAccounts({ minAccounts, limit }: { minAccounts: number; limit: number }) {
|
|
await requireStaff();
|
|
const clusters: MultiAccountCluster[] = [];
|
|
|
|
const ipGroups = await prisma.user.groupBy({
|
|
by: ["ipCurrent"],
|
|
where: { ipCurrent: { not: "" } },
|
|
_count: { id: true },
|
|
having: { id: { _count: { gte: minAccounts } } },
|
|
orderBy: { _count: { id: "desc" } },
|
|
take: limit,
|
|
});
|
|
|
|
for (const group of ipGroups) {
|
|
const users = await prisma.user.findMany({
|
|
where: { ipCurrent: group.ipCurrent },
|
|
select: { id: true, username: true, rank: true, online: true },
|
|
orderBy: { id: "asc" },
|
|
});
|
|
clusters.push({
|
|
key: group.ipCurrent,
|
|
label: `IP: ${group.ipCurrent}`,
|
|
accountCount: group._count.id,
|
|
accounts: users.map((u) => ({ id: u.id, username: u.username, rank: u.rank, online: u.online })),
|
|
});
|
|
}
|
|
|
|
return { ok: true as const, data: { clusters } };
|
|
}
|