try fix upload for server
This commit is contained in:
@@ -1,10 +1,6 @@
|
|||||||
'use server'
|
'use server'
|
||||||
import { getSessionData } from '@/app/actions/session';
|
import { getSessionData } from '@/app/actions/session';
|
||||||
import { localDevelopmentUrl, testUserData } from '@/app/actions/definitions';
|
import { localDevelopmentUrl, testUserData, API_BASE_URL } from '@/app/actions/definitions';
|
||||||
|
|
||||||
const API_BASE_URL = process.env.NODE_ENV === 'development'
|
|
||||||
? localDevelopmentUrl
|
|
||||||
: 'http://app:8080';
|
|
||||||
|
|
||||||
const FRUITS_URL = 'https://www.fruityvice.com/api/fruit/';
|
const FRUITS_URL = 'https://www.fruityvice.com/api/fruit/';
|
||||||
const ALL = 'all';
|
const ALL = 'all';
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
'use server'
|
'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 { createSession, deleteSession, getSessionData } from '@/app/actions/session';
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
|
|
||||||
const API_BASE_URL = process.env.NODE_ENV === 'development'
|
|
||||||
? localDevelopmentUrl
|
|
||||||
: 'http://app:8080';
|
|
||||||
|
|
||||||
export async function logout() {
|
export async function logout() {
|
||||||
const token = await getSessionData('token');
|
const token = await getSessionData('token');
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ export const loginFormSchema = z
|
|||||||
|
|
||||||
export const localDevelopmentUrl = 'http://localhost';
|
export const localDevelopmentUrl = 'http://localhost';
|
||||||
|
|
||||||
|
export const API_BASE_URL = process.env.NODE_ENV === 'development'
|
||||||
|
? localDevelopmentUrl
|
||||||
|
: 'http://app:8080';
|
||||||
|
|
||||||
export const testUserData = {
|
export const testUserData = {
|
||||||
fullName: 'test',
|
fullName: 'test',
|
||||||
email: 'test@mail.com',
|
email: 'test@mail.com',
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
'use server'
|
'use server'
|
||||||
|
|
||||||
import { getSessionData } from '@/app/actions/session';
|
import { getSessionData } from '@/app/actions/session';
|
||||||
import { localDevelopmentUrl } from '@/app/actions/definitions';
|
import { API_BASE_URL } from '@/app/actions/definitions';
|
||||||
const API_BASE_URL = process.env.NODE_ENV === 'development'
|
|
||||||
? localDevelopmentUrl
|
|
||||||
: 'http://app:8080';
|
|
||||||
|
|
||||||
|
|
||||||
export async function getUserFilesData() {
|
export async function getUserFilesData() {
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
'use server'
|
'use server'
|
||||||
|
|
||||||
import { localDevelopmentUrl } from '@/app/actions/definitions';
|
import { API_BASE_URL } from '@/app/actions/definitions';
|
||||||
import { getSessionData } from '@/app/actions/session';
|
import { getSessionData } from '@/app/actions/session';
|
||||||
|
|
||||||
const API_BASE_URL = process.env.NODE_ENV === 'development'
|
|
||||||
? localDevelopmentUrl
|
|
||||||
: 'http://app:8080';
|
|
||||||
|
|
||||||
interface initMessageBody {
|
interface initMessageBody {
|
||||||
file_name: string,
|
file_name: string,
|
||||||
file_type: string,
|
file_type: string,
|
||||||
|
|||||||
@@ -105,11 +105,6 @@ export default function TanstakFilesTable() {
|
|||||||
}
|
}
|
||||||
}, [userFilesData]);
|
}, [userFilesData]);
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
console.log('typeof window !== undefined');
|
|
||||||
console.log(userFilesData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Состояния
|
// Состояния
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'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 { IconShieldAdd } from '@/app/ui/icons/icons';
|
||||||
import { fileUpload, cancelUpload, chunkUpload, checkChunkStatus } from '@/app/actions/fileUpload';
|
import { fileUpload, cancelUpload, chunkUpload, checkChunkStatus } from '@/app/actions/fileUpload';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
@@ -38,6 +38,7 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
|
|||||||
const [uploadId, setUploadId] = useState<string | null>(null);
|
const [uploadId, setUploadId] = useState<string | null>(null);
|
||||||
const [isFileUploaded, setIsFileUploaded] = useState<boolean>(false);
|
const [isFileUploaded, setIsFileUploaded] = useState<boolean>(false);
|
||||||
const [uploadProgress, setUploadProgress] = useState<number>(0);
|
const [uploadProgress, setUploadProgress] = useState<number>(0);
|
||||||
|
const [isUploading, setIsUploading] = useState(false);
|
||||||
|
|
||||||
const t = useTranslations('Global');
|
const t = useTranslations('Global');
|
||||||
let SUPPORTED_FORMATS: string[] = [];
|
let SUPPORTED_FORMATS: string[] = [];
|
||||||
@@ -92,12 +93,17 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
|
|||||||
break;
|
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 } => {
|
const validateFile = (file: File): { isValid: boolean; errorMessage?: string } => {
|
||||||
if (!SUPPORTED_FORMATS.includes(file.type as string)) {
|
if (!SUPPORTED_FORMATS.includes(file.type as string)) {
|
||||||
const extension = file.name.split('.').pop()?.toLowerCase();
|
const extension = file.name.split('.').pop()?.toLowerCase();
|
||||||
if (!extension || !ALLOWED_EXTENSIONS.includes(extension as string)) {
|
if (!extension || !allowedExtensions.includes(extension as string)) {
|
||||||
return {
|
return {
|
||||||
isValid: false,
|
isValid: false,
|
||||||
errorMessage: t('unsupported-file-format')
|
errorMessage: t('unsupported-file-format')
|
||||||
@@ -239,7 +245,15 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
|
|||||||
setUploadProgress(0);
|
setUploadProgress(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlerFileUpload = async (file: SelectedFile): Promise<void> => {
|
const handlerFileUpload = useCallback(async (file: SelectedFile): Promise<void> => {
|
||||||
|
// Защита от повторного вызова
|
||||||
|
if (isUploading) return;
|
||||||
|
|
||||||
|
// Сбрасываем состояния
|
||||||
|
setError(null);
|
||||||
|
setIsUploading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
const extension = file.name.split('.').pop();
|
const extension = file.name.split('.').pop();
|
||||||
|
|
||||||
if (!extension) {
|
if (!extension) {
|
||||||
@@ -264,8 +278,11 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
|
|||||||
let start = 0;
|
let start = 0;
|
||||||
let chunkIndex = 0;
|
let chunkIndex = 0;
|
||||||
|
|
||||||
try {
|
console.log('start upload');
|
||||||
|
console.log(`total chanks - ${totalChunks}`);
|
||||||
|
|
||||||
while (start < fileSize) {
|
while (start < fileSize) {
|
||||||
|
console.log(`chank-${chunkIndex}`);
|
||||||
const end = Math.min(start + CHUNK_SIZE, fileSize);
|
const end = Math.min(start + CHUNK_SIZE, fileSize);
|
||||||
const chunk = file.file.slice(start, end);
|
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('upload_id', response.upload_id);
|
||||||
formData.append('chunk_number', chunkIndex.toString());
|
formData.append('chunk_number', chunkIndex.toString());
|
||||||
formData.append('chunk', chunk);
|
formData.append('chunk', chunk);
|
||||||
|
|
||||||
const chunkResponse = await chunkUpload(formData);
|
const chunkResponse = await chunkUpload(formData);
|
||||||
|
|
||||||
if (chunkResponse.message_desc !== 'Chunk uploaded successfully') {
|
if (chunkResponse.message_desc !== 'Chunk uploaded successfully') {
|
||||||
throw 'Er1: response error'
|
throw new Error('Er1: response error');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chunkIndex === totalChunks - 1) {
|
if (chunkIndex === totalChunks - 1) {
|
||||||
@@ -285,27 +303,32 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
|
|||||||
setUploadId(null);
|
setUploadId(null);
|
||||||
setIsFileUploaded(true);
|
setIsFileUploaded(true);
|
||||||
} else {
|
} else {
|
||||||
throw 'Er2: not all chunks was uploaded'
|
throw new Error('Er2: not all chunks was uploaded');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
start = end;
|
start = end;
|
||||||
chunkIndex++;
|
chunkIndex++;
|
||||||
setUploadProgress(
|
setUploadProgress(Math.floor(chunkIndex / totalChunks * 100));
|
||||||
() => {
|
|
||||||
return Math.floor(chunkIndex / totalChunks * 100);
|
|
||||||
}
|
}
|
||||||
);
|
} else {
|
||||||
|
console.error(response);
|
||||||
|
throw new Error('Failed to get upload_id');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError(t('error-uploading-file'));
|
setError(t('error-uploading-file'));
|
||||||
console.error('Chunk upload error:', error);
|
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);
|
||||||
}
|
}
|
||||||
} else {
|
} finally {
|
||||||
console.error(response);
|
setIsUploading(false);
|
||||||
setError(t('error-uploading-file'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}, [isUploading, fileType]); // Добавьте зависимости
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Обработка закрытия вкладки
|
// Обработка закрытия вкладки
|
||||||
@@ -373,9 +396,7 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
|
|||||||
<input
|
<input
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
type="file"
|
type="file"
|
||||||
accept={
|
accept={acceptString}
|
||||||
ALLOWED_EXTENSIONS.map(e => `.${e}`).join(',')
|
|
||||||
}
|
|
||||||
style={{ display: 'none' }}
|
style={{ display: 'none' }}
|
||||||
onChange={handleFileInputChange}
|
onChange={handleFileInputChange}
|
||||||
aria-label="Выбор файла для защиты"
|
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-size')}: {t('to')} {(maxFileSize / 1024 / 1024)} {t('mb')}
|
||||||
</p>
|
</p>
|
||||||
<p className="description">
|
<p className="description">
|
||||||
{t('file-format')}: {ALLOWED_EXTENSIONS.join(', ')}
|
{t('file-format')}: {acceptString}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
@@ -462,7 +483,7 @@ export default function UploadSectionFile({ fileType, allowedExtensions, maxFile
|
|||||||
handlerFileUpload(selectedFile);
|
handlerFileUpload(selectedFile);
|
||||||
}}
|
}}
|
||||||
type="button"
|
type="button"
|
||||||
disabled={error || isFileUploaded ? true : false}
|
disabled={error || isFileUploaded || isUploading ? true : false}
|
||||||
>
|
>
|
||||||
{t('upload-file')}
|
{t('upload-file')}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export default function NotificationsButton() {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div className="notification-footer">
|
<div className="notification-footer">
|
||||||
<Link href="/pages/notifications" className="notification-link">
|
<Link href="#" className="notification-link">
|
||||||
{t('view-all')}
|
{t('view-all')}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export default function UserMenuButton() {
|
|||||||
</svg>
|
</svg>
|
||||||
{t('settings')}
|
{t('settings')}
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/pages/faq" className="user-menu-item">
|
<Link href="#" className="user-menu-item">
|
||||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"></path>
|
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
Reference in New Issue
Block a user