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
+49
-21
@@ -1,5 +1,7 @@
|
||||
import { count, desc, inArray, like, or } from "drizzle-orm";
|
||||
import { and, count, desc, eq, gte, inArray, like, lt, or } from "drizzle-orm";
|
||||
import { AdminAuditLog, db, User } from "@/lib/db";
|
||||
import { readAuditChanges } from "./audit-diff";
|
||||
import { type AuditFilters, normalizeAuditFilters } from "./audit-filters";
|
||||
|
||||
interface AuditEntry {
|
||||
userId: number;
|
||||
@@ -68,23 +70,32 @@ export async function logAudit(entry: AuditEntry): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
interface GetLogsOptions {
|
||||
search?: string;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
}
|
||||
|
||||
export async function getAuditLogs(options: GetLogsOptions = {}) {
|
||||
const { search, page = 1, perPage = 20 } = options;
|
||||
export async function getAuditLogs(options: AuditFilters = {}) {
|
||||
const { search, actor, action, from, until, page, perPage } =
|
||||
normalizeAuditFilters(options);
|
||||
const skip = (page - 1) * perPage;
|
||||
|
||||
const where = search
|
||||
? or(
|
||||
like(AdminAuditLog.action, `%${search}%`),
|
||||
like(AdminAuditLog.target, `%${search}%`),
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const where = and(
|
||||
search
|
||||
? or(
|
||||
like(AdminAuditLog.action, `%${search}%`),
|
||||
like(AdminAuditLog.target, `%${search}%`),
|
||||
)
|
||||
: undefined,
|
||||
action ? eq(AdminAuditLog.action, action) : undefined,
|
||||
actor
|
||||
? /^[1-9]\d*$/.test(actor) && Number.isSafeInteger(Number(actor))
|
||||
? eq(AdminAuditLog.userId, Number(actor))
|
||||
: inArray(
|
||||
AdminAuditLog.userId,
|
||||
db
|
||||
.select({ id: User.id })
|
||||
.from(User)
|
||||
.where(eq(User.username, actor)),
|
||||
)
|
||||
: undefined,
|
||||
from ? gte(AdminAuditLog.createdAt, from) : undefined,
|
||||
until ? lt(AdminAuditLog.createdAt, until) : undefined,
|
||||
);
|
||||
const [rows, totalResult] = await Promise.all([
|
||||
db
|
||||
.select({
|
||||
@@ -94,6 +105,8 @@ export async function getAuditLogs(options: GetLogsOptions = {}) {
|
||||
target: AdminAuditLog.target,
|
||||
targetId: AdminAuditLog.targetId,
|
||||
diff: AdminAuditLog.diff,
|
||||
before: AdminAuditLog.before,
|
||||
after: AdminAuditLog.after,
|
||||
createdAt: AdminAuditLog.createdAt,
|
||||
})
|
||||
.from(AdminAuditLog)
|
||||
@@ -116,10 +129,25 @@ export async function getAuditLogs(options: GetLogsOptions = {}) {
|
||||
: [];
|
||||
const userMap = new Map(users.map((u) => [u.id, u.username]));
|
||||
|
||||
const enrichedRows = rows.map((r) => ({
|
||||
...r,
|
||||
username: userMap.get(r.userId) ?? `User #${r.userId}`,
|
||||
}));
|
||||
const enrichedRows = rows.map((r) => {
|
||||
const details = readAuditChanges(r.diff, r.before, r.after);
|
||||
return {
|
||||
...r,
|
||||
// Only sanitized changes may cross the server/client boundary.
|
||||
before: null,
|
||||
after: null,
|
||||
diff: details.invalid
|
||||
? "[Unavailable legacy details]"
|
||||
: details.changes.length
|
||||
? JSON.stringify(
|
||||
Object.fromEntries(
|
||||
details.changes.map(({ key, from, to }) => [key, { from, to }]),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
username: userMap.get(r.userId) ?? `User #${r.userId}`,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
rows: enrichedRows,
|
||||
|
||||
Reference in new issue
Block a user