Private
Public Access
1
0

Add 17 features: My Work view, card copy/numbering, calendar, bulk ops, custom fields, automations, webhooks, invites, import/export, and more

Features implemented across 6 batches:
- Batch A: Fix ColorPicker gradient sync, tenant custom logo, unarchive cards, board backgrounds
- Batch B: My Work view, card copy/duplicate, card numbering (BOARD-123)
- Batch C: Due date reminders (cron), card watching with notifications
- Batch D: Bulk card operations, column sorting, calendar view
- Batch E: Invite by link, board import/export (Pounce + Trello JSON)
- Batch F: Webhooks with HMAC signing, custom fields, automation rules engine

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Catherine Renelle
2026-02-27 08:50:15 -05:00
parent 3a11ed01e9
commit 8c41b33313
54 changed files with 3656 additions and 82 deletions
+53
View File
@@ -3,6 +3,7 @@ import { createServer } from 'http';
import { Server } from 'socket.io';
import { PrismaClient } from '@prisma/client';
import pino from 'pino';
import cron from 'node-cron';
import { handler } from './build/handler.js';
const logger = pino({ name: 'pounce' });
@@ -148,6 +149,58 @@ io.on('connection', (socket) => {
// Store io globally so SvelteKit API routes can broadcast via realtime.ts
globalThis.__socketIO = io;
// ─── Due Date Reminder Cron (daily at 8:00 UTC) ────────────
cron.schedule('0 8 * * *', async () => {
try {
const now = new Date();
const in24h = new Date(now.getTime() + 24 * 60 * 60 * 1000);
const cards = await prisma.card.findMany({
where: {
archived: false,
dueReminderSent: false,
dueDate: { gte: now, lte: in24h }
},
include: {
assignees: { select: { userId: true } },
column: { select: { board: { select: { id: true, title: true } } } }
}
});
for (const card of cards) {
const boardId = card.column.board.id;
const boardTitle = card.column.board.title;
for (const assignee of card.assignees) {
await prisma.notification.create({
data: {
userId: assignee.userId,
type: 'due_soon',
title: `"${card.title}" is due soon`,
body: `Card in ${boardTitle} is due within 24 hours`,
link: `/boards/${boardId}?card=${card.id}`
}
});
io.to('user:' + assignee.userId).emit('notification:new', {
type: 'due_soon',
title: `"${card.title}" is due soon`
});
}
await prisma.card.update({
where: { id: card.id },
data: { dueReminderSent: true }
});
}
if (cards.length > 0) {
logger.info({ count: cards.length }, 'Due date reminders sent');
}
} catch (err) {
logger.error({ err }, 'Due date reminder cron failed');
}
});
// SvelteKit handler
app.use(handler);