expire-share-links.js 1.5 KB

123456789101112131415161718192021222324252627282930313233343536
  1. // Remove expired share links.
  2. //
  3. // imagedrop's share_links rows carry an `expires_at` (ISO-8601 TEXT, written by
  4. // the client as `new Date(...).toISOString()`). The app already refuses to serve
  5. // an expired link, but the rows pile up forever (most of the table is expired).
  6. // A periodic cron sweep deletes the ones whose expiry has passed.
  7. //
  8. // runAs:'service' so the delete bypasses RLS (no per-row owner check needed).
  9. export const on = {
  10. type: 'cron',
  11. schedule: '*/30 * * * *', // every 30 minutes, UTC
  12. runAs: 'service',
  13. };
  14. export default async function (ctx) {
  15. const now = Date.now();
  16. // Same toISOString() format the client writes, so a lexicographic '<' on the
  17. // TEXT column is a chronological compare — the DB does the filtering, no
  18. // full-table scan. Each candidate is re-verified by parsing the date before
  19. // it is deleted (defence in depth for a destructive op).
  20. const nowIso = new Date(now).toISOString();
  21. const rows = await ctx.db.from('share_links').lt('expires_at', nowIso).limit(2000).select();
  22. if (!Array.isArray(rows)) return;
  23. let removed = 0;
  24. for (const row of rows) {
  25. const exp = Date.parse(row.expires_at);
  26. if (!Number.isFinite(exp) || exp >= now) continue; // skip anything not actually expired
  27. // .eq('id', …) targets the row by primary key for the DELETE.
  28. await ctx.db.from('share_links').eq('id', row.id).delete();
  29. removed++;
  30. }
  31. if (removed > 0) console.log('expire-share-links: removed ' + removed + ' expired share link(s)');
  32. }