273 lines
7.8 KiB
Go
Raw Permalink Normal View History

2026-03-12 20:51:38 -07:00
package handlers
import (
"encoding/json"
"fmt"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
"setec-manager/internal/deploy"
)
type autarchStatus struct {
Installed bool `json:"installed"`
InstallDir string `json:"install_dir"`
GitCommit string `json:"git_commit"`
VenvReady bool `json:"venv_ready"`
PipPackages int `json:"pip_packages"`
WebRunning bool `json:"web_running"`
WebStatus string `json:"web_status"`
DNSRunning bool `json:"dns_running"`
DNSStatus string `json:"dns_status"`
}
func (h *Handler) AutarchStatus(w http.ResponseWriter, r *http.Request) {
status := h.getAutarchStatus()
h.render(w, "autarch.html", status)
}
func (h *Handler) AutarchStatusAPI(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, h.getAutarchStatus())
}
func (h *Handler) getAutarchStatus() autarchStatus {
dir := h.Config.Autarch.InstallDir
status := autarchStatus{InstallDir: dir}
// Check if installed
if _, err := os.Stat(filepath.Join(dir, ".git")); err == nil {
status.Installed = true
}
// Git commit
if hash, message, err := deploy.CurrentCommit(dir); err == nil {
status.GitCommit = hash + " " + message
}
// Venv
status.VenvReady = deploy.VenvExists(dir)
// Pip packages
venvDir := filepath.Join(dir, "venv")
if pkgs, err := deploy.ListPackages(venvDir); err == nil {
status.PipPackages = len(pkgs)
}
// Web service
webActive, _ := deploy.IsActive("autarch-web")
status.WebRunning = webActive
if webActive {
status.WebStatus = "active"
} else {
status.WebStatus = "inactive"
}
// DNS service
dnsActive, _ := deploy.IsActive("autarch-dns")
status.DNSRunning = dnsActive
if dnsActive {
status.DNSStatus = "active"
} else {
status.DNSStatus = "inactive"
}
return status
}
func (h *Handler) AutarchInstall(w http.ResponseWriter, r *http.Request) {
dir := h.Config.Autarch.InstallDir
repo := h.Config.Autarch.GitRepo
branch := h.Config.Autarch.GitBranch
if _, err := os.Stat(filepath.Join(dir, ".git")); err == nil {
writeError(w, http.StatusConflict, "AUTARCH already installed at "+dir)
return
}
depID, _ := h.DB.CreateDeployment(nil, "autarch_install")
var output strings.Builder
steps := []struct {
label string
fn func() error
}{
{"Clone from GitHub", func() error {
os.MkdirAll(filepath.Dir(dir), 0755)
out, err := deploy.Clone(repo, branch, dir)
output.WriteString(out)
return err
}},
{"Create Python venv", func() error {
return deploy.CreateVenv(dir)
}},
{"Upgrade pip", func() error {
venvDir := filepath.Join(dir, "venv")
deploy.UpgradePip(venvDir)
return nil
}},
{"Install pip packages", func() error {
reqFile := filepath.Join(dir, "requirements.txt")
if _, err := os.Stat(reqFile); err != nil {
return nil
}
venvDir := filepath.Join(dir, "venv")
out, err := deploy.InstallRequirements(venvDir, reqFile)
output.WriteString(out)
return err
}},
{"Install npm packages", func() error {
out, _ := deploy.NpmInstall(dir)
output.WriteString(out)
return nil
}},
{"Set permissions", func() error {
exec.Command("chown", "-R", "root:root", dir).Run()
exec.Command("chmod", "-R", "755", dir).Run()
for _, d := range []string{"data", "data/certs", "data/dns", "results", "dossiers", "models"} {
os.MkdirAll(filepath.Join(dir, d), 0755)
}
confPath := filepath.Join(dir, "autarch_settings.conf")
if _, err := os.Stat(confPath); err == nil {
exec.Command("chmod", "600", confPath).Run()
}
return nil
}},
{"Install systemd units", func() error {
h.installAutarchUnits(dir)
return nil
}},
}
for _, step := range steps {
output.WriteString(fmt.Sprintf("\n=== %s ===\n", step.label))
if err := step.fn(); err != nil {
h.DB.FinishDeployment(depID, "failed", output.String())
writeError(w, http.StatusInternalServerError, fmt.Sprintf("%s failed: %v", step.label, err))
return
}
}
h.DB.FinishDeployment(depID, "success", output.String())
writeJSON(w, http.StatusOK, map[string]string{"status": "installed"})
}
func (h *Handler) AutarchUpdate(w http.ResponseWriter, r *http.Request) {
dir := h.Config.Autarch.InstallDir
depID, _ := h.DB.CreateDeployment(nil, "autarch_update")
var output strings.Builder
// Git pull
out, err := deploy.Pull(dir)
output.WriteString(out)
if err != nil {
h.DB.FinishDeployment(depID, "failed", output.String())
writeError(w, http.StatusInternalServerError, "git pull failed")
return
}
// Reinstall pip packages
reqFile := filepath.Join(dir, "requirements.txt")
if _, err := os.Stat(reqFile); err == nil {
venvDir := filepath.Join(dir, "venv")
pipOut, _ := deploy.InstallRequirements(venvDir, reqFile)
output.WriteString(pipOut)
}
// Restart services
deploy.Restart("autarch-web")
deploy.Restart("autarch-dns")
h.DB.FinishDeployment(depID, "success", output.String())
writeJSON(w, http.StatusOK, map[string]string{"status": "updated"})
}
func (h *Handler) AutarchStart(w http.ResponseWriter, r *http.Request) {
deploy.Start("autarch-web")
deploy.Start("autarch-dns")
writeJSON(w, http.StatusOK, map[string]string{"status": "started"})
}
func (h *Handler) AutarchStop(w http.ResponseWriter, r *http.Request) {
deploy.Stop("autarch-web")
deploy.Stop("autarch-dns")
writeJSON(w, http.StatusOK, map[string]string{"status": "stopped"})
}
func (h *Handler) AutarchRestart(w http.ResponseWriter, r *http.Request) {
deploy.Restart("autarch-web")
deploy.Restart("autarch-dns")
writeJSON(w, http.StatusOK, map[string]string{"status": "restarted"})
}
func (h *Handler) AutarchConfig(w http.ResponseWriter, r *http.Request) {
confPath := filepath.Join(h.Config.Autarch.InstallDir, "autarch_settings.conf")
data, err := os.ReadFile(confPath)
if err != nil {
writeError(w, http.StatusNotFound, "config not found")
return
}
writeJSON(w, http.StatusOK, map[string]string{"config": string(data)})
}
func (h *Handler) AutarchConfigUpdate(w http.ResponseWriter, r *http.Request) {
var body struct {
Config string `json:"config"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
writeError(w, http.StatusBadRequest, "invalid body")
return
}
confPath := filepath.Join(h.Config.Autarch.InstallDir, "autarch_settings.conf")
if err := os.WriteFile(confPath, []byte(body.Config), 0600); err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusOK, map[string]string{"status": "saved"})
}
func (h *Handler) AutarchDNSBuild(w http.ResponseWriter, r *http.Request) {
dnsDir := filepath.Join(h.Config.Autarch.InstallDir, "services", "dns-server")
depID, _ := h.DB.CreateDeployment(nil, "dns_build")
cmd := exec.Command("go", "build", "-o", "autarch-dns", ".")
cmd.Dir = dnsDir
out, err := cmd.CombinedOutput()
if err != nil {
h.DB.FinishDeployment(depID, "failed", string(out))
writeError(w, http.StatusInternalServerError, "build failed: "+string(out))
return
}
h.DB.FinishDeployment(depID, "success", string(out))
writeJSON(w, http.StatusOK, map[string]string{"status": "built"})
}
func (h *Handler) installAutarchUnits(dir string) {
webUnit := deploy.GenerateUnit(deploy.UnitConfig{
Name: "autarch-web",
Description: "AUTARCH Web Dashboard",
ExecStart: filepath.Join(dir, "venv", "bin", "python3") + " " + filepath.Join(dir, "autarch_web.py"),
WorkingDirectory: dir,
User: "root",
Environment: map[string]string{"PYTHONUNBUFFERED": "1"},
})
dnsUnit := deploy.GenerateUnit(deploy.UnitConfig{
Name: "autarch-dns",
Description: "AUTARCH DNS Server",
ExecStart: filepath.Join(dir, "services", "dns-server", "autarch-dns") + " --config " + filepath.Join(dir, "data", "dns", "config.json"),
WorkingDirectory: dir,
User: "root",
})
deploy.InstallUnit("autarch-web", webUnit)
deploy.InstallUnit("autarch-dns", dnsUnit)
}