dreame-shopify-app/app/services/logger.server.ts

67 lines
1.7 KiB
TypeScript
Raw Permalink Normal View History

2026-06-19 00:53:13 +08:00
import db from "../db.server";
import type { LogEntry } from "./activity.server";
const SENSITIVE_KEYS = new Set(["api_secret", "app_secret", "apiSecret", "dreame-api-sign", "sign", "signature"]);
export async function logActivityPush(entry: LogEntry): Promise<void> {
await db.activityPushLog.create({
data: {
shop: entry.shop,
orderId: entry.orderId || "unknown",
customerId: entry.customerId,
email: entry.email,
activityId: entry.activityId || "unknown",
eventType: entry.eventType,
eventId: entry.eventId || "not-generated",
status: entry.status,
requestBody: sanitizeBody(entry.requestBody),
responseBody: sanitizeBody(entry.responseBody),
errorMessage: entry.errorMessage,
},
});
if (Math.random() < 0.05) {
await pruneLogs();
}
}
export async function pruneLogs(days = Number(process.env.LOG_RETENTION_DAYS || 7)): Promise<void> {
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
await db.activityPushLog.deleteMany({
where: {
createdAt: {
lt: cutoff,
},
},
});
}
export function sanitizeBody(body: string | undefined): string | undefined {
if (!body) {
return undefined;
}
try {
return JSON.stringify(maskSensitive(JSON.parse(body)));
} catch {
return body.replace(/[a-f0-9]{32}/gi, "[redacted-md5]");
}
}
function maskSensitive(value: unknown): unknown {
if (Array.isArray(value)) {
return value.map(maskSensitive);
}
if (value && typeof value === "object") {
return Object.fromEntries(
Object.entries(value as Record<string, unknown>).map(([key, child]) => [
key,
SENSITIVE_KEYS.has(key) ? "[redacted]" : maskSensitive(child),
]),
);
}
return value;
}