Files

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
}