Версия 1.2.2: JSON-ошибки как на бэкенде, лимиты 100 МБ, овал-подсказка, безопасный парсинг ответов

- med_diag_json_error() — ошибки в формате {'error': '...'} (WP_REST_Response)
- 4 проверки лимита 100 МБ (CONTENT_LENGTH, UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, size)
- Проверка расширения файла (mp4/avi/mov/mkv/webm/m4v)
- Имя файла только ASCII (med-diag-upload.ext) — защита multipart
- filename скачивания из Content-Disposition бэкенда
- Овал .med-scan-oval (inset 12%, затемнение вне зоны)
- escapeHtml() + parseJson() — безопасная обработка ответов (XSS, HTML-ошибки)
- README: nginx лимиты, таблица ошибок
This commit is contained in:
apuc committed 2026-09-18 11:16:40 +03:00
1 parent 867fe7bc66
commit 90d9576425
4 files changed
+180 -40

No files matched your search

+36 -11
View File
@@ -20,6 +20,29 @@
return USE_PROXY ? PROXY_URL + path : API_URL + path;
}
// Экранирование текста перед вставкой в innerHTML: сообщение об ошибке
// может прийти из ответа сервера (HTML-страница nginx/PHP и т.п.).
function escapeHtml(text) {
return String(text === undefined || text === null ? '' : text)
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
// Безопасный разбор ответа: если сервер вернул не JSON (например,
// HTML-страницу ошибки), не падаем с "Unexpected token '<'".
async function parseJson(response) {
var ct = (response.headers.get('content-type') || '').toLowerCase();
if (ct.indexOf('application/json') !== -1) {
return await response.json();
}
// HTML-ответ превращаем в короткое текстовое сообщение
var text = (await response.text()).replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
if (text.length > 300) text = text.slice(0, 300) + '…';
return { error: text || ('Ошибка сервера (HTTP ' + response.status + ')') };
}
var uploadArea = document.getElementById('uploadArea');
var fileInput = document.getElementById('fileInput');
var analyzeBtn = document.getElementById('analyzeBtn');
@@ -85,14 +108,14 @@
});
if (!response.ok) {
var err = await response.json();
var err = await parseJson(response);
var e = new Error(err.error || 'Ошибка анализа');
e.videoUrl = err.video_url;
throw e;
}
setProgress(90, 'Формирование результата...');
var data = await response.json();
var data = await parseJson(response);
if (data.error) {
showError(data.error);
@@ -130,7 +153,7 @@
function showError(msg, videoUrl) {
progressContainer.style.display = 'none';
resultCard.style.display = 'block';
var html = '<div class="med-error-box"><strong>Ошибка:</strong> ' + msg + '</div>';
var html = '<div class="med-error-box"><strong>Ошибка:</strong> ' + escapeHtml(msg) + '</div>';
if (videoUrl) {
html += '<div style="text-align:center; margin-top:15px">' +
'<a class="med-btn-download" href="' + downloadUrl(videoUrl) + '">🎬 Скачать записанное видео</a>' +
@@ -257,7 +280,7 @@
video: {
facingMode: 'user',
width: { ideal: 1280 },
height: { ideal: 720 }
height: { ideal: 1280 }
},
audio: false
});
@@ -277,11 +300,11 @@
scanLastFrameTime = 0;
updateScanProgress(0);
// Canvas 720×720 — квадрат, лицо целиком влезает в portrait и landscape.
// Canvas 1080×1080 — квадрат, лицо целиком влезает в portrait и landscape.
// Серверное extract_frames масштабирует до 1280×720 для SDK.
scanCanvas = document.createElement('canvas');
scanCanvas.width = 720;
scanCanvas.height = 720;
scanCanvas.width = 1080;
scanCanvas.height = 1080;
scanCtx = scanCanvas.getContext('2d');
var captureStream;
@@ -320,16 +343,18 @@
return;
}
if (ts - scanLastFrameTime >= FRAME_INTERVAL) {
// Квадратный center-crop: берём центральную квадратную область из видео.
// Квадратный center-crop с запасом 10%: берём чуть меньший
// квадрат, чтобы голова гарантированно помещалась целиком
// (иначе SDK не находит лицо из-за обрезанной головы).
var vw = scanVideo.videoWidth;
var vh = scanVideo.videoHeight;
if (vw && vh) {
var side = Math.min(vw, vh);
var side = Math.min(vw, vh) * 0.9;
var sx = (vw - side) / 2;
var sy = (vh - side) / 2;
scanCtx.drawImage(scanVideo, sx, sy, side, side, 0, 0, 720, 720);
scanCtx.drawImage(scanVideo, sx, sy, side, side, 0, 0, 1080, 1080);
} else {
scanCtx.drawImage(scanVideo, 0, 0, 720, 720);
scanCtx.drawImage(scanVideo, 0, 0, 1080, 1080);
}
scanFrameCount++;
scanLastFrameTime = ts;