1- import { encryptNoteData , decryptNoteData } from '@/lib/encryption' ;
1+ import { encryptNoteData } from '@/lib/encryption' ;
22import type { FileAttachment } from '@/types/note' ;
33
44const API_BASE_URL = import . meta. env . VITE_API_URL || 'http://localhost:3001/api' ;
@@ -34,7 +34,17 @@ export class FileService {
3434
3535 private async encryptFile ( file : File , userId : string ) : Promise < { encryptedData : string ; encryptedTitle : string ; iv : string ; salt : string } > {
3636 const fileBuffer = await file . arrayBuffer ( ) ;
37- const base64Content = btoa ( String . fromCharCode ( ...new Uint8Array ( fileBuffer ) ) ) ;
37+
38+ // Convert ArrayBuffer to base64 safely for large files
39+ const bytes = new Uint8Array ( fileBuffer ) ;
40+ let base64Content = '' ;
41+ const chunkSize = 0x8000 ; // 32KB chunks to avoid function argument limits
42+
43+ for ( let i = 0 ; i < bytes . length ; i += chunkSize ) {
44+ const chunk = bytes . subarray ( i , Math . min ( i + chunkSize , bytes . length ) ) ;
45+ base64Content += String . fromCharCode . apply ( null , Array . from ( chunk ) ) ;
46+ }
47+ base64Content = btoa ( base64Content ) ;
3848
3949 const encrypted = await encryptNoteData ( userId , file . name , base64Content ) ;
4050 return {
@@ -45,15 +55,50 @@ export class FileService {
4555 } ;
4656 }
4757
48- private async decryptFile ( encryptedData : string , encryptedTitle : string , iv : string , salt : string , userId : string ) : Promise < ArrayBuffer > {
49- const decrypted = await decryptNoteData ( userId , encryptedTitle , encryptedData , iv , salt ) ;
58+ private async decryptFile ( encryptedData : string , iv : string , salt : string , userId : string ) : Promise < ArrayBuffer > {
59+
60+ const { encryptionService } = await import ( '@/lib/encryption' ) ;
61+
62+ // Convert base64 strings to required formats
63+ const ivBytes = this . base64ToUint8Array ( iv ) ;
64+ const saltBytes = this . base64ToUint8Array ( salt ) ;
65+ const encryptedBytes = this . base64ToArrayBuffer ( encryptedData ) ;
66+
67+ // Derive the same key used for encryption
68+ const key = await encryptionService . deriveKey ( userId , saltBytes ) ;
69+
70+ // Decrypt the file content
71+ const decryptedBuffer = await crypto . subtle . decrypt (
72+ { name : 'AES-GCM' , iv : ivBytes } ,
73+ key ,
74+ encryptedBytes
75+ ) ;
76+
77+ // The decrypted content is base64-encoded, so we need to decode it
78+ const decryptedText = new TextDecoder ( ) . decode ( decryptedBuffer ) ;
5079
51- const binaryString = atob ( decrypted . content ) ;
52- const bytes = new Uint8Array ( binaryString . length ) ;
53- for ( let i = 0 ; i < binaryString . length ; i ++ ) {
54- bytes [ i ] = binaryString . charCodeAt ( i ) ;
80+ // Decode the base64 to get the actual file content
81+ const actualFileContent = atob ( decryptedText ) ;
82+ const finalBuffer = new Uint8Array ( actualFileContent . length ) ;
83+ for ( let i = 0 ; i < actualFileContent . length ; i ++ ) {
84+ finalBuffer [ i ] = actualFileContent . charCodeAt ( i ) ;
5585 }
56- return bytes . buffer ;
86+
87+ return finalBuffer . buffer ;
88+ }
89+
90+ private base64ToUint8Array ( base64 : string ) : Uint8Array {
91+ const binary = atob ( base64 ) ;
92+ const bytes = new Uint8Array ( binary . length ) ;
93+ for ( let i = 0 ; i < binary . length ; i ++ ) {
94+ bytes [ i ] = binary . charCodeAt ( i ) ;
95+ }
96+ return bytes ;
97+ }
98+
99+ private base64ToArrayBuffer ( base64 : string ) : ArrayBuffer {
100+ const bytes = this . base64ToUint8Array ( base64 ) ;
101+ return bytes . buffer . slice ( bytes . byteOffset , bytes . byteOffset + bytes . byteLength ) ;
57102 }
58103
59104 async uploadFiles (
@@ -78,6 +123,7 @@ export class FileService {
78123 userId : string ,
79124 onProgress ?: ( progress : UploadProgress ) => void
80125 ) : Promise < FileAttachment > {
126+
81127 if ( file . size > 10 * 1024 * 1024 ) {
82128 throw new Error ( 'File size exceeds 10MB limit' ) ;
83129 }
@@ -150,13 +196,13 @@ export class FileService {
150196 const encryptedFile = await response . json ( ) ;
151197 const decryptedBuffer = await this . decryptFile (
152198 encryptedFile . encryptedData ,
153- encryptedFile . encryptedTitle ,
154199 encryptedFile . iv ,
155200 encryptedFile . salt ,
156201 userId
157202 ) ;
158203
159- return new Blob ( [ decryptedBuffer ] , { type : attachment . mimeType } ) ;
204+ const blob = new Blob ( [ decryptedBuffer ] , { type : attachment . mimeType } ) ;
205+ return blob ;
160206 }
161207
162208 async removeFile ( attachmentId : string ) : Promise < void > {
0 commit comments