Use storage service for call links
This commit is contained in:
parent
50447b7686
commit
5a75246e42
23 changed files with 583 additions and 50 deletions
|
@ -31,12 +31,17 @@ import type {
|
|||
CallHistoryPagination,
|
||||
CallLogEventTarget,
|
||||
} from '../types/CallDisposition';
|
||||
import type { CallLinkStateType, CallLinkType } from '../types/CallLink';
|
||||
import type {
|
||||
CallLinkRecord,
|
||||
CallLinkStateType,
|
||||
CallLinkType,
|
||||
} from '../types/CallLink';
|
||||
import type { AttachmentDownloadJobType } from '../types/AttachmentDownload';
|
||||
import type { GroupSendEndorsementsData } from '../types/GroupSendEndorsements';
|
||||
import type { SyncTaskType } from '../util/syncTasks';
|
||||
import type { AttachmentBackupJobType } from '../types/AttachmentBackup';
|
||||
import type { SingleProtoJobQueue } from '../jobs/singleProtoJobQueue';
|
||||
import type { DeleteCallLinkOptions } from './server/callLinks';
|
||||
|
||||
export type ReadableDB = Database & { __readable_db: never };
|
||||
export type WritableDB = ReadableDB & { __writable_db: never };
|
||||
|
@ -568,6 +573,8 @@ type ReadableInterface = {
|
|||
callLinkExists(roomId: string): boolean;
|
||||
getAllCallLinks: () => ReadonlyArray<CallLinkType>;
|
||||
getCallLinkByRoomId: (roomId: string) => CallLinkType | undefined;
|
||||
getCallLinkRecordByRoomId: (roomId: string) => CallLinkRecord | undefined;
|
||||
getAllCallLinkRecordsWithAdminKey(): ReadonlyArray<CallLinkRecord>;
|
||||
getAllMarkedDeletedCallLinks(): ReadonlyArray<CallLinkType>;
|
||||
getMessagesBetween: (
|
||||
conversationId: string,
|
||||
|
@ -799,13 +806,14 @@ type WritableInterface = {
|
|||
markCallHistoryMissed(callIds: ReadonlyArray<string>): void;
|
||||
getRecentStaleRingsAndMarkOlderMissed(): ReadonlyArray<MaybeStaleCallHistory>;
|
||||
insertCallLink(callLink: CallLinkType): void;
|
||||
updateCallLink(callLink: CallLinkType): void;
|
||||
updateCallLinkAdminKeyByRoomId(roomId: string, adminKey: string): void;
|
||||
updateCallLinkState(
|
||||
roomId: string,
|
||||
callLinkState: CallLinkStateType
|
||||
): CallLinkType;
|
||||
beginDeleteAllCallLinks(): void;
|
||||
beginDeleteCallLink(roomId: string): void;
|
||||
beginDeleteCallLink(roomId: string, options: DeleteCallLinkOptions): void;
|
||||
finalizeDeleteCallLink(roomId: string): void;
|
||||
_removeAllCallLinks(): void;
|
||||
deleteCallLinkFromSync(roomId: string): void;
|
||||
|
|
|
@ -174,10 +174,13 @@ import {
|
|||
callLinkExists,
|
||||
getAllCallLinks,
|
||||
getCallLinkByRoomId,
|
||||
getCallLinkRecordByRoomId,
|
||||
insertCallLink,
|
||||
updateCallLink,
|
||||
updateCallLinkAdminKeyByRoomId,
|
||||
updateCallLinkState,
|
||||
beginDeleteAllCallLinks,
|
||||
getAllCallLinkRecordsWithAdminKey,
|
||||
getAllMarkedDeletedCallLinks,
|
||||
finalizeDeleteCallLink,
|
||||
beginDeleteCallLink,
|
||||
|
@ -304,6 +307,8 @@ export const DataReader: ServerReadableInterface = {
|
|||
callLinkExists,
|
||||
getAllCallLinks,
|
||||
getCallLinkByRoomId,
|
||||
getCallLinkRecordByRoomId,
|
||||
getAllCallLinkRecordsWithAdminKey,
|
||||
getAllMarkedDeletedCallLinks,
|
||||
getMessagesBetween,
|
||||
getNearbyMessageFromDeletedSet,
|
||||
|
@ -439,6 +444,7 @@ export const DataWriter: ServerWritableInterface = {
|
|||
saveCallHistory,
|
||||
markCallHistoryMissed,
|
||||
insertCallLink,
|
||||
updateCallLink,
|
||||
updateCallLinkAdminKeyByRoomId,
|
||||
updateCallLinkState,
|
||||
beginDeleteAllCallLinks,
|
||||
|
@ -6445,6 +6451,14 @@ function eraseStorageServiceState(db: WritableDB): void {
|
|||
storageVersion = null,
|
||||
storageUnknownFields = null,
|
||||
storageNeedsSync = 0;
|
||||
|
||||
-- Call links
|
||||
UPDATE callLinks
|
||||
SET
|
||||
storageID = null,
|
||||
storageVersion = null,
|
||||
storageUnknownFields = null,
|
||||
storageNeedsSync = 0;
|
||||
`);
|
||||
}
|
||||
|
||||
|
|
38
ts/sql/migrations/1190-call-links-storage.ts
Normal file
38
ts/sql/migrations/1190-call-links-storage.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
import type { Database } from '@signalapp/better-sqlite3';
|
||||
import type { LoggerType } from '../../types/Logging';
|
||||
|
||||
export const version = 1190;
|
||||
|
||||
export function updateToSchemaVersion1190(
|
||||
currentVersion: number,
|
||||
db: Database,
|
||||
logger: LoggerType
|
||||
): void {
|
||||
if (currentVersion >= 1190) {
|
||||
return;
|
||||
}
|
||||
|
||||
db.transaction(() => {
|
||||
db.exec(`
|
||||
ALTER TABLE callLinks ADD COLUMN storageID TEXT;
|
||||
ALTER TABLE callLinks ADD COLUMN storageVersion INTEGER;
|
||||
ALTER TABLE callLinks ADD COLUMN storageUnknownFields BLOB;
|
||||
ALTER TABLE callLinks ADD COLUMN storageNeedsSync INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE callLinks ADD COLUMN deletedAt INTEGER;
|
||||
`);
|
||||
db.prepare(
|
||||
`
|
||||
UPDATE callLinks
|
||||
SET deletedAt = $deletedAt
|
||||
WHERE deleted = 1;
|
||||
`
|
||||
).run({
|
||||
deletedAt: new Date().getTime(),
|
||||
});
|
||||
|
||||
db.pragma('user_version = 1190');
|
||||
})();
|
||||
logger.info('updateToSchemaVersion1190: success!');
|
||||
}
|
|
@ -94,10 +94,11 @@ import { updateToSchemaVersion1140 } from './1140-call-links-deleted-column';
|
|||
import { updateToSchemaVersion1150 } from './1150-expire-timer-version';
|
||||
import { updateToSchemaVersion1160 } from './1160-optimize-calls-unread-count';
|
||||
import { updateToSchemaVersion1170 } from './1170-update-call-history-unread-index';
|
||||
import { updateToSchemaVersion1180 } from './1180-add-attachment-download-source';
|
||||
import {
|
||||
updateToSchemaVersion1180,
|
||||
updateToSchemaVersion1190,
|
||||
version as MAX_VERSION,
|
||||
} from './1180-add-attachment-download-source';
|
||||
} from './1190-call-links-storage';
|
||||
|
||||
function updateToSchemaVersion1(
|
||||
currentVersion: number,
|
||||
|
@ -2060,6 +2061,7 @@ export const SCHEMA_VERSIONS = [
|
|||
updateToSchemaVersion1160,
|
||||
updateToSchemaVersion1170,
|
||||
updateToSchemaVersion1180,
|
||||
updateToSchemaVersion1190,
|
||||
];
|
||||
|
||||
export class DBVersionFromFutureError extends Error {
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { CallLinkRootKey } from '@signalapp/ringrtc';
|
||||
import type { CallLinkStateType, CallLinkType } from '../../types/CallLink';
|
||||
import type {
|
||||
CallLinkRecord,
|
||||
CallLinkStateType,
|
||||
CallLinkType,
|
||||
} from '../../types/CallLink';
|
||||
import {
|
||||
callLinkRestrictionsSchema,
|
||||
callLinkRecordSchema,
|
||||
|
@ -31,6 +35,19 @@ export function getCallLinkByRoomId(
|
|||
db: ReadableDB,
|
||||
roomId: string
|
||||
): CallLinkType | undefined {
|
||||
const callLinkRecord = getCallLinkRecordByRoomId(db, roomId);
|
||||
if (!callLinkRecord) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return callLinkFromRecord(callLinkRecord);
|
||||
}
|
||||
|
||||
// When you need to access all the fields (such as deleted and storage fields)
|
||||
export function getCallLinkRecordByRoomId(
|
||||
db: ReadableDB,
|
||||
roomId: string
|
||||
): CallLinkRecord | undefined {
|
||||
const row = prepare(db, 'SELECT * FROM callLinks WHERE roomId = $roomId').get(
|
||||
{
|
||||
roomId,
|
||||
|
@ -41,7 +58,7 @@ export function getCallLinkByRoomId(
|
|||
return undefined;
|
||||
}
|
||||
|
||||
return callLinkFromRecord(callLinkRecordSchema.parse(row));
|
||||
return callLinkRecordSchema.parse(row);
|
||||
}
|
||||
|
||||
export function getAllCallLinks(db: ReadableDB): ReadonlyArray<CallLinkType> {
|
||||
|
@ -69,7 +86,11 @@ function _insertCallLink(db: WritableDB, callLink: CallLinkType): void {
|
|||
name,
|
||||
restrictions,
|
||||
revoked,
|
||||
expiration
|
||||
expiration,
|
||||
storageID,
|
||||
storageVersion,
|
||||
storageUnknownFields,
|
||||
storageNeedsSync
|
||||
) VALUES (
|
||||
$roomId,
|
||||
$rootKey,
|
||||
|
@ -77,7 +98,11 @@ function _insertCallLink(db: WritableDB, callLink: CallLinkType): void {
|
|||
$name,
|
||||
$restrictions,
|
||||
$revoked,
|
||||
$expiration
|
||||
$expiration,
|
||||
$storageID,
|
||||
$storageVersion,
|
||||
$storageUnknownFields,
|
||||
$storageNeedsSync
|
||||
)
|
||||
`
|
||||
).run(data);
|
||||
|
@ -87,6 +112,30 @@ export function insertCallLink(db: WritableDB, callLink: CallLinkType): void {
|
|||
_insertCallLink(db, callLink);
|
||||
}
|
||||
|
||||
export function updateCallLink(db: WritableDB, callLink: CallLinkType): void {
|
||||
const { roomId, rootKey } = callLink;
|
||||
assertRoomIdMatchesRootKey(roomId, rootKey);
|
||||
|
||||
const data = callLinkToRecord(callLink);
|
||||
// Do not write roomId or rootKey since they should never change
|
||||
db.prepare(
|
||||
`
|
||||
UPDATE callLinks
|
||||
SET
|
||||
adminKey = $adminKey,
|
||||
name = $name,
|
||||
restrictions = $restrictions,
|
||||
revoked = $revoked,
|
||||
expiration = $expiration,
|
||||
storageID = $storageID,
|
||||
storageVersion = $storageVersion,
|
||||
storageUnknownFields = $storageUnknownFields,
|
||||
storageNeedsSync = $storageNeedsSync
|
||||
WHERE roomId = $roomId
|
||||
`
|
||||
).run(data);
|
||||
}
|
||||
|
||||
export function updateCallLinkState(
|
||||
db: WritableDB,
|
||||
roomId: string,
|
||||
|
@ -167,7 +216,16 @@ export function deleteCallLinkFromSync(db: WritableDB, roomId: string): void {
|
|||
})();
|
||||
}
|
||||
|
||||
export function beginDeleteCallLink(db: WritableDB, roomId: string): void {
|
||||
export type DeleteCallLinkOptions = {
|
||||
storageNeedsSync: boolean;
|
||||
deletedAt?: number;
|
||||
};
|
||||
|
||||
export function beginDeleteCallLink(
|
||||
db: WritableDB,
|
||||
roomId: string,
|
||||
options: DeleteCallLinkOptions
|
||||
): void {
|
||||
db.transaction(() => {
|
||||
// If adminKey is null, then we should delete the call link
|
||||
const [deleteNonAdminCallLinksQuery, deleteNonAdminCallLinksParams] = sql`
|
||||
|
@ -182,11 +240,17 @@ export function beginDeleteCallLink(db: WritableDB, roomId: string): void {
|
|||
|
||||
// Skip this query if the call is already deleted
|
||||
if (result.changes === 0) {
|
||||
const { storageNeedsSync } = options;
|
||||
const deletedAt = options.deletedAt ?? new Date().getTime();
|
||||
|
||||
// If the admin key is not null, we should mark it for deletion
|
||||
const [markAdminCallLinksDeletedQuery, markAdminCallLinksDeletedParams] =
|
||||
sql`
|
||||
UPDATE callLinks
|
||||
SET deleted = 1
|
||||
SET
|
||||
deleted = 1,
|
||||
deletedAt = ${deletedAt},
|
||||
storageNeedsSync = ${storageNeedsSync ? 1 : 0}
|
||||
WHERE adminKey IS NOT NULL
|
||||
AND roomId = ${roomId};
|
||||
`;
|
||||
|
@ -201,14 +265,21 @@ export function beginDeleteCallLink(db: WritableDB, roomId: string): void {
|
|||
}
|
||||
|
||||
export function beginDeleteAllCallLinks(db: WritableDB): void {
|
||||
const deletedAt = new Date().getTime();
|
||||
db.transaction(() => {
|
||||
const [markAdminCallLinksDeletedQuery] = sql`
|
||||
const [markAdminCallLinksDeletedQuery, markAdminCallLinksDeletedParams] =
|
||||
sql`
|
||||
UPDATE callLinks
|
||||
SET deleted = 1
|
||||
SET
|
||||
deleted = 1,
|
||||
deletedAt = ${deletedAt},
|
||||
storageNeedsSync = 1
|
||||
WHERE adminKey IS NOT NULL;
|
||||
`;
|
||||
|
||||
db.prepare(markAdminCallLinksDeletedQuery).run();
|
||||
db.prepare(markAdminCallLinksDeletedQuery).run(
|
||||
markAdminCallLinksDeletedParams
|
||||
);
|
||||
|
||||
const [deleteNonAdminCallLinksQuery] = sql`
|
||||
DELETE FROM callLinks
|
||||
|
@ -219,6 +290,21 @@ export function beginDeleteAllCallLinks(db: WritableDB): void {
|
|||
})();
|
||||
}
|
||||
|
||||
// When you need to access the deleted field
|
||||
export function getAllCallLinkRecordsWithAdminKey(
|
||||
db: ReadableDB
|
||||
): ReadonlyArray<CallLinkRecord> {
|
||||
const [query] = sql`
|
||||
SELECT * FROM callLinks
|
||||
WHERE adminKey IS NOT NULL
|
||||
AND rootKey IS NOT NULL;
|
||||
`;
|
||||
return db
|
||||
.prepare(query)
|
||||
.all()
|
||||
.map(item => callLinkRecordSchema.parse(item));
|
||||
}
|
||||
|
||||
export function getAllMarkedDeletedCallLinks(
|
||||
db: ReadableDB
|
||||
): ReadonlyArray<CallLinkType> {
|
||||
|
@ -231,9 +317,13 @@ export function getAllMarkedDeletedCallLinks(
|
|||
.map(item => callLinkFromRecord(callLinkRecordSchema.parse(item)));
|
||||
}
|
||||
|
||||
// TODO: Run this after uploading storage records, maybe periodically on startup
|
||||
export function finalizeDeleteCallLink(db: WritableDB, roomId: string): void {
|
||||
const [query, params] = sql`
|
||||
DELETE FROM callLinks WHERE roomId = ${roomId} AND deleted = 1;
|
||||
DELETE FROM callLinks
|
||||
WHERE roomId = ${roomId}
|
||||
AND deleted = 1
|
||||
AND storageNeedsSync = 0;
|
||||
`;
|
||||
db.prepare(query).run(params);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue