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:
parent
48fbb5bcb3
commit
70889421ea
250
PRODUCTION_URLS.md
Normal file
250
PRODUCTION_URLS.md
Normal 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
|
||||||
|
|
||||||
|
**Больше не нужно менять код при деплое!** 🚀
|
||||||
|
|
||||||
@ -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
2
frontend/.env.production
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
VITE_API_URL=/api
|
||||||
|
VITE_WS_URL=
|
||||||
@ -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,
|
||||||
|
|||||||
@ -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');
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
7
start.sh
7
start.sh
@ -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 ""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user