Xray интеграция
Как это работает
Section titled “Как это работает”Xray предоставляет gRPC API для управления конфигурацией в реальном времени без перезапуска. Панель не трогает конфиги напрямую — она общается с агентом, который запущен рядом с Xray на каждой ноде.
Панель (Go) → gRPC → Агент (Go) → Xray gRPC API → Xray-coreАгент на ноде
Section titled “Агент на ноде”Лёгкий Go-сервис. Запускается в одном docker-compose с Xray.
Proto-контракт (agent.proto)
Section titled “Proto-контракт (agent.proto)”service NodeAgent { // Управление клиентами rpc AddClient(AddClientRequest) returns (ClientResponse); rpc RemoveClient(RemoveClientRequest) returns (ClientResponse); rpc UpdateClient(UpdateClientRequest) returns (ClientResponse);
// Статистика rpc GetTrafficStats(GetTrafficStatsRequest) returns (TrafficStats); rpc GetSystemStats(Empty) returns (SystemStats);
// Управление инбаундами rpc AddInbound(AddInboundRequest) returns (InboundResponse); rpc RemoveInbound(RemoveInboundRequest) returns (InboundResponse); rpc GetInbounds(Empty) returns (InboundsResponse);}Ключевые сообщения
Section titled “Ключевые сообщения”message AddClientRequest { string inbound_tag = 1; string uuid = 2; // для VLESS/VMess string password = 3; // для Trojan/SS string email = 4; // уникальный тег в Xray (формат: user_id@panel) string flow = 5; // "xtls-rprx-vision" для VLESS+XTLS int64 expire_time = 6; // unix timestamp, 0 = без ограничений}
message TrafficStats { string email = 1; int64 bytes_up = 2; int64 bytes_down = 3;}
message SystemStats { float cpu_percent = 1; int64 mem_used_mb = 2; int32 connections = 3; int64 uptime_sec = 4;}Xray API (внутри агента)
Section titled “Xray API (внутри агента)”Агент общается с Xray через его встроенный gRPC API (порт 10085 по умолчанию).
Используемые сервисы Xray
Section titled “Используемые сервисы Xray”HandlerService → AddInbound, RemoveInbound, AddUser, RemoveUser, AlterInboundStatsService → GetStats, QueryStatsПример: добавить пользователя в VLESS инбаунд
Section titled “Пример: добавить пользователя в VLESS инбаунд”// Внутри агентаfunc (a *Agent) addVlessClient(inboundTag, uuid, email, flow string) error { client := &proxyman_command.AddUserOperation{ User: &protocol.User{ Email: email, Account: serial.ToTypedMessage(&vless.Account{ Id: uuid, Flow: flow, }), }, } _, err := a.handlerClient.AlterInbound(ctx, &proxyman_command.AlterInboundRequest{ Tag: inboundTag, Operation: serial.ToTypedMessage(client), }) return err}Пример: получить статистику трафика
Section titled “Пример: получить статистику трафика”// QueryStats с паттерном "user>>>email>>>traffic"resp, err := a.statsClient.QueryStats(ctx, &stats_command.QueryStatsRequest{ Pattern: "user>>>", Reset_: true, // сбрасывает счётчик после чтения (дельта)})Reset_: true — критически важно. Xray считает трафик накопительно, панель читает дельту и сбрасывает.
Синхронизация пользователей
Section titled “Синхронизация пользователей”Добавление (новая подписка / новая нода)
Section titled “Добавление (новая подписка / новая нода)”1. Получить все активные node_inbounds для нод, доступных по плану2. Для каждого inbound → gRPC AddClient к агенту3. UPDATE user_inbound_sync SET sync_status = 'synced'4. При ошибке → sync_status = 'error', retry через AsynqУдаление (истекла подписка / бан)
Section titled “Удаление (истекла подписка / бан)”1. Получить все user_inbound_sync WHERE user_id = ? AND sync_status = 'synced'2. Для каждого → gRPC RemoveClient3. UPDATE sync_status = 'removed'Email-тег в Xray
Section titled “Email-тег в Xray”Xray идентифицирует клиентов по полю email (не обязательно реальный email). Используем формат:
{user_id}@astralЭто позволяет однозначно привязать статистику трафика к пользователю в панели.
Конфигурация инбаунда (пример VLESS+WS+TLS)
Section titled “Конфигурация инбаунда (пример VLESS+WS+TLS)”Хранится в node_inbounds.settings и stream_settings как JSONB:
// settings{ "clients": [], "decryption": "none"}
// stream_settings{ "network": "ws", "security": "tls", "tlsSettings": { "certificates": [ { "certificateFile": "/etc/ssl/certs/domain.crt", "keyFile": "/etc/ssl/private/domain.key" } ] }, "wsSettings": { "path": "/vless" }}При добавлении новой ноды агент получает полный конфиг инбаунда и применяет его через AddInbound.
Сбор трафика (крон)
Section titled “Сбор трафика (крон)”Каждые 5 минут:1. Для каждой активной ноды → gRPC GetTrafficStats (с Reset_=true)2. Парсим email тег → достаём user_id3. INSERT INTO traffic_snapshots4. UPSERT INTO daily_traffic (bytes += delta)5. UPDATE subscriptions SET traffic_used_bytes += delta6. Если traffic_used_bytes >= traffic_limit_bytes → suspend подписку → syncDeployment агента на ноде
Section titled “Deployment агента на ноде”# docker-compose.yml на нодеservices: xray: image: ghcr.io/xtls/xray-core:latest ports: - "443:443" volumes: - ./config.json:/etc/xray/config.json - ./ssl:/etc/ssl
agent: image: ghcr.io/your-org/astral-agent:latest environment: XRAY_API_ADDR: "xray:10085" AGENT_PORT: "50051" AGENT_TOKEN: "${AGENT_SECRET_TOKEN}" # Для auth на панели ports: - "50051:50051" depends_on: - xrayПанель подключается к node.host:50051, передаёт AGENT_TOKEN в gRPC metadata.
Версионирование Xray
Section titled “Версионирование Xray”Xray API иногда меняется между версиями. Агент хранит версию Xray и при старте репортит её панели. Панель логирует несовместимые версии.