97 lines
3.0 KiB
Go
97 lines
3.0 KiB
Go
// Package cli — mode CLI pendamping (F-028).
|
|
// Seluruh logika inti di-share dengan TUI; paket ini hanya presentasi + kode keluar.
|
|
package cli
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"runtime"
|
|
"time"
|
|
|
|
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/audio"
|
|
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/config"
|
|
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/docker"
|
|
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/i18n"
|
|
"gitea.oxpoe.qzz.io/oxpoe/tia-love/internal/stack"
|
|
)
|
|
|
|
// Doctor memeriksa kesehatan sistem dan menulis laporan ke w.
|
|
// Kode keluar: 0 = sehat (warning boleh), 1 = ada kegagalan fatal.
|
|
func Doctor(w io.Writer) int {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
fail := 0
|
|
|
|
// Konfigurasi dimuat lebih dulu — host & compose dipakai di bawah.
|
|
cfg, cfgErr := config.Load()
|
|
|
|
// Engine Docker/Podman — dependency inti (auto-detect bila host kosong).
|
|
engineName := ""
|
|
switch cl, engine, err := docker.Detect(cfg.Docker.Host); {
|
|
case err != nil:
|
|
fmt.Fprintf(w, "engine : %s %v\n", i18n.T("error.fail"), err)
|
|
fail++
|
|
default:
|
|
defer cl.Close()
|
|
engineName = engine.Name
|
|
host := engine.Host
|
|
if host == "" {
|
|
host = i18n.T("doctor.hostDefault")
|
|
}
|
|
if info, err := cl.Info(ctx); err != nil {
|
|
fmt.Fprintf(w, "engine : %s %s %v\n", i18n.T("error.warn"), engine.Name, err)
|
|
} else {
|
|
fmt.Fprintf(w, "engine : %s %s\n", i18n.T("error.ok"),
|
|
i18n.Tf("doctor.engineOk", engine.Name, info.ServerVersion, info.APIVersion,
|
|
info.Running, info.Containers, info.Images))
|
|
}
|
|
fmt.Fprintf(w, "socket : %s\n", host)
|
|
}
|
|
|
|
// Audio — opsional; engine init async, tunggu maks 2 dtk.
|
|
eng := audio.New(audio.Options{Enabled: true, Volume: 70, MuteOnSSH: true})
|
|
eng.WaitReady(2 * time.Second)
|
|
fmt.Fprintf(w, "audio : %s\n", eng.Status())
|
|
|
|
fmt.Fprintf(w, "go : %s (%s/%s)\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
|
|
|
// Compose — dipakai operasi stack (up/down/pull); "auto" mengikuti engine.
|
|
compose := docker.NewCompose(cfg.Docker.ComposeCommand, engineName)
|
|
if ver, err := compose.Version(ctx); err != nil {
|
|
fmt.Fprintf(w, "compose : %s %v\n", i18n.T("error.warn"), err)
|
|
} else {
|
|
fmt.Fprintf(w, "compose : %s %s\n", i18n.T("error.ok"), i18n.Tf("doctor.composeOk", ver, compose.Command()))
|
|
}
|
|
|
|
cfgPath := config.Path()
|
|
switch {
|
|
case cfgErr != nil:
|
|
fmt.Fprintf(w, "config : %s %v\n", i18n.T("error.warn"), cfgErr)
|
|
default:
|
|
if _, err := os.Stat(cfgPath); err != nil {
|
|
fmt.Fprintf(w, "config : %s\n", i18n.Tf("doctor.configMissing", cfgPath))
|
|
} else {
|
|
fmt.Fprintf(w, "config : %s\n", cfgPath)
|
|
}
|
|
}
|
|
|
|
stacks := config.ExpandPath(cfg.StacksDir)
|
|
if st, err := os.Stat(stacks); err == nil && st.IsDir() {
|
|
n := 0
|
|
if list, err := stack.ScanFS(stacks); err == nil {
|
|
n = len(list)
|
|
}
|
|
fmt.Fprintf(w, "stacks : %s\n", i18n.Tf("doctor.stacksCount", stacks, n))
|
|
} else {
|
|
fmt.Fprintf(w, "stacks : %s\n", i18n.Tf("doctor.stacksMissing", stacks))
|
|
}
|
|
|
|
if fail > 0 {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|