import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { integrationRequestContract } from '@/lib/api/contracts/common' import { parseRequest, validationErrorResponse } from '@/lib/api/server' import { env } from '@/lib/core/config/env' import type { TokenBucketConfig } from '@/lib/core/rate-limiter' import { RateLimiter } from '@/lib/core/rate-limiter' import { generateRequestId, getClientIp } from '@/lib/core/utils/request' import { getEmailDomain } from '@/lib/core/utils/urls' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { sendEmail } from '@/lib/messaging/email/mailer' import { getFromEmailAddress } from '@/lib/messaging/email/utils' const logger = createLogger('IntegrationRequestAPI') const rateLimiter = new RateLimiter() const PUBLIC_ENDPOINT_RATE_LIMIT: TokenBucketConfig = { maxTokens: 10, refillRate: 5, refillIntervalMs: 60_000, } export const POST = withRouteHandler(async (req: NextRequest) => { const requestId = generateRequestId() try { const ip = getClientIp(req) const storageKey = `public:integration-request:${ip}` const { allowed, remaining, resetAt } = await rateLimiter.checkRateLimitDirect( storageKey, PUBLIC_ENDPOINT_RATE_LIMIT ) if (!allowed) { logger.warn(`[${requestId}] Rate limit exceeded for IP ${ip}`, { remaining, resetAt }) return NextResponse.json( { error: 'Too many requests. Please try again later.' }, { status: 429, headers: { 'Retry-After': String(Math.ceil((resetAt.getTime() - Date.now()) / 1000)) }, } ) } const parsed = await parseRequest( integrationRequestContract, req, {}, { validationErrorResponse: (err) => validationErrorResponse(err, 'Invalid request data'), } ) if (!parsed.success) return parsed.response const { integrationName, email, useCase } = parsed.data.body logger.info(`[${requestId}] Processing integration request`, { integrationName, email: `${email.substring(0, 3)}***`, }) const emailText = `Integration: ${integrationName} From: ${email} Submitted: ${new Date().toISOString()} ${useCase ? `Use Case:\n${useCase}` : 'No use case provided.'} ` const emailResult = await sendEmail({ to: [`help@${env.EMAIL_DOMAIN || getEmailDomain()}`], subject: `[INTEGRATION REQUEST] ${integrationName}`, text: emailText, from: getFromEmailAddress(), replyTo: email, emailType: 'transactional', }) if (!emailResult.success) { logger.error(`[${requestId}] Error sending integration request email`, emailResult.message) return NextResponse.json({ error: 'Failed to send request' }, { status: 500 }) } logger.info(`[${requestId}] Integration request email sent successfully`) return NextResponse.json( { success: true, message: 'Integration request submitted successfully' }, { status: 200 } ) } catch (error) { if (error instanceof Error && error.message.includes('not configured')) { logger.error(`[${requestId}] Email service configuration error`, error) return NextResponse.json( { error: 'Email service is temporarily unavailable. Please try again later.' }, { status: 500 } ) } logger.error(`[${requestId}] Error processing integration request`, error) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } })