修复SS2022订阅下发错误的问题
This commit is contained in:
@@ -264910,7 +264910,7 @@ function dqt({ className: e }) {
|
||||
e.change >= 0
|
||||
? Q.jsx(iat, { className: "mr-1 h-3 w-3" })
|
||||
: Q.jsx(eat, { className: "mr-1 h-3 w-3" }),
|
||||
Math.abs(e.change),
|
||||
Math.abs(Number(e.change || 0)).toFixed(2),
|
||||
"%",
|
||||
],
|
||||
}),
|
||||
@@ -264971,7 +264971,11 @@ function dqt({ className: e }) {
|
||||
"font-medium",
|
||||
e.change >= 0 ? "text-green-600" : "text-red-600",
|
||||
),
|
||||
children: [e.change >= 0 ? "+" : "", e.change, "%"],
|
||||
children: [
|
||||
e.change >= 0 ? "+" : "",
|
||||
Number(e.change || 0).toFixed(2),
|
||||
"%",
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@@ -265063,7 +265067,7 @@ function dqt({ className: e }) {
|
||||
e.change >= 0
|
||||
? Q.jsx(iat, { className: "mr-1 h-3 w-3" })
|
||||
: Q.jsx(eat, { className: "mr-1 h-3 w-3" }),
|
||||
Math.abs(e.change),
|
||||
Math.abs(Number(e.change || 0)).toFixed(2),
|
||||
"%",
|
||||
],
|
||||
}),
|
||||
@@ -265124,7 +265128,11 @@ function dqt({ className: e }) {
|
||||
"font-medium",
|
||||
e.change >= 0 ? "text-green-600" : "text-red-600",
|
||||
),
|
||||
children: [e.change >= 0 ? "+" : "", e.change, "%"],
|
||||
children: [
|
||||
e.change >= 0 ? "+" : "",
|
||||
Number(e.change || 0).toFixed(2),
|
||||
"%",
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@@ -272190,8 +272198,8 @@ function HGt({ open: e, onOpenChange: t, result: n }) {
|
||||
Q.jsx("div", { children: n.config.encryption || "无" }),
|
||||
Q.jsx("div", { className: "text-muted-foreground", children: "发件人" }),
|
||||
Q.jsx("div", {
|
||||
children: n.config.from.address
|
||||
? `${n.config.from.address}${n.config.from.name ? ` (${n.config.from.name})` : ""}`
|
||||
children: (n.config.from?.address || n.config.from_address)
|
||||
? `${n.config.from?.address || n.config.from_address}${n.config.from?.name || n.config.from_name ? ` (${n.config.from?.name || n.config.from_name})` : ""}`
|
||||
: "未设置",
|
||||
}),
|
||||
Q.jsx("div", { className: "text-muted-foreground", children: "用户名" }),
|
||||
|
||||
@@ -28,7 +28,8 @@ func GenerateClashWithTemplate(templateName string, servers []model.Server, user
|
||||
proxyNames := make([]string, 0, len(servers))
|
||||
for _, s := range servers {
|
||||
conf := service.BuildNodeConfig(&s)
|
||||
proxy := buildClashProxy(conf, user)
|
||||
password := service.GenerateServerPassword(&s, &user)
|
||||
proxy := buildClashProxy(conf, password)
|
||||
if proxy == nil {
|
||||
continue
|
||||
}
|
||||
@@ -52,7 +53,7 @@ func GenerateClashWithTemplate(templateName string, servers []model.Server, user
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func buildClashProxy(conf service.NodeServerConfig, user model.User) map[string]any {
|
||||
func buildClashProxy(conf service.NodeServerConfig, password string) map[string]any {
|
||||
switch conf.Protocol {
|
||||
case "shadowsocks":
|
||||
cipher, _ := conf.Cipher.(string)
|
||||
@@ -62,7 +63,7 @@ func buildClashProxy(conf service.NodeServerConfig, user model.User) map[string]
|
||||
"server": conf.RawHost,
|
||||
"port": conf.ServerPort,
|
||||
"cipher": cipher,
|
||||
"password": user.UUID,
|
||||
"password": password,
|
||||
}
|
||||
case "vmess":
|
||||
return map[string]any{
|
||||
@@ -70,7 +71,7 @@ func buildClashProxy(conf service.NodeServerConfig, user model.User) map[string]
|
||||
"type": "vmess",
|
||||
"server": conf.RawHost,
|
||||
"port": conf.ServerPort,
|
||||
"uuid": user.UUID,
|
||||
"uuid": password,
|
||||
"alterId": 0,
|
||||
"cipher": "auto",
|
||||
"udp": true,
|
||||
@@ -81,7 +82,7 @@ func buildClashProxy(conf service.NodeServerConfig, user model.User) map[string]
|
||||
"type": "trojan",
|
||||
"server": conf.RawHost,
|
||||
"port": conf.ServerPort,
|
||||
"password": user.UUID,
|
||||
"password": password,
|
||||
"udp": true,
|
||||
}
|
||||
default:
|
||||
@@ -161,7 +162,8 @@ func generateClashFallback(servers []model.Server, user model.User) string {
|
||||
var proxyNames []string
|
||||
for _, s := range servers {
|
||||
conf := service.BuildNodeConfig(&s)
|
||||
proxy := buildClashProxy(conf, user)
|
||||
password := service.GenerateServerPassword(&s, &user)
|
||||
proxy := buildClashProxy(conf, password)
|
||||
if proxy == nil {
|
||||
continue
|
||||
}
|
||||
@@ -174,7 +176,7 @@ func generateClashFallback(servers []model.Server, user model.User) string {
|
||||
conf.RawHost,
|
||||
conf.ServerPort,
|
||||
fmt.Sprint(proxy["cipher"]),
|
||||
user.UUID,
|
||||
fmt.Sprint(proxy["password"]),
|
||||
))
|
||||
case "vmess":
|
||||
builder.WriteString(fmt.Sprintf(
|
||||
@@ -182,7 +184,7 @@ func generateClashFallback(servers []model.Server, user model.User) string {
|
||||
conf.Name,
|
||||
conf.RawHost,
|
||||
conf.ServerPort,
|
||||
user.UUID,
|
||||
fmt.Sprint(proxy["uuid"]),
|
||||
))
|
||||
case "trojan":
|
||||
builder.WriteString(fmt.Sprintf(
|
||||
@@ -190,7 +192,7 @@ func generateClashFallback(servers []model.Server, user model.User) string {
|
||||
conf.Name,
|
||||
conf.RawHost,
|
||||
conf.ServerPort,
|
||||
user.UUID,
|
||||
fmt.Sprint(proxy["password"]),
|
||||
))
|
||||
}
|
||||
proxyNames = append(proxyNames, fmt.Sprintf("\"%s\"", conf.Name))
|
||||
|
||||
@@ -14,7 +14,8 @@ func GenerateGeneralLinks(servers []model.Server, user model.User) string {
|
||||
var links []string
|
||||
for _, s := range servers {
|
||||
conf := service.BuildNodeConfig(&s)
|
||||
link := BuildLink(conf, user)
|
||||
password := service.GenerateServerPassword(&s, &user)
|
||||
link := BuildLink(conf, password)
|
||||
if link != "" {
|
||||
links = append(links, link)
|
||||
}
|
||||
@@ -22,33 +23,32 @@ func GenerateGeneralLinks(servers []model.Server, user model.User) string {
|
||||
return strings.Join(links, "\n")
|
||||
}
|
||||
|
||||
func BuildLink(c service.NodeServerConfig, user model.User) string {
|
||||
func BuildLink(c service.NodeServerConfig, password string) string {
|
||||
switch c.Protocol {
|
||||
case "shadowsocks":
|
||||
return buildShadowsocks(c, user)
|
||||
return buildShadowsocks(c, password)
|
||||
case "vmess":
|
||||
return buildVmess(c, user)
|
||||
return buildVmess(c, password)
|
||||
case "vless":
|
||||
return buildVless(c, user)
|
||||
return buildVless(c, password)
|
||||
case "trojan":
|
||||
return buildTrojan(c, user)
|
||||
return buildTrojan(c, password)
|
||||
case "hysteria", "hysteria2":
|
||||
return buildHysteria(c, user)
|
||||
return buildHysteria(c, password)
|
||||
case "tuic":
|
||||
return buildTuic(c, user)
|
||||
return buildTuic(c, password)
|
||||
case "anytls":
|
||||
return buildAnyTLS(c, user)
|
||||
return buildAnyTLS(c, password)
|
||||
case "socks":
|
||||
return buildSocks(c, user)
|
||||
return buildSocks(c, password)
|
||||
case "http":
|
||||
return buildHttp(c, user)
|
||||
return buildHttp(c, password)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func buildShadowsocks(c service.NodeServerConfig, user model.User) string {
|
||||
func buildShadowsocks(c service.NodeServerConfig, password string) string {
|
||||
cipher := toString(c.Cipher)
|
||||
password := user.UUID
|
||||
userInfo := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", cipher, password)))
|
||||
userInfo = strings.TrimRight(strings.ReplaceAll(strings.ReplaceAll(userInfo, "+", "-"), "/", "_"), "=")
|
||||
|
||||
@@ -64,13 +64,13 @@ func buildShadowsocks(c service.NodeServerConfig, user model.User) string {
|
||||
return link + "#" + url.PathEscape(c.Name)
|
||||
}
|
||||
|
||||
func buildVmess(c service.NodeServerConfig, user model.User) string {
|
||||
func buildVmess(c service.NodeServerConfig, password string) string {
|
||||
m := map[string]any{
|
||||
"v": "2",
|
||||
"ps": c.Name,
|
||||
"add": c.RawHost,
|
||||
"port": fmt.Sprintf("%d", c.ServerPort),
|
||||
"id": user.UUID,
|
||||
"id": password,
|
||||
"aid": "0",
|
||||
"net": toString(c.Network),
|
||||
"type": "none",
|
||||
@@ -105,7 +105,7 @@ func buildVmess(c service.NodeServerConfig, user model.User) string {
|
||||
return "vmess://" + base64.StdEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
func buildVless(c service.NodeServerConfig, user model.User) string {
|
||||
func buildVless(c service.NodeServerConfig, password string) string {
|
||||
params := url.Values{}
|
||||
params.Set("encryption", "none")
|
||||
if c.Flow != nil {
|
||||
@@ -145,10 +145,10 @@ func buildVless(c service.NodeServerConfig, user model.User) string {
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("vless://%s@%s:%d?%s#%s", user.UUID, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
return fmt.Sprintf("vless://%s@%s:%d?%s#%s", password, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
func buildTrojan(c service.NodeServerConfig, user model.User) string {
|
||||
func buildTrojan(c service.NodeServerConfig, password string) string {
|
||||
params := url.Values{}
|
||||
security := "tls"
|
||||
if toInt(c.Tls) == 2 {
|
||||
@@ -176,10 +176,10 @@ func buildTrojan(c service.NodeServerConfig, user model.User) string {
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("trojan://%s@%s:%d?%s#%s", user.UUID, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
return fmt.Sprintf("trojan://%s@%s:%d?%s#%s", password, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
func buildHysteria(c service.NodeServerConfig, user model.User) string {
|
||||
func buildHysteria(c service.NodeServerConfig, password string) string {
|
||||
params := url.Values{}
|
||||
params.Set("sni", toString(c.ServerName))
|
||||
|
||||
@@ -188,39 +188,43 @@ func buildHysteria(c service.NodeServerConfig, user model.User) string {
|
||||
params.Set("obfs", "salamander")
|
||||
params.Set("obfs-password", toString(c.ObfsPassword))
|
||||
}
|
||||
return fmt.Sprintf("hysteria2://%s@%s:%d?%s#%s", user.UUID, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
return fmt.Sprintf("hysteria2://%s@%s:%d?%s#%s", password, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
params.Set("protocol", "udp")
|
||||
params.Set("auth", user.UUID)
|
||||
if c.UpMbps != nil { params.Set("upmbps", fmt.Sprintf("%v", c.UpMbps)) }
|
||||
if c.DownMbps != nil { params.Set("downmbps", fmt.Sprintf("%v", c.DownMbps)) }
|
||||
params.Set("auth", password)
|
||||
if c.UpMbps != nil {
|
||||
params.Set("upmbps", fmt.Sprintf("%v", c.UpMbps))
|
||||
}
|
||||
if c.DownMbps != nil {
|
||||
params.Set("downmbps", fmt.Sprintf("%v", c.DownMbps))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("hysteria://%s:%d?%s#%s", wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
func buildTuic(c service.NodeServerConfig, user model.User) string {
|
||||
func buildTuic(c service.NodeServerConfig, password string) string {
|
||||
params := url.Values{}
|
||||
params.Set("sni", toString(c.ServerName))
|
||||
params.Set("congestion_control", toString(c.CongestionControl))
|
||||
params.Set("udp-relay-mode", "native")
|
||||
|
||||
return fmt.Sprintf("tuic://%s:%s@%s:%d?%s#%s", user.UUID, user.UUID, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
return fmt.Sprintf("tuic://%s:%s@%s:%d?%s#%s", password, password, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
func buildAnyTLS(c service.NodeServerConfig, user model.User) string {
|
||||
func buildAnyTLS(c service.NodeServerConfig, password string) string {
|
||||
params := url.Values{}
|
||||
params.Set("sni", toString(c.ServerName))
|
||||
return fmt.Sprintf("anytls://%s@%s:%d?%s#%s", user.UUID, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
return fmt.Sprintf("anytls://%s@%s:%d?%s#%s", password, wrapIPv6(c.RawHost), c.ServerPort, params.Encode(), url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
func buildSocks(c service.NodeServerConfig, user model.User) string {
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(user.UUID + ":" + user.UUID))
|
||||
func buildSocks(c service.NodeServerConfig, password string) string {
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(password + ":" + password))
|
||||
return fmt.Sprintf("socks://%s@%s:%d#%s", auth, wrapIPv6(c.RawHost), c.ServerPort, url.PathEscape(c.Name))
|
||||
}
|
||||
|
||||
func buildHttp(c service.NodeServerConfig, user model.User) string {
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(user.UUID + ":" + user.UUID))
|
||||
func buildHttp(c service.NodeServerConfig, password string) string {
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(password + ":" + password))
|
||||
link := fmt.Sprintf("http://%s@%s:%d", auth, wrapIPv6(c.RawHost), c.ServerPort)
|
||||
if toInt(c.Tls) > 0 {
|
||||
params := url.Values{}
|
||||
@@ -238,16 +242,23 @@ func wrapIPv6(host string) string {
|
||||
}
|
||||
|
||||
func toString(v any) string {
|
||||
if s, ok := v.(string); ok { return s }
|
||||
if v == nil { return "" }
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
}
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%v", v)
|
||||
}
|
||||
|
||||
func toInt(v any) int {
|
||||
switch typed := v.(type) {
|
||||
case int: return typed
|
||||
case int64: return int(typed)
|
||||
case float64: return int(typed)
|
||||
case int:
|
||||
return typed
|
||||
case int64:
|
||||
return int(typed)
|
||||
case float64:
|
||||
return int(typed)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ func GenerateSingBox(servers []model.Server, user model.User) (string, error) {
|
||||
proxyTags := make([]string, 0, len(servers))
|
||||
for _, s := range servers {
|
||||
conf := service.BuildNodeConfig(&s)
|
||||
outbound := buildSingBoxOutbound(conf, user)
|
||||
password := service.GenerateServerPassword(&s, &user)
|
||||
outbound := buildSingBoxOutbound(conf, password)
|
||||
if outbound == nil {
|
||||
continue
|
||||
}
|
||||
@@ -59,7 +60,7 @@ func GenerateSingBox(servers []model.Server, user model.User) (string, error) {
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func buildSingBoxOutbound(conf service.NodeServerConfig, user model.User) map[string]any {
|
||||
func buildSingBoxOutbound(conf service.NodeServerConfig, password string) map[string]any {
|
||||
outbound := map[string]any{
|
||||
"tag": conf.Name,
|
||||
"server": conf.RawHost,
|
||||
@@ -70,14 +71,14 @@ func buildSingBoxOutbound(conf service.NodeServerConfig, user model.User) map[st
|
||||
case "shadowsocks":
|
||||
outbound["type"] = "shadowsocks"
|
||||
outbound["method"] = conf.Cipher
|
||||
outbound["password"] = user.UUID
|
||||
outbound["password"] = password
|
||||
case "vmess":
|
||||
outbound["type"] = "vmess"
|
||||
outbound["uuid"] = user.UUID
|
||||
outbound["uuid"] = password
|
||||
outbound["security"] = "auto"
|
||||
case "trojan":
|
||||
outbound["type"] = "trojan"
|
||||
outbound["password"] = user.UUID
|
||||
outbound["password"] = password
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -91,6 +92,7 @@ func generateSingBoxFallback(servers []model.Server, user model.User) (string, e
|
||||
|
||||
for _, s := range servers {
|
||||
conf := service.BuildNodeConfig(&s)
|
||||
password := service.GenerateServerPassword(&s, &user)
|
||||
outbound := map[string]interface{}{
|
||||
"tag": conf.Name,
|
||||
"server": conf.RawHost,
|
||||
@@ -101,14 +103,14 @@ func generateSingBoxFallback(servers []model.Server, user model.User) (string, e
|
||||
case "shadowsocks":
|
||||
outbound["type"] = "shadowsocks"
|
||||
outbound["method"] = conf.Cipher
|
||||
outbound["password"] = user.UUID
|
||||
outbound["password"] = password
|
||||
case "vmess":
|
||||
outbound["type"] = "vmess"
|
||||
outbound["uuid"] = user.UUID
|
||||
outbound["uuid"] = password
|
||||
outbound["security"] = "auto"
|
||||
case "trojan":
|
||||
outbound["type"] = "trojan"
|
||||
outbound["password"] = user.UUID
|
||||
outbound["password"] = password
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -57,11 +57,15 @@ func LoadEmailConfig() EmailConfig {
|
||||
|
||||
func (cfg EmailConfig) DebugConfig() map[string]any {
|
||||
return map[string]any{
|
||||
"driver": "smtp",
|
||||
"host": cfg.Host,
|
||||
"port": cfg.Port,
|
||||
"encryption": cfg.Encryption,
|
||||
"username": cfg.Username,
|
||||
"driver": "smtp",
|
||||
"host": cfg.Host,
|
||||
"port": cfg.Port,
|
||||
"encryption": cfg.Encryption,
|
||||
"username": cfg.Username,
|
||||
"from": map[string]any{
|
||||
"address": cfg.SenderAddress(),
|
||||
"name": cfg.FromName,
|
||||
},
|
||||
"from_address": cfg.SenderAddress(),
|
||||
"from_name": cfg.FromName,
|
||||
}
|
||||
|
||||
@@ -21,17 +21,35 @@ var serverTypeAliases = map[string]string{
|
||||
}
|
||||
|
||||
var validServerTypes = map[string]struct{}{
|
||||
"anytls": {},
|
||||
"http": {},
|
||||
"hysteria": {},
|
||||
"mieru": {},
|
||||
"naive": {},
|
||||
"anytls": {},
|
||||
"http": {},
|
||||
"hysteria": {},
|
||||
"mieru": {},
|
||||
"naive": {},
|
||||
"shadowsocks": {},
|
||||
"socks": {},
|
||||
"trojan": {},
|
||||
"tuic": {},
|
||||
"vless": {},
|
||||
"vmess": {},
|
||||
"socks": {},
|
||||
"trojan": {},
|
||||
"tuic": {},
|
||||
"vless": {},
|
||||
"vmess": {},
|
||||
}
|
||||
|
||||
var shadowsocks2022CipherConfigs = map[string]struct {
|
||||
serverKeySize int
|
||||
userKeySize int
|
||||
}{
|
||||
"2022-blake3-aes-128-gcm": {
|
||||
serverKeySize: 16,
|
||||
userKeySize: 16,
|
||||
},
|
||||
"2022-blake3-aes-256-gcm": {
|
||||
serverKeySize: 32,
|
||||
userKeySize: 32,
|
||||
},
|
||||
"2022-blake3-chacha20-poly1305": {
|
||||
serverKeySize: 32,
|
||||
userKeySize: 32,
|
||||
},
|
||||
}
|
||||
|
||||
type NodeUser struct {
|
||||
@@ -47,41 +65,41 @@ type NodeBaseConfig struct {
|
||||
}
|
||||
|
||||
type NodeServerConfig struct {
|
||||
Name string `json:"-"`
|
||||
Protocol string `json:"protocol"`
|
||||
RawHost string `json:"-"`
|
||||
ListenIP string `json:"listen_ip"`
|
||||
ServerPort int `json:"server_port"`
|
||||
Network any `json:"network"`
|
||||
NetworkSettings any `json:"networkSettings"`
|
||||
Cipher any `json:"cipher,omitempty"`
|
||||
Plugin any `json:"plugin,omitempty"`
|
||||
PluginOpts any `json:"plugin_opts,omitempty"`
|
||||
ServerKey any `json:"server_key,omitempty"`
|
||||
Host any `json:"host,omitempty"`
|
||||
ServerName any `json:"server_name,omitempty"`
|
||||
Tls any `json:"tls,omitempty"`
|
||||
TlsSettings any `json:"tls_settings,omitempty"`
|
||||
Flow any `json:"flow,omitempty"`
|
||||
Multiplex any `json:"multiplex,omitempty"`
|
||||
UpMbps any `json:"up_mbps,omitempty"`
|
||||
DownMbps any `json:"down_mbps,omitempty"`
|
||||
Version any `json:"version,omitempty"`
|
||||
Obfs any `json:"obfs,omitempty"`
|
||||
ObfsPassword any `json:"obfs-password,omitempty"`
|
||||
CongestionControl any `json:"congestion_control,omitempty"`
|
||||
AuthTimeout any `json:"auth_timeout,omitempty"`
|
||||
ZeroRTTHandshake any `json:"zero_rtt_handshake,omitempty"`
|
||||
Heartbeat any `json:"heartbeat,omitempty"`
|
||||
PaddingScheme any `json:"padding_scheme,omitempty"`
|
||||
Transport any `json:"transport,omitempty"`
|
||||
TrafficPattern any `json:"traffic_pattern,omitempty"`
|
||||
Decryption any `json:"decryption,omitempty"`
|
||||
Routes []model.ServerRoute `json:"routes,omitempty"`
|
||||
CustomOutbounds any `json:"custom_outbounds,omitempty"`
|
||||
CustomRoutes any `json:"custom_routes,omitempty"`
|
||||
CertConfig any `json:"cert_config,omitempty"`
|
||||
BaseConfig NodeBaseConfig `json:"base_config"`
|
||||
Name string `json:"-"`
|
||||
Protocol string `json:"protocol"`
|
||||
RawHost string `json:"-"`
|
||||
ListenIP string `json:"listen_ip"`
|
||||
ServerPort int `json:"server_port"`
|
||||
Network any `json:"network"`
|
||||
NetworkSettings any `json:"networkSettings"`
|
||||
Cipher any `json:"cipher,omitempty"`
|
||||
Plugin any `json:"plugin,omitempty"`
|
||||
PluginOpts any `json:"plugin_opts,omitempty"`
|
||||
ServerKey any `json:"server_key,omitempty"`
|
||||
Host any `json:"host,omitempty"`
|
||||
ServerName any `json:"server_name,omitempty"`
|
||||
Tls any `json:"tls,omitempty"`
|
||||
TlsSettings any `json:"tls_settings,omitempty"`
|
||||
Flow any `json:"flow,omitempty"`
|
||||
Multiplex any `json:"multiplex,omitempty"`
|
||||
UpMbps any `json:"up_mbps,omitempty"`
|
||||
DownMbps any `json:"down_mbps,omitempty"`
|
||||
Version any `json:"version,omitempty"`
|
||||
Obfs any `json:"obfs,omitempty"`
|
||||
ObfsPassword any `json:"obfs-password,omitempty"`
|
||||
CongestionControl any `json:"congestion_control,omitempty"`
|
||||
AuthTimeout any `json:"auth_timeout,omitempty"`
|
||||
ZeroRTTHandshake any `json:"zero_rtt_handshake,omitempty"`
|
||||
Heartbeat any `json:"heartbeat,omitempty"`
|
||||
PaddingScheme any `json:"padding_scheme,omitempty"`
|
||||
Transport any `json:"transport,omitempty"`
|
||||
TrafficPattern any `json:"traffic_pattern,omitempty"`
|
||||
Decryption any `json:"decryption,omitempty"`
|
||||
Routes []model.ServerRoute `json:"routes,omitempty"`
|
||||
CustomOutbounds any `json:"custom_outbounds,omitempty"`
|
||||
CustomRoutes any `json:"custom_routes,omitempty"`
|
||||
CertConfig any `json:"cert_config,omitempty"`
|
||||
BaseConfig NodeBaseConfig `json:"base_config"`
|
||||
}
|
||||
|
||||
func NormalizeServerType(serverType string) string {
|
||||
@@ -165,6 +183,31 @@ func AvailableServersForUser(user *model.User) ([]model.Server, error) {
|
||||
return filtered, nil
|
||||
}
|
||||
|
||||
func GenerateServerPassword(node *model.Server, user *model.User) string {
|
||||
if node == nil || user == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if NormalizeServerType(node.Type) != "shadowsocks" {
|
||||
return user.UUID
|
||||
}
|
||||
|
||||
settings := parseObject(node.ProtocolSettings)
|
||||
cipher := getMapString(settings, "cipher")
|
||||
config, ok := shadowsocks2022CipherConfigs[cipher]
|
||||
if !ok {
|
||||
return user.UUID
|
||||
}
|
||||
|
||||
createdAt := resolveServerCreatedAt(node)
|
||||
serverKey := serverKey(createdAt, config.serverKeySize)
|
||||
if serverKey == "" {
|
||||
return user.UUID
|
||||
}
|
||||
|
||||
return serverKey + ":" + uuidPrefixBase64(user.UUID, config.userKeySize)
|
||||
}
|
||||
|
||||
func CurrentRate(server *model.Server) float64 {
|
||||
if !server.RateTimeEnable {
|
||||
return float64(server.Rate)
|
||||
@@ -322,7 +365,10 @@ func serverKey(createdAt *time.Time, size int) string {
|
||||
if createdAt == nil {
|
||||
return ""
|
||||
}
|
||||
sum := md5.Sum([]byte(strconv.FormatInt(createdAt.Unix(), 10)))
|
||||
|
||||
// Match XBoard's Helper::getServerKey(created_at, size):
|
||||
// base64_encode(substr(md5($timestamp), 0, size))
|
||||
sum := md5.Sum([]byte(createdAt.Format("2006-01-02 15:04:05")))
|
||||
hex := fmt.Sprintf("%x", sum)
|
||||
if size > len(hex) {
|
||||
size = len(hex)
|
||||
@@ -330,6 +376,38 @@ func serverKey(createdAt *time.Time, size int) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(hex[:size]))
|
||||
}
|
||||
|
||||
func resolveServerCreatedAt(node *model.Server) *time.Time {
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if node.ParentID == nil || *node.ParentID <= 0 {
|
||||
return node.CreatedAt
|
||||
}
|
||||
|
||||
var parent struct {
|
||||
CreatedAt *time.Time `gorm:"column:created_at"`
|
||||
}
|
||||
if err := database.DB.Model(&model.Server{}).
|
||||
Select("created_at").
|
||||
Where("id = ?", *node.ParentID).
|
||||
First(&parent).Error; err == nil && parent.CreatedAt != nil {
|
||||
return parent.CreatedAt
|
||||
}
|
||||
|
||||
return node.CreatedAt
|
||||
}
|
||||
|
||||
func uuidPrefixBase64(uuid string, size int) string {
|
||||
if size <= 0 {
|
||||
return ""
|
||||
}
|
||||
if size > len(uuid) {
|
||||
size = len(uuid)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString([]byte(uuid[:size]))
|
||||
}
|
||||
|
||||
func parseIntSlice(raw *string) []int {
|
||||
if raw == nil || strings.TrimSpace(*raw) == "" {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user