api-v1/jobs/expirationJob.js

72 lines
2.6 KiB
JavaScript

import schedule from "node-cron";
import pool from "../db/pool.js";
import { getIO, getSocketId } from "../socket.js";
// Run every hour
export const startExpirationJob = () => {
schedule.schedule("0 * * * *", async () => {
console.log("Running listing expiration check...");
const client = await pool.connect();
try {
await client.query("BEGIN");
// 1. Identify expired listings (active & not updated in last 48h)
// Using INTERVAL '48 hours'
const findExpiredQuery = `
SELECT id, title, seller_id
FROM listings
WHERE status = 'active'
AND updated_at < NOW() - INTERVAL '48 hours'
AND deleted = FALSE
FOR UPDATE SKIP LOCKED
`;
const { rows: expiredListings } = await client.query(findExpiredQuery);
if (expiredListings.length === 0) {
await client.query("COMMIT");
return;
}
console.log(`Found ${expiredListings.length} listings to expire.`);
// 2. Update status to 'expired'
const expiredIds = expiredListings.map(l => l.id);
await client.query(`
UPDATE listings
SET status = 'expired'
WHERE id = ANY($1::uuid[])
`, [expiredIds]);
// 3. Create Notifications & Real-time Alerts
for (const listing of expiredListings) {
const message = `Your listing "${listing.title}" has expired after 48 hours of inactivity. Click here to re-list it.`;
// Insert Notification
await client.query(`
INSERT INTO notifications (user_id, type, message, data)
VALUES ($1, 'listing_expired', $2, $3)
`, [listing.seller_id, message, { listing_id: listing.id }]);
// Real-time Socket Emit
const socketId = getSocketId(listing.seller_id);
if (socketId) {
getIO().to(socketId).emit("notification", {
type: "listing_expired",
message,
data: { listing_id: listing.id }
});
}
}
await client.query("COMMIT");
console.log("Expiration check completed successfully.");
} catch (err) {
await client.query("ROLLBACK");
console.error("Error in expiration job:", err);
} finally {
client.release();
}
});
};