initial project setup

Fastify + Prisma backend, React + Vite frontend, Docker deployment.
Multi-board feedback platform with anonymous cookie auth, passkey
upgrade path, ALTCHA spam protection, plugin system, and full
privacy-first architecture.
This commit is contained in:
2026-03-19 18:05:16 +02:00
commit f07eddf29e
77 changed files with 7031 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
import cron from "node-cron";
import { PrismaClient } from "@prisma/client";
import { config } from "../config.js";
import { cleanExpiredChallenges } from "../routes/passkey.js";
const prisma = new PrismaClient();
export function startCronJobs() {
// prune old activity events - daily at 3am
cron.schedule("0 3 * * *", async () => {
const cutoff = new Date();
cutoff.setDate(cutoff.getDate() - config.DATA_RETENTION_ACTIVITY_DAYS);
const result = await prisma.activityEvent.deleteMany({
where: { createdAt: { lt: cutoff } },
});
if (result.count > 0) {
console.log(`Pruned ${result.count} old activity events`);
}
});
// prune orphaned anonymous users - daily at 4am
cron.schedule("0 4 * * *", async () => {
const cutoff = new Date();
cutoff.setDate(cutoff.getDate() - config.DATA_RETENTION_ORPHAN_USER_DAYS);
const result = await prisma.user.deleteMany({
where: {
authMethod: "COOKIE",
createdAt: { lt: cutoff },
posts: { none: {} },
comments: { none: {} },
votes: { none: {} },
},
});
if (result.count > 0) {
console.log(`Pruned ${result.count} orphaned users`);
}
});
// clean webauthn challenges - every 10 minutes
cron.schedule("*/10 * * * *", () => {
cleanExpiredChallenges();
});
// remove failed push subscriptions - daily at 5am
cron.schedule("0 5 * * *", async () => {
// subscriptions with no associated user get cleaned by cascade
// this handles any other stale ones
const cutoff = new Date();
cutoff.setDate(cutoff.getDate() - 30);
const result = await prisma.pushSubscription.deleteMany({
where: { createdAt: { lt: cutoff } },
});
if (result.count > 0) {
console.log(`Cleaned ${result.count} old push subscriptions`);
}
});
}