@@ -1381,12 +1381,87 @@ async function executeBlogAction(
13811381 excerpt = plainText . substring ( 0 , 160 ) + ( plainText . length > 160 ? '...' : '' ) ;
13821382 }
13831383
1384- const { data, error } = await supabase . from ( 'blog_posts' ) . insert ( {
1384+ // --- Auto-fetch featured image ---
1385+ let featuredImage : string | null = null ;
1386+ let featuredImageAlt : string | null = null ;
1387+ const imageQuery = topic || title ;
1388+
1389+ // Strategy 1: Unsplash (free, fast, high quality photos)
1390+ const unsplashKey = Deno . env . get ( 'UNSPLASH_ACCESS_KEY' ) ;
1391+ if ( ! featuredImage && unsplashKey ) {
1392+ try {
1393+ const searchUrl = new URL ( 'https://api.unsplash.com/search/photos' ) ;
1394+ searchUrl . searchParams . set ( 'query' , imageQuery ) ;
1395+ searchUrl . searchParams . set ( 'per_page' , '1' ) ;
1396+ searchUrl . searchParams . set ( 'orientation' , 'landscape' ) ;
1397+ const uResp = await fetch ( searchUrl . toString ( ) , {
1398+ headers : { 'Authorization' : `Client-ID ${ unsplashKey } ` , 'Accept-Version' : 'v1' } ,
1399+ } ) ;
1400+ if ( uResp . ok ) {
1401+ const uData = await uResp . json ( ) ;
1402+ const photo = uData . results ?. [ 0 ] ;
1403+ if ( photo ) {
1404+ featuredImage = photo . urls ?. regular ;
1405+ featuredImageAlt = photo . alt_description || photo . description || `Photo by ${ photo . user ?. name } on Unsplash` ;
1406+ console . log ( `[write_blog_post] Unsplash image found: ${ featuredImage } ` ) ;
1407+ }
1408+ }
1409+ } catch ( e ) {
1410+ console . error ( '[write_blog_post] Unsplash fetch failed:' , e ) ;
1411+ }
1412+ }
1413+
1414+ // Strategy 2: Gemini image generation via Lovable AI gateway
1415+ const lovableApiKey = Deno . env . get ( 'LOVABLE_API_KEY' ) ;
1416+ if ( ! featuredImage && lovableApiKey ) {
1417+ try {
1418+ const imgResp = await fetch ( 'https://ai.gateway.lovable.dev/v1/chat/completions' , {
1419+ method : 'POST' ,
1420+ headers : { 'Authorization' : `Bearer ${ lovableApiKey } ` , 'Content-Type' : 'application/json' } ,
1421+ body : JSON . stringify ( {
1422+ model : 'google/gemini-2.5-flash-image' ,
1423+ messages : [ { role : 'user' , content : `Generate a professional, modern blog header image for an article titled "${ title } " about "${ topic } ". The image should be visually striking, landscape oriented, suitable as a blog featured image. No text in the image.` } ] ,
1424+ modalities : [ 'image' , 'text' ] ,
1425+ } ) ,
1426+ } ) ;
1427+ if ( imgResp . ok ) {
1428+ const imgData = await imgResp . json ( ) ;
1429+ const base64Url = imgData . choices ?. [ 0 ] ?. message ?. images ?. [ 0 ] ?. image_url ?. url ;
1430+ if ( base64Url ) {
1431+ // Upload base64 image to Supabase storage
1432+ const base64Data = base64Url . replace ( / ^ d a t a : i m a g e \/ \w + ; b a s e 6 4 , / , '' ) ;
1433+ const imageBytes = Uint8Array . from ( atob ( base64Data ) , c => c . charCodeAt ( 0 ) ) ;
1434+ const fileName = `blog/${ slug } -${ Date . now ( ) } .png` ;
1435+ const { error : uploadErr } = await supabase . storage
1436+ . from ( 'cms-images' )
1437+ . upload ( fileName , imageBytes , { contentType : 'image/png' , upsert : true } ) ;
1438+ if ( ! uploadErr ) {
1439+ const { data : urlData } = supabase . storage . from ( 'cms-images' ) . getPublicUrl ( fileName ) ;
1440+ featuredImage = urlData . publicUrl ;
1441+ featuredImageAlt = `Featured image for ${ title } ` ;
1442+ console . log ( `[write_blog_post] Gemini image generated and uploaded: ${ featuredImage } ` ) ;
1443+ } else {
1444+ console . error ( '[write_blog_post] Image upload failed:' , uploadErr . message ) ;
1445+ }
1446+ }
1447+ }
1448+ } catch ( e ) {
1449+ console . error ( '[write_blog_post] Gemini image generation failed:' , e ) ;
1450+ }
1451+ }
1452+
1453+ const insertData : Record < string , unknown > = {
13851454 title, slug, status : 'draft' , excerpt, content_json : tiptapDoc ,
13861455 meta_json : { tone, language, generated_by : 'flowpilot' , topic } ,
1387- } ) . select ( ) . single ( ) ;
1456+ } ;
1457+ if ( featuredImage ) {
1458+ insertData . featured_image = featuredImage ;
1459+ insertData . featured_image_alt = featuredImageAlt ;
1460+ }
1461+
1462+ const { data, error } = await supabase . from ( 'blog_posts' ) . insert ( insertData ) . select ( ) . single ( ) ;
13881463 if ( error ) throw new Error ( `Blog insert failed: ${ error . message } ` ) ;
1389- return { blog_post_id : data . id , slug : data . slug , title : data . title , status : 'draft' , has_content : ! ! markdownContent } ;
1464+ return { blog_post_id : data . id , slug : data . slug , title : data . title , status : 'draft' , has_content : ! ! markdownContent , has_featured_image : ! ! featuredImage } ;
13901465}
13911466
13921467// =============================================================================
0 commit comments