This commit is contained in:
2026-03-18 16:09:47 +07:00
parent ab3d27a4f1
commit 1ad90395dc
7 changed files with 364 additions and 22 deletions

View File

@@ -1,11 +1,53 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import API_CONFIG from './api/config';
function App() {
const [apiStatus, setApiStatus] = useState('Checking...');
const [apiUrl, setApiUrl] = useState('');
useEffect(() => {
// Показыва какой API URL используется
const baseUrl = API_CONFIG.getBaseUrl();
const fullUrl = `${window.location.origin}${baseUrl}`;
setApiUrl(fullUrl);
// Проверяем подключение к API
const checkAPI = async () => {
try {
const response = await fetch(`${baseUrl}/health`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
});
if (response.ok) {
setApiStatus('✅ Connected');
} else {
setApiStatus(`⚠️ Error: ${response.status}`);
}
} catch (error) {
setApiStatus(`❌ Failed: ${error.message}`);
}
};
checkAPI();
}, []);
return (
<div style={{ padding: '20px', fontFamily: 'Arial' }}>
<h1>PostgreSQL Admin Panel</h1>
<p>Frontend is loading...</p>
<p>API Status: <span id="status">Checking...</span></p>
<p>Frontend is ready! 🚀</p>
<hr />
<h3>🔌 API Configuration</h3>
<p><strong>API Base URL:</strong> {apiUrl}</p>
<p><strong>API Status:</strong> {apiStatus}</p>
<hr />
<h3>💡 How it works:</h3>
<ul>
<li> Frontend automatically detects API URL from window.location</li>
<li> Nginx proxies /api/* to backend internally</li>
<li> Works on localhost, IP, or domain without configuration</li>
<li> No hardcoded environment variables needed for API URL</li>
</ul>
</div>
);
}

View File

@@ -0,0 +1,44 @@
// Автоматическое определение API URL без необходимости env переменных
// Работает на localhost, IP адресе или доменном имени
const API_CONFIG = {
// Определяй базовый URL на основе текущего location
// Фронтенд обращается к /api, nginx перенаправляет на backend внутри Docker сети
getBaseUrl: () => {
// Используем относительный путь /api вместо абсолютного URL
// Например: http://localhost:3000/api или http://185.56.162.170:8080/api
return '/api';
},
// Получить полный URL для API запроса
getApiUrl: (endpoint) => {
const baseUrl = API_CONFIG.getBaseUrl();
// Убираем слэш в начале endpoint если есть
const cleanEndpoint = endpoint.startsWith('/') ? endpoint.slice(1) : endpoint;
return `${baseUrl}/${cleanEndpoint}`;
},
// Хелпер для fetch запросов
fetch: async (endpoint, options = {}) => {
const url = API_CONFIG.getApiUrl(endpoint);
const defaultHeaders = {
'Content-Type': 'application/json',
};
const response = await fetch(url, {
...options,
headers: {
...defaultHeaders,
...options.headers,
},
});
if (!response.ok) {
throw new Error(`API Error: ${response.status} ${response.statusText}`);
}
return response.json();
},
};
export default API_CONFIG;

View File

@@ -0,0 +1,59 @@
/**
* @example Пример использования API конфига в React компоненте
*
* import API_CONFIG from './api/config';
*
* function MyComponent() {
* useEffect(() => {
* // Вариант 1: Простой fetch
* fetch(API_CONFIG.getApiUrl('users'))
* .then(r => r.json())
* .then(data => console.log(data));
*
* // Вариант 2: Использовать хелпер API_CONFIG.fetch
* API_CONFIG.fetch('users')
* .then(data => console.log(data))
* .catch(error => console.error(error));
*
* // Вариант 3: С параметрами
* API_CONFIG.fetch('users/123', {
* method: 'GET',
* });
*
* // Вариант 4: POST запрос
* API_CONFIG.fetch('users', {
* method: 'POST',
* body: JSON.stringify({ name: 'John' }),
* });
* }, []);
* }
*/
import React, { useEffect, useState } from 'react';
import API_CONFIG from '../api/config';
function ExampleComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// Получи данные с API
API_CONFIG.fetch('users')
.then(data => setData(data))
.catch(err => setError(err.message))
.finally(() => setLoading(false));
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<div>
<h2>Users from API</h2>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
export default ExampleComponent;