mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 14:27:30 +00:00
Merge pull request #4506 from Infisical/feat/local-redis-geo
Local redis for geo
This commit is contained in:
@@ -56,6 +56,15 @@ export const mockKeyStore = (): TKeyStoreFactory => {
|
|||||||
incrementBy: async () => {
|
incrementBy: async () => {
|
||||||
return 1;
|
return 1;
|
||||||
},
|
},
|
||||||
|
pgGetIntItem: async (key) => {
|
||||||
|
const value = store[key];
|
||||||
|
if (typeof value === "number") {
|
||||||
|
return Number(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pgIncrementBy: async () => {
|
||||||
|
return 1;
|
||||||
|
},
|
||||||
getItems: async (keys) => {
|
getItems: async (keys) => {
|
||||||
const values = keys.map((key) => {
|
const values = keys.map((key) => {
|
||||||
const value = store[key];
|
const value = store[key];
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { mockSmtpServer } from "./mocks/smtp";
|
|||||||
import { initDbConnection } from "@app/db";
|
import { initDbConnection } from "@app/db";
|
||||||
import { queueServiceFactory } from "@app/queue";
|
import { queueServiceFactory } from "@app/queue";
|
||||||
import { keyStoreFactory } from "@app/keystore/keystore";
|
import { keyStoreFactory } from "@app/keystore/keystore";
|
||||||
|
import { keyValueStoreDALFactory } from "@app/keystore/key-value-store-dal";
|
||||||
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
||||||
import { buildRedisFromConfig } from "@app/lib/config/redis";
|
import { buildRedisFromConfig } from "@app/lib/config/redis";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
@@ -62,7 +63,8 @@ export default {
|
|||||||
|
|
||||||
const smtp = mockSmtpServer();
|
const smtp = mockSmtpServer();
|
||||||
const queue = queueServiceFactory(envCfg, { dbConnectionUrl: envCfg.DB_CONNECTION_URI });
|
const queue = queueServiceFactory(envCfg, { dbConnectionUrl: envCfg.DB_CONNECTION_URI });
|
||||||
const keyStore = keyStoreFactory(envCfg);
|
const keyValueStoreDAL = keyValueStoreDALFactory(db);
|
||||||
|
const keyStore = keyStoreFactory(envCfg, keyValueStoreDAL);
|
||||||
|
|
||||||
await queue.initialize();
|
await queue.initialize();
|
||||||
|
|
||||||
|
|||||||
Vendored
+8
@@ -191,6 +191,9 @@ import {
|
|||||||
TInternalKms,
|
TInternalKms,
|
||||||
TInternalKmsInsert,
|
TInternalKmsInsert,
|
||||||
TInternalKmsUpdate,
|
TInternalKmsUpdate,
|
||||||
|
TKeyValueStore,
|
||||||
|
TKeyValueStoreInsert,
|
||||||
|
TKeyValueStoreUpdate,
|
||||||
TKmipClientCertificates,
|
TKmipClientCertificates,
|
||||||
TKmipClientCertificatesInsert,
|
TKmipClientCertificatesInsert,
|
||||||
TKmipClientCertificatesUpdate,
|
TKmipClientCertificatesUpdate,
|
||||||
@@ -1264,5 +1267,10 @@ declare module "knex/types/tables" {
|
|||||||
TUserNotificationsInsert,
|
TUserNotificationsInsert,
|
||||||
TUserNotificationsUpdate
|
TUserNotificationsUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.KeyValueStore]: KnexOriginal.CompositeTableType<
|
||||||
|
TKeyValueStore,
|
||||||
|
TKeyValueStoreInsert,
|
||||||
|
TKeyValueStoreUpdate
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasTable(TableName.KeyValueStore))) {
|
||||||
|
await knex.schema.createTable(TableName.KeyValueStore, (t) => {
|
||||||
|
t.text("key").primary();
|
||||||
|
t.bigint("integerValue");
|
||||||
|
t.datetime("expiresAt");
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.KeyValueStore);
|
||||||
|
}
|
||||||
@@ -61,6 +61,7 @@ export * from "./integration-auths";
|
|||||||
export * from "./integrations";
|
export * from "./integrations";
|
||||||
export * from "./internal-certificate-authorities";
|
export * from "./internal-certificate-authorities";
|
||||||
export * from "./internal-kms";
|
export * from "./internal-kms";
|
||||||
|
export * from "./key-value-store";
|
||||||
export * from "./kmip-client-certificates";
|
export * from "./kmip-client-certificates";
|
||||||
export * from "./kmip-clients";
|
export * from "./kmip-clients";
|
||||||
export * from "./kmip-org-configs";
|
export * from "./kmip-org-configs";
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const KeyValueStoreSchema = z.object({
|
||||||
|
key: z.string(),
|
||||||
|
integerValue: z.coerce.number().nullable().optional(),
|
||||||
|
expiresAt: z.date().nullable().optional(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TKeyValueStore = z.infer<typeof KeyValueStoreSchema>;
|
||||||
|
export type TKeyValueStoreInsert = Omit<z.input<typeof KeyValueStoreSchema>, TImmutableDBKeys>;
|
||||||
|
export type TKeyValueStoreUpdate = Partial<Omit<z.input<typeof KeyValueStoreSchema>, TImmutableDBKeys>>;
|
||||||
@@ -179,7 +179,8 @@ export enum TableName {
|
|||||||
SecretScanningConfig = "secret_scanning_configs",
|
SecretScanningConfig = "secret_scanning_configs",
|
||||||
// reminders
|
// reminders
|
||||||
Reminder = "reminders",
|
Reminder = "reminders",
|
||||||
ReminderRecipient = "reminders_recipients"
|
ReminderRecipient = "reminders_recipients",
|
||||||
|
KeyValueStore = "key_value_store"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt" | "commitId";
|
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt" | "commitId";
|
||||||
|
|||||||
@@ -46,7 +46,10 @@ export const dynamicSecretLeaseDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const countLeasesForDynamicSecret = async (dynamicSecretId: string, tx?: Knex) => {
|
const countLeasesForDynamicSecret = async (dynamicSecretId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await (tx || db)(TableName.DynamicSecretLease).count("*").where({ dynamicSecretId }).first();
|
const doc = await (tx || db.replicaNode())(TableName.DynamicSecretLease)
|
||||||
|
.count("*")
|
||||||
|
.where({ dynamicSecretId })
|
||||||
|
.first();
|
||||||
return parseInt(doc || "0", 10);
|
return parseInt(doc || "0", 10);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "DynamicSecretCountLeases" });
|
throw new DatabaseError({ error, name: "DynamicSecretCountLeases" });
|
||||||
@@ -55,7 +58,7 @@ export const dynamicSecretLeaseDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await (tx || db)(TableName.DynamicSecretLease)
|
const doc = await (tx || db.replicaNode())(TableName.DynamicSecretLease)
|
||||||
.where({ [`${TableName.DynamicSecretLease}.id` as "id"]: id })
|
.where({ [`${TableName.DynamicSecretLease}.id` as "id"]: id })
|
||||||
.first()
|
.first()
|
||||||
.join(
|
.join(
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const gatewayDALFactory = (db: TDbClient) => {
|
|||||||
{ offset, limit, sort, tx }: TFindOpt<TGateways> = {}
|
{ offset, limit, sort, tx }: TFindOpt<TGateways> = {}
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const query = (tx || db)(TableName.Gateway)
|
const query = (tx || db.replicaNode())(TableName.Gateway)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
.where(buildFindFilter(filter, TableName.Gateway, ["orgId"]))
|
.where(buildFindFilter(filter, TableName.Gateway, ["orgId"]))
|
||||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Gateway}.identityId`)
|
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Gateway}.identityId`)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
.whereIn(`${TableName.ProjectMembership}.projectId`, projectIds)
|
.whereIn(`${TableName.ProjectMembership}.projectId`, projectIds)
|
||||||
.pluck(`${TableName.ProjectMembership}.projectId`);
|
.pluck(`${TableName.ProjectMembership}.projectId`);
|
||||||
|
|
||||||
const userGroupMemberships: string[] = await (tx || db)(TableName.UserGroupMembership)
|
const userGroupMemberships: string[] = await (tx || db.replicaNode())(TableName.UserGroupMembership)
|
||||||
.where(`${TableName.UserGroupMembership}.userId`, userId)
|
.where(`${TableName.UserGroupMembership}.userId`, userId)
|
||||||
.whereNot(`${TableName.UserGroupMembership}.groupId`, groupId)
|
.whereNot(`${TableName.UserGroupMembership}.groupId`, groupId)
|
||||||
.join(
|
.join(
|
||||||
@@ -79,7 +79,7 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
.pluck(`${TableName.GroupProjectMembership}.groupId`);
|
.pluck(`${TableName.GroupProjectMembership}.groupId`);
|
||||||
|
|
||||||
// main query
|
// main query
|
||||||
const members = await (tx || db)(TableName.UserGroupMembership)
|
const members = await (tx || db.replicaNode())(TableName.UserGroupMembership)
|
||||||
.where(`${TableName.UserGroupMembership}.groupId`, groupId)
|
.where(`${TableName.UserGroupMembership}.groupId`, groupId)
|
||||||
.where(`${TableName.UserGroupMembership}.isPending`, false)
|
.where(`${TableName.UserGroupMembership}.isPending`, false)
|
||||||
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => {
|
const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
// count org users
|
// count org users
|
||||||
const userDoc = await (tx || db)(TableName.OrgMembership)
|
const userDoc = await (tx || db.replicaNode())(TableName.OrgMembership)
|
||||||
.where({ status: OrgMembershipStatus.Accepted })
|
.where({ status: OrgMembershipStatus.Accepted })
|
||||||
.andWhere((bd) => {
|
.andWhere((bd) => {
|
||||||
if (orgId) {
|
if (orgId) {
|
||||||
@@ -42,7 +42,7 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
const userCount = Number(userDoc?.[0].count);
|
const userCount = Number(userDoc?.[0].count);
|
||||||
|
|
||||||
// count org identities
|
// count org identities
|
||||||
const identityDoc = await (tx || db)(TableName.IdentityOrgMembership)
|
const identityDoc = await (tx || db.replicaNode())(TableName.IdentityOrgMembership)
|
||||||
.where((bd) => {
|
.where((bd) => {
|
||||||
if (orgId) {
|
if (orgId) {
|
||||||
void bd.where({ orgId });
|
void bd.where({ orgId });
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findProjectRequestCount = async (projectId: string, userId: string, policyId?: string, tx?: Knex) => {
|
const findProjectRequestCount = async (projectId: string, userId: string, policyId?: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await (tx || db)
|
const docs = await (tx || db.replicaNode())
|
||||||
.with(
|
.with(
|
||||||
"temp",
|
"temp",
|
||||||
(tx || db.replicaNode())(TableName.SecretApprovalRequest)
|
(tx || db.replicaNode())(TableName.SecretApprovalRequest)
|
||||||
@@ -494,7 +494,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
.distinctOn(`${TableName.SecretApprovalRequest}.id`)
|
.distinctOn(`${TableName.SecretApprovalRequest}.id`)
|
||||||
.as("inner");
|
.as("inner");
|
||||||
|
|
||||||
const query = (tx || db)
|
const query = (tx || db.replicaNode())
|
||||||
.select("*")
|
.select("*")
|
||||||
.select(db.raw("count(*) OVER() as total_count"))
|
.select(db.raw("count(*) OVER() as total_count"))
|
||||||
.from(innerQuery)
|
.from(innerQuery)
|
||||||
|
|||||||
+1
-1
@@ -377,7 +377,7 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
|
|||||||
// special query for migration to v2 secret
|
// special query for migration to v2 secret
|
||||||
const findByProjectId = async (projectId: string, tx?: Knex) => {
|
const findByProjectId = async (projectId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await (tx || db)(TableName.SecretApprovalRequestSecret)
|
const docs = await (tx || db.replicaNode())(TableName.SecretApprovalRequestSecret)
|
||||||
.join(
|
.join(
|
||||||
TableName.SecretApprovalRequest,
|
TableName.SecretApprovalRequest,
|
||||||
`${TableName.SecretApprovalRequest}.id`,
|
`${TableName.SecretApprovalRequest}.id`,
|
||||||
|
|||||||
@@ -787,6 +787,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
await secretV2BridgeDAL.invalidateSecretCacheByProjectId(projectId, tx);
|
||||||
return {
|
return {
|
||||||
secrets: { created: newSecrets, updated: updatedSecrets, deleted: deletedSecret },
|
secrets: { created: newSecrets, updated: updatedSecrets, deleted: deletedSecret },
|
||||||
approval: updatedSecretApproval
|
approval: updatedSecretApproval
|
||||||
@@ -976,6 +977,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
await secretV2BridgeDAL.invalidateSecretCacheByProjectId(projectId, tx);
|
||||||
return {
|
return {
|
||||||
secrets: { created: newSecrets, updated: updatedSecrets, deleted: deletedSecret },
|
secrets: { created: newSecrets, updated: updatedSecrets, deleted: deletedSecret },
|
||||||
approval: updatedSecretApproval
|
approval: updatedSecretApproval
|
||||||
@@ -983,7 +985,6 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await secretV2BridgeDAL.invalidateSecretCacheByProjectId(projectId);
|
|
||||||
await snapshotService.performSnapshot(folderId);
|
await snapshotService.performSnapshot(folderId);
|
||||||
const [folder] = await folderDAL.findSecretPathByFolderIds(projectId, [folderId]);
|
const [folder] = await folderDAL.findSecretPathByFolderIds(projectId, [folderId]);
|
||||||
if (!folder) {
|
if (!folder) {
|
||||||
|
|||||||
@@ -509,9 +509,9 @@ export const secretReplicationServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
await secretV2BridgeDAL.invalidateSecretCacheByProjectId(projectId, tx);
|
||||||
});
|
});
|
||||||
|
|
||||||
await secretV2BridgeDAL.invalidateSecretCacheByProjectId(projectId);
|
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
orgId,
|
orgId,
|
||||||
|
|||||||
+1
-2
@@ -361,9 +361,8 @@ export const secretRotationQueueFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
await secretV2BridgeDAL.invalidateSecretCacheByProjectId(secretRotation.projectId, tx);
|
||||||
});
|
});
|
||||||
|
|
||||||
await secretV2BridgeDAL.invalidateSecretCacheByProjectId(secretRotation.projectId);
|
|
||||||
} else {
|
} else {
|
||||||
if (!botKey)
|
if (!botKey)
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ export const snapshotDALFactory = (db: TDbClient) => {
|
|||||||
// then joins with respective secrets and folder
|
// then joins with respective secrets and folder
|
||||||
const findRecursivelySnapshots = async (snapshotId: string, tx?: Knex) => {
|
const findRecursivelySnapshots = async (snapshotId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const data = await (tx || db)
|
const data = await (tx || db.replicaNode())
|
||||||
.withRecursive("parent", (qb) => {
|
.withRecursive("parent", (qb) => {
|
||||||
void qb
|
void qb
|
||||||
.from(TableName.Snapshot)
|
.from(TableName.Snapshot)
|
||||||
@@ -419,7 +419,7 @@ export const snapshotDALFactory = (db: TDbClient) => {
|
|||||||
// then joins with respective secrets and folder
|
// then joins with respective secrets and folder
|
||||||
const findRecursivelySnapshotsV2Bridge = async (snapshotId: string, tx?: Knex) => {
|
const findRecursivelySnapshotsV2Bridge = async (snapshotId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const data = await (tx || db)
|
const data = await (tx || db.replicaNode())
|
||||||
.withRecursive("parent", (qb) => {
|
.withRecursive("parent", (qb) => {
|
||||||
void qb
|
void qb
|
||||||
.from(TableName.Snapshot)
|
.from(TableName.Snapshot)
|
||||||
@@ -581,7 +581,11 @@ export const snapshotDALFactory = (db: TDbClient) => {
|
|||||||
const docs = await (tx || db.replicaNode())(TableName.Snapshot)
|
const docs = await (tx || db.replicaNode())(TableName.Snapshot)
|
||||||
.where(`${TableName.Snapshot}.folderId`, folderId)
|
.where(`${TableName.Snapshot}.folderId`, folderId)
|
||||||
.join<TSecretSnapshots>(
|
.join<TSecretSnapshots>(
|
||||||
(tx || db)(TableName.Snapshot).groupBy("folderId").max("createdAt").select("folderId").as("latestVersion"),
|
(tx || db.replicaNode())(TableName.Snapshot)
|
||||||
|
.groupBy("folderId")
|
||||||
|
.max("createdAt")
|
||||||
|
.select("folderId")
|
||||||
|
.as("latestVersion"),
|
||||||
(bd) => {
|
(bd) => {
|
||||||
bd.on(`${TableName.Snapshot}.folderId`, "latestVersion.folderId").andOn(
|
bd.on(`${TableName.Snapshot}.folderId`, "latestVersion.folderId").andOn(
|
||||||
`${TableName.Snapshot}.createdAt`,
|
`${TableName.Snapshot}.createdAt`,
|
||||||
@@ -766,7 +770,7 @@ export const snapshotDALFactory = (db: TDbClient) => {
|
|||||||
)
|
)
|
||||||
.orderBy(`${TableName.Snapshot}.createdAt`, "desc")
|
.orderBy(`${TableName.Snapshot}.createdAt`, "desc")
|
||||||
.where(`${TableName.Snapshot}.folderId`, folderId);
|
.where(`${TableName.Snapshot}.folderId`, folderId);
|
||||||
const data = await (tx || db)
|
const data = await (tx || db.replicaNode())
|
||||||
.with("w", query)
|
.with("w", query)
|
||||||
.select("*")
|
.select("*")
|
||||||
.from<Awaited<typeof query>[number]>("w")
|
.from<Awaited<typeof query>[number]>("w")
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TDbClient } from "@app/db";
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { ormify, TOrmify } from "@app/lib/knex";
|
||||||
|
import { logger } from "@app/lib/logger";
|
||||||
|
import { QueueName } from "@app/queue";
|
||||||
|
|
||||||
|
export interface TKeyValueStoreDALFactory extends TOrmify<TableName.KeyValueStore> {
|
||||||
|
incrementBy: (key: string, dto: { incr?: number; tx?: Knex; expiresAt?: Date }) => Promise<number>;
|
||||||
|
findOneInt: (key: string, tx?: Knex) => Promise<number | undefined>;
|
||||||
|
pruneExpiredKeys: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QUERY_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
|
||||||
|
const CACHE_KEY_PRUNE_BATCH_SIZE = 10000;
|
||||||
|
const MAX_RETRY_ON_FAILURE = 3;
|
||||||
|
|
||||||
|
export const keyValueStoreDALFactory = (db: TDbClient): TKeyValueStoreDALFactory => {
|
||||||
|
const keyValueStoreOrm = ormify(db, TableName.KeyValueStore);
|
||||||
|
|
||||||
|
const incrementBy: TKeyValueStoreDALFactory["incrementBy"] = async (key, { incr = 1, tx, expiresAt }) => {
|
||||||
|
return (tx || db)(TableName.KeyValueStore)
|
||||||
|
.insert({ key, integerValue: 1, expiresAt })
|
||||||
|
.onConflict("key")
|
||||||
|
.merge({
|
||||||
|
integerValue: db.raw(`"${TableName.KeyValueStore}"."integerValue" + ?`, [incr]),
|
||||||
|
expiresAt
|
||||||
|
})
|
||||||
|
.returning("integerValue")
|
||||||
|
.then((result) => Number(result[0]?.integerValue || 0));
|
||||||
|
};
|
||||||
|
|
||||||
|
const findOneInt: TKeyValueStoreDALFactory["findOneInt"] = async (key, tx) => {
|
||||||
|
const doc = await (tx || db.replicaNode())(TableName.KeyValueStore)
|
||||||
|
.where({ key })
|
||||||
|
.andWhere(
|
||||||
|
(builder) =>
|
||||||
|
void builder
|
||||||
|
.whereNull("expiresAt") // no expiry
|
||||||
|
.orWhere("expiresAt", ">", db.fn.now()) // or not expired
|
||||||
|
)
|
||||||
|
.first()
|
||||||
|
.select("integerValue");
|
||||||
|
return Number(doc?.integerValue || 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const pruneExpiredKeys: TKeyValueStoreDALFactory["pruneExpiredKeys"] = async () => {
|
||||||
|
let deletedIds: { key: string }[] = [];
|
||||||
|
let numberOfRetryOnFailure = 0;
|
||||||
|
let isRetrying = false;
|
||||||
|
|
||||||
|
logger.info(`${QueueName.DailyResourceCleanUp}: db key value store clean up started`);
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
deletedIds = await db.transaction(async (trx) => {
|
||||||
|
await trx.raw(`SET statement_timeout = ${QUERY_TIMEOUT_MS}`);
|
||||||
|
|
||||||
|
const findExpiredKeysSubQuery = trx(TableName.KeyValueStore)
|
||||||
|
.where("expiresAt", "<", db.fn.now())
|
||||||
|
.select("key")
|
||||||
|
.limit(CACHE_KEY_PRUNE_BATCH_SIZE);
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const results = await trx(TableName.KeyValueStore)
|
||||||
|
.whereIn("key", findExpiredKeysSubQuery)
|
||||||
|
.del()
|
||||||
|
.returning("key");
|
||||||
|
|
||||||
|
return results;
|
||||||
|
});
|
||||||
|
|
||||||
|
numberOfRetryOnFailure = 0; // reset
|
||||||
|
} catch (error) {
|
||||||
|
numberOfRetryOnFailure += 1;
|
||||||
|
deletedIds = [];
|
||||||
|
logger.error(error, "Failed to clean up db key value");
|
||||||
|
} finally {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, 10); // time to breathe for db
|
||||||
|
});
|
||||||
|
}
|
||||||
|
isRetrying = numberOfRetryOnFailure > 0;
|
||||||
|
} while (deletedIds.length > 0 || (isRetrying && numberOfRetryOnFailure < MAX_RETRY_ON_FAILURE));
|
||||||
|
logger.info(`${QueueName.DailyResourceCleanUp}: db key value store clean up completed`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...keyValueStoreOrm, incrementBy, findOneInt, pruneExpiredKeys };
|
||||||
|
};
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
import { Cluster, Redis } from "ioredis";
|
import { Cluster, Redis } from "ioredis";
|
||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { buildRedisFromConfig, TRedisConfigKeys } from "@app/lib/config/redis";
|
import { buildRedisFromConfig, TRedisConfigKeys } from "@app/lib/config/redis";
|
||||||
import { pgAdvisoryLockHashText } from "@app/lib/crypto/hashtext";
|
import { pgAdvisoryLockHashText } from "@app/lib/crypto/hashtext";
|
||||||
import { applyJitter } from "@app/lib/dates";
|
import { applyJitter } from "@app/lib/dates";
|
||||||
import { delay as delayMs } from "@app/lib/delay";
|
import { delay as delayMs } from "@app/lib/delay";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { ExecutionResult, Redlock, Settings } from "@app/lib/red-lock";
|
import { ExecutionResult, Redlock, Settings } from "@app/lib/red-lock";
|
||||||
|
|
||||||
|
import { TKeyValueStoreDALFactory } from "./key-value-store-dal";
|
||||||
|
|
||||||
export const PgSqlLock = {
|
export const PgSqlLock = {
|
||||||
BootUpMigration: 2023,
|
BootUpMigration: 2023,
|
||||||
SuperAdminInit: 2024,
|
SuperAdminInit: 2024,
|
||||||
@@ -95,13 +99,17 @@ export type TKeyStoreFactory = {
|
|||||||
deleteItemsByKeyIn: (keys: string[]) => Promise<number>;
|
deleteItemsByKeyIn: (keys: string[]) => Promise<number>;
|
||||||
deleteItems: (arg: TDeleteItems) => Promise<number>;
|
deleteItems: (arg: TDeleteItems) => Promise<number>;
|
||||||
incrementBy: (key: string, value: number) => Promise<number>;
|
incrementBy: (key: string, value: number) => Promise<number>;
|
||||||
|
getKeysByPattern: (pattern: string, limit?: number) => Promise<string[]>;
|
||||||
|
// pg
|
||||||
|
pgIncrementBy: (key: string, dto: { incr?: number; expiry?: string; tx?: Knex }) => Promise<number>;
|
||||||
|
pgGetIntItem: (key: string, prefix?: string) => Promise<number | undefined>;
|
||||||
|
// locks
|
||||||
acquireLock(
|
acquireLock(
|
||||||
resources: string[],
|
resources: string[],
|
||||||
duration: number,
|
duration: number,
|
||||||
settings?: Partial<Settings>
|
settings?: Partial<Settings>
|
||||||
): Promise<{ release: () => Promise<ExecutionResult> }>;
|
): Promise<{ release: () => Promise<ExecutionResult> }>;
|
||||||
waitTillReady: ({ key, waitingCb, keyCheckCb, waitIteration, delay, jitter }: TWaitTillReady) => Promise<void>;
|
waitTillReady: ({ key, waitingCb, keyCheckCb, waitIteration, delay, jitter }: TWaitTillReady) => Promise<void>;
|
||||||
getKeysByPattern: (pattern: string, limit?: number) => Promise<string[]>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const pickPrimaryOrSecondaryRedis = (primary: Redis | Cluster, secondaries?: Array<Redis | Cluster>) => {
|
const pickPrimaryOrSecondaryRedis = (primary: Redis | Cluster, secondaries?: Array<Redis | Cluster>) => {
|
||||||
@@ -114,7 +122,10 @@ interface TKeyStoreFactoryDTO extends TRedisConfigKeys {
|
|||||||
REDIS_READ_REPLICAS?: { host: string; port: number }[];
|
REDIS_READ_REPLICAS?: { host: string; port: number }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const keyStoreFactory = (redisConfigKeys: TKeyStoreFactoryDTO): TKeyStoreFactory => {
|
export const keyStoreFactory = (
|
||||||
|
redisConfigKeys: TKeyStoreFactoryDTO,
|
||||||
|
keyValueStoreDAL: TKeyValueStoreDALFactory
|
||||||
|
): TKeyStoreFactory => {
|
||||||
const primaryRedis = buildRedisFromConfig(redisConfigKeys);
|
const primaryRedis = buildRedisFromConfig(redisConfigKeys);
|
||||||
const redisReadReplicas = redisConfigKeys.REDIS_READ_REPLICAS?.map((el) => {
|
const redisReadReplicas = redisConfigKeys.REDIS_READ_REPLICAS?.map((el) => {
|
||||||
if (redisConfigKeys.REDIS_URL) {
|
if (redisConfigKeys.REDIS_URL) {
|
||||||
@@ -189,29 +200,6 @@ export const keyStoreFactory = (redisConfigKeys: TKeyStoreFactoryDTO): TKeyStore
|
|||||||
|
|
||||||
const setExpiry = async (key: string, expiryInSeconds: number) => primaryRedis.expire(key, expiryInSeconds);
|
const setExpiry = async (key: string, expiryInSeconds: number) => primaryRedis.expire(key, expiryInSeconds);
|
||||||
|
|
||||||
const waitTillReady = async ({
|
|
||||||
key,
|
|
||||||
waitingCb,
|
|
||||||
keyCheckCb,
|
|
||||||
waitIteration = 10,
|
|
||||||
delay = 1000,
|
|
||||||
jitter = 200
|
|
||||||
}: TWaitTillReady) => {
|
|
||||||
let attempts = 0;
|
|
||||||
let isReady = keyCheckCb(await getItem(key));
|
|
||||||
while (!isReady) {
|
|
||||||
if (attempts > waitIteration) return;
|
|
||||||
// eslint-disable-next-line
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
waitingCb?.();
|
|
||||||
setTimeout(resolve, Math.max(0, applyJitter(delay, jitter)));
|
|
||||||
});
|
|
||||||
attempts += 1;
|
|
||||||
// eslint-disable-next-line
|
|
||||||
isReady = keyCheckCb(await getItem(key));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getKeysByPattern = async (pattern: string, limit?: number) => {
|
const getKeysByPattern = async (pattern: string, limit?: number) => {
|
||||||
let cursor = "0";
|
let cursor = "0";
|
||||||
const allKeys: string[] = [];
|
const allKeys: string[] = [];
|
||||||
@@ -236,6 +224,37 @@ export const keyStoreFactory = (redisConfigKeys: TKeyStoreFactoryDTO): TKeyStore
|
|||||||
return allKeys;
|
return allKeys;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const pgIncrementBy: TKeyStoreFactory["pgIncrementBy"] = async (key, { incr = 1, tx, expiry }) => {
|
||||||
|
const expiresAt = expiry ? new Date(Date.now() + ms(expiry)) : undefined;
|
||||||
|
return keyValueStoreDAL.incrementBy(key, { incr, expiresAt, tx });
|
||||||
|
};
|
||||||
|
|
||||||
|
const pgGetIntItem = async (key: string, prefix?: string) =>
|
||||||
|
keyValueStoreDAL.findOneInt(prefix ? `${prefix}:${key}` : key);
|
||||||
|
|
||||||
|
const waitTillReady = async ({
|
||||||
|
key,
|
||||||
|
waitingCb,
|
||||||
|
keyCheckCb,
|
||||||
|
waitIteration = 10,
|
||||||
|
delay = 1000,
|
||||||
|
jitter = 200
|
||||||
|
}: TWaitTillReady) => {
|
||||||
|
let attempts = 0;
|
||||||
|
let isReady = keyCheckCb(await getItem(key));
|
||||||
|
while (!isReady) {
|
||||||
|
if (attempts > waitIteration) return;
|
||||||
|
// eslint-disable-next-line
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
waitingCb?.();
|
||||||
|
setTimeout(resolve, Math.max(0, applyJitter(delay, jitter)));
|
||||||
|
});
|
||||||
|
attempts += 1;
|
||||||
|
// eslint-disable-next-line
|
||||||
|
isReady = keyCheckCb(await getItem(key));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
setItem,
|
setItem,
|
||||||
getItem,
|
getItem,
|
||||||
@@ -250,6 +269,8 @@ export const keyStoreFactory = (redisConfigKeys: TKeyStoreFactoryDTO): TKeyStore
|
|||||||
waitTillReady,
|
waitTillReady,
|
||||||
getKeysByPattern,
|
getKeysByPattern,
|
||||||
deleteItemsByKeyIn,
|
deleteItemsByKeyIn,
|
||||||
getItems
|
getItems,
|
||||||
|
pgGetIntItem,
|
||||||
|
pgIncrementBy
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -53,6 +53,15 @@ export const inMemoryKeyStore = (): TKeyStoreFactory => {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
pgGetIntItem: async (key) => {
|
||||||
|
const value = store[key];
|
||||||
|
if (typeof value === "number") {
|
||||||
|
return Number(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pgIncrementBy: async () => {
|
||||||
|
return 1;
|
||||||
|
},
|
||||||
incrementBy: async () => {
|
incrementBy: async () => {
|
||||||
return 1;
|
return 1;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -410,6 +410,7 @@ const envSchema = z
|
|||||||
Boolean(data.INF_APP_CONNECTION_GITHUB_RADAR_APP_CLIENT_ID) &&
|
Boolean(data.INF_APP_CONNECTION_GITHUB_RADAR_APP_CLIENT_ID) &&
|
||||||
Boolean(data.INF_APP_CONNECTION_GITHUB_RADAR_APP_CLIENT_SECRET) &&
|
Boolean(data.INF_APP_CONNECTION_GITHUB_RADAR_APP_CLIENT_SECRET) &&
|
||||||
Boolean(data.INF_APP_CONNECTION_GITHUB_RADAR_APP_WEBHOOK_SECRET),
|
Boolean(data.INF_APP_CONNECTION_GITHUB_RADAR_APP_WEBHOOK_SECRET),
|
||||||
|
isSecondaryInstance: Boolean(data.INFISICAL_PRIMARY_INSTANCE_URL),
|
||||||
isHsmConfigured:
|
isHsmConfigured:
|
||||||
Boolean(data.HSM_LIB_PATH) && Boolean(data.HSM_PIN) && Boolean(data.HSM_KEY_LABEL) && data.HSM_SLOT !== undefined,
|
Boolean(data.HSM_LIB_PATH) && Boolean(data.HSM_PIN) && Boolean(data.HSM_KEY_LABEL) && data.HSM_SLOT !== undefined,
|
||||||
samlDefaultOrgSlug: data.DEFAULT_SAML_ORG_SLUG,
|
samlDefaultOrgSlug: data.DEFAULT_SAML_ORG_SLUG,
|
||||||
|
|||||||
@@ -250,12 +250,12 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(
|
|||||||
.returning("*");
|
.returning("*");
|
||||||
if ($incr) {
|
if ($incr) {
|
||||||
Object.entries($incr).forEach(([incrementField, incrementValue]) => {
|
Object.entries($incr).forEach(([incrementField, incrementValue]) => {
|
||||||
void query.increment(incrementField, incrementValue);
|
void query.increment(incrementField, incrementValue as number);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ($decr) {
|
if ($decr) {
|
||||||
Object.entries($decr).forEach(([incrementField, incrementValue]) => {
|
Object.entries($decr).forEach(([incrementField, incrementValue]) => {
|
||||||
void query.decrement(incrementField, incrementValue);
|
void query.decrement(incrementField, incrementValue as number);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const [docs] = await query;
|
const [docs] = await query;
|
||||||
@@ -273,12 +273,12 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(
|
|||||||
// increment and decrement operation in update
|
// increment and decrement operation in update
|
||||||
if ($incr) {
|
if ($incr) {
|
||||||
Object.entries($incr).forEach(([incrementField, incrementValue]) => {
|
Object.entries($incr).forEach(([incrementField, incrementValue]) => {
|
||||||
void query.increment(incrementField, incrementValue);
|
void query.increment(incrementField, incrementValue as number);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ($decr) {
|
if ($decr) {
|
||||||
Object.entries($decr).forEach(([incrementField, incrementValue]) => {
|
Object.entries($decr).forEach(([incrementField, incrementValue]) => {
|
||||||
void query.increment(incrementField, incrementValue);
|
void query.decrement(incrementField, incrementValue as number);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return (await query) as Tables[Tname]["base"][];
|
return (await query) as Tables[Tname]["base"][];
|
||||||
|
|||||||
+3
-1
@@ -5,6 +5,7 @@ import "./lib/telemetry/instrumentation";
|
|||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
||||||
|
import { keyValueStoreDALFactory } from "@app/keystore/key-value-store-dal";
|
||||||
|
|
||||||
import { runMigrations } from "./auto-start-migrations";
|
import { runMigrations } from "./auto-start-migrations";
|
||||||
import { initAuditLogDbConnection, initDbConnection } from "./db";
|
import { initAuditLogDbConnection, initDbConnection } from "./db";
|
||||||
@@ -54,7 +55,8 @@ const run = async () => {
|
|||||||
|
|
||||||
await queue.initialize();
|
await queue.initialize();
|
||||||
|
|
||||||
const keyStore = keyStoreFactory(envConfig);
|
const keyValueStoreDAL = keyValueStoreDALFactory(db);
|
||||||
|
const keyStore = keyStoreFactory(envConfig, keyValueStoreDAL);
|
||||||
const redis = buildRedisFromConfig(envConfig);
|
const redis = buildRedisFromConfig(envConfig);
|
||||||
|
|
||||||
const hsmModule = initializeHsmModule(envConfig);
|
const hsmModule = initializeHsmModule(envConfig);
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ import { sshHostGroupMembershipDALFactory } from "@app/ee/services/ssh-host-grou
|
|||||||
import { sshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service";
|
import { sshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service";
|
||||||
import { trustedIpDALFactory } from "@app/ee/services/trusted-ip/trusted-ip-dal";
|
import { trustedIpDALFactory } from "@app/ee/services/trusted-ip/trusted-ip-dal";
|
||||||
import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service";
|
import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service";
|
||||||
|
import { keyValueStoreDALFactory } from "@app/keystore/key-value-store-dal";
|
||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig, TEnvConfig } from "@app/lib/config/env";
|
import { getConfig, TEnvConfig } from "@app/lib/config/env";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
@@ -507,6 +508,7 @@ export const registerRoutes = async (
|
|||||||
const microsoftTeamsIntegrationDAL = microsoftTeamsIntegrationDALFactory(db);
|
const microsoftTeamsIntegrationDAL = microsoftTeamsIntegrationDALFactory(db);
|
||||||
const projectMicrosoftTeamsConfigDAL = projectMicrosoftTeamsConfigDALFactory(db);
|
const projectMicrosoftTeamsConfigDAL = projectMicrosoftTeamsConfigDALFactory(db);
|
||||||
const secretScanningV2DAL = secretScanningV2DALFactory(db);
|
const secretScanningV2DAL = secretScanningV2DALFactory(db);
|
||||||
|
const keyValueStoreDAL = keyValueStoreDALFactory(db);
|
||||||
|
|
||||||
const eventBusService = eventBusFactory(server.redis);
|
const eventBusService = eventBusFactory(server.redis);
|
||||||
const sseService = sseServiceFactory(eventBusService, server.redis);
|
const sseService = sseServiceFactory(eventBusService, server.redis);
|
||||||
@@ -643,6 +645,7 @@ export const registerRoutes = async (
|
|||||||
const folderTreeCheckpointDAL = folderTreeCheckpointDALFactory(db);
|
const folderTreeCheckpointDAL = folderTreeCheckpointDALFactory(db);
|
||||||
const folderCommitDAL = folderCommitDALFactory(db);
|
const folderCommitDAL = folderCommitDALFactory(db);
|
||||||
const folderTreeCheckpointResourcesDAL = folderTreeCheckpointResourcesDALFactory(db);
|
const folderTreeCheckpointResourcesDAL = folderTreeCheckpointResourcesDALFactory(db);
|
||||||
|
|
||||||
const folderCommitQueueService = folderCommitQueueServiceFactory({
|
const folderCommitQueueService = folderCommitQueueServiceFactory({
|
||||||
queueService,
|
queueService,
|
||||||
folderTreeCheckpointDAL,
|
folderTreeCheckpointDAL,
|
||||||
@@ -1682,6 +1685,7 @@ export const registerRoutes = async (
|
|||||||
userDAL,
|
userDAL,
|
||||||
identityDAL
|
identityDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const dailyResourceCleanUp = dailyResourceCleanUpQueueServiceFactory({
|
const dailyResourceCleanUp = dailyResourceCleanUpQueueServiceFactory({
|
||||||
auditLogDAL,
|
auditLogDAL,
|
||||||
queueService,
|
queueService,
|
||||||
@@ -1694,7 +1698,8 @@ export const registerRoutes = async (
|
|||||||
identityUniversalAuthClientSecretDAL: identityUaClientSecretDAL,
|
identityUniversalAuthClientSecretDAL: identityUaClientSecretDAL,
|
||||||
serviceTokenService,
|
serviceTokenService,
|
||||||
orgService,
|
orgService,
|
||||||
userNotificationDAL
|
userNotificationDAL,
|
||||||
|
keyValueStoreDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const dailyReminderQueueService = dailyReminderQueueServiceFactory({
|
const dailyReminderQueueService = dailyReminderQueueServiceFactory({
|
||||||
|
|||||||
@@ -661,7 +661,7 @@ describe("folderCommitServiceFactory", () => {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(mockFolderCommitDAL.create).toHaveBeenCalled();
|
expect(mockFolderCommitDAL.create).toHaveBeenCalled();
|
||||||
expect(mockSecretV2BridgeDAL.invalidateSecretCacheByProjectId).toHaveBeenCalledWith(projectId);
|
expect(mockSecretV2BridgeDAL.invalidateSecretCacheByProjectId).toHaveBeenCalledWith(projectId, {});
|
||||||
|
|
||||||
// Check that we got the right counts
|
// Check that we got the right counts
|
||||||
expect(result.totalChanges).toEqual(2);
|
expect(result.totalChanges).toEqual(2);
|
||||||
|
|||||||
@@ -1386,7 +1386,7 @@ export const folderCommitServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Invalidate cache to reflect the changes
|
// Invalidate cache to reflect the changes
|
||||||
await secretV2BridgeDAL.invalidateSecretCacheByProjectId(projectId);
|
await secretV2BridgeDAL.invalidateSecretCacheByProjectId(projectId, tx);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
secretChangesCount: secretChanges.length,
|
secretChangesCount: secretChanges.length,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export const identityDALFactory = (db: TDbClient) => {
|
|||||||
} as const;
|
} as const;
|
||||||
const tableName = authMethodToTableName[authMethod];
|
const tableName = authMethodToTableName[authMethod];
|
||||||
if (!tableName) return;
|
if (!tableName) return;
|
||||||
const data = await db(tableName).where({ identityId }).first();
|
const data = await db.replicaNode()(tableName).where({ identityId }).first();
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
return data.accessTokenTrustedIps;
|
return data.accessTokenTrustedIps;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const integrationAuthDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const getByOrg = async (orgId: string, tx?: Knex) => {
|
const getByOrg = async (orgId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const integrationAuths = await (tx || db)(TableName.IntegrationAuth)
|
const integrationAuths = await (tx || db.replicaNode())(TableName.IntegrationAuth)
|
||||||
.join(TableName.Project, `${TableName.Project}.id`, `${TableName.IntegrationAuth}.projectId`)
|
.join(TableName.Project, `${TableName.Project}.id`, `${TableName.IntegrationAuth}.projectId`)
|
||||||
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Project}.orgId`)
|
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Project}.orgId`)
|
||||||
.where(`${TableName.Organization}.id`, "=", orgId)
|
.where(`${TableName.Organization}.id`, "=", orgId)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const kmsRootConfigDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const result = await (tx || db)(TableName.KmsServerRootConfig)
|
const result = await (tx || db?.replicaNode?.() || db)(TableName.KmsServerRootConfig)
|
||||||
.where({ id } as never)
|
.where({ id } as never)
|
||||||
.first("*");
|
.first("*");
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export const reminderDALFactory = (db: TDbClient) => {
|
|||||||
const findSecretDailyReminders = async (tx?: Knex) => {
|
const findSecretDailyReminders = async (tx?: Knex) => {
|
||||||
const { startOfDay, endOfDay } = getTodayDateRange();
|
const { startOfDay, endOfDay } = getTodayDateRange();
|
||||||
|
|
||||||
const rawReminders = await (tx || db)(TableName.Reminder)
|
const rawReminders = await (tx || db.replicaNode())(TableName.Reminder)
|
||||||
.whereBetween("nextReminderDate", [startOfDay, endOfDay])
|
.whereBetween("nextReminderDate", [startOfDay, endOfDay])
|
||||||
.leftJoin(TableName.ReminderRecipient, `${TableName.Reminder}.id`, `${TableName.ReminderRecipient}.reminderId`)
|
.leftJoin(TableName.ReminderRecipient, `${TableName.Reminder}.id`, `${TableName.ReminderRecipient}.reminderId`)
|
||||||
.leftJoin<TUsers>(TableName.Users, `${TableName.ReminderRecipient}.userId`, `${TableName.Users}.id`)
|
.leftJoin<TUsers>(TableName.Users, `${TableName.ReminderRecipient}.userId`, `${TableName.Users}.id`)
|
||||||
@@ -90,7 +90,7 @@ export const reminderDALFactory = (db: TDbClient) => {
|
|||||||
const futureDate = new Date(startOfDay);
|
const futureDate = new Date(startOfDay);
|
||||||
futureDate.setDate(futureDate.getDate() + daysAhead);
|
futureDate.setDate(futureDate.getDate() + daysAhead);
|
||||||
|
|
||||||
const reminders = await (tx || db)(TableName.Reminder)
|
const reminders = await (tx || db.replicaNode())(TableName.Reminder)
|
||||||
.where("nextReminderDate", ">=", startOfDay)
|
.where("nextReminderDate", ">=", startOfDay)
|
||||||
.where("nextReminderDate", "<=", futureDate)
|
.where("nextReminderDate", "<=", futureDate)
|
||||||
.orderBy("nextReminderDate", "asc")
|
.orderBy("nextReminderDate", "asc")
|
||||||
@@ -101,7 +101,7 @@ export const reminderDALFactory = (db: TDbClient) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const findSecretReminder = async (secretId: string, tx?: Knex) => {
|
const findSecretReminder = async (secretId: string, tx?: Knex) => {
|
||||||
const rawReminders = await (tx || db)(TableName.Reminder)
|
const rawReminders = await (tx || db.replicaNode())(TableName.Reminder)
|
||||||
.where(`${TableName.Reminder}.secretId`, secretId)
|
.where(`${TableName.Reminder}.secretId`, secretId)
|
||||||
.leftJoin(TableName.ReminderRecipient, `${TableName.Reminder}.id`, `${TableName.ReminderRecipient}.reminderId`)
|
.leftJoin(TableName.ReminderRecipient, `${TableName.Reminder}.id`, `${TableName.ReminderRecipient}.reminderId`)
|
||||||
.select(selectAllTableCols(TableName.Reminder))
|
.select(selectAllTableCols(TableName.Reminder))
|
||||||
@@ -125,7 +125,7 @@ export const reminderDALFactory = (db: TDbClient) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const findSecretReminders = async (secretIds: string[], tx?: Knex) => {
|
const findSecretReminders = async (secretIds: string[], tx?: Knex) => {
|
||||||
const rawReminders = await (tx || db)(TableName.Reminder)
|
const rawReminders = await (tx || db.replicaNode())(TableName.Reminder)
|
||||||
.whereIn(`${TableName.Reminder}.secretId`, secretIds)
|
.whereIn(`${TableName.Reminder}.secretId`, secretIds)
|
||||||
.leftJoin(TableName.ReminderRecipient, `${TableName.Reminder}.id`, `${TableName.ReminderRecipient}.reminderId`)
|
.leftJoin(TableName.ReminderRecipient, `${TableName.Reminder}.id`, `${TableName.ReminderRecipient}.reminderId`)
|
||||||
.select(selectAllTableCols(TableName.Reminder))
|
.select(selectAllTableCols(TableName.Reminder))
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { TAuditLogDALFactory } from "@app/ee/services/audit-log/audit-log-dal";
|
import { TAuditLogDALFactory } from "@app/ee/services/audit-log/audit-log-dal";
|
||||||
import { TSnapshotDALFactory } from "@app/ee/services/secret-snapshot/snapshot-dal";
|
import { TSnapshotDALFactory } from "@app/ee/services/secret-snapshot/snapshot-dal";
|
||||||
|
import { TKeyValueStoreDALFactory } from "@app/keystore/key-value-store-dal";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
||||||
@@ -27,6 +28,7 @@ type TDailyResourceCleanUpQueueServiceFactoryDep = {
|
|||||||
queueService: TQueueServiceFactory;
|
queueService: TQueueServiceFactory;
|
||||||
orgService: TOrgServiceFactory;
|
orgService: TOrgServiceFactory;
|
||||||
userNotificationDAL: Pick<TUserNotificationDALFactory, "pruneNotifications">;
|
userNotificationDAL: Pick<TUserNotificationDALFactory, "pruneNotifications">;
|
||||||
|
keyValueStoreDAL: Pick<TKeyValueStoreDALFactory, "pruneExpiredKeys">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDailyResourceCleanUpQueueServiceFactory = ReturnType<typeof dailyResourceCleanUpQueueServiceFactory>;
|
export type TDailyResourceCleanUpQueueServiceFactory = ReturnType<typeof dailyResourceCleanUpQueueServiceFactory>;
|
||||||
@@ -43,7 +45,8 @@ export const dailyResourceCleanUpQueueServiceFactory = ({
|
|||||||
identityUniversalAuthClientSecretDAL,
|
identityUniversalAuthClientSecretDAL,
|
||||||
serviceTokenService,
|
serviceTokenService,
|
||||||
orgService,
|
orgService,
|
||||||
userNotificationDAL
|
userNotificationDAL,
|
||||||
|
keyValueStoreDAL
|
||||||
}: TDailyResourceCleanUpQueueServiceFactoryDep) => {
|
}: TDailyResourceCleanUpQueueServiceFactoryDep) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
@@ -52,6 +55,10 @@ export const dailyResourceCleanUpQueueServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
|
if (appCfg.isSecondaryInstance) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await queueService.stopRepeatableJob(
|
await queueService.stopRepeatableJob(
|
||||||
QueueName.AuditLogPrune,
|
QueueName.AuditLogPrune,
|
||||||
QueueJobs.AuditLogPrune,
|
QueueJobs.AuditLogPrune,
|
||||||
@@ -82,6 +89,7 @@ export const dailyResourceCleanUpQueueServiceFactory = ({
|
|||||||
await orgService.notifyInvitedUsers();
|
await orgService.notifyInvitedUsers();
|
||||||
await auditLogDAL.pruneAuditLog();
|
await auditLogDAL.pruneAuditLog();
|
||||||
await userNotificationDAL.pruneNotifications();
|
await userNotificationDAL.pruneNotifications();
|
||||||
|
await keyValueStoreDAL.pruneExpiredKeys();
|
||||||
logger.info(`${QueueName.DailyResourceCleanUp}: queue task completed`);
|
logger.info(`${QueueName.DailyResourceCleanUp}: queue task completed`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error, `${QueueName.DailyResourceCleanUp}: resource cleanup failed`);
|
logger.error(error, `${QueueName.DailyResourceCleanUp}: resource cleanup failed`);
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export const secretFolderVersionDALFactory = (db: TDbClient) => {
|
|||||||
)
|
)
|
||||||
.whereIn(`${TableName.SecretFolderVersion}.folderId`, folderIds)
|
.whereIn(`${TableName.SecretFolderVersion}.folderId`, folderIds)
|
||||||
.join(
|
.join(
|
||||||
(tx || db)(TableName.SecretFolderVersion)
|
(tx || db.replicaNode())(TableName.SecretFolderVersion)
|
||||||
.groupBy("folderId")
|
.groupBy("folderId")
|
||||||
.max("version")
|
.max("version")
|
||||||
.select("folderId")
|
.select("folderId")
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
|||||||
// we are using postion based sorting as its a small list
|
// we are using postion based sorting as its a small list
|
||||||
// this will return the last value of the position in a folder with secret imports
|
// this will return the last value of the position in a folder with secret imports
|
||||||
const findLastImportPosition = async (folderId: string, tx?: Knex) => {
|
const findLastImportPosition = async (folderId: string, tx?: Knex) => {
|
||||||
const lastPos = await (tx || db)(TableName.SecretImport)
|
const lastPos = await (tx || db.replicaNode())(TableName.SecretImport)
|
||||||
.where({ folderId })
|
.where({ folderId })
|
||||||
.max("position", { as: "position" })
|
.max("position", { as: "position" })
|
||||||
.first();
|
.first();
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ export const secretSharingDALFactory = (db: TDbClient) => {
|
|||||||
const findActiveSharedSecrets = async (filters: Partial<TSecretSharing>, tx?: Knex) => {
|
const findActiveSharedSecrets = async (filters: Partial<TSecretSharing>, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
return await (tx || db)(TableName.SecretSharing)
|
return await (tx || db.replicaNode())(TableName.SecretSharing)
|
||||||
.where(filters)
|
.where(filters)
|
||||||
.andWhere("expiresAt", ">", now)
|
.andWhere("expiresAt", ">", now)
|
||||||
.andWhere("encryptedValue", "<>", "")
|
.andWhere("encryptedValue", "<>", "")
|
||||||
|
|||||||
@@ -50,20 +50,19 @@ interface TSecretV2DalArg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SECRET_DAL_TTL = () => applyJitter(10 * 60, 2 * 60);
|
export const SECRET_DAL_TTL = () => applyJitter(10 * 60, 2 * 60);
|
||||||
export const SECRET_DAL_VERSION_TTL = 15 * 60;
|
export const SECRET_DAL_VERSION_TTL = "15m";
|
||||||
export const MAX_SECRET_CACHE_BYTES = 25 * 1024 * 1024;
|
export const MAX_SECRET_CACHE_BYTES = 25 * 1024 * 1024;
|
||||||
export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
|
export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
|
||||||
const secretOrm = ormify(db, TableName.SecretV2);
|
const secretOrm = ormify(db, TableName.SecretV2);
|
||||||
|
|
||||||
const invalidateSecretCacheByProjectId = async (projectId: string) => {
|
const invalidateSecretCacheByProjectId = async (projectId: string, tx?: Knex) => {
|
||||||
const secretDalVersionKey = SecretServiceCacheKeys.getSecretDalVersion(projectId);
|
const secretDalVersionKey = SecretServiceCacheKeys.getSecretDalVersion(projectId);
|
||||||
await keyStore.incrementBy(secretDalVersionKey, 1);
|
await keyStore.pgIncrementBy(secretDalVersionKey, { incr: 1, tx, expiry: SECRET_DAL_VERSION_TTL });
|
||||||
await keyStore.setExpiry(secretDalVersionKey, SECRET_DAL_VERSION_TTL);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const findOne = async (filter: Partial<TSecretsV2>, tx?: Knex) => {
|
const findOne = async (filter: Partial<TSecretsV2>, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await (tx || db)(TableName.SecretV2)
|
const docs = await (tx || db.replicaNode())(TableName.SecretV2)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
.where(buildFindFilter(filter, TableName.SecretV2))
|
.where(buildFindFilter(filter, TableName.SecretV2))
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
@@ -144,7 +143,7 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
|
|||||||
const find = async (filter: TFindFilter<TSecretsV2>, opts: TFindOpt<TSecretsV2> = {}) => {
|
const find = async (filter: TFindFilter<TSecretsV2>, opts: TFindOpt<TSecretsV2> = {}) => {
|
||||||
const { offset, limit, sort, tx } = opts;
|
const { offset, limit, sort, tx } = opts;
|
||||||
try {
|
try {
|
||||||
const query = (tx || db)(TableName.SecretV2)
|
const query = (tx || db.replicaNode())(TableName.SecretV2)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
.where(buildFindFilter(filter))
|
.where(buildFindFilter(filter))
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
@@ -888,13 +887,13 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
|
|||||||
const findSecretsWithReminderRecipients = async (ids: string[], limit: number, tx?: Knex) => {
|
const findSecretsWithReminderRecipients = async (ids: string[], limit: number, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
// Create a subquery to get limited secret IDs
|
// Create a subquery to get limited secret IDs
|
||||||
const limitedSecretIds = (tx || db)(TableName.SecretV2)
|
const limitedSecretIds = (tx || db.replicaNode())(TableName.SecretV2)
|
||||||
.whereIn(`${TableName.SecretV2}.id`, ids)
|
.whereIn(`${TableName.SecretV2}.id`, ids)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.select("id");
|
.select("id");
|
||||||
|
|
||||||
// Join with all recipients for the limited secrets
|
// Join with all recipients for the limited secrets
|
||||||
const docs = await (tx || db)(TableName.SecretV2)
|
const docs = await (tx || db.replicaNode())(TableName.SecretV2)
|
||||||
.whereIn(`${TableName.SecretV2}.id`, limitedSecretIds)
|
.whereIn(`${TableName.SecretV2}.id`, limitedSecretIds)
|
||||||
.leftJoin(TableName.Reminder, `${TableName.SecretV2}.id`, `${TableName.Reminder}.secretId`)
|
.leftJoin(TableName.Reminder, `${TableName.SecretV2}.id`, `${TableName.Reminder}.secretId`)
|
||||||
.leftJoin(TableName.ReminderRecipient, `${TableName.Reminder}.id`, `${TableName.ReminderRecipient}.reminderId`)
|
.leftJoin(TableName.ReminderRecipient, `${TableName.Reminder}.id`, `${TableName.ReminderRecipient}.reminderId`)
|
||||||
@@ -926,13 +925,13 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
|
|||||||
const findSecretsWithReminderRecipientsOld = async (ids: string[], limit: number, tx?: Knex) => {
|
const findSecretsWithReminderRecipientsOld = async (ids: string[], limit: number, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
// Create a subquery to get limited secret IDs
|
// Create a subquery to get limited secret IDs
|
||||||
const limitedSecretIds = (tx || db)(TableName.SecretV2)
|
const limitedSecretIds = (tx || db.replicaNode())(TableName.SecretV2)
|
||||||
.whereIn(`${TableName.SecretV2}.id`, ids)
|
.whereIn(`${TableName.SecretV2}.id`, ids)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.select("id");
|
.select("id");
|
||||||
|
|
||||||
// Join with all recipients for the limited secrets
|
// Join with all recipients for the limited secrets
|
||||||
const docs = await (tx || db)(TableName.SecretV2)
|
const docs = await (tx || db.replicaNode())(TableName.SecretV2)
|
||||||
.whereIn(`${TableName.SecretV2}.id`, limitedSecretIds)
|
.whereIn(`${TableName.SecretV2}.id`, limitedSecretIds)
|
||||||
.leftJoin(TableName.Reminder, `${TableName.SecretV2}.id`, `${TableName.Reminder}.secretId`)
|
.leftJoin(TableName.Reminder, `${TableName.SecretV2}.id`, `${TableName.Reminder}.secretId`)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ type TSecretV2BridgeServiceFactoryDep = {
|
|||||||
>;
|
>;
|
||||||
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
|
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
|
||||||
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
keyStore: Pick<TKeyStoreFactory, "getItem" | "setExpiry" | "setItemWithExpiry" | "deleteItem">;
|
keyStore: Pick<TKeyStoreFactory, "getItem" | "setExpiry" | "setItemWithExpiry" | "deleteItem" | "pgGetIntItem">;
|
||||||
reminderService: Pick<TReminderServiceFactory, "createReminder" | "getReminder">;
|
reminderService: Pick<TReminderServiceFactory, "createReminder" | "getReminder">;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -360,6 +360,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await secretDAL.invalidateSecretCacheByProjectId(projectId, tx);
|
||||||
return createdSecret;
|
return createdSecret;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -377,7 +378,6 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await secretDAL.invalidateSecretCacheByProjectId(projectId);
|
|
||||||
if (inputSecret.type === SecretType.Shared) {
|
if (inputSecret.type === SecretType.Shared) {
|
||||||
await snapshotService.performSnapshot(folderId);
|
await snapshotService.performSnapshot(folderId);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
@@ -566,8 +566,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
await $validateSecretReferences(projectId, permission, allSecretReferences);
|
await $validateSecretReferences(projectId, permission, allSecretReferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedSecret = await secretDAL.transaction(async (tx) =>
|
const updatedSecret = await secretDAL.transaction(async (tx) => {
|
||||||
fnSecretBulkUpdate({
|
const modifiedSecretsInDB = await fnSecretBulkUpdate({
|
||||||
folderId,
|
folderId,
|
||||||
orgId: actorOrgId,
|
orgId: actorOrgId,
|
||||||
resourceMetadataDAL,
|
resourceMetadataDAL,
|
||||||
@@ -598,8 +598,11 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorId
|
actorId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
await secretDAL.invalidateSecretCacheByProjectId(projectId, tx);
|
||||||
|
return modifiedSecretsInDB;
|
||||||
|
});
|
||||||
if (inputSecret.secretReminderRepeatDays) {
|
if (inputSecret.secretReminderRepeatDays) {
|
||||||
await reminderService.createReminder({
|
await reminderService.createReminder({
|
||||||
actor,
|
actor,
|
||||||
@@ -615,7 +618,6 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await secretDAL.invalidateSecretCacheByProjectId(projectId);
|
|
||||||
if (inputSecret.type === SecretType.Shared) {
|
if (inputSecret.type === SecretType.Shared) {
|
||||||
await snapshotService.performSnapshot(folderId);
|
await snapshotService.performSnapshot(folderId);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
@@ -715,8 +717,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const deletedSecret = await secretDAL.transaction(async (tx) =>
|
const deletedSecret = await secretDAL.transaction(async (tx) => {
|
||||||
fnSecretBulkDelete({
|
const modifiedSecretsInDB = await fnSecretBulkDelete({
|
||||||
projectId,
|
projectId,
|
||||||
folderId,
|
folderId,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -732,10 +734,11 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
tx
|
tx
|
||||||
})
|
});
|
||||||
);
|
await secretDAL.invalidateSecretCacheByProjectId(projectId, tx);
|
||||||
|
return modifiedSecretsInDB;
|
||||||
|
});
|
||||||
|
|
||||||
await secretDAL.invalidateSecretCacheByProjectId(projectId);
|
|
||||||
if (inputSecret.type === SecretType.Shared) {
|
if (inputSecret.type === SecretType.Shared) {
|
||||||
await snapshotService.performSnapshot(folderId);
|
await snapshotService.performSnapshot(folderId);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
@@ -1027,7 +1030,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
});
|
});
|
||||||
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret);
|
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret);
|
||||||
|
|
||||||
const cachedSecretDalVersion = await keyStore.getItem(SecretServiceCacheKeys.getSecretDalVersion(projectId));
|
const cachedSecretDalVersion = await keyStore.pgGetIntItem(SecretServiceCacheKeys.getSecretDalVersion(projectId));
|
||||||
const secretDalVersion = Number(cachedSecretDalVersion || 0);
|
const secretDalVersion = Number(cachedSecretDalVersion || 0);
|
||||||
const cacheKey = SecretServiceCacheKeys.getSecretsOfServiceLayer(projectId, secretDalVersion, {
|
const cacheKey = SecretServiceCacheKeys.getSecretsOfServiceLayer(projectId, secretDalVersion, {
|
||||||
...dto,
|
...dto,
|
||||||
@@ -1692,7 +1695,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.SecretManager, projectId });
|
await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.SecretManager, projectId });
|
||||||
|
|
||||||
const executeBulkInsert = async (tx: Knex) => {
|
const executeBulkInsert = async (tx: Knex) => {
|
||||||
return fnSecretBulkInsert({
|
const modifiedSecretsInDB = await fnSecretBulkInsert({
|
||||||
inputSecrets: inputSecrets.map((el) => {
|
inputSecrets: inputSecrets.map((el) => {
|
||||||
const references = secretReferencesGroupByInputSecretKey[el.secretKey]?.nestedReferences;
|
const references = secretReferencesGroupByInputSecretKey[el.secretKey]?.nestedReferences;
|
||||||
|
|
||||||
@@ -1728,13 +1731,14 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
});
|
});
|
||||||
|
await secretDAL.invalidateSecretCacheByProjectId(projectId, tx);
|
||||||
|
return modifiedSecretsInDB;
|
||||||
};
|
};
|
||||||
|
|
||||||
const newSecrets = providedTx
|
const newSecrets = providedTx
|
||||||
? await executeBulkInsert(providedTx)
|
? await executeBulkInsert(providedTx)
|
||||||
: await secretDAL.transaction(executeBulkInsert);
|
: await secretDAL.transaction(executeBulkInsert);
|
||||||
|
|
||||||
await secretDAL.invalidateSecretCacheByProjectId(projectId);
|
|
||||||
await snapshotService.performSnapshot(folderId);
|
await snapshotService.performSnapshot(folderId);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
actor,
|
actor,
|
||||||
@@ -2099,6 +2103,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await secretDAL.invalidateSecretCacheByProjectId(projectId, tx);
|
||||||
return updatedSecrets;
|
return updatedSecrets;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2106,7 +2111,6 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
? await executeBulkUpdate(providedTx)
|
? await executeBulkUpdate(providedTx)
|
||||||
: await secretDAL.transaction(executeBulkUpdate);
|
: await secretDAL.transaction(executeBulkUpdate);
|
||||||
|
|
||||||
await secretDAL.invalidateSecretCacheByProjectId(projectId);
|
|
||||||
await Promise.allSettled(folders.map((el) => (el?.id ? snapshotService.performSnapshot(el.id) : undefined)));
|
await Promise.allSettled(folders.map((el) => (el?.id ? snapshotService.performSnapshot(el.id) : undefined)));
|
||||||
await Promise.allSettled(
|
await Promise.allSettled(
|
||||||
folders.map((el) =>
|
folders.map((el) =>
|
||||||
@@ -2233,7 +2237,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const executeBulkDelete = async (tx: Knex) => {
|
const executeBulkDelete = async (tx: Knex) => {
|
||||||
return fnSecretBulkDelete({
|
const modifiedSecretsInDB = await fnSecretBulkDelete({
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretQueueService,
|
secretQueueService,
|
||||||
folderCommitService,
|
folderCommitService,
|
||||||
@@ -2249,6 +2253,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
commitChanges,
|
commitChanges,
|
||||||
tx
|
tx
|
||||||
});
|
});
|
||||||
|
await secretDAL.invalidateSecretCacheByProjectId(projectId, tx);
|
||||||
|
return modifiedSecretsInDB;
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -2256,7 +2262,6 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
? await executeBulkDelete(providedTx)
|
? await executeBulkDelete(providedTx)
|
||||||
: await secretDAL.transaction(executeBulkDelete);
|
: await secretDAL.transaction(executeBulkDelete);
|
||||||
|
|
||||||
await secretDAL.invalidateSecretCacheByProjectId(projectId);
|
|
||||||
await snapshotService.performSnapshot(folderId);
|
await snapshotService.performSnapshot(folderId);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
actor,
|
actor,
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export const secretVersionV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
.where(`${TableName.SecretVersionV2}.folderId`, folderId)
|
.where(`${TableName.SecretVersionV2}.folderId`, folderId)
|
||||||
.join(TableName.SecretV2, `${TableName.SecretV2}.id`, `${TableName.SecretVersionV2}.secretId`)
|
.join(TableName.SecretV2, `${TableName.SecretV2}.id`, `${TableName.SecretVersionV2}.secretId`)
|
||||||
.join<TSecretVersionsV2, TSecretVersionsV2 & { secretId: string; max: number }>(
|
.join<TSecretVersionsV2, TSecretVersionsV2 & { secretId: string; max: number }>(
|
||||||
(tx || db)(TableName.SecretVersionV2)
|
(tx || db.replicaNode())(TableName.SecretVersionV2)
|
||||||
.where(`${TableName.SecretVersionV2}.folderId`, folderId)
|
.where(`${TableName.SecretVersionV2}.folderId`, folderId)
|
||||||
.groupBy("secretId")
|
.groupBy("secretId")
|
||||||
.max("version")
|
.max("version")
|
||||||
@@ -121,7 +121,7 @@ export const secretVersionV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
.where("folderId", folderId)
|
.where("folderId", folderId)
|
||||||
.whereIn(`${TableName.SecretVersionV2}.secretId`, secretIds)
|
.whereIn(`${TableName.SecretVersionV2}.secretId`, secretIds)
|
||||||
.join(
|
.join(
|
||||||
(tx || db)(TableName.SecretVersionV2)
|
(tx || db.replicaNode())(TableName.SecretVersionV2)
|
||||||
.groupBy("secretId")
|
.groupBy("secretId")
|
||||||
.max("version")
|
.max("version")
|
||||||
.select("secretId")
|
.select("secretId")
|
||||||
@@ -189,7 +189,7 @@ export const secretVersionV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
const { offset, limit, sort = [["createdAt", "desc"]] } = findOpt;
|
const { offset, limit, sort = [["createdAt", "desc"]] } = findOpt;
|
||||||
const query = (tx || db)(TableName.SecretVersionV2)
|
const query = (tx || db.replicaNode())(TableName.SecretVersionV2)
|
||||||
.leftJoin(TableName.Users, `${TableName.Users}.id`, `${TableName.SecretVersionV2}.userActorId`)
|
.leftJoin(TableName.Users, `${TableName.Users}.id`, `${TableName.SecretVersionV2}.userActorId`)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.ProjectMembership,
|
TableName.ProjectMembership,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const superAdminDALFactory = (db: TDbClient) => {
|
|||||||
const superAdminOrm = ormify(db, TableName.SuperAdmin);
|
const superAdminOrm = ormify(db, TableName.SuperAdmin);
|
||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
const config = await (tx || db)(TableName.SuperAdmin)
|
const config = await (tx || db.replicaNode())(TableName.SuperAdmin)
|
||||||
.where(`${TableName.SuperAdmin}.id`, id)
|
.where(`${TableName.SuperAdmin}.id`, id)
|
||||||
.leftJoin(TableName.Organization, `${TableName.SuperAdmin}.defaultAuthOrgId`, `${TableName.Organization}.id`)
|
.leftJoin(TableName.Organization, `${TableName.SuperAdmin}.defaultAuthOrgId`, `${TableName.Organization}.id`)
|
||||||
.leftJoin(TableName.SamlConfig, (qb) => {
|
.leftJoin(TableName.SamlConfig, (qb) => {
|
||||||
|
|||||||
@@ -19,10 +19,14 @@ export type TUserDALFactory = ReturnType<typeof userDALFactory>;
|
|||||||
export const userDALFactory = (db: TDbClient) => {
|
export const userDALFactory = (db: TDbClient) => {
|
||||||
const userOrm = ormify(db, TableName.Users);
|
const userOrm = ormify(db, TableName.Users);
|
||||||
const findUserByUsername = async (username: string, tx?: Knex) =>
|
const findUserByUsername = async (username: string, tx?: Knex) =>
|
||||||
(tx || db)(TableName.Users).whereRaw('lower("username") = :username', { username: username.toLowerCase() });
|
(tx || db.replicaNode())(TableName.Users).whereRaw('lower("username") = :username', {
|
||||||
|
username: username.toLowerCase()
|
||||||
|
});
|
||||||
|
|
||||||
const findUserByEmail = async (email: string, tx?: Knex) =>
|
const findUserByEmail = async (email: string, tx?: Knex) =>
|
||||||
(tx || db)(TableName.Users).whereRaw('lower("email") = :email', { email: email.toLowerCase() }).where({
|
(tx || db.replicaNode())(TableName.Users)
|
||||||
|
.whereRaw('lower("email") = :email', { email: email.toLowerCase() })
|
||||||
|
.where({
|
||||||
isEmailVerified: true
|
isEmailVerified: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user