@@ -37,6 +37,9 @@ import type {
3737
3838const { Controller, ExtensionController, HttpError, Post } = extension . import ( 'extensionController' ) ;
3939const { Context } = extension . import ( 'core' ) ;
40+ const getApp = extension . import ( 'core' ) . util . helpers . get_app as (
41+ query : { uid : string } ,
42+ ) => Promise < { id ?: unknown } | null > ;
4043class UploadProgressTracker implements UploadProgressTrackerLike {
4144 total = 0 ;
4245 progress = 0 ;
@@ -84,6 +87,7 @@ export class FSController extends ExtensionController {
8487 const storageAllowanceMax = this . #getStorageAllowanceMaxOverride( req ) ;
8588 const requestBody = this . #withGuiMetadata( req . body , req . body ) ;
8689 requestBody . fileMetadata = this . #normalizeFileMetadataPath( req , requestBody . fileMetadata , requestBody ) ;
90+ requestBody . fileMetadata = await this . #resolveAssociatedAppMetadata( requestBody . fileMetadata , requestBody ) ;
8791 await this . #assertWriteAccess( req , requestBody . fileMetadata , {
8892 pathAlreadyNormalized : true ,
8993 } ) ;
@@ -116,16 +120,22 @@ export class FSController extends ExtensionController {
116120 async startBatchWrites ( req : Request < RouteParams , null , SignedWriteRequest [ ] > , res : Response < SignedWriteResponse [ ] > ) {
117121 const userId = this . #getActorUserId( req ) ;
118122 const storageAllowanceMax = this . #getStorageAllowanceMaxOverride( req ) ;
123+ const appUidLookupCache = new Map < string , Promise < number | null > > ( ) ;
119124 const requests = Array . isArray ( req . body )
120- ? req . body . map ( ( requestBody ) => {
125+ ? await Promise . all ( req . body . map ( async ( requestBody ) => {
121126 const normalizedRequestBody = this . #withGuiMetadata( requestBody , req . body ) ;
122127 normalizedRequestBody . fileMetadata = this . #normalizeFileMetadataPath(
123128 req ,
124129 normalizedRequestBody . fileMetadata ,
125130 normalizedRequestBody ,
126131 ) ;
132+ normalizedRequestBody . fileMetadata = await this . #resolveAssociatedAppMetadata(
133+ normalizedRequestBody . fileMetadata ,
134+ normalizedRequestBody ,
135+ appUidLookupCache ,
136+ ) ;
127137 return normalizedRequestBody ;
128- } )
138+ } ) )
129139 : [ ] ;
130140 await this . #assertBatchWriteAccess(
131141 req ,
@@ -260,6 +270,7 @@ export class FSController extends ExtensionController {
260270 const storageAllowanceMax = this . #getStorageAllowanceMaxOverride( req ) ;
261271 const requestBody = this . #withGuiMetadata( req . body , req . body ) ;
262272 requestBody . fileMetadata = this . #normalizeFileMetadataPath( req , requestBody . fileMetadata , requestBody ) ;
273+ requestBody . fileMetadata = await this . #resolveAssociatedAppMetadata( requestBody . fileMetadata , requestBody ) ;
263274 await this . #assertWriteAccess( req , requestBody . fileMetadata , {
264275 pathAlreadyNormalized : true ,
265276 } ) ;
@@ -285,6 +296,7 @@ export class FSController extends ExtensionController {
285296 const userId = this . #getActorUserId( req ) ;
286297 const storageAllowanceMax = this . #getStorageAllowanceMaxOverride( req ) ;
287298 const requestMode = this . #resolveBatchWriteRequestMode( req ) ;
299+ const appUidLookupCache = new Map < string , Promise < number | null > > ( ) ;
288300 if ( requestMode === 'multipart' ) {
289301 let parsedManifest : ParsedMultipartBatchManifest | null = null ;
290302 let preparedBatch : PreparedBatchWrite | null = null ;
@@ -341,6 +353,17 @@ export class FSController extends ExtensionController {
341353 if ( ! parsedManifest ) {
342354 throw new HttpError ( 400 , 'Batch write manifest is missing' ) ;
343355 }
356+ parsedManifest = {
357+ ...parsedManifest ,
358+ items : await Promise . all ( parsedManifest . items . map ( async ( item ) => ( {
359+ ...item ,
360+ fileMetadata : await this . #resolveAssociatedAppMetadata(
361+ item . fileMetadata ,
362+ item ,
363+ appUidLookupCache ,
364+ ) ,
365+ } ) ) ) ,
366+ } ;
344367 const activeManifestItems = parsedManifest . items
345368 . filter ( ( item ) => ! parsedManifest ?. ignoredItemIndexes ?. has ( item . index ) ) ;
346369
@@ -490,15 +513,20 @@ export class FSController extends ExtensionController {
490513 }
491514
492515 const requests = Array . isArray ( req . body )
493- ? req . body . map ( ( requestBody ) => {
516+ ? await Promise . all ( req . body . map ( async ( requestBody ) => {
494517 const normalizedRequestBody = this . #withGuiMetadata( requestBody , req . body ) ;
495518 normalizedRequestBody . fileMetadata = this . #normalizeFileMetadataPath(
496519 req ,
497520 normalizedRequestBody . fileMetadata ,
498521 normalizedRequestBody ,
499522 ) ;
523+ normalizedRequestBody . fileMetadata = await this . #resolveAssociatedAppMetadata(
524+ normalizedRequestBody . fileMetadata ,
525+ normalizedRequestBody ,
526+ appUidLookupCache ,
527+ ) ;
500528 return normalizedRequestBody ;
501- } )
529+ } ) )
502530 : [ ] ;
503531 const filteredRequests = requests . filter ( ( requestBody ) => {
504532 return ! this . #shouldIgnoreUploadPath( requestBody . fileMetadata . path ) ;
@@ -796,9 +824,77 @@ export class FSController extends ExtensionController {
796824 normalizedFileMetadata . bucketRegion = bucketRegion ;
797825 }
798826
827+ const associatedAppId = this . #toNumber( this . #firstDefined(
828+ metadataRecord . associatedAppId ,
829+ metadataRecord . associated_app_id ,
830+ fallbackRecord . associatedAppId ,
831+ fallbackRecord . associated_app_id ,
832+ ) ) ;
833+ if ( associatedAppId !== undefined ) {
834+ normalizedFileMetadata . associatedAppId = associatedAppId ;
835+ }
836+
799837 return normalizedFileMetadata as unknown as FSEntryWriteInput ;
800838 }
801839
840+ async #resolveAssociatedAppMetadata (
841+ fileMetadata : FSEntryWriteInput ,
842+ fallbackSource ?: unknown ,
843+ appUidLookupCache ?: Map < string , Promise < number | null > > ,
844+ ) : Promise < FSEntryWriteInput > {
845+ const metadataRecord = this . #toObjectRecord( fileMetadata ) ;
846+ const fallbackRecord = this . #toObjectRecord( fallbackSource ) ;
847+
848+ const associatedAppId = this . #toNumber( this . #firstDefined(
849+ metadataRecord . associatedAppId ,
850+ metadataRecord . associated_app_id ,
851+ fallbackRecord . associatedAppId ,
852+ fallbackRecord . associated_app_id ,
853+ ) ) ;
854+ if ( associatedAppId !== undefined ) {
855+ return {
856+ ...fileMetadata ,
857+ associatedAppId,
858+ } ;
859+ }
860+
861+ const appUid = this . #firstDefined(
862+ metadataRecord . appUID ,
863+ metadataRecord . appUid ,
864+ metadataRecord . app_uid ,
865+ fallbackRecord . appUID ,
866+ fallbackRecord . appUid ,
867+ fallbackRecord . app_uid ,
868+ ) ;
869+ if ( typeof appUid !== 'string' || appUid . trim ( ) . length === 0 ) {
870+ return fileMetadata ;
871+ }
872+
873+ const normalizedAppUid = appUid . trim ( ) ;
874+ const lookupPromise = ( ( ) => {
875+ const cachedLookup = appUidLookupCache ?. get ( normalizedAppUid ) ;
876+ if ( cachedLookup ) {
877+ return cachedLookup ;
878+ }
879+
880+ const createdLookupPromise = ( async ( ) => {
881+ const app = await getApp ( { uid : normalizedAppUid } ) ;
882+ return this . #toNumber( app ?. id ) ?? null ;
883+ } ) ( ) ;
884+ appUidLookupCache ?. set ( normalizedAppUid , createdLookupPromise ) ;
885+ return createdLookupPromise ;
886+ } ) ( ) ;
887+
888+ const resolvedAppId = await lookupPromise ;
889+ if ( resolvedAppId === null ) {
890+ return fileMetadata ;
891+ }
892+ return {
893+ ...fileMetadata ,
894+ associatedAppId : resolvedAppId ,
895+ } ;
896+ }
897+
802898 #toStorageCapacityCandidate ( value : unknown ) : number | undefined {
803899 const capacity = Number ( value ) ;
804900 if ( ! Number . isFinite ( capacity ) || capacity < 0 ) {
0 commit comments