Skip to content

Конфигурации нод

Конфигурация — это переиспользуемый шаблон Xray inbound. Создаёшь его один раз в админке, потом привязываешь к любым нодам. При привязке агент применяет inbound к Xray на этой ноде.

server_configs (шаблоны) node_configs (привязки)
┌─────────────────────┐ ┌──────────────────────────────┐
│ VLESS TCP Reality │────┐ │ node: NL-1, port: 443 │
│ VLESS XHTTP TLS │ ├───▶│ node: DE-1, port: 443 │
│ Trojan WS TLS │ │ │ node: US-1, port: 8443 │
└─────────────────────┘ └───▶│ node: JP-1, port: 443 │
└──────────────────────────────┘

Поддерживаемые комбинации

Section titled “Поддерживаемые комбинации”
Протокол Транспорт Безопасность Кейс
VLESS TCP Reality Цензурозащищённый, без CDN
VLESS TCP XTLS Максимальная скорость
VLESS XHTTP TLS CDN (Cloudflare), обход DPI
VLESS XHTTP Reality Новый транспорт + Reality
VLESS WS TLS Классика с CDN
VLESS gRPC TLS CDN, мобильные сети
Trojan WS TLS Альтернатива VLESS
Trojan gRPC TLS CDN-compatible
VMess WS TLS Обратная совместимость
Shadowsocks TCP Простой, быстрый

server_configs — шаблоны конфигураций

Section titled “server_configs — шаблоны конфигураций”
id uuid PRIMARY KEY
name varchar -- "VLESS TCP Reality (Fast)"
description text
protocol enum(vless, vmess, trojan, shadowsocks)
transport enum(tcp, ws, grpc, xhttp, quic)
security enum(none, tls, reality)
-- Настройки транспорта (зависят от поля transport)
transport_settings jsonb
-- ws: { "path": "/ws", "host": "domain.com" }
-- grpc: { "serviceName": "grpc" }
-- xhttp: { "path": "/xhttp", "mode": "stream-one" }
-- Настройки безопасности (зависят от поля security)
security_settings jsonb
-- tls: { "serverName": "domain.com" }
-- reality: { "dest": "www.google.com:443", "serverNames": [...],
-- "privateKey": "...", "publicKey": "...",
-- "shortIds": [...], "fingerprint": "chrome" }
-- VLESS-специфичные настройки
flow varchar null -- "xtls-rprx-vision" для Reality/XTLS, иначе null
sniffing bool DEFAULT true
is_active bool DEFAULT true
created_at timestamptz
updated_at timestamptz

node_server_configs — привязки конфига к ноде

Section titled “node_server_configs — привязки конфига к ноде”
id uuid PRIMARY KEY
node_id uuid REFERENCES nodes(id)
config_id uuid REFERENCES server_configs(id)
port int -- порт на конкретной ноде (может отличаться)
tag varchar -- xray inbound tag: "vless-reality-nl1" (auto или custom)
-- TLS-сертификаты (если security = tls)
-- Хранятся здесь, т.к. у каждой ноды свой домен и свой сертификат
tls_cert text null
tls_key text null
status enum(pending, applied, error) DEFAULT 'pending'
error_msg text null
applied_at timestamptz null
created_at timestamptz
UNIQUE (node_id, config_id)
UNIQUE (node_id, tag)
UNIQUE (node_id, port)

Как конфиг превращается в Xray inbound

Section titled “Как конфиг превращается в Xray inbound”

Панель собирает JSON для Xray из полей server_config + node_server_config:

internal/xray/config_builder.go
func BuildInbound(cfg *ServerConfig, binding *NodeServerConfig) (*XrayInbound, error) {
inbound := &XrayInbound{
Tag: binding.Tag,
Port: binding.Port,
Protocol: string(cfg.Protocol),
Sniffing: XraySniffing{
Enabled: cfg.Sniffing,
DestOverride: []string{"http", "tls", "quic"},
},
Settings: buildProtocolSettings(cfg),
StreamSettings: buildStreamSettings(cfg, binding),
}
return inbound, nil
}
func buildStreamSettings(cfg *ServerConfig, binding *NodeServerConfig) XrayStreamSettings {
s := XrayStreamSettings{
Network: string(cfg.Transport),
Security: string(cfg.Security),
}
switch cfg.Transport {
case TransportWS:
s.WSSettings = &XrayWSSettings{
Path: cfg.TransportSettings["path"].(string),
}
case TransportXHTTP:
s.XHTTPSettings = &XrayXHTTPSettings{
Path: cfg.TransportSettings["path"].(string),
Mode: cfg.TransportSettings["mode"].(string),
}
case TransportGRPC:
s.GRPCSettings = &XrayGRPCSettings{
ServiceName: cfg.TransportSettings["serviceName"].(string),
}
}
switch cfg.Security {
case SecurityTLS:
s.TLSSettings = &XrayTLSSettings{
ServerName: cfg.SecuritySettings["serverName"].(string),
Certificates: []XrayCert{{
Certificate: binding.TLSCert,
Key: binding.TLSKey,
}},
}
case SecurityReality:
s.RealitySettings = &XrayRealitySettings{
Dest: cfg.SecuritySettings["dest"].(string),
ServerNames: cfg.SecuritySettings["serverNames"].([]string),
PrivateKey: cfg.SecuritySettings["privateKey"].(string),
ShortIds: cfg.SecuritySettings["shortIds"].([]string),
Fingerprint: cfg.SecuritySettings["fingerprint"].(string),
}
}
return s
}

1. Создание конфига (конструктор)

Section titled “1. Создание конфига (конструктор)”
Шаг 1: Название
"VLESS TCP Reality — Premium"
Шаг 2: Протокол
○ VLESS ○ Trojan ○ VMess ○ Shadowsocks
Шаг 3: Транспорт
○ TCP ○ WebSocket ○ gRPC ○ XHTTP
Шаг 4: Безопасность
○ TLS ● Reality ○ None
(недоступные комбинации задизейблены)
Шаг 5: Настройки (зависят от выбора)
Reality:
Destination: [ www.google.com:443 ]
Server names: [ www.google.com ]
Private key: [ генерируется ] [↻ regenerate]
Public key: [ генерируется ] [copy]
Short IDs: [ генерируется ] [+ добавить]
Fingerprint: [ chrome ▾ ]
Flow: [ xtls-rprx-vision ▾ ]

2. Привязка конфига к ноде

Section titled “2. Привязка конфига к ноде”

На странице ноды или конфига:

Нода: NL-1 (Netherlands)
Привязанные конфиги:
┌──────────────────────────┬──────┬────────────┬─────────┐
│ Конфиг │ Порт │ Тег │ Статус │
├──────────────────────────┼──────┼────────────┼─────────┤
│ VLESS TCP Reality │ 443 │ vless-real │ ✓ applied│
│ VLESS XHTTP TLS │ 8443 │ vless-xhttp│ ✓ applied│
└──────────────────────────┴──────┴────────────┴─────────┘
[+ Привязать конфиг]
Конфиг: [ VLESS TCP Reality ▾ ]
Порт: [ 443 ]
Тег: [ vless-reality-nl1 ] (auto)
TLS сертификат: (если нужен)
[ Загрузить cert ] [ Загрузить key ]

При нажатии “Привязать”:

  1. Создаётся запись в node_server_configs со статусом pending
  2. Воркер enqueue задачу apply_config
  3. apply_config: собирает Xray JSON → gRPC AddInbound к агенту
  4. Статус обновляется в applied или error

При генерации ссылки для пользователя панель итерирует по всем node_server_configs с status = 'applied' для нод, доступных по плану, и генерирует connection string для каждого:

func BuildSubscriptionConfig(userID string, nodes []NodeWithConfigs) []string {
var links []string
for _, node := range nodes {
for _, binding := range node.AppliedConfigs {
link := buildLink(userID, node, binding)
// vless://UUID@node.host:443?type=tcp&security=reality&...#NL-1 Reality
links = append(links, link)
}
}
return links
}

Пользователь получает все доступные серверы одним импортом.


# Конфиги
GET /api/v1/admin/server-configs
POST /api/v1/admin/server-configs
GET /api/v1/admin/server-configs/:id
PATCH /api/v1/admin/server-configs/:id
DELETE /api/v1/admin/server-configs/:id
# Привязки
GET /api/v1/admin/nodes/:id/configs
POST /api/v1/admin/nodes/:id/configs # привязать конфиг к ноде
DELETE /api/v1/admin/nodes/:id/configs/:bindingId # отвязать
POST /api/v1/admin/nodes/:id/configs/:bindingId/apply # переприменить