Add timestamp utilities with helpful names
This commit is contained in:
parent
9fa3359477
commit
a75402d290
8 changed files with 71 additions and 17 deletions
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import { fromEncodedBinaryToArrayBuffer, constantTimeEqual } from './Crypto';
|
import { fromEncodedBinaryToArrayBuffer, constantTimeEqual } from './Crypto';
|
||||||
import { isNotNil } from './util/isNotNil';
|
import { isNotNil } from './util/isNotNil';
|
||||||
|
import { isMoreRecentThan } from './util/timestamp';
|
||||||
|
|
||||||
const TIMESTAMP_THRESHOLD = 5 * 1000; // 5 seconds
|
const TIMESTAMP_THRESHOLD = 5 * 1000; // 5 seconds
|
||||||
const Direction = {
|
const Direction = {
|
||||||
|
@ -839,7 +840,7 @@ export class SignalProtocolStore extends EventsMixin {
|
||||||
isNonBlockingApprovalRequired(identityRecord: IdentityKeyType): boolean {
|
isNonBlockingApprovalRequired(identityRecord: IdentityKeyType): boolean {
|
||||||
return (
|
return (
|
||||||
!identityRecord.firstUse &&
|
!identityRecord.firstUse &&
|
||||||
Date.now() - identityRecord.timestamp < TIMESTAMP_THRESHOLD &&
|
isMoreRecentThan(identityRecord.timestamp, TIMESTAMP_THRESHOLD) &&
|
||||||
!identityRecord.nonblockingApproval
|
!identityRecord.nonblockingApproval
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1138,7 +1139,7 @@ export class SignalProtocolStore extends EventsMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
Date.now() - identityRecord.timestamp < TIMESTAMP_THRESHOLD &&
|
isMoreRecentThan(identityRecord.timestamp, TIMESTAMP_THRESHOLD) &&
|
||||||
!identityRecord.nonblockingApproval &&
|
!identityRecord.nonblockingApproval &&
|
||||||
!identityRecord.firstUse
|
!identityRecord.firstUse
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -8,6 +8,9 @@ import { getTitleBarVisibility, TitleBarVisibility } from './types/Settings';
|
||||||
import { isWindowDragElement } from './util/isWindowDragElement';
|
import { isWindowDragElement } from './util/isWindowDragElement';
|
||||||
import { assert } from './util/assert';
|
import { assert } from './util/assert';
|
||||||
import { routineProfileRefresh } from './routineProfileRefresh';
|
import { routineProfileRefresh } from './routineProfileRefresh';
|
||||||
|
import { isMoreRecentThan, isOlderThan } from './util/timestamp';
|
||||||
|
|
||||||
|
const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;
|
||||||
|
|
||||||
export async function startApp(): Promise<void> {
|
export async function startApp(): Promise<void> {
|
||||||
window.startupProcessingQueue = new window.Signal.Util.StartupQueue();
|
window.startupProcessingQueue = new window.Signal.Util.StartupQueue();
|
||||||
|
@ -555,12 +558,11 @@ export async function startApp(): Promise<void> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// How long since we were last running?
|
// How long since we were last running?
|
||||||
const now = Date.now();
|
|
||||||
const lastHeartbeat = window.storage.get('lastHeartbeat');
|
const lastHeartbeat = window.storage.get('lastHeartbeat');
|
||||||
await window.storage.put('lastStartup', Date.now());
|
await window.storage.put('lastStartup', Date.now());
|
||||||
|
|
||||||
const THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000;
|
const THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000;
|
||||||
if (lastHeartbeat > 0 && now - lastHeartbeat > THIRTY_DAYS) {
|
if (lastHeartbeat > 0 && isOlderThan(lastHeartbeat, THIRTY_DAYS)) {
|
||||||
await unlinkAndDisconnect();
|
await unlinkAndDisconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2097,12 +2099,14 @@ export async function startApp(): Promise<void> {
|
||||||
// once we stop processing the queue.
|
// once we stop processing the queue.
|
||||||
window.attachmentDownloadQueue = undefined;
|
window.attachmentDownloadQueue = undefined;
|
||||||
|
|
||||||
const THREE_DAYS_AGO = Date.now() - 3600 * 72 * 1000;
|
|
||||||
const MAX_ATTACHMENT_MSGS_TO_DOWNLOAD = 250;
|
const MAX_ATTACHMENT_MSGS_TO_DOWNLOAD = 250;
|
||||||
const attachmentsToDownload = attachmentDownloadQueue.filter(
|
const attachmentsToDownload = attachmentDownloadQueue.filter(
|
||||||
(message, index) =>
|
(message, index) =>
|
||||||
index <= MAX_ATTACHMENT_MSGS_TO_DOWNLOAD ||
|
index <= MAX_ATTACHMENT_MSGS_TO_DOWNLOAD ||
|
||||||
message.getReceivedAt() > THREE_DAYS_AGO ||
|
isMoreRecentThan(
|
||||||
|
message.getReceivedAt(),
|
||||||
|
MAX_ATTACHMENT_DOWNLOAD_AGE
|
||||||
|
) ||
|
||||||
// Stickers and long text attachments has to be downloaded for UI
|
// Stickers and long text attachments has to be downloaded for UI
|
||||||
// to display the message properly.
|
// to display the message properly.
|
||||||
message.hasRequiredAttachmentDownloads()
|
message.hasRequiredAttachmentDownloads()
|
||||||
|
|
|
@ -8,12 +8,14 @@ import { assert } from './util/assert';
|
||||||
import { missingCaseError } from './util/missingCaseError';
|
import { missingCaseError } from './util/missingCaseError';
|
||||||
import { isNormalNumber } from './util/isNormalNumber';
|
import { isNormalNumber } from './util/isNormalNumber';
|
||||||
import { map, take } from './util/iterables';
|
import { map, take } from './util/iterables';
|
||||||
|
import { isOlderThan } from './util/timestamp';
|
||||||
import { ConversationModel } from './models/conversations';
|
import { ConversationModel } from './models/conversations';
|
||||||
|
|
||||||
const STORAGE_KEY = 'lastAttemptedToRefreshProfilesAt';
|
const STORAGE_KEY = 'lastAttemptedToRefreshProfilesAt';
|
||||||
const MAX_AGE_TO_BE_CONSIDERED_ACTIVE = 30 * 24 * 60 * 60 * 1000;
|
const MAX_AGE_TO_BE_CONSIDERED_ACTIVE = 30 * 24 * 60 * 60 * 1000;
|
||||||
const MAX_AGE_TO_BE_CONSIDERED_RECENTLY_REFRESHED = 1 * 24 * 60 * 60 * 1000;
|
const MAX_AGE_TO_BE_CONSIDERED_RECENTLY_REFRESHED = 1 * 24 * 60 * 60 * 1000;
|
||||||
const MAX_CONVERSATIONS_TO_REFRESH = 50;
|
const MAX_CONVERSATIONS_TO_REFRESH = 50;
|
||||||
|
const MIN_ELAPSED_DURATION_TO_REFRESH_AGAIN = 12 * 3600 * 1000;
|
||||||
|
|
||||||
// This type is a little stricter than what's on `window.storage`, and only requires what
|
// This type is a little stricter than what's on `window.storage`, and only requires what
|
||||||
// we need for easier testing.
|
// we need for easier testing.
|
||||||
|
@ -81,8 +83,7 @@ function hasEnoughTimeElapsedSinceLastRefresh(storage: StorageType): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNormalNumber(storedValue)) {
|
if (isNormalNumber(storedValue)) {
|
||||||
const twelveHoursAgo = Date.now() - 43200000;
|
return isOlderThan(storedValue, MIN_ELAPSED_DURATION_TO_REFRESH_AGAIN);
|
||||||
return storedValue < twelveHoursAgo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(
|
assert(
|
||||||
|
|
37
ts/test-both/util/timestamp_test.ts
Normal file
37
ts/test-both/util/timestamp_test.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { assert } from 'chai';
|
||||||
|
|
||||||
|
import { isOlderThan, isMoreRecentThan } from '../../util/timestamp';
|
||||||
|
|
||||||
|
const ONE_HOUR = 3600 * 1000;
|
||||||
|
const ONE_DAY = 24 * ONE_HOUR;
|
||||||
|
|
||||||
|
describe('timestamp', () => {
|
||||||
|
describe('isOlderThan', () => {
|
||||||
|
it('returns false on recent and future timestamps', () => {
|
||||||
|
assert.isFalse(isOlderThan(Date.now(), ONE_DAY));
|
||||||
|
assert.isFalse(isOlderThan(Date.now() + ONE_DAY, ONE_DAY));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true on old enough timestamps', () => {
|
||||||
|
assert.isFalse(isOlderThan(Date.now() - ONE_DAY + ONE_HOUR, ONE_DAY));
|
||||||
|
assert.isTrue(isOlderThan(Date.now() - ONE_DAY - ONE_HOUR, ONE_DAY));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isMoreRecentThan', () => {
|
||||||
|
it('returns true on recent and future timestamps', () => {
|
||||||
|
assert.isTrue(isMoreRecentThan(Date.now(), ONE_DAY));
|
||||||
|
assert.isTrue(isMoreRecentThan(Date.now() + ONE_DAY, ONE_DAY));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false on old enough timestamps', () => {
|
||||||
|
assert.isTrue(isMoreRecentThan(Date.now() - ONE_DAY + ONE_HOUR, ONE_DAY));
|
||||||
|
assert.isFalse(
|
||||||
|
isMoreRecentThan(Date.now() - ONE_DAY - ONE_HOUR, ONE_DAY)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -17,8 +17,10 @@ import ProvisioningCipher from './ProvisioningCipher';
|
||||||
import WebSocketResource, {
|
import WebSocketResource, {
|
||||||
IncomingWebSocketRequest,
|
IncomingWebSocketRequest,
|
||||||
} from './WebsocketResources';
|
} from './WebsocketResources';
|
||||||
|
import { isMoreRecentThan, isOlderThan } from '../util/timestamp';
|
||||||
|
|
||||||
const ARCHIVE_AGE = 30 * 24 * 60 * 60 * 1000;
|
const ARCHIVE_AGE = 30 * 24 * 60 * 60 * 1000;
|
||||||
|
const PREKEY_ROTATION_AGE = 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
function getIdentifier(id: string) {
|
function getIdentifier(id: string) {
|
||||||
if (!id || !id.length) {
|
if (!id || !id.length) {
|
||||||
|
@ -321,10 +323,9 @@ export default class AccountManager extends EventTarget {
|
||||||
existingKeys.sort((a, b) => (b.created_at || 0) - (a.created_at || 0));
|
existingKeys.sort((a, b) => (b.created_at || 0) - (a.created_at || 0));
|
||||||
const confirmedKeys = existingKeys.filter(key => key.confirmed);
|
const confirmedKeys = existingKeys.filter(key => key.confirmed);
|
||||||
|
|
||||||
const ONE_DAY_AGO = Date.now() - 24 * 60 * 60 * 1000;
|
|
||||||
if (
|
if (
|
||||||
confirmedKeys.length >= 3 &&
|
confirmedKeys.length >= 3 &&
|
||||||
confirmedKeys[0].created_at > ONE_DAY_AGO
|
isMoreRecentThan(confirmedKeys[0].created_at, PREKEY_ROTATION_AGE)
|
||||||
) {
|
) {
|
||||||
window.log.warn(
|
window.log.warn(
|
||||||
'rotateSignedPreKey: 3+ confirmed keys, most recent is less than a day old. Cancelling rotation.'
|
'rotateSignedPreKey: 3+ confirmed keys, most recent is less than a day old. Cancelling rotation.'
|
||||||
|
@ -437,9 +438,8 @@ export default class AccountManager extends EventTarget {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const createdAt = key.created_at || 0;
|
const createdAt = key.created_at || 0;
|
||||||
const age = Date.now() - createdAt;
|
|
||||||
|
|
||||||
if (age > ARCHIVE_AGE) {
|
if (isOlderThan(createdAt, ARCHIVE_AGE)) {
|
||||||
window.log.info(
|
window.log.info(
|
||||||
'Removing confirmed signed prekey:',
|
'Removing confirmed signed prekey:',
|
||||||
key.keyId,
|
key.keyId,
|
||||||
|
@ -463,8 +463,7 @@ export default class AccountManager extends EventTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdAt = key.created_at || 0;
|
const createdAt = key.created_at || 0;
|
||||||
const age = Date.now() - createdAt;
|
if (isOlderThan(createdAt, ARCHIVE_AGE)) {
|
||||||
if (age > ARCHIVE_AGE) {
|
|
||||||
window.log.info(
|
window.log.info(
|
||||||
'Removing unconfirmed signed prekey:',
|
'Removing unconfirmed signed prekey:',
|
||||||
key.keyId,
|
key.keyId,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { isMoreRecentThan } from './timestamp';
|
||||||
|
|
||||||
const SIX_HOURS = 1000 * 60 * 60 * 6;
|
const SIX_HOURS = 1000 * 60 * 60 * 6;
|
||||||
|
|
||||||
export function isConversationUnregistered({
|
export function isConversationUnregistered({
|
||||||
|
@ -8,6 +10,6 @@ export function isConversationUnregistered({
|
||||||
}: Readonly<{ discoveredUnregisteredAt?: number }>): boolean {
|
}: Readonly<{ discoveredUnregisteredAt?: number }>): boolean {
|
||||||
return Boolean(
|
return Boolean(
|
||||||
discoveredUnregisteredAt &&
|
discoveredUnregisteredAt &&
|
||||||
discoveredUnregisteredAt > Date.now() - SIX_HOURS
|
isMoreRecentThan(discoveredUnregisteredAt, SIX_HOURS)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14026,7 +14026,7 @@
|
||||||
"rule": "jQuery-load(",
|
"rule": "jQuery-load(",
|
||||||
"path": "ts/LibSignalStore.js",
|
"path": "ts/LibSignalStore.js",
|
||||||
"line": " await window.ConversationController.load();",
|
"line": " await window.ConversationController.load();",
|
||||||
"lineNumber": 810,
|
"lineNumber": 811,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2021-02-27T00:48:49.313Z"
|
"updated": "2021-02-27T00:48:49.313Z"
|
||||||
},
|
},
|
||||||
|
@ -14034,7 +14034,7 @@
|
||||||
"rule": "jQuery-load(",
|
"rule": "jQuery-load(",
|
||||||
"path": "ts/LibSignalStore.ts",
|
"path": "ts/LibSignalStore.ts",
|
||||||
"line": " await window.ConversationController.load();",
|
"line": " await window.ConversationController.load();",
|
||||||
"lineNumber": 1221,
|
"lineNumber": 1222,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2021-02-27T00:48:49.313Z"
|
"updated": "2021-02-27T00:48:49.313Z"
|
||||||
},
|
},
|
||||||
|
|
10
ts/util/timestamp.ts
Normal file
10
ts/util/timestamp.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
export function isMoreRecentThan(timestamp: number, delta: number): boolean {
|
||||||
|
return timestamp > Date.now() - delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isOlderThan(timestamp: number, delta: number): boolean {
|
||||||
|
return timestamp <= Date.now() - delta;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue