detached b4026a4

refactor: Simplify error response handling in deploy process

- Remove `DeploymentError` and `ConflictCheckResponse` structs.
- Use `DeployResponse` for all deployment-related responses.
- Simplify error handling and logging in deployment functions.
- Update `checkDeployConflict` to return `DeployResponse`.
- Refactor `handleDeployResponse` to use `DeployResponse`.
- Add `StatusCode` field to `DeployResponse` for better error reporting.
- Remove redundant error handling and improve error messages.
commit
b4026a4d2d64b71c3d4e3fd1426cd450b780cbd0
author
Brice · · 2024-11-24 10:29 UTC
parent
96d1318
signature
unsigned
7 files · +106 -154
M internal/cli/cmd/deploy.go +15 -35
@@ -6,7 +6,6 @@ import (
66 "bytes"
77 "context"
88 "encoding/json"
9- "errors"
109 "fmt"
1110 "io"
1211 "net/http"
@@ -81,27 +80,27 @@ func deployImage(a *cli.App, reader io.Reader, imageName, port, targetDomain str
8180
8281 log.Info("Deploying image", "image", imageName)
8382 // Check for conflicts first
84- conflictResp, err := checkDeployConflict(a, targetDomain, port)
83+ resp, err := checkDeployConflict(a, targetDomain, port)
8584 if err != nil {
8685 return fmt.Errorf("conflict check failed: %w", err)
8786 }
8887
8988 var shortID string
90- if len(conflictResp.ContainerID) >= 12 {
91- shortID = conflictResp.ContainerID[:12]
89+ if len(resp.ContainerID) >= 12 {
90+ shortID = resp.ContainerID[:12]
9291 } else {
93- shortID = conflictResp.ContainerID // Use full ID if less than 12 chars
92+ shortID = resp.ContainerID // Use full ID if less than 12 chars
9493 }
9594
9695 // If there's a conflict, handle it before proceeding
97- if !conflictResp.Success && conflictResp.ContainerID != "" {
96+ if !resp.Success && resp.ContainerID != "" {
9897 log.Warn("Container already exists",
99- "name", conflictResp.ContainerName,
98+ "name", resp.ContainerName,
10099 "id", shortID,
101- "state", conflictResp.State,
102- "uptime", conflictResp.RunningTime)
100+ "state", resp.State,
101+ "uptime", resp.RunningTime)
103102
104- if err := handler.HandleExistingContainer(a, conflictResp); err != nil {
103+ if err := handler.HandleExistingContainer(a, resp); err != nil {
105104 if strings.Contains(err.Error(), "cancelled by user") {
106105 return fmt.Errorf("deployment cancelled by user")
107106 }
@@ -136,35 +135,17 @@ func deployImage(a *cli.App, reader io.Reader, imageName, port, targetDomain str
136135 ctx, cancel := context.WithTimeout(context.Background(), 60*time.Minute)
137136 defer cancel()
138137
139- resp, err := chunkedClient.SendFileAsChunks(ctx, "/deploy/chunked", headers, imageReader, size, imageName)
138+ chunkResp, err := chunkedClient.SendFileAsChunks(ctx, "/deploy/chunked", headers, imageReader, size, imageName)
140139 if err != nil {
141- var deployErr *common.DeploymentError
142- if errors.As(err, &deployErr) && deployErr.StatusCode == http.StatusConflict {
143- var deployResponse common.DeployResponse
144- if jsonErr := json.Unmarshal([]byte(deployErr.RawResponse), &deployResponse); jsonErr == nil {
145- if deployResponse.ContainerID != "" {
146- log.Warn("Container already exists")
147- // Retry the deployment after handling the conflict
148- if _, err := imageReader.Seek(0, 0); err != nil {
149- return fmt.Errorf("failed to reset image reader: %w", err)
150- }
151- resp, err = chunkedClient.SendFileAsChunks(ctx, "/deploy", headers, imageReader, size, imageName)
152- if err != nil {
153- return fmt.Errorf("deployment failed after container removal: %w", err)
154- }
155- }
156- }
157- } else {
158- return fmt.Errorf("deployment failed: %w", err)
159- }
140+ return fmt.Errorf("failed to deploy image: %w", err)
160141 }
161142
162- if resp == nil {
143+ if chunkResp == nil {
163144 return fmt.Errorf("received nil response from server")
164145 }
165146
166147 var deployResp common.DeployResponse
167- if err := json.Unmarshal(resp.Body, &deployResp); err != nil {
148+ if err := json.Unmarshal(chunkResp.Body, &deployResp); err != nil {
168149 return fmt.Errorf("failed to parse deployment response: %w", err)
169150 }
170151
@@ -224,7 +205,7 @@ func validateDeployInputs(imageName, port, targetDomain string) error {
224205 return handler.ValidateTargetDomain(targetDomain)
225206 }
226207
227-func checkDeployConflict(a *cli.App, targetDomain string, port string) (*common.ConflictCheckResponse, error) {
208+func checkDeployConflict(a *cli.App, targetDomain string, port string) (*common.DeployResponse, error) {
228209
229210 // Create request to check-conflict endpoint with both domain and port parameters
230211 reqUrl := fmt.Sprintf("%s/api/deploy/check-conflict?domain=%s&port=%s",
@@ -248,8 +229,7 @@ func checkDeployConflict(a *cli.App, targetDomain string, port string) (*common.
248229 }
249230 defer resp.Body.Close()
250231
251- // Parse response
252- var conflictResp common.ConflictCheckResponse
232+ var conflictResp common.DeployResponse
253233 if err := json.NewDecoder(resp.Body).Decode(&conflictResp); err != nil {
254234 return nil, fmt.Errorf("failed to parse conflict check response: %w", err)
255235 }
@@ -6,7 +6,6 @@ import (
6 "bytes"6 "bytes"
7 "context"7 "context"
8 "encoding/json"8 "encoding/json"
9- "errors"
10 "fmt"9 "fmt"
11 "io"10 "io"
12 "net/http"11 "net/http"
@@ -81,27 +80,27 @@ func deployImage(a *cli.App, reader io.Reader, imageName, port, targetDomain str
81 80
82 log.Info("Deploying image", "image", imageName)81 log.Info("Deploying image", "image", imageName)
83 // Check for conflicts first82 // Check for conflicts first
84- conflictResp, err := checkDeployConflict(a, targetDomain, port)83+ resp, err := checkDeployConflict(a, targetDomain, port)
85 if err != nil {84 if err != nil {
86 return fmt.Errorf("conflict check failed: %w", err)85 return fmt.Errorf("conflict check failed: %w", err)
87 }86 }
88 87
89 var shortID string88 var shortID string
90- if len(conflictResp.ContainerID) >= 12 {89+ if len(resp.ContainerID) >= 12 {
91- shortID = conflictResp.ContainerID[:12]90+ shortID = resp.ContainerID[:12]
92 } else {91 } else {
93- shortID = conflictResp.ContainerID // Use full ID if less than 12 chars92+ shortID = resp.ContainerID // Use full ID if less than 12 chars
94 }93 }
95 94
96 // If there's a conflict, handle it before proceeding95 // If there's a conflict, handle it before proceeding
97- if !conflictResp.Success && conflictResp.ContainerID != "" {96+ if !resp.Success && resp.ContainerID != "" {
98 log.Warn("Container already exists",97 log.Warn("Container already exists",
99- "name", conflictResp.ContainerName,98+ "name", resp.ContainerName,
100 "id", shortID,99 "id", shortID,
101- "state", conflictResp.State,100+ "state", resp.State,
102- "uptime", conflictResp.RunningTime)101+ "uptime", resp.RunningTime)
103 102
104- if err := handler.HandleExistingContainer(a, conflictResp); err != nil {103+ if err := handler.HandleExistingContainer(a, resp); err != nil {
105 if strings.Contains(err.Error(), "cancelled by user") {104 if strings.Contains(err.Error(), "cancelled by user") {
106 return fmt.Errorf("deployment cancelled by user")105 return fmt.Errorf("deployment cancelled by user")
107 }106 }
@@ -136,35 +135,17 @@ func deployImage(a *cli.App, reader io.Reader, imageName, port, targetDomain str
136 ctx, cancel := context.WithTimeout(context.Background(), 60*time.Minute)135 ctx, cancel := context.WithTimeout(context.Background(), 60*time.Minute)
137 defer cancel()136 defer cancel()
138 137
139- resp, err := chunkedClient.SendFileAsChunks(ctx, "/deploy/chunked", headers, imageReader, size, imageName)138+ chunkResp, err := chunkedClient.SendFileAsChunks(ctx, "/deploy/chunked", headers, imageReader, size, imageName)
140 if err != nil {139 if err != nil {
141- var deployErr *common.DeploymentError140+ return fmt.Errorf("failed to deploy image: %w", err)
142- if errors.As(err, &deployErr) && deployErr.StatusCode == http.StatusConflict {
143- var deployResponse common.DeployResponse
144- if jsonErr := json.Unmarshal([]byte(deployErr.RawResponse), &deployResponse); jsonErr == nil {
145- if deployResponse.ContainerID != "" {
146- log.Warn("Container already exists")
147- // Retry the deployment after handling the conflict
148- if _, err := imageReader.Seek(0, 0); err != nil {
149- return fmt.Errorf("failed to reset image reader: %w", err)
150- }
151- resp, err = chunkedClient.SendFileAsChunks(ctx, "/deploy", headers, imageReader, size, imageName)
152- if err != nil {
153- return fmt.Errorf("deployment failed after container removal: %w", err)
154- }
155- }
156- }
157- } else {
158- return fmt.Errorf("deployment failed: %w", err)
159- }
160 }141 }
161 142
162- if resp == nil {143+ if chunkResp == nil {
163 return fmt.Errorf("received nil response from server")144 return fmt.Errorf("received nil response from server")
164 }145 }
165 146
166 var deployResp common.DeployResponse147 var deployResp common.DeployResponse
167- if err := json.Unmarshal(resp.Body, &deployResp); err != nil {148+ if err := json.Unmarshal(chunkResp.Body, &deployResp); err != nil {
168 return fmt.Errorf("failed to parse deployment response: %w", err)149 return fmt.Errorf("failed to parse deployment response: %w", err)
169 }150 }
170 151
@@ -224,7 +205,7 @@ func validateDeployInputs(imageName, port, targetDomain string) error {
224 return handler.ValidateTargetDomain(targetDomain)205 return handler.ValidateTargetDomain(targetDomain)
225 }206 }
226 207
227-func checkDeployConflict(a *cli.App, targetDomain string, port string) (*common.ConflictCheckResponse, error) {208+func checkDeployConflict(a *cli.App, targetDomain string, port string) (*common.DeployResponse, error) {
228 209
229 // Create request to check-conflict endpoint with both domain and port parameters210 // Create request to check-conflict endpoint with both domain and port parameters
230 reqUrl := fmt.Sprintf("%s/api/deploy/check-conflict?domain=%s&port=%s",211 reqUrl := fmt.Sprintf("%s/api/deploy/check-conflict?domain=%s&port=%s",
@@ -248,8 +229,7 @@ func checkDeployConflict(a *cli.App, targetDomain string, port string) (*common.
248 }229 }
249 defer resp.Body.Close()230 defer resp.Body.Close()
250 231
251- // Parse response232+ var conflictResp common.DeployResponse
252- var conflictResp common.ConflictCheckResponse
253 if err := json.NewDecoder(resp.Body).Decode(&conflictResp); err != nil {233 if err := json.NewDecoder(resp.Body).Decode(&conflictResp); err != nil {
254 return nil, fmt.Errorf("failed to parse conflict check response: %w", err)234 return nil, fmt.Errorf("failed to parse conflict check response: %w", err)
255 }235 }
M internal/cli/handler/chunked_transfer.go +8 -11
@@ -29,6 +29,8 @@ import (
2929 "golang.org/x/exp/rand"
3030 )
3131
32+type DeployResponse = common.DeployResponse
33+
3234 // UploadProgress represents the current state of the upload
3335 type UploadProgress struct {
3436 Completed int
@@ -425,8 +427,7 @@ func handleFinalResponse(resp *Response, responseType string) error {
425427
426428 // handleDeployResponse handles the response from a deploy operation
427429 func handleDeployResponse(resp *Response) error {
428- // If not a conflict, process as normal deploy response
429- var finalResp common.DeployResponse
430+ finalResp := DeployResponse{}
430431 if err := json.Unmarshal(resp.Body, &finalResp); err != nil {
431432 return fmt.Errorf("failed to unmarshal deploy response: %w", err)
432433 }
@@ -527,15 +528,11 @@ func (c *ChunkedClient) attemptChunkSend(ctx context.Context, endpoint string, h
527528
528529 if resp.StatusCode != http.StatusOK {
529530 return &Response{
530- Http: resp,
531- Body: body,
532- StatusCode: resp.StatusCode,
533- Header: resp.Header,
534- }, &common.DeploymentError{
535- StatusCode: resp.StatusCode,
536- Message: "Request failed",
537- RawResponse: string(body),
538- }
531+ Http: resp,
532+ Body: body,
533+ StatusCode: resp.StatusCode,
534+ Header: resp.Header,
535+ }, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
539536 }
540537
541538 return &Response{
@@ -29,6 +29,8 @@ import (
29 "golang.org/x/exp/rand"29 "golang.org/x/exp/rand"
30 )30 )
31 31
32+type DeployResponse = common.DeployResponse
33+
32 // UploadProgress represents the current state of the upload34 // UploadProgress represents the current state of the upload
33 type UploadProgress struct {35 type UploadProgress struct {
34 Completed int36 Completed int
@@ -425,8 +427,7 @@ func handleFinalResponse(resp *Response, responseType string) error {
425 427
426 // handleDeployResponse handles the response from a deploy operation428 // handleDeployResponse handles the response from a deploy operation
427 func handleDeployResponse(resp *Response) error {429 func handleDeployResponse(resp *Response) error {
428- // If not a conflict, process as normal deploy response430+ finalResp := DeployResponse{}
429- var finalResp common.DeployResponse
430 if err := json.Unmarshal(resp.Body, &finalResp); err != nil {431 if err := json.Unmarshal(resp.Body, &finalResp); err != nil {
431 return fmt.Errorf("failed to unmarshal deploy response: %w", err)432 return fmt.Errorf("failed to unmarshal deploy response: %w", err)
432 }433 }
@@ -527,15 +528,11 @@ func (c *ChunkedClient) attemptChunkSend(ctx context.Context, endpoint string, h
527 528
528 if resp.StatusCode != http.StatusOK {529 if resp.StatusCode != http.StatusOK {
529 return &Response{530 return &Response{
530- Http: resp,531+ Http: resp,
531- Body: body,532+ Body: body,
532- StatusCode: resp.StatusCode,533+ StatusCode: resp.StatusCode,
533- Header: resp.Header,534+ Header: resp.Header,
534- }, &common.DeploymentError{535+ }, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
535- StatusCode: resp.StatusCode,
536- Message: "Request failed",
537- RawResponse: string(body),
538- }
539 }536 }
540 537
541 return &Response{538 return &Response{
M internal/cli/handler/deploy_survey.go +1 -1
@@ -13,7 +13,7 @@ import (
1313 "github.com/charmbracelet/log"
1414 )
1515
16-func HandleExistingContainer(app *cli.App, conflictResp *common.ConflictCheckResponse) error {
16+func HandleExistingContainer(app *cli.App, conflictResp *common.DeployResponse) error {
1717 // Add a small delay to ensure terminal is clear
1818 time.Sleep(100 * time.Millisecond)
1919
@@ -13,7 +13,7 @@ import (
13 "github.com/charmbracelet/log"13 "github.com/charmbracelet/log"
14 )14 )
15 15
16-func HandleExistingContainer(app *cli.App, conflictResp *common.ConflictCheckResponse) error {16+func HandleExistingContainer(app *cli.App, conflictResp *common.DeployResponse) error {
17 // Add a small delay to ensure terminal is clear17 // Add a small delay to ensure terminal is clear
18 time.Sleep(100 * time.Millisecond)18 time.Sleep(100 * time.Millisecond)
19 19
M internal/cli/handler/http.go +2 -10
@@ -80,17 +80,9 @@ func SendHTTPRequest(a *cli.App, rp *common.RequestPayload, method string, endpo
8080 reauthenticated = true
8181 continue
8282 }
83- return nil, &common.DeploymentError{
84- StatusCode: resp.StatusCode,
85- Message: errorResp.Message,
86- RawResponse: string(body),
87- }
88- }
89- return nil, &common.DeploymentError{
90- StatusCode: resp.StatusCode,
91- Message: "Unexpected error occurred",
92- RawResponse: string(body),
83+ return nil, fmt.Errorf("failed to send request: %s", errorResp.Message)
9384 }
85+ return nil, fmt.Errorf("failed to send request: %s", string(body))
9486 }
9587
9688 return &Response{Http: resp, Body: body, StatusCode: resp.StatusCode}, nil
@@ -80,17 +80,9 @@ func SendHTTPRequest(a *cli.App, rp *common.RequestPayload, method string, endpo
80 reauthenticated = true80 reauthenticated = true
81 continue81 continue
82 }82 }
83- return nil, &common.DeploymentError{83+ return nil, fmt.Errorf("failed to send request: %s", errorResp.Message)
84- StatusCode: resp.StatusCode,
85- Message: errorResp.Message,
86- RawResponse: string(body),
87- }
88- }
89- return nil, &common.DeploymentError{
90- StatusCode: resp.StatusCode,
91- Message: "Unexpected error occurred",
92- RawResponse: string(body),
93 }84 }
85+ return nil, fmt.Errorf("failed to send request: %s", string(body))
94 }86 }
95 87
96 return &Response{Http: resp, Body: body, StatusCode: resp.StatusCode}, nil88 return &Response{Http: resp, Body: body, StatusCode: resp.StatusCode}, nil
M internal/common/payload.go +7 -26
@@ -21,9 +21,13 @@ type Payload interface {
2121 type DeployResponse struct {
2222 Success bool `json:"success"`
2323 Message string `json:"message"`
24- Domain string `json:"domain"`
25- ContainerID string `json:"container_id"`
26- ContainerName string `json:"container_name"`
24+ StatusCode int `json:"status_code,omitempty"`
25+ Domain string `json:"domain,omitempty"`
26+ ContainerID string `json:"container_id,omitempty"`
27+ ContainerName string `json:"container_name,omitempty"`
28+ State string `json:"state,omitempty"`
29+ RunningTime string `json:"running_time,omitempty"`
30+ Ports string `json:"ports,omitempty"`
2731 }
2832
2933 // PushResponse represents the response structure for push requests
@@ -49,18 +53,6 @@ type StartResponse struct {
4953 Message string `json:"message"`
5054 }
5155
52-// DeploymentError represents an error that occurred during deployment
53-type DeploymentError struct {
54- StatusCode int `json:"status"`
55- Message string `json:"message"`
56- RawResponse string `json:"raw_response"`
57-}
58-
59-// Error implements the error interface for DeploymentError
60-func (e *DeploymentError) Error() string {
61- return fmt.Sprintf("Deployment failed (status %d): %s", e.StatusCode, e.Message)
62-}
63-
6456 // DeployPayload represents the payload for deployment requests
6557 type DeployPayload struct {
6658 Port string `json:"port"`
@@ -86,17 +78,6 @@ type ChunkMetadata struct {
8678 TransferID string `json:"transfer_id"`
8779 }
8880
89-type ConflictCheckResponse struct {
90- Success bool `json:"success"`
91- Message string `json:"message"`
92- ContainerID string `json:"container_id"`
93- ContainerName string `json:"container_name"`
94- Domain string `json:"domain"`
95- State string `json:"state"`
96- RunningTime string `json:"running_time"`
97- Ports string `json:"ports"`
98-}
99-
10081 type ConflictAction struct {
10182 Action string `json:"action"` // "stop", "remove", or "cancel"
10283 Force bool `json:"force"`
@@ -21,9 +21,13 @@ type Payload interface {
21 type DeployResponse struct {21 type DeployResponse struct {
22 Success bool `json:"success"`22 Success bool `json:"success"`
23 Message string `json:"message"`23 Message string `json:"message"`
24- Domain string `json:"domain"`24+ StatusCode int `json:"status_code,omitempty"`
25- ContainerID string `json:"container_id"`25+ Domain string `json:"domain,omitempty"`
26- ContainerName string `json:"container_name"`26+ ContainerID string `json:"container_id,omitempty"`
27+ ContainerName string `json:"container_name,omitempty"`
28+ State string `json:"state,omitempty"`
29+ RunningTime string `json:"running_time,omitempty"`
30+ Ports string `json:"ports,omitempty"`
27 }31 }
28 32
29 // PushResponse represents the response structure for push requests33 // PushResponse represents the response structure for push requests
@@ -49,18 +53,6 @@ type StartResponse struct {
49 Message string `json:"message"`53 Message string `json:"message"`
50 }54 }
51 55
52-// DeploymentError represents an error that occurred during deployment
53-type DeploymentError struct {
54- StatusCode int `json:"status"`
55- Message string `json:"message"`
56- RawResponse string `json:"raw_response"`
57-}
58-
59-// Error implements the error interface for DeploymentError
60-func (e *DeploymentError) Error() string {
61- return fmt.Sprintf("Deployment failed (status %d): %s", e.StatusCode, e.Message)
62-}
63-
64 // DeployPayload represents the payload for deployment requests56 // DeployPayload represents the payload for deployment requests
65 type DeployPayload struct {57 type DeployPayload struct {
66 Port string `json:"port"`58 Port string `json:"port"`
@@ -86,17 +78,6 @@ type ChunkMetadata struct {
86 TransferID string `json:"transfer_id"`78 TransferID string `json:"transfer_id"`
87 }79 }
88 80
89-type ConflictCheckResponse struct {
90- Success bool `json:"success"`
91- Message string `json:"message"`
92- ContainerID string `json:"container_id"`
93- ContainerName string `json:"container_name"`
94- Domain string `json:"domain"`
95- State string `json:"state"`
96- RunningTime string `json:"running_time"`
97- Ports string `json:"ports"`
98-}
99-
100 type ConflictAction struct {81 type ConflictAction struct {
101 Action string `json:"action"` // "stop", "remove", or "cancel"82 Action string `json:"action"` // "stop", "remove", or "cancel"
102 Force bool `json:"force"`83 Force bool `json:"force"`
M internal/httpserve/handlers/endpoint_deploy.go +59 -58
@@ -25,8 +25,9 @@ func PostDeploy(c echo.Context, a *server.App) error {
2525 // Debug
2626 log.Error("Failed to validate and prepare payload", "error", err)
2727 return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{
28- Success: false,
29- Message: fmt.Sprintf("Invalid payload: %v", err),
28+ Success: false,
29+ Message: fmt.Sprintf("Invalid payload: %v", err),
30+ StatusCode: http.StatusBadRequest,
3031 })
3132 }
3233
@@ -35,8 +36,9 @@ func PostDeploy(c echo.Context, a *server.App) error {
3536 // Debug
3637 log.Error("Failed to save and import image", "error", err)
3738 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{
38- Success: false,
39- Message: fmt.Sprintf("Failed to save or import image: %v", err),
39+ Success: false,
40+ Message: fmt.Sprintf("Failed to save or import image: %v", err),
41+ StatusCode: http.StatusInternalServerError,
4042 })
4143 }
4244
@@ -52,8 +54,9 @@ func PostDeploy(c echo.Context, a *server.App) error {
5254 if existingContainerID == "" || existingContainerName == "" {
5355 log.Error("Failed to extract container ID or Name from error message")
5456 return sendJSONResponse(c, http.StatusConflict, DeployResponse{
55- Success: false,
56- Message: "A container for this deploy already exists but could not be identified.",
57+ Success: false,
58+ Message: "A container for this deploy already exists but could not be identified.",
59+ StatusCode: http.StatusConflict,
5760 })
5861 }
5962
@@ -62,30 +65,25 @@ func PostDeploy(c echo.Context, a *server.App) error {
6265 Message: "A container for this deploy already exists.",
6366 ContainerID: existingContainerID,
6467 ContainerName: existingContainerName,
68+ StatusCode: http.StatusConflict,
6569 })
6670 }
6771 log.Error("Failed to create or start container", "error", err)
6872 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{
69- Success: false,
70- Message: fmt.Sprintf("Failed to create or start container: %v", err),
73+ Success: false,
74+ Message: fmt.Sprintf("Failed to create or start container: %v", err),
75+ StatusCode: http.StatusInternalServerError,
7176 })
7277 }
73- response := DeployResponse{
78+
79+ // Arrived here means deployment was successful
80+ return sendJSONResponse(c, http.StatusOK, DeployResponse{
7481 Success: true,
7582 Message: "Deployment successful",
7683 Domain: payload.TargetDomain,
7784 ContainerID: containerID,
7885 ContainerName: containerName,
79- }
80-
81- // Debug: Verify containerID in response
82- if response.ContainerID != containerID {
83- log.Error("Container ID mismatch", "responseID", response.ContainerID, "actualID", containerID)
84- } else {
85- log.Info("Container ID successfully stored in response", "containerID", containerID)
86- }
87-
88- return sendJSONResponse(c, http.StatusOK, response)
86+ })
8987 }
9088
9189 // PostDeployChunked handles the chunked container deployment request
@@ -97,8 +95,9 @@ func handleChunkedDeploy(c echo.Context, a *server.App, metadataStr string) erro
9795 var metadata ChunkMetadata
9896 if err := json.Unmarshal([]byte(metadataStr), &metadata); err != nil {
9997 return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{
100- Success: false,
101- Message: "Invalid chunk metadata",
98+ Success: false,
99+ Message: "Invalid chunk metadata",
100+ StatusCode: http.StatusBadRequest,
102101 })
103102 }
104103
@@ -106,34 +105,42 @@ func handleChunkedDeploy(c echo.Context, a *server.App, metadataStr string) erro
106105 if err != nil {
107106 log.Error("Failed to validate and prepare payload", "error", err)
108107 return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{
109- Success: false,
110- Message: fmt.Sprintf("Invalid payload: %v", err),
108+ Success: false,
109+ Message: fmt.Sprintf("Invalid payload: %v", err),
110+ StatusCode: http.StatusBadRequest,
111111 })
112112 }
113113
114- // Authenticate request
114+ // Auth verification steps
115+
115116 auth := c.Request().Header.Get("Authorization")
116117 if auth == "" {
117118 return sendJSONResponse(c, http.StatusUnauthorized, DeployResponse{
118- Success: false,
119- Message: "Authentication required",
119+ Success: false,
120+ Message: "Authentication required",
121+ StatusCode: http.StatusUnauthorized,
120122 })
121123 }
122124
125+ // Validate the token through GitHub API
123126 token := strings.TrimPrefix(auth, "Bearer ")
124127 if !authToken.IsValidGitHubToken(token) {
125128 return sendJSONResponse(c, http.StatusUnauthorized, DeployResponse{
126- Success: false,
127- Message: "Invalid or expired token",
129+ Success: false,
130+ Message: "Invalid or expired token",
131+ StatusCode: http.StatusUnauthorized,
128132 })
129133 }
130134
135+ // TODO: Here we should probably check that the github user is the same as the one in DB
136+
131137 if metadata.TotalChunks == 1 {
132138 chunkData, err := io.ReadAll(c.Request().Body)
133139 if err != nil {
134140 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{
135- Success: false,
136- Message: "Failed to read chunk data",
141+ Success: false,
142+ Message: "Failed to read chunk data",
143+ StatusCode: http.StatusInternalServerError,
137144 })
138145 }
139146
@@ -159,17 +166,20 @@ func handleChunkedDeploy(c echo.Context, a *server.App, metadataStr string) erro
159166 if err != nil {
160167 chunkStore.mu.Unlock()
161168 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{
162- Success: false,
163- Message: "Failed to read chunk data",
169+ Success: false,
170+ Message: "Failed to read chunk data",
171+ StatusCode: http.StatusInternalServerError,
164172 })
165173 }
166174
167- // Add chunk validation
175+ // Chunks validation steps
176+ //
168177 if len(chunkData) == 0 {
169178 log.Error("Received empty chunk")
170179 return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{
171- Success: false,
172- Message: "Received empty chunk",
180+ Success: false,
181+ Message: "Received empty chunk",
182+ StatusCode: http.StatusBadRequest,
173183 })
174184 }
175185
@@ -182,10 +192,11 @@ func handleChunkedDeploy(c echo.Context, a *server.App, metadataStr string) erro
182192 Success: false,
183193 Message: fmt.Sprintf("Chunk size mismatch: expected %d, got %d",
184194 metadata.ChunkSize, len(chunkData)),
195+ StatusCode: http.StatusBadRequest,
185196 })
186197 }
187198
188- // Store the complete chunk
199+ // Store the complete chunk into our store
189200 chunkStore.chunks[metadata.TransferID][metadata.ChunkNumber] = chunkData
190201 chunkStore.mu.Unlock()
191202
@@ -196,9 +207,10 @@ func handleChunkedDeploy(c echo.Context, a *server.App, metadataStr string) erro
196207
197208 // Include domain information in intermediate chunk responses
198209 return sendJSONResponse(c, http.StatusOK, DeployResponse{
199- Success: true,
200- Message: fmt.Sprintf("Chunk %d/%d received", metadata.ChunkNumber+1, metadata.TotalChunks),
201- Domain: payload.TargetDomain,
210+ Success: true,
211+ Message: fmt.Sprintf("Chunk %d/%d received", metadata.ChunkNumber+1, metadata.TotalChunks),
212+ Domain: payload.TargetDomain,
213+ StatusCode: http.StatusOK,
202214 })
203215 }
204216
@@ -255,12 +267,7 @@ func processCompleteChunkedDeployTansfert(c echo.Context, a *server.App, transfe
255267 log.Warn("Container already exists",
256268 "name", containerName,
257269 "id", existingContainerID)
258- return sendJSONResponse(c, http.StatusConflict, DeployResponse{
259- Success: false,
260- Message: fmt.Sprintf("Container with name '%s' already exists", containerName),
261- ContainerID: existingContainerID,
262- ContainerName: containerName,
263- })
270+ return sendDeployErrorResponse(c, "Container already exists", nil)
264271 }
265272
266273 // Import the image
@@ -274,11 +281,7 @@ func processCompleteChunkedDeployTansfert(c echo.Context, a *server.App, transfe
274281 log.Error("Platform compatibility check failed",
275282 "error", err,
276283 "imageName", imageName)
277- return c.JSON(http.StatusBadRequest, &common.DeploymentError{
278- StatusCode: http.StatusBadRequest,
279- Message: "Platform compatibility error",
280- RawResponse: err.Error(),
281- })
284+ return sendDeployErrorResponse(c, "Platform compatibility check failed", err)
282285 }
283286 return sendDeployErrorResponse(c, "Failed to import image", err)
284287 }
@@ -299,11 +302,7 @@ func processCompleteChunkedDeployTansfert(c echo.Context, a *server.App, transfe
299302 existingContainerName, _ := docker.GetContainerName(existingContainerID)
300303 existingContainerName = strings.TrimPrefix(existingContainerName, "/")
301304
302- return c.JSON(http.StatusBadRequest, &common.DeploymentError{
303- StatusCode: http.StatusBadRequest,
304- Message: fmt.Sprintf("Container with name '%s' already exists", existingContainerName),
305- RawResponse: err.Error(),
306- })
305+ return sendDeployErrorResponse(c, "Container already in use", err)
307306 }
308307
309308 return sendDeployErrorResponse(c, "Failed to create or start container", err)
@@ -327,10 +326,12 @@ func processCompleteChunkedDeployTansfert(c echo.Context, a *server.App, transfe
327326 }
328327
329328 func sendDeployErrorResponse(c echo.Context, message string, err error) error {
330- log.Error(message, "error", err)
329+ // The error becomes the message
330+ errorMsg := fmt.Sprintf("%s: %s", message, err.Error())
331331 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{
332- Success: false,
333- Message: message,
332+ Success: false,
333+ Message: errorMsg,
334+ StatusCode: http.StatusInternalServerError,
334335 })
335336 }
336337
@@ -25,8 +25,9 @@ func PostDeploy(c echo.Context, a *server.App) error {
25 // Debug25 // Debug
26 log.Error("Failed to validate and prepare payload", "error", err)26 log.Error("Failed to validate and prepare payload", "error", err)
27 return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{27 return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{
28- Success: false,28+ Success: false,
29- Message: fmt.Sprintf("Invalid payload: %v", err),29+ Message: fmt.Sprintf("Invalid payload: %v", err),
30+ StatusCode: http.StatusBadRequest,
30 })31 })
31 }32 }
32 33
@@ -35,8 +36,9 @@ func PostDeploy(c echo.Context, a *server.App) error {
35 // Debug36 // Debug
36 log.Error("Failed to save and import image", "error", err)37 log.Error("Failed to save and import image", "error", err)
37 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{38 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{
38- Success: false,39+ Success: false,
39- Message: fmt.Sprintf("Failed to save or import image: %v", err),40+ Message: fmt.Sprintf("Failed to save or import image: %v", err),
41+ StatusCode: http.StatusInternalServerError,
40 })42 })
41 }43 }
42 44
@@ -52,8 +54,9 @@ func PostDeploy(c echo.Context, a *server.App) error {
52 if existingContainerID == "" || existingContainerName == "" {54 if existingContainerID == "" || existingContainerName == "" {
53 log.Error("Failed to extract container ID or Name from error message")55 log.Error("Failed to extract container ID or Name from error message")
54 return sendJSONResponse(c, http.StatusConflict, DeployResponse{56 return sendJSONResponse(c, http.StatusConflict, DeployResponse{
55- Success: false,57+ Success: false,
56- Message: "A container for this deploy already exists but could not be identified.",58+ Message: "A container for this deploy already exists but could not be identified.",
59+ StatusCode: http.StatusConflict,
57 })60 })
58 }61 }
59 62
@@ -62,30 +65,25 @@ func PostDeploy(c echo.Context, a *server.App) error {
62 Message: "A container for this deploy already exists.",65 Message: "A container for this deploy already exists.",
63 ContainerID: existingContainerID,66 ContainerID: existingContainerID,
64 ContainerName: existingContainerName,67 ContainerName: existingContainerName,
68+ StatusCode: http.StatusConflict,
65 })69 })
66 }70 }
67 log.Error("Failed to create or start container", "error", err)71 log.Error("Failed to create or start container", "error", err)
68 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{72 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{
69- Success: false,73+ Success: false,
70- Message: fmt.Sprintf("Failed to create or start container: %v", err),74+ Message: fmt.Sprintf("Failed to create or start container: %v", err),
75+ StatusCode: http.StatusInternalServerError,
71 })76 })
72 }77 }
73- response := DeployResponse{78+
79+ // Arrived here means deployment was successful
80+ return sendJSONResponse(c, http.StatusOK, DeployResponse{
74 Success: true,81 Success: true,
75 Message: "Deployment successful",82 Message: "Deployment successful",
76 Domain: payload.TargetDomain,83 Domain: payload.TargetDomain,
77 ContainerID: containerID,84 ContainerID: containerID,
78 ContainerName: containerName,85 ContainerName: containerName,
79- }86+ })
80-
81- // Debug: Verify containerID in response
82- if response.ContainerID != containerID {
83- log.Error("Container ID mismatch", "responseID", response.ContainerID, "actualID", containerID)
84- } else {
85- log.Info("Container ID successfully stored in response", "containerID", containerID)
86- }
87-
88- return sendJSONResponse(c, http.StatusOK, response)
89 }87 }
90 88
91 // PostDeployChunked handles the chunked container deployment request89 // PostDeployChunked handles the chunked container deployment request
@@ -97,8 +95,9 @@ func handleChunkedDeploy(c echo.Context, a *server.App, metadataStr string) erro
97 var metadata ChunkMetadata95 var metadata ChunkMetadata
98 if err := json.Unmarshal([]byte(metadataStr), &metadata); err != nil {96 if err := json.Unmarshal([]byte(metadataStr), &metadata); err != nil {
99 return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{97 return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{
100- Success: false,98+ Success: false,
101- Message: "Invalid chunk metadata",99+ Message: "Invalid chunk metadata",
100+ StatusCode: http.StatusBadRequest,
102 })101 })
103 }102 }
104 103
@@ -106,34 +105,42 @@ func handleChunkedDeploy(c echo.Context, a *server.App, metadataStr string) erro
106 if err != nil {105 if err != nil {
107 log.Error("Failed to validate and prepare payload", "error", err)106 log.Error("Failed to validate and prepare payload", "error", err)
108 return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{107 return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{
109- Success: false,108+ Success: false,
110- Message: fmt.Sprintf("Invalid payload: %v", err),109+ Message: fmt.Sprintf("Invalid payload: %v", err),
110+ StatusCode: http.StatusBadRequest,
111 })111 })
112 }112 }
113 113
114- // Authenticate request114+ // Auth verification steps
115+
115 auth := c.Request().Header.Get("Authorization")116 auth := c.Request().Header.Get("Authorization")
116 if auth == "" {117 if auth == "" {
117 return sendJSONResponse(c, http.StatusUnauthorized, DeployResponse{118 return sendJSONResponse(c, http.StatusUnauthorized, DeployResponse{
118- Success: false,119+ Success: false,
119- Message: "Authentication required",120+ Message: "Authentication required",
121+ StatusCode: http.StatusUnauthorized,
120 })122 })
121 }123 }
122 124
125+ // Validate the token through GitHub API
123 token := strings.TrimPrefix(auth, "Bearer ")126 token := strings.TrimPrefix(auth, "Bearer ")
124 if !authToken.IsValidGitHubToken(token) {127 if !authToken.IsValidGitHubToken(token) {
125 return sendJSONResponse(c, http.StatusUnauthorized, DeployResponse{128 return sendJSONResponse(c, http.StatusUnauthorized, DeployResponse{
126- Success: false,129+ Success: false,
127- Message: "Invalid or expired token",130+ Message: "Invalid or expired token",
131+ StatusCode: http.StatusUnauthorized,
128 })132 })
129 }133 }
130 134
135+ // TODO: Here we should probably check that the github user is the same as the one in DB
136+
131 if metadata.TotalChunks == 1 {137 if metadata.TotalChunks == 1 {
132 chunkData, err := io.ReadAll(c.Request().Body)138 chunkData, err := io.ReadAll(c.Request().Body)
133 if err != nil {139 if err != nil {
134 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{140 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{
135- Success: false,141+ Success: false,
136- Message: "Failed to read chunk data",142+ Message: "Failed to read chunk data",
143+ StatusCode: http.StatusInternalServerError,
137 })144 })
138 }145 }
139 146
@@ -159,17 +166,20 @@ func handleChunkedDeploy(c echo.Context, a *server.App, metadataStr string) erro
159 if err != nil {166 if err != nil {
160 chunkStore.mu.Unlock()167 chunkStore.mu.Unlock()
161 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{168 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{
162- Success: false,169+ Success: false,
163- Message: "Failed to read chunk data",170+ Message: "Failed to read chunk data",
171+ StatusCode: http.StatusInternalServerError,
164 })172 })
165 }173 }
166 174
167- // Add chunk validation175+ // Chunks validation steps
176+ //
168 if len(chunkData) == 0 {177 if len(chunkData) == 0 {
169 log.Error("Received empty chunk")178 log.Error("Received empty chunk")
170 return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{179 return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{
171- Success: false,180+ Success: false,
172- Message: "Received empty chunk",181+ Message: "Received empty chunk",
182+ StatusCode: http.StatusBadRequest,
173 })183 })
174 }184 }
175 185
@@ -182,10 +192,11 @@ func handleChunkedDeploy(c echo.Context, a *server.App, metadataStr string) erro
182 Success: false,192 Success: false,
183 Message: fmt.Sprintf("Chunk size mismatch: expected %d, got %d",193 Message: fmt.Sprintf("Chunk size mismatch: expected %d, got %d",
184 metadata.ChunkSize, len(chunkData)),194 metadata.ChunkSize, len(chunkData)),
195+ StatusCode: http.StatusBadRequest,
185 })196 })
186 }197 }
187 198
188- // Store the complete chunk199+ // Store the complete chunk into our store
189 chunkStore.chunks[metadata.TransferID][metadata.ChunkNumber] = chunkData200 chunkStore.chunks[metadata.TransferID][metadata.ChunkNumber] = chunkData
190 chunkStore.mu.Unlock()201 chunkStore.mu.Unlock()
191 202
@@ -196,9 +207,10 @@ func handleChunkedDeploy(c echo.Context, a *server.App, metadataStr string) erro
196 207
197 // Include domain information in intermediate chunk responses208 // Include domain information in intermediate chunk responses
198 return sendJSONResponse(c, http.StatusOK, DeployResponse{209 return sendJSONResponse(c, http.StatusOK, DeployResponse{
199- Success: true,210+ Success: true,
200- Message: fmt.Sprintf("Chunk %d/%d received", metadata.ChunkNumber+1, metadata.TotalChunks),211+ Message: fmt.Sprintf("Chunk %d/%d received", metadata.ChunkNumber+1, metadata.TotalChunks),
201- Domain: payload.TargetDomain,212+ Domain: payload.TargetDomain,
213+ StatusCode: http.StatusOK,
202 })214 })
203 }215 }
204 216
@@ -255,12 +267,7 @@ func processCompleteChunkedDeployTansfert(c echo.Context, a *server.App, transfe
255 log.Warn("Container already exists",267 log.Warn("Container already exists",
256 "name", containerName,268 "name", containerName,
257 "id", existingContainerID)269 "id", existingContainerID)
258- return sendJSONResponse(c, http.StatusConflict, DeployResponse{270+ return sendDeployErrorResponse(c, "Container already exists", nil)
259- Success: false,
260- Message: fmt.Sprintf("Container with name '%s' already exists", containerName),
261- ContainerID: existingContainerID,
262- ContainerName: containerName,
263- })
264 }271 }
265 272
266 // Import the image273 // Import the image
@@ -274,11 +281,7 @@ func processCompleteChunkedDeployTansfert(c echo.Context, a *server.App, transfe
274 log.Error("Platform compatibility check failed",281 log.Error("Platform compatibility check failed",
275 "error", err,282 "error", err,
276 "imageName", imageName)283 "imageName", imageName)
277- return c.JSON(http.StatusBadRequest, &common.DeploymentError{284+ return sendDeployErrorResponse(c, "Platform compatibility check failed", err)
278- StatusCode: http.StatusBadRequest,
279- Message: "Platform compatibility error",
280- RawResponse: err.Error(),
281- })
282 }285 }
283 return sendDeployErrorResponse(c, "Failed to import image", err)286 return sendDeployErrorResponse(c, "Failed to import image", err)
284 }287 }
@@ -299,11 +302,7 @@ func processCompleteChunkedDeployTansfert(c echo.Context, a *server.App, transfe
299 existingContainerName, _ := docker.GetContainerName(existingContainerID)302 existingContainerName, _ := docker.GetContainerName(existingContainerID)
300 existingContainerName = strings.TrimPrefix(existingContainerName, "/")303 existingContainerName = strings.TrimPrefix(existingContainerName, "/")
301 304
302- return c.JSON(http.StatusBadRequest, &common.DeploymentError{305+ return sendDeployErrorResponse(c, "Container already in use", err)
303- StatusCode: http.StatusBadRequest,
304- Message: fmt.Sprintf("Container with name '%s' already exists", existingContainerName),
305- RawResponse: err.Error(),
306- })
307 }306 }
308 307
309 return sendDeployErrorResponse(c, "Failed to create or start container", err)308 return sendDeployErrorResponse(c, "Failed to create or start container", err)
@@ -327,10 +326,12 @@ func processCompleteChunkedDeployTansfert(c echo.Context, a *server.App, transfe
327 }326 }
328 327
329 func sendDeployErrorResponse(c echo.Context, message string, err error) error {328 func sendDeployErrorResponse(c echo.Context, message string, err error) error {
330- log.Error(message, "error", err)329+ // The error becomes the message
330+ errorMsg := fmt.Sprintf("%s: %s", message, err.Error())
331 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{331 return sendJSONResponse(c, http.StatusInternalServerError, DeployResponse{
332- Success: false,332+ Success: false,
333- Message: message,333+ Message: errorMsg,
334+ StatusCode: http.StatusInternalServerError,
334 })335 })
335 }336 }
336 337
M internal/httpserve/handlers/endpoint_deploy_check.go +14 -13
@@ -5,21 +5,19 @@ import (
55 "net/http"
66 "strings"
77
8- "github.com/bnema/gordon/internal/common"
98 "github.com/bnema/gordon/internal/server"
109 "github.com/bnema/gordon/pkg/docker"
1110 "github.com/charmbracelet/log"
1211 "github.com/labstack/echo/v4"
1312 )
1413
15-type ConflictCheckResponse = common.ConflictCheckResponse
16-
1714 func CheckDeployConflict(c echo.Context, a *server.App) error {
1815 targetDomain := c.QueryParam("domain")
1916 if targetDomain == "" {
20- return sendJSONResponse(c, http.StatusBadRequest, ConflictCheckResponse{
21- Success: false,
22- Message: "Target domain is required",
17+ return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{
18+ Success: false,
19+ Message: "Target domain is required",
20+ StatusCode: http.StatusBadRequest,
2321 })
2422 }
2523
@@ -28,18 +26,20 @@ func CheckDeployConflict(c echo.Context, a *server.App) error {
2826 containerName := strings.Split(cleanDomain, ".")[0]
2927
3028 if containerName == "" {
31- return sendJSONResponse(c, http.StatusBadRequest, ConflictCheckResponse{
32- Success: false,
33- Message: "Invalid target domain format",
29+ return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{
30+ Success: false,
31+ Message: "Invalid target domain format",
32+ StatusCode: http.StatusBadRequest,
3433 })
3534 }
3635
3736 // Check if container exists
3837 containerID := docker.GetContainerIDByName(containerName)
3938 if containerID == "" {
40- return sendJSONResponse(c, http.StatusOK, ConflictCheckResponse{
41- Success: true,
42- Message: "No conflicts found",
39+ return sendJSONResponse(c, http.StatusOK, DeployResponse{
40+ Success: true,
41+ Message: "No conflicts found",
42+ StatusCode: http.StatusOK,
4343 })
4444 }
4545
@@ -62,7 +62,7 @@ func CheckDeployConflict(c echo.Context, a *server.App) error {
6262 uptime = "unknown"
6363 }
6464
65- return sendJSONResponse(c, http.StatusConflict, ConflictCheckResponse{
65+ return sendJSONResponse(c, http.StatusConflict, DeployResponse{
6666 Success: false,
6767 Message: fmt.Sprintf("Container with name '%s' already exists", containerName),
6868 ContainerID: containerID,
@@ -70,5 +70,6 @@ func CheckDeployConflict(c echo.Context, a *server.App) error {
7070 State: state,
7171 RunningTime: uptime,
7272 Ports: ports,
73+ StatusCode: http.StatusConflict,
7374 })
7475 }
@@ -5,21 +5,19 @@ import (
5 "net/http"5 "net/http"
6 "strings"6 "strings"
7 7
8- "github.com/bnema/gordon/internal/common"
9 "github.com/bnema/gordon/internal/server"8 "github.com/bnema/gordon/internal/server"
10 "github.com/bnema/gordon/pkg/docker"9 "github.com/bnema/gordon/pkg/docker"
11 "github.com/charmbracelet/log"10 "github.com/charmbracelet/log"
12 "github.com/labstack/echo/v4"11 "github.com/labstack/echo/v4"
13 )12 )
14 13
15-type ConflictCheckResponse = common.ConflictCheckResponse
16-
17 func CheckDeployConflict(c echo.Context, a *server.App) error {14 func CheckDeployConflict(c echo.Context, a *server.App) error {
18 targetDomain := c.QueryParam("domain")15 targetDomain := c.QueryParam("domain")
19 if targetDomain == "" {16 if targetDomain == "" {
20- return sendJSONResponse(c, http.StatusBadRequest, ConflictCheckResponse{17+ return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{
21- Success: false,18+ Success: false,
22- Message: "Target domain is required",19+ Message: "Target domain is required",
20+ StatusCode: http.StatusBadRequest,
23 })21 })
24 }22 }
25 23
@@ -28,18 +26,20 @@ func CheckDeployConflict(c echo.Context, a *server.App) error {
28 containerName := strings.Split(cleanDomain, ".")[0]26 containerName := strings.Split(cleanDomain, ".")[0]
29 27
30 if containerName == "" {28 if containerName == "" {
31- return sendJSONResponse(c, http.StatusBadRequest, ConflictCheckResponse{29+ return sendJSONResponse(c, http.StatusBadRequest, DeployResponse{
32- Success: false,30+ Success: false,
33- Message: "Invalid target domain format",31+ Message: "Invalid target domain format",
32+ StatusCode: http.StatusBadRequest,
34 })33 })
35 }34 }
36 35
37 // Check if container exists36 // Check if container exists
38 containerID := docker.GetContainerIDByName(containerName)37 containerID := docker.GetContainerIDByName(containerName)
39 if containerID == "" {38 if containerID == "" {
40- return sendJSONResponse(c, http.StatusOK, ConflictCheckResponse{39+ return sendJSONResponse(c, http.StatusOK, DeployResponse{
41- Success: true,40+ Success: true,
42- Message: "No conflicts found",41+ Message: "No conflicts found",
42+ StatusCode: http.StatusOK,
43 })43 })
44 }44 }
45 45
@@ -62,7 +62,7 @@ func CheckDeployConflict(c echo.Context, a *server.App) error {
62 uptime = "unknown"62 uptime = "unknown"
63 }63 }
64 64
65- return sendJSONResponse(c, http.StatusConflict, ConflictCheckResponse{65+ return sendJSONResponse(c, http.StatusConflict, DeployResponse{
66 Success: false,66 Success: false,
67 Message: fmt.Sprintf("Container with name '%s' already exists", containerName),67 Message: fmt.Sprintf("Container with name '%s' already exists", containerName),
68 ContainerID: containerID,68 ContainerID: containerID,
@@ -70,5 +70,6 @@ func CheckDeployConflict(c echo.Context, a *server.App) error {
70 State: state,70 State: state,
71 RunningTime: uptime,71 RunningTime: uptime,
72 Ports: ports,72 Ports: ports,
73+ StatusCode: http.StatusConflict,
73 })74 })
74 }75 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close