Fix admin chat/command logs crashing on missing Prisma models.
chatlogs_room and commandlogs are @@ignore (no client delegates); load them via raw SQL with user joins instead of fetchAdminList(chatLog/commandLog). Co-authored-by: Cursor <[email protected]>
This commit is contained in:
1 parent
ef0d673cfe
commit
afeabdad1b
3 files changed
+164
-42
No files matched your search
@@ -0,0 +1,160 @@
|
|||||||
|
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<Record<string, string>>;
|
||||||
|
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<string, string>,
|
||||||
|
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<RawRow[]>`
|
||||||
|
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<RawRow[]>`
|
||||||
|
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<RawRow[]>`
|
||||||
|
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<RawRow[]>`
|
||||||
|
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 };
|
||||||
|
}
|
||||||
@@ -1,32 +1,13 @@
|
|||||||
import { fetchAdminList } from "@/lib/admin-list";
|
import { loadChatLogList } from "../_lib/load-ignored-logs";
|
||||||
import { PERMS } from "@/lib/permissions";
|
|
||||||
import { ChatTable } from "./chat-table";
|
import { ChatTable } from "./chat-table";
|
||||||
|
|
||||||
export default async function ChatLogsPage({
|
export default async function ChatLogsPage({
|
||||||
params: paramsPromise,
|
|
||||||
searchParams,
|
searchParams,
|
||||||
}: {
|
}: {
|
||||||
params: Promise<{ locale: string }>;
|
params: Promise<{ locale: string }>;
|
||||||
searchParams: Promise<Record<string, string>>;
|
searchParams: Promise<Record<string, string>>;
|
||||||
}) {
|
}) {
|
||||||
const result = await fetchAdminList(
|
const result = await loadChatLogList({ searchParams, defaultPerPage: 50 });
|
||||||
{
|
|
||||||
permission: PERMS.LOGS_VIEW,
|
|
||||||
model: "chatLog",
|
|
||||||
searchFields: ["message", "user.username"],
|
|
||||||
defaultPerPage: 50,
|
|
||||||
include: { user: { select: { username: true } } },
|
|
||||||
mapRow: (l) => ({
|
|
||||||
id: l.id,
|
|
||||||
username: l.user.username,
|
|
||||||
message: l.message,
|
|
||||||
timestamp: l.timestamp,
|
|
||||||
roomId: l.roomId,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
paramsPromise,
|
|
||||||
searchParams,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|||||||
@@ -1,32 +1,13 @@
|
|||||||
import { fetchAdminList } from "@/lib/admin-list";
|
import { loadCommandLogList } from "../_lib/load-ignored-logs";
|
||||||
import { PERMS } from "@/lib/permissions";
|
|
||||||
import { CommandsTable } from "./commands-table";
|
import { CommandsTable } from "./commands-table";
|
||||||
|
|
||||||
export default async function CommandLogsPage({
|
export default async function CommandLogsPage({
|
||||||
params: paramsPromise,
|
|
||||||
searchParams,
|
searchParams,
|
||||||
}: {
|
}: {
|
||||||
params: Promise<{ locale: string }>;
|
params: Promise<{ locale: string }>;
|
||||||
searchParams: Promise<Record<string, string>>;
|
searchParams: Promise<Record<string, string>>;
|
||||||
}) {
|
}) {
|
||||||
const result = await fetchAdminList(
|
const result = await loadCommandLogList({ searchParams, defaultPerPage: 50 });
|
||||||
{
|
|
||||||
permission: PERMS.LOGS_VIEW,
|
|
||||||
model: "commandLog",
|
|
||||||
searchFields: ["command", "params", "user.username"],
|
|
||||||
defaultPerPage: 50,
|
|
||||||
include: { user: { select: { username: true } } },
|
|
||||||
mapRow: (l) => ({
|
|
||||||
id: l.id,
|
|
||||||
username: l.user.username,
|
|
||||||
command: l.command,
|
|
||||||
params: l.params,
|
|
||||||
timestamp: l.timestamp,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
paramsPromise,
|
|
||||||
searchParams,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|||||||
Reference in new issue
Block a user