Files
EpicNext-Cms/src/app/admin/devops/errors/page.tsx
T
openhands 17847545dd
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 1m52s
Improvements: remove dead config, fix ESM, add URL validation, unify types, add missing logging
- Remove .prettierrc (dead config, Biome replaces Prettier)
- Rename lighthouserc.json to lighthouserc.cjs with module.exports for ESM compat
- Add logger.warn to empty catch blocks in auth, register, site-settings, prisma-cache, redis, security, rate-limit
- Unify ActionResult type: action-helper.ts uses 'ok' consistent with safe-action-shared.ts
- Add noUnusedLocals + noUnusedParameters to tsconfig + fix 25 pre-existing unused vars
- Replace barrel export src/types/index.ts with direct @/types/common imports
- Make trustHost conditional (development only) in auth.ts
- Add pre-flight URL validation to update-Nitrov3.sh to catch image.library.url misconfigurations
- Improve NITRO_IMAGE_LIBRARY_URL content validation in pre-flight & post-compute checks
2026-07-26 20:28:11 +02:00

91 lines
2.4 KiB
TypeScript

import { redirect } from "next/navigation";
import { getTranslations } from "next-intl/server";
import { Card, CardContent } from "@/components/ui/card";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
export default async function EmulatorErrorsPage({
searchParams,
}: {
searchParams: Promise<Record<string, string>>;
}) {
const { session, permissions } = await getAdminContext();
if (!canAccess(permissions, PERMS.DEVOPS_VIEW, session.user.rank)) {
redirect("/admin");
}
const t = await getTranslations("pages.admin.devops");
const params = await searchParams;
const page = Math.max(1, Number(params.page) || 1);
const perPage = 50;
const [errors, total] = await Promise.all([
prisma.emulatorErrors.findMany({
orderBy: { id: "desc" },
skip: (page - 1) * perPage,
take: perPage,
}),
prisma.emulatorErrors.count(),
]);
const lastPage = Math.ceil(total / perPage);
return (
<div className="space-y-6">
<p className="text-muted-foreground">
{total} total errors — showing page {page} of {lastPage}
</p>
<Card>
<CardContent className="p-0">
{errors.length === 0 ? (
<div className="admin-empty">{t("noEmulatorErrors")}</div>
) : (
<div className="divide-y">
{errors.map((error) => (
<div key={error.id} className="p-4">
<div className="flex items-center justify-between mb-2">
<span className="font-mono text-xs text-muted-foreground">
#{error.id}
</span>
<span className="text-xs text-muted-foreground">
{new Date(error.timestamp * 1000).toLocaleString()}
</span>
</div>
<pre className="text-xs whitespace-pre-wrap break-all font-mono bg-muted p-2 rounded">
{Buffer.from(error.stacktrace).toString("utf-8")}
</pre>
</div>
))}
</div>
)}
</CardContent>
</Card>
{lastPage > 1 && (
<div className="flex justify-center gap-2">
{page > 1 && (
<a
href={`?page=${page - 1}`}
className="text-sm text-primary hover:underline"
>
Previous
</a>
)}
<span className="text-sm text-muted-foreground">
Page {page} of {lastPage}
</span>
{page < lastPage && (
<a
href={`?page=${page + 1}`}
className="text-sm text-primary hover:underline"
>
Next
</a>
)}
</div>
)}
</div>
);
}