Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/llms-txt-hub / apps/web/app/api/newsletter/subscribe/route.ts |
| Date | Not specified in source |
| Repository | Janady13/llms-txt-hub |
| Revision | e0f4bf978d4df168369c7601d9c5f5ea89e84377 |
| Branch | main |
| Path or archive member | apps/web/app/api/newsletter/subscribe/route.ts |
| Original SHA-256 | 53d8f2670bf6c167f947e0dfa24590ed2ba0f2828b342a24268d1c15753ede67 |
| Git blob SHA | c3cac8e8b9d644be462ca1185296e54e2f347793 |
| Reading-copy SHA-256 | 53d8f2670bf6c167f947e0dfa24590ed2ba0f2828b342a24268d1c15753ede67 |
Recovered source transcript
Download complete source text ↓146 lines · 3.8 KB
import * as Sentry from '@sentry/nextjs'
import { logger } from '@thedaviddias/logging'
import {
createNewsletterProvider,
getProviderFromEnv,
subscribeSchema
} from '@thedaviddias/newsletter'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
// Get provider configuration from environment
const providerConfig = getProviderFromEnv()
// Add logger to config if we have one
if (providerConfig) {
providerConfig.logger = logger
}
const provider = providerConfig ? createNewsletterProvider(providerConfig) : null
/**
* Handles POST requests to subscribe a user to the newsletter
*/
export async function POST(request: NextRequest) {
try {
// Debug logging
logger.debug('Newsletter API called', {
data: { hasProvider: !!provider, provider: providerConfig?.provider },
tags: { type: 'newsletter' }
})
// CSRF is enforced by the app proxy for all non-GET API routes
if (!provider) {
logger.error('Newsletter provider is not configured. Check your environment variables.')
Sentry.captureMessage('Newsletter provider not configured', 'warning')
return NextResponse.json({ error: 'Newsletter service is not configured' }, { status: 500 })
}
const body = await request.json()
const data = subscribeSchema.parse(body)
// Use the provider to subscribe
const result = await provider.subscribe(data)
if (!result.success) {
// Special handling for unsubscribed users (MailerLite specific)
if (result.error === 'EMAIL_UNSUBSCRIBED') {
return NextResponse.json(
{
error: result.message,
code: 'EMAIL_UNSUBSCRIBED'
},
{ status: 422 }
)
}
// For iframe-only providers like Substack
if (result.error === 'IFRAME_ONLY') {
return NextResponse.json(
{
error: result.message,
code: 'IFRAME_ONLY'
},
{ status: 501 } // Not Implemented
)
}
return NextResponse.json(
{
error: result.message || 'Failed to subscribe to newsletter'
},
{ status: 400 }
)
}
return NextResponse.json({
success: true,
message: result.message,
subscriber: result.subscriber
? {
id: result.subscriber.id,
email: result.subscriber.email,
status: result.subscriber.status
}
: undefined
})
} catch (error) {
logger.error('Newsletter subscription error', {
data: error,
tags: { type: 'newsletter' }
})
// Capture error in Sentry with context
if (error instanceof Error) {
Sentry.captureException(error, {
tags: {
operation: 'newsletter_subscription',
provider: providerConfig?.provider
},
extra: {
hasProvider: !!provider,
provider: providerConfig?.provider
}
})
}
if (error instanceof z.ZodError) {
return NextResponse.json(
{
error: 'Invalid request data',
details: error.errors
},
{ status: 400 }
)
}
// Return more detailed error in development
const isDevelopment = process.env.NODE_ENV === 'development'
return NextResponse.json(
{
error: 'Failed to subscribe to newsletter',
...(isDevelopment &&
error instanceof Error && {
debug: {
message: error.message,
type: error.name
}
})
},
{ status: 500 }
)
}
}
/**
* Handles GET requests returning newsletter endpoint info
*/
export async function GET() {
return NextResponse.json({
message: 'Newsletter subscription endpoint',
provider: providerConfig?.provider || 'not configured',
methods: ['POST']
})
}