feat: gamification queue with deduplication and unpublish revoke

- Add migration 0004: partial unique index on user_points (user_id, action, recording_id)
  for RECORDING_CREATE and RECORDING_FEATURED to prevent earn-on-republish farming
- Add revokePoints() to gamification lib; awardPoints() now uses onConflictDoNothing
- Add gamificationQueue (BullMQ) with 3-attempt exponential backoff
- Add gamification worker handling awardPoints, revokePoints, checkAchievements jobs
- Move all inline gamification calls in recordings + comments resolvers to queue
- Revoke RECORDING_CREATE points when a recording is unpublished (published → draft)
- Register gamification worker at server startup alongside mail worker

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 19:50:33 +01:00
parent 1b724e86c9
commit 5f40a812d3
8 changed files with 183 additions and 34 deletions

View File

@@ -17,6 +17,7 @@ import { redis } from "./lib/auth";
import { logger } from "./lib/logger";
import { migrate } from "drizzle-orm/node-postgres/migrator";
import { startMailWorker } from "./queues/workers/mail";
import { startGamificationWorker } from "./queues/workers/gamification";
const PORT = parseInt(process.env.PORT || "4000");
const UPLOAD_DIR = process.env.UPLOAD_DIR || "/data/uploads";
@@ -31,6 +32,7 @@ async function main() {
// Start background workers
startMailWorker();
startGamificationWorker();
logger.info("Queue workers started");
const fastify = Fastify({ loggerInstance: logger });