nsfwapp/backend/ai_server_process_windows.go
2026-06-25 14:54:58 +02:00

129 lines
2.9 KiB
Go

//go:build windows
package main
import (
"os"
"os/exec"
"strconv"
"strings"
"syscall"
"time"
)
const windowsCreateNewProcessGroupForAIServer = 0x00000200
func prepareAIServerCommandForLifecycle(cmd *exec.Cmd) {
if cmd == nil {
return
}
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{}
}
cmd.SysProcAttr.CreationFlags |= windowsCreateNewProcessGroupForAIServer
}
func terminateAIServerProcessTree(cmd *exec.Cmd) {
if cmd == nil || cmd.Process == nil || cmd.Process.Pid <= 0 {
return
}
terminateWindowsPIDTree(cmd.Process.Pid)
_ = cmd.Process.Kill()
}
func stopStaleAIServerOnPort(port string) error {
portNumber, err := strconv.Atoi(strings.TrimSpace(port))
if err != nil || portNumber <= 0 || portNumber > 65535 {
return nil
}
pids, err := windowsPIDsListeningOnPort(portNumber)
if err != nil {
return err
}
currentPID := os.Getpid()
for _, pid := range pids {
if pid <= 0 || pid == currentPID {
continue
}
if !windowsPIDLooksLikeAIServer(pid) {
appLogf("⚠️ Listener auf AI-Port %d übersprungen, Prozess sieht nicht nach AI Server aus: pid=%d", portNumber, pid)
continue
}
appLogf("🧹 Alter AI Server auf Port %d wird beendet: pid=%d", portNumber, pid)
terminateWindowsPIDTree(pid)
}
if len(pids) > 0 {
time.Sleep(500 * time.Millisecond)
}
return nil
}
func windowsPIDsListeningOnPort(port int) ([]int, error) {
ps := "Get-NetTCPConnection -LocalPort " + strconv.Itoa(port) + " -State Listen -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess -Unique"
cmd := exec.Command(
"powershell",
"-NoProfile",
"-NonInteractive",
"-ExecutionPolicy", "Bypass",
"-Command", ps,
)
hideCommandWindow(cmd)
out, err := cmd.Output()
if err != nil {
return nil, err
}
pidSet := map[int]struct{}{}
for _, line := range strings.Split(string(out), "\n") {
pid, err := strconv.Atoi(strings.TrimSpace(line))
if err != nil || pid <= 0 {
continue
}
pidSet[pid] = struct{}{}
}
pids := make([]int, 0, len(pidSet))
for pid := range pidSet {
pids = append(pids, pid)
}
return pids, nil
}
func terminateWindowsPIDTree(pid int) {
if pid <= 0 {
return
}
cmd := exec.Command("taskkill", "/PID", strconv.Itoa(pid), "/T", "/F")
hideCommandWindow(cmd)
_ = cmd.Run()
}
func windowsPIDLooksLikeAIServer(pid int) bool {
ps := "(Get-CimInstance Win32_Process -Filter \"ProcessId = " + strconv.Itoa(pid) + "\" -ErrorAction SilentlyContinue).CommandLine"
cmd := exec.Command(
"powershell",
"-NoProfile",
"-NonInteractive",
"-ExecutionPolicy", "Bypass",
"-Command", ps,
)
hideCommandWindow(cmd)
out, err := cmd.Output()
if err != nil {
return false
}
cmdline := strings.ToLower(strings.TrimSpace(string(out)))
return strings.Contains(cmdline, "ai_server:app") ||
strings.Contains(cmdline, "ai_server.py") ||
(strings.Contains(cmdline, "uvicorn") && strings.Contains(cmdline, "ai_server"))
}