@strapi/content-releases 5.12.0 → 5.12.2

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.
Files changed (259) hide show
  1. package/dist/admin/assets/purchase-page-illustration-dark.svg.js +6 -0
  2. package/dist/admin/assets/purchase-page-illustration-dark.svg.js.map +1 -0
  3. package/dist/admin/assets/purchase-page-illustration-dark.svg.mjs +4 -0
  4. package/dist/admin/assets/purchase-page-illustration-dark.svg.mjs.map +1 -0
  5. package/dist/admin/assets/purchase-page-illustration-light.svg.js +6 -0
  6. package/dist/admin/assets/purchase-page-illustration-light.svg.js.map +1 -0
  7. package/dist/admin/assets/purchase-page-illustration-light.svg.mjs +4 -0
  8. package/dist/admin/assets/purchase-page-illustration-light.svg.mjs.map +1 -0
  9. package/dist/admin/components/EntryValidationPopover.js +344 -0
  10. package/dist/admin/components/EntryValidationPopover.js.map +1 -0
  11. package/dist/admin/components/EntryValidationPopover.mjs +342 -0
  12. package/dist/admin/components/EntryValidationPopover.mjs.map +1 -0
  13. package/dist/admin/components/RelativeTime.js +76 -0
  14. package/dist/admin/components/RelativeTime.js.map +1 -0
  15. package/dist/admin/components/RelativeTime.mjs +55 -0
  16. package/dist/admin/components/RelativeTime.mjs.map +1 -0
  17. package/dist/admin/components/ReleaseAction.js +201 -0
  18. package/dist/admin/components/ReleaseAction.js.map +1 -0
  19. package/dist/admin/components/ReleaseAction.mjs +199 -0
  20. package/dist/admin/components/ReleaseAction.mjs.map +1 -0
  21. package/dist/admin/components/ReleaseActionMenu.js +243 -0
  22. package/dist/admin/components/ReleaseActionMenu.js.map +1 -0
  23. package/dist/admin/components/ReleaseActionMenu.mjs +222 -0
  24. package/dist/admin/components/ReleaseActionMenu.mjs.map +1 -0
  25. package/dist/admin/components/ReleaseActionModal.js +268 -0
  26. package/dist/admin/components/ReleaseActionModal.js.map +1 -0
  27. package/dist/admin/components/ReleaseActionModal.mjs +244 -0
  28. package/dist/admin/components/ReleaseActionModal.mjs.map +1 -0
  29. package/dist/admin/components/ReleaseActionOptions.js +104 -0
  30. package/dist/admin/components/ReleaseActionOptions.js.map +1 -0
  31. package/dist/admin/components/ReleaseActionOptions.mjs +102 -0
  32. package/dist/admin/components/ReleaseActionOptions.mjs.map +1 -0
  33. package/dist/admin/components/ReleaseListCell.js +103 -0
  34. package/dist/admin/components/ReleaseListCell.js.map +1 -0
  35. package/dist/admin/components/ReleaseListCell.mjs +100 -0
  36. package/dist/admin/components/ReleaseListCell.mjs.map +1 -0
  37. package/dist/admin/components/ReleaseModal.js +323 -0
  38. package/dist/admin/components/ReleaseModal.js.map +1 -0
  39. package/dist/admin/components/ReleaseModal.mjs +302 -0
  40. package/dist/admin/components/ReleaseModal.mjs.map +1 -0
  41. package/dist/admin/components/ReleasesPanel.js +138 -0
  42. package/dist/admin/components/ReleasesPanel.js.map +1 -0
  43. package/dist/admin/components/ReleasesPanel.mjs +136 -0
  44. package/dist/admin/components/ReleasesPanel.mjs.map +1 -0
  45. package/dist/admin/constants.js +77 -0
  46. package/dist/admin/constants.js.map +1 -0
  47. package/dist/admin/constants.mjs +75 -0
  48. package/dist/admin/constants.mjs.map +1 -0
  49. package/dist/admin/index.js +120 -14
  50. package/dist/admin/index.js.map +1 -1
  51. package/dist/admin/index.mjs +121 -13
  52. package/dist/admin/index.mjs.map +1 -1
  53. package/dist/admin/{chunks/hooks-DA5VbUAp.js → modules/hooks.js} +1 -1
  54. package/dist/admin/modules/hooks.js.map +1 -0
  55. package/dist/admin/{chunks/hooks-CFk_8Q0b.mjs → modules/hooks.mjs} +2 -2
  56. package/dist/admin/modules/hooks.mjs.map +1 -0
  57. package/dist/admin/pages/App.js +29 -0
  58. package/dist/admin/pages/App.js.map +1 -0
  59. package/dist/admin/pages/App.mjs +27 -0
  60. package/dist/admin/pages/App.mjs.map +1 -0
  61. package/dist/admin/pages/PurchaseContentReleases.js +192 -0
  62. package/dist/admin/pages/PurchaseContentReleases.js.map +1 -0
  63. package/dist/admin/pages/PurchaseContentReleases.mjs +190 -0
  64. package/dist/admin/pages/PurchaseContentReleases.mjs.map +1 -0
  65. package/dist/admin/pages/ReleaseDetailsPage.js +821 -0
  66. package/dist/admin/pages/ReleaseDetailsPage.js.map +1 -0
  67. package/dist/admin/pages/ReleaseDetailsPage.mjs +800 -0
  68. package/dist/admin/pages/ReleaseDetailsPage.mjs.map +1 -0
  69. package/dist/admin/pages/ReleasesPage.js +397 -0
  70. package/dist/admin/pages/ReleasesPage.js.map +1 -0
  71. package/dist/admin/pages/ReleasesPage.mjs +375 -0
  72. package/dist/admin/pages/ReleasesPage.mjs.map +1 -0
  73. package/dist/admin/{chunks/ReleasesSettingsPage-KRcoI1bC.js → pages/ReleasesSettingsPage.js} +9 -17
  74. package/dist/admin/pages/ReleasesSettingsPage.js.map +1 -0
  75. package/dist/admin/{chunks/ReleasesSettingsPage-DUKdFdvx.mjs → pages/ReleasesSettingsPage.mjs} +5 -13
  76. package/dist/admin/pages/ReleasesSettingsPage.mjs.map +1 -0
  77. package/dist/admin/pluginId.js +6 -0
  78. package/dist/admin/pluginId.js.map +1 -0
  79. package/dist/admin/pluginId.mjs +4 -0
  80. package/dist/admin/pluginId.mjs.map +1 -0
  81. package/dist/admin/services/release.js +464 -0
  82. package/dist/admin/services/release.js.map +1 -0
  83. package/dist/admin/services/release.mjs +447 -0
  84. package/dist/admin/services/release.mjs.map +1 -0
  85. package/dist/admin/store/hooks.js +8 -0
  86. package/dist/admin/store/hooks.js.map +1 -0
  87. package/dist/admin/store/hooks.mjs +6 -0
  88. package/dist/admin/store/hooks.mjs.map +1 -0
  89. package/dist/admin/{chunks/en-BOpqX2t_.js → translations/en.json.js} +2 -2
  90. package/dist/admin/translations/en.json.js.map +1 -0
  91. package/dist/admin/{chunks/en-aQo8Bn_U.mjs → translations/en.json.mjs} +1 -1
  92. package/dist/admin/translations/en.json.mjs.map +1 -0
  93. package/dist/admin/{chunks/uk-9T9su-bj.js → translations/uk.json.js} +2 -2
  94. package/dist/admin/translations/uk.json.js.map +1 -0
  95. package/dist/admin/{chunks/uk-Bp9HotPq.mjs → translations/uk.json.mjs} +1 -1
  96. package/dist/admin/translations/uk.json.mjs.map +1 -0
  97. package/dist/admin/utils/api.js +8 -0
  98. package/dist/admin/utils/api.js.map +1 -0
  99. package/dist/admin/utils/api.mjs +6 -0
  100. package/dist/admin/utils/api.mjs.map +1 -0
  101. package/dist/admin/utils/prefixPluginTranslations.js +11 -0
  102. package/dist/admin/utils/prefixPluginTranslations.js.map +1 -0
  103. package/dist/admin/utils/prefixPluginTranslations.mjs +9 -0
  104. package/dist/admin/utils/prefixPluginTranslations.mjs.map +1 -0
  105. package/dist/admin/utils/time.js +42 -0
  106. package/dist/admin/utils/time.js.map +1 -0
  107. package/dist/admin/utils/time.mjs +39 -0
  108. package/dist/admin/utils/time.mjs.map +1 -0
  109. package/dist/admin/{chunks/schemas-DS7NeFDN.js → validation/schemas.js} +1 -1
  110. package/dist/admin/validation/schemas.js.map +1 -0
  111. package/dist/admin/{chunks/schemas-DMt8h1z-.mjs → validation/schemas.mjs} +2 -2
  112. package/dist/admin/validation/schemas.mjs.map +1 -0
  113. package/dist/server/bootstrap.js +68 -0
  114. package/dist/server/bootstrap.js.map +1 -0
  115. package/dist/server/bootstrap.mjs +66 -0
  116. package/dist/server/bootstrap.mjs.map +1 -0
  117. package/dist/server/constants.js +74 -0
  118. package/dist/server/constants.js.map +1 -0
  119. package/dist/server/constants.mjs +69 -0
  120. package/dist/server/constants.mjs.map +1 -0
  121. package/dist/server/content-types/index.js +12 -0
  122. package/dist/server/content-types/index.js.map +1 -0
  123. package/dist/server/content-types/index.mjs +10 -0
  124. package/dist/server/content-types/index.mjs.map +1 -0
  125. package/dist/server/content-types/release/index.js +10 -0
  126. package/dist/server/content-types/release/index.js.map +1 -0
  127. package/dist/server/content-types/release/index.mjs +8 -0
  128. package/dist/server/content-types/release/index.mjs.map +1 -0
  129. package/dist/server/content-types/release/schema.js +58 -0
  130. package/dist/server/content-types/release/schema.js.map +1 -0
  131. package/dist/server/content-types/release/schema.mjs +56 -0
  132. package/dist/server/content-types/release/schema.mjs.map +1 -0
  133. package/dist/server/content-types/release-action/index.js +10 -0
  134. package/dist/server/content-types/release-action/index.js.map +1 -0
  135. package/dist/server/content-types/release-action/index.mjs +8 -0
  136. package/dist/server/content-types/release-action/index.mjs.map +1 -0
  137. package/dist/server/content-types/release-action/schema.js +55 -0
  138. package/dist/server/content-types/release-action/schema.js.map +1 -0
  139. package/dist/server/content-types/release-action/schema.mjs +53 -0
  140. package/dist/server/content-types/release-action/schema.mjs.map +1 -0
  141. package/dist/server/controllers/index.js +14 -0
  142. package/dist/server/controllers/index.js.map +1 -0
  143. package/dist/server/controllers/index.mjs +12 -0
  144. package/dist/server/controllers/index.mjs.map +1 -0
  145. package/dist/server/controllers/release-action.js +150 -0
  146. package/dist/server/controllers/release-action.js.map +1 -0
  147. package/dist/server/controllers/release-action.mjs +148 -0
  148. package/dist/server/controllers/release-action.mjs.map +1 -0
  149. package/dist/server/controllers/release.js +302 -0
  150. package/dist/server/controllers/release.js.map +1 -0
  151. package/dist/server/controllers/release.mjs +300 -0
  152. package/dist/server/controllers/release.mjs.map +1 -0
  153. package/dist/server/controllers/settings.js +37 -0
  154. package/dist/server/controllers/settings.js.map +1 -0
  155. package/dist/server/controllers/settings.mjs +35 -0
  156. package/dist/server/controllers/settings.mjs.map +1 -0
  157. package/dist/server/controllers/validation/release-action.js +34 -0
  158. package/dist/server/controllers/validation/release-action.js.map +1 -0
  159. package/dist/server/controllers/validation/release-action.mjs +30 -0
  160. package/dist/server/controllers/validation/release-action.mjs.map +1 -0
  161. package/dist/server/controllers/validation/release.js +26 -0
  162. package/dist/server/controllers/validation/release.js.map +1 -0
  163. package/dist/server/controllers/validation/release.mjs +22 -0
  164. package/dist/server/controllers/validation/release.mjs.map +1 -0
  165. package/dist/server/controllers/validation/settings.js +32 -0
  166. package/dist/server/controllers/validation/settings.js.map +1 -0
  167. package/dist/server/controllers/validation/settings.mjs +10 -0
  168. package/dist/server/controllers/validation/settings.mjs.map +1 -0
  169. package/dist/server/destroy.js +15 -0
  170. package/dist/server/destroy.js.map +1 -0
  171. package/dist/server/destroy.mjs +13 -0
  172. package/dist/server/destroy.mjs.map +1 -0
  173. package/dist/server/index.js +16 -2336
  174. package/dist/server/index.js.map +1 -1
  175. package/dist/server/index.mjs +7 -2308
  176. package/dist/server/index.mjs.map +1 -1
  177. package/dist/server/middlewares/documents.js +104 -0
  178. package/dist/server/middlewares/documents.js.map +1 -0
  179. package/dist/server/middlewares/documents.mjs +101 -0
  180. package/dist/server/middlewares/documents.mjs.map +1 -0
  181. package/dist/server/migrations/database/5.0.0-document-id-in-actions.js +51 -0
  182. package/dist/server/migrations/database/5.0.0-document-id-in-actions.js.map +1 -0
  183. package/dist/server/migrations/database/5.0.0-document-id-in-actions.mjs +49 -0
  184. package/dist/server/migrations/database/5.0.0-document-id-in-actions.mjs.map +1 -0
  185. package/dist/server/migrations/index.js +205 -0
  186. package/dist/server/migrations/index.js.map +1 -0
  187. package/dist/server/migrations/index.mjs +198 -0
  188. package/dist/server/migrations/index.mjs.map +1 -0
  189. package/dist/server/register.js +23 -0
  190. package/dist/server/register.js.map +1 -0
  191. package/dist/server/register.mjs +21 -0
  192. package/dist/server/register.mjs.map +1 -0
  193. package/dist/server/routes/index.js +14 -0
  194. package/dist/server/routes/index.js.map +1 -0
  195. package/dist/server/routes/index.mjs +12 -0
  196. package/dist/server/routes/index.mjs.map +1 -0
  197. package/dist/server/routes/release-action.js +100 -0
  198. package/dist/server/routes/release-action.js.map +1 -0
  199. package/dist/server/routes/release-action.mjs +98 -0
  200. package/dist/server/routes/release-action.mjs.map +1 -0
  201. package/dist/server/routes/release.js +154 -0
  202. package/dist/server/routes/release.js.map +1 -0
  203. package/dist/server/routes/release.mjs +152 -0
  204. package/dist/server/routes/release.mjs.map +1 -0
  205. package/dist/server/routes/settings.js +46 -0
  206. package/dist/server/routes/settings.js.map +1 -0
  207. package/dist/server/routes/settings.mjs +44 -0
  208. package/dist/server/routes/settings.mjs.map +1 -0
  209. package/dist/server/services/index.js +18 -0
  210. package/dist/server/services/index.js.map +1 -0
  211. package/dist/server/services/index.mjs +16 -0
  212. package/dist/server/services/index.mjs.map +1 -0
  213. package/dist/server/services/release-action.js +323 -0
  214. package/dist/server/services/release-action.js.map +1 -0
  215. package/dist/server/services/release-action.mjs +321 -0
  216. package/dist/server/services/release-action.mjs.map +1 -0
  217. package/dist/server/services/release.js +324 -0
  218. package/dist/server/services/release.js.map +1 -0
  219. package/dist/server/services/release.mjs +322 -0
  220. package/dist/server/services/release.mjs.map +1 -0
  221. package/dist/server/services/scheduling.js +70 -0
  222. package/dist/server/services/scheduling.js.map +1 -0
  223. package/dist/server/services/scheduling.mjs +68 -0
  224. package/dist/server/services/scheduling.mjs.map +1 -0
  225. package/dist/server/services/settings.js +34 -0
  226. package/dist/server/services/settings.js.map +1 -0
  227. package/dist/server/services/settings.mjs +32 -0
  228. package/dist/server/services/settings.mjs.map +1 -0
  229. package/dist/server/services/validation.js +91 -0
  230. package/dist/server/services/validation.js.map +1 -0
  231. package/dist/server/services/validation.mjs +86 -0
  232. package/dist/server/services/validation.mjs.map +1 -0
  233. package/dist/server/utils/index.js +93 -0
  234. package/dist/server/utils/index.js.map +1 -0
  235. package/dist/server/utils/index.mjs +87 -0
  236. package/dist/server/utils/index.mjs.map +1 -0
  237. package/package.json +7 -7
  238. package/dist/admin/chunks/App-BkWgp5q_.mjs +0 -1845
  239. package/dist/admin/chunks/App-BkWgp5q_.mjs.map +0 -1
  240. package/dist/admin/chunks/App-CJiqPP7-.js +0 -1866
  241. package/dist/admin/chunks/App-CJiqPP7-.js.map +0 -1
  242. package/dist/admin/chunks/PurchaseContentReleases-CzayeVUD.mjs +0 -193
  243. package/dist/admin/chunks/PurchaseContentReleases-CzayeVUD.mjs.map +0 -1
  244. package/dist/admin/chunks/PurchaseContentReleases-Z9uEPb5b.js +0 -195
  245. package/dist/admin/chunks/PurchaseContentReleases-Z9uEPb5b.js.map +0 -1
  246. package/dist/admin/chunks/ReleasesSettingsPage-DUKdFdvx.mjs.map +0 -1
  247. package/dist/admin/chunks/ReleasesSettingsPage-KRcoI1bC.js.map +0 -1
  248. package/dist/admin/chunks/en-BOpqX2t_.js.map +0 -1
  249. package/dist/admin/chunks/en-aQo8Bn_U.mjs.map +0 -1
  250. package/dist/admin/chunks/hooks-CFk_8Q0b.mjs.map +0 -1
  251. package/dist/admin/chunks/hooks-DA5VbUAp.js.map +0 -1
  252. package/dist/admin/chunks/index-DBUaMD56.mjs +0 -1619
  253. package/dist/admin/chunks/index-DBUaMD56.mjs.map +0 -1
  254. package/dist/admin/chunks/index-vjWrvGN3.js +0 -1658
  255. package/dist/admin/chunks/index-vjWrvGN3.js.map +0 -1
  256. package/dist/admin/chunks/schemas-DMt8h1z-.mjs.map +0 -1
  257. package/dist/admin/chunks/schemas-DS7NeFDN.js.map +0 -1
  258. package/dist/admin/chunks/uk-9T9su-bj.js.map +0 -1
  259. package/dist/admin/chunks/uk-Bp9HotPq.mjs.map +0 -1
@@ -0,0 +1,322 @@
1
+ import { setCreatorFields, errors } from '@strapi/utils';
2
+ import { RELEASE_MODEL_UID, RELEASE_ACTION_MODEL_UID, ALLOWED_WEBHOOK_EVENTS } from '../constants.mjs';
3
+ import { getService } from '../utils/index.mjs';
4
+
5
+ const createReleaseService = ({ strapi })=>{
6
+ const dispatchWebhook = (event, { isPublished, release, error })=>{
7
+ strapi.eventHub.emit(event, {
8
+ isPublished,
9
+ error,
10
+ release
11
+ });
12
+ };
13
+ /**
14
+ * Given a release id, it returns the actions formatted ready to be used to publish them.
15
+ * We split them by contentType and type (publish/unpublish) and extract only the documentIds and locales.
16
+ */ const getFormattedActions = async (releaseId)=>{
17
+ const actions = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({
18
+ where: {
19
+ release: {
20
+ id: releaseId
21
+ }
22
+ }
23
+ });
24
+ if (actions.length === 0) {
25
+ throw new errors.ValidationError('No entries to publish');
26
+ }
27
+ /**
28
+ * We separate publish and unpublish actions, grouping them by contentType and extracting only their documentIds and locales.
29
+ */ const formattedActions = {};
30
+ for (const action of actions){
31
+ const contentTypeUid = action.contentType;
32
+ if (!formattedActions[contentTypeUid]) {
33
+ formattedActions[contentTypeUid] = {
34
+ publish: [],
35
+ unpublish: []
36
+ };
37
+ }
38
+ formattedActions[contentTypeUid][action.type].push({
39
+ documentId: action.entryDocumentId,
40
+ locale: action.locale
41
+ });
42
+ }
43
+ return formattedActions;
44
+ };
45
+ return {
46
+ async create (releaseData, { user }) {
47
+ const releaseWithCreatorFields = await setCreatorFields({
48
+ user
49
+ })(releaseData);
50
+ const { validatePendingReleasesLimit, validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService('release-validation', {
51
+ strapi
52
+ });
53
+ await Promise.all([
54
+ validatePendingReleasesLimit(),
55
+ validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
56
+ validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
57
+ ]);
58
+ const release = await strapi.db.query(RELEASE_MODEL_UID).create({
59
+ data: {
60
+ ...releaseWithCreatorFields,
61
+ status: 'empty'
62
+ }
63
+ });
64
+ if (releaseWithCreatorFields.scheduledAt) {
65
+ const schedulingService = getService('scheduling', {
66
+ strapi
67
+ });
68
+ await schedulingService.set(release.id, release.scheduledAt);
69
+ }
70
+ strapi.telemetry.send('didCreateContentRelease');
71
+ return release;
72
+ },
73
+ async findOne (id, query = {}) {
74
+ const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query);
75
+ const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({
76
+ ...dbQuery,
77
+ where: {
78
+ id
79
+ }
80
+ });
81
+ return release;
82
+ },
83
+ findPage (query) {
84
+ const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});
85
+ return strapi.db.query(RELEASE_MODEL_UID).findPage({
86
+ ...dbQuery,
87
+ populate: {
88
+ actions: {
89
+ count: true
90
+ }
91
+ }
92
+ });
93
+ },
94
+ findMany (query) {
95
+ const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});
96
+ return strapi.db.query(RELEASE_MODEL_UID).findMany({
97
+ ...dbQuery
98
+ });
99
+ },
100
+ async update (id, releaseData, { user }) {
101
+ const releaseWithCreatorFields = await setCreatorFields({
102
+ user,
103
+ isEdition: true
104
+ })(releaseData);
105
+ const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService('release-validation', {
106
+ strapi
107
+ });
108
+ await Promise.all([
109
+ validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
110
+ validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
111
+ ]);
112
+ const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({
113
+ where: {
114
+ id
115
+ }
116
+ });
117
+ if (!release) {
118
+ throw new errors.NotFoundError(`No release found for id ${id}`);
119
+ }
120
+ if (release.releasedAt) {
121
+ throw new errors.ValidationError('Release already published');
122
+ }
123
+ const updatedRelease = await strapi.db.query(RELEASE_MODEL_UID).update({
124
+ where: {
125
+ id
126
+ },
127
+ data: releaseWithCreatorFields
128
+ });
129
+ const schedulingService = getService('scheduling', {
130
+ strapi
131
+ });
132
+ if (releaseData.scheduledAt) {
133
+ // set function always cancel the previous job if it exists, so we can call it directly
134
+ await schedulingService.set(id, releaseData.scheduledAt);
135
+ } else if (release.scheduledAt) {
136
+ // When user don't send a scheduledAt and we have one on the release, means that user want to unschedule it
137
+ schedulingService.cancel(id);
138
+ }
139
+ this.updateReleaseStatus(id);
140
+ strapi.telemetry.send('didUpdateContentRelease');
141
+ return updatedRelease;
142
+ },
143
+ async getAllComponents () {
144
+ const contentManagerComponentsService = strapi.plugin('content-manager').service('components');
145
+ const components = await contentManagerComponentsService.findAllComponents();
146
+ const componentsMap = components.reduce((acc, component)=>{
147
+ acc[component.uid] = component;
148
+ return acc;
149
+ }, {});
150
+ return componentsMap;
151
+ },
152
+ async delete (releaseId) {
153
+ const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({
154
+ where: {
155
+ id: releaseId
156
+ },
157
+ populate: {
158
+ actions: {
159
+ select: [
160
+ 'id'
161
+ ]
162
+ }
163
+ }
164
+ });
165
+ if (!release) {
166
+ throw new errors.NotFoundError(`No release found for id ${releaseId}`);
167
+ }
168
+ if (release.releasedAt) {
169
+ throw new errors.ValidationError('Release already published');
170
+ }
171
+ // Only delete the release and its actions is you in fact can delete all the actions and the release
172
+ // Otherwise, if the transaction fails it throws an error
173
+ await strapi.db.transaction(async ()=>{
174
+ await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
175
+ where: {
176
+ id: {
177
+ $in: release.actions.map((action)=>action.id)
178
+ }
179
+ }
180
+ });
181
+ await strapi.db.query(RELEASE_MODEL_UID).delete({
182
+ where: {
183
+ id: releaseId
184
+ }
185
+ });
186
+ });
187
+ if (release.scheduledAt) {
188
+ const schedulingService = getService('scheduling', {
189
+ strapi
190
+ });
191
+ await schedulingService.cancel(release.id);
192
+ }
193
+ strapi.telemetry.send('didDeleteContentRelease');
194
+ return release;
195
+ },
196
+ async publish (releaseId) {
197
+ const { release, error } = await strapi.db.transaction(async ({ trx })=>{
198
+ /**
199
+ * We lock the release in this transaction, so any other process trying to publish it will wait until this transaction is finished
200
+ * In this transaction we don't care about rollback, becasue we want to persist the lock until the end and if it fails we want to change the release status to failed
201
+ */ const lockedRelease = await strapi.db?.queryBuilder(RELEASE_MODEL_UID).where({
202
+ id: releaseId
203
+ }).select([
204
+ 'id',
205
+ 'name',
206
+ 'releasedAt',
207
+ 'status'
208
+ ]).first().transacting(trx).forUpdate().execute();
209
+ if (!lockedRelease) {
210
+ throw new errors.NotFoundError(`No release found for id ${releaseId}`);
211
+ }
212
+ if (lockedRelease.releasedAt) {
213
+ throw new errors.ValidationError('Release already published');
214
+ }
215
+ if (lockedRelease.status === 'failed') {
216
+ throw new errors.ValidationError('Release failed to publish');
217
+ }
218
+ try {
219
+ strapi.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
220
+ const formattedActions = await getFormattedActions(releaseId);
221
+ await strapi.db.transaction(async ()=>Promise.all(Object.keys(formattedActions).map(async (contentTypeUid)=>{
222
+ const contentType = contentTypeUid;
223
+ const { publish, unpublish } = formattedActions[contentType];
224
+ return Promise.all([
225
+ ...publish.map((params)=>strapi.documents(contentType).publish(params)),
226
+ ...unpublish.map((params)=>strapi.documents(contentType).unpublish(params))
227
+ ]);
228
+ })));
229
+ const release = await strapi.db.query(RELEASE_MODEL_UID).update({
230
+ where: {
231
+ id: releaseId
232
+ },
233
+ data: {
234
+ status: 'done',
235
+ releasedAt: new Date()
236
+ }
237
+ });
238
+ dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
239
+ isPublished: true,
240
+ release
241
+ });
242
+ strapi.telemetry.send('didPublishContentRelease');
243
+ return {
244
+ release,
245
+ error: null
246
+ };
247
+ } catch (error) {
248
+ dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
249
+ isPublished: false,
250
+ error
251
+ });
252
+ // We need to run the update in the same transaction because the release is locked
253
+ await strapi.db?.queryBuilder(RELEASE_MODEL_UID).where({
254
+ id: releaseId
255
+ }).update({
256
+ status: 'failed'
257
+ }).transacting(trx).execute();
258
+ // At this point, we don't want to throw the error because if that happen we rollback the change in the release status
259
+ // We want to throw the error after the transaction is finished, so we return the error
260
+ return {
261
+ release: null,
262
+ error
263
+ };
264
+ }
265
+ });
266
+ // Now the first transaction is commited, we can safely throw the error if it exists
267
+ if (error instanceof Error) {
268
+ throw error;
269
+ }
270
+ return release;
271
+ },
272
+ async updateReleaseStatus (releaseId) {
273
+ const releaseActionService = getService('release-action', {
274
+ strapi
275
+ });
276
+ const [totalActions, invalidActions] = await Promise.all([
277
+ releaseActionService.countActions({
278
+ filters: {
279
+ release: releaseId
280
+ }
281
+ }),
282
+ releaseActionService.countActions({
283
+ filters: {
284
+ release: releaseId,
285
+ isEntryValid: false
286
+ }
287
+ })
288
+ ]);
289
+ if (totalActions > 0) {
290
+ if (invalidActions > 0) {
291
+ return strapi.db.query(RELEASE_MODEL_UID).update({
292
+ where: {
293
+ id: releaseId
294
+ },
295
+ data: {
296
+ status: 'blocked'
297
+ }
298
+ });
299
+ }
300
+ return strapi.db.query(RELEASE_MODEL_UID).update({
301
+ where: {
302
+ id: releaseId
303
+ },
304
+ data: {
305
+ status: 'ready'
306
+ }
307
+ });
308
+ }
309
+ return strapi.db.query(RELEASE_MODEL_UID).update({
310
+ where: {
311
+ id: releaseId
312
+ },
313
+ data: {
314
+ status: 'empty'
315
+ }
316
+ });
317
+ }
318
+ };
319
+ };
320
+
321
+ export { createReleaseService as default };
322
+ //# sourceMappingURL=release.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release.mjs","sources":["../../../server/src/services/release.ts"],"sourcesContent":["import { setCreatorFields, errors } from '@strapi/utils';\n\nimport type { Core, Struct, UID, Data } from '@strapi/types';\n\nimport { ALLOWED_WEBHOOK_EVENTS, RELEASE_ACTION_MODEL_UID, RELEASE_MODEL_UID } from '../constants';\nimport type {\n GetReleases,\n CreateRelease,\n UpdateRelease,\n PublishRelease,\n GetRelease,\n Release,\n DeleteRelease,\n} from '../../../shared/contracts/releases';\nimport type { ReleaseAction } from '../../../shared/contracts/release-actions';\nimport type { UserInfo } from '../../../shared/types';\nimport { getService } from '../utils';\n\nconst createReleaseService = ({ strapi }: { strapi: Core.Strapi }) => {\n const dispatchWebhook = (\n event: string,\n { isPublished, release, error }: { isPublished: boolean; release?: any; error?: unknown }\n ) => {\n strapi.eventHub.emit(event, {\n isPublished,\n error,\n release,\n });\n };\n\n /**\n * Given a release id, it returns the actions formatted ready to be used to publish them.\n * We split them by contentType and type (publish/unpublish) and extract only the documentIds and locales.\n */\n const getFormattedActions = async (releaseId: Release['id']) => {\n const actions = (await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({\n where: {\n release: {\n id: releaseId,\n },\n },\n })) as ReleaseAction[];\n\n if (actions.length === 0) {\n throw new errors.ValidationError('No entries to publish');\n }\n\n /**\n * We separate publish and unpublish actions, grouping them by contentType and extracting only their documentIds and locales.\n */\n const formattedActions: {\n [key: UID.ContentType]: {\n publish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n unpublish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n };\n } = {};\n\n for (const action of actions) {\n const contentTypeUid: UID.ContentType = action.contentType;\n\n if (!formattedActions[contentTypeUid]) {\n formattedActions[contentTypeUid] = {\n publish: [],\n unpublish: [],\n };\n }\n\n formattedActions[contentTypeUid][action.type].push({\n documentId: action.entryDocumentId,\n locale: action.locale,\n });\n }\n\n return formattedActions;\n };\n\n return {\n async create(releaseData: CreateRelease.Request['body'], { user }: { user: UserInfo }) {\n const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);\n\n const {\n validatePendingReleasesLimit,\n validateUniqueNameForPendingRelease,\n validateScheduledAtIsLaterThanNow,\n } = getService('release-validation', { strapi });\n\n await Promise.all([\n validatePendingReleasesLimit(),\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).create({\n data: {\n ...releaseWithCreatorFields,\n status: 'empty',\n },\n });\n\n if (releaseWithCreatorFields.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n\n await schedulingService.set(release.id, release.scheduledAt);\n }\n\n strapi.telemetry.send('didCreateContentRelease');\n\n return release;\n },\n\n async findOne(id: GetRelease.Request['params']['id'], query = {}) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query);\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n ...dbQuery,\n where: { id },\n });\n\n return release;\n },\n\n findPage(query?: GetReleases.Request['query']) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findPage({\n ...dbQuery,\n populate: {\n actions: {\n count: true,\n },\n },\n });\n },\n\n findMany(query?: any) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findMany({\n ...dbQuery,\n });\n },\n\n async update(\n id: Data.ID,\n releaseData: UpdateRelease.Request['body'],\n { user }: { user: UserInfo }\n ) {\n const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(\n releaseData\n );\n\n const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(\n 'release-validation',\n { strapi }\n );\n\n await Promise.all([\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${id}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n const updatedRelease = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: { id },\n data: releaseWithCreatorFields,\n });\n\n const schedulingService = getService('scheduling', { strapi });\n\n if (releaseData.scheduledAt) {\n // set function always cancel the previous job if it exists, so we can call it directly\n await schedulingService.set(id, releaseData.scheduledAt);\n } else if (release.scheduledAt) {\n // When user don't send a scheduledAt and we have one on the release, means that user want to unschedule it\n schedulingService.cancel(id);\n }\n\n this.updateReleaseStatus(id);\n\n strapi.telemetry.send('didUpdateContentRelease');\n\n return updatedRelease;\n },\n\n async getAllComponents() {\n const contentManagerComponentsService = strapi\n .plugin('content-manager')\n .service('components');\n\n const components = await contentManagerComponentsService.findAllComponents();\n\n const componentsMap = components.reduce(\n (\n acc: { [key: Struct.ComponentSchema['uid']]: Struct.ComponentSchema },\n component: Struct.ComponentSchema\n ) => {\n acc[component.uid] = component;\n\n return acc;\n },\n {}\n );\n\n return componentsMap;\n },\n\n async delete(releaseId: DeleteRelease.Request['params']['id']) {\n const release: Release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n where: { id: releaseId },\n populate: {\n actions: {\n select: ['id'],\n },\n },\n });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n // Only delete the release and its actions is you in fact can delete all the actions and the release\n // Otherwise, if the transaction fails it throws an error\n await strapi.db.transaction(async () => {\n await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({\n where: {\n id: {\n $in: release.actions.map((action) => action.id),\n },\n },\n });\n\n await strapi.db.query(RELEASE_MODEL_UID).delete({\n where: {\n id: releaseId,\n },\n });\n });\n\n if (release.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n await schedulingService.cancel(release.id);\n }\n\n strapi.telemetry.send('didDeleteContentRelease');\n\n return release;\n },\n\n async publish(releaseId: PublishRelease.Request['params']['id']) {\n const {\n release,\n error,\n }: { release: Pick<Release, 'id' | 'releasedAt' | 'status'> | null; error: unknown | null } =\n await strapi.db.transaction(async ({ trx }) => {\n /**\n * We lock the release in this transaction, so any other process trying to publish it will wait until this transaction is finished\n * In this transaction we don't care about rollback, becasue we want to persist the lock until the end and if it fails we want to change the release status to failed\n */\n const lockedRelease = (await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .select(['id', 'name', 'releasedAt', 'status'])\n .first()\n .transacting(trx)\n .forUpdate()\n .execute()) as Pick<Release, 'id' | 'name' | 'releasedAt' | 'status'> | undefined;\n\n if (!lockedRelease) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (lockedRelease.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n if (lockedRelease.status === 'failed') {\n throw new errors.ValidationError('Release failed to publish');\n }\n\n try {\n strapi.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);\n\n const formattedActions = await getFormattedActions(releaseId);\n\n await strapi.db.transaction(async () =>\n Promise.all(\n Object.keys(formattedActions).map(async (contentTypeUid) => {\n const contentType = contentTypeUid as UID.ContentType;\n const { publish, unpublish } = formattedActions[contentType];\n\n return Promise.all([\n ...publish.map((params) => strapi.documents(contentType).publish(params)),\n ...unpublish.map((params) => strapi.documents(contentType).unpublish(params)),\n ]);\n })\n )\n );\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'done',\n releasedAt: new Date(),\n },\n });\n\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: true,\n release,\n });\n\n strapi.telemetry.send('didPublishContentRelease');\n\n return { release, error: null };\n } catch (error) {\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: false,\n error,\n });\n\n // We need to run the update in the same transaction because the release is locked\n await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .update({\n status: 'failed',\n })\n .transacting(trx)\n .execute();\n\n // At this point, we don't want to throw the error because if that happen we rollback the change in the release status\n // We want to throw the error after the transaction is finished, so we return the error\n return {\n release: null,\n error,\n };\n }\n });\n\n // Now the first transaction is commited, we can safely throw the error if it exists\n if (error instanceof Error) {\n throw error;\n }\n\n return release;\n },\n\n async updateReleaseStatus(releaseId: Release['id']) {\n const releaseActionService = getService('release-action', { strapi });\n\n const [totalActions, invalidActions] = await Promise.all([\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n },\n }),\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n isEntryValid: false,\n },\n }),\n ]);\n\n if (totalActions > 0) {\n if (invalidActions > 0) {\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'blocked',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'ready',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'empty',\n },\n });\n },\n };\n};\n\nexport type ReleaseService = ReturnType<typeof createReleaseService>;\n\nexport default createReleaseService;\n"],"names":["createReleaseService","strapi","dispatchWebhook","event","isPublished","release","error","eventHub","emit","getFormattedActions","releaseId","actions","db","query","RELEASE_ACTION_MODEL_UID","findMany","where","id","length","errors","ValidationError","formattedActions","action","contentTypeUid","contentType","publish","unpublish","type","push","documentId","entryDocumentId","locale","create","releaseData","user","releaseWithCreatorFields","setCreatorFields","validatePendingReleasesLimit","validateUniqueNameForPendingRelease","validateScheduledAtIsLaterThanNow","getService","Promise","all","name","scheduledAt","RELEASE_MODEL_UID","data","status","schedulingService","set","telemetry","send","findOne","dbQuery","get","transform","findPage","populate","count","update","isEdition","NotFoundError","releasedAt","updatedRelease","cancel","updateReleaseStatus","getAllComponents","contentManagerComponentsService","plugin","service","components","findAllComponents","componentsMap","reduce","acc","component","uid","delete","select","transaction","deleteMany","$in","map","trx","lockedRelease","queryBuilder","first","transacting","forUpdate","execute","log","info","Object","keys","params","documents","Date","ALLOWED_WEBHOOK_EVENTS","RELEASES_PUBLISH","Error","releaseActionService","totalActions","invalidActions","countActions","filters","isEntryValid"],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ var nodeSchedule = require('node-schedule');
4
+ var utils = require('@strapi/utils');
5
+ var index = require('../utils/index.js');
6
+ var constants = require('../constants.js');
7
+
8
+ const createSchedulingService = ({ strapi })=>{
9
+ const scheduledJobs = new Map();
10
+ return {
11
+ async set (releaseId, scheduleDate) {
12
+ const release = await strapi.db.query(constants.RELEASE_MODEL_UID).findOne({
13
+ where: {
14
+ id: releaseId,
15
+ releasedAt: null
16
+ }
17
+ });
18
+ if (!release) {
19
+ throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
20
+ }
21
+ const job = nodeSchedule.scheduleJob(scheduleDate, async ()=>{
22
+ try {
23
+ await index.getService('release', {
24
+ strapi
25
+ }).publish(releaseId);
26
+ // @TODO: Trigger webhook with success message
27
+ } catch (error) {
28
+ // @TODO: Trigger webhook with error message
29
+ }
30
+ this.cancel(releaseId);
31
+ });
32
+ if (scheduledJobs.has(releaseId)) {
33
+ this.cancel(releaseId);
34
+ }
35
+ scheduledJobs.set(releaseId, job);
36
+ return scheduledJobs;
37
+ },
38
+ cancel (releaseId) {
39
+ if (scheduledJobs.has(releaseId)) {
40
+ scheduledJobs.get(releaseId).cancel();
41
+ scheduledJobs.delete(releaseId);
42
+ }
43
+ return scheduledJobs;
44
+ },
45
+ getAll () {
46
+ return scheduledJobs;
47
+ },
48
+ /**
49
+ * On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released
50
+ * This is useful in case the server was restarted and the scheduled jobs were lost
51
+ * This also could be used to sync different Strapi instances in case of a cluster
52
+ */ async syncFromDatabase () {
53
+ const releases = await strapi.db.query(constants.RELEASE_MODEL_UID).findMany({
54
+ where: {
55
+ scheduledAt: {
56
+ $gte: new Date()
57
+ },
58
+ releasedAt: null
59
+ }
60
+ });
61
+ for (const release of releases){
62
+ this.set(release.id, release.scheduledAt);
63
+ }
64
+ return scheduledJobs;
65
+ }
66
+ };
67
+ };
68
+
69
+ module.exports = createSchedulingService;
70
+ //# sourceMappingURL=scheduling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduling.js","sources":["../../../server/src/services/scheduling.ts"],"sourcesContent":["import { scheduleJob, Job } from 'node-schedule';\nimport type { Core } from '@strapi/types';\n\nimport { errors } from '@strapi/utils';\nimport { Release } from '../../../shared/contracts/releases';\nimport { getService } from '../utils';\nimport { RELEASE_MODEL_UID } from '../constants';\n\nconst createSchedulingService = ({ strapi }: { strapi: Core.Strapi }) => {\n const scheduledJobs = new Map<Release['id'], Job>();\n\n return {\n async set(releaseId: Release['id'], scheduleDate: Date) {\n const release = await strapi.db\n .query(RELEASE_MODEL_UID)\n .findOne({ where: { id: releaseId, releasedAt: null } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n const job = scheduleJob(scheduleDate, async () => {\n try {\n await getService('release', { strapi }).publish(releaseId);\n // @TODO: Trigger webhook with success message\n } catch (error) {\n // @TODO: Trigger webhook with error message\n }\n\n this.cancel(releaseId);\n });\n\n if (scheduledJobs.has(releaseId)) {\n this.cancel(releaseId);\n }\n\n scheduledJobs.set(releaseId, job);\n\n return scheduledJobs;\n },\n\n cancel(releaseId: Release['id']) {\n if (scheduledJobs.has(releaseId)) {\n scheduledJobs.get(releaseId)!.cancel();\n scheduledJobs.delete(releaseId);\n }\n\n return scheduledJobs;\n },\n\n getAll() {\n return scheduledJobs;\n },\n\n /**\n * On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released\n * This is useful in case the server was restarted and the scheduled jobs were lost\n * This also could be used to sync different Strapi instances in case of a cluster\n */\n async syncFromDatabase() {\n const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({\n where: {\n scheduledAt: {\n $gte: new Date(),\n },\n releasedAt: null,\n },\n });\n\n for (const release of releases) {\n this.set(release.id, release.scheduledAt);\n }\n\n return scheduledJobs;\n },\n };\n};\n\nexport default createSchedulingService;\n"],"names":["createSchedulingService","strapi","scheduledJobs","Map","set","releaseId","scheduleDate","release","db","query","RELEASE_MODEL_UID","findOne","where","id","releasedAt","errors","NotFoundError","job","scheduleJob","getService","publish","error","cancel","has","get","delete","getAll","syncFromDatabase","releases","findMany","scheduledAt","$gte","Date"],"mappings":";;;;;;;AAQA,MAAMA,uBAA0B,GAAA,CAAC,EAAEC,MAAM,EAA2B,GAAA;AAClE,IAAA,MAAMC,gBAAgB,IAAIC,GAAAA,EAAAA;IAE1B,OAAO;QACL,MAAMC,GAAAA,CAAAA,CAAIC,SAAwB,EAAEC,YAAkB,EAAA;YACpD,MAAMC,OAAAA,GAAU,MAAMN,MAAOO,CAAAA,EAAE,CAC5BC,KAAK,CAACC,2BACNC,CAAAA,CAAAA,OAAO,CAAC;gBAAEC,KAAO,EAAA;oBAAEC,EAAIR,EAAAA,SAAAA;oBAAWS,UAAY,EAAA;AAAK;AAAE,aAAA,CAAA;AAExD,YAAA,IAAI,CAACP,OAAS,EAAA;gBACZ,MAAM,IAAIQ,aAAOC,aAAa,CAAC,CAAC,wBAAwB,EAAEX,UAAU,CAAC,CAAA;AACvE;YAEA,MAAMY,GAAAA,GAAMC,yBAAYZ,YAAc,EAAA,UAAA;gBACpC,IAAI;AACF,oBAAA,MAAMa,iBAAW,SAAW,EAAA;AAAElB,wBAAAA;AAAO,qBAAA,CAAA,CAAGmB,OAAO,CAACf,SAAAA,CAAAA;;AAElD,iBAAA,CAAE,OAAOgB,KAAO,EAAA;;AAEhB;gBAEA,IAAI,CAACC,MAAM,CAACjB,SAAAA,CAAAA;AACd,aAAA,CAAA;YAEA,IAAIH,aAAAA,CAAcqB,GAAG,CAAClB,SAAY,CAAA,EAAA;gBAChC,IAAI,CAACiB,MAAM,CAACjB,SAAAA,CAAAA;AACd;YAEAH,aAAcE,CAAAA,GAAG,CAACC,SAAWY,EAAAA,GAAAA,CAAAA;YAE7B,OAAOf,aAAAA;AACT,SAAA;AAEAoB,QAAAA,MAAAA,CAAAA,CAAOjB,SAAwB,EAAA;YAC7B,IAAIH,aAAAA,CAAcqB,GAAG,CAAClB,SAAY,CAAA,EAAA;gBAChCH,aAAcsB,CAAAA,GAAG,CAACnB,SAAAA,CAAAA,CAAYiB,MAAM,EAAA;AACpCpB,gBAAAA,aAAAA,CAAcuB,MAAM,CAACpB,SAAAA,CAAAA;AACvB;YAEA,OAAOH,aAAAA;AACT,SAAA;AAEAwB,QAAAA,MAAAA,CAAAA,GAAAA;YACE,OAAOxB,aAAAA;AACT,SAAA;AAEA;;;;AAIC,QACD,MAAMyB,gBAAAA,CAAAA,GAAAA;YACJ,MAAMC,QAAAA,GAAW,MAAM3B,MAAOO,CAAAA,EAAE,CAACC,KAAK,CAACC,2BAAmBmB,CAAAA,CAAAA,QAAQ,CAAC;gBACjEjB,KAAO,EAAA;oBACLkB,WAAa,EAAA;AACXC,wBAAAA,IAAAA,EAAM,IAAIC,IAAAA;AACZ,qBAAA;oBACAlB,UAAY,EAAA;AACd;AACF,aAAA,CAAA;YAEA,KAAK,MAAMP,WAAWqB,QAAU,CAAA;AAC9B,gBAAA,IAAI,CAACxB,GAAG,CAACG,QAAQM,EAAE,EAAEN,QAAQuB,WAAW,CAAA;AAC1C;YAEA,OAAO5B,aAAAA;AACT;AACF,KAAA;AACF;;;;"}
@@ -0,0 +1,68 @@
1
+ import { scheduleJob } from 'node-schedule';
2
+ import { errors } from '@strapi/utils';
3
+ import { getService } from '../utils/index.mjs';
4
+ import { RELEASE_MODEL_UID } from '../constants.mjs';
5
+
6
+ const createSchedulingService = ({ strapi })=>{
7
+ const scheduledJobs = new Map();
8
+ return {
9
+ async set (releaseId, scheduleDate) {
10
+ const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({
11
+ where: {
12
+ id: releaseId,
13
+ releasedAt: null
14
+ }
15
+ });
16
+ if (!release) {
17
+ throw new errors.NotFoundError(`No release found for id ${releaseId}`);
18
+ }
19
+ const job = scheduleJob(scheduleDate, async ()=>{
20
+ try {
21
+ await getService('release', {
22
+ strapi
23
+ }).publish(releaseId);
24
+ // @TODO: Trigger webhook with success message
25
+ } catch (error) {
26
+ // @TODO: Trigger webhook with error message
27
+ }
28
+ this.cancel(releaseId);
29
+ });
30
+ if (scheduledJobs.has(releaseId)) {
31
+ this.cancel(releaseId);
32
+ }
33
+ scheduledJobs.set(releaseId, job);
34
+ return scheduledJobs;
35
+ },
36
+ cancel (releaseId) {
37
+ if (scheduledJobs.has(releaseId)) {
38
+ scheduledJobs.get(releaseId).cancel();
39
+ scheduledJobs.delete(releaseId);
40
+ }
41
+ return scheduledJobs;
42
+ },
43
+ getAll () {
44
+ return scheduledJobs;
45
+ },
46
+ /**
47
+ * On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released
48
+ * This is useful in case the server was restarted and the scheduled jobs were lost
49
+ * This also could be used to sync different Strapi instances in case of a cluster
50
+ */ async syncFromDatabase () {
51
+ const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
52
+ where: {
53
+ scheduledAt: {
54
+ $gte: new Date()
55
+ },
56
+ releasedAt: null
57
+ }
58
+ });
59
+ for (const release of releases){
60
+ this.set(release.id, release.scheduledAt);
61
+ }
62
+ return scheduledJobs;
63
+ }
64
+ };
65
+ };
66
+
67
+ export { createSchedulingService as default };
68
+ //# sourceMappingURL=scheduling.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduling.mjs","sources":["../../../server/src/services/scheduling.ts"],"sourcesContent":["import { scheduleJob, Job } from 'node-schedule';\nimport type { Core } from '@strapi/types';\n\nimport { errors } from '@strapi/utils';\nimport { Release } from '../../../shared/contracts/releases';\nimport { getService } from '../utils';\nimport { RELEASE_MODEL_UID } from '../constants';\n\nconst createSchedulingService = ({ strapi }: { strapi: Core.Strapi }) => {\n const scheduledJobs = new Map<Release['id'], Job>();\n\n return {\n async set(releaseId: Release['id'], scheduleDate: Date) {\n const release = await strapi.db\n .query(RELEASE_MODEL_UID)\n .findOne({ where: { id: releaseId, releasedAt: null } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n const job = scheduleJob(scheduleDate, async () => {\n try {\n await getService('release', { strapi }).publish(releaseId);\n // @TODO: Trigger webhook with success message\n } catch (error) {\n // @TODO: Trigger webhook with error message\n }\n\n this.cancel(releaseId);\n });\n\n if (scheduledJobs.has(releaseId)) {\n this.cancel(releaseId);\n }\n\n scheduledJobs.set(releaseId, job);\n\n return scheduledJobs;\n },\n\n cancel(releaseId: Release['id']) {\n if (scheduledJobs.has(releaseId)) {\n scheduledJobs.get(releaseId)!.cancel();\n scheduledJobs.delete(releaseId);\n }\n\n return scheduledJobs;\n },\n\n getAll() {\n return scheduledJobs;\n },\n\n /**\n * On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released\n * This is useful in case the server was restarted and the scheduled jobs were lost\n * This also could be used to sync different Strapi instances in case of a cluster\n */\n async syncFromDatabase() {\n const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({\n where: {\n scheduledAt: {\n $gte: new Date(),\n },\n releasedAt: null,\n },\n });\n\n for (const release of releases) {\n this.set(release.id, release.scheduledAt);\n }\n\n return scheduledJobs;\n },\n };\n};\n\nexport default createSchedulingService;\n"],"names":["createSchedulingService","strapi","scheduledJobs","Map","set","releaseId","scheduleDate","release","db","query","RELEASE_MODEL_UID","findOne","where","id","releasedAt","errors","NotFoundError","job","scheduleJob","getService","publish","error","cancel","has","get","delete","getAll","syncFromDatabase","releases","findMany","scheduledAt","$gte","Date"],"mappings":";;;;;AAQA,MAAMA,uBAA0B,GAAA,CAAC,EAAEC,MAAM,EAA2B,GAAA;AAClE,IAAA,MAAMC,gBAAgB,IAAIC,GAAAA,EAAAA;IAE1B,OAAO;QACL,MAAMC,GAAAA,CAAAA,CAAIC,SAAwB,EAAEC,YAAkB,EAAA;YACpD,MAAMC,OAAAA,GAAU,MAAMN,MAAOO,CAAAA,EAAE,CAC5BC,KAAK,CAACC,iBACNC,CAAAA,CAAAA,OAAO,CAAC;gBAAEC,KAAO,EAAA;oBAAEC,EAAIR,EAAAA,SAAAA;oBAAWS,UAAY,EAAA;AAAK;AAAE,aAAA,CAAA;AAExD,YAAA,IAAI,CAACP,OAAS,EAAA;gBACZ,MAAM,IAAIQ,OAAOC,aAAa,CAAC,CAAC,wBAAwB,EAAEX,UAAU,CAAC,CAAA;AACvE;YAEA,MAAMY,GAAAA,GAAMC,YAAYZ,YAAc,EAAA,UAAA;gBACpC,IAAI;AACF,oBAAA,MAAMa,WAAW,SAAW,EAAA;AAAElB,wBAAAA;AAAO,qBAAA,CAAA,CAAGmB,OAAO,CAACf,SAAAA,CAAAA;;AAElD,iBAAA,CAAE,OAAOgB,KAAO,EAAA;;AAEhB;gBAEA,IAAI,CAACC,MAAM,CAACjB,SAAAA,CAAAA;AACd,aAAA,CAAA;YAEA,IAAIH,aAAAA,CAAcqB,GAAG,CAAClB,SAAY,CAAA,EAAA;gBAChC,IAAI,CAACiB,MAAM,CAACjB,SAAAA,CAAAA;AACd;YAEAH,aAAcE,CAAAA,GAAG,CAACC,SAAWY,EAAAA,GAAAA,CAAAA;YAE7B,OAAOf,aAAAA;AACT,SAAA;AAEAoB,QAAAA,MAAAA,CAAAA,CAAOjB,SAAwB,EAAA;YAC7B,IAAIH,aAAAA,CAAcqB,GAAG,CAAClB,SAAY,CAAA,EAAA;gBAChCH,aAAcsB,CAAAA,GAAG,CAACnB,SAAAA,CAAAA,CAAYiB,MAAM,EAAA;AACpCpB,gBAAAA,aAAAA,CAAcuB,MAAM,CAACpB,SAAAA,CAAAA;AACvB;YAEA,OAAOH,aAAAA;AACT,SAAA;AAEAwB,QAAAA,MAAAA,CAAAA,GAAAA;YACE,OAAOxB,aAAAA;AACT,SAAA;AAEA;;;;AAIC,QACD,MAAMyB,gBAAAA,CAAAA,GAAAA;YACJ,MAAMC,QAAAA,GAAW,MAAM3B,MAAOO,CAAAA,EAAE,CAACC,KAAK,CAACC,iBAAmBmB,CAAAA,CAAAA,QAAQ,CAAC;gBACjEjB,KAAO,EAAA;oBACLkB,WAAa,EAAA;AACXC,wBAAAA,IAAAA,EAAM,IAAIC,IAAAA;AACZ,qBAAA;oBACAlB,UAAY,EAAA;AACd;AACF,aAAA,CAAA;YAEA,KAAK,MAAMP,WAAWqB,QAAU,CAAA;AAC9B,gBAAA,IAAI,CAACxB,GAAG,CAACG,QAAQM,EAAE,EAAEN,QAAQuB,WAAW,CAAA;AAC1C;YAEA,OAAO5B,aAAAA;AACT;AACF,KAAA;AACF;;;;"}
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+
3
+ const DEFAULT_SETTINGS = {
4
+ defaultTimezone: null
5
+ };
6
+ const createSettingsService = ({ strapi })=>{
7
+ const getStore = async ()=>strapi.store({
8
+ type: 'core',
9
+ name: 'content-releases'
10
+ });
11
+ return {
12
+ async update ({ settings }) {
13
+ const store = await getStore();
14
+ store.set({
15
+ key: 'settings',
16
+ value: settings
17
+ });
18
+ return settings;
19
+ },
20
+ async find () {
21
+ const store = await getStore();
22
+ const settings = await store.get({
23
+ key: 'settings'
24
+ });
25
+ return {
26
+ ...DEFAULT_SETTINGS,
27
+ ...settings || {}
28
+ };
29
+ }
30
+ };
31
+ };
32
+
33
+ module.exports = createSettingsService;
34
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sources":["../../../server/src/services/settings.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport type { Settings } from '../../../shared/contracts/settings';\n\nconst DEFAULT_SETTINGS = {\n defaultTimezone: null,\n} satisfies Settings;\n\nconst createSettingsService = ({ strapi }: { strapi: Core.Strapi }) => {\n const getStore = async () => strapi.store({ type: 'core', name: 'content-releases' });\n\n return {\n async update({ settings }: { settings: Settings }): Promise<Settings> {\n const store = await getStore();\n store.set({ key: 'settings', value: settings });\n return settings;\n },\n async find(): Promise<Settings> {\n const store = await getStore();\n const settings = (await store.get({ key: 'settings' })) as Settings | undefined;\n\n return {\n ...DEFAULT_SETTINGS,\n ...(settings || {}),\n };\n },\n };\n};\n\nexport type SettingsService = ReturnType<typeof createSettingsService>;\n\nexport default createSettingsService;\n"],"names":["DEFAULT_SETTINGS","defaultTimezone","createSettingsService","strapi","getStore","store","type","name","update","settings","set","key","value","find","get"],"mappings":";;AAIA,MAAMA,gBAAmB,GAAA;IACvBC,eAAiB,EAAA;AACnB,CAAA;AAEA,MAAMC,qBAAwB,GAAA,CAAC,EAAEC,MAAM,EAA2B,GAAA;AAChE,IAAA,MAAMC,QAAW,GAAA,UAAYD,MAAOE,CAAAA,KAAK,CAAC;YAAEC,IAAM,EAAA,MAAA;YAAQC,IAAM,EAAA;AAAmB,SAAA,CAAA;IAEnF,OAAO;QACL,MAAMC,MAAAA,CAAAA,CAAO,EAAEC,QAAQ,EAA0B,EAAA;AAC/C,YAAA,MAAMJ,QAAQ,MAAMD,QAAAA,EAAAA;AACpBC,YAAAA,KAAAA,CAAMK,GAAG,CAAC;gBAAEC,GAAK,EAAA,UAAA;gBAAYC,KAAOH,EAAAA;AAAS,aAAA,CAAA;YAC7C,OAAOA,QAAAA;AACT,SAAA;QACA,MAAMI,IAAAA,CAAAA,GAAAA;AACJ,YAAA,MAAMR,QAAQ,MAAMD,QAAAA,EAAAA;AACpB,YAAA,MAAMK,QAAY,GAAA,MAAMJ,KAAMS,CAAAA,GAAG,CAAC;gBAAEH,GAAK,EAAA;AAAW,aAAA,CAAA;YAEpD,OAAO;AACL,gBAAA,GAAGX,gBAAgB;gBACnB,GAAIS,QAAAA,IAAY;AAClB,aAAA;AACF;AACF,KAAA;AACF;;;;"}
@@ -0,0 +1,32 @@
1
+ const DEFAULT_SETTINGS = {
2
+ defaultTimezone: null
3
+ };
4
+ const createSettingsService = ({ strapi })=>{
5
+ const getStore = async ()=>strapi.store({
6
+ type: 'core',
7
+ name: 'content-releases'
8
+ });
9
+ return {
10
+ async update ({ settings }) {
11
+ const store = await getStore();
12
+ store.set({
13
+ key: 'settings',
14
+ value: settings
15
+ });
16
+ return settings;
17
+ },
18
+ async find () {
19
+ const store = await getStore();
20
+ const settings = await store.get({
21
+ key: 'settings'
22
+ });
23
+ return {
24
+ ...DEFAULT_SETTINGS,
25
+ ...settings || {}
26
+ };
27
+ }
28
+ };
29
+ };
30
+
31
+ export { createSettingsService as default };
32
+ //# sourceMappingURL=settings.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.mjs","sources":["../../../server/src/services/settings.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport type { Settings } from '../../../shared/contracts/settings';\n\nconst DEFAULT_SETTINGS = {\n defaultTimezone: null,\n} satisfies Settings;\n\nconst createSettingsService = ({ strapi }: { strapi: Core.Strapi }) => {\n const getStore = async () => strapi.store({ type: 'core', name: 'content-releases' });\n\n return {\n async update({ settings }: { settings: Settings }): Promise<Settings> {\n const store = await getStore();\n store.set({ key: 'settings', value: settings });\n return settings;\n },\n async find(): Promise<Settings> {\n const store = await getStore();\n const settings = (await store.get({ key: 'settings' })) as Settings | undefined;\n\n return {\n ...DEFAULT_SETTINGS,\n ...(settings || {}),\n };\n },\n };\n};\n\nexport type SettingsService = ReturnType<typeof createSettingsService>;\n\nexport default createSettingsService;\n"],"names":["DEFAULT_SETTINGS","defaultTimezone","createSettingsService","strapi","getStore","store","type","name","update","settings","set","key","value","find","get"],"mappings":"AAIA,MAAMA,gBAAmB,GAAA;IACvBC,eAAiB,EAAA;AACnB,CAAA;AAEA,MAAMC,qBAAwB,GAAA,CAAC,EAAEC,MAAM,EAA2B,GAAA;AAChE,IAAA,MAAMC,QAAW,GAAA,UAAYD,MAAOE,CAAAA,KAAK,CAAC;YAAEC,IAAM,EAAA,MAAA;YAAQC,IAAM,EAAA;AAAmB,SAAA,CAAA;IAEnF,OAAO;QACL,MAAMC,MAAAA,CAAAA,CAAO,EAAEC,QAAQ,EAA0B,EAAA;AAC/C,YAAA,MAAMJ,QAAQ,MAAMD,QAAAA,EAAAA;AACpBC,YAAAA,KAAAA,CAAMK,GAAG,CAAC;gBAAEC,GAAK,EAAA,UAAA;gBAAYC,KAAOH,EAAAA;AAAS,aAAA,CAAA;YAC7C,OAAOA,QAAAA;AACT,SAAA;QACA,MAAMI,IAAAA,CAAAA,GAAAA;AACJ,YAAA,MAAMR,QAAQ,MAAMD,QAAAA,EAAAA;AACpB,YAAA,MAAMK,QAAY,GAAA,MAAMJ,KAAMS,CAAAA,GAAG,CAAC;gBAAEH,GAAK,EAAA;AAAW,aAAA,CAAA;YAEpD,OAAO;AACL,gBAAA,GAAGX,gBAAgB;gBACnB,GAAIS,QAAAA,IAAY;AAClB,aAAA;AACF;AACF,KAAA;AACF;;;;"}