diff options
| author | sergei <sergei@em-sysadmin.xyz> | 2026-04-14 06:23:55 +0400 |
|---|---|---|
| committer | sergei <sergei@em-sysadmin.xyz> | 2026-04-14 06:23:55 +0400 |
| commit | 3d51aa455006903345f554a2dd90034993796114 (patch) | |
| tree | 62a7be2faf047f5eb7886feebc3b815556f03d7f /internal/config/outbounds.go | |
| download | vpnem-3d51aa455006903345f554a2dd90034993796114.tar.gz vpnem-3d51aa455006903345f554a2dd90034993796114.tar.bz2 vpnem-3d51aa455006903345f554a2dd90034993796114.zip | |
- Multi-protocol VPS nodes (VLESS-REALITY + Hysteria2 + SOCKS5)
- Smart load balancing via recommendation API
- Windows/Linux client (Go + Wails + sing-box)
- Server API with RealIP detection and connection tracking
- Auto-deployment via vpnui control plane
- Silent Windows installer with UAC elevation
- Load-based server recommendation (no sticky sessions)
- Best Server one-click connection workflow
Diffstat (limited to 'internal/config/outbounds.go')
| -rw-r--r-- | internal/config/outbounds.go | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/internal/config/outbounds.go b/internal/config/outbounds.go new file mode 100644 index 0000000..582b625 --- /dev/null +++ b/internal/config/outbounds.go @@ -0,0 +1,212 @@ +package config + +import "vpnem/internal/models" + +type InboundConfig map[string]any + +func BuildOutbound(server models.Server) map[string]any { + return BuildOutboundWithTag(server, "proxy") +} + +func BuildOutboundWithTag(server models.Server, tag string) map[string]any { + switch server.Type { + case "vless": + return buildVLESSOutbound(server, tag) + case "vless-reality": + return buildVLESSRealityOutbound(server, tag) + case "vmess": + return buildVMessOutbound(server, tag) + case "shadowsocks": + return buildShadowsocksOutbound(server, tag) + case "hysteria2": + return buildHysteria2Outbound(server, tag) + default: + return buildSOCKSOutbound(server, tag) + } +} + +func BuildHysteria2Inbound(_ any, port int, password string, obfsPassword string, upMbps int, downMbps int, certPath string, keyPath string) (*InboundConfig, error) { + if password == "" { + return nil, errConfig("hysteria2 inbound requires password") + } + if certPath == "" || keyPath == "" { + return nil, errConfig("hysteria2 inbound requires certificate and key paths") + } + inbound := InboundConfig{ + "type": "hysteria2", + "tag": "hysteria2-in", + "listen": "::", + "listen_port": port, + "users": []map[string]any{ + {"name": "user-01", "password": password}, + }, + "tls": map[string]any{ + "enabled": true, + "alpn": []string{"h3"}, + "min_version": "1.3", + "max_version": "1.3", + "certificate_path": certPath, + "key_path": keyPath, + }, + } + if upMbps > 0 { + inbound["up_mbps"] = upMbps + } + if downMbps > 0 { + inbound["down_mbps"] = downMbps + } + if obfsPassword != "" { + inbound["obfs"] = map[string]any{ + "type": "salamander", + "password": obfsPassword, + } + } + return &inbound, nil +} + +func buildVLESSOutbound(server models.Server, tag string) map[string]any { + outbound := map[string]any{ + "type": "vless", "tag": tag, + "server": server.Server, "server_port": server.ServerPort, "uuid": server.UUID, + } + applyTLS(outbound, server.TLS) + applyTransport(outbound, server.Transport) + return outbound +} + +func buildVLESSRealityOutbound(server models.Server, tag string) map[string]any { + outbound := map[string]any{ + "type": "vless", "tag": tag, + "server": server.Server, "server_port": server.ServerPort, "uuid": server.UUID, + } + applyTLS(outbound, server.TLS) + return outbound +} + +func buildVMessOutbound(server models.Server, tag string) map[string]any { + outbound := map[string]any{ + "type": "vmess", "tag": tag, + "server": server.Server, "server_port": server.ServerPort, + "uuid": server.UUID, "security": "auto", "alter_id": 0, + } + applyTLS(outbound, server.TLS) + applyTransport(outbound, server.Transport) + return outbound +} + +func buildShadowsocksOutbound(server models.Server, tag string) map[string]any { + return map[string]any{ + "type": "shadowsocks", "tag": tag, + "server": server.Server, "server_port": server.ServerPort, + "method": server.Method, "password": server.Password, + } +} + +func buildHysteria2Outbound(server models.Server, tag string) map[string]any { + outbound := map[string]any{ + "type": "hysteria2", "tag": tag, + "server": server.Server, "server_port": server.ServerPort, + "password": server.Password, + } + if server.UpMbps > 0 { + outbound["up_mbps"] = server.UpMbps + } + if server.DownMbps > 0 { + outbound["down_mbps"] = server.DownMbps + } + if server.ObfsPassword != "" { + outbound["obfs"] = map[string]any{"type": "salamander", "password": server.ObfsPassword} + } + tlsConfig := map[string]any{ + "enabled": true, + "insecure": true, + "alpn": []string{"h3"}, + "min_version": "1.3", + "max_version": "1.3", + } + if server.TLS != nil { + if server.TLS.ServerName != "" { + tlsConfig["server_name"] = server.TLS.ServerName + } + if len(server.TLS.ALPN) > 0 { + tlsConfig["alpn"] = server.TLS.ALPN + } + if server.TLS.MinVersion != "" { + tlsConfig["min_version"] = server.TLS.MinVersion + } + if server.TLS.MaxVersion != "" { + tlsConfig["max_version"] = server.TLS.MaxVersion + } + if server.TLS.Insecure { + tlsConfig["insecure"] = true + } + } + outbound["tls"] = tlsConfig + return outbound +} + +func buildSOCKSOutbound(server models.Server, tag string) map[string]any { + return map[string]any{ + "type": "socks", "tag": tag, + "server": server.Server, "server_port": server.ServerPort, + "udp_over_tcp": server.UDPOverTCP, + } +} + +func applyTLS(outbound map[string]any, tls *models.TLS) { + if tls == nil { + return + } + tlsConfig := map[string]any{ + "enabled": tls.Enabled, + "server_name": tls.ServerName, + } + if tls.Insecure { + tlsConfig["insecure"] = true + } + if len(tls.ALPN) > 0 { + tlsConfig["alpn"] = tls.ALPN + } + if tls.MinVersion != "" { + tlsConfig["min_version"] = tls.MinVersion + } + if tls.MaxVersion != "" { + tlsConfig["max_version"] = tls.MaxVersion + } + if tls.Reality != nil && tls.Reality.Enabled { + tlsConfig["reality"] = map[string]any{ + "enabled": true, + "public_key": tls.Reality.PublicKey, + "short_id": tls.Reality.ShortID, + } + if tls.Reality.Fingerprint != "" { + tlsConfig["utls"] = map[string]any{ + "enabled": true, + "fingerprint": tls.Reality.Fingerprint, + } + } + } + outbound["tls"] = tlsConfig +} + +func errConfig(message string) error { + return &configError{message: message} +} + +type configError struct { + message string +} + +func (e *configError) Error() string { + return e.message +} + +func applyTransport(outbound map[string]any, transport *models.Transport) { + if transport == nil { + return + } + outbound["transport"] = map[string]any{ + "type": transport.Type, + "path": transport.Path, + } +} |
