* feat: Cal.diy — community-driven MIT-licensed fork of Cal.com This squashed commit contains all Cal.diy changes applied on top of calcom/cal.com main: - Rebrand Cal.com to Cal.diy across the entire codebase - Remove Enterprise Edition (EE) features, license checks, and AGPL restrictions - Switch license from AGPL-3.0 to MIT - Remove docs/ directory (migrated to Nextra at cal.diy) - Remove dead code: org tests, EE tips, platform nav, premium username, SAML/SSO, etc. - Clean up .env.example for self-hosted Cal.diy - Update Docker image references to calcom/cal.diy - Update README, CONTRIBUTING.md, and issue templates for Cal.diy community fork - Add PR welcome bot for Cal.diy contributors - Fix API v2 breaking changes oasdiff ignore entries - Replace Blacksmith CI runners with default GitHub Actions 3893 files changed, 20789 insertions(+), 411020 deletions(-) Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * refactor: remove org-specific /organizations/:orgId endpoints from API v2 atoms controllers (#1701) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix: revert Cal.diy Inc to Cal.com, Inc. in license files, copyright notices, and package metadata (#1702) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * rip out org related comments in api v2 --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
134 lines
4.2 KiB
TypeScript
134 lines
4.2 KiB
TypeScript
import type { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
|
import type { WatchlistRepository } from "@calcom/features/watchlist/lib/repository/WatchlistRepository";
|
|
import { MembershipRole, type WatchlistSource, type WatchlistType } from "@calcom/prisma/enums";
|
|
import { WatchlistErrors } from "../errors/WatchlistErrors";
|
|
|
|
class PermissionCheckService {
|
|
constructor(_prisma?: unknown) {}
|
|
async checkPermission(..._args: unknown[]) { return true; }
|
|
async hasPermission(..._args: unknown[]) { return true; }
|
|
async getTeamIdsWithPermission(..._args: unknown[]): Promise<number[]> { return []; }
|
|
}
|
|
|
|
export interface ListWatchlistEntriesInput {
|
|
organizationId: number;
|
|
userId: number;
|
|
limit: number;
|
|
offset: number;
|
|
searchTerm?: string;
|
|
filters?: {
|
|
type?: WatchlistType;
|
|
source?: WatchlistSource;
|
|
};
|
|
}
|
|
|
|
export interface GetWatchlistEntryDetailsInput {
|
|
organizationId: number;
|
|
userId: number;
|
|
entryId: string;
|
|
}
|
|
|
|
type Deps = {
|
|
watchlistRepo: WatchlistRepository;
|
|
userRepo: UserRepository;
|
|
permissionCheckService: PermissionCheckService;
|
|
};
|
|
|
|
export class OrganizationWatchlistQueryService {
|
|
constructor(private readonly deps: Deps) {}
|
|
|
|
private async checkReadPermission(userId: number, organizationId: number): Promise<void> {
|
|
const hasPermission = await this.deps.permissionCheckService.checkPermission({
|
|
userId,
|
|
teamId: organizationId,
|
|
permission: "watchlist.read",
|
|
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
|
|
});
|
|
|
|
if (!hasPermission) {
|
|
throw WatchlistErrors.permissionDenied("You are not authorized to view watchlist entries");
|
|
}
|
|
}
|
|
|
|
async listWatchlistEntries(input: ListWatchlistEntriesInput) {
|
|
await this.checkReadPermission(input.userId, input.organizationId);
|
|
|
|
const result = await this.deps.watchlistRepo.findOrgAndGlobalEntries({
|
|
organizationId: input.organizationId,
|
|
limit: input.limit,
|
|
offset: input.offset,
|
|
searchTerm: input.searchTerm,
|
|
filters: input.filters,
|
|
});
|
|
|
|
const userIds = result.rows
|
|
.map((entry) => entry.latestAudit?.changedByUserId)
|
|
.filter((id): id is number => id !== null && id !== undefined);
|
|
|
|
const uniqueUserIds = Array.from(new Set(userIds));
|
|
|
|
const users = uniqueUserIds.length > 0 ? await this.deps.userRepo.findUsersByIds(uniqueUserIds) : [];
|
|
|
|
const userMap = new Map(users.map((u) => [u.id, u]));
|
|
|
|
const rowsWithCreators = result.rows.map((entry) => {
|
|
if (entry.latestAudit?.changedByUserId) {
|
|
const changedByUser = userMap.get(entry.latestAudit.changedByUserId);
|
|
return {
|
|
...entry,
|
|
latestAudit: {
|
|
...entry.latestAudit,
|
|
changedByUser,
|
|
},
|
|
};
|
|
}
|
|
return entry;
|
|
});
|
|
|
|
return {
|
|
rows: rowsWithCreators,
|
|
meta: result.meta,
|
|
};
|
|
}
|
|
|
|
async getWatchlistEntryDetails(input: GetWatchlistEntryDetailsInput) {
|
|
await this.checkReadPermission(input.userId, input.organizationId);
|
|
|
|
const result = await this.deps.watchlistRepo.findEntryWithAuditAndReports(input.entryId);
|
|
|
|
if (!result.entry) {
|
|
throw WatchlistErrors.notFound("Blocklist entry not found");
|
|
}
|
|
|
|
const isOrgEntry = result.entry.organizationId === input.organizationId;
|
|
const isGlobalEntry = result.entry.isGlobal && result.entry.organizationId === null;
|
|
|
|
if (!isOrgEntry && !isGlobalEntry) {
|
|
throw WatchlistErrors.permissionDenied(
|
|
"You can only view blocklist entries from your organization or global entries"
|
|
);
|
|
}
|
|
|
|
const userIds = result.auditHistory
|
|
.map((audit) => audit.changedByUserId)
|
|
.filter((id): id is number => id !== null && id !== undefined);
|
|
|
|
const uniqueUserIds = Array.from(new Set(userIds));
|
|
|
|
const users = uniqueUserIds.length > 0 ? await this.deps.userRepo.findUsersByIds(uniqueUserIds) : [];
|
|
|
|
const userMap = new Map(users.map((u) => [u.id, u]));
|
|
|
|
const auditHistoryWithUsers = result.auditHistory.map((audit) => ({
|
|
...audit,
|
|
changedByUser: audit.changedByUserId ? userMap.get(audit.changedByUserId) : undefined,
|
|
}));
|
|
|
|
return {
|
|
entry: result.entry,
|
|
auditHistory: auditHistoryWithUsers,
|
|
isReadOnly: isGlobalEntry,
|
|
};
|
|
}
|
|
}
|