Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 100 additions & 4 deletions extensions/fsv2/src/controllers/FSController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ import type {

const { Controller, ExtensionController, HttpError, Post } = extension.import('extensionController');
const { Context } = extension.import('core');
const getApp = extension.import('core').util.helpers.get_app as (
query: { uid: string },
) => Promise<{ id?: unknown } | null>;
class UploadProgressTracker implements UploadProgressTrackerLike {
total = 0;
progress = 0;
Expand Down Expand Up @@ -84,6 +87,7 @@ export class FSController extends ExtensionController {
const storageAllowanceMax = this.#getStorageAllowanceMaxOverride(req);
const requestBody = this.#withGuiMetadata(req.body, req.body);
requestBody.fileMetadata = this.#normalizeFileMetadataPath(req, requestBody.fileMetadata, requestBody);
requestBody.fileMetadata = await this.#resolveAssociatedAppMetadata(requestBody.fileMetadata, requestBody);
await this.#assertWriteAccess(req, requestBody.fileMetadata, {
pathAlreadyNormalized: true,
});
Expand Down Expand Up @@ -116,16 +120,22 @@ export class FSController extends ExtensionController {
async startBatchWrites (req: Request<RouteParams, null, SignedWriteRequest[]>, res: Response<SignedWriteResponse[]>) {
const userId = this.#getActorUserId(req);
const storageAllowanceMax = this.#getStorageAllowanceMaxOverride(req);
const appUidLookupCache = new Map<string, Promise<number | null>>();
const requests = Array.isArray(req.body)
? req.body.map((requestBody) => {
? await Promise.all(req.body.map(async (requestBody) => {
const normalizedRequestBody = this.#withGuiMetadata(requestBody, req.body);
normalizedRequestBody.fileMetadata = this.#normalizeFileMetadataPath(
req,
normalizedRequestBody.fileMetadata,
normalizedRequestBody,
);
normalizedRequestBody.fileMetadata = await this.#resolveAssociatedAppMetadata(
normalizedRequestBody.fileMetadata,
normalizedRequestBody,
appUidLookupCache,
);
return normalizedRequestBody;
})
}))
: [];
await this.#assertBatchWriteAccess(
req,
Expand Down Expand Up @@ -260,6 +270,7 @@ export class FSController extends ExtensionController {
const storageAllowanceMax = this.#getStorageAllowanceMaxOverride(req);
const requestBody = this.#withGuiMetadata(req.body, req.body);
requestBody.fileMetadata = this.#normalizeFileMetadataPath(req, requestBody.fileMetadata, requestBody);
requestBody.fileMetadata = await this.#resolveAssociatedAppMetadata(requestBody.fileMetadata, requestBody);
await this.#assertWriteAccess(req, requestBody.fileMetadata, {
pathAlreadyNormalized: true,
});
Expand All @@ -285,6 +296,7 @@ export class FSController extends ExtensionController {
const userId = this.#getActorUserId(req);
const storageAllowanceMax = this.#getStorageAllowanceMaxOverride(req);
const requestMode = this.#resolveBatchWriteRequestMode(req);
const appUidLookupCache = new Map<string, Promise<number | null>>();
if ( requestMode === 'multipart' ) {
let parsedManifest: ParsedMultipartBatchManifest | null = null;
let preparedBatch: PreparedBatchWrite | null = null;
Expand Down Expand Up @@ -341,6 +353,17 @@ export class FSController extends ExtensionController {
if ( ! parsedManifest ) {
throw new HttpError(400, 'Batch write manifest is missing');
}
parsedManifest = {
...parsedManifest,
items: await Promise.all(parsedManifest.items.map(async (item) => ({
...item,
fileMetadata: await this.#resolveAssociatedAppMetadata(
item.fileMetadata,
item,
appUidLookupCache,
),
}))),
};
const activeManifestItems = parsedManifest.items
.filter((item) => !parsedManifest?.ignoredItemIndexes?.has(item.index));

Expand Down Expand Up @@ -490,15 +513,20 @@ export class FSController extends ExtensionController {
}

const requests = Array.isArray(req.body)
? req.body.map((requestBody) => {
? await Promise.all(req.body.map(async (requestBody) => {
const normalizedRequestBody = this.#withGuiMetadata(requestBody, req.body);
normalizedRequestBody.fileMetadata = this.#normalizeFileMetadataPath(
req,
normalizedRequestBody.fileMetadata,
normalizedRequestBody,
);
normalizedRequestBody.fileMetadata = await this.#resolveAssociatedAppMetadata(
normalizedRequestBody.fileMetadata,
normalizedRequestBody,
appUidLookupCache,
);
return normalizedRequestBody;
})
}))
: [];
const filteredRequests = requests.filter((requestBody) => {
return !this.#shouldIgnoreUploadPath(requestBody.fileMetadata.path);
Expand Down Expand Up @@ -796,9 +824,77 @@ export class FSController extends ExtensionController {
normalizedFileMetadata.bucketRegion = bucketRegion;
}

const associatedAppId = this.#toNumber(this.#firstDefined(
metadataRecord.associatedAppId,
metadataRecord.associated_app_id,
fallbackRecord.associatedAppId,
fallbackRecord.associated_app_id,
));
if ( associatedAppId !== undefined ) {
normalizedFileMetadata.associatedAppId = associatedAppId;
}

return normalizedFileMetadata as unknown as FSEntryWriteInput;
}

async #resolveAssociatedAppMetadata (
fileMetadata: FSEntryWriteInput,
fallbackSource?: unknown,
appUidLookupCache?: Map<string, Promise<number | null>>,
): Promise<FSEntryWriteInput> {
const metadataRecord = this.#toObjectRecord(fileMetadata);
const fallbackRecord = this.#toObjectRecord(fallbackSource);

const associatedAppId = this.#toNumber(this.#firstDefined(
metadataRecord.associatedAppId,
metadataRecord.associated_app_id,
fallbackRecord.associatedAppId,
fallbackRecord.associated_app_id,
));
if ( associatedAppId !== undefined ) {
return {
...fileMetadata,
associatedAppId,
};
}

const appUid = this.#firstDefined(
metadataRecord.appUID,
metadataRecord.appUid,
metadataRecord.app_uid,
fallbackRecord.appUID,
fallbackRecord.appUid,
fallbackRecord.app_uid,
);
if ( typeof appUid !== 'string' || appUid.trim().length === 0 ) {
return fileMetadata;
}

const normalizedAppUid = appUid.trim();
const lookupPromise = (() => {
const cachedLookup = appUidLookupCache?.get(normalizedAppUid);
if ( cachedLookup ) {
return cachedLookup;
}

const createdLookupPromise = (async () => {
const app = await getApp({ uid: normalizedAppUid });
return this.#toNumber(app?.id) ?? null;
})();
appUidLookupCache?.set(normalizedAppUid, createdLookupPromise);
return createdLookupPromise;
})();

const resolvedAppId = await lookupPromise;
if ( resolvedAppId === null ) {
return fileMetadata;
}
return {
...fileMetadata,
associatedAppId: resolvedAppId,
};
}

#toStorageCapacityCandidate (value: unknown): number | undefined {
const capacity = Number(value);
if ( !Number.isFinite(capacity) || capacity < 0 ) {
Expand Down
3 changes: 0 additions & 3 deletions src/backend/src/modules/web/WebServerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,9 +350,6 @@ class WebServerService extends BaseService {
process.on('SIGINT', () => {
this.beginGracefulShutdown('SIGINT');
});
process.on('SIGABRT', () => {
this.beginGracefulShutdown('SIGABRT');
});
}

beginGracefulShutdown (signal) {
Expand Down
10 changes: 7 additions & 3 deletions src/gui/src/helpers/item_icon.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,12 @@ const item_icon = async (fsentry) => {
// --------------------------------------------------
// app icon
// --------------------------------------------------
else if ( fsentry.associated_app && fsentry.associated_app?.name ) {
if ( fsentry.associated_app.icon )
else if (
fsentry.associated_app
|| fsentry.associated_app_id
|| fsentry.associatedAppId
) {
if ( fsentry.associated_app?.icon )
{
return { image: fsentry.associated_app.icon, type: 'icon' };
}
Expand Down Expand Up @@ -257,4 +261,4 @@ const item_icon = async (fsentry) => {
}
};

export default item_icon;
export default item_icon;
1 change: 1 addition & 0 deletions src/puter-js/src/modules/FileSystem/operations/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,7 @@ const upload = async function (items, dirPath, options = {}) {
overwrite: overwriteEnabled,
dedupeName: options.dedupeName ?? true,
createMissingParents: shouldCreateMissingParents,
app_uid: options.appUID,
};

return {
Expand Down
Loading