v1.2
This commit is contained in:
962
templates/mobile_debug_simple.html
Normal file
962
templates/mobile_debug_simple.html
Normal file
@@ -0,0 +1,962 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>Mobile Debug - Rabota.Today</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #0b1c34;
|
||||
padding: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 24px;
|
||||
padding: 20px;
|
||||
margin-bottom: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 22px;
|
||||
margin-bottom: 15px;
|
||||
color: #0b1c34;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
margin-bottom: 12px;
|
||||
color: #1f3f60;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
margin: 10px 0;
|
||||
color: #1f3f60;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
background: #f5f9ff;
|
||||
padding: 12px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: 600;
|
||||
color: #1f3f60;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #333;
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.success { color: #10b981; font-weight: 600; }
|
||||
.error { color: #ef4444; font-weight: 600; }
|
||||
.warning { color: #f59e0b; font-weight: 600; }
|
||||
.info { color: #3b82f6; font-weight: 600; }
|
||||
|
||||
button {
|
||||
background: #0b1c34;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 14px 16px;
|
||||
border-radius: 40px;
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
width: 100%;
|
||||
margin: 8px 0;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.98);
|
||||
background: #1b3f6b;
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
background: #e5e7eb;
|
||||
color: #1f3f60;
|
||||
}
|
||||
|
||||
button.secondary:active {
|
||||
background: #d1d5db;
|
||||
}
|
||||
|
||||
button.small {
|
||||
padding: 8px 12px;
|
||||
font-size: 13px;
|
||||
width: auto;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.test-result {
|
||||
margin-top: 15px;
|
||||
padding: 15px;
|
||||
background: #f5f5f5;
|
||||
border-radius: 16px;
|
||||
font-size: 13px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
display: none;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
border: 1px solid #dee9f5;
|
||||
}
|
||||
|
||||
.loader {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid #f3f3f3;
|
||||
border-top: 3px solid #0b1c34;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flex-start {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.ip-input {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #dee9f5;
|
||||
border-radius: 30px;
|
||||
font-size: 15px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.protocol-selector {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin: 15px 0;
|
||||
background: #f0f7ff;
|
||||
padding: 5px;
|
||||
border-radius: 40px;
|
||||
}
|
||||
|
||||
.protocol-btn {
|
||||
flex: 1;
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 10px;
|
||||
border-radius: 40px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: #385073;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.protocol-btn.active {
|
||||
background: white;
|
||||
color: #0b1c34;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.share-link {
|
||||
background: #e8f0fe;
|
||||
padding: 15px;
|
||||
border-radius: 16px;
|
||||
margin-top: 15px;
|
||||
word-break: break-all;
|
||||
font-size: 13px;
|
||||
border: 1px solid #3b82f6;
|
||||
}
|
||||
|
||||
.share-link a {
|
||||
color: #3b82f6;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
padding: 6px;
|
||||
border-bottom: 1px solid #dee9f5;
|
||||
font-size: 12px;
|
||||
color: #4f7092;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
border-radius: 20px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.status-badge.success {
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
}
|
||||
|
||||
.status-badge.error {
|
||||
background: #fee2e2;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.status-badge.warning {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.json-view {
|
||||
background: #1e293b;
|
||||
color: #e2e8f0;
|
||||
padding: 12px;
|
||||
border-radius: 12px;
|
||||
font-family: monospace;
|
||||
font-size: 11px;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.protocol-info {
|
||||
background: #dbeafe;
|
||||
padding: 12px;
|
||||
border-radius: 12px;
|
||||
margin: 10px 0;
|
||||
font-size: 13px;
|
||||
border-left: 4px solid #3b82f6;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- Заголовок -->
|
||||
<div class="card">
|
||||
<h1>📱 Rabota.Today Mobile Debug</h1>
|
||||
<div class="protocol-info" id="protocolInfo">
|
||||
<strong>🔄 Определение протокола:</strong> Загрузка...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Информация об устройстве -->
|
||||
<div class="card">
|
||||
<h2>📱 Информация об устройстве</h2>
|
||||
|
||||
<div class="info-row">
|
||||
<div class="label">🌐 Текущий протокол:</div>
|
||||
<div class="value" id="currentProtocol"></div>
|
||||
</div>
|
||||
|
||||
<div class="info-row">
|
||||
<div class="label">🔗 Базовый URL API:</div>
|
||||
<div class="value" id="baseApiUrl"></div>
|
||||
</div>
|
||||
|
||||
<div class="info-row">
|
||||
<div class="label">📱 Устройство:</div>
|
||||
<div class="value" id="deviceInfo"></div>
|
||||
</div>
|
||||
|
||||
<div class="info-row">
|
||||
<div class="label">🌍 Текущий URL:</div>
|
||||
<div class="value" id="currentUrl"></div>
|
||||
</div>
|
||||
|
||||
<div class="info-row">
|
||||
<div class="label">📶 Статус сети:</div>
|
||||
<div class="value" id="networkStatus"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Выбор протокола -->
|
||||
<div class="card">
|
||||
<h2>🔧 Настройки подключения</h2>
|
||||
|
||||
<div class="protocol-selector" id="protocolSelector">
|
||||
<button class="protocol-btn active" data-protocol="auto" onclick="setProtocol('auto')">🔄 Авто</button>
|
||||
<button class="protocol-btn" data-protocol="https" onclick="setProtocol('https')">🔒 HTTPS</button>
|
||||
<button class="protocol-btn" data-protocol="http" onclick="setProtocol('http')">⚠️ HTTP</button>
|
||||
</div>
|
||||
|
||||
<div class="flex-start" style="margin-top: 10px;">
|
||||
<button class="secondary small" onclick="copyDebugInfo()">📋 Копировать информацию</button>
|
||||
<button class="secondary small" onclick="saveIp()">💾 Сохранить настройки</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Основные тесты -->
|
||||
<div class="card">
|
||||
<h2>🔍 Диагностика API</h2>
|
||||
|
||||
<button onclick="runAllTests()" id="runAllBtn">▶️ Запустить все тесты</button>
|
||||
|
||||
<div class="flex" style="margin: 10px 0;">
|
||||
<button class="secondary" onclick="testConnection()" style="flex: 1;">🔌 Тест соединения</button>
|
||||
<button class="secondary" onclick="testCORS()" style="flex: 1;">🌐 Тест CORS</button>
|
||||
</div>
|
||||
|
||||
<div class="flex" style="margin: 10px 0;">
|
||||
<button class="secondary" onclick="testHealth()" style="flex: 1;">🏥 Health API</button>
|
||||
<button class="secondary" onclick="testRegister()" style="flex: 1;">📝 Тест регистрации</button>
|
||||
</div>
|
||||
|
||||
<div class="flex" style="margin: 10px 0;">
|
||||
<button class="secondary" onclick="testLogin()" style="flex: 1;">🔑 Тест входа</button>
|
||||
<button class="secondary" onclick="testAuth()" style="flex: 1;">👤 Тест авторизации</button>
|
||||
</div>
|
||||
|
||||
<div id="mainResult" class="test-result"></div>
|
||||
</div>
|
||||
|
||||
<!-- Результаты тестов -->
|
||||
<div class="card" id="resultsCard" style="display: none;">
|
||||
<h2>📊 Полные результаты</h2>
|
||||
<div id="results" class="json-view"></div>
|
||||
|
||||
<div id="shareLink" class="share-link" style="display: none;">
|
||||
<div>🔗 Результаты готовы:</div>
|
||||
<textarea id="resultsText" style="width: 100%; height: 100px; margin: 10px 0; padding: 8px; border-radius: 8px; border: 1px solid #3b82f6;" readonly></textarea>
|
||||
<button onclick="copyResults()" class="secondary">📋 Копировать результаты</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Лог ошибок -->
|
||||
<div class="card">
|
||||
<h2>📝 Лог событий</h2>
|
||||
<div id="log" style="max-height: 200px; overflow-y: auto; font-size: 12px; background: #f5f5f5; padding: 10px; border-radius: 12px;">
|
||||
<div class="log-entry">✅ Диагностика запущена</div>
|
||||
</div>
|
||||
<div class="flex" style="margin-top: 10px;">
|
||||
<button class="secondary small" onclick="clearLog()">Очистить лог</button>
|
||||
<button class="secondary small" onclick="downloadLog()">📥 Скачать лог</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Информация о помощи -->
|
||||
<div class="card">
|
||||
<h2>🆘 Помощь</h2>
|
||||
<div class="info-row">
|
||||
<div class="label">Если тесты не работают:</div>
|
||||
<ul style="margin-left: 20px; margin-top: 5px;">
|
||||
<li>Проверьте, что сервер запущен</li>
|
||||
<li>Убедитесь, что вы в одной сети</li>
|
||||
<li>Проверьте firewall (порт 8000)</li>
|
||||
<li>Попробуйте HTTP вместо HTTPS</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="label">Быстрые ссылки:</div>
|
||||
<div class="flex-start">
|
||||
<a href="/" class="secondary small" style="padding: 8px 12px;">🏠 Главная</a>
|
||||
<a href="/register" class="secondary small" style="padding: 8px 12px;">📝 Регистрация</a>
|
||||
<a href="/login" class="secondary small" style="padding: 8px 12px;">🔑 Вход</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ==================== КОНФИГУРАЦИЯ ====================
|
||||
|
||||
// Определяем базовый URL динамически
|
||||
const currentProtocol = window.location.protocol; // http: или https:
|
||||
const currentHost = window.location.host; // yarmarka.rabota.today или IP:порт
|
||||
let API_BASE_URL = `${currentProtocol}//${currentHost}/api`;
|
||||
|
||||
// Хранилище результатов
|
||||
let testResults = {
|
||||
device: {},
|
||||
connection: [],
|
||||
health: {},
|
||||
cors: {},
|
||||
register: {},
|
||||
login: {},
|
||||
auth: {},
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
let currentProtocolMode = 'auto'; // auto, https, http
|
||||
|
||||
// ==================== ИНИЦИАЛИЗАЦИЯ ====================
|
||||
|
||||
function init() {
|
||||
// Обновляем информацию
|
||||
updateDeviceInfo();
|
||||
updateProtocolInfo();
|
||||
|
||||
// Загружаем сохраненные настройки
|
||||
const savedMode = localStorage.getItem('protocolMode');
|
||||
if (savedMode) {
|
||||
setProtocol(savedMode);
|
||||
}
|
||||
|
||||
addLog('✅ Диагностика инициализирована');
|
||||
addLog(`📡 API URL: ${API_BASE_URL}`);
|
||||
}
|
||||
|
||||
function updateDeviceInfo() {
|
||||
const info = {
|
||||
userAgent: navigator.userAgent,
|
||||
platform: navigator.platform,
|
||||
language: navigator.language,
|
||||
screen: `${window.screen.width}x${window.screen.height}`,
|
||||
online: navigator.onLine,
|
||||
vendor: navigator.vendor,
|
||||
cookieEnabled: navigator.cookieEnabled
|
||||
};
|
||||
|
||||
document.getElementById('deviceInfo').textContent =
|
||||
`${info.platform}, ${info.screen}, ${info.language}`;
|
||||
|
||||
document.getElementById('currentUrl').textContent = window.location.href;
|
||||
document.getElementById('currentProtocol').textContent = currentProtocol;
|
||||
document.getElementById('baseApiUrl').textContent = API_BASE_URL;
|
||||
document.getElementById('networkStatus').textContent =
|
||||
info.online ? '✅ Онлайн' : '❌ Офлайн';
|
||||
|
||||
testResults.device = info;
|
||||
}
|
||||
|
||||
function updateProtocolInfo() {
|
||||
const protocolInfo = document.getElementById('protocolInfo');
|
||||
const isHttps = currentProtocol === 'https:';
|
||||
|
||||
if (isHttps) {
|
||||
protocolInfo.innerHTML = `
|
||||
<strong>🔄 Текущий протокол: HTTPS (защищенный)</strong><br>
|
||||
<span class="info">⚠️ API запросы должны идти через HTTPS или относительные пути</span>
|
||||
`;
|
||||
} else {
|
||||
protocolInfo.innerHTML = `
|
||||
<strong>🔄 Текущий протокол: HTTP (незащищенный)</strong><br>
|
||||
<span class="success">✅ API запросы будут работать напрямую</span>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== УПРАВЛЕНИЕ ПРОТОКОЛОМ ====================
|
||||
|
||||
function setProtocol(mode) {
|
||||
currentProtocolMode = mode;
|
||||
|
||||
// Обновляем кнопки
|
||||
document.querySelectorAll('.protocol-btn').forEach(btn => {
|
||||
btn.classList.remove('active');
|
||||
if (btn.dataset.protocol === mode) {
|
||||
btn.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Сохраняем настройку
|
||||
localStorage.setItem('protocolMode', mode);
|
||||
|
||||
// Перестраиваем API URL в зависимости от режима
|
||||
if (mode === 'https') {
|
||||
API_BASE_URL = `https://${currentHost}/api`;
|
||||
} else if (mode === 'http') {
|
||||
// Для HTTP нужно убедиться, что есть порт 8000
|
||||
const hostWithoutPort = currentHost.split(':')[0];
|
||||
API_BASE_URL = `http://${hostWithoutPort}:8000/api`;
|
||||
} else {
|
||||
// Auto - используем текущий протокол
|
||||
API_BASE_URL = `${currentProtocol}//${currentHost}/api`;
|
||||
}
|
||||
|
||||
document.getElementById('baseApiUrl').textContent = API_BASE_URL;
|
||||
addLog(`🔄 Протокол изменен на ${mode}, API URL: ${API_BASE_URL}`);
|
||||
}
|
||||
|
||||
// ==================== ЛОГИРОВАНИЕ ====================
|
||||
|
||||
function addLog(message, type = 'info') {
|
||||
const logDiv = document.getElementById('log');
|
||||
const entry = document.createElement('div');
|
||||
entry.className = 'log-entry';
|
||||
|
||||
let icon = '📌';
|
||||
if (type === 'success') icon = '✅';
|
||||
if (type === 'error') icon = '❌';
|
||||
if (type === 'warning') icon = '⚠️';
|
||||
|
||||
entry.textContent = `[${new Date().toLocaleTimeString()}] ${icon} ${message}`;
|
||||
logDiv.insertBefore(entry, logDiv.firstChild);
|
||||
|
||||
// Ограничиваем количество записей
|
||||
if (logDiv.children.length > 20) {
|
||||
logDiv.removeChild(logDiv.lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
function clearLog() {
|
||||
document.getElementById('log').innerHTML = '<div class="log-entry">✅ Лог очищен</div>';
|
||||
}
|
||||
|
||||
function downloadLog() {
|
||||
const logEntries = [];
|
||||
document.querySelectorAll('#log .log-entry').forEach(entry => {
|
||||
logEntries.push(entry.textContent);
|
||||
});
|
||||
|
||||
const logText = logEntries.join('\n');
|
||||
const blob = new Blob([logText], { type: 'text/plain' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `debug-log-${new Date().toISOString()}.txt`;
|
||||
a.click();
|
||||
}
|
||||
|
||||
// ==================== ОТОБРАЖЕНИЕ РЕЗУЛЬТАТОВ ====================
|
||||
|
||||
function showResult(title, content, isSuccess = true) {
|
||||
const resultDiv = document.getElementById('mainResult');
|
||||
resultDiv.style.display = 'block';
|
||||
resultDiv.innerHTML = `
|
||||
<div class="${isSuccess ? 'success' : 'error'}" style="margin-bottom: 10px;">
|
||||
${isSuccess ? '✅' : '❌'} ${title}
|
||||
</div>
|
||||
<pre style="white-space: pre-wrap; font-size: 12px; background: white; padding: 10px; border-radius: 8px;">${JSON.stringify(content, null, 2)}</pre>
|
||||
`;
|
||||
}
|
||||
|
||||
function updateFullResults() {
|
||||
const resultsDiv = document.getElementById('results');
|
||||
const resultsText = document.getElementById('resultsText');
|
||||
const resultsCard = document.getElementById('resultsCard');
|
||||
|
||||
resultsCard.style.display = 'block';
|
||||
const resultsJson = JSON.stringify(testResults, null, 2);
|
||||
resultsDiv.textContent = resultsJson;
|
||||
resultsText.value = resultsJson;
|
||||
document.getElementById('shareLink').style.display = 'block';
|
||||
}
|
||||
|
||||
function copyResults() {
|
||||
const resultsText = document.getElementById('resultsText');
|
||||
resultsText.select();
|
||||
document.execCommand('copy');
|
||||
addLog('✅ Результаты скопированы');
|
||||
alert('Результаты скопированы в буфер обмена!');
|
||||
}
|
||||
|
||||
function copyDebugInfo() {
|
||||
const info = {
|
||||
url: window.location.href,
|
||||
protocol: currentProtocol,
|
||||
apiUrl: API_BASE_URL,
|
||||
device: testResults.device,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = JSON.stringify(info, null, 2);
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(textarea);
|
||||
|
||||
addLog('✅ Информация скопирована');
|
||||
}
|
||||
|
||||
function saveIp() {
|
||||
addLog('✅ Настройки сохранены');
|
||||
alert('Настройки сохранены');
|
||||
}
|
||||
|
||||
// ==================== ТЕСТЫ API ====================
|
||||
|
||||
async function testConnection() {
|
||||
addLog('🔄 Тест соединения...');
|
||||
|
||||
const urls = [
|
||||
`${API_BASE_URL}/health`,
|
||||
`${API_BASE_URL.replace('/api', '')}/health`,
|
||||
`${window.location.protocol}//${window.location.host}/api/health`,
|
||||
`http://${window.location.hostname}:8000/api/health`
|
||||
];
|
||||
|
||||
let results = [];
|
||||
|
||||
for (const url of urls) {
|
||||
try {
|
||||
const start = Date.now();
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: { 'Accept': 'application/json' },
|
||||
mode: 'cors'
|
||||
});
|
||||
const time = Date.now() - start;
|
||||
|
||||
let data = null;
|
||||
try {
|
||||
data = await response.json();
|
||||
} catch {
|
||||
data = await response.text();
|
||||
}
|
||||
|
||||
const result = {
|
||||
url,
|
||||
status: response.status,
|
||||
time: `${time}ms`,
|
||||
ok: response.ok,
|
||||
data: data
|
||||
};
|
||||
|
||||
results.push(result);
|
||||
|
||||
if (response.ok) {
|
||||
addLog(`✅ Доступно: ${url} (${time}ms)`);
|
||||
} else {
|
||||
addLog(`⚠️ Ошибка ${response.status}: ${url}`);
|
||||
}
|
||||
} catch (e) {
|
||||
results.push({
|
||||
url,
|
||||
error: e.message,
|
||||
ok: false
|
||||
});
|
||||
addLog(`❌ Недоступно: ${url} - ${e.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
testResults.connection = results;
|
||||
|
||||
// Проверяем, есть ли успешные подключения
|
||||
const hasSuccess = results.some(r => r.ok);
|
||||
showResult('Тест соединения', results, hasSuccess);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
async function testHealth() {
|
||||
addLog('🔄 Тест Health API...');
|
||||
|
||||
try {
|
||||
const url = `${API_BASE_URL}/health`;
|
||||
const response = await fetch(url, {
|
||||
headers: { 'Accept': 'application/json' }
|
||||
});
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await response.json();
|
||||
} catch {
|
||||
data = await response.text();
|
||||
}
|
||||
|
||||
testResults.health = {
|
||||
status: response.status,
|
||||
ok: response.ok,
|
||||
data,
|
||||
url
|
||||
};
|
||||
|
||||
if (response.ok) {
|
||||
addLog('✅ Health API работает');
|
||||
showResult('Health API', data, true);
|
||||
} else {
|
||||
addLog(`❌ Health API ошибка: ${response.status}`);
|
||||
showResult('Health API ошибка', { status: response.status, data }, false);
|
||||
}
|
||||
|
||||
return { ok: response.ok, data };
|
||||
|
||||
} catch (error) {
|
||||
testResults.health = { error: error.message };
|
||||
addLog(`❌ Health API: ${error.message}`, 'error');
|
||||
showResult('Health API ошибка', { error: error.message }, false);
|
||||
return { ok: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async function testCORS() {
|
||||
addLog('🔄 Тест CORS...');
|
||||
|
||||
try {
|
||||
const url = `${API_BASE_URL}/health`;
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
const corsHeader = response.headers.get('access-control-allow-origin');
|
||||
const corsCredentials = response.headers.get('access-control-allow-credentials');
|
||||
|
||||
testResults.cors = {
|
||||
allowed: corsHeader === '*' || corsHeader === window.location.origin,
|
||||
header: corsHeader,
|
||||
credentials: corsCredentials,
|
||||
url: url
|
||||
};
|
||||
|
||||
if (corsHeader) {
|
||||
addLog(`✅ CORS заголовок: ${corsHeader}`);
|
||||
showResult('CORS тест', testResults.cors, true);
|
||||
} else {
|
||||
addLog('⚠️ Нет CORS заголовка', 'warning');
|
||||
showResult('CORS предупреждение', testResults.cors, false);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
testResults.cors = { error: error.message };
|
||||
addLog(`❌ CORS тест: ${error.message}`, 'error');
|
||||
showResult('CORS ошибка', { error: error.message }, false);
|
||||
}
|
||||
}
|
||||
|
||||
async function testRegister() {
|
||||
addLog('🔄 Тест регистрации...');
|
||||
|
||||
const testData = {
|
||||
full_name: "Тест Тестов",
|
||||
email: `test${Date.now()}@example.com`,
|
||||
phone: "+79991234567",
|
||||
telegram: null,
|
||||
password: "password123",
|
||||
role: "employee"
|
||||
};
|
||||
|
||||
try {
|
||||
const url = `${API_BASE_URL}/register`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(testData)
|
||||
});
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await response.json();
|
||||
} catch {
|
||||
data = await response.text();
|
||||
}
|
||||
|
||||
testResults.register = {
|
||||
status: response.status,
|
||||
ok: response.ok,
|
||||
data: data,
|
||||
url: url
|
||||
};
|
||||
|
||||
if (response.ok) {
|
||||
addLog(`✅ Регистрация работает, создан пользователь ID: ${data.user_id}`);
|
||||
showResult('Регистрация', {
|
||||
success: true,
|
||||
user_id: data.user_id,
|
||||
email: testData.email
|
||||
}, true);
|
||||
} else {
|
||||
addLog(`❌ Регистрация ошибка: ${response.status}`);
|
||||
showResult('Ошибка регистрации', { status: response.status, data }, false);
|
||||
}
|
||||
|
||||
return { ok: response.ok, data };
|
||||
|
||||
} catch (error) {
|
||||
testResults.register = { error: error.message };
|
||||
addLog(`❌ Регистрация: ${error.message}`, 'error');
|
||||
showResult('Ошибка регистрации', { error: error.message }, false);
|
||||
return { ok: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async function testLogin() {
|
||||
addLog('🔄 Тест входа...');
|
||||
|
||||
try {
|
||||
const url = `${API_BASE_URL}/login`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: "admin@rabota.today",
|
||||
password: "admin123"
|
||||
})
|
||||
});
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await response.json();
|
||||
} catch {
|
||||
data = await response.text();
|
||||
}
|
||||
|
||||
testResults.login = {
|
||||
status: response.status,
|
||||
ok: response.ok,
|
||||
data: data,
|
||||
url: url
|
||||
};
|
||||
|
||||
if (response.ok) {
|
||||
addLog(`✅ Вход работает, получен токен для ${data.full_name}`);
|
||||
showResult('Вход в систему', {
|
||||
success: true,
|
||||
user: data.full_name,
|
||||
role: data.role
|
||||
}, true);
|
||||
|
||||
// Сохраняем токен для теста авторизации
|
||||
if (data.access_token) {
|
||||
localStorage.setItem('test_token', data.access_token);
|
||||
}
|
||||
} else {
|
||||
addLog(`❌ Вход ошибка: ${response.status}`);
|
||||
showResult('Ошибка входа', { status: response.status, data }, false);
|
||||
}
|
||||
|
||||
return { ok: response.ok, data };
|
||||
|
||||
} catch (error) {
|
||||
testResults.login = { error: error.message };
|
||||
addLog(`❌ Вход: ${error.message}`, 'error');
|
||||
showResult('Ошибка входа', { error: error.message }, false);
|
||||
return { ok: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async function testAuth() {
|
||||
addLog('🔄 Тест авторизации...');
|
||||
|
||||
const token = localStorage.getItem('test_token');
|
||||
if (!token) {
|
||||
addLog('⚠️ Нет токена для теста авторизации', 'warning');
|
||||
showResult('Тест авторизации', { error: 'Сначала выполните вход' }, false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = `${API_BASE_URL}/user`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await response.json();
|
||||
} catch {
|
||||
data = await response.text();
|
||||
}
|
||||
|
||||
testResults.auth = {
|
||||
status: response.status,
|
||||
ok: response.ok,
|
||||
data: data
|
||||
};
|
||||
|
||||
if (response.ok) {
|
||||
addLog(`✅ Авторизация работает, пользователь: ${data.full_name}`);
|
||||
showResult('Авторизация', {
|
||||
success: true,
|
||||
user: data.full_name,
|
||||
email: data.email
|
||||
}, true);
|
||||
} else {
|
||||
addLog(`❌ Авторизация ошибка: ${response.status}`);
|
||||
showResult('Ошибка авторизации', { status: response.status, data }, false);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
testResults.auth = { error: error.message };
|
||||
addLog(`❌ Авторизация: ${error.message}`, 'error');
|
||||
showResult('Ошибка авторизации', { error: error.message }, false);
|
||||
}
|
||||
}
|
||||
|
||||
async function runAllTests() {
|
||||
const btn = document.getElementById('runAllBtn');
|
||||
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<div class="loader"></div> Выполнение тестов...';
|
||||
|
||||
// Очищаем предыдущие результаты
|
||||
testResults = {
|
||||
device: testResults.device,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
addLog('🚀 Запуск всех тестов...');
|
||||
|
||||
// Последовательное выполнение тестов
|
||||
await testConnection();
|
||||
await testHealth();
|
||||
await testCORS();
|
||||
await testRegister();
|
||||
await testLogin();
|
||||
await testAuth();
|
||||
|
||||
// Обновляем полные результаты
|
||||
updateFullResults();
|
||||
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '▶️ Запустить все тесты';
|
||||
|
||||
addLog('✅ Все тесты завершены');
|
||||
}
|
||||
|
||||
// ==================== ЗАПУСК ====================
|
||||
|
||||
// Запускаем инициализацию при загрузке
|
||||
window.addEventListener('load', () => {
|
||||
init();
|
||||
|
||||
// Автоматически запускаем базовые тесты через 1 секунду
|
||||
setTimeout(() => {
|
||||
testConnection();
|
||||
}, 1000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user