mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-04-24 18:38:15 +01:00
Initial donationReceipts data types
This commit is contained in:
@@ -3,6 +3,9 @@
|
||||
|
||||
import type { Database } from '@signalapp/sqlcipher';
|
||||
import type { ReadonlyDeep } from 'type-fest';
|
||||
|
||||
import { strictAssert } from '../util/assert';
|
||||
|
||||
import type {
|
||||
ConversationAttributesType,
|
||||
MessageAttributesType,
|
||||
@@ -49,7 +52,7 @@ import type { SyncTaskType } from '../util/syncTasks';
|
||||
import type { AttachmentBackupJobType } from '../types/AttachmentBackup';
|
||||
import type { GifType } from '../components/fun/panels/FunPanelGifs';
|
||||
import type { NotificationProfileType } from '../types/NotificationProfile';
|
||||
import { strictAssert } from '../util/assert';
|
||||
import type { DonationReceipt } from '../types/Donations';
|
||||
|
||||
export type ReadableDB = Database & { __readable_db: never };
|
||||
export type WritableDB = ReadableDB & { __writable_db: never };
|
||||
@@ -876,6 +879,9 @@ type ReadableInterface = {
|
||||
getAllNotificationProfiles(): Array<NotificationProfileType>;
|
||||
getNotificationProfileById(id: string): NotificationProfileType | undefined;
|
||||
|
||||
getAllDonationReceipts(): Array<DonationReceipt>;
|
||||
getDonationReceiptById(id: string): DonationReceipt | undefined;
|
||||
|
||||
getMessagesNeedingUpgrade: (
|
||||
limit: number,
|
||||
options: { maxVersion: number }
|
||||
@@ -1185,6 +1191,10 @@ type WritableInterface = {
|
||||
createNotificationProfile(profile: NotificationProfileType): void;
|
||||
updateNotificationProfile(profile: NotificationProfileType): void;
|
||||
|
||||
_deleteAllDonationReceipts(): void;
|
||||
deleteDonationReceiptById(id: string): void;
|
||||
createDonationReceipt(profile: DonationReceipt): void;
|
||||
|
||||
removeAll: () => void;
|
||||
removeAllConfiguration: () => void;
|
||||
eraseStorageServiceState: () => void;
|
||||
|
||||
@@ -218,6 +218,13 @@ import {
|
||||
updateCallLinkState,
|
||||
updateDefunctCallLink,
|
||||
} from './server/callLinks';
|
||||
import {
|
||||
_deleteAllDonationReceipts,
|
||||
createDonationReceipt,
|
||||
deleteDonationReceiptById,
|
||||
getAllDonationReceipts,
|
||||
getDonationReceiptById,
|
||||
} from './server/donationReceipts';
|
||||
import {
|
||||
deleteAllEndorsementsForGroup,
|
||||
getGroupSendCombinedEndorsementExpiration,
|
||||
@@ -391,6 +398,9 @@ export const DataReader: ServerReadableInterface = {
|
||||
getAllNotificationProfiles,
|
||||
getNotificationProfileById,
|
||||
|
||||
getAllDonationReceipts,
|
||||
getDonationReceiptById,
|
||||
|
||||
callLinkExists,
|
||||
defunctCallLinkExists,
|
||||
getAllCallLinks,
|
||||
@@ -624,6 +634,10 @@ export const DataWriter: ServerWritableInterface = {
|
||||
markNotificationProfileDeleted,
|
||||
updateNotificationProfile,
|
||||
|
||||
_deleteAllDonationReceipts,
|
||||
deleteDonationReceiptById,
|
||||
createDonationReceipt,
|
||||
|
||||
removeAll,
|
||||
removeAllConfiguration,
|
||||
eraseStorageServiceState,
|
||||
@@ -7495,6 +7509,7 @@ function removeAll(db: WritableDB): void {
|
||||
DELETE FROM callsHistory;
|
||||
DELETE FROM conversations;
|
||||
DELETE FROM defunctCallLinks;
|
||||
DELETE FROM donationReceipts;
|
||||
DELETE FROM emojis;
|
||||
DELETE FROM groupCallRingCancellations;
|
||||
DELETE FROM groupSendCombinedEndorsement;
|
||||
|
||||
35
ts/sql/migrations/1380-donation-receipts.ts
Normal file
35
ts/sql/migrations/1380-donation-receipts.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { LoggerType } from '../../types/Logging';
|
||||
import type { WritableDB } from '../Interface';
|
||||
|
||||
export const version = 1380;
|
||||
|
||||
export function updateToSchemaVersion1380(
|
||||
currentVersion: number,
|
||||
db: WritableDB,
|
||||
logger: LoggerType
|
||||
): void {
|
||||
if (currentVersion >= 1380) {
|
||||
return;
|
||||
}
|
||||
|
||||
db.transaction(() => {
|
||||
db.exec(`
|
||||
CREATE TABLE donationReceipts(
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
currencyType TEXT NOT NULL,
|
||||
paymentAmount INTEGER NOT NULL,
|
||||
paymentDetailJson TEXT NOT NULL,
|
||||
paymentType TEXT NOT NULL,
|
||||
timestamp INTEGER NOT NULL
|
||||
) STRICT;
|
||||
|
||||
CREATE INDEX donationReceipts_byTimestamp on donationReceipts(timestamp);
|
||||
`);
|
||||
db.pragma('user_version = 1380');
|
||||
})();
|
||||
|
||||
logger.info('updateToSchemaVersion1380: success!');
|
||||
}
|
||||
@@ -112,10 +112,11 @@ import { updateToSchemaVersion1330 } from './1330-sync-tasks-type-index';
|
||||
import { updateToSchemaVersion1340 } from './1340-recent-gifs';
|
||||
import { updateToSchemaVersion1350 } from './1350-notification-profiles';
|
||||
import { updateToSchemaVersion1360 } from './1360-attachments';
|
||||
import { updateToSchemaVersion1370 } from './1370-message-attachment-indexes';
|
||||
import {
|
||||
updateToSchemaVersion1370,
|
||||
updateToSchemaVersion1380,
|
||||
version as MAX_VERSION,
|
||||
} from './1370-message-attachment-indexes';
|
||||
} from './1380-donation-receipts';
|
||||
|
||||
import { DataWriter } from '../Server';
|
||||
|
||||
@@ -2106,6 +2107,7 @@ export const SCHEMA_VERSIONS = [
|
||||
updateToSchemaVersion1350,
|
||||
updateToSchemaVersion1360,
|
||||
updateToSchemaVersion1370,
|
||||
updateToSchemaVersion1380,
|
||||
];
|
||||
|
||||
export class DBVersionFromFutureError extends Error {
|
||||
|
||||
108
ts/sql/server/donationReceipts.ts
Normal file
108
ts/sql/server/donationReceipts.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { omit } from 'lodash';
|
||||
|
||||
import * as Errors from '../../types/errors';
|
||||
import { safeParseLoose } from '../../util/schemas';
|
||||
import { sql } from '../util';
|
||||
import { sqlLogger } from '../sqlLogger';
|
||||
import { donationReceiptSchema } from '../../types/Donations';
|
||||
|
||||
import type { DonationReceipt } from '../../types/Donations';
|
||||
import type { ReadableDB, WritableDB } from '../Interface';
|
||||
|
||||
type DonationReceiptForDatabase = Readonly<
|
||||
{
|
||||
paymentDetailJson: string;
|
||||
paymentType: string;
|
||||
} & Omit<DonationReceipt, 'paymentType' | 'paymentDetail'>
|
||||
>;
|
||||
|
||||
function hydrateDonationReceipt(
|
||||
receipt: DonationReceiptForDatabase
|
||||
): DonationReceipt {
|
||||
const readyForParse = {
|
||||
...omit(receipt, ['paymentDetailJson']),
|
||||
paymentDetail: JSON.parse(receipt.paymentDetailJson),
|
||||
};
|
||||
|
||||
const result = safeParseLoose(donationReceiptSchema, readyForParse);
|
||||
if (result.success) {
|
||||
return result.data;
|
||||
}
|
||||
|
||||
sqlLogger.error(
|
||||
`hydrateDonationReceipt: Parse failed for payment type ${readyForParse.paymentType}:`,
|
||||
Errors.toLogFormat(result.error)
|
||||
);
|
||||
const toFix = readyForParse as unknown as DonationReceipt;
|
||||
toFix.paymentDetail = null;
|
||||
return toFix;
|
||||
}
|
||||
export function freezeDonationReceipt(
|
||||
receipt: DonationReceipt
|
||||
): DonationReceiptForDatabase {
|
||||
return {
|
||||
...omit(receipt, ['paymentDetail']),
|
||||
paymentDetailJson: JSON.stringify(receipt.paymentDetail),
|
||||
};
|
||||
}
|
||||
|
||||
export function getAllDonationReceipts(db: ReadableDB): Array<DonationReceipt> {
|
||||
const donationReceipts = db
|
||||
.prepare('SELECT * FROM donationReceipts ORDER BY timestamp DESC;')
|
||||
.all<DonationReceiptForDatabase>();
|
||||
|
||||
return donationReceipts.map(hydrateDonationReceipt);
|
||||
}
|
||||
export function getDonationReceiptById(
|
||||
db: ReadableDB,
|
||||
id: string
|
||||
): DonationReceipt | undefined {
|
||||
const [query, parameters] =
|
||||
sql`SELECT * FROM donationReceipts WHERE id = ${id}`;
|
||||
const fromDatabase = db
|
||||
.prepare(query)
|
||||
.get<DonationReceiptForDatabase>(parameters);
|
||||
|
||||
if (fromDatabase) {
|
||||
return hydrateDonationReceipt(fromDatabase);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
export function _deleteAllDonationReceipts(db: WritableDB): void {
|
||||
db.prepare('DELETE FROM donationReceipts;').run();
|
||||
}
|
||||
export function deleteDonationReceiptById(db: WritableDB, id: string): void {
|
||||
const [query, parameters] =
|
||||
sql`DELETE FROM donationReceipts WHERE id = ${id};`;
|
||||
db.prepare(query).run(parameters);
|
||||
}
|
||||
export function createDonationReceipt(
|
||||
db: WritableDB,
|
||||
receipt: DonationReceipt
|
||||
): void {
|
||||
const forDatabase = freezeDonationReceipt(receipt);
|
||||
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO donationReceipts(
|
||||
id,
|
||||
currencyType,
|
||||
paymentAmount,
|
||||
paymentDetailJson,
|
||||
paymentType,
|
||||
timestamp
|
||||
) VALUES (
|
||||
$id,
|
||||
$currencyType,
|
||||
$paymentAmount,
|
||||
$paymentDetailJson,
|
||||
$paymentType,
|
||||
$timestamp
|
||||
);
|
||||
`
|
||||
).run(forDatabase);
|
||||
}
|
||||
Reference in New Issue
Block a user