11// Package main provides the AWS Lambda entry point for the GitHub bot.
2- // This Lambda handler supports both API Gateway (webhooks ) and EventBridge
2+ // This Lambda handler supports API Gateway HTTP API (v2.0 ) and EventBridge
33// (scheduled sync) events.
44package main
55
@@ -40,14 +40,14 @@ func initApp() {
4040 })
4141}
4242
43- // APIGatewayHandler processes incoming API Gateway requests.
43+ // APIGatewayHandler processes incoming API Gateway HTTP API (v2.0) requests.
4444// handles GitHub webhook events, status checks, and config endpoints.
4545// validates webhook signatures before processing events.
46- func APIGatewayHandler (ctx context.Context , req awsevents.APIGatewayProxyRequest ) (awsevents.APIGatewayProxyResponse , error ) {
46+ func APIGatewayHandler (ctx context.Context , req awsevents.APIGatewayV2HTTPRequest ) (awsevents.APIGatewayV2HTTPResponse , error ) {
4747 initApp ()
4848 if initErr != nil {
4949 logger .Error ("initialization failed" , slog .String ("error" , initErr .Error ()))
50- return awsevents.APIGatewayProxyResponse {
50+ return awsevents.APIGatewayV2HTTPResponse {
5151 StatusCode : 500 ,
5252 Body : "service initialization failed" ,
5353 }, nil
@@ -58,7 +58,7 @@ func APIGatewayHandler(ctx context.Context, req awsevents.APIGatewayProxyRequest
5858 logger .Debug ("received api gateway request" , slog .String ("request" , string (j )))
5959 }
6060
61- path := req .Path
61+ path := req .RawPath
6262 if appInst .Config .BasePath != "" {
6363 path = strings .TrimPrefix (path , appInst .Config .BasePath )
6464 if path == "" {
@@ -74,30 +74,23 @@ func APIGatewayHandler(ctx context.Context, req awsevents.APIGatewayProxyRequest
7474 return handleServerConfig (ctx , req )
7575 }
7676
77- if req .HTTPMethod != "POST" {
78- return awsevents.APIGatewayProxyResponse {
77+ if req .RequestContext . HTTP . Method != "POST" {
78+ return awsevents.APIGatewayV2HTTPResponse {
7979 StatusCode : 405 ,
8080 Body : "method not allowed" ,
8181 }, nil
8282 }
8383
84- eventType := req .Headers ["X-GitHub-Event" ]
85- if eventType == "" {
86- eventType = req .Headers ["x-github-event" ]
87- }
88-
89- signature := req .Headers ["X-Hub-Signature-256" ]
90- if signature == "" {
91- signature = req .Headers ["x-hub-signature-256" ]
92- }
84+ eventType := req .Headers ["x-github-event" ]
85+ signature := req .Headers ["x-hub-signature-256" ]
9386
9487 if err := github .ValidateWebhookSignature (
9588 []byte (req .Body ),
9689 signature ,
9790 appInst .Config .GitHubWebhookSecret ,
9891 ); err != nil {
9992 logger .Warn ("webhook signature validation failed" , slog .String ("error" , err .Error ()))
100- return awsevents.APIGatewayProxyResponse {
93+ return awsevents.APIGatewayV2HTTPResponse {
10194 StatusCode : 401 ,
10295 Body : "unauthorized" ,
10396 }, nil
@@ -107,23 +100,23 @@ func APIGatewayHandler(ctx context.Context, req awsevents.APIGatewayProxyRequest
107100 logger .Error ("webhook processing failed" ,
108101 slog .String ("event_type" , eventType ),
109102 slog .String ("error" , err .Error ()))
110- return awsevents.APIGatewayProxyResponse {
103+ return awsevents.APIGatewayV2HTTPResponse {
111104 StatusCode : 500 ,
112105 Body : "webhook processing failed" ,
113106 }, nil
114107 }
115108
116- return awsevents.APIGatewayProxyResponse {
109+ return awsevents.APIGatewayV2HTTPResponse {
117110 StatusCode : 200 ,
118111 Body : "ok" ,
119112 }, nil
120113}
121114
122115// handleServerStatus returns the application status and feature flags.
123116// responds with JSON containing configuration state and enabled features.
124- func handleServerStatus (ctx context.Context , req awsevents.APIGatewayProxyRequest ) (awsevents.APIGatewayProxyResponse , error ) {
125- if req .HTTPMethod != "GET" {
126- return awsevents.APIGatewayProxyResponse {
117+ func handleServerStatus (ctx context.Context , req awsevents.APIGatewayV2HTTPRequest ) (awsevents.APIGatewayV2HTTPResponse , error ) {
118+ if req .RequestContext . HTTP . Method != "GET" {
119+ return awsevents.APIGatewayV2HTTPResponse {
127120 StatusCode : 405 ,
128121 Body : "method not allowed" ,
129122 }, nil
@@ -133,13 +126,13 @@ func handleServerStatus(ctx context.Context, req awsevents.APIGatewayProxyReques
133126 body , err := json .Marshal (status )
134127 if err != nil {
135128 logger .Error ("failed to marshal status response" , slog .String ("error" , err .Error ()))
136- return awsevents.APIGatewayProxyResponse {
129+ return awsevents.APIGatewayV2HTTPResponse {
137130 StatusCode : 500 ,
138131 Body : "failed to generate status response" ,
139132 }, nil
140133 }
141134
142- return awsevents.APIGatewayProxyResponse {
135+ return awsevents.APIGatewayV2HTTPResponse {
143136 StatusCode : 200 ,
144137 Headers : map [string ]string {"Content-Type" : "application/json" },
145138 Body : string (body ),
@@ -148,9 +141,9 @@ func handleServerStatus(ctx context.Context, req awsevents.APIGatewayProxyReques
148141
149142// handleServerConfig returns the application configuration with secrets
150143// redacted. useful for debugging and verifying environment settings.
151- func handleServerConfig (ctx context.Context , req awsevents.APIGatewayProxyRequest ) (awsevents.APIGatewayProxyResponse , error ) {
152- if req .HTTPMethod != "GET" {
153- return awsevents.APIGatewayProxyResponse {
144+ func handleServerConfig (ctx context.Context , req awsevents.APIGatewayV2HTTPRequest ) (awsevents.APIGatewayV2HTTPResponse , error ) {
145+ if req .RequestContext . HTTP . Method != "GET" {
146+ return awsevents.APIGatewayV2HTTPResponse {
154147 StatusCode : 405 ,
155148 Body : "method not allowed" ,
156149 }, nil
@@ -160,13 +153,13 @@ func handleServerConfig(ctx context.Context, req awsevents.APIGatewayProxyReques
160153 body , err := json .Marshal (redacted )
161154 if err != nil {
162155 logger .Error ("failed to marshal config response" , slog .String ("error" , err .Error ()))
163- return awsevents.APIGatewayProxyResponse {
156+ return awsevents.APIGatewayV2HTTPResponse {
164157 StatusCode : 500 ,
165158 Body : "failed to generate config response" ,
166159 }, nil
167160 }
168161
169- return awsevents.APIGatewayProxyResponse {
162+ return awsevents.APIGatewayV2HTTPResponse {
170163 StatusCode : 200 ,
171164 Headers : map [string ]string {"Content-Type" : "application/json" },
172165 Body : string (body ),
@@ -196,17 +189,19 @@ func EventBridgeHandler(ctx context.Context, evt awsevents.CloudWatchEvent) erro
196189}
197190
198191// UniversalHandler detects the event type and routes to the appropriate
199- // handler. supports both API Gateway and EventBridge events.
192+ // handler. supports API Gateway HTTP API (v2.0) and EventBridge events.
200193func UniversalHandler (ctx context.Context , event json.RawMessage ) (any , error ) {
201194 if initErr != nil {
202195 return nil , initErr
203196 }
204197
205- var apiGatewayReq awsevents.APIGatewayProxyRequest
206- if err := json .Unmarshal (event , & apiGatewayReq ); err == nil && apiGatewayReq .RequestContext .RequestID != "" {
198+ // try API Gateway HTTP API (v2.0)
199+ var apiGatewayReq awsevents.APIGatewayV2HTTPRequest
200+ if err := json .Unmarshal (event , & apiGatewayReq ); err == nil && apiGatewayReq .RequestContext .HTTP .Method != "" {
207201 return APIGatewayHandler (ctx , apiGatewayReq )
208202 }
209203
204+ // try EventBridge
210205 var eventBridgeEvent awsevents.CloudWatchEvent
211206 if err := json .Unmarshal (event , & eventBridgeEvent ); err == nil && eventBridgeEvent .DetailType != "" {
212207 return nil , EventBridgeHandler (ctx , eventBridgeEvent )
0 commit comments