feat: orphan stack detection/deletion, filebrowser infra, setup scripts
- Add orphan detection: stacks not in catalog marked as "Elavult"
- Add DELETE /api/stacks/{name} endpoint with HDD data handling
- Add GET /api/stacks/{name}/hdd-data endpoint
- Add delete confirmation modal with HDD data checkbox (Hungarian UI)
- Add filebrowser to protected stacks list
- Add scripts/hdd-setup.sh and scripts/docker-setup.sh for node setup
- Hide "Frissítés" and "Részletek" buttons for orphaned stacks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,7 @@ const (
|
||||
StatePaused ContainerState = "paused"
|
||||
StateUnknown ContainerState = "unknown"
|
||||
StateNotDeployed ContainerState = "not_deployed"
|
||||
StateOrphaned ContainerState = "orphaned"
|
||||
)
|
||||
|
||||
// ContainerInfo holds status info about a single container within a stack.
|
||||
@@ -47,6 +48,7 @@ type Stack struct {
|
||||
State ContainerState `json:"state"`
|
||||
Deployed bool `json:"deployed"` // Has app.yaml with deployed=true
|
||||
Protected bool `json:"protected"`
|
||||
Orphaned bool `json:"orphaned"` // Deployed but no catalog template
|
||||
Containers []ContainerInfo `json:"containers"`
|
||||
AppConfig *AppConfig `json:"app_config,omitempty"`
|
||||
LastUpdated time.Time `json:"last_updated"`
|
||||
@@ -166,6 +168,25 @@ func (m *Manager) ScanStacks() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Detect orphaned stacks (deployed but no longer in catalog)
|
||||
catalogTemplates := m.getCatalogTemplateSlugs()
|
||||
if catalogTemplates != nil {
|
||||
orphanCount := 0
|
||||
for _, stack := range m.stacks {
|
||||
if stack.Protected || !stack.Deployed {
|
||||
stack.Orphaned = false
|
||||
continue
|
||||
}
|
||||
stack.Orphaned = !catalogTemplates[stack.Name]
|
||||
if stack.Orphaned {
|
||||
orphanCount++
|
||||
}
|
||||
}
|
||||
if orphanCount > 0 {
|
||||
m.logger.Printf("[INFO] Detected %d orphaned stack(s)", orphanCount)
|
||||
}
|
||||
}
|
||||
|
||||
deployedCount := 0
|
||||
for _, s := range m.stacks {
|
||||
if s.Deployed {
|
||||
@@ -733,4 +754,25 @@ func (m *Manager) CommittedMemory() (requestMB int, limitMB int) {
|
||||
limitMB += ParseMemoryMB(s.Meta.Resources.MemLimit)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getCatalogTemplateSlugs reads the synced catalog cache and returns a set of
|
||||
// template slugs (directory names) that have a docker-compose.yml.
|
||||
func (m *Manager) getCatalogTemplateSlugs() map[string]bool {
|
||||
cacheDir := filepath.Join(m.cfg.Paths.DataDir, "catalog-cache", "templates")
|
||||
entries, err := os.ReadDir(cacheDir)
|
||||
if err != nil {
|
||||
m.logger.Printf("[WARN] Cannot read catalog cache for orphan detection: %v", err)
|
||||
return nil
|
||||
}
|
||||
slugs := make(map[string]bool, len(entries))
|
||||
for _, e := range entries {
|
||||
if e.IsDir() {
|
||||
composePath := filepath.Join(cacheDir, e.Name(), "docker-compose.yml")
|
||||
if _, err := os.Stat(composePath); err == nil {
|
||||
slugs[e.Name()] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return slugs
|
||||
}
|
||||
Reference in New Issue
Block a user