Adds the composable monorepo template system that generates project skeletons with pluggable components (service, worker, app-react, app-astro, cli). Key changes: - Monorepo skeleton templates with shared pkg/, scripts/, and git hooks - Component templates (service, worker, app-react, app-astro, cli) with Dockerfiles, CI steps, and component.yaml manifests - Component domain model with validation and dependency resolution - Component handler endpoints for CRUD and composition - Template provider extended with BuildComposableProject and component assembly - Deployer extended with composable project deployment support - Handler timeout constants (TimeoutFastLookup through TimeoutLongRunning) - envutil package for centralized env var reads with defaults - api.DecodeJSON helper for standardized request body decoding - Standardized response helpers (WriteBadRequest, WriteNotFound, etc.) - Replaced fullstack-app cookbook with composable-app cookbook - Hardened handler timeouts, logging, and error responses across all handlers Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
100 lines
3.6 KiB
Go
100 lines
3.6 KiB
Go
// 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)
|
|
}
|
|
|
|
// 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
|
|
}
|