mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-04-27 03:43:27 +01:00
Refactor build expiration checks
This commit is contained in:
88
ts/util/buildExpiration.ts
Normal file
88
ts/util/buildExpiration.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { Environment, getEnvironment } from '../environment';
|
||||
import type { LoggerType } from '../types/Logging';
|
||||
import { isNotUpdatable } from './version';
|
||||
import { isInPast } from './timestamp';
|
||||
import { DAY } from './durations';
|
||||
|
||||
const NINETY_ONE_DAYS = 91 * DAY;
|
||||
const THIRTY_ONE_DAYS = 31 * DAY;
|
||||
const SIXTY_DAYS = 60 * DAY;
|
||||
|
||||
export type GetBuildExpirationTimestampOptionsType = Readonly<{
|
||||
version: string;
|
||||
packagedBuildExpiration: number;
|
||||
remoteBuildExpiration: number | undefined;
|
||||
autoDownloadUpdate: boolean;
|
||||
logger: LoggerType;
|
||||
}>;
|
||||
|
||||
export function getBuildExpirationTimestamp({
|
||||
version,
|
||||
packagedBuildExpiration,
|
||||
remoteBuildExpiration,
|
||||
autoDownloadUpdate,
|
||||
logger,
|
||||
}: GetBuildExpirationTimestampOptionsType): number {
|
||||
const localBuildExpiration =
|
||||
isNotUpdatable(version) || autoDownloadUpdate
|
||||
? packagedBuildExpiration
|
||||
: packagedBuildExpiration - SIXTY_DAYS;
|
||||
|
||||
// Log the expiration date in this selector because it invalidates only
|
||||
// if one of the arguments changes.
|
||||
let result: number;
|
||||
let type: string;
|
||||
if (remoteBuildExpiration && remoteBuildExpiration < localBuildExpiration) {
|
||||
type = 'remote';
|
||||
result = remoteBuildExpiration;
|
||||
} else {
|
||||
type = 'local';
|
||||
result = localBuildExpiration;
|
||||
}
|
||||
logger.info(`Build expires (${type}): ${new Date(result).toISOString()}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
export type HasBuildExpiredOptionsType = Readonly<{
|
||||
buildExpirationTimestamp: number;
|
||||
autoDownloadUpdate: boolean;
|
||||
now: number;
|
||||
logger: LoggerType;
|
||||
}>;
|
||||
|
||||
export function hasBuildExpired({
|
||||
buildExpirationTimestamp,
|
||||
autoDownloadUpdate,
|
||||
now,
|
||||
logger,
|
||||
}: HasBuildExpiredOptionsType): boolean {
|
||||
if (
|
||||
getEnvironment() !== Environment.PackagedApp &&
|
||||
buildExpirationTimestamp === 0
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isInPast(buildExpirationTimestamp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const safeExpirationMs = autoDownloadUpdate
|
||||
? NINETY_ONE_DAYS
|
||||
: THIRTY_ONE_DAYS;
|
||||
|
||||
const buildExpirationDuration = buildExpirationTimestamp - now;
|
||||
const tooFarIntoFuture = buildExpirationDuration > safeExpirationMs;
|
||||
|
||||
if (tooFarIntoFuture) {
|
||||
logger.error(
|
||||
'Build expiration is set too far into the future',
|
||||
buildExpirationTimestamp
|
||||
);
|
||||
}
|
||||
|
||||
return tooFarIntoFuture || isInPast(buildExpirationTimestamp);
|
||||
}
|
||||
@@ -34,3 +34,54 @@ export function safeSetTimeout(
|
||||
|
||||
return setTimeout(callback, delayMs);
|
||||
}
|
||||
|
||||
// Set timeout for a delay that might be longer than MAX_SAFE_TIMEOUT_DELAY. The
|
||||
// callback is guaranteed to execute after desired delay.
|
||||
export class LongTimeout {
|
||||
#callback: VoidFunction;
|
||||
#fireTime: number;
|
||||
#timer: NodeJS.Timeout | undefined;
|
||||
|
||||
constructor(callback: VoidFunction, providedDelayMs: number) {
|
||||
let delayMs = providedDelayMs;
|
||||
|
||||
if (delayMs < 0) {
|
||||
logging.warn('safeSetTimeout: timeout is less than zero');
|
||||
delayMs = 0;
|
||||
}
|
||||
if (Number.isNaN(delayMs)) {
|
||||
throw new Error('NaN delayMs');
|
||||
}
|
||||
if (!Number.isFinite(delayMs)) {
|
||||
throw new Error('Infinite delayMs');
|
||||
}
|
||||
|
||||
this.#callback = callback;
|
||||
this.#fireTime = Date.now() + delayMs;
|
||||
this.#schedule();
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
if (this.#timer != null) {
|
||||
clearTimeout(this.#timer);
|
||||
}
|
||||
this.#timer = undefined;
|
||||
}
|
||||
|
||||
#schedule(): void {
|
||||
const remainingMs = this.#fireTime - Date.now();
|
||||
if (remainingMs <= MAX_SAFE_TIMEOUT_DELAY) {
|
||||
this.#timer = setTimeout(() => this.#fire(), remainingMs);
|
||||
return;
|
||||
}
|
||||
|
||||
this.#timer = setTimeout(() => {
|
||||
this.#schedule();
|
||||
}, MAX_SAFE_TIMEOUT_DELAY);
|
||||
}
|
||||
|
||||
#fire(): void {
|
||||
this.clear();
|
||||
this.#callback();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user