- Add ListPipelines/GetPipeline to CIProvider port with Woodpecker adapter
- Add DNS alias endpoints: GET/POST/DELETE /projects/{id}/domains
- Implement worker executor daemon, build executor, and git operations
- Add build service, worker service, and build audit tracking
- Add worker registry with PostgreSQL adapter and migration
- Add multi-provider code agent interface (Claude Code + OpenCode)
- Add create-and-build combo endpoint
- Update landing-page cookbook to reflect all gaps closed
- Fix tech debt: unified validation, auth scopes, error wrapping, slog patterns
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
119 lines
3.8 KiB
Go
119 lines
3.8 KiB
Go
// Package domain contains pure domain models with no external dependencies.
|
|
// These types represent the core business concepts of the application.
|
|
package domain
|
|
|
|
import "regexp"
|
|
|
|
// ProjectID is a strongly-typed identifier for projects.
|
|
type ProjectID string
|
|
|
|
// Project name/ID constraints.
|
|
const (
|
|
// MaxProjectNameLen is the maximum length for project names (K8s name limit).
|
|
MaxProjectNameLen = 63
|
|
)
|
|
|
|
// projectIDRegex validates project IDs used for referencing existing projects.
|
|
// Allows uppercase, lowercase, digits, dashes, and underscores. Must start with a letter.
|
|
var projectIDRegex = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_-]*$`)
|
|
|
|
// projectNameRegex validates project names for DNS/K8s resource creation.
|
|
// Lowercase only, digits, dashes. Must start with a lowercase letter.
|
|
var projectNameRegex = regexp.MustCompile(`^[a-z][a-z0-9-]*$`)
|
|
|
|
// reservedProjectNames are names that cannot be used for new projects.
|
|
var reservedProjectNames = map[string]bool{
|
|
"www": true, "api": true, "git": true, "ci": true,
|
|
"registry": true, "admin": true, "root": true,
|
|
"rdev": true, "pantheon": true,
|
|
}
|
|
|
|
// ValidateProjectID validates a project ID for referencing existing projects.
|
|
// Allows letters, digits, dashes, underscores. Must start with a letter. Max 63 chars.
|
|
func ValidateProjectID(id string) error {
|
|
if id == "" {
|
|
return ErrInvalidProjectName
|
|
}
|
|
if len(id) > MaxProjectNameLen {
|
|
return ErrInvalidProjectName
|
|
}
|
|
if !projectIDRegex.MatchString(id) {
|
|
return ErrInvalidProjectName
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateProjectName validates a project name for DNS/K8s resource creation.
|
|
// Lowercase letters, digits, dashes only. Must start with a lowercase letter.
|
|
// Max 63 chars. Rejects reserved names.
|
|
func ValidateProjectName(name string) error {
|
|
if name == "" {
|
|
return ErrInvalidProjectName
|
|
}
|
|
if len(name) > MaxProjectNameLen {
|
|
return ErrInvalidProjectName
|
|
}
|
|
if !projectNameRegex.MatchString(name) {
|
|
return ErrInvalidProjectName
|
|
}
|
|
if reservedProjectNames[name] {
|
|
return ErrInvalidProjectName
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Project represents a claudebox project that can execute commands.
|
|
type Project struct {
|
|
ID ProjectID
|
|
Name string
|
|
Description string
|
|
PodName string
|
|
Status ProjectStatus
|
|
Workspace string
|
|
|
|
// AgentProvider specifies which code agent to use for this project.
|
|
// Empty string means use the system default (typically Claude Code).
|
|
AgentProvider AgentProvider
|
|
}
|
|
|
|
// ProjectStatus represents the current state of a project's pod.
|
|
type ProjectStatus string
|
|
|
|
const (
|
|
ProjectStatusRunning ProjectStatus = "running"
|
|
ProjectStatusPending ProjectStatus = "pending"
|
|
ProjectStatusFailed ProjectStatus = "failed"
|
|
ProjectStatusNotFound ProjectStatus = "not_found"
|
|
ProjectStatusUnknown ProjectStatus = "unknown"
|
|
ProjectStatusError ProjectStatus = "error"
|
|
)
|
|
|
|
// IsAvailable returns true if the project can accept commands.
|
|
func (s ProjectStatus) IsAvailable() bool {
|
|
return s == ProjectStatusRunning
|
|
}
|
|
|
|
// IsTerminal returns true if the status is a final state.
|
|
func (s ProjectStatus) IsTerminal() bool {
|
|
return s == ProjectStatusFailed || s == ProjectStatusNotFound
|
|
}
|
|
|
|
// K8s label and annotation constants for project discovery.
|
|
// Pods with these labels are discovered as rdev projects.
|
|
const (
|
|
// LabelProject marks a pod as an rdev project when set to "true".
|
|
LabelProject = "rdev.orchard9.ai/project"
|
|
|
|
// LabelName specifies the project name (used as project ID).
|
|
LabelName = "rdev.orchard9.ai/name"
|
|
|
|
// LabelWorkspace specifies the workspace path inside the pod.
|
|
LabelWorkspace = "rdev.orchard9.ai/workspace"
|
|
|
|
// LabelAgentProvider specifies which code agent to use ("claudecode", "opencode").
|
|
LabelAgentProvider = "rdev.orchard9.ai/agent-provider"
|
|
|
|
// AnnotDescription provides a human-readable description of the project.
|
|
AnnotDescription = "rdev.orchard9.ai/description"
|
|
)
|