962 lines
34 KiB
HTML
962 lines
34 KiB
HTML
<!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> |