Files
kettuRay/core/xray_config.go
2026-03-31 14:40:03 +03:00

178 lines
3.5 KiB
Go

package core
import (
"encoding/json"
"fmt"
)
const DefaultSocksPort = 10808
// GenerateXrayConfig generates a full JSON config for xray-core.
// Xray acts as backend: listens SOCKS5 on a local port, connects to the remote server.
func GenerateXrayConfig(link *ProxyLink, socksPort int) (string, error) {
if socksPort == 0 {
socksPort = DefaultSocksPort
}
config := map[string]any{
"log": map[string]any{
"loglevel": "warning",
},
"inbounds": []any{
buildSocksInbound(socksPort),
},
"outbounds": []any{
buildProxyOutbound(link),
buildDirectOutbound(),
},
}
data, err := json.MarshalIndent(config, "", " ")
if err != nil {
return "", fmt.Errorf("failed to marshal xray config: %w", err)
}
return string(data), nil
}
func buildSocksInbound(port int) map[string]any {
return map[string]any{
"port": port,
"listen": "127.0.0.1",
"protocol": "socks",
"settings": map[string]any{
"auth": "noauth",
"udp": true,
},
"tag": "socks-in",
}
}
func buildProxyOutbound(link *ProxyLink) map[string]any {
outbound := map[string]any{
"tag": "proxy",
"protocol": link.Protocol,
}
switch link.Protocol {
case "trojan":
outbound["settings"] = buildTrojanSettings(link)
case "vless":
outbound["settings"] = buildVlessSettings(link)
}
outbound["streamSettings"] = buildStreamSettings(link)
return outbound
}
func buildTrojanSettings(link *ProxyLink) map[string]any {
return map[string]any{
"servers": []any{
map[string]any{
"address": link.Address,
"port": link.Port,
"password": link.Credential,
},
},
}
}
func buildVlessSettings(link *ProxyLink) map[string]any {
user := map[string]any{
"id": link.Credential,
"encryption": "none",
}
if link.Flow != "" {
user["flow"] = link.Flow
}
return map[string]any{
"vnext": []any{
map[string]any{
"address": link.Address,
"port": link.Port,
"users": []any{user},
},
},
}
}
func buildStreamSettings(link *ProxyLink) map[string]any {
stream := map[string]any{
"network": link.Transport,
}
// Security
if link.Security != "" && link.Security != "none" {
stream["security"] = link.Security
}
// Transport-specific settings
switch link.Transport {
case "grpc":
stream["grpcSettings"] = map[string]any{
"serviceName": link.ServiceName,
}
case "xhttp":
xhttp := map[string]any{
"path": defaultIfEmpty(link.Path, "/"),
}
if link.Host != "" {
xhttp["host"] = link.Host
}
stream["xhttpSettings"] = xhttp
case "ws":
ws := map[string]any{
"path": defaultIfEmpty(link.Path, "/"),
}
if link.Host != "" {
ws["headers"] = map[string]any{
"Host": link.Host,
}
}
stream["wsSettings"] = ws
case "h2", "http":
h2 := map[string]any{
"path": defaultIfEmpty(link.Path, "/"),
}
if link.Host != "" {
h2["host"] = []string{link.Host}
}
stream["httpSettings"] = h2
}
// Security-specific settings
switch link.Security {
case "reality":
stream["realitySettings"] = map[string]any{
"publicKey": link.PublicKey,
"shortId": link.ShortId,
"serverName": link.Sni,
"fingerprint": link.Fingerprint,
}
case "tls":
tls := map[string]any{
"fingerprint": link.Fingerprint,
}
if link.Sni != "" {
tls["serverName"] = link.Sni
}
stream["tlsSettings"] = tls
}
return stream
}
func buildDirectOutbound() map[string]any {
return map[string]any{
"tag": "direct",
"protocol": "freedom",
}
}
func defaultIfEmpty(s, def string) string {
if s == "" {
return def
}
return s
}