From 427a047984ac4c30f6089091f12530e3b6cc6808 Mon Sep 17 00:00:00 2001 From: 2alf Date: Wed, 24 Dec 2025 21:36:56 +0100 Subject: [PATCH] User-closed request cron job 1. Added `closedBy` field to schema which can be either "user" | "verification" 2. When user closes request => closedBy: "user" 3. When verification closes request => closedBy: "verification" 4. Cron only deletes requests where closedBy === "user" after 1 day Reduces clustering in UI and database. --- convex/crons.ts | 12 ++++++++++++ convex/leaks.ts | 1 + convex/requests.ts | 43 ++++++++++++++++++++++++++++++++++++++++--- convex/schema.ts | 1 + 4 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 convex/crons.ts diff --git a/convex/crons.ts b/convex/crons.ts new file mode 100644 index 0000000..1964000 --- /dev/null +++ b/convex/crons.ts @@ -0,0 +1,12 @@ +import { cronJobs } from "convex/server"; +import { internal } from "./_generated/api"; + +const crons = cronJobs(); + +crons.daily( + "delete closed requests after a day", + { hourUTC: 1, minuteUTC: 0 }, + internal.requests.deleteOldClosedRequests +); + +export default crons; \ No newline at end of file diff --git a/convex/leaks.ts b/convex/leaks.ts index 92bb903..55bcebf 100644 --- a/convex/leaks.ts +++ b/convex/leaks.ts @@ -544,6 +544,7 @@ export const applyConsensusResult = internalMutation({ await ctx.db.patch(args.requestId, { closed: true, + closedBy: "verification", }); const submitter = await ctx.db.get(leak.submittedBy); diff --git a/convex/requests.ts b/convex/requests.ts index 47c581a..e314ddf 100644 --- a/convex/requests.ts +++ b/convex/requests.ts @@ -1,6 +1,6 @@ import { v } from "convex/values"; import { getAuthUserId } from "@convex-dev/auth/server"; -import { query, mutation } from "./_generated/server"; +import { query, mutation, internalMutation } from "./_generated/server"; import { Id } from "./_generated/dataModel"; // url validation helper @@ -8,8 +8,6 @@ function isValidUrl(url: string): boolean { return /^https?:\/\/.+/i.test(url); // only http and https } - - /** * Get all open (non-closed) requests from the database. * Returns requests with submitter names populated. @@ -279,6 +277,7 @@ export const closeRequest = mutation({ // Close the request await ctx.db.patch(args.requestId, { closed: true, + closedBy: "user", }); return { success: true as const }; @@ -454,3 +453,41 @@ export const getRequestsWithVerificationStatus = query({ return requestsWithStatus; }, }); + +/* + Delete closed requests after a day --> internal mutation called by a cron job +*/ + +export const deleteOldClosedRequests = internalMutation({ + args: {}, + returns: v.number(), + handler: async (ctx) => { + + //const daysAgo = Date.now() - 30 * 24 * 60 * 60 * 1000; // 30 days in milliseconds + const daysAgo = Date.now() - 24 * 60 * 60 * 1000; // 1 day in milliseconds + + const oldClosedRequests = await ctx.db + .query("requests") + .withIndex("by_closed", (q) => q.eq("closed", true)) + .collect(); + + let deletedCount = 0; + + for (const request of oldClosedRequests) { + if (request._creationTime < daysAgo && request.closedBy === "user") { + // remove from users array of requests + if (request.submittedBy) { + const user = await ctx.db.get(request.submittedBy); + if (user && user.requests) { + await ctx.db.patch(request.submittedBy, { + requests: user.requests.filter((id) => id !== request._id), + }); + } + } + await ctx.db.delete(request._id); + deletedCount++; + } + } + return deletedCount; + }, +}); diff --git a/convex/schema.ts b/convex/schema.ts index dda21dd..d119a62 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -43,6 +43,7 @@ const requests = defineTable({ ), targetUrl: v.string(), closed: v.boolean(), + closedBy: v.optional(v.union(v.literal("user"), v.literal("verification"))), leaks: v.array(v.id("leaks")), submittedBy: v.id("users"), })