@strapi/content-releases 0.0.0-next.6d59515520a3850456f256fb0e4c54b75054ddf4 → 0.0.0-next.7abe81e395faf152048bfba0b088b9062e8ac602
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/{App-_20W9dYa.js → App-1hHIqUoZ.js} +253 -191
- package/dist/_chunks/App-1hHIqUoZ.js.map +1 -0
- package/dist/_chunks/{App-L1jSxCiL.mjs → App-U6GbyLIE.mjs} +257 -195
- package/dist/_chunks/App-U6GbyLIE.mjs.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs +51 -0
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js +51 -0
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +1 -0
- package/dist/_chunks/{en-MyLPoISH.mjs → en-GqXgfmzl.mjs} +9 -3
- package/dist/_chunks/en-GqXgfmzl.mjs.map +1 -0
- package/dist/_chunks/{en-gYDqKYFd.js → en-bDhIlw-B.js} +9 -3
- package/dist/_chunks/en-bDhIlw-B.js.map +1 -0
- package/dist/_chunks/{index-c4zRX_sg.mjs → index-gkExFBa0.mjs} +89 -26
- package/dist/_chunks/index-gkExFBa0.mjs.map +1 -0
- package/dist/_chunks/{index-KJa1Rb5F.js → index-l-FvkQlQ.js} +88 -25
- package/dist/_chunks/index-l-FvkQlQ.js.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +197 -34
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +197 -34
- package/dist/server/index.mjs.map +1 -1
- package/package.json +10 -9
- package/dist/_chunks/App-L1jSxCiL.mjs.map +0 -1
- package/dist/_chunks/App-_20W9dYa.js.map +0 -1
- package/dist/_chunks/en-MyLPoISH.mjs.map +0 -1
- package/dist/_chunks/en-gYDqKYFd.js.map +0 -1
- package/dist/_chunks/index-KJa1Rb5F.js.map +0 -1
- package/dist/_chunks/index-c4zRX_sg.mjs.map +0 -1
package/dist/server/index.js
CHANGED
|
@@ -3,6 +3,7 @@ const utils = require("@strapi/utils");
|
|
|
3
3
|
const lodash = require("lodash");
|
|
4
4
|
const _ = require("lodash/fp");
|
|
5
5
|
const EE = require("@strapi/strapi/dist/utils/ee");
|
|
6
|
+
const nodeSchedule = require("node-schedule");
|
|
6
7
|
const yup = require("yup");
|
|
7
8
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
8
9
|
function _interopNamespace(e) {
|
|
@@ -106,6 +107,9 @@ const register = async ({ strapi: strapi2 }) => {
|
|
|
106
107
|
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType);
|
|
107
108
|
}
|
|
108
109
|
};
|
|
110
|
+
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
111
|
+
return strapi2.plugin("content-releases").service(name);
|
|
112
|
+
};
|
|
109
113
|
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
110
114
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
111
115
|
if (features$1.isEnabled("cms-content-releases")) {
|
|
@@ -153,6 +157,24 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
153
157
|
}
|
|
154
158
|
}
|
|
155
159
|
});
|
|
160
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
161
|
+
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
162
|
+
strapi2.log.error(
|
|
163
|
+
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
164
|
+
);
|
|
165
|
+
throw err;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const destroy = async ({ strapi: strapi2 }) => {
|
|
171
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
172
|
+
const scheduledJobs = getService("scheduling", {
|
|
173
|
+
strapi: strapi2
|
|
174
|
+
}).getAll();
|
|
175
|
+
for (const [, job] of scheduledJobs) {
|
|
176
|
+
job.cancel();
|
|
177
|
+
}
|
|
156
178
|
}
|
|
157
179
|
};
|
|
158
180
|
const schema$1 = {
|
|
@@ -181,6 +203,12 @@ const schema$1 = {
|
|
|
181
203
|
releasedAt: {
|
|
182
204
|
type: "datetime"
|
|
183
205
|
},
|
|
206
|
+
scheduledAt: {
|
|
207
|
+
type: "datetime"
|
|
208
|
+
},
|
|
209
|
+
timezone: {
|
|
210
|
+
type: "string"
|
|
211
|
+
},
|
|
184
212
|
actions: {
|
|
185
213
|
type: "relation",
|
|
186
214
|
relation: "oneToMany",
|
|
@@ -243,9 +271,6 @@ const contentTypes = {
|
|
|
243
271
|
release: release$1,
|
|
244
272
|
"release-action": releaseAction$1
|
|
245
273
|
};
|
|
246
|
-
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
247
|
-
return strapi2.plugin("content-releases").service(name);
|
|
248
|
-
};
|
|
249
274
|
const getGroupName = (queryValue) => {
|
|
250
275
|
switch (queryValue) {
|
|
251
276
|
case "contentType":
|
|
@@ -261,17 +286,24 @@ const getGroupName = (queryValue) => {
|
|
|
261
286
|
const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
262
287
|
async create(releaseData, { user }) {
|
|
263
288
|
const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
|
|
264
|
-
const {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
289
|
+
const {
|
|
290
|
+
validatePendingReleasesLimit,
|
|
291
|
+
validateUniqueNameForPendingRelease,
|
|
292
|
+
validateScheduledAtIsLaterThanNow
|
|
293
|
+
} = getService("release-validation", { strapi: strapi2 });
|
|
268
294
|
await Promise.all([
|
|
269
295
|
validatePendingReleasesLimit(),
|
|
270
|
-
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name)
|
|
296
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
297
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
271
298
|
]);
|
|
272
|
-
|
|
299
|
+
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
273
300
|
data: releaseWithCreatorFields
|
|
274
301
|
});
|
|
302
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
|
|
303
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
304
|
+
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
305
|
+
}
|
|
306
|
+
return release2;
|
|
275
307
|
},
|
|
276
308
|
async findOne(id, query = {}) {
|
|
277
309
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
|
|
@@ -366,6 +398,14 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
366
398
|
},
|
|
367
399
|
async update(id, releaseData, { user }) {
|
|
368
400
|
const releaseWithCreatorFields = await utils.setCreatorFields({ user, isEdition: true })(releaseData);
|
|
401
|
+
const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(
|
|
402
|
+
"release-validation",
|
|
403
|
+
{ strapi: strapi2 }
|
|
404
|
+
);
|
|
405
|
+
await Promise.all([
|
|
406
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
407
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
408
|
+
]);
|
|
369
409
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
|
|
370
410
|
if (!release2) {
|
|
371
411
|
throw new utils.errors.NotFoundError(`No release found for id ${id}`);
|
|
@@ -381,6 +421,14 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
381
421
|
// @ts-expect-error see above
|
|
382
422
|
data: releaseWithCreatorFields
|
|
383
423
|
});
|
|
424
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
425
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
426
|
+
if (releaseData.scheduledAt) {
|
|
427
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
428
|
+
} else if (release2.scheduledAt) {
|
|
429
|
+
schedulingService.cancel(id);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
384
432
|
return updatedRelease;
|
|
385
433
|
},
|
|
386
434
|
async createAction(releaseId, action) {
|
|
@@ -538,6 +586,10 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
538
586
|
});
|
|
539
587
|
await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
|
|
540
588
|
});
|
|
589
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
|
|
590
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
591
|
+
await schedulingService.cancel(release2.id);
|
|
592
|
+
}
|
|
541
593
|
return release2;
|
|
542
594
|
},
|
|
543
595
|
async publish(releaseId) {
|
|
@@ -565,27 +617,53 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
565
617
|
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
566
618
|
throw new utils.errors.ValidationError("No entries to publish");
|
|
567
619
|
}
|
|
568
|
-
const
|
|
620
|
+
const collectionTypeActions = {};
|
|
621
|
+
const singleTypeActions = [];
|
|
569
622
|
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
570
623
|
const contentTypeUid = action.contentType;
|
|
571
|
-
if (
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
624
|
+
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
625
|
+
if (!collectionTypeActions[contentTypeUid]) {
|
|
626
|
+
collectionTypeActions[contentTypeUid] = {
|
|
627
|
+
entriestoPublishIds: [],
|
|
628
|
+
entriesToUnpublishIds: []
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
if (action.type === "publish") {
|
|
632
|
+
collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
633
|
+
} else {
|
|
634
|
+
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
635
|
+
}
|
|
579
636
|
} else {
|
|
580
|
-
|
|
637
|
+
singleTypeActions.push({
|
|
638
|
+
uid: contentTypeUid,
|
|
639
|
+
action: action.type,
|
|
640
|
+
id: action.entry.id
|
|
641
|
+
});
|
|
581
642
|
}
|
|
582
643
|
}
|
|
583
644
|
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
584
645
|
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
585
646
|
await strapi2.db.transaction(async () => {
|
|
586
|
-
for (const
|
|
647
|
+
for (const { uid, action, id } of singleTypeActions) {
|
|
648
|
+
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
649
|
+
const entry = await strapi2.entityService.findOne(uid, id, { populate });
|
|
650
|
+
try {
|
|
651
|
+
if (action === "publish") {
|
|
652
|
+
await entityManagerService.publish(entry, uid);
|
|
653
|
+
} else {
|
|
654
|
+
await entityManagerService.unpublish(entry, uid);
|
|
655
|
+
}
|
|
656
|
+
} catch (error) {
|
|
657
|
+
if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
|
|
658
|
+
;
|
|
659
|
+
else {
|
|
660
|
+
throw error;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
587
665
|
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
588
|
-
const { entriestoPublishIds, entriesToUnpublishIds } =
|
|
666
|
+
const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
|
|
589
667
|
const entriesToPublish = await strapi2.entityService.findMany(
|
|
590
668
|
contentTypeUid,
|
|
591
669
|
{
|
|
@@ -711,27 +789,93 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
711
789
|
throw new utils.errors.ValidationError("You have reached the maximum number of pending releases");
|
|
712
790
|
}
|
|
713
791
|
},
|
|
714
|
-
async validateUniqueNameForPendingRelease(name) {
|
|
792
|
+
async validateUniqueNameForPendingRelease(name, id) {
|
|
715
793
|
const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
|
|
716
794
|
filters: {
|
|
717
795
|
releasedAt: {
|
|
718
796
|
$null: true
|
|
719
797
|
},
|
|
720
|
-
name
|
|
798
|
+
name,
|
|
799
|
+
...id && { id: { $ne: id } }
|
|
721
800
|
}
|
|
722
801
|
});
|
|
723
802
|
const isNameUnique = pendingReleases.length === 0;
|
|
724
803
|
if (!isNameUnique) {
|
|
725
804
|
throw new utils.errors.ValidationError(`Release with name ${name} already exists`);
|
|
726
805
|
}
|
|
806
|
+
},
|
|
807
|
+
async validateScheduledAtIsLaterThanNow(scheduledAt) {
|
|
808
|
+
if (scheduledAt && new Date(scheduledAt) <= /* @__PURE__ */ new Date()) {
|
|
809
|
+
throw new utils.errors.ValidationError("Scheduled at must be later than now");
|
|
810
|
+
}
|
|
727
811
|
}
|
|
728
812
|
});
|
|
813
|
+
const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
814
|
+
const scheduledJobs = /* @__PURE__ */ new Map();
|
|
815
|
+
return {
|
|
816
|
+
async set(releaseId, scheduleDate) {
|
|
817
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId, releasedAt: null } });
|
|
818
|
+
if (!release2) {
|
|
819
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
820
|
+
}
|
|
821
|
+
const job = nodeSchedule.scheduleJob(scheduleDate, async () => {
|
|
822
|
+
try {
|
|
823
|
+
await getService("release").publish(releaseId);
|
|
824
|
+
} catch (error) {
|
|
825
|
+
}
|
|
826
|
+
this.cancel(releaseId);
|
|
827
|
+
});
|
|
828
|
+
if (scheduledJobs.has(releaseId)) {
|
|
829
|
+
this.cancel(releaseId);
|
|
830
|
+
}
|
|
831
|
+
scheduledJobs.set(releaseId, job);
|
|
832
|
+
return scheduledJobs;
|
|
833
|
+
},
|
|
834
|
+
cancel(releaseId) {
|
|
835
|
+
if (scheduledJobs.has(releaseId)) {
|
|
836
|
+
scheduledJobs.get(releaseId).cancel();
|
|
837
|
+
scheduledJobs.delete(releaseId);
|
|
838
|
+
}
|
|
839
|
+
return scheduledJobs;
|
|
840
|
+
},
|
|
841
|
+
getAll() {
|
|
842
|
+
return scheduledJobs;
|
|
843
|
+
},
|
|
844
|
+
/**
|
|
845
|
+
* On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released
|
|
846
|
+
* This is useful in case the server was restarted and the scheduled jobs were lost
|
|
847
|
+
* This also could be used to sync different Strapi instances in case of a cluster
|
|
848
|
+
*/
|
|
849
|
+
async syncFromDatabase() {
|
|
850
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
851
|
+
where: {
|
|
852
|
+
scheduledAt: {
|
|
853
|
+
$gte: /* @__PURE__ */ new Date()
|
|
854
|
+
},
|
|
855
|
+
releasedAt: null
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
for (const release2 of releases) {
|
|
859
|
+
this.set(release2.id, release2.scheduledAt);
|
|
860
|
+
}
|
|
861
|
+
return scheduledJobs;
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
};
|
|
729
865
|
const services = {
|
|
730
866
|
release: createReleaseService,
|
|
731
|
-
"release-validation": createReleaseValidationService
|
|
867
|
+
"release-validation": createReleaseValidationService,
|
|
868
|
+
...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
|
|
732
869
|
};
|
|
733
870
|
const RELEASE_SCHEMA = yup__namespace.object().shape({
|
|
734
|
-
name: yup__namespace.string().trim().required()
|
|
871
|
+
name: yup__namespace.string().trim().required(),
|
|
872
|
+
// scheduledAt is a date, but we always receive strings from the client
|
|
873
|
+
scheduledAt: yup__namespace.string().nullable(),
|
|
874
|
+
timezone: yup__namespace.string().when("scheduledAt", {
|
|
875
|
+
is: (scheduledAt) => !!scheduledAt,
|
|
876
|
+
then: yup__namespace.string().required(),
|
|
877
|
+
otherwise: yup__namespace.string().nullable()
|
|
878
|
+
})
|
|
735
879
|
}).required().noUnknown();
|
|
736
880
|
const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
|
|
737
881
|
const releaseController = {
|
|
@@ -771,19 +915,18 @@ const releaseController = {
|
|
|
771
915
|
const id = ctx.params.id;
|
|
772
916
|
const releaseService = getService("release", { strapi });
|
|
773
917
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
});
|
|
778
|
-
const sanitizedRelease = await permissionsManager.sanitizeOutput(release2);
|
|
918
|
+
if (!release2) {
|
|
919
|
+
throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
|
|
920
|
+
}
|
|
779
921
|
const count = await releaseService.countActions({
|
|
780
922
|
filters: {
|
|
781
923
|
release: id
|
|
782
924
|
}
|
|
783
925
|
});
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
926
|
+
const sanitizedRelease = {
|
|
927
|
+
...release2,
|
|
928
|
+
createdBy: release2.createdBy ? strapi.admin.services.user.sanitizeUser(release2.createdBy) : null
|
|
929
|
+
};
|
|
787
930
|
const data = {
|
|
788
931
|
...sanitizedRelease,
|
|
789
932
|
actions: {
|
|
@@ -836,8 +979,27 @@ const releaseController = {
|
|
|
836
979
|
const id = ctx.params.id;
|
|
837
980
|
const releaseService = getService("release", { strapi });
|
|
838
981
|
const release2 = await releaseService.publish(id, { user });
|
|
982
|
+
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
983
|
+
releaseService.countActions({
|
|
984
|
+
filters: {
|
|
985
|
+
release: id,
|
|
986
|
+
type: "publish"
|
|
987
|
+
}
|
|
988
|
+
}),
|
|
989
|
+
releaseService.countActions({
|
|
990
|
+
filters: {
|
|
991
|
+
release: id,
|
|
992
|
+
type: "unpublish"
|
|
993
|
+
}
|
|
994
|
+
})
|
|
995
|
+
]);
|
|
839
996
|
ctx.body = {
|
|
840
|
-
data: release2
|
|
997
|
+
data: release2,
|
|
998
|
+
meta: {
|
|
999
|
+
totalEntries: countPublishActions + countUnpublishActions,
|
|
1000
|
+
totalPublishedEntries: countPublishActions,
|
|
1001
|
+
totalUnpublishedEntries: countUnpublishActions
|
|
1002
|
+
}
|
|
841
1003
|
};
|
|
842
1004
|
}
|
|
843
1005
|
};
|
|
@@ -1109,6 +1271,7 @@ const getPlugin = () => {
|
|
|
1109
1271
|
return {
|
|
1110
1272
|
register,
|
|
1111
1273
|
bootstrap,
|
|
1274
|
+
destroy,
|
|
1112
1275
|
contentTypes,
|
|
1113
1276
|
services,
|
|
1114
1277
|
controllers,
|