@strapi/content-releases 4.20.3 → 4.20.4
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-hWBsb1nt.mjs → App-6ugQxqYE.mjs} +571 -542
- package/dist/_chunks/App-6ugQxqYE.mjs.map +1 -0
- package/dist/_chunks/{App-C_iroyIu.js → App-P1kyM3gT.js} +592 -563
- package/dist/_chunks/App-P1kyM3gT.js.map +1 -0
- package/dist/_chunks/{en-UyU8mm8l.mjs → en-WuuhP6Bn.mjs} +3 -3
- package/dist/_chunks/en-WuuhP6Bn.mjs.map +1 -0
- package/dist/_chunks/{en-oj7ZfWI3.js → en-gcJJ5htG.js} +3 -3
- package/dist/_chunks/en-gcJJ5htG.js.map +1 -0
- package/dist/_chunks/{index-SDpSekBU.js → index-2xzbhaQP.js} +3 -3
- package/dist/_chunks/{index-SDpSekBU.js.map → index-2xzbhaQP.js.map} +1 -1
- package/dist/_chunks/{index-rEfNT9PC.mjs → index-_eBuegHN.mjs} +13 -13
- package/dist/_chunks/{index-rEfNT9PC.mjs.map → index-_eBuegHN.mjs.map} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +298 -24
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +297 -24
- package/dist/server/index.mjs.map +1 -1
- package/package.json +11 -11
- package/dist/_chunks/App-C_iroyIu.js.map +0 -1
- package/dist/_chunks/App-hWBsb1nt.mjs.map +0 -1
- package/dist/_chunks/en-UyU8mm8l.mjs.map +0 -1
- package/dist/_chunks/en-oj7ZfWI3.js.map +0 -1
package/dist/server/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const utils = require("@strapi/utils");
|
|
3
|
+
const isEqual = require("lodash/isEqual");
|
|
3
4
|
const lodash = require("lodash");
|
|
4
5
|
const _ = require("lodash/fp");
|
|
5
6
|
const EE = require("@strapi/strapi/dist/utils/ee");
|
|
@@ -24,6 +25,7 @@ function _interopNamespace(e) {
|
|
|
24
25
|
n.default = e;
|
|
25
26
|
return Object.freeze(n);
|
|
26
27
|
}
|
|
28
|
+
const isEqual__default = /* @__PURE__ */ _interopDefault(isEqual);
|
|
27
29
|
const ___default = /* @__PURE__ */ _interopDefault(_);
|
|
28
30
|
const EE__default = /* @__PURE__ */ _interopDefault(EE);
|
|
29
31
|
const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
|
|
@@ -76,6 +78,29 @@ const ACTIONS = [
|
|
|
76
78
|
const ALLOWED_WEBHOOK_EVENTS = {
|
|
77
79
|
RELEASES_PUBLISH: "releases.publish"
|
|
78
80
|
};
|
|
81
|
+
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
82
|
+
return strapi2.plugin("content-releases").service(name);
|
|
83
|
+
};
|
|
84
|
+
const getPopulatedEntry = async (contentTypeUid, entryId, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
85
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
86
|
+
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
87
|
+
const entry = await strapi2.entityService.findOne(contentTypeUid, entryId, { populate });
|
|
88
|
+
return entry;
|
|
89
|
+
};
|
|
90
|
+
const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
91
|
+
try {
|
|
92
|
+
await strapi2.entityValidator.validateEntityCreation(
|
|
93
|
+
strapi2.getModel(contentTypeUid),
|
|
94
|
+
entry,
|
|
95
|
+
void 0,
|
|
96
|
+
// @ts-expect-error - FIXME: entity here is unnecessary
|
|
97
|
+
entry
|
|
98
|
+
);
|
|
99
|
+
return true;
|
|
100
|
+
} catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
79
104
|
async function deleteActionsOnDisableDraftAndPublish({
|
|
80
105
|
oldContentTypes,
|
|
81
106
|
contentTypes: contentTypes2
|
|
@@ -102,31 +127,151 @@ async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes:
|
|
|
102
127
|
});
|
|
103
128
|
}
|
|
104
129
|
}
|
|
130
|
+
async function migrateIsValidAndStatusReleases() {
|
|
131
|
+
const releasesWithoutStatus = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
132
|
+
where: {
|
|
133
|
+
status: null,
|
|
134
|
+
releasedAt: null
|
|
135
|
+
},
|
|
136
|
+
populate: {
|
|
137
|
+
actions: {
|
|
138
|
+
populate: {
|
|
139
|
+
entry: true
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
utils.mapAsync(releasesWithoutStatus, async (release2) => {
|
|
145
|
+
const actions = release2.actions;
|
|
146
|
+
const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
|
|
147
|
+
for (const action of notValidatedActions) {
|
|
148
|
+
if (action.entry) {
|
|
149
|
+
const populatedEntry = await getPopulatedEntry(action.contentType, action.entry.id, {
|
|
150
|
+
strapi
|
|
151
|
+
});
|
|
152
|
+
if (populatedEntry) {
|
|
153
|
+
const isEntryValid = getEntryValidStatus(action.contentType, populatedEntry, { strapi });
|
|
154
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
155
|
+
where: {
|
|
156
|
+
id: action.id
|
|
157
|
+
},
|
|
158
|
+
data: {
|
|
159
|
+
isEntryValid
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
166
|
+
});
|
|
167
|
+
const publishedReleases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
168
|
+
where: {
|
|
169
|
+
status: null,
|
|
170
|
+
releasedAt: {
|
|
171
|
+
$notNull: true
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
utils.mapAsync(publishedReleases, async (release2) => {
|
|
176
|
+
return strapi.db.query(RELEASE_MODEL_UID).update({
|
|
177
|
+
where: {
|
|
178
|
+
id: release2.id
|
|
179
|
+
},
|
|
180
|
+
data: {
|
|
181
|
+
status: "done"
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
187
|
+
if (oldContentTypes !== void 0 && contentTypes2 !== void 0) {
|
|
188
|
+
const contentTypesWithDraftAndPublish = Object.keys(oldContentTypes).filter(
|
|
189
|
+
(uid) => oldContentTypes[uid]?.options?.draftAndPublish
|
|
190
|
+
);
|
|
191
|
+
const releasesAffected = /* @__PURE__ */ new Set();
|
|
192
|
+
utils.mapAsync(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
|
|
193
|
+
const oldContentType = oldContentTypes[contentTypeUID];
|
|
194
|
+
const contentType = contentTypes2[contentTypeUID];
|
|
195
|
+
if (!isEqual__default.default(oldContentType?.attributes, contentType?.attributes)) {
|
|
196
|
+
const actions = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
197
|
+
where: {
|
|
198
|
+
contentType: contentTypeUID
|
|
199
|
+
},
|
|
200
|
+
populate: {
|
|
201
|
+
entry: true,
|
|
202
|
+
release: true
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
await utils.mapAsync(actions, async (action) => {
|
|
206
|
+
if (action.entry) {
|
|
207
|
+
const populatedEntry = await getPopulatedEntry(contentTypeUID, action.entry.id, {
|
|
208
|
+
strapi
|
|
209
|
+
});
|
|
210
|
+
if (populatedEntry) {
|
|
211
|
+
const isEntryValid = await getEntryValidStatus(contentTypeUID, populatedEntry, {
|
|
212
|
+
strapi
|
|
213
|
+
});
|
|
214
|
+
releasesAffected.add(action.release.id);
|
|
215
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
216
|
+
where: {
|
|
217
|
+
id: action.id
|
|
218
|
+
},
|
|
219
|
+
data: {
|
|
220
|
+
isEntryValid
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}).then(() => {
|
|
228
|
+
utils.mapAsync(releasesAffected, async (releaseId) => {
|
|
229
|
+
return getService("release", { strapi }).updateReleaseStatus(releaseId);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
105
234
|
const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
|
|
106
235
|
const register = async ({ strapi: strapi2 }) => {
|
|
107
236
|
if (features$2.isEnabled("cms-content-releases")) {
|
|
108
237
|
await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
|
|
109
238
|
strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish);
|
|
110
|
-
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType);
|
|
239
|
+
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
111
240
|
}
|
|
112
241
|
};
|
|
113
|
-
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
114
|
-
return strapi2.plugin("content-releases").service(name);
|
|
115
|
-
};
|
|
116
242
|
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
117
243
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
118
244
|
if (features$1.isEnabled("cms-content-releases")) {
|
|
245
|
+
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
246
|
+
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
247
|
+
);
|
|
119
248
|
strapi2.db.lifecycles.subscribe({
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
249
|
+
models: contentTypesWithDraftAndPublish,
|
|
250
|
+
async afterDelete(event) {
|
|
251
|
+
try {
|
|
252
|
+
const { model, result } = event;
|
|
253
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
254
|
+
const { id } = result;
|
|
255
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
256
|
+
where: {
|
|
257
|
+
actions: {
|
|
258
|
+
target_type: model.uid,
|
|
259
|
+
target_id: id
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
264
|
+
where: {
|
|
265
|
+
target_type: model.uid,
|
|
266
|
+
target_id: id
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
for (const release2 of releases) {
|
|
270
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
128
271
|
}
|
|
129
|
-
}
|
|
272
|
+
}
|
|
273
|
+
} catch (error) {
|
|
274
|
+
strapi2.log.error("Error while deleting release actions after entry delete", { error });
|
|
130
275
|
}
|
|
131
276
|
},
|
|
132
277
|
/**
|
|
@@ -146,18 +291,75 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
146
291
|
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
147
292
|
*/
|
|
148
293
|
async afterDeleteMany(event) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
294
|
+
try {
|
|
295
|
+
const { model, state } = event;
|
|
296
|
+
const entriesToDelete = state.entriesToDelete;
|
|
297
|
+
if (entriesToDelete) {
|
|
298
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
299
|
+
where: {
|
|
300
|
+
actions: {
|
|
301
|
+
target_type: model.uid,
|
|
302
|
+
target_id: {
|
|
303
|
+
$in: entriesToDelete.map(
|
|
304
|
+
(entry) => entry.id
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
157
308
|
}
|
|
309
|
+
});
|
|
310
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
311
|
+
where: {
|
|
312
|
+
target_type: model.uid,
|
|
313
|
+
target_id: {
|
|
314
|
+
$in: entriesToDelete.map((entry) => entry.id)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
for (const release2 of releases) {
|
|
319
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
158
320
|
}
|
|
321
|
+
}
|
|
322
|
+
} catch (error) {
|
|
323
|
+
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
324
|
+
error
|
|
159
325
|
});
|
|
160
326
|
}
|
|
327
|
+
},
|
|
328
|
+
async afterUpdate(event) {
|
|
329
|
+
try {
|
|
330
|
+
const { model, result } = event;
|
|
331
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
332
|
+
const isEntryValid = await getEntryValidStatus(
|
|
333
|
+
model.uid,
|
|
334
|
+
result,
|
|
335
|
+
{
|
|
336
|
+
strapi: strapi2
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
340
|
+
where: {
|
|
341
|
+
target_type: model.uid,
|
|
342
|
+
target_id: result.id
|
|
343
|
+
},
|
|
344
|
+
data: {
|
|
345
|
+
isEntryValid
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
349
|
+
where: {
|
|
350
|
+
actions: {
|
|
351
|
+
target_type: model.uid,
|
|
352
|
+
target_id: result.id
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
for (const release2 of releases) {
|
|
357
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
} catch (error) {
|
|
361
|
+
strapi2.log.error("Error while updating release actions after entry update", { error });
|
|
362
|
+
}
|
|
161
363
|
}
|
|
162
364
|
});
|
|
163
365
|
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
@@ -215,6 +417,11 @@ const schema$1 = {
|
|
|
215
417
|
timezone: {
|
|
216
418
|
type: "string"
|
|
217
419
|
},
|
|
420
|
+
status: {
|
|
421
|
+
type: "enumeration",
|
|
422
|
+
enum: ["ready", "blocked", "failed", "done", "empty"],
|
|
423
|
+
required: true
|
|
424
|
+
},
|
|
218
425
|
actions: {
|
|
219
426
|
type: "relation",
|
|
220
427
|
relation: "oneToMany",
|
|
@@ -267,6 +474,9 @@ const schema = {
|
|
|
267
474
|
relation: "manyToOne",
|
|
268
475
|
target: RELEASE_MODEL_UID,
|
|
269
476
|
inversedBy: "actions"
|
|
477
|
+
},
|
|
478
|
+
isEntryValid: {
|
|
479
|
+
type: "boolean"
|
|
270
480
|
}
|
|
271
481
|
}
|
|
272
482
|
};
|
|
@@ -311,7 +521,10 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
311
521
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
312
522
|
]);
|
|
313
523
|
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
314
|
-
data:
|
|
524
|
+
data: {
|
|
525
|
+
...releaseWithCreatorFields,
|
|
526
|
+
status: "empty"
|
|
527
|
+
}
|
|
315
528
|
});
|
|
316
529
|
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
|
|
317
530
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
@@ -446,6 +659,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
446
659
|
schedulingService.cancel(id);
|
|
447
660
|
}
|
|
448
661
|
}
|
|
662
|
+
this.updateReleaseStatus(id);
|
|
449
663
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
450
664
|
return updatedRelease;
|
|
451
665
|
},
|
|
@@ -465,11 +679,14 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
465
679
|
throw new utils.errors.ValidationError("Release already published");
|
|
466
680
|
}
|
|
467
681
|
const { entry, type } = action;
|
|
468
|
-
|
|
682
|
+
const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
|
|
683
|
+
const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
|
|
684
|
+
const releaseAction2 = await strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
469
685
|
data: {
|
|
470
686
|
type,
|
|
471
687
|
contentType: entry.contentType,
|
|
472
688
|
locale: entry.locale,
|
|
689
|
+
isEntryValid,
|
|
473
690
|
entry: {
|
|
474
691
|
id: entry.id,
|
|
475
692
|
__type: entry.contentType,
|
|
@@ -479,6 +696,8 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
479
696
|
},
|
|
480
697
|
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
481
698
|
});
|
|
699
|
+
this.updateReleaseStatus(releaseId);
|
|
700
|
+
return releaseAction2;
|
|
482
701
|
},
|
|
483
702
|
async findActions(releaseId, query) {
|
|
484
703
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
@@ -743,6 +962,12 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
743
962
|
error
|
|
744
963
|
});
|
|
745
964
|
}
|
|
965
|
+
strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
966
|
+
where: { id: releaseId },
|
|
967
|
+
data: {
|
|
968
|
+
status: "failed"
|
|
969
|
+
}
|
|
970
|
+
});
|
|
746
971
|
throw error;
|
|
747
972
|
}
|
|
748
973
|
},
|
|
@@ -783,7 +1008,51 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
783
1008
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
784
1009
|
);
|
|
785
1010
|
}
|
|
1011
|
+
this.updateReleaseStatus(releaseId);
|
|
786
1012
|
return deletedAction;
|
|
1013
|
+
},
|
|
1014
|
+
async updateReleaseStatus(releaseId) {
|
|
1015
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
1016
|
+
this.countActions({
|
|
1017
|
+
filters: {
|
|
1018
|
+
release: releaseId
|
|
1019
|
+
}
|
|
1020
|
+
}),
|
|
1021
|
+
this.countActions({
|
|
1022
|
+
filters: {
|
|
1023
|
+
release: releaseId,
|
|
1024
|
+
isEntryValid: false
|
|
1025
|
+
}
|
|
1026
|
+
})
|
|
1027
|
+
]);
|
|
1028
|
+
if (totalActions > 0) {
|
|
1029
|
+
if (invalidActions > 0) {
|
|
1030
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1031
|
+
where: {
|
|
1032
|
+
id: releaseId
|
|
1033
|
+
},
|
|
1034
|
+
data: {
|
|
1035
|
+
status: "blocked"
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1040
|
+
where: {
|
|
1041
|
+
id: releaseId
|
|
1042
|
+
},
|
|
1043
|
+
data: {
|
|
1044
|
+
status: "ready"
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1049
|
+
where: {
|
|
1050
|
+
id: releaseId
|
|
1051
|
+
},
|
|
1052
|
+
data: {
|
|
1053
|
+
status: "empty"
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
787
1056
|
}
|
|
788
1057
|
};
|
|
789
1058
|
};
|
|
@@ -960,7 +1229,12 @@ const releaseController = {
|
|
|
960
1229
|
}
|
|
961
1230
|
};
|
|
962
1231
|
});
|
|
963
|
-
|
|
1232
|
+
const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
|
|
1233
|
+
where: {
|
|
1234
|
+
releasedAt: null
|
|
1235
|
+
}
|
|
1236
|
+
});
|
|
1237
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
964
1238
|
}
|
|
965
1239
|
},
|
|
966
1240
|
async findOne(ctx) {
|