strapi-plugin-bulk-updater 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server/index.js
CHANGED
|
@@ -719,8 +719,9 @@ const bulkUpdaterService = ({ strapi }) => {
|
|
|
719
719
|
const configuredCollections = strapi.config.get(
|
|
720
720
|
"plugin::bulk-updater.collections"
|
|
721
721
|
);
|
|
722
|
+
const isWildcard = configuredCollections === "*" || configuredCollections === "all" || Array.isArray(configuredCollections) && (configuredCollections.includes("*") || configuredCollections.includes("all"));
|
|
722
723
|
let targetUids;
|
|
723
|
-
if (configuredCollections && Array.isArray(configuredCollections) && configuredCollections.length > 0) {
|
|
724
|
+
if (!isWildcard && configuredCollections && Array.isArray(configuredCollections) && configuredCollections.length > 0) {
|
|
724
725
|
targetUids = configuredCollections.filter((uid) => uid in allContentTypes);
|
|
725
726
|
} else {
|
|
726
727
|
targetUids = Object.keys(allContentTypes).filter(
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../server/src/routes/index.ts","../../server/src/controllers/bulk-updater.ts","../../server/src/controllers/license.ts","../../server/src/controllers/audit.ts","../../server/src/services/bulk-updater.ts","../../server/src/services/license.ts","../../server/src/services/audit.ts","../../server/src/index.ts"],"sourcesContent":["export default {\n admin: {\n type: 'admin',\n routes: [\n {\n method: 'GET',\n path: '/models',\n handler: 'bulkUpdater.getModels',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/collections/:uid/entries',\n handler: 'bulkUpdater.getCollectionEntries',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/relation-options',\n handler: 'bulkUpdater.getRelationOptions',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batch-update',\n handler: 'bulkUpdater.batchUpdate',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/pending-batches',\n handler: 'bulkUpdater.getPendingBatches',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batches/:batchId/approve',\n handler: 'bulkUpdater.approveBatch',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batches/:batchId/reject',\n handler: 'bulkUpdater.rejectBatch',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batches/:batchId/execute-now',\n handler: 'bulkUpdater.executeNow',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/license',\n handler: 'license.getStatus',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/license/verify',\n handler: 'license.verify',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/license/deactivate',\n handler: 'license.deactivate',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/audit/history',\n handler: 'audit.getHistory',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/audit/:batchId/rollback',\n handler: 'audit.rollback',\n config: {\n policies: [],\n },\n },\n ],\n },\n};\n","import type { Core } from '@strapi/strapi';\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getModels(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const data = await service.getModels();\n\n if (data?.etag) {\n ctx.set('ETag', data.etag);\n ctx.set('Cache-Control', 'private, no-cache');\n\n if (ctx.request.headers['if-none-match'] === data.etag) {\n ctx.status = 304;\n return;\n }\n }\n\n ctx.body = data;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async getCollectionEntries(ctx: any) {\n try {\n const { uid } = ctx.params;\n const { page, pageSize, attribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const data = await service.getCollectionEntries(uid, {\n page: page ? Number(page) : 1,\n pageSize: pageSize ? Number(pageSize) : 10,\n attribute: attribute ? String(attribute) : undefined,\n filterOperator: filterOperator ? String(filterOperator) : undefined,\n filterQuery: filterQuery !== undefined ? String(filterQuery) : undefined,\n search: search ? String(search) : undefined,\n sortField: sortField ? String(sortField) : undefined,\n sortOrder: sortOrder === 'asc' ? 'asc' : 'desc',\n });\n\n if (data?.etag) {\n ctx.set('ETag', data.etag);\n ctx.set('Cache-Control', 'private, no-cache');\n\n if (ctx.request.headers['if-none-match'] === data.etag) {\n ctx.status = 304;\n return;\n }\n }\n\n ctx.body = data;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async getRelationOptions(ctx: any) {\n try {\n const { targetUid } = ctx.query;\n if (!targetUid) {\n ctx.status = 400;\n ctx.body = { success: false, error: 'targetUid is required' };\n return;\n }\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const data = await service.getRelationOptions(String(targetUid));\n ctx.body = { success: true, ...data };\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async batchUpdate(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.batchUpdate(ctx.request.body || {}, currentUser);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async getPendingBatches(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const result = await service.getPendingBatches();\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async approveBatch(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const { executeImmediately = true } = ctx.request.body || {};\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.approveBatch(batchId, currentUser, { executeImmediately });\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async rejectBatch(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const { reason } = ctx.request.body || {};\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.rejectBatch(batchId, currentUser, reason);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async executeNow(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.executeNow(batchId, currentUser);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n});\n","import type { Core } from '@strapi/strapi';\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getStatus(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('license');\n const license = await service.getLicenseStatus();\n ctx.body = { success: true, license };\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async verify(ctx: any) {\n try {\n const { licenseKey } = ctx.request.body || {};\n const service = strapi.plugin('bulk-updater').service('license');\n const result = await service.verifyAndActivateLicense(licenseKey);\n if (!result.success) {\n ctx.status = 400;\n ctx.body = result;\n return;\n }\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async deactivate(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('license');\n const license = await service.deactivateLicense();\n ctx.body = { success: true, license };\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n});\n","import type { Core } from '@strapi/strapi';\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getHistory(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('audit');\n const data = await service.getHistory();\n ctx.body = data;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async rollback(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const service = strapi.plugin('bulk-updater').service('audit');\n const currentUser = ctx.state.user;\n const result = await service.rollbackBatch(batchId, currentUser);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n});\n","import type { Core } from '@strapi/strapi';\nimport crypto from 'crypto';\n\n// In-process memory caches (scoped to pod)\ninterface CacheEntry {\n signature: string;\n data: any;\n timestamp: number;\n}\n\nconst queryCache = new Map<string, CacheEntry>();\nconst modelsCache = new Map<string, { signature: string; totalCount: number; entries: any[] }>();\nconst MAX_QUERY_CACHE_SIZE = 500;\nconst CACHE_TTL_MS = 60 * 1000; // 60-second TTL fallback\n\nexport interface RelationMeta {\n isRelation: boolean;\n relationType: string;\n targetUid: string;\n targetDisplayName: string;\n isMulti: boolean;\n}\n\nlet staticSchemaCache: {\n attributes: string[];\n targetUids: string[];\n metaByUid: Record<\n string,\n {\n uid: string;\n kind: string;\n isSingleType: boolean;\n singularName: string;\n pluralName: string;\n displayName: string;\n attributes: string[];\n relations: Record<string, RelationMeta>;\n }\n >;\n relationsMeta: Record<string, RelationMeta>;\n} | null = null;\n\nexport interface ExecutionWindowConfig {\n enabled: boolean;\n timeZone: string;\n businessHours: {\n start: string;\n end: string;\n };\n offPeakStartTime: string;\n autoQueueDuringBusinessHours: boolean;\n}\n\nexport function getSafeTimezone(timeZone?: string): string {\n if (!timeZone || typeof timeZone !== 'string') {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';\n }\n try {\n Intl.DateTimeFormat(undefined, { timeZone: timeZone.trim() });\n return timeZone.trim();\n } catch {\n return 'UTC';\n }\n}\n\nexport function getTimeInTimezone(timeZone: string, date: Date = new Date()): { hours: number; minutes: number; timeString: string } {\n const safeTz = getSafeTimezone(timeZone);\n const formatter = new Intl.DateTimeFormat('en-GB', {\n timeZone: safeTz,\n hour: '2-digit',\n minute: '2-digit',\n hourCycle: 'h23',\n });\n const timeString = formatter.format(date);\n const [h, m] = timeString.split(':').map(Number);\n return { hours: h, minutes: m, timeString };\n}\n\nexport function getExecutionWindowConfig(strapi: Core.Strapi): ExecutionWindowConfig {\n const pluginConfig = strapi.config.get('plugin::bulk-updater.executionWindow') as Partial<ExecutionWindowConfig> | undefined;\n const configuredTz = pluginConfig?.timeZone ||\n strapi.config.get('server.timeZone') ||\n Intl.DateTimeFormat().resolvedOptions().timeZone ||\n 'UTC';\n const timeZone = getSafeTimezone(configuredTz);\n\n return {\n enabled: pluginConfig?.enabled ?? true,\n timeZone,\n businessHours: {\n start: pluginConfig?.businessHours?.start || '09:00',\n end: pluginConfig?.businessHours?.end || '18:00',\n },\n offPeakStartTime: pluginConfig?.offPeakStartTime || '18:00',\n autoQueueDuringBusinessHours: pluginConfig?.autoQueueDuringBusinessHours ?? true,\n };\n}\n\nexport function checkBusinessHours(config: ExecutionWindowConfig, testDate?: Date) {\n const timeZone = getSafeTimezone(config.timeZone);\n const { hours, minutes, timeString } = getTimeInTimezone(timeZone, testDate || new Date());\n\n const currentMinutes = hours * 60 + minutes;\n const [startH, startM] = (config.businessHours.start || '09:00').split(':').map(Number);\n const [endH, endM] = (config.businessHours.end || '18:00').split(':').map(Number);\n const startMinutes = startH * 60 + startM;\n const endMinutes = endH * 60 + endM;\n\n const isBusinessHours = currentMinutes >= startMinutes && currentMinutes < endMinutes;\n\n return {\n isBusinessHours,\n currentServerTime: timeString,\n timeZone,\n businessHours: config.businessHours,\n offPeakStartTime: config.offPeakStartTime,\n policyMessage: isBusinessHours\n ? `Server policy restricts bulk updates during peak business hours (${config.businessHours.start} - ${config.businessHours.end} ${timeZone}). Current time: ${timeString} (${timeZone}). Updates will be queued for off-peak execution at ${config.offPeakStartTime} ${timeZone}.`\n : `Currently in off-peak execution window (outside ${config.businessHours.start} - ${config.businessHours.end} ${timeZone}). Current time: ${timeString} (${timeZone}). Direct execution is permitted.`,\n };\n}\n\nexport interface PendingBatch {\n id: string;\n createdAt: string;\n createdBy: { id?: string | number; email?: string; firstname?: string; lastname?: string; username?: string };\n payload: {\n attribute: string;\n value: string;\n selectedEntries?: Array<{ uid: string; documentId: string }>;\n selectedCollections?: string[];\n isRegex?: boolean;\n regexPattern?: string;\n publishOnApproval?: boolean;\n };\n summary: {\n attribute: string;\n value: string;\n totalTargets: number;\n collectionBreakdown: Record<string, number>;\n publishOnApproval?: boolean;\n };\n status: 'pending_approval' | 'queued_offpeak' | 'approved' | 'rejected' | 'executed';\n executionMode: 'maker_checker' | 'queue_offpeak' | 'immediate';\n scheduledFor?: string;\n makerNotes?: string;\n approval?: {\n checkerUser?: { id?: string | number; email?: string; firstname?: string; lastname?: string; username?: string };\n decisionDate?: string;\n rejectionReason?: string;\n };\n executionResult?: any;\n}\n\nconst pendingBatches = new Map<string, PendingBatch>();\nlet isRunnerRunning = false;\nlet runnerInterval: NodeJS.Timeout | null = null;\n\nexport function invalidateCollectionCache(uid: string) {\n modelsCache.delete(uid);\n for (const key of queryCache.keys()) {\n if (key.startsWith(`${uid}:`)) {\n queryCache.delete(key);\n }\n }\n}\n\nasync function getCollectionSignature(strapi: Core.Strapi, uid: string): Promise<string> {\n const contentType = strapi.contentTypes[uid];\n if (!contentType) return 'invalid';\n\n const isSingleType = contentType.kind === 'singleType';\n try {\n if (isSingleType) {\n const doc: any = await (strapi.documents(uid as any) as any).findFirst({\n fields: ['updatedAt', 'id'],\n });\n return doc ? `single:${doc.id}:${doc.updatedAt}` : 'single:empty';\n }\n\n const [total, latest] = await Promise.all([\n strapi.documents(uid as any).count(),\n strapi.documents(uid as any).findMany({\n limit: 1,\n sort: { updatedAt: 'desc' },\n fields: ['updatedAt', 'id'],\n }),\n ]);\n\n const latestDoc = latest && latest.length > 0 ? latest[0] : null;\n return `${total}:${latestDoc?.updatedAt || '0'}:${latestDoc?.id || '0'}`;\n } catch {\n return `${Date.now()}`;\n }\n}\n\nasync function computeTargetsSummary(strapi: Core.Strapi, { selectedCollections = [], selectedEntries = [] }: any) {\n const targetsMap = new Map<string, { uid: string; documentId: string }>();\n const breakdown: Record<string, number> = {};\n\n for (const colUid of selectedCollections) {\n const contentType = strapi.contentTypes[colUid];\n const displayName = contentType?.info?.displayName || colUid;\n try {\n const entries = await strapi.documents(colUid as any).findMany();\n const count = entries ? entries.length : 0;\n breakdown[displayName] = (breakdown[displayName] || 0) + count;\n for (const entry of (entries || []) as any[]) {\n targetsMap.set(`${colUid}@@@${entry.documentId}`, { uid: colUid, documentId: entry.documentId });\n }\n } catch {\n // Ignore count failures\n }\n }\n\n for (const item of selectedEntries) {\n if (!item.uid || !item.documentId) continue;\n const key = `${item.uid}@@@${item.documentId}`;\n if (!targetsMap.has(key)) {\n targetsMap.set(key, { uid: item.uid, documentId: item.documentId });\n const contentType = strapi.contentTypes[item.uid];\n const displayName = contentType?.info?.displayName || item.uid;\n breakdown[displayName] = (breakdown[displayName] || 0) + 1;\n }\n }\n\n return {\n totalTargets: targetsMap.size,\n collectionBreakdown: breakdown,\n };\n}\n\nasync function executeBatchInternal(\n strapi: Core.Strapi,\n {\n attribute,\n value,\n selectedEntries = [],\n selectedCollections = [],\n isRegex = false,\n regexPattern = '',\n publishOnApproval = true,\n }: {\n attribute: string;\n value: string;\n selectedEntries?: Array<{ uid: string; documentId: string }>;\n selectedCollections?: string[];\n isRegex?: boolean;\n regexPattern?: string;\n publishOnApproval?: boolean;\n },\n currentUser?: any,\n existingBatchId?: string\n) {\n if (!attribute) {\n throw new Error('Attribute name is required.');\n }\n\n const licenseService = strapi.plugin('bulk-updater').service('license');\n const license = await licenseService.getLicenseStatus();\n\n // Check Pro feature gating\n if (isRegex && !license.isPro) {\n throw new Error(\n 'Regex find & replace is a Pro feature. Please activate a Pro license in Plugin Settings.'\n );\n }\n\n const targetsMap = new Map<string, { uid: string; documentId: string }>();\n\n // 1. Add entire collections if selected\n for (const colUid of selectedCollections) {\n if (!strapi.contentTypes[colUid]) continue;\n const entries = await strapi.documents(colUid as any).findMany();\n for (const entry of (entries || []) as any[]) {\n const key = `${colUid}@@@${entry.documentId}`;\n targetsMap.set(key, { uid: colUid, documentId: entry.documentId });\n }\n }\n\n // 2. Add individually selected entries\n for (const item of selectedEntries) {\n if (!item.uid || !item.documentId) continue;\n const key = `${item.uid}@@@${item.documentId}`;\n targetsMap.set(key, { uid: item.uid, documentId: item.documentId });\n }\n\n if (targetsMap.size === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = existingBatchId || `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const results: any[] = [];\n let updatedCount = 0;\n let skippedCount = 0;\n\n let regexObj: RegExp | null = null;\n if (isRegex && regexPattern) {\n regexObj = new RegExp(regexPattern, 'g');\n }\n\n const targets = Array.from(targetsMap.values());\n const CHUNK_SIZE = 25; // 25 concurrent updates per batch window\n\n // Request-scoped DataLoader for deduplicating lookups during batch itemization\n const documentLoader = new Map<string, Promise<any>>();\n const getDocumentDeduplicated = (uid: string, documentId: string) => {\n const loaderKey = `${uid}@@@${documentId}`;\n if (!documentLoader.has(loaderKey)) {\n documentLoader.set(\n loaderKey,\n strapi.documents(uid as any).findOne({ documentId })\n );\n }\n return documentLoader.get(loaderKey);\n };\n\n for (let i = 0; i < targets.length; i += CHUNK_SIZE) {\n const chunk = targets.slice(i, i + CHUNK_SIZE);\n const chunkPromises = chunk.map(async (target) => {\n const contentType = strapi.contentTypes[target.uid];\n const displayName = contentType?.info?.displayName || target.uid;\n\n try {\n const currentEntry: any = await getDocumentDeduplicated(target.uid, target.documentId);\n\n const entryLabel =\n currentEntry?.name ||\n currentEntry?.title ||\n currentEntry?.email ||\n `Entry #${target.documentId}`;\n\n const attrDef = contentType?.attributes?.[attribute];\n const hasAttribute = Boolean(attrDef);\n\n if (hasAttribute) {\n if (attrDef.type === 'relation') {\n const isMulti = attrDef.relation === 'oneToMany' || attrDef.relation === 'manyToMany';\n\n // Retrieve current relation state with populate for accurate audit logging\n const currentEntryWithPopulate: any = await strapi.documents(target.uid as any).findOne({\n documentId: target.documentId,\n populate: [attribute],\n });\n const currentRel = currentEntryWithPopulate?.[attribute];\n\n let oldDocIds: string[] = [];\n let oldDisplay = '';\n if (isMulti && Array.isArray(currentRel)) {\n oldDocIds = currentRel.map((r: any) => r.documentId).filter(Boolean);\n oldDisplay = currentRel.map((r: any) => r.name || r.title || r.email || r.documentId).join(', ') || '(empty)';\n } else if (currentRel) {\n oldDocIds = currentRel.documentId ? [currentRel.documentId] : [];\n oldDisplay = currentRel.name || currentRel.title || currentRel.email || currentRel.documentId || '(empty)';\n } else {\n oldDisplay = '(unlinked / none)';\n }\n\n // Parse relation command:\n let mode: 'set' | 'connect' | 'disconnect' | 'clear' = 'set';\n let targetDocIds: string[] = [];\n let newDisplay = '';\n\n if (value === '__clear__' || value === '') {\n mode = 'clear';\n newDisplay = '(cleared / unlinked)';\n } else {\n try {\n const parsed = JSON.parse(value);\n if (parsed && typeof parsed === 'object' && parsed.mode) {\n mode = parsed.mode;\n targetDocIds = Array.isArray(parsed.documentIds) ? parsed.documentIds : (parsed.documentId ? [parsed.documentId] : []);\n newDisplay = parsed.label || targetDocIds.join(', ') || `[${mode}]`;\n } else if (Array.isArray(parsed)) {\n targetDocIds = parsed;\n newDisplay = targetDocIds.join(', ');\n } else {\n targetDocIds = [value];\n newDisplay = value;\n }\n } catch {\n targetDocIds = [value];\n newDisplay = value;\n }\n }\n\n // Construct Document Service relation mutation payload\n let relationPayload: any;\n if (mode === 'clear') {\n relationPayload = isMulti ? { set: [] } : null;\n } else if (mode === 'set') {\n relationPayload = isMulti ? { set: targetDocIds } : (targetDocIds[0] || null);\n } else if (mode === 'connect') {\n relationPayload = { connect: targetDocIds };\n } else if (mode === 'disconnect') {\n relationPayload = { disconnect: targetDocIds };\n }\n\n await strapi.documents(target.uid as any).update({\n documentId: target.documentId,\n data: {\n [attribute]: relationPayload,\n },\n });\n\n // If publishOnApproval is true and content type has draftAndPublish enabled, publish the document\n const isDraftAndPublishRel = Boolean(contentType?.options?.draftAndPublish);\n if (publishOnApproval && isDraftAndPublishRel) {\n try {\n await (strapi.documents(target.uid as any) as any).publish({\n documentId: target.documentId,\n });\n } catch (publishErr) {\n strapi.log.warn(`[bulk-updater] Failed to auto-publish ${target.uid}/${target.documentId}:`, publishErr);\n }\n }\n\n invalidateCollectionCache(target.uid);\n if (attrDef.target) {\n invalidateCollectionCache(attrDef.target);\n }\n\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: entryLabel,\n attribute,\n oldValue: { isRelation: true, documentIds: oldDocIds, display: oldDisplay },\n newValue: { isRelation: true, mode, documentIds: targetDocIds, display: newDisplay },\n status: 'updated' as const,\n message: `Successfully updated relation \"${attribute}\"`,\n };\n }\n\n let newValue = value;\n const currentVal = currentEntry?.[attribute];\n\n if (regexObj && typeof currentVal === 'string') {\n newValue = currentVal.replace(regexObj, value);\n }\n\n await strapi.documents(target.uid as any).update({\n documentId: target.documentId,\n data: {\n [attribute]: newValue,\n },\n });\n\n // If publishOnApproval is true and content type has draftAndPublish enabled, publish the document\n const isDraftAndPublishScalar = Boolean(contentType?.options?.draftAndPublish);\n if (publishOnApproval && isDraftAndPublishScalar) {\n try {\n await (strapi.documents(target.uid as any) as any).publish({\n documentId: target.documentId,\n });\n } catch (publishErr) {\n strapi.log.warn(`[bulk-updater] Failed to auto-publish ${target.uid}/${target.documentId}:`, publishErr);\n }\n }\n\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: entryLabel,\n attribute,\n oldValue: currentVal ?? null,\n newValue,\n status: 'updated' as const,\n message: `Successfully updated ${attribute}`,\n };\n } else {\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: entryLabel,\n attribute,\n status: 'skipped' as const,\n message: `Skipped: Attribute \"${attribute}\" does not exist in schema for ${displayName}`,\n };\n }\n } catch (updateErr: any) {\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: `Entry #${target.documentId}`,\n attribute,\n status: 'error' as const,\n message: updateErr.message,\n };\n }\n });\n\n const chunkResults = await Promise.all(chunkPromises);\n for (const res of chunkResults) {\n if (res.status === 'updated') updatedCount++;\n else if (res.status === 'skipped') skippedCount++;\n results.push(res);\n }\n }\n\n // Invalidate local in-process cache for all affected collection UIDs\n const affectedUids = new Set(targets.map((t) => t.uid));\n affectedUids.forEach((uid) => {\n invalidateCollectionCache(uid);\n });\n\n const batchSummary = {\n batchId,\n attribute,\n value,\n updatedCount,\n skippedCount,\n totalTargets: targetsMap.size,\n publishOnApproval,\n };\n\n // Stateless audit recording via consumer's declared audit schema\n await strapi.plugin('bulk-updater').service('audit').recordBatch(\n batchSummary,\n results,\n currentUser\n );\n\n return {\n success: true,\n batchId,\n attribute,\n value,\n updatedCount,\n skippedCount,\n totalTargets: targetsMap.size,\n results,\n };\n}\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => {\n // Initialize periodic runner for off-peak queue execution\n if (!runnerInterval) {\n runnerInterval = setInterval(async () => {\n if (isRunnerRunning) return;\n isRunnerRunning = true;\n try {\n const config = getExecutionWindowConfig(strapi);\n const { isBusinessHours } = checkBusinessHours(config);\n\n for (const [id, batch] of pendingBatches.entries()) {\n if (batch.status === 'queued_offpeak' && !isBusinessHours) {\n strapi.log.info(`[bulk-updater] Executing off-peak queued batch: ${id}`);\n const result = await executeBatchInternal(strapi, batch.payload, batch.createdBy, id);\n batch.status = 'executed';\n batch.executionResult = result;\n }\n }\n } catch (err) {\n strapi.log.error('[bulk-updater] Scheduled runner error:', err);\n } finally {\n isRunnerRunning = false;\n }\n }, 30000);\n }\n\n return {\n /**\n * Lightweight Schema Discovery:\n * Returns schema definitions, valid attributes, and total counts (<15KB payload)\n * Previews up to 10 entries per collection to ensure instantaneous initial paint.\n * Utilizes in-memory schema memoization and database high-watermark signature caching.\n */\n async getModels() {\n const allContentTypes = strapi.contentTypes || {};\n const auditConfig = strapi.plugin('bulk-updater').service('audit').getAuditConfig();\n const auditContentType = auditConfig?.contentType;\n\n // 1. Static schema discovery memoized in-memory\n if (!staticSchemaCache) {\n const allAttributesSet = new Set<string>();\n const systemFields = [\n 'createdAt',\n 'updatedAt',\n 'publishedAt',\n 'createdBy',\n 'updatedBy',\n 'locale',\n 'localizations',\n ];\n\n const configuredCollections = strapi.config.get(\n 'plugin::bulk-updater.collections'\n ) as string[] | undefined;\n\n let targetUids: string[];\n if (configuredCollections && Array.isArray(configuredCollections) && configuredCollections.length > 0) {\n targetUids = configuredCollections.filter((uid) => uid in allContentTypes);\n } else {\n targetUids = Object.keys(allContentTypes).filter(\n (uid) =>\n uid.startsWith('api::') &&\n !uid.includes('bulk-updater') &&\n uid !== auditContentType\n );\n }\n\n const metaByUid: Record<string, any> = {};\n const allRelationsMeta: Record<string, RelationMeta> = {};\n\n for (const uid of targetUids) {\n const contentType = allContentTypes[uid];\n if (!contentType || (contentType.kind !== 'collectionType' && contentType.kind !== 'singleType')) continue;\n\n const isSingleType = contentType.kind === 'singleType';\n const rawAttrs = contentType.attributes || {};\n const relationsByAttr: Record<string, RelationMeta> = {};\n\n const validAttrs = Object.keys(rawAttrs).filter((attrKey) => {\n if (systemFields.includes(attrKey)) return false;\n if (rawAttrs[attrKey].type === 'component' || rawAttrs[attrKey].type === 'dynamiczone') return false;\n\n if (rawAttrs[attrKey].type === 'relation') {\n const rel = rawAttrs[attrKey];\n const targetUid = rel.target;\n if (!targetUid || !allContentTypes[targetUid]) return false;\n const targetInfo = allContentTypes[targetUid]?.info;\n const isMulti = rel.relation === 'oneToMany' || rel.relation === 'manyToMany';\n const relMeta: RelationMeta = {\n isRelation: true,\n relationType: rel.relation,\n targetUid,\n targetDisplayName: targetInfo?.displayName || targetUid,\n isMulti,\n };\n relationsByAttr[attrKey] = relMeta;\n allRelationsMeta[attrKey] = relMeta;\n return true;\n }\n\n return true;\n });\n\n validAttrs.forEach((attr) => allAttributesSet.add(attr));\n\n metaByUid[uid] = {\n uid,\n kind: contentType.kind,\n isSingleType,\n singularName: contentType.info?.singularName || uid,\n pluralName: contentType.info?.pluralName || uid,\n displayName: contentType.info?.displayName || uid,\n attributes: validAttrs,\n relations: relationsByAttr,\n };\n }\n\n staticSchemaCache = {\n attributes: Array.from(allAttributesSet),\n targetUids: Object.keys(metaByUid),\n metaByUid,\n relationsMeta: allRelationsMeta,\n };\n }\n\n const collections = [];\n const signatures: string[] = [];\n\n // 2. High-watermark check per collection\n for (const uid of staticSchemaCache.targetUids) {\n const meta = staticSchemaCache.metaByUid[uid];\n if (!meta) continue;\n\n const signature = await getCollectionSignature(strapi, uid);\n signatures.push(`${uid}=${signature}`);\n\n const cached = modelsCache.get(uid);\n if (cached && cached.signature === signature) {\n collections.push({\n ...meta,\n totalEntries: cached.totalCount,\n entries: cached.entries,\n });\n } else {\n let entries: any[] = [];\n let totalCount = 0;\n try {\n const relKeys = Object.keys(meta.relations || {});\n const populateOption = relKeys.length > 0\n ? relKeys.reduce((acc, k) => ({ ...acc, [k]: true }), {})\n : undefined;\n\n if (meta.isSingleType) {\n const singleDoc: any = await (strapi.documents(uid as any) as any).findFirst({\n populate: populateOption,\n });\n if (singleDoc) {\n entries = [singleDoc];\n totalCount = 1;\n }\n } else {\n totalCount = await strapi.documents(uid as any).count();\n entries = await strapi.documents(uid as any).findMany({\n limit: 10,\n sort: { createdAt: 'desc' },\n populate: populateOption,\n });\n }\n } catch (err) {\n strapi.log.error(`[bulk-updater] Failed to fetch entries for ${uid}:`, err);\n entries = [];\n totalCount = 0;\n }\n\n const formattedEntries = (entries || []).map((entry: any) => ({\n id: entry.id,\n documentId: entry.documentId,\n ...entry,\n }));\n\n modelsCache.set(uid, {\n signature,\n totalCount,\n entries: formattedEntries,\n });\n\n collections.push({\n ...meta,\n totalEntries: totalCount,\n entries: formattedEntries,\n });\n }\n }\n\n // Get license status\n const licenseService = strapi.plugin('bulk-updater').service('license');\n const license = await licenseService.getLicenseStatus();\n\n const executionConfig = getExecutionWindowConfig(strapi);\n const windowStatus = checkBusinessHours(executionConfig);\n const pendingCount = Array.from(pendingBatches.values()).filter(\n (b) => b.status === 'pending_approval' || b.status === 'queued_offpeak'\n ).length;\n\n const etag = `W/\"models-${crypto.createHash('md5').update(`${signatures.join('|')}:${license.tier}:${pendingCount}:${windowStatus.isBusinessHours}:${windowStatus.currentServerTime}:${windowStatus.timeZone}`).digest('hex')}\"`;\n\n return {\n success: true,\n attributes: staticSchemaCache.attributes,\n collections,\n relationsMeta: staticSchemaCache.relationsMeta,\n license,\n executionWindow: {\n ...executionConfig,\n ...windowStatus,\n },\n pendingBatchesCount: pendingCount,\n auditEnabled: Boolean(auditConfig?.enabled),\n etag,\n };\n },\n\n /**\n * On-Demand Paginated Collection Entries:\n * Supports server-side pagination (limit / start) and database query pushdown for filters.\n * Cached in-process with cross-pod high-watermark signature validation.\n */\n async getCollectionEntries(\n uid: string,\n {\n page = 1,\n pageSize = 10,\n attribute,\n filterOperator,\n filterQuery,\n search,\n sortField,\n sortOrder = 'desc',\n }: {\n page?: number;\n pageSize?: number;\n attribute?: string;\n filterOperator?: string;\n filterQuery?: string;\n search?: string;\n sortField?: string;\n sortOrder?: 'asc' | 'desc';\n }\n ) {\n const contentType = strapi.contentTypes[uid];\n if (!contentType) {\n throw new Error(`Collection with UID \"${uid}\" does not exist.`);\n }\n\n const limit = Math.min(Math.max(Number(pageSize) || 10, 1), 100);\n const currentPage = Math.max(Number(page) || 1, 1);\n const start = (currentPage - 1) * limit;\n\n const isSingleType = contentType.kind === 'singleType';\n\n // 1. Signature-based caching verification\n const signature = await getCollectionSignature(strapi, uid);\n const cacheKey = `${uid}:${limit}:${start}:${attribute || ''}:${filterOperator || ''}:${filterQuery || ''}:${search || ''}:${sortField || ''}:${sortOrder}`;\n const cached = queryCache.get(cacheKey);\n\n const etag = `W/\"entry-${crypto.createHash('md5').update(`${cacheKey}:${signature}`).digest('hex')}\"`;\n\n if (cached && cached.signature === signature && Date.now() - cached.timestamp < CACHE_TTL_MS) {\n return {\n ...cached.data,\n cached: true,\n etag,\n };\n }\n\n let result: any;\n\n if (isSingleType) {\n const singleDoc: any = await (strapi.documents(uid as any) as any).findFirst();\n result = {\n uid,\n displayName: contentType.info?.displayName || uid,\n isSingleType: true,\n entries: singleDoc ? [singleDoc] : [],\n pagination: {\n page: 1,\n pageSize: 1,\n total: singleDoc ? 1 : 0,\n pageCount: singleDoc ? 1 : 0,\n },\n };\n } else {\n // Build database filters pushdown\n const filters: Record<string, any> = {};\n\n if (attribute && filterOperator && filterQuery !== undefined && filterQuery !== '') {\n switch (filterOperator) {\n case 'equals':\n filters[attribute] = { $eq: filterQuery };\n break;\n case 'notEquals':\n filters[attribute] = { $ne: filterQuery };\n break;\n case 'contains':\n filters[attribute] = { $containsi: filterQuery };\n break;\n case 'notContains':\n filters[attribute] = { $notContainsi: filterQuery };\n break;\n case 'startsWith':\n filters[attribute] = { $startsWith: filterQuery };\n break;\n case 'endsWith':\n filters[attribute] = { $endsWith: filterQuery };\n break;\n case 'empty':\n filters[attribute] = { $null: true };\n break;\n case 'notEmpty':\n filters[attribute] = { $notNull: true };\n break;\n }\n }\n\n if (search && search.trim() !== '') {\n const stringAttrs = Object.keys(contentType.attributes || {}).filter(\n (k) =>\n contentType.attributes[k].type === 'string' ||\n contentType.attributes[k].type === 'text' ||\n contentType.attributes[k].type === 'email'\n );\n\n if (stringAttrs.length > 0) {\n filters.$or = stringAttrs.slice(0, 5).map((attr) => ({\n [attr]: { $containsi: search.trim() },\n }));\n }\n }\n\n const sortClause: Record<string, 'asc' | 'desc'> = {};\n if (sortField && contentType.attributes?.[sortField]) {\n sortClause[sortField] = sortOrder;\n } else {\n sortClause['createdAt'] = 'desc';\n }\n\n const relationAttrs = Object.keys(contentType.attributes || {}).filter(\n (k) => contentType.attributes[k].type === 'relation'\n );\n const populateOption = relationAttrs.length > 0\n ? relationAttrs.reduce((acc, k) => ({ ...acc, [k]: true }), {})\n : undefined;\n\n const [entries, totalEntries] = await Promise.all([\n strapi.documents(uid as any).findMany({\n start,\n limit,\n filters: Object.keys(filters).length > 0 ? filters : undefined,\n sort: sortClause,\n populate: populateOption,\n }),\n strapi.documents(uid as any).count({\n filters: Object.keys(filters).length > 0 ? filters : undefined,\n }),\n ]);\n\n const totalPages = Math.ceil(totalEntries / limit);\n\n result = {\n uid,\n displayName: contentType.info?.displayName || uid,\n isSingleType: false,\n entries: (entries || []).map((entry: any) => ({\n id: entry.id,\n documentId: entry.documentId,\n ...entry,\n })),\n pagination: {\n page: currentPage,\n pageSize: limit,\n total: totalEntries,\n pageCount: totalPages,\n },\n };\n }\n\n // Store in queryCache with bounding limit\n if (queryCache.size >= MAX_QUERY_CACHE_SIZE) {\n const toDelete = Array.from(queryCache.keys()).slice(0, 100);\n toDelete.forEach((k) => queryCache.delete(k));\n }\n queryCache.set(cacheKey, { signature, data: result, timestamp: Date.now() });\n\n return {\n ...result,\n cached: false,\n etag,\n };\n },\n\n /**\n * Get available target entities for a relation field:\n * Cached in-process with cross-pod signature validation.\n */\n async getRelationOptions(targetUid: string) {\n if (!targetUid || !strapi.contentTypes[targetUid]) {\n throw new Error(`Target content type \"${targetUid}\" does not exist.`);\n }\n\n const cacheKey = `rel_options:${targetUid}`;\n const signature = await getCollectionSignature(strapi, targetUid);\n const cached = queryCache.get(cacheKey);\n if (cached && cached.signature === signature) {\n return cached.data;\n }\n\n const docs = await strapi.documents(targetUid as any).findMany({\n limit: 100,\n sort: { createdAt: 'desc' },\n });\n\n const options = (docs || []).map((doc: any) => {\n const label = doc.name || doc.title || doc.email || doc.username || `Entry #${doc.id || doc.documentId}`;\n return {\n documentId: doc.documentId,\n label,\n };\n });\n\n const result = { targetUid, options };\n queryCache.set(cacheKey, { signature, data: result, timestamp: Date.now() });\n return result;\n },\n\n /**\n * Batch Update API with Maker-Checker and Off-Peak Scheduling:\n */\n async batchUpdate(\n {\n attribute,\n value,\n selectedEntries = [],\n selectedCollections = [],\n isRegex = false,\n regexPattern = '',\n executionMode = 'immediate',\n makerNotes = '',\n forceImmediate = false,\n publishOnApproval = true,\n }: {\n attribute: string;\n value: string;\n selectedEntries?: Array<{ uid: string; documentId: string }>;\n selectedCollections?: string[];\n isRegex?: boolean;\n regexPattern?: string;\n executionMode?: 'immediate' | 'queue_offpeak' | 'maker_checker';\n makerNotes?: string;\n forceImmediate?: boolean;\n publishOnApproval?: boolean;\n },\n currentUser?: any\n ) {\n if (!attribute) {\n throw new Error('Attribute name is required.');\n }\n\n const licenseService = strapi.plugin('bulk-updater').service('license');\n const license = await licenseService.getLicenseStatus();\n const isEnterprise = license.tier === 'enterprise';\n\n const executionConfig = getExecutionWindowConfig(strapi);\n const { isBusinessHours, policyMessage } = checkBusinessHours(executionConfig);\n\n // 1. Enterprise Maker-Checker Flow:\n if (executionMode === 'maker_checker') {\n const summary = await computeTargetsSummary(strapi, { selectedCollections, selectedEntries });\n if (summary.totalTargets === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const batch: PendingBatch = {\n id: batchId,\n createdAt: new Date().toISOString(),\n createdBy: currentUser\n ? {\n id: currentUser.id,\n email: currentUser.email,\n firstname: currentUser.firstname,\n lastname: currentUser.lastname,\n username: currentUser.username,\n }\n : { email: 'admin@system.local' },\n payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n summary: {\n attribute,\n value,\n totalTargets: summary.totalTargets,\n collectionBreakdown: summary.collectionBreakdown,\n publishOnApproval,\n },\n status: 'pending_approval',\n executionMode: 'maker_checker',\n makerNotes,\n };\n\n pendingBatches.set(batchId, batch);\n\n return {\n success: true,\n pending: true,\n batchId,\n status: 'pending_approval',\n message: 'Batch submitted for Checker authorization (4-Eyes Maker-Checker Workflow).',\n batch,\n };\n }\n\n // 2. Off-Peak Queued Batch:\n if (executionMode === 'queue_offpeak') {\n const summary = await computeTargetsSummary(strapi, { selectedCollections, selectedEntries });\n if (summary.totalTargets === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const batch: PendingBatch = {\n id: batchId,\n createdAt: new Date().toISOString(),\n createdBy: currentUser\n ? {\n id: currentUser.id,\n email: currentUser.email,\n firstname: currentUser.firstname,\n lastname: currentUser.lastname,\n username: currentUser.username,\n }\n : { email: 'admin@system.local' },\n payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n summary: {\n attribute,\n value,\n totalTargets: summary.totalTargets,\n collectionBreakdown: summary.collectionBreakdown,\n publishOnApproval,\n },\n status: 'queued_offpeak',\n executionMode: 'queue_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n makerNotes,\n };\n\n pendingBatches.set(batchId, batch);\n\n return {\n success: true,\n pending: true,\n batchId,\n status: 'queued_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n message: `Batch queued for server off-peak maintenance window (${executionConfig.offPeakStartTime}).`,\n batch,\n };\n }\n\n // 3. Immediate execution request with Server Business Hours enforcement:\n if (executionConfig.enabled && executionConfig.autoQueueDuringBusinessHours && isBusinessHours && !forceImmediate) {\n const summary = await computeTargetsSummary(strapi, { selectedCollections, selectedEntries });\n if (summary.totalTargets === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const batch: PendingBatch = {\n id: batchId,\n createdAt: new Date().toISOString(),\n createdBy: currentUser\n ? {\n id: currentUser.id,\n email: currentUser.email,\n firstname: currentUser.firstname,\n lastname: currentUser.lastname,\n username: currentUser.username,\n }\n : { email: 'admin@system.local' },\n payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n summary: {\n attribute,\n value,\n totalTargets: summary.totalTargets,\n collectionBreakdown: summary.collectionBreakdown,\n publishOnApproval,\n },\n status: 'queued_offpeak',\n executionMode: 'queue_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n makerNotes: 'Automatically queued due to peak business hours restriction.',\n };\n\n pendingBatches.set(batchId, batch);\n\n return {\n success: true,\n pending: true,\n batchId,\n status: 'queued_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n message: policyMessage,\n batch,\n };\n }\n\n // 4. Immediate execution\n return await executeBatchInternal(\n strapi,\n { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n currentUser\n );\n },\n\n /**\n * Get Pending & Queued Batches\n */\n getPendingBatches() {\n const executionConfig = getExecutionWindowConfig(strapi);\n const windowStatus = checkBusinessHours(executionConfig);\n const batches = Array.from(pendingBatches.values()).sort(\n (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()\n );\n\n return {\n success: true,\n batches,\n executionWindow: {\n ...executionConfig,\n ...windowStatus,\n },\n };\n },\n\n /**\n * Checker Approves Batch\n */\n async approveBatch(batchId: string, checkerUser: any, options: { executeImmediately?: boolean } = { executeImmediately: true }) {\n const batch = pendingBatches.get(batchId);\n if (!batch) {\n throw new Error(`Pending batch ${batchId} not found.`);\n }\n if (batch.status !== 'pending_approval') {\n throw new Error(`Batch ${batchId} is not pending approval (current status: ${batch.status}).`);\n }\n\n batch.approval = {\n checkerUser: checkerUser\n ? {\n id: checkerUser.id,\n email: checkerUser.email,\n firstname: checkerUser.firstname,\n lastname: checkerUser.lastname,\n username: checkerUser.username,\n }\n : { email: 'checker@system.local' },\n decisionDate: new Date().toISOString(),\n };\n\n const config = getExecutionWindowConfig(strapi);\n const { isBusinessHours } = checkBusinessHours(config);\n\n if (options.executeImmediately || !isBusinessHours) {\n batch.status = 'approved';\n const result = await executeBatchInternal(strapi, batch.payload, checkerUser, batch.id);\n batch.status = 'executed';\n batch.executionResult = result;\n return {\n success: true,\n batchId,\n status: 'executed',\n message: 'Batch approved and executed successfully by Checker.',\n batch,\n result,\n };\n } else {\n batch.status = 'approved';\n return {\n success: true,\n batchId,\n status: 'approved',\n message: `Batch approved by Checker. Queued for execution during off-peak window (${config.offPeakStartTime}).`,\n batch,\n };\n }\n },\n\n /**\n * Checker Rejects Batch\n */\n rejectBatch(batchId: string, checkerUser: any, reason: string = 'Rejected by Checker') {\n const batch = pendingBatches.get(batchId);\n if (!batch) {\n throw new Error(`Pending batch ${batchId} not found.`);\n }\n batch.status = 'rejected';\n batch.approval = {\n checkerUser: checkerUser\n ? {\n id: checkerUser.id,\n email: checkerUser.email,\n firstname: checkerUser.firstname,\n lastname: checkerUser.lastname,\n username: checkerUser.username,\n }\n : { email: 'checker@system.local' },\n decisionDate: new Date().toISOString(),\n rejectionReason: reason,\n };\n\n return {\n success: true,\n batchId,\n status: 'rejected',\n message: `Batch rejected by Checker: ${reason}`,\n batch,\n };\n },\n\n /**\n * Run Batch Immediately\n */\n async executeNow(batchId: string, currentUser: any) {\n const batch = pendingBatches.get(batchId);\n if (!batch) {\n throw new Error(`Pending batch ${batchId} not found.`);\n }\n if (batch.status === 'executed') {\n throw new Error(`Batch ${batchId} has already been executed.`);\n }\n\n const result = await executeBatchInternal(strapi, batch.payload, currentUser, batch.id);\n batch.status = 'executed';\n batch.executionResult = result;\n\n return {\n success: true,\n batchId,\n status: 'executed',\n message: 'Batch executed immediately.',\n batch,\n result,\n };\n },\n\n invalidateCache(uid?: string) {\n if (uid) {\n invalidateCollectionCache(uid);\n } else {\n queryCache.clear();\n modelsCache.clear();\n staticSchemaCache = null;\n }\n },\n };\n};\n","import type { Core } from '@strapi/strapi';\n\nexport interface LicenseStatus {\n isPro: boolean;\n tier: 'free' | 'pro' | 'enterprise';\n licenseKey: string | null;\n licensedTo: string | null;\n activatedAt: string | null;\n features: string[];\n}\n\nconst DEFAULT_FREE_STATUS: LicenseStatus = {\n isPro: false,\n tier: 'free',\n licenseKey: null,\n licensedTo: null,\n activatedAt: null,\n features: [\n 'Basic attribute batch updates',\n 'Schema compatibility checks',\n 'Interactive target selection',\n ],\n};\n\nconst PRO_FEATURES = [\n 'Basic attribute batch updates',\n 'Schema compatibility checks',\n 'Interactive target selection',\n 'Regex find & replace within attributes',\n 'Cross-relation and dynamic zone updates',\n 'Audit log export (CSV / JSON)',\n 'Unlimited collection batch operations',\n 'Priority enterprise support',\n];\n\nconst ENTERPRISE_FEATURES = [\n ...PRO_FEATURES,\n 'Maker-Checker Approval Workflow (4-Eyes Principle)',\n 'Server-Enforced Off-Peak Maintenance Window',\n 'Scheduled batch queuing during business hours',\n];\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getStore() {\n return strapi.store({\n type: 'plugin',\n name: 'bulk-updater',\n key: 'license',\n });\n },\n\n async getLicenseStatus(): Promise<LicenseStatus> {\n // Check developer config override in config/plugins.ts\n const isConfigEnterprise = strapi.config.get('plugin::bulk-updater.makerChecker.enabled');\n\n const store = await this.getStore();\n const stored = await store.get({});\n if (!stored || !stored.licenseKey) {\n if (isConfigEnterprise) {\n return {\n isPro: true,\n tier: 'enterprise',\n licenseKey: 'CONFIG-OVERRIDE',\n licensedTo: 'Enterprise Developer Config',\n activatedAt: new Date().toISOString(),\n features: ENTERPRISE_FEATURES,\n };\n }\n return DEFAULT_FREE_STATUS;\n }\n if (isConfigEnterprise) {\n return {\n ...stored,\n isPro: true,\n tier: 'enterprise',\n features: Array.from(new Set([...(stored.features || []), ...ENTERPRISE_FEATURES])),\n };\n }\n return stored;\n },\n\n async verifyAndActivateLicense(licenseKey: string): Promise<{ success: boolean; license?: LicenseStatus; error?: string }> {\n if (!licenseKey || typeof licenseKey !== 'string') {\n return { success: false, error: 'A valid license key is required.' };\n }\n\n const trimmed = licenseKey.trim();\n\n // Verification Logic:\n // Supports live LemonSqueezy / Gumroad / Keygen keys or offline signature/prefix keys\n // For demonstration and production readiness, keys with prefix PRO- or ENTERPRISE- are valid Pro licenses\n const isEnterprise = trimmed.toUpperCase().startsWith('ENT-') || trimmed.toUpperCase().startsWith('ENTERPRISE-');\n const isPro = trimmed.toUpperCase().startsWith('PRO-') || isEnterprise;\n\n if (!isPro) {\n return {\n success: false,\n error: 'Invalid license key. License keys must start with PRO- or ENTERPRISE-.',\n };\n }\n\n const tier = isEnterprise ? 'enterprise' : 'pro';\n const status: LicenseStatus = {\n isPro: true,\n tier,\n licenseKey: trimmed,\n licensedTo: `Licensed Customer (${tier.toUpperCase()})`,\n activatedAt: new Date().toISOString(),\n features: isEnterprise ? ENTERPRISE_FEATURES : PRO_FEATURES,\n };\n\n const store = await this.getStore();\n await store.set({ value: status });\n\n return {\n success: true,\n license: status,\n };\n },\n\n async deactivateLicense(): Promise<LicenseStatus> {\n const store = await this.getStore();\n await store.set({ value: DEFAULT_FREE_STATUS });\n return DEFAULT_FREE_STATUS;\n },\n});\n","import type { Core } from '@strapi/strapi';\n\nexport interface AuditConfig {\n enabled: boolean;\n mode?: 'custom' | 'default' | 'enterprise_native' | 'disabled';\n contentType?: string;\n fields?: Record<string, string>;\n isEnterpriseNativeAvailable?: boolean;\n error?: string;\n}\n\nconst DEFAULT_FIELDS: Record<string, string> = {\n batchId: 'batchId',\n attribute: 'attribute',\n value: 'value',\n userEmail: 'userEmail',\n userName: 'userName',\n updatedCount: 'updatedCount',\n skippedCount: 'skippedCount',\n totalTargets: 'totalTargets',\n diffs: 'diffs',\n isRolledBack: 'isRolledBack',\n rolledBackAt: 'rolledBackAt',\n rolledBackBy: 'rolledBackBy',\n executedAt: 'executedAt',\n};\n\nconst DEFAULT_CANDIDATE_TYPES = [\n 'api::audit-log.audit-log',\n 'api::audit.audit',\n 'api::bulk-audit-log.bulk-audit-log',\n];\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n /**\n * Detects whether Strapi Enterprise Native Audit Log service is available.\n */\n getEnterpriseAuditService() {\n try {\n const svc =\n (strapi as any).service?.('admin::audit-logs') ||\n (strapi as any).admin?.services?.['audit-logs'];\n return svc || null;\n } catch {\n return null;\n }\n },\n\n /**\n * Resolves the audit configuration:\n * 1. Explicit consumer custom schema (full declarative control)\n * 2. Zero-config convention auto-detection (api::audit-log.audit-log)\n * 3. Strapi Enterprise native audit log bridge\n */\n getAuditConfig(): AuditConfig {\n const rawConfig = strapi.config.get('plugin::bulk-updater.audit') as any;\n const eeService = this.getEnterpriseAuditService();\n const isEnterpriseNativeAvailable = Boolean(\n eeService && typeof eeService.save === 'function'\n );\n\n // If consumer explicitly disabled audit\n if (rawConfig && rawConfig.enabled === false) {\n return {\n enabled: false,\n mode: 'disabled',\n isEnterpriseNativeAvailable,\n };\n }\n\n // Tier 1: Explicit consumer custom schema\n if (rawConfig?.contentType) {\n const { contentType, fields } = rawConfig;\n const schema = strapi.contentTypes[contentType];\n if (!schema) {\n return {\n enabled: false,\n mode: 'disabled',\n isEnterpriseNativeAvailable,\n error: `Declared audit contentType \"${contentType}\" does not exist in Strapi schema registry.`,\n };\n }\n\n // Check fields or fallback to DEFAULT_FIELDS\n const resolvedFields = { ...DEFAULT_FIELDS, ...(fields || {}) };\n return {\n enabled: true,\n mode: 'custom',\n contentType,\n fields: resolvedFields,\n isEnterpriseNativeAvailable,\n };\n }\n\n // Tier 2: Zero-config auto-discovery of default audit collection\n for (const candidate of DEFAULT_CANDIDATE_TYPES) {\n if (strapi.contentTypes[candidate]) {\n return {\n enabled: true,\n mode: 'default',\n contentType: candidate,\n fields: DEFAULT_FIELDS,\n isEnterpriseNativeAvailable,\n };\n }\n }\n\n // Tier 3: Strapi Enterprise native audit logs\n if (isEnterpriseNativeAvailable) {\n return {\n enabled: true,\n mode: 'enterprise_native',\n isEnterpriseNativeAvailable: true,\n };\n }\n\n // Tier 4: Audit disabled / unconfigured\n return {\n enabled: false,\n mode: 'disabled',\n isEnterpriseNativeAvailable: false,\n error:\n 'No audit collection configured or detected. To enable rollback, provide a collection or use Strapi Enterprise.',\n };\n },\n\n /**\n * Optionally writes an audit event directly to Strapi Enterprise's native Audit Logs table.\n */\n async recordToEnterpriseNative(\n action: string,\n payload: Record<string, any>,\n user?: any\n ) {\n try {\n const eeService = this.getEnterpriseAuditService();\n if (eeService && typeof eeService.save === 'function') {\n await eeService.save({\n action,\n date: new Date(),\n userId: user?.id,\n payload,\n });\n strapi.log.info(\n `[bulk-updater] Successfully dispatched event to Strapi Enterprise Audit Log: ${action}`\n );\n }\n } catch (err: any) {\n strapi.log.warn(\n `[bulk-updater] Note: Could not dispatch to Strapi Enterprise native audit log: ${err.message}`\n );\n }\n },\n\n /**\n * Records a batch execution:\n * - Dispatches to Strapi Enterprise native audit log if available\n * - Records to consumer or auto-detected collection type for one-click rollback\n */\n async recordBatch(\n batchSummary: {\n batchId: string;\n attribute: string;\n value: string;\n updatedCount: number;\n skippedCount: number;\n totalTargets: number;\n },\n results: any[],\n user?: any\n ) {\n const config = this.getAuditConfig();\n\n // 1. Always attempt Strapi Enterprise native logging if available\n await this.recordToEnterpriseNative(\n 'bulk-updater.batch-update',\n {\n batchId: batchSummary.batchId,\n attribute: batchSummary.attribute,\n value: batchSummary.value,\n updatedCount: batchSummary.updatedCount,\n skippedCount: batchSummary.skippedCount,\n totalTargets: batchSummary.totalTargets,\n },\n user\n );\n\n // 2. Persist to rollback-capable collection type if available\n if (!config.enabled || !config.contentType || !config.fields) {\n return null;\n }\n\n const f = config.fields;\n const mappedData: any = {};\n\n if (f.batchId) mappedData[f.batchId] = batchSummary.batchId;\n if (f.attribute) mappedData[f.attribute] = batchSummary.attribute;\n if (f.value) mappedData[f.value] = batchSummary.value;\n if (f.userEmail) mappedData[f.userEmail] = user?.email || 'admin@strapi.io';\n if (f.userName) {\n mappedData[f.userName] = user?.firstname\n ? `${user.firstname} ${user.lastname || ''}`.trim()\n : user?.email || 'Admin';\n }\n if (f.updatedCount) mappedData[f.updatedCount] = batchSummary.updatedCount;\n if (f.skippedCount) mappedData[f.skippedCount] = batchSummary.skippedCount;\n if (f.totalTargets) mappedData[f.totalTargets] = batchSummary.totalTargets;\n if (f.diffs) mappedData[f.diffs] = results;\n if (f.isRolledBack) mappedData[f.isRolledBack] = false;\n if (f.executedAt) mappedData[f.executedAt] = new Date().toISOString();\n\n try {\n const created = await strapi.documents(config.contentType as any).create({\n data: mappedData,\n });\n return created;\n } catch (err: any) {\n strapi.log.error(`[bulk-updater] Failed to record audit log: ${err.message}`);\n return null;\n }\n },\n\n /**\n * Fetches audit history for the UI\n */\n async getHistory() {\n const config = this.getAuditConfig();\n\n if (config.mode === 'enterprise_native' && !config.contentType) {\n return {\n enabled: true,\n mode: 'enterprise_native',\n message:\n 'Strapi Enterprise Native Audit Logs active. Batch events are recorded directly to Strapi Settings > Audit Logs. (To enable one-click document rollback, declare or add an audit-log collection).',\n history: [],\n };\n }\n\n if (!config.enabled || !config.contentType || !config.fields) {\n return {\n enabled: false,\n message:\n config.error ||\n 'Audit trail is disabled. To enable audit logs & rollback, declare an audit schema in config/plugins.ts or create api::audit-log.audit-log.',\n history: [],\n };\n }\n\n try {\n const records: any[] = await strapi.documents(config.contentType as any).findMany({\n sort: { createdAt: 'desc' } as any,\n });\n\n const f = config.fields;\n const normalized = (records || []).map((r: any) => ({\n id: r.id,\n documentId: r.documentId,\n batchId: r[f.batchId] || r.documentId,\n attribute: r[f.attribute],\n value: r[f.value],\n userEmail: r[f.userEmail] || 'admin@strapi.io',\n userName: r[f.userName] || 'Admin',\n updatedCount: r[f.updatedCount] || 0,\n skippedCount: r[f.skippedCount] || 0,\n totalTargets: r[f.totalTargets] || 0,\n diffs: typeof r[f.diffs] === 'string' ? JSON.parse(r[f.diffs]) : r[f.diffs] || [],\n isRolledBack: Boolean(r[f.isRolledBack]),\n rolledBackAt: r[f.rolledBackAt] || null,\n rolledBackBy: r[f.rolledBackBy] || null,\n executedAt: r[f.executedAt] || r.createdAt,\n }));\n\n return {\n enabled: true,\n mode: config.mode,\n contentType: config.contentType,\n isEnterpriseNativeAvailable: config.isEnterpriseNativeAvailable,\n history: normalized,\n };\n } catch (err: any) {\n return {\n enabled: false,\n message: `Failed to read audit history: ${err.message}`,\n history: [],\n };\n }\n },\n\n /**\n * Performs deterministic rollback of a batch\n */\n async rollbackBatch(batchId: string, currentUser?: any) {\n const config = this.getAuditConfig();\n if (!config.enabled || !config.contentType || !config.fields) {\n throw new Error('Audit trail is not configured or disabled.');\n }\n\n const f = config.fields;\n const records: any[] = await strapi.documents(config.contentType as any).findMany({\n filters: {\n [f.batchId]: { $eq: batchId },\n } as any,\n });\n\n const record = records?.[0];\n if (!record) {\n throw new Error(`Batch record with ID \"${batchId}\" was not found in audit trail.`);\n }\n\n if (record[f.isRolledBack]) {\n throw new Error(`Batch \"${batchId}\" has already been rolled back.`);\n }\n\n const diffs =\n typeof record[f.diffs] === 'string'\n ? JSON.parse(record[f.diffs])\n : record[f.diffs] || [];\n\n let revertedCount = 0;\n const rollbackDetails = [];\n\n for (const item of diffs) {\n if (item.status === 'updated' && item.uid && item.documentId && item.attribute) {\n try {\n const isRel = item.oldValue && typeof item.oldValue === 'object' && item.oldValue.isRelation;\n let rollbackPayload: any;\n\n if (isRel) {\n const oldDocIds = Array.isArray(item.oldValue.documentIds) ? item.oldValue.documentIds : [];\n rollbackPayload = { set: oldDocIds };\n } else {\n rollbackPayload = item.oldValue;\n }\n\n await strapi.documents(item.uid as any).update({\n documentId: item.documentId,\n data: {\n [item.attribute]: rollbackPayload,\n },\n });\n\n const ct = strapi.contentTypes[item.uid];\n if (ct?.options?.draftAndPublish) {\n try {\n await (strapi.documents(item.uid as any) as any).publish({\n documentId: item.documentId,\n });\n } catch {}\n }\n\n try {\n strapi.plugin('bulk-updater').service('bulkUpdater').invalidateCollectionCache(item.uid);\n } catch {}\n\n revertedCount++;\n rollbackDetails.push({\n uid: item.uid,\n documentId: item.documentId,\n label: item.label,\n attribute: item.attribute,\n revertedTo: item.oldValue,\n status: 'reverted',\n });\n } catch (err: any) {\n rollbackDetails.push({\n uid: item.uid,\n documentId: item.documentId,\n label: item.label,\n status: 'error',\n message: err.message,\n });\n }\n }\n }\n\n const updateData: any = {};\n if (f.isRolledBack) updateData[f.isRolledBack] = true;\n if (f.rolledBackAt) updateData[f.rolledBackAt] = new Date().toISOString();\n if (f.rolledBackBy) {\n updateData[f.rolledBackBy] = currentUser?.email || 'Admin';\n }\n\n await strapi.documents(config.contentType as any).update({\n documentId: record.documentId,\n data: updateData,\n });\n\n // Invalidate affected collection caches\n try {\n const affectedUids = new Set(rollbackDetails.map((d: any) => d.uid));\n const bulkService = strapi.plugin('bulk-updater').service('bulkUpdater');\n affectedUids.forEach((uid: any) => {\n if (uid && bulkService?.invalidateCache) {\n bulkService.invalidateCache(uid);\n }\n });\n } catch {}\n\n // Mirror rollback event to Strapi Enterprise native audit log if available\n await this.recordToEnterpriseNative(\n 'bulk-updater.rollback',\n {\n batchId,\n revertedCount,\n },\n currentUser\n );\n\n return {\n success: true,\n batchId,\n revertedCount,\n details: rollbackDetails,\n };\n },\n});\n","import routes from './routes';\nimport bulkUpdaterController from './controllers/bulk-updater';\nimport licenseController from './controllers/license';\nimport auditController from './controllers/audit';\nimport bulkUpdaterService from './services/bulk-updater';\nimport licenseService from './services/license';\nimport auditService from './services/audit';\n\nexport default {\n register({ strapi }: { strapi: any }) {},\n bootstrap({ strapi }: { strapi: any }) {},\n routes,\n controllers: {\n bulkUpdater: bulkUpdaterController,\n license: licenseController,\n audit: auditController,\n },\n services: {\n bulkUpdater: bulkUpdaterService,\n license: licenseService,\n audit: auditService,\n },\n};\n"],"names":["licenseService","crypto"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../server/src/routes/index.ts","../../server/src/controllers/bulk-updater.ts","../../server/src/controllers/license.ts","../../server/src/controllers/audit.ts","../../server/src/services/bulk-updater.ts","../../server/src/services/license.ts","../../server/src/services/audit.ts","../../server/src/index.ts"],"sourcesContent":["export default {\n admin: {\n type: 'admin',\n routes: [\n {\n method: 'GET',\n path: '/models',\n handler: 'bulkUpdater.getModels',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/collections/:uid/entries',\n handler: 'bulkUpdater.getCollectionEntries',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/relation-options',\n handler: 'bulkUpdater.getRelationOptions',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batch-update',\n handler: 'bulkUpdater.batchUpdate',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/pending-batches',\n handler: 'bulkUpdater.getPendingBatches',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batches/:batchId/approve',\n handler: 'bulkUpdater.approveBatch',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batches/:batchId/reject',\n handler: 'bulkUpdater.rejectBatch',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batches/:batchId/execute-now',\n handler: 'bulkUpdater.executeNow',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/license',\n handler: 'license.getStatus',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/license/verify',\n handler: 'license.verify',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/license/deactivate',\n handler: 'license.deactivate',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/audit/history',\n handler: 'audit.getHistory',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/audit/:batchId/rollback',\n handler: 'audit.rollback',\n config: {\n policies: [],\n },\n },\n ],\n },\n};\n","import type { Core } from '@strapi/strapi';\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getModels(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const data = await service.getModels();\n\n if (data?.etag) {\n ctx.set('ETag', data.etag);\n ctx.set('Cache-Control', 'private, no-cache');\n\n if (ctx.request.headers['if-none-match'] === data.etag) {\n ctx.status = 304;\n return;\n }\n }\n\n ctx.body = data;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async getCollectionEntries(ctx: any) {\n try {\n const { uid } = ctx.params;\n const { page, pageSize, attribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const data = await service.getCollectionEntries(uid, {\n page: page ? Number(page) : 1,\n pageSize: pageSize ? Number(pageSize) : 10,\n attribute: attribute ? String(attribute) : undefined,\n filterOperator: filterOperator ? String(filterOperator) : undefined,\n filterQuery: filterQuery !== undefined ? String(filterQuery) : undefined,\n search: search ? String(search) : undefined,\n sortField: sortField ? String(sortField) : undefined,\n sortOrder: sortOrder === 'asc' ? 'asc' : 'desc',\n });\n\n if (data?.etag) {\n ctx.set('ETag', data.etag);\n ctx.set('Cache-Control', 'private, no-cache');\n\n if (ctx.request.headers['if-none-match'] === data.etag) {\n ctx.status = 304;\n return;\n }\n }\n\n ctx.body = data;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async getRelationOptions(ctx: any) {\n try {\n const { targetUid } = ctx.query;\n if (!targetUid) {\n ctx.status = 400;\n ctx.body = { success: false, error: 'targetUid is required' };\n return;\n }\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const data = await service.getRelationOptions(String(targetUid));\n ctx.body = { success: true, ...data };\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async batchUpdate(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.batchUpdate(ctx.request.body || {}, currentUser);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async getPendingBatches(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const result = await service.getPendingBatches();\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async approveBatch(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const { executeImmediately = true } = ctx.request.body || {};\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.approveBatch(batchId, currentUser, { executeImmediately });\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async rejectBatch(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const { reason } = ctx.request.body || {};\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.rejectBatch(batchId, currentUser, reason);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async executeNow(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.executeNow(batchId, currentUser);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n});\n","import type { Core } from '@strapi/strapi';\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getStatus(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('license');\n const license = await service.getLicenseStatus();\n ctx.body = { success: true, license };\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async verify(ctx: any) {\n try {\n const { licenseKey } = ctx.request.body || {};\n const service = strapi.plugin('bulk-updater').service('license');\n const result = await service.verifyAndActivateLicense(licenseKey);\n if (!result.success) {\n ctx.status = 400;\n ctx.body = result;\n return;\n }\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async deactivate(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('license');\n const license = await service.deactivateLicense();\n ctx.body = { success: true, license };\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n});\n","import type { Core } from '@strapi/strapi';\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getHistory(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('audit');\n const data = await service.getHistory();\n ctx.body = data;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async rollback(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const service = strapi.plugin('bulk-updater').service('audit');\n const currentUser = ctx.state.user;\n const result = await service.rollbackBatch(batchId, currentUser);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n});\n","import type { Core } from '@strapi/strapi';\nimport crypto from 'crypto';\n\n// In-process memory caches (scoped to pod)\ninterface CacheEntry {\n signature: string;\n data: any;\n timestamp: number;\n}\n\nconst queryCache = new Map<string, CacheEntry>();\nconst modelsCache = new Map<string, { signature: string; totalCount: number; entries: any[] }>();\nconst MAX_QUERY_CACHE_SIZE = 500;\nconst CACHE_TTL_MS = 60 * 1000; // 60-second TTL fallback\n\nexport interface RelationMeta {\n isRelation: boolean;\n relationType: string;\n targetUid: string;\n targetDisplayName: string;\n isMulti: boolean;\n}\n\nlet staticSchemaCache: {\n attributes: string[];\n targetUids: string[];\n metaByUid: Record<\n string,\n {\n uid: string;\n kind: string;\n isSingleType: boolean;\n singularName: string;\n pluralName: string;\n displayName: string;\n attributes: string[];\n relations: Record<string, RelationMeta>;\n }\n >;\n relationsMeta: Record<string, RelationMeta>;\n} | null = null;\n\nexport interface ExecutionWindowConfig {\n enabled: boolean;\n timeZone: string;\n businessHours: {\n start: string;\n end: string;\n };\n offPeakStartTime: string;\n autoQueueDuringBusinessHours: boolean;\n}\n\nexport function getSafeTimezone(timeZone?: string): string {\n if (!timeZone || typeof timeZone !== 'string') {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';\n }\n try {\n Intl.DateTimeFormat(undefined, { timeZone: timeZone.trim() });\n return timeZone.trim();\n } catch {\n return 'UTC';\n }\n}\n\nexport function getTimeInTimezone(timeZone: string, date: Date = new Date()): { hours: number; minutes: number; timeString: string } {\n const safeTz = getSafeTimezone(timeZone);\n const formatter = new Intl.DateTimeFormat('en-GB', {\n timeZone: safeTz,\n hour: '2-digit',\n minute: '2-digit',\n hourCycle: 'h23',\n });\n const timeString = formatter.format(date);\n const [h, m] = timeString.split(':').map(Number);\n return { hours: h, minutes: m, timeString };\n}\n\nexport function getExecutionWindowConfig(strapi: Core.Strapi): ExecutionWindowConfig {\n const pluginConfig = strapi.config.get('plugin::bulk-updater.executionWindow') as Partial<ExecutionWindowConfig> | undefined;\n const configuredTz = pluginConfig?.timeZone ||\n strapi.config.get('server.timeZone') ||\n Intl.DateTimeFormat().resolvedOptions().timeZone ||\n 'UTC';\n const timeZone = getSafeTimezone(configuredTz);\n\n return {\n enabled: pluginConfig?.enabled ?? true,\n timeZone,\n businessHours: {\n start: pluginConfig?.businessHours?.start || '09:00',\n end: pluginConfig?.businessHours?.end || '18:00',\n },\n offPeakStartTime: pluginConfig?.offPeakStartTime || '18:00',\n autoQueueDuringBusinessHours: pluginConfig?.autoQueueDuringBusinessHours ?? true,\n };\n}\n\nexport function checkBusinessHours(config: ExecutionWindowConfig, testDate?: Date) {\n const timeZone = getSafeTimezone(config.timeZone);\n const { hours, minutes, timeString } = getTimeInTimezone(timeZone, testDate || new Date());\n\n const currentMinutes = hours * 60 + minutes;\n const [startH, startM] = (config.businessHours.start || '09:00').split(':').map(Number);\n const [endH, endM] = (config.businessHours.end || '18:00').split(':').map(Number);\n const startMinutes = startH * 60 + startM;\n const endMinutes = endH * 60 + endM;\n\n const isBusinessHours = currentMinutes >= startMinutes && currentMinutes < endMinutes;\n\n return {\n isBusinessHours,\n currentServerTime: timeString,\n timeZone,\n businessHours: config.businessHours,\n offPeakStartTime: config.offPeakStartTime,\n policyMessage: isBusinessHours\n ? `Server policy restricts bulk updates during peak business hours (${config.businessHours.start} - ${config.businessHours.end} ${timeZone}). Current time: ${timeString} (${timeZone}). Updates will be queued for off-peak execution at ${config.offPeakStartTime} ${timeZone}.`\n : `Currently in off-peak execution window (outside ${config.businessHours.start} - ${config.businessHours.end} ${timeZone}). Current time: ${timeString} (${timeZone}). Direct execution is permitted.`,\n };\n}\n\nexport interface PendingBatch {\n id: string;\n createdAt: string;\n createdBy: { id?: string | number; email?: string; firstname?: string; lastname?: string; username?: string };\n payload: {\n attribute: string;\n value: string;\n selectedEntries?: Array<{ uid: string; documentId: string }>;\n selectedCollections?: string[];\n isRegex?: boolean;\n regexPattern?: string;\n publishOnApproval?: boolean;\n };\n summary: {\n attribute: string;\n value: string;\n totalTargets: number;\n collectionBreakdown: Record<string, number>;\n publishOnApproval?: boolean;\n };\n status: 'pending_approval' | 'queued_offpeak' | 'approved' | 'rejected' | 'executed';\n executionMode: 'maker_checker' | 'queue_offpeak' | 'immediate';\n scheduledFor?: string;\n makerNotes?: string;\n approval?: {\n checkerUser?: { id?: string | number; email?: string; firstname?: string; lastname?: string; username?: string };\n decisionDate?: string;\n rejectionReason?: string;\n };\n executionResult?: any;\n}\n\nconst pendingBatches = new Map<string, PendingBatch>();\nlet isRunnerRunning = false;\nlet runnerInterval: NodeJS.Timeout | null = null;\n\nexport function invalidateCollectionCache(uid: string) {\n modelsCache.delete(uid);\n for (const key of queryCache.keys()) {\n if (key.startsWith(`${uid}:`)) {\n queryCache.delete(key);\n }\n }\n}\n\nasync function getCollectionSignature(strapi: Core.Strapi, uid: string): Promise<string> {\n const contentType = strapi.contentTypes[uid];\n if (!contentType) return 'invalid';\n\n const isSingleType = contentType.kind === 'singleType';\n try {\n if (isSingleType) {\n const doc: any = await (strapi.documents(uid as any) as any).findFirst({\n fields: ['updatedAt', 'id'],\n });\n return doc ? `single:${doc.id}:${doc.updatedAt}` : 'single:empty';\n }\n\n const [total, latest] = await Promise.all([\n strapi.documents(uid as any).count(),\n strapi.documents(uid as any).findMany({\n limit: 1,\n sort: { updatedAt: 'desc' },\n fields: ['updatedAt', 'id'],\n }),\n ]);\n\n const latestDoc = latest && latest.length > 0 ? latest[0] : null;\n return `${total}:${latestDoc?.updatedAt || '0'}:${latestDoc?.id || '0'}`;\n } catch {\n return `${Date.now()}`;\n }\n}\n\nasync function computeTargetsSummary(strapi: Core.Strapi, { selectedCollections = [], selectedEntries = [] }: any) {\n const targetsMap = new Map<string, { uid: string; documentId: string }>();\n const breakdown: Record<string, number> = {};\n\n for (const colUid of selectedCollections) {\n const contentType = strapi.contentTypes[colUid];\n const displayName = contentType?.info?.displayName || colUid;\n try {\n const entries = await strapi.documents(colUid as any).findMany();\n const count = entries ? entries.length : 0;\n breakdown[displayName] = (breakdown[displayName] || 0) + count;\n for (const entry of (entries || []) as any[]) {\n targetsMap.set(`${colUid}@@@${entry.documentId}`, { uid: colUid, documentId: entry.documentId });\n }\n } catch {\n // Ignore count failures\n }\n }\n\n for (const item of selectedEntries) {\n if (!item.uid || !item.documentId) continue;\n const key = `${item.uid}@@@${item.documentId}`;\n if (!targetsMap.has(key)) {\n targetsMap.set(key, { uid: item.uid, documentId: item.documentId });\n const contentType = strapi.contentTypes[item.uid];\n const displayName = contentType?.info?.displayName || item.uid;\n breakdown[displayName] = (breakdown[displayName] || 0) + 1;\n }\n }\n\n return {\n totalTargets: targetsMap.size,\n collectionBreakdown: breakdown,\n };\n}\n\nasync function executeBatchInternal(\n strapi: Core.Strapi,\n {\n attribute,\n value,\n selectedEntries = [],\n selectedCollections = [],\n isRegex = false,\n regexPattern = '',\n publishOnApproval = true,\n }: {\n attribute: string;\n value: string;\n selectedEntries?: Array<{ uid: string; documentId: string }>;\n selectedCollections?: string[];\n isRegex?: boolean;\n regexPattern?: string;\n publishOnApproval?: boolean;\n },\n currentUser?: any,\n existingBatchId?: string\n) {\n if (!attribute) {\n throw new Error('Attribute name is required.');\n }\n\n const licenseService = strapi.plugin('bulk-updater').service('license');\n const license = await licenseService.getLicenseStatus();\n\n // Check Pro feature gating\n if (isRegex && !license.isPro) {\n throw new Error(\n 'Regex find & replace is a Pro feature. Please activate a Pro license in Plugin Settings.'\n );\n }\n\n const targetsMap = new Map<string, { uid: string; documentId: string }>();\n\n // 1. Add entire collections if selected\n for (const colUid of selectedCollections) {\n if (!strapi.contentTypes[colUid]) continue;\n const entries = await strapi.documents(colUid as any).findMany();\n for (const entry of (entries || []) as any[]) {\n const key = `${colUid}@@@${entry.documentId}`;\n targetsMap.set(key, { uid: colUid, documentId: entry.documentId });\n }\n }\n\n // 2. Add individually selected entries\n for (const item of selectedEntries) {\n if (!item.uid || !item.documentId) continue;\n const key = `${item.uid}@@@${item.documentId}`;\n targetsMap.set(key, { uid: item.uid, documentId: item.documentId });\n }\n\n if (targetsMap.size === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = existingBatchId || `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const results: any[] = [];\n let updatedCount = 0;\n let skippedCount = 0;\n\n let regexObj: RegExp | null = null;\n if (isRegex && regexPattern) {\n regexObj = new RegExp(regexPattern, 'g');\n }\n\n const targets = Array.from(targetsMap.values());\n const CHUNK_SIZE = 25; // 25 concurrent updates per batch window\n\n // Request-scoped DataLoader for deduplicating lookups during batch itemization\n const documentLoader = new Map<string, Promise<any>>();\n const getDocumentDeduplicated = (uid: string, documentId: string) => {\n const loaderKey = `${uid}@@@${documentId}`;\n if (!documentLoader.has(loaderKey)) {\n documentLoader.set(\n loaderKey,\n strapi.documents(uid as any).findOne({ documentId })\n );\n }\n return documentLoader.get(loaderKey);\n };\n\n for (let i = 0; i < targets.length; i += CHUNK_SIZE) {\n const chunk = targets.slice(i, i + CHUNK_SIZE);\n const chunkPromises = chunk.map(async (target) => {\n const contentType = strapi.contentTypes[target.uid];\n const displayName = contentType?.info?.displayName || target.uid;\n\n try {\n const currentEntry: any = await getDocumentDeduplicated(target.uid, target.documentId);\n\n const entryLabel =\n currentEntry?.name ||\n currentEntry?.title ||\n currentEntry?.email ||\n `Entry #${target.documentId}`;\n\n const attrDef = contentType?.attributes?.[attribute];\n const hasAttribute = Boolean(attrDef);\n\n if (hasAttribute) {\n if (attrDef.type === 'relation') {\n const isMulti = attrDef.relation === 'oneToMany' || attrDef.relation === 'manyToMany';\n\n // Retrieve current relation state with populate for accurate audit logging\n const currentEntryWithPopulate: any = await strapi.documents(target.uid as any).findOne({\n documentId: target.documentId,\n populate: [attribute],\n });\n const currentRel = currentEntryWithPopulate?.[attribute];\n\n let oldDocIds: string[] = [];\n let oldDisplay = '';\n if (isMulti && Array.isArray(currentRel)) {\n oldDocIds = currentRel.map((r: any) => r.documentId).filter(Boolean);\n oldDisplay = currentRel.map((r: any) => r.name || r.title || r.email || r.documentId).join(', ') || '(empty)';\n } else if (currentRel) {\n oldDocIds = currentRel.documentId ? [currentRel.documentId] : [];\n oldDisplay = currentRel.name || currentRel.title || currentRel.email || currentRel.documentId || '(empty)';\n } else {\n oldDisplay = '(unlinked / none)';\n }\n\n // Parse relation command:\n let mode: 'set' | 'connect' | 'disconnect' | 'clear' = 'set';\n let targetDocIds: string[] = [];\n let newDisplay = '';\n\n if (value === '__clear__' || value === '') {\n mode = 'clear';\n newDisplay = '(cleared / unlinked)';\n } else {\n try {\n const parsed = JSON.parse(value);\n if (parsed && typeof parsed === 'object' && parsed.mode) {\n mode = parsed.mode;\n targetDocIds = Array.isArray(parsed.documentIds) ? parsed.documentIds : (parsed.documentId ? [parsed.documentId] : []);\n newDisplay = parsed.label || targetDocIds.join(', ') || `[${mode}]`;\n } else if (Array.isArray(parsed)) {\n targetDocIds = parsed;\n newDisplay = targetDocIds.join(', ');\n } else {\n targetDocIds = [value];\n newDisplay = value;\n }\n } catch {\n targetDocIds = [value];\n newDisplay = value;\n }\n }\n\n // Construct Document Service relation mutation payload\n let relationPayload: any;\n if (mode === 'clear') {\n relationPayload = isMulti ? { set: [] } : null;\n } else if (mode === 'set') {\n relationPayload = isMulti ? { set: targetDocIds } : (targetDocIds[0] || null);\n } else if (mode === 'connect') {\n relationPayload = { connect: targetDocIds };\n } else if (mode === 'disconnect') {\n relationPayload = { disconnect: targetDocIds };\n }\n\n await strapi.documents(target.uid as any).update({\n documentId: target.documentId,\n data: {\n [attribute]: relationPayload,\n },\n });\n\n // If publishOnApproval is true and content type has draftAndPublish enabled, publish the document\n const isDraftAndPublishRel = Boolean(contentType?.options?.draftAndPublish);\n if (publishOnApproval && isDraftAndPublishRel) {\n try {\n await (strapi.documents(target.uid as any) as any).publish({\n documentId: target.documentId,\n });\n } catch (publishErr) {\n strapi.log.warn(`[bulk-updater] Failed to auto-publish ${target.uid}/${target.documentId}:`, publishErr);\n }\n }\n\n invalidateCollectionCache(target.uid);\n if (attrDef.target) {\n invalidateCollectionCache(attrDef.target);\n }\n\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: entryLabel,\n attribute,\n oldValue: { isRelation: true, documentIds: oldDocIds, display: oldDisplay },\n newValue: { isRelation: true, mode, documentIds: targetDocIds, display: newDisplay },\n status: 'updated' as const,\n message: `Successfully updated relation \"${attribute}\"`,\n };\n }\n\n let newValue = value;\n const currentVal = currentEntry?.[attribute];\n\n if (regexObj && typeof currentVal === 'string') {\n newValue = currentVal.replace(regexObj, value);\n }\n\n await strapi.documents(target.uid as any).update({\n documentId: target.documentId,\n data: {\n [attribute]: newValue,\n },\n });\n\n // If publishOnApproval is true and content type has draftAndPublish enabled, publish the document\n const isDraftAndPublishScalar = Boolean(contentType?.options?.draftAndPublish);\n if (publishOnApproval && isDraftAndPublishScalar) {\n try {\n await (strapi.documents(target.uid as any) as any).publish({\n documentId: target.documentId,\n });\n } catch (publishErr) {\n strapi.log.warn(`[bulk-updater] Failed to auto-publish ${target.uid}/${target.documentId}:`, publishErr);\n }\n }\n\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: entryLabel,\n attribute,\n oldValue: currentVal ?? null,\n newValue,\n status: 'updated' as const,\n message: `Successfully updated ${attribute}`,\n };\n } else {\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: entryLabel,\n attribute,\n status: 'skipped' as const,\n message: `Skipped: Attribute \"${attribute}\" does not exist in schema for ${displayName}`,\n };\n }\n } catch (updateErr: any) {\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: `Entry #${target.documentId}`,\n attribute,\n status: 'error' as const,\n message: updateErr.message,\n };\n }\n });\n\n const chunkResults = await Promise.all(chunkPromises);\n for (const res of chunkResults) {\n if (res.status === 'updated') updatedCount++;\n else if (res.status === 'skipped') skippedCount++;\n results.push(res);\n }\n }\n\n // Invalidate local in-process cache for all affected collection UIDs\n const affectedUids = new Set(targets.map((t) => t.uid));\n affectedUids.forEach((uid) => {\n invalidateCollectionCache(uid);\n });\n\n const batchSummary = {\n batchId,\n attribute,\n value,\n updatedCount,\n skippedCount,\n totalTargets: targetsMap.size,\n publishOnApproval,\n };\n\n // Stateless audit recording via consumer's declared audit schema\n await strapi.plugin('bulk-updater').service('audit').recordBatch(\n batchSummary,\n results,\n currentUser\n );\n\n return {\n success: true,\n batchId,\n attribute,\n value,\n updatedCount,\n skippedCount,\n totalTargets: targetsMap.size,\n results,\n };\n}\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => {\n // Initialize periodic runner for off-peak queue execution\n if (!runnerInterval) {\n runnerInterval = setInterval(async () => {\n if (isRunnerRunning) return;\n isRunnerRunning = true;\n try {\n const config = getExecutionWindowConfig(strapi);\n const { isBusinessHours } = checkBusinessHours(config);\n\n for (const [id, batch] of pendingBatches.entries()) {\n if (batch.status === 'queued_offpeak' && !isBusinessHours) {\n strapi.log.info(`[bulk-updater] Executing off-peak queued batch: ${id}`);\n const result = await executeBatchInternal(strapi, batch.payload, batch.createdBy, id);\n batch.status = 'executed';\n batch.executionResult = result;\n }\n }\n } catch (err) {\n strapi.log.error('[bulk-updater] Scheduled runner error:', err);\n } finally {\n isRunnerRunning = false;\n }\n }, 30000);\n }\n\n return {\n /**\n * Lightweight Schema Discovery:\n * Returns schema definitions, valid attributes, and total counts (<15KB payload)\n * Previews up to 10 entries per collection to ensure instantaneous initial paint.\n * Utilizes in-memory schema memoization and database high-watermark signature caching.\n */\n async getModels() {\n const allContentTypes = strapi.contentTypes || {};\n const auditConfig = strapi.plugin('bulk-updater').service('audit').getAuditConfig();\n const auditContentType = auditConfig?.contentType;\n\n // 1. Static schema discovery memoized in-memory\n if (!staticSchemaCache) {\n const allAttributesSet = new Set<string>();\n const systemFields = [\n 'createdAt',\n 'updatedAt',\n 'publishedAt',\n 'createdBy',\n 'updatedBy',\n 'locale',\n 'localizations',\n ];\n\n const configuredCollections = strapi.config.get(\n 'plugin::bulk-updater.collections'\n ) as string[] | string | undefined;\n\n const isWildcard =\n configuredCollections === '*' ||\n configuredCollections === 'all' ||\n (Array.isArray(configuredCollections) &&\n (configuredCollections.includes('*') || configuredCollections.includes('all')));\n\n let targetUids: string[];\n if (\n !isWildcard &&\n configuredCollections &&\n Array.isArray(configuredCollections) &&\n configuredCollections.length > 0\n ) {\n targetUids = configuredCollections.filter((uid) => uid in allContentTypes);\n } else {\n targetUids = Object.keys(allContentTypes).filter(\n (uid) =>\n uid.startsWith('api::') &&\n !uid.includes('bulk-updater') &&\n uid !== auditContentType\n );\n }\n\n const metaByUid: Record<string, any> = {};\n const allRelationsMeta: Record<string, RelationMeta> = {};\n\n for (const uid of targetUids) {\n const contentType = allContentTypes[uid];\n if (!contentType || (contentType.kind !== 'collectionType' && contentType.kind !== 'singleType')) continue;\n\n const isSingleType = contentType.kind === 'singleType';\n const rawAttrs = contentType.attributes || {};\n const relationsByAttr: Record<string, RelationMeta> = {};\n\n const validAttrs = Object.keys(rawAttrs).filter((attrKey) => {\n if (systemFields.includes(attrKey)) return false;\n if (rawAttrs[attrKey].type === 'component' || rawAttrs[attrKey].type === 'dynamiczone') return false;\n\n if (rawAttrs[attrKey].type === 'relation') {\n const rel = rawAttrs[attrKey];\n const targetUid = rel.target;\n if (!targetUid || !allContentTypes[targetUid]) return false;\n const targetInfo = allContentTypes[targetUid]?.info;\n const isMulti = rel.relation === 'oneToMany' || rel.relation === 'manyToMany';\n const relMeta: RelationMeta = {\n isRelation: true,\n relationType: rel.relation,\n targetUid,\n targetDisplayName: targetInfo?.displayName || targetUid,\n isMulti,\n };\n relationsByAttr[attrKey] = relMeta;\n allRelationsMeta[attrKey] = relMeta;\n return true;\n }\n\n return true;\n });\n\n validAttrs.forEach((attr) => allAttributesSet.add(attr));\n\n metaByUid[uid] = {\n uid,\n kind: contentType.kind,\n isSingleType,\n singularName: contentType.info?.singularName || uid,\n pluralName: contentType.info?.pluralName || uid,\n displayName: contentType.info?.displayName || uid,\n attributes: validAttrs,\n relations: relationsByAttr,\n };\n }\n\n staticSchemaCache = {\n attributes: Array.from(allAttributesSet),\n targetUids: Object.keys(metaByUid),\n metaByUid,\n relationsMeta: allRelationsMeta,\n };\n }\n\n const collections = [];\n const signatures: string[] = [];\n\n // 2. High-watermark check per collection\n for (const uid of staticSchemaCache.targetUids) {\n const meta = staticSchemaCache.metaByUid[uid];\n if (!meta) continue;\n\n const signature = await getCollectionSignature(strapi, uid);\n signatures.push(`${uid}=${signature}`);\n\n const cached = modelsCache.get(uid);\n if (cached && cached.signature === signature) {\n collections.push({\n ...meta,\n totalEntries: cached.totalCount,\n entries: cached.entries,\n });\n } else {\n let entries: any[] = [];\n let totalCount = 0;\n try {\n const relKeys = Object.keys(meta.relations || {});\n const populateOption = relKeys.length > 0\n ? relKeys.reduce((acc, k) => ({ ...acc, [k]: true }), {})\n : undefined;\n\n if (meta.isSingleType) {\n const singleDoc: any = await (strapi.documents(uid as any) as any).findFirst({\n populate: populateOption,\n });\n if (singleDoc) {\n entries = [singleDoc];\n totalCount = 1;\n }\n } else {\n totalCount = await strapi.documents(uid as any).count();\n entries = await strapi.documents(uid as any).findMany({\n limit: 10,\n sort: { createdAt: 'desc' },\n populate: populateOption,\n });\n }\n } catch (err) {\n strapi.log.error(`[bulk-updater] Failed to fetch entries for ${uid}:`, err);\n entries = [];\n totalCount = 0;\n }\n\n const formattedEntries = (entries || []).map((entry: any) => ({\n id: entry.id,\n documentId: entry.documentId,\n ...entry,\n }));\n\n modelsCache.set(uid, {\n signature,\n totalCount,\n entries: formattedEntries,\n });\n\n collections.push({\n ...meta,\n totalEntries: totalCount,\n entries: formattedEntries,\n });\n }\n }\n\n // Get license status\n const licenseService = strapi.plugin('bulk-updater').service('license');\n const license = await licenseService.getLicenseStatus();\n\n const executionConfig = getExecutionWindowConfig(strapi);\n const windowStatus = checkBusinessHours(executionConfig);\n const pendingCount = Array.from(pendingBatches.values()).filter(\n (b) => b.status === 'pending_approval' || b.status === 'queued_offpeak'\n ).length;\n\n const etag = `W/\"models-${crypto.createHash('md5').update(`${signatures.join('|')}:${license.tier}:${pendingCount}:${windowStatus.isBusinessHours}:${windowStatus.currentServerTime}:${windowStatus.timeZone}`).digest('hex')}\"`;\n\n return {\n success: true,\n attributes: staticSchemaCache.attributes,\n collections,\n relationsMeta: staticSchemaCache.relationsMeta,\n license,\n executionWindow: {\n ...executionConfig,\n ...windowStatus,\n },\n pendingBatchesCount: pendingCount,\n auditEnabled: Boolean(auditConfig?.enabled),\n etag,\n };\n },\n\n /**\n * On-Demand Paginated Collection Entries:\n * Supports server-side pagination (limit / start) and database query pushdown for filters.\n * Cached in-process with cross-pod high-watermark signature validation.\n */\n async getCollectionEntries(\n uid: string,\n {\n page = 1,\n pageSize = 10,\n attribute,\n filterOperator,\n filterQuery,\n search,\n sortField,\n sortOrder = 'desc',\n }: {\n page?: number;\n pageSize?: number;\n attribute?: string;\n filterOperator?: string;\n filterQuery?: string;\n search?: string;\n sortField?: string;\n sortOrder?: 'asc' | 'desc';\n }\n ) {\n const contentType = strapi.contentTypes[uid];\n if (!contentType) {\n throw new Error(`Collection with UID \"${uid}\" does not exist.`);\n }\n\n const limit = Math.min(Math.max(Number(pageSize) || 10, 1), 100);\n const currentPage = Math.max(Number(page) || 1, 1);\n const start = (currentPage - 1) * limit;\n\n const isSingleType = contentType.kind === 'singleType';\n\n // 1. Signature-based caching verification\n const signature = await getCollectionSignature(strapi, uid);\n const cacheKey = `${uid}:${limit}:${start}:${attribute || ''}:${filterOperator || ''}:${filterQuery || ''}:${search || ''}:${sortField || ''}:${sortOrder}`;\n const cached = queryCache.get(cacheKey);\n\n const etag = `W/\"entry-${crypto.createHash('md5').update(`${cacheKey}:${signature}`).digest('hex')}\"`;\n\n if (cached && cached.signature === signature && Date.now() - cached.timestamp < CACHE_TTL_MS) {\n return {\n ...cached.data,\n cached: true,\n etag,\n };\n }\n\n let result: any;\n\n if (isSingleType) {\n const singleDoc: any = await (strapi.documents(uid as any) as any).findFirst();\n result = {\n uid,\n displayName: contentType.info?.displayName || uid,\n isSingleType: true,\n entries: singleDoc ? [singleDoc] : [],\n pagination: {\n page: 1,\n pageSize: 1,\n total: singleDoc ? 1 : 0,\n pageCount: singleDoc ? 1 : 0,\n },\n };\n } else {\n // Build database filters pushdown\n const filters: Record<string, any> = {};\n\n if (attribute && filterOperator && filterQuery !== undefined && filterQuery !== '') {\n switch (filterOperator) {\n case 'equals':\n filters[attribute] = { $eq: filterQuery };\n break;\n case 'notEquals':\n filters[attribute] = { $ne: filterQuery };\n break;\n case 'contains':\n filters[attribute] = { $containsi: filterQuery };\n break;\n case 'notContains':\n filters[attribute] = { $notContainsi: filterQuery };\n break;\n case 'startsWith':\n filters[attribute] = { $startsWith: filterQuery };\n break;\n case 'endsWith':\n filters[attribute] = { $endsWith: filterQuery };\n break;\n case 'empty':\n filters[attribute] = { $null: true };\n break;\n case 'notEmpty':\n filters[attribute] = { $notNull: true };\n break;\n }\n }\n\n if (search && search.trim() !== '') {\n const stringAttrs = Object.keys(contentType.attributes || {}).filter(\n (k) =>\n contentType.attributes[k].type === 'string' ||\n contentType.attributes[k].type === 'text' ||\n contentType.attributes[k].type === 'email'\n );\n\n if (stringAttrs.length > 0) {\n filters.$or = stringAttrs.slice(0, 5).map((attr) => ({\n [attr]: { $containsi: search.trim() },\n }));\n }\n }\n\n const sortClause: Record<string, 'asc' | 'desc'> = {};\n if (sortField && contentType.attributes?.[sortField]) {\n sortClause[sortField] = sortOrder;\n } else {\n sortClause['createdAt'] = 'desc';\n }\n\n const relationAttrs = Object.keys(contentType.attributes || {}).filter(\n (k) => contentType.attributes[k].type === 'relation'\n );\n const populateOption = relationAttrs.length > 0\n ? relationAttrs.reduce((acc, k) => ({ ...acc, [k]: true }), {})\n : undefined;\n\n const [entries, totalEntries] = await Promise.all([\n strapi.documents(uid as any).findMany({\n start,\n limit,\n filters: Object.keys(filters).length > 0 ? filters : undefined,\n sort: sortClause,\n populate: populateOption,\n }),\n strapi.documents(uid as any).count({\n filters: Object.keys(filters).length > 0 ? filters : undefined,\n }),\n ]);\n\n const totalPages = Math.ceil(totalEntries / limit);\n\n result = {\n uid,\n displayName: contentType.info?.displayName || uid,\n isSingleType: false,\n entries: (entries || []).map((entry: any) => ({\n id: entry.id,\n documentId: entry.documentId,\n ...entry,\n })),\n pagination: {\n page: currentPage,\n pageSize: limit,\n total: totalEntries,\n pageCount: totalPages,\n },\n };\n }\n\n // Store in queryCache with bounding limit\n if (queryCache.size >= MAX_QUERY_CACHE_SIZE) {\n const toDelete = Array.from(queryCache.keys()).slice(0, 100);\n toDelete.forEach((k) => queryCache.delete(k));\n }\n queryCache.set(cacheKey, { signature, data: result, timestamp: Date.now() });\n\n return {\n ...result,\n cached: false,\n etag,\n };\n },\n\n /**\n * Get available target entities for a relation field:\n * Cached in-process with cross-pod signature validation.\n */\n async getRelationOptions(targetUid: string) {\n if (!targetUid || !strapi.contentTypes[targetUid]) {\n throw new Error(`Target content type \"${targetUid}\" does not exist.`);\n }\n\n const cacheKey = `rel_options:${targetUid}`;\n const signature = await getCollectionSignature(strapi, targetUid);\n const cached = queryCache.get(cacheKey);\n if (cached && cached.signature === signature) {\n return cached.data;\n }\n\n const docs = await strapi.documents(targetUid as any).findMany({\n limit: 100,\n sort: { createdAt: 'desc' },\n });\n\n const options = (docs || []).map((doc: any) => {\n const label = doc.name || doc.title || doc.email || doc.username || `Entry #${doc.id || doc.documentId}`;\n return {\n documentId: doc.documentId,\n label,\n };\n });\n\n const result = { targetUid, options };\n queryCache.set(cacheKey, { signature, data: result, timestamp: Date.now() });\n return result;\n },\n\n /**\n * Batch Update API with Maker-Checker and Off-Peak Scheduling:\n */\n async batchUpdate(\n {\n attribute,\n value,\n selectedEntries = [],\n selectedCollections = [],\n isRegex = false,\n regexPattern = '',\n executionMode = 'immediate',\n makerNotes = '',\n forceImmediate = false,\n publishOnApproval = true,\n }: {\n attribute: string;\n value: string;\n selectedEntries?: Array<{ uid: string; documentId: string }>;\n selectedCollections?: string[];\n isRegex?: boolean;\n regexPattern?: string;\n executionMode?: 'immediate' | 'queue_offpeak' | 'maker_checker';\n makerNotes?: string;\n forceImmediate?: boolean;\n publishOnApproval?: boolean;\n },\n currentUser?: any\n ) {\n if (!attribute) {\n throw new Error('Attribute name is required.');\n }\n\n const licenseService = strapi.plugin('bulk-updater').service('license');\n const license = await licenseService.getLicenseStatus();\n const isEnterprise = license.tier === 'enterprise';\n\n const executionConfig = getExecutionWindowConfig(strapi);\n const { isBusinessHours, policyMessage } = checkBusinessHours(executionConfig);\n\n // 1. Enterprise Maker-Checker Flow:\n if (executionMode === 'maker_checker') {\n const summary = await computeTargetsSummary(strapi, { selectedCollections, selectedEntries });\n if (summary.totalTargets === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const batch: PendingBatch = {\n id: batchId,\n createdAt: new Date().toISOString(),\n createdBy: currentUser\n ? {\n id: currentUser.id,\n email: currentUser.email,\n firstname: currentUser.firstname,\n lastname: currentUser.lastname,\n username: currentUser.username,\n }\n : { email: 'admin@system.local' },\n payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n summary: {\n attribute,\n value,\n totalTargets: summary.totalTargets,\n collectionBreakdown: summary.collectionBreakdown,\n publishOnApproval,\n },\n status: 'pending_approval',\n executionMode: 'maker_checker',\n makerNotes,\n };\n\n pendingBatches.set(batchId, batch);\n\n return {\n success: true,\n pending: true,\n batchId,\n status: 'pending_approval',\n message: 'Batch submitted for Checker authorization (4-Eyes Maker-Checker Workflow).',\n batch,\n };\n }\n\n // 2. Off-Peak Queued Batch:\n if (executionMode === 'queue_offpeak') {\n const summary = await computeTargetsSummary(strapi, { selectedCollections, selectedEntries });\n if (summary.totalTargets === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const batch: PendingBatch = {\n id: batchId,\n createdAt: new Date().toISOString(),\n createdBy: currentUser\n ? {\n id: currentUser.id,\n email: currentUser.email,\n firstname: currentUser.firstname,\n lastname: currentUser.lastname,\n username: currentUser.username,\n }\n : { email: 'admin@system.local' },\n payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n summary: {\n attribute,\n value,\n totalTargets: summary.totalTargets,\n collectionBreakdown: summary.collectionBreakdown,\n publishOnApproval,\n },\n status: 'queued_offpeak',\n executionMode: 'queue_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n makerNotes,\n };\n\n pendingBatches.set(batchId, batch);\n\n return {\n success: true,\n pending: true,\n batchId,\n status: 'queued_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n message: `Batch queued for server off-peak maintenance window (${executionConfig.offPeakStartTime}).`,\n batch,\n };\n }\n\n // 3. Immediate execution request with Server Business Hours enforcement:\n if (executionConfig.enabled && executionConfig.autoQueueDuringBusinessHours && isBusinessHours && !forceImmediate) {\n const summary = await computeTargetsSummary(strapi, { selectedCollections, selectedEntries });\n if (summary.totalTargets === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const batch: PendingBatch = {\n id: batchId,\n createdAt: new Date().toISOString(),\n createdBy: currentUser\n ? {\n id: currentUser.id,\n email: currentUser.email,\n firstname: currentUser.firstname,\n lastname: currentUser.lastname,\n username: currentUser.username,\n }\n : { email: 'admin@system.local' },\n payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n summary: {\n attribute,\n value,\n totalTargets: summary.totalTargets,\n collectionBreakdown: summary.collectionBreakdown,\n publishOnApproval,\n },\n status: 'queued_offpeak',\n executionMode: 'queue_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n makerNotes: 'Automatically queued due to peak business hours restriction.',\n };\n\n pendingBatches.set(batchId, batch);\n\n return {\n success: true,\n pending: true,\n batchId,\n status: 'queued_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n message: policyMessage,\n batch,\n };\n }\n\n // 4. Immediate execution\n return await executeBatchInternal(\n strapi,\n { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n currentUser\n );\n },\n\n /**\n * Get Pending & Queued Batches\n */\n getPendingBatches() {\n const executionConfig = getExecutionWindowConfig(strapi);\n const windowStatus = checkBusinessHours(executionConfig);\n const batches = Array.from(pendingBatches.values()).sort(\n (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()\n );\n\n return {\n success: true,\n batches,\n executionWindow: {\n ...executionConfig,\n ...windowStatus,\n },\n };\n },\n\n /**\n * Checker Approves Batch\n */\n async approveBatch(batchId: string, checkerUser: any, options: { executeImmediately?: boolean } = { executeImmediately: true }) {\n const batch = pendingBatches.get(batchId);\n if (!batch) {\n throw new Error(`Pending batch ${batchId} not found.`);\n }\n if (batch.status !== 'pending_approval') {\n throw new Error(`Batch ${batchId} is not pending approval (current status: ${batch.status}).`);\n }\n\n batch.approval = {\n checkerUser: checkerUser\n ? {\n id: checkerUser.id,\n email: checkerUser.email,\n firstname: checkerUser.firstname,\n lastname: checkerUser.lastname,\n username: checkerUser.username,\n }\n : { email: 'checker@system.local' },\n decisionDate: new Date().toISOString(),\n };\n\n const config = getExecutionWindowConfig(strapi);\n const { isBusinessHours } = checkBusinessHours(config);\n\n if (options.executeImmediately || !isBusinessHours) {\n batch.status = 'approved';\n const result = await executeBatchInternal(strapi, batch.payload, checkerUser, batch.id);\n batch.status = 'executed';\n batch.executionResult = result;\n return {\n success: true,\n batchId,\n status: 'executed',\n message: 'Batch approved and executed successfully by Checker.',\n batch,\n result,\n };\n } else {\n batch.status = 'approved';\n return {\n success: true,\n batchId,\n status: 'approved',\n message: `Batch approved by Checker. Queued for execution during off-peak window (${config.offPeakStartTime}).`,\n batch,\n };\n }\n },\n\n /**\n * Checker Rejects Batch\n */\n rejectBatch(batchId: string, checkerUser: any, reason: string = 'Rejected by Checker') {\n const batch = pendingBatches.get(batchId);\n if (!batch) {\n throw new Error(`Pending batch ${batchId} not found.`);\n }\n batch.status = 'rejected';\n batch.approval = {\n checkerUser: checkerUser\n ? {\n id: checkerUser.id,\n email: checkerUser.email,\n firstname: checkerUser.firstname,\n lastname: checkerUser.lastname,\n username: checkerUser.username,\n }\n : { email: 'checker@system.local' },\n decisionDate: new Date().toISOString(),\n rejectionReason: reason,\n };\n\n return {\n success: true,\n batchId,\n status: 'rejected',\n message: `Batch rejected by Checker: ${reason}`,\n batch,\n };\n },\n\n /**\n * Run Batch Immediately\n */\n async executeNow(batchId: string, currentUser: any) {\n const batch = pendingBatches.get(batchId);\n if (!batch) {\n throw new Error(`Pending batch ${batchId} not found.`);\n }\n if (batch.status === 'executed') {\n throw new Error(`Batch ${batchId} has already been executed.`);\n }\n\n const result = await executeBatchInternal(strapi, batch.payload, currentUser, batch.id);\n batch.status = 'executed';\n batch.executionResult = result;\n\n return {\n success: true,\n batchId,\n status: 'executed',\n message: 'Batch executed immediately.',\n batch,\n result,\n };\n },\n\n invalidateCache(uid?: string) {\n if (uid) {\n invalidateCollectionCache(uid);\n } else {\n queryCache.clear();\n modelsCache.clear();\n staticSchemaCache = null;\n }\n },\n };\n};\n","import type { Core } from '@strapi/strapi';\n\nexport interface LicenseStatus {\n isPro: boolean;\n tier: 'free' | 'pro' | 'enterprise';\n licenseKey: string | null;\n licensedTo: string | null;\n activatedAt: string | null;\n features: string[];\n}\n\nconst DEFAULT_FREE_STATUS: LicenseStatus = {\n isPro: false,\n tier: 'free',\n licenseKey: null,\n licensedTo: null,\n activatedAt: null,\n features: [\n 'Basic attribute batch updates',\n 'Schema compatibility checks',\n 'Interactive target selection',\n ],\n};\n\nconst PRO_FEATURES = [\n 'Basic attribute batch updates',\n 'Schema compatibility checks',\n 'Interactive target selection',\n 'Regex find & replace within attributes',\n 'Cross-relation and dynamic zone updates',\n 'Audit log export (CSV / JSON)',\n 'Unlimited collection batch operations',\n 'Priority enterprise support',\n];\n\nconst ENTERPRISE_FEATURES = [\n ...PRO_FEATURES,\n 'Maker-Checker Approval Workflow (4-Eyes Principle)',\n 'Server-Enforced Off-Peak Maintenance Window',\n 'Scheduled batch queuing during business hours',\n];\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getStore() {\n return strapi.store({\n type: 'plugin',\n name: 'bulk-updater',\n key: 'license',\n });\n },\n\n async getLicenseStatus(): Promise<LicenseStatus> {\n // Check developer config override in config/plugins.ts\n const isConfigEnterprise = strapi.config.get('plugin::bulk-updater.makerChecker.enabled');\n\n const store = await this.getStore();\n const stored = await store.get({});\n if (!stored || !stored.licenseKey) {\n if (isConfigEnterprise) {\n return {\n isPro: true,\n tier: 'enterprise',\n licenseKey: 'CONFIG-OVERRIDE',\n licensedTo: 'Enterprise Developer Config',\n activatedAt: new Date().toISOString(),\n features: ENTERPRISE_FEATURES,\n };\n }\n return DEFAULT_FREE_STATUS;\n }\n if (isConfigEnterprise) {\n return {\n ...stored,\n isPro: true,\n tier: 'enterprise',\n features: Array.from(new Set([...(stored.features || []), ...ENTERPRISE_FEATURES])),\n };\n }\n return stored;\n },\n\n async verifyAndActivateLicense(licenseKey: string): Promise<{ success: boolean; license?: LicenseStatus; error?: string }> {\n if (!licenseKey || typeof licenseKey !== 'string') {\n return { success: false, error: 'A valid license key is required.' };\n }\n\n const trimmed = licenseKey.trim();\n\n // Verification Logic:\n // Supports live LemonSqueezy / Gumroad / Keygen keys or offline signature/prefix keys\n // For demonstration and production readiness, keys with prefix PRO- or ENTERPRISE- are valid Pro licenses\n const isEnterprise = trimmed.toUpperCase().startsWith('ENT-') || trimmed.toUpperCase().startsWith('ENTERPRISE-');\n const isPro = trimmed.toUpperCase().startsWith('PRO-') || isEnterprise;\n\n if (!isPro) {\n return {\n success: false,\n error: 'Invalid license key. License keys must start with PRO- or ENTERPRISE-.',\n };\n }\n\n const tier = isEnterprise ? 'enterprise' : 'pro';\n const status: LicenseStatus = {\n isPro: true,\n tier,\n licenseKey: trimmed,\n licensedTo: `Licensed Customer (${tier.toUpperCase()})`,\n activatedAt: new Date().toISOString(),\n features: isEnterprise ? ENTERPRISE_FEATURES : PRO_FEATURES,\n };\n\n const store = await this.getStore();\n await store.set({ value: status });\n\n return {\n success: true,\n license: status,\n };\n },\n\n async deactivateLicense(): Promise<LicenseStatus> {\n const store = await this.getStore();\n await store.set({ value: DEFAULT_FREE_STATUS });\n return DEFAULT_FREE_STATUS;\n },\n});\n","import type { Core } from '@strapi/strapi';\n\nexport interface AuditConfig {\n enabled: boolean;\n mode?: 'custom' | 'default' | 'enterprise_native' | 'disabled';\n contentType?: string;\n fields?: Record<string, string>;\n isEnterpriseNativeAvailable?: boolean;\n error?: string;\n}\n\nconst DEFAULT_FIELDS: Record<string, string> = {\n batchId: 'batchId',\n attribute: 'attribute',\n value: 'value',\n userEmail: 'userEmail',\n userName: 'userName',\n updatedCount: 'updatedCount',\n skippedCount: 'skippedCount',\n totalTargets: 'totalTargets',\n diffs: 'diffs',\n isRolledBack: 'isRolledBack',\n rolledBackAt: 'rolledBackAt',\n rolledBackBy: 'rolledBackBy',\n executedAt: 'executedAt',\n};\n\nconst DEFAULT_CANDIDATE_TYPES = [\n 'api::audit-log.audit-log',\n 'api::audit.audit',\n 'api::bulk-audit-log.bulk-audit-log',\n];\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n /**\n * Detects whether Strapi Enterprise Native Audit Log service is available.\n */\n getEnterpriseAuditService() {\n try {\n const svc =\n (strapi as any).service?.('admin::audit-logs') ||\n (strapi as any).admin?.services?.['audit-logs'];\n return svc || null;\n } catch {\n return null;\n }\n },\n\n /**\n * Resolves the audit configuration:\n * 1. Explicit consumer custom schema (full declarative control)\n * 2. Zero-config convention auto-detection (api::audit-log.audit-log)\n * 3. Strapi Enterprise native audit log bridge\n */\n getAuditConfig(): AuditConfig {\n const rawConfig = strapi.config.get('plugin::bulk-updater.audit') as any;\n const eeService = this.getEnterpriseAuditService();\n const isEnterpriseNativeAvailable = Boolean(\n eeService && typeof eeService.save === 'function'\n );\n\n // If consumer explicitly disabled audit\n if (rawConfig && rawConfig.enabled === false) {\n return {\n enabled: false,\n mode: 'disabled',\n isEnterpriseNativeAvailable,\n };\n }\n\n // Tier 1: Explicit consumer custom schema\n if (rawConfig?.contentType) {\n const { contentType, fields } = rawConfig;\n const schema = strapi.contentTypes[contentType];\n if (!schema) {\n return {\n enabled: false,\n mode: 'disabled',\n isEnterpriseNativeAvailable,\n error: `Declared audit contentType \"${contentType}\" does not exist in Strapi schema registry.`,\n };\n }\n\n // Check fields or fallback to DEFAULT_FIELDS\n const resolvedFields = { ...DEFAULT_FIELDS, ...(fields || {}) };\n return {\n enabled: true,\n mode: 'custom',\n contentType,\n fields: resolvedFields,\n isEnterpriseNativeAvailable,\n };\n }\n\n // Tier 2: Zero-config auto-discovery of default audit collection\n for (const candidate of DEFAULT_CANDIDATE_TYPES) {\n if (strapi.contentTypes[candidate]) {\n return {\n enabled: true,\n mode: 'default',\n contentType: candidate,\n fields: DEFAULT_FIELDS,\n isEnterpriseNativeAvailable,\n };\n }\n }\n\n // Tier 3: Strapi Enterprise native audit logs\n if (isEnterpriseNativeAvailable) {\n return {\n enabled: true,\n mode: 'enterprise_native',\n isEnterpriseNativeAvailable: true,\n };\n }\n\n // Tier 4: Audit disabled / unconfigured\n return {\n enabled: false,\n mode: 'disabled',\n isEnterpriseNativeAvailable: false,\n error:\n 'No audit collection configured or detected. To enable rollback, provide a collection or use Strapi Enterprise.',\n };\n },\n\n /**\n * Optionally writes an audit event directly to Strapi Enterprise's native Audit Logs table.\n */\n async recordToEnterpriseNative(\n action: string,\n payload: Record<string, any>,\n user?: any\n ) {\n try {\n const eeService = this.getEnterpriseAuditService();\n if (eeService && typeof eeService.save === 'function') {\n await eeService.save({\n action,\n date: new Date(),\n userId: user?.id,\n payload,\n });\n strapi.log.info(\n `[bulk-updater] Successfully dispatched event to Strapi Enterprise Audit Log: ${action}`\n );\n }\n } catch (err: any) {\n strapi.log.warn(\n `[bulk-updater] Note: Could not dispatch to Strapi Enterprise native audit log: ${err.message}`\n );\n }\n },\n\n /**\n * Records a batch execution:\n * - Dispatches to Strapi Enterprise native audit log if available\n * - Records to consumer or auto-detected collection type for one-click rollback\n */\n async recordBatch(\n batchSummary: {\n batchId: string;\n attribute: string;\n value: string;\n updatedCount: number;\n skippedCount: number;\n totalTargets: number;\n },\n results: any[],\n user?: any\n ) {\n const config = this.getAuditConfig();\n\n // 1. Always attempt Strapi Enterprise native logging if available\n await this.recordToEnterpriseNative(\n 'bulk-updater.batch-update',\n {\n batchId: batchSummary.batchId,\n attribute: batchSummary.attribute,\n value: batchSummary.value,\n updatedCount: batchSummary.updatedCount,\n skippedCount: batchSummary.skippedCount,\n totalTargets: batchSummary.totalTargets,\n },\n user\n );\n\n // 2. Persist to rollback-capable collection type if available\n if (!config.enabled || !config.contentType || !config.fields) {\n return null;\n }\n\n const f = config.fields;\n const mappedData: any = {};\n\n if (f.batchId) mappedData[f.batchId] = batchSummary.batchId;\n if (f.attribute) mappedData[f.attribute] = batchSummary.attribute;\n if (f.value) mappedData[f.value] = batchSummary.value;\n if (f.userEmail) mappedData[f.userEmail] = user?.email || 'admin@strapi.io';\n if (f.userName) {\n mappedData[f.userName] = user?.firstname\n ? `${user.firstname} ${user.lastname || ''}`.trim()\n : user?.email || 'Admin';\n }\n if (f.updatedCount) mappedData[f.updatedCount] = batchSummary.updatedCount;\n if (f.skippedCount) mappedData[f.skippedCount] = batchSummary.skippedCount;\n if (f.totalTargets) mappedData[f.totalTargets] = batchSummary.totalTargets;\n if (f.diffs) mappedData[f.diffs] = results;\n if (f.isRolledBack) mappedData[f.isRolledBack] = false;\n if (f.executedAt) mappedData[f.executedAt] = new Date().toISOString();\n\n try {\n const created = await strapi.documents(config.contentType as any).create({\n data: mappedData,\n });\n return created;\n } catch (err: any) {\n strapi.log.error(`[bulk-updater] Failed to record audit log: ${err.message}`);\n return null;\n }\n },\n\n /**\n * Fetches audit history for the UI\n */\n async getHistory() {\n const config = this.getAuditConfig();\n\n if (config.mode === 'enterprise_native' && !config.contentType) {\n return {\n enabled: true,\n mode: 'enterprise_native',\n message:\n 'Strapi Enterprise Native Audit Logs active. Batch events are recorded directly to Strapi Settings > Audit Logs. (To enable one-click document rollback, declare or add an audit-log collection).',\n history: [],\n };\n }\n\n if (!config.enabled || !config.contentType || !config.fields) {\n return {\n enabled: false,\n message:\n config.error ||\n 'Audit trail is disabled. To enable audit logs & rollback, declare an audit schema in config/plugins.ts or create api::audit-log.audit-log.',\n history: [],\n };\n }\n\n try {\n const records: any[] = await strapi.documents(config.contentType as any).findMany({\n sort: { createdAt: 'desc' } as any,\n });\n\n const f = config.fields;\n const normalized = (records || []).map((r: any) => ({\n id: r.id,\n documentId: r.documentId,\n batchId: r[f.batchId] || r.documentId,\n attribute: r[f.attribute],\n value: r[f.value],\n userEmail: r[f.userEmail] || 'admin@strapi.io',\n userName: r[f.userName] || 'Admin',\n updatedCount: r[f.updatedCount] || 0,\n skippedCount: r[f.skippedCount] || 0,\n totalTargets: r[f.totalTargets] || 0,\n diffs: typeof r[f.diffs] === 'string' ? JSON.parse(r[f.diffs]) : r[f.diffs] || [],\n isRolledBack: Boolean(r[f.isRolledBack]),\n rolledBackAt: r[f.rolledBackAt] || null,\n rolledBackBy: r[f.rolledBackBy] || null,\n executedAt: r[f.executedAt] || r.createdAt,\n }));\n\n return {\n enabled: true,\n mode: config.mode,\n contentType: config.contentType,\n isEnterpriseNativeAvailable: config.isEnterpriseNativeAvailable,\n history: normalized,\n };\n } catch (err: any) {\n return {\n enabled: false,\n message: `Failed to read audit history: ${err.message}`,\n history: [],\n };\n }\n },\n\n /**\n * Performs deterministic rollback of a batch\n */\n async rollbackBatch(batchId: string, currentUser?: any) {\n const config = this.getAuditConfig();\n if (!config.enabled || !config.contentType || !config.fields) {\n throw new Error('Audit trail is not configured or disabled.');\n }\n\n const f = config.fields;\n const records: any[] = await strapi.documents(config.contentType as any).findMany({\n filters: {\n [f.batchId]: { $eq: batchId },\n } as any,\n });\n\n const record = records?.[0];\n if (!record) {\n throw new Error(`Batch record with ID \"${batchId}\" was not found in audit trail.`);\n }\n\n if (record[f.isRolledBack]) {\n throw new Error(`Batch \"${batchId}\" has already been rolled back.`);\n }\n\n const diffs =\n typeof record[f.diffs] === 'string'\n ? JSON.parse(record[f.diffs])\n : record[f.diffs] || [];\n\n let revertedCount = 0;\n const rollbackDetails = [];\n\n for (const item of diffs) {\n if (item.status === 'updated' && item.uid && item.documentId && item.attribute) {\n try {\n const isRel = item.oldValue && typeof item.oldValue === 'object' && item.oldValue.isRelation;\n let rollbackPayload: any;\n\n if (isRel) {\n const oldDocIds = Array.isArray(item.oldValue.documentIds) ? item.oldValue.documentIds : [];\n rollbackPayload = { set: oldDocIds };\n } else {\n rollbackPayload = item.oldValue;\n }\n\n await strapi.documents(item.uid as any).update({\n documentId: item.documentId,\n data: {\n [item.attribute]: rollbackPayload,\n },\n });\n\n const ct = strapi.contentTypes[item.uid];\n if (ct?.options?.draftAndPublish) {\n try {\n await (strapi.documents(item.uid as any) as any).publish({\n documentId: item.documentId,\n });\n } catch {}\n }\n\n try {\n strapi.plugin('bulk-updater').service('bulkUpdater').invalidateCollectionCache(item.uid);\n } catch {}\n\n revertedCount++;\n rollbackDetails.push({\n uid: item.uid,\n documentId: item.documentId,\n label: item.label,\n attribute: item.attribute,\n revertedTo: item.oldValue,\n status: 'reverted',\n });\n } catch (err: any) {\n rollbackDetails.push({\n uid: item.uid,\n documentId: item.documentId,\n label: item.label,\n status: 'error',\n message: err.message,\n });\n }\n }\n }\n\n const updateData: any = {};\n if (f.isRolledBack) updateData[f.isRolledBack] = true;\n if (f.rolledBackAt) updateData[f.rolledBackAt] = new Date().toISOString();\n if (f.rolledBackBy) {\n updateData[f.rolledBackBy] = currentUser?.email || 'Admin';\n }\n\n await strapi.documents(config.contentType as any).update({\n documentId: record.documentId,\n data: updateData,\n });\n\n // Invalidate affected collection caches\n try {\n const affectedUids = new Set(rollbackDetails.map((d: any) => d.uid));\n const bulkService = strapi.plugin('bulk-updater').service('bulkUpdater');\n affectedUids.forEach((uid: any) => {\n if (uid && bulkService?.invalidateCache) {\n bulkService.invalidateCache(uid);\n }\n });\n } catch {}\n\n // Mirror rollback event to Strapi Enterprise native audit log if available\n await this.recordToEnterpriseNative(\n 'bulk-updater.rollback',\n {\n batchId,\n revertedCount,\n },\n currentUser\n );\n\n return {\n success: true,\n batchId,\n revertedCount,\n details: rollbackDetails,\n };\n },\n});\n","import routes from './routes';\nimport bulkUpdaterController from './controllers/bulk-updater';\nimport licenseController from './controllers/license';\nimport auditController from './controllers/audit';\nimport bulkUpdaterService from './services/bulk-updater';\nimport licenseService from './services/license';\nimport auditService from './services/audit';\n\nexport default {\n register({ strapi }: { strapi: any }) {},\n bootstrap({ strapi }: { strapi: any }) {},\n routes,\n controllers: {\n bulkUpdater: bulkUpdaterController,\n license: licenseController,\n audit: auditController,\n },\n services: {\n bulkUpdater: bulkUpdaterService,\n license: licenseService,\n audit: auditService,\n },\n};\n"],"names":["licenseService","crypto"],"mappings":"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"}
|
package/dist/server/index.mjs
CHANGED
|
@@ -716,8 +716,9 @@ const bulkUpdaterService = ({ strapi }) => {
|
|
|
716
716
|
const configuredCollections = strapi.config.get(
|
|
717
717
|
"plugin::bulk-updater.collections"
|
|
718
718
|
);
|
|
719
|
+
const isWildcard = configuredCollections === "*" || configuredCollections === "all" || Array.isArray(configuredCollections) && (configuredCollections.includes("*") || configuredCollections.includes("all"));
|
|
719
720
|
let targetUids;
|
|
720
|
-
if (configuredCollections && Array.isArray(configuredCollections) && configuredCollections.length > 0) {
|
|
721
|
+
if (!isWildcard && configuredCollections && Array.isArray(configuredCollections) && configuredCollections.length > 0) {
|
|
721
722
|
targetUids = configuredCollections.filter((uid) => uid in allContentTypes);
|
|
722
723
|
} else {
|
|
723
724
|
targetUids = Object.keys(allContentTypes).filter(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../server/src/routes/index.ts","../../server/src/controllers/bulk-updater.ts","../../server/src/controllers/license.ts","../../server/src/controllers/audit.ts","../../server/src/services/bulk-updater.ts","../../server/src/services/license.ts","../../server/src/services/audit.ts","../../server/src/index.ts"],"sourcesContent":["export default {\n admin: {\n type: 'admin',\n routes: [\n {\n method: 'GET',\n path: '/models',\n handler: 'bulkUpdater.getModels',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/collections/:uid/entries',\n handler: 'bulkUpdater.getCollectionEntries',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/relation-options',\n handler: 'bulkUpdater.getRelationOptions',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batch-update',\n handler: 'bulkUpdater.batchUpdate',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/pending-batches',\n handler: 'bulkUpdater.getPendingBatches',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batches/:batchId/approve',\n handler: 'bulkUpdater.approveBatch',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batches/:batchId/reject',\n handler: 'bulkUpdater.rejectBatch',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batches/:batchId/execute-now',\n handler: 'bulkUpdater.executeNow',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/license',\n handler: 'license.getStatus',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/license/verify',\n handler: 'license.verify',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/license/deactivate',\n handler: 'license.deactivate',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/audit/history',\n handler: 'audit.getHistory',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/audit/:batchId/rollback',\n handler: 'audit.rollback',\n config: {\n policies: [],\n },\n },\n ],\n },\n};\n","import type { Core } from '@strapi/strapi';\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getModels(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const data = await service.getModels();\n\n if (data?.etag) {\n ctx.set('ETag', data.etag);\n ctx.set('Cache-Control', 'private, no-cache');\n\n if (ctx.request.headers['if-none-match'] === data.etag) {\n ctx.status = 304;\n return;\n }\n }\n\n ctx.body = data;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async getCollectionEntries(ctx: any) {\n try {\n const { uid } = ctx.params;\n const { page, pageSize, attribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const data = await service.getCollectionEntries(uid, {\n page: page ? Number(page) : 1,\n pageSize: pageSize ? Number(pageSize) : 10,\n attribute: attribute ? String(attribute) : undefined,\n filterOperator: filterOperator ? String(filterOperator) : undefined,\n filterQuery: filterQuery !== undefined ? String(filterQuery) : undefined,\n search: search ? String(search) : undefined,\n sortField: sortField ? String(sortField) : undefined,\n sortOrder: sortOrder === 'asc' ? 'asc' : 'desc',\n });\n\n if (data?.etag) {\n ctx.set('ETag', data.etag);\n ctx.set('Cache-Control', 'private, no-cache');\n\n if (ctx.request.headers['if-none-match'] === data.etag) {\n ctx.status = 304;\n return;\n }\n }\n\n ctx.body = data;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async getRelationOptions(ctx: any) {\n try {\n const { targetUid } = ctx.query;\n if (!targetUid) {\n ctx.status = 400;\n ctx.body = { success: false, error: 'targetUid is required' };\n return;\n }\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const data = await service.getRelationOptions(String(targetUid));\n ctx.body = { success: true, ...data };\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async batchUpdate(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.batchUpdate(ctx.request.body || {}, currentUser);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async getPendingBatches(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const result = await service.getPendingBatches();\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async approveBatch(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const { executeImmediately = true } = ctx.request.body || {};\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.approveBatch(batchId, currentUser, { executeImmediately });\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async rejectBatch(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const { reason } = ctx.request.body || {};\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.rejectBatch(batchId, currentUser, reason);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async executeNow(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.executeNow(batchId, currentUser);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n});\n","import type { Core } from '@strapi/strapi';\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getStatus(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('license');\n const license = await service.getLicenseStatus();\n ctx.body = { success: true, license };\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async verify(ctx: any) {\n try {\n const { licenseKey } = ctx.request.body || {};\n const service = strapi.plugin('bulk-updater').service('license');\n const result = await service.verifyAndActivateLicense(licenseKey);\n if (!result.success) {\n ctx.status = 400;\n ctx.body = result;\n return;\n }\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async deactivate(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('license');\n const license = await service.deactivateLicense();\n ctx.body = { success: true, license };\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n});\n","import type { Core } from '@strapi/strapi';\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getHistory(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('audit');\n const data = await service.getHistory();\n ctx.body = data;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async rollback(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const service = strapi.plugin('bulk-updater').service('audit');\n const currentUser = ctx.state.user;\n const result = await service.rollbackBatch(batchId, currentUser);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n});\n","import type { Core } from '@strapi/strapi';\nimport crypto from 'crypto';\n\n// In-process memory caches (scoped to pod)\ninterface CacheEntry {\n signature: string;\n data: any;\n timestamp: number;\n}\n\nconst queryCache = new Map<string, CacheEntry>();\nconst modelsCache = new Map<string, { signature: string; totalCount: number; entries: any[] }>();\nconst MAX_QUERY_CACHE_SIZE = 500;\nconst CACHE_TTL_MS = 60 * 1000; // 60-second TTL fallback\n\nexport interface RelationMeta {\n isRelation: boolean;\n relationType: string;\n targetUid: string;\n targetDisplayName: string;\n isMulti: boolean;\n}\n\nlet staticSchemaCache: {\n attributes: string[];\n targetUids: string[];\n metaByUid: Record<\n string,\n {\n uid: string;\n kind: string;\n isSingleType: boolean;\n singularName: string;\n pluralName: string;\n displayName: string;\n attributes: string[];\n relations: Record<string, RelationMeta>;\n }\n >;\n relationsMeta: Record<string, RelationMeta>;\n} | null = null;\n\nexport interface ExecutionWindowConfig {\n enabled: boolean;\n timeZone: string;\n businessHours: {\n start: string;\n end: string;\n };\n offPeakStartTime: string;\n autoQueueDuringBusinessHours: boolean;\n}\n\nexport function getSafeTimezone(timeZone?: string): string {\n if (!timeZone || typeof timeZone !== 'string') {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';\n }\n try {\n Intl.DateTimeFormat(undefined, { timeZone: timeZone.trim() });\n return timeZone.trim();\n } catch {\n return 'UTC';\n }\n}\n\nexport function getTimeInTimezone(timeZone: string, date: Date = new Date()): { hours: number; minutes: number; timeString: string } {\n const safeTz = getSafeTimezone(timeZone);\n const formatter = new Intl.DateTimeFormat('en-GB', {\n timeZone: safeTz,\n hour: '2-digit',\n minute: '2-digit',\n hourCycle: 'h23',\n });\n const timeString = formatter.format(date);\n const [h, m] = timeString.split(':').map(Number);\n return { hours: h, minutes: m, timeString };\n}\n\nexport function getExecutionWindowConfig(strapi: Core.Strapi): ExecutionWindowConfig {\n const pluginConfig = strapi.config.get('plugin::bulk-updater.executionWindow') as Partial<ExecutionWindowConfig> | undefined;\n const configuredTz = pluginConfig?.timeZone ||\n strapi.config.get('server.timeZone') ||\n Intl.DateTimeFormat().resolvedOptions().timeZone ||\n 'UTC';\n const timeZone = getSafeTimezone(configuredTz);\n\n return {\n enabled: pluginConfig?.enabled ?? true,\n timeZone,\n businessHours: {\n start: pluginConfig?.businessHours?.start || '09:00',\n end: pluginConfig?.businessHours?.end || '18:00',\n },\n offPeakStartTime: pluginConfig?.offPeakStartTime || '18:00',\n autoQueueDuringBusinessHours: pluginConfig?.autoQueueDuringBusinessHours ?? true,\n };\n}\n\nexport function checkBusinessHours(config: ExecutionWindowConfig, testDate?: Date) {\n const timeZone = getSafeTimezone(config.timeZone);\n const { hours, minutes, timeString } = getTimeInTimezone(timeZone, testDate || new Date());\n\n const currentMinutes = hours * 60 + minutes;\n const [startH, startM] = (config.businessHours.start || '09:00').split(':').map(Number);\n const [endH, endM] = (config.businessHours.end || '18:00').split(':').map(Number);\n const startMinutes = startH * 60 + startM;\n const endMinutes = endH * 60 + endM;\n\n const isBusinessHours = currentMinutes >= startMinutes && currentMinutes < endMinutes;\n\n return {\n isBusinessHours,\n currentServerTime: timeString,\n timeZone,\n businessHours: config.businessHours,\n offPeakStartTime: config.offPeakStartTime,\n policyMessage: isBusinessHours\n ? `Server policy restricts bulk updates during peak business hours (${config.businessHours.start} - ${config.businessHours.end} ${timeZone}). Current time: ${timeString} (${timeZone}). Updates will be queued for off-peak execution at ${config.offPeakStartTime} ${timeZone}.`\n : `Currently in off-peak execution window (outside ${config.businessHours.start} - ${config.businessHours.end} ${timeZone}). Current time: ${timeString} (${timeZone}). Direct execution is permitted.`,\n };\n}\n\nexport interface PendingBatch {\n id: string;\n createdAt: string;\n createdBy: { id?: string | number; email?: string; firstname?: string; lastname?: string; username?: string };\n payload: {\n attribute: string;\n value: string;\n selectedEntries?: Array<{ uid: string; documentId: string }>;\n selectedCollections?: string[];\n isRegex?: boolean;\n regexPattern?: string;\n publishOnApproval?: boolean;\n };\n summary: {\n attribute: string;\n value: string;\n totalTargets: number;\n collectionBreakdown: Record<string, number>;\n publishOnApproval?: boolean;\n };\n status: 'pending_approval' | 'queued_offpeak' | 'approved' | 'rejected' | 'executed';\n executionMode: 'maker_checker' | 'queue_offpeak' | 'immediate';\n scheduledFor?: string;\n makerNotes?: string;\n approval?: {\n checkerUser?: { id?: string | number; email?: string; firstname?: string; lastname?: string; username?: string };\n decisionDate?: string;\n rejectionReason?: string;\n };\n executionResult?: any;\n}\n\nconst pendingBatches = new Map<string, PendingBatch>();\nlet isRunnerRunning = false;\nlet runnerInterval: NodeJS.Timeout | null = null;\n\nexport function invalidateCollectionCache(uid: string) {\n modelsCache.delete(uid);\n for (const key of queryCache.keys()) {\n if (key.startsWith(`${uid}:`)) {\n queryCache.delete(key);\n }\n }\n}\n\nasync function getCollectionSignature(strapi: Core.Strapi, uid: string): Promise<string> {\n const contentType = strapi.contentTypes[uid];\n if (!contentType) return 'invalid';\n\n const isSingleType = contentType.kind === 'singleType';\n try {\n if (isSingleType) {\n const doc: any = await (strapi.documents(uid as any) as any).findFirst({\n fields: ['updatedAt', 'id'],\n });\n return doc ? `single:${doc.id}:${doc.updatedAt}` : 'single:empty';\n }\n\n const [total, latest] = await Promise.all([\n strapi.documents(uid as any).count(),\n strapi.documents(uid as any).findMany({\n limit: 1,\n sort: { updatedAt: 'desc' },\n fields: ['updatedAt', 'id'],\n }),\n ]);\n\n const latestDoc = latest && latest.length > 0 ? latest[0] : null;\n return `${total}:${latestDoc?.updatedAt || '0'}:${latestDoc?.id || '0'}`;\n } catch {\n return `${Date.now()}`;\n }\n}\n\nasync function computeTargetsSummary(strapi: Core.Strapi, { selectedCollections = [], selectedEntries = [] }: any) {\n const targetsMap = new Map<string, { uid: string; documentId: string }>();\n const breakdown: Record<string, number> = {};\n\n for (const colUid of selectedCollections) {\n const contentType = strapi.contentTypes[colUid];\n const displayName = contentType?.info?.displayName || colUid;\n try {\n const entries = await strapi.documents(colUid as any).findMany();\n const count = entries ? entries.length : 0;\n breakdown[displayName] = (breakdown[displayName] || 0) + count;\n for (const entry of (entries || []) as any[]) {\n targetsMap.set(`${colUid}@@@${entry.documentId}`, { uid: colUid, documentId: entry.documentId });\n }\n } catch {\n // Ignore count failures\n }\n }\n\n for (const item of selectedEntries) {\n if (!item.uid || !item.documentId) continue;\n const key = `${item.uid}@@@${item.documentId}`;\n if (!targetsMap.has(key)) {\n targetsMap.set(key, { uid: item.uid, documentId: item.documentId });\n const contentType = strapi.contentTypes[item.uid];\n const displayName = contentType?.info?.displayName || item.uid;\n breakdown[displayName] = (breakdown[displayName] || 0) + 1;\n }\n }\n\n return {\n totalTargets: targetsMap.size,\n collectionBreakdown: breakdown,\n };\n}\n\nasync function executeBatchInternal(\n strapi: Core.Strapi,\n {\n attribute,\n value,\n selectedEntries = [],\n selectedCollections = [],\n isRegex = false,\n regexPattern = '',\n publishOnApproval = true,\n }: {\n attribute: string;\n value: string;\n selectedEntries?: Array<{ uid: string; documentId: string }>;\n selectedCollections?: string[];\n isRegex?: boolean;\n regexPattern?: string;\n publishOnApproval?: boolean;\n },\n currentUser?: any,\n existingBatchId?: string\n) {\n if (!attribute) {\n throw new Error('Attribute name is required.');\n }\n\n const licenseService = strapi.plugin('bulk-updater').service('license');\n const license = await licenseService.getLicenseStatus();\n\n // Check Pro feature gating\n if (isRegex && !license.isPro) {\n throw new Error(\n 'Regex find & replace is a Pro feature. Please activate a Pro license in Plugin Settings.'\n );\n }\n\n const targetsMap = new Map<string, { uid: string; documentId: string }>();\n\n // 1. Add entire collections if selected\n for (const colUid of selectedCollections) {\n if (!strapi.contentTypes[colUid]) continue;\n const entries = await strapi.documents(colUid as any).findMany();\n for (const entry of (entries || []) as any[]) {\n const key = `${colUid}@@@${entry.documentId}`;\n targetsMap.set(key, { uid: colUid, documentId: entry.documentId });\n }\n }\n\n // 2. Add individually selected entries\n for (const item of selectedEntries) {\n if (!item.uid || !item.documentId) continue;\n const key = `${item.uid}@@@${item.documentId}`;\n targetsMap.set(key, { uid: item.uid, documentId: item.documentId });\n }\n\n if (targetsMap.size === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = existingBatchId || `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const results: any[] = [];\n let updatedCount = 0;\n let skippedCount = 0;\n\n let regexObj: RegExp | null = null;\n if (isRegex && regexPattern) {\n regexObj = new RegExp(regexPattern, 'g');\n }\n\n const targets = Array.from(targetsMap.values());\n const CHUNK_SIZE = 25; // 25 concurrent updates per batch window\n\n // Request-scoped DataLoader for deduplicating lookups during batch itemization\n const documentLoader = new Map<string, Promise<any>>();\n const getDocumentDeduplicated = (uid: string, documentId: string) => {\n const loaderKey = `${uid}@@@${documentId}`;\n if (!documentLoader.has(loaderKey)) {\n documentLoader.set(\n loaderKey,\n strapi.documents(uid as any).findOne({ documentId })\n );\n }\n return documentLoader.get(loaderKey);\n };\n\n for (let i = 0; i < targets.length; i += CHUNK_SIZE) {\n const chunk = targets.slice(i, i + CHUNK_SIZE);\n const chunkPromises = chunk.map(async (target) => {\n const contentType = strapi.contentTypes[target.uid];\n const displayName = contentType?.info?.displayName || target.uid;\n\n try {\n const currentEntry: any = await getDocumentDeduplicated(target.uid, target.documentId);\n\n const entryLabel =\n currentEntry?.name ||\n currentEntry?.title ||\n currentEntry?.email ||\n `Entry #${target.documentId}`;\n\n const attrDef = contentType?.attributes?.[attribute];\n const hasAttribute = Boolean(attrDef);\n\n if (hasAttribute) {\n if (attrDef.type === 'relation') {\n const isMulti = attrDef.relation === 'oneToMany' || attrDef.relation === 'manyToMany';\n\n // Retrieve current relation state with populate for accurate audit logging\n const currentEntryWithPopulate: any = await strapi.documents(target.uid as any).findOne({\n documentId: target.documentId,\n populate: [attribute],\n });\n const currentRel = currentEntryWithPopulate?.[attribute];\n\n let oldDocIds: string[] = [];\n let oldDisplay = '';\n if (isMulti && Array.isArray(currentRel)) {\n oldDocIds = currentRel.map((r: any) => r.documentId).filter(Boolean);\n oldDisplay = currentRel.map((r: any) => r.name || r.title || r.email || r.documentId).join(', ') || '(empty)';\n } else if (currentRel) {\n oldDocIds = currentRel.documentId ? [currentRel.documentId] : [];\n oldDisplay = currentRel.name || currentRel.title || currentRel.email || currentRel.documentId || '(empty)';\n } else {\n oldDisplay = '(unlinked / none)';\n }\n\n // Parse relation command:\n let mode: 'set' | 'connect' | 'disconnect' | 'clear' = 'set';\n let targetDocIds: string[] = [];\n let newDisplay = '';\n\n if (value === '__clear__' || value === '') {\n mode = 'clear';\n newDisplay = '(cleared / unlinked)';\n } else {\n try {\n const parsed = JSON.parse(value);\n if (parsed && typeof parsed === 'object' && parsed.mode) {\n mode = parsed.mode;\n targetDocIds = Array.isArray(parsed.documentIds) ? parsed.documentIds : (parsed.documentId ? [parsed.documentId] : []);\n newDisplay = parsed.label || targetDocIds.join(', ') || `[${mode}]`;\n } else if (Array.isArray(parsed)) {\n targetDocIds = parsed;\n newDisplay = targetDocIds.join(', ');\n } else {\n targetDocIds = [value];\n newDisplay = value;\n }\n } catch {\n targetDocIds = [value];\n newDisplay = value;\n }\n }\n\n // Construct Document Service relation mutation payload\n let relationPayload: any;\n if (mode === 'clear') {\n relationPayload = isMulti ? { set: [] } : null;\n } else if (mode === 'set') {\n relationPayload = isMulti ? { set: targetDocIds } : (targetDocIds[0] || null);\n } else if (mode === 'connect') {\n relationPayload = { connect: targetDocIds };\n } else if (mode === 'disconnect') {\n relationPayload = { disconnect: targetDocIds };\n }\n\n await strapi.documents(target.uid as any).update({\n documentId: target.documentId,\n data: {\n [attribute]: relationPayload,\n },\n });\n\n // If publishOnApproval is true and content type has draftAndPublish enabled, publish the document\n const isDraftAndPublishRel = Boolean(contentType?.options?.draftAndPublish);\n if (publishOnApproval && isDraftAndPublishRel) {\n try {\n await (strapi.documents(target.uid as any) as any).publish({\n documentId: target.documentId,\n });\n } catch (publishErr) {\n strapi.log.warn(`[bulk-updater] Failed to auto-publish ${target.uid}/${target.documentId}:`, publishErr);\n }\n }\n\n invalidateCollectionCache(target.uid);\n if (attrDef.target) {\n invalidateCollectionCache(attrDef.target);\n }\n\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: entryLabel,\n attribute,\n oldValue: { isRelation: true, documentIds: oldDocIds, display: oldDisplay },\n newValue: { isRelation: true, mode, documentIds: targetDocIds, display: newDisplay },\n status: 'updated' as const,\n message: `Successfully updated relation \"${attribute}\"`,\n };\n }\n\n let newValue = value;\n const currentVal = currentEntry?.[attribute];\n\n if (regexObj && typeof currentVal === 'string') {\n newValue = currentVal.replace(regexObj, value);\n }\n\n await strapi.documents(target.uid as any).update({\n documentId: target.documentId,\n data: {\n [attribute]: newValue,\n },\n });\n\n // If publishOnApproval is true and content type has draftAndPublish enabled, publish the document\n const isDraftAndPublishScalar = Boolean(contentType?.options?.draftAndPublish);\n if (publishOnApproval && isDraftAndPublishScalar) {\n try {\n await (strapi.documents(target.uid as any) as any).publish({\n documentId: target.documentId,\n });\n } catch (publishErr) {\n strapi.log.warn(`[bulk-updater] Failed to auto-publish ${target.uid}/${target.documentId}:`, publishErr);\n }\n }\n\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: entryLabel,\n attribute,\n oldValue: currentVal ?? null,\n newValue,\n status: 'updated' as const,\n message: `Successfully updated ${attribute}`,\n };\n } else {\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: entryLabel,\n attribute,\n status: 'skipped' as const,\n message: `Skipped: Attribute \"${attribute}\" does not exist in schema for ${displayName}`,\n };\n }\n } catch (updateErr: any) {\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: `Entry #${target.documentId}`,\n attribute,\n status: 'error' as const,\n message: updateErr.message,\n };\n }\n });\n\n const chunkResults = await Promise.all(chunkPromises);\n for (const res of chunkResults) {\n if (res.status === 'updated') updatedCount++;\n else if (res.status === 'skipped') skippedCount++;\n results.push(res);\n }\n }\n\n // Invalidate local in-process cache for all affected collection UIDs\n const affectedUids = new Set(targets.map((t) => t.uid));\n affectedUids.forEach((uid) => {\n invalidateCollectionCache(uid);\n });\n\n const batchSummary = {\n batchId,\n attribute,\n value,\n updatedCount,\n skippedCount,\n totalTargets: targetsMap.size,\n publishOnApproval,\n };\n\n // Stateless audit recording via consumer's declared audit schema\n await strapi.plugin('bulk-updater').service('audit').recordBatch(\n batchSummary,\n results,\n currentUser\n );\n\n return {\n success: true,\n batchId,\n attribute,\n value,\n updatedCount,\n skippedCount,\n totalTargets: targetsMap.size,\n results,\n };\n}\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => {\n // Initialize periodic runner for off-peak queue execution\n if (!runnerInterval) {\n runnerInterval = setInterval(async () => {\n if (isRunnerRunning) return;\n isRunnerRunning = true;\n try {\n const config = getExecutionWindowConfig(strapi);\n const { isBusinessHours } = checkBusinessHours(config);\n\n for (const [id, batch] of pendingBatches.entries()) {\n if (batch.status === 'queued_offpeak' && !isBusinessHours) {\n strapi.log.info(`[bulk-updater] Executing off-peak queued batch: ${id}`);\n const result = await executeBatchInternal(strapi, batch.payload, batch.createdBy, id);\n batch.status = 'executed';\n batch.executionResult = result;\n }\n }\n } catch (err) {\n strapi.log.error('[bulk-updater] Scheduled runner error:', err);\n } finally {\n isRunnerRunning = false;\n }\n }, 30000);\n }\n\n return {\n /**\n * Lightweight Schema Discovery:\n * Returns schema definitions, valid attributes, and total counts (<15KB payload)\n * Previews up to 10 entries per collection to ensure instantaneous initial paint.\n * Utilizes in-memory schema memoization and database high-watermark signature caching.\n */\n async getModels() {\n const allContentTypes = strapi.contentTypes || {};\n const auditConfig = strapi.plugin('bulk-updater').service('audit').getAuditConfig();\n const auditContentType = auditConfig?.contentType;\n\n // 1. Static schema discovery memoized in-memory\n if (!staticSchemaCache) {\n const allAttributesSet = new Set<string>();\n const systemFields = [\n 'createdAt',\n 'updatedAt',\n 'publishedAt',\n 'createdBy',\n 'updatedBy',\n 'locale',\n 'localizations',\n ];\n\n const configuredCollections = strapi.config.get(\n 'plugin::bulk-updater.collections'\n ) as string[] | undefined;\n\n let targetUids: string[];\n if (configuredCollections && Array.isArray(configuredCollections) && configuredCollections.length > 0) {\n targetUids = configuredCollections.filter((uid) => uid in allContentTypes);\n } else {\n targetUids = Object.keys(allContentTypes).filter(\n (uid) =>\n uid.startsWith('api::') &&\n !uid.includes('bulk-updater') &&\n uid !== auditContentType\n );\n }\n\n const metaByUid: Record<string, any> = {};\n const allRelationsMeta: Record<string, RelationMeta> = {};\n\n for (const uid of targetUids) {\n const contentType = allContentTypes[uid];\n if (!contentType || (contentType.kind !== 'collectionType' && contentType.kind !== 'singleType')) continue;\n\n const isSingleType = contentType.kind === 'singleType';\n const rawAttrs = contentType.attributes || {};\n const relationsByAttr: Record<string, RelationMeta> = {};\n\n const validAttrs = Object.keys(rawAttrs).filter((attrKey) => {\n if (systemFields.includes(attrKey)) return false;\n if (rawAttrs[attrKey].type === 'component' || rawAttrs[attrKey].type === 'dynamiczone') return false;\n\n if (rawAttrs[attrKey].type === 'relation') {\n const rel = rawAttrs[attrKey];\n const targetUid = rel.target;\n if (!targetUid || !allContentTypes[targetUid]) return false;\n const targetInfo = allContentTypes[targetUid]?.info;\n const isMulti = rel.relation === 'oneToMany' || rel.relation === 'manyToMany';\n const relMeta: RelationMeta = {\n isRelation: true,\n relationType: rel.relation,\n targetUid,\n targetDisplayName: targetInfo?.displayName || targetUid,\n isMulti,\n };\n relationsByAttr[attrKey] = relMeta;\n allRelationsMeta[attrKey] = relMeta;\n return true;\n }\n\n return true;\n });\n\n validAttrs.forEach((attr) => allAttributesSet.add(attr));\n\n metaByUid[uid] = {\n uid,\n kind: contentType.kind,\n isSingleType,\n singularName: contentType.info?.singularName || uid,\n pluralName: contentType.info?.pluralName || uid,\n displayName: contentType.info?.displayName || uid,\n attributes: validAttrs,\n relations: relationsByAttr,\n };\n }\n\n staticSchemaCache = {\n attributes: Array.from(allAttributesSet),\n targetUids: Object.keys(metaByUid),\n metaByUid,\n relationsMeta: allRelationsMeta,\n };\n }\n\n const collections = [];\n const signatures: string[] = [];\n\n // 2. High-watermark check per collection\n for (const uid of staticSchemaCache.targetUids) {\n const meta = staticSchemaCache.metaByUid[uid];\n if (!meta) continue;\n\n const signature = await getCollectionSignature(strapi, uid);\n signatures.push(`${uid}=${signature}`);\n\n const cached = modelsCache.get(uid);\n if (cached && cached.signature === signature) {\n collections.push({\n ...meta,\n totalEntries: cached.totalCount,\n entries: cached.entries,\n });\n } else {\n let entries: any[] = [];\n let totalCount = 0;\n try {\n const relKeys = Object.keys(meta.relations || {});\n const populateOption = relKeys.length > 0\n ? relKeys.reduce((acc, k) => ({ ...acc, [k]: true }), {})\n : undefined;\n\n if (meta.isSingleType) {\n const singleDoc: any = await (strapi.documents(uid as any) as any).findFirst({\n populate: populateOption,\n });\n if (singleDoc) {\n entries = [singleDoc];\n totalCount = 1;\n }\n } else {\n totalCount = await strapi.documents(uid as any).count();\n entries = await strapi.documents(uid as any).findMany({\n limit: 10,\n sort: { createdAt: 'desc' },\n populate: populateOption,\n });\n }\n } catch (err) {\n strapi.log.error(`[bulk-updater] Failed to fetch entries for ${uid}:`, err);\n entries = [];\n totalCount = 0;\n }\n\n const formattedEntries = (entries || []).map((entry: any) => ({\n id: entry.id,\n documentId: entry.documentId,\n ...entry,\n }));\n\n modelsCache.set(uid, {\n signature,\n totalCount,\n entries: formattedEntries,\n });\n\n collections.push({\n ...meta,\n totalEntries: totalCount,\n entries: formattedEntries,\n });\n }\n }\n\n // Get license status\n const licenseService = strapi.plugin('bulk-updater').service('license');\n const license = await licenseService.getLicenseStatus();\n\n const executionConfig = getExecutionWindowConfig(strapi);\n const windowStatus = checkBusinessHours(executionConfig);\n const pendingCount = Array.from(pendingBatches.values()).filter(\n (b) => b.status === 'pending_approval' || b.status === 'queued_offpeak'\n ).length;\n\n const etag = `W/\"models-${crypto.createHash('md5').update(`${signatures.join('|')}:${license.tier}:${pendingCount}:${windowStatus.isBusinessHours}:${windowStatus.currentServerTime}:${windowStatus.timeZone}`).digest('hex')}\"`;\n\n return {\n success: true,\n attributes: staticSchemaCache.attributes,\n collections,\n relationsMeta: staticSchemaCache.relationsMeta,\n license,\n executionWindow: {\n ...executionConfig,\n ...windowStatus,\n },\n pendingBatchesCount: pendingCount,\n auditEnabled: Boolean(auditConfig?.enabled),\n etag,\n };\n },\n\n /**\n * On-Demand Paginated Collection Entries:\n * Supports server-side pagination (limit / start) and database query pushdown for filters.\n * Cached in-process with cross-pod high-watermark signature validation.\n */\n async getCollectionEntries(\n uid: string,\n {\n page = 1,\n pageSize = 10,\n attribute,\n filterOperator,\n filterQuery,\n search,\n sortField,\n sortOrder = 'desc',\n }: {\n page?: number;\n pageSize?: number;\n attribute?: string;\n filterOperator?: string;\n filterQuery?: string;\n search?: string;\n sortField?: string;\n sortOrder?: 'asc' | 'desc';\n }\n ) {\n const contentType = strapi.contentTypes[uid];\n if (!contentType) {\n throw new Error(`Collection with UID \"${uid}\" does not exist.`);\n }\n\n const limit = Math.min(Math.max(Number(pageSize) || 10, 1), 100);\n const currentPage = Math.max(Number(page) || 1, 1);\n const start = (currentPage - 1) * limit;\n\n const isSingleType = contentType.kind === 'singleType';\n\n // 1. Signature-based caching verification\n const signature = await getCollectionSignature(strapi, uid);\n const cacheKey = `${uid}:${limit}:${start}:${attribute || ''}:${filterOperator || ''}:${filterQuery || ''}:${search || ''}:${sortField || ''}:${sortOrder}`;\n const cached = queryCache.get(cacheKey);\n\n const etag = `W/\"entry-${crypto.createHash('md5').update(`${cacheKey}:${signature}`).digest('hex')}\"`;\n\n if (cached && cached.signature === signature && Date.now() - cached.timestamp < CACHE_TTL_MS) {\n return {\n ...cached.data,\n cached: true,\n etag,\n };\n }\n\n let result: any;\n\n if (isSingleType) {\n const singleDoc: any = await (strapi.documents(uid as any) as any).findFirst();\n result = {\n uid,\n displayName: contentType.info?.displayName || uid,\n isSingleType: true,\n entries: singleDoc ? [singleDoc] : [],\n pagination: {\n page: 1,\n pageSize: 1,\n total: singleDoc ? 1 : 0,\n pageCount: singleDoc ? 1 : 0,\n },\n };\n } else {\n // Build database filters pushdown\n const filters: Record<string, any> = {};\n\n if (attribute && filterOperator && filterQuery !== undefined && filterQuery !== '') {\n switch (filterOperator) {\n case 'equals':\n filters[attribute] = { $eq: filterQuery };\n break;\n case 'notEquals':\n filters[attribute] = { $ne: filterQuery };\n break;\n case 'contains':\n filters[attribute] = { $containsi: filterQuery };\n break;\n case 'notContains':\n filters[attribute] = { $notContainsi: filterQuery };\n break;\n case 'startsWith':\n filters[attribute] = { $startsWith: filterQuery };\n break;\n case 'endsWith':\n filters[attribute] = { $endsWith: filterQuery };\n break;\n case 'empty':\n filters[attribute] = { $null: true };\n break;\n case 'notEmpty':\n filters[attribute] = { $notNull: true };\n break;\n }\n }\n\n if (search && search.trim() !== '') {\n const stringAttrs = Object.keys(contentType.attributes || {}).filter(\n (k) =>\n contentType.attributes[k].type === 'string' ||\n contentType.attributes[k].type === 'text' ||\n contentType.attributes[k].type === 'email'\n );\n\n if (stringAttrs.length > 0) {\n filters.$or = stringAttrs.slice(0, 5).map((attr) => ({\n [attr]: { $containsi: search.trim() },\n }));\n }\n }\n\n const sortClause: Record<string, 'asc' | 'desc'> = {};\n if (sortField && contentType.attributes?.[sortField]) {\n sortClause[sortField] = sortOrder;\n } else {\n sortClause['createdAt'] = 'desc';\n }\n\n const relationAttrs = Object.keys(contentType.attributes || {}).filter(\n (k) => contentType.attributes[k].type === 'relation'\n );\n const populateOption = relationAttrs.length > 0\n ? relationAttrs.reduce((acc, k) => ({ ...acc, [k]: true }), {})\n : undefined;\n\n const [entries, totalEntries] = await Promise.all([\n strapi.documents(uid as any).findMany({\n start,\n limit,\n filters: Object.keys(filters).length > 0 ? filters : undefined,\n sort: sortClause,\n populate: populateOption,\n }),\n strapi.documents(uid as any).count({\n filters: Object.keys(filters).length > 0 ? filters : undefined,\n }),\n ]);\n\n const totalPages = Math.ceil(totalEntries / limit);\n\n result = {\n uid,\n displayName: contentType.info?.displayName || uid,\n isSingleType: false,\n entries: (entries || []).map((entry: any) => ({\n id: entry.id,\n documentId: entry.documentId,\n ...entry,\n })),\n pagination: {\n page: currentPage,\n pageSize: limit,\n total: totalEntries,\n pageCount: totalPages,\n },\n };\n }\n\n // Store in queryCache with bounding limit\n if (queryCache.size >= MAX_QUERY_CACHE_SIZE) {\n const toDelete = Array.from(queryCache.keys()).slice(0, 100);\n toDelete.forEach((k) => queryCache.delete(k));\n }\n queryCache.set(cacheKey, { signature, data: result, timestamp: Date.now() });\n\n return {\n ...result,\n cached: false,\n etag,\n };\n },\n\n /**\n * Get available target entities for a relation field:\n * Cached in-process with cross-pod signature validation.\n */\n async getRelationOptions(targetUid: string) {\n if (!targetUid || !strapi.contentTypes[targetUid]) {\n throw new Error(`Target content type \"${targetUid}\" does not exist.`);\n }\n\n const cacheKey = `rel_options:${targetUid}`;\n const signature = await getCollectionSignature(strapi, targetUid);\n const cached = queryCache.get(cacheKey);\n if (cached && cached.signature === signature) {\n return cached.data;\n }\n\n const docs = await strapi.documents(targetUid as any).findMany({\n limit: 100,\n sort: { createdAt: 'desc' },\n });\n\n const options = (docs || []).map((doc: any) => {\n const label = doc.name || doc.title || doc.email || doc.username || `Entry #${doc.id || doc.documentId}`;\n return {\n documentId: doc.documentId,\n label,\n };\n });\n\n const result = { targetUid, options };\n queryCache.set(cacheKey, { signature, data: result, timestamp: Date.now() });\n return result;\n },\n\n /**\n * Batch Update API with Maker-Checker and Off-Peak Scheduling:\n */\n async batchUpdate(\n {\n attribute,\n value,\n selectedEntries = [],\n selectedCollections = [],\n isRegex = false,\n regexPattern = '',\n executionMode = 'immediate',\n makerNotes = '',\n forceImmediate = false,\n publishOnApproval = true,\n }: {\n attribute: string;\n value: string;\n selectedEntries?: Array<{ uid: string; documentId: string }>;\n selectedCollections?: string[];\n isRegex?: boolean;\n regexPattern?: string;\n executionMode?: 'immediate' | 'queue_offpeak' | 'maker_checker';\n makerNotes?: string;\n forceImmediate?: boolean;\n publishOnApproval?: boolean;\n },\n currentUser?: any\n ) {\n if (!attribute) {\n throw new Error('Attribute name is required.');\n }\n\n const licenseService = strapi.plugin('bulk-updater').service('license');\n const license = await licenseService.getLicenseStatus();\n const isEnterprise = license.tier === 'enterprise';\n\n const executionConfig = getExecutionWindowConfig(strapi);\n const { isBusinessHours, policyMessage } = checkBusinessHours(executionConfig);\n\n // 1. Enterprise Maker-Checker Flow:\n if (executionMode === 'maker_checker') {\n const summary = await computeTargetsSummary(strapi, { selectedCollections, selectedEntries });\n if (summary.totalTargets === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const batch: PendingBatch = {\n id: batchId,\n createdAt: new Date().toISOString(),\n createdBy: currentUser\n ? {\n id: currentUser.id,\n email: currentUser.email,\n firstname: currentUser.firstname,\n lastname: currentUser.lastname,\n username: currentUser.username,\n }\n : { email: 'admin@system.local' },\n payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n summary: {\n attribute,\n value,\n totalTargets: summary.totalTargets,\n collectionBreakdown: summary.collectionBreakdown,\n publishOnApproval,\n },\n status: 'pending_approval',\n executionMode: 'maker_checker',\n makerNotes,\n };\n\n pendingBatches.set(batchId, batch);\n\n return {\n success: true,\n pending: true,\n batchId,\n status: 'pending_approval',\n message: 'Batch submitted for Checker authorization (4-Eyes Maker-Checker Workflow).',\n batch,\n };\n }\n\n // 2. Off-Peak Queued Batch:\n if (executionMode === 'queue_offpeak') {\n const summary = await computeTargetsSummary(strapi, { selectedCollections, selectedEntries });\n if (summary.totalTargets === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const batch: PendingBatch = {\n id: batchId,\n createdAt: new Date().toISOString(),\n createdBy: currentUser\n ? {\n id: currentUser.id,\n email: currentUser.email,\n firstname: currentUser.firstname,\n lastname: currentUser.lastname,\n username: currentUser.username,\n }\n : { email: 'admin@system.local' },\n payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n summary: {\n attribute,\n value,\n totalTargets: summary.totalTargets,\n collectionBreakdown: summary.collectionBreakdown,\n publishOnApproval,\n },\n status: 'queued_offpeak',\n executionMode: 'queue_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n makerNotes,\n };\n\n pendingBatches.set(batchId, batch);\n\n return {\n success: true,\n pending: true,\n batchId,\n status: 'queued_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n message: `Batch queued for server off-peak maintenance window (${executionConfig.offPeakStartTime}).`,\n batch,\n };\n }\n\n // 3. Immediate execution request with Server Business Hours enforcement:\n if (executionConfig.enabled && executionConfig.autoQueueDuringBusinessHours && isBusinessHours && !forceImmediate) {\n const summary = await computeTargetsSummary(strapi, { selectedCollections, selectedEntries });\n if (summary.totalTargets === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const batch: PendingBatch = {\n id: batchId,\n createdAt: new Date().toISOString(),\n createdBy: currentUser\n ? {\n id: currentUser.id,\n email: currentUser.email,\n firstname: currentUser.firstname,\n lastname: currentUser.lastname,\n username: currentUser.username,\n }\n : { email: 'admin@system.local' },\n payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n summary: {\n attribute,\n value,\n totalTargets: summary.totalTargets,\n collectionBreakdown: summary.collectionBreakdown,\n publishOnApproval,\n },\n status: 'queued_offpeak',\n executionMode: 'queue_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n makerNotes: 'Automatically queued due to peak business hours restriction.',\n };\n\n pendingBatches.set(batchId, batch);\n\n return {\n success: true,\n pending: true,\n batchId,\n status: 'queued_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n message: policyMessage,\n batch,\n };\n }\n\n // 4. Immediate execution\n return await executeBatchInternal(\n strapi,\n { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n currentUser\n );\n },\n\n /**\n * Get Pending & Queued Batches\n */\n getPendingBatches() {\n const executionConfig = getExecutionWindowConfig(strapi);\n const windowStatus = checkBusinessHours(executionConfig);\n const batches = Array.from(pendingBatches.values()).sort(\n (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()\n );\n\n return {\n success: true,\n batches,\n executionWindow: {\n ...executionConfig,\n ...windowStatus,\n },\n };\n },\n\n /**\n * Checker Approves Batch\n */\n async approveBatch(batchId: string, checkerUser: any, options: { executeImmediately?: boolean } = { executeImmediately: true }) {\n const batch = pendingBatches.get(batchId);\n if (!batch) {\n throw new Error(`Pending batch ${batchId} not found.`);\n }\n if (batch.status !== 'pending_approval') {\n throw new Error(`Batch ${batchId} is not pending approval (current status: ${batch.status}).`);\n }\n\n batch.approval = {\n checkerUser: checkerUser\n ? {\n id: checkerUser.id,\n email: checkerUser.email,\n firstname: checkerUser.firstname,\n lastname: checkerUser.lastname,\n username: checkerUser.username,\n }\n : { email: 'checker@system.local' },\n decisionDate: new Date().toISOString(),\n };\n\n const config = getExecutionWindowConfig(strapi);\n const { isBusinessHours } = checkBusinessHours(config);\n\n if (options.executeImmediately || !isBusinessHours) {\n batch.status = 'approved';\n const result = await executeBatchInternal(strapi, batch.payload, checkerUser, batch.id);\n batch.status = 'executed';\n batch.executionResult = result;\n return {\n success: true,\n batchId,\n status: 'executed',\n message: 'Batch approved and executed successfully by Checker.',\n batch,\n result,\n };\n } else {\n batch.status = 'approved';\n return {\n success: true,\n batchId,\n status: 'approved',\n message: `Batch approved by Checker. Queued for execution during off-peak window (${config.offPeakStartTime}).`,\n batch,\n };\n }\n },\n\n /**\n * Checker Rejects Batch\n */\n rejectBatch(batchId: string, checkerUser: any, reason: string = 'Rejected by Checker') {\n const batch = pendingBatches.get(batchId);\n if (!batch) {\n throw new Error(`Pending batch ${batchId} not found.`);\n }\n batch.status = 'rejected';\n batch.approval = {\n checkerUser: checkerUser\n ? {\n id: checkerUser.id,\n email: checkerUser.email,\n firstname: checkerUser.firstname,\n lastname: checkerUser.lastname,\n username: checkerUser.username,\n }\n : { email: 'checker@system.local' },\n decisionDate: new Date().toISOString(),\n rejectionReason: reason,\n };\n\n return {\n success: true,\n batchId,\n status: 'rejected',\n message: `Batch rejected by Checker: ${reason}`,\n batch,\n };\n },\n\n /**\n * Run Batch Immediately\n */\n async executeNow(batchId: string, currentUser: any) {\n const batch = pendingBatches.get(batchId);\n if (!batch) {\n throw new Error(`Pending batch ${batchId} not found.`);\n }\n if (batch.status === 'executed') {\n throw new Error(`Batch ${batchId} has already been executed.`);\n }\n\n const result = await executeBatchInternal(strapi, batch.payload, currentUser, batch.id);\n batch.status = 'executed';\n batch.executionResult = result;\n\n return {\n success: true,\n batchId,\n status: 'executed',\n message: 'Batch executed immediately.',\n batch,\n result,\n };\n },\n\n invalidateCache(uid?: string) {\n if (uid) {\n invalidateCollectionCache(uid);\n } else {\n queryCache.clear();\n modelsCache.clear();\n staticSchemaCache = null;\n }\n },\n };\n};\n","import type { Core } from '@strapi/strapi';\n\nexport interface LicenseStatus {\n isPro: boolean;\n tier: 'free' | 'pro' | 'enterprise';\n licenseKey: string | null;\n licensedTo: string | null;\n activatedAt: string | null;\n features: string[];\n}\n\nconst DEFAULT_FREE_STATUS: LicenseStatus = {\n isPro: false,\n tier: 'free',\n licenseKey: null,\n licensedTo: null,\n activatedAt: null,\n features: [\n 'Basic attribute batch updates',\n 'Schema compatibility checks',\n 'Interactive target selection',\n ],\n};\n\nconst PRO_FEATURES = [\n 'Basic attribute batch updates',\n 'Schema compatibility checks',\n 'Interactive target selection',\n 'Regex find & replace within attributes',\n 'Cross-relation and dynamic zone updates',\n 'Audit log export (CSV / JSON)',\n 'Unlimited collection batch operations',\n 'Priority enterprise support',\n];\n\nconst ENTERPRISE_FEATURES = [\n ...PRO_FEATURES,\n 'Maker-Checker Approval Workflow (4-Eyes Principle)',\n 'Server-Enforced Off-Peak Maintenance Window',\n 'Scheduled batch queuing during business hours',\n];\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getStore() {\n return strapi.store({\n type: 'plugin',\n name: 'bulk-updater',\n key: 'license',\n });\n },\n\n async getLicenseStatus(): Promise<LicenseStatus> {\n // Check developer config override in config/plugins.ts\n const isConfigEnterprise = strapi.config.get('plugin::bulk-updater.makerChecker.enabled');\n\n const store = await this.getStore();\n const stored = await store.get({});\n if (!stored || !stored.licenseKey) {\n if (isConfigEnterprise) {\n return {\n isPro: true,\n tier: 'enterprise',\n licenseKey: 'CONFIG-OVERRIDE',\n licensedTo: 'Enterprise Developer Config',\n activatedAt: new Date().toISOString(),\n features: ENTERPRISE_FEATURES,\n };\n }\n return DEFAULT_FREE_STATUS;\n }\n if (isConfigEnterprise) {\n return {\n ...stored,\n isPro: true,\n tier: 'enterprise',\n features: Array.from(new Set([...(stored.features || []), ...ENTERPRISE_FEATURES])),\n };\n }\n return stored;\n },\n\n async verifyAndActivateLicense(licenseKey: string): Promise<{ success: boolean; license?: LicenseStatus; error?: string }> {\n if (!licenseKey || typeof licenseKey !== 'string') {\n return { success: false, error: 'A valid license key is required.' };\n }\n\n const trimmed = licenseKey.trim();\n\n // Verification Logic:\n // Supports live LemonSqueezy / Gumroad / Keygen keys or offline signature/prefix keys\n // For demonstration and production readiness, keys with prefix PRO- or ENTERPRISE- are valid Pro licenses\n const isEnterprise = trimmed.toUpperCase().startsWith('ENT-') || trimmed.toUpperCase().startsWith('ENTERPRISE-');\n const isPro = trimmed.toUpperCase().startsWith('PRO-') || isEnterprise;\n\n if (!isPro) {\n return {\n success: false,\n error: 'Invalid license key. License keys must start with PRO- or ENTERPRISE-.',\n };\n }\n\n const tier = isEnterprise ? 'enterprise' : 'pro';\n const status: LicenseStatus = {\n isPro: true,\n tier,\n licenseKey: trimmed,\n licensedTo: `Licensed Customer (${tier.toUpperCase()})`,\n activatedAt: new Date().toISOString(),\n features: isEnterprise ? ENTERPRISE_FEATURES : PRO_FEATURES,\n };\n\n const store = await this.getStore();\n await store.set({ value: status });\n\n return {\n success: true,\n license: status,\n };\n },\n\n async deactivateLicense(): Promise<LicenseStatus> {\n const store = await this.getStore();\n await store.set({ value: DEFAULT_FREE_STATUS });\n return DEFAULT_FREE_STATUS;\n },\n});\n","import type { Core } from '@strapi/strapi';\n\nexport interface AuditConfig {\n enabled: boolean;\n mode?: 'custom' | 'default' | 'enterprise_native' | 'disabled';\n contentType?: string;\n fields?: Record<string, string>;\n isEnterpriseNativeAvailable?: boolean;\n error?: string;\n}\n\nconst DEFAULT_FIELDS: Record<string, string> = {\n batchId: 'batchId',\n attribute: 'attribute',\n value: 'value',\n userEmail: 'userEmail',\n userName: 'userName',\n updatedCount: 'updatedCount',\n skippedCount: 'skippedCount',\n totalTargets: 'totalTargets',\n diffs: 'diffs',\n isRolledBack: 'isRolledBack',\n rolledBackAt: 'rolledBackAt',\n rolledBackBy: 'rolledBackBy',\n executedAt: 'executedAt',\n};\n\nconst DEFAULT_CANDIDATE_TYPES = [\n 'api::audit-log.audit-log',\n 'api::audit.audit',\n 'api::bulk-audit-log.bulk-audit-log',\n];\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n /**\n * Detects whether Strapi Enterprise Native Audit Log service is available.\n */\n getEnterpriseAuditService() {\n try {\n const svc =\n (strapi as any).service?.('admin::audit-logs') ||\n (strapi as any).admin?.services?.['audit-logs'];\n return svc || null;\n } catch {\n return null;\n }\n },\n\n /**\n * Resolves the audit configuration:\n * 1. Explicit consumer custom schema (full declarative control)\n * 2. Zero-config convention auto-detection (api::audit-log.audit-log)\n * 3. Strapi Enterprise native audit log bridge\n */\n getAuditConfig(): AuditConfig {\n const rawConfig = strapi.config.get('plugin::bulk-updater.audit') as any;\n const eeService = this.getEnterpriseAuditService();\n const isEnterpriseNativeAvailable = Boolean(\n eeService && typeof eeService.save === 'function'\n );\n\n // If consumer explicitly disabled audit\n if (rawConfig && rawConfig.enabled === false) {\n return {\n enabled: false,\n mode: 'disabled',\n isEnterpriseNativeAvailable,\n };\n }\n\n // Tier 1: Explicit consumer custom schema\n if (rawConfig?.contentType) {\n const { contentType, fields } = rawConfig;\n const schema = strapi.contentTypes[contentType];\n if (!schema) {\n return {\n enabled: false,\n mode: 'disabled',\n isEnterpriseNativeAvailable,\n error: `Declared audit contentType \"${contentType}\" does not exist in Strapi schema registry.`,\n };\n }\n\n // Check fields or fallback to DEFAULT_FIELDS\n const resolvedFields = { ...DEFAULT_FIELDS, ...(fields || {}) };\n return {\n enabled: true,\n mode: 'custom',\n contentType,\n fields: resolvedFields,\n isEnterpriseNativeAvailable,\n };\n }\n\n // Tier 2: Zero-config auto-discovery of default audit collection\n for (const candidate of DEFAULT_CANDIDATE_TYPES) {\n if (strapi.contentTypes[candidate]) {\n return {\n enabled: true,\n mode: 'default',\n contentType: candidate,\n fields: DEFAULT_FIELDS,\n isEnterpriseNativeAvailable,\n };\n }\n }\n\n // Tier 3: Strapi Enterprise native audit logs\n if (isEnterpriseNativeAvailable) {\n return {\n enabled: true,\n mode: 'enterprise_native',\n isEnterpriseNativeAvailable: true,\n };\n }\n\n // Tier 4: Audit disabled / unconfigured\n return {\n enabled: false,\n mode: 'disabled',\n isEnterpriseNativeAvailable: false,\n error:\n 'No audit collection configured or detected. To enable rollback, provide a collection or use Strapi Enterprise.',\n };\n },\n\n /**\n * Optionally writes an audit event directly to Strapi Enterprise's native Audit Logs table.\n */\n async recordToEnterpriseNative(\n action: string,\n payload: Record<string, any>,\n user?: any\n ) {\n try {\n const eeService = this.getEnterpriseAuditService();\n if (eeService && typeof eeService.save === 'function') {\n await eeService.save({\n action,\n date: new Date(),\n userId: user?.id,\n payload,\n });\n strapi.log.info(\n `[bulk-updater] Successfully dispatched event to Strapi Enterprise Audit Log: ${action}`\n );\n }\n } catch (err: any) {\n strapi.log.warn(\n `[bulk-updater] Note: Could not dispatch to Strapi Enterprise native audit log: ${err.message}`\n );\n }\n },\n\n /**\n * Records a batch execution:\n * - Dispatches to Strapi Enterprise native audit log if available\n * - Records to consumer or auto-detected collection type for one-click rollback\n */\n async recordBatch(\n batchSummary: {\n batchId: string;\n attribute: string;\n value: string;\n updatedCount: number;\n skippedCount: number;\n totalTargets: number;\n },\n results: any[],\n user?: any\n ) {\n const config = this.getAuditConfig();\n\n // 1. Always attempt Strapi Enterprise native logging if available\n await this.recordToEnterpriseNative(\n 'bulk-updater.batch-update',\n {\n batchId: batchSummary.batchId,\n attribute: batchSummary.attribute,\n value: batchSummary.value,\n updatedCount: batchSummary.updatedCount,\n skippedCount: batchSummary.skippedCount,\n totalTargets: batchSummary.totalTargets,\n },\n user\n );\n\n // 2. Persist to rollback-capable collection type if available\n if (!config.enabled || !config.contentType || !config.fields) {\n return null;\n }\n\n const f = config.fields;\n const mappedData: any = {};\n\n if (f.batchId) mappedData[f.batchId] = batchSummary.batchId;\n if (f.attribute) mappedData[f.attribute] = batchSummary.attribute;\n if (f.value) mappedData[f.value] = batchSummary.value;\n if (f.userEmail) mappedData[f.userEmail] = user?.email || 'admin@strapi.io';\n if (f.userName) {\n mappedData[f.userName] = user?.firstname\n ? `${user.firstname} ${user.lastname || ''}`.trim()\n : user?.email || 'Admin';\n }\n if (f.updatedCount) mappedData[f.updatedCount] = batchSummary.updatedCount;\n if (f.skippedCount) mappedData[f.skippedCount] = batchSummary.skippedCount;\n if (f.totalTargets) mappedData[f.totalTargets] = batchSummary.totalTargets;\n if (f.diffs) mappedData[f.diffs] = results;\n if (f.isRolledBack) mappedData[f.isRolledBack] = false;\n if (f.executedAt) mappedData[f.executedAt] = new Date().toISOString();\n\n try {\n const created = await strapi.documents(config.contentType as any).create({\n data: mappedData,\n });\n return created;\n } catch (err: any) {\n strapi.log.error(`[bulk-updater] Failed to record audit log: ${err.message}`);\n return null;\n }\n },\n\n /**\n * Fetches audit history for the UI\n */\n async getHistory() {\n const config = this.getAuditConfig();\n\n if (config.mode === 'enterprise_native' && !config.contentType) {\n return {\n enabled: true,\n mode: 'enterprise_native',\n message:\n 'Strapi Enterprise Native Audit Logs active. Batch events are recorded directly to Strapi Settings > Audit Logs. (To enable one-click document rollback, declare or add an audit-log collection).',\n history: [],\n };\n }\n\n if (!config.enabled || !config.contentType || !config.fields) {\n return {\n enabled: false,\n message:\n config.error ||\n 'Audit trail is disabled. To enable audit logs & rollback, declare an audit schema in config/plugins.ts or create api::audit-log.audit-log.',\n history: [],\n };\n }\n\n try {\n const records: any[] = await strapi.documents(config.contentType as any).findMany({\n sort: { createdAt: 'desc' } as any,\n });\n\n const f = config.fields;\n const normalized = (records || []).map((r: any) => ({\n id: r.id,\n documentId: r.documentId,\n batchId: r[f.batchId] || r.documentId,\n attribute: r[f.attribute],\n value: r[f.value],\n userEmail: r[f.userEmail] || 'admin@strapi.io',\n userName: r[f.userName] || 'Admin',\n updatedCount: r[f.updatedCount] || 0,\n skippedCount: r[f.skippedCount] || 0,\n totalTargets: r[f.totalTargets] || 0,\n diffs: typeof r[f.diffs] === 'string' ? JSON.parse(r[f.diffs]) : r[f.diffs] || [],\n isRolledBack: Boolean(r[f.isRolledBack]),\n rolledBackAt: r[f.rolledBackAt] || null,\n rolledBackBy: r[f.rolledBackBy] || null,\n executedAt: r[f.executedAt] || r.createdAt,\n }));\n\n return {\n enabled: true,\n mode: config.mode,\n contentType: config.contentType,\n isEnterpriseNativeAvailable: config.isEnterpriseNativeAvailable,\n history: normalized,\n };\n } catch (err: any) {\n return {\n enabled: false,\n message: `Failed to read audit history: ${err.message}`,\n history: [],\n };\n }\n },\n\n /**\n * Performs deterministic rollback of a batch\n */\n async rollbackBatch(batchId: string, currentUser?: any) {\n const config = this.getAuditConfig();\n if (!config.enabled || !config.contentType || !config.fields) {\n throw new Error('Audit trail is not configured or disabled.');\n }\n\n const f = config.fields;\n const records: any[] = await strapi.documents(config.contentType as any).findMany({\n filters: {\n [f.batchId]: { $eq: batchId },\n } as any,\n });\n\n const record = records?.[0];\n if (!record) {\n throw new Error(`Batch record with ID \"${batchId}\" was not found in audit trail.`);\n }\n\n if (record[f.isRolledBack]) {\n throw new Error(`Batch \"${batchId}\" has already been rolled back.`);\n }\n\n const diffs =\n typeof record[f.diffs] === 'string'\n ? JSON.parse(record[f.diffs])\n : record[f.diffs] || [];\n\n let revertedCount = 0;\n const rollbackDetails = [];\n\n for (const item of diffs) {\n if (item.status === 'updated' && item.uid && item.documentId && item.attribute) {\n try {\n const isRel = item.oldValue && typeof item.oldValue === 'object' && item.oldValue.isRelation;\n let rollbackPayload: any;\n\n if (isRel) {\n const oldDocIds = Array.isArray(item.oldValue.documentIds) ? item.oldValue.documentIds : [];\n rollbackPayload = { set: oldDocIds };\n } else {\n rollbackPayload = item.oldValue;\n }\n\n await strapi.documents(item.uid as any).update({\n documentId: item.documentId,\n data: {\n [item.attribute]: rollbackPayload,\n },\n });\n\n const ct = strapi.contentTypes[item.uid];\n if (ct?.options?.draftAndPublish) {\n try {\n await (strapi.documents(item.uid as any) as any).publish({\n documentId: item.documentId,\n });\n } catch {}\n }\n\n try {\n strapi.plugin('bulk-updater').service('bulkUpdater').invalidateCollectionCache(item.uid);\n } catch {}\n\n revertedCount++;\n rollbackDetails.push({\n uid: item.uid,\n documentId: item.documentId,\n label: item.label,\n attribute: item.attribute,\n revertedTo: item.oldValue,\n status: 'reverted',\n });\n } catch (err: any) {\n rollbackDetails.push({\n uid: item.uid,\n documentId: item.documentId,\n label: item.label,\n status: 'error',\n message: err.message,\n });\n }\n }\n }\n\n const updateData: any = {};\n if (f.isRolledBack) updateData[f.isRolledBack] = true;\n if (f.rolledBackAt) updateData[f.rolledBackAt] = new Date().toISOString();\n if (f.rolledBackBy) {\n updateData[f.rolledBackBy] = currentUser?.email || 'Admin';\n }\n\n await strapi.documents(config.contentType as any).update({\n documentId: record.documentId,\n data: updateData,\n });\n\n // Invalidate affected collection caches\n try {\n const affectedUids = new Set(rollbackDetails.map((d: any) => d.uid));\n const bulkService = strapi.plugin('bulk-updater').service('bulkUpdater');\n affectedUids.forEach((uid: any) => {\n if (uid && bulkService?.invalidateCache) {\n bulkService.invalidateCache(uid);\n }\n });\n } catch {}\n\n // Mirror rollback event to Strapi Enterprise native audit log if available\n await this.recordToEnterpriseNative(\n 'bulk-updater.rollback',\n {\n batchId,\n revertedCount,\n },\n currentUser\n );\n\n return {\n success: true,\n batchId,\n revertedCount,\n details: rollbackDetails,\n };\n },\n});\n","import routes from './routes';\nimport bulkUpdaterController from './controllers/bulk-updater';\nimport licenseController from './controllers/license';\nimport auditController from './controllers/audit';\nimport bulkUpdaterService from './services/bulk-updater';\nimport licenseService from './services/license';\nimport auditService from './services/audit';\n\nexport default {\n register({ strapi }: { strapi: any }) {},\n bootstrap({ strapi }: { strapi: any }) {},\n routes,\n controllers: {\n bulkUpdater: bulkUpdaterController,\n license: licenseController,\n audit: auditController,\n },\n services: {\n bulkUpdater: bulkUpdaterService,\n license: licenseService,\n audit: auditService,\n },\n};\n"],"names":["licenseService"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../server/src/routes/index.ts","../../server/src/controllers/bulk-updater.ts","../../server/src/controllers/license.ts","../../server/src/controllers/audit.ts","../../server/src/services/bulk-updater.ts","../../server/src/services/license.ts","../../server/src/services/audit.ts","../../server/src/index.ts"],"sourcesContent":["export default {\n admin: {\n type: 'admin',\n routes: [\n {\n method: 'GET',\n path: '/models',\n handler: 'bulkUpdater.getModels',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/collections/:uid/entries',\n handler: 'bulkUpdater.getCollectionEntries',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/relation-options',\n handler: 'bulkUpdater.getRelationOptions',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batch-update',\n handler: 'bulkUpdater.batchUpdate',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/pending-batches',\n handler: 'bulkUpdater.getPendingBatches',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batches/:batchId/approve',\n handler: 'bulkUpdater.approveBatch',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batches/:batchId/reject',\n handler: 'bulkUpdater.rejectBatch',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/batches/:batchId/execute-now',\n handler: 'bulkUpdater.executeNow',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/license',\n handler: 'license.getStatus',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/license/verify',\n handler: 'license.verify',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/license/deactivate',\n handler: 'license.deactivate',\n config: {\n policies: [],\n },\n },\n {\n method: 'GET',\n path: '/audit/history',\n handler: 'audit.getHistory',\n config: {\n policies: [],\n },\n },\n {\n method: 'POST',\n path: '/audit/:batchId/rollback',\n handler: 'audit.rollback',\n config: {\n policies: [],\n },\n },\n ],\n },\n};\n","import type { Core } from '@strapi/strapi';\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getModels(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const data = await service.getModels();\n\n if (data?.etag) {\n ctx.set('ETag', data.etag);\n ctx.set('Cache-Control', 'private, no-cache');\n\n if (ctx.request.headers['if-none-match'] === data.etag) {\n ctx.status = 304;\n return;\n }\n }\n\n ctx.body = data;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async getCollectionEntries(ctx: any) {\n try {\n const { uid } = ctx.params;\n const { page, pageSize, attribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const data = await service.getCollectionEntries(uid, {\n page: page ? Number(page) : 1,\n pageSize: pageSize ? Number(pageSize) : 10,\n attribute: attribute ? String(attribute) : undefined,\n filterOperator: filterOperator ? String(filterOperator) : undefined,\n filterQuery: filterQuery !== undefined ? String(filterQuery) : undefined,\n search: search ? String(search) : undefined,\n sortField: sortField ? String(sortField) : undefined,\n sortOrder: sortOrder === 'asc' ? 'asc' : 'desc',\n });\n\n if (data?.etag) {\n ctx.set('ETag', data.etag);\n ctx.set('Cache-Control', 'private, no-cache');\n\n if (ctx.request.headers['if-none-match'] === data.etag) {\n ctx.status = 304;\n return;\n }\n }\n\n ctx.body = data;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async getRelationOptions(ctx: any) {\n try {\n const { targetUid } = ctx.query;\n if (!targetUid) {\n ctx.status = 400;\n ctx.body = { success: false, error: 'targetUid is required' };\n return;\n }\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const data = await service.getRelationOptions(String(targetUid));\n ctx.body = { success: true, ...data };\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async batchUpdate(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.batchUpdate(ctx.request.body || {}, currentUser);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async getPendingBatches(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const result = await service.getPendingBatches();\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async approveBatch(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const { executeImmediately = true } = ctx.request.body || {};\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.approveBatch(batchId, currentUser, { executeImmediately });\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async rejectBatch(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const { reason } = ctx.request.body || {};\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.rejectBatch(batchId, currentUser, reason);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async executeNow(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const service = strapi.plugin('bulk-updater').service('bulkUpdater');\n const currentUser = ctx.state.user;\n const result = await service.executeNow(batchId, currentUser);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n});\n","import type { Core } from '@strapi/strapi';\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getStatus(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('license');\n const license = await service.getLicenseStatus();\n ctx.body = { success: true, license };\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async verify(ctx: any) {\n try {\n const { licenseKey } = ctx.request.body || {};\n const service = strapi.plugin('bulk-updater').service('license');\n const result = await service.verifyAndActivateLicense(licenseKey);\n if (!result.success) {\n ctx.status = 400;\n ctx.body = result;\n return;\n }\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async deactivate(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('license');\n const license = await service.deactivateLicense();\n ctx.body = { success: true, license };\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n});\n","import type { Core } from '@strapi/strapi';\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getHistory(ctx: any) {\n try {\n const service = strapi.plugin('bulk-updater').service('audit');\n const data = await service.getHistory();\n ctx.body = data;\n } catch (err: any) {\n ctx.status = 500;\n ctx.body = { success: false, error: err.message };\n }\n },\n\n async rollback(ctx: any) {\n try {\n const { batchId } = ctx.params;\n const service = strapi.plugin('bulk-updater').service('audit');\n const currentUser = ctx.state.user;\n const result = await service.rollbackBatch(batchId, currentUser);\n ctx.body = result;\n } catch (err: any) {\n ctx.status = 400;\n ctx.body = { success: false, error: err.message };\n }\n },\n});\n","import type { Core } from '@strapi/strapi';\nimport crypto from 'crypto';\n\n// In-process memory caches (scoped to pod)\ninterface CacheEntry {\n signature: string;\n data: any;\n timestamp: number;\n}\n\nconst queryCache = new Map<string, CacheEntry>();\nconst modelsCache = new Map<string, { signature: string; totalCount: number; entries: any[] }>();\nconst MAX_QUERY_CACHE_SIZE = 500;\nconst CACHE_TTL_MS = 60 * 1000; // 60-second TTL fallback\n\nexport interface RelationMeta {\n isRelation: boolean;\n relationType: string;\n targetUid: string;\n targetDisplayName: string;\n isMulti: boolean;\n}\n\nlet staticSchemaCache: {\n attributes: string[];\n targetUids: string[];\n metaByUid: Record<\n string,\n {\n uid: string;\n kind: string;\n isSingleType: boolean;\n singularName: string;\n pluralName: string;\n displayName: string;\n attributes: string[];\n relations: Record<string, RelationMeta>;\n }\n >;\n relationsMeta: Record<string, RelationMeta>;\n} | null = null;\n\nexport interface ExecutionWindowConfig {\n enabled: boolean;\n timeZone: string;\n businessHours: {\n start: string;\n end: string;\n };\n offPeakStartTime: string;\n autoQueueDuringBusinessHours: boolean;\n}\n\nexport function getSafeTimezone(timeZone?: string): string {\n if (!timeZone || typeof timeZone !== 'string') {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';\n }\n try {\n Intl.DateTimeFormat(undefined, { timeZone: timeZone.trim() });\n return timeZone.trim();\n } catch {\n return 'UTC';\n }\n}\n\nexport function getTimeInTimezone(timeZone: string, date: Date = new Date()): { hours: number; minutes: number; timeString: string } {\n const safeTz = getSafeTimezone(timeZone);\n const formatter = new Intl.DateTimeFormat('en-GB', {\n timeZone: safeTz,\n hour: '2-digit',\n minute: '2-digit',\n hourCycle: 'h23',\n });\n const timeString = formatter.format(date);\n const [h, m] = timeString.split(':').map(Number);\n return { hours: h, minutes: m, timeString };\n}\n\nexport function getExecutionWindowConfig(strapi: Core.Strapi): ExecutionWindowConfig {\n const pluginConfig = strapi.config.get('plugin::bulk-updater.executionWindow') as Partial<ExecutionWindowConfig> | undefined;\n const configuredTz = pluginConfig?.timeZone ||\n strapi.config.get('server.timeZone') ||\n Intl.DateTimeFormat().resolvedOptions().timeZone ||\n 'UTC';\n const timeZone = getSafeTimezone(configuredTz);\n\n return {\n enabled: pluginConfig?.enabled ?? true,\n timeZone,\n businessHours: {\n start: pluginConfig?.businessHours?.start || '09:00',\n end: pluginConfig?.businessHours?.end || '18:00',\n },\n offPeakStartTime: pluginConfig?.offPeakStartTime || '18:00',\n autoQueueDuringBusinessHours: pluginConfig?.autoQueueDuringBusinessHours ?? true,\n };\n}\n\nexport function checkBusinessHours(config: ExecutionWindowConfig, testDate?: Date) {\n const timeZone = getSafeTimezone(config.timeZone);\n const { hours, minutes, timeString } = getTimeInTimezone(timeZone, testDate || new Date());\n\n const currentMinutes = hours * 60 + minutes;\n const [startH, startM] = (config.businessHours.start || '09:00').split(':').map(Number);\n const [endH, endM] = (config.businessHours.end || '18:00').split(':').map(Number);\n const startMinutes = startH * 60 + startM;\n const endMinutes = endH * 60 + endM;\n\n const isBusinessHours = currentMinutes >= startMinutes && currentMinutes < endMinutes;\n\n return {\n isBusinessHours,\n currentServerTime: timeString,\n timeZone,\n businessHours: config.businessHours,\n offPeakStartTime: config.offPeakStartTime,\n policyMessage: isBusinessHours\n ? `Server policy restricts bulk updates during peak business hours (${config.businessHours.start} - ${config.businessHours.end} ${timeZone}). Current time: ${timeString} (${timeZone}). Updates will be queued for off-peak execution at ${config.offPeakStartTime} ${timeZone}.`\n : `Currently in off-peak execution window (outside ${config.businessHours.start} - ${config.businessHours.end} ${timeZone}). Current time: ${timeString} (${timeZone}). Direct execution is permitted.`,\n };\n}\n\nexport interface PendingBatch {\n id: string;\n createdAt: string;\n createdBy: { id?: string | number; email?: string; firstname?: string; lastname?: string; username?: string };\n payload: {\n attribute: string;\n value: string;\n selectedEntries?: Array<{ uid: string; documentId: string }>;\n selectedCollections?: string[];\n isRegex?: boolean;\n regexPattern?: string;\n publishOnApproval?: boolean;\n };\n summary: {\n attribute: string;\n value: string;\n totalTargets: number;\n collectionBreakdown: Record<string, number>;\n publishOnApproval?: boolean;\n };\n status: 'pending_approval' | 'queued_offpeak' | 'approved' | 'rejected' | 'executed';\n executionMode: 'maker_checker' | 'queue_offpeak' | 'immediate';\n scheduledFor?: string;\n makerNotes?: string;\n approval?: {\n checkerUser?: { id?: string | number; email?: string; firstname?: string; lastname?: string; username?: string };\n decisionDate?: string;\n rejectionReason?: string;\n };\n executionResult?: any;\n}\n\nconst pendingBatches = new Map<string, PendingBatch>();\nlet isRunnerRunning = false;\nlet runnerInterval: NodeJS.Timeout | null = null;\n\nexport function invalidateCollectionCache(uid: string) {\n modelsCache.delete(uid);\n for (const key of queryCache.keys()) {\n if (key.startsWith(`${uid}:`)) {\n queryCache.delete(key);\n }\n }\n}\n\nasync function getCollectionSignature(strapi: Core.Strapi, uid: string): Promise<string> {\n const contentType = strapi.contentTypes[uid];\n if (!contentType) return 'invalid';\n\n const isSingleType = contentType.kind === 'singleType';\n try {\n if (isSingleType) {\n const doc: any = await (strapi.documents(uid as any) as any).findFirst({\n fields: ['updatedAt', 'id'],\n });\n return doc ? `single:${doc.id}:${doc.updatedAt}` : 'single:empty';\n }\n\n const [total, latest] = await Promise.all([\n strapi.documents(uid as any).count(),\n strapi.documents(uid as any).findMany({\n limit: 1,\n sort: { updatedAt: 'desc' },\n fields: ['updatedAt', 'id'],\n }),\n ]);\n\n const latestDoc = latest && latest.length > 0 ? latest[0] : null;\n return `${total}:${latestDoc?.updatedAt || '0'}:${latestDoc?.id || '0'}`;\n } catch {\n return `${Date.now()}`;\n }\n}\n\nasync function computeTargetsSummary(strapi: Core.Strapi, { selectedCollections = [], selectedEntries = [] }: any) {\n const targetsMap = new Map<string, { uid: string; documentId: string }>();\n const breakdown: Record<string, number> = {};\n\n for (const colUid of selectedCollections) {\n const contentType = strapi.contentTypes[colUid];\n const displayName = contentType?.info?.displayName || colUid;\n try {\n const entries = await strapi.documents(colUid as any).findMany();\n const count = entries ? entries.length : 0;\n breakdown[displayName] = (breakdown[displayName] || 0) + count;\n for (const entry of (entries || []) as any[]) {\n targetsMap.set(`${colUid}@@@${entry.documentId}`, { uid: colUid, documentId: entry.documentId });\n }\n } catch {\n // Ignore count failures\n }\n }\n\n for (const item of selectedEntries) {\n if (!item.uid || !item.documentId) continue;\n const key = `${item.uid}@@@${item.documentId}`;\n if (!targetsMap.has(key)) {\n targetsMap.set(key, { uid: item.uid, documentId: item.documentId });\n const contentType = strapi.contentTypes[item.uid];\n const displayName = contentType?.info?.displayName || item.uid;\n breakdown[displayName] = (breakdown[displayName] || 0) + 1;\n }\n }\n\n return {\n totalTargets: targetsMap.size,\n collectionBreakdown: breakdown,\n };\n}\n\nasync function executeBatchInternal(\n strapi: Core.Strapi,\n {\n attribute,\n value,\n selectedEntries = [],\n selectedCollections = [],\n isRegex = false,\n regexPattern = '',\n publishOnApproval = true,\n }: {\n attribute: string;\n value: string;\n selectedEntries?: Array<{ uid: string; documentId: string }>;\n selectedCollections?: string[];\n isRegex?: boolean;\n regexPattern?: string;\n publishOnApproval?: boolean;\n },\n currentUser?: any,\n existingBatchId?: string\n) {\n if (!attribute) {\n throw new Error('Attribute name is required.');\n }\n\n const licenseService = strapi.plugin('bulk-updater').service('license');\n const license = await licenseService.getLicenseStatus();\n\n // Check Pro feature gating\n if (isRegex && !license.isPro) {\n throw new Error(\n 'Regex find & replace is a Pro feature. Please activate a Pro license in Plugin Settings.'\n );\n }\n\n const targetsMap = new Map<string, { uid: string; documentId: string }>();\n\n // 1. Add entire collections if selected\n for (const colUid of selectedCollections) {\n if (!strapi.contentTypes[colUid]) continue;\n const entries = await strapi.documents(colUid as any).findMany();\n for (const entry of (entries || []) as any[]) {\n const key = `${colUid}@@@${entry.documentId}`;\n targetsMap.set(key, { uid: colUid, documentId: entry.documentId });\n }\n }\n\n // 2. Add individually selected entries\n for (const item of selectedEntries) {\n if (!item.uid || !item.documentId) continue;\n const key = `${item.uid}@@@${item.documentId}`;\n targetsMap.set(key, { uid: item.uid, documentId: item.documentId });\n }\n\n if (targetsMap.size === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = existingBatchId || `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const results: any[] = [];\n let updatedCount = 0;\n let skippedCount = 0;\n\n let regexObj: RegExp | null = null;\n if (isRegex && regexPattern) {\n regexObj = new RegExp(regexPattern, 'g');\n }\n\n const targets = Array.from(targetsMap.values());\n const CHUNK_SIZE = 25; // 25 concurrent updates per batch window\n\n // Request-scoped DataLoader for deduplicating lookups during batch itemization\n const documentLoader = new Map<string, Promise<any>>();\n const getDocumentDeduplicated = (uid: string, documentId: string) => {\n const loaderKey = `${uid}@@@${documentId}`;\n if (!documentLoader.has(loaderKey)) {\n documentLoader.set(\n loaderKey,\n strapi.documents(uid as any).findOne({ documentId })\n );\n }\n return documentLoader.get(loaderKey);\n };\n\n for (let i = 0; i < targets.length; i += CHUNK_SIZE) {\n const chunk = targets.slice(i, i + CHUNK_SIZE);\n const chunkPromises = chunk.map(async (target) => {\n const contentType = strapi.contentTypes[target.uid];\n const displayName = contentType?.info?.displayName || target.uid;\n\n try {\n const currentEntry: any = await getDocumentDeduplicated(target.uid, target.documentId);\n\n const entryLabel =\n currentEntry?.name ||\n currentEntry?.title ||\n currentEntry?.email ||\n `Entry #${target.documentId}`;\n\n const attrDef = contentType?.attributes?.[attribute];\n const hasAttribute = Boolean(attrDef);\n\n if (hasAttribute) {\n if (attrDef.type === 'relation') {\n const isMulti = attrDef.relation === 'oneToMany' || attrDef.relation === 'manyToMany';\n\n // Retrieve current relation state with populate for accurate audit logging\n const currentEntryWithPopulate: any = await strapi.documents(target.uid as any).findOne({\n documentId: target.documentId,\n populate: [attribute],\n });\n const currentRel = currentEntryWithPopulate?.[attribute];\n\n let oldDocIds: string[] = [];\n let oldDisplay = '';\n if (isMulti && Array.isArray(currentRel)) {\n oldDocIds = currentRel.map((r: any) => r.documentId).filter(Boolean);\n oldDisplay = currentRel.map((r: any) => r.name || r.title || r.email || r.documentId).join(', ') || '(empty)';\n } else if (currentRel) {\n oldDocIds = currentRel.documentId ? [currentRel.documentId] : [];\n oldDisplay = currentRel.name || currentRel.title || currentRel.email || currentRel.documentId || '(empty)';\n } else {\n oldDisplay = '(unlinked / none)';\n }\n\n // Parse relation command:\n let mode: 'set' | 'connect' | 'disconnect' | 'clear' = 'set';\n let targetDocIds: string[] = [];\n let newDisplay = '';\n\n if (value === '__clear__' || value === '') {\n mode = 'clear';\n newDisplay = '(cleared / unlinked)';\n } else {\n try {\n const parsed = JSON.parse(value);\n if (parsed && typeof parsed === 'object' && parsed.mode) {\n mode = parsed.mode;\n targetDocIds = Array.isArray(parsed.documentIds) ? parsed.documentIds : (parsed.documentId ? [parsed.documentId] : []);\n newDisplay = parsed.label || targetDocIds.join(', ') || `[${mode}]`;\n } else if (Array.isArray(parsed)) {\n targetDocIds = parsed;\n newDisplay = targetDocIds.join(', ');\n } else {\n targetDocIds = [value];\n newDisplay = value;\n }\n } catch {\n targetDocIds = [value];\n newDisplay = value;\n }\n }\n\n // Construct Document Service relation mutation payload\n let relationPayload: any;\n if (mode === 'clear') {\n relationPayload = isMulti ? { set: [] } : null;\n } else if (mode === 'set') {\n relationPayload = isMulti ? { set: targetDocIds } : (targetDocIds[0] || null);\n } else if (mode === 'connect') {\n relationPayload = { connect: targetDocIds };\n } else if (mode === 'disconnect') {\n relationPayload = { disconnect: targetDocIds };\n }\n\n await strapi.documents(target.uid as any).update({\n documentId: target.documentId,\n data: {\n [attribute]: relationPayload,\n },\n });\n\n // If publishOnApproval is true and content type has draftAndPublish enabled, publish the document\n const isDraftAndPublishRel = Boolean(contentType?.options?.draftAndPublish);\n if (publishOnApproval && isDraftAndPublishRel) {\n try {\n await (strapi.documents(target.uid as any) as any).publish({\n documentId: target.documentId,\n });\n } catch (publishErr) {\n strapi.log.warn(`[bulk-updater] Failed to auto-publish ${target.uid}/${target.documentId}:`, publishErr);\n }\n }\n\n invalidateCollectionCache(target.uid);\n if (attrDef.target) {\n invalidateCollectionCache(attrDef.target);\n }\n\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: entryLabel,\n attribute,\n oldValue: { isRelation: true, documentIds: oldDocIds, display: oldDisplay },\n newValue: { isRelation: true, mode, documentIds: targetDocIds, display: newDisplay },\n status: 'updated' as const,\n message: `Successfully updated relation \"${attribute}\"`,\n };\n }\n\n let newValue = value;\n const currentVal = currentEntry?.[attribute];\n\n if (regexObj && typeof currentVal === 'string') {\n newValue = currentVal.replace(regexObj, value);\n }\n\n await strapi.documents(target.uid as any).update({\n documentId: target.documentId,\n data: {\n [attribute]: newValue,\n },\n });\n\n // If publishOnApproval is true and content type has draftAndPublish enabled, publish the document\n const isDraftAndPublishScalar = Boolean(contentType?.options?.draftAndPublish);\n if (publishOnApproval && isDraftAndPublishScalar) {\n try {\n await (strapi.documents(target.uid as any) as any).publish({\n documentId: target.documentId,\n });\n } catch (publishErr) {\n strapi.log.warn(`[bulk-updater] Failed to auto-publish ${target.uid}/${target.documentId}:`, publishErr);\n }\n }\n\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: entryLabel,\n attribute,\n oldValue: currentVal ?? null,\n newValue,\n status: 'updated' as const,\n message: `Successfully updated ${attribute}`,\n };\n } else {\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: entryLabel,\n attribute,\n status: 'skipped' as const,\n message: `Skipped: Attribute \"${attribute}\" does not exist in schema for ${displayName}`,\n };\n }\n } catch (updateErr: any) {\n return {\n uid: target.uid,\n collectionName: displayName,\n documentId: target.documentId,\n label: `Entry #${target.documentId}`,\n attribute,\n status: 'error' as const,\n message: updateErr.message,\n };\n }\n });\n\n const chunkResults = await Promise.all(chunkPromises);\n for (const res of chunkResults) {\n if (res.status === 'updated') updatedCount++;\n else if (res.status === 'skipped') skippedCount++;\n results.push(res);\n }\n }\n\n // Invalidate local in-process cache for all affected collection UIDs\n const affectedUids = new Set(targets.map((t) => t.uid));\n affectedUids.forEach((uid) => {\n invalidateCollectionCache(uid);\n });\n\n const batchSummary = {\n batchId,\n attribute,\n value,\n updatedCount,\n skippedCount,\n totalTargets: targetsMap.size,\n publishOnApproval,\n };\n\n // Stateless audit recording via consumer's declared audit schema\n await strapi.plugin('bulk-updater').service('audit').recordBatch(\n batchSummary,\n results,\n currentUser\n );\n\n return {\n success: true,\n batchId,\n attribute,\n value,\n updatedCount,\n skippedCount,\n totalTargets: targetsMap.size,\n results,\n };\n}\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => {\n // Initialize periodic runner for off-peak queue execution\n if (!runnerInterval) {\n runnerInterval = setInterval(async () => {\n if (isRunnerRunning) return;\n isRunnerRunning = true;\n try {\n const config = getExecutionWindowConfig(strapi);\n const { isBusinessHours } = checkBusinessHours(config);\n\n for (const [id, batch] of pendingBatches.entries()) {\n if (batch.status === 'queued_offpeak' && !isBusinessHours) {\n strapi.log.info(`[bulk-updater] Executing off-peak queued batch: ${id}`);\n const result = await executeBatchInternal(strapi, batch.payload, batch.createdBy, id);\n batch.status = 'executed';\n batch.executionResult = result;\n }\n }\n } catch (err) {\n strapi.log.error('[bulk-updater] Scheduled runner error:', err);\n } finally {\n isRunnerRunning = false;\n }\n }, 30000);\n }\n\n return {\n /**\n * Lightweight Schema Discovery:\n * Returns schema definitions, valid attributes, and total counts (<15KB payload)\n * Previews up to 10 entries per collection to ensure instantaneous initial paint.\n * Utilizes in-memory schema memoization and database high-watermark signature caching.\n */\n async getModels() {\n const allContentTypes = strapi.contentTypes || {};\n const auditConfig = strapi.plugin('bulk-updater').service('audit').getAuditConfig();\n const auditContentType = auditConfig?.contentType;\n\n // 1. Static schema discovery memoized in-memory\n if (!staticSchemaCache) {\n const allAttributesSet = new Set<string>();\n const systemFields = [\n 'createdAt',\n 'updatedAt',\n 'publishedAt',\n 'createdBy',\n 'updatedBy',\n 'locale',\n 'localizations',\n ];\n\n const configuredCollections = strapi.config.get(\n 'plugin::bulk-updater.collections'\n ) as string[] | string | undefined;\n\n const isWildcard =\n configuredCollections === '*' ||\n configuredCollections === 'all' ||\n (Array.isArray(configuredCollections) &&\n (configuredCollections.includes('*') || configuredCollections.includes('all')));\n\n let targetUids: string[];\n if (\n !isWildcard &&\n configuredCollections &&\n Array.isArray(configuredCollections) &&\n configuredCollections.length > 0\n ) {\n targetUids = configuredCollections.filter((uid) => uid in allContentTypes);\n } else {\n targetUids = Object.keys(allContentTypes).filter(\n (uid) =>\n uid.startsWith('api::') &&\n !uid.includes('bulk-updater') &&\n uid !== auditContentType\n );\n }\n\n const metaByUid: Record<string, any> = {};\n const allRelationsMeta: Record<string, RelationMeta> = {};\n\n for (const uid of targetUids) {\n const contentType = allContentTypes[uid];\n if (!contentType || (contentType.kind !== 'collectionType' && contentType.kind !== 'singleType')) continue;\n\n const isSingleType = contentType.kind === 'singleType';\n const rawAttrs = contentType.attributes || {};\n const relationsByAttr: Record<string, RelationMeta> = {};\n\n const validAttrs = Object.keys(rawAttrs).filter((attrKey) => {\n if (systemFields.includes(attrKey)) return false;\n if (rawAttrs[attrKey].type === 'component' || rawAttrs[attrKey].type === 'dynamiczone') return false;\n\n if (rawAttrs[attrKey].type === 'relation') {\n const rel = rawAttrs[attrKey];\n const targetUid = rel.target;\n if (!targetUid || !allContentTypes[targetUid]) return false;\n const targetInfo = allContentTypes[targetUid]?.info;\n const isMulti = rel.relation === 'oneToMany' || rel.relation === 'manyToMany';\n const relMeta: RelationMeta = {\n isRelation: true,\n relationType: rel.relation,\n targetUid,\n targetDisplayName: targetInfo?.displayName || targetUid,\n isMulti,\n };\n relationsByAttr[attrKey] = relMeta;\n allRelationsMeta[attrKey] = relMeta;\n return true;\n }\n\n return true;\n });\n\n validAttrs.forEach((attr) => allAttributesSet.add(attr));\n\n metaByUid[uid] = {\n uid,\n kind: contentType.kind,\n isSingleType,\n singularName: contentType.info?.singularName || uid,\n pluralName: contentType.info?.pluralName || uid,\n displayName: contentType.info?.displayName || uid,\n attributes: validAttrs,\n relations: relationsByAttr,\n };\n }\n\n staticSchemaCache = {\n attributes: Array.from(allAttributesSet),\n targetUids: Object.keys(metaByUid),\n metaByUid,\n relationsMeta: allRelationsMeta,\n };\n }\n\n const collections = [];\n const signatures: string[] = [];\n\n // 2. High-watermark check per collection\n for (const uid of staticSchemaCache.targetUids) {\n const meta = staticSchemaCache.metaByUid[uid];\n if (!meta) continue;\n\n const signature = await getCollectionSignature(strapi, uid);\n signatures.push(`${uid}=${signature}`);\n\n const cached = modelsCache.get(uid);\n if (cached && cached.signature === signature) {\n collections.push({\n ...meta,\n totalEntries: cached.totalCount,\n entries: cached.entries,\n });\n } else {\n let entries: any[] = [];\n let totalCount = 0;\n try {\n const relKeys = Object.keys(meta.relations || {});\n const populateOption = relKeys.length > 0\n ? relKeys.reduce((acc, k) => ({ ...acc, [k]: true }), {})\n : undefined;\n\n if (meta.isSingleType) {\n const singleDoc: any = await (strapi.documents(uid as any) as any).findFirst({\n populate: populateOption,\n });\n if (singleDoc) {\n entries = [singleDoc];\n totalCount = 1;\n }\n } else {\n totalCount = await strapi.documents(uid as any).count();\n entries = await strapi.documents(uid as any).findMany({\n limit: 10,\n sort: { createdAt: 'desc' },\n populate: populateOption,\n });\n }\n } catch (err) {\n strapi.log.error(`[bulk-updater] Failed to fetch entries for ${uid}:`, err);\n entries = [];\n totalCount = 0;\n }\n\n const formattedEntries = (entries || []).map((entry: any) => ({\n id: entry.id,\n documentId: entry.documentId,\n ...entry,\n }));\n\n modelsCache.set(uid, {\n signature,\n totalCount,\n entries: formattedEntries,\n });\n\n collections.push({\n ...meta,\n totalEntries: totalCount,\n entries: formattedEntries,\n });\n }\n }\n\n // Get license status\n const licenseService = strapi.plugin('bulk-updater').service('license');\n const license = await licenseService.getLicenseStatus();\n\n const executionConfig = getExecutionWindowConfig(strapi);\n const windowStatus = checkBusinessHours(executionConfig);\n const pendingCount = Array.from(pendingBatches.values()).filter(\n (b) => b.status === 'pending_approval' || b.status === 'queued_offpeak'\n ).length;\n\n const etag = `W/\"models-${crypto.createHash('md5').update(`${signatures.join('|')}:${license.tier}:${pendingCount}:${windowStatus.isBusinessHours}:${windowStatus.currentServerTime}:${windowStatus.timeZone}`).digest('hex')}\"`;\n\n return {\n success: true,\n attributes: staticSchemaCache.attributes,\n collections,\n relationsMeta: staticSchemaCache.relationsMeta,\n license,\n executionWindow: {\n ...executionConfig,\n ...windowStatus,\n },\n pendingBatchesCount: pendingCount,\n auditEnabled: Boolean(auditConfig?.enabled),\n etag,\n };\n },\n\n /**\n * On-Demand Paginated Collection Entries:\n * Supports server-side pagination (limit / start) and database query pushdown for filters.\n * Cached in-process with cross-pod high-watermark signature validation.\n */\n async getCollectionEntries(\n uid: string,\n {\n page = 1,\n pageSize = 10,\n attribute,\n filterOperator,\n filterQuery,\n search,\n sortField,\n sortOrder = 'desc',\n }: {\n page?: number;\n pageSize?: number;\n attribute?: string;\n filterOperator?: string;\n filterQuery?: string;\n search?: string;\n sortField?: string;\n sortOrder?: 'asc' | 'desc';\n }\n ) {\n const contentType = strapi.contentTypes[uid];\n if (!contentType) {\n throw new Error(`Collection with UID \"${uid}\" does not exist.`);\n }\n\n const limit = Math.min(Math.max(Number(pageSize) || 10, 1), 100);\n const currentPage = Math.max(Number(page) || 1, 1);\n const start = (currentPage - 1) * limit;\n\n const isSingleType = contentType.kind === 'singleType';\n\n // 1. Signature-based caching verification\n const signature = await getCollectionSignature(strapi, uid);\n const cacheKey = `${uid}:${limit}:${start}:${attribute || ''}:${filterOperator || ''}:${filterQuery || ''}:${search || ''}:${sortField || ''}:${sortOrder}`;\n const cached = queryCache.get(cacheKey);\n\n const etag = `W/\"entry-${crypto.createHash('md5').update(`${cacheKey}:${signature}`).digest('hex')}\"`;\n\n if (cached && cached.signature === signature && Date.now() - cached.timestamp < CACHE_TTL_MS) {\n return {\n ...cached.data,\n cached: true,\n etag,\n };\n }\n\n let result: any;\n\n if (isSingleType) {\n const singleDoc: any = await (strapi.documents(uid as any) as any).findFirst();\n result = {\n uid,\n displayName: contentType.info?.displayName || uid,\n isSingleType: true,\n entries: singleDoc ? [singleDoc] : [],\n pagination: {\n page: 1,\n pageSize: 1,\n total: singleDoc ? 1 : 0,\n pageCount: singleDoc ? 1 : 0,\n },\n };\n } else {\n // Build database filters pushdown\n const filters: Record<string, any> = {};\n\n if (attribute && filterOperator && filterQuery !== undefined && filterQuery !== '') {\n switch (filterOperator) {\n case 'equals':\n filters[attribute] = { $eq: filterQuery };\n break;\n case 'notEquals':\n filters[attribute] = { $ne: filterQuery };\n break;\n case 'contains':\n filters[attribute] = { $containsi: filterQuery };\n break;\n case 'notContains':\n filters[attribute] = { $notContainsi: filterQuery };\n break;\n case 'startsWith':\n filters[attribute] = { $startsWith: filterQuery };\n break;\n case 'endsWith':\n filters[attribute] = { $endsWith: filterQuery };\n break;\n case 'empty':\n filters[attribute] = { $null: true };\n break;\n case 'notEmpty':\n filters[attribute] = { $notNull: true };\n break;\n }\n }\n\n if (search && search.trim() !== '') {\n const stringAttrs = Object.keys(contentType.attributes || {}).filter(\n (k) =>\n contentType.attributes[k].type === 'string' ||\n contentType.attributes[k].type === 'text' ||\n contentType.attributes[k].type === 'email'\n );\n\n if (stringAttrs.length > 0) {\n filters.$or = stringAttrs.slice(0, 5).map((attr) => ({\n [attr]: { $containsi: search.trim() },\n }));\n }\n }\n\n const sortClause: Record<string, 'asc' | 'desc'> = {};\n if (sortField && contentType.attributes?.[sortField]) {\n sortClause[sortField] = sortOrder;\n } else {\n sortClause['createdAt'] = 'desc';\n }\n\n const relationAttrs = Object.keys(contentType.attributes || {}).filter(\n (k) => contentType.attributes[k].type === 'relation'\n );\n const populateOption = relationAttrs.length > 0\n ? relationAttrs.reduce((acc, k) => ({ ...acc, [k]: true }), {})\n : undefined;\n\n const [entries, totalEntries] = await Promise.all([\n strapi.documents(uid as any).findMany({\n start,\n limit,\n filters: Object.keys(filters).length > 0 ? filters : undefined,\n sort: sortClause,\n populate: populateOption,\n }),\n strapi.documents(uid as any).count({\n filters: Object.keys(filters).length > 0 ? filters : undefined,\n }),\n ]);\n\n const totalPages = Math.ceil(totalEntries / limit);\n\n result = {\n uid,\n displayName: contentType.info?.displayName || uid,\n isSingleType: false,\n entries: (entries || []).map((entry: any) => ({\n id: entry.id,\n documentId: entry.documentId,\n ...entry,\n })),\n pagination: {\n page: currentPage,\n pageSize: limit,\n total: totalEntries,\n pageCount: totalPages,\n },\n };\n }\n\n // Store in queryCache with bounding limit\n if (queryCache.size >= MAX_QUERY_CACHE_SIZE) {\n const toDelete = Array.from(queryCache.keys()).slice(0, 100);\n toDelete.forEach((k) => queryCache.delete(k));\n }\n queryCache.set(cacheKey, { signature, data: result, timestamp: Date.now() });\n\n return {\n ...result,\n cached: false,\n etag,\n };\n },\n\n /**\n * Get available target entities for a relation field:\n * Cached in-process with cross-pod signature validation.\n */\n async getRelationOptions(targetUid: string) {\n if (!targetUid || !strapi.contentTypes[targetUid]) {\n throw new Error(`Target content type \"${targetUid}\" does not exist.`);\n }\n\n const cacheKey = `rel_options:${targetUid}`;\n const signature = await getCollectionSignature(strapi, targetUid);\n const cached = queryCache.get(cacheKey);\n if (cached && cached.signature === signature) {\n return cached.data;\n }\n\n const docs = await strapi.documents(targetUid as any).findMany({\n limit: 100,\n sort: { createdAt: 'desc' },\n });\n\n const options = (docs || []).map((doc: any) => {\n const label = doc.name || doc.title || doc.email || doc.username || `Entry #${doc.id || doc.documentId}`;\n return {\n documentId: doc.documentId,\n label,\n };\n });\n\n const result = { targetUid, options };\n queryCache.set(cacheKey, { signature, data: result, timestamp: Date.now() });\n return result;\n },\n\n /**\n * Batch Update API with Maker-Checker and Off-Peak Scheduling:\n */\n async batchUpdate(\n {\n attribute,\n value,\n selectedEntries = [],\n selectedCollections = [],\n isRegex = false,\n regexPattern = '',\n executionMode = 'immediate',\n makerNotes = '',\n forceImmediate = false,\n publishOnApproval = true,\n }: {\n attribute: string;\n value: string;\n selectedEntries?: Array<{ uid: string; documentId: string }>;\n selectedCollections?: string[];\n isRegex?: boolean;\n regexPattern?: string;\n executionMode?: 'immediate' | 'queue_offpeak' | 'maker_checker';\n makerNotes?: string;\n forceImmediate?: boolean;\n publishOnApproval?: boolean;\n },\n currentUser?: any\n ) {\n if (!attribute) {\n throw new Error('Attribute name is required.');\n }\n\n const licenseService = strapi.plugin('bulk-updater').service('license');\n const license = await licenseService.getLicenseStatus();\n const isEnterprise = license.tier === 'enterprise';\n\n const executionConfig = getExecutionWindowConfig(strapi);\n const { isBusinessHours, policyMessage } = checkBusinessHours(executionConfig);\n\n // 1. Enterprise Maker-Checker Flow:\n if (executionMode === 'maker_checker') {\n const summary = await computeTargetsSummary(strapi, { selectedCollections, selectedEntries });\n if (summary.totalTargets === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const batch: PendingBatch = {\n id: batchId,\n createdAt: new Date().toISOString(),\n createdBy: currentUser\n ? {\n id: currentUser.id,\n email: currentUser.email,\n firstname: currentUser.firstname,\n lastname: currentUser.lastname,\n username: currentUser.username,\n }\n : { email: 'admin@system.local' },\n payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n summary: {\n attribute,\n value,\n totalTargets: summary.totalTargets,\n collectionBreakdown: summary.collectionBreakdown,\n publishOnApproval,\n },\n status: 'pending_approval',\n executionMode: 'maker_checker',\n makerNotes,\n };\n\n pendingBatches.set(batchId, batch);\n\n return {\n success: true,\n pending: true,\n batchId,\n status: 'pending_approval',\n message: 'Batch submitted for Checker authorization (4-Eyes Maker-Checker Workflow).',\n batch,\n };\n }\n\n // 2. Off-Peak Queued Batch:\n if (executionMode === 'queue_offpeak') {\n const summary = await computeTargetsSummary(strapi, { selectedCollections, selectedEntries });\n if (summary.totalTargets === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const batch: PendingBatch = {\n id: batchId,\n createdAt: new Date().toISOString(),\n createdBy: currentUser\n ? {\n id: currentUser.id,\n email: currentUser.email,\n firstname: currentUser.firstname,\n lastname: currentUser.lastname,\n username: currentUser.username,\n }\n : { email: 'admin@system.local' },\n payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n summary: {\n attribute,\n value,\n totalTargets: summary.totalTargets,\n collectionBreakdown: summary.collectionBreakdown,\n publishOnApproval,\n },\n status: 'queued_offpeak',\n executionMode: 'queue_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n makerNotes,\n };\n\n pendingBatches.set(batchId, batch);\n\n return {\n success: true,\n pending: true,\n batchId,\n status: 'queued_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n message: `Batch queued for server off-peak maintenance window (${executionConfig.offPeakStartTime}).`,\n batch,\n };\n }\n\n // 3. Immediate execution request with Server Business Hours enforcement:\n if (executionConfig.enabled && executionConfig.autoQueueDuringBusinessHours && isBusinessHours && !forceImmediate) {\n const summary = await computeTargetsSummary(strapi, { selectedCollections, selectedEntries });\n if (summary.totalTargets === 0) {\n throw new Error('No collections or entries were selected for update.');\n }\n\n const batchId = `batch_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n const batch: PendingBatch = {\n id: batchId,\n createdAt: new Date().toISOString(),\n createdBy: currentUser\n ? {\n id: currentUser.id,\n email: currentUser.email,\n firstname: currentUser.firstname,\n lastname: currentUser.lastname,\n username: currentUser.username,\n }\n : { email: 'admin@system.local' },\n payload: { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n summary: {\n attribute,\n value,\n totalTargets: summary.totalTargets,\n collectionBreakdown: summary.collectionBreakdown,\n publishOnApproval,\n },\n status: 'queued_offpeak',\n executionMode: 'queue_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n makerNotes: 'Automatically queued due to peak business hours restriction.',\n };\n\n pendingBatches.set(batchId, batch);\n\n return {\n success: true,\n pending: true,\n batchId,\n status: 'queued_offpeak',\n scheduledFor: executionConfig.offPeakStartTime,\n message: policyMessage,\n batch,\n };\n }\n\n // 4. Immediate execution\n return await executeBatchInternal(\n strapi,\n { attribute, value, selectedEntries, selectedCollections, isRegex, regexPattern, publishOnApproval },\n currentUser\n );\n },\n\n /**\n * Get Pending & Queued Batches\n */\n getPendingBatches() {\n const executionConfig = getExecutionWindowConfig(strapi);\n const windowStatus = checkBusinessHours(executionConfig);\n const batches = Array.from(pendingBatches.values()).sort(\n (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()\n );\n\n return {\n success: true,\n batches,\n executionWindow: {\n ...executionConfig,\n ...windowStatus,\n },\n };\n },\n\n /**\n * Checker Approves Batch\n */\n async approveBatch(batchId: string, checkerUser: any, options: { executeImmediately?: boolean } = { executeImmediately: true }) {\n const batch = pendingBatches.get(batchId);\n if (!batch) {\n throw new Error(`Pending batch ${batchId} not found.`);\n }\n if (batch.status !== 'pending_approval') {\n throw new Error(`Batch ${batchId} is not pending approval (current status: ${batch.status}).`);\n }\n\n batch.approval = {\n checkerUser: checkerUser\n ? {\n id: checkerUser.id,\n email: checkerUser.email,\n firstname: checkerUser.firstname,\n lastname: checkerUser.lastname,\n username: checkerUser.username,\n }\n : { email: 'checker@system.local' },\n decisionDate: new Date().toISOString(),\n };\n\n const config = getExecutionWindowConfig(strapi);\n const { isBusinessHours } = checkBusinessHours(config);\n\n if (options.executeImmediately || !isBusinessHours) {\n batch.status = 'approved';\n const result = await executeBatchInternal(strapi, batch.payload, checkerUser, batch.id);\n batch.status = 'executed';\n batch.executionResult = result;\n return {\n success: true,\n batchId,\n status: 'executed',\n message: 'Batch approved and executed successfully by Checker.',\n batch,\n result,\n };\n } else {\n batch.status = 'approved';\n return {\n success: true,\n batchId,\n status: 'approved',\n message: `Batch approved by Checker. Queued for execution during off-peak window (${config.offPeakStartTime}).`,\n batch,\n };\n }\n },\n\n /**\n * Checker Rejects Batch\n */\n rejectBatch(batchId: string, checkerUser: any, reason: string = 'Rejected by Checker') {\n const batch = pendingBatches.get(batchId);\n if (!batch) {\n throw new Error(`Pending batch ${batchId} not found.`);\n }\n batch.status = 'rejected';\n batch.approval = {\n checkerUser: checkerUser\n ? {\n id: checkerUser.id,\n email: checkerUser.email,\n firstname: checkerUser.firstname,\n lastname: checkerUser.lastname,\n username: checkerUser.username,\n }\n : { email: 'checker@system.local' },\n decisionDate: new Date().toISOString(),\n rejectionReason: reason,\n };\n\n return {\n success: true,\n batchId,\n status: 'rejected',\n message: `Batch rejected by Checker: ${reason}`,\n batch,\n };\n },\n\n /**\n * Run Batch Immediately\n */\n async executeNow(batchId: string, currentUser: any) {\n const batch = pendingBatches.get(batchId);\n if (!batch) {\n throw new Error(`Pending batch ${batchId} not found.`);\n }\n if (batch.status === 'executed') {\n throw new Error(`Batch ${batchId} has already been executed.`);\n }\n\n const result = await executeBatchInternal(strapi, batch.payload, currentUser, batch.id);\n batch.status = 'executed';\n batch.executionResult = result;\n\n return {\n success: true,\n batchId,\n status: 'executed',\n message: 'Batch executed immediately.',\n batch,\n result,\n };\n },\n\n invalidateCache(uid?: string) {\n if (uid) {\n invalidateCollectionCache(uid);\n } else {\n queryCache.clear();\n modelsCache.clear();\n staticSchemaCache = null;\n }\n },\n };\n};\n","import type { Core } from '@strapi/strapi';\n\nexport interface LicenseStatus {\n isPro: boolean;\n tier: 'free' | 'pro' | 'enterprise';\n licenseKey: string | null;\n licensedTo: string | null;\n activatedAt: string | null;\n features: string[];\n}\n\nconst DEFAULT_FREE_STATUS: LicenseStatus = {\n isPro: false,\n tier: 'free',\n licenseKey: null,\n licensedTo: null,\n activatedAt: null,\n features: [\n 'Basic attribute batch updates',\n 'Schema compatibility checks',\n 'Interactive target selection',\n ],\n};\n\nconst PRO_FEATURES = [\n 'Basic attribute batch updates',\n 'Schema compatibility checks',\n 'Interactive target selection',\n 'Regex find & replace within attributes',\n 'Cross-relation and dynamic zone updates',\n 'Audit log export (CSV / JSON)',\n 'Unlimited collection batch operations',\n 'Priority enterprise support',\n];\n\nconst ENTERPRISE_FEATURES = [\n ...PRO_FEATURES,\n 'Maker-Checker Approval Workflow (4-Eyes Principle)',\n 'Server-Enforced Off-Peak Maintenance Window',\n 'Scheduled batch queuing during business hours',\n];\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async getStore() {\n return strapi.store({\n type: 'plugin',\n name: 'bulk-updater',\n key: 'license',\n });\n },\n\n async getLicenseStatus(): Promise<LicenseStatus> {\n // Check developer config override in config/plugins.ts\n const isConfigEnterprise = strapi.config.get('plugin::bulk-updater.makerChecker.enabled');\n\n const store = await this.getStore();\n const stored = await store.get({});\n if (!stored || !stored.licenseKey) {\n if (isConfigEnterprise) {\n return {\n isPro: true,\n tier: 'enterprise',\n licenseKey: 'CONFIG-OVERRIDE',\n licensedTo: 'Enterprise Developer Config',\n activatedAt: new Date().toISOString(),\n features: ENTERPRISE_FEATURES,\n };\n }\n return DEFAULT_FREE_STATUS;\n }\n if (isConfigEnterprise) {\n return {\n ...stored,\n isPro: true,\n tier: 'enterprise',\n features: Array.from(new Set([...(stored.features || []), ...ENTERPRISE_FEATURES])),\n };\n }\n return stored;\n },\n\n async verifyAndActivateLicense(licenseKey: string): Promise<{ success: boolean; license?: LicenseStatus; error?: string }> {\n if (!licenseKey || typeof licenseKey !== 'string') {\n return { success: false, error: 'A valid license key is required.' };\n }\n\n const trimmed = licenseKey.trim();\n\n // Verification Logic:\n // Supports live LemonSqueezy / Gumroad / Keygen keys or offline signature/prefix keys\n // For demonstration and production readiness, keys with prefix PRO- or ENTERPRISE- are valid Pro licenses\n const isEnterprise = trimmed.toUpperCase().startsWith('ENT-') || trimmed.toUpperCase().startsWith('ENTERPRISE-');\n const isPro = trimmed.toUpperCase().startsWith('PRO-') || isEnterprise;\n\n if (!isPro) {\n return {\n success: false,\n error: 'Invalid license key. License keys must start with PRO- or ENTERPRISE-.',\n };\n }\n\n const tier = isEnterprise ? 'enterprise' : 'pro';\n const status: LicenseStatus = {\n isPro: true,\n tier,\n licenseKey: trimmed,\n licensedTo: `Licensed Customer (${tier.toUpperCase()})`,\n activatedAt: new Date().toISOString(),\n features: isEnterprise ? ENTERPRISE_FEATURES : PRO_FEATURES,\n };\n\n const store = await this.getStore();\n await store.set({ value: status });\n\n return {\n success: true,\n license: status,\n };\n },\n\n async deactivateLicense(): Promise<LicenseStatus> {\n const store = await this.getStore();\n await store.set({ value: DEFAULT_FREE_STATUS });\n return DEFAULT_FREE_STATUS;\n },\n});\n","import type { Core } from '@strapi/strapi';\n\nexport interface AuditConfig {\n enabled: boolean;\n mode?: 'custom' | 'default' | 'enterprise_native' | 'disabled';\n contentType?: string;\n fields?: Record<string, string>;\n isEnterpriseNativeAvailable?: boolean;\n error?: string;\n}\n\nconst DEFAULT_FIELDS: Record<string, string> = {\n batchId: 'batchId',\n attribute: 'attribute',\n value: 'value',\n userEmail: 'userEmail',\n userName: 'userName',\n updatedCount: 'updatedCount',\n skippedCount: 'skippedCount',\n totalTargets: 'totalTargets',\n diffs: 'diffs',\n isRolledBack: 'isRolledBack',\n rolledBackAt: 'rolledBackAt',\n rolledBackBy: 'rolledBackBy',\n executedAt: 'executedAt',\n};\n\nconst DEFAULT_CANDIDATE_TYPES = [\n 'api::audit-log.audit-log',\n 'api::audit.audit',\n 'api::bulk-audit-log.bulk-audit-log',\n];\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n /**\n * Detects whether Strapi Enterprise Native Audit Log service is available.\n */\n getEnterpriseAuditService() {\n try {\n const svc =\n (strapi as any).service?.('admin::audit-logs') ||\n (strapi as any).admin?.services?.['audit-logs'];\n return svc || null;\n } catch {\n return null;\n }\n },\n\n /**\n * Resolves the audit configuration:\n * 1. Explicit consumer custom schema (full declarative control)\n * 2. Zero-config convention auto-detection (api::audit-log.audit-log)\n * 3. Strapi Enterprise native audit log bridge\n */\n getAuditConfig(): AuditConfig {\n const rawConfig = strapi.config.get('plugin::bulk-updater.audit') as any;\n const eeService = this.getEnterpriseAuditService();\n const isEnterpriseNativeAvailable = Boolean(\n eeService && typeof eeService.save === 'function'\n );\n\n // If consumer explicitly disabled audit\n if (rawConfig && rawConfig.enabled === false) {\n return {\n enabled: false,\n mode: 'disabled',\n isEnterpriseNativeAvailable,\n };\n }\n\n // Tier 1: Explicit consumer custom schema\n if (rawConfig?.contentType) {\n const { contentType, fields } = rawConfig;\n const schema = strapi.contentTypes[contentType];\n if (!schema) {\n return {\n enabled: false,\n mode: 'disabled',\n isEnterpriseNativeAvailable,\n error: `Declared audit contentType \"${contentType}\" does not exist in Strapi schema registry.`,\n };\n }\n\n // Check fields or fallback to DEFAULT_FIELDS\n const resolvedFields = { ...DEFAULT_FIELDS, ...(fields || {}) };\n return {\n enabled: true,\n mode: 'custom',\n contentType,\n fields: resolvedFields,\n isEnterpriseNativeAvailable,\n };\n }\n\n // Tier 2: Zero-config auto-discovery of default audit collection\n for (const candidate of DEFAULT_CANDIDATE_TYPES) {\n if (strapi.contentTypes[candidate]) {\n return {\n enabled: true,\n mode: 'default',\n contentType: candidate,\n fields: DEFAULT_FIELDS,\n isEnterpriseNativeAvailable,\n };\n }\n }\n\n // Tier 3: Strapi Enterprise native audit logs\n if (isEnterpriseNativeAvailable) {\n return {\n enabled: true,\n mode: 'enterprise_native',\n isEnterpriseNativeAvailable: true,\n };\n }\n\n // Tier 4: Audit disabled / unconfigured\n return {\n enabled: false,\n mode: 'disabled',\n isEnterpriseNativeAvailable: false,\n error:\n 'No audit collection configured or detected. To enable rollback, provide a collection or use Strapi Enterprise.',\n };\n },\n\n /**\n * Optionally writes an audit event directly to Strapi Enterprise's native Audit Logs table.\n */\n async recordToEnterpriseNative(\n action: string,\n payload: Record<string, any>,\n user?: any\n ) {\n try {\n const eeService = this.getEnterpriseAuditService();\n if (eeService && typeof eeService.save === 'function') {\n await eeService.save({\n action,\n date: new Date(),\n userId: user?.id,\n payload,\n });\n strapi.log.info(\n `[bulk-updater] Successfully dispatched event to Strapi Enterprise Audit Log: ${action}`\n );\n }\n } catch (err: any) {\n strapi.log.warn(\n `[bulk-updater] Note: Could not dispatch to Strapi Enterprise native audit log: ${err.message}`\n );\n }\n },\n\n /**\n * Records a batch execution:\n * - Dispatches to Strapi Enterprise native audit log if available\n * - Records to consumer or auto-detected collection type for one-click rollback\n */\n async recordBatch(\n batchSummary: {\n batchId: string;\n attribute: string;\n value: string;\n updatedCount: number;\n skippedCount: number;\n totalTargets: number;\n },\n results: any[],\n user?: any\n ) {\n const config = this.getAuditConfig();\n\n // 1. Always attempt Strapi Enterprise native logging if available\n await this.recordToEnterpriseNative(\n 'bulk-updater.batch-update',\n {\n batchId: batchSummary.batchId,\n attribute: batchSummary.attribute,\n value: batchSummary.value,\n updatedCount: batchSummary.updatedCount,\n skippedCount: batchSummary.skippedCount,\n totalTargets: batchSummary.totalTargets,\n },\n user\n );\n\n // 2. Persist to rollback-capable collection type if available\n if (!config.enabled || !config.contentType || !config.fields) {\n return null;\n }\n\n const f = config.fields;\n const mappedData: any = {};\n\n if (f.batchId) mappedData[f.batchId] = batchSummary.batchId;\n if (f.attribute) mappedData[f.attribute] = batchSummary.attribute;\n if (f.value) mappedData[f.value] = batchSummary.value;\n if (f.userEmail) mappedData[f.userEmail] = user?.email || 'admin@strapi.io';\n if (f.userName) {\n mappedData[f.userName] = user?.firstname\n ? `${user.firstname} ${user.lastname || ''}`.trim()\n : user?.email || 'Admin';\n }\n if (f.updatedCount) mappedData[f.updatedCount] = batchSummary.updatedCount;\n if (f.skippedCount) mappedData[f.skippedCount] = batchSummary.skippedCount;\n if (f.totalTargets) mappedData[f.totalTargets] = batchSummary.totalTargets;\n if (f.diffs) mappedData[f.diffs] = results;\n if (f.isRolledBack) mappedData[f.isRolledBack] = false;\n if (f.executedAt) mappedData[f.executedAt] = new Date().toISOString();\n\n try {\n const created = await strapi.documents(config.contentType as any).create({\n data: mappedData,\n });\n return created;\n } catch (err: any) {\n strapi.log.error(`[bulk-updater] Failed to record audit log: ${err.message}`);\n return null;\n }\n },\n\n /**\n * Fetches audit history for the UI\n */\n async getHistory() {\n const config = this.getAuditConfig();\n\n if (config.mode === 'enterprise_native' && !config.contentType) {\n return {\n enabled: true,\n mode: 'enterprise_native',\n message:\n 'Strapi Enterprise Native Audit Logs active. Batch events are recorded directly to Strapi Settings > Audit Logs. (To enable one-click document rollback, declare or add an audit-log collection).',\n history: [],\n };\n }\n\n if (!config.enabled || !config.contentType || !config.fields) {\n return {\n enabled: false,\n message:\n config.error ||\n 'Audit trail is disabled. To enable audit logs & rollback, declare an audit schema in config/plugins.ts or create api::audit-log.audit-log.',\n history: [],\n };\n }\n\n try {\n const records: any[] = await strapi.documents(config.contentType as any).findMany({\n sort: { createdAt: 'desc' } as any,\n });\n\n const f = config.fields;\n const normalized = (records || []).map((r: any) => ({\n id: r.id,\n documentId: r.documentId,\n batchId: r[f.batchId] || r.documentId,\n attribute: r[f.attribute],\n value: r[f.value],\n userEmail: r[f.userEmail] || 'admin@strapi.io',\n userName: r[f.userName] || 'Admin',\n updatedCount: r[f.updatedCount] || 0,\n skippedCount: r[f.skippedCount] || 0,\n totalTargets: r[f.totalTargets] || 0,\n diffs: typeof r[f.diffs] === 'string' ? JSON.parse(r[f.diffs]) : r[f.diffs] || [],\n isRolledBack: Boolean(r[f.isRolledBack]),\n rolledBackAt: r[f.rolledBackAt] || null,\n rolledBackBy: r[f.rolledBackBy] || null,\n executedAt: r[f.executedAt] || r.createdAt,\n }));\n\n return {\n enabled: true,\n mode: config.mode,\n contentType: config.contentType,\n isEnterpriseNativeAvailable: config.isEnterpriseNativeAvailable,\n history: normalized,\n };\n } catch (err: any) {\n return {\n enabled: false,\n message: `Failed to read audit history: ${err.message}`,\n history: [],\n };\n }\n },\n\n /**\n * Performs deterministic rollback of a batch\n */\n async rollbackBatch(batchId: string, currentUser?: any) {\n const config = this.getAuditConfig();\n if (!config.enabled || !config.contentType || !config.fields) {\n throw new Error('Audit trail is not configured or disabled.');\n }\n\n const f = config.fields;\n const records: any[] = await strapi.documents(config.contentType as any).findMany({\n filters: {\n [f.batchId]: { $eq: batchId },\n } as any,\n });\n\n const record = records?.[0];\n if (!record) {\n throw new Error(`Batch record with ID \"${batchId}\" was not found in audit trail.`);\n }\n\n if (record[f.isRolledBack]) {\n throw new Error(`Batch \"${batchId}\" has already been rolled back.`);\n }\n\n const diffs =\n typeof record[f.diffs] === 'string'\n ? JSON.parse(record[f.diffs])\n : record[f.diffs] || [];\n\n let revertedCount = 0;\n const rollbackDetails = [];\n\n for (const item of diffs) {\n if (item.status === 'updated' && item.uid && item.documentId && item.attribute) {\n try {\n const isRel = item.oldValue && typeof item.oldValue === 'object' && item.oldValue.isRelation;\n let rollbackPayload: any;\n\n if (isRel) {\n const oldDocIds = Array.isArray(item.oldValue.documentIds) ? item.oldValue.documentIds : [];\n rollbackPayload = { set: oldDocIds };\n } else {\n rollbackPayload = item.oldValue;\n }\n\n await strapi.documents(item.uid as any).update({\n documentId: item.documentId,\n data: {\n [item.attribute]: rollbackPayload,\n },\n });\n\n const ct = strapi.contentTypes[item.uid];\n if (ct?.options?.draftAndPublish) {\n try {\n await (strapi.documents(item.uid as any) as any).publish({\n documentId: item.documentId,\n });\n } catch {}\n }\n\n try {\n strapi.plugin('bulk-updater').service('bulkUpdater').invalidateCollectionCache(item.uid);\n } catch {}\n\n revertedCount++;\n rollbackDetails.push({\n uid: item.uid,\n documentId: item.documentId,\n label: item.label,\n attribute: item.attribute,\n revertedTo: item.oldValue,\n status: 'reverted',\n });\n } catch (err: any) {\n rollbackDetails.push({\n uid: item.uid,\n documentId: item.documentId,\n label: item.label,\n status: 'error',\n message: err.message,\n });\n }\n }\n }\n\n const updateData: any = {};\n if (f.isRolledBack) updateData[f.isRolledBack] = true;\n if (f.rolledBackAt) updateData[f.rolledBackAt] = new Date().toISOString();\n if (f.rolledBackBy) {\n updateData[f.rolledBackBy] = currentUser?.email || 'Admin';\n }\n\n await strapi.documents(config.contentType as any).update({\n documentId: record.documentId,\n data: updateData,\n });\n\n // Invalidate affected collection caches\n try {\n const affectedUids = new Set(rollbackDetails.map((d: any) => d.uid));\n const bulkService = strapi.plugin('bulk-updater').service('bulkUpdater');\n affectedUids.forEach((uid: any) => {\n if (uid && bulkService?.invalidateCache) {\n bulkService.invalidateCache(uid);\n }\n });\n } catch {}\n\n // Mirror rollback event to Strapi Enterprise native audit log if available\n await this.recordToEnterpriseNative(\n 'bulk-updater.rollback',\n {\n batchId,\n revertedCount,\n },\n currentUser\n );\n\n return {\n success: true,\n batchId,\n revertedCount,\n details: rollbackDetails,\n };\n },\n});\n","import routes from './routes';\nimport bulkUpdaterController from './controllers/bulk-updater';\nimport licenseController from './controllers/license';\nimport auditController from './controllers/audit';\nimport bulkUpdaterService from './services/bulk-updater';\nimport licenseService from './services/license';\nimport auditService from './services/audit';\n\nexport default {\n register({ strapi }: { strapi: any }) {},\n bootstrap({ strapi }: { strapi: any }) {},\n routes,\n controllers: {\n bulkUpdater: bulkUpdaterController,\n license: licenseController,\n audit: auditController,\n },\n services: {\n bulkUpdater: bulkUpdaterService,\n license: licenseService,\n audit: auditService,\n },\n};\n"],"names":["licenseService"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-plugin-bulk-updater",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Bulk update attributes and relationships across multiple collection types in Strapi v5 with schema validation, maker-checker governance, off-peak scheduling, and 3-tier audit rollback.",
|
|
5
5
|
"strapi": {
|
|
6
6
|
"displayName": "Bulk Attribute & Relation Updater",
|