556 lines
20 KiB
HTML
556 lines
20 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: 20px;
|
||
color: #333;
|
||
}
|
||
|
||
.container {
|
||
max-width: 500px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.card {
|
||
background: white;
|
||
border-radius: 24px;
|
||
padding: 25px;
|
||
margin-bottom: 20px;
|
||
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||
}
|
||
|
||
h1 {
|
||
font-size: 24px;
|
||
margin-bottom: 20px;
|
||
color: #0b1c34;
|
||
}
|
||
|
||
h2 {
|
||
font-size: 18px;
|
||
margin-bottom: 15px;
|
||
color: #1f3f60;
|
||
}
|
||
|
||
.info-row {
|
||
background: #f5f9ff;
|
||
padding: 12px;
|
||
border-radius: 12px;
|
||
margin-bottom: 10px;
|
||
font-size: 14px;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.info-label {
|
||
font-weight: 600;
|
||
color: #1f3f60;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.info-value {
|
||
color: #333;
|
||
font-family: monospace;
|
||
}
|
||
|
||
.success {
|
||
color: #10b981;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.error {
|
||
color: #ef4444;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.warning {
|
||
color: #f59e0b;
|
||
font-weight: 600;
|
||
}
|
||
|
||
button {
|
||
background: #0b1c34;
|
||
color: white;
|
||
border: none;
|
||
padding: 14px 20px;
|
||
border-radius: 40px;
|
||
font-weight: 600;
|
||
font-size: 16px;
|
||
width: 100%;
|
||
margin: 10px 0;
|
||
cursor: pointer;
|
||
transition: 0.2s;
|
||
}
|
||
|
||
button:active {
|
||
transform: scale(0.98);
|
||
background: #1b3f6b;
|
||
}
|
||
|
||
button.secondary {
|
||
background: #e5e7eb;
|
||
color: #1f3f60;
|
||
}
|
||
|
||
.test-result {
|
||
margin-top: 15px;
|
||
padding: 15px;
|
||
background: #f5f5f5;
|
||
border-radius: 16px;
|
||
font-size: 14px;
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
display: none;
|
||
}
|
||
|
||
.test-result pre {
|
||
white-space: pre-wrap;
|
||
word-wrap: break-word;
|
||
font-family: monospace;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.ip-input {
|
||
width: 100%;
|
||
padding: 12px;
|
||
border: 2px solid #dee9f5;
|
||
border-radius: 30px;
|
||
font-size: 16px;
|
||
margin-bottom: 10px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<!-- Информация об устройстве -->
|
||
<div class="card">
|
||
<h1>📱 Mobile Debug Tool</h1>
|
||
|
||
<div class="info-row">
|
||
<div class="info-label">User Agent:</div>
|
||
<div class="info-value" id="userAgent"></div>
|
||
</div>
|
||
|
||
<div class="info-row">
|
||
<div class="info-label">Платформа:</div>
|
||
<div class="info-value" id="platform"></div>
|
||
</div>
|
||
|
||
<div class="info-row">
|
||
<div class="info-label">Язык:</div>
|
||
<div class="info-value" id="language"></div>
|
||
</div>
|
||
|
||
<div class="info-row">
|
||
<div class="info-label">Размер экрана:</div>
|
||
<div class="info-value" id="screenSize"></div>
|
||
</div>
|
||
|
||
<div class="info-row">
|
||
<div class="info-label">Онлайн:</div>
|
||
<div class="info-value" id="online"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Настройки подключения -->
|
||
<div class="card">
|
||
<h2>🔧 Настройки подключения</h2>
|
||
|
||
<input type="text" class="ip-input" id="serverIp" placeholder="IP сервера (например, 192.168.1.100)">
|
||
<div class="info-row">
|
||
<div class="info-label">Текущий URL:</div>
|
||
<div class="info-value" id="currentUrl"></div>
|
||
</div>
|
||
|
||
<div class="flex">
|
||
<button onclick="testConnection()" id="testBtn">🔍 Проверить соединение</button>
|
||
<button class="secondary" onclick="copyInfo()">📋 Копировать</button>
|
||
</div>
|
||
|
||
<div id="connectionResult" class="test-result"></div>
|
||
</div>
|
||
|
||
<!-- Тесты API -->
|
||
<div class="card">
|
||
<h2>🧪 Тесты API</h2>
|
||
|
||
<button onclick="testHealth()">🏥 Проверить Health</button>
|
||
<div id="healthResult" class="test-result"></div>
|
||
|
||
<button onclick="testRegister()">📝 Тест регистрации</button>
|
||
<div id="registerResult" class="test-result"></div>
|
||
|
||
<button onclick="testLogin()">🔑 Тест входа</button>
|
||
<div id="loginResult" class="test-result"></div>
|
||
|
||
<button onclick="testCORS()">🌐 Проверить CORS</button>
|
||
<div id="corsResult" class="test-result"></div>
|
||
</div>
|
||
|
||
<!-- Логи ошибок -->
|
||
<div class="card">
|
||
<h2>⚠️ Логи ошибок</h2>
|
||
<div id="errorLogs" class="info-row" style="min-height: 100px; max-height: 200px; overflow-y: auto;">
|
||
Ошибок нет
|
||
</div>
|
||
<button class="secondary" onclick="clearLogs()">Очистить логи</button>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// Конфигурация
|
||
let serverIp = localStorage.getItem('serverIp') || window.location.hostname;
|
||
document.getElementById('serverIp').value = serverIp;
|
||
|
||
// Логирование ошибок
|
||
let errorLogs = [];
|
||
|
||
function logError(message, data = null) {
|
||
const timestamp = new Date().toLocaleTimeString();
|
||
const logEntry = `[${timestamp}] ${message} ${data ? JSON.stringify(data) : ''}`;
|
||
errorLogs.push(logEntry);
|
||
|
||
const logsDiv = document.getElementById('errorLogs');
|
||
logsDiv.innerHTML = errorLogs.map(log => `<div>${log}</div>`).join('');
|
||
console.error(logEntry);
|
||
}
|
||
|
||
function clearLogs() {
|
||
errorLogs = [];
|
||
document.getElementById('errorLogs').innerHTML = 'Ошибок нет';
|
||
}
|
||
|
||
// Информация об устройстве
|
||
function updateDeviceInfo() {
|
||
document.getElementById('userAgent').textContent = navigator.userAgent;
|
||
document.getElementById('platform').textContent = navigator.platform;
|
||
document.getElementById('language').textContent = navigator.language;
|
||
document.getElementById('screenSize').textContent = `${window.screen.width}x${window.screen.height}`;
|
||
document.getElementById('online').textContent = navigator.onLine ? '✅ Да' : '❌ Нет';
|
||
document.getElementById('currentUrl').textContent = window.location.href;
|
||
}
|
||
|
||
updateDeviceInfo();
|
||
|
||
// Сохранение IP
|
||
document.getElementById('serverIp').addEventListener('change', function(e) {
|
||
serverIp = e.target.value;
|
||
localStorage.setItem('serverIp', serverIp);
|
||
});
|
||
|
||
// Копирование информации
|
||
function copyInfo() {
|
||
const info = {
|
||
userAgent: navigator.userAgent,
|
||
platform: navigator.platform,
|
||
language: navigator.language,
|
||
screenSize: `${window.screen.width}x${window.screen.height}`,
|
||
online: navigator.onLine,
|
||
url: window.location.href,
|
||
serverIp: serverIp
|
||
};
|
||
|
||
navigator.clipboard.writeText(JSON.stringify(info, null, 2))
|
||
.then(() => alert('Информация скопирована!'))
|
||
.catch(() => alert('Ошибка копирования'));
|
||
}
|
||
|
||
// Проверка соединения
|
||
async function testConnection() {
|
||
const btn = document.getElementById('testBtn');
|
||
const resultDiv = document.getElementById('connectionResult');
|
||
|
||
btn.disabled = true;
|
||
btn.innerHTML = '<div class="loader"></div> Проверка...';
|
||
resultDiv.style.display = 'block';
|
||
resultDiv.innerHTML = '<div class="flex"><div class="loader"></div> Проверка соединения...</div>';
|
||
|
||
try {
|
||
// Пробуем разные варианты подключения
|
||
const urls = [
|
||
`http://${serverIp}:8000/api/health`,
|
||
`http://${serverIp}:8000/health`,
|
||
`http://${window.location.hostname}:8000/api/health`,
|
||
`http://127.0.0.1:8000/api/health`,
|
||
`http://localhost:8000/api/health`
|
||
];
|
||
|
||
let success = false;
|
||
let results = [];
|
||
|
||
for (const url of urls) {
|
||
try {
|
||
const start = Date.now();
|
||
const response = await fetch(url, {
|
||
mode: 'no-cors',
|
||
headers: { 'Accept': 'application/json' }
|
||
});
|
||
const time = Date.now() - start;
|
||
|
||
results.push({
|
||
url,
|
||
status: response.status,
|
||
time: `${time}ms`,
|
||
ok: response.ok
|
||
});
|
||
|
||
if (response.ok) {
|
||
success = true;
|
||
}
|
||
} catch (e) {
|
||
results.push({
|
||
url,
|
||
error: e.message,
|
||
time: 'failed'
|
||
});
|
||
}
|
||
}
|
||
|
||
let html = '<h3>Результаты проверки:</h3>';
|
||
results.forEach(r => {
|
||
html += `<div style="margin: 10px 0; padding: 8px; background: ${r.ok ? '#d1fae5' : '#fee2e2'}; border-radius: 8px;">`;
|
||
html += `<div><strong>URL:</strong> ${r.url}</div>`;
|
||
if (r.status) html += `<div><strong>Статус:</strong> ${r.status}</div>`;
|
||
if (r.time) html += `<div><strong>Время:</strong> ${r.time}</div>`;
|
||
if (r.error) html += `<div><strong>Ошибка:</strong> ${r.error}</div>`;
|
||
if (r.ok) html += `<div class="success">✅ Доступно</div>`;
|
||
else html += `<div class="error">❌ Недоступно</div>`;
|
||
html += '</div>';
|
||
});
|
||
|
||
html += `<div style="margin-top: 15px; font-weight: 600;">
|
||
Общий статус: ${success ? '✅ Сервер доступен' : '❌ Сервер недоступен'}
|
||
</div>`;
|
||
|
||
resultDiv.innerHTML = html;
|
||
|
||
} catch (error) {
|
||
logError('Connection test error', error);
|
||
resultDiv.innerHTML = `<div class="error">❌ Ошибка: ${error.message}</div>`;
|
||
}
|
||
|
||
btn.disabled = false;
|
||
btn.innerHTML = '🔍 Проверить соединение';
|
||
}
|
||
|
||
// Тест Health
|
||
async function testHealth() {
|
||
const resultDiv = document.getElementById('healthResult');
|
||
resultDiv.style.display = 'block';
|
||
resultDiv.innerHTML = '<div class="flex"><div class="loader"></div> Проверка...</div>';
|
||
|
||
try {
|
||
const url = `http://${serverIp}:8000/api/health`;
|
||
const response = await fetch(url, {
|
||
headers: { 'Accept': 'application/json' }
|
||
});
|
||
|
||
const data = await response.json();
|
||
|
||
resultDiv.innerHTML = `
|
||
<div class="success">✅ Успешно!</div>
|
||
<pre>${JSON.stringify(data, null, 2)}</pre>
|
||
`;
|
||
|
||
} catch (error) {
|
||
logError('Health test error', error);
|
||
resultDiv.innerHTML = `
|
||
<div class="error">❌ Ошибка: ${error.message}</div>
|
||
<div style="margin-top: 10px;">
|
||
<strong>Проверьте:</strong>
|
||
<ul>
|
||
<li>Правильный ли IP сервера? (${serverIp})</li>
|
||
<li>Сервер запущен? (python main.py)</li>
|
||
<li>Оба устройства в одной WiFi сети?</li>
|
||
<li>Firewall не блокирует порт 8000?</li>
|
||
</ul>
|
||
</div>
|
||
`;
|
||
}
|
||
}
|
||
|
||
// Тест CORS
|
||
async function testCORS() {
|
||
const resultDiv = document.getElementById('corsResult');
|
||
resultDiv.style.display = 'block';
|
||
resultDiv.innerHTML = '<div class="flex"><div class="loader"></div> Проверка CORS...</div>';
|
||
|
||
try {
|
||
const url = `http://${serverIp}:8000/api/health`;
|
||
|
||
// Пробуем разные режимы
|
||
const tests = [
|
||
{ mode: 'cors', credentials: 'omit' },
|
||
{ mode: 'cors', credentials: 'include' },
|
||
{ mode: 'no-cors' }
|
||
];
|
||
|
||
let results = [];
|
||
|
||
for (const test of tests) {
|
||
try {
|
||
const response = await fetch(url, {
|
||
mode: test.mode,
|
||
credentials: test.credentials,
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Accept': 'application/json'
|
||
}
|
||
});
|
||
|
||
results.push({
|
||
test: JSON.stringify(test),
|
||
status: response.status,
|
||
ok: response.ok
|
||
});
|
||
} catch (e) {
|
||
results.push({
|
||
test: JSON.stringify(test),
|
||
error: e.message
|
||
});
|
||
}
|
||
}
|
||
|
||
let html = '<h3>Результаты CORS тестов:</h3>';
|
||
results.forEach(r => {
|
||
html += `<div style="margin: 10px 0; padding: 8px; background: ${r.ok ? '#d1fae5' : '#fee2e2'}; border-radius: 8px;">`;
|
||
html += `<div><strong>Тест:</strong> ${r.test}</div>`;
|
||
if (r.status) html += `<div><strong>Статус:</strong> ${r.status}</div>`;
|
||
if (r.error) html += `<div><strong>Ошибка:</strong> ${r.error}</div>`;
|
||
html += '</div>';
|
||
});
|
||
|
||
resultDiv.innerHTML = html;
|
||
|
||
} catch (error) {
|
||
logError('CORS test error', error);
|
||
resultDiv.innerHTML = `<div class="error">❌ Ошибка: ${error.message}</div>`;
|
||
}
|
||
}
|
||
|
||
// Тест регистрации
|
||
async function testRegister() {
|
||
const resultDiv = document.getElementById('registerResult');
|
||
resultDiv.style.display = 'block';
|
||
resultDiv.innerHTML = '<div class="flex"><div class="loader"></div> Тест регистрации...</div>';
|
||
|
||
const testData = {
|
||
full_name: "Тест Тестов",
|
||
email: `test${Date.now()}@example.com`,
|
||
phone: "+7 (999) 123-45-67",
|
||
telegram: "@test",
|
||
password: "password123",
|
||
role: "employee"
|
||
};
|
||
|
||
try {
|
||
const url = `http://${serverIp}:8000/api/register`;
|
||
|
||
const response = await fetch(url, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Accept': 'application/json'
|
||
},
|
||
body: JSON.stringify(testData)
|
||
});
|
||
|
||
const data = await response.json();
|
||
|
||
resultDiv.innerHTML = `
|
||
<div class="${response.ok ? 'success' : 'error'}">
|
||
${response.ok ? '✅ Успешно!' : '❌ Ошибка'}
|
||
</div>
|
||
<div><strong>Статус:</strong> ${response.status}</div>
|
||
<pre>${JSON.stringify(data, null, 2)}</pre>
|
||
`;
|
||
|
||
} catch (error) {
|
||
logError('Register test error', error);
|
||
resultDiv.innerHTML = `
|
||
<div class="error">❌ Ошибка: ${error.message}</div>
|
||
<div style="margin-top: 10px;">
|
||
<strong>Тип ошибки:</strong> ${error.name}<br>
|
||
<strong>Стектрейс:</strong> ${error.stack}
|
||
</div>
|
||
`;
|
||
}
|
||
}
|
||
|
||
// Тест входа
|
||
async function testLogin() {
|
||
const resultDiv = document.getElementById('loginResult');
|
||
resultDiv.style.display = 'block';
|
||
resultDiv.innerHTML = '<div class="flex"><div class="loader"></div> Тест входа...</div>';
|
||
|
||
try {
|
||
const url = `http://${serverIp}:8000/api/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"
|
||
})
|
||
});
|
||
|
||
const data = await response.json();
|
||
|
||
resultDiv.innerHTML = `
|
||
<div class="${response.ok ? 'success' : 'error'}">
|
||
${response.ok ? '✅ Успешно!' : '❌ Ошибка'}
|
||
</div>
|
||
<div><strong>Статус:</strong> ${response.status}</div>
|
||
<pre>${JSON.stringify(data, null, 2)}</pre>
|
||
`;
|
||
|
||
} catch (error) {
|
||
logError('Login test error', error);
|
||
resultDiv.innerHTML = `<div class="error">❌ Ошибка: ${error.message}</div>`;
|
||
}
|
||
}
|
||
|
||
// Автоматический тест при загрузке
|
||
setTimeout(() => {
|
||
testConnection();
|
||
}, 1000);
|
||
</script>
|
||
</body>
|
||
</html> |