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 {
@@ -58,116 +50,29 @@ func APIGatewayHandler(ctx context.Context, req awsevents.APIGatewayV2HTTPReques
5850 logger .Debug ("received api gateway request" , slog .String ("request" , string (j )))
5951 }
6052
61- path := req .RawPath
62- if appInst .Config .BasePath != "" {
63- path = strings .TrimPrefix (path , appInst .Config .BasePath )
64- if path == "" {
65- path = "/"
66- }
67- }
68-
69- if path == "/server/status" {
70- return handleServerStatus (ctx , req )
71- }
72-
73- if path == "/server/config" {
74- return handleServerConfig (ctx , req )
75- }
76-
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- }
108-
109- return awsevents.APIGatewayV2HTTPResponse {
110- StatusCode : 200 ,
111- Body : "ok" ,
112- }, nil
113- }
114-
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
53+ headers := make (map [string ]string )
54+ for key , value := range req .Headers {
55+ headers [strings .ToLower (key )] = value
13356 }
13457
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
58+ appReq := app.Request {
59+ Type : app .RequestTypeHTTP ,
60+ Method : req .RequestContext .HTTP .Method ,
61+ Path : req .RawPath ,
62+ Headers : headers ,
63+ Body : []byte (req .Body ),
15064 }
15165
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- }
66+ resp := appInst .HandleRequest (ctx , appReq )
16167
16268 return awsevents.APIGatewayV2HTTPResponse {
163- StatusCode : 200 ,
164- Headers : map [ string ] string { "Content-Type" : "application/json" } ,
165- Body : string (body ),
69+ StatusCode : resp . StatusCode ,
70+ Headers : resp . Headers ,
71+ Body : string (resp . Body ),
16672 }, nil
16773}
16874
169- // EventBridgeHandler processes EventBridge scheduled events.
170- // typically handles scheduled Okta group sync operations.
75+ // EventBridgeHandler converts EventBridge events to unified app.Request.
17176func EventBridgeHandler (ctx context.Context , evt awsevents.CloudWatchEvent ) error {
17277 initApp ()
17378 if initErr != nil {
@@ -185,23 +90,33 @@ func EventBridgeHandler(ctx context.Context, evt awsevents.CloudWatchEvent) erro
18590 return err
18691 }
18792
188- return appInst .ProcessScheduledEvent (ctx , detail )
93+ req := app.Request {
94+ Type : app .RequestTypeScheduled ,
95+ ScheduledAction : detail .Action ,
96+ ScheduledData : detail .Data ,
97+ }
98+
99+ resp := appInst .HandleRequest (ctx , req )
100+
101+ if resp .StatusCode >= 400 {
102+ return fmt .Errorf ("scheduled event failed: %s" , string (resp .Body ))
103+ }
104+
105+ return nil
189106}
190107
191- // UniversalHandler detects the event type and routes to the appropriate
192- // handler. supports API Gateway HTTP API (v2.0) and EventBridge events.
108+ // UniversalHandler detects event type and routes to the appropriate handler.
193109func UniversalHandler (ctx context.Context , event json.RawMessage ) (any , error ) {
110+ initApp ()
194111 if initErr != nil {
195112 return nil , initErr
196113 }
197114
198- // try API Gateway HTTP API (v2.0)
199115 var apiGatewayReq awsevents.APIGatewayV2HTTPRequest
200116 if err := json .Unmarshal (event , & apiGatewayReq ); err == nil && apiGatewayReq .RequestContext .HTTP .Method != "" {
201117 return APIGatewayHandler (ctx , apiGatewayReq )
202118 }
203119
204- // try EventBridge
205120 var eventBridgeEvent awsevents.CloudWatchEvent
206121 if err := json .Unmarshal (event , & eventBridgeEvent ); err == nil && eventBridgeEvent .DetailType != "" {
207122 return nil , EventBridgeHandler (ctx , eventBridgeEvent )
0 commit comments