Parse large Excel imports asynchronously to avoid gateway 502 timeouts.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,7 +2,11 @@
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { api } from '@/lib/api';
|
||||
import type { ExcelPreviewResult, ImportContactsResult } from '@mento/shared';
|
||||
import type {
|
||||
ExcelImportPrepareStatus,
|
||||
ExcelPreviewResult,
|
||||
ImportContactsResult,
|
||||
} from '@mento/shared';
|
||||
import { UiBusyOverlay, UiButton, UiModal, UiSelect } from '@/components/ui';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -131,6 +135,10 @@ function updateImportProgress(processed: number, total: number) {
|
||||
progressLabel.value = `ورود فایل: ${processed.toLocaleString('fa-IR')} از ${total.toLocaleString('fa-IR')} ردیف`;
|
||||
}
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise((r) => setTimeout(r, ms));
|
||||
}
|
||||
|
||||
async function postPrepare(file: File, map?: Record<string, string>) {
|
||||
if (!props.preparePath) {
|
||||
throw new Error('preparePath تنظیم نشده');
|
||||
@@ -140,16 +148,43 @@ async function postPrepare(file: File, map?: Record<string, string>) {
|
||||
if (map && Object.values(map).some(Boolean)) {
|
||||
form.append('mapping', JSON.stringify(map));
|
||||
}
|
||||
// فقط آپلود — پارس روی سرور ناهمگام است؛ تایماوت کوتاه کافی است
|
||||
const { data } = await api.post<ImportContactsResult>(props.preparePath, form, {
|
||||
headers: { 'Content-Type': undefined },
|
||||
timeout: 10 * 60 * 1000,
|
||||
timeout: 3 * 60 * 1000,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
async function waitPrepareReady(sessionId: string) {
|
||||
if (!props.preparePath) throw new Error('preparePath تنظیم نشده');
|
||||
const statusUrl = `${props.preparePath}/${sessionId}`;
|
||||
const started = Date.now();
|
||||
const maxMs = 15 * 60 * 1000;
|
||||
while (Date.now() - started < maxMs) {
|
||||
const { data } = await api.get<ExcelImportPrepareStatus>(statusUrl, {
|
||||
timeout: 30_000,
|
||||
});
|
||||
if (data.status === 'ready') {
|
||||
progressLabel.value = `فایل آماده شد — ${data.total.toLocaleString('fa-IR')} ردیف`;
|
||||
return data;
|
||||
}
|
||||
if (data.status === 'error') {
|
||||
throw new Error(data.error || 'آمادهسازی فایل اکسل ناموفق بود');
|
||||
}
|
||||
const parsed = data.parsed || 0;
|
||||
progressPct.value = null;
|
||||
progressLabel.value = parsed
|
||||
? `در حال خواندن فایل روی سرور… ${parsed.toLocaleString('fa-IR')} ردیف`
|
||||
: 'در حال خواندن فایل روی سرور… لطفاً صبر کنید';
|
||||
await sleep(1200);
|
||||
}
|
||||
throw new Error('آمادهسازی فایل بیش از حد طول کشید. دوباره تلاش کنید.');
|
||||
}
|
||||
|
||||
async function runChunkedImport(file: File, map?: Record<string, string>) {
|
||||
phase.value = 'import';
|
||||
progressLabel.value = 'در حال آمادهسازی و خواندن فایل…';
|
||||
progressLabel.value = 'در حال ارسال فایل…';
|
||||
progressPct.value = 0;
|
||||
|
||||
let acc: ImportContactsResult = { created: 0, skipped: 0, errors: [] };
|
||||
@@ -160,19 +195,24 @@ async function runChunkedImport(file: File, map?: Record<string, string>) {
|
||||
|
||||
if (props.preparePath) {
|
||||
const prep = await postPrepare(file, map);
|
||||
acc = mergeImportResults(acc, prep);
|
||||
if (
|
||||
prep.errors?.some((e) => e.row === 1 && /بیش از .* ردیف|خواندن فایل/.test(e.message))
|
||||
) {
|
||||
return acc;
|
||||
}
|
||||
if (!prep.sessionId) {
|
||||
if (!prep.total) return acc;
|
||||
throw new Error(prep.errors[0]?.message || 'آمادهسازی فایل ناموفق بود');
|
||||
}
|
||||
sessionId = prep.sessionId;
|
||||
updateImportProgress(0, prep.total ?? 0);
|
||||
if (!prep.hasMore && (prep.total ?? 0) === 0) return acc;
|
||||
progressLabel.value = 'در حال خواندن فایل روی سرور…';
|
||||
const ready = await waitPrepareReady(sessionId);
|
||||
if (!ready.total) {
|
||||
return {
|
||||
...acc,
|
||||
total: 0,
|
||||
processed: 0,
|
||||
hasMore: false,
|
||||
errors: ready.error
|
||||
? [{ row: 1, message: ready.error }]
|
||||
: [{ row: 1, message: 'هیچ ردیف دادهای در فایل نیست' }],
|
||||
};
|
||||
}
|
||||
updateImportProgress(0, ready.total);
|
||||
}
|
||||
|
||||
while (guard < maxChunks) {
|
||||
|
||||
Reference in New Issue
Block a user