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:
parent
769d33777d
commit
85757ca717
10
app/app.py
10
app/app.py
@ -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
107
test_api.py
Normal 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
199
test_browser.html
Normal 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
87
test_websocket.py
Normal 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())
|
Loading…
x
Reference in New Issue
Block a user