From ff97f14229a0284407b297d5158b44c171bb9a32 Mon Sep 17 00:00:00 2001 From: Paul Payne Date: Sat, 11 Oct 2025 22:20:26 +0000 Subject: [PATCH] Get templates from embedded package. --- README.md | 157 +-------------------------- internal/api/v1/handlers.go | 41 ++++--- internal/api/v1/handlers_services.go | 24 ++-- internal/node/node.go | 52 ++++----- internal/services/services.go | 137 +++++++++++++++-------- main.go | 21 ++-- 6 files changed, 160 insertions(+), 272 deletions(-) diff --git a/README.md b/README.md index 03c686e..6b28ceb 100644 --- a/README.md +++ b/README.md @@ -10,159 +10,4 @@ make dev ## Usage -### Batch Configuration Update Endpoint - -#### Overview - -The batch configuration update endpoint allows updating multiple configuration values in a single atomic request. - -#### Endpoint - -``` -PATCH /api/v1/instances/{name}/config -``` - -#### Request Format - -```json -{ - "updates": [ - {"path": "string", "value": "any"}, - {"path": "string", "value": "any"} - ] -} -``` - -#### Response Format - -Success (200 OK): -```json -{ - "message": "Configuration updated successfully", - "updated": 3 -} -``` - -Error (400 Bad Request / 404 Not Found / 500 Internal Server Error): -```json -{ - "error": "error message" -} -``` - -#### Usage Examples - -##### Example 1: Update Basic Configuration Values - -```bash -curl -X PATCH http://localhost:8080/api/v1/instances/my-cloud/config \ - -H "Content-Type: application/json" \ - -d '{ - "updates": [ - {"path": "baseDomain", "value": "example.com"}, - {"path": "domain", "value": "wild.example.com"}, - {"path": "internalDomain", "value": "int.wild.example.com"} - ] - }' -``` - -Response: -```json -{ - "message": "Configuration updated successfully", - "updated": 3 -} -``` - -##### Example 2: Update Nested Configuration Values - -```bash -curl -X PATCH http://localhost:8080/api/v1/instances/my-cloud/config \ - -H "Content-Type: application/json" \ - -d '{ - "updates": [ - {"path": "cluster.name", "value": "prod-cluster"}, - {"path": "cluster.loadBalancerIp", "value": "192.168.1.100"}, - {"path": "cluster.ipAddressPool", "value": "192.168.1.100-192.168.1.200"} - ] - }' -``` - -##### Example 3: Update Array Values - -```bash -curl -X PATCH http://localhost:8080/api/v1/instances/my-cloud/config \ - -H "Content-Type: application/json" \ - -d '{ - "updates": [ - {"path": "cluster.nodes.activeNodes[0]", "value": "node-1"}, - {"path": "cluster.nodes.activeNodes[1]", "value": "node-2"} - ] - }' -``` - -##### Example 4: Error Handling - Invalid Instance - -```bash -curl -X PATCH http://localhost:8080/api/v1/instances/nonexistent/config \ - -H "Content-Type: application/json" \ - -d '{ - "updates": [ - {"path": "baseDomain", "value": "example.com"} - ] - }' -``` - -Response (404): -```json -{ - "error": "Instance not found: instance nonexistent does not exist" -} -``` - -##### Example 5: Error Handling - Empty Updates - -```bash -curl -X PATCH http://localhost:8080/api/v1/instances/my-cloud/config \ - -H "Content-Type: application/json" \ - -d '{ - "updates": [] - }' -``` - -Response (400): -```json -{ - "error": "updates array is required and cannot be empty" -} -``` - -##### Example 6: Error Handling - Missing Path - -```bash -curl -X PATCH http://localhost:8080/api/v1/instances/my-cloud/config \ - -H "Content-Type: application/json" \ - -d '{ - "updates": [ - {"path": "", "value": "example.com"} - ] - }' -``` - -Response (400): -```json -{ - "error": "update[0]: path is required" -} -``` - -#### Configuration Path Syntax - -The `path` field uses YAML path syntax as implemented by the `yq` tool: - -- Simple fields: `baseDomain` -- Nested fields: `cluster.name` -- Array elements: `cluster.nodes.activeNodes[0]` -- Array append: `cluster.nodes.activeNodes[+]` - -Refer to the yq documentation for advanced path syntax. +TBD diff --git a/internal/api/v1/handlers.go b/internal/api/v1/handlers.go index 2b99613..ca989e2 100644 --- a/internal/api/v1/handlers.go +++ b/internal/api/v1/handlers.go @@ -21,36 +21,32 @@ import ( // API holds all dependencies for API handlers type API struct { - dataDir string - directoryPath string // Path to Wild Cloud Directory - appsDir string - config *config.Manager - secrets *secrets.Manager - context *context.Manager - instance *instance.Manager - broadcaster *operations.Broadcaster // SSE broadcaster for operation output + dataDir string + appsDir string // Path to external apps directory + config *config.Manager + secrets *secrets.Manager + context *context.Manager + instance *instance.Manager + broadcaster *operations.Broadcaster // SSE broadcaster for operation output } // NewAPI creates a new API handler with all dependencies -func NewAPI(dataDir, directoryPath string) (*API, error) { +// Note: Setup files (cluster-services, cluster-nodes, etc.) are now embedded in the binary +func NewAPI(dataDir, appsDir string) (*API, error) { // Ensure base directories exist instancesDir := filepath.Join(dataDir, "instances") if err := os.MkdirAll(instancesDir, 0755); err != nil { return nil, fmt.Errorf("failed to create instances directory: %w", err) } - // Apps directory is now in Wild Cloud Directory - appsDir := filepath.Join(directoryPath, "apps") - return &API{ - dataDir: dataDir, - directoryPath: directoryPath, - appsDir: appsDir, - config: config.NewManager(), - secrets: secrets.NewManager(), - context: context.NewManager(dataDir), - instance: instance.NewManager(dataDir), - broadcaster: operations.NewBroadcaster(), + dataDir: dataDir, + appsDir: appsDir, + config: config.NewManager(), + secrets: secrets.NewManager(), + context: context.NewManager(dataDir), + instance: instance.NewManager(dataDir), + broadcaster: operations.NewBroadcaster(), }, nil } @@ -427,7 +423,7 @@ func (api *API) SetContext(w http.ResponseWriter, r *http.Request) { } // StatusHandler returns daemon status information -func (api *API) StatusHandler(w http.ResponseWriter, r *http.Request, startTime time.Time, dataDir, directoryPath string) { +func (api *API) StatusHandler(w http.ResponseWriter, r *http.Request, startTime time.Time, dataDir, appsDir string) { // Get list of instances instances, err := api.instance.ListInstances() if err != nil { @@ -443,7 +439,8 @@ func (api *API) StatusHandler(w http.ResponseWriter, r *http.Request, startTime "uptime": uptime.String(), "uptimeSeconds": int(uptime.Seconds()), "dataDir": dataDir, - "directoryPath": directoryPath, + "appsDir": appsDir, + "setupFiles": "embedded", // Indicate that setup files are now embedded "instances": map[string]interface{}{ "count": len(instances), "names": instances, diff --git a/internal/api/v1/handlers_services.go b/internal/api/v1/handlers_services.go index 2554aa5..7e53cf9 100644 --- a/internal/api/v1/handlers_services.go +++ b/internal/api/v1/handlers_services.go @@ -27,7 +27,7 @@ func (api *API) ServicesList(w http.ResponseWriter, r *http.Request) { } // List services - servicesMgr := services.NewManager(api.dataDir, filepath.Join(api.directoryPath, "setup", "cluster-services")) + servicesMgr := services.NewManager(api.dataDir) svcList, err := servicesMgr.List(instanceName) if err != nil { respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to list services: %v", err)) @@ -52,7 +52,7 @@ func (api *API) ServicesGet(w http.ResponseWriter, r *http.Request) { } // Get service - servicesMgr := services.NewManager(api.dataDir, filepath.Join(api.directoryPath, "setup", "cluster-services")) + servicesMgr := services.NewManager(api.dataDir) service, err := servicesMgr.Get(instanceName, serviceName) if err != nil { respondError(w, http.StatusNotFound, fmt.Sprintf("Service not found: %v", err)) @@ -109,7 +109,7 @@ func (api *API) ServicesInstall(w http.ResponseWriter, r *http.Request) { }() fmt.Printf("[DEBUG] Service install goroutine started: service=%s instance=%s opID=%s\n", req.Name, instanceName, opID) - servicesMgr := services.NewManager(api.dataDir, filepath.Join(api.directoryPath, "setup", "cluster-services")) + servicesMgr := services.NewManager(api.dataDir) opsMgr.UpdateStatus(instanceName, opID, "running") if err := servicesMgr.Install(instanceName, req.Name, req.Fetch, req.Deploy, opID, api.broadcaster); err != nil { @@ -159,7 +159,7 @@ func (api *API) ServicesInstallAll(w http.ResponseWriter, r *http.Request) { // Install in background go func() { - servicesMgr := services.NewManager(api.dataDir, filepath.Join(api.directoryPath, "setup", "cluster-services")) + servicesMgr := services.NewManager(api.dataDir) opsMgr.UpdateStatus(instanceName, opID, "running") if err := servicesMgr.InstallAll(instanceName, req.Fetch, req.Deploy, opID, api.broadcaster); err != nil { @@ -197,7 +197,7 @@ func (api *API) ServicesDelete(w http.ResponseWriter, r *http.Request) { // Delete in background go func() { - servicesMgr := services.NewManager(api.dataDir, filepath.Join(api.directoryPath, "setup", "cluster-services")) + servicesMgr := services.NewManager(api.dataDir) opsMgr.UpdateStatus(instanceName, opID, "running") if err := servicesMgr.Delete(instanceName, serviceName); err != nil { @@ -226,7 +226,7 @@ func (api *API) ServicesGetStatus(w http.ResponseWriter, r *http.Request) { } // Get status - servicesMgr := services.NewManager(api.dataDir, filepath.Join(api.directoryPath, "setup", "cluster-services")) + servicesMgr := services.NewManager(api.dataDir) status, err := servicesMgr.GetStatus(instanceName, serviceName) if err != nil { respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to get status: %v", err)) @@ -241,7 +241,7 @@ func (api *API) ServicesGetManifest(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) serviceName := vars["service"] - servicesMgr := services.NewManager(api.dataDir, filepath.Join(api.directoryPath, "setup", "cluster-services")) + servicesMgr := services.NewManager(api.dataDir) manifest, err := servicesMgr.GetManifest(serviceName) if err != nil { respondError(w, http.StatusNotFound, fmt.Sprintf("Service not found: %v", err)) @@ -256,7 +256,7 @@ func (api *API) ServicesGetConfig(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) serviceName := vars["service"] - servicesMgr := services.NewManager(api.dataDir, filepath.Join(api.directoryPath, "setup", "cluster-services")) + servicesMgr := services.NewManager(api.dataDir) // Get manifest manifest, err := servicesMgr.GetManifest(serviceName) @@ -286,7 +286,7 @@ func (api *API) ServicesGetInstanceConfig(w http.ResponseWriter, r *http.Request return } - servicesMgr := services.NewManager(api.dataDir, filepath.Join(api.directoryPath, "setup", "cluster-services")) + servicesMgr := services.NewManager(api.dataDir) // Get manifest to know which config paths to read manifest, err := servicesMgr.GetManifest(serviceName) @@ -364,7 +364,7 @@ func (api *API) ServicesFetch(w http.ResponseWriter, r *http.Request) { } // Fetch service files - servicesMgr := services.NewManager(api.dataDir, filepath.Join(api.directoryPath, "setup", "cluster-services")) + servicesMgr := services.NewManager(api.dataDir) if err := servicesMgr.Fetch(instanceName, serviceName); err != nil { respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to fetch service: %v", err)) return @@ -388,7 +388,7 @@ func (api *API) ServicesCompile(w http.ResponseWriter, r *http.Request) { } // Compile templates - servicesMgr := services.NewManager(api.dataDir, filepath.Join(api.directoryPath, "setup", "cluster-services")) + servicesMgr := services.NewManager(api.dataDir) if err := servicesMgr.Compile(instanceName, serviceName); err != nil { respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to compile templates: %v", err)) return @@ -412,7 +412,7 @@ func (api *API) ServicesDeploy(w http.ResponseWriter, r *http.Request) { } // Deploy service (without operation tracking for standalone deploy) - servicesMgr := services.NewManager(api.dataDir, filepath.Join(api.directoryPath, "setup", "cluster-services")) + servicesMgr := services.NewManager(api.dataDir) if err := servicesMgr.Deploy(instanceName, serviceName, "", nil); err != nil { respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to deploy service: %v", err)) return diff --git a/internal/node/node.go b/internal/node/node.go index cd21ad0..e22f245 100644 --- a/internal/node/node.go +++ b/internal/node/node.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/wild-cloud/wild-central/daemon/internal/config" + "github.com/wild-cloud/wild-central/daemon/internal/setup" "github.com/wild-cloud/wild-central/daemon/internal/tools" ) @@ -335,11 +336,11 @@ func (m *Manager) Apply(instanceName, nodeIdentifier string, opts ApplyOptions) } } - // Always auto-fetch templates if they don't exist + // Always auto-extract templates from embedded files if they don't exist templatesDir := filepath.Join(setupDir, "patch.templates") if !m.templatesExist(templatesDir) { - if err := m.copyTemplatesFromDirectory(templatesDir); err != nil { - return fmt.Errorf("failed to copy templates: %w", err) + if err := m.extractEmbeddedTemplates(templatesDir); err != nil { + return fmt.Errorf("failed to extract templates: %w", err) } } @@ -504,36 +505,31 @@ func (m *Manager) templatesExist(templatesDir string) bool { return err1 == nil && err2 == nil } -// copyTemplatesFromDirectory copies patch templates from directory/ to instance -func (m *Manager) copyTemplatesFromDirectory(destDir string) error { - // Find the directory/setup/cluster-nodes/patch.templates directory - // It should be in the same parent as the data directory - sourceDir := filepath.Join(filepath.Dir(m.dataDir), "directory", "setup", "cluster-nodes", "patch.templates") - - // Check if source directory exists - if _, err := os.Stat(sourceDir); err != nil { - return fmt.Errorf("source templates directory not found: %s", sourceDir) - } - +// extractEmbeddedTemplates extracts patch templates from embedded files to instance directory +func (m *Manager) extractEmbeddedTemplates(destDir string) error { // Create destination directory if err := os.MkdirAll(destDir, 0755); err != nil { return fmt.Errorf("failed to create templates directory: %w", err) } - // Copy controlplane.yaml - if err := m.copyFile( - filepath.Join(sourceDir, "controlplane.yaml"), - filepath.Join(destDir, "controlplane.yaml"), - ); err != nil { - return fmt.Errorf("failed to copy controlplane template: %w", err) + // Get embedded template files + controlplaneData, err := setup.GetClusterNodesFile("patch.templates/controlplane.yaml") + if err != nil { + return fmt.Errorf("failed to get controlplane template: %w", err) } - // Copy worker.yaml - if err := m.copyFile( - filepath.Join(sourceDir, "worker.yaml"), - filepath.Join(destDir, "worker.yaml"), - ); err != nil { - return fmt.Errorf("failed to copy worker template: %w", err) + workerData, err := setup.GetClusterNodesFile("patch.templates/worker.yaml") + if err != nil { + return fmt.Errorf("failed to get worker template: %w", err) + } + + // Write templates + if err := os.WriteFile(filepath.Join(destDir, "controlplane.yaml"), controlplaneData, 0644); err != nil { + return fmt.Errorf("failed to write controlplane template: %w", err) + } + + if err := os.WriteFile(filepath.Join(destDir, "worker.yaml"), workerData, 0644); err != nil { + return fmt.Errorf("failed to write worker template: %w", err) } return nil @@ -660,9 +656,9 @@ func (m *Manager) Update(instanceName string, hostname string, updates map[strin return nil } -// FetchTemplates copies patch templates from directory/ to instance +// FetchTemplates extracts patch templates from embedded files to instance func (m *Manager) FetchTemplates(instanceName string) error { instancePath := m.GetInstancePath(instanceName) destDir := filepath.Join(instancePath, "setup", "cluster-nodes", "patch.templates") - return m.copyTemplatesFromDirectory(destDir) + return m.extractEmbeddedTemplates(destDir) } diff --git a/internal/services/services.go b/internal/services/services.go index e5f7dc6..c196d94 100644 --- a/internal/services/services.go +++ b/internal/services/services.go @@ -2,6 +2,7 @@ package services import ( "fmt" + "io/fs" "os" "os/exec" "path/filepath" @@ -10,30 +11,41 @@ import ( "gopkg.in/yaml.v3" "github.com/wild-cloud/wild-central/daemon/internal/operations" + "github.com/wild-cloud/wild-central/daemon/internal/setup" "github.com/wild-cloud/wild-central/daemon/internal/storage" "github.com/wild-cloud/wild-central/daemon/internal/tools" ) // Manager handles base service operations type Manager struct { - dataDir string - servicesDir string // Path to services directory - manifests map[string]*ServiceManifest // Cached service manifests + dataDir string + manifests map[string]*ServiceManifest // Cached service manifests } // NewManager creates a new services manager -func NewManager(dataDir, servicesDir string) *Manager { +// Note: Service definitions are now loaded from embedded setup files +func NewManager(dataDir string) *Manager { m := &Manager{ - dataDir: dataDir, - servicesDir: servicesDir, + dataDir: dataDir, } - // Load all service manifests - manifests, err := LoadAllManifests(servicesDir) - if err != nil { - // Log error but continue - services without manifests will fall back to hardcoded map - fmt.Printf("Warning: failed to load service manifests: %v\n", err) - manifests = make(map[string]*ServiceManifest) + // Load all service manifests from embedded files + manifests := make(map[string]*ServiceManifest) + services, err := setup.ListServices() + if err == nil { + for _, serviceName := range services { + manifest, err := setup.GetManifest(serviceName) + if err == nil { + // Convert setup.ServiceManifest to services.ServiceManifest + manifests[serviceName] = &ServiceManifest{ + Name: manifest.Name, + Description: manifest.Description, + Category: manifest.Category, + } + } + } + } else { + fmt.Printf("Warning: failed to load service manifests from embedded files: %v\n", err) } m.manifests = manifests @@ -124,18 +136,13 @@ func (m *Manager) checkServiceStatus(instanceName, serviceName string) string { func (m *Manager) List(instanceName string) ([]Service, error) { services := []Service{} - // Discover services from the services directory - entries, err := os.ReadDir(m.servicesDir) + // Discover services from embedded setup files + serviceNames, err := setup.ListServices() if err != nil { - return nil, fmt.Errorf("failed to read services directory: %w", err) + return nil, fmt.Errorf("failed to list services from embedded files: %w", err) } - for _, entry := range entries { - if !entry.IsDir() { - continue // Skip non-directories like README.md - } - - name := entry.Name() + for _, name := range serviceNames { // Get service info from manifest if available var namespace, description, version string @@ -232,11 +239,17 @@ func (m *Manager) InstallAll(instanceName string, fetch, deploy bool, opID strin func (m *Manager) Delete(instanceName, serviceName string) error { kubeconfigPath := tools.GetKubeconfigPath(m.dataDir, instanceName) - serviceDir := filepath.Join(m.servicesDir, serviceName) - manifestsFile := filepath.Join(serviceDir, "manifests.yaml") + // Check if service exists in embedded files + if !setup.ServiceExists(serviceName) { + return fmt.Errorf("service %s not found", serviceName) + } + + // Get manifests file from embedded setup or instance directory + instanceServiceDir := filepath.Join(m.dataDir, "instances", instanceName, "setup", "cluster-services", serviceName) + manifestsFile := filepath.Join(instanceServiceDir, "manifests.yaml") if !storage.FileExists(manifestsFile) { - return fmt.Errorf("service %s not found", serviceName) + return fmt.Errorf("service manifests not found - service may not be installed") } cmd := exec.Command("kubectl", "delete", "-f", manifestsFile) @@ -292,12 +305,11 @@ func (m *Manager) GetConfigReferences(serviceName string) ([]string, error) { return manifest.ConfigReferences, nil } -// Fetch copies service files from directory to instance +// Fetch extracts service files from embedded setup to instance func (m *Manager) Fetch(instanceName, serviceName string) error { - // 1. Validate service exists in directory - sourceDir := filepath.Join(m.servicesDir, serviceName) - if !dirExists(sourceDir) { - return fmt.Errorf("service %s not found in directory", serviceName) + // 1. Validate service exists in embedded files + if !setup.ServiceExists(serviceName) { + return fmt.Errorf("service %s not found in embedded files", serviceName) } // 2. Create instance service directory @@ -307,31 +319,36 @@ func (m *Manager) Fetch(instanceName, serviceName string) error { return fmt.Errorf("failed to create service directory: %w", err) } - // 3. Copy files: + // 3. Extract files from embedded setup: // - README.md (if exists, optional) // - install.sh (if exists, optional) + // - wild-manifest.yaml // - kustomize.template/* (if exists, optional) - // Copy README.md - copyFileIfExists(filepath.Join(sourceDir, "README.md"), - filepath.Join(instanceDir, "README.md")) - - // Copy install.sh (optional) - installSh := filepath.Join(sourceDir, "install.sh") - if fileExists(installSh) { - if err := copyFile(installSh, filepath.Join(instanceDir, "install.sh")); err != nil { - return fmt.Errorf("failed to copy install.sh: %w", err) - } - // Make install.sh executable - os.Chmod(filepath.Join(instanceDir, "install.sh"), 0755) + // Extract README.md if it exists + if readmeData, err := setup.GetServiceFile(serviceName, "README.md"); err == nil { + os.WriteFile(filepath.Join(instanceDir, "README.md"), readmeData, 0644) } - // Copy kustomize.template directory if it exists - templateDir := filepath.Join(sourceDir, "kustomize.template") - if dirExists(templateDir) { + // Extract install.sh if it exists + if installData, err := setup.GetServiceFile(serviceName, "install.sh"); err == nil { + installPath := filepath.Join(instanceDir, "install.sh") + if err := os.WriteFile(installPath, installData, 0755); err != nil { + return fmt.Errorf("failed to write install.sh: %w", err) + } + } + + // Extract wild-manifest.yaml + if manifestData, err := setup.GetServiceFile(serviceName, "wild-manifest.yaml"); err == nil { + os.WriteFile(filepath.Join(instanceDir, "wild-manifest.yaml"), manifestData, 0644) + } + + // Extract kustomize.template directory + templateFS, err := setup.GetKustomizeTemplate(serviceName) + if err == nil { destTemplateDir := filepath.Join(instanceDir, "kustomize.template") - if err := copyDir(templateDir, destTemplateDir); err != nil { - return fmt.Errorf("failed to copy templates: %w", err) + if err := extractFS(templateFS, destTemplateDir); err != nil { + return fmt.Errorf("failed to extract templates: %w", err) } } @@ -404,6 +421,32 @@ func copyDir(src, dst string) error { return nil } +// extractFS extracts files from an fs.FS to a destination directory +func extractFS(fsys fs.FS, dst string) error { + return fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + // Create destination path + dstPath := filepath.Join(dst, path) + + if d.IsDir() { + // Create directory + return os.MkdirAll(dstPath, 0755) + } + + // Read file from embedded FS + data, err := fs.ReadFile(fsys, path) + if err != nil { + return err + } + + // Write file to destination + return os.WriteFile(dstPath, data, 0644) + }) +} + // Compile processes gomplate templates into final Kubernetes manifests func (m *Manager) Compile(instanceName, serviceName string) error { instanceDir := filepath.Join(m.dataDir, "instances", instanceName) diff --git a/main.go b/main.go index 1ea0604..3bda96e 100644 --- a/main.go +++ b/main.go @@ -24,14 +24,21 @@ func main() { dataDir = "/var/lib/wild-central" } - // Get directory path from environment (required) - directoryPath := os.Getenv("WILD_DIRECTORY") - if directoryPath == "" { - log.Fatal("WILD_DIRECTORY environment variable is required") + // Get apps directory from environment or use default + // Note: Setup files (cluster-services, cluster-nodes, etc.) are now embedded in the binary + appsDir := os.Getenv("WILD_DIRECTORY") + if appsDir == "" { + // Default apps directory + appsDir = "/opt/wild-cloud/apps" + log.Printf("WILD_DIRECTORY not set, using default apps directory: %s", appsDir) + } else { + // If WILD_DIRECTORY is set, use it as-is for backward compatibility + // (it might point to the old directory structure with apps/ subdirectory) + log.Printf("Using WILD_DIRECTORY for apps: %s", appsDir) } // Create API handler with all dependencies - api, err := v1.NewAPI(dataDir, directoryPath) + api, err := v1.NewAPI(dataDir, appsDir) if err != nil { log.Fatalf("Failed to initialize API: %v", err) } @@ -51,7 +58,7 @@ func main() { // Status endpoint router.HandleFunc("/api/v1/status", func(w http.ResponseWriter, r *http.Request) { - api.StatusHandler(w, r, startTime, dataDir, directoryPath) + api.StatusHandler(w, r, startTime, dataDir, appsDir) }).Methods("GET") // Default server settings @@ -61,7 +68,7 @@ func main() { addr := fmt.Sprintf("%s:%d", host, port) log.Printf("Starting wild-central daemon on %s", addr) log.Printf("Data directory: %s", dataDir) - log.Printf("Wild Cloud Directory: %s", directoryPath) + log.Printf("Apps directory: %s", appsDir) if err := http.ListenAndServe(addr, router); err != nil { log.Fatal("Server failed to start:", err)