Skip to content

Commit c37ff42

Browse files
authored
Merge pull request #1644 from session-foundation/fix-qa-issues-fs-changes
fix: qa issues from fs changes
2 parents 502406e + b84ba43 commit c37ff42

File tree

12 files changed

+149
-65
lines changed

12 files changed

+149
-65
lines changed

ts/components/conversation/right-panel/overlay/message-info/OverlayMessageInfo.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,12 @@ async function getPropsForMessageInfo(
117117
const found = await Data.getMessageById(messageId);
118118
const attachmentsWithMediaDetails: Array<PropsForAttachment> = [];
119119
if (found) {
120+
const attachmentsInMsg = found.get('attachments') || [];
121+
120122
// process attachments so we have the fileSize, url and screenshots
121123
for (let i = 0; i < attachments.length; i++) {
122124
const props = found.getPropsForAttachment(attachments[i]);
125+
const fsUrl = attachmentsInMsg?.[i].url;
123126
if (
124127
props?.contentType &&
125128
GoogleChrome.isVideoTypeSupported(props?.contentType) &&
@@ -134,6 +137,7 @@ async function getPropsForMessageInfo(
134137
attachmentsWithMediaDetails.push({
135138
...props,
136139
duration,
140+
url: fsUrl,
137141
});
138142
} else if (props?.contentType && isAudio(props.contentType) && !props.duration && props.url) {
139143
// eslint-disable-next-line no-await-in-loop
@@ -145,9 +149,10 @@ async function getPropsForMessageInfo(
145149
attachmentsWithMediaDetails.push({
146150
...props,
147151
duration,
152+
url: fsUrl,
148153
});
149154
} else if (props) {
150-
attachmentsWithMediaDetails.push(props);
155+
attachmentsWithMediaDetails.push({ ...props, url: fsUrl });
151156
}
152157
}
153158

ts/components/conversation/right-panel/overlay/message-info/components/AttachmentInfo.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { PropsForAttachment } from '../../../../../../state/ducks/conversations'
44
import { Flex } from '../../../../../basic/Flex';
55
import { tr } from '../../../../../../localization/localeTools';
66
import { saveLogToDesktop } from '../../../../../../util/logger/renderer_process_logging';
7+
import { extractDetailsFromUrlFragment } from '../../../../../../session/url';
8+
import { isDevProd } from '../../../../../../shared/env_vars';
79

810
type Props = {
911
attachment: PropsForAttachment;
@@ -16,7 +18,7 @@ const StyledLabelContainer = styled(Flex)`
1618
}
1719
`;
1820

19-
function formatAttachmentUrl(attachment: PropsForAttachment) {
21+
function formatAttachmentUrl(attachment: Pick<PropsForAttachment, 'url'>) {
2022
// Note: desktop overwrites the url with the local path once the file is downloaded,
2123
// and I think this is how we know the file was downloaded.
2224

@@ -38,12 +40,23 @@ function formatAttachmentUrl(attachment: PropsForAttachment) {
3840
return fileId;
3941
}
4042

43+
function extractAttachmentDetails(attachment: Pick<PropsForAttachment, 'url'>) {
44+
const fileUrl = URL.canParse(attachment?.url) && new URL(attachment.url);
45+
return {
46+
deterministicEncryption:
47+
(fileUrl && extractDetailsFromUrlFragment(fileUrl)?.deterministicEncryption) || false,
48+
fsHost: fileUrl ? fileUrl.hostname : tr('attachmentsNa'),
49+
};
50+
}
51+
4152
export const AttachmentInfo = (props: Props) => {
4253
const { attachment } = props;
4354

4455
// NOTE the attachment.url will be an empty string if the attachment is broken
4556
const hasError = attachment.error || attachment.url === '';
4657

58+
const { deterministicEncryption, fsHost } = extractAttachmentDetails(attachment);
59+
4760
return (
4861
<Flex $container={true} $flexDirection="column" $flexGap="var(--margins-xs)">
4962
<LabelWithInfo label={tr('attachmentsFileId')} info={formatAttachmentUrl(attachment)} />
@@ -79,6 +92,15 @@ export const AttachmentInfo = (props: Props) => {
7992
}}
8093
/>
8194
) : null}
95+
{isDevProd() ? (
96+
<>
97+
<LabelWithInfo
98+
label="Uses Deterministic Encryption"
99+
info={deterministicEncryption ? 'Yes' : 'No'}
100+
/>
101+
<LabelWithInfo label="Fs host" info={fsHost} />
102+
</>
103+
) : null}
82104
</StyledLabelContainer>
83105
</Flex>
84106
);

ts/interactions/avatar-interactions/nts-avatar-interactions.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
UserConfigWrapperActions,
1111
} from '../../webworker/workers/browser/libsession_worker_interface';
1212
import { UserUtils } from '../../session/utils';
13-
import { fromBase64ToArray } from '../../session/utils/String';
13+
import { fromHexToArray } from '../../session/utils/String';
1414

1515
export async function uploadAndSetOurAvatarShared({
1616
decryptedAvatarData,
@@ -43,10 +43,10 @@ export async function uploadAndSetOurAvatarShared({
4343
encryptionKey = encryptedContent.encryptionKey;
4444
} else {
4545
// if this is a reupload, reuse the current profile key. Otherwise generate a new one
46-
const existingProfileKey = ourConvo.getProfileKey();
46+
const existingProfileKeyHex = ourConvo.getProfileKeyHex();
4747
const profileKey =
48-
context === 'reuploadAvatar' && existingProfileKey
49-
? fromBase64ToArray(existingProfileKey)
48+
context === 'reuploadAvatar' && existingProfileKeyHex
49+
? fromHexToArray(existingProfileKeyHex)
5050
: randombytes_buf(32);
5151
encryptedData = await encryptProfile(mainAvatarDetails.outputBuffer, profileKey);
5252
encryptionKey = profileKey;
@@ -103,6 +103,6 @@ export async function uploadAndSetOurAvatarShared({
103103

104104
return {
105105
avatarPointer: ourConvo.getAvatarPointer(),
106-
profileKey: ourConvo.getProfileKey(),
106+
profileKey: ourConvo.getProfileKeyHex(),
107107
};
108108
}

ts/models/conversation.ts

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -215,16 +215,9 @@ type SetSessionProfileReturn = {
215215
* We need to do some extra processing for private actions, as they have a updatedAtSeconds field.
216216
*/
217217
function isSetProfileWithUpdatedAtSeconds<T extends SetSessionProfileDetails>(
218-
_action: T
219-
): _action is Extract<T, { profileUpdatedAtSeconds: number }> {
220-
/**
221-
* We temporarily want to not write the profileUpdatedAtSeconds as we want this behavior to
222-
* be used only once a user has updated their profile picture (and resized it).
223-
*/
224-
window.log.debug('isSetProfileWithUpdatedAtSeconds forced to return false for now');
225-
return false;
226-
227-
// return 'profileUpdatedAtSeconds' in action;
218+
action: T
219+
): action is Extract<T, { profileUpdatedAtSeconds: number }> {
220+
return 'profileUpdatedAtSeconds' in action;
228221
}
229222

230223
/**
@@ -1545,7 +1538,7 @@ export class ConversationModel extends Model<ConversationAttributes> {
15451538
this.getAvatarInProfilePath() ||
15461539
this.getFallbackAvatarInProfilePath() ||
15471540
this.getAvatarPointer() ||
1548-
this.getProfileKey()
1541+
this.getProfileKeyHex()
15491542
) {
15501543
this.set({
15511544
avatarInProfile: undefined,
@@ -1573,7 +1566,7 @@ export class ConversationModel extends Model<ConversationAttributes> {
15731566
: to_hex(newProfile.profileKey);
15741567

15751568
const existingAvatarPointer = this.getAvatarPointer();
1576-
const existingProfileKeyHex = this.getProfileKey();
1569+
const existingProfileKeyHex = this.getProfileKeyHex();
15771570
const hasAvatarInNewProfile = !!newProfile.avatarPointer || !!newProfileKeyHex;
15781571
// if no changes are needed, return early
15791572
if (
@@ -1603,7 +1596,7 @@ export class ConversationModel extends Model<ConversationAttributes> {
16031596
: to_hex(newProfile.profileKey);
16041597

16051598
const existingAvatarPointer = this.getAvatarPointer();
1606-
const existingProfileKeyHex = this.getProfileKey();
1599+
const existingProfileKeyHex = this.getProfileKeyHex();
16071600
const originalAvatar = this.getAvatarInProfilePath();
16081601
const originalFallbackAvatar = this.getFallbackAvatarInProfilePath();
16091602

@@ -1730,7 +1723,7 @@ export class ConversationModel extends Model<ConversationAttributes> {
17301723
* Returns the profile key attributes of this instance.
17311724
* If the attribute is unset, empty, or not a string, returns `undefined`.
17321725
*/
1733-
public getProfileKey(): string | undefined {
1726+
public getProfileKeyHex(): string | undefined {
17341727
const profileKey = this.get('profileKey');
17351728
if (!profileKey || !isString(profileKey)) {
17361729
return undefined;
@@ -1768,11 +1761,11 @@ export class ConversationModel extends Model<ConversationAttributes> {
17681761
}
17691762
const avatarPointer = this.getAvatarPointer() ?? null;
17701763
const displayName = this.getRealSessionUsername() ?? '';
1771-
const profileKey = this.getProfileKey() ?? null;
1764+
const profileKeyHex = this.getProfileKeyHex() ?? null;
17721765
const updatedAtSeconds = this.getProfileUpdatedSeconds();
17731766

17741767
return new OutgoingUserProfile({
1775-
profilePic: { url: avatarPointer, key: profileKey ? from_hex(profileKey) : null },
1768+
profilePic: { url: avatarPointer, key: profileKeyHex ? from_hex(profileKeyHex) : null },
17761769
displayName,
17771770
updatedAtSeconds,
17781771
});

ts/session/apis/file_server_api/FileServerApi.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ import { FS, type FILE_SERVER_TARGET_TYPE } from './FileServerTarget';
2020
const RELEASE_VERSION_ENDPOINT = '/session_version';
2121
const FILE_ENDPOINT = '/file';
2222

23+
function getShortTTLHeadersIfNeeded(): Record<string, string> {
24+
if (window.sessionFeatureFlags?.fsTTL30s) {
25+
return { 'X-FS-TTL': '30' };
26+
}
27+
return {};
28+
}
29+
2330
/**
2431
* Upload a file to the file server v2 using the onion v4 encoding
2532
* @param fileContent the data to send
@@ -48,7 +55,7 @@ export const uploadFileToFsWithOnionV4 = async (
4855
endpoint: FILE_ENDPOINT,
4956
method: 'POST',
5057
timeoutMs: 30 * DURATION.SECONDS, // longer time for file upload
51-
headers: window.sessionFeatureFlags.fsTTL30s ? { 'X-FS-TTL': '30' } : {},
58+
headers: getShortTTLHeadersIfNeeded(),
5259
});
5360

5461
if (!batchGlobalIsSuccess(result)) {
@@ -70,11 +77,15 @@ export const uploadFileToFsWithOnionV4 = async (
7077

7178
// we now have the `fileUrl` provide the `serverPubkey` and the deterministic flag as an url fragment.
7279
const urlParams = new URLSearchParams();
73-
urlParams.set(queryParamServerEd25519Pubkey, FS.FILE_SERVERS[target].edPk);
80+
// Note: we don't want to set the pk for the default FS (it breaks prod builds on mobile)
81+
if (target !== 'DEFAULT') {
82+
urlParams.set(queryParamServerEd25519Pubkey, FS.FILE_SERVERS[target].edPk);
83+
}
7484
if (deterministicEncryption) {
7585
urlParams.set(queryParamDeterministicEncryption, '');
7686
}
77-
const fileUrl = `${FS.FILE_SERVERS[target].url}${FILE_ENDPOINT}/${fileId}#${urlParams.toString()}`;
87+
const urlParamStr = urlParams.toString();
88+
const fileUrl = `${FS.FILE_SERVERS[target].url}${FILE_ENDPOINT}/${fileId}${urlParamStr ? `#${urlParamStr}` : ''}`;
7889
const expiresMs = Math.floor(expires * 1000);
7990
return {
8091
fileUrl,
@@ -193,30 +204,24 @@ export const getLatestReleaseFromFileServer = async (
193204
*
194205
*/
195206
export const extendFileExpiry = async (fileId: string, fsTarget: FILE_SERVER_TARGET_TYPE) => {
196-
// TODO: remove this once QA is done
197-
198-
if (!FS.supportsFsExtend(fsTarget)) {
199-
throw new Error('extendFileExpiry: only works with potato for now');
200-
}
201207
if (window.sessionFeatureFlags?.debugServerRequests) {
202208
window.log.info(`about to renew expiry of file: "${fileId}"`);
203209
}
204210

205211
const method = 'POST';
206212
const endpoint = `/file/${fileId}/extend`;
207-
const params = {
213+
214+
const result = await OnionSending.sendJsonViaOnionV4ToFileServer({
208215
abortSignal: new AbortController().signal,
209216
endpoint,
210217
method,
211218
stringifiedBody: null,
212-
headers: {},
219+
headers: getShortTTLHeadersIfNeeded(),
213220
timeoutMs: 10 * DURATION.SECONDS,
214221
target: fsTarget,
215-
};
216-
217-
const result = await OnionSending.sendJsonViaOnionV4ToFileServer(params);
222+
});
218223

219-
if (!batchGlobalIsSuccess(result) || OnionV4.parseStatusCodeFromV4Request(result) !== 200) {
224+
if (!batchGlobalIsSuccess(result)) {
220225
return null;
221226
}
222227

ts/session/apis/snode_api/swarm_polling_config/SwarmPollingGroupConfig.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ async function scheduleAvatarDownloadJobIfNeeded(groupPk: GroupPubkeyType) {
279279

280280
if (!profileUrl || !profileKeyHex) {
281281
// no avatar set for this group: make sure we also remove the one we might have locally.
282-
if (conversation.getAvatarPointer() || conversation.getProfileKey()) {
282+
if (conversation.getAvatarPointer() || conversation.getProfileKeyHex()) {
283283
await conversation.setSessionProfile({
284284
type: 'resetAvatarGroup',
285285
displayName: null,
@@ -291,7 +291,7 @@ async function scheduleAvatarDownloadJobIfNeeded(groupPk: GroupPubkeyType) {
291291

292292
// here, an avatar for this group is set. First we need to make sure if that's the same as we already have
293293
const prevPointer = conversation.getAvatarPointer();
294-
const prevProfileKey = conversation.getProfileKey();
294+
const prevProfileKey = conversation.getProfileKeyHex();
295295

296296
if (prevPointer !== profileUrl || prevProfileKey !== profileKeyHex) {
297297
// set the avatar for this group, it will be downloaded by the job scheduled below

ts/session/utils/job_runners/jobs/AvatarDownloadJob.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function shouldAddAvatarDownloadJob({ conversationId }: { conversationId:
3636
return false;
3737
}
3838
const prevPointer = conversation.getAvatarPointer();
39-
const profileKey = conversation.getProfileKey();
39+
const profileKey = conversation.getProfileKeyHex();
4040
const hasNoAvatar = isEmpty(prevPointer) || isEmpty(profileKey);
4141

4242
if (hasNoAvatar) {
@@ -111,7 +111,7 @@ class AvatarDownloadJob extends PersistedJob<AvatarDownloadPersistedData> {
111111
}
112112
let changes = false;
113113
const toDownloadPointer = conversation.getAvatarPointer();
114-
const toDownloadProfileKey = conversation.getProfileKey();
114+
const toDownloadProfileKey = conversation.getProfileKeyHex();
115115

116116
// if there is an avatar and profileKey for that user/group ('', null and undefined excluded), download, decrypt and save the avatar locally.
117117
if (toDownloadPointer && toDownloadProfileKey) {

ts/session/utils/job_runners/jobs/AvatarMigrateJob.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class AvatarMigrateJob extends PersistedJob<AvatarMigratePersistedData> {
109109
window.log.warn('AvatarMigrateJob: no avatar pointer found for conversation');
110110
return RunJobResult.Success;
111111
}
112-
const existingProfileKeyHex = conversation.getProfileKey();
112+
const existingProfileKeyHex = conversation.getProfileKeyHex();
113113
if (!existingProfileKeyHex) {
114114
window.log.warn('AvatarMigrateJob: no profileKey found for conversation');
115115
return RunJobResult.Success;

0 commit comments

Comments
 (0)