feat(cms): improve catalog, editorial recovery and operations
This commit is contained in:
1 parent
2fead134e6
commit
c389c3893d
122 files changed
+8137
-703
No files matched your search
@@ -0,0 +1,73 @@
|
||||
import type { CmsErrorRecord } from "./error-monitor";
|
||||
|
||||
export function errorOperationLink(record: CmsErrorRecord): string | null {
|
||||
const context = record.context;
|
||||
if (record.source === "server") {
|
||||
if (context.module === "news") {
|
||||
const id = String(context.articleId ?? "");
|
||||
return /^[1-9]\d{0,18}$/.test(id)
|
||||
? `/admin/articles/${id}`
|
||||
: "/admin/articles";
|
||||
}
|
||||
const routes: Record<string, string> = {
|
||||
catalog: "/admin/catalog",
|
||||
media: "/admin/media",
|
||||
users: "/admin/users",
|
||||
settings: "/admin/settings",
|
||||
studio: "/admin/studio",
|
||||
};
|
||||
if (
|
||||
typeof context.module === "string" &&
|
||||
Object.hasOwn(routes, context.module)
|
||||
)
|
||||
return routes[context.module];
|
||||
}
|
||||
// Never use arbitrary error-provided URLs as navigation targets.
|
||||
const candidate = context.path;
|
||||
if (typeof candidate !== "string") return null;
|
||||
if (
|
||||
/^\/admin\/(articles|users|catalog|media|settings|studio)$/.test(candidate)
|
||||
)
|
||||
return candidate;
|
||||
if (/^\/admin\/articles\/[1-9]\d{0,18}$/.test(candidate)) return candidate;
|
||||
return null;
|
||||
}
|
||||
export function summarizeErrorGroup(events: CmsErrorRecord[]) {
|
||||
const sorted = [...events].sort((a, b) => a.at.localeCompare(b.at));
|
||||
const first = sorted[0];
|
||||
const latest = sorted.at(-1);
|
||||
const users = new Set<string>();
|
||||
let unidentified = 0;
|
||||
const releases = new Map<
|
||||
string,
|
||||
{ release: string; count: number; firstAt: string; lastAt: string }
|
||||
>();
|
||||
for (const event of sorted) {
|
||||
const raw = event.source === "server" ? event.context.userId : null;
|
||||
const id =
|
||||
typeof raw === "number" && Number.isSafeInteger(raw) && raw > 0
|
||||
? String(raw)
|
||||
: typeof raw === "string" && /^[1-9]\d{0,14}$/.test(raw)
|
||||
? raw
|
||||
: null;
|
||||
if (id) users.add(id);
|
||||
else unidentified++;
|
||||
const prior = releases.get(event.release);
|
||||
releases.set(event.release, {
|
||||
release: event.release,
|
||||
count: (prior?.count ?? 0) + 1,
|
||||
firstAt: prior?.firstAt ?? event.at,
|
||||
lastAt: event.at,
|
||||
});
|
||||
}
|
||||
return {
|
||||
count: events.length,
|
||||
firstAt: first?.at ?? null,
|
||||
lastAt: latest?.at ?? null,
|
||||
affectedUsers: users.size,
|
||||
unidentified,
|
||||
releases: [...releases.values()],
|
||||
latest,
|
||||
operationLink: latest ? errorOperationLink(latest) : null,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user