import { NextResponse } from 'next/server'; import { z } from 'zod'; import { withAuth } from '@/lib/api/helpers'; import { parseBody } from '@/lib/api/route-helpers'; import { buildTrackedUrl, createTrackedLink } from '@/lib/services/tracked-links.service'; import { errorResponse } from '@/lib/errors'; /** * POST /api/v1/tracked-links * * Mints a new tracked redirect-link the rep can drop into an outgoing * email or chat. Body: { targetUrl, sendId? }. Returns the slug + the * full public URL (`/q/`) - caller pastes the URL into * the message draft. * * Gated on `email.send` since this surface is consumed from compose UIs. * Tracked links aren't sensitive on their own but reps shouldn't be * able to mint click-trackers without the underlying send permission. */ const createSchema = z.object({ targetUrl: z.string().url(), sendId: z.string().min(1).optional(), }); export const POST = withAuth(async (req, ctx) => { try { const body = await parseBody(req, createSchema); const allowed = ctx.isSuperAdmin ? true : !!ctx.permissions?.email?.send; if (!allowed) { return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); } const row = await createTrackedLink({ portId: ctx.portId, targetUrl: body.targetUrl, sendId: body.sendId, createdByUserId: ctx.userId, }); return NextResponse.json({ data: { id: row.id, slug: row.slug, targetUrl: row.targetUrl, url: buildTrackedUrl(row.slug), }, }); } catch (error) { return errorResponse(error); } });