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>
29 lines
1.0 KiB
TypeScript
29 lines
1.0 KiB
TypeScript
import pino from "pino";
|
|
|
|
// Structured JSON logger for Node.js server code (server components, server actions,
|
|
// background workers). NOT available in the Edge runtime (middleware) — use
|
|
// console.log with JSON.stringify there instead.
|
|
//
|
|
// Secrets are never passed as log fields. Any field named `password`, `secret`,
|
|
// `token`, or `key` is explicitly excluded by callers. Sensitive env vars
|
|
// (AUTH_SECRET, VAPID_PRIVATE_KEY, etc.) must not appear in log payloads.
|
|
//
|
|
// Log level is controlled by the LOG_LEVEL env var (default: info).
|
|
// In development, pino-pretty formats output with colour for readability.
|
|
// In production, single-line JSON goes to stdout and is collected by Docker.
|
|
|
|
const level = process.env.LOG_LEVEL ?? "info";
|
|
|
|
const logger = pino(
|
|
{
|
|
level,
|
|
base: { pid: undefined, hostname: undefined },
|
|
timestamp: pino.stdTimeFunctions.isoTime,
|
|
},
|
|
process.env.NODE_ENV === "production"
|
|
? undefined
|
|
: pino.transport({ target: "pino-pretty", options: { colorize: true } }),
|
|
);
|
|
|
|
export default logger;
|