strapi-plugin-bulk-updater 1.3.3 → 1.3.6
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-C2lw4D0D.js → BulkUpdater-CXDs0Dws.js} +181 -142
- package/dist/_chunks/BulkUpdater-CXDs0Dws.js.map +1 -0
- package/dist/_chunks/{BulkUpdater-CgsqzdyX.mjs → BulkUpdater-D812YfOE.mjs} +182 -143
- package/dist/_chunks/BulkUpdater-D812YfOE.mjs.map +1 -0
- package/dist/admin/index.js +43 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +42 -2
- package/dist/admin/index.mjs.map +1 -1
- package/dist/server/index.js +405 -62
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +405 -62
- package/dist/server/index.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/_chunks/BulkUpdater-C2lw4D0D.js.map +0 -1
- package/dist/_chunks/BulkUpdater-CgsqzdyX.mjs.map +0 -1
package/dist/server/index.js
CHANGED
|
@@ -688,6 +688,149 @@ const csvController = ({ strapi: strapi2 }) => ({
|
|
|
688
688
|
}
|
|
689
689
|
}
|
|
690
690
|
});
|
|
691
|
+
const FIELDS_TO_IGNORE = [
|
|
692
|
+
"createdAt",
|
|
693
|
+
"updatedAt",
|
|
694
|
+
"publishedAt",
|
|
695
|
+
"createdBy",
|
|
696
|
+
"updatedBy",
|
|
697
|
+
"strapi_stage",
|
|
698
|
+
"strapi_assignee"
|
|
699
|
+
];
|
|
700
|
+
function buildHistoryPopulate(attributes, allComponents, depth2 = 0) {
|
|
701
|
+
if (depth2 > 5) return void 0;
|
|
702
|
+
const populate2 = {};
|
|
703
|
+
for (const [key, def] of Object.entries(attributes || {})) {
|
|
704
|
+
if (def.type === "component" && def.component) {
|
|
705
|
+
const comp = allComponents[def.component];
|
|
706
|
+
if (comp?.attributes) {
|
|
707
|
+
const subPopulate = buildHistoryPopulate(comp.attributes, allComponents, depth2 + 1);
|
|
708
|
+
if (subPopulate && Object.keys(subPopulate).length > 0) {
|
|
709
|
+
populate2[key] = {
|
|
710
|
+
populate: subPopulate
|
|
711
|
+
};
|
|
712
|
+
} else {
|
|
713
|
+
populate2[key] = true;
|
|
714
|
+
}
|
|
715
|
+
} else {
|
|
716
|
+
populate2[key] = true;
|
|
717
|
+
}
|
|
718
|
+
} else if (def.type === "dynamiczone") {
|
|
719
|
+
populate2[key] = true;
|
|
720
|
+
} else if (def.type === "relation" || def.type === "media") {
|
|
721
|
+
populate2[key] = true;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return Object.keys(populate2).length > 0 ? populate2 : void 0;
|
|
725
|
+
}
|
|
726
|
+
let cachedAdminUserId = null;
|
|
727
|
+
async function recordContentHistoryVersion(strapi2, options) {
|
|
728
|
+
const { uid, documentId, userId, statusOverride } = options;
|
|
729
|
+
if (!uid || !uid.startsWith("api::") || !documentId) {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
try {
|
|
733
|
+
const historyQuery = strapi2.db?.query?.("plugin::content-manager.history-version");
|
|
734
|
+
if (!historyQuery) {
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
const model = strapi2.getModel?.(uid);
|
|
738
|
+
if (!model) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
let entry = null;
|
|
742
|
+
try {
|
|
743
|
+
entry = await strapi2.db.query(uid).findOne({
|
|
744
|
+
where: { documentId },
|
|
745
|
+
populate: true
|
|
746
|
+
});
|
|
747
|
+
} catch {
|
|
748
|
+
try {
|
|
749
|
+
const populateOption = buildHistoryPopulate(model.attributes || {}, strapi2.components || {});
|
|
750
|
+
entry = await strapi2.documents(uid).findOne({
|
|
751
|
+
documentId,
|
|
752
|
+
...populateOption ? { populate: populateOption } : {}
|
|
753
|
+
});
|
|
754
|
+
} catch {
|
|
755
|
+
entry = null;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (!entry) {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
const attributesSchema = model.attributes || {};
|
|
762
|
+
const sanitizedSchema = Object.fromEntries(
|
|
763
|
+
Object.entries(attributesSchema).filter(([k]) => !FIELDS_TO_IGNORE.includes(k))
|
|
764
|
+
);
|
|
765
|
+
const sanitizedData = Object.fromEntries(
|
|
766
|
+
Object.entries(entry).filter(([k]) => !FIELDS_TO_IGNORE.includes(k) && k !== "id")
|
|
767
|
+
);
|
|
768
|
+
let status2 = statusOverride || "published";
|
|
769
|
+
if (!statusOverride) {
|
|
770
|
+
const hasDraftAndPublish = Boolean(model.options?.draftAndPublish);
|
|
771
|
+
if (hasDraftAndPublish) {
|
|
772
|
+
try {
|
|
773
|
+
const docMetaService = strapi2.plugin("content-manager")?.service("document-metadata");
|
|
774
|
+
if (docMetaService) {
|
|
775
|
+
const meta = await docMetaService.getMetadata(uid, entry);
|
|
776
|
+
status2 = docMetaService.getStatus(entry, meta.availableStatus);
|
|
777
|
+
} else {
|
|
778
|
+
status2 = entry.publishedAt ? "published" : "draft";
|
|
779
|
+
}
|
|
780
|
+
} catch {
|
|
781
|
+
status2 = entry.publishedAt ? "published" : "draft";
|
|
782
|
+
}
|
|
783
|
+
} else {
|
|
784
|
+
status2 = "published";
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
let authorId = userId;
|
|
788
|
+
if (!authorId) {
|
|
789
|
+
authorId = strapi2.requestContext?.get?.()?.state?.user?.id || null;
|
|
790
|
+
}
|
|
791
|
+
let numericUserId = authorId ? Number(authorId) : null;
|
|
792
|
+
if (!numericUserId || isNaN(numericUserId)) {
|
|
793
|
+
if (cachedAdminUserId) {
|
|
794
|
+
numericUserId = cachedAdminUserId;
|
|
795
|
+
} else {
|
|
796
|
+
try {
|
|
797
|
+
const firstAdmin = await strapi2.db?.query?.("admin::user")?.findOne?.({ select: ["id"] });
|
|
798
|
+
if (firstAdmin?.id) {
|
|
799
|
+
cachedAdminUserId = Number(firstAdmin.id);
|
|
800
|
+
numericUserId = cachedAdminUserId;
|
|
801
|
+
}
|
|
802
|
+
} catch {
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
await historyQuery.create({
|
|
807
|
+
data: {
|
|
808
|
+
contentType: uid,
|
|
809
|
+
relatedDocumentId: documentId,
|
|
810
|
+
locale: entry.locale || null,
|
|
811
|
+
status: status2,
|
|
812
|
+
data: sanitizedData,
|
|
813
|
+
schema: sanitizedSchema,
|
|
814
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
815
|
+
...numericUserId && !isNaN(numericUserId) ? { createdBy: numericUserId } : {}
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
} catch (err) {
|
|
819
|
+
strapi2.log?.warn?.(`[bulk-updater] Non-blocking Content History notice for ${uid}/${documentId}: ${err?.message}`);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
async function recordContentHistoryVersionsBatch(strapi2, items) {
|
|
823
|
+
if (!items || items.length === 0) return;
|
|
824
|
+
const BATCH_SIZE = 10;
|
|
825
|
+
for (let i = 0; i < items.length; i += BATCH_SIZE) {
|
|
826
|
+
const slice = items.slice(i, i + BATCH_SIZE);
|
|
827
|
+
await Promise.all(slice.map((item) => recordContentHistoryVersion(strapi2, item)));
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
const contentHistoryService = {
|
|
831
|
+
recordContentHistoryVersion,
|
|
832
|
+
recordContentHistoryVersionsBatch
|
|
833
|
+
};
|
|
691
834
|
const queryCache = /* @__PURE__ */ new Map();
|
|
692
835
|
const modelsCache = /* @__PURE__ */ new Map();
|
|
693
836
|
const MAX_QUERY_CACHE_SIZE = 500;
|
|
@@ -1064,6 +1207,12 @@ async function executeBatchInternal(strapi2, {
|
|
|
1064
1207
|
}
|
|
1065
1208
|
}
|
|
1066
1209
|
invalidateCollectionCache(target.uid);
|
|
1210
|
+
await recordContentHistoryVersion(strapi2, {
|
|
1211
|
+
uid: target.uid,
|
|
1212
|
+
documentId: target.documentId,
|
|
1213
|
+
userId: currentUser?.id,
|
|
1214
|
+
statusOverride: publishOnApproval && isDraftAndPublishComp ? "published" : void 0
|
|
1215
|
+
});
|
|
1067
1216
|
return {
|
|
1068
1217
|
uid: target.uid,
|
|
1069
1218
|
collectionName: displayName,
|
|
@@ -1149,6 +1298,12 @@ async function executeBatchInternal(strapi2, {
|
|
|
1149
1298
|
if (attrDef.target) {
|
|
1150
1299
|
invalidateCollectionCache(attrDef.target);
|
|
1151
1300
|
}
|
|
1301
|
+
await recordContentHistoryVersion(strapi2, {
|
|
1302
|
+
uid: target.uid,
|
|
1303
|
+
documentId: target.documentId,
|
|
1304
|
+
userId: currentUser?.id,
|
|
1305
|
+
statusOverride: publishOnApproval && isDraftAndPublishRel ? "published" : void 0
|
|
1306
|
+
});
|
|
1152
1307
|
return {
|
|
1153
1308
|
uid: target.uid,
|
|
1154
1309
|
collectionName: displayName,
|
|
@@ -1182,6 +1337,12 @@ async function executeBatchInternal(strapi2, {
|
|
|
1182
1337
|
strapi2.log.warn(`[bulk-updater] Failed to auto-publish ${target.uid}/${target.documentId}:`, publishErr);
|
|
1183
1338
|
}
|
|
1184
1339
|
}
|
|
1340
|
+
await recordContentHistoryVersion(strapi2, {
|
|
1341
|
+
uid: target.uid,
|
|
1342
|
+
documentId: target.documentId,
|
|
1343
|
+
userId: currentUser?.id,
|
|
1344
|
+
statusOverride: publishOnApproval && isDraftAndPublishScalar ? "published" : void 0
|
|
1345
|
+
});
|
|
1185
1346
|
return {
|
|
1186
1347
|
uid: target.uid,
|
|
1187
1348
|
collectionName: displayName,
|
|
@@ -1352,6 +1513,12 @@ async function executePublishBatchInternal(strapi2, {
|
|
|
1352
1513
|
documentId: target.documentId
|
|
1353
1514
|
});
|
|
1354
1515
|
invalidateCollectionCache(target.uid);
|
|
1516
|
+
await recordContentHistoryVersion(strapi2, {
|
|
1517
|
+
uid: target.uid,
|
|
1518
|
+
documentId: target.documentId,
|
|
1519
|
+
userId: currentUser?.id,
|
|
1520
|
+
statusOverride: "published"
|
|
1521
|
+
});
|
|
1355
1522
|
return {
|
|
1356
1523
|
uid: target.uid,
|
|
1357
1524
|
collectionName: displayName,
|
|
@@ -3207,7 +3374,7 @@ const auditService = ({ strapi: strapi2 }) => ({
|
|
|
3207
3374
|
/**
|
|
3208
3375
|
* Helper to revert diff items back to original values
|
|
3209
3376
|
*/
|
|
3210
|
-
async revertDiffItems(diffs) {
|
|
3377
|
+
async revertDiffItems(diffs, userId) {
|
|
3211
3378
|
let revertedCount = 0;
|
|
3212
3379
|
const rollbackDetails = [];
|
|
3213
3380
|
for (const item of diffs) {
|
|
@@ -3265,6 +3432,15 @@ const auditService = ({ strapi: strapi2 }) => ({
|
|
|
3265
3432
|
} catch {
|
|
3266
3433
|
}
|
|
3267
3434
|
}
|
|
3435
|
+
try {
|
|
3436
|
+
await recordContentHistoryVersion(strapi2, {
|
|
3437
|
+
uid: item.uid,
|
|
3438
|
+
documentId: item.documentId,
|
|
3439
|
+
userId,
|
|
3440
|
+
statusOverride: ct?.options?.draftAndPublish ? "published" : void 0
|
|
3441
|
+
});
|
|
3442
|
+
} catch {
|
|
3443
|
+
}
|
|
3268
3444
|
try {
|
|
3269
3445
|
const bulkService = strapi2.plugin("bulk-updater").service("bulkUpdater");
|
|
3270
3446
|
if (bulkService?.invalidateCache) {
|
|
@@ -3315,7 +3491,7 @@ const auditService = ({ strapi: strapi2 }) => ({
|
|
|
3315
3491
|
throw new Error(`Batch "${batchId}" has already been rolled back.`);
|
|
3316
3492
|
}
|
|
3317
3493
|
const diffs2 = Array.isArray(record2.diffs) ? record2.diffs : [];
|
|
3318
|
-
const { revertedCount: revertedCount2, rollbackDetails: rollbackDetails2 } = await this.revertDiffItems(diffs2);
|
|
3494
|
+
const { revertedCount: revertedCount2, rollbackDetails: rollbackDetails2 } = await this.revertDiffItems(diffs2, currentUser?.id);
|
|
3319
3495
|
record2.isRolledBack = true;
|
|
3320
3496
|
record2.rolledBackAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3321
3497
|
record2.rolledBackBy = currentUser?.email || "Admin";
|
|
@@ -3363,7 +3539,7 @@ const auditService = ({ strapi: strapi2 }) => ({
|
|
|
3363
3539
|
throw new Error(`Batch "${batchId}" has already been rolled back.`);
|
|
3364
3540
|
}
|
|
3365
3541
|
const diffs = typeof record[f.diffs] === "string" ? JSON.parse(record[f.diffs]) : record[f.diffs] || [];
|
|
3366
|
-
const { revertedCount, rollbackDetails } = await this.revertDiffItems(diffs);
|
|
3542
|
+
const { revertedCount, rollbackDetails } = await this.revertDiffItems(diffs, currentUser?.id);
|
|
3367
3543
|
const updateData = {};
|
|
3368
3544
|
if (f.isRolledBack) updateData[f.isRolledBack] = true;
|
|
3369
3545
|
if (f.rolledBackAt) updateData[f.rolledBackAt] = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -3639,77 +3815,93 @@ function escapeCsvValue(val) {
|
|
|
3639
3815
|
}
|
|
3640
3816
|
return str2;
|
|
3641
3817
|
}
|
|
3642
|
-
function
|
|
3818
|
+
function parseCsvRows(csvText) {
|
|
3643
3819
|
if (!csvText) return [];
|
|
3644
|
-
let
|
|
3645
|
-
if (
|
|
3646
|
-
|
|
3820
|
+
let text = csvText;
|
|
3821
|
+
if (text.charCodeAt(0) === 65279) {
|
|
3822
|
+
text = text.slice(1);
|
|
3647
3823
|
}
|
|
3648
|
-
const
|
|
3649
|
-
let
|
|
3824
|
+
const rows = [];
|
|
3825
|
+
let currentRow = [];
|
|
3826
|
+
let currentCell = "";
|
|
3650
3827
|
let inQuotes = false;
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
const
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3828
|
+
let cellIsQuoted = false;
|
|
3829
|
+
for (let i = 0; i < text.length; i++) {
|
|
3830
|
+
const char = text[i];
|
|
3831
|
+
const nextChar = text[i + 1];
|
|
3832
|
+
if (inQuotes) {
|
|
3833
|
+
if (char === '"') {
|
|
3834
|
+
if (nextChar === '"') {
|
|
3835
|
+
currentCell += '"';
|
|
3836
|
+
i++;
|
|
3837
|
+
} else {
|
|
3838
|
+
inQuotes = false;
|
|
3839
|
+
}
|
|
3658
3840
|
} else {
|
|
3659
|
-
|
|
3660
|
-
}
|
|
3661
|
-
} else if ((char === "\r" || char === "\n") && !inQuotes) {
|
|
3662
|
-
if (char === "\r" && nextChar === "\n") {
|
|
3663
|
-
i++;
|
|
3841
|
+
currentCell += char;
|
|
3664
3842
|
}
|
|
3665
|
-
if (current.trim().length > 0) {
|
|
3666
|
-
lines.push(current);
|
|
3667
|
-
}
|
|
3668
|
-
current = "";
|
|
3669
3843
|
} else {
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
let inQ = false;
|
|
3681
|
-
for (let i = 0; i < line.length; i++) {
|
|
3682
|
-
const c = line[i];
|
|
3683
|
-
const nc = line[i + 1];
|
|
3684
|
-
if (c === '"') {
|
|
3685
|
-
if (inQ && nc === '"') {
|
|
3686
|
-
field += '"';
|
|
3844
|
+
if (char === '"' && !cellIsQuoted && currentCell.trim() === "") {
|
|
3845
|
+
inQuotes = true;
|
|
3846
|
+
cellIsQuoted = true;
|
|
3847
|
+
currentCell = "";
|
|
3848
|
+
} else if (char === ",") {
|
|
3849
|
+
currentRow.push(cellIsQuoted ? currentCell : currentCell.trim());
|
|
3850
|
+
currentCell = "";
|
|
3851
|
+
cellIsQuoted = false;
|
|
3852
|
+
} else if (char === "\r") {
|
|
3853
|
+
if (nextChar === "\n") {
|
|
3687
3854
|
i++;
|
|
3688
|
-
} else {
|
|
3689
|
-
inQ = !inQ;
|
|
3690
3855
|
}
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3856
|
+
currentRow.push(cellIsQuoted ? currentCell : currentCell.trim());
|
|
3857
|
+
if (currentRow.length > 1 || currentRow.length === 1 && currentRow[0] !== "") {
|
|
3858
|
+
rows.push(currentRow);
|
|
3859
|
+
}
|
|
3860
|
+
currentRow = [];
|
|
3861
|
+
currentCell = "";
|
|
3862
|
+
cellIsQuoted = false;
|
|
3863
|
+
} else if (char === "\n") {
|
|
3864
|
+
currentRow.push(cellIsQuoted ? currentCell : currentCell.trim());
|
|
3865
|
+
if (currentRow.length > 1 || currentRow.length === 1 && currentRow[0] !== "") {
|
|
3866
|
+
rows.push(currentRow);
|
|
3867
|
+
}
|
|
3868
|
+
currentRow = [];
|
|
3869
|
+
currentCell = "";
|
|
3870
|
+
cellIsQuoted = false;
|
|
3694
3871
|
} else {
|
|
3695
|
-
|
|
3872
|
+
if (!(cellIsQuoted && !inQuotes && (char === " " || char === " "))) {
|
|
3873
|
+
currentCell += char;
|
|
3874
|
+
}
|
|
3696
3875
|
}
|
|
3697
3876
|
}
|
|
3698
|
-
fields2.push(field);
|
|
3699
|
-
return fields2.map((f) => f.trim());
|
|
3700
3877
|
}
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
headers.forEach((h, index2) => {
|
|
3707
|
-
rowObj[h] = values[index2] !== void 0 ? values[index2] : "";
|
|
3708
|
-
});
|
|
3709
|
-
rows.push(rowObj);
|
|
3878
|
+
if (currentCell !== "" || cellIsQuoted || currentRow.length > 0) {
|
|
3879
|
+
currentRow.push(cellIsQuoted ? currentCell : currentCell.trim());
|
|
3880
|
+
if (currentRow.length > 1 || currentRow.length === 1 && currentRow[0] !== "") {
|
|
3881
|
+
rows.push(currentRow);
|
|
3882
|
+
}
|
|
3710
3883
|
}
|
|
3711
3884
|
return rows;
|
|
3712
3885
|
}
|
|
3886
|
+
function parseCsv(csvText) {
|
|
3887
|
+
const rows = parseCsvRows(csvText);
|
|
3888
|
+
if (rows.length < 2) return [];
|
|
3889
|
+
const headers = rows[0].map((h) => h.trim());
|
|
3890
|
+
const records = [];
|
|
3891
|
+
for (let i = 1; i < rows.length; i++) {
|
|
3892
|
+
const row = rows[i];
|
|
3893
|
+
const record = {};
|
|
3894
|
+
headers.forEach((h, idx) => {
|
|
3895
|
+
let val = row[idx] !== void 0 ? row[idx] : "";
|
|
3896
|
+
if (typeof val === "string" && /^'[=+@-]/.test(val)) {
|
|
3897
|
+
val = val.substring(1);
|
|
3898
|
+
}
|
|
3899
|
+
record[h] = val;
|
|
3900
|
+
});
|
|
3901
|
+
records.push(record);
|
|
3902
|
+
}
|
|
3903
|
+
return records;
|
|
3904
|
+
}
|
|
3713
3905
|
function discoverExportFields(ct, allComponents, maxDepth = 4) {
|
|
3714
3906
|
const fields2 = [{ key: "documentId", label: "documentId", type: "string" }];
|
|
3715
3907
|
const excluded = ["createdBy", "updatedBy", "localizations"];
|
|
@@ -4203,6 +4395,15 @@ const csvService = ({ strapi: strapi2 }) => ({
|
|
|
4203
4395
|
strapi2.log.warn(`[bulk-updater] Auto-publish failed for ${docId}:`, pubErr);
|
|
4204
4396
|
}
|
|
4205
4397
|
}
|
|
4398
|
+
try {
|
|
4399
|
+
await recordContentHistoryVersion(strapi2, {
|
|
4400
|
+
uid,
|
|
4401
|
+
documentId: docId,
|
|
4402
|
+
userId: user?.id,
|
|
4403
|
+
statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
|
|
4404
|
+
});
|
|
4405
|
+
} catch {
|
|
4406
|
+
}
|
|
4206
4407
|
stats.updated++;
|
|
4207
4408
|
stats.diffs.push({
|
|
4208
4409
|
uid,
|
|
@@ -4238,6 +4439,15 @@ const csvService = ({ strapi: strapi2 }) => ({
|
|
|
4238
4439
|
strapi2.log.warn(`[bulk-updater] Auto-publish failed for ${docId}:`, pubErr);
|
|
4239
4440
|
}
|
|
4240
4441
|
}
|
|
4442
|
+
try {
|
|
4443
|
+
await recordContentHistoryVersion(strapi2, {
|
|
4444
|
+
uid,
|
|
4445
|
+
documentId: docId,
|
|
4446
|
+
userId: user?.id,
|
|
4447
|
+
statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
|
|
4448
|
+
});
|
|
4449
|
+
} catch {
|
|
4450
|
+
}
|
|
4241
4451
|
stats.updated++;
|
|
4242
4452
|
stats.diffs.push({
|
|
4243
4453
|
uid,
|
|
@@ -4264,6 +4474,15 @@ const csvService = ({ strapi: strapi2 }) => ({
|
|
|
4264
4474
|
strapi2.log.warn(`[bulk-updater] Auto-publish failed for ${newDocId}:`, pubErr);
|
|
4265
4475
|
}
|
|
4266
4476
|
}
|
|
4477
|
+
try {
|
|
4478
|
+
await recordContentHistoryVersion(strapi2, {
|
|
4479
|
+
uid,
|
|
4480
|
+
documentId: newDocId,
|
|
4481
|
+
userId: user?.id,
|
|
4482
|
+
statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
|
|
4483
|
+
});
|
|
4484
|
+
} catch {
|
|
4485
|
+
}
|
|
4267
4486
|
stats.created++;
|
|
4268
4487
|
if (!docId) {
|
|
4269
4488
|
stats.newWithoutDocId++;
|
|
@@ -4302,6 +4521,15 @@ const csvService = ({ strapi: strapi2 }) => ({
|
|
|
4302
4521
|
strapi2.log.warn(`[bulk-updater] Auto-publish failed for ${docId}:`, pubErr);
|
|
4303
4522
|
}
|
|
4304
4523
|
}
|
|
4524
|
+
try {
|
|
4525
|
+
await recordContentHistoryVersion(strapi2, {
|
|
4526
|
+
uid,
|
|
4527
|
+
documentId: docId,
|
|
4528
|
+
userId: user?.id,
|
|
4529
|
+
statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
|
|
4530
|
+
});
|
|
4531
|
+
} catch {
|
|
4532
|
+
}
|
|
4305
4533
|
stats.updated++;
|
|
4306
4534
|
stats.diffs.push({
|
|
4307
4535
|
uid,
|
|
@@ -4328,6 +4556,15 @@ const csvService = ({ strapi: strapi2 }) => ({
|
|
|
4328
4556
|
strapi2.log.warn(`[bulk-updater] Auto-publish failed for ${newDocId}:`, pubErr);
|
|
4329
4557
|
}
|
|
4330
4558
|
}
|
|
4559
|
+
try {
|
|
4560
|
+
await recordContentHistoryVersion(strapi2, {
|
|
4561
|
+
uid,
|
|
4562
|
+
documentId: newDocId,
|
|
4563
|
+
userId: user?.id,
|
|
4564
|
+
statusOverride: publish && ct.options?.draftAndPublish ? "published" : void 0
|
|
4565
|
+
});
|
|
4566
|
+
} catch {
|
|
4567
|
+
}
|
|
4331
4568
|
stats.created++;
|
|
4332
4569
|
if (!docId) {
|
|
4333
4570
|
stats.newWithoutDocId++;
|
|
@@ -33808,8 +34045,8 @@ const index = {
|
|
|
33808
34045
|
},
|
|
33809
34046
|
bootstrap({ strapi: strapi2 }) {
|
|
33810
34047
|
strapi2.documents.use(async (context, next) => {
|
|
34048
|
+
const params = context.params;
|
|
33811
34049
|
if (["create", "update"].includes(context.action)) {
|
|
33812
|
-
const params = context.params;
|
|
33813
34050
|
const data = params?.data;
|
|
33814
34051
|
if (data && typeof data === "object") {
|
|
33815
34052
|
let scanDataTree = function(obj) {
|
|
@@ -33909,8 +34146,113 @@ const index = {
|
|
|
33909
34146
|
}
|
|
33910
34147
|
}
|
|
33911
34148
|
}
|
|
33912
|
-
|
|
34149
|
+
const result = await next();
|
|
34150
|
+
return result;
|
|
34151
|
+
});
|
|
34152
|
+
strapi2.server.use(async (ctx, next) => {
|
|
34153
|
+
if (!ctx.path.startsWith("/content-manager/history-versions")) {
|
|
34154
|
+
await next();
|
|
34155
|
+
return;
|
|
34156
|
+
}
|
|
34157
|
+
await next();
|
|
34158
|
+
if (ctx.status === 404 || ctx.status === 405) {
|
|
34159
|
+
if (ctx.method === "GET" && ctx.path === "/content-manager/history-versions") {
|
|
34160
|
+
const { contentType, documentId, locale: locale2, page = 1, pageSize = 20 } = ctx.query;
|
|
34161
|
+
if (!contentType) return;
|
|
34162
|
+
const historyQuery = strapi2.db?.query?.("plugin::content-manager.history-version");
|
|
34163
|
+
if (!historyQuery) return;
|
|
34164
|
+
const pageNum = Math.max(1, parseInt(page, 10) || 1);
|
|
34165
|
+
const limit = Math.min(100, Math.max(1, parseInt(pageSize, 10) || 20));
|
|
34166
|
+
const whereClause = {
|
|
34167
|
+
contentType,
|
|
34168
|
+
...documentId ? { relatedDocumentId: documentId } : {},
|
|
34169
|
+
...locale2 ? { locale: locale2 } : {}
|
|
34170
|
+
};
|
|
34171
|
+
const [results, total] = await Promise.all([
|
|
34172
|
+
historyQuery.findMany({
|
|
34173
|
+
where: whereClause,
|
|
34174
|
+
populate: ["createdBy"],
|
|
34175
|
+
orderBy: { createdAt: "desc" },
|
|
34176
|
+
limit,
|
|
34177
|
+
offset: (pageNum - 1) * limit
|
|
34178
|
+
}),
|
|
34179
|
+
historyQuery.count({ where: whereClause })
|
|
34180
|
+
]);
|
|
34181
|
+
const sanitizedResults = (results || []).map((ver) => ({
|
|
34182
|
+
...ver,
|
|
34183
|
+
createdBy: ver.createdBy ? {
|
|
34184
|
+
id: ver.createdBy.id,
|
|
34185
|
+
firstname: ver.createdBy.firstname,
|
|
34186
|
+
lastname: ver.createdBy.lastname,
|
|
34187
|
+
username: ver.createdBy.username,
|
|
34188
|
+
email: ver.createdBy.email
|
|
34189
|
+
} : void 0,
|
|
34190
|
+
meta: {
|
|
34191
|
+
unknownAttributes: { added: {}, removed: {} }
|
|
34192
|
+
}
|
|
34193
|
+
}));
|
|
34194
|
+
ctx.status = 200;
|
|
34195
|
+
ctx.body = {
|
|
34196
|
+
data: sanitizedResults,
|
|
34197
|
+
meta: {
|
|
34198
|
+
pagination: {
|
|
34199
|
+
page: pageNum,
|
|
34200
|
+
pageSize: limit,
|
|
34201
|
+
pageCount: Math.ceil(total / limit) || 1,
|
|
34202
|
+
total
|
|
34203
|
+
}
|
|
34204
|
+
}
|
|
34205
|
+
};
|
|
34206
|
+
} else if (ctx.method === "PUT" && ctx.path.endsWith("/restore")) {
|
|
34207
|
+
const match = ctx.path.match(/\/content-manager\/history-versions\/([^/]+)\/restore/);
|
|
34208
|
+
const versionId = match?.[1];
|
|
34209
|
+
if (!versionId) return;
|
|
34210
|
+
const historyQuery = strapi2.db?.query?.("plugin::content-manager.history-version");
|
|
34211
|
+
const version = await historyQuery?.findOne?.({ where: { id: versionId } });
|
|
34212
|
+
if (!version) {
|
|
34213
|
+
ctx.status = 404;
|
|
34214
|
+
ctx.body = { error: "Version not found" };
|
|
34215
|
+
return;
|
|
34216
|
+
}
|
|
34217
|
+
const restored = await strapi2.documents(version.contentType).update({
|
|
34218
|
+
documentId: version.relatedDocumentId,
|
|
34219
|
+
locale: version.locale || void 0,
|
|
34220
|
+
data: version.data
|
|
34221
|
+
});
|
|
34222
|
+
await recordContentHistoryVersion(strapi2, {
|
|
34223
|
+
uid: version.contentType,
|
|
34224
|
+
documentId: version.relatedDocumentId,
|
|
34225
|
+
statusOverride: version.status || "published"
|
|
34226
|
+
});
|
|
34227
|
+
ctx.status = 200;
|
|
34228
|
+
ctx.body = {
|
|
34229
|
+
data: {
|
|
34230
|
+
documentId: restored?.documentId || version.relatedDocumentId
|
|
34231
|
+
}
|
|
34232
|
+
};
|
|
34233
|
+
}
|
|
34234
|
+
}
|
|
33913
34235
|
});
|
|
34236
|
+
try {
|
|
34237
|
+
const knex = strapi2.db?.connection;
|
|
34238
|
+
if (knex) {
|
|
34239
|
+
if (knex.client?.config?.client === "sqlite3" || knex.client?.dialect === "sqlite3") {
|
|
34240
|
+
knex.raw("PRAGMA busy_timeout = 10000;").catch(() => {
|
|
34241
|
+
});
|
|
34242
|
+
}
|
|
34243
|
+
knex.schema.hasTable("strapi_history_versions").then((hasTable) => {
|
|
34244
|
+
if (hasTable) {
|
|
34245
|
+
knex.raw(`
|
|
34246
|
+
CREATE INDEX IF NOT EXISTS idx_history_versions_lookup
|
|
34247
|
+
ON strapi_history_versions (content_type, related_document_id, created_at)
|
|
34248
|
+
`).catch(() => {
|
|
34249
|
+
});
|
|
34250
|
+
}
|
|
34251
|
+
}).catch(() => {
|
|
34252
|
+
});
|
|
34253
|
+
}
|
|
34254
|
+
} catch {
|
|
34255
|
+
}
|
|
33914
34256
|
},
|
|
33915
34257
|
routes,
|
|
33916
34258
|
contentTypes,
|
|
@@ -33924,7 +34266,8 @@ const index = {
|
|
|
33924
34266
|
bulkUpdater: bulkUpdaterService,
|
|
33925
34267
|
audit: auditService,
|
|
33926
34268
|
validationRules: validationRulesService,
|
|
33927
|
-
csv: csvService
|
|
34269
|
+
csv: csvService,
|
|
34270
|
+
contentHistory: contentHistoryService
|
|
33928
34271
|
}
|
|
33929
34272
|
};
|
|
33930
34273
|
module.exports = index;
|