add pice check for file upload

This commit is contained in:
smanylov
2026-03-04 19:21:44 +07:00
parent 0351c561b5
commit 640f167e4b
4 changed files with 106 additions and 18 deletions
+46 -1
View File
@@ -12,7 +12,7 @@ interface initMessageBody {
file_size: number | string,
action?: string,
token?: string,
convertTo? : string | undefined | null
convertTo?: string | undefined | null
}
export async function fileUpload(messageBody: initMessageBody, convertTo?: string | null | undefined) {
@@ -229,3 +229,48 @@ export async function getAllowedFilesExtensions(type: string) {
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'
}
}
}
+43 -6
View File
@@ -2,7 +2,7 @@
import { useRef, useState, useCallback, ChangeEvent, DragEvent, useEffect, useMemo } from 'react';
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 { useNavigationBlocker } from '@/app/hooks/useNavigationBlocker';
import { useQueryClient } from '@tanstack/react-query';
@@ -113,8 +113,16 @@ export default function UploadSectionFile({
setError(null);
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) {
if (selectedFileNames.has(file.name)) {
duplicateNames.push(file.name);
continue;
}
const validation = validateFile(file);
if (!validation.isValid) {
@@ -141,6 +149,8 @@ export default function UploadSectionFile({
}
}
currentFileNames.add(file.name);
newFiles.push({
file,
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]);
}, [fileType, t]);
}, [fileType, selectedFiles]);
const handleFileInputChange = (event: ChangeEvent<HTMLInputElement>): void => {
const files = Array.from(event.target.files || []);
@@ -204,6 +219,7 @@ export default function UploadSectionFile({
f.uploadId !== fileId && f.name !== fileId
));
} else {
console.log('clear all')
selectedFiles.forEach(file => {
if (file.preview) {
URL.revokeObjectURL(file.preview);
@@ -223,10 +239,21 @@ export default function UploadSectionFile({
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()}`;
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 {
setSelectedFiles(prev => prev.map(f =>
f.name === fileInfo.name ? { ...f, status: 'uploading' } : f
@@ -315,13 +342,20 @@ export default function UploadSectionFile({
}, [selectedFiles]);
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 currentPending = selectedFilesRef.current.filter(f => f.status === 'pending');
const batch = currentPending.slice(0, maxParallelUploads);
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')) {
await uploadNextBatch();
@@ -329,6 +363,10 @@ export default function UploadSectionFile({
};
await uploadNextBatch();
} else {
const errorMessage = `${t('there-are-not-enough-funds-to-protect-files', { count: priceCheck?.tokens_count })}`;
setError(errorMessage);
}
}, [maxParallelUploads]);
const getTotalProgress = useMemo(() => {
@@ -399,7 +437,6 @@ export default function UploadSectionFile({
}));
}
return (
<div className="upload-section" >
<h3>
@@ -607,7 +644,7 @@ export default function UploadSectionFile({
<div className="mt-2 text-center" >
<button
className="btn btn-confirm"
onClick={() => uploadSingleFile(file)
onClick={() => uploadSingleFile(file, 'single')
}
type="button"
disabled={uploadingCount >= maxParallelUploads}
+4 -1
View File
@@ -293,7 +293,10 @@
"security-details": "Security details",
"number-of-checks": "Number of checks",
"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": {
"and": "and",
+4 -1
View File
@@ -293,7 +293,10 @@
"security-details": "Детали защиты",
"number-of-checks": "Количество проверок",
"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": {
"and": "и",