v2.0.10: загрузка через ВМ-буфер (паттерн drhider)
Deploy contracts-flask / validate (push) Canceled after 0s
Deploy contracts-flask / validate (push) Canceled after 0s
- /api/upload_refs: pull файлов с ВМ (SSRF-guard, лимит 50МБ, ретраи, DELETE) - uploadFile: PUT на WebDAV /contracts-upload/ → refs → pull - nginx: location /contracts-upload/ (WebDAV + CORS для managed-фронта)
This commit is contained in:
+25
-12
@@ -241,21 +241,18 @@ function convertDoc(file, onProgress) {
|
||||
}
|
||||
|
||||
/**
|
||||
* ⛔ НЕ МЕНЯТЬ ⛔ uploadFile — fetch-загрузка с честным счётчиком времени.
|
||||
* ⛔ НЕ МЕНЯТЬ БЕЗ РАЗРЕШЕНИЯ НАЕЛЯ ⛔ uploadFile — загрузка через ВМ-буфер (паттерн drhider).
|
||||
*
|
||||
* v2.0.3: вместо фейкового ↑N% — честный счётчик ⏳ соединение... Nс → ⏳ отправка... Nс.
|
||||
* fetch() не даёт реальный upload progress, поэтому считаем секунды.
|
||||
* Кэш-бастинг: ?_=Date.now()
|
||||
* Фаза 1: PUT файла на ВМ (WebDAV /contracts-upload/, мимо шлюза кластера ~64КБ).
|
||||
* Фаза 2: POST /api/upload_refs {files:[{name,size,url}]} — бэк сам тянет файл с ВМ.
|
||||
* Честный счётчик времени: ⏳ соединение... Nс → ⏳ отправка... Nс (fetch не даёт progress).
|
||||
*/
|
||||
function uploadFile(file, onProgress, zipSource) {
|
||||
var startTime = Date.now();
|
||||
var phase = 'connecting'; // connecting → uploading
|
||||
if (onProgress) onProgress({ kind: 'connecting', elapsed: 0 });
|
||||
var fd = new FormData();
|
||||
fd.append('files', file, file.name);
|
||||
if (state.contractId) fd.append('contract_id', state.contractId);
|
||||
fd.append('batch_id', state.batchId);
|
||||
if (zipSource) fd.append('zip_source', zipSource);
|
||||
var token = crypto.randomUUID();
|
||||
var vmUrl = VM_UPLOAD_URL + token + '_0';
|
||||
|
||||
// Честный счётчик: каждую секунду обновляем elapsed
|
||||
var timer = setInterval(function() {
|
||||
@@ -263,17 +260,33 @@ function uploadFile(file, onProgress, zipSource) {
|
||||
if (onProgress) onProgress({ kind: phase, elapsed: elapsed });
|
||||
}, 1000);
|
||||
|
||||
return fetch(UPLOAD_URL + '?_=' + Date.now(), { method: 'POST', body: fd })
|
||||
// Фаза 1: PUT файла на ВМ-буфер
|
||||
return fetch(vmUrl, { method: 'PUT', body: file, headers: { 'Content-Type': 'application/octet-stream' } })
|
||||
.then(function(r) {
|
||||
if (!r.ok) throw new Error('VM upload HTTP ' + r.status);
|
||||
phase = 'uploading';
|
||||
// Фаза 2: refs на бэкенд → pull с ВМ
|
||||
var body = { batch_id: state.batchId, files: [{ name: file.name, size: file.size, url: vmUrl }] };
|
||||
if (state.contractId) body.contract_id = state.contractId;
|
||||
if (zipSource) body.zip_source = zipSource;
|
||||
return fetch('/api/upload_refs?_=' + Date.now(), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
})
|
||||
.then(function(r) {
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
return r.json();
|
||||
})
|
||||
.then(function(data) {
|
||||
clearInterval(timer);
|
||||
if (data.ok) {
|
||||
if (data.ok && data.results && data.results.length === 1) {
|
||||
var res = data.results[0];
|
||||
if (!res.ok) throw new Error(res.error || 'Неизвестная ошибка');
|
||||
var elapsed = Math.floor((Date.now() - startTime) / 1000);
|
||||
if (onProgress) onProgress({ kind: 'uploading', elapsed: elapsed });
|
||||
return data;
|
||||
return res; // {ok, doc_id, contract_id, parsed}
|
||||
}
|
||||
throw new Error(data.error || 'Неизвестная ошибка');
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user