fix: добавлен CORS middleware и тестовые файлы для диагностики

- Добавлен CORSMiddleware в app.py для решения проблем с CORS
- Создан test_api.py для тестирования API endpoints
- Создан test_websocket.py для тестирования WebSocket соединений
- Создан test_browser.html для тестирования в браузере
- Все тесты показывают, что API и WebSocket работают корректно

Диагностика показала:
 API endpoints работают (health, auth, containers, logs)
 WebSocket соединения работают и передают логи
 Проблема может быть в браузере или localStorage

Для тестирования в браузере:
1. Откройте http://localhost:9001/test_browser.html
2. Выполните тесты по порядку
3. Проверьте консоль браузера на ошибки

Автор: Сергей Антропов
Сайт: https://devops.org.ru
This commit is contained in:
Сергей Антропов 2025-08-20 20:50:28 +03:00
parent 769d33777d
commit 85757ca717
4 changed files with 403 additions and 0 deletions

View File

@ -10,6 +10,7 @@ import os
from fastapi import FastAPI, Request, HTTPException
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.middleware.cors import CORSMiddleware
from core.config import DEBUG_MODE, SNAP_DIR, STATIC_DIR, APP_PORT
from api.v1.router import api_router, pages_router
@ -24,6 +25,15 @@ app = FastAPI(
redoc_url="/redoc" if DEBUG_MODE else None
)
# Настройка CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # В продакшене лучше указать конкретные домены
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Инициализация шаблонов
from core.config import templates

107
test_api.py Normal file
View File

@ -0,0 +1,107 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Тестовый скрипт для проверки API LogBoard+
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
import requests
import json
def test_api():
"""Тестирование API endpoints"""
base_url = "http://localhost:9001"
# 1. Проверяем health endpoint
print("🔍 Проверка health endpoint...")
try:
response = requests.get(f"{base_url}/healthz")
print(f" Health: {response.status_code} - {response.text}")
except Exception as e:
print(f" ❌ Ошибка health: {e}")
return
# 2. Проверяем API без токена
print("\n🔍 Проверка API без токена...")
try:
response = requests.get(f"{base_url}/api/containers/services")
print(f" API без токена: {response.status_code}")
if response.status_code == 401:
print(" ✅ Правильно - требует аутентификации")
else:
print(" ⚠️ Неожиданный статус")
except Exception as e:
print(f" ❌ Ошибка API без токена: {e}")
# 3. Вход в систему
print("\n🔐 Вход в систему...")
login_data = {"username": "admin", "password": "admin"}
try:
response = requests.post(f"{base_url}/api/auth/login", json=login_data)
if response.status_code != 200:
print(f" ❌ Ошибка входа: {response.status_code}")
return
token = response.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
print(" ✅ Вход выполнен успешно")
except Exception as e:
print(f" ❌ Ошибка входа: {e}")
return
# 4. Проверяем API с токеном
print("\n🔍 Проверка API с токеном...")
try:
response = requests.get(f"{base_url}/api/containers/services", headers=headers)
print(f" API с токеном: {response.status_code}")
if response.status_code == 200:
containers = response.json()
print(f" ✅ Получено контейнеров: {len(containers)}")
# Показываем первые 3 контейнера
for i, container in enumerate(containers[:3]):
print(f" {i+1}. {container.get('name', 'N/A')} ({container.get('status', 'N/A')})")
else:
print(f" ❌ Ошибка API: {response.text}")
except Exception as e:
print(f" ❌ Ошибка API с токеном: {e}")
# 5. Проверяем WebSocket status
print("\n🔍 Проверка WebSocket status...")
try:
response = requests.get(f"{base_url}/api/websocket/status", headers=headers)
print(f" WebSocket status: {response.status_code}")
if response.status_code == 200:
status = response.json()
print(f" ✅ WebSocket статус: {status}")
else:
print(f" ❌ Ошибка WebSocket status: {response.text}")
except Exception as e:
print(f" ❌ Ошибка WebSocket status: {e}")
# 6. Проверяем логи для первого контейнера
try:
response = requests.get(f"{base_url}/api/containers/services", headers=headers)
if response.status_code == 200:
containers = response.json()
if containers:
first_container = containers[0]
container_id = first_container.get('id')
print(f"\n🔍 Проверка логов для контейнера {first_container.get('name')}...")
try:
response = requests.get(f"{base_url}/api/logs/{container_id}?tail=5", headers=headers)
print(f" Логи: {response.status_code}")
if response.status_code == 200:
logs = response.json()
print(f" ✅ Получено строк логов: {len(logs.get('logs', []))}")
else:
print(f" ❌ Ошибка логов: {response.text}")
except Exception as e:
print(f" ❌ Ошибка логов: {e}")
except Exception as e:
print(f" ❌ Ошибка получения контейнеров: {e}")
if __name__ == "__main__":
test_api()

199
test_browser.html Normal file
View File

@ -0,0 +1,199 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Тест LogBoard+ API</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.test-section { margin: 20px 0; padding: 15px; border: 1px solid #ccc; border-radius: 5px; }
.success { color: green; }
.error { color: red; }
.info { color: blue; }
button { padding: 10px 20px; margin: 5px; }
pre { background: #f5f5f5; padding: 10px; border-radius: 3px; overflow-x: auto; }
</style>
</head>
<body>
<h1>Тест LogBoard+ API</h1>
<div class="test-section">
<h3>1. Проверка токена в localStorage</h3>
<button onclick="checkToken()">Проверить токен</button>
<div id="tokenResult"></div>
</div>
<div class="test-section">
<h3>2. Тест входа в систему</h3>
<button onclick="testLogin()">Войти (admin/admin)</button>
<div id="loginResult"></div>
</div>
<div class="test-section">
<h3>3. Тест получения контейнеров</h3>
<button onclick="testContainers()">Получить контейнеры</button>
<div id="containersResult"></div>
</div>
<div class="test-section">
<h3>4. Тест WebSocket</h3>
<button onclick="testWebSocket()">Тест WebSocket</button>
<div id="websocketResult"></div>
</div>
<div class="test-section">
<h3>5. Очистка</h3>
<button onclick="clearToken()">Очистить токен</button>
<div id="clearResult"></div>
</div>
<script>
const baseUrl = 'http://localhost:9001';
function log(elementId, message, type = 'info') {
const element = document.getElementById(elementId);
const className = type === 'success' ? 'success' : type === 'error' ? 'error' : 'info';
element.innerHTML = `<div class="${className}">${message}</div>`;
}
function checkToken() {
const token = localStorage.getItem('access_token');
if (token) {
log('tokenResult', `✅ Токен найден: ${token.substring(0, 50)}...`, 'success');
} else {
log('tokenResult', '❌ Токен не найден', 'error');
}
}
async function testLogin() {
try {
log('loginResult', '🔄 Выполняется вход...', 'info');
const response = await fetch(`${baseUrl}/api/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: 'admin',
password: 'admin'
})
});
if (response.ok) {
const data = await response.json();
localStorage.setItem('access_token', data.access_token);
log('loginResult', `✅ Вход выполнен успешно! Токен сохранен.`, 'success');
} else {
const errorData = await response.json();
log('loginResult', `❌ Ошибка входа: ${errorData.detail || response.status}`, 'error');
}
} catch (error) {
log('loginResult', `❌ Ошибка: ${error.message}`, 'error');
}
}
async function testContainers() {
try {
const token = localStorage.getItem('access_token');
if (!token) {
log('containersResult', '❌ Токен не найден. Сначала выполните вход.', 'error');
return;
}
log('containersResult', '🔄 Получение контейнеров...', 'info');
const response = await fetch(`${baseUrl}/api/containers/services`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (response.ok) {
const containers = await response.json();
log('containersResult', `✅ Получено контейнеров: ${containers.length}`, 'success');
const containerList = containers.slice(0, 3).map(c =>
`${c.name} (${c.status})`
).join('<br>');
document.getElementById('containersResult').innerHTML +=
`<div class="success">Первые 3 контейнера:<br>${containerList}</div>`;
} else {
const errorText = await response.text();
log('containersResult', `❌ Ошибка: ${response.status} - ${errorText}`, 'error');
}
} catch (error) {
log('containersResult', `❌ Ошибка: ${error.message}`, 'error');
}
}
function testWebSocket() {
const token = localStorage.getItem('access_token');
if (!token) {
log('websocketResult', '❌ Токен не найден. Сначала выполните вход.', 'error');
return;
}
log('websocketResult', '🔄 Подключение к WebSocket...', 'info');
// Получаем первый контейнер для теста
fetch(`${baseUrl}/api/containers/services`, {
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => response.json())
.then(containers => {
if (containers.length === 0) {
log('websocketResult', '❌ Нет контейнеров для тестирования', 'error');
return;
}
const container = containers[0];
const wsUrl = `ws://localhost:9001/api/websocket/logs/${container.id}?tail=5&token=${token}&service=${container.name}`;
log('websocketResult', `🔗 Подключение к: ${wsUrl.substring(0, 80)}...`, 'info');
const ws = new WebSocket(wsUrl);
ws.onopen = function() {
log('websocketResult', '✅ WebSocket соединение установлено!', 'success');
};
ws.onmessage = function(event) {
const message = event.data;
log('websocketResult', `📨 Получено сообщение: ${message.substring(0, 100)}...`, 'success');
// Закрываем соединение после получения первого сообщения
setTimeout(() => {
ws.close();
log('websocketResult', '🔌 WebSocket соединение закрыто', 'info');
}, 1000);
};
ws.onerror = function(error) {
log('websocketResult', `❌ WebSocket ошибка: ${error}`, 'error');
};
ws.onclose = function() {
log('websocketResult', '🔌 WebSocket соединение закрыто', 'info');
};
})
.catch(error => {
log('websocketResult', `❌ Ошибка получения контейнеров: ${error.message}`, 'error');
});
}
function clearToken() {
localStorage.removeItem('access_token');
log('clearResult', '✅ Токен очищен', 'success');
}
// Автоматическая проверка токена при загрузке
window.onload = function() {
checkToken();
};
</script>
</body>
</html>

87
test_websocket.py Normal file
View File

@ -0,0 +1,87 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Тестовый скрипт для проверки WebSocket соединений LogBoard+
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
import asyncio
import websockets
import json
import requests
async def test_websocket():
"""Тестирование WebSocket соединений"""
base_url = "http://localhost:9001"
# 1. Получаем токен
print("🔐 Получение токена...")
login_data = {"username": "admin", "password": "admin"}
try:
response = requests.post(f"{base_url}/api/auth/login", json=login_data)
if response.status_code != 200:
print(f" ❌ Ошибка входа: {response.status_code}")
return
token = response.json()["access_token"]
print(" ✅ Токен получен")
except Exception as e:
print(f" ❌ Ошибка входа: {e}")
return
# 2. Получаем список контейнеров
print("\n🔍 Получение контейнеров...")
headers = {"Authorization": f"Bearer {token}"}
try:
response = requests.get(f"{base_url}/api/containers/services", headers=headers)
if response.status_code != 200:
print(f" ❌ Ошибка получения контейнеров: {response.status_code}")
return
containers = response.json()
print(f" ✅ Получено контейнеров: {len(containers)}")
if not containers:
print(" ❌ Нет контейнеров для тестирования")
return
first_container = containers[0]
container_id = first_container.get('id')
container_name = first_container.get('name')
print(f" 📦 Тестируем контейнер: {container_name} ({container_id})")
except Exception as e:
print(f" ❌ Ошибка получения контейнеров: {e}")
return
# 3. Тестируем WebSocket соединение
print(f"\n🔌 Тестирование WebSocket для {container_name}...")
# Формируем WebSocket URL
ws_url = f"ws://localhost:9001/api/websocket/logs/{container_id}?tail=10&token={token}&service={container_name}"
print(f" 🔗 WebSocket URL: {ws_url}")
try:
async with websockets.connect(ws_url) as websocket:
print(" ✅ WebSocket соединение установлено")
# Ждем сообщения
print(" 📡 Ожидание сообщений...")
message_count = 0
max_messages = 5
async for message in websocket:
message_count += 1
print(f" 📨 Сообщение {message_count}: {message[:100]}...")
if message_count >= max_messages:
print(f" ✅ Получено {message_count} сообщений")
break
except Exception as e:
print(f" ❌ Ошибка WebSocket: {e}")
if __name__ == "__main__":
asyncio.run(test_websocket())