ميزة: تحديث صفحات الخصوصية والشروط مع تاريخ آخر تحديث ثابت وفترة احتفاظ ديناميكية بالملفات
ميزة: إضافة خدمة تحليلات لتكامل Google Analytics اختبار: تحديث اختبارات خدمة واجهة برمجة التطبيقات (API) لتعكس تغييرات نقاط النهاية إصلاح: تعديل خدمة واجهة برمجة التطبيقات (API) لدعم تحميل ملفات متعددة ومصادقة المستخدم ميزة: تطبيق مخزن مصادقة باستخدام Zustand لإدارة المستخدمين إصلاح: تحسين إعدادات Nginx لتعزيز الأمان ودعم التحليلات
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useState, useCallback, useRef } from 'react';
|
||||
import { uploadFile, type TaskResponse } from '@/services/api';
|
||||
import { trackEvent } from '@/services/analytics';
|
||||
|
||||
interface UseFileUploadOptions {
|
||||
endpoint: string;
|
||||
@@ -38,26 +39,45 @@ export function useFileUpload({
|
||||
setError(null);
|
||||
setTaskId(null);
|
||||
setUploadProgress(0);
|
||||
const ext = selectedFile.name.split('.').pop()?.toLowerCase() || 'unknown';
|
||||
const sizeMb = Number((selectedFile.size / (1024 * 1024)).toFixed(2));
|
||||
|
||||
// Client-side size check
|
||||
const maxBytes = maxSizeMB * 1024 * 1024;
|
||||
if (selectedFile.size > maxBytes) {
|
||||
setError(`File too large. Maximum size is ${maxSizeMB}MB.`);
|
||||
trackEvent('upload_rejected_client', {
|
||||
endpoint,
|
||||
reason: 'size_limit',
|
||||
file_ext: ext,
|
||||
size_mb: sizeMb,
|
||||
max_size_mb: maxSizeMB,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Client-side type check
|
||||
if (acceptedTypes && acceptedTypes.length > 0) {
|
||||
const ext = selectedFile.name.split('.').pop()?.toLowerCase();
|
||||
if (!ext || !acceptedTypes.includes(ext)) {
|
||||
const selectedExt = selectedFile.name.split('.').pop()?.toLowerCase();
|
||||
if (!selectedExt || !acceptedTypes.includes(selectedExt)) {
|
||||
setError(`Invalid file type. Accepted: ${acceptedTypes.join(', ')}`);
|
||||
trackEvent('upload_rejected_client', {
|
||||
endpoint,
|
||||
reason: 'invalid_type',
|
||||
file_ext: ext,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setFile(selectedFile);
|
||||
trackEvent('file_selected', {
|
||||
endpoint,
|
||||
file_ext: ext,
|
||||
size_mb: sizeMb,
|
||||
});
|
||||
},
|
||||
[maxSizeMB, acceptedTypes]
|
||||
[maxSizeMB, acceptedTypes, endpoint]
|
||||
);
|
||||
|
||||
const startUpload = useCallback(async (): Promise<string | null> => {
|
||||
@@ -69,6 +89,7 @@ export function useFileUpload({
|
||||
setIsUploading(true);
|
||||
setError(null);
|
||||
setUploadProgress(0);
|
||||
trackEvent('upload_started', { endpoint });
|
||||
|
||||
try {
|
||||
const response: TaskResponse = await uploadFile(
|
||||
@@ -80,11 +101,13 @@ export function useFileUpload({
|
||||
|
||||
setTaskId(response.task_id);
|
||||
setIsUploading(false);
|
||||
trackEvent('upload_accepted', { endpoint });
|
||||
return response.task_id;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Upload failed.';
|
||||
setError(message);
|
||||
setIsUploading(false);
|
||||
trackEvent('upload_failed', { endpoint });
|
||||
return null;
|
||||
}
|
||||
}, [file, endpoint]);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { getTaskStatus, type TaskStatus, type TaskResult } from '@/services/api';
|
||||
import { trackEvent } from '@/services/analytics';
|
||||
|
||||
interface UseTaskPollingOptions {
|
||||
taskId: string | null;
|
||||
@@ -54,22 +55,26 @@ export function useTaskPolling({
|
||||
|
||||
if (taskResult?.status === 'completed') {
|
||||
setResult(taskResult);
|
||||
trackEvent('task_completed', { task_id: taskId });
|
||||
onComplete?.(taskResult);
|
||||
} else {
|
||||
const errMsg = taskResult?.error || 'Processing failed.';
|
||||
setError(errMsg);
|
||||
trackEvent('task_failed', { task_id: taskId, reason: 'result_failed' });
|
||||
onError?.(errMsg);
|
||||
}
|
||||
} else if (taskStatus.state === 'FAILURE') {
|
||||
stopPolling();
|
||||
const errMsg = taskStatus.error || 'Task failed.';
|
||||
setError(errMsg);
|
||||
trackEvent('task_failed', { task_id: taskId, reason: 'state_failure' });
|
||||
onError?.(errMsg);
|
||||
}
|
||||
} catch (err) {
|
||||
stopPolling();
|
||||
const errMsg = err instanceof Error ? err.message : 'Polling failed.';
|
||||
setError(errMsg);
|
||||
trackEvent('task_failed', { task_id: taskId, reason: 'polling_error' });
|
||||
onError?.(errMsg);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user