import { type Router as ExpressRouter, Router, Response } from 'express'; import { db } from '../db/index.js'; import { users, integrationTokens, tipFeedback, tipViews, sessions, userConsents } from '../db/schema.js'; import { eq, and, isNull } from 'drizzle-orm'; import { requireAuth, AuthenticatedRequest } from '../middleware/session.js'; const router: ExpressRouter = Router(); /** GET /api/user/me */ router.get('/me', requireAuth, async (req: AuthenticatedRequest, res: Response) => { const [user] = await db.select().from(users).where(eq(users.id, req.userId!)).limit(1); if (!user || user.deletedAt) { res.status(404).json({ error: 'User not found' }); return; } res.json({ id: user.id, email: user.email, name: user.name, image: user.image, role: user.role, createdAt: user.createdAt, }); }); /** POST /api/user/consent — record data:core consent */ router.post('/consent', requireAuth, async (req: AuthenticatedRequest, res: Response) => { const now = new Date().toISOString(); await db .insert(userConsents) .values({ userId: req.userId!, consentKey: 'data:core', grantedAt: now }) .onConflictDoUpdate({ target: [userConsents.userId, userConsents.consentKey], set: { grantedAt: now, revokedAt: null }, }); res.json({ ok: true }); }); /** DELETE /api/user/me — account deletion */ router.delete('/me', requireAuth, async (req: AuthenticatedRequest, res: Response) => { const userId = req.userId!; // Revoke all integration tokens const tokens = await db .select() .from(integrationTokens) .where(eq(integrationTokens.userId, userId)); for (const token of tokens) { if (token.provider === 'todoist') { await fetch('https://api.todoist.com/sync/v9/access_tokens/revoke', { method: 'POST', headers: { Authorization: `Bearer ${token.accessToken}` }, }).catch(() => {}); } } // Delete cascade await db.delete(integrationTokens).where(eq(integrationTokens.userId, userId)); await db.delete(tipFeedback).where(eq(tipFeedback.userId, userId)); await db.delete(tipViews).where(eq(tipViews.userId, userId)); await db.delete(sessions).where(eq(sessions.userId, userId)); // Soft-delete user (GDPR: keep audit trail row without PII) await db .update(users) .set({ deletedAt: new Date().toISOString(), email: `deleted:${userId}`, name: null, image: null, googleId: null }) .where(eq(users.id, userId)); res.clearCookie('sid').json({ ok: true }); }); export { router as userRouter };