platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
96
selective-vpn-api/app/vpn_handlers_status.go
Normal file
96
selective-vpn-api/app/vpn_handlers_status.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// аккуратный разбор лога autoloop: игнорим "route:", смотрим status
|
||||
func parseAutoloopStatus(lines []string) (word, raw string) {
|
||||
for i := len(lines) - 1; i >= 0; i-- {
|
||||
line := strings.TrimSpace(lines[i])
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if idx := strings.Index(line, "autoloop:"); idx >= 0 {
|
||||
line = strings.TrimSpace(line[idx+len("autoloop:"):])
|
||||
}
|
||||
lower := strings.ToLower(line)
|
||||
|
||||
// route: default dev ... - нам неинтересно
|
||||
if strings.HasPrefix(lower, "route: ") {
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.Contains(lower, "status: connected"),
|
||||
strings.Contains(lower, "after connect: connected"):
|
||||
return "CONNECTED", line
|
||||
case strings.Contains(lower, "status: reconnecting"):
|
||||
return "RECONNECTING", line
|
||||
case strings.Contains(lower, "status: disconnected"),
|
||||
strings.Contains(lower, "still disconnected"):
|
||||
return "DISCONNECTED", line
|
||||
case strings.Contains(lower, "timeout"),
|
||||
strings.Contains(lower, "failed"):
|
||||
return "ERROR", line
|
||||
}
|
||||
}
|
||||
return "unknown", ""
|
||||
}
|
||||
|
||||
func handleVPNAutoloopStatus(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
lines := tailFile(autoloopLogPath, 200)
|
||||
word, raw := parseAutoloopStatus(lines)
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"raw_text": raw,
|
||||
"status_word": word,
|
||||
})
|
||||
}
|
||||
|
||||
func handleVPNStatus(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// desired location
|
||||
loc := ""
|
||||
if data, err := os.ReadFile(desiredLocation); err == nil {
|
||||
loc = parseStoredVPNLocationPrimary(string(data))
|
||||
}
|
||||
|
||||
// unit state
|
||||
stdout, _, _, err := runCommand("systemctl", "is-active", adgvpnUnit)
|
||||
unitState := strings.TrimSpace(stdout)
|
||||
if err != nil || unitState == "" {
|
||||
unitState = "unknown"
|
||||
}
|
||||
|
||||
// автолуп
|
||||
lines := tailFile(autoloopLogPath, 200)
|
||||
word, raw := parseAutoloopStatus(lines)
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"desired_location": loc,
|
||||
"status_word": word,
|
||||
"raw_text": raw,
|
||||
"unit_state": unitState,
|
||||
})
|
||||
}
|
||||
|
||||
func parseStoredVPNLocationPrimary(raw string) string {
|
||||
v := strings.TrimSpace(raw)
|
||||
if v == "" {
|
||||
return ""
|
||||
}
|
||||
if p := strings.SplitN(v, "|", 2); len(p) == 2 {
|
||||
return strings.TrimSpace(p[0])
|
||||
}
|
||||
return v
|
||||
}
|
||||
Reference in New Issue
Block a user