diff --git a/e2e/env.go b/e2e/env.go index 56f45e8..677aa33 100644 --- a/e2e/env.go +++ b/e2e/env.go @@ -1075,7 +1075,15 @@ func CreateTestDeployment(t *testing.T, env *E2ETestEnv, name, tarballPath strin t.Fatalf("failed to decode response: %v", err) } - return result["id"].(string) + // Try both "id" and "deployment_id" field names + if id, ok := result["deployment_id"].(string); ok { + return id + } + if id, ok := result["id"].(string); ok { + return id + } + t.Fatalf("deployment response missing id field: %+v", result) + return "" } // DeleteDeployment deletes a deployment by ID diff --git a/pkg/gateway/handlers/deployments/domain_handler.go b/pkg/gateway/handlers/deployments/domain_handler.go index a313c41..051aa74 100644 --- a/pkg/gateway/handlers/deployments/domain_handler.go +++ b/pkg/gateway/handlers/deployments/domain_handler.go @@ -32,7 +32,11 @@ func NewDomainHandler(service *DeploymentService, logger *zap.Logger) *DomainHan // HandleAddDomain adds a custom domain to a deployment func (h *DomainHandler) HandleAddDomain(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) + return + } var req struct { DeploymentName string `json:"deployment_name"` @@ -145,7 +149,11 @@ func (h *DomainHandler) HandleAddDomain(w http.ResponseWriter, r *http.Request) // HandleVerifyDomain verifies domain ownership via TXT record func (h *DomainHandler) HandleVerifyDomain(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) + return + } var req struct { Domain string `json:"domain"` @@ -241,7 +249,11 @@ func (h *DomainHandler) HandleVerifyDomain(w http.ResponseWriter, r *http.Reques // HandleListDomains lists all domains for a deployment func (h *DomainHandler) HandleListDomains(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) + return + } deploymentName := r.URL.Query().Get("deployment_name") if deploymentName == "" { @@ -304,7 +316,11 @@ func (h *DomainHandler) HandleListDomains(w http.ResponseWriter, r *http.Request // HandleRemoveDomain removes a custom domain func (h *DomainHandler) HandleRemoveDomain(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) + return + } domain := r.URL.Query().Get("domain") if domain == "" { diff --git a/pkg/gateway/handlers/deployments/list_handler.go b/pkg/gateway/handlers/deployments/list_handler.go index 86d71f1..6e37966 100644 --- a/pkg/gateway/handlers/deployments/list_handler.go +++ b/pkg/gateway/handlers/deployments/list_handler.go @@ -26,7 +26,11 @@ func NewListHandler(service *DeploymentService, logger *zap.Logger) *ListHandler // HandleList lists all deployments for a namespace func (h *ListHandler) HandleList(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) + return + } type deploymentRow struct { ID string `db:"id"` @@ -97,15 +101,29 @@ func (h *ListHandler) HandleList(w http.ResponseWriter, r *http.Request) { // HandleGet gets a specific deployment func (h *ListHandler) HandleGet(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) - name := r.URL.Query().Get("name") - - if name == "" { - http.Error(w, "name query parameter is required", http.StatusBadRequest) + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) return } - deployment, err := h.service.GetDeployment(ctx, namespace, name) + // Support both 'name' and 'id' query parameters + name := r.URL.Query().Get("name") + id := r.URL.Query().Get("id") + + if name == "" && id == "" { + http.Error(w, "name or id query parameter is required", http.StatusBadRequest) + return + } + + var deployment *deployments.Deployment + var err error + + if id != "" { + deployment, err = h.service.GetDeploymentByID(ctx, namespace, id) + } else { + deployment, err = h.service.GetDeployment(ctx, namespace, name) + } if err != nil { if err == deployments.ErrDeploymentNotFound { http.Error(w, "Deployment not found", http.StatusNotFound) @@ -150,21 +168,36 @@ func (h *ListHandler) HandleGet(w http.ResponseWriter, r *http.Request) { // HandleDelete deletes a deployment func (h *ListHandler) HandleDelete(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) - name := r.URL.Query().Get("name") + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) + return + } - if name == "" { - http.Error(w, "name query parameter is required", http.StatusBadRequest) + // Support both 'name' and 'id' query parameters + name := r.URL.Query().Get("name") + id := r.URL.Query().Get("id") + + if name == "" && id == "" { + http.Error(w, "name or id query parameter is required", http.StatusBadRequest) return } h.logger.Info("Deleting deployment", zap.String("namespace", namespace), zap.String("name", name), + zap.String("id", id), ) // Get deployment - deployment, err := h.service.GetDeployment(ctx, namespace, name) + var deployment *deployments.Deployment + var err error + + if id != "" { + deployment, err = h.service.GetDeploymentByID(ctx, namespace, id) + } else { + deployment, err = h.service.GetDeployment(ctx, namespace, name) + } if err != nil { if err == deployments.ErrDeploymentNotFound { http.Error(w, "Deployment not found", http.StatusNotFound) diff --git a/pkg/gateway/handlers/deployments/logs_handler.go b/pkg/gateway/handlers/deployments/logs_handler.go index 50aa639..42f5840 100644 --- a/pkg/gateway/handlers/deployments/logs_handler.go +++ b/pkg/gateway/handlers/deployments/logs_handler.go @@ -32,7 +32,11 @@ func NewLogsHandler(service *DeploymentService, processManager *process.Manager, // HandleLogs streams deployment logs func (h *LogsHandler) HandleLogs(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) + return + } name := r.URL.Query().Get("name") if name == "" { @@ -113,7 +117,11 @@ func (h *LogsHandler) HandleLogs(w http.ResponseWriter, r *http.Request) { // HandleGetEvents gets deployment events func (h *LogsHandler) HandleGetEvents(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) + return + } name := r.URL.Query().Get("name") if name == "" { diff --git a/pkg/gateway/handlers/deployments/nextjs_handler.go b/pkg/gateway/handlers/deployments/nextjs_handler.go index a624228..1cc8382 100644 --- a/pkg/gateway/handlers/deployments/nextjs_handler.go +++ b/pkg/gateway/handlers/deployments/nextjs_handler.go @@ -46,7 +46,11 @@ func NewNextJSHandler( // HandleUpload handles Next.js deployment upload func (h *NextJSHandler) HandleUpload(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) + return + } // Parse multipart form if err := r.ParseMultipartForm(200 << 20); err != nil { // 200MB max diff --git a/pkg/gateway/handlers/deployments/rollback_handler.go b/pkg/gateway/handlers/deployments/rollback_handler.go index 8b44bc8..f752d70 100644 --- a/pkg/gateway/handlers/deployments/rollback_handler.go +++ b/pkg/gateway/handlers/deployments/rollback_handler.go @@ -30,7 +30,11 @@ func NewRollbackHandler(service *DeploymentService, updateHandler *UpdateHandler // HandleRollback handles deployment rollback func (h *RollbackHandler) HandleRollback(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) + return + } var req struct { Name string `json:"name"` @@ -317,7 +321,11 @@ func (h *RollbackHandler) rollbackDynamic(ctx context.Context, current *deployme // HandleListVersions lists all versions of a deployment func (h *RollbackHandler) HandleListVersions(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) + return + } name := r.URL.Query().Get("name") if name == "" { diff --git a/pkg/gateway/handlers/deployments/service.go b/pkg/gateway/handlers/deployments/service.go index 631e57b..2a0d573 100644 --- a/pkg/gateway/handlers/deployments/service.go +++ b/pkg/gateway/handlers/deployments/service.go @@ -169,6 +169,76 @@ func (s *DeploymentService) GetDeployment(ctx context.Context, namespace, name s }, nil } +// GetDeploymentByID retrieves a deployment by namespace and ID +func (s *DeploymentService) GetDeploymentByID(ctx context.Context, namespace, id string) (*deployments.Deployment, error) { + type deploymentRow struct { + ID string `db:"id"` + Namespace string `db:"namespace"` + Name string `db:"name"` + Type string `db:"type"` + Version int `db:"version"` + Status string `db:"status"` + ContentCID string `db:"content_cid"` + BuildCID string `db:"build_cid"` + HomeNodeID string `db:"home_node_id"` + Port int `db:"port"` + Subdomain string `db:"subdomain"` + Environment string `db:"environment"` + MemoryLimitMB int `db:"memory_limit_mb"` + CPULimitPercent int `db:"cpu_limit_percent"` + DiskLimitMB int `db:"disk_limit_mb"` + HealthCheckPath string `db:"health_check_path"` + HealthCheckInterval int `db:"health_check_interval"` + RestartPolicy string `db:"restart_policy"` + MaxRestartCount int `db:"max_restart_count"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` + DeployedBy string `db:"deployed_by"` + } + + var rows []deploymentRow + query := `SELECT * FROM deployments WHERE namespace = ? AND id = ? LIMIT 1` + err := s.db.Query(ctx, &rows, query, namespace, id) + if err != nil { + return nil, fmt.Errorf("failed to query deployment: %w", err) + } + + if len(rows) == 0 { + return nil, deployments.ErrDeploymentNotFound + } + + row := rows[0] + var env map[string]string + if err := json.Unmarshal([]byte(row.Environment), &env); err != nil { + env = make(map[string]string) + } + + return &deployments.Deployment{ + ID: row.ID, + Namespace: row.Namespace, + Name: row.Name, + Type: deployments.DeploymentType(row.Type), + Version: row.Version, + Status: deployments.DeploymentStatus(row.Status), + ContentCID: row.ContentCID, + BuildCID: row.BuildCID, + HomeNodeID: row.HomeNodeID, + Port: row.Port, + Subdomain: row.Subdomain, + Environment: env, + MemoryLimitMB: row.MemoryLimitMB, + CPULimitPercent: row.CPULimitPercent, + DiskLimitMB: row.DiskLimitMB, + HealthCheckPath: row.HealthCheckPath, + HealthCheckInterval: row.HealthCheckInterval, + RestartPolicy: deployments.RestartPolicy(row.RestartPolicy), + MaxRestartCount: row.MaxRestartCount, + CreatedAt: row.CreatedAt, + UpdatedAt: row.UpdatedAt, + DeployedBy: row.DeployedBy, + }, nil +} + // CreateDNSRecords creates DNS records for a deployment func (s *DeploymentService) CreateDNSRecords(ctx context.Context, deployment *deployments.Deployment) error { // Get node IP diff --git a/pkg/gateway/handlers/deployments/static_handler.go b/pkg/gateway/handlers/deployments/static_handler.go index d4d16f2..00f7638 100644 --- a/pkg/gateway/handlers/deployments/static_handler.go +++ b/pkg/gateway/handlers/deployments/static_handler.go @@ -1,6 +1,7 @@ package deployments import ( + "context" "encoding/json" "fmt" "io" @@ -10,11 +11,21 @@ import ( "time" "github.com/DeBrosOfficial/network/pkg/deployments" + "github.com/DeBrosOfficial/network/pkg/gateway/ctxkeys" "github.com/DeBrosOfficial/network/pkg/ipfs" "github.com/google/uuid" "go.uber.org/zap" ) +// getNamespaceFromContext extracts the namespace from the request context +// Returns empty string if namespace is not found +func getNamespaceFromContext(ctx context.Context) string { + if ns, ok := ctx.Value(ctxkeys.NamespaceOverride).(string); ok { + return ns + } + return "" +} + // StaticDeploymentHandler handles static site deployments type StaticDeploymentHandler struct { service *DeploymentService @@ -34,7 +45,13 @@ func NewStaticDeploymentHandler(service *DeploymentService, ipfsClient ipfs.IPFS // HandleUpload handles static site upload and deployment func (h *StaticDeploymentHandler) HandleUpload(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) + + // Get namespace from context (set by auth middleware) + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) + return + } // Parse multipart form if err := r.ParseMultipartForm(100 << 20); err != nil { // 100MB max diff --git a/pkg/gateway/handlers/deployments/update_handler.go b/pkg/gateway/handlers/deployments/update_handler.go index 3f8b421..2bf0c85 100644 --- a/pkg/gateway/handlers/deployments/update_handler.go +++ b/pkg/gateway/handlers/deployments/update_handler.go @@ -46,7 +46,11 @@ func NewUpdateHandler( // HandleUpdate handles deployment updates func (h *UpdateHandler) HandleUpdate(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - namespace := ctx.Value("namespace").(string) + namespace := getNamespaceFromContext(ctx) + if namespace == "" { + http.Error(w, "Namespace not found in context", http.StatusUnauthorized) + return + } // Parse multipart form if err := r.ParseMultipartForm(200 << 20); err != nil { diff --git a/pkg/gateway/middleware.go b/pkg/gateway/middleware.go index 4255af1..3683b66 100644 --- a/pkg/gateway/middleware.go +++ b/pkg/gateway/middleware.go @@ -497,13 +497,15 @@ func (g *Gateway) getDeploymentByDomain(ctx context.Context, domain string) (*de SELECT d.id, d.namespace, d.name, d.type, d.port, d.content_cid, d.status FROM deployments d LEFT JOIN deployment_domains dd ON d.id = dd.deployment_id - WHERE (d.name || '.node-' || d.home_node_id || '.orama.network' = ? + WHERE (d.name || '.' || d.home_node_id || '.orama.network' = ? + OR d.name || '.node-' || d.home_node_id || '.orama.network' = ? + OR d.name || '.orama.network' = ? OR dd.domain = ? AND dd.verification_status = 'verified') AND d.status = 'active' LIMIT 1 ` - result, err := db.Query(internalCtx, query, domain, domain) + result, err := db.Query(internalCtx, query, domain, domain, domain, domain) if err != nil || result.Count == 0 { return nil, err }