Add backend log management

This commit is contained in:
inman
2026-06-03 11:49:45 +08:00
parent 13ddc66cfe
commit fb0229ba06
14 changed files with 804 additions and 2 deletions

40
app/api/logs/route.ts Normal file
View File

@@ -0,0 +1,40 @@
import { jsonError, jsonOk } from "@/lib/server/api";
import { appLogFilePath, appLogMaxBytes, clearAppLogs, listAppLogs, type AppLogLevel } from "@/lib/server/log-manager";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
export async function GET(request: Request) {
try {
const url = new URL(request.url);
const level = parseLevel(url.searchParams.get("level"));
const q = url.searchParams.get("q") || undefined;
const limit = Number(url.searchParams.get("limit") || 100);
const entries = await listAppLogs({
level,
q,
limit: Number.isFinite(limit) ? limit : 100
});
return jsonOk({
entries,
logPath: appLogFilePath(),
maxBytes: appLogMaxBytes()
});
} catch (error) {
return jsonError(error, 500, { request, source: "api.logs" });
}
}
export async function DELETE(request: Request) {
try {
await clearAppLogs();
return jsonOk({ ok: true });
} catch (error) {
return jsonError(error, 500, { request, source: "api.logs" });
}
}
function parseLevel(value: string | null): AppLogLevel | "all" {
if (value === "info" || value === "warning" || value === "error") return value;
return "all";
}

View File

@@ -1207,6 +1207,163 @@ h3 {
gap: 8px;
}
.log-manager {
display: grid;
gap: 14px;
}
.log-actions {
display: grid;
grid-template-columns: minmax(240px, 0.8fr) minmax(260px, 1fr) auto;
align-items: center;
gap: 12px;
}
.log-tabs {
width: 100%;
}
.log-search {
min-width: 0;
height: 42px;
display: flex;
align-items: center;
gap: 8px;
border: 1px solid var(--line);
border-radius: 8px;
background: #ffffff;
padding: 0 12px;
}
.log-search svg {
width: 17px;
height: 17px;
color: var(--muted);
flex: 0 0 auto;
}
.log-search input {
min-width: 0;
width: 100%;
border: 0;
outline: 0;
background: transparent;
color: var(--ink);
}
.log-storage {
display: grid;
grid-template-columns: minmax(0, 1.5fr) repeat(2, minmax(150px, 0.6fr));
gap: 10px;
}
.log-storage div {
min-width: 0;
display: grid;
gap: 4px;
padding: 10px 12px;
border: 1px solid var(--line);
border-radius: 8px;
background: #f8faf8;
}
.log-storage span {
color: var(--muted);
font-size: 12px;
font-weight: 700;
}
.log-storage strong {
min-width: 0;
overflow-wrap: anywhere;
font-size: 13px;
}
.log-list {
display: grid;
gap: 10px;
}
.log-row {
min-width: 0;
display: grid;
gap: 10px;
padding: 12px;
border: 1px solid var(--line);
border-radius: 8px;
background: #ffffff;
}
.log-row-error {
border-color: #e6b8ae;
background: #fffaf8;
}
.log-row-warning {
border-color: #ead2a4;
background: #fffaf0;
}
.log-row-main {
min-width: 0;
display: grid;
grid-template-columns: auto minmax(0, 1fr);
align-items: start;
gap: 10px;
}
.log-message {
min-width: 0;
display: grid;
gap: 4px;
}
.log-message strong {
overflow-wrap: anywhere;
font-size: 14px;
}
.log-message small {
min-width: 0;
color: var(--muted);
font-size: 12px;
overflow-wrap: anywhere;
}
.log-detail {
min-width: 0;
}
.log-detail summary {
cursor: pointer;
color: var(--green-dark);
font-size: 12px;
font-weight: 800;
}
.log-detail pre {
max-height: 360px;
overflow: auto;
margin: 8px 0 0;
padding: 10px;
border: 1px solid var(--line);
border-radius: 8px;
background: #f8fbfb;
color: #27312d;
font-size: 12px;
line-height: 1.55;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
.log-empty {
display: grid;
place-items: center;
min-height: 160px;
color: var(--muted);
font-weight: 800;
}
.image-upload-button {
flex: 0 0 auto;
}
@@ -2221,6 +2378,11 @@ button:active:not(:disabled),
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.log-actions,
.log-storage {
grid-template-columns: 1fr;
}
.settings-engine-row {
display: grid;
grid-template-columns: 1fr;
@@ -2278,6 +2440,7 @@ button:active:not(:disabled),
.create-mode-bar,
.asset-manager-toolbar,
.log-actions,
.settings-actions {
padding: 8px;
}
@@ -2450,6 +2613,11 @@ button:active:not(:disabled),
display: grid;
}
.log-tabs {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.asset-view-switch {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@@ -2459,6 +2627,7 @@ button:active:not(:disabled),
}
.asset-view-switch button,
.log-tabs button,
.settings-tabs button {
min-width: 0;
min-height: 38px;

7
app/logs/page.tsx Normal file
View File

@@ -0,0 +1,7 @@
import { LogManager } from "@/components/log-manager";
export const dynamic = "force-dynamic";
export default function LogsPage() {
return <LogManager />;
}