@@ -6,7 +8,7 @@
Установка ·
Обновление ·
Скриншоты ·
- Что нового в 2.2.0 ·
+ Что нового в 2.3.0 ·
Issues ·
Поддержка
@@ -60,10 +62,10 @@ curl -fsSL https://raw.githubusercontent.com/NodeFlow-dev/nodeflow/main/install.
2. предложит режим доступа: Caddy cookie-gate (ссылка активации ставит защищённую cookie) или без него — токен администратора Panel нужен в любом случае;
3. установит Docker и **Caddy** — Caddy сам получит сертификат Let's Encrypt и будет проксировать HTTPS на Panel, которая слушает только `127.0.0.1:8080`;
4. скачает install kit последнего релиза, проверит его по SHA-256, который GitHub публикует для каждого asset релиза, возьмёт из него `compose.release.yaml`, сгенерирует `.env`, CA, сертификат mTLS и ключ подписи обновлений в `/opt/nodeflow`;
-5. выполнит `docker compose pull && docker compose up -d` с образом `ghcr.io/nodeflow-dev/nodeflow-panel:2.2.0` — на сервере ничего не собирается, миграции БД применяет сам образ;
+5. выполнит `docker compose pull && docker compose up -d` с образом `ghcr.io/nodeflow-dev/nodeflow-panel:2.3.0` — на сервере ничего не собирается, миграции БД применяет сам образ;
6. опубликует подписанные релизы Node Agent (amd64 и arm64) и сохранит реквизиты входа в `~/nodeflow-credentials.txt` (права `0600`).
-Файрвол установщик не меняет. Для автоматизации без вопросов передайте переменные `NODEFLOW_DOMAIN`, `NODEFLOW_AUTH_MODE=cookie|none` и при необходимости `NODEFLOW_VERSION=2.2.0` (по умолчанию — последний релиз):
+Файрвол установщик не меняет. Для автоматизации без вопросов передайте переменные `NODEFLOW_DOMAIN`, `NODEFLOW_AUTH_MODE=cookie|none` и при необходимости `NODEFLOW_VERSION=2.3.0` (по умолчанию — последний релиз):
```bash
curl -fsSL https://raw.githubusercontent.com/NodeFlow-dev/nodeflow/main/install.sh \
@@ -92,7 +94,7 @@ sudo NODE_AGENT_TOKEN=<токен> bash install-node.sh
Команды выполняются от root (`sudo -i`): каталог `/opt/nodeflow` закрыт для остальных пользователей.
```bash
-v=2.2.0
+v=2.3.0
kit=NodeFlow-Panel-$v-Agent-$v-install-kit
curl -fsSLO "https://github.com/NodeFlow-dev/nodeflow/releases/download/v$v/$kit.tar.gz"
# сверьте с digest asset'а на странице релиза или в API:
@@ -121,7 +123,7 @@ curl -fsSL https://raw.githubusercontent.com/NodeFlow-dev/nodeflow/main/install.
Найдя `/opt/nodeflow/.env`, он работает как апгрейдер: делает `pg_dump` и архив конфигурации в `/var/backups/nodeflow/`, сохраняет `.env`, `tls/`, `pki/` и Caddy-сниппет `/etc/caddy/conf.d/nodeflow-panel.caddy`, переводит установку со сборки из исходников на образ ghcr.io, запускает миграции и проверяет, что Panel отвечает новой версией. При ошибке возвращаются прежние `compose.yaml` и `.env`; дамп БД остаётся (миграции автоматически не откатываются).
-Затем в **Настройки → Node Agent** обновите ноды до 2.2.0 — updater заменит Agent атомарно и откатит его при неудачном запуске. Самообновление не меняет systemd-юнит, поэтому на нодах, установленных версиями 1.0.x, один раз добавьте права на настройку pipe-буферов ядра (без этого splice остаётся на 256 КиБ) — или переустановите Agent из меню ноды:
+Затем в **Настройки → Node Agent** обновите ноды до 2.3.0 — updater заменит Agent атомарно и откатит его при неудачном запуске. Самообновление не меняет systemd-юнит, поэтому на нодах, установленных версиями 1.0.x, один раз добавьте права на настройку pipe-буферов ядра (без этого splice остаётся на 256 КиБ) — или переустановите Agent из меню ноды:
```bash
sudo install -d /etc/systemd/system/nodeflow-node-agent.service.d
@@ -175,7 +177,7 @@ go test ./...
## Документация
-- Заметки к релизам [2.2.0](docs/releases/2.2.0.md), [2.1.0](docs/releases/2.1.0.md), [2.0.1](docs/releases/2.0.1.md) и [2.0.0](docs/releases/2.0.0.md), [CHANGELOG](CHANGELOG.md)
+- Заметки к релизам [2.3.0](docs/releases/2.3.0.md), [2.2.0](docs/releases/2.2.0.md), [2.1.0](docs/releases/2.1.0.md), [2.0.1](docs/releases/2.0.1.md) и [2.0.0](docs/releases/2.0.0.md), [CHANGELOG](CHANGELOG.md)
- [API Panel](docs/api-contract.md)
- [Настройки HAProxy ноды и редактор конфига](docs/features/haproxy-configuration.md)
- [Архитектура](docs/architecture.md)
diff --git a/cmd/node-agent/main.go b/cmd/node-agent/main.go
index 3d69c1d..21adfb1 100644
--- a/cmd/node-agent/main.go
+++ b/cmd/node-agent/main.go
@@ -14,7 +14,7 @@ import (
"github.com/nodeflow/nodeflow/internal/agent"
)
-var version = "2.2.0"
+var version = "2.3.0"
func main() {
showVersion := flag.Bool("version", false, "print Node Agent version and exit")
diff --git a/cmd/panel-api/main.go b/cmd/panel-api/main.go
index 18e31e8..63681b1 100644
--- a/cmd/panel-api/main.go
+++ b/cmd/panel-api/main.go
@@ -18,7 +18,7 @@ import (
// panelVersion is the published Panel release version. Release builds may
// replace it with -ldflags "-X main.panelVersion=...".
-var panelVersion = "2.2.0"
+var panelVersion = "2.3.0"
func main() {
if len(os.Args) > 1 {
diff --git a/compose.release.yaml b/compose.release.yaml
index 28dac25..b51a015 100644
--- a/compose.release.yaml
+++ b/compose.release.yaml
@@ -30,7 +30,7 @@ services:
<<: *hardening
migrate:
- image: ghcr.io/nodeflow-dev/nodeflow-panel:${NODEFLOW_VERSION:-2.2.0}
+ image: ghcr.io/nodeflow-dev/nodeflow-panel:${NODEFLOW_VERSION:-2.3.0}
restart: "no"
command: ["migrate"]
depends_on:
@@ -79,7 +79,7 @@ services:
<<: *hardening
panel-api:
- image: ghcr.io/nodeflow-dev/nodeflow-panel:${NODEFLOW_VERSION:-2.2.0}
+ image: ghcr.io/nodeflow-dev/nodeflow-panel:${NODEFLOW_VERSION:-2.3.0}
restart: unless-stopped
depends_on:
migrate:
diff --git a/docs/brand/nodeflow-hero.svg b/docs/brand/nodeflow-hero.svg
index 7476609..2b1aa20 100644
--- a/docs/brand/nodeflow-hero.svg
+++ b/docs/brand/nodeflow-hero.svg
@@ -18,7 +18,7 @@
Control the flow.Keep the control.Ноды, TCP/SNI-маршруты, трафик и безопасные ревизии HAProxy —в одной прозрачной control plane.
- v2.2.0HAProxyDocker Compose
+ v2.3.0HAProxyDocker ComposePaneldesired stateNode AgentmTLS heartbeat
diff --git a/docs/install/README-NODE-AGENT.txt b/docs/install/README-NODE-AGENT.txt
index b2b04e7..8b7d728 100644
--- a/docs/install/README-NODE-AGENT.txt
+++ b/docs/install/README-NODE-AGENT.txt
@@ -2,13 +2,13 @@ NODEFLOW NODE AGENT — SIGNED RELEASE UPLOAD
===========================================
Бинарники Agent — отдельные assets GitHub Release:
- nodeflow-node-agent-2.2.0-linux-amd64
- nodeflow-node-agent-2.2.0-linux-arm64
+ nodeflow-node-agent-2.3.0-linux-amd64
+ nodeflow-node-agent-2.3.0-linux-arm64
install.sh публикует их в Panel сам. Для ручной загрузки скачайте файл и
сверьте SHA-256 с digest, который GitHub показывает для asset'а:
- v=2.2.0 f=nodeflow-node-agent-2.2.0-linux-amd64
+ v=2.3.0 f=nodeflow-node-agent-2.3.0-linux-amd64
curl -fsSLO https://github.com/NodeFlow-dev/nodeflow/releases/download/v$v/$f
curl -fsSL https://api.github.com/repos/NodeFlow-dev/nodeflow/releases/tags/v$v \
| jq -r --arg n "$f" '.assets[] | select(.name == $n) | .digest'
@@ -17,7 +17,7 @@ install.sh публикует их в Panel сам. Для ручной загр
В Panel откройте:
Настройки → Node Agent (подписанные релизы)
-Версия 2.2.0, ОС linux, архитектура amd64 или arm64 — по файлу. Выберите
+Версия 2.3.0, ОС linux, архитектура amd64 или arm64 — по файлу. Выберите
бинарник и нажмите «Загрузить и подписать». Затем назначьте релиз на
странице нужной ноды.
diff --git a/docs/install/index.html b/docs/install/index.html
index 6823e2a..6747a42 100644
--- a/docs/install/index.html
+++ b/docs/install/index.html
@@ -34,7 +34,7 @@
Как установить Panel и подключить первую HAPro
Корень комплектаinstall.sh
Установщик и апгрейдер Panel, compose.release.yaml для готового образа ghcr.io/nodeflow-dev/nodeflow-panel, шаблон nodeflow.env.example, SHA256SUMS файлов комплекта.
Скрипты PKI (init-mtls-pki.sh, init-update-signing-key.sh), ручная установка Agent и подготовка UFW ноды, systemd-юнит Agent, эта инструкция и примеры reverse proxy в docs/install/reverse-proxy/.
-
Бинарники Agent в комплект не входят.nodeflow-node-agent-2.2.0-linux-amd64 и nodeflow-node-agent-2.2.0-linux-arm64 лежат отдельными assets GitHub Release. install.sh скачивает оба, сверяет SHA-256 с digest, который GitHub публикует для asset'а, и публикует их в Panel.
+
Бинарники Agent в комплект не входят.nodeflow-node-agent-2.3.0-linux-amd64 и nodeflow-node-agent-2.3.0-linux-arm64 лежат отдельными assets GitHub Release. install.sh скачивает оба, сверяет SHA-256 с digest, который GitHub публикует для asset'а, и публикует их в Panel.
1
Подготовьте сервер Panel
Нужен домен, публичный IP и чистый Ubuntu или Debian.
@@ -158,7 +158,7 @@
Как установить Panel и подключить первую HAPro
5
Опубликуйте Node Agent
Без совместимого подписанного релиза Panel не начнёт установку ноды.
Скачайте nodeflow-node-agent-2.2.0-linux-amd64 (или -arm64) из assets GitHub Release.
Сверьте sha256sum файла с digest asset'а на странице релиза (или в https://api.github.com/repos/NodeFlow-dev/nodeflow/releases/tags/v2.2.0, поле digest).
Скачайте nodeflow-node-agent-2.3.0-linux-amd64 (или -arm64) из assets GitHub Release.
Сверьте sha256sum файла с digest asset'а на странице релиза (или в https://api.github.com/repos/NodeFlow-dev/nodeflow/releases/tags/v2.3.0, поле digest).
Panel подписывает бинарник собственным Ed25519-ключом. Нода проверяет подпись, SHA-256 и порядок релиза; при неудачном запуске updater возвращает прежний Agent.
@@ -177,7 +177,7 @@
Как установить Panel и подключить первую HAPro
3. Проверить обновление
cd /opt/nodeflow
sudo docker compose ps
curl -fsS http://127.0.0.1:8080/healthz
-
После обновления Panel обновите Agent на нодах: «Настройки → Node Agent» → «Обновить до 2.2.0». Для нод, установленных версиями 1.0.x, см. раздел «Обновление с 1.0.x» в заметках к релизу 2.0.0 (однократный systemd drop-in для pipe-буферов ядра).
+
После обновления Panel обновите Agent на нодах: «Настройки → Node Agent» → «Обновить до 2.3.0». Для нод, установленных версиями 1.0.x, см. раздел «Обновление с 1.0.x» в заметках к релизу 2.0.0 (однократный systemd drop-in для pipe-буферов ядра).
8
Проверьте результат
Четыре короткие проверки после установки или обновления.
diff --git a/docs/releases/2.2.0.en.md b/docs/releases/2.2.0.en.md
new file mode 100644
index 0000000..7fb99cd
--- /dev/null
+++ b/docs/releases/2.2.0.en.md
@@ -0,0 +1,39 @@
+# NodeFlow 2.2.0
+
+Panel **2.2.0** · Node Agent **2.2.0** · HAProxy renderer **v21**
+
+Client stickiness in DNS pools with backup servers, copying routes between nodes, and a route template library. Two migrations (000056, 000057). The Agent is unchanged. Thanks to @fesevu (#19, #20).
+
+## New
+
+- **Client stickiness and `leastconn` in pools with backup servers (#19).** A DNS pool with a backup server now supports client stickiness (sticky) and `leastconn` balancing. A new failover route is created without stickiness by default.
+ - Limitations: in backup-server mode, `leastping`/`leastconn` with a tolerance greater than 0 are rejected; `leastping` combined with `source` stickiness is rejected too.
+ - A client stuck to a backup server stays on it until the stickiness entry expires, even after the primary server is back.
+- **Copying routes between nodes (#20).** A route can be copied to another node. The copy is created as a disabled draft — check the ports and addresses and enable it manually.
+- **Route template library (#20).** A route can be saved as a template and used to create new routes on any node. Templates store `schema_version`. Migration 000056 (`route_templates`).
+
+## Fixes
+
+- **Rendering of existing failover routes does not change.** Migration 000057 keeps the previous balancing for routes with the legacy `source_table`: such routes are republished once with an identical HAProxy config. Re-saving an old failover route in the UI also keeps its balancing.
+- **Audit:** route copying (`route.copy`) and template actions (`route_template.*`) are written to the audit log.
+- Clear error messages and names for copies/templates; the copy and template dialogs follow the panel's style.
+
+## Upgrading from 2.1.0
+
+1. **Panel.** Run the installer again:
+
+ ```bash
+ curl -fsSL https://raw.githubusercontent.com/NodeFlow-dev/nodeflow/main/install.sh | sudo sh
+ ```
+
+ Manually: set `NODEFLOW_VERSION=2.2.0` in `/opt/nodeflow/.env` and run `docker compose pull && docker compose up -d`. The image applies migrations 000056 and 000057 itself.
+2. **Node Agent — optional.** The Agent code did not change in 2.2.0; everything works with Agent 2.1.x. To keep versions uniform: "Settings → Node Agent" → "Update to 2.2.0".
+
+Upgrading from 1.0.x? Read the [2.0.0 release notes](2.0.0.md) (Russian) first: they cover a one-time systemd drop-in for kernel pipe buffers.
+
+## Release files
+
+- `NodeFlow-Panel-2.2.0-Agent-2.2.0-install-kit.tar.gz` — install kit.
+- `nodeflow-node-agent-2.2.0-linux-amd64`, `nodeflow-node-agent-2.2.0-linux-arm64` — Node Agent binaries.
+
+The Panel ships as the `ghcr.io/nodeflow-dev/nodeflow-panel:2.2.0` image (linux/amd64 and linux/arm64). The SHA-256 of each file is the `digest` field in the release API; `install.sh` and `install-node.sh` verify it themselves.
diff --git a/docs/releases/2.3.0.en.md b/docs/releases/2.3.0.en.md
new file mode 100644
index 0000000..2ffe05a
--- /dev/null
+++ b/docs/releases/2.3.0.en.md
@@ -0,0 +1,21 @@
+# NodeFlow 2.3.0
+
+Panel **2.3.0** · Node Agent **2.3.0** · HAProxy renderer **v21**
+
+English UI with a language switch, an English README and stable error codes in the API. No migrations. Thanks to @ANHELL404 (#21, #23).
+
+## New
+
+- **English UI.** The panel is now available in English (ru/en locales). Switch the language in the sidebar or on the login page; the HAProxy directive help is available in English too.
+- **English README** — [README.en.md](../../README.en.md) and English release notes for 2.2.0 and 2.3.0.
+- **Stable codes in the API.** Route copy errors, template apply errors and HAProxy lint messages now carry `error_code` and `message_key` with `message_params`; the built-from-routes note carries `note_key` (`built_from_routes`). The Russian `message` field is kept for compatibility.
+- **`copy_suffix`** — route copy parameter that sets the suffix of the copy name.
+
+## Fixes
+
+- The Agent bootstrap script installs the binary into `/usr/local/bin` (#21, #23). Thanks to @ANHELL404.
+
+## Upgrading
+
+- No migrations.
+- Agent code is unchanged — only the bootstrap script served by the Panel changed. Upgrading the Agent on existing nodes is optional; new installs get the fixed script right away.
diff --git a/docs/releases/2.3.0.md b/docs/releases/2.3.0.md
new file mode 100644
index 0000000..cdcb5bb
--- /dev/null
+++ b/docs/releases/2.3.0.md
@@ -0,0 +1,21 @@
+# NodeFlow 2.3.0
+
+Panel **2.3.0** · Node Agent **2.3.0** · HAProxy renderer **v21**
+
+Английский интерфейс с переключателем языка, README на английском и стабильные коды ошибок в API. Миграций нет. Спасибо @ANHELL404 (#21, #23).
+
+## Новое
+
+- **Английский интерфейс.** Панель переведена на английский (локализации ru/en). Язык переключается в боковой панели и на странице входа; справка по директивам HAProxy тоже доступна на английском.
+- **README на английском** — [README.en.md](../../README.en.md) и английские заметки к релизам 2.2.0 и 2.3.0.
+- **Стабильные коды в API.** Ошибки копирования маршрутов, применения шаблонов и сообщения линтера HAProxy теперь содержат `error_code` и `message_key` с `message_params`; заметка о сборке из маршрутов — `note_key` (`built_from_routes`). Поле `message` на русском сохранено для совместимости.
+- **`copy_suffix`** — параметр копирования маршрута, задающий суффикс имени копии.
+
+## Исправления
+
+- Bootstrap-скрипт установки Agent кладёт бинарник в `/usr/local/bin` (#21, #23). Спасибо @ANHELL404.
+
+## Обновление
+
+- Миграций нет.
+- Код Agent не менялся — изменён только bootstrap-скрипт, который отдаёт Panel. Обновлять Agent на существующих нодах необязательно; новые установки сразу получают исправленный скрипт.
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 803e3af..2a8c04a 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "nodeflow-frontend",
- "version": "2.2.0",
+ "version": "2.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "nodeflow-frontend",
- "version": "2.2.0",
+ "version": "2.3.0",
"dependencies": {
"@mantine/core": "^8.3.10",
"@mantine/hooks": "^8.3.10",
diff --git a/frontend/package.json b/frontend/package.json
index 8d52542..14f68a5 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,14 +1,14 @@
{
"name": "nodeflow-frontend",
"private": true,
- "version": "2.2.0",
+ "version": "2.3.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"typecheck": "tsc -b --pretty false",
"preview": "vite preview",
- "test": "node --experimental-strip-types --test tests/*.test.ts"
+ "test": "node --experimental-strip-types --import ./tests/setup.ts --test tests/*.test.ts"
},
"dependencies": {
"@mantine/core": "^8.3.10",
diff --git a/frontend/src/app/App.tsx b/frontend/src/app/App.tsx
index adcf310..a63c28a 100644
--- a/frontend/src/app/App.tsx
+++ b/frontend/src/app/App.tsx
@@ -2,6 +2,7 @@ import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
import { lazy, Suspense } from 'react';
import { AppFrame } from '../components/AppFrame';
import { demoSuffix } from '../lib/navigation';
+import { t } from '../i18n';
const NodesOverviewPage = lazy(() => import('../pages/NodesOverviewPage').then((module) => ({ default: module.NodesOverviewPage })));
const NodeDetailPage = lazy(() => import('../pages/NodeDetailPage').then((module) => ({ default: module.NodeDetailPage })));
@@ -17,14 +18,14 @@ export function App() {
}>
} />
- }>} />
- }>} />
- }>} />
- }>} />
- }>} />
- }>} />
- }>} />
- }>} />
+ }>} />
+ }>} />
+ }>} />
+ }>} />
+ }>} />
+ }>} />
+ }>} />
+ }>} />
} />
diff --git a/frontend/src/components/AppFrame.tsx b/frontend/src/components/AppFrame.tsx
index 7c8e4d2..9654416 100644
--- a/frontend/src/components/AppFrame.tsx
+++ b/frontend/src/components/AppFrame.tsx
@@ -5,13 +5,15 @@ import { useEffect, useRef, useState } from 'react';
import { NavLink, Outlet, useLocation } from 'react-router-dom';
import { api } from '../lib/api';
import { preloadAppRoute, preloadInternalLink } from '../lib/navigation';
+import { LanguageMenu } from './LanguageMenu';
import { LoginPanel } from './LoginPanel';
import { NodeFlowLogo } from './NodeFlowLogo';
+import { t } from '../i18n';
const navigation = [
- { to: '/nodes', label: 'Ноды', icon: IconStack2 },
- { to: '/traffic', label: 'Трафик', icon: IconChartLine },
- { to: '/settings', label: 'Настройки', icon: IconSettings },
+ { to: '/nodes', label: t('app.nodes'), icon: IconStack2 },
+ { to: '/traffic', label: t('app.traffic'), icon: IconChartLine },
+ { to: '/settings', label: t('app.settings'), icon: IconSettings },
];
export function AppFrame() {
@@ -74,7 +76,7 @@ export function AppFrame() {
};
if (authState === 'checking') {
- return ;
+ return ;
}
if (authState === 'unauthenticated') {
@@ -97,10 +99,10 @@ export function AppFrame() {
>
-
+
-
+
{navigation.map(({ to, label, icon: Icon }) => {
@@ -120,13 +122,14 @@ export function AppFrame() {
);
})}
+