refactor: use shared DTO types and improve HTTP handler consistency
- Use dto.RouteInfo and dto.Attachment via type aliases - Add toAttachmentResponse and toRouteInfoResponse helper functions - Replace http.Error with h.sendError for consistent JSON responses - Add input validation in ListAttachments client method
- commit
- 3e849f349beaaaaa98b7e84ce57deb02be06361b
- author
- brice · · 2026-01-17 11:42 UTC
- parent
- d2abd75
- signature
- unsigned
2 files · +45 -59
M internal/adapters/in/cli/remote/client.go +7 -15
| @@ -12,6 +12,7 @@ import ( | |||
| 12 | 12 | "strings" | |
| 13 | 13 | "time" | |
| 14 | 14 | ||
| 15 | + | "gordon/internal/adapters/dto" | |
| 15 | 16 | "gordon/internal/domain" | |
| 16 | 17 | ) | |
| 17 | 18 | ||
| @@ -119,21 +120,9 @@ func parseResponse(resp *http.Response, target any) error { | |||
| 119 | 120 | ||
| 120 | 121 | // Routes API | |
| 121 | 122 | ||
| 122 | - | type RouteInfo struct { | |
| 123 | - | Domain string `json:"domain"` | |
| 124 | - | Image string `json:"image"` | |
| 125 | - | ContainerID string `json:"container_id"` | |
| 126 | - | ContainerStatus string `json:"container_status"` | |
| 127 | - | Network string `json:"network"` | |
| 128 | - | Attachments []Attachment `json:"attachments"` | |
| 129 | - | } | |
| 130 | - | ||
| 131 | - | type Attachment struct { | |
| 132 | - | Name string `json:"name"` | |
| 133 | - | Image string `json:"image"` | |
| 134 | - | ContainerID string `json:"container_id"` | |
| 135 | - | Status string `json:"status"` | |
| 136 | - | } | |
| 123 | + | // Type aliases for API types using shared DTO package. | |
| 124 | + | type RouteInfo = dto.RouteInfo | |
| 125 | + | type Attachment = dto.Attachment | |
| 137 | 126 | ||
| 138 | 127 | // ListRoutes returns all configured routes. | |
| 139 | 128 | func (c *Client) ListRoutes(ctx context.Context) ([]domain.Route, error) { | |
| @@ -188,6 +177,9 @@ func (c *Client) ListNetworks(ctx context.Context) ([]*domain.NetworkInfo, error | |||
| 188 | 177 | ||
| 189 | 178 | // ListAttachments returns attachments for a domain. | |
| 190 | 179 | func (c *Client) ListAttachments(ctx context.Context, routeDomain string) ([]Attachment, error) { | |
| 180 | + | if routeDomain == "" { | |
| 181 | + | return nil, fmt.Errorf("domain is required") | |
| 182 | + | } | |
| 191 | 183 | path := "/routes/" + url.PathEscape(routeDomain) + "/attachments" | |
| 192 | 184 | resp, err := c.request(ctx, http.MethodGet, path, nil) | |
| 193 | 185 | if err != nil { | |
| @@ -12,6 +12,7 @@ import ( | |||||
| 12 | "strings" | 12 | "strings" | ||
| 13 | "time" | 13 | "time" | ||
| 14 | 14 | ||||
| 15 | + | "gordon/internal/adapters/dto" | |||
| 15 | "gordon/internal/domain" | 16 | "gordon/internal/domain" | ||
| 16 | ) | 17 | ) | ||
| 17 | 18 | ||||
| @@ -119,21 +120,9 @@ func parseResponse(resp *http.Response, target any) error { | |||||
| 119 | 120 | ||||
| 120 | // Routes API | 121 | // Routes API | ||
| 121 | 122 | ||||
| 122 | - | type RouteInfo struct { | 123 | + | // Type aliases for API types using shared DTO package. |
| 123 | - | Domain string `json:"domain"` | 124 | + | type RouteInfo = dto.RouteInfo |
| 124 | - | Image string `json:"image"` | 125 | + | type Attachment = dto.Attachment |
| 125 | - | ContainerID string `json:"container_id"` | |||
| 126 | - | ContainerStatus string `json:"container_status"` | |||
| 127 | - | Network string `json:"network"` | |||
| 128 | - | Attachments []Attachment `json:"attachments"` | |||
| 129 | - | } | |||
| 130 | - | ||||
| 131 | - | type Attachment struct { | |||
| 132 | - | Name string `json:"name"` | |||
| 133 | - | Image string `json:"image"` | |||
| 134 | - | ContainerID string `json:"container_id"` | |||
| 135 | - | Status string `json:"status"` | |||
| 136 | - | } | |||
| 137 | 126 | ||||
| 138 | // ListRoutes returns all configured routes. | 127 | // ListRoutes returns all configured routes. | ||
| 139 | func (c *Client) ListRoutes(ctx context.Context) ([]domain.Route, error) { | 128 | func (c *Client) ListRoutes(ctx context.Context) ([]domain.Route, error) { | ||
| @@ -188,6 +177,9 @@ func (c *Client) ListNetworks(ctx context.Context) ([]*domain.NetworkInfo, error | |||||
| 188 | 177 | ||||
| 189 | // ListAttachments returns attachments for a domain. | 178 | // ListAttachments returns attachments for a domain. | ||
| 190 | func (c *Client) ListAttachments(ctx context.Context, routeDomain string) ([]Attachment, error) { | 179 | func (c *Client) ListAttachments(ctx context.Context, routeDomain string) ([]Attachment, error) { | ||
| 180 | + | if routeDomain == "" { | |||
| 181 | + | return nil, fmt.Errorf("domain is required") | |||
| 182 | + | } | |||
| 191 | path := "/routes/" + url.PathEscape(routeDomain) + "/attachments" | 183 | path := "/routes/" + url.PathEscape(routeDomain) + "/attachments" | ||
| 192 | resp, err := c.request(ctx, http.MethodGet, path, nil) | 184 | resp, err := c.request(ctx, http.MethodGet, path, nil) | ||
| 193 | if err != nil { | 185 | if err != nil { | ||
M internal/adapters/in/http/admin/handler.go +38 -44
| @@ -11,6 +11,7 @@ import ( | |||
| 11 | 11 | ||
| 12 | 12 | "github.com/bnema/zerowrap" | |
| 13 | 13 | ||
| 14 | + | "gordon/internal/adapters/dto" | |
| 14 | 15 | "gordon/internal/boundaries/in" | |
| 15 | 16 | "gordon/internal/boundaries/out" | |
| 16 | 17 | "gordon/internal/domain" | |
| @@ -34,20 +35,34 @@ type Handler struct { | |||
| 34 | 35 | log zerowrap.Logger | |
| 35 | 36 | } | |
| 36 | 37 | ||
| 37 | - | type routeInfoResponse struct { | |
| 38 | - | Domain string `json:"domain"` | |
| 39 | - | Image string `json:"image"` | |
| 40 | - | ContainerID string `json:"container_id"` | |
| 41 | - | ContainerStatus string `json:"container_status"` | |
| 42 | - | Network string `json:"network"` | |
| 43 | - | Attachments []attachmentResponse `json:"attachments"` | |
| 38 | + | // Type aliases for API responses using shared DTO types. | |
| 39 | + | type routeInfoResponse = dto.RouteInfo | |
| 40 | + | type attachmentResponse = dto.Attachment | |
| 41 | + | ||
| 42 | + | // toAttachmentResponse converts a domain.Attachment to a dto.Attachment. | |
| 43 | + | func toAttachmentResponse(a domain.Attachment) dto.Attachment { | |
| 44 | + | return dto.Attachment{ | |
| 45 | + | Name: a.Name, | |
| 46 | + | Image: a.Image, | |
| 47 | + | ContainerID: a.ContainerID, | |
| 48 | + | Status: a.Status, | |
| 49 | + | } | |
| 44 | 50 | } | |
| 45 | 51 | ||
| 46 | - | type attachmentResponse struct { | |
| 47 | - | Name string `json:"name"` | |
| 48 | - | Image string `json:"image"` | |
| 49 | - | ContainerID string `json:"container_id"` | |
| 50 | - | Status string `json:"status"` | |
| 52 | + | // toRouteInfoResponse converts a domain.RouteInfo to a dto.RouteInfo. | |
| 53 | + | func toRouteInfoResponse(r domain.RouteInfo) dto.RouteInfo { | |
| 54 | + | attachments := make([]dto.Attachment, 0, len(r.Attachments)) | |
| 55 | + | for _, a := range r.Attachments { | |
| 56 | + | attachments = append(attachments, toAttachmentResponse(a)) | |
| 57 | + | } | |
| 58 | + | return dto.RouteInfo{ | |
| 59 | + | Domain: r.Domain, | |
| 60 | + | Image: r.Image, | |
| 61 | + | ContainerID: r.ContainerID, | |
| 62 | + | ContainerStatus: r.ContainerStatus, | |
| 63 | + | Network: r.Network, | |
| 64 | + | Attachments: attachments, | |
| 65 | + | } | |
| 51 | 66 | } | |
| 52 | 67 | ||
| 53 | 68 | // NewHandler creates a new admin HTTP handler. | |
| @@ -152,7 +167,7 @@ func (h *Handler) handleRoutes(w http.ResponseWriter, r *http.Request, path stri | |||
| 152 | 167 | case http.MethodDelete: | |
| 153 | 168 | h.handleRoutesDelete(w, r, routeDomain) | |
| 154 | 169 | default: | |
| 155 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| 170 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") | |
| 156 | 171 | } | |
| 157 | 172 | } | |
| 158 | 173 | ||
| @@ -170,23 +185,7 @@ func (h *Handler) handleRoutesGet(w http.ResponseWriter, r *http.Request, routeD | |||
| 170 | 185 | routes := h.containerSvc.ListRoutesWithDetails(ctx) | |
| 171 | 186 | response := make([]routeInfoResponse, 0, len(routes)) | |
| 172 | 187 | for _, route := range routes { | |
| 173 | - | attachments := make([]attachmentResponse, 0, len(route.Attachments)) | |
| 174 | - | for _, attachment := range route.Attachments { | |
| 175 | - | attachments = append(attachments, attachmentResponse{ | |
| 176 | - | Name: attachment.Name, | |
| 177 | - | Image: attachment.Image, | |
| 178 | - | ContainerID: attachment.ContainerID, | |
| 179 | - | Status: attachment.Status, | |
| 180 | - | }) | |
| 181 | - | } | |
| 182 | - | response = append(response, routeInfoResponse{ | |
| 183 | - | Domain: route.Domain, | |
| 184 | - | Image: route.Image, | |
| 185 | - | ContainerID: route.ContainerID, | |
| 186 | - | ContainerStatus: route.ContainerStatus, | |
| 187 | - | Network: route.Network, | |
| 188 | - | Attachments: attachments, | |
| 189 | - | }) | |
| 188 | + | response = append(response, toRouteInfoResponse(route)) | |
| 190 | 189 | } | |
| 191 | 190 | h.sendJSON(w, http.StatusOK, map[string]any{"routes": response}) | |
| 192 | 191 | return | |
| @@ -206,12 +205,7 @@ func (h *Handler) handleRoutesGet(w http.ResponseWriter, r *http.Request, routeD | |||
| 206 | 205 | attachments := h.containerSvc.ListAttachments(ctx, parentDomain) | |
| 207 | 206 | response := make([]attachmentResponse, 0, len(attachments)) | |
| 208 | 207 | for _, attachment := range attachments { | |
| 209 | - | response = append(response, attachmentResponse{ | |
| 210 | - | Name: attachment.Name, | |
| 211 | - | Image: attachment.Image, | |
| 212 | - | ContainerID: attachment.ContainerID, | |
| 213 | - | Status: attachment.Status, | |
| 214 | - | }) | |
| 208 | + | response = append(response, toAttachmentResponse(attachment)) | |
| 215 | 209 | } | |
| 216 | 210 | h.sendJSON(w, http.StatusOK, map[string]any{"attachments": response}) | |
| 217 | 211 | return | |
| @@ -331,7 +325,7 @@ func (h *Handler) handleRoutesDelete(w http.ResponseWriter, r *http.Request, rou | |||
| 331 | 325 | ||
| 332 | 326 | func (h *Handler) handleNetworks(w http.ResponseWriter, r *http.Request) { | |
| 333 | 327 | if r.Method != http.MethodGet { | |
| 334 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| 328 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") | |
| 335 | 329 | return | |
| 336 | 330 | } | |
| 337 | 331 | ||
| @@ -433,7 +427,7 @@ func (h *Handler) handleSecrets(w http.ResponseWriter, r *http.Request, path str | |||
| 433 | 427 | h.sendJSON(w, http.StatusOK, map[string]string{"status": "deleted"}) | |
| 434 | 428 | ||
| 435 | 429 | default: | |
| 436 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| 430 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") | |
| 437 | 431 | } | |
| 438 | 432 | } | |
| 439 | 433 | ||
| @@ -441,7 +435,7 @@ func (h *Handler) handleSecrets(w http.ResponseWriter, r *http.Request, path str | |||
| 441 | 435 | // Returns detailed health status for all routes including HTTP probe results. | |
| 442 | 436 | func (h *Handler) handleHealth(w http.ResponseWriter, r *http.Request) { | |
| 443 | 437 | if r.Method != http.MethodGet { | |
| 444 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| 438 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") | |
| 445 | 439 | return | |
| 446 | 440 | } | |
| 447 | 441 | ||
| @@ -473,7 +467,7 @@ func (h *Handler) handleHealth(w http.ResponseWriter, r *http.Request) { | |||
| 473 | 467 | // handleStatus handles /admin/status endpoint. | |
| 474 | 468 | func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) { | |
| 475 | 469 | if r.Method != http.MethodGet { | |
| 476 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| 470 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") | |
| 477 | 471 | return | |
| 478 | 472 | } | |
| 479 | 473 | ||
| @@ -515,7 +509,7 @@ func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) { | |||
| 515 | 509 | // This reloads configuration from file into memory and triggers container sync. | |
| 516 | 510 | func (h *Handler) handleReload(w http.ResponseWriter, r *http.Request) { | |
| 517 | 511 | if r.Method != http.MethodPost { | |
| 518 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| 512 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") | |
| 519 | 513 | return | |
| 520 | 514 | } | |
| 521 | 515 | ||
| @@ -550,7 +544,7 @@ func (h *Handler) handleReload(w http.ResponseWriter, r *http.Request) { | |||
| 550 | 544 | // handleConfig handles /admin/config endpoint. | |
| 551 | 545 | func (h *Handler) handleConfig(w http.ResponseWriter, r *http.Request) { | |
| 552 | 546 | if r.Method != http.MethodGet { | |
| 553 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| 547 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") | |
| 554 | 548 | return | |
| 555 | 549 | } | |
| 556 | 550 | ||
| @@ -587,7 +581,7 @@ func (h *Handler) handleConfig(w http.ResponseWriter, r *http.Request) { | |||
| 587 | 581 | // POST triggers a deployment for the specified domain. | |
| 588 | 582 | func (h *Handler) handleDeploy(w http.ResponseWriter, r *http.Request, path string) { | |
| 589 | 583 | if r.Method != http.MethodPost { | |
| 590 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| 584 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") | |
| 591 | 585 | return | |
| 592 | 586 | } | |
| 593 | 587 | ||
| @@ -635,7 +629,7 @@ func (h *Handler) handleDeploy(w http.ResponseWriter, r *http.Request, path stri | |||
| 635 | 629 | // GET /admin/logs/:domain - Container logs for a specific domain | |
| 636 | 630 | func (h *Handler) handleLogs(w http.ResponseWriter, r *http.Request, path string) { | |
| 637 | 631 | if r.Method != http.MethodGet { | |
| 638 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| 632 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") | |
| 639 | 633 | return | |
| 640 | 634 | } | |
| 641 | 635 | ||
| @@ -11,6 +11,7 @@ import ( | |||||
| 11 | 11 | ||||
| 12 | "github.com/bnema/zerowrap" | 12 | "github.com/bnema/zerowrap" | ||
| 13 | 13 | ||||
| 14 | + | "gordon/internal/adapters/dto" | |||
| 14 | "gordon/internal/boundaries/in" | 15 | "gordon/internal/boundaries/in" | ||
| 15 | "gordon/internal/boundaries/out" | 16 | "gordon/internal/boundaries/out" | ||
| 16 | "gordon/internal/domain" | 17 | "gordon/internal/domain" | ||
| @@ -34,20 +35,34 @@ type Handler struct { | |||||
| 34 | log zerowrap.Logger | 35 | log zerowrap.Logger | ||
| 35 | } | 36 | } | ||
| 36 | 37 | ||||
| 37 | - | type routeInfoResponse struct { | 38 | + | // Type aliases for API responses using shared DTO types. |
| 38 | - | Domain string `json:"domain"` | 39 | + | type routeInfoResponse = dto.RouteInfo |
| 39 | - | Image string `json:"image"` | 40 | + | type attachmentResponse = dto.Attachment |
| 40 | - | ContainerID string `json:"container_id"` | 41 | + | |
| 41 | - | ContainerStatus string `json:"container_status"` | 42 | + | // toAttachmentResponse converts a domain.Attachment to a dto.Attachment. |
| 42 | - | Network string `json:"network"` | 43 | + | func toAttachmentResponse(a domain.Attachment) dto.Attachment { |
| 43 | - | Attachments []attachmentResponse `json:"attachments"` | 44 | + | return dto.Attachment{ |
| 45 | + | Name: a.Name, | |||
| 46 | + | Image: a.Image, | |||
| 47 | + | ContainerID: a.ContainerID, | |||
| 48 | + | Status: a.Status, | |||
| 49 | + | } | |||
| 44 | } | 50 | } | ||
| 45 | 51 | ||||
| 46 | - | type attachmentResponse struct { | 52 | + | // toRouteInfoResponse converts a domain.RouteInfo to a dto.RouteInfo. |
| 47 | - | Name string `json:"name"` | 53 | + | func toRouteInfoResponse(r domain.RouteInfo) dto.RouteInfo { |
| 48 | - | Image string `json:"image"` | 54 | + | attachments := make([]dto.Attachment, 0, len(r.Attachments)) |
| 49 | - | ContainerID string `json:"container_id"` | 55 | + | for _, a := range r.Attachments { |
| 50 | - | Status string `json:"status"` | 56 | + | attachments = append(attachments, toAttachmentResponse(a)) |
| 57 | + | } | |||
| 58 | + | return dto.RouteInfo{ | |||
| 59 | + | Domain: r.Domain, | |||
| 60 | + | Image: r.Image, | |||
| 61 | + | ContainerID: r.ContainerID, | |||
| 62 | + | ContainerStatus: r.ContainerStatus, | |||
| 63 | + | Network: r.Network, | |||
| 64 | + | Attachments: attachments, | |||
| 65 | + | } | |||
| 51 | } | 66 | } | ||
| 52 | 67 | ||||
| 53 | // NewHandler creates a new admin HTTP handler. | 68 | // NewHandler creates a new admin HTTP handler. | ||
| @@ -152,7 +167,7 @@ func (h *Handler) handleRoutes(w http.ResponseWriter, r *http.Request, path stri | |||||
| 152 | case http.MethodDelete: | 167 | case http.MethodDelete: | ||
| 153 | h.handleRoutesDelete(w, r, routeDomain) | 168 | h.handleRoutesDelete(w, r, routeDomain) | ||
| 154 | default: | 169 | default: | ||
| 155 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | 170 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") |
| 156 | } | 171 | } | ||
| 157 | } | 172 | } | ||
| 158 | 173 | ||||
| @@ -170,23 +185,7 @@ func (h *Handler) handleRoutesGet(w http.ResponseWriter, r *http.Request, routeD | |||||
| 170 | routes := h.containerSvc.ListRoutesWithDetails(ctx) | 185 | routes := h.containerSvc.ListRoutesWithDetails(ctx) | ||
| 171 | response := make([]routeInfoResponse, 0, len(routes)) | 186 | response := make([]routeInfoResponse, 0, len(routes)) | ||
| 172 | for _, route := range routes { | 187 | for _, route := range routes { | ||
| 173 | - | attachments := make([]attachmentResponse, 0, len(route.Attachments)) | 188 | + | response = append(response, toRouteInfoResponse(route)) |
| 174 | - | for _, attachment := range route.Attachments { | |||
| 175 | - | attachments = append(attachments, attachmentResponse{ | |||
| 176 | - | Name: attachment.Name, | |||
| 177 | - | Image: attachment.Image, | |||
| 178 | - | ContainerID: attachment.ContainerID, | |||
| 179 | - | Status: attachment.Status, | |||
| 180 | - | }) | |||
| 181 | - | } | |||
| 182 | - | response = append(response, routeInfoResponse{ | |||
| 183 | - | Domain: route.Domain, | |||
| 184 | - | Image: route.Image, | |||
| 185 | - | ContainerID: route.ContainerID, | |||
| 186 | - | ContainerStatus: route.ContainerStatus, | |||
| 187 | - | Network: route.Network, | |||
| 188 | - | Attachments: attachments, | |||
| 189 | - | }) | |||
| 190 | } | 189 | } | ||
| 191 | h.sendJSON(w, http.StatusOK, map[string]any{"routes": response}) | 190 | h.sendJSON(w, http.StatusOK, map[string]any{"routes": response}) | ||
| 192 | return | 191 | return | ||
| @@ -206,12 +205,7 @@ func (h *Handler) handleRoutesGet(w http.ResponseWriter, r *http.Request, routeD | |||||
| 206 | attachments := h.containerSvc.ListAttachments(ctx, parentDomain) | 205 | attachments := h.containerSvc.ListAttachments(ctx, parentDomain) | ||
| 207 | response := make([]attachmentResponse, 0, len(attachments)) | 206 | response := make([]attachmentResponse, 0, len(attachments)) | ||
| 208 | for _, attachment := range attachments { | 207 | for _, attachment := range attachments { | ||
| 209 | - | response = append(response, attachmentResponse{ | 208 | + | response = append(response, toAttachmentResponse(attachment)) |
| 210 | - | Name: attachment.Name, | |||
| 211 | - | Image: attachment.Image, | |||
| 212 | - | ContainerID: attachment.ContainerID, | |||
| 213 | - | Status: attachment.Status, | |||
| 214 | - | }) | |||
| 215 | } | 209 | } | ||
| 216 | h.sendJSON(w, http.StatusOK, map[string]any{"attachments": response}) | 210 | h.sendJSON(w, http.StatusOK, map[string]any{"attachments": response}) | ||
| 217 | return | 211 | return | ||
| @@ -331,7 +325,7 @@ func (h *Handler) handleRoutesDelete(w http.ResponseWriter, r *http.Request, rou | |||||
| 331 | 325 | ||||
| 332 | func (h *Handler) handleNetworks(w http.ResponseWriter, r *http.Request) { | 326 | func (h *Handler) handleNetworks(w http.ResponseWriter, r *http.Request) { | ||
| 333 | if r.Method != http.MethodGet { | 327 | if r.Method != http.MethodGet { | ||
| 334 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | 328 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") |
| 335 | return | 329 | return | ||
| 336 | } | 330 | } | ||
| 337 | 331 | ||||
| @@ -433,7 +427,7 @@ func (h *Handler) handleSecrets(w http.ResponseWriter, r *http.Request, path str | |||||
| 433 | h.sendJSON(w, http.StatusOK, map[string]string{"status": "deleted"}) | 427 | h.sendJSON(w, http.StatusOK, map[string]string{"status": "deleted"}) | ||
| 434 | 428 | ||||
| 435 | default: | 429 | default: | ||
| 436 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | 430 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") |
| 437 | } | 431 | } | ||
| 438 | } | 432 | } | ||
| 439 | 433 | ||||
| @@ -441,7 +435,7 @@ func (h *Handler) handleSecrets(w http.ResponseWriter, r *http.Request, path str | |||||
| 441 | // Returns detailed health status for all routes including HTTP probe results. | 435 | // Returns detailed health status for all routes including HTTP probe results. | ||
| 442 | func (h *Handler) handleHealth(w http.ResponseWriter, r *http.Request) { | 436 | func (h *Handler) handleHealth(w http.ResponseWriter, r *http.Request) { | ||
| 443 | if r.Method != http.MethodGet { | 437 | if r.Method != http.MethodGet { | ||
| 444 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | 438 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") |
| 445 | return | 439 | return | ||
| 446 | } | 440 | } | ||
| 447 | 441 | ||||
| @@ -473,7 +467,7 @@ func (h *Handler) handleHealth(w http.ResponseWriter, r *http.Request) { | |||||
| 473 | // handleStatus handles /admin/status endpoint. | 467 | // handleStatus handles /admin/status endpoint. | ||
| 474 | func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) { | 468 | func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) { | ||
| 475 | if r.Method != http.MethodGet { | 469 | if r.Method != http.MethodGet { | ||
| 476 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | 470 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") |
| 477 | return | 471 | return | ||
| 478 | } | 472 | } | ||
| 479 | 473 | ||||
| @@ -515,7 +509,7 @@ func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) { | |||||
| 515 | // This reloads configuration from file into memory and triggers container sync. | 509 | // This reloads configuration from file into memory and triggers container sync. | ||
| 516 | func (h *Handler) handleReload(w http.ResponseWriter, r *http.Request) { | 510 | func (h *Handler) handleReload(w http.ResponseWriter, r *http.Request) { | ||
| 517 | if r.Method != http.MethodPost { | 511 | if r.Method != http.MethodPost { | ||
| 518 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | 512 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") |
| 519 | return | 513 | return | ||
| 520 | } | 514 | } | ||
| 521 | 515 | ||||
| @@ -550,7 +544,7 @@ func (h *Handler) handleReload(w http.ResponseWriter, r *http.Request) { | |||||
| 550 | // handleConfig handles /admin/config endpoint. | 544 | // handleConfig handles /admin/config endpoint. | ||
| 551 | func (h *Handler) handleConfig(w http.ResponseWriter, r *http.Request) { | 545 | func (h *Handler) handleConfig(w http.ResponseWriter, r *http.Request) { | ||
| 552 | if r.Method != http.MethodGet { | 546 | if r.Method != http.MethodGet { | ||
| 553 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | 547 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") |
| 554 | return | 548 | return | ||
| 555 | } | 549 | } | ||
| 556 | 550 | ||||
| @@ -587,7 +581,7 @@ func (h *Handler) handleConfig(w http.ResponseWriter, r *http.Request) { | |||||
| 587 | // POST triggers a deployment for the specified domain. | 581 | // POST triggers a deployment for the specified domain. | ||
| 588 | func (h *Handler) handleDeploy(w http.ResponseWriter, r *http.Request, path string) { | 582 | func (h *Handler) handleDeploy(w http.ResponseWriter, r *http.Request, path string) { | ||
| 589 | if r.Method != http.MethodPost { | 583 | if r.Method != http.MethodPost { | ||
| 590 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | 584 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") |
| 591 | return | 585 | return | ||
| 592 | } | 586 | } | ||
| 593 | 587 | ||||
| @@ -635,7 +629,7 @@ func (h *Handler) handleDeploy(w http.ResponseWriter, r *http.Request, path stri | |||||
| 635 | // GET /admin/logs/:domain - Container logs for a specific domain | 629 | // GET /admin/logs/:domain - Container logs for a specific domain | ||
| 636 | func (h *Handler) handleLogs(w http.ResponseWriter, r *http.Request, path string) { | 630 | func (h *Handler) handleLogs(w http.ResponseWriter, r *http.Request, path string) { | ||
| 637 | if r.Method != http.MethodGet { | 631 | if r.Method != http.MethodGet { | ||
| 638 | - | http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | 632 | + | h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") |
| 639 | return | 633 | return | ||
| 640 | } | 634 | } | ||
| 641 | 635 | ||||