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.
This commit is contained in:
2alf
2025-12-24 21:36:56 +01:00
parent 1ee81a4e05
commit 427a047984
4 changed files with 54 additions and 3 deletions
+12
View File
@@ -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;
+1
View File
@@ -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);
+40 -3
View File
@@ -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;
},
});
+1
View File
@@ -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"),
})