import { Router, Request, Response } from 'express'; import { asyncHandler } from '../utils/asyncHandler'; import { db } from '../db'; import { users } from '../db/schema'; import { eq } from 'drizzle-orm'; import { AppError } from '../middleware/errorHandler'; import { z } from 'zod'; import { sendPushNotification, isExpoPushToken, createTaskReminderPayload } from '../services/pushNotifications'; const router = Router(); const registerPushTokenSchema = z.object({ pushToken: z.string().min(1), }); router.post('/push-token', asyncHandler(async (req: Request, res: Response) => { if (!req.user) { throw new AppError('UNAUTHORIZED', 'Authentication required', 401); } const data = registerPushTokenSchema.parse(req.body); if (!isExpoPushToken(data.pushToken)) { throw new AppError('INVALID_PUSH_TOKEN', 'Invalid Expo push token format', 400); } await db .update(users) .set({ expoPushToken: data.pushToken, pushTokenUpdatedAt: Date.now(), }) .where(eq(users.id, req.user.userId)); res.json({ success: true }); })); router.delete('/push-token', asyncHandler(async (req: Request, res: Response) => { if (!req.user) { throw new AppError('UNAUTHORIZED', 'Authentication required', 401); } await db .update(users) .set({ expoPushToken: null, pushTokenUpdatedAt: Date.now(), }) .where(eq(users.id, req.user.userId)); res.json({ success: true }); })); router.post('/test', asyncHandler(async (req: Request, res: Response) => { if (!req.user) { throw new AppError('UNAUTHORIZED', 'Authentication required', 401); } const user = await db.select().from(users).where(eq(users.id, req.user.userId)).limit(1); if (user.length === 0 || !user[0].expoPushToken) { throw new AppError('NO_PUSH_TOKEN', 'No push token registered', 400); } const ticket = await sendPushNotification({ to: user[0].expoPushToken!, title: 'Test Notification', body: 'Push notifications are working!', data: { type: 'test' }, sound: 'default', }); res.json({ success: true, ticket }); })); router.post('/send-reminder', asyncHandler(async (req: Request, res: Response) => { if (!req.user) { throw new AppError('UNAUTHORIZED', 'Authentication required', 401); } const { taskId, taskTitle, dueTime, repeat } = req.body; if (!taskId || !taskTitle || !dueTime) { throw new AppError('INVALID_PAYLOAD', 'Missing required fields', 400); } const user = await db.select().from(users).where(eq(users.id, req.user.userId)).limit(1); if (user.length === 0 || !user[0].expoPushToken) { throw new AppError('NO_PUSH_TOKEN', 'No push token registered', 400); } const ticket = await sendPushNotification( createTaskReminderPayload(user[0].expoPushToken!, taskTitle, dueTime, taskId, repeat) ); res.json({ success: true, ticket }); })); export default router;