@strapi/content-releases 0.0.0-next.c5f067b5650921187770124e9b6c8186e805e242 → 0.0.0-next.d470b4f75cf00f24f440b80300f1c833c322b871
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-5PsAyVt2.js → App-1hHIqUoZ.js} +127 -112
- package/dist/_chunks/App-1hHIqUoZ.js.map +1 -0
- package/dist/_chunks/{App-3ycH2d3s.mjs → App-U6GbyLIE.mjs} +129 -114
- 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-SOqjCdyh.mjs → en-GqXgfmzl.mjs} +6 -3
- package/dist/_chunks/en-GqXgfmzl.mjs.map +1 -0
- package/dist/_chunks/{en-2DuPv5k0.js → en-bDhIlw-B.js} +6 -3
- package/dist/_chunks/en-bDhIlw-B.js.map +1 -0
- package/dist/_chunks/{index-4gUWuCQV.mjs → index-gkExFBa0.mjs} +57 -16
- package/dist/_chunks/index-gkExFBa0.mjs.map +1 -0
- package/dist/_chunks/{index-D57Rztnc.js → index-l-FvkQlQ.js} +57 -16
- 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 +139 -21
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +139 -21
- package/dist/server/index.mjs.map +1 -1
- package/package.json +10 -9
- package/dist/_chunks/App-3ycH2d3s.mjs.map +0 -1
- package/dist/_chunks/App-5PsAyVt2.js.map +0 -1
- package/dist/_chunks/en-2DuPv5k0.js.map +0 -1
- package/dist/_chunks/en-SOqjCdyh.mjs.map +0 -1
- package/dist/_chunks/index-4gUWuCQV.mjs.map +0 -1
- package/dist/_chunks/index-D57Rztnc.js.map +0 -1
package/dist/server/index.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { contentTypes as contentTypes$1, mapAsync, setCreatorFields, errors, val
|
|
|
2
2
|
import { difference, keys } from "lodash";
|
|
3
3
|
import _ from "lodash/fp";
|
|
4
4
|
import EE from "@strapi/strapi/dist/utils/ee";
|
|
5
|
+
import { scheduleJob } from "node-schedule";
|
|
5
6
|
import * as yup from "yup";
|
|
6
7
|
const RELEASE_MODEL_UID = "plugin::content-releases.release";
|
|
7
8
|
const RELEASE_ACTION_MODEL_UID = "plugin::content-releases.release-action";
|
|
@@ -83,6 +84,9 @@ const register = async ({ strapi: strapi2 }) => {
|
|
|
83
84
|
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType);
|
|
84
85
|
}
|
|
85
86
|
};
|
|
87
|
+
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
88
|
+
return strapi2.plugin("content-releases").service(name);
|
|
89
|
+
};
|
|
86
90
|
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
87
91
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
88
92
|
if (features$1.isEnabled("cms-content-releases")) {
|
|
@@ -130,6 +134,24 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
130
134
|
}
|
|
131
135
|
}
|
|
132
136
|
});
|
|
137
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
138
|
+
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
139
|
+
strapi2.log.error(
|
|
140
|
+
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
141
|
+
);
|
|
142
|
+
throw err;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const destroy = async ({ strapi: strapi2 }) => {
|
|
148
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
149
|
+
const scheduledJobs = getService("scheduling", {
|
|
150
|
+
strapi: strapi2
|
|
151
|
+
}).getAll();
|
|
152
|
+
for (const [, job] of scheduledJobs) {
|
|
153
|
+
job.cancel();
|
|
154
|
+
}
|
|
133
155
|
}
|
|
134
156
|
};
|
|
135
157
|
const schema$1 = {
|
|
@@ -158,6 +180,12 @@ const schema$1 = {
|
|
|
158
180
|
releasedAt: {
|
|
159
181
|
type: "datetime"
|
|
160
182
|
},
|
|
183
|
+
scheduledAt: {
|
|
184
|
+
type: "datetime"
|
|
185
|
+
},
|
|
186
|
+
timezone: {
|
|
187
|
+
type: "string"
|
|
188
|
+
},
|
|
161
189
|
actions: {
|
|
162
190
|
type: "relation",
|
|
163
191
|
relation: "oneToMany",
|
|
@@ -220,9 +248,6 @@ const contentTypes = {
|
|
|
220
248
|
release: release$1,
|
|
221
249
|
"release-action": releaseAction$1
|
|
222
250
|
};
|
|
223
|
-
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
224
|
-
return strapi2.plugin("content-releases").service(name);
|
|
225
|
-
};
|
|
226
251
|
const getGroupName = (queryValue) => {
|
|
227
252
|
switch (queryValue) {
|
|
228
253
|
case "contentType":
|
|
@@ -238,17 +263,24 @@ const getGroupName = (queryValue) => {
|
|
|
238
263
|
const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
239
264
|
async create(releaseData, { user }) {
|
|
240
265
|
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
|
241
|
-
const {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
266
|
+
const {
|
|
267
|
+
validatePendingReleasesLimit,
|
|
268
|
+
validateUniqueNameForPendingRelease,
|
|
269
|
+
validateScheduledAtIsLaterThanNow
|
|
270
|
+
} = getService("release-validation", { strapi: strapi2 });
|
|
245
271
|
await Promise.all([
|
|
246
272
|
validatePendingReleasesLimit(),
|
|
247
|
-
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name)
|
|
273
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
274
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
248
275
|
]);
|
|
249
|
-
|
|
276
|
+
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
250
277
|
data: releaseWithCreatorFields
|
|
251
278
|
});
|
|
279
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
|
|
280
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
281
|
+
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
282
|
+
}
|
|
283
|
+
return release2;
|
|
252
284
|
},
|
|
253
285
|
async findOne(id, query = {}) {
|
|
254
286
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
|
|
@@ -343,6 +375,14 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
343
375
|
},
|
|
344
376
|
async update(id, releaseData, { user }) {
|
|
345
377
|
const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(releaseData);
|
|
378
|
+
const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(
|
|
379
|
+
"release-validation",
|
|
380
|
+
{ strapi: strapi2 }
|
|
381
|
+
);
|
|
382
|
+
await Promise.all([
|
|
383
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
384
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
385
|
+
]);
|
|
346
386
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
|
|
347
387
|
if (!release2) {
|
|
348
388
|
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
@@ -358,6 +398,14 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
358
398
|
// @ts-expect-error see above
|
|
359
399
|
data: releaseWithCreatorFields
|
|
360
400
|
});
|
|
401
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
402
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
403
|
+
if (releaseData.scheduledAt) {
|
|
404
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
405
|
+
} else if (release2.scheduledAt) {
|
|
406
|
+
schedulingService.cancel(id);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
361
409
|
return updatedRelease;
|
|
362
410
|
},
|
|
363
411
|
async createAction(releaseId, action) {
|
|
@@ -515,6 +563,10 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
515
563
|
});
|
|
516
564
|
await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
|
|
517
565
|
});
|
|
566
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
|
|
567
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
568
|
+
await schedulingService.cancel(release2.id);
|
|
569
|
+
}
|
|
518
570
|
return release2;
|
|
519
571
|
},
|
|
520
572
|
async publish(releaseId) {
|
|
@@ -714,27 +766,93 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
714
766
|
throw new errors.ValidationError("You have reached the maximum number of pending releases");
|
|
715
767
|
}
|
|
716
768
|
},
|
|
717
|
-
async validateUniqueNameForPendingRelease(name) {
|
|
769
|
+
async validateUniqueNameForPendingRelease(name, id) {
|
|
718
770
|
const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
|
|
719
771
|
filters: {
|
|
720
772
|
releasedAt: {
|
|
721
773
|
$null: true
|
|
722
774
|
},
|
|
723
|
-
name
|
|
775
|
+
name,
|
|
776
|
+
...id && { id: { $ne: id } }
|
|
724
777
|
}
|
|
725
778
|
});
|
|
726
779
|
const isNameUnique = pendingReleases.length === 0;
|
|
727
780
|
if (!isNameUnique) {
|
|
728
781
|
throw new errors.ValidationError(`Release with name ${name} already exists`);
|
|
729
782
|
}
|
|
783
|
+
},
|
|
784
|
+
async validateScheduledAtIsLaterThanNow(scheduledAt) {
|
|
785
|
+
if (scheduledAt && new Date(scheduledAt) <= /* @__PURE__ */ new Date()) {
|
|
786
|
+
throw new errors.ValidationError("Scheduled at must be later than now");
|
|
787
|
+
}
|
|
730
788
|
}
|
|
731
789
|
});
|
|
790
|
+
const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
791
|
+
const scheduledJobs = /* @__PURE__ */ new Map();
|
|
792
|
+
return {
|
|
793
|
+
async set(releaseId, scheduleDate) {
|
|
794
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId, releasedAt: null } });
|
|
795
|
+
if (!release2) {
|
|
796
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
797
|
+
}
|
|
798
|
+
const job = scheduleJob(scheduleDate, async () => {
|
|
799
|
+
try {
|
|
800
|
+
await getService("release").publish(releaseId);
|
|
801
|
+
} catch (error) {
|
|
802
|
+
}
|
|
803
|
+
this.cancel(releaseId);
|
|
804
|
+
});
|
|
805
|
+
if (scheduledJobs.has(releaseId)) {
|
|
806
|
+
this.cancel(releaseId);
|
|
807
|
+
}
|
|
808
|
+
scheduledJobs.set(releaseId, job);
|
|
809
|
+
return scheduledJobs;
|
|
810
|
+
},
|
|
811
|
+
cancel(releaseId) {
|
|
812
|
+
if (scheduledJobs.has(releaseId)) {
|
|
813
|
+
scheduledJobs.get(releaseId).cancel();
|
|
814
|
+
scheduledJobs.delete(releaseId);
|
|
815
|
+
}
|
|
816
|
+
return scheduledJobs;
|
|
817
|
+
},
|
|
818
|
+
getAll() {
|
|
819
|
+
return scheduledJobs;
|
|
820
|
+
},
|
|
821
|
+
/**
|
|
822
|
+
* On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released
|
|
823
|
+
* This is useful in case the server was restarted and the scheduled jobs were lost
|
|
824
|
+
* This also could be used to sync different Strapi instances in case of a cluster
|
|
825
|
+
*/
|
|
826
|
+
async syncFromDatabase() {
|
|
827
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
828
|
+
where: {
|
|
829
|
+
scheduledAt: {
|
|
830
|
+
$gte: /* @__PURE__ */ new Date()
|
|
831
|
+
},
|
|
832
|
+
releasedAt: null
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
for (const release2 of releases) {
|
|
836
|
+
this.set(release2.id, release2.scheduledAt);
|
|
837
|
+
}
|
|
838
|
+
return scheduledJobs;
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
};
|
|
732
842
|
const services = {
|
|
733
843
|
release: createReleaseService,
|
|
734
|
-
"release-validation": createReleaseValidationService
|
|
844
|
+
"release-validation": createReleaseValidationService,
|
|
845
|
+
...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
|
|
735
846
|
};
|
|
736
847
|
const RELEASE_SCHEMA = yup.object().shape({
|
|
737
|
-
name: yup.string().trim().required()
|
|
848
|
+
name: yup.string().trim().required(),
|
|
849
|
+
// scheduledAt is a date, but we always receive strings from the client
|
|
850
|
+
scheduledAt: yup.string().nullable(),
|
|
851
|
+
timezone: yup.string().when("scheduledAt", {
|
|
852
|
+
is: (scheduledAt) => !!scheduledAt,
|
|
853
|
+
then: yup.string().required(),
|
|
854
|
+
otherwise: yup.string().nullable()
|
|
855
|
+
})
|
|
738
856
|
}).required().noUnknown();
|
|
739
857
|
const validateRelease = validateYupSchema(RELEASE_SCHEMA);
|
|
740
858
|
const releaseController = {
|
|
@@ -774,19 +892,18 @@ const releaseController = {
|
|
|
774
892
|
const id = ctx.params.id;
|
|
775
893
|
const releaseService = getService("release", { strapi });
|
|
776
894
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
});
|
|
781
|
-
const sanitizedRelease = await permissionsManager.sanitizeOutput(release2);
|
|
895
|
+
if (!release2) {
|
|
896
|
+
throw new errors.NotFoundError(`Release not found for id: ${id}`);
|
|
897
|
+
}
|
|
782
898
|
const count = await releaseService.countActions({
|
|
783
899
|
filters: {
|
|
784
900
|
release: id
|
|
785
901
|
}
|
|
786
902
|
});
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
903
|
+
const sanitizedRelease = {
|
|
904
|
+
...release2,
|
|
905
|
+
createdBy: release2.createdBy ? strapi.admin.services.user.sanitizeUser(release2.createdBy) : null
|
|
906
|
+
};
|
|
790
907
|
const data = {
|
|
791
908
|
...sanitizedRelease,
|
|
792
909
|
actions: {
|
|
@@ -1131,6 +1248,7 @@ const getPlugin = () => {
|
|
|
1131
1248
|
return {
|
|
1132
1249
|
register,
|
|
1133
1250
|
bootstrap,
|
|
1251
|
+
destroy,
|
|
1134
1252
|
contentTypes,
|
|
1135
1253
|
services,
|
|
1136
1254
|
controllers,
|