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:
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user