From 0dce40826673386afa331607ea6ddf09f787694b Mon Sep 17 00:00:00 2001 From: Benjamin Piouffle Date: Tue, 31 Dec 2024 17:12:07 +0100 Subject: [PATCH] feat(cron): implement permanent deletion of records --- cron/daily/96-permanant-delete.ts | 71 +++++++++++++++++++++++++++++++ server/models/ConnectedAccount.ts | 4 ++ 2 files changed, 75 insertions(+) create mode 100644 cron/daily/96-permanant-delete.ts diff --git a/cron/daily/96-permanant-delete.ts b/cron/daily/96-permanant-delete.ts new file mode 100644 index 00000000000..bb419ea1d39 --- /dev/null +++ b/cron/daily/96-permanant-delete.ts @@ -0,0 +1,71 @@ +import { Transaction } from 'sequelize'; + +import models, { ModelType, Op, sequelize } from '../../server/models'; +import { runCronJob } from '../utils'; + +enum RetentionPeriod { + FINANCIAL = '7 year', + SENSITIVE = '1 year', + DEFAULT = '6 months', + REDUCED = '1 month', +} + +const MODEL_RETENTION_PERIODS = new Map([ + [models.Comment, RetentionPeriod.SENSITIVE], + [models.ConnectedAccount, RetentionPeriod.DEFAULT], + [models.Conversation, RetentionPeriod.DEFAULT], + [models.Expense, RetentionPeriod.SENSITIVE], + [models.ExpenseItem, RetentionPeriod.SENSITIVE], + [models.HostApplication, RetentionPeriod.SENSITIVE], + [models.LegalDocument, RetentionPeriod.FINANCIAL], + [models.Location, RetentionPeriod.SENSITIVE], + [models.Member, RetentionPeriod.FINANCIAL], + [models.MemberInvitation, RetentionPeriod.DEFAULT], + [models.OAuthAuthorizationCode, RetentionPeriod.REDUCED], + [models.Order, RetentionPeriod.FINANCIAL], + [models.PaymentMethod, RetentionPeriod.FINANCIAL], + [models.PayoutMethod, RetentionPeriod.FINANCIAL], + [models.PaypalPlan, RetentionPeriod.DEFAULT], + [models.PaypalProduct, RetentionPeriod.DEFAULT], + [models.PersonalToken, RetentionPeriod.DEFAULT], + [models.RecurringExpense, RetentionPeriod.DEFAULT], + [models.RequiredLegalDocument, RetentionPeriod.FINANCIAL], + [models.Subscription, RetentionPeriod.FINANCIAL], + [models.SuspendedAsset, RetentionPeriod.DEFAULT], + [models.Tier, RetentionPeriod.DEFAULT], + [models.Transaction, RetentionPeriod.FINANCIAL], + [models.TransactionSettlement, RetentionPeriod.FINANCIAL], + [models.TransactionsImport, RetentionPeriod.FINANCIAL], + [models.Update, RetentionPeriod.DEFAULT], + [models.User, RetentionPeriod.FINANCIAL], + [models.UserToken, RetentionPeriod.SENSITIVE], + [models.VirtualCard, RetentionPeriod.FINANCIAL], + [models.VirtualCardRequest, RetentionPeriod.FINANCIAL], +]); + +const run = async () => { + return sequelize.transaction(async (transaction: Transaction) => { + for (const [model, retentionPeriod] of MODEL_RETENTION_PERIODS) { + const result = await model.destroy({ + transaction, + force: true, + where: { + deletedAt: { + [Op.not]: null, + [Op.lte]: `NOW() - INTERVAL(${retentionPeriod})`, + }, + }, + }); + + console.log(`Deleted ${result} records for ${model.name}`); + } + + if (process.env.DRY_RUN !== 'false') { + await transaction.rollback(); + } + }); +}; + +if (require.main === module) { + runCronJob('checks', run, 24 * 60 * 60); +} diff --git a/server/models/ConnectedAccount.ts b/server/models/ConnectedAccount.ts index 24c98effc16..122be15c40a 100644 --- a/server/models/ConnectedAccount.ts +++ b/server/models/ConnectedAccount.ts @@ -32,6 +32,7 @@ class ConnectedAccount extends Model< declare public CreatedByUserId: CreationOptional; declare public createdAt: CreationOptional; declare public updatedAt: CreationOptional; + declare public deletedAt: CreationOptional; declare public collective?: NonAttribute; declare public getCollective: BelongsToGetAssociationMixin; @@ -111,6 +112,9 @@ ConnectedAccount.init( type: DataTypes.DATE, defaultValue: DataTypes.NOW, }, + deletedAt: { + type: DataTypes.DATE, + }, updatedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW,