ghost 5.54.4 → 5.55.0

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 (154) hide show
  1. package/components/tryghost-adapter-cache-memory-ttl-5.55.0.tgz +0 -0
  2. package/components/tryghost-adapter-cache-redis-5.55.0.tgz +0 -0
  3. package/components/{tryghost-adapter-manager-5.54.4.tgz → tryghost-adapter-manager-5.55.0.tgz} +0 -0
  4. package/components/tryghost-announcement-bar-settings-5.55.0.tgz +0 -0
  5. package/components/{tryghost-api-framework-5.54.4.tgz → tryghost-api-framework-5.55.0.tgz} +0 -0
  6. package/components/tryghost-api-version-compatibility-service-5.55.0.tgz +0 -0
  7. package/components/tryghost-audience-feedback-5.55.0.tgz +0 -0
  8. package/components/{tryghost-bootstrap-socket-5.54.4.tgz → tryghost-bootstrap-socket-5.55.0.tgz} +0 -0
  9. package/components/tryghost-collections-5.55.0.tgz +0 -0
  10. package/components/tryghost-constants-5.55.0.tgz +0 -0
  11. package/components/{tryghost-custom-theme-settings-service-5.54.4.tgz → tryghost-custom-theme-settings-service-5.55.0.tgz} +0 -0
  12. package/components/{tryghost-data-generator-5.54.4.tgz → tryghost-data-generator-5.55.0.tgz} +0 -0
  13. package/components/tryghost-domain-events-5.55.0.tgz +0 -0
  14. package/components/tryghost-dynamic-routing-events-5.55.0.tgz +0 -0
  15. package/components/tryghost-email-analytics-provider-mailgun-5.55.0.tgz +0 -0
  16. package/components/tryghost-email-analytics-service-5.55.0.tgz +0 -0
  17. package/components/tryghost-email-content-generator-5.55.0.tgz +0 -0
  18. package/components/tryghost-email-events-5.55.0.tgz +0 -0
  19. package/components/tryghost-email-service-5.55.0.tgz +0 -0
  20. package/components/tryghost-email-suppression-list-5.55.0.tgz +0 -0
  21. package/components/tryghost-event-aware-cache-wrapper-5.55.0.tgz +0 -0
  22. package/components/tryghost-express-dynamic-redirects-5.55.0.tgz +0 -0
  23. package/components/{tryghost-external-media-inliner-5.54.4.tgz → tryghost-external-media-inliner-5.55.0.tgz} +0 -0
  24. package/components/tryghost-extract-api-key-5.55.0.tgz +0 -0
  25. package/components/tryghost-html-to-plaintext-5.55.0.tgz +0 -0
  26. package/components/tryghost-i18n-5.55.0.tgz +0 -0
  27. package/components/{tryghost-importer-handler-content-files-5.54.4.tgz → tryghost-importer-handler-content-files-5.55.0.tgz} +0 -0
  28. package/components/{tryghost-importer-revue-5.54.4.tgz → tryghost-importer-revue-5.55.0.tgz} +0 -0
  29. package/components/tryghost-in-memory-repository-5.55.0.tgz +0 -0
  30. package/components/{tryghost-job-manager-5.54.4.tgz → tryghost-job-manager-5.55.0.tgz} +0 -0
  31. package/components/tryghost-link-redirects-5.55.0.tgz +0 -0
  32. package/components/{tryghost-link-replacer-5.54.4.tgz → tryghost-link-replacer-5.55.0.tgz} +0 -0
  33. package/components/tryghost-link-tracking-5.55.0.tgz +0 -0
  34. package/components/{tryghost-magic-link-5.54.4.tgz → tryghost-magic-link-5.55.0.tgz} +0 -0
  35. package/components/tryghost-mail-events-5.55.0.tgz +0 -0
  36. package/components/tryghost-mailgun-client-5.55.0.tgz +0 -0
  37. package/components/{tryghost-member-attribution-5.54.4.tgz → tryghost-member-attribution-5.55.0.tgz} +0 -0
  38. package/components/tryghost-member-events-5.55.0.tgz +0 -0
  39. package/components/tryghost-members-api-5.55.0.tgz +0 -0
  40. package/components/tryghost-members-csv-5.55.0.tgz +0 -0
  41. package/components/{tryghost-members-events-service-5.54.4.tgz → tryghost-members-events-service-5.55.0.tgz} +0 -0
  42. package/components/tryghost-members-importer-5.55.0.tgz +0 -0
  43. package/components/tryghost-members-offers-5.55.0.tgz +0 -0
  44. package/components/tryghost-members-payments-5.55.0.tgz +0 -0
  45. package/components/{tryghost-members-ssr-5.54.4.tgz → tryghost-members-ssr-5.55.0.tgz} +0 -0
  46. package/components/tryghost-members-stripe-service-5.55.0.tgz +0 -0
  47. package/components/tryghost-mentions-email-report-5.55.0.tgz +0 -0
  48. package/components/{tryghost-milestones-5.54.4.tgz → tryghost-milestones-5.55.0.tgz} +0 -0
  49. package/components/tryghost-minifier-5.55.0.tgz +0 -0
  50. package/components/tryghost-model-to-domain-event-interceptor-5.55.0.tgz +0 -0
  51. package/components/tryghost-mw-api-version-mismatch-5.55.0.tgz +0 -0
  52. package/components/tryghost-mw-cache-control-5.55.0.tgz +0 -0
  53. package/components/{tryghost-mw-error-handler-5.54.4.tgz → tryghost-mw-error-handler-5.55.0.tgz} +0 -0
  54. package/components/tryghost-mw-session-from-token-5.55.0.tgz +0 -0
  55. package/components/tryghost-mw-update-user-last-seen-5.55.0.tgz +0 -0
  56. package/components/tryghost-mw-version-match-5.55.0.tgz +0 -0
  57. package/components/tryghost-mw-vhost-5.55.0.tgz +0 -0
  58. package/components/tryghost-oembed-service-5.55.0.tgz +0 -0
  59. package/components/{tryghost-package-json-5.54.4.tgz → tryghost-package-json-5.55.0.tgz} +0 -0
  60. package/components/tryghost-post-revisions-5.55.0.tgz +0 -0
  61. package/components/tryghost-posts-service-5.55.0.tgz +0 -0
  62. package/components/tryghost-referrers-5.55.0.tgz +0 -0
  63. package/components/{tryghost-security-5.54.4.tgz → tryghost-security-5.55.0.tgz} +0 -0
  64. package/components/tryghost-session-service-5.55.0.tgz +0 -0
  65. package/components/tryghost-settings-path-manager-5.55.0.tgz +0 -0
  66. package/components/tryghost-slack-notifications-5.55.0.tgz +0 -0
  67. package/components/{tryghost-staff-service-5.54.4.tgz → tryghost-staff-service-5.55.0.tgz} +0 -0
  68. package/components/tryghost-stats-service-5.55.0.tgz +0 -0
  69. package/components/{tryghost-tiers-5.54.4.tgz → tryghost-tiers-5.55.0.tgz} +0 -0
  70. package/components/tryghost-update-check-service-5.55.0.tgz +0 -0
  71. package/components/tryghost-verification-trigger-5.55.0.tgz +0 -0
  72. package/components/tryghost-version-notifications-data-service-5.55.0.tgz +0 -0
  73. package/components/{tryghost-webmentions-5.54.4.tgz → tryghost-webmentions-5.55.0.tgz} +0 -0
  74. package/content/themes/casper/assets/built/screen.css +1 -1
  75. package/content/themes/casper/assets/built/screen.css.map +1 -1
  76. package/content/themes/casper/assets/css/screen.css +22 -6
  77. package/content/themes/casper/page.hbs +26 -24
  78. package/core/built/admin/assets/{chunk.143.3416d003faf26a5d1543.js → chunk.143.60a057de7aaa4abc6a26.js} +6 -7
  79. package/core/built/admin/assets/{chunk.178.6e1499eeefacd04470fe.js → chunk.178.f18f14cc0e6c67a6fd36.js} +4 -4
  80. package/core/built/admin/assets/{chunk.486.3896d2a7816cf03a0794.js → chunk.757.c768dcbeaf1cee916395.js} +1146 -1360
  81. package/core/built/admin/assets/{ghost-14eecea754ec854244d71e1300ec75f2.js → ghost-135607a3c421c472feb9f59f14c625fb.js} +782 -901
  82. package/core/built/admin/assets/{vendor-0d244308df4eb04209cbd5fa6abe358b.js → vendor-0a8fc851393c473cd269f04fc4e97974.js} +2167 -2552
  83. package/core/built/admin/index.html +5 -5
  84. package/core/frontend/helpers/collection.js +146 -0
  85. package/core/frontend/src/cards/css/header_v2.css +283 -0
  86. package/core/frontend/src/cards/css/signup.css +1 -0
  87. package/core/server/api/endpoints/collections.js +0 -19
  88. package/core/server/api/endpoints/posts-public.js +5 -2
  89. package/core/server/api/endpoints/utils/serializers/output/index.js +0 -4
  90. package/core/server/data/migrations/versions/5.55/2023-07-10-05-15-55-add-built-in-collections.js +60 -0
  91. package/core/server/data/migrations/versions/5.55/2023-07-10-05-16-55-add-built-in-collection-posts.js +99 -0
  92. package/core/server/data/schema/fixtures/fixtures.json +33 -0
  93. package/core/server/lib/lexical.js +2 -2
  94. package/core/server/models/post.js +1 -1
  95. package/core/server/services/collections/BookshelfCollectionsRepository.js +39 -10
  96. package/core/server/services/collections/PostsRepository.js +17 -52
  97. package/core/server/services/collections/service.js +4 -24
  98. package/core/server/web/api/endpoints/admin/routes.js +0 -1
  99. package/package.json +144 -144
  100. package/yarn.lock +1820 -1678
  101. package/components/tryghost-adapter-cache-memory-ttl-5.54.4.tgz +0 -0
  102. package/components/tryghost-adapter-cache-redis-5.54.4.tgz +0 -0
  103. package/components/tryghost-announcement-bar-settings-5.54.4.tgz +0 -0
  104. package/components/tryghost-api-version-compatibility-service-5.54.4.tgz +0 -0
  105. package/components/tryghost-audience-feedback-5.54.4.tgz +0 -0
  106. package/components/tryghost-collections-5.54.4.tgz +0 -0
  107. package/components/tryghost-constants-5.54.4.tgz +0 -0
  108. package/components/tryghost-domain-events-5.54.4.tgz +0 -0
  109. package/components/tryghost-dynamic-routing-events-5.54.4.tgz +0 -0
  110. package/components/tryghost-email-analytics-provider-mailgun-5.54.4.tgz +0 -0
  111. package/components/tryghost-email-analytics-service-5.54.4.tgz +0 -0
  112. package/components/tryghost-email-content-generator-5.54.4.tgz +0 -0
  113. package/components/tryghost-email-events-5.54.4.tgz +0 -0
  114. package/components/tryghost-email-service-5.54.4.tgz +0 -0
  115. package/components/tryghost-email-suppression-list-5.54.4.tgz +0 -0
  116. package/components/tryghost-event-aware-cache-wrapper-5.54.4.tgz +0 -0
  117. package/components/tryghost-express-dynamic-redirects-5.54.4.tgz +0 -0
  118. package/components/tryghost-extract-api-key-5.54.4.tgz +0 -0
  119. package/components/tryghost-html-to-plaintext-5.54.4.tgz +0 -0
  120. package/components/tryghost-i18n-5.54.4.tgz +0 -0
  121. package/components/tryghost-in-memory-repository-5.54.4.tgz +0 -0
  122. package/components/tryghost-link-redirects-5.54.4.tgz +0 -0
  123. package/components/tryghost-link-tracking-5.54.4.tgz +0 -0
  124. package/components/tryghost-mail-events-5.54.4.tgz +0 -0
  125. package/components/tryghost-mailgun-client-5.54.4.tgz +0 -0
  126. package/components/tryghost-member-events-5.54.4.tgz +0 -0
  127. package/components/tryghost-members-api-5.54.4.tgz +0 -0
  128. package/components/tryghost-members-csv-5.54.4.tgz +0 -0
  129. package/components/tryghost-members-importer-5.54.4.tgz +0 -0
  130. package/components/tryghost-members-offers-5.54.4.tgz +0 -0
  131. package/components/tryghost-members-payments-5.54.4.tgz +0 -0
  132. package/components/tryghost-members-stripe-service-5.54.4.tgz +0 -0
  133. package/components/tryghost-mentions-email-report-5.54.4.tgz +0 -0
  134. package/components/tryghost-minifier-5.54.4.tgz +0 -0
  135. package/components/tryghost-model-to-domain-event-interceptor-5.54.4.tgz +0 -0
  136. package/components/tryghost-mw-api-version-mismatch-5.54.4.tgz +0 -0
  137. package/components/tryghost-mw-cache-control-5.54.4.tgz +0 -0
  138. package/components/tryghost-mw-session-from-token-5.54.4.tgz +0 -0
  139. package/components/tryghost-mw-update-user-last-seen-5.54.4.tgz +0 -0
  140. package/components/tryghost-mw-version-match-5.54.4.tgz +0 -0
  141. package/components/tryghost-mw-vhost-5.54.4.tgz +0 -0
  142. package/components/tryghost-oembed-service-5.54.4.tgz +0 -0
  143. package/components/tryghost-post-revisions-5.54.4.tgz +0 -0
  144. package/components/tryghost-posts-service-5.54.4.tgz +0 -0
  145. package/components/tryghost-referrers-5.54.4.tgz +0 -0
  146. package/components/tryghost-session-service-5.54.4.tgz +0 -0
  147. package/components/tryghost-settings-path-manager-5.54.4.tgz +0 -0
  148. package/components/tryghost-slack-notifications-5.54.4.tgz +0 -0
  149. package/components/tryghost-stats-service-5.54.4.tgz +0 -0
  150. package/components/tryghost-update-check-service-5.54.4.tgz +0 -0
  151. package/components/tryghost-verification-trigger-5.54.4.tgz +0 -0
  152. package/components/tryghost-version-notifications-data-service-5.54.4.tgz +0 -0
  153. package/core/server/api/endpoints/utils/serializers/output/collections.js +0 -10
  154. /package/core/built/admin/assets/{chunk.486.3896d2a7816cf03a0794.js.LICENSE.txt → chunk.757.c768dcbeaf1cee916395.js.LICENSE.txt} +0 -0
@@ -1,5 +1,24 @@
1
1
  {
2
2
  "models": [
3
+ {
4
+ "name": "Collection",
5
+ "entries": [
6
+ {
7
+ "title": "Latest",
8
+ "slug": "latest",
9
+ "description": "All posts",
10
+ "type": "automatic",
11
+ "filter": ""
12
+ },
13
+ {
14
+ "title": "Featured",
15
+ "slug": "featured",
16
+ "description": "Featured posts",
17
+ "type": "automatic",
18
+ "filter": "featured:true"
19
+ }
20
+ ]
21
+ },
3
22
  {
4
23
  "name": "Product",
5
24
  "entries": [
@@ -918,6 +937,20 @@
918
937
  "coming-soon": ["News"]
919
938
  }
920
939
  },
940
+ {
941
+ "from": {
942
+ "model": "Collection",
943
+ "match": "slug",
944
+ "relation": "posts"
945
+ },
946
+ "to": {
947
+ "model": "Post",
948
+ "match": "slug"
949
+ },
950
+ "entries": {
951
+ "latest": ["coming-soon"]
952
+ }
953
+ },
921
954
  {
922
955
  "from": {
923
956
  "model": "User",
@@ -26,7 +26,7 @@ module.exports = {
26
26
  return lexicalHtmlRenderer;
27
27
  },
28
28
 
29
- render(lexical, userOptions = {}) {
29
+ async render(lexical, userOptions = {}) {
30
30
  const options = Object.assign({
31
31
  siteUrl: config.get('url'),
32
32
  imageOptimization: config.get('imageOptimization'),
@@ -45,7 +45,7 @@ module.exports = {
45
45
  }
46
46
  }, userOptions);
47
47
 
48
- return this.lexicalHtmlRenderer.render(lexical, options);
48
+ return await this.lexicalHtmlRenderer.render(lexical, options);
49
49
  },
50
50
 
51
51
  get nodes() {
@@ -688,7 +688,7 @@ Post = ghostBookshelf.Model.extend({
688
688
  )
689
689
  ) {
690
690
  try {
691
- this.set('html', lexicalLib.render(this.get('lexical')));
691
+ this.set('html', await lexicalLib.render(this.get('lexical')));
692
692
  } catch (err) {
693
693
  throw new errors.ValidationError({
694
694
  message: tpl(messages.invalidLexicalStructure),
@@ -12,12 +12,20 @@ module.exports = class BookshelfCollectionsRepository {
12
12
  this.#model = model;
13
13
  }
14
14
 
15
+ async createTransaction(cb) {
16
+ return this.#model.transaction(cb);
17
+ }
18
+
15
19
  /**
16
20
  * @param {string} id
17
21
  * @returns {Promise<Collection>}
18
22
  */
19
- async getById(id) {
20
- const model = await this.#model.findOne({id}, {require: false, withRelated: ['posts']});
23
+ async getById(id, options = {}) {
24
+ const model = await this.#model.findOne({id}, {
25
+ require: false,
26
+ withRelated: ['posts'],
27
+ transacting: options.transaction
28
+ });
21
29
  if (!model) {
22
30
  return null;
23
31
  }
@@ -28,8 +36,12 @@ module.exports = class BookshelfCollectionsRepository {
28
36
  * @param {string} slug
29
37
  * @returns {Promise<Collection>}
30
38
  */
31
- async getBySlug(slug) {
32
- const model = await this.#model.findOne({slug}, {require: false, withRelated: ['posts']});
39
+ async getBySlug(slug, options = {}) {
40
+ const model = await this.#model.findOne({slug}, {
41
+ require: false,
42
+ withRelated: ['posts'],
43
+ transacting: options.transaction
44
+ });
33
45
  if (!model) {
34
46
  return null;
35
47
  }
@@ -40,9 +52,15 @@ module.exports = class BookshelfCollectionsRepository {
40
52
  * @param {object} [options]
41
53
  * @param {string} [options.filter]
42
54
  * @param {string} [options.order]
55
+ * @param {import('knex').Transaction} [options.transaction]
43
56
  */
44
57
  async getAll(options = {}) {
45
- const models = await this.#model.findAll({...options, withRelated: ['posts']});
58
+ const models = await this.#model.findAll({
59
+ ...options,
60
+ transacting: options.transaction,
61
+ withRelated: ['posts']
62
+ });
63
+
46
64
  return await Promise.all(models.map(model => this.#modelToCollection(model)));
47
65
  }
48
66
 
@@ -65,9 +83,11 @@ module.exports = class BookshelfCollectionsRepository {
65
83
 
66
84
  /**
67
85
  * @param {Collection} collection
86
+ * @param {object} [options]
87
+ * @param {import('knex').Transaction} [options.transaction]
68
88
  * @returns {Promise<void>}
69
89
  */
70
- async save(collection) {
90
+ async save(collection, options = {}) {
71
91
  if (collection.deleted) {
72
92
  await this.#model.destroy({id: collection.id});
73
93
  return;
@@ -85,13 +105,22 @@ module.exports = class BookshelfCollectionsRepository {
85
105
  updated_at: collection.updatedAt
86
106
  };
87
107
 
88
- const existing = await this.#model.findOne({id: data.id}, {require: false});
108
+ const existing = await this.#model.findOne(
109
+ {id: data.id},
110
+ {
111
+ require: false,
112
+ transacting: options.transaction
113
+ }
114
+ );
89
115
 
90
116
  if (!existing) {
91
- await this.#model.add(data);
117
+ await this.#model.add(data, {
118
+ transacting: options.transaction
119
+ });
92
120
  } else {
93
- await this.#model.edit(data, {
94
- id: data.id
121
+ return this.#model.edit(data, {
122
+ id: data.id,
123
+ transacting: options.transaction
95
124
  });
96
125
  }
97
126
  }
@@ -1,57 +1,28 @@
1
1
  class PostsRepository {
2
- constructor({models, browsePostsAPI, moment}) {
2
+ constructor({models, moment}) {
3
3
  this.models = models;
4
- this.browsePostsAPI = browsePostsAPI;
5
4
  this.moment = moment;
6
5
  }
7
6
 
8
- /**
9
- * @NOTE: This is a copy of the date serialization from Posts Content API.
10
- * We need the dates serialized instead of keeping them as plain dates
11
- * to be able to apply NQL filtering inside of Collections.
12
- * @param {Object} attrs
13
- * @returns
14
- */
15
- serializeDates(attrs) {
16
- const formatDate = (date) => {
17
- return this.moment(date)
18
- .toISOString(true);
19
- };
20
-
21
- ['created_at', 'updated_at', 'published_at'].forEach((field) => {
22
- if (attrs[field]) {
23
- attrs[field] = formatDate(attrs[field]);
24
- }
25
- });
26
-
27
- return attrs;
28
- }
29
-
30
- async getAll({filter}) {
31
- const response = await this.browsePostsAPI({
32
- options: {
33
- filter: `(${filter})+type:post`,
34
- limit: 'all'
35
- }
7
+ async getAll({filter, transaction}) {
8
+ const {data: models} = await this.models.Post.findPage({
9
+ filter: `(${filter})+type:post`,
10
+ transacting: transaction,
11
+ limit: 'all',
12
+ status: 'all',
13
+ withRelated: ['tags']
36
14
  });
37
15
 
38
- response.posts = response.posts
39
- .map(this.serializeDates.bind(this));
16
+ const json = models.map(m => m.toJSON());
40
17
 
41
- return response.posts;
42
- }
43
-
44
- async getBulk(ids) {
45
- const response = await this.browsePostsAPI({
46
- options: {
47
- filter: `id:[${ids.join(',')}]+type:post`
48
- }
18
+ return json.map((postJson) => {
19
+ return {
20
+ id: postJson.id,
21
+ featured: postJson.featured,
22
+ published_at: this.moment(postJson.published_at).toISOString(true),
23
+ tags: postJson.tags.map(tag => tag.slug)
24
+ };
49
25
  });
50
-
51
- response.posts = response.posts
52
- .map(this.serializeDates.bind(this));
53
-
54
- return response.posts;
55
26
  }
56
27
  }
57
28
 
@@ -60,12 +31,6 @@ module.exports = PostsRepository;
60
31
  module.exports.getInstance = () => {
61
32
  const moment = require('moment-timezone');
62
33
  const models = require('../../models');
63
- const browsePostsAPI = async (options) => {
64
- const rawPosts = await require('../../api/').endpoints.posts.browse.query(options);
65
- await require('../../api/').endpoints.serializers.output.posts.all(rawPosts, {}, options);
66
-
67
- return options.response;
68
- };
69
34
 
70
- return new PostsRepository({models, browsePostsAPI, moment});
35
+ return new PostsRepository({models, moment});
71
36
  };
@@ -19,8 +19,10 @@ class CollectionsServiceWrapper {
19
19
  postsRepository: postsRepository,
20
20
  DomainEvents: DomainEvents,
21
21
  slugService: {
22
- async generate(input) {
23
- return models.Collection.generateSlug(models.Collection, input, {});
22
+ async generate(input, options) {
23
+ return models.Collection.generateSlug(models.Collection, input, {
24
+ transacting: options.transaction
25
+ });
24
26
  }
25
27
  }
26
28
  });
@@ -33,28 +35,6 @@ class CollectionsServiceWrapper {
33
35
  return;
34
36
  }
35
37
 
36
- const existingBuiltins = await this.api.getAll({filter: 'slug:featured'});
37
-
38
- if (!existingBuiltins.data.length) {
39
- await this.api.createCollection({
40
- title: 'Index',
41
- slug: 'index',
42
- description: 'Collection with all posts',
43
- type: 'automatic',
44
- deletable: false,
45
- filter: 'status:published'
46
- });
47
-
48
- await this.api.createCollection({
49
- title: 'Featured Posts',
50
- slug: 'featured',
51
- description: 'Collection of featured posts',
52
- type: 'automatic',
53
- deletable: false,
54
- filter: 'featured:true'
55
- });
56
- }
57
-
58
38
  this.api.subscribeToEvents();
59
39
  require('./intercept-events')();
60
40
  }
@@ -25,7 +25,6 @@ module.exports = function apiRoutes() {
25
25
  router.post('/collections', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.add));
26
26
  router.put('/collections/:id', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.edit));
27
27
  router.del('/collections/:id', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.destroy));
28
- router.get('/collections/:id/posts', mw.authAdminApi, labs.enabledMiddleware('collections'), http(api.collections.browsePosts));
29
28
 
30
29
  // ## Configuration
31
30
  router.get('/config', mw.authAdminApi, http(api.config.read));