@@ -70,16 +70,25 @@ function createPartial(options: UploadOptions, actorKey: string, quotaUserId: st
7070}
7171
7272function activePartials ( actorKey : string ) {
73- return [ ...partialsCache . values ( ) ] . filter ( ( partial ) => partial . actorKey === actorKey ) . length ;
73+ let count = 0 ;
74+ for ( const partial of partialsCache . values ( ) ) {
75+ if ( partial . actorKey === actorKey && ++ count >= MAX_PARTIALS ) return count ;
76+ }
77+
78+ return count ;
7479}
7580
7681function quotaReservations ( quotaUserId : string ) {
77- const reservations = [ ...partialsCache . values ( ) ] . filter ( ( partial ) => partial . quotaUserId === quotaUserId ) ;
82+ let size = 0 ;
83+ let files = 0 ;
84+ for ( const partial of partialsCache . values ( ) ) {
85+ if ( partial . quotaUserId !== quotaUserId || partial . finalized ) continue ;
86+
87+ size += partial . total ;
88+ files ++ ;
89+ }
7890
79- return {
80- size : reservations . reduce ( ( total , partial ) => total + partial . total , 0 ) ,
81- files : reservations . filter ( ( partial ) => ! partial . finalized ) . length ,
82- } ;
91+ return { size, files } ;
8392}
8493
8594async function deletePartial ( identifier : string , deleteFiles = true ) {
@@ -98,12 +107,16 @@ async function deletePartial(identifier: string, deleteFiles = true) {
98107}
99108
100109async function deleteOrphanedPartialFiles ( ) {
101- const activePrefixes = [ ...partialsCache . values ( ) ] . map ( ( partial ) => partial . prefix ) ;
102110 const tempFiles = await readdir ( config . core . tempDirectory ) ;
103- const orphaned = tempFiles . filter (
104- ( file ) =>
105- file . startsWith ( 'zipline_partial_' ) && ! activePrefixes . some ( ( prefix ) => file . startsWith ( prefix ) ) ,
106- ) ;
111+ const orphaned = tempFiles . filter ( ( file ) => {
112+ if ( ! file . startsWith ( 'zipline_partial_' ) ) return false ;
113+
114+ for ( const partial of partialsCache . values ( ) ) {
115+ if ( file . startsWith ( partial . prefix ) ) return false ;
116+ }
117+
118+ return true ;
119+ } ) ;
107120
108121 await Promise . all ( orphaned . map ( ( file ) => rm ( join ( config . core . tempDirectory , file ) , { force : true } ) ) ) ;
109122
@@ -307,7 +320,7 @@ export default typedPlugin(
307320
308321 const data : Prisma . FileCreateInput = {
309322 name : `${ fileName } ${ extension } ` ,
310- size : 0 ,
323+ size : total ,
311324 type : mimetype ,
312325 User : {
313326 connect : {
@@ -327,9 +340,23 @@ export default typedPlugin(
327340 }
328341 if ( ! req . user && folder ) data . anonymous = true ;
329342
330- const fileUpload = await prisma . file . create ( {
331- data,
332- } ) ;
343+ let fileUpload ;
344+ try {
345+ fileUpload = await prisma . $transaction ( async ( tx ) => {
346+ if ( quotaUser ?. quota ) {
347+ await tx . $queryRaw `SELECT "id" FROM "User" WHERE "id" = ${ quotaUser . id } FOR UPDATE` ;
348+
349+ const quotaCheck = await checkQuota ( quotaUser , total , 1 , tx ) ;
350+ if ( quotaCheck !== true )
351+ throw new ApiError ( 5002 , typeof quotaCheck === 'string' ? quotaCheck : undefined ) ;
352+ }
353+
354+ return tx . file . create ( { data } ) ;
355+ } ) ;
356+ } catch ( error ) {
357+ await deletePartial ( options . partial . identifier ) ;
358+ throw error ;
359+ }
333360
334361 const urlPath =
335362 options . extensionless && config . files . extensionlessUrls
@@ -378,6 +405,9 @@ export default typedPlugin(
378405 result = await prisma . file . update ( msg . data ) ;
379406 await deletePartial ( partialIdentifier , false ) ;
380407 break ;
408+ case 'file.delete' :
409+ result = await prisma . file . delete ( msg . data ) ;
410+ break ;
381411 case 'user.findUnique' :
382412 result = await prisma . user . findUnique ( msg . data ) ;
383413 break ;
0 commit comments