batch identity key emails

This commit is contained in:
miloschwartz
2026-08-18 15:59:51 -04:00
parent 809bc662e0
commit bdd7f40688
2 changed files with 24 additions and 13 deletions
+14 -2
View File
@@ -7,6 +7,18 @@ import VirtualApiKeyGenerated from "@server/emails/templates/VirtualApiKeyGenera
import { formatVirtualApiKeyCredential } from "@server/lib/virtualApiKey"; import { formatVirtualApiKeyCredential } from "@server/lib/virtualApiKey";
const EMAIL_GATEWAY_URL_LIMIT = 5; const EMAIL_GATEWAY_URL_LIMIT = 5;
const VIRTUAL_API_KEY_EMAIL_BATCH_SIZE = 50;
export async function mapInBatches<T>(
items: T[],
fn: (item: T) => Promise<void>,
batchSize = VIRTUAL_API_KEY_EMAIL_BATCH_SIZE
): Promise<void> {
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
await Promise.all(batch.map(fn));
}
}
async function listVirtualApiKeyGatewayUrls(params: { async function listVirtualApiKeyGatewayUrls(params: {
orgId: string; orgId: string;
@@ -161,7 +173,7 @@ export async function sendVirtualApiKeyEmails(params: {
? `Your identity key for ${params.orgName}` ? `Your identity key for ${params.orgName}`
: `Virtual API key for ${params.orgName}`; : `Virtual API key for ${params.orgName}`;
for (const to of params.recipients) { await mapInBatches(params.recipients, async (to) => {
await sendEmail( await sendEmail(
params.isIdentityKey params.isIdentityKey
? IdentityApiKeyGenerated({ ? IdentityApiKeyGenerated({
@@ -184,5 +196,5 @@ export async function sendVirtualApiKeyEmails(params: {
subject subject
} }
); );
} });
} }
@@ -12,7 +12,8 @@ import config from "@server/lib/config";
import { getOrCreateUserVirtualApiKey } from "@server/lib/virtualApiKey"; import { getOrCreateUserVirtualApiKey } from "@server/lib/virtualApiKey";
import { import {
sendVirtualApiKeyEmails, sendVirtualApiKeyEmails,
listOrgInferenceGatewayUrls listOrgInferenceGatewayUrls,
mapInBatches
} from "@server/lib/sendVirtualApiKeyEmail"; } from "@server/lib/sendVirtualApiKeyEmail";
import type { EmailIdentityKeysResponse } from "@server/routers/virtualApiKey/types"; import type { EmailIdentityKeysResponse } from "@server/routers/virtualApiKey/types";
import rateLimit, { ipKeyGenerator } from "express-rate-limit"; import rateLimit, { ipKeyGenerator } from "express-rate-limit";
@@ -218,15 +219,13 @@ export async function emailIdentityKeys(
const orgName = org?.name || orgId; const orgName = org?.name || orgId;
const gatewayUrls = await listOrgInferenceGatewayUrls(orgId); const gatewayUrls = await listOrgInferenceGatewayUrls(orgId);
const recipients = members.flatMap(({ user }) =>
user.email ? [{ user, email: user.email }] : []
);
let sent = 0; let sent = 0;
let skipped = 0; const skipped = members.length - recipients.length;
for (const { user } of members) {
if (!user.email) {
skipped += 1;
continue;
}
await mapInBatches(recipients, async ({ user, email }) => {
const { key, secret } = await getOrCreateUserVirtualApiKey({ const { key, secret } = await getOrCreateUserVirtualApiKey({
orgId, orgId,
user, user,
@@ -234,7 +233,7 @@ export async function emailIdentityKeys(
}); });
await sendVirtualApiKeyEmails({ await sendVirtualApiKeyEmails({
recipients: [user.email], recipients: [email],
orgName, orgName,
orgId, orgId,
keyName: key.name, keyName: key.name,
@@ -242,11 +241,11 @@ export async function emailIdentityKeys(
secret, secret,
allResources: true, allResources: true,
isIdentityKey: true, isIdentityKey: true,
accountLabel: user.email || user.name || user.username, accountLabel: email,
gatewayUrls gatewayUrls
}); });
sent += 1; sent += 1;
} });
return response<EmailIdentityKeysResponse>(res, { return response<EmailIdentityKeysResponse>(res, {
data: { sent, skipped }, data: { sent, skipped },