1- // Package main provides the AWS Lambda entry point for the GitHub bot.
2- // This Lambda handler supports API Gateway HTTP API (v2.0) and EventBridge
3- // (scheduled sync) events.
41package main
52
63import (
@@ -15,7 +12,6 @@ import (
1512 "github.com/aws/aws-lambda-go/lambda"
1613 "github.com/cruxstack/github-ops-app/internal/app"
1714 "github.com/cruxstack/github-ops-app/internal/config"
18- "github.com/cruxstack/github-ops-app/internal/github"
1915)
2016
2117var (
2521 initErr error
2622)
2723
28- // initApp initializes the application instance once using sync.Once.
29- // stores any initialization error in the initErr global variable.
3024func initApp () {
3125 initOnce .Do (func () {
3226 logger = config .NewLogger ()
@@ -40,9 +34,7 @@ func initApp() {
4034 })
4135}
4236
43- // APIGatewayHandler processes incoming API Gateway HTTP API (v2.0) requests.
44- // handles GitHub webhook events, status checks, and config endpoints.
45- // validates webhook signatures before processing events.
37+ // APIGatewayHandler converts API Gateway requests to unified app.Request.
4638func APIGatewayHandler (ctx context.Context , req awsevents.APIGatewayV2HTTPRequest ) (awsevents.APIGatewayV2HTTPResponse , error ) {
4739 initApp ()
4840 if initErr != nil {
@@ -66,108 +58,29 @@ func APIGatewayHandler(ctx context.Context, req awsevents.APIGatewayV2HTTPReques
6658 }
6759 }
6860
69- if path == "/server/status" {
70- return handleServerStatus (ctx , req )
61+ headers := make (map [string ]string )
62+ for key , value := range req .Headers {
63+ headers [strings .ToLower (key )] = value
7164 }
7265
73- if path == "/server/config" {
74- return handleServerConfig (ctx , req )
66+ appReq := app.Request {
67+ Type : app .RequestTypeHTTP ,
68+ Method : req .RequestContext .HTTP .Method ,
69+ Path : path ,
70+ Headers : headers ,
71+ Body : []byte (req .Body ),
7572 }
7673
77- if req .RequestContext .HTTP .Method != "POST" {
78- return awsevents.APIGatewayV2HTTPResponse {
79- StatusCode : 405 ,
80- Body : "method not allowed" ,
81- }, nil
82- }
83-
84- eventType := req .Headers ["x-github-event" ]
85- signature := req .Headers ["x-hub-signature-256" ]
86-
87- if err := github .ValidateWebhookSignature (
88- []byte (req .Body ),
89- signature ,
90- appInst .Config .GitHubWebhookSecret ,
91- ); err != nil {
92- logger .Warn ("webhook signature validation failed" , slog .String ("error" , err .Error ()))
93- return awsevents.APIGatewayV2HTTPResponse {
94- StatusCode : 401 ,
95- Body : "unauthorized" ,
96- }, nil
97- }
98-
99- if err := appInst .ProcessWebhook (ctx , []byte (req .Body ), eventType ); err != nil {
100- logger .Error ("webhook processing failed" ,
101- slog .String ("event_type" , eventType ),
102- slog .String ("error" , err .Error ()))
103- return awsevents.APIGatewayV2HTTPResponse {
104- StatusCode : 500 ,
105- Body : "webhook processing failed" ,
106- }, nil
107- }
74+ resp := appInst .HandleRequest (ctx , appReq )
10875
10976 return awsevents.APIGatewayV2HTTPResponse {
110- StatusCode : 200 ,
111- Body : "ok" ,
77+ StatusCode : resp .StatusCode ,
78+ Headers : resp .Headers ,
79+ Body : string (resp .Body ),
11280 }, nil
11381}
11482
115- // handleServerStatus returns the application status and feature flags.
116- // responds with JSON containing configuration state and enabled features.
117- func handleServerStatus (ctx context.Context , req awsevents.APIGatewayV2HTTPRequest ) (awsevents.APIGatewayV2HTTPResponse , error ) {
118- if req .RequestContext .HTTP .Method != "GET" {
119- return awsevents.APIGatewayV2HTTPResponse {
120- StatusCode : 405 ,
121- Body : "method not allowed" ,
122- }, nil
123- }
124-
125- status := appInst .GetStatus ()
126- body , err := json .Marshal (status )
127- if err != nil {
128- logger .Error ("failed to marshal status response" , slog .String ("error" , err .Error ()))
129- return awsevents.APIGatewayV2HTTPResponse {
130- StatusCode : 500 ,
131- Body : "failed to generate status response" ,
132- }, nil
133- }
134-
135- return awsevents.APIGatewayV2HTTPResponse {
136- StatusCode : 200 ,
137- Headers : map [string ]string {"Content-Type" : "application/json" },
138- Body : string (body ),
139- }, nil
140- }
141-
142- // handleServerConfig returns the application configuration with secrets
143- // redacted. useful for debugging and verifying environment settings.
144- func handleServerConfig (ctx context.Context , req awsevents.APIGatewayV2HTTPRequest ) (awsevents.APIGatewayV2HTTPResponse , error ) {
145- if req .RequestContext .HTTP .Method != "GET" {
146- return awsevents.APIGatewayV2HTTPResponse {
147- StatusCode : 405 ,
148- Body : "method not allowed" ,
149- }, nil
150- }
151-
152- redacted := appInst .Config .Redacted ()
153- body , err := json .Marshal (redacted )
154- if err != nil {
155- logger .Error ("failed to marshal config response" , slog .String ("error" , err .Error ()))
156- return awsevents.APIGatewayV2HTTPResponse {
157- StatusCode : 500 ,
158- Body : "failed to generate config response" ,
159- }, nil
160- }
161-
162- return awsevents.APIGatewayV2HTTPResponse {
163- StatusCode : 200 ,
164- Headers : map [string ]string {"Content-Type" : "application/json" },
165- Body : string (body ),
166- }, nil
167- }
168-
169- // EventBridgeHandler processes EventBridge scheduled events.
170- // typically handles scheduled Okta group sync operations.
83+ // EventBridgeHandler converts EventBridge events to unified app.Request.
17184func EventBridgeHandler (ctx context.Context , evt awsevents.CloudWatchEvent ) error {
17285 initApp ()
17386 if initErr != nil {
@@ -185,23 +98,33 @@ func EventBridgeHandler(ctx context.Context, evt awsevents.CloudWatchEvent) erro
18598 return err
18699 }
187100
188- return appInst .ProcessScheduledEvent (ctx , detail )
101+ req := app.Request {
102+ Type : app .RequestTypeScheduled ,
103+ ScheduledAction : detail .Action ,
104+ ScheduledData : detail .Data ,
105+ }
106+
107+ resp := appInst .HandleRequest (ctx , req )
108+
109+ if resp .StatusCode >= 400 {
110+ return fmt .Errorf ("scheduled event failed: %s" , string (resp .Body ))
111+ }
112+
113+ return nil
189114}
190115
191- // UniversalHandler detects the event type and routes to the appropriate
192- // handler. supports API Gateway HTTP API (v2.0) and EventBridge events.
116+ // UniversalHandler detects event type and routes to the appropriate handler.
193117func UniversalHandler (ctx context.Context , event json.RawMessage ) (any , error ) {
118+ initApp ()
194119 if initErr != nil {
195120 return nil , initErr
196121 }
197122
198- // try API Gateway HTTP API (v2.0)
199123 var apiGatewayReq awsevents.APIGatewayV2HTTPRequest
200124 if err := json .Unmarshal (event , & apiGatewayReq ); err == nil && apiGatewayReq .RequestContext .HTTP .Method != "" {
201125 return APIGatewayHandler (ctx , apiGatewayReq )
202126 }
203127
204- // try EventBridge
205128 var eventBridgeEvent awsevents.CloudWatchEvent
206129 if err := json .Unmarshal (event , & eventBridgeEvent ); err == nil && eventBridgeEvent .DetailType != "" {
207130 return nil , EventBridgeHandler (ctx , eventBridgeEvent )
0 commit comments