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> |