mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-14 08:53:11 +00:00
Bulk import and list paths get single-round-trip locks, drizzle returning reads, pg_trgm search, shared drizzleBulk helpers, and consolidated campaign formActions; simplify debts land as C9 in the roadmap.
711 lines
21 KiB
TypeScript
711 lines
21 KiB
TypeScript
import type { CampaignUser, User } from '@/payload-types'
|
|
import type { Access, FieldAccess, Payload, PayloadRequest, Where } from 'payload'
|
|
|
|
import { relationshipId } from '@/utilities/relationship'
|
|
|
|
type CampaignActor = CampaignUser | User | null | undefined
|
|
type NucleusID = number
|
|
type AccessibleNucleusIDs = NucleusID[] | null
|
|
type ContactID = number
|
|
|
|
const ACCESSIBLE_NUCLEUS_IDS_CONTEXT_KEY = 'campaignAccessibleNucleusIds'
|
|
const ACCESSIBLE_CONTACT_IDS_CONTEXT_KEY = 'campaignAccessibleContactIds'
|
|
const ACCESSIBLE_LEADERSHIP_IDS_CONTEXT_KEY = 'campaignAccessibleLeadershipIds'
|
|
const CAMPAIGN_USER_PHONE_ACCESS_CONTEXT_KEY = 'campaignUserPhoneAccess'
|
|
|
|
type LeadershipID = number
|
|
type AccessibleLeadershipIDs = LeadershipID[] | null
|
|
|
|
type DynamicFind = (args: {
|
|
collection: string
|
|
depth: number
|
|
limit: number
|
|
overrideAccess: true
|
|
pagination: false
|
|
req?: PayloadRequest
|
|
select: Record<string, true>
|
|
where: Record<string, unknown>
|
|
}) => Promise<{ docs: Array<Record<string, unknown>> }>
|
|
|
|
export const isPayloadAdmin = (user: CampaignActor): user is User => user?.collection === 'users'
|
|
|
|
const isCampaignUser = (user: CampaignActor): user is CampaignUser =>
|
|
user?.collection === 'campaignUser'
|
|
|
|
export const isCampaignGeneral = (user: CampaignActor): boolean =>
|
|
isCampaignUser(user) && user.role === 'geral'
|
|
|
|
export const getFreshCampaignUser = async (
|
|
req: PayloadRequest,
|
|
user: CampaignActor = req.user,
|
|
): Promise<CampaignUser | null> => {
|
|
if (!isCampaignUser(user)) return null
|
|
|
|
try {
|
|
return await req.payload.findByID({
|
|
collection: 'campaignUser',
|
|
id: user.id,
|
|
depth: 0,
|
|
overrideAccess: true,
|
|
req,
|
|
})
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export const canManageCampaignUsers: Access = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
return isCampaignGeneral(await getFreshCampaignUser(req))
|
|
}
|
|
|
|
export const canManageCampaignUserRole: FieldAccess = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
return isCampaignGeneral(await getFreshCampaignUser(req))
|
|
}
|
|
|
|
export const canReadCampaignUsers: Access = ({ req }) =>
|
|
isPayloadAdmin(req.user) || isCampaignUser(req.user)
|
|
|
|
export const canReadCampaignUserIdentity: FieldAccess = ({ req, id }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
if (!isCampaignUser(req.user)) return false
|
|
|
|
return id !== undefined && String(id) === String(req.user.id)
|
|
}
|
|
|
|
export const canCreateElectoralNucleus: Access = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
return isCampaignGeneral(await getFreshCampaignUser(req))
|
|
}
|
|
|
|
/**
|
|
* Nucleus IDs where `coordinatorID` is an assigned coordinator, without
|
|
* requiring a `PayloadRequest`. This is the canonical implementation of the
|
|
* "coordinators contains user.id" lookup: `getAccessibleNucleusIds` uses it
|
|
* for its (request-scoped, context-cached) coordinator branch, and read-only
|
|
* call sites without a `PayloadRequest` (e.g. the supporter overview
|
|
* aggregate) call it directly instead of re-implementing the query.
|
|
*/
|
|
export const getCoordinatorNucleusIds = async (
|
|
payload: Pick<Payload, 'find'>,
|
|
coordinatorID: number,
|
|
req?: PayloadRequest,
|
|
): Promise<number[]> => {
|
|
const find = payload.find.bind(payload) as unknown as DynamicFind
|
|
const result = await find({
|
|
collection: 'electoralNucleus',
|
|
where: { coordinators: { contains: coordinatorID } },
|
|
depth: 0,
|
|
limit: 0,
|
|
pagination: false,
|
|
select: { id: true },
|
|
overrideAccess: true,
|
|
...(req ? { req } : {}),
|
|
})
|
|
|
|
return result.docs.map((doc) => relationshipId(doc.id)).filter((id): id is number => id !== null)
|
|
}
|
|
|
|
export const canReadElectoralNucleus: Access = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const ids = await getAccessibleNucleusIds(req)
|
|
if (ids === null) return true
|
|
|
|
return {
|
|
id: {
|
|
in: ids,
|
|
},
|
|
}
|
|
}
|
|
|
|
export const canUpdateElectoralNucleus: Access = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (isCampaignGeneral(currentUser)) return true
|
|
if (currentUser?.role !== 'coordenador') return false
|
|
|
|
return {
|
|
coordinators: {
|
|
contains: currentUser.id,
|
|
},
|
|
}
|
|
}
|
|
|
|
export const canDeleteElectoralNucleus: Access = ({ req }) => isPayloadAdmin(req.user)
|
|
|
|
export const canReadContacts: Access = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (isCampaignGeneral(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)
|
|
|
|
export const canSetAdministrativeLeadershipField: FieldAccess = ({ req }) =>
|
|
isPayloadAdmin(req.user)
|
|
|
|
export const canReadLeadership: Access = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (isCampaignGeneral(currentUser)) return true
|
|
if (!currentUser) return false
|
|
|
|
if (currentUser.role === 'lideranca') {
|
|
const leadershipScope: Where = {
|
|
and: [{ user: { equals: currentUser.id } }, { supportStatus: { equals: 'engajado' } }],
|
|
}
|
|
return leadershipScope
|
|
}
|
|
|
|
const nucleusIDs = await getAccessibleNucleusIds(req, currentUser)
|
|
|
|
return {
|
|
nucleus: {
|
|
in: nucleusIDs ?? [],
|
|
},
|
|
}
|
|
}
|
|
|
|
export const canCreateLeadership: Access = async ({ data, req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (isCampaignGeneral(currentUser)) return true
|
|
if (currentUser?.role !== 'coordenador') return false
|
|
|
|
const nucleus = relationshipId(data?.nucleus)
|
|
if (!nucleus) return false
|
|
|
|
const nucleusIDs = await getAccessibleNucleusIds(req, currentUser)
|
|
return nucleusIDs?.includes(nucleus) ?? false
|
|
}
|
|
|
|
export const canManageLeadership: Access = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (isCampaignGeneral(currentUser)) return true
|
|
if (currentUser?.role !== 'coordenador') return false
|
|
|
|
const nucleusIDs = await getAccessibleNucleusIds(req, currentUser)
|
|
|
|
return {
|
|
nucleus: {
|
|
in: nucleusIDs ?? [],
|
|
},
|
|
}
|
|
}
|
|
|
|
export const canDeleteLeadership: Access = ({ req }) => isPayloadAdmin(req.user)
|
|
|
|
export const canCreateNucleusUpdate: Access = async ({ data, req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (isCampaignGeneral(currentUser)) return true
|
|
if (!currentUser) return false
|
|
|
|
const nucleus = relationshipId(data?.nucleus)
|
|
if (!nucleus) return false
|
|
|
|
const nucleusIDs = await getAccessibleNucleusIds(req, currentUser)
|
|
return nucleusIDs?.includes(nucleus) ?? false
|
|
}
|
|
|
|
export const canReadNucleusUpdate: Access = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (isCampaignGeneral(currentUser)) return true
|
|
if (!currentUser) return false
|
|
|
|
const nucleusIDs = await getAccessibleNucleusIds(req, currentUser)
|
|
const nucleusScope: Where = {
|
|
nucleus: {
|
|
in: nucleusIDs ?? [],
|
|
},
|
|
}
|
|
|
|
if (currentUser.role !== 'lideranca') return nucleusScope
|
|
|
|
return {
|
|
and: [nucleusScope, { author: { equals: currentUser.id } }],
|
|
}
|
|
}
|
|
|
|
export const canMutateNucleusUpdate: Access = ({ req }) => isPayloadAdmin(req.user)
|
|
|
|
export const canSetNucleusUpdateAuthor: FieldAccess = ({ req }) => isPayloadAdmin(req.user)
|
|
|
|
export const canCreateCampaignInvite: Access = async ({ data, req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (isCampaignGeneral(currentUser)) return true
|
|
if (currentUser?.role !== 'coordenador') return false
|
|
|
|
const leadershipID = relationshipId(data?.leadership)
|
|
if (!leadershipID) return false
|
|
|
|
try {
|
|
const leadership = await req.payload.findByID({
|
|
collection: 'leadership',
|
|
id: leadershipID,
|
|
depth: 0,
|
|
overrideAccess: true,
|
|
req,
|
|
})
|
|
const nucleusID = relationshipId(leadership.nucleus)
|
|
const accessibleNucleusIDs = await getAccessibleNucleusIds(req, currentUser)
|
|
|
|
return nucleusID !== null && (accessibleNucleusIDs?.includes(nucleusID) ?? false)
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
export const canReadCampaignInvite: Access = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (isCampaignGeneral(currentUser)) return true
|
|
if (currentUser?.role !== 'coordenador') return false
|
|
|
|
const accessibleNucleusIDs = await getAccessibleNucleusIds(req, currentUser)
|
|
const leaderships = await req.payload.find({
|
|
collection: 'leadership',
|
|
where: {
|
|
nucleus: {
|
|
in: accessibleNucleusIDs ?? [],
|
|
},
|
|
},
|
|
depth: 0,
|
|
limit: 0,
|
|
pagination: false,
|
|
overrideAccess: true,
|
|
req,
|
|
})
|
|
|
|
return {
|
|
leadership: {
|
|
in: leaderships.docs.map((leadership) => leadership.id),
|
|
},
|
|
}
|
|
}
|
|
|
|
export const canMutateCampaignInvite: Access = ({ req }) => isPayloadAdmin(req.user)
|
|
|
|
export const canSetCampaignInviteSystemField: FieldAccess = ({ req }) => isPayloadAdmin(req.user)
|
|
|
|
export const canReadLeadershipInternal: FieldAccess = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
return currentUser?.role === 'geral' || currentUser?.role === 'coordenador'
|
|
}
|
|
|
|
export const canManageLeadershipInternal: FieldAccess = canReadLeadershipInternal
|
|
|
|
export const canCreateSupporter: Access = async ({ data, req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (isCampaignGeneral(currentUser)) return true
|
|
if (currentUser?.role !== 'coordenador') return false
|
|
|
|
const nucleus = relationshipId(data?.nucleus)
|
|
if (!nucleus) return false
|
|
|
|
const nucleusIDs = await getAccessibleNucleusIds(req, currentUser)
|
|
return nucleusIDs?.includes(nucleus) ?? false
|
|
}
|
|
|
|
export const canReadSupporter: Access = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (isCampaignGeneral(currentUser)) return true
|
|
if (currentUser?.role !== 'coordenador') return false
|
|
|
|
const nucleusIDs = await getAccessibleNucleusIds(req, currentUser)
|
|
|
|
return {
|
|
nucleus: {
|
|
in: nucleusIDs ?? [],
|
|
},
|
|
}
|
|
}
|
|
|
|
export const canManageSupporter: Access = canReadSupporter
|
|
|
|
export const canDeleteSupporter: Access = ({ req }) => isPayloadAdmin(req.user)
|
|
|
|
export const canCreateNucleusCoordinators: FieldAccess = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
return isCampaignGeneral(await getFreshCampaignUser(req))
|
|
}
|
|
|
|
export const canManageNucleusCoordinators: FieldAccess = ({ req }) => isPayloadAdmin(req.user)
|
|
|
|
export const canManageNucleusLifecycle: FieldAccess = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
return isCampaignGeneral(await getFreshCampaignUser(req))
|
|
}
|
|
|
|
export const canSetDerivedNucleusField: FieldAccess = ({ req }) => isPayloadAdmin(req.user)
|
|
|
|
/** Public TSE election data: any authenticated campaign or admin user may read. */
|
|
export const canReadElectionData: Access = ({ req }) =>
|
|
isPayloadAdmin(req.user) || isCampaignUser(req.user)
|
|
|
|
/** Election reference data is mutated only by Payload admins (or CLI with overrideAccess). */
|
|
export const canMutateElectionData: Access = ({ req }) => isPayloadAdmin(req.user)
|
|
|
|
const canStaffCreateActionPlan: FieldAccess = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
return currentUser?.role === 'geral' || currentUser?.role === 'coordenador'
|
|
}
|
|
|
|
export const canCreateActionPlan: Access = canStaffCreateActionPlan
|
|
|
|
export const canReadActionPlan: Access = async ({ req }): Promise<boolean | Where> => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (!currentUser) return false
|
|
if (isCampaignGeneral(currentUser)) return true
|
|
|
|
if (currentUser.role === 'coordenador') {
|
|
return {
|
|
coordinators: {
|
|
contains: currentUser.id,
|
|
},
|
|
}
|
|
}
|
|
|
|
if (currentUser.role === 'lideranca') {
|
|
const leadershipIDs = await getAccessibleLeadershipIds(req, currentUser)
|
|
return {
|
|
leadership: {
|
|
in: leadershipIDs ?? [],
|
|
},
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
/** Same row scope as read — liderança write limits are enforced in `beforeChange`. */
|
|
export const canUpdateActionPlan: Access = canReadActionPlan
|
|
|
|
export const canDeleteActionPlan: Access = ({ req }) => isPayloadAdmin(req.user)
|
|
|
|
export const canSetActionPlanSystemField: FieldAccess = ({ req }) => isPayloadAdmin(req.user)
|
|
|
|
export const canSetActionPlanStatus: FieldAccess = canStaffCreateActionPlan
|
|
|
|
export const canCreateActionPlanCoordinators: FieldAccess = canStaffCreateActionPlan
|
|
|
|
export const canManageActionPlanCoordinators: FieldAccess = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
return isCampaignGeneral(await getFreshCampaignUser(req))
|
|
}
|
|
|
|
/**
|
|
* Returns null for unrestricted general coordination, otherwise the nucleus
|
|
* IDs currently assigned to the authenticated campaign user.
|
|
*/
|
|
export const getAccessibleNucleusIds = async (
|
|
req: PayloadRequest,
|
|
user: CampaignActor = req.user,
|
|
): Promise<AccessibleNucleusIDs> => {
|
|
const currentUser =
|
|
isCampaignUser(user) && user === req.user ? await getFreshCampaignUser(req, user) : user
|
|
|
|
if (!isCampaignUser(currentUser)) return []
|
|
if (isCampaignGeneral(currentUser)) return null
|
|
|
|
const context = req.context as Record<string, unknown>
|
|
const cacheKey = `${ACCESSIBLE_NUCLEUS_IDS_CONTEXT_KEY}:${currentUser.id}:${currentUser.role}`
|
|
const cached = context[cacheKey]
|
|
|
|
if (Array.isArray(cached)) {
|
|
return cached.filter((id): id is number => typeof id === 'number')
|
|
}
|
|
|
|
const collections = req.payload.collections as Record<string, unknown>
|
|
const find = req.payload.find.bind(req.payload) as unknown as DynamicFind
|
|
let ids: NucleusID[] = []
|
|
|
|
if (currentUser.role === 'coordenador' && collections.electoralNucleus) {
|
|
ids = await getCoordinatorNucleusIds(req.payload, currentUser.id, req)
|
|
}
|
|
|
|
if (currentUser.role === 'lideranca' && collections.leadership) {
|
|
const result = await find({
|
|
collection: 'leadership',
|
|
depth: 0,
|
|
limit: 0,
|
|
overrideAccess: true,
|
|
pagination: false,
|
|
req,
|
|
select: { nucleus: true },
|
|
where: {
|
|
and: [{ user: { equals: currentUser.id } }, { supportStatus: { equals: 'engajado' } }],
|
|
},
|
|
})
|
|
|
|
ids = result.docs
|
|
.map((doc) => relationshipId(doc.nucleus))
|
|
.filter((id): id is number => id !== null)
|
|
}
|
|
|
|
const uniqueIDs = [...new Set(ids)]
|
|
context[cacheKey] = uniqueIDs
|
|
|
|
return uniqueIDs
|
|
}
|
|
|
|
export const canReadCampaignUserPhone: FieldAccess = async ({ doc, id, req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (!currentUser) return false
|
|
if (id !== undefined && String(id) === String(currentUser.id)) return true
|
|
if (isCampaignGeneral(currentUser)) return true
|
|
if (id === undefined) return false
|
|
|
|
const targetUserID = Number(id)
|
|
if (!Number.isInteger(targetUserID) || targetUserID <= 0) return false
|
|
|
|
const context = req.context as Record<string, unknown>
|
|
const cacheKey = `${CAMPAIGN_USER_PHONE_ACCESS_CONTEXT_KEY}:${currentUser.id}:${targetUserID}`
|
|
if (typeof context[cacheKey] === 'boolean') return context[cacheKey]
|
|
|
|
let targetRole =
|
|
typeof doc === 'object' && doc !== null && 'role' in doc ? doc.role : undefined
|
|
if (targetRole === undefined) {
|
|
try {
|
|
const target = await req.payload.findByID({
|
|
collection: 'campaignUser',
|
|
id: targetUserID,
|
|
depth: 0,
|
|
select: { role: true },
|
|
overrideAccess: true,
|
|
req,
|
|
})
|
|
targetRole = target.role
|
|
} catch {
|
|
// Missing target only — fall through to the coordinator-scope check.
|
|
}
|
|
}
|
|
if (targetRole === 'geral') {
|
|
context[cacheKey] = true
|
|
return true
|
|
}
|
|
|
|
const nucleusIDs = await getAccessibleNucleusIds(req, currentUser)
|
|
if (!nucleusIDs?.length) {
|
|
context[cacheKey] = false
|
|
return false
|
|
}
|
|
|
|
const find = req.payload.find.bind(req.payload) as unknown as DynamicFind
|
|
const result = await find({
|
|
collection: 'electoralNucleus',
|
|
depth: 0,
|
|
limit: 1,
|
|
overrideAccess: true,
|
|
pagination: false,
|
|
req,
|
|
select: { id: true },
|
|
where: {
|
|
and: [{ id: { in: nucleusIDs } }, { coordinators: { contains: targetUserID } }],
|
|
},
|
|
})
|
|
const allowed = result.docs.length > 0
|
|
context[cacheKey] = allowed
|
|
return allowed
|
|
}
|
|
|
|
export const canCreateCampaignUserPhone: FieldAccess = async ({ req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
return isCampaignGeneral(await getFreshCampaignUser(req))
|
|
}
|
|
|
|
export const canUpdateCampaignUserPhone: FieldAccess = async ({ id, req }) => {
|
|
if (isPayloadAdmin(req.user)) return true
|
|
|
|
const currentUser = await getFreshCampaignUser(req)
|
|
if (!currentUser) return false
|
|
if (id !== undefined && String(id) === String(currentUser.id)) return true
|
|
return isCampaignGeneral(currentUser)
|
|
}
|
|
|
|
export 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 []
|
|
|
|
const context = req.context as Record<string, unknown>
|
|
const cacheKey = `${ACCESSIBLE_CONTACT_IDS_CONTEXT_KEY}:${currentUser.id}:${currentUser.role}`
|
|
const cached = context[cacheKey]
|
|
|
|
if (Array.isArray(cached)) {
|
|
return cached.filter((id): id is number => typeof id === 'number')
|
|
}
|
|
|
|
const nucleusIDs = await getAccessibleNucleusIds(req, currentUser)
|
|
const leadershipWhere =
|
|
currentUser.role === 'lideranca'
|
|
? {
|
|
and: [{ user: { equals: currentUser.id } }, { supportStatus: { equals: 'engajado' } }],
|
|
}
|
|
: {
|
|
nucleus: {
|
|
in: nucleusIDs ?? [],
|
|
},
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
if (currentUser.role === 'coordenador' && collections.supporter) {
|
|
const supporterResult = await find({
|
|
collection: 'supporter',
|
|
depth: 0,
|
|
limit: 0,
|
|
overrideAccess: true,
|
|
pagination: false,
|
|
req,
|
|
select: { contact: true },
|
|
where: {
|
|
nucleus: {
|
|
in: nucleusIDs ?? [],
|
|
},
|
|
},
|
|
})
|
|
for (const doc of supporterResult.docs) {
|
|
const id = relationshipId(doc.contact)
|
|
if (id !== null) contactIDs.push(id)
|
|
}
|
|
}
|
|
|
|
const ids = [...new Set(contactIDs)]
|
|
context[cacheKey] = ids
|
|
|
|
return ids
|
|
}
|
|
|
|
/**
|
|
* Returns null for unrestricted general coordination, otherwise the engaged
|
|
* leadership IDs currently linked to the authenticated campaign user.
|
|
*/
|
|
export const getAccessibleLeadershipIds = async (
|
|
req: PayloadRequest,
|
|
user: CampaignActor = req.user,
|
|
): Promise<AccessibleLeadershipIDs> => {
|
|
const currentUser =
|
|
isCampaignUser(user) && user === req.user ? await getFreshCampaignUser(req, user) : user
|
|
|
|
if (!isCampaignUser(currentUser)) return []
|
|
if (isCampaignGeneral(currentUser)) return null
|
|
|
|
const context = req.context as Record<string, unknown>
|
|
const cacheKey = `${ACCESSIBLE_LEADERSHIP_IDS_CONTEXT_KEY}:${currentUser.id}:${currentUser.role}`
|
|
const cached = context[cacheKey]
|
|
|
|
if (Array.isArray(cached)) {
|
|
return cached.filter((id): id is number => typeof id === 'number')
|
|
}
|
|
|
|
const collections = req.payload.collections as Record<string, unknown>
|
|
const find = req.payload.find.bind(req.payload) as unknown as DynamicFind
|
|
let ids: LeadershipID[] = []
|
|
|
|
if (currentUser.role === 'lideranca' && collections.leadership) {
|
|
const result = await find({
|
|
collection: 'leadership',
|
|
depth: 0,
|
|
limit: 0,
|
|
overrideAccess: true,
|
|
pagination: false,
|
|
req,
|
|
select: { id: true },
|
|
where: {
|
|
and: [{ user: { equals: currentUser.id } }, { supportStatus: { equals: 'engajado' } }],
|
|
},
|
|
})
|
|
|
|
ids = result.docs.map((doc) => relationshipId(doc.id)).filter((id): id is number => id !== null)
|
|
}
|
|
|
|
if (currentUser.role === 'coordenador' && collections.leadership) {
|
|
const nucleusIDs = await getAccessibleNucleusIds(req, currentUser)
|
|
const result = await find({
|
|
collection: 'leadership',
|
|
depth: 0,
|
|
limit: 0,
|
|
overrideAccess: true,
|
|
pagination: false,
|
|
req,
|
|
select: { id: true },
|
|
where: {
|
|
nucleus: {
|
|
in: nucleusIDs ?? [],
|
|
},
|
|
},
|
|
})
|
|
|
|
ids = result.docs.map((doc) => relationshipId(doc.id)).filter((id): id is number => id !== null)
|
|
}
|
|
|
|
const uniqueIDs = [...new Set(ids)]
|
|
context[cacheKey] = uniqueIDs
|
|
|
|
return uniqueIDs
|
|
}
|