mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-29 19:13:59 +02:00
Server: Allow setting email key to prevent the same email to be sent multiple times
This commit is contained in:
parent
b3cec3be37
commit
391204c31e
Binary file not shown.
@ -394,6 +394,7 @@ export interface Email extends WithDates {
|
||||
sent_time?: number;
|
||||
sent_success?: number;
|
||||
error?: string;
|
||||
key?: string;
|
||||
}
|
||||
|
||||
export interface Token extends WithDates {
|
||||
@ -549,6 +550,7 @@ export const databaseSchema: DatabaseTables = {
|
||||
error: { type: 'string' },
|
||||
updated_time: { type: 'string' },
|
||||
created_time: { type: 'string' },
|
||||
key: { type: 'string' },
|
||||
},
|
||||
tokens: {
|
||||
id: { type: 'number' },
|
||||
|
16
packages/server/src/migrations/20210809163307_email_key.ts
Normal file
16
packages/server/src/migrations/20210809163307_email_key.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Knex } from 'knex';
|
||||
import { DbConnection } from '../db';
|
||||
|
||||
export async function up(db: DbConnection): Promise<any> {
|
||||
await db.schema.alterTable('emails', function(table: Knex.CreateTableBuilder) {
|
||||
table.text('key', 'mediumtext').defaultTo('').notNullable();
|
||||
});
|
||||
|
||||
await db.schema.alterTable('emails', function(table: Knex.CreateTableBuilder) {
|
||||
table.unique(['recipient_email', 'key']);
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(_db: DbConnection): Promise<any> {
|
||||
|
||||
}
|
48
packages/server/src/models/EmailModel.test.ts
Normal file
48
packages/server/src/models/EmailModel.test.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { EmailSender } from '../db';
|
||||
import { beforeAllDb, afterAllTests, beforeEachDb, models, createUserAndSession } from '../utils/testing/testUtils';
|
||||
import paymentFailedTemplate from '../views/emails/paymentFailedTemplate';
|
||||
|
||||
describe('EmailModel', function() {
|
||||
|
||||
beforeAll(async () => {
|
||||
await beforeAllDb('EmailModel');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await afterAllTests();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await beforeEachDb();
|
||||
});
|
||||
|
||||
test('should not send the same keyed email twice', async function() {
|
||||
const { user } = await createUserAndSession();
|
||||
|
||||
const sendEmail = async (key: string) => {
|
||||
await models().email().push({
|
||||
...paymentFailedTemplate(),
|
||||
recipient_email: user.email,
|
||||
recipient_id: user.id,
|
||||
recipient_name: user.full_name || '',
|
||||
sender_id: EmailSender.Support,
|
||||
key: key,
|
||||
});
|
||||
};
|
||||
|
||||
const beforeCount = (await models().email().all()).length;
|
||||
|
||||
await sendEmail('payment_failed_1');
|
||||
|
||||
expect((await models().email().all()).length).toBe(beforeCount + 1);
|
||||
|
||||
await sendEmail('payment_failed_1');
|
||||
|
||||
expect((await models().email().all()).length).toBe(beforeCount + 1);
|
||||
|
||||
await sendEmail('payment_failed_2');
|
||||
|
||||
expect((await models().email().all()).length).toBe(beforeCount + 2);
|
||||
});
|
||||
|
||||
});
|
@ -6,6 +6,7 @@ export interface EmailToSend {
|
||||
recipient_email: string;
|
||||
subject: string;
|
||||
body: string;
|
||||
key?: string;
|
||||
|
||||
recipient_name?: string;
|
||||
recipient_id?: Uuid;
|
||||
@ -26,12 +27,26 @@ export default class EmailModel extends BaseModel<Email> {
|
||||
return false;
|
||||
}
|
||||
|
||||
public async push(email: EmailToSend) {
|
||||
public async push(email: EmailToSend): Promise<Email | null> {
|
||||
if (email.key) {
|
||||
const existingEmail = await this.byRecipientAndKey(email.recipient_email, email.key);
|
||||
if (existingEmail) return null; // noop - the email has already been sent
|
||||
}
|
||||
|
||||
const output = await super.save({ ...email });
|
||||
EmailModel.eventEmitter.emit('queued');
|
||||
return output;
|
||||
}
|
||||
|
||||
private async byRecipientAndKey(recipientEmail: string, key: string): Promise<Email> {
|
||||
if (!key) throw new Error('Key cannot be empty');
|
||||
|
||||
return this.db(this.tableName)
|
||||
.where('recipient_email', '=', recipientEmail)
|
||||
.where('key', '=', key)
|
||||
.first();
|
||||
}
|
||||
|
||||
public async needToBeSent(): Promise<Email[]> {
|
||||
return this.db(this.tableName).where('sent_time', '=', 0);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user