Merge branch 'main' into ENG-3016

This commit is contained in:
x032205
2025-07-10 12:28:39 -04:00
11 changed files with 210 additions and 34 deletions
+3
View File
@@ -4,6 +4,7 @@ import "ts-node/register";
import dotenv from "dotenv";
import type { Knex } from "knex";
import path from "path";
import { initLogger } from "@app/lib/logger";
// Update with your config settings. .
dotenv.config({
@@ -13,6 +14,8 @@ dotenv.config({
path: path.join(__dirname, "../../../.env")
});
initLogger();
export default {
development: {
client: "postgres",
@@ -0,0 +1,48 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
const MIGRATION_TIMEOUT = 30 * 60 * 1000; // 30 minutes
export async function up(knex: Knex): Promise<void> {
const result = await knex.raw("SHOW statement_timeout");
const originalTimeout = result.rows[0].statement_timeout;
try {
await knex.raw(`SET statement_timeout = ${MIGRATION_TIMEOUT}`);
// iat means IdentityAccessToken
await knex.raw(`
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_iat_identity_id
ON ${TableName.IdentityAccessToken} ("identityId")
`);
await knex.raw(`
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_iat_ua_client_secret_id
ON ${TableName.IdentityAccessToken} ("identityUAClientSecretId")
`);
} finally {
await knex.raw(`SET statement_timeout = '${originalTimeout}'`);
}
}
export async function down(knex: Knex): Promise<void> {
const result = await knex.raw("SHOW statement_timeout");
const originalTimeout = result.rows[0].statement_timeout;
try {
await knex.raw(`SET statement_timeout = ${MIGRATION_TIMEOUT}`);
await knex.raw(`
DROP INDEX IF EXISTS idx_iat_identity_id
`);
await knex.raw(`
DROP INDEX IF EXISTS idx_iat_ua_client_secret_id
`);
} finally {
await knex.raw(`SET statement_timeout = '${originalTimeout}'`);
}
}
export const config = { transaction: false };
@@ -108,16 +108,16 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
const now = new Date();
const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
const oneMonthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
const threeMonthsAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);
const twelveMonthsAgo = new Date(now.getTime() - 360 * 24 * 60 * 60 * 1000);
const memberships = await db
.replicaNode()(TableName.OrgMembership)
.where("status", "invited")
.where((qb) => {
// lastInvitedAt is null AND createdAt is between 1 week and 3 months ago
// lastInvitedAt is null AND createdAt is between 1 week and 12 months ago
void qb
.whereNull(`${TableName.OrgMembership}.lastInvitedAt`)
.whereBetween(`${TableName.OrgMembership}.createdAt`, [threeMonthsAgo, oneWeekAgo]);
.whereBetween(`${TableName.OrgMembership}.createdAt`, [twelveMonthsAgo, oneWeekAgo]);
})
.orWhere((qb) => {
// lastInvitedAt is older than 1 week ago AND createdAt is younger than 1 month ago
+27 -14
View File
@@ -36,6 +36,8 @@ import { getConfig } from "@app/lib/config/env";
import { generateAsymmetricKeyPair } from "@app/lib/crypto";
import { generateSymmetricKey, infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { generateUserSrpKeys } from "@app/lib/crypto/srp";
import { applyJitter } from "@app/lib/dates";
import { delay as delayMs } from "@app/lib/delay";
import {
BadRequestError,
ForbiddenRequestError,
@@ -44,9 +46,10 @@ import {
UnauthorizedError
} from "@app/lib/errors";
import { groupBy } from "@app/lib/fn";
import { logger } from "@app/lib/logger";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { isDisposableEmail } from "@app/lib/validator";
import { TQueueServiceFactory } from "@app/queue";
import { QueueName, TQueueServiceFactory } from "@app/queue";
import { getDefaultOrgMembershipRoleForUpdateOrg } from "@app/services/org/org-role-fns";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
@@ -1438,6 +1441,8 @@ export const orgServiceFactory = ({
* Re-send emails to users who haven't accepted an invite yet
*/
const notifyInvitedUsers = async () => {
logger.info(`${QueueName.DailyResourceCleanUp}: notify invited users started`);
const invitedUsers = await orgMembershipDAL.findRecentInvitedMemberships();
const appCfg = getConfig();
@@ -1461,24 +1466,32 @@ export const orgServiceFactory = ({
});
if (invitedUser.inviteEmail) {
await smtpService.sendMail({
template: SmtpTemplates.OrgInvite,
subjectLine: `Reminder: You have been invited to ${org.name} on Infisical`,
recipients: [invitedUser.inviteEmail],
substitutions: {
organizationName: org.name,
email: invitedUser.inviteEmail,
organizationId: org.id.toString(),
token,
callback_url: `${appCfg.SITE_URL}/signupinvite`
}
});
notifiedUsers.push(invitedUser.id);
await delayMs(Math.max(0, applyJitter(0, 2000)));
try {
await smtpService.sendMail({
template: SmtpTemplates.OrgInvite,
subjectLine: `Reminder: You have been invited to ${org.name} on Infisical`,
recipients: [invitedUser.inviteEmail],
substitutions: {
organizationName: org.name,
email: invitedUser.inviteEmail,
organizationId: org.id.toString(),
token,
callback_url: `${appCfg.SITE_URL}/signupinvite`
}
});
notifiedUsers.push(invitedUser.id);
} catch (err) {
logger.error(err, `${QueueName.DailyResourceCleanUp}: notify invited users failed to send email`);
}
}
})
);
await orgMembershipDAL.updateLastInvitedAtByIds(notifiedUsers);
logger.info(`${QueueName.DailyResourceCleanUp}: notify invited users completed`);
};
return {