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.
This commit is contained in:
Primakov Alexandr Alexandrovich 2025-10-12 23:53:47 +03:00
parent 48fbb5bcb3
commit 70889421ea
7 changed files with 288 additions and 4 deletions

250
PRODUCTION_URLS.md Normal file
View File

@ -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
**Больше не нужно менять код при деплое!** 🚀

View File

@ -82,6 +82,14 @@ echo -e "${GREEN}✓ Файлы скопированы${NC}"
# 4. Сборка frontend # 4. Сборка frontend
echo -e "${YELLOW}[4/10] Сборка frontend...${NC}" echo -e "${YELLOW}[4/10] Сборка frontend...${NC}"
cd frontend 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 install
sudo -u "$REAL_USER" npm run build sudo -u "$REAL_USER" npm run build
cd .. cd ..

2
frontend/.env.production Normal file
View File

@ -0,0 +1,2 @@
VITE_API_URL=/api
VITE_WS_URL=

View File

@ -1,7 +1,8 @@
import axios from 'axios'; import axios from 'axios';
import type { Repository, RepositoryCreate, Review, ReviewStats } from '../types'; 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({ const api = axios.create({
baseURL: API_BASE_URL, baseURL: API_BASE_URL,

View File

@ -1,7 +1,5 @@
import { WebSocketMessage } from '../types'; import { WebSocketMessage } from '../types';
const WS_URL = import.meta.env.VITE_WS_URL || 'ws://localhost:8000';
export class WebSocketClient { export class WebSocketClient {
private ws: WebSocket | null = null; private ws: WebSocket | null = null;
private listeners: Map<string, Set<(data: any) => void>> = new Map(); private listeners: Map<string, Set<(data: any) => void>> = new Map();
@ -9,13 +7,26 @@ export class WebSocketClient {
private maxReconnectAttempts = 5; private maxReconnectAttempts = 5;
private reconnectDelay = 3000; 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() { connect() {
if (this.ws?.readyState === WebSocket.OPEN) { if (this.ws?.readyState === WebSocket.OPEN) {
return; return;
} }
try { try {
this.ws = new WebSocket(`${WS_URL}/ws/reviews`); const wsUrl = this.getWebSocketUrl();
this.ws = new WebSocket(`${wsUrl}/ws/reviews`);
this.ws.onopen = () => { this.ws.onopen = () => {
console.log('WebSocket connected'); console.log('WebSocket connected');

View File

@ -45,6 +45,11 @@ echo.
REM 4. Сборка frontend REM 4. Сборка frontend
echo [STEP 4/7] Сборка 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 call npm run build
echo [OK] Frontend собран в backend/public echo [OK] Frontend собран в backend/public
echo. echo.

View File

@ -48,6 +48,13 @@ echo ""
# 4. Сборка frontend # 4. Сборка frontend
echo -e "${YELLOW}🔨 Сборка frontend...${NC}" echo -e "${YELLOW}🔨 Сборка frontend...${NC}"
# Создаем .env.production для production
cat > .env.production << 'EOF'
VITE_API_URL=/api
VITE_WS_URL=
EOF
npm run build npm run build
echo -e "${GREEN}✅ Frontend собран в backend/public${NC}" echo -e "${GREEN}✅ Frontend собран в backend/public${NC}"
echo "" echo ""