Implement tasks 60, 61, 62: backups, rate limiting, structured logging
Task 60 — Postgres backups: - deploy/backups/: backup.sh (pg_dump -Fc nightly), retain.sh (14/8/6 tiers), restore.sh, entrypoint.sh, crontab - famapp-backup Alpine service + backups volume added to deploy/compose.yaml - Restore procedure in deploy/backups/README.md Task 61 — Rate limiting on share links: - src/lib/rate-limit.ts: Edge-compatible sliding-window counter (50/min, LRU eviction) with consume(), isRateLimited(), recordFailure() exports - middleware.ts: enforces 429 with Retry-After: 60 for /s/[token] (IP + token prefix) - /s/[token]/page.tsx: tracks only failed resolveShareToken calls via recordFailure() Task 62 — Structured logging: - pino + pino-pretty installed; serverExternalPackages added to next.config.ts - src/lib/logger.ts: JSON in production, pretty in dev, level from LOG_LEVEL env - middleware.ts: structured JSON request log (method, path, status, ms, authenticated) - _core/push.ts, notify.ts, reminders.ts: console.error/log → logger.error/info Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
b6abe052c9
commit
285a460eb8
@@ -1,5 +1,6 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "@/lib/db";
|
||||
import logger from "@/lib/logger";
|
||||
import { notifications, users } from "./schema";
|
||||
import { sendPush } from "./push";
|
||||
|
||||
@@ -25,7 +26,7 @@ export async function notify(userId: string, payload: NotifyPayload) {
|
||||
|
||||
if (channels.includes("push") && user.notifPush && pushEnabled) {
|
||||
await sendPush(userId, payload).catch((err) =>
|
||||
console.error("[famapp] push channel failed:", err),
|
||||
logger.error({ err }, "push channel failed"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,7 +47,7 @@ export async function notify(userId: string, payload: NotifyPayload) {
|
||||
method: "POST",
|
||||
headers: { Title: payload.title, "Content-Type": "text/plain" },
|
||||
body: payload.body,
|
||||
}).catch((err) => console.error("[famapp] ntfy delivery failed:", err));
|
||||
}).catch((err) => logger.error({ err }, "ntfy delivery failed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import webPush from "web-push";
|
||||
import { and, eq, inArray } from "drizzle-orm";
|
||||
import { db } from "@/lib/db";
|
||||
import logger from "@/lib/logger";
|
||||
import { pushSubscriptions } from "./schema";
|
||||
|
||||
function ensureVapidConfigured() {
|
||||
@@ -40,7 +41,7 @@ export async function sendPush(
|
||||
if (status === 404 || status === 410) {
|
||||
staleIds.push(sub.id);
|
||||
} else {
|
||||
console.error("[famapp] push delivery failed:", err);
|
||||
logger.error({ err }, "push delivery failed");
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { and, eq, inArray, isNull, lte, sql } from "drizzle-orm";
|
||||
import { db } from "@/lib/db";
|
||||
import logger from "@/lib/logger";
|
||||
import { reminders } from "./schema";
|
||||
import { notify } from "./notify";
|
||||
|
||||
@@ -67,7 +68,7 @@ export async function tickReminders() {
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("[famapp] reminder tick error:", err);
|
||||
logger.error({ err }, "reminder tick error");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -82,7 +83,7 @@ export async function tickReminders() {
|
||||
channels: ["push", "inapp"],
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("[famapp] reminder delivery failed:", reminder.id, err);
|
||||
logger.error({ reminderId: reminder.id, err }, "reminder delivery failed");
|
||||
}
|
||||
}),
|
||||
);
|
||||
@@ -93,7 +94,7 @@ let workerTimer: ReturnType<typeof setInterval> | null = null;
|
||||
export function startReminderWorker() {
|
||||
if (workerTimer) return;
|
||||
workerTimer = setInterval(() => {
|
||||
tickReminders().catch((err) => console.error("[famapp] reminder worker uncaught:", err));
|
||||
tickReminders().catch((err) => logger.error({ err }, "reminder worker uncaught error"));
|
||||
}, 30_000);
|
||||
console.log("[famapp] reminder worker started (30s tick)");
|
||||
logger.info("reminder worker started (30s tick)");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user