Files
cal.diy/packages/features/watchlist/lib/service/OrganizationBlockingService.ts
T
Syed Ali Shahbazandcubic-dev-ai[bot] 59fca85d92 fix: use graceful filtering for previously hard failing blocked users in team events (#26446)
* init

* typefix

* --

* fix test

* update index

* cleanup

* rm unnecessary comments

* improvements

* add tests

* test fixes

* fixes

* fixes

* fixes

* add try catch to make booking fail-open

* Update packages/features/watchlist/operations/check-user-blocking.ts

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* fix type

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-01-08 08:30:54 -03:00

120 lines
3.9 KiB
TypeScript

import type { BulkBlockingResult, BlockingResult, IBlockingService } from "../interface/IBlockingService";
import type { IOrganizationWatchlistRepository } from "../interface/IWatchlistRepositories";
import { WatchlistType } from "../types";
import { extractDomainFromEmail, normalizeDomain, normalizeEmail } from "../utils/normalization";
type Deps = {
orgRepo: IOrganizationWatchlistRepository;
};
/**
* Service for organization-specific blocking operations
* Handles blocking rules that apply only to a specific organization
*/
export class OrganizationBlockingService implements IBlockingService {
constructor(private readonly deps: Deps) {}
async isBlocked(email: string, organizationId: number): Promise<BlockingResult> {
const normalizedEmail = normalizeEmail(email);
const normalizedDomain = extractDomainFromEmail(normalizedEmail);
const emailPromise = this.deps.orgRepo.findBlockedEmail({ email: normalizedEmail, organizationId });
const domainPromise = this.deps.orgRepo.findBlockedDomain(normalizedDomain, organizationId);
const [emailEntry, domainEntry] = await Promise.all([emailPromise, domainPromise]);
if (emailEntry) {
return {
isBlocked: true,
reason: WatchlistType.EMAIL,
watchlistEntry: emailEntry,
};
}
if (domainEntry) {
return {
isBlocked: true,
reason: WatchlistType.DOMAIN,
watchlistEntry: domainEntry,
};
}
return { isBlocked: false };
}
/**
* Bulk check multiple emails in a single query for an organization.
* Returns Map<email (lowercase), BlockingResult> for efficient lookup.
*/
async areBlocked(emails: string[], organizationId: number): Promise<BulkBlockingResult> {
const result: BulkBlockingResult = new Map();
if (emails.length === 0 || !organizationId) {
return result;
}
// Normalize and extract domains
const normalizedEmails = emails.map((e) => normalizeEmail(e));
const uniqueDomains = Array.from(new Set(normalizedEmails.map((e) => extractDomainFromEmail(e))));
// Single DB query for all emails and domains
const blockingEntries = await this.deps.orgRepo.findBlockingEntriesForEmailsAndDomains({
emails: normalizedEmails,
domains: uniqueDomains,
organizationId,
});
// Build lookup maps for O(1) checks (Maps serve as both lookup and entry storage)
const emailEntries = new Map<string, (typeof blockingEntries)[0]>();
const domainEntries = new Map<string, (typeof blockingEntries)[0]>();
for (const entry of blockingEntries) {
if (entry.type === WatchlistType.EMAIL) {
emailEntries.set(entry.value.toLowerCase(), entry);
} else if (entry.type === WatchlistType.DOMAIN) {
domainEntries.set(entry.value.toLowerCase(), entry);
}
}
// Map results for each email
for (const email of normalizedEmails) {
const domain = extractDomainFromEmail(email);
const emailEntry = emailEntries.get(email);
const domainEntry = domainEntries.get(domain);
if (emailEntry) {
result.set(email, {
isBlocked: true,
reason: WatchlistType.EMAIL,
watchlistEntry: emailEntry,
});
} else if (domainEntry) {
result.set(email, {
isBlocked: true,
reason: WatchlistType.DOMAIN,
watchlistEntry: domainEntry,
});
} else {
result.set(email, { isBlocked: false });
}
}
return result;
}
async isDomainBlocked(domain: string, organizationId: number): Promise<BlockingResult> {
const normalizedDomain = normalizeDomain(domain);
return this.deps.orgRepo.findBlockedDomain(normalizedDomain, organizationId).then((domainEntry) => {
if (domainEntry) {
return {
isBlocked: true,
reason: WatchlistType.DOMAIN,
watchlistEntry: domainEntry,
};
}
return { isBlocked: false };
});
}
}