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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | 2x 2x 22x 22x 2x 15x 15x 22x 22x 22x 17x 11x 11x 8x 8x 3x 6x 6x 1x 1x 5x 4x 4x 4x 1x 1x 3x 2x 2x 2x 1x 5x 15x 5x 15x 2x 15x 1x 15x 6x 15x | import crypto from 'crypto' import type { Payload } from 'payload' import { getRolesSlug } from './getRolesSlug' import type { SystemRole, SyncResults } from '../types' // Re-export for backward compatibility (will be removed in next major version) export type DefaultRole = SystemRole /** * Generate a hash from role configuration * Used to detect when a role's configuration has changed */ function generateRoleHash(permissions: string[], visibleFor?: string[]): string { const data = JSON.stringify({ permissions: permissions.sort(), visibleFor: visibleFor ? visibleFor.sort() : [] }) return crypto .createHash('sha256') .update(data) .digest('hex') .substring(0, 16) } /** * Sync system-managed roles with optimistic locking * This ensures role configurations are up-to-date while preventing race conditions */ export async function syncSystemRoles( payload: Payload, defaultRoles: SystemRole[] ): Promise<SyncResults> { const results: SyncResults = { created: [], updated: [], failed: [], skipped: [], } for (const defaultRole of defaultRoles) { const newHash = generateRoleHash(defaultRole.permissions, defaultRole.visibleFor) try { // Find existing role const existing = await payload.find({ collection: getRolesSlug() as 'roles', where: { name: { equals: defaultRole.name } }, limit: 1, }) if (existing.docs.length === 0) { // Create new role try { await payload.create({ collection: getRolesSlug() as 'roles', data: { ...defaultRole, active: defaultRole.active ?? true, // Ensure active is always boolean configHash: newHash, configVersion: 1, systemManaged: true, }, }) results.created.push(defaultRole.name) console.info(`✅ Created role: ${defaultRole.name}`) } catch (createError) { // Another instance might have created it concurrently results.failed.push({ role: defaultRole.name, error: `Create failed: ${createError instanceof Error ? createError.message : String(createError)}`, }) } } else { const role = existing.docs[0] // Skip non-system-managed roles (user-created) if (!role.systemManaged) { results.skipped.push(defaultRole.name) continue } // Check if update needed (hash mismatch) if (role.configHash !== newHash) { try { // Optimistic locking update // First check if version still matches const currentRole = await payload.findByID({ collection: getRolesSlug() as 'roles', id: role.id, }) if (currentRole.configVersion !== role.configVersion) { // Another instance already updated results.failed.push({ role: defaultRole.name, error: 'Concurrent update detected', }) continue } // Safe to update const updateResult = await payload.update({ collection: getRolesSlug() as 'roles', id: role.id, data: { permissions: defaultRole.permissions, protected: defaultRole.protected ?? role.protected, visibleFor: defaultRole.visibleFor, configHash: newHash, configVersion: (role.configVersion || 0) + 1, }, }) Eif (updateResult) { results.updated.push(defaultRole.name) console.info(`🔄 Updated role: ${defaultRole.name}`) } } catch (updateError) { // Update failed - likely due to version mismatch (another instance updated) results.failed.push({ role: defaultRole.name, error: `Update failed (likely concurrent update): ${updateError instanceof Error ? updateError.message : String(updateError)}`, }) } } } } catch (error) { results.failed.push({ role: defaultRole.name, error: error instanceof Error ? error.message : String(error), }) } } // Log summary if (results.created.length > 0) { console.info('✅ Created roles:', results.created.join(', ')) } if (results.updated.length > 0) { console.info('🔄 Updated roles:', results.updated.join(', ')) } if (results.skipped.length > 0) { console.info('⏭️ Skipped user-created roles:', results.skipped.join(', ')) } if (results.failed.length > 0) { console.warn('⚠️ Failed operations (likely handled by another instance):', results.failed) } return results } |