Minor improvements on new reminders logic

This commit is contained in:
Carlos Monastyrski
2025-07-11 08:02:18 -03:00
parent e2559f10bc
commit 50ba2e543c
14 changed files with 136 additions and 35 deletions
+1 -1
View File
@@ -93,7 +93,7 @@ import { TProjectEnvServiceFactory } from "@app/services/project-env/project-env
import { TProjectKeyServiceFactory } from "@app/services/project-key/project-key-service"; import { TProjectKeyServiceFactory } from "@app/services/project-key/project-key-service";
import { TProjectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service"; import { TProjectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service";
import { TProjectRoleServiceFactory } from "@app/services/project-role/project-role-service"; import { TProjectRoleServiceFactory } from "@app/services/project-role/project-role-service";
import { TReminderServiceFactory } from "@app/services/reminder/reminder-service"; import { TReminderServiceFactory } from "@app/services/reminder/reminder-types";
import { TSecretServiceFactory } from "@app/services/secret/secret-service"; import { TSecretServiceFactory } from "@app/services/secret/secret-service";
import { TSecretBlindIndexServiceFactory } from "@app/services/secret-blind-index/secret-blind-index-service"; import { TSecretBlindIndexServiceFactory } from "@app/services/secret-blind-index/secret-blind-index-service";
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service"; import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
-1
View File
@@ -173,7 +173,6 @@ export enum TableName {
SecretScanningScan = "secret_scanning_scans", SecretScanningScan = "secret_scanning_scans",
SecretScanningFinding = "secret_scanning_findings", SecretScanningFinding = "secret_scanning_findings",
SecretScanningConfig = "secret_scanning_configs", SecretScanningConfig = "secret_scanning_configs",
// reminders // reminders
Reminder = "reminders", Reminder = "reminders",
ReminderRecipient = "reminders_recipients" ReminderRecipient = "reminders_recipients"
+1 -1
View File
@@ -63,7 +63,7 @@ import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal"; import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal"; import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
import { TProjectRoleDALFactory } from "../project-role/project-role-dal"; import { TProjectRoleDALFactory } from "../project-role/project-role-dal";
import { TReminderServiceFactory } from "../reminder/reminder-service"; import { TReminderServiceFactory } from "../reminder/reminder-types";
import { TSecretDALFactory } from "../secret/secret-dal"; import { TSecretDALFactory } from "../secret/secret-dal";
import { fnDeleteProjectSecretReminders } from "../secret/secret-fns"; import { fnDeleteProjectSecretReminders } from "../secret/secret-fns";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
@@ -59,7 +59,7 @@ import { TProjectMembershipDALFactory } from "../project-membership/project-memb
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal"; import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
import { TProjectRoleDALFactory } from "../project-role/project-role-dal"; import { TProjectRoleDALFactory } from "../project-role/project-role-dal";
import { getPredefinedRoles } from "../project-role/project-role-fns"; import { getPredefinedRoles } from "../project-role/project-role-fns";
import { TReminderServiceFactory } from "../reminder/reminder-service"; import { TReminderServiceFactory } from "../reminder/reminder-types";
import { TSecretDALFactory } from "../secret/secret-dal"; import { TSecretDALFactory } from "../secret/secret-dal";
import { fnDeleteProjectSecretReminders } from "../secret/secret-fns"; import { fnDeleteProjectSecretReminders } from "../secret/secret-fns";
import { ROOT_FOLDER_NAME, TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { ROOT_FOLDER_NAME, TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
+14 -4
View File
@@ -19,11 +19,21 @@ export const reminderDALFactory = (db: TDbClient) => {
const reminderOrm = ormify(db, TableName.Reminder); const reminderOrm = ormify(db, TableName.Reminder);
const getTodayDateRange = () => { const getTodayDateRange = () => {
const now = new Date(); const today = new Date();
const startOfDay = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 0, 0, 0, 0)); const year = today.getUTCFullYear();
const endOfDay = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 23, 59, 59, 999)); const month = today.getUTCMonth();
const date = today.getUTCDate();
return { startOfDay, endOfDay }; // Start of day: 00:00:00.000 UTC
const startOfDay = new Date(Date.UTC(year, month, date, 0, 0, 0, 0));
// End of day: 23:59:59.999 UTC
const endOfDay = new Date(Date.UTC(year, month, date, 23, 59, 59, 999));
return {
startOfDay,
endOfDay
};
}; };
const findSecretDailyReminders = async (tx?: Knex) => { const findSecretDailyReminders = async (tx?: Knex) => {
@@ -6,7 +6,7 @@ import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
import { TSecretReminderRecipientsDALFactory } from "../secret-reminder-recipients/secret-reminder-recipients-dal"; import { TSecretReminderRecipientsDALFactory } from "../secret-reminder-recipients/secret-reminder-recipients-dal";
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal"; import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
import { TReminderServiceFactory } from "./reminder-service"; import { TReminderServiceFactory } from "./reminder-types";
type TDailyReminderQueueServiceFactoryDep = { type TDailyReminderQueueServiceFactoryDep = {
reminderService: TReminderServiceFactory; reminderService: TReminderServiceFactory;
@@ -13,7 +13,7 @@ import { TReminderRecipientDALFactory } from "../reminder-recipients/reminder-re
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal"; import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service"; import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
import { TReminderDALFactory } from "./reminder-dal"; import { TReminderDALFactory } from "./reminder-dal";
import { TBatchCreateReminderDTO, TCreateReminderDTO } from "./reminder-types"; import { TBatchCreateReminderDTO, TCreateReminderDTO, TReminderServiceFactory } from "./reminder-types";
type TReminderServiceFactoryDep = { type TReminderServiceFactoryDep = {
reminderDAL: TReminderDALFactory; reminderDAL: TReminderDALFactory;
@@ -24,8 +24,6 @@ type TReminderServiceFactoryDep = {
secretV2BridgeDAL: Pick<TSecretV2BridgeDALFactory, "invalidateSecretCacheByProjectId">; secretV2BridgeDAL: Pick<TSecretV2BridgeDALFactory, "invalidateSecretCacheByProjectId">;
}; };
export type TReminderServiceFactory = ReturnType<typeof reminderServiceFactory>;
export const reminderServiceFactory = ({ export const reminderServiceFactory = ({
reminderDAL, reminderDAL,
reminderRecipientDAL, reminderRecipientDAL,
@@ -33,8 +31,8 @@ export const reminderServiceFactory = ({
projectMembershipDAL, projectMembershipDAL,
permissionService, permissionService,
secretV2BridgeDAL secretV2BridgeDAL
}: TReminderServiceFactoryDep) => { }: TReminderServiceFactoryDep): TReminderServiceFactory => {
const addDays = (days: number, fromDate: Date = new Date()): Date => { const $addDays = (days: number, fromDate: Date = new Date()): Date => {
const result = new Date(fromDate); const result = new Date(fromDate);
result.setDate(result.getDate() + days); result.setDate(result.getDate() + days);
return result; return result;
@@ -74,7 +72,7 @@ export const reminderServiceFactory = ({
} }
}; };
const createReminderInternal = async ({ const createReminderInternal: TReminderServiceFactory["createReminderInternal"] = async ({
secretId, secretId,
message, message,
repeatDays, repeatDays,
@@ -98,7 +96,7 @@ export const reminderServiceFactory = ({
} }
if (repeatDays && repeatDays > 0) { if (repeatDays && repeatDays > 0) {
nextReminderDate = addDays(repeatDays); nextReminderDate = $addDays(repeatDays);
} }
if (!nextReminderDate) { if (!nextReminderDate) {
@@ -133,7 +131,7 @@ export const reminderServiceFactory = ({
return { id: reminderId, created: !existingReminder }; return { id: reminderId, created: !existingReminder };
}; };
const createReminder = async ({ const createReminder: TReminderServiceFactory["createReminder"] = async ({
actor, actor,
actorId, actorId,
actorOrgId, actorOrgId,
@@ -157,7 +155,7 @@ export const reminderServiceFactory = ({
return response; return response;
}; };
const getReminder = async ({ const getReminder: TReminderServiceFactory["getReminder"] = async ({
secretId, secretId,
projectId, projectId,
actor, actor,
@@ -187,7 +185,7 @@ export const reminderServiceFactory = ({
return reminder; return reminder;
}; };
const sendDailyReminders = async () => { const sendDailyReminders: TReminderServiceFactory["sendDailyReminders"] = async () => {
const remindersToSend = await reminderDAL.findSecretDailyReminders(); const remindersToSend = await reminderDAL.findSecretDailyReminders();
for (const reminder of remindersToSend) { for (const reminder of remindersToSend) {
@@ -211,7 +209,7 @@ export const reminderServiceFactory = ({
} }
}); });
if (reminder.repeatDays) { if (reminder.repeatDays) {
await reminderDAL.updateById(reminder.id, { nextReminderDate: addDays(reminder.repeatDays) }, tx); await reminderDAL.updateById(reminder.id, { nextReminderDate: $addDays(reminder.repeatDays) }, tx);
} else { } else {
await reminderDAL.deleteById(reminder.id, tx); await reminderDAL.deleteById(reminder.id, tx);
} }
@@ -225,7 +223,7 @@ export const reminderServiceFactory = ({
} }
}; };
const deleteReminder = async ({ const deleteReminder: TReminderServiceFactory["deleteReminder"] = async ({
actor, actor,
actorId, actorId,
actorOrgId, actorOrgId,
@@ -253,7 +251,11 @@ export const reminderServiceFactory = ({
await secretV2BridgeDAL.invalidateSecretCacheByProjectId(projectId); await secretV2BridgeDAL.invalidateSecretCacheByProjectId(projectId);
}; };
const removeReminderRecipients = async (secretId: string, projectId: string, tx?: Knex) => { const removeReminderRecipients: TReminderServiceFactory["removeReminderRecipients"] = async (
secretId: string,
projectId: string,
tx?: Knex
) => {
const reminder = await reminderDAL.findOne({ secretId }, tx); const reminder = await reminderDAL.findOne({ secretId }, tx);
if (!reminder) { if (!reminder) {
return; return;
@@ -262,12 +264,19 @@ export const reminderServiceFactory = ({
await secretV2BridgeDAL.invalidateSecretCacheByProjectId(projectId); await secretV2BridgeDAL.invalidateSecretCacheByProjectId(projectId);
}; };
const deleteReminderBySecretId = async (secretId: string, projectId: string, tx?: Knex) => { const deleteReminderBySecretId: TReminderServiceFactory["deleteReminderBySecretId"] = async (
secretId: string,
projectId: string,
tx?: Knex
) => {
await reminderDAL.delete({ secretId }, tx); await reminderDAL.delete({ secretId }, tx);
await secretV2BridgeDAL.invalidateSecretCacheByProjectId(projectId); await secretV2BridgeDAL.invalidateSecretCacheByProjectId(projectId);
}; };
const batchCreateReminders = async (remindersData: TBatchCreateReminderDTO, tx?: Knex) => { const batchCreateReminders: TReminderServiceFactory["batchCreateReminders"] = async (
remindersData: TBatchCreateReminderDTO,
tx?: Knex
) => {
if (!remindersData || remindersData.length === 0) { if (!remindersData || remindersData.length === 0) {
return { created: 0, reminderIds: [] }; return { created: 0, reminderIds: [] };
} }
@@ -280,7 +289,7 @@ export const reminderServiceFactory = ({
} }
if (repeatDays && repeatDays > 0 && !nextReminderDate) { if (repeatDays && repeatDays > 0 && !nextReminderDate) {
nextReminderDate = addDays(repeatDays); nextReminderDate = $addDays(repeatDays);
} }
if (!nextReminderDate) { if (!nextReminderDate) {
@@ -1,12 +1,15 @@
import { Knex } from "knex";
import { ActorAuthMethod, ActorType } from "../auth/auth-type"; import { ActorAuthMethod, ActorType } from "../auth/auth-type";
export type TReminder = { export type TReminder = {
id: string; id: string;
secretId?: string; secretId?: string | null;
message?: string; message?: string | null;
repeatDays?: number; repeatDays?: number | null;
nextReminderDate?: Date; nextReminderDate: Date;
createdAt: Date; createdAt: Date;
updatedAt: Date;
}; };
export type TCreateReminderDTO = { export type TCreateReminderDTO = {
@@ -32,3 +35,82 @@ export type TBatchCreateReminderDTO = {
recipients?: string[] | null; recipients?: string[] | null;
projectId?: string; projectId?: string;
}[]; }[];
export interface TReminderServiceFactory {
createReminder: ({
actor,
actorId,
actorOrgId,
actorAuthMethod,
projectId,
reminder
}: TCreateReminderDTO) => Promise<{
id: string;
created: boolean;
}>;
getReminder: ({
secretId,
projectId,
actor,
actorId,
actorOrgId,
actorAuthMethod
}: {
secretId: string;
projectId: string;
actor: ActorType;
actorId: string;
actorOrgId: string;
actorAuthMethod: ActorAuthMethod;
}) => Promise<(TReminder & { recipients: string[] }) | null>;
sendDailyReminders: () => Promise<void>;
deleteReminder: ({
actor,
actorId,
actorOrgId,
actorAuthMethod,
secretId,
projectId
}: {
actor: ActorType;
actorId: string;
actorOrgId: string;
actorAuthMethod: ActorAuthMethod;
secretId: string;
projectId: string;
}) => Promise<void>;
removeReminderRecipients: (secretId: string, projectId: string, tx?: Knex) => Promise<void>;
deleteReminderBySecretId: (secretId: string, projectId: string, tx?: Knex) => Promise<void>;
batchCreateReminders: (
remindersData: TBatchCreateReminderDTO,
tx?: Knex
) => Promise<{
created: number;
reminderIds: string[];
}>;
createReminderInternal: ({
secretId,
message,
repeatDays,
nextReminderDate,
recipients,
projectId
}: {
secretId?: string;
message?: string | null;
repeatDays?: number | null;
nextReminderDate?: string | null;
recipients?: string[] | null;
projectId: string;
}) => Promise<{
id: string;
created: boolean;
}>;
}
@@ -466,6 +466,7 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
} }
}; };
// This method currently uses too many joins which is not performant, in case we need to add more filters we should consider refactoring this method
const findByFolderIds = async (dto: { const findByFolderIds = async (dto: {
folderIds: string[]; folderIds: string[];
userId?: string; userId?: string;
@@ -389,7 +389,7 @@ export const fnSecretBulkDelete = async ({
tx tx
); );
await Promise.allSettled( await Promise.all(
deletedSecrets deletedSecrets
.filter(({ reminderRepeatDays }) => Boolean(reminderRepeatDays)) .filter(({ reminderRepeatDays }) => Boolean(reminderRepeatDays))
.map(({ id, reminderRepeatDays }) => .map(({ id, reminderRepeatDays }) =>
@@ -32,7 +32,7 @@ import { TFolderCommitServiceFactory } from "../folder-commit/folder-commit-serv
import { TKmsServiceFactory } from "../kms/kms-service"; import { TKmsServiceFactory } from "../kms/kms-service";
import { KmsDataKey } from "../kms/kms-types"; import { KmsDataKey } from "../kms/kms-types";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TReminderServiceFactory } from "../reminder/reminder-service"; import { TReminderServiceFactory } from "../reminder/reminder-types";
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal"; import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
import { TSecretQueueFactory } from "../secret/secret-queue"; import { TSecretQueueFactory } from "../secret/secret-queue";
import { TGetASecretByIdDTO } from "../secret/secret-types"; import { TGetASecretByIdDTO } from "../secret/secret-types";
+1 -1
View File
@@ -34,7 +34,7 @@ import { KmsDataKey } from "../kms/kms-types";
import { getBotKeyFnFactory } from "../project-bot/project-bot-fns"; import { getBotKeyFnFactory } from "../project-bot/project-bot-fns";
import { TProjectBotServiceFactory } from "../project-bot/project-bot-service"; import { TProjectBotServiceFactory } from "../project-bot/project-bot-service";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TReminderServiceFactory } from "../reminder/reminder-service"; import { TReminderServiceFactory } from "../reminder/reminder-types";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal"; import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
import { TSecretDALFactory } from "./secret-dal"; import { TSecretDALFactory } from "./secret-dal";
+2 -2
View File
@@ -48,7 +48,7 @@ import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TProjectKeyDALFactory } from "../project-key/project-key-dal"; import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal"; import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal"; import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
import { TReminderServiceFactory } from "../reminder/reminder-service"; import { TReminderServiceFactory } from "../reminder/reminder-types";
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal"; import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema"; import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
@@ -1044,7 +1044,7 @@ export const secretQueueFactory = ({
} }
}); });
// TODO: remove this queue (needed for queue initialization and perform the migration) // TODO(Carlos): remove this queue (needed for queue initialization and perform the migration)
queueService.start(QueueName.SecretReminder, async ({ data }) => { queueService.start(QueueName.SecretReminder, async ({ data }) => {
logger.info(`(deprecated) secretReminderQueue.process: [secretDocument=${data.secretId}]`); logger.info(`(deprecated) secretReminderQueue.process: [secretDocument=${data.secretId}]`);
}); });
@@ -48,7 +48,7 @@ import { ChangeType } from "../folder-commit/folder-commit-service";
import { TProjectDALFactory } from "../project/project-dal"; import { TProjectDALFactory } from "../project/project-dal";
import { TProjectBotServiceFactory } from "../project-bot/project-bot-service"; import { TProjectBotServiceFactory } from "../project-bot/project-bot-service";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TReminderServiceFactory } from "../reminder/reminder-service"; import { TReminderServiceFactory } from "../reminder/reminder-types";
import { TSecretBlindIndexDALFactory } from "../secret-blind-index/secret-blind-index-dal"; import { TSecretBlindIndexDALFactory } from "../secret-blind-index/secret-blind-index-dal";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal"; import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";