@strapi/content-releases 0.0.0-next.6d59515520a3850456f256fb0e4c54b75054ddf4 → 0.0.0-next.aa7c7ec6724534e157d8a23fe85ee8318dabbf37
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-L1jSxCiL.mjs → App-8FCxPK8-.mjs} +252 -195
- package/dist/_chunks/App-8FCxPK8-.mjs.map +1 -0
- package/dist/_chunks/{App-_20W9dYa.js → App-pspKUC-W.js} +248 -191
- package/dist/_chunks/App-pspKUC-W.js.map +1 -0
- package/dist/_chunks/{en-MyLPoISH.mjs → en-m9eTk4UF.mjs} +7 -3
- package/dist/_chunks/en-m9eTk4UF.mjs.map +1 -0
- package/dist/_chunks/{en-gYDqKYFd.js → en-r9YocBH0.js} +7 -3
- package/dist/_chunks/en-r9YocBH0.js.map +1 -0
- package/dist/_chunks/{index-c4zRX_sg.mjs → index-8aK7GzI5.mjs} +75 -26
- package/dist/_chunks/index-8aK7GzI5.mjs.map +1 -0
- package/dist/_chunks/{index-KJa1Rb5F.js → index-nGaPcY9m.js} +74 -25
- package/dist/_chunks/index-nGaPcY9m.js.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +185 -34
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +185 -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,9 @@ const schema$1 = {
|
|
|
181
203
|
releasedAt: {
|
|
182
204
|
type: "datetime"
|
|
183
205
|
},
|
|
206
|
+
scheduledAt: {
|
|
207
|
+
type: "datetime"
|
|
208
|
+
},
|
|
184
209
|
actions: {
|
|
185
210
|
type: "relation",
|
|
186
211
|
relation: "oneToMany",
|
|
@@ -243,9 +268,6 @@ const contentTypes = {
|
|
|
243
268
|
release: release$1,
|
|
244
269
|
"release-action": releaseAction$1
|
|
245
270
|
};
|
|
246
|
-
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
247
|
-
return strapi2.plugin("content-releases").service(name);
|
|
248
|
-
};
|
|
249
271
|
const getGroupName = (queryValue) => {
|
|
250
272
|
switch (queryValue) {
|
|
251
273
|
case "contentType":
|
|
@@ -261,17 +283,24 @@ const getGroupName = (queryValue) => {
|
|
|
261
283
|
const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
262
284
|
async create(releaseData, { user }) {
|
|
263
285
|
const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
|
|
264
|
-
const {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
286
|
+
const {
|
|
287
|
+
validatePendingReleasesLimit,
|
|
288
|
+
validateUniqueNameForPendingRelease,
|
|
289
|
+
validateScheduledAtIsLaterThanNow
|
|
290
|
+
} = getService("release-validation", { strapi: strapi2 });
|
|
268
291
|
await Promise.all([
|
|
269
292
|
validatePendingReleasesLimit(),
|
|
270
|
-
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name)
|
|
293
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
294
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
271
295
|
]);
|
|
272
|
-
|
|
296
|
+
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
273
297
|
data: releaseWithCreatorFields
|
|
274
298
|
});
|
|
299
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
|
|
300
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
301
|
+
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
302
|
+
}
|
|
303
|
+
return release2;
|
|
275
304
|
},
|
|
276
305
|
async findOne(id, query = {}) {
|
|
277
306
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
|
|
@@ -366,6 +395,14 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
366
395
|
},
|
|
367
396
|
async update(id, releaseData, { user }) {
|
|
368
397
|
const releaseWithCreatorFields = await utils.setCreatorFields({ user, isEdition: true })(releaseData);
|
|
398
|
+
const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(
|
|
399
|
+
"release-validation",
|
|
400
|
+
{ strapi: strapi2 }
|
|
401
|
+
);
|
|
402
|
+
await Promise.all([
|
|
403
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
404
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
405
|
+
]);
|
|
369
406
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
|
|
370
407
|
if (!release2) {
|
|
371
408
|
throw new utils.errors.NotFoundError(`No release found for id ${id}`);
|
|
@@ -381,6 +418,14 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
381
418
|
// @ts-expect-error see above
|
|
382
419
|
data: releaseWithCreatorFields
|
|
383
420
|
});
|
|
421
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
422
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
423
|
+
if (releaseData.scheduledAt) {
|
|
424
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
425
|
+
} else if (release2.scheduledAt) {
|
|
426
|
+
schedulingService.cancel(id);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
384
429
|
return updatedRelease;
|
|
385
430
|
},
|
|
386
431
|
async createAction(releaseId, action) {
|
|
@@ -565,27 +610,53 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
565
610
|
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
566
611
|
throw new utils.errors.ValidationError("No entries to publish");
|
|
567
612
|
}
|
|
568
|
-
const
|
|
613
|
+
const collectionTypeActions = {};
|
|
614
|
+
const singleTypeActions = [];
|
|
569
615
|
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
570
616
|
const contentTypeUid = action.contentType;
|
|
571
|
-
if (
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
617
|
+
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
618
|
+
if (!collectionTypeActions[contentTypeUid]) {
|
|
619
|
+
collectionTypeActions[contentTypeUid] = {
|
|
620
|
+
entriestoPublishIds: [],
|
|
621
|
+
entriesToUnpublishIds: []
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
if (action.type === "publish") {
|
|
625
|
+
collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
626
|
+
} else {
|
|
627
|
+
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
628
|
+
}
|
|
579
629
|
} else {
|
|
580
|
-
|
|
630
|
+
singleTypeActions.push({
|
|
631
|
+
uid: contentTypeUid,
|
|
632
|
+
action: action.type,
|
|
633
|
+
id: action.entry.id
|
|
634
|
+
});
|
|
581
635
|
}
|
|
582
636
|
}
|
|
583
637
|
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
584
638
|
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
585
639
|
await strapi2.db.transaction(async () => {
|
|
586
|
-
for (const
|
|
640
|
+
for (const { uid, action, id } of singleTypeActions) {
|
|
641
|
+
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
642
|
+
const entry = await strapi2.entityService.findOne(uid, id, { populate });
|
|
643
|
+
try {
|
|
644
|
+
if (action === "publish") {
|
|
645
|
+
await entityManagerService.publish(entry, uid);
|
|
646
|
+
} else {
|
|
647
|
+
await entityManagerService.unpublish(entry, uid);
|
|
648
|
+
}
|
|
649
|
+
} catch (error) {
|
|
650
|
+
if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
|
|
651
|
+
;
|
|
652
|
+
else {
|
|
653
|
+
throw error;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
587
658
|
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
588
|
-
const { entriestoPublishIds, entriesToUnpublishIds } =
|
|
659
|
+
const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
|
|
589
660
|
const entriesToPublish = await strapi2.entityService.findMany(
|
|
590
661
|
contentTypeUid,
|
|
591
662
|
{
|
|
@@ -711,27 +782,88 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
711
782
|
throw new utils.errors.ValidationError("You have reached the maximum number of pending releases");
|
|
712
783
|
}
|
|
713
784
|
},
|
|
714
|
-
async validateUniqueNameForPendingRelease(name) {
|
|
785
|
+
async validateUniqueNameForPendingRelease(name, id) {
|
|
715
786
|
const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
|
|
716
787
|
filters: {
|
|
717
788
|
releasedAt: {
|
|
718
789
|
$null: true
|
|
719
790
|
},
|
|
720
|
-
name
|
|
791
|
+
name,
|
|
792
|
+
...id && { id: { $ne: id } }
|
|
721
793
|
}
|
|
722
794
|
});
|
|
723
795
|
const isNameUnique = pendingReleases.length === 0;
|
|
724
796
|
if (!isNameUnique) {
|
|
725
797
|
throw new utils.errors.ValidationError(`Release with name ${name} already exists`);
|
|
726
798
|
}
|
|
799
|
+
},
|
|
800
|
+
async validateScheduledAtIsLaterThanNow(scheduledAt) {
|
|
801
|
+
if (scheduledAt && new Date(scheduledAt) <= /* @__PURE__ */ new Date()) {
|
|
802
|
+
throw new utils.errors.ValidationError("Scheduled at must be later than now");
|
|
803
|
+
}
|
|
727
804
|
}
|
|
728
805
|
});
|
|
806
|
+
const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
807
|
+
const scheduledJobs = /* @__PURE__ */ new Map();
|
|
808
|
+
return {
|
|
809
|
+
async set(releaseId, scheduleDate) {
|
|
810
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId, releasedAt: null } });
|
|
811
|
+
if (!release2) {
|
|
812
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
813
|
+
}
|
|
814
|
+
const job = nodeSchedule.scheduleJob(scheduleDate, async () => {
|
|
815
|
+
try {
|
|
816
|
+
await getService("release").publish(releaseId);
|
|
817
|
+
} catch (error) {
|
|
818
|
+
}
|
|
819
|
+
this.cancel(releaseId);
|
|
820
|
+
});
|
|
821
|
+
if (scheduledJobs.has(releaseId)) {
|
|
822
|
+
this.cancel(releaseId);
|
|
823
|
+
}
|
|
824
|
+
scheduledJobs.set(releaseId, job);
|
|
825
|
+
return scheduledJobs;
|
|
826
|
+
},
|
|
827
|
+
cancel(releaseId) {
|
|
828
|
+
if (scheduledJobs.has(releaseId)) {
|
|
829
|
+
scheduledJobs.get(releaseId).cancel();
|
|
830
|
+
scheduledJobs.delete(releaseId);
|
|
831
|
+
}
|
|
832
|
+
return scheduledJobs;
|
|
833
|
+
},
|
|
834
|
+
getAll() {
|
|
835
|
+
return scheduledJobs;
|
|
836
|
+
},
|
|
837
|
+
/**
|
|
838
|
+
* On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released
|
|
839
|
+
* This is useful in case the server was restarted and the scheduled jobs were lost
|
|
840
|
+
* This also could be used to sync different Strapi instances in case of a cluster
|
|
841
|
+
*/
|
|
842
|
+
async syncFromDatabase() {
|
|
843
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
844
|
+
where: {
|
|
845
|
+
scheduledAt: {
|
|
846
|
+
$gte: /* @__PURE__ */ new Date()
|
|
847
|
+
},
|
|
848
|
+
releasedAt: null
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
for (const release2 of releases) {
|
|
852
|
+
this.set(release2.id, release2.scheduledAt);
|
|
853
|
+
}
|
|
854
|
+
return scheduledJobs;
|
|
855
|
+
}
|
|
856
|
+
};
|
|
857
|
+
};
|
|
729
858
|
const services = {
|
|
730
859
|
release: createReleaseService,
|
|
731
|
-
"release-validation": createReleaseValidationService
|
|
860
|
+
"release-validation": createReleaseValidationService,
|
|
861
|
+
...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
|
|
732
862
|
};
|
|
733
863
|
const RELEASE_SCHEMA = yup__namespace.object().shape({
|
|
734
|
-
name: yup__namespace.string().trim().required()
|
|
864
|
+
name: yup__namespace.string().trim().required(),
|
|
865
|
+
// scheduledAt is a date, but we always receive strings from the client
|
|
866
|
+
scheduledAt: yup__namespace.string().nullable()
|
|
735
867
|
}).required().noUnknown();
|
|
736
868
|
const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
|
|
737
869
|
const releaseController = {
|
|
@@ -771,19 +903,18 @@ const releaseController = {
|
|
|
771
903
|
const id = ctx.params.id;
|
|
772
904
|
const releaseService = getService("release", { strapi });
|
|
773
905
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
});
|
|
778
|
-
const sanitizedRelease = await permissionsManager.sanitizeOutput(release2);
|
|
906
|
+
if (!release2) {
|
|
907
|
+
throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
|
|
908
|
+
}
|
|
779
909
|
const count = await releaseService.countActions({
|
|
780
910
|
filters: {
|
|
781
911
|
release: id
|
|
782
912
|
}
|
|
783
913
|
});
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
914
|
+
const sanitizedRelease = {
|
|
915
|
+
...release2,
|
|
916
|
+
createdBy: release2.createdBy ? strapi.admin.services.user.sanitizeUser(release2.createdBy) : null
|
|
917
|
+
};
|
|
787
918
|
const data = {
|
|
788
919
|
...sanitizedRelease,
|
|
789
920
|
actions: {
|
|
@@ -836,8 +967,27 @@ const releaseController = {
|
|
|
836
967
|
const id = ctx.params.id;
|
|
837
968
|
const releaseService = getService("release", { strapi });
|
|
838
969
|
const release2 = await releaseService.publish(id, { user });
|
|
970
|
+
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
971
|
+
releaseService.countActions({
|
|
972
|
+
filters: {
|
|
973
|
+
release: id,
|
|
974
|
+
type: "publish"
|
|
975
|
+
}
|
|
976
|
+
}),
|
|
977
|
+
releaseService.countActions({
|
|
978
|
+
filters: {
|
|
979
|
+
release: id,
|
|
980
|
+
type: "unpublish"
|
|
981
|
+
}
|
|
982
|
+
})
|
|
983
|
+
]);
|
|
839
984
|
ctx.body = {
|
|
840
|
-
data: release2
|
|
985
|
+
data: release2,
|
|
986
|
+
meta: {
|
|
987
|
+
totalEntries: countPublishActions + countUnpublishActions,
|
|
988
|
+
totalPublishedEntries: countPublishActions,
|
|
989
|
+
totalUnpublishedEntries: countUnpublishActions
|
|
990
|
+
}
|
|
841
991
|
};
|
|
842
992
|
}
|
|
843
993
|
};
|
|
@@ -1109,6 +1259,7 @@ const getPlugin = () => {
|
|
|
1109
1259
|
return {
|
|
1110
1260
|
register,
|
|
1111
1261
|
bootstrap,
|
|
1262
|
+
destroy,
|
|
1112
1263
|
contentTypes,
|
|
1113
1264
|
services,
|
|
1114
1265
|
controllers,
|