All files / src/hooks afterChangeHook.ts

97.05% Statements 33/34
100% Branches 15/15
100% Functions 2/2
96.96% Lines 32/33

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126    3x                 3x       38x   14x 1x       13x     13x 5x   5x   5x                   3x 2x   2x     2x                         2x     2x 2x           2x         11x 6x   6x   6x                   5x 4x   4x   4x                         3x     3x   1x     1x     1x       11x      
import type { CollectionAfterChangeHook } from 'payload'
import type { CollectionPermissionConfig } from '../types'
import { getRolesSlug } from '../utils/getRolesSlug'
 
/**
 * AfterChange hook that handles automatic role assignment
 *
 * This hook runs after user creation and:
 * - Assigns super admin role to first user if autoAssignFirstUser is true
 * - Assigns default role to new users if defaultRole is configured
 */
export const createAfterChangeHook = (
  collectionSlug: string,
  config: CollectionPermissionConfig
): CollectionAfterChangeHook => {
  return async ({ doc, req, operation }) => {
    // Only run for create operations
    if (operation !== 'create') {
      return doc
    }
 
    // Debug logging
    console.info(`[afterChangeHook] Collection: ${collectionSlug}, Operation: ${operation}, Doc ID: ${doc.id}, Has Role: ${!!doc.role}`)
 
    // Handle first user auto-assignment (only if configured)
    if (config.autoAssignFirstUser && doc.id === 1 && !doc.role) {
      console.info('[afterChangeHook] First user detected without role, assigning super admin...')
 
      try {
        // Find super admin role
        const superAdminRole = await req.payload.find({
          collection: getRolesSlug() as 'roles',
          where: {
            name: {
              equals: 'super_admin',
            },
          },
          limit: 1,
        })
 
        if (superAdminRole.docs.length > 0) {
          console.info(`[afterChangeHook] Found super admin role with ID: ${superAdminRole.docs[0].id}`)
 
          try {
            // Update user with super admin role using overrideAccess
            // Use context to signal that this is a system update
            await req.payload.update({
              collection: collectionSlug as Parameters<typeof req.payload.update>[0]['collection'],
              id: doc.id,
              data: {
                role: superAdminRole.docs[0].id,
              },
              context: {
                isSystemUpdate: true,  // Signal to filterOptions that this is system update
                skipValidation: true,
              },
              overrideAccess: true, // Bypass validation and access control
            })
 
            console.info('✅ First user automatically assigned Super Admin role')
 
            // Update the doc object to reflect the change
            doc.role = superAdminRole.docs[0].id
            return doc
          } catch (updateError) {
            console.error('[afterChangeHook] Failed to update user with role:', updateError)
          }
        }
      } catch (error) {
        console.error('Error assigning super admin role after creation:', error)
      }
    }
 
    // Handle default role assignment
    if (config.defaultRole && !doc.role) {
      console.info(`[afterChangeHook] Assigning default role '${config.defaultRole}' to new user...`)
 
      try {
        // Find the default role
        const defaultRole = await req.payload.find({
          collection: getRolesSlug() as 'roles',
          where: {
            name: {
              equals: config.defaultRole,
            },
          },
          limit: 1,
        })
 
        if (defaultRole.docs.length > 0) {
          console.info(`[afterChangeHook] Found default role with ID: ${defaultRole.docs[0].id}`)
 
          try {
            // Update user with default role
            await req.payload.update({
              collection: collectionSlug as Parameters<typeof req.payload.update>[0]['collection'],
              id: doc.id,
              data: {
                role: defaultRole.docs[0].id,
              },
              context: {
                isSystemUpdate: true,
                skipValidation: true,
              },
              overrideAccess: true,
            })
 
            console.info(`✅ User assigned default role '${config.defaultRole}'`)
 
            // Update the doc object to reflect the change
            doc.role = defaultRole.docs[0].id
          } catch (updateError) {
            console.error('[afterChangeHook] Failed to update user with default role:', updateError)
          }
        } else {
          console.warn(`⚠️ Default role '${config.defaultRole}' not found`)
        }
      } catch (error) {
        console.error('Error assigning default role after creation:', error)
      }
    }
 
    return doc
  }
}