Files
Desktop/ts/sql/migrations/1630-message-pin-message-data.std.ts
Jamie 8ca20a37ad Add backup support for pinned messages
Co-authored-by: Scott Nonnenberg <scott@signal.org>
2026-01-12 14:04:23 -08:00

80 lines
2.1 KiB
TypeScript

// Copyright 2026 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { LoggerType } from '../../types/Logging.std.js';
import { isAciString } from '../../util/isAciString.std.js';
import type { WritableDB } from '../Interface.std.js';
import { sql } from '../util.std.js';
type Pin = Readonly<{
pinMessageId: string;
targetAuthorAci: string | null;
targetSentTimestamp: number | null;
}>;
function getAllPins(db: WritableDB): ReadonlyArray<Pin> {
const [query, params] = sql`
SELECT
pin.id AS pinMessageId,
target.sourceServiceId AS targetAuthorAci,
target.sent_at AS targetSentTimestamp
FROM messages AS pin
LEFT JOIN messages AS target
ON pin.json ->> '$.pinnedMessageId' = target.id
WHERE pin.type IS 'pinned-message-notification'
`;
return db.prepare(query).all<Pin>(params);
}
function getReasonCannotUpdate(pin: Pin): string | null {
if (pin.targetSentTimestamp == null) {
return 'target message not found';
}
if (pin.targetAuthorAci == null) {
return 'target message missing sourceServiceId';
}
if (!isAciString(pin.targetAuthorAci)) {
return 'target message sourceServiceId is not aci';
}
return null;
}
function updatePin(db: WritableDB, pin: Pin): void {
const [query, params] = sql`
UPDATE messages
SET json = json_patch(json, ${JSON.stringify({
pinnedMessageId: null,
pinMessage: {
targetAuthorAci: pin.targetAuthorAci,
targetSentTimestamp: pin.targetSentTimestamp,
},
})})
WHERE id = ${pin.pinMessageId};
`;
db.prepare(query).run(params);
}
function deletePin(db: WritableDB, pin: Pin) {
const [query, params] = sql`
DELETE FROM messages
WHERE id = ${pin.pinMessageId};
`;
db.prepare(query).run(params);
}
export default function updateToSchemaVersion1630(
db: WritableDB,
logger: LoggerType
): void {
for (const pin of getAllPins(db)) {
const reason = getReasonCannotUpdate(pin);
if (reason == null) {
updatePin(db, pin);
} else {
deletePin(db, pin);
logger.warn(
`Dropped pin message ${pin.pinMessageId} (reason: ${reason})`
);
}
}
}