97 lines
2.8 KiB
TypeScript
97 lines
2.8 KiB
TypeScript
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; |