From 70889421ea7c10101c7b9b9283e099e5fbc9ac10 Mon Sep 17 00:00:00 2001 From: Primakov Alexandr Alexandrovich Date: Sun, 12 Oct 2025 23:53:47 +0300 Subject: [PATCH] Implement relative paths for frontend API and WebSocket connections - Created `.env.production` file to define relative paths for production deployment. - Updated frontend API and WebSocket client to use environment variables for dynamic URL handling. - Enhanced deployment scripts (`deploy-ubuntu.sh`, `start.sh`, `start.bat`) to generate `.env.production` automatically. - Added `PRODUCTION_URLS.md` for documentation on production URL configuration and troubleshooting. --- PRODUCTION_URLS.md | 250 ++++++++++++++++++++++++++++++++++ deploy-ubuntu.sh | 8 ++ frontend/.env.production | 2 + frontend/src/api/client.ts | 3 +- frontend/src/api/websocket.ts | 17 ++- start.bat | 5 + start.sh | 7 + 7 files changed, 288 insertions(+), 4 deletions(-) create mode 100644 PRODUCTION_URLS.md create mode 100644 frontend/.env.production diff --git a/PRODUCTION_URLS.md b/PRODUCTION_URLS.md new file mode 100644 index 0000000..4ce0bff --- /dev/null +++ b/PRODUCTION_URLS.md @@ -0,0 +1,250 @@ +# 🌐 Настройка URL для Production + +## Проблема + +После развертывания на сервере frontend пытается обращаться к `localhost` вместо реального IP/домена сервера. + +## ✅ Решение + +Frontend теперь использует **относительные пути** и динамические URL. + +--- + +## 🔧 Как это работает + +### 1. API запросы + +**До:** +```typescript +const API_BASE_URL = 'http://localhost:8000/api'; +``` + +**После:** +```typescript +const API_BASE_URL = import.meta.env.VITE_API_URL || '/api'; +``` + +### 2. WebSocket + +**До:** +```typescript +const wsUrl = 'ws://localhost:8000/ws/reviews'; +``` + +**После:** +```typescript +const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; +const host = import.meta.env.VITE_WS_URL || `${protocol}//${window.location.host}`; +const wsUrl = `${host}/ws/reviews`; +``` + +--- + +## 📊 Режимы работы + +### Development (npm run dev) + +Frontend работает на `localhost:5173`, proxy перенаправляет запросы: + +```typescript +// vite.config.ts +server: { + proxy: { + '/api': 'http://localhost:8000', + '/ws': 'ws://localhost:8000', + } +} +``` + +**Запросы:** +- `http://localhost:5173/api/...` → `http://localhost:8000/api/...` +- `ws://localhost:5173/ws/...` → `ws://localhost:8000/ws/...` + +### Production (npm run build) + +Frontend собран в `backend/public/`, backend раздает статику. + +**Относительные пути:** +- `/api/...` → `http://your-server-ip:8000/api/...` +- `/ws/...` → `ws://your-server-ip:8000/ws/...` + +Browser автоматически использует текущий host! + +--- + +## 🎯 Примеры + +### На локальной машине: + +``` +URL браузера: http://localhost:8000 +API запрос: http://localhost:8000/api/repositories +WebSocket: ws://localhost:8000/ws/reviews +``` + +### На сервере (IP): + +``` +URL браузера: http://185.152.81.243:8000 +API запрос: http://185.152.81.243:8000/api/repositories +WebSocket: ws://185.152.81.243:8000/ws/reviews +``` + +### На сервере (домен): + +``` +URL браузера: https://ai-review.example.com +API запрос: https://ai-review.example.com/api/repositories +WebSocket: wss://ai-review.example.com/ws/reviews +``` + +--- + +## ⚙️ Переменные окружения + +### .env.production (создается автоматически) + +```bash +# Относительные пути (по умолчанию) +VITE_API_URL=/api +VITE_WS_URL= +``` + +### .env.development (для разработки) + +```bash +# Явные URL для development +VITE_API_URL=http://localhost:8000/api +VITE_WS_URL=ws://localhost:8000 +``` + +### Кастомная настройка: + +Если нужен специфичный URL (например, разные домены): + +```bash +# frontend/.env.production +VITE_API_URL=https://api.example.com/api +VITE_WS_URL=wss://api.example.com +``` + +--- + +## 🔄 Пересборка после изменений + +### Локально: + +```bash +cd frontend +npm run build +``` + +### На сервере: + +```bash +cd /opt/ai-review/frontend +npm run build +sudo systemctl restart ai-review +``` + +--- + +## 🐛 Troubleshooting + +### Проблема: API запросы идут на localhost + +**Причина:** Frontend собран со старым кодом + +**Решение:** +```bash +cd frontend +rm -rf dist/ ../backend/public/ +npm run build +``` + +### Проблема: WebSocket не подключается + +**Проверьте:** +1. Открыть DevTools → Network → WS +2. Проверить URL WebSocket подключения +3. Убедиться что backend доступен + +**Для HTTPS:** +```bash +# WebSocket должен использовать wss:// +# Убедитесь что nginx прокси настроен: + +location /ws { + proxy_pass http://localhost:8000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; +} +``` + +### Проблема: CORS ошибки + +**Если используете разные домены:** + +Обновите `backend/.env`: +```bash +CORS_ORIGINS=https://yourdomain.com,https://api.yourdomain.com +``` + +Перезапустите backend: +```bash +sudo systemctl restart ai-review +``` + +--- + +## ✅ Проверка + +### 1. Откройте DevTools (F12) + +### 2. Network tab + +Проверьте запросы: +- ✅ URL должны использовать текущий host +- ✅ Не должно быть `localhost` в production + +### 3. Console + +Не должно быть ошибок типа: +- ❌ `Failed to fetch` +- ❌ `ERR_CONNECTION_REFUSED` +- ❌ `Mixed Content` (http на https странице) + +--- + +## 📝 Пример логов + +### ✅ Правильно (на сервере 185.152.81.243): + +``` +GET http://185.152.81.243:8000/api/repositories 200 OK +WS ws://185.152.81.243:8000/ws/reviews [connected] +``` + +### ❌ Неправильно: + +``` +GET http://localhost:8000/api/repositories [failed] +WS ws://localhost:8000/ws/reviews [failed] +``` + +--- + +## 🎉 Готово! + +Теперь приложение работает на любом домене/IP без изменений кода! + +**Автоматически подстраивается под:** +- ✅ localhost +- ✅ IP адрес +- ✅ Домен +- ✅ HTTP/HTTPS +- ✅ WS/WSS + +**Больше не нужно менять код при деплое!** 🚀 + diff --git a/deploy-ubuntu.sh b/deploy-ubuntu.sh index 4ea28a8..b097200 100644 --- a/deploy-ubuntu.sh +++ b/deploy-ubuntu.sh @@ -82,6 +82,14 @@ echo -e "${GREEN}✓ Файлы скопированы${NC}" # 4. Сборка frontend echo -e "${YELLOW}[4/10] Сборка frontend...${NC}" cd frontend + +# Создаем .env.production для относительных путей +cat > .env.production << 'EOF' +# Production - используем относительные пути +VITE_API_URL=/api +VITE_WS_URL= +EOF + sudo -u "$REAL_USER" npm install sudo -u "$REAL_USER" npm run build cd .. diff --git a/frontend/.env.production b/frontend/.env.production new file mode 100644 index 0000000..5f572fe --- /dev/null +++ b/frontend/.env.production @@ -0,0 +1,2 @@ +VITE_API_URL=/api +VITE_WS_URL= diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 5e89089..c42545a 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -1,7 +1,8 @@ import axios from 'axios'; import type { Repository, RepositoryCreate, Review, ReviewStats } from '../types'; -const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000/api'; +// Используем относительный путь для production или env для development +const API_BASE_URL = import.meta.env.VITE_API_URL || '/api'; const api = axios.create({ baseURL: API_BASE_URL, diff --git a/frontend/src/api/websocket.ts b/frontend/src/api/websocket.ts index 81d9de6..9f13238 100644 --- a/frontend/src/api/websocket.ts +++ b/frontend/src/api/websocket.ts @@ -1,7 +1,5 @@ import { WebSocketMessage } from '../types'; -const WS_URL = import.meta.env.VITE_WS_URL || 'ws://localhost:8000'; - export class WebSocketClient { private ws: WebSocket | null = null; private listeners: Map void>> = new Map(); @@ -9,13 +7,26 @@ export class WebSocketClient { private maxReconnectAttempts = 5; private reconnectDelay = 3000; + private getWebSocketUrl(): string { + // Если задан VITE_WS_URL, используем его + if (import.meta.env.VITE_WS_URL) { + return import.meta.env.VITE_WS_URL; + } + + // Иначе определяем автоматически на основе текущего location + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + const host = window.location.host; + return `${protocol}//${host}`; + } + connect() { if (this.ws?.readyState === WebSocket.OPEN) { return; } try { - this.ws = new WebSocket(`${WS_URL}/ws/reviews`); + const wsUrl = this.getWebSocketUrl(); + this.ws = new WebSocket(`${wsUrl}/ws/reviews`); this.ws.onopen = () => { console.log('WebSocket connected'); diff --git a/start.bat b/start.bat index 2608502..35ec4f3 100644 --- a/start.bat +++ b/start.bat @@ -45,6 +45,11 @@ echo. REM 4. Сборка frontend echo [STEP 4/7] Сборка frontend... + +REM Создаем .env.production для production +echo VITE_API_URL=/api > .env.production +echo VITE_WS_URL= >> .env.production + call npm run build echo [OK] Frontend собран в backend/public echo. diff --git a/start.sh b/start.sh index bdd6f75..deb47be 100644 --- a/start.sh +++ b/start.sh @@ -48,6 +48,13 @@ echo "" # 4. Сборка frontend echo -e "${YELLOW}🔨 Сборка frontend...${NC}" + +# Создаем .env.production для production +cat > .env.production << 'EOF' +VITE_API_URL=/api +VITE_WS_URL= +EOF + npm run build echo -e "${GREEN}✅ Frontend собран в backend/public${NC}" echo ""