import { FastifyInstance } from 'fastify'; import { prisma } from '../services/prisma'; import { z } from 'zod'; /** * Student-facing public routes (query by phone number). * Protected by ADMIN_API_KEY in the guarded scope. */ export async function studentRoutes(fastify: FastifyInstance) { // GET /v1/student/me?phone=221771234567 // Returns user profile + enrollments + generated documents fastify.get('/me', async (req, reply) => { const query = req.query as { phone?: string }; const phoneSchema = z.string().min(7); const phoneResult = phoneSchema.safeParse(query.phone); if (!phoneResult.success) { return reply.code(400).send({ error: 'phone query param is required' }); } const phone = phoneResult.data.replace(/\s+/g, '').replace(/^\+/, ''); const user = await prisma.user.findUnique({ where: { phone }, include: { enrollments: { include: { track: { include: { days: { orderBy: { dayNumber: 'asc' }, take: 1 } } } }, orderBy: { startedAt: 'desc' } }, payments: { where: { status: 'COMPLETED' }, orderBy: { createdAt: 'desc' } }, progress: { orderBy: { lastInteraction: 'desc' } } } }); if (!user) { return reply.code(404).send({ error: 'User not found. Send INSCRIPTION on WhatsApp to register.' }); } return { id: user.id, phone: user.phone, name: user.name, language: user.language, activity: user.activity, createdAt: user.createdAt, enrollments: user.enrollments.map(e => ({ id: e.id, trackId: e.trackId, trackTitle: e.track.title, status: e.status, currentDay: e.currentDay, totalDays: e.track.duration, progressPercent: Math.round((e.currentDay / e.track.duration) * 100), startedAt: e.startedAt, completedAt: e.completedAt, })), payments: user.payments, // R2 document URLs are stored as Payment metadata (future enhancement) }; }); }