mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-08-11 13:48:37 +02:00
Refactor into multiple functions
This commit is contained in:
parent
df9d99b8cb
commit
addaf6f751
@ -53,6 +53,103 @@ export const useSanitizeOperation = () => {
|
|||||||
return { operation, operationId, fileId };
|
return { operation, operationId, fileId };
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const buildFormData = useCallback((parameters: SanitizeParameters, file: File): FormData => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('fileInput', file);
|
||||||
|
|
||||||
|
// Add parameters
|
||||||
|
formData.append('removeJavaScript', parameters.removeJavaScript.toString());
|
||||||
|
formData.append('removeEmbeddedFiles', parameters.removeEmbeddedFiles.toString());
|
||||||
|
formData.append('removeXMPMetadata', parameters.removeXMPMetadata.toString());
|
||||||
|
formData.append('removeMetadata', parameters.removeMetadata.toString());
|
||||||
|
formData.append('removeLinks', parameters.removeLinks.toString());
|
||||||
|
formData.append('removeFonts', parameters.removeFonts.toString());
|
||||||
|
|
||||||
|
return formData;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const sanitizeFile = useCallback(async (
|
||||||
|
file: File,
|
||||||
|
parameters: SanitizeParameters,
|
||||||
|
generateSanitizedFileName: (originalFileName?: string) => string,
|
||||||
|
operationId: string,
|
||||||
|
fileId: string
|
||||||
|
): Promise<File | null> => {
|
||||||
|
try {
|
||||||
|
const formData = buildFormData(parameters, file);
|
||||||
|
|
||||||
|
const response = await fetch('/api/v1/security/sanitize-pdf', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
markOperationFailed(fileId, operationId, errorText);
|
||||||
|
console.error(`Error sanitizing file ${file.name}:`, errorText);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blob = await response.blob();
|
||||||
|
const sanitizedFileName = generateSanitizedFileName(file.name);
|
||||||
|
const sanitizedFile = new File([blob], sanitizedFileName, { type: blob.type });
|
||||||
|
|
||||||
|
markOperationApplied(fileId, operationId);
|
||||||
|
return sanitizedFile;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error sanitizing file ${file.name}:`, error);
|
||||||
|
markOperationFailed(fileId, operationId, error instanceof Error ? error.message : 'Unknown error');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, [buildFormData, markOperationApplied, markOperationFailed]);
|
||||||
|
|
||||||
|
const createDownloadInfo = useCallback(async (results: File[]): Promise<void> => {
|
||||||
|
if (results.length === 1) {
|
||||||
|
const url = window.URL.createObjectURL(results[0]);
|
||||||
|
setDownloadUrl(url);
|
||||||
|
} else {
|
||||||
|
const { zipFile } = await zipFileService.createZipFromFiles(results, 'sanitized_files.zip');
|
||||||
|
const url = window.URL.createObjectURL(zipFile);
|
||||||
|
setDownloadUrl(url);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const generateThumbnailsForResults = useCallback(async (results: File[]): Promise<void> => {
|
||||||
|
const thumbnails = await Promise.all(
|
||||||
|
results.map(async (file) => {
|
||||||
|
try {
|
||||||
|
const thumbnail = await generateThumbnailForFile(file);
|
||||||
|
return thumbnail || '';
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Failed to generate thumbnail for ${file.name}:`, error);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
setThumbnails(thumbnails);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const processResults = useCallback(async (results: File[]): Promise<void> => {
|
||||||
|
setFiles(results);
|
||||||
|
setIsGeneratingThumbnails(true);
|
||||||
|
|
||||||
|
// Add sanitized files to FileContext for future use
|
||||||
|
await addFiles(results);
|
||||||
|
|
||||||
|
// Create download info - single file or ZIP
|
||||||
|
await createDownloadInfo(results);
|
||||||
|
|
||||||
|
// Generate thumbnails
|
||||||
|
await generateThumbnailsForResults(results);
|
||||||
|
|
||||||
|
setIsGeneratingThumbnails(false);
|
||||||
|
setStatus(results.length === 1
|
||||||
|
? t('sanitize.completed', 'Sanitization completed successfully')
|
||||||
|
: t('sanitize.completedMultiple', 'Sanitized {{count}} files successfully', { count: results.length })
|
||||||
|
);
|
||||||
|
}, [addFiles, createDownloadInfo, generateThumbnailsForResults, t]);
|
||||||
|
|
||||||
const executeOperation = useCallback(async (
|
const executeOperation = useCallback(async (
|
||||||
parameters: SanitizeParameters,
|
parameters: SanitizeParameters,
|
||||||
selectedFiles: File[],
|
selectedFiles: File[],
|
||||||
@ -64,8 +161,8 @@ export const useSanitizeOperation = () => {
|
|||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setErrorMessage(null);
|
setErrorMessage(null);
|
||||||
setStatus(selectedFiles.length === 1
|
setStatus(selectedFiles.length === 1
|
||||||
? t('sanitize.processing', 'Sanitizing PDF...')
|
? t('sanitize.processing', 'Sanitizing PDF...')
|
||||||
: t('sanitize.processingMultiple', 'Sanitizing {{count}} PDFs...', { count: selectedFiles.length })
|
: t('sanitize.processingMultiple', 'Sanitizing {{count}} PDFs...', { count: selectedFiles.length })
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -79,49 +176,20 @@ export const useSanitizeOperation = () => {
|
|||||||
const { operation, operationId, fileId } = createOperation(parameters, [file]);
|
const { operation, operationId, fileId } = createOperation(parameters, [file]);
|
||||||
recordOperation(fileId, operation);
|
recordOperation(fileId, operation);
|
||||||
|
|
||||||
setStatus(selectedFiles.length === 1
|
setStatus(selectedFiles.length === 1
|
||||||
? t('sanitize.processing', 'Sanitizing PDF...')
|
? t('sanitize.processing', 'Sanitizing PDF...')
|
||||||
: t('sanitize.processingFile', 'Processing file {{current}} of {{total}}: {{filename}}', {
|
: t('sanitize.processingFile', 'Processing file {{current}} of {{total}}: {{filename}}', {
|
||||||
current: i + 1,
|
current: i + 1,
|
||||||
total: selectedFiles.length,
|
total: selectedFiles.length,
|
||||||
filename: file.name
|
filename: file.name
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
const sanitizedFile = await sanitizeFile(file, parameters, generateSanitizedFileName, operationId, fileId);
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('fileInput', file);
|
if (sanitizedFile) {
|
||||||
|
|
||||||
// Add parameters
|
|
||||||
formData.append('removeJavaScript', parameters.removeJavaScript.toString());
|
|
||||||
formData.append('removeEmbeddedFiles', parameters.removeEmbeddedFiles.toString());
|
|
||||||
formData.append('removeXMPMetadata', parameters.removeXMPMetadata.toString());
|
|
||||||
formData.append('removeMetadata', parameters.removeMetadata.toString());
|
|
||||||
formData.append('removeLinks', parameters.removeLinks.toString());
|
|
||||||
formData.append('removeFonts', parameters.removeFonts.toString());
|
|
||||||
|
|
||||||
const response = await fetch('/api/v1/security/sanitize-pdf', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorText = await response.text();
|
|
||||||
markOperationFailed(fileId, operationId, errorText);
|
|
||||||
console.error(`Error sanitizing file ${file.name}:`, errorText);
|
|
||||||
failedFiles.push(file.name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const blob = await response.blob();
|
|
||||||
const sanitizedFileName = generateSanitizedFileName(file.name);
|
|
||||||
const sanitizedFile = new File([blob], sanitizedFileName, { type: blob.type });
|
|
||||||
|
|
||||||
results.push(sanitizedFile);
|
results.push(sanitizedFile);
|
||||||
markOperationApplied(fileId, operationId);
|
} else {
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error sanitizing file ${file.name}:`, error);
|
|
||||||
markOperationFailed(fileId, operationId, error instanceof Error ? error.message : 'Unknown error');
|
|
||||||
failedFiles.push(file.name);
|
failedFiles.push(file.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,41 +203,7 @@ export const useSanitizeOperation = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (results.length > 0) {
|
if (results.length > 0) {
|
||||||
setFiles(results);
|
await processResults(results);
|
||||||
setIsGeneratingThumbnails(true);
|
|
||||||
|
|
||||||
// Add sanitized files to FileContext for future use
|
|
||||||
await addFiles(results);
|
|
||||||
|
|
||||||
// Create download info - single file or ZIP
|
|
||||||
if (results.length === 1) {
|
|
||||||
const url = window.URL.createObjectURL(results[0]);
|
|
||||||
setDownloadUrl(url);
|
|
||||||
} else {
|
|
||||||
const { zipFile } = await zipFileService.createZipFromFiles(results, 'sanitized_files.zip');
|
|
||||||
const url = window.URL.createObjectURL(zipFile);
|
|
||||||
setDownloadUrl(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate thumbnails
|
|
||||||
const thumbnails = await Promise.all(
|
|
||||||
results.map(async (file) => {
|
|
||||||
try {
|
|
||||||
const thumbnail = await generateThumbnailForFile(file);
|
|
||||||
return thumbnail || '';
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(`Failed to generate thumbnail for ${file.name}:`, error);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
setThumbnails(thumbnails);
|
|
||||||
setIsGeneratingThumbnails(false);
|
|
||||||
setStatus(results.length === 1
|
|
||||||
? t('sanitize.completed', 'Sanitization completed successfully')
|
|
||||||
: t('sanitize.completedMultiple', 'Sanitized {{count}} files successfully', { count: results.length })
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
setErrorMessage(t('sanitize.errorAllFilesFailed', 'All files failed to sanitize'));
|
setErrorMessage(t('sanitize.errorAllFilesFailed', 'All files failed to sanitize'));
|
||||||
}
|
}
|
||||||
@ -182,7 +216,7 @@ export const useSanitizeOperation = () => {
|
|||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [t, createOperation, recordOperation, markOperationApplied, markOperationFailed, addFiles]);
|
}, [t, createOperation, recordOperation, sanitizeFile, processResults]);
|
||||||
|
|
||||||
const resetResults = useCallback(() => {
|
const resetResults = useCallback(() => {
|
||||||
if (downloadUrl) {
|
if (downloadUrl) {
|
||||||
|
@ -7,7 +7,7 @@ import { PDFDocument, PDFPage, PageOperation } from './pageEditor';
|
|||||||
|
|
||||||
export type ModeType = 'viewer' | 'pageEditor' | 'fileEditor' | 'merge' | 'split' | 'compress' | 'ocr';
|
export type ModeType = 'viewer' | 'pageEditor' | 'fileEditor' | 'merge' | 'split' | 'compress' | 'ocr';
|
||||||
|
|
||||||
export type OperationType = 'merge' | 'split' | 'compress' | 'add' | 'remove' | 'replace' | 'convert' | 'upload' | 'ocr';
|
export type OperationType = 'merge' | 'split' | 'compress' | 'add' | 'remove' | 'replace' | 'convert' | 'upload' | 'ocr' | 'sanitize';
|
||||||
|
|
||||||
export interface FileOperation {
|
export interface FileOperation {
|
||||||
id: string;
|
id: string;
|
||||||
@ -51,25 +51,25 @@ export interface FileContextState {
|
|||||||
// Core file management
|
// Core file management
|
||||||
activeFiles: File[];
|
activeFiles: File[];
|
||||||
processedFiles: Map<File, ProcessedFile>;
|
processedFiles: Map<File, ProcessedFile>;
|
||||||
|
|
||||||
// Current navigation state
|
// Current navigation state
|
||||||
currentMode: ModeType;
|
currentMode: ModeType;
|
||||||
|
|
||||||
// Edit history and state
|
// Edit history and state
|
||||||
fileEditHistory: Map<string, FileEditHistory>;
|
fileEditHistory: Map<string, FileEditHistory>;
|
||||||
globalFileOperations: FileOperation[];
|
globalFileOperations: FileOperation[];
|
||||||
// New comprehensive operation history
|
// New comprehensive operation history
|
||||||
fileOperationHistory: Map<string, FileOperationHistory>;
|
fileOperationHistory: Map<string, FileOperationHistory>;
|
||||||
|
|
||||||
// UI state that persists across views
|
// UI state that persists across views
|
||||||
selectedFileIds: string[];
|
selectedFileIds: string[];
|
||||||
selectedPageNumbers: number[];
|
selectedPageNumbers: number[];
|
||||||
viewerConfig: ViewerConfig;
|
viewerConfig: ViewerConfig;
|
||||||
|
|
||||||
// Processing state
|
// Processing state
|
||||||
isProcessing: boolean;
|
isProcessing: boolean;
|
||||||
processingProgress: number;
|
processingProgress: number;
|
||||||
|
|
||||||
// Export state
|
// Export state
|
||||||
lastExportConfig?: {
|
lastExportConfig?: {
|
||||||
filename: string;
|
filename: string;
|
||||||
@ -89,7 +89,7 @@ export interface FileContextActions {
|
|||||||
removeFiles: (fileIds: string[], deleteFromStorage?: boolean) => void;
|
removeFiles: (fileIds: string[], deleteFromStorage?: boolean) => void;
|
||||||
replaceFile: (oldFileId: string, newFile: File) => Promise<void>;
|
replaceFile: (oldFileId: string, newFile: File) => Promise<void>;
|
||||||
clearAllFiles: () => void;
|
clearAllFiles: () => void;
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
setCurrentMode: (mode: ModeType) => void;
|
setCurrentMode: (mode: ModeType) => void;
|
||||||
// Selection management
|
// Selection management
|
||||||
@ -97,12 +97,12 @@ export interface FileContextActions {
|
|||||||
setSelectedPages: (pageNumbers: number[]) => void;
|
setSelectedPages: (pageNumbers: number[]) => void;
|
||||||
updateProcessedFile: (file: File, processedFile: ProcessedFile) => void;
|
updateProcessedFile: (file: File, processedFile: ProcessedFile) => void;
|
||||||
clearSelections: () => void;
|
clearSelections: () => void;
|
||||||
|
|
||||||
// Edit operations
|
// Edit operations
|
||||||
applyPageOperations: (fileId: string, operations: PageOperation[]) => void;
|
applyPageOperations: (fileId: string, operations: PageOperation[]) => void;
|
||||||
applyFileOperation: (operation: FileOperation) => void;
|
applyFileOperation: (operation: FileOperation) => void;
|
||||||
undoLastOperation: (fileId?: string) => void;
|
undoLastOperation: (fileId?: string) => void;
|
||||||
|
|
||||||
// Operation history management
|
// Operation history management
|
||||||
recordOperation: (fileId: string, operation: FileOperation | PageOperation) => void;
|
recordOperation: (fileId: string, operation: FileOperation | PageOperation) => void;
|
||||||
markOperationApplied: (fileId: string, operationId: string) => void;
|
markOperationApplied: (fileId: string, operationId: string) => void;
|
||||||
@ -110,31 +110,31 @@ export interface FileContextActions {
|
|||||||
getFileHistory: (fileId: string) => FileOperationHistory | undefined;
|
getFileHistory: (fileId: string) => FileOperationHistory | undefined;
|
||||||
getAppliedOperations: (fileId: string) => (FileOperation | PageOperation)[];
|
getAppliedOperations: (fileId: string) => (FileOperation | PageOperation)[];
|
||||||
clearFileHistory: (fileId: string) => void;
|
clearFileHistory: (fileId: string) => void;
|
||||||
|
|
||||||
// Viewer state
|
// Viewer state
|
||||||
updateViewerConfig: (config: Partial<ViewerConfig>) => void;
|
updateViewerConfig: (config: Partial<ViewerConfig>) => void;
|
||||||
|
|
||||||
// Export configuration
|
// Export configuration
|
||||||
setExportConfig: (config: FileContextState['lastExportConfig']) => void;
|
setExportConfig: (config: FileContextState['lastExportConfig']) => void;
|
||||||
|
|
||||||
|
|
||||||
// Utility
|
// Utility
|
||||||
getFileById: (fileId: string) => File | undefined;
|
getFileById: (fileId: string) => File | undefined;
|
||||||
getProcessedFileById: (fileId: string) => ProcessedFile | undefined;
|
getProcessedFileById: (fileId: string) => ProcessedFile | undefined;
|
||||||
getCurrentFile: () => File | undefined;
|
getCurrentFile: () => File | undefined;
|
||||||
getCurrentProcessedFile: () => ProcessedFile | undefined;
|
getCurrentProcessedFile: () => ProcessedFile | undefined;
|
||||||
|
|
||||||
// Context persistence
|
// Context persistence
|
||||||
saveContext: () => Promise<void>;
|
saveContext: () => Promise<void>;
|
||||||
loadContext: () => Promise<void>;
|
loadContext: () => Promise<void>;
|
||||||
resetContext: () => void;
|
resetContext: () => void;
|
||||||
|
|
||||||
// Navigation guard system
|
// Navigation guard system
|
||||||
setHasUnsavedChanges: (hasChanges: boolean) => void;
|
setHasUnsavedChanges: (hasChanges: boolean) => void;
|
||||||
requestNavigation: (navigationFn: () => void) => boolean;
|
requestNavigation: (navigationFn: () => void) => boolean;
|
||||||
confirmNavigation: () => void;
|
confirmNavigation: () => void;
|
||||||
cancelNavigation: () => void;
|
cancelNavigation: () => void;
|
||||||
|
|
||||||
// Memory management
|
// Memory management
|
||||||
trackBlobUrl: (url: string) => void;
|
trackBlobUrl: (url: string) => void;
|
||||||
trackPdfDocument: (fileId: string, pdfDoc: any) => void;
|
trackPdfDocument: (fileId: string, pdfDoc: any) => void;
|
||||||
@ -163,4 +163,4 @@ export interface FileContextUrlParams {
|
|||||||
pageIds?: string[];
|
pageIds?: string[];
|
||||||
zoom?: number;
|
zoom?: number;
|
||||||
page?: number;
|
page?: number;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user