teqo-backup/src/utilities/access/contacts.ts
Francisco Ignácio Fontoura Solla 77023db99d B34+: scale and DRY after municipality chip lists.
Memoize access reads per req, cache portfolio index, trim chip revalidates, and shrink RSC payload via catalog-backed labels.
2026-07-28 19:49:29 -03:00

118 lines
3.6 KiB
TypeScript

// ---------------------------------------------------------------------------
// Contacts
// ---------------------------------------------------------------------------
import type { Access, PayloadRequest } from 'payload'
import { getAccessibleMunicipalityIds } from '@/utilities/access/municipalities'
import type { CampaignActor, DynamicFind } from '@/utilities/access/shared'
import {
getFreshCampaignUser,
isCampaignUnrestricted,
isCampaignUser,
isPayloadAdmin,
memoizePerRequest,
} from '@/utilities/access/shared'
import { relationshipId } from '@/utilities/relationship'
type ContactID = number
const ACCESSIBLE_CONTACT_IDS_MEMO_KEY = 'campaignAccessibleContactIds'
const getAccessibleContactIds = async (
req: PayloadRequest,
user: CampaignActor = req.user,
): Promise<ContactID[]> => {
const currentUser =
isCampaignUser(user) && user === req.user ? await getFreshCampaignUser(req, user) : user
if (!isCampaignUser(currentUser)) return []
return memoizePerRequest(
req,
`${ACCESSIBLE_CONTACT_IDS_MEMO_KEY}:${currentUser.id}:${currentUser.role}`,
async () => {
const municipalityIDs = await getAccessibleMunicipalityIds(req, currentUser)
const leadershipWhere =
currentUser.role === 'leader'
? {
and: [
{ user: { equals: currentUser.id } },
{ supportStatus: { equals: 'engajado' } },
],
}
: {
municipalities: {
in: municipalityIDs ?? [],
},
}
const find = req.payload.find.bind(req.payload) as unknown as DynamicFind
const collections = req.payload.collections as Record<string, unknown>
const contactIDs: ContactID[] = []
if (collections.leadership) {
const leadershipResult = await find({
collection: 'leadership',
depth: 0,
limit: 0,
overrideAccess: true,
pagination: false,
req,
select: { contact: true },
where: leadershipWhere,
})
for (const doc of leadershipResult.docs) {
const id = relationshipId(doc.contact)
if (id !== null) contactIDs.push(id)
}
}
// Advisors read contacts of supporters in their municipalities; leaders read
// contacts of the supporters they created — mirroring `canReadSupporter`.
const supporterWhere =
currentUser.role === 'advisor'
? { municipality: { in: municipalityIDs ?? [] } }
: currentUser.role === 'leader'
? { createdBy: { equals: currentUser.id } }
: null
if (supporterWhere && collections.supporter) {
const supporterResult = await find({
collection: 'supporter',
depth: 0,
limit: 0,
overrideAccess: true,
pagination: false,
req,
select: { contact: true },
where: supporterWhere,
})
for (const doc of supporterResult.docs) {
const id = relationshipId(doc.contact)
if (id !== null) contactIDs.push(id)
}
}
return [...new Set(contactIDs)]
},
)
}
export const canReadContacts: Access = async ({ req }) => {
if (isPayloadAdmin(req.user)) return true
const currentUser = await getFreshCampaignUser(req)
if (isCampaignUnrestricted(currentUser)) return true
if (!currentUser) return false
const ids = await getAccessibleContactIds(req, currentUser)
return {
id: {
in: ids,
},
}
}
export const canManageContacts: Access = ({ req }) => isPayloadAdmin(req.user)