feat(evobgp): прокси /api/evobgp на бэкенде, URL и токен из env Docker
Publish Docker image / build-and-push (push) Successful in 1m39s

- Express проксирует на EVOBGP_API_URL с EVOBGP_API_TOKEN (без CORS)
- фронт по умолчанию baseURL /api/evobgp; прямой режим через VITE или localStorage
- Communities: подсказка про переменные контейнера; очистка URL включает прокси
- DOCKER.md и Dockerfile.fast: документация переменных

Made-with: Cursor
This commit is contained in:
2026-04-07 23:45:57 +07:00
parent 0746fb4f1e
commit a3291d6b2b
6 changed files with 209 additions and 25 deletions
+29 -11
View File
@@ -67,9 +67,9 @@ function CommunitiesManager() {
const [evobgpSettingsOpen, setEvobgpSettingsOpen] = useState(() => {
if (import.meta.env?.VITE_EVOBGP_API_URL) return false;
try {
return !window.localStorage?.getItem('evobgp_api_url');
return Boolean(window.localStorage?.getItem('evobgp_api_url'));
} catch {
return true;
return false;
}
});
@@ -293,9 +293,26 @@ function CommunitiesManager() {
const handleSave = () => saveAll(items);
const saveEvobgpConnection = () => {
const norm = normalizeEvobgpApiBaseUrl(evobgpUrlDraft);
const trimmedUrl = String(evobgpUrlDraft || '').trim();
if (!trimmedUrl) {
try {
window.localStorage.removeItem('evobgp_api_url');
window.localStorage.removeItem('evobgp_api_token');
} catch (_e) {
setError('Не удалось очистить настройки в localStorage.');
return;
}
setEvobgpUrlDraft('');
setEvobgpTokenDraft('');
setError('');
setSuccess('Включён серверный прокси /api/evobgp (EVOBGP_API_URL и EVOBGP_API_TOKEN в контейнере).');
fetchItems();
setTimeout(() => setSuccess(''), 5000);
return;
}
const norm = normalizeEvobgpApiBaseUrl(trimmedUrl);
if (!norm) {
setError('Укажите корректный URL EvoBGP (например http://192.168.0.1:8080). Порт должен совпадать с API EvoBGP, не с этим интерфейсом.');
setError('Укажите корректный URL EvoBGP (например http://192.168.0.1:8080). Порт у API EvoBGP, не у этого интерфейса.');
return;
}
try {
@@ -307,7 +324,7 @@ function CommunitiesManager() {
}
setEvobgpUrlDraft(norm);
setError('');
setSuccess('Подключение к EvoBGP сохранено. Загружаем данные…');
setSuccess(рямое подключение к EvoBGP из браузера сохранено. Загружаем данные…');
fetchItems();
setTimeout(() => setSuccess(''), 4000);
};
@@ -370,24 +387,25 @@ function CommunitiesManager() {
{evobgpSettingsOpen && (
<div className="card-body">
<p className="text-secondary small mb-3">
Укажите <strong>прямой URL сервера EvoBGP</strong> (схема + хост + порт API), например{' '}
<code>http://192.168.100.67:8080</code> — не адрес этого веб-интерфейса. К пути будет добавлено{' '}
<code>/v1</code> автоматически. Токен Bearer из EvoBGP.
<strong>По умолчанию</strong> запросы идут на внутренний прокси <code>/api/evobgp</code>: URL и токен задаются в{' '}
<strong>переменных окружения контейнера</strong> <code>EVOBGP_API_URL</code> и <code>EVOBGP_API_TOKEN</code>{' '}
(без CORS). Ниже только <strong>опциональное</strong> прямое подключение из браузера к EvoBGP; оставьте URL пустым и
нажмите «Сохранить», чтобы снова использовать прокси.
</p>
<div className="row g-2 align-items-end">
<div className="col-12 col-md-6">
<label className="form-label">Base URL EvoBGP</label>
<label className="form-label">Прямой Base URL EvoBGP (необязательно)</label>
<input
type="url"
className="form-control"
placeholder="http://192.168.100.67:8080"
placeholder="оставьте пустым для прокси"
autoComplete="off"
value={evobgpUrlDraft}
onChange={(e) => setEvobgpUrlDraft(e.target.value)}
/>
</div>
<div className="col-12 col-md-4">
<label className="form-label">API token</label>
<label className="form-label">Токен (только для прямого режима)</label>
<input
type="password"
className="form-control"