11import type { NextRequest } from 'next/server' ;
22import { ImageResponse } from 'next/og' ;
33import { loadFonts , createOGResponse } from '@/utils/og-image' ;
4- import { getHackathon } from '@/server/services/hackathons ' ;
4+ import axios from 'axios ' ;
55
66export const runtime = 'edge' ;
77
@@ -13,7 +13,16 @@ export async function GET(
1313 const fonts = await loadFonts ( ) ;
1414
1515 try {
16- const hackathon = await getHackathon ( id ) ;
16+ const res = await axios . get (
17+ `${ process . env . NEXTAUTH_URL } /api/hackathons/${ id } ` ,
18+ {
19+ headers : {
20+ 'Cache-Control' : 'no-store' ,
21+ } ,
22+ }
23+ ) ;
24+
25+ const hackathon = res . data || null ;
1726
1827 if ( ! hackathon ) {
1928 return createOGResponse ( {
@@ -24,18 +33,96 @@ export async function GET(
2433 } ) ;
2534 }
2635
36+ // If hackathon has a banner, show it as the OG image
37+ if ( hackathon . banner && hackathon . banner . trim ( ) !== '' ) {
38+ try {
39+ // Fetch the banner image with axios
40+ const imageResponse = await axios . get ( hackathon . banner , {
41+ responseType : 'arraybuffer' ,
42+ headers : {
43+ 'Accept' : 'image/*' ,
44+ } ,
45+ } ) ;
46+
47+ const imageBuffer = imageResponse . data ;
48+
49+ // Check if we got actual image data
50+ if ( ! imageBuffer || imageBuffer . byteLength === 0 ) {
51+ throw new Error ( 'Banner image is empty' ) ;
52+ }
53+
54+ // Convert ArrayBuffer to base64 in Edge Runtime
55+ const base64 = btoa (
56+ new Uint8Array ( imageBuffer ) . reduce (
57+ ( data , byte ) => data + String . fromCharCode ( byte ) ,
58+ ''
59+ )
60+ ) ;
61+ const contentType = imageResponse . headers [ 'content-type' ] || 'image/png' ;
62+ const imageDataUrl = `data:${ contentType } ;base64,${ base64 } ` ;
63+
64+ return new ImageResponse (
65+ (
66+ < div
67+ style = { {
68+ display : 'flex' ,
69+ height : '100%' ,
70+ width : '100%' ,
71+ position : 'relative' ,
72+ overflow : 'hidden' ,
73+ } }
74+ >
75+ < img
76+ src = { imageDataUrl }
77+ alt = { hackathon . title }
78+ style = { {
79+ width : '100%' ,
80+ height : '100%' ,
81+ objectFit : 'cover' ,
82+ } }
83+ />
84+ </ div >
85+ ) ,
86+ {
87+ width : 1280 ,
88+ height : 720 ,
89+ fonts : [
90+ { name : 'Geist-Medium' , data : fonts . medium , weight : 600 } ,
91+ { name : 'Geist-Mono' , data : fonts . regular , weight : 500 } ,
92+ { name : 'Geist-Light' , data : fonts . light , weight : 300 }
93+ ] ,
94+ }
95+ ) ;
96+ } catch ( imageError : any ) {
97+ // If banner fails to load, silently fallback to title/description
98+ // This is expected behavior when banner doesn't exist or fails to load
99+ if ( imageError . response ?. status !== 404 ) {
100+ console . warn ( 'Failed to load banner image:' , imageError . message ) ;
101+ }
102+ return createOGResponse ( {
103+ title : hackathon . title ,
104+ description : hackathon . description ,
105+ path : 'hackathons' ,
106+ fonts
107+ } ) ;
108+ }
109+ }
110+
111+ // If no banner, show title and description
27112 return createOGResponse ( {
28113 title : hackathon . title ,
29114 description : hackathon . description ,
30115 path : 'hackathons' ,
31116 fonts
32117 } ) ;
33- } catch ( error ) {
118+
119+ } catch ( error : any ) {
120+ console . error ( 'Error fetching hackathon:' , error . message || error ) ;
34121 return createOGResponse ( {
35122 title : 'Hackathons' ,
36123 description : 'Join exciting blockchain hackathons and build the future on Avalanche' ,
37124 path : 'hackathons' ,
38125 fonts
39126 } ) ;
40127 }
41- }
128+ }
0 commit comments