package stack import ( "os" "path/filepath" "sort" "strings" "gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/docker" ) // ComposeFileNames — nama file compose yang diterima (REA modul 2 — Dockge). var ComposeFileNames = []string{"compose.yaml", "docker-compose.yaml", "docker-compose.yml", "compose.yml"} // Status — status stack (nilai mengikuti referensi Dockge). type Status int const ( StatusUnknown Status = iota StatusCreatedFile StatusCreatedStack StatusRunning StatusExited ) // Stack — satu stack terkelola di direktori stack. type Stack struct { Name string Dir string ComposeFile string Status Status } // ScanFS — daftar stack dari disk: setiap subfolder yang berisi file compose. // Folder tidak ada → daftar kosong, bukan error (belum onboarding). func ScanFS(dir string) ([]Stack, error) { entries, err := os.ReadDir(dir) if err != nil { if os.IsNotExist(err) { return nil, nil } return nil, err } var out []Stack for _, e := range entries { if !e.IsDir() { continue } stackDir := filepath.Join(dir, e.Name()) if f := findComposeFile(stackDir); f != "" { out = append(out, Stack{ Name: e.Name(), Dir: stackDir, ComposeFile: f, Status: StatusCreatedFile, }) } } sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name }) return out, nil } func findComposeFile(dir string) string { for _, name := range ComposeFileNames { p := filepath.Join(dir, name) if st, err := os.Stat(p); err == nil && !st.IsDir() { return p } } return "" } // Reconcile — overlay status dari `docker compose ls` (parity Dockge: // nama project compose = nama folder stack). func Reconcile(stacks []Stack, projects []docker.ComposeProject) []Stack { byName := make(map[string]Status, len(projects)) for _, p := range projects { byName[p.Name] = parseComposeStatus(p.Status) } for i := range stacks { if s, ok := byName[stacks[i].Name]; ok { stacks[i].Status = s } } return stacks } func parseComposeStatus(s string) Status { switch { case strings.HasPrefix(s, "running"): return StatusRunning case strings.HasPrefix(s, "exited"): return StatusExited case strings.HasPrefix(s, "created"): return StatusCreatedStack default: return StatusUnknown } }