This commit is contained in:
2026-03-16 18:57:22 +03:00
parent 65eca64a5f
commit 668e62d652
18 changed files with 6386 additions and 1347 deletions

556
templates/mobile_debug.html Normal file
View 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>