platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
102
selective-vpn-api/app/routes_handlers_ops_core.go
Normal file
102
selective-vpn-api/app/routes_handlers_ops_core.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// EN: `routesClear` contains core logic for routes clear.
|
||||
// RU: `routesClear` - содержит основную логику для routes clear.
|
||||
// ---------------------------------------------------------------------
|
||||
func routesClear() cmdResult {
|
||||
return withRoutesOpLock("routes clear", routesClearUnlocked)
|
||||
}
|
||||
|
||||
func routesClearUnlocked() cmdResult {
|
||||
cacheMeta, cacheErr := saveRoutesClearCache()
|
||||
|
||||
stdout, stderr, _, err := runCommand("ip", "rule", "show")
|
||||
if err == nil && stdout != "" {
|
||||
removeTrafficRulesForTable()
|
||||
}
|
||||
|
||||
_, _, _, _ = runCommand("ip", "route", "flush", "table", routesTableName())
|
||||
_, _, _, _ = runCommand("nft", "flush", "set", "inet", "agvpn", "agvpn4")
|
||||
_, _, _, _ = runCommand("nft", "flush", "set", "inet", "agvpn", "agvpn_dyn4")
|
||||
iface := strings.TrimSpace(cacheMeta.Iface)
|
||||
if iface == "" {
|
||||
iface, _ = resolveTrafficIface(loadTrafficModeState().PreferredIface)
|
||||
}
|
||||
_ = writeStatusSnapshot(0, iface)
|
||||
|
||||
res := cmdResult{
|
||||
OK: true,
|
||||
Message: "routes cleared",
|
||||
ExitCode: 0,
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
}
|
||||
if cacheErr != nil {
|
||||
res.Message = fmt.Sprintf("%s (cache warning: %v)", res.Message, cacheErr)
|
||||
} else {
|
||||
res.Message = fmt.Sprintf(
|
||||
"%s (cache saved: agvpn4=%d agvpn_dyn4=%d routes=%d iface=%s at=%s)",
|
||||
res.Message,
|
||||
cacheMeta.IPCount,
|
||||
cacheMeta.DynIPCount,
|
||||
cacheMeta.RouteCount,
|
||||
ifaceOrDash(cacheMeta.Iface),
|
||||
cacheMeta.CreatedAt,
|
||||
)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func withRoutesOpLock(opName string, fn func() cmdResult) cmdResult {
|
||||
lock, err := os.OpenFile(lockFile, os.O_CREATE|os.O_RDWR, 0o644)
|
||||
if err != nil {
|
||||
return cmdResult{
|
||||
OK: false,
|
||||
Message: fmt.Sprintf("%s lock open error: %v", opName, err),
|
||||
}
|
||||
}
|
||||
defer lock.Close()
|
||||
|
||||
if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
|
||||
return cmdResult{
|
||||
OK: false,
|
||||
Message: fmt.Sprintf("%s skipped: routes operation already running", opName),
|
||||
}
|
||||
}
|
||||
defer syscall.Flock(int(lock.Fd()), syscall.LOCK_UN)
|
||||
|
||||
return fn()
|
||||
}
|
||||
|
||||
func writeStatusSnapshot(ipCount int, iface string) error {
|
||||
if ipCount < 0 {
|
||||
ipCount = 0
|
||||
}
|
||||
iface = strings.TrimSpace(iface)
|
||||
if iface == "" {
|
||||
iface = "-"
|
||||
}
|
||||
st := Status{
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
IPCount: ipCount,
|
||||
DomainCount: countDomainsFromMap(lastIPsMapPath),
|
||||
Iface: iface,
|
||||
Table: routesTableName(),
|
||||
Mark: MARK,
|
||||
}
|
||||
data, err := json.MarshalIndent(st, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(statusFilePath, data, 0o644)
|
||||
}
|
||||
Reference in New Issue
Block a user