| 1 | import { Router, Response, NextFunction } from 'express'
|
| 2 | import React from 'react'
|
| 3 | import { renderToBuffer } from '@react-pdf/renderer'
|
| 4 | import { z } from 'zod'
|
| 5 | import { requireAuth, AuthenticatedRequest } from '@app/middleware/auth'
|
| 6 | import { buildReportData } from './report.data'
|
| 7 | import ReportDocument from './ReportDocument'
|
| 8 |
|
| 9 | const router = Router()
|
| 10 |
|
| 11 | const ReportSchema = z
|
| 12 | .object({
|
| 13 | periodStart: z.iso.datetime({ offset: true }),
|
| 14 | periodEnd: z.iso.datetime({ offset: true }),
|
| 15 | })
|
| 16 | .refine(
|
| 17 | (d) => {
|
| 18 | const start = new Date(d.periodStart)
|
| 19 | const end = new Date(d.periodEnd)
|
| 20 | const diff = (end.getTime() - start.getTime()) / 86_400_000
|
| 21 | return diff > 0 && diff <= 90
|
| 22 | },
|
| 23 | { message: 'periodEnd must be after periodStart and range ≤ 90 days' },
|
| 24 | )
|
| 25 |
|
| 26 | router.post(
|
| 27 | '/',
|
| 28 | requireAuth,
|
| 29 | async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
|
| 30 | try {
|
| 31 | req.log.info("Requirements: %s", req.body.periodStart, req.body.periodEnd)
|
| 32 |
|
| 33 | const parsed = ReportSchema.safeParse(req.body)
|
| 34 | if (!parsed.success) {
|
| 35 | return res.status(400).json({ error: parsed.error.issues[0]?.message ?? 'Invalid request' })
|
| 36 | }
|
| 37 |
|
| 38 | const { periodStart, periodEnd } = parsed.data
|
| 39 | const data = await buildReportData(
|
| 40 | req.userId!,
|
| 41 | new Date(periodStart),
|
| 42 | new Date(periodEnd),
|
| 43 | )
|
| 44 |
|
| 45 | const element = React.createElement(ReportDocument, { data })
|
| 46 | const buffer = await renderToBuffer(
|
| 47 | element as React.ReactElement<any>,
|
| 48 | )
|
| 49 |
|
| 50 | res.setHeader('Content-Type', 'application/pdf')
|
| 51 | res.setHeader(
|
| 52 | 'Content-Disposition',
|
| 53 | `attachment; filename="Nexo-report-${periodStart.slice(0, 7)}.pdf"`,
|
| 54 | )
|
| 55 | return res.send(buffer)
|
| 56 | } catch (err) {
|
| 57 | return next(err)
|
| 58 | }
|
| 59 | },
|
| 60 | )
|
| 61 |
|
| 62 | export default router
|