add pice check for file upload
This commit is contained in:
@@ -229,3 +229,48 @@ export async function getAllowedFilesExtensions(type: string) {
|
|||||||
return error
|
return error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function checkOperationPrice(action: string, filesCount: number, fileType: string) {
|
||||||
|
const token = await getSessionData('token');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/v1/data`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 30008,
|
||||||
|
message_body: {
|
||||||
|
auth_token: token,
|
||||||
|
action: action,
|
||||||
|
file_type: fileType,
|
||||||
|
count_files: filesCount
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
let parsed = await response.json();
|
||||||
|
console.log(parsed);
|
||||||
|
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
return parsed.message_body;
|
||||||
|
} else {
|
||||||
|
throw parsed;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw (`${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
cost: 0,
|
||||||
|
success: false,
|
||||||
|
tokens_count: 0,
|
||||||
|
error: 'error'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useRef, useState, useCallback, ChangeEvent, DragEvent, useEffect, useMemo } from 'react';
|
import { useRef, useState, useCallback, ChangeEvent, DragEvent, useEffect, useMemo } from 'react';
|
||||||
import { IconShieldAdd } from '@/app/ui/icons/icons';
|
import { IconShieldAdd } from '@/app/ui/icons/icons';
|
||||||
import { fileUpload, cancelUpload, chunkUpload, checkChunkStatus } from '@/app/actions/fileUpload';
|
import { fileUpload, cancelUpload, chunkUpload, checkChunkStatus, checkOperationPrice } from '@/app/actions/fileUpload';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { useNavigationBlocker } from '@/app/hooks/useNavigationBlocker';
|
import { useNavigationBlocker } from '@/app/hooks/useNavigationBlocker';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
@@ -113,8 +113,16 @@ export default function UploadSectionFile({
|
|||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
const newFiles: SelectedFile[] = [];
|
const newFiles: SelectedFile[] = [];
|
||||||
|
const selectedFileNames = new Set(selectedFiles.map(f => f.name));
|
||||||
|
const currentFileNames = new Set<string>();
|
||||||
|
const duplicateNames: string[] = [];
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
|
if (selectedFileNames.has(file.name)) {
|
||||||
|
duplicateNames.push(file.name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const validation = validateFile(file);
|
const validation = validateFile(file);
|
||||||
|
|
||||||
if (!validation.isValid) {
|
if (!validation.isValid) {
|
||||||
@@ -141,6 +149,8 @@ export default function UploadSectionFile({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentFileNames.add(file.name);
|
||||||
|
|
||||||
newFiles.push({
|
newFiles.push({
|
||||||
file,
|
file,
|
||||||
name: file.name,
|
name: file.name,
|
||||||
@@ -152,8 +162,13 @@ export default function UploadSectionFile({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (duplicateNames.length > 0) {
|
||||||
|
const uniqueDuplicates = [...new Set(duplicateNames)];
|
||||||
|
setError(`${t('duplicate-files')}: ${uniqueDuplicates.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
setSelectedFiles(prev => [...prev, ...newFiles]);
|
setSelectedFiles(prev => [...prev, ...newFiles]);
|
||||||
}, [fileType, t]);
|
}, [fileType, selectedFiles]);
|
||||||
|
|
||||||
const handleFileInputChange = (event: ChangeEvent<HTMLInputElement>): void => {
|
const handleFileInputChange = (event: ChangeEvent<HTMLInputElement>): void => {
|
||||||
const files = Array.from(event.target.files || []);
|
const files = Array.from(event.target.files || []);
|
||||||
@@ -204,6 +219,7 @@ export default function UploadSectionFile({
|
|||||||
f.uploadId !== fileId && f.name !== fileId
|
f.uploadId !== fileId && f.name !== fileId
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
|
console.log('clear all')
|
||||||
selectedFiles.forEach(file => {
|
selectedFiles.forEach(file => {
|
||||||
if (file.preview) {
|
if (file.preview) {
|
||||||
URL.revokeObjectURL(file.preview);
|
URL.revokeObjectURL(file.preview);
|
||||||
@@ -223,10 +239,21 @@ export default function UploadSectionFile({
|
|||||||
setError(null);
|
setError(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const uploadSingleFile = async (fileInfo: SelectedFile): Promise<void> => {
|
const uploadSingleFile = async (fileInfo: SelectedFile, uploadType: 'single' | 'multi'): Promise<void> => {
|
||||||
const fileId = `${fileInfo.name}-${Date.now()}`;
|
const fileId = `${fileInfo.name}-${Date.now()}`;
|
||||||
isCancelledRef.current.set(fileId, false);
|
isCancelledRef.current.set(fileId, false);
|
||||||
|
|
||||||
|
if (uploadType === 'single') {
|
||||||
|
setError(null);
|
||||||
|
const priceCheck = await checkOperationPrice('checkTokensForProtect', 1, fileType);
|
||||||
|
|
||||||
|
if (!priceCheck.success) {
|
||||||
|
const errorMessage = `${t('there-are-not-enough-funds-to-protect-file', { count: priceCheck?.tokens_count })}`;
|
||||||
|
setError(errorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setSelectedFiles(prev => prev.map(f =>
|
setSelectedFiles(prev => prev.map(f =>
|
||||||
f.name === fileInfo.name ? { ...f, status: 'uploading' } : f
|
f.name === fileInfo.name ? { ...f, status: 'uploading' } : f
|
||||||
@@ -315,13 +342,20 @@ export default function UploadSectionFile({
|
|||||||
}, [selectedFiles]);
|
}, [selectedFiles]);
|
||||||
|
|
||||||
const handleStartAllUploads = useCallback(async (): Promise<void> => {
|
const handleStartAllUploads = useCallback(async (): Promise<void> => {
|
||||||
|
setError(null);
|
||||||
|
if (selectedFilesRef.current.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const priceCheck = await checkOperationPrice('checkTokensForProtect', selectedFilesRef.current.length, fileType);
|
||||||
|
|
||||||
|
if (priceCheck.success) {
|
||||||
const uploadNextBatch = async () => {
|
const uploadNextBatch = async () => {
|
||||||
const currentPending = selectedFilesRef.current.filter(f => f.status === 'pending');
|
const currentPending = selectedFilesRef.current.filter(f => f.status === 'pending');
|
||||||
const batch = currentPending.slice(0, maxParallelUploads);
|
const batch = currentPending.slice(0, maxParallelUploads);
|
||||||
|
|
||||||
if (batch.length === 0) return;
|
if (batch.length === 0) return;
|
||||||
|
|
||||||
await Promise.all(batch.map(file => uploadSingleFile(file)));
|
await Promise.all(batch.map(file => uploadSingleFile(file, 'multi')));
|
||||||
|
|
||||||
if (selectedFilesRef.current.some(f => f.status === 'pending')) {
|
if (selectedFilesRef.current.some(f => f.status === 'pending')) {
|
||||||
await uploadNextBatch();
|
await uploadNextBatch();
|
||||||
@@ -329,6 +363,10 @@ export default function UploadSectionFile({
|
|||||||
};
|
};
|
||||||
|
|
||||||
await uploadNextBatch();
|
await uploadNextBatch();
|
||||||
|
} else {
|
||||||
|
const errorMessage = `${t('there-are-not-enough-funds-to-protect-files', { count: priceCheck?.tokens_count })}`;
|
||||||
|
setError(errorMessage);
|
||||||
|
}
|
||||||
}, [maxParallelUploads]);
|
}, [maxParallelUploads]);
|
||||||
|
|
||||||
const getTotalProgress = useMemo(() => {
|
const getTotalProgress = useMemo(() => {
|
||||||
@@ -399,7 +437,6 @@ export default function UploadSectionFile({
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="upload-section" >
|
<div className="upload-section" >
|
||||||
<h3>
|
<h3>
|
||||||
@@ -607,7 +644,7 @@ export default function UploadSectionFile({
|
|||||||
<div className="mt-2 text-center" >
|
<div className="mt-2 text-center" >
|
||||||
<button
|
<button
|
||||||
className="btn btn-confirm"
|
className="btn btn-confirm"
|
||||||
onClick={() => uploadSingleFile(file)
|
onClick={() => uploadSingleFile(file, 'single')
|
||||||
}
|
}
|
||||||
type="button"
|
type="button"
|
||||||
disabled={uploadingCount >= maxParallelUploads}
|
disabled={uploadingCount >= maxParallelUploads}
|
||||||
|
|||||||
@@ -293,7 +293,10 @@
|
|||||||
"security-details": "Security details",
|
"security-details": "Security details",
|
||||||
"number-of-checks": "Number of checks",
|
"number-of-checks": "Number of checks",
|
||||||
"format": "Format",
|
"format": "Format",
|
||||||
"file-name": "File name"
|
"file-name": "File name",
|
||||||
|
"there-are-not-enough-funds-to-protect-files": "There are not enough funds to protect files. {count} tokens required",
|
||||||
|
"there-are-not-enough-funds-to-protect-file": "There are not enough funds to protect file. {count} tokens required",
|
||||||
|
"duplicate-files": "Duplicate files"
|
||||||
},
|
},
|
||||||
"Login-register-form": {
|
"Login-register-form": {
|
||||||
"and": "and",
|
"and": "and",
|
||||||
|
|||||||
@@ -293,7 +293,10 @@
|
|||||||
"security-details": "Детали защиты",
|
"security-details": "Детали защиты",
|
||||||
"number-of-checks": "Количество проверок",
|
"number-of-checks": "Количество проверок",
|
||||||
"format": "Формат",
|
"format": "Формат",
|
||||||
"file-name": "Имя файла"
|
"file-name": "Имя файла",
|
||||||
|
"there-are-not-enough-funds-to-protect-files": "Для защиты файлов недостаточно средств. Необходимо {count} токен(ов/а)",
|
||||||
|
"there-are-not-enough-funds-to-protect-file": "Для защиты файла недостаточно средств. Необходимо {count} токен(ов/а)",
|
||||||
|
"duplicate-files": "Дубликаты файлов"
|
||||||
},
|
},
|
||||||
"Login-register-form": {
|
"Login-register-form": {
|
||||||
"and": "и",
|
"and": "и",
|
||||||
|
|||||||
Reference in New Issue
Block a user