deployments.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. package handlers
  2. import (
  3. "fmt"
  4. "net/http"
  5. "git.linuxforward.com/byop/byop-engine/dbstore"
  6. "git.linuxforward.com/byop/byop-engine/models"
  7. "github.com/gin-gonic/gin"
  8. )
  9. // DeploymentHandler handles deployment-related operations and contains integrated service logic
  10. type DeploymentHandler struct {
  11. store *dbstore.SQLiteStore
  12. }
  13. // NewDeploymentHandler creates a new DeploymentHandler
  14. func NewDeploymentHandler(store *dbstore.SQLiteStore) *DeploymentHandler {
  15. return &DeploymentHandler{
  16. store: store,
  17. }
  18. }
  19. // ListDeployments returns all deployments with optional filtering
  20. func (h *DeploymentHandler) ListDeployments(c *gin.Context) {
  21. filter := make(map[string]interface{})
  22. ctx := c.Request.Context()
  23. // Attempt to bind query parameters, but allow empty filters
  24. if err := c.ShouldBindQuery(&filter); err != nil && len(filter) > 0 {
  25. appErr := models.NewErrValidation("invalid_query_params", nil, err)
  26. models.RespondWithError(c, appErr)
  27. return
  28. }
  29. deployments, err := h.store.GetAllDeployments(ctx)
  30. if err != nil {
  31. appErr := models.NewErrInternalServer("failed_list_deployments", fmt.Errorf("Failed to list deployments: %w", err))
  32. models.RespondWithError(c, appErr)
  33. return
  34. }
  35. // If empty, return an empty list
  36. if len(deployments) == 0 {
  37. c.JSON(http.StatusOK, []models.Deployment{})
  38. return
  39. }
  40. c.JSON(http.StatusOK, deployments)
  41. }
  42. // CreateDeployment creates a new deployment
  43. func (h *DeploymentHandler) CreateDeployment(c *gin.Context) {
  44. var deployment models.Deployment
  45. ctx := c.Request.Context()
  46. if err := c.ShouldBindJSON(&deployment); err != nil {
  47. appErr := models.NewErrValidation("invalid_request_body", nil, err)
  48. models.RespondWithError(c, appErr)
  49. return
  50. }
  51. // Basic validation
  52. validationErrors := make(map[string]string)
  53. if deployment.AppID == 0 {
  54. validationErrors["app_id"] = "App ID is required"
  55. }
  56. if deployment.Environment == "" {
  57. validationErrors["environment"] = "Environment is required"
  58. }
  59. if len(validationErrors) > 0 {
  60. appErr := models.NewErrValidation("deployment_validation_failed", validationErrors, nil)
  61. models.RespondWithError(c, appErr)
  62. return
  63. }
  64. // Set default status
  65. if deployment.Status == "" {
  66. deployment.Status = "pending"
  67. }
  68. // TODO: Add complex deployment logic with cloud providers
  69. err := h.store.CreateDeployment(ctx, &deployment)
  70. if err != nil {
  71. appErr := models.NewErrInternalServer("failed_create_deployment", fmt.Errorf("Failed to create deployment: %w", err))
  72. models.RespondWithError(c, appErr)
  73. return
  74. }
  75. // GORM automatically sets the ID after creation
  76. c.JSON(http.StatusCreated, deployment)
  77. }
  78. // GetDeployment returns a specific deployment
  79. func (h *DeploymentHandler) GetDeployment(c *gin.Context) {
  80. ctx := c.Request.Context()
  81. id, err := parseUintID(c, "id")
  82. if err != nil {
  83. models.RespondWithError(c, err)
  84. return
  85. }
  86. deployment, err := h.store.GetDeploymentByID(ctx, id)
  87. if err != nil {
  88. models.RespondWithError(c, err)
  89. return
  90. }
  91. c.JSON(http.StatusOK, deployment)
  92. }
  93. // UpdateDeployment updates a deployment
  94. func (h *DeploymentHandler) UpdateDeployment(c *gin.Context) {
  95. ctx := c.Request.Context()
  96. id, err := parseUintID(c, "id")
  97. if err != nil {
  98. models.RespondWithError(c, err)
  99. return
  100. }
  101. var updatedDeployment models.Deployment
  102. if err := c.ShouldBindJSON(&updatedDeployment); err != nil {
  103. appErr := models.NewErrValidation("invalid_request_body", nil, err)
  104. models.RespondWithError(c, appErr)
  105. return
  106. }
  107. // Ensure the ID matches the URL parameter
  108. updatedDeployment.ID = id
  109. // Basic validation for update
  110. validationErrors := make(map[string]string)
  111. if updatedDeployment.AppID == 0 {
  112. validationErrors["app_id"] = "App ID is required"
  113. }
  114. if updatedDeployment.Environment == "" {
  115. validationErrors["environment"] = "Environment is required"
  116. }
  117. if len(validationErrors) > 0 {
  118. appErr := models.NewErrValidation("deployment_update_validation_failed", validationErrors, nil)
  119. models.RespondWithError(c, appErr)
  120. return
  121. }
  122. if err := h.store.UpdateDeployment(ctx, &updatedDeployment); err != nil {
  123. models.RespondWithError(c, err)
  124. return
  125. }
  126. c.JSON(http.StatusOK, updatedDeployment)
  127. }
  128. // DeleteDeployment deletes a deployment
  129. func (h *DeploymentHandler) DeleteDeployment(c *gin.Context) {
  130. ctx := c.Request.Context()
  131. id, err := parseUintID(c, "id")
  132. if err != nil {
  133. models.RespondWithError(c, err)
  134. return
  135. }
  136. if err := h.store.DeleteDeployment(ctx, id); err != nil {
  137. models.RespondWithError(c, err)
  138. return
  139. }
  140. c.JSON(http.StatusOK, gin.H{"message": "Deployment deleted successfully"})
  141. }
  142. // UpdateDeploymentStatus updates the status of a deployment
  143. func (h *DeploymentHandler) UpdateDeploymentStatus(c *gin.Context) {
  144. ctx := c.Request.Context()
  145. id, err := parseUintID(c, "id")
  146. if err != nil {
  147. models.RespondWithError(c, err)
  148. return
  149. }
  150. var statusUpdate struct {
  151. Status string `json:"status" binding:"required"`
  152. }
  153. if err := c.ShouldBindJSON(&statusUpdate); err != nil {
  154. appErr := models.NewErrValidation("invalid_status_update_body", nil, err)
  155. models.RespondWithError(c, appErr)
  156. return
  157. }
  158. // Get current deployment
  159. deployment, err := h.store.GetDeploymentByID(ctx, id)
  160. if err != nil {
  161. models.RespondWithError(c, err)
  162. return
  163. }
  164. // Update the status
  165. deployment.Status = statusUpdate.Status
  166. if err := h.store.UpdateDeployment(ctx, deployment); err != nil {
  167. if _, ok := err.(models.CustomError); !ok {
  168. err = models.NewErrInternalServer("failed_update_deployment_status", fmt.Errorf("Failed to update deployment status: %w", err))
  169. }
  170. models.RespondWithError(c, err)
  171. return
  172. }
  173. c.Status(http.StatusOK)
  174. }
  175. // GetDeploymentsByClient returns all deployments for a specific client
  176. func (h *DeploymentHandler) GetDeploymentsByClient(c *gin.Context) {
  177. ctx := c.Request.Context()
  178. clientID, err := parseUintID(c, "clientId")
  179. if err != nil {
  180. models.RespondWithError(c, err)
  181. return
  182. }
  183. deployments, err := h.store.GetDeploymentsByClientID(ctx, clientID)
  184. if err != nil {
  185. if _, ok := err.(models.CustomError); !ok {
  186. err = models.NewErrInternalServer("failed_fetch_deployments_by_client", fmt.Errorf("Failed to fetch deployments for client %d: %w", clientID, err))
  187. }
  188. models.RespondWithError(c, err)
  189. return
  190. }
  191. c.JSON(http.StatusOK, deployments)
  192. }
  193. // GetDeploymentsByApp returns all deployments for a specific app
  194. func (h *DeploymentHandler) GetDeploymentsByApp(c *gin.Context) {
  195. ctx := c.Request.Context()
  196. appID, err := parseUintID(c, "appId")
  197. if err != nil {
  198. models.RespondWithError(c, err)
  199. return
  200. }
  201. deployments, err := h.store.GetDeploymentsByAppID(ctx, appID)
  202. if err != nil {
  203. if _, ok := err.(models.CustomError); !ok {
  204. err = models.NewErrInternalServer("failed_fetch_deployments_by_app", fmt.Errorf("Failed to fetch deployments for app %d: %w", appID, err))
  205. }
  206. models.RespondWithError(c, err)
  207. return
  208. }
  209. c.JSON(http.StatusOK, deployments)
  210. }
  211. // GetDeploymentsByUser returns all deployments created by a specific user (via their apps)
  212. func (h *DeploymentHandler) GetDeploymentsByUser(c *gin.Context) {
  213. ctx := c.Request.Context()
  214. userID, err := parseUintID(c, "userId")
  215. if err != nil {
  216. models.RespondWithError(c, err)
  217. return
  218. }
  219. deployments, err := h.store.GetDeploymentsByUserID(ctx, userID)
  220. if err != nil {
  221. if _, ok := err.(models.CustomError); !ok {
  222. err = models.NewErrInternalServer("failed_fetch_deployments_by_user", fmt.Errorf("Failed to fetch deployments for user %d: %w", userID, err))
  223. }
  224. models.RespondWithError(c, err)
  225. return
  226. }
  227. c.JSON(http.StatusOK, deployments)
  228. }