import { redirect } from "next/navigation"; import { calcPagination, parseListParams } from "@/lib/admin-helpers"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; type ListParamsInput = { searchParams: Promise>; defaultPerPage?: number; }; async function requireLogsView() { const { session, permissions } = await getAdminContext(); if (!canAccess(permissions, PERMS.LOGS_VIEW, session.user.rank)) { redirect("/admin"); } } function parsePageParams( rawParams: Record, defaultPerPage: number, ) { const sp = new URLSearchParams(rawParams); const parsed = parseListParams(sp); const perPage = Number(rawParams.perPage) || defaultPerPage; return { search: parsed.search.trim(), page: parsed.page, perPage }; } export type ChatLogListRow = { id: number; username: string; message: string; timestamp: number; roomId: number; }; export async function loadChatLogList({ searchParams, defaultPerPage = 50, }: ListParamsInput) { await requireLogsView(); const rawParams = await searchParams; const { search, page, perPage } = parsePageParams(rawParams, defaultPerPage); const offset = (page - 1) * perPage; const like = `%${search}%`; type RawRow = { roomId: number; message: string; timestamp: number; username: string | null; }; const [rawRows, countRows] = search ? await Promise.all([ prisma.$queryRaw` SELECT c.room_id AS roomId, c.message, c.timestamp, u.username AS username FROM chatlogs_room c LEFT JOIN users u ON u.id = c.user_from_id WHERE c.message LIKE ${like} OR u.username LIKE ${like} ORDER BY c.timestamp DESC LIMIT ${perPage} OFFSET ${offset}`, prisma.$queryRaw<{ count: bigint }[]>` SELECT COUNT(*) AS count FROM chatlogs_room c LEFT JOIN users u ON u.id = c.user_from_id WHERE c.message LIKE ${like} OR u.username LIKE ${like}`, ]) : await Promise.all([ prisma.$queryRaw` SELECT c.room_id AS roomId, c.message, c.timestamp, u.username AS username FROM chatlogs_room c LEFT JOIN users u ON u.id = c.user_from_id ORDER BY c.timestamp DESC LIMIT ${perPage} OFFSET ${offset}`, prisma.$queryRaw<{ count: bigint }[]>` SELECT COUNT(*) AS count FROM chatlogs_room`, ]); const total = Number(countRows[0]?.count ?? 0); const pagination = calcPagination(total, page, perPage); const rows: ChatLogListRow[] = rawRows.map((row, index) => ({ id: pagination.offset + index + 1, username: row.username ?? "-", message: row.message, timestamp: row.timestamp, roomId: row.roomId, })); return { rows, ...pagination }; } export type CommandLogListRow = { id: number; username: string; command: string; params: string; timestamp: number; }; export async function loadCommandLogList({ searchParams, defaultPerPage = 50, }: ListParamsInput) { await requireLogsView(); const rawParams = await searchParams; const { search, page, perPage } = parsePageParams(rawParams, defaultPerPage); const offset = (page - 1) * perPage; const like = `%${search}%`; type RawRow = { command: string; params: string; timestamp: number; username: string | null; }; const [rawRows, countRows] = search ? await Promise.all([ prisma.$queryRaw` SELECT c.command, c.params, c.timestamp, u.username AS username FROM commandlogs c LEFT JOIN users u ON u.id = c.user_id WHERE c.command LIKE ${like} OR c.params LIKE ${like} OR u.username LIKE ${like} ORDER BY c.timestamp DESC LIMIT ${perPage} OFFSET ${offset}`, prisma.$queryRaw<{ count: bigint }[]>` SELECT COUNT(*) AS count FROM commandlogs c LEFT JOIN users u ON u.id = c.user_id WHERE c.command LIKE ${like} OR c.params LIKE ${like} OR u.username LIKE ${like}`, ]) : await Promise.all([ prisma.$queryRaw` SELECT c.command, c.params, c.timestamp, u.username AS username FROM commandlogs c LEFT JOIN users u ON u.id = c.user_id ORDER BY c.timestamp DESC LIMIT ${perPage} OFFSET ${offset}`, prisma.$queryRaw<{ count: bigint }[]>` SELECT COUNT(*) AS count FROM commandlogs`, ]); const total = Number(countRows[0]?.count ?? 0); const pagination = calcPagination(total, page, perPage); const rows: CommandLogListRow[] = rawRows.map((row, index) => ({ id: pagination.offset + index + 1, username: row.username ?? "-", command: row.command, params: row.params, timestamp: row.timestamp, })); return { rows, ...pagination }; } export type TradeLogListRow = { id: number; userOneName: string; userTwoName: string; status: string; timestamp: number; }; export async function loadTradeLogList({ searchParams, defaultPerPage = 50, }: ListParamsInput) { await requireLogsView(); const rawParams = await searchParams; const { search, page, perPage } = parsePageParams(rawParams, defaultPerPage); const offset = (page - 1) * perPage; const like = `%${search}%`; type RawRow = { id: number; timestamp: number; userOneName: string | null; userTwoName: string | null; }; const [rawRows, countRows] = search ? await Promise.all([ prisma.$queryRaw` SELECT t.id, t.timestamp, u1.username AS userOneName, u2.username AS userTwoName FROM room_trade_log t LEFT JOIN users u1 ON u1.id = t.user_one_id LEFT JOIN users u2 ON u2.id = t.user_two_id WHERE u1.username LIKE ${like} OR u2.username LIKE ${like} ORDER BY t.timestamp DESC LIMIT ${perPage} OFFSET ${offset}`, prisma.$queryRaw<{ count: bigint }[]>` SELECT COUNT(*) AS count FROM room_trade_log t LEFT JOIN users u1 ON u1.id = t.user_one_id LEFT JOIN users u2 ON u2.id = t.user_two_id WHERE u1.username LIKE ${like} OR u2.username LIKE ${like}`, ]) : await Promise.all([ prisma.$queryRaw` SELECT t.id, t.timestamp, u1.username AS userOneName, u2.username AS userTwoName FROM room_trade_log t LEFT JOIN users u1 ON u1.id = t.user_one_id LEFT JOIN users u2 ON u2.id = t.user_two_id ORDER BY t.timestamp DESC LIMIT ${perPage} OFFSET ${offset}`, prisma.$queryRaw<{ count: bigint }[]>` SELECT COUNT(*) AS count FROM room_trade_log`, ]); const total = Number(countRows[0]?.count ?? 0); const pagination = calcPagination(total, page, perPage); const rows: TradeLogListRow[] = rawRows.map((row) => ({ id: row.id, userOneName: row.userOneName ?? "-", userTwoName: row.userTwoName ?? "-", // room_trade_log only stores completed trades; no status column exists status: "completed", timestamp: row.timestamp, })); return { rows, ...pagination }; }