// Package domain contains pure domain models with no external dependencies. package domain import "time" // DeploySpec defines a deployment request. type DeploySpec struct { ProjectName string // Project identifier ComponentPath string // Component path within monorepo (e.g., "services/auth-api"), empty for single-app projects Image string // Container image (e.g., zot.threesix.svc:5000/myapp:latest) Domain string // Domain for ingress (e.g., myapp.threesix.ai) Port int // Container port to expose Replicas int // Number of replicas EnvVars map[string]string // Plain environment variables Secrets map[string]string // Secret environment variables (stored in K8s Secret) BasePath string // URL path prefix for Ingress (e.g., "/api/auth", "/"); empty defaults to "/" // SiblingServices maps environment variable names to internal service URLs. // Used for service discovery - each component gets env vars for all sibling services. // Example: {"AUTH_SVC_URL": "http://myproject-auth-svc:8001", "CHAT_SVC_URL": "http://myproject-chat-svc:8002"} SiblingServices map[string]string // ExtraLabels are additional k8s labels applied to the Deployment and Pod template. // Used for Citadel agent routing (citadel.io/environment, citadel.io/service). ExtraLabels map[string]string } // DeploymentName returns the K8s resource name for this deployment. // For monorepo components, it's "{project}-{component}", for single apps it's just "{project}". func (s DeploySpec) DeploymentName() string { if s.ComponentPath == "" { return s.ProjectName } // Extract component name from path (e.g., "services/auth-api" -> "auth-api") parts := splitPath(s.ComponentPath) if len(parts) > 0 { return s.ProjectName + "-" + parts[len(parts)-1] } return s.ProjectName } // splitPath splits a path like "services/auth-api" into ["services", "auth-api"]. func splitPath(path string) []string { var parts []string current := "" for _, c := range path { if c == '/' { if current != "" { parts = append(parts, current) current = "" } } else { current += string(c) } } if current != "" { parts = append(parts, current) } return parts } // DeployStatus represents the current state of a deployment. type DeployStatus struct { ProjectName string ComponentPath string // Component path if this is a component deployment Image string Replicas int ReadyReplicas int URL string Status DeploymentStatus CreatedAt time.Time UpdatedAt time.Time } // ComponentDeployStatus holds deployment status for a component. type ComponentDeployStatus struct { ComponentPath string // e.g., "services/auth-api" ComponentName string // e.g., "auth-api" ComponentType string // e.g., "service", "worker", "app" Image string // Container image Replicas int // Desired replicas ReadyReplicas int // Ready replicas URL string // URL if component has ingress Status DeploymentStatus // Current status } // ProjectDeployStatus holds overall deployment status for a project. type ProjectDeployStatus struct { ProjectName string // Project identifier Components []ComponentDeployStatus // Status of each deployed component OverallURL string // Primary URL (usually app or main service) } // DeploymentStatus represents the state of a deployment. type DeploymentStatus string const ( DeploymentStatusNone DeploymentStatus = "none" // No deployment exists DeploymentStatusPending DeploymentStatus = "pending" // Deployment created, waiting for pods DeploymentStatusRunning DeploymentStatus = "running" // All pods are ready DeploymentStatusFailed DeploymentStatus = "failed" // Pods failed to start DeploymentStatusUpdating DeploymentStatus = "updating" // Rolling update in progress DeploymentStatusUnknown DeploymentStatus = "unknown" // Status could not be determined ) // IsReady returns true if the deployment is fully available. func (s DeploymentStatus) IsReady() bool { return s == DeploymentStatusRunning }