v1.2
This commit is contained in:
556
templates/mobile_debug.html
Normal file
556
templates/mobile_debug.html
Normal file
@@ -0,0 +1,556 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user