@@ -8,6 +8,7 @@ type GithubEnv = {
88 GITHUB_CLIENT_ID : string ;
99 GITHUB_APP_PRIVATE_KEY : string ;
1010 GITHUB_INSTALLATION_ID : string ;
11+ TURNSTILE_SECRET_KEY : string ;
1112} ;
1213
1314type GithubIssue = {
@@ -121,26 +122,57 @@ const getInstallationAccessToken = async (
121122 return data . token ;
122123} ;
123124
125+ const verifyTurnstile = async ( token : string , secretKey : string ) => {
126+ const url = "https://challenges.cloudflare.com/turnstile/v0/siteverify" ;
127+ const formData = new FormData ( ) ;
128+ formData . append ( "secret" , secretKey ) ;
129+ formData . append ( "response" , token ) ;
130+
131+ try {
132+ const result = await fetch ( url , {
133+ body : formData ,
134+ method : "POST" ,
135+ } ) ;
136+ const outcome = ( await result . json ( ) ) as { success : boolean } ;
137+ return outcome . success ;
138+ } catch ( err ) {
139+ return false ;
140+ }
141+ } ;
142+
124143const app = new Hono ( )
125144 . post (
126145 "/" ,
127146 zValidator (
128- "form " ,
147+ "json " ,
129148 z . object ( {
130149 title : z . string ( ) ,
131150 body : z . string ( ) ,
132151 labels : z . array ( z . string ( ) ) ,
152+ turnstileToken : z . string ( ) . optional ( ) ,
133153 } ) ,
134154 ) ,
135155 async ( c ) => {
136- const { title, body, labels } = c . req . valid ( "form " ) ;
156+ const { title, body, labels, turnstileToken } = c . req . valid ( "json " ) ;
137157
138158 const {
139159 GITHUB_CLIENT_ID ,
140160 GITHUB_APP_PRIVATE_KEY ,
141161 GITHUB_INSTALLATION_ID ,
162+ TURNSTILE_SECRET_KEY ,
142163 } = env < GithubEnv > ( c ) ;
143164
165+ // Verify Turnstile token if provided
166+ if ( turnstileToken ) {
167+ const isValid = await verifyTurnstile (
168+ turnstileToken ,
169+ TURNSTILE_SECRET_KEY ,
170+ ) ;
171+ if ( ! isValid ) {
172+ return c . json ( { error : "Invalid Turnstile verification" } , 400 ) ;
173+ }
174+ }
175+
144176 // base64 encoded private key to utf8, not using buffer
145177 const privateKey = atob ( GITHUB_APP_PRIVATE_KEY ) ;
146178
0 commit comments