Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3363260
D13495.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
22 KB
Referenced Files
None
Subscribers
None
D13495.diff
View Options
diff --git a/keyserver/src/creators/farcaster-channel-tag-creator.js b/keyserver/src/creators/farcaster-channel-tag-creator.js
--- a/keyserver/src/creators/farcaster-channel-tag-creator.js
+++ b/keyserver/src/creators/farcaster-channel-tag-creator.js
@@ -8,6 +8,7 @@
CreateOrUpdateFarcasterChannelTagResponse,
} from 'lib/types/community-types.js';
import { threadPermissions } from 'lib/types/thread-permission-types.js';
+import type { BlobOperationResult } from 'lib/utils/blob-service.js';
import { ServerError } from 'lib/utils/errors.js';
import {
@@ -18,11 +19,10 @@
import { fetchCommunityInfos } from '../fetchers/community-fetchers.js';
import { checkThreadPermission } from '../fetchers/thread-permission-fetchers.js';
import {
- uploadBlob,
+ uploadBlobKeyserverWrapper,
assignHolder,
download,
deleteBlob,
- type BlobOperationResult,
type BlobDownloadResult,
} from '../services/blob.js';
import { Viewer } from '../session/viewer.js';
@@ -162,7 +162,7 @@
const hash = farcasterChannelTagBlobHash(farcasterChannelID);
const blob = new Blob([payloadString]);
- const uploadResult = await uploadBlob(blob, hash);
+ const uploadResult = await uploadBlobKeyserverWrapper(blob, hash);
if (!uploadResult.success) {
return uploadResult;
diff --git a/keyserver/src/creators/invite-link-creator.js b/keyserver/src/creators/invite-link-creator.js
--- a/keyserver/src/creators/invite-link-creator.js
+++ b/keyserver/src/creators/invite-link-creator.js
@@ -11,6 +11,7 @@
InviteLink,
} from 'lib/types/link-types.js';
import { threadPermissions } from 'lib/types/thread-permission-types.js';
+import type { BlobOperationResult } from 'lib/utils/blob-service.js';
import { ServerError } from 'lib/utils/errors.js';
import { reservedUsernamesSet } from 'lib/utils/reserved-users.js';
@@ -27,9 +28,8 @@
download,
type BlobDownloadResult,
assignHolder,
- uploadBlob,
+ uploadBlobKeyserverWrapper,
deleteBlob,
- type BlobOperationResult,
} from '../services/blob.js';
import { Viewer } from '../session/viewer.js';
import { thisKeyserverID } from '../user/identity.js';
@@ -272,7 +272,7 @@
const key = inviteLinkBlobHash(linkSecret);
const blob = new Blob([payloadString]);
- const uploadResult = await uploadBlob(blob, key);
+ const uploadResult = await uploadBlobKeyserverWrapper(blob, key);
if (!uploadResult.success) {
return uploadResult;
}
diff --git a/keyserver/src/push/encrypted-notif-utils-api.js b/keyserver/src/push/encrypted-notif-utils-api.js
--- a/keyserver/src/push/encrypted-notif-utils-api.js
+++ b/keyserver/src/push/encrypted-notif-utils-api.js
@@ -69,6 +69,8 @@
const unencryptedDataBytes = new TextEncoder().encode(unencryptedData);
return await encrypt(encryptionKeyBytes, unencryptedDataBytes);
},
+ normalizeUint8ArrayForBlobUpload: (uint8Array: Uint8Array) =>
+ new Blob([uint8Array]),
};
export default encryptedNotifUtilsAPI;
diff --git a/keyserver/src/services/blob.js b/keyserver/src/services/blob.js
--- a/keyserver/src/services/blob.js
+++ b/keyserver/src/services/blob.js
@@ -5,6 +5,10 @@
getBlobFetchableURL,
makeBlobServiceEndpointURL,
} from 'lib/utils/blob-service.js';
+import {
+ uploadBlob,
+ type BlobOperationResult,
+} from 'lib/utils/blob-service.js';
import { createHTTPAuthorizationHeader } from 'lib/utils/services-utils.js';
import { verifyUserLoggedIn } from '../user/login.js';
@@ -35,49 +39,6 @@
+holder: string,
};
-export type BlobOperationResult =
- | {
- +success: true,
- }
- | {
- +success: false,
- +reason: 'HASH_IN_USE' | 'OTHER',
- +status: number,
- +statusText: string,
- };
-
-async function uploadBlob(
- blob: Blob,
- hash: string,
-): Promise<BlobOperationResult> {
- const formData = new FormData();
- formData.append('blob_hash', hash);
- formData.append('blob_data', blob);
-
- const headers = await createRequestHeaders(false);
- const uploadBlobResponse = await fetch(
- makeBlobServiceEndpointURL(blobService.httpEndpoints.UPLOAD_BLOB),
- {
- method: blobService.httpEndpoints.UPLOAD_BLOB.method,
- body: formData,
- headers,
- },
- );
-
- if (!uploadBlobResponse.ok) {
- const { status, statusText } = uploadBlobResponse;
- const reason = status === 409 ? 'HASH_IN_USE' : 'OTHER';
- return {
- success: false,
- reason,
- status,
- statusText,
- };
- }
-
- return { success: true };
-}
-
async function assignHolder(
params: BlobDescriptor,
): Promise<BlobOperationResult> {
@@ -103,6 +64,14 @@
return { success: true };
}
+async function uploadBlobKeyserverWrapper(
+ blob: Blob,
+ hash: string,
+): Promise<BlobOperationResult> {
+ const authHeaders = await createRequestHeaders(false);
+ return uploadBlob(blob, hash, authHeaders);
+}
+
async function upload(
blob: Blob,
params: BlobDescriptor,
@@ -117,10 +86,9 @@
},
> {
const { hash, holder } = params;
-
const [holderResult, uploadResult] = await Promise.all([
assignHolder({ hash, holder }),
- uploadBlob(blob, hash),
+ uploadBlobKeyserverWrapper(blob, hash),
]);
if (holderResult.success && uploadResult.success) {
return { success: true };
@@ -171,4 +139,11 @@
});
}
-export { upload, uploadBlob, assignHolder, download, deleteBlob };
+export {
+ upload,
+ uploadBlob,
+ assignHolder,
+ download,
+ deleteBlob,
+ uploadBlobKeyserverWrapper,
+};
diff --git a/lib/facts/blob-service.js b/lib/facts/blob-service.js
--- a/lib/facts/blob-service.js
+++ b/lib/facts/blob-service.js
@@ -2,7 +2,7 @@
import { isDev } from '../utils/dev-utils.js';
-type BlobServicePath = '/blob/:blobHash' | '/blob';
+type BlobServicePath = '/blob/:blobHash' | '/blob' | '/holders';
export type BlobServiceHTTPEndpoint = {
+path: BlobServicePath,
@@ -23,6 +23,10 @@
path: '/blob',
method: 'POST',
},
+ ASSIGN_MULTIPLE_HOLDERS: {
+ path: '/holders',
+ method: 'POST',
+ },
UPLOAD_BLOB: {
path: '/blob',
method: 'PUT',
diff --git a/lib/push/android-notif-creators.js b/lib/push/android-notif-creators.js
--- a/lib/push/android-notif-creators.js
+++ b/lib/push/android-notif-creators.js
@@ -7,7 +7,9 @@
prepareEncryptedAndroidVisualNotifications,
prepareEncryptedAndroidSilentNotifications,
prepareLargeNotifData,
+ type LargeNotifEncryptionResult,
type LargeNotifData,
+ generateBlobHolders,
} from './crypto.js';
import { hasMinCodeVersion } from '../shared/version-utils.js';
import type { PlatformDetails } from '../types/device-types.js';
@@ -70,7 +72,7 @@
inputData: AndroidNotifInputData,
devices: $ReadOnlyArray<NotificationTargetDevice>,
largeNotifToEncryptionResultPromises?: {
- [string]: Promise<LargeNotifData>,
+ [string]: Promise<LargeNotifEncryptionResult>,
},
): Promise<{
+targetedNotifications: $ReadOnlyArray<TargetedAndroidNotification>,
@@ -210,6 +212,7 @@
const copyWithMessageInfosDataBlob = JSON.stringify(
copyWithMessageInfos.data,
);
+
if (
canQueryBlobService &&
largeNotifToEncryptionResultPromises &&
@@ -219,14 +222,13 @@
await largeNotifToEncryptionResultPromises[copyWithMessageInfosDataBlob];
blobHash = largeNotifData.blobHash;
encryptionKey = largeNotifData.encryptionKey;
- blobHolders = largeNotifData.blobHolders;
+ blobHolders = generateBlobHolders(devicesWithExcessiveSizeNoHolders.length);
encryptedCopyWithMessageInfos =
largeNotifData.encryptedCopyWithMessageInfos;
} else if (canQueryBlobService && largeNotifToEncryptionResultPromises) {
largeNotifToEncryptionResultPromises[copyWithMessageInfosDataBlob] =
prepareLargeNotifData(
copyWithMessageInfosDataBlob,
- devicesWithExcessiveSizeNoHolders.length,
encryptedNotifUtilsAPI,
);
@@ -234,7 +236,7 @@
await largeNotifToEncryptionResultPromises[copyWithMessageInfosDataBlob];
blobHash = largeNotifData.blobHash;
encryptionKey = largeNotifData.encryptionKey;
- blobHolders = largeNotifData.blobHolders;
+ blobHolders = generateBlobHolders(devicesWithExcessiveSizeNoHolders.length);
encryptedCopyWithMessageInfos =
largeNotifData.encryptedCopyWithMessageInfos;
} else if (canQueryBlobService) {
diff --git a/lib/push/apns-notif-creators.js b/lib/push/apns-notif-creators.js
--- a/lib/push/apns-notif-creators.js
+++ b/lib/push/apns-notif-creators.js
@@ -12,6 +12,8 @@
prepareEncryptedAPNsSilentNotifications,
prepareLargeNotifData,
type LargeNotifData,
+ type LargeNotifEncryptionResult,
+ generateBlobHolders,
} from './crypto.js';
import { getAPNsNotificationTopic } from '../shared/notif-utils.js';
import { hasMinCodeVersion } from '../shared/version-utils.js';
@@ -45,7 +47,7 @@
inputData: APNsNotifInputData,
devices: $ReadOnlyArray<NotificationTargetDevice>,
largeNotifToEncryptionResultPromises?: {
- [string]: Promise<LargeNotifData>,
+ [string]: Promise<LargeNotifEncryptionResult>,
},
): Promise<{
+targetedNotifications: $ReadOnlyArray<TargetedAPNsNotification>,
@@ -260,14 +262,14 @@
await largeNotifToEncryptionResultPromises[copyWithMessageInfosBlob];
blobHash = largeNotifData.blobHash;
encryptionKey = largeNotifData.encryptionKey;
- blobHolders = largeNotifData.blobHolders;
+ blobHolders = generateBlobHolders(devicesWithExcessiveSizeNoHolders.length);
encryptedCopyWithMessageInfos =
largeNotifData.encryptedCopyWithMessageInfos;
} else if (canQueryBlobService && largeNotifToEncryptionResultPromises) {
largeNotifToEncryptionResultPromises[copyWithMessageInfosBlob] =
prepareLargeNotifData(
copyWithMessageInfosBlob,
- devicesWithExcessiveSizeNoHolders.length,
+
encryptedNotifUtilsAPI,
);
@@ -275,7 +277,7 @@
await largeNotifToEncryptionResultPromises[copyWithMessageInfosBlob];
blobHash = largeNotifData.blobHash;
encryptionKey = largeNotifData.encryptionKey;
- blobHolders = largeNotifData.blobHolders;
+ blobHolders = generateBlobHolders(devicesWithExcessiveSizeNoHolders.length);
encryptedCopyWithMessageInfos =
largeNotifData.encryptedCopyWithMessageInfos;
} else if (canQueryBlobService) {
diff --git a/lib/push/crypto.js b/lib/push/crypto.js
--- a/lib/push/crypto.js
+++ b/lib/push/crypto.js
@@ -20,6 +20,7 @@
APNsNotificationRescind,
APNsBadgeOnlyNotification,
} from '../types/notif-types.js';
+import { toBase64URL } from '../utils/base64.js';
async function encryptAndroidNotificationPayload<T>(
encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
@@ -611,21 +612,26 @@
return Promise.all(notificationPromises);
}
-export type LargeNotifData = {
- +blobHolders: $ReadOnlyArray<string>,
+export type LargeNotifEncryptionResult = {
+blobHash: string,
+encryptionKey: string,
+encryptedCopyWithMessageInfos: Uint8Array,
};
+export type LargeNotifData = $ReadOnly<{
+ ...LargeNotifEncryptionResult,
+ +blobHolders: $ReadOnlyArray<string>,
+}>;
+
+function generateBlobHolders(numberOfDevices: number): $ReadOnlyArray<string> {
+ return Array.from({ length: numberOfDevices }, () => uuid.v4());
+}
+
async function prepareLargeNotifData(
copyWithMessageInfos: string,
- numberOfDevices: number,
encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
-): Promise<LargeNotifData> {
+): Promise<LargeNotifEncryptionResult> {
const encryptionKey = await encryptedNotifUtilsAPI.generateAESKey();
-
- const blobHolders = Array.from({ length: numberOfDevices }, () => uuid.v4());
const encryptedCopyWithMessageInfos =
await encryptedNotifUtilsAPI.encryptWithAESKey(
encryptionKey,
@@ -634,9 +640,9 @@
const blobHash = await encryptedNotifUtilsAPI.getBlobHash(
encryptedCopyWithMessageInfos,
);
+ const blobHashBase64url = toBase64URL(blobHash);
return {
- blobHolders,
- blobHash,
+ blobHash: blobHashBase64url,
encryptedCopyWithMessageInfos,
encryptionKey,
};
@@ -650,4 +656,5 @@
prepareEncryptedWebNotifications,
prepareEncryptedWNSNotifications,
prepareLargeNotifData,
+ generateBlobHolders,
};
diff --git a/lib/push/send-hooks.react.js b/lib/push/send-hooks.react.js
--- a/lib/push/send-hooks.react.js
+++ b/lib/push/send-hooks.react.js
@@ -1,9 +1,11 @@
// @flow
import invariant from 'invariant';
+import _groupBy from 'lodash/fp/groupBy.js';
import * as React from 'react';
import uuid from 'uuid';
+import type { LargeNotifData } from './crypto.js';
import {
preparePushNotifs,
prepareOwnDevicesPushNotifs,
@@ -14,6 +16,7 @@
import { usePeerOlmSessionsCreatorContext } from '../components/peer-olm-session-creator-provider.react.js';
import { thickRawThreadInfosSelector } from '../selectors/thread-selectors.js';
import { IdentityClientContext } from '../shared/identity-client-context.js';
+import type { AuthMetadata } from '../shared/identity-client-context.js';
import { useTunnelbroker } from '../tunnelbroker/tunnelbroker-context.js';
import type {
TargetedAPNsNotification,
@@ -21,6 +24,7 @@
TargetedWebNotification,
TargetedWNSNotification,
NotificationsCreationData,
+ EncryptedNotifUtilsAPI,
} from '../types/notif-types.js';
import { deviceToTunnelbrokerMessageTypes } from '../types/tunnelbroker/messages.js';
import type {
@@ -29,9 +33,12 @@
TunnelbrokerWebPushNotif,
TunnelbrokerWNSNotif,
} from '../types/tunnelbroker/notif-types.js';
+import { uploadBlob, assignMultipleHolders } from '../utils/blob-service.js';
import { getConfig } from '../utils/config.js';
import { getMessageForException } from '../utils/errors.js';
+import { values } from '../utils/objects.js';
import { useSelector } from '../utils/redux-utils.js';
+import { createDefaultHTTPRequestHeaders } from '../utils/services-utils.js';
function apnsNotifToTunnelbrokerAPNsNotif(
targetedNotification: TargetedAPNsNotification,
@@ -119,7 +126,9 @@
if (!notifCreationData) {
return;
}
- const { deviceID, userID: senderUserID } = await getAuthMetadata();
+ const authMetadata = await getAuthMetadata();
+ const { deviceID, userID: senderUserID } = authMetadata;
+
if (!deviceID || !senderUserID) {
return;
}
@@ -176,6 +185,18 @@
};
}
+ if (preparedPushNotifs) {
+ try {
+ await uploadLargeNotifBlobs(
+ preparedPushNotifs,
+ authMetadata,
+ encryptedNotifUtilsAPI,
+ );
+ } catch (e) {
+ console.log('Failed to upload blobs', e);
+ }
+ }
+
const sendPromises = [];
for (const userID in allPreparedPushNotifs) {
for (const notif of allPreparedPushNotifs[userID]
@@ -238,4 +259,79 @@
);
}
+async function uploadLargeNotifBlobs(
+ pushNotifs: PerUserTargetedNotifications,
+ authMetadata: AuthMetadata,
+ encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
+): Promise<void> {
+ const largeNotifArray = values(pushNotifs)
+ .map(({ largeNotifDataArray }) => largeNotifDataArray)
+ .flat();
+
+ if (largeNotifArray.length === 0) {
+ return;
+ }
+
+ const largeNotifsByHash: {
+ +[blobHash: string]: $ReadOnlyArray<LargeNotifData>,
+ } = _groupBy(largeNotifData => largeNotifData.blobHash)(largeNotifArray);
+
+ const uploads = Object.entries(largeNotifsByHash).map(
+ ([blobHash, [{ encryptedCopyWithMessageInfos }]]) => ({
+ blobHash,
+ encryptedCopyWithMessageInfos,
+ }),
+ );
+
+ const assignments = Object.entries(largeNotifsByHash)
+ .map(([blobHash, largeNotifs]) =>
+ largeNotifs
+ .map(({ blobHolders }) => blobHolders)
+ .flat()
+ .map(holder => ({ blobHash, holder })),
+ )
+ .flat();
+
+ const authHeaders = createDefaultHTTPRequestHeaders(authMetadata);
+ const uploadPromises = uploads.map(
+ ({ blobHash, encryptedCopyWithMessageInfos }) =>
+ uploadBlob(
+ encryptedNotifUtilsAPI.normalizeUint8ArrayForBlobUpload(
+ encryptedCopyWithMessageInfos,
+ ),
+ blobHash,
+ authHeaders,
+ ),
+ );
+ const assignmentPromise = assignMultipleHolders(assignments, authHeaders);
+
+ const [uploadResults, assignmentResult] = await Promise.all([
+ Promise.all(uploadPromises),
+ assignmentPromise,
+ ]);
+
+ for (const uploadResult of uploadResults) {
+ if (uploadResult.success) {
+ continue;
+ }
+ const { reason, statusText } = uploadResult;
+ console.log(
+ `Failed to upload. Reason: ${reason}, status text: ${statusText}`,
+ );
+ }
+
+ if (assignmentResult.success) {
+ return;
+ }
+
+ if (assignmentResult.error) {
+ const { statusText } = assignmentResult;
+ console.log(`Failed to assign all holders. Status text: ${statusText}`);
+ return;
+ }
+
+ for (const [blobHash, holder] of assignmentResult.failedAssignments) {
+ console.log(`Assingnemt failed for holder: ${holder} and hash ${blobHash}`);
+ }
+}
export { useSendPushNotifs };
diff --git a/lib/push/send-utils.js b/lib/push/send-utils.js
--- a/lib/push/send-utils.js
+++ b/lib/push/send-utils.js
@@ -13,7 +13,7 @@
createAPNsBadgeOnlyNotification,
createAPNsNotificationRescind,
} from './apns-notif-creators.js';
-import type { LargeNotifData } from './crypto.js';
+import type { LargeNotifEncryptionResult, LargeNotifData } from './crypto';
import {
stringToVersionKey,
getDevicesByPlatform,
@@ -470,7 +470,7 @@
input: NotifCreatorInput,
devices: $ReadOnlyArray<NotificationTargetDevice>,
largeNotifToEncryptionResultPromises?: {
- [string]: Promise<LargeNotifData>,
+ [string]: Promise<LargeNotifEncryptionResult>,
},
) => Promise<{
+targetedNotifications: $ReadOnlyArray<TargetedNotificationType>,
@@ -500,7 +500,7 @@
NotifCreatorInput,
>,
largeNotifToEncryptionResultPromises?: {
- [string]: Promise<LargeNotifData>,
+ [string]: Promise<LargeNotifEncryptionResult>,
},
): Promise<{
+targetedNotificationsWithPlatform: $ReadOnlyArray<{
@@ -601,7 +601,7 @@
async function buildNotifsForUserDevices(
inputData: BuildNotifsForUserDevicesInputData,
largeNotifToEncryptionResultPromises: {
- [string]: Promise<LargeNotifData>,
+ [string]: Promise<LargeNotifEncryptionResult>,
},
): Promise<?{
+targetedNotificationsWithPlatform: $ReadOnlyArray<TargetedNotificationWithPlatform>,
@@ -1011,7 +1011,7 @@
);
const largeNotifToEncryptionResultPromises: {
- [string]: Promise<LargeNotifData>,
+ [string]: Promise<LargeNotifEncryptionResult>,
} = {};
for (const userID in mergedUsersToCollapsableInfo) {
@@ -1067,7 +1067,7 @@
.map(({ largeNotifDataArray: array }) => array)
.filter(Boolean)
.flat();
- console.log(largeNotifDataArray);
+
return {
targetedNotifications: targetedNotifsWithPlatform,
largeNotifDataArray,
diff --git a/lib/types/notif-types.js b/lib/types/notif-types.js
--- a/lib/types/notif-types.js
+++ b/lib/types/notif-types.js
@@ -426,4 +426,5 @@
encryptionKey: string,
unencrypotedData: string,
) => Promise<Uint8Array>,
+ +normalizeUint8ArrayForBlobUpload: (array: Uint8Array) => string | Blob,
};
diff --git a/lib/utils/__mocks__/config.js b/lib/utils/__mocks__/config.js
--- a/lib/utils/__mocks__/config.js
+++ b/lib/utils/__mocks__/config.js
@@ -54,6 +54,7 @@
getEncryptedNotifHash: jest.fn(),
getBlobHash: jest.fn(),
getNotifByteSize: jest.fn(),
+ normalizeUint8ArrayForBlobUpload: jest.fn(),
},
});
diff --git a/lib/utils/blob-service.js b/lib/utils/blob-service.js
--- a/lib/utils/blob-service.js
+++ b/lib/utils/blob-service.js
@@ -64,6 +64,96 @@
return `${urlSafeDeviceID}:${uuid.v4()}`;
}
+export type BlobOperationResult =
+ | {
+ +success: true,
+ }
+ | {
+ +success: false,
+ +reason: 'HASH_IN_USE' | 'OTHER',
+ +status: number,
+ +statusText: string,
+ };
+
+async function uploadBlob(
+ blob: Blob | string,
+ hash: string,
+ headers: { [string]: string },
+): Promise<BlobOperationResult> {
+ const formData = new FormData();
+ formData.append('blob_hash', hash);
+ if (typeof blob === 'string') {
+ formData.append('base64_data', blob);
+ } else {
+ formData.append('blob_data', blob);
+ }
+
+ const uploadBlobResponse = await fetch(
+ makeBlobServiceEndpointURL(blobServiceConfig.httpEndpoints.UPLOAD_BLOB),
+ {
+ method: blobServiceConfig.httpEndpoints.UPLOAD_BLOB.method,
+ body: formData,
+ headers,
+ },
+ );
+
+ if (!uploadBlobResponse.ok) {
+ const { status, statusText } = uploadBlobResponse;
+ const reason = status === 409 ? 'HASH_IN_USE' : 'OTHER';
+ return {
+ success: false,
+ reason,
+ status,
+ statusText,
+ };
+ }
+
+ return { success: true };
+}
+
+async function assignMultipleHolders(
+ holders: $ReadOnlyArray<{ +blobHash: string, +holder: string }>,
+ headers: { [string]: string },
+): Promise<
+ | { +success: true }
+ | { +error: true, status: number, statusText: string }
+ | {
+ +failedAssignments: $ReadOnlyArray<{
+ +blobHash: string,
+ +holder: string,
+ }>,
+ },
+> {
+ const assignMultipleHoldersResponse = await fetch(
+ makeBlobServiceEndpointURL(
+ blobServiceConfig.httpEndpoints.ASSIGN_MULTIPLE_HOLDERS,
+ ),
+ {
+ method: blobServiceConfig.httpEndpoints.ASSIGN_MULTIPLE_HOLDERS.method,
+ headers: { ...headers, 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ requests: holders,
+ }),
+ },
+ );
+
+ if (!assignMultipleHoldersResponse.ok) {
+ const { status, statusText } = assignMultipleHoldersResponse;
+ return { error: true, status, statusText };
+ }
+
+ const { results } = await assignMultipleHoldersResponse.json();
+ const failedRequests = results
+ .filter(result => !result.success)
+ .map(({ blobHash, holder }) => ({ blobHash, holder }));
+
+ if (failedRequests.length !== 0) {
+ return { failedAssignments: failedRequests };
+ }
+
+ return { success: true };
+}
+
export {
makeBlobServiceURI,
isBlobServiceURI,
@@ -72,4 +162,6 @@
generateBlobHolder,
getBlobFetchableURL,
makeBlobServiceEndpointURL,
+ uploadBlob,
+ assignMultipleHolders,
};
diff --git a/native/push/encrypted-notif-utils-api.js b/native/push/encrypted-notif-utils-api.js
--- a/native/push/encrypted-notif-utils-api.js
+++ b/native/push/encrypted-notif-utils-api.js
@@ -56,6 +56,8 @@
new Uint8Array(unencryptedDataBytes),
);
},
+ normalizeUint8ArrayForBlobUpload: (array: Uint8Array) =>
+ commUtilsModule.base64EncodeBuffer(array.buffer),
};
export default encryptedNotifUtilsAPI;
diff --git a/web/push-notif/encrypted-notif-utils-api.js b/web/push-notif/encrypted-notif-utils-api.js
--- a/web/push-notif/encrypted-notif-utils-api.js
+++ b/web/push-notif/encrypted-notif-utils-api.js
@@ -56,6 +56,8 @@
unencryptedDataBytes,
);
},
+ normalizeUint8ArrayForBlobUpload: (uint8Array: Uint8Array) =>
+ new Blob([uint8Array]),
};
export default encryptedNotifUtilsAPI;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 26, 1:13 AM (20 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2581866
Default Alt Text
D13495.diff (22 KB)
Attached To
Mode
D13495: Implement sending large thick thread notifs
Attached
Detach File
Event Timeline
Log In to Comment