strapi-plugin-bulk-updater 1.5.0 → 1.5.3
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/_chunks/{BulkUpdater-CSmtXjbM.js → BulkUpdater-DMkx6dLf.js} +134 -136
- package/dist/_chunks/BulkUpdater-DMkx6dLf.js.map +1 -0
- package/dist/_chunks/{BulkUpdater-By9Dd_YF.mjs → BulkUpdater-DUfv52ng.mjs} +134 -136
- package/dist/_chunks/BulkUpdater-DUfv52ng.mjs.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +201 -40
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +201 -40
- package/dist/server/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/_chunks/BulkUpdater-By9Dd_YF.mjs.map +0 -1
- package/dist/_chunks/BulkUpdater-CSmtXjbM.js.map +0 -1
package/dist/server/index.mjs
CHANGED
|
@@ -362,10 +362,10 @@ const bulkUpdaterController = ({ strapi }) => ({
|
|
|
362
362
|
ctx.set("ETag", data.etag);
|
|
363
363
|
ctx.set("Cache-Control", "private, no-cache");
|
|
364
364
|
}
|
|
365
|
-
ctx.body = { success: true, ...data };
|
|
365
|
+
ctx.body = { success: true, status: "success", ...data };
|
|
366
366
|
} catch (err) {
|
|
367
367
|
ctx.status = 500;
|
|
368
|
-
ctx.body = { success: false, error: err.message };
|
|
368
|
+
ctx.body = { success: false, status: "error", error: err.message };
|
|
369
369
|
}
|
|
370
370
|
},
|
|
371
371
|
async getRelationOptions(ctx) {
|
|
@@ -967,6 +967,7 @@ function formatEntryRecord(entry) {
|
|
|
967
967
|
const formatted = {
|
|
968
968
|
id: entry.id,
|
|
969
969
|
documentId: entry.documentId,
|
|
970
|
+
status: entry.status !== void 0 ? entry.status : void 0,
|
|
970
971
|
...entry
|
|
971
972
|
};
|
|
972
973
|
if (entry.updatedAt !== void 0 && formatted.updated_at === void 0) {
|
|
@@ -989,6 +990,119 @@ function formatEntryRecord(entry) {
|
|
|
989
990
|
}
|
|
990
991
|
return formatted;
|
|
991
992
|
}
|
|
993
|
+
async function populateDocumentLifecycleStatus(strapi, uid, entries) {
|
|
994
|
+
if (!entries || !Array.isArray(entries) || entries.length === 0) {
|
|
995
|
+
return entries;
|
|
996
|
+
}
|
|
997
|
+
const contentType = strapi?.contentTypes?.[uid];
|
|
998
|
+
if (!contentType) {
|
|
999
|
+
return entries;
|
|
1000
|
+
}
|
|
1001
|
+
const hasDraftAndPublish = Boolean(contentType.options?.draftAndPublish);
|
|
1002
|
+
if (!hasDraftAndPublish) {
|
|
1003
|
+
for (const entry of entries) {
|
|
1004
|
+
if (entry && typeof entry === "object") {
|
|
1005
|
+
entry.status = "published";
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
return entries;
|
|
1009
|
+
}
|
|
1010
|
+
const hasSlugInSchema = Boolean(contentType.attributes?.slug);
|
|
1011
|
+
const docIds = [];
|
|
1012
|
+
const slugs = [];
|
|
1013
|
+
for (const entry of entries) {
|
|
1014
|
+
if (!entry || typeof entry !== "object") continue;
|
|
1015
|
+
if (entry.documentId) {
|
|
1016
|
+
docIds.push(String(entry.documentId));
|
|
1017
|
+
} else if (hasSlugInSchema && entry.slug) {
|
|
1018
|
+
slugs.push(String(entry.slug));
|
|
1019
|
+
} else {
|
|
1020
|
+
entry.status = "unknown";
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
if (docIds.length === 0 && slugs.length === 0) {
|
|
1024
|
+
return entries;
|
|
1025
|
+
}
|
|
1026
|
+
let filterClause;
|
|
1027
|
+
if (docIds.length > 0 && slugs.length > 0) {
|
|
1028
|
+
filterClause = {
|
|
1029
|
+
$or: [
|
|
1030
|
+
{ documentId: { $in: docIds } },
|
|
1031
|
+
{ slug: { $in: slugs } }
|
|
1032
|
+
]
|
|
1033
|
+
};
|
|
1034
|
+
} else if (docIds.length > 0) {
|
|
1035
|
+
filterClause = { documentId: { $in: docIds } };
|
|
1036
|
+
} else {
|
|
1037
|
+
filterClause = { slug: { $in: slugs } };
|
|
1038
|
+
}
|
|
1039
|
+
const queryFields = ["documentId", "updatedAt", "publishedAt"];
|
|
1040
|
+
if (hasSlugInSchema) {
|
|
1041
|
+
queryFields.push("slug");
|
|
1042
|
+
}
|
|
1043
|
+
let publishedRecords = [];
|
|
1044
|
+
try {
|
|
1045
|
+
const docService = strapi?.documents?.(uid);
|
|
1046
|
+
if (docService?.findMany) {
|
|
1047
|
+
publishedRecords = await docService.findMany({
|
|
1048
|
+
status: "published",
|
|
1049
|
+
filters: filterClause,
|
|
1050
|
+
fields: queryFields,
|
|
1051
|
+
limit: Math.max(entries.length, 100)
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
} catch (err) {
|
|
1055
|
+
strapi?.log?.warn?.(`[bulk-updater] Failed to fetch published status for ${uid}: ${err?.message || err}`);
|
|
1056
|
+
for (const entry of entries) {
|
|
1057
|
+
if (entry && typeof entry === "object" && entry.status !== "unknown") {
|
|
1058
|
+
entry.status = "unknown";
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
return entries;
|
|
1062
|
+
}
|
|
1063
|
+
const publishedByDocId = /* @__PURE__ */ new Map();
|
|
1064
|
+
const publishedBySlug = /* @__PURE__ */ new Map();
|
|
1065
|
+
for (const pub of publishedRecords || []) {
|
|
1066
|
+
if (pub?.documentId) {
|
|
1067
|
+
publishedByDocId.set(String(pub.documentId), pub);
|
|
1068
|
+
}
|
|
1069
|
+
if (pub?.slug && hasSlugInSchema) {
|
|
1070
|
+
publishedBySlug.set(String(pub.slug), pub);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
for (const entry of entries) {
|
|
1074
|
+
if (!entry || typeof entry !== "object") continue;
|
|
1075
|
+
if (entry.status === "unknown") continue;
|
|
1076
|
+
const pub = entry.documentId && publishedByDocId.get(String(entry.documentId)) || hasSlugInSchema && entry.slug && publishedBySlug.get(String(entry.slug));
|
|
1077
|
+
if (!pub) {
|
|
1078
|
+
entry.status = "draft";
|
|
1079
|
+
entry.publishedAt = null;
|
|
1080
|
+
entry.published_at = null;
|
|
1081
|
+
} else {
|
|
1082
|
+
entry.publishedAt = pub.publishedAt || pub.published_at || null;
|
|
1083
|
+
entry.published_at = entry.publishedAt;
|
|
1084
|
+
const draftUpdatedTime = new Date(entry.updatedAt || entry.updated_at || 0).getTime();
|
|
1085
|
+
const pubUpdatedTime = new Date(pub.updatedAt || pub.updated_at || 0).getTime();
|
|
1086
|
+
const pubPublishedTime = new Date(pub.publishedAt || pub.published_at || 0).getTime();
|
|
1087
|
+
const lastPublishedTime = Math.max(
|
|
1088
|
+
isNaN(pubUpdatedTime) ? 0 : pubUpdatedTime,
|
|
1089
|
+
isNaN(pubPublishedTime) ? 0 : pubPublishedTime
|
|
1090
|
+
);
|
|
1091
|
+
if (isNaN(draftUpdatedTime) || lastPublishedTime === 0) {
|
|
1092
|
+
if (pub.publishedAt) {
|
|
1093
|
+
entry.status = "published";
|
|
1094
|
+
} else {
|
|
1095
|
+
entry.status = "unknown";
|
|
1096
|
+
}
|
|
1097
|
+
} else if (draftUpdatedTime > lastPublishedTime) {
|
|
1098
|
+
entry.status = "modified";
|
|
1099
|
+
} else {
|
|
1100
|
+
entry.status = "published";
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
return entries;
|
|
1105
|
+
}
|
|
992
1106
|
function resolveContentTypeField(contentType, field) {
|
|
993
1107
|
if (!field) return null;
|
|
994
1108
|
const attrs = contentType?.attributes || {};
|
|
@@ -1113,14 +1227,15 @@ function discoverComponentAttributes(strapi, compUid, prefix, currentDepth, maxD
|
|
|
1113
1227
|
}
|
|
1114
1228
|
function parseDynamicZonePath(path) {
|
|
1115
1229
|
if (!path) return { cleanPath: "", rootKey: "", subPath: "" };
|
|
1116
|
-
const bracketMatch = path.match(/^([^.[]+)\[([^\]]+)\]
|
|
1230
|
+
const bracketMatch = path.match(/^([^.[]+)\[([^\]]+)\](?:\.(.+))?$/);
|
|
1117
1231
|
if (bracketMatch) {
|
|
1118
1232
|
const rootKey = bracketMatch[1];
|
|
1119
|
-
const selector = bracketMatch[2];
|
|
1120
|
-
const subPath = bracketMatch[3];
|
|
1121
|
-
const cleanPath = `${rootKey}.${subPath}
|
|
1122
|
-
|
|
1123
|
-
|
|
1233
|
+
const selector = bracketMatch[2].trim();
|
|
1234
|
+
const subPath = bracketMatch[3] || "";
|
|
1235
|
+
const cleanPath = subPath ? `${rootKey}.${subPath}` : rootKey;
|
|
1236
|
+
const numMatch = selector.match(/^#?(\d+)/);
|
|
1237
|
+
if (numMatch) {
|
|
1238
|
+
return { cleanPath, rootKey, subPath, targetIndex: parseInt(numMatch[1], 10) };
|
|
1124
1239
|
}
|
|
1125
1240
|
return { cleanPath, rootKey, subPath, targetComponentUid: selector };
|
|
1126
1241
|
}
|
|
@@ -2052,6 +2167,9 @@ const bulkUpdaterService = ({ strapi }) => {
|
|
|
2052
2167
|
isRunnerRunning = false;
|
|
2053
2168
|
}
|
|
2054
2169
|
}, 3e4);
|
|
2170
|
+
if (typeof runnerInterval.unref === "function") {
|
|
2171
|
+
runnerInterval.unref();
|
|
2172
|
+
}
|
|
2055
2173
|
}
|
|
2056
2174
|
return {
|
|
2057
2175
|
/**
|
|
@@ -2254,6 +2372,7 @@ const bulkUpdaterService = ({ strapi }) => {
|
|
|
2254
2372
|
totalCount = 0;
|
|
2255
2373
|
}
|
|
2256
2374
|
const formattedEntries = (entries || []).map(formatEntryRecord);
|
|
2375
|
+
await populateDocumentLifecycleStatus(strapi, uid, formattedEntries);
|
|
2257
2376
|
modelsCache.set(uid, {
|
|
2258
2377
|
signature,
|
|
2259
2378
|
totalCount,
|
|
@@ -2369,6 +2488,7 @@ const bulkUpdaterService = ({ strapi }) => {
|
|
|
2369
2488
|
if (!colHasAttribute) {
|
|
2370
2489
|
result = {
|
|
2371
2490
|
uid,
|
|
2491
|
+
status: "success",
|
|
2372
2492
|
displayName: contentType.info?.displayName || uid,
|
|
2373
2493
|
isSingleType,
|
|
2374
2494
|
draftAndPublish: Boolean(contentType.options?.draftAndPublish),
|
|
@@ -2510,12 +2630,15 @@ const bulkUpdaterService = ({ strapi }) => {
|
|
|
2510
2630
|
} catch {
|
|
2511
2631
|
singleDoc = null;
|
|
2512
2632
|
}
|
|
2633
|
+
const formattedEntries = singleDoc ? [formatEntryRecord(singleDoc)] : [];
|
|
2634
|
+
await populateDocumentLifecycleStatus(strapi, uid, formattedEntries);
|
|
2513
2635
|
result = {
|
|
2514
2636
|
uid,
|
|
2637
|
+
status: "success",
|
|
2515
2638
|
displayName: contentType.info?.displayName || uid,
|
|
2516
2639
|
isSingleType: true,
|
|
2517
2640
|
draftAndPublish: Boolean(contentType.options?.draftAndPublish),
|
|
2518
|
-
entries:
|
|
2641
|
+
entries: formattedEntries,
|
|
2519
2642
|
pagination: {
|
|
2520
2643
|
page: 1,
|
|
2521
2644
|
pageSize: 1,
|
|
@@ -2602,12 +2725,15 @@ const bulkUpdaterService = ({ strapi }) => {
|
|
|
2602
2725
|
const matchedEntries = (rawEntries || []).filter(passesDzFilter);
|
|
2603
2726
|
const totalEntries = matchedEntries.length;
|
|
2604
2727
|
const paginatedEntries = matchedEntries.slice(start, start + limit);
|
|
2728
|
+
const formattedEntries = paginatedEntries.map(formatEntryRecord);
|
|
2729
|
+
await populateDocumentLifecycleStatus(strapi, uid, formattedEntries);
|
|
2605
2730
|
result = {
|
|
2606
2731
|
uid,
|
|
2732
|
+
status: "success",
|
|
2607
2733
|
displayName: contentType.info?.displayName || uid,
|
|
2608
2734
|
isSingleType: false,
|
|
2609
2735
|
draftAndPublish: Boolean(contentType.options?.draftAndPublish),
|
|
2610
|
-
entries:
|
|
2736
|
+
entries: formattedEntries,
|
|
2611
2737
|
pagination: {
|
|
2612
2738
|
page: currentPage,
|
|
2613
2739
|
pageSize: limit,
|
|
@@ -2631,12 +2757,15 @@ const bulkUpdaterService = ({ strapi }) => {
|
|
|
2631
2757
|
filters: Object.keys(filters).length > 0 ? filters : void 0
|
|
2632
2758
|
})
|
|
2633
2759
|
]);
|
|
2760
|
+
const formattedEntries = (entries || []).map(formatEntryRecord);
|
|
2761
|
+
await populateDocumentLifecycleStatus(strapi, uid, formattedEntries);
|
|
2634
2762
|
result = {
|
|
2635
2763
|
uid,
|
|
2764
|
+
status: "success",
|
|
2636
2765
|
displayName: contentType.info?.displayName || uid,
|
|
2637
2766
|
isSingleType: false,
|
|
2638
2767
|
draftAndPublish: Boolean(contentType.options?.draftAndPublish),
|
|
2639
|
-
entries:
|
|
2768
|
+
entries: formattedEntries,
|
|
2640
2769
|
pagination: {
|
|
2641
2770
|
page: currentPage,
|
|
2642
2771
|
pageSize: limit,
|
|
@@ -4273,42 +4402,74 @@ const auditService = ({ strapi }) => ({
|
|
|
4273
4402
|
for (const item of diffs) {
|
|
4274
4403
|
if (item.status === "updated" && item.uid && item.documentId && item.attribute) {
|
|
4275
4404
|
try {
|
|
4405
|
+
const ct = strapi.contentTypes[item.uid];
|
|
4406
|
+
if (!ct) {
|
|
4407
|
+
throw new Error(`Content type "${item.uid}" does not exist in schema.`);
|
|
4408
|
+
}
|
|
4276
4409
|
const isRel = item.oldValue && typeof item.oldValue === "object" && item.oldValue.isRelation;
|
|
4277
4410
|
let rollbackPayload;
|
|
4278
4411
|
if (isRel) {
|
|
4279
4412
|
const oldDocIds = Array.isArray(item.oldValue.documentIds) ? item.oldValue.documentIds : [];
|
|
4280
4413
|
rollbackPayload = { set: oldDocIds };
|
|
4281
4414
|
} else {
|
|
4282
|
-
rollbackPayload = item.oldValue;
|
|
4415
|
+
rollbackPayload = item.oldValue ?? null;
|
|
4283
4416
|
}
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4417
|
+
const parsed = parseDynamicZonePath(item.attribute);
|
|
4418
|
+
const rootKey = parsed.rootKey;
|
|
4419
|
+
const rootAttrDef = ct.attributes?.[rootKey];
|
|
4420
|
+
const isComponent = rootAttrDef?.type === "component";
|
|
4421
|
+
const isDynamicZone = rootAttrDef?.type === "dynamiczone";
|
|
4422
|
+
const isNested = item.attribute.includes(".") || item.attribute.includes("[") || isComponent || isDynamicZone || isDeepComponentField(ct, strapi.components, item.attribute);
|
|
4423
|
+
let wasPublished = false;
|
|
4424
|
+
if (isNested && (isComponent || isDynamicZone || rootAttrDef)) {
|
|
4425
|
+
const populateOption = buildDeepPopulate$1(ct.attributes, strapi.components);
|
|
4426
|
+
const findOneArgs = { documentId: item.documentId };
|
|
4427
|
+
if (populateOption) {
|
|
4428
|
+
findOneArgs.populate = populateOption;
|
|
4429
|
+
}
|
|
4430
|
+
const currentDoc = await strapi.documents(item.uid).findOne(findOneArgs);
|
|
4431
|
+
if (!currentDoc) {
|
|
4432
|
+
throw new Error(`Document "${item.documentId}" not found in collection "${item.uid}".`);
|
|
4433
|
+
}
|
|
4434
|
+
wasPublished = Boolean(currentDoc?.publishedAt);
|
|
4435
|
+
const isRootDz = isDynamicZone;
|
|
4436
|
+
const existingRoot = currentDoc?.[rootKey] || (isRootDz ? [] : {});
|
|
4437
|
+
let restoredRoot;
|
|
4438
|
+
if (Array.isArray(item.dynamicZoneDiffs) && item.dynamicZoneDiffs.length > 0) {
|
|
4439
|
+
let workingRoot = existingRoot;
|
|
4440
|
+
for (const dzDiff of item.dynamicZoneDiffs) {
|
|
4441
|
+
const parsedDz = parseDynamicZonePath(dzDiff.attribute || item.attribute);
|
|
4442
|
+
const dzParts = parsedDz.cleanPath.split(".").slice(1);
|
|
4443
|
+
const targetIndexes = dzDiff.index !== void 0 ? [dzDiff.index] : parsedDz.targetIndex !== void 0 ? [parsedDz.targetIndex] : void 0;
|
|
4444
|
+
workingRoot = setDeepProperty(workingRoot, dzParts, dzDiff.oldValue ?? null, {
|
|
4445
|
+
targetIndexes,
|
|
4446
|
+
targetComponentUid: dzDiff.componentUid || parsedDz.targetComponentUid,
|
|
4447
|
+
strapi
|
|
4448
|
+
});
|
|
4449
|
+
}
|
|
4450
|
+
restoredRoot = workingRoot;
|
|
4303
4451
|
} else {
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4452
|
+
const parts = parsed.cleanPath.split(".");
|
|
4453
|
+
const pathParts = parts.slice(1);
|
|
4454
|
+
const targetIndexes = parsed.targetIndex !== void 0 ? [parsed.targetIndex] : void 0;
|
|
4455
|
+
const targetComponentUid = parsed.targetComponentUid;
|
|
4456
|
+
restoredRoot = setDeepProperty(existingRoot, pathParts, rollbackPayload, {
|
|
4457
|
+
targetIndexes,
|
|
4458
|
+
targetComponentUid,
|
|
4459
|
+
strapi
|
|
4309
4460
|
});
|
|
4310
4461
|
}
|
|
4462
|
+
await strapi.documents(item.uid).update({
|
|
4463
|
+
documentId: item.documentId,
|
|
4464
|
+
data: {
|
|
4465
|
+
[rootKey]: restoredRoot
|
|
4466
|
+
}
|
|
4467
|
+
});
|
|
4311
4468
|
} else {
|
|
4469
|
+
const currentDoc = await strapi.documents(item.uid).findOne({
|
|
4470
|
+
documentId: item.documentId
|
|
4471
|
+
});
|
|
4472
|
+
wasPublished = Boolean(currentDoc?.publishedAt);
|
|
4312
4473
|
await strapi.documents(item.uid).update({
|
|
4313
4474
|
documentId: item.documentId,
|
|
4314
4475
|
data: {
|
|
@@ -4316,13 +4477,13 @@ const auditService = ({ strapi }) => ({
|
|
|
4316
4477
|
}
|
|
4317
4478
|
});
|
|
4318
4479
|
}
|
|
4319
|
-
|
|
4320
|
-
if (ct?.options?.draftAndPublish) {
|
|
4480
|
+
if (ct?.options?.draftAndPublish && wasPublished) {
|
|
4321
4481
|
try {
|
|
4322
4482
|
await strapi.documents(item.uid).publish({
|
|
4323
4483
|
documentId: item.documentId
|
|
4324
4484
|
});
|
|
4325
|
-
} catch {
|
|
4485
|
+
} catch (publishErr) {
|
|
4486
|
+
strapi.log.warn(`[bulk-updater] Failed to auto-publish rollback for ${item.uid}/${item.documentId}:`, publishErr?.message);
|
|
4326
4487
|
}
|
|
4327
4488
|
}
|
|
4328
4489
|
try {
|
|
@@ -4330,7 +4491,7 @@ const auditService = ({ strapi }) => ({
|
|
|
4330
4491
|
uid: item.uid,
|
|
4331
4492
|
documentId: item.documentId,
|
|
4332
4493
|
userId,
|
|
4333
|
-
statusOverride: ct?.options?.draftAndPublish ? "published" : void 0
|
|
4494
|
+
statusOverride: ct?.options?.draftAndPublish ? wasPublished ? "published" : "draft" : void 0
|
|
4334
4495
|
});
|
|
4335
4496
|
} catch {
|
|
4336
4497
|
}
|