diff --git a/src/app/actions/action.ts b/src/app/actions/action.ts
index cbeafc2..14694f2 100644
--- a/src/app/actions/action.ts
+++ b/src/app/actions/action.ts
@@ -1,10 +1,6 @@
'use server'
import { getSessionData } from '@/app/actions/session';
-import { localDevelopmentUrl, testUserData } from '@/app/actions/definitions';
-
-const API_BASE_URL = process.env.NODE_ENV === 'development'
- ? localDevelopmentUrl
- : 'http://app:8080';
+import { localDevelopmentUrl, testUserData, API_BASE_URL } from '@/app/actions/definitions';
const FRUITS_URL = 'https://www.fruityvice.com/api/fruit/';
const ALL = 'all';
diff --git a/src/app/actions/auth.ts b/src/app/actions/auth.ts
index 8088167..5a2bd43 100644
--- a/src/app/actions/auth.ts
+++ b/src/app/actions/auth.ts
@@ -1,13 +1,9 @@
'use server'
-import { SignupFormSchema, loginFormSchema, localDevelopmentUrl } from '@/app/actions/definitions';
+import { SignupFormSchema, loginFormSchema, API_BASE_URL } from '@/app/actions/definitions';
import { createSession, deleteSession, getSessionData } from '@/app/actions/session';
import { redirect } from 'next/navigation';
-const API_BASE_URL = process.env.NODE_ENV === 'development'
- ? localDevelopmentUrl
- : 'http://app:8080';
-
export async function logout() {
const token = await getSessionData('token');
diff --git a/src/app/actions/definitions.ts b/src/app/actions/definitions.ts
index 454a4e7..fb719c8 100644
--- a/src/app/actions/definitions.ts
+++ b/src/app/actions/definitions.ts
@@ -78,6 +78,10 @@ export const loginFormSchema = z
export const localDevelopmentUrl = 'http://localhost';
+export const API_BASE_URL = process.env.NODE_ENV === 'development'
+ ? localDevelopmentUrl
+ : 'http://app:8080';
+
export const testUserData = {
fullName: 'test',
email: 'test@mail.com',
diff --git a/src/app/actions/fileEntity.ts b/src/app/actions/fileEntity.ts
index ae8b6b0..92ea466 100644
--- a/src/app/actions/fileEntity.ts
+++ b/src/app/actions/fileEntity.ts
@@ -1,10 +1,7 @@
'use server'
import { getSessionData } from '@/app/actions/session';
-import { localDevelopmentUrl } from '@/app/actions/definitions';
-const API_BASE_URL = process.env.NODE_ENV === 'development'
- ? localDevelopmentUrl
- : 'http://app:8080';
+import { API_BASE_URL } from '@/app/actions/definitions';
export async function getUserFilesData() {
diff --git a/src/app/actions/fileUpload.ts b/src/app/actions/fileUpload.ts
index 43d7124..cc304d0 100644
--- a/src/app/actions/fileUpload.ts
+++ b/src/app/actions/fileUpload.ts
@@ -1,12 +1,8 @@
'use server'
-import { localDevelopmentUrl } from '@/app/actions/definitions';
+import { API_BASE_URL } from '@/app/actions/definitions';
import { getSessionData } from '@/app/actions/session';
-const API_BASE_URL = process.env.NODE_ENV === 'development'
- ? localDevelopmentUrl
- : 'http://app:8080';
-
interface initMessageBody {
file_name: string,
file_type: string,
diff --git a/src/app/components/tanstakTable.tsx b/src/app/components/tanstakTable.tsx
index 07584fc..e483206 100644
--- a/src/app/components/tanstakTable.tsx
+++ b/src/app/components/tanstakTable.tsx
@@ -105,11 +105,6 @@ export default function TanstakFilesTable() {
}
}, [userFilesData]);
- if (typeof window !== 'undefined') {
- console.log('typeof window !== undefined');
- console.log(userFilesData)
- }
-
// Состояния
const [sorting, setSorting] = useState([]);
const [columnFilters, setColumnFilters] = useState([]);
diff --git a/src/app/components/upload-section-file.tsx b/src/app/components/upload-section-file.tsx
index 36c4ea2..b79764d 100644
--- a/src/app/components/upload-section-file.tsx
+++ b/src/app/components/upload-section-file.tsx
@@ -1,6 +1,6 @@
'use client'
-import { useRef, useState, useCallback, ChangeEvent, DragEvent, useEffect } from 'react';
+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 { useTranslations } from 'next-intl';
@@ -38,6 +38,7 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
const [uploadId, setUploadId] = useState(null);
const [isFileUploaded, setIsFileUploaded] = useState(false);
const [uploadProgress, setUploadProgress] = useState(0);
+ const [isUploading, setIsUploading] = useState(false);
const t = useTranslations('Global');
let SUPPORTED_FORMATS: string[] = [];
@@ -92,12 +93,17 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
break;
}
- const ALLOWED_EXTENSIONS: string[] = allowedExtensions;
+ const acceptString = useMemo(() => {
+ if (!allowedExtensions || !Array.isArray(allowedExtensions)) {
+ return '';
+ }
+ return allowedExtensions.map(e => `.${e}`).join(', ');
+ }, [allowedExtensions]);
const validateFile = (file: File): { isValid: boolean; errorMessage?: string } => {
if (!SUPPORTED_FORMATS.includes(file.type as string)) {
const extension = file.name.split('.').pop()?.toLowerCase();
- if (!extension || !ALLOWED_EXTENSIONS.includes(extension as string)) {
+ if (!extension || !allowedExtensions.includes(extension as string)) {
return {
isValid: false,
errorMessage: t('unsupported-file-format')
@@ -239,33 +245,44 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
setUploadProgress(0);
};
- const handlerFileUpload = async (file: SelectedFile): Promise => {
- const extension = file.name.split('.').pop();
+ const handlerFileUpload = useCallback(async (file: SelectedFile): Promise => {
+ // Защита от повторного вызова
+ if (isUploading) return;
- if (!extension) {
- setError(t('file-has-no-extension'));
- return;
- }
+ // Сбрасываем состояния
+ setError(null);
+ setIsUploading(true);
- const initMessageBody = {
- file_name: file.name,
- file_type: fileType,
- extension: extension,
- file_size: file.file.size
- };
+ try {
+ const extension = file.name.split('.').pop();
- const response = await fileUpload(initMessageBody) as FileUploadInitResponse;
+ if (!extension) {
+ setError(t('file-has-no-extension'));
+ return;
+ }
- if (response?.upload_id) {
- setUploadId(response.upload_id);
- const CHUNK_SIZE = response.chunk_size;
- const totalChunks = response.total_chunks;
- const fileSize = file.file.size;
- let start = 0;
- let chunkIndex = 0;
+ const initMessageBody = {
+ file_name: file.name,
+ file_type: fileType,
+ extension: extension,
+ file_size: file.file.size
+ };
+
+ const response = await fileUpload(initMessageBody) as FileUploadInitResponse;
+
+ if (response?.upload_id) {
+ setUploadId(response.upload_id);
+ const CHUNK_SIZE = response.chunk_size;
+ const totalChunks = response.total_chunks;
+ const fileSize = file.file.size;
+ let start = 0;
+ let chunkIndex = 0;
+
+ console.log('start upload');
+ console.log(`total chanks - ${totalChunks}`);
- try {
while (start < fileSize) {
+ console.log(`chank-${chunkIndex}`);
const end = Math.min(start + CHUNK_SIZE, fileSize);
const chunk = file.file.slice(start, end);
@@ -273,10 +290,11 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
formData.append('upload_id', response.upload_id);
formData.append('chunk_number', chunkIndex.toString());
formData.append('chunk', chunk);
+
const chunkResponse = await chunkUpload(formData);
if (chunkResponse.message_desc !== 'Chunk uploaded successfully') {
- throw 'Er1: response error'
+ throw new Error('Er1: response error');
}
if (chunkIndex === totalChunks - 1) {
@@ -285,27 +303,32 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
setUploadId(null);
setIsFileUploaded(true);
} else {
- throw 'Er2: not all chunks was uploaded'
+ throw new Error('Er2: not all chunks was uploaded');
}
}
start = end;
chunkIndex++;
- setUploadProgress(
- () => {
- return Math.floor(chunkIndex / totalChunks * 100);
- }
- );
+ setUploadProgress(Math.floor(chunkIndex / totalChunks * 100));
}
- } catch (error) {
- setError(t('error-uploading-file'));
- console.error('Chunk upload error:', error);
+ } else {
+ console.error(response);
+ throw new Error('Failed to get upload_id');
}
- } else {
- console.error(response);
+ } catch (error) {
setError(t('error-uploading-file'));
+ console.error('Chunk upload error:', error);
+
+ // Для дебага в production
+ if (error instanceof Error) {
+ console.error('Error name:', error.name);
+ console.error('Error message:', error.message);
+ if (error.cause) console.error('Error cause:', error.cause);
+ }
+ } finally {
+ setIsUploading(false);
}
- }
+ }, [isUploading, fileType]); // Добавьте зависимости
useEffect(() => {
// Обработка закрытия вкладки
@@ -373,9 +396,7 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
`.${e}`).join(',')
- }
+ accept={acceptString}
style={{ display: 'none' }}
onChange={handleFileInputChange}
aria-label="Выбор файла для защиты"
@@ -398,7 +419,7 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
{t('file-size')}: {t('to')} {(maxFileSize / 1024 / 1024)} {t('mb')}
- {t('file-format')}: {ALLOWED_EXTENSIONS.join(', ')}
+ {t('file-format')}: {acceptString}
{error && (
@@ -462,7 +483,7 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
handlerFileUpload(selectedFile);
}}
type="button"
- disabled={error || isFileUploaded ? true : false}
+ disabled={error || isFileUploaded || isUploading ? true : false}
>
{t('upload-file')}
diff --git a/src/app/ui/header/notificationsButton.tsx b/src/app/ui/header/notificationsButton.tsx
index 4ae1292..3e883e5 100644
--- a/src/app/ui/header/notificationsButton.tsx
+++ b/src/app/ui/header/notificationsButton.tsx
@@ -89,7 +89,7 @@ export default function NotificationsButton() {
)}
-
+
{t('view-all')}
diff --git a/src/app/ui/header/userMenuButton.tsx b/src/app/ui/header/userMenuButton.tsx
index 077748d..dfd2d84 100644
--- a/src/app/ui/header/userMenuButton.tsx
+++ b/src/app/ui/header/userMenuButton.tsx
@@ -80,7 +80,7 @@ export default function UserMenuButton() {
{t('settings')}
-
+