feat(evobgp): прокси /api/evobgp на бэкенде, URL и токен из env Docker
Publish Docker image / build-and-push (push) Successful in 1m39s
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:
@@ -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"
|
||||
|
||||
@@ -29,25 +29,30 @@ export function normalizeEvobgpApiBaseUrl(input) {
|
||||
}
|
||||
}
|
||||
|
||||
function isProxyBase(base) {
|
||||
return base === '/api/evobgp' || (typeof base === 'string' && base.startsWith('/api/evobgp'));
|
||||
}
|
||||
|
||||
/**
|
||||
* База EvoBGP API: VITE_EVOBGP_API_URL → localStorage → в dev только proxy /v1.
|
||||
* Не используем относительный /v1 в production без настройки — запросы уходили бы на хост SPA, а не на EvoBGP.
|
||||
* База EvoBGP API:
|
||||
* 1) VITE_EVOBGP_API_URL — прямой URL (обход прокси, для dev/особых случаев)
|
||||
* 2) localStorage evobgp_api_url — прямой URL из браузера
|
||||
* 3) /api/evobgp — прокси router-lists-ui → EvoBGP (токен и URL только на сервере, без CORS)
|
||||
*/
|
||||
export function getEvobgpApiBaseUrl() {
|
||||
const envUrl = import.meta?.env?.VITE_EVOBGP_API_URL;
|
||||
if (envUrl && String(envUrl).trim()) {
|
||||
return normalizeEvobgpApiBaseUrl(String(envUrl).trim());
|
||||
const n = normalizeEvobgpApiBaseUrl(String(envUrl).trim());
|
||||
if (n) return n;
|
||||
}
|
||||
if (typeof window !== 'undefined') {
|
||||
const stored = window.localStorage?.getItem(LS_KEY_URL);
|
||||
if (stored && String(stored).trim()) {
|
||||
return normalizeEvobgpApiBaseUrl(String(stored).trim());
|
||||
const n = normalizeEvobgpApiBaseUrl(String(stored).trim());
|
||||
if (n) return n;
|
||||
}
|
||||
}
|
||||
if (import.meta?.env?.DEV) {
|
||||
return '/v1';
|
||||
}
|
||||
return '';
|
||||
return '/api/evobgp';
|
||||
}
|
||||
|
||||
function getAuthToken() {
|
||||
@@ -66,16 +71,18 @@ evobgpApi.interceptors.request.use((config) => {
|
||||
if (!base) {
|
||||
return Promise.reject(
|
||||
new Error(
|
||||
'EvoBGP API URL не задан. Укажите полный URL API (например http://192.168.0.1:8080) в разделе Communities или задайте VITE_EVOBGP_API_URL при сборке.',
|
||||
'EvoBGP API URL не задан или некорректен. Проверьте VITE_EVOBGP_API_URL или URL в настройках Communities.',
|
||||
),
|
||||
);
|
||||
}
|
||||
config.baseURL = base;
|
||||
const token = getAuthToken();
|
||||
if (token) {
|
||||
config.headers = config.headers || {};
|
||||
if (!config.headers.Authorization) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
if (!isProxyBase(base)) {
|
||||
const token = getAuthToken();
|
||||
if (token) {
|
||||
config.headers = config.headers || {};
|
||||
if (!config.headers.Authorization) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return config;
|
||||
|
||||
Reference in New Issue
Block a user