211 lines
4.5 KiB
Go
211 lines
4.5 KiB
Go
package transportcfg
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
KindSingBox = "singbox"
|
|
KindDNSTT = "dnstt"
|
|
KindPhoenix = "phoenix"
|
|
|
|
defaultSingBoxUnitTemplate = "singbox@.service"
|
|
)
|
|
|
|
type Client struct {
|
|
ID string
|
|
Kind string
|
|
Config map[string]any
|
|
}
|
|
|
|
func BackendUnit(client Client) string {
|
|
kind := strings.ToLower(strings.TrimSpace(client.Kind))
|
|
unit := strings.TrimSpace(ConfigString(client.Config, "unit"))
|
|
if kind == KindSingBox {
|
|
return resolveSingBoxBackendUnit(client.ID, unit)
|
|
}
|
|
if unit != "" {
|
|
return unit
|
|
}
|
|
return DefaultBackendUnit(kind)
|
|
}
|
|
|
|
func DefaultBackendUnit(kind string) string {
|
|
switch strings.ToLower(strings.TrimSpace(kind)) {
|
|
case KindSingBox:
|
|
return defaultSingBoxUnitTemplate
|
|
case KindDNSTT:
|
|
return "dnstt-client.service"
|
|
case KindPhoenix:
|
|
return "phoenix.service"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func resolveSingBoxBackendUnit(clientID, configuredUnit string) string {
|
|
unit := strings.TrimSpace(configuredUnit)
|
|
if unit == "" {
|
|
unit = defaultSingBoxUnitTemplate
|
|
}
|
|
if strings.HasSuffix(unit, "@.service") {
|
|
instance := sanitizeSystemdInstanceID(clientID)
|
|
if instance == "" {
|
|
instance = "client"
|
|
}
|
|
return strings.TrimSuffix(unit, "@.service") + "@" + instance + ".service"
|
|
}
|
|
return unit
|
|
}
|
|
|
|
func sanitizeSystemdInstanceID(in string) string {
|
|
s := strings.ToLower(strings.TrimSpace(in))
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
var b strings.Builder
|
|
b.Grow(len(s))
|
|
lastDash := false
|
|
for i := 0; i < len(s); i++ {
|
|
ch := s[i]
|
|
if (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '.' {
|
|
b.WriteByte(ch)
|
|
lastDash = false
|
|
continue
|
|
}
|
|
if ch == '-' {
|
|
if lastDash {
|
|
continue
|
|
}
|
|
b.WriteByte('-')
|
|
lastDash = true
|
|
continue
|
|
}
|
|
if !lastDash {
|
|
b.WriteByte('-')
|
|
lastDash = true
|
|
}
|
|
}
|
|
return strings.Trim(b.String(), "-")
|
|
}
|
|
|
|
func DNSTTSSHTunnelEnabled(client Client) bool {
|
|
if strings.ToLower(strings.TrimSpace(client.Kind)) != KindDNSTT {
|
|
return false
|
|
}
|
|
return ConfigBool(client.Config, "ssh_tunnel") || ConfigBool(client.Config, "ssh_overlay")
|
|
}
|
|
|
|
func DNSTTSSHUnit(client Client) string {
|
|
unit := strings.TrimSpace(ConfigString(client.Config, "ssh_unit"))
|
|
if unit != "" {
|
|
return unit
|
|
}
|
|
return "dnstt-ssh-tunnel.service"
|
|
}
|
|
|
|
func RuntimeMode(cfg map[string]any) string {
|
|
mode := strings.ToLower(strings.TrimSpace(ConfigString(cfg, "runtime_mode")))
|
|
switch mode {
|
|
case "", "exec", "external", "companion":
|
|
return "exec"
|
|
case "embedded":
|
|
return "embedded"
|
|
case "sidecar":
|
|
return "sidecar"
|
|
default:
|
|
return mode
|
|
}
|
|
}
|
|
|
|
func SystemdActionUnits(client Client, action string) ([]string, string, string) {
|
|
unit := BackendUnit(client)
|
|
if unit == "" {
|
|
return nil, "TRANSPORT_BACKEND_UNIT_REQUIRED", "systemd unit is required"
|
|
}
|
|
if !DNSTTSSHTunnelEnabled(client) {
|
|
return []string{unit}, "", ""
|
|
}
|
|
sshUnit := DNSTTSSHUnit(client)
|
|
if strings.TrimSpace(sshUnit) == "" {
|
|
return nil, "TRANSPORT_BACKEND_UNIT_REQUIRED", "dnstt ssh tunnel unit is required"
|
|
}
|
|
switch action {
|
|
case "stop":
|
|
return []string{unit, sshUnit}, "", ""
|
|
default:
|
|
return []string{sshUnit, unit}, "", ""
|
|
}
|
|
}
|
|
|
|
func SystemdHealthUnits(client Client) ([]string, string, string) {
|
|
unit := BackendUnit(client)
|
|
if unit == "" {
|
|
return nil, "TRANSPORT_BACKEND_UNIT_REQUIRED", "systemd unit is required"
|
|
}
|
|
units := []string{unit}
|
|
if DNSTTSSHTunnelEnabled(client) {
|
|
sshUnit := DNSTTSSHUnit(client)
|
|
if strings.TrimSpace(sshUnit) == "" {
|
|
return nil, "TRANSPORT_BACKEND_UNIT_REQUIRED", "dnstt ssh tunnel unit is required"
|
|
}
|
|
units = append(units, sshUnit)
|
|
}
|
|
return units, "", ""
|
|
}
|
|
|
|
func ConfigString(cfg map[string]any, key string) string {
|
|
if cfg == nil {
|
|
return ""
|
|
}
|
|
raw, ok := cfg[key]
|
|
if !ok || raw == nil {
|
|
return ""
|
|
}
|
|
switch v := raw.(type) {
|
|
case string:
|
|
return strings.TrimSpace(v)
|
|
default:
|
|
return strings.TrimSpace(fmt.Sprint(v))
|
|
}
|
|
}
|
|
|
|
func ConfigBool(cfg map[string]any, key string) bool {
|
|
if cfg == nil {
|
|
return false
|
|
}
|
|
raw, ok := cfg[key]
|
|
if !ok || raw == nil {
|
|
return false
|
|
}
|
|
switch v := raw.(type) {
|
|
case bool:
|
|
return v
|
|
case string:
|
|
s := strings.ToLower(strings.TrimSpace(v))
|
|
return s == "1" || s == "true" || s == "yes" || s == "on"
|
|
case float64:
|
|
return v != 0
|
|
case int:
|
|
return v != 0
|
|
default:
|
|
s := strings.ToLower(strings.TrimSpace(fmt.Sprint(v)))
|
|
return s == "1" || s == "true" || s == "yes" || s == "on"
|
|
}
|
|
}
|
|
|
|
func ConfigHasKey(cfg map[string]any, key string) bool {
|
|
if cfg == nil {
|
|
return false
|
|
}
|
|
raw, ok := cfg[key]
|
|
if !ok || raw == nil {
|
|
return false
|
|
}
|
|
if s, ok := raw.(string); ok {
|
|
return strings.TrimSpace(s) != ""
|
|
}
|
|
return true
|
|
}
|