1+ import { NextRequest , NextResponse } from 'next/server'
2+ import { MarkdownEnhancer } from '@/lib/content/markdownEnhancer'
3+ import { validateApiKey , validateSubscription } from '@/lib/services/auth-service'
4+ import type { ApiKeyWithRelations } from '@/lib/services/auth-service'
5+ import { CacheService } from '@/lib/services/cache-service'
6+ import { getEndpointPricing , trackEndpointUsage , logApiRequest } from '@/lib/services/billing-service'
7+ import { withRateLimit } from '@/middleware/rateLimiter'
8+
9+
10+ async function handleRequest ( request : NextRequest ) {
11+
12+ // Validate and type the API key
13+ const TAVILY_API_KEY = process . env . TAVILY_API_KEY as string
14+ if ( ! TAVILY_API_KEY ) {
15+ throw new Error ( 'TAVILY_API_KEY environment variable is not set' )
16+ }
17+ const startTime = Date . now ( )
18+ const cacheService = CacheService . getInstance ( )
19+ let keyData : ApiKeyWithRelations | null = null
20+
21+ try {
22+ const apiKey = request . headers . get ( 'x-api-key' )
23+ keyData = await validateApiKey ( apiKey )
24+
25+ if ( ! keyData ) {
26+ return NextResponse . json ( { error : 'Invalid API key' } , { status : 401 } )
27+ }
28+
29+ const activeSubscription = await validateSubscription ( keyData )
30+ const endpointPricing = await getEndpointPricing ( '/api/article' , 'POST' )
31+
32+ if ( ! endpointPricing ) {
33+ return NextResponse . json (
34+ { error : 'Endpoint pricing not configured' } ,
35+ { status : 500 }
36+ )
37+ }
38+
39+ // Parse request body
40+ const body = await request . json ( )
41+ const { content, title } = body
42+
43+ if ( ! content || typeof content !== 'string' || ! title || typeof title !== 'string' ) {
44+ return NextResponse . json (
45+ { error : 'Content and title are required and must be strings' } ,
46+ { status : 400 }
47+ )
48+ }
49+
50+ // Check cache
51+ const cacheKey = `article:${ Buffer . from ( title + content ) . toString ( 'base64' ) } `
52+ const cachedResult = await cacheService . get < any > ( cacheKey )
53+
54+ if ( cachedResult ) {
55+ await logApiRequest (
56+ keyData . id ,
57+ '/api/article' ,
58+ 'POST' ,
59+ 200 ,
60+ Date . now ( ) - startTime ,
61+ endpointPricing . id ,
62+ endpointPricing . pricePerRequest
63+ )
64+ return NextResponse . json ( cachedResult )
65+ }
66+
67+ // Process content
68+ const enhancer = new MarkdownEnhancer ( TAVILY_API_KEY )
69+ const result = await enhancer . enhance ( content , title )
70+
71+ // Cache result
72+ await cacheService . set ( cacheKey , result )
73+
74+ // Track usage and billing
75+ await trackEndpointUsage (
76+ activeSubscription . id ,
77+ endpointPricing . id ,
78+ endpointPricing . pricePerRequest
79+ )
80+
81+ await logApiRequest (
82+ keyData . id ,
83+ '/api/article' ,
84+ 'POST' ,
85+ 200 ,
86+ Date . now ( ) - startTime ,
87+ endpointPricing . id ,
88+ endpointPricing . pricePerRequest
89+ )
90+
91+ return NextResponse . json ( result )
92+
93+ } catch ( error ) {
94+ const status = error instanceof Error && error . message . includes ( 'Rate limit' ) ? 429 : 500
95+
96+ if ( keyData ?. id ) {
97+ await logApiRequest (
98+ keyData . id ,
99+ '/api/article' ,
100+ 'POST' ,
101+ status ,
102+ Date . now ( ) - startTime ,
103+ undefined ,
104+ undefined ,
105+ error instanceof Error ? error . message : 'Unknown error'
106+ )
107+ }
108+
109+ return NextResponse . json (
110+ { error : error instanceof Error ? error . message : 'Unknown error' } ,
111+ { status }
112+ )
113+ }
114+ }
115+
116+ export async function POST ( request : NextRequest ) {
117+ // Get rate limit from API key
118+ const apiKey = request . headers . get ( 'x-api-key' )
119+ const keyData = await validateApiKey ( apiKey )
120+
121+ if ( ! keyData ) {
122+ return NextResponse . json ( { error : 'Invalid API key' } , { status : 401 } )
123+ }
124+
125+ return withRateLimit (
126+ request ,
127+ handleRequest ,
128+ { perMinute : keyData . requestPerMinute ?? 60 }
129+ )
130+ }
131+
132+ export const runtime = 'nodejs'
133+ export const maxDuration = 60 // in seconds
134+ export const dynamic = 'force-dynamic'
0 commit comments