Add proposal approval api endpoint

Change-Id: I3ab864af51735ee7c484df9c66d0b3ec2f6acb36
diff --git a/server/api/api.go b/server/api/api.go
index fd78ee4..ae0af36 100644
--- a/server/api/api.go
+++ b/server/api/api.go
@@ -13,10 +13,10 @@
 const HealthCheckPath = "/healthcheck"
 
 type API struct {
-	Logger  *log.Logger
-	Root    *gin.Engine
-	APIRoot *gin.RouterGroup // 'api/v1'
-	Auth    *gin.RouterGroup // 'api/v1/auth'
+	Logger   *log.Logger
+	Root     *gin.Engine
+	APIRoot  *gin.RouterGroup // 'api/v1'
+	Proposal *gin.RouterGroup // 'api/v1/proposal'
 }
 
 // Init initializes api
@@ -29,6 +29,7 @@
 	})
 	apiObj.APIRoot = router.Group(APIURLSuffix)
 	apiObj.initHealthCheck()
+	apiObj.initProposal()
 
 	apiObj.Root.NoRoute(func(c *gin.Context) {
 		c.JSON(http.StatusNotFound, "Page not found")
diff --git a/server/api/proposal.go b/server/api/proposal.go
new file mode 100644
index 0000000..2adc126
--- /dev/null
+++ b/server/api/proposal.go
@@ -0,0 +1,43 @@
+package api
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+)
+
+func (apiObj *API) initProposal() {
+	apiObj.Proposal = apiObj.APIRoot.Group("/proposal")
+	apiObj.Proposal.POST("/approve", proposalApprovalHandler)
+}
+
+func proposalApprovalHandler(c *gin.Context) {
+	app, err := getApp(c)
+	if err != nil {
+		responseFormat(c, http.StatusInternalServerError, err.Error())
+		return
+	}
+
+	// Read the raw body for signature validation
+	body, err := c.GetRawData()
+	if err != nil {
+		responseFormat(c, http.StatusBadRequest, "Failed to read request body")
+		return
+	}
+
+	// Get the signature from headers
+	signature := c.GetHeader("X-Hub-Signature-256")
+	if signature == "" {
+		// Fallback to the older signature header
+		signature = c.GetHeader("X-Hub-Signature")
+	}
+
+	err = app.ProposalApproval(body, signature)
+	if err != nil {
+		responseFormat(c, http.StatusBadRequest, fmt.Sprintf("Failed to process webhook: %v", err))
+		return
+	}
+
+	responseFormat(c, http.StatusOK, "Proposal approved successfully")
+}