11import { NextRequest , NextResponse } from 'next/server' ;
22import { getDb } from '@/lib/db' ;
3+ import type { DiscountType } from '@/lib/types' ;
34
45export async function GET ( req : NextRequest ) {
56 const { searchParams } = new URL ( req . url ) ;
@@ -22,19 +23,106 @@ export async function GET(req: NextRequest) {
2223 }
2324}
2425
26+ function slugify ( value : string ) : string {
27+ return value
28+ . toLowerCase ( )
29+ . replace ( / [ ^ a - z 0 - 9 ] + / g, '-' )
30+ . replace ( / ( ^ - | - $ ) / g, '' ) ;
31+ }
32+
33+ /** Human-readable badge text from structured discount fields. */
34+ function formatDiscount ( type : DiscountType | null , value : number | null ) : string | null {
35+ if ( value == null || Number . isNaN ( value ) ) return null ;
36+ if ( type === 'percent' ) return `${ value } %` ;
37+ if ( type === 'fixed' ) return `$${ value } off` ;
38+ return null ;
39+ }
40+
41+ /**
42+ * Resolve a store by website domain (preferred) or name, creating it if needed.
43+ * Returns the store id.
44+ */
45+ async function resolveStoreId (
46+ db : ReturnType < typeof getDb > ,
47+ opts : { name : string | null ; website : string | null ; logoCandidate : string | null }
48+ ) : Promise < number > {
49+ const name = opts . name ?. trim ( ) || null ;
50+
51+ // Derive a stable slug: prefer the website host, fall back to the name.
52+ let slugBase = name ;
53+ let website = opts . website ?. trim ( ) || null ;
54+ if ( website ) {
55+ try {
56+ const host = new URL ( website ) . hostname . replace ( / ^ w w w \. / , '' ) ;
57+ slugBase = slugBase ?? host . split ( '.' ) [ 0 ] ;
58+ website = `${ new URL ( website ) . protocol } //${ new URL ( website ) . hostname } ` ;
59+ } catch {
60+ /* keep website as-is */
61+ }
62+ }
63+ if ( ! slugBase ) slugBase = 'unknown-store' ;
64+ const slug = slugify ( slugBase ) || 'unknown-store' ;
65+
66+ const existing = await db . sql `SELECT id FROM stores WHERE slug = ${ slug } LIMIT 1` ;
67+ if ( existing . length > 0 ) return existing [ 0 ] . id ;
68+
69+ await db . sql `
70+ INSERT INTO stores (name, slug, website)
71+ VALUES (${ name ?? slugBase } , ${ slug } , ${ website } )
72+ ` ;
73+ const created = await db . sql `SELECT id FROM stores WHERE slug = ${ slug } LIMIT 1` ;
74+ return created [ 0 ] . id ;
75+ }
76+
2577export async function POST ( req : NextRequest ) {
2678 try {
2779 const body = await req . json ( ) ;
28- const { store_id, code, title, description, discount, expiry_date, url } = body ;
80+ const {
81+ url,
82+ code,
83+ discount_type,
84+ discount_value,
85+ expiry_date,
86+ // AI-scraped listing metadata
87+ title,
88+ description,
89+ store_name,
90+ store_website,
91+ image_url,
92+ } = body ;
2993
30- if ( ! store_id || ! title ) {
31- return NextResponse . json ( { error : 'store_id and title are required' } , { status : 400 } ) ;
94+ if ( ! url ) {
95+ return NextResponse . json ( { error : 'url is required' } , { status : 400 } ) ;
3296 }
97+ if ( ! title ) {
98+ return NextResponse . json (
99+ { error : 'title is required (scrape the URL first)' } ,
100+ { status : 400 }
101+ ) ;
102+ }
103+
104+ const type : DiscountType | null =
105+ discount_type === 'percent' || discount_type === 'fixed' ? discount_type : null ;
106+ const value =
107+ discount_value === '' || discount_value == null ? null : Number ( discount_value ) ;
108+ const discount = formatDiscount ( type , value ) ;
33109
34110 const db = getDb ( ) ;
111+ const storeId = await resolveStoreId ( db , {
112+ name : store_name ?? null ,
113+ website : store_website ?? null ,
114+ logoCandidate : image_url ?? null ,
115+ } ) ;
116+
35117 await db . sql `
36- INSERT INTO coupons (store_id, code, title, description, discount, expiry_date, url)
37- VALUES (${ store_id } , ${ code } , ${ title } , ${ description } , ${ discount } , ${ expiry_date } , ${ url } )
118+ INSERT INTO coupons (
119+ store_id, code, title, description, discount, discount_type, discount_value,
120+ expiry_date, url, image_url
121+ )
122+ VALUES (
123+ ${ storeId } , ${ code ?. trim ( ) || null } , ${ title } , ${ description ?? null } , ${ discount } ,
124+ ${ type } , ${ value } , ${ expiry_date || null } , ${ url } , ${ image_url ?? null }
125+ )
38126 ` ;
39127 return NextResponse . json ( { success : true } , { status : 201 } ) ;
40128 } catch ( err ) {
0 commit comments