ghost 5.2.2 → 5.3.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 (259) hide show
  1. package/content/themes/casper/assets/built/screen.css +1 -1
  2. package/content/themes/casper/assets/built/screen.css.map +1 -1
  3. package/content/themes/casper/assets/css/screen.css +31 -1
  4. package/content/themes/casper/default.hbs +1 -0
  5. package/content/themes/casper/package.json +1 -1
  6. package/content/themes/casper/partials/icons/search.hbs +1 -0
  7. package/core/boot.js +3 -1
  8. package/core/bridge.js +7 -0
  9. package/core/built/assets/codemirror/{codemirror-d25c379b87ec8b33d54ac7149bc0b6ae.js → codemirror-30201ab2cff61db847193fc9f5ed922d.js} +1 -1
  10. package/core/built/assets/ghost-dark-9e5d1f0dfae41232e5e34e4d0df53ae0.css +1 -0
  11. package/core/built/assets/ghost.min-e7cfbd1800f8e99b9158f74f1e39cd76.css +1 -0
  12. package/core/built/assets/{ghost.min-971a0ff706bbf9e24f0b0a5770c8fc41.js → ghost.min-f4bba3a2a5ef256b82641345505d4f0f.js} +69 -66
  13. package/core/built/assets/icons/activity-placeholder.svg +3 -0
  14. package/core/built/assets/simplemde/{simplemde-3ffc0ec9e9fecf29b9a499db678c9e65.js → simplemde-fb4527da6e489b34dc24b28ea870b286.js} +1 -1
  15. package/core/built/assets/{vendor.min-ea369e6487643585f35409d474b06789.js → vendor.min-4076498ccd6c8412365f43b156084ed8.js} +119 -117
  16. package/core/built/assets/{vendor.min-ba66b98f7c24fa40e061c7ffc94f4e23.css → vendor.min-4a6661c574707ceca220aa2e76558995.css} +2 -1
  17. package/core/frontend/helpers/comment_count.js +25 -0
  18. package/core/frontend/helpers/comments.js +67 -0
  19. package/core/frontend/helpers/date.js +3 -1
  20. package/core/frontend/helpers/ghost_head.js +13 -0
  21. package/core/frontend/services/admin-auth-assets/index.js +4 -0
  22. package/core/frontend/services/admin-auth-assets/service.js +93 -0
  23. package/core/frontend/services/comment-counts-assets/index.js +4 -0
  24. package/core/frontend/services/comment-counts-assets/service.js +59 -0
  25. package/core/frontend/services/theme-engine/middleware/update-global-template-options.js +5 -1
  26. package/core/frontend/src/admin-auth/index.html +5 -0
  27. package/core/frontend/src/admin-auth/message-handler.js +75 -0
  28. package/core/frontend/src/comment-counts/js/comment-counts.js +71 -0
  29. package/core/frontend/web/site.js +3 -0
  30. package/core/server/api/{canary → endpoints}/actions.js +0 -0
  31. package/core/server/api/{canary → endpoints}/authentication.js +0 -0
  32. package/core/server/api/{canary → endpoints}/authors-public.js +0 -0
  33. package/core/server/api/endpoints/comments-comments.js +247 -0
  34. package/core/server/api/endpoints/comments.js +25 -0
  35. package/core/server/api/{canary → endpoints}/config.js +0 -0
  36. package/core/server/api/{canary → endpoints}/custom-theme-settings.js +0 -0
  37. package/core/server/api/{canary → endpoints}/db.js +0 -0
  38. package/core/server/api/{canary → endpoints}/email-post.js +0 -0
  39. package/core/server/api/{canary → endpoints}/email-previews.js +0 -0
  40. package/core/server/api/{canary → endpoints}/emails.js +0 -0
  41. package/core/server/api/endpoints/explore.js +12 -0
  42. package/core/server/api/{canary → endpoints}/files.js +0 -0
  43. package/core/server/api/{canary → endpoints}/identities.js +0 -0
  44. package/core/server/api/{canary → endpoints}/images.js +0 -0
  45. package/core/server/api/{canary → endpoints}/index.js +16 -0
  46. package/core/server/api/{canary → endpoints}/integrations.js +0 -0
  47. package/core/server/api/{canary → endpoints}/invites.js +0 -0
  48. package/core/server/api/{canary → endpoints}/labels.js +0 -0
  49. package/core/server/api/{canary → endpoints}/mail.js +0 -0
  50. package/core/server/api/{canary → endpoints}/media.js +0 -0
  51. package/core/server/api/{canary → endpoints}/member-signin-urls.js +0 -0
  52. package/core/server/api/{canary → endpoints}/members-stripe-connect.js +0 -0
  53. package/core/server/api/{canary → endpoints}/members.js +0 -0
  54. package/core/server/api/{canary → endpoints}/newsletters-public.js +0 -0
  55. package/core/server/api/{canary → endpoints}/newsletters.js +0 -0
  56. package/core/server/api/{canary → endpoints}/notifications.js +0 -0
  57. package/core/server/api/{canary → endpoints}/oembed.js +0 -0
  58. package/core/server/api/{canary → endpoints}/offers-public.js +0 -0
  59. package/core/server/api/{canary → endpoints}/offers.js +0 -0
  60. package/core/server/api/{canary → endpoints}/pages-public.js +0 -0
  61. package/core/server/api/{canary → endpoints}/pages.js +0 -0
  62. package/core/server/api/{canary → endpoints}/posts-public.js +0 -0
  63. package/core/server/api/{canary → endpoints}/posts.js +0 -0
  64. package/core/server/api/{canary → endpoints}/previews.js +0 -0
  65. package/core/server/api/{canary → endpoints}/redirects.js +0 -0
  66. package/core/server/api/{canary → endpoints}/roles.js +0 -0
  67. package/core/server/api/{canary → endpoints}/schedules.js +0 -0
  68. package/core/server/api/{canary → endpoints}/session.js +0 -0
  69. package/core/server/api/{canary → endpoints}/settings-public.js +0 -0
  70. package/core/server/api/{canary → endpoints}/settings.js +0 -0
  71. package/core/server/api/{canary → endpoints}/site.js +0 -0
  72. package/core/server/api/{canary → endpoints}/slack.js +0 -0
  73. package/core/server/api/{canary → endpoints}/slugs.js +0 -0
  74. package/core/server/api/{canary → endpoints}/snippets.js +0 -0
  75. package/core/server/api/{canary → endpoints}/stats.js +0 -0
  76. package/core/server/api/{canary → endpoints}/tags-public.js +0 -0
  77. package/core/server/api/{canary → endpoints}/tags.js +0 -0
  78. package/core/server/api/{canary → endpoints}/themes.js +0 -0
  79. package/core/server/api/{canary → endpoints}/tiers-public.js +0 -0
  80. package/core/server/api/{canary → endpoints}/tiers.js +0 -0
  81. package/core/server/api/{canary → endpoints}/users.js +0 -0
  82. package/core/server/api/{canary → endpoints}/utils/index.js +0 -0
  83. package/core/server/api/{canary → endpoints}/utils/permissions.js +3 -3
  84. package/core/server/api/{canary → endpoints}/utils/serializers/index.js +0 -0
  85. package/core/server/api/{canary → endpoints}/utils/serializers/input/authors.js +1 -1
  86. package/core/server/api/{canary → endpoints}/utils/serializers/input/db.js +1 -1
  87. package/core/server/api/{canary → endpoints}/utils/serializers/input/index.js +0 -0
  88. package/core/server/api/{canary → endpoints}/utils/serializers/input/integrations.js +1 -1
  89. package/core/server/api/{canary → endpoints}/utils/serializers/input/media.js +0 -0
  90. package/core/server/api/{canary → endpoints}/utils/serializers/input/members.js +1 -1
  91. package/core/server/api/{canary → endpoints}/utils/serializers/input/pages.js +1 -1
  92. package/core/server/api/{canary → endpoints}/utils/serializers/input/posts.js +1 -1
  93. package/core/server/api/{canary → endpoints}/utils/serializers/input/settings.js +2 -1
  94. package/core/server/api/{canary → endpoints}/utils/serializers/input/tags.js +1 -1
  95. package/core/server/api/{canary → endpoints}/utils/serializers/input/tiers.js +0 -0
  96. package/core/server/api/{canary → endpoints}/utils/serializers/input/users.js +1 -1
  97. package/core/server/api/{canary → endpoints}/utils/serializers/input/utils/slug-filter-order.js +0 -0
  98. package/core/server/api/{canary → endpoints}/utils/serializers/input/utils/url.js +0 -0
  99. package/core/server/api/{canary → endpoints}/utils/serializers/input/webhooks.js +1 -1
  100. package/core/server/api/{canary → endpoints}/utils/serializers/output/all.js +1 -1
  101. package/core/server/api/{canary → endpoints}/utils/serializers/output/authentication.js +1 -1
  102. package/core/server/api/endpoints/utils/serializers/output/comments.js +5 -0
  103. package/core/server/api/{canary → endpoints}/utils/serializers/output/config.js +1 -1
  104. package/core/server/api/{canary → endpoints}/utils/serializers/output/custom-theme-settings.js +0 -0
  105. package/core/server/api/{canary → endpoints}/utils/serializers/output/db.js +1 -1
  106. package/core/server/api/{canary → endpoints}/utils/serializers/output/default.js +1 -1
  107. package/core/server/api/{canary → endpoints}/utils/serializers/output/email-posts.js +0 -0
  108. package/core/server/api/endpoints/utils/serializers/output/explore.js +11 -0
  109. package/core/server/api/{canary → endpoints}/utils/serializers/output/files.js +0 -0
  110. package/core/server/api/{canary → endpoints}/utils/serializers/output/images.js +1 -1
  111. package/core/server/api/{canary → endpoints}/utils/serializers/output/index.js +8 -0
  112. package/core/server/api/{canary → endpoints}/utils/serializers/output/mail.js +1 -1
  113. package/core/server/api/{canary → endpoints}/utils/serializers/output/mappers/actions.js +0 -0
  114. package/core/server/api/{canary → endpoints}/utils/serializers/output/mappers/authors.js +0 -0
  115. package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +50 -0
  116. package/core/server/api/{canary → endpoints}/utils/serializers/output/mappers/emails.js +0 -0
  117. package/core/server/api/{canary → endpoints}/utils/serializers/output/mappers/images.js +0 -0
  118. package/core/server/api/{canary → endpoints}/utils/serializers/output/mappers/index.js +1 -0
  119. package/core/server/api/{canary → endpoints}/utils/serializers/output/mappers/integrations.js +0 -0
  120. package/core/server/api/{canary → endpoints}/utils/serializers/output/mappers/newsletters.js +0 -0
  121. package/core/server/api/{canary → endpoints}/utils/serializers/output/mappers/pages.js +0 -0
  122. package/core/server/api/{canary → endpoints}/utils/serializers/output/mappers/posts.js +0 -0
  123. package/core/server/api/{canary → endpoints}/utils/serializers/output/mappers/settings.js +0 -0
  124. package/core/server/api/{canary → endpoints}/utils/serializers/output/mappers/snippets.js +0 -0
  125. package/core/server/api/{canary → endpoints}/utils/serializers/output/mappers/tags.js +0 -0
  126. package/core/server/api/{canary → endpoints}/utils/serializers/output/mappers/users.js +0 -0
  127. package/core/server/api/{canary → endpoints}/utils/serializers/output/media.js +0 -0
  128. package/core/server/api/{canary → endpoints}/utils/serializers/output/members-stripe-connect.js +0 -0
  129. package/core/server/api/{canary → endpoints}/utils/serializers/output/members.js +1 -1
  130. package/core/server/api/{canary → endpoints}/utils/serializers/output/notifications.js +1 -1
  131. package/core/server/api/{canary → endpoints}/utils/serializers/output/oembed.js +1 -1
  132. package/core/server/api/{canary → endpoints}/utils/serializers/output/offers.js +1 -1
  133. package/core/server/api/{canary → endpoints}/utils/serializers/output/pages.js +1 -1
  134. package/core/server/api/{canary → endpoints}/utils/serializers/output/posts.js +1 -1
  135. package/core/server/api/{canary → endpoints}/utils/serializers/output/previews.js +0 -0
  136. package/core/server/api/{canary → endpoints}/utils/serializers/output/redirects.js +0 -0
  137. package/core/server/api/{canary → endpoints}/utils/serializers/output/roles.js +1 -1
  138. package/core/server/api/{canary → endpoints}/utils/serializers/output/schedules.js +0 -0
  139. package/core/server/api/{canary → endpoints}/utils/serializers/output/session.js +1 -1
  140. package/core/server/api/{canary → endpoints}/utils/serializers/output/settings.js +0 -0
  141. package/core/server/api/{canary → endpoints}/utils/serializers/output/site.js +1 -1
  142. package/core/server/api/{canary → endpoints}/utils/serializers/output/slack.js +1 -1
  143. package/core/server/api/{canary → endpoints}/utils/serializers/output/slugs.js +1 -1
  144. package/core/server/api/{canary → endpoints}/utils/serializers/output/themes.js +1 -1
  145. package/core/server/api/{canary → endpoints}/utils/serializers/output/tiers.js +1 -1
  146. package/core/server/api/{canary → endpoints}/utils/serializers/output/users.js +1 -1
  147. package/core/server/api/{canary → endpoints}/utils/serializers/output/utils/clean.js +2 -1
  148. package/core/server/api/{canary → endpoints}/utils/serializers/output/utils/date.js +0 -0
  149. package/core/server/api/endpoints/utils/serializers/output/utils/extra-attrs.js +67 -0
  150. package/core/server/api/{canary → endpoints}/utils/serializers/output/utils/post-gating.js +0 -0
  151. package/core/server/api/{canary → endpoints}/utils/serializers/output/utils/url.js +1 -1
  152. package/core/server/api/{canary → endpoints}/utils/validators/index.js +0 -0
  153. package/core/server/api/{canary → endpoints}/utils/validators/input/files.js +0 -0
  154. package/core/server/api/{canary → endpoints}/utils/validators/input/images.js +0 -0
  155. package/core/server/api/{canary → endpoints}/utils/validators/input/index.js +0 -0
  156. package/core/server/api/{canary → endpoints}/utils/validators/input/invitations.js +1 -1
  157. package/core/server/api/{canary → endpoints}/utils/validators/input/invites.js +0 -0
  158. package/core/server/api/{canary → endpoints}/utils/validators/input/labels.js +0 -0
  159. package/core/server/api/{canary → endpoints}/utils/validators/input/media.js +0 -0
  160. package/core/server/api/{canary → endpoints}/utils/validators/input/members.js +0 -0
  161. package/core/server/api/{canary → endpoints}/utils/validators/input/oembed.js +0 -0
  162. package/core/server/api/{canary → endpoints}/utils/validators/input/pages.js +0 -0
  163. package/core/server/api/{canary → endpoints}/utils/validators/input/password_reset.js +1 -1
  164. package/core/server/api/{canary → endpoints}/utils/validators/input/posts.js +0 -0
  165. package/core/server/api/{canary → endpoints}/utils/validators/input/settings.js +0 -0
  166. package/core/server/api/{canary → endpoints}/utils/validators/input/setup.js +1 -1
  167. package/core/server/api/{canary → endpoints}/utils/validators/input/snippets.js +0 -0
  168. package/core/server/api/{canary → endpoints}/utils/validators/input/tags.js +0 -0
  169. package/core/server/api/{canary → endpoints}/utils/validators/input/tiers.js +0 -0
  170. package/core/server/api/{canary → endpoints}/utils/validators/input/users.js +1 -1
  171. package/core/server/api/{canary → endpoints}/utils/validators/input/webhooks.js +0 -0
  172. package/core/server/api/{canary → endpoints}/utils/validators/output/index.js +0 -0
  173. package/core/server/api/{canary → endpoints}/utils/validators/utils/json-schema.js +0 -0
  174. package/core/server/api/{canary → endpoints}/webhooks.js +0 -0
  175. package/core/server/api/index.js +1 -1
  176. package/core/server/data/exporter/table-lists.js +4 -2
  177. package/core/server/data/migrations/versions/5.3/2022-07-04-13-49-add-comments-table.js +13 -0
  178. package/core/server/data/migrations/versions/5.3/2022-07-05-09-36-add-comments-likes-table.js +9 -0
  179. package/core/server/data/migrations/versions/5.3/2022-07-05-09-47-add-comments-reports-table.js +10 -0
  180. package/core/server/data/migrations/versions/5.3/2022-07-05-10-00-add-comment-related-fields-to-members.js +21 -0
  181. package/core/server/data/migrations/versions/5.3/2022-07-05-12-55-add-comments-crud-permissions.js +68 -0
  182. package/core/server/data/migrations/versions/5.3/2022-07-05-15-35-add-comment-notifications-field-to-users-table.js +7 -0
  183. package/core/server/data/migrations/versions/5.3/2022-07-06-07-26-add-comments-enabled-setting.js +8 -0
  184. package/core/server/data/migrations/versions/5.3/2022-07-06-07-58-add-ghost-explore-integration-role.js +31 -0
  185. package/core/server/data/migrations/versions/5.3/2022-07-06-09-13-add-ghost-explore-integration-role-permissions.js +11 -0
  186. package/core/server/data/migrations/versions/5.3/2022-07-06-09-17-add-ghost-explore-integration.js +38 -0
  187. package/core/server/data/migrations/versions/5.3/2022-07-06-09-26-add-ghost-explore-integration-api-key.js +73 -0
  188. package/core/server/data/schema/default-settings/default-settings.json +14 -0
  189. package/core/server/data/schema/fixtures/fixtures.json +65 -2
  190. package/core/server/data/schema/schema.js +31 -1
  191. package/core/server/models/base/plugins/crud.js +15 -0
  192. package/core/server/models/comment-like.js +34 -0
  193. package/core/server/models/comment.js +163 -0
  194. package/core/server/models/member.js +2 -1
  195. package/core/server/models/user.js +2 -1
  196. package/core/server/services/auth/session/express-session.js +1 -1
  197. package/core/server/services/comments/email-templates/new-comment-reply.hbs +199 -0
  198. package/core/server/services/comments/email-templates/new-comment-reply.txt.js +14 -0
  199. package/core/server/services/comments/email-templates/new-comment.hbs +199 -0
  200. package/core/server/services/comments/email-templates/new-comment.txt.js +14 -0
  201. package/core/server/services/comments/emails.js +164 -0
  202. package/core/server/services/comments/index.js +26 -0
  203. package/core/server/services/comments/service.js +24 -0
  204. package/core/server/services/explore/index.js +18 -0
  205. package/core/server/services/explore/service.js +55 -0
  206. package/core/server/services/members/middleware.js +4 -4
  207. package/core/server/services/members/utils.js +2 -1
  208. package/core/server/services/permissions/can-this.js +18 -5
  209. package/core/server/services/permissions/parse-context.js +5 -0
  210. package/core/server/services/permissions/providers.js +17 -1
  211. package/core/server/services/posts/posts-service.js +14 -0
  212. package/core/server/services/users.js +84 -1
  213. package/core/server/web/admin/app.js +4 -0
  214. package/core/server/web/admin/views/default-prod.html +5 -5
  215. package/core/server/web/admin/views/default.html +5 -5
  216. package/core/server/web/api/app.js +2 -2
  217. package/core/server/web/api/{canary → endpoints}/admin/app.js +4 -4
  218. package/core/server/web/api/{canary → endpoints}/admin/middleware.js +1 -0
  219. package/core/server/web/api/{canary → endpoints}/admin/routes.js +6 -1
  220. package/core/server/web/api/{canary → endpoints}/content/app.js +4 -4
  221. package/core/server/web/api/{canary → endpoints}/content/middleware.js +0 -0
  222. package/core/server/web/api/{canary → endpoints}/content/routes.js +1 -1
  223. package/core/server/web/api/testmode/routes.js +1 -1
  224. package/core/server/web/comments/index.js +1 -0
  225. package/core/server/web/comments/routes.js +28 -0
  226. package/core/server/web/members/app.js +5 -0
  227. package/core/server/web/parent/frontend.js +1 -0
  228. package/core/shared/config/defaults.json +16 -6
  229. package/core/shared/config/env/config.testing.json +4 -0
  230. package/core/shared/config/overrides.json +1 -0
  231. package/core/shared/labs.js +2 -1
  232. package/core/shared/settings-cache/public.js +2 -1
  233. package/package.json +20 -20
  234. package/yarn.lock +425 -275
  235. package/Gruntfile.js +0 -384
  236. package/core/built/assets/ghost-dark-923c90399aa560625a983a6ae9abb38c.css +0 -1
  237. package/core/built/assets/ghost.min-c74f50609022cebf13ad28810def68b6.css +0 -1
  238. package/core/built/assets/img/contributors/AileenCGN-bf8b9ffbb34c0fd93beb8136af07771b.jpeg +0 -0
  239. package/core/built/assets/img/contributors/ErisDS-c958ccb9e3597320dee745a42f478569.jpeg +0 -0
  240. package/core/built/assets/img/contributors/GeorginaLusby-6cea5defddee3c4ea7320e580521e832.jpeg +0 -0
  241. package/core/built/assets/img/contributors/JohnONolan-47041b80c35c6341b9b929b03139aecc.jpeg +0 -0
  242. package/core/built/assets/img/contributors/acburdine-d9777fe2601dc215afb6723315829c89.jpeg +0 -0
  243. package/core/built/assets/img/contributors/bnookala-5896fcdd2f477495323e420efe890657.jpeg +0 -0
  244. package/core/built/assets/img/contributors/cobbspur-07ded67009757d12517621fc856eba62.jpeg +0 -0
  245. package/core/built/assets/img/contributors/dbalders-452347a406c2ca23657daea9100878f3.jpeg +0 -0
  246. package/core/built/assets/img/contributors/disordinary-b9997e5debb59b7aadc79ba90955b662.jpeg +0 -0
  247. package/core/built/assets/img/contributors/felixrieseberg-ecf29e8eadc58fab999c507049f898cf.jpeg +0 -0
  248. package/core/built/assets/img/contributors/frantzypants-637b03f85dff89700a661fde79daea5c.jpeg +0 -0
  249. package/core/built/assets/img/contributors/halfdan-00d6783e5fba2900ee1380939297d8ee.jpeg +0 -0
  250. package/core/built/assets/img/contributors/jaswilli-8cc9a8d2539ca03239d113dfb25ff5c2.jpeg +0 -0
  251. package/core/built/assets/img/contributors/kevinansfield-925606c55bc2f3f2f05c0fa58b953ad1.jpeg +0 -0
  252. package/core/built/assets/img/contributors/kevinkucharczyk-3c7dfe2a103a83737b9d5ee8e19d67f8.jpeg +0 -0
  253. package/core/built/assets/img/contributors/kirrg001-79823418f2ca21e81719653f0286f95b.jpeg +0 -0
  254. package/core/built/assets/img/contributors/mixonic-1ff87736dd02cfa080ae109b45131aa6.png +0 -0
  255. package/core/built/assets/img/contributors/rwjblue-5c7cc009cda45baca2d45f0d1ed19e48.jpeg +0 -0
  256. package/core/built/assets/img/contributors/sebgie-0fb02df00ee7834dbcc8beba84aec81e.png +0 -0
  257. package/core/built/assets/img/contributors/tgriesser-d871cbf74a871c0fb6d855e76a893f7e.png +0 -0
  258. package/core/server/api/canary/utils/serializers/output/utils/extra-attrs.js +0 -33
  259. package/urls.json +0 -597
@@ -0,0 +1,18 @@
1
+ const ExploreService = require('./service');
2
+
3
+ const MembersService = require('../members');
4
+ const PostsService = require('../posts/posts-service')();
5
+ const PublicConfigService = require('../public-config');
6
+ const StatsService = require('../stats');
7
+ const StripeService = require('../stripe');
8
+
9
+ const models = require('../../models');
10
+
11
+ module.exports = new ExploreService({
12
+ MembersService,
13
+ PostsService,
14
+ PublicConfigService,
15
+ StatsService,
16
+ StripeService,
17
+ UserModel: models.User
18
+ });
@@ -0,0 +1,55 @@
1
+ const ghostVersion = require('@tryghost/version');
2
+
3
+ module.exports = class ExploreService {
4
+ /**
5
+ * @param {Object} options
6
+ * @param {Object} options.MembersService
7
+ * @param {Object} options.PostsService
8
+ * @param {Object} options.PublicConfigService
9
+ * @param {Object} options.StatsService
10
+ * @param {Object} options.StripeService
11
+ * @param {Object} options.UserModel
12
+ */
13
+ constructor({MembersService, PostsService, PublicConfigService, StatsService, StripeService, UserModel}) {
14
+ this.MembersService = MembersService;
15
+ this.PostsService = PostsService;
16
+ this.PublicConfigService = PublicConfigService;
17
+ this.StatsService = StatsService;
18
+ this.StripeService = StripeService;
19
+ this.UserModel = UserModel;
20
+ }
21
+
22
+ /**
23
+ * Build and return the response object containing the data for the Ghost Explore endpoint
24
+ */
25
+ async fetchData() {
26
+ const totalMembers = await this.MembersService.stats.getTotalMembers();
27
+ const mrrStats = await this.StatsService.getMRRHistory();
28
+
29
+ const {description, icon, title, url} = this.PublicConfigService.site;
30
+
31
+ const exploreProperties = {
32
+ version: ghostVersion.full,
33
+ totalMembers,
34
+ mrrStats,
35
+ site: {
36
+ description,
37
+ icon,
38
+ title,
39
+ url
40
+ },
41
+ stripe: {
42
+ configured: this.StripeService.api.configured,
43
+ livemode: (this.StripeService.api.configured && this.StripeService.api.mode === 'live')
44
+ }
45
+ };
46
+
47
+ const mostRecentlyPublishedPost = await this.PostsService.getMostRecentlyPublishedPost();
48
+ exploreProperties.mostRecentlyPublishedAt = mostRecentlyPublishedPost?.get('published_at') || null;
49
+
50
+ const owner = await this.UserModel.findOne({role: 'Owner', status: 'all'});
51
+ exploreProperties.ownerEmail = owner?.get('email') || null;
52
+
53
+ return exploreProperties;
54
+ }
55
+ };
@@ -75,7 +75,7 @@ const getMemberNewsletters = async function (req, res) {
75
75
  return res.end('Email address not found.');
76
76
  }
77
77
 
78
- const data = _.pick(memberData.toJSON(), 'uuid', 'email', 'name', 'newsletters', 'status');
78
+ const data = _.pick(memberData.toJSON(), 'uuid', 'email', 'name', 'newsletters', 'enable_comment_notifications', 'status');
79
79
  return res.json(data);
80
80
  } catch (err) {
81
81
  res.writeHead(400);
@@ -91,7 +91,7 @@ const updateMemberNewsletters = async function (req, res) {
91
91
  return res.end('Invalid member uuid');
92
92
  }
93
93
 
94
- const data = _.pick(req.body, 'newsletters');
94
+ const data = _.pick(req.body, 'newsletters', 'enable_comment_notifications');
95
95
  const memberData = await membersService.api.members.get({
96
96
  uuid: memberUuid
97
97
  });
@@ -106,7 +106,7 @@ const updateMemberNewsletters = async function (req, res) {
106
106
  };
107
107
 
108
108
  const updatedMember = await membersService.api.members.update(data, options);
109
- const updatedMemberData = _.pick(updatedMember.toJSON(), ['uuid', 'email', 'name', 'newsletters', 'status']);
109
+ const updatedMemberData = _.pick(updatedMember.toJSON(), ['uuid', 'email', 'name', 'newsletters', 'enable_comment_notifications', 'status']);
110
110
  res.json(updatedMemberData);
111
111
  } catch (err) {
112
112
  res.writeHead(400);
@@ -116,7 +116,7 @@ const updateMemberNewsletters = async function (req, res) {
116
116
 
117
117
  const updateMemberData = async function (req, res) {
118
118
  try {
119
- const data = _.pick(req.body, 'name', 'subscribed', 'newsletters');
119
+ const data = _.pick(req.body, 'name', 'subscribed', 'newsletters', 'enable_comment_notifications');
120
120
  const member = await membersService.ssr.getMemberDataFromSession(req, res);
121
121
  if (member) {
122
122
  const options = {
@@ -16,7 +16,8 @@ module.exports.formattedMemberResponse = function formattedMemberResponse(member
16
16
  avatar_image: member.avatar_image,
17
17
  subscribed: !!member.subscribed,
18
18
  subscriptions: member.subscriptions || [],
19
- paid: member.status !== 'free'
19
+ paid: member.status !== 'free',
20
+ enable_comment_notifications: member.enable_comment_notifications
20
21
  };
21
22
  if (member.newsletters) {
22
23
  data.newsletters = formatNewsletterResponse(member.newsletters);
@@ -27,7 +27,8 @@ CanThisResult.prototype.buildObjectTypeHandlers = function (objTypes, actType, c
27
27
  permission: models.Permission,
28
28
  setting: models.Settings,
29
29
  invite: models.Invite,
30
- integration: models.Integration
30
+ integration: models.Integration,
31
+ comment: models.Comment
31
32
  };
32
33
 
33
34
  // Iterate through the object types, i.e. ['post', 'tag', 'user']
@@ -57,10 +58,12 @@ CanThisResult.prototype.buildObjectTypeHandlers = function (objTypes, actType, c
57
58
  return permissionLoad.then(function (loadedPermissions) {
58
59
  // Iterate through the user permissions looking for an affirmation
59
60
  const userPermissions = loadedPermissions.user ? loadedPermissions.user.permissions : null;
60
-
61
61
  const apiKeyPermissions = loadedPermissions.apiKey ? loadedPermissions.apiKey.permissions : null;
62
+ const memberPermissions = loadedPermissions.member ? loadedPermissions.member.permissions : null;
63
+
62
64
  let hasUserPermission;
63
65
  let hasApiKeyPermission;
66
+ let hasMemberPermission = false;
64
67
 
65
68
  const checkPermission = function (perm) {
66
69
  let permObjId;
@@ -91,6 +94,10 @@ CanThisResult.prototype.buildObjectTypeHandlers = function (objTypes, actType, c
91
94
  hasUserPermission = _.some(userPermissions, checkPermission);
92
95
  }
93
96
 
97
+ if (loadedPermissions.member) {
98
+ hasMemberPermission = _.some(memberPermissions, checkPermission);
99
+ }
100
+
94
101
  // Check api key permissions if they were passed
95
102
  hasApiKeyPermission = true;
96
103
  if (!_.isNull(apiKeyPermissions)) {
@@ -102,7 +109,7 @@ CanThisResult.prototype.buildObjectTypeHandlers = function (objTypes, actType, c
102
109
  // Offer a chance for the TargetModel to override the results
103
110
  if (TargetModel && _.isFunction(TargetModel.permissible)) {
104
111
  return TargetModel.permissible(
105
- modelId, actType, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasApiKeyPermission
112
+ modelId, actType, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasApiKeyPermission, hasMemberPermission
106
113
  );
107
114
  }
108
115
 
@@ -122,6 +129,7 @@ CanThisResult.prototype.beginCheck = function (context) {
122
129
  const self = this;
123
130
  let userPermissionLoad;
124
131
  let apiKeyPermissionLoad;
132
+ let memberPermissionLoad;
125
133
  let permissionsLoad;
126
134
 
127
135
  // Get context.user, context.api_key and context.app
@@ -147,11 +155,16 @@ CanThisResult.prototype.beginCheck = function (context) {
147
155
  apiKeyPermissionLoad = Promise.resolve(null);
148
156
  }
149
157
 
158
+ if (context.member) {
159
+ memberPermissionLoad = providers.member(context.member.id);
160
+ }
161
+
150
162
  // Wait for both user and app permissions to load
151
- permissionsLoad = Promise.all([userPermissionLoad, apiKeyPermissionLoad]).then(function (result) {
163
+ permissionsLoad = Promise.all([userPermissionLoad, apiKeyPermissionLoad, memberPermissionLoad]).then(function (result) {
152
164
  return {
153
165
  user: result[0],
154
- apiKey: result[1]
166
+ apiKey: result[1],
167
+ member: result[2]
155
168
  };
156
169
  });
157
170
 
@@ -12,6 +12,7 @@ module.exports = function parseContext(context) {
12
12
  user: null,
13
13
  api_key: null,
14
14
  integration: null,
15
+ member: null,
15
16
  public: true
16
17
  };
17
18
 
@@ -37,5 +38,9 @@ module.exports = function parseContext(context) {
37
38
  parsed.public = (context.api_key.type === 'content');
38
39
  }
39
40
 
41
+ if (context && context.member) {
42
+ parsed.member = context.member;
43
+ }
44
+
40
45
  return parsed;
41
46
  };
@@ -6,7 +6,8 @@ const tpl = require('@tryghost/tpl');
6
6
 
7
7
  const messages = {
8
8
  userNotFound: 'User not found',
9
- apiKeyNotFound: 'API Key not found'
9
+ apiKeyNotFound: 'API Key not found',
10
+ memberNotFound: 'Member not found'
10
11
  };
11
12
 
12
13
  module.exports = {
@@ -72,5 +73,20 @@ module.exports = {
72
73
 
73
74
  return {permissions, roles};
74
75
  });
76
+ },
77
+
78
+ async member(id) {
79
+ const foundMember = await models.Member.findOne({id});
80
+ if (!foundMember) {
81
+ throw new errors.NotFoundError({
82
+ message: tpl(messages.memberNotFound)
83
+ });
84
+ }
85
+
86
+ // @TODO: figure out how we want to associate members with permissions
87
+ // Dirty code to load all comment permissions except moderation for members
88
+ const permissions = await models.Permission.findAll({filter: 'object_type:comment+action_type:-moderate'});
89
+
90
+ return {permissions: permissions.models};
75
91
  }
76
92
  };
@@ -76,6 +76,20 @@ class PostsService {
76
76
  }
77
77
  }
78
78
 
79
+ /**
80
+ * Returns the most recently `published_at` post that was published or sent
81
+ * via email
82
+ */
83
+ async getMostRecentlyPublishedPost() {
84
+ const recentlyPublishedPost = await this.models.Post.findPage({
85
+ status: ['published', 'sent'],
86
+ order: 'published_at DESC',
87
+ limit: 1
88
+ });
89
+
90
+ return recentlyPublishedPost?.data[0];
91
+ }
92
+
79
93
  /**
80
94
  * Calculates if the email should be tried to be sent out
81
95
  * @private
@@ -46,6 +46,8 @@ class Users {
46
46
  this.auth = auth;
47
47
  this.apiMail = apiMail;
48
48
  this.apiSettings = apiSettings;
49
+
50
+ this.assignTagToUserPosts = this.assignTagToUserPosts.bind(this);
49
51
  }
50
52
 
51
53
  async resetAllPasswords(frameOptions) {
@@ -68,6 +70,77 @@ class Users {
68
70
  });
69
71
  }
70
72
 
73
+ async assignTagToUserPosts({id, context, transacting}) {
74
+ // create an internal tag to assign to reassigned posts
75
+ // in following format: `#{author_slug}`
76
+ const author = await this.models.User.findOne({
77
+ id
78
+ }, {
79
+ id,
80
+ context,
81
+ transacting
82
+ });
83
+ let tag = await this.models.Tag.findOne({
84
+ slug: `hash-${author.get('slug')}`
85
+ }, {
86
+ context,
87
+ transacting
88
+ });
89
+
90
+ if (!tag) {
91
+ tag = await this.models.Tag.add({
92
+ slug: `#${author.get('slug')}`
93
+ }, {
94
+ context,
95
+ transacting
96
+ });
97
+ }
98
+
99
+ const userPosts = await this.models.Base.knex('posts_authors')
100
+ .transacting(transacting)
101
+ .where('author_id', id)
102
+ .select('post_id');
103
+ const usersPostIds = userPosts.map(p => p.post_id);
104
+
105
+ // Add a tag to all posts that do not have the author tag yet
106
+ // NOTE: the method is implemented in an iterative way to avoid
107
+ // memory consumption in case the user has thousands of posts
108
+ // assigned to them. Also, didn't have any "bulk" way to add
109
+ // a tag to multiple posts as the "sort_order" needs custom
110
+ // logic to be run for each post.
111
+ // Rewrite this bit if/when we hit a performance bottleneck here
112
+ for (const postId of usersPostIds) {
113
+ const post = await this.models.Post.findOne({
114
+ id: postId
115
+ }, {
116
+ id: postId,
117
+ withRelated: ['tags'],
118
+ context,
119
+ transacting
120
+ });
121
+
122
+ // check if tag already assigned to the post
123
+ const existingTagSlugs = post.relations.tags.models.map(t => t.get('slug'));
124
+
125
+ if (!existingTagSlugs.includes(tag.get('slug'))) {
126
+ await this.models.Post.edit({
127
+ tags: [...post.relations.tags.models, tag]
128
+ }, {
129
+ id: postId,
130
+ context,
131
+ transacting
132
+ });
133
+ }
134
+ }
135
+ }
136
+
137
+ /**
138
+ *
139
+ * @param {Object} frameOptions
140
+ * @param {string} frameOptions.id - user ID to destroy
141
+ * @param {Object} frameOptions.context - frame context to perform the action
142
+ * @returns
143
+ */
71
144
  async destroyUser(frameOptions) {
72
145
  const backupPath = await this.dbBackup.backup();
73
146
  const parsedFileName = path.parse(backupPath);
@@ -76,7 +149,17 @@ class Users {
76
149
  return this.models.Base.transaction(async (t) => {
77
150
  frameOptions.transacting = t;
78
151
 
79
- await this.models.Post.reassignByAuthor(frameOptions);
152
+ await this.assignTagToUserPosts({
153
+ id: frameOptions.id,
154
+ context: frameOptions.context,
155
+ transacting: frameOptions.transacting
156
+ });
157
+
158
+ await this.models.Post.reassignByAuthor({
159
+ id: frameOptions.id,
160
+ context: frameOptions.context,
161
+ transacting: frameOptions.transacting
162
+ });
80
163
 
81
164
  try {
82
165
  await this.models.ApiKey.destroy({
@@ -21,6 +21,10 @@ module.exports = function setupAdminApp() {
21
21
  {maxAge: (configMaxAge || configMaxAge === 0) ? configMaxAge : constants.ONE_YEAR_MS, fallthrough: false}
22
22
  ));
23
23
 
24
+ adminApp.use('/auth-frame', serveStatic(
25
+ config.get('paths').adminAuthAssets
26
+ ));
27
+
24
28
  // Ember CLI's live-reload script
25
29
  if (config.get('env') === 'development') {
26
30
  adminApp.get('/ember-cli-live-reload.js', function emberLiveReload(req, res) {
@@ -8,7 +8,7 @@
8
8
  <title>Ghost Admin</title>
9
9
 
10
10
 
11
- <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.2%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
11
+ <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.3%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
12
12
 
13
13
  <meta name="HandheldFriendly" content="True" />
14
14
  <meta name="MobileOptimized" content="320" />
@@ -37,8 +37,8 @@
37
37
  </style>
38
38
 
39
39
 
40
- <link rel="stylesheet" href="assets/vendor.min-ba66b98f7c24fa40e061c7ffc94f4e23.css">
41
- <link rel="stylesheet" href="assets/ghost.min-c74f50609022cebf13ad28810def68b6.css" title="light">
40
+ <link rel="stylesheet" href="assets/vendor.min-4a6661c574707ceca220aa2e76558995.css">
41
+ <link rel="stylesheet" href="assets/ghost.min-e7cfbd1800f8e99b9158f74f1e39cd76.css" title="light">
42
42
 
43
43
 
44
44
 
@@ -56,8 +56,8 @@
56
56
  <div id="ember-basic-dropdown-wormhole"></div>
57
57
 
58
58
 
59
- <script src="assets/vendor.min-ea369e6487643585f35409d474b06789.js"></script>
60
- <script src="assets/ghost.min-971a0ff706bbf9e24f0b0a5770c8fc41.js"></script>
59
+ <script src="assets/vendor.min-4076498ccd6c8412365f43b156084ed8.js"></script>
60
+ <script src="assets/ghost.min-f4bba3a2a5ef256b82641345505d4f0f.js"></script>
61
61
 
62
62
  </body>
63
63
  </html>
@@ -8,7 +8,7 @@
8
8
  <title>Ghost Admin</title>
9
9
 
10
10
 
11
- <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.2%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
11
+ <meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%2F%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.3%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
12
12
 
13
13
  <meta name="HandheldFriendly" content="True" />
14
14
  <meta name="MobileOptimized" content="320" />
@@ -37,8 +37,8 @@
37
37
  </style>
38
38
 
39
39
 
40
- <link rel="stylesheet" href="assets/vendor.min-ba66b98f7c24fa40e061c7ffc94f4e23.css">
41
- <link rel="stylesheet" href="assets/ghost.min-c74f50609022cebf13ad28810def68b6.css" title="light">
40
+ <link rel="stylesheet" href="assets/vendor.min-4a6661c574707ceca220aa2e76558995.css">
41
+ <link rel="stylesheet" href="assets/ghost.min-e7cfbd1800f8e99b9158f74f1e39cd76.css" title="light">
42
42
 
43
43
 
44
44
 
@@ -56,8 +56,8 @@
56
56
  <div id="ember-basic-dropdown-wormhole"></div>
57
57
 
58
58
 
59
- <script src="assets/vendor.min-ea369e6487643585f35409d474b06789.js"></script>
60
- <script src="assets/ghost.min-971a0ff706bbf9e24f0b0a5770c8fc41.js"></script>
59
+ <script src="assets/vendor.min-4076498ccd6c8412365f43b156084ed8.js"></script>
60
+ <script src="assets/ghost.min-f4bba3a2a5ef256b82641345505d4f0f.js"></script>
61
61
 
62
62
  </body>
63
63
  </html>
@@ -16,8 +16,8 @@ module.exports = function setupApiApp() {
16
16
  apiApp.use(APIVersionCompatibilityService.versionRewrites);
17
17
  apiApp.use(APIVersionCompatibilityService.contentVersion);
18
18
 
19
- apiApp.lazyUse('/content/', require('./canary/content/app'));
20
- apiApp.lazyUse('/admin/', require('./canary/admin/app'));
19
+ apiApp.lazyUse('/content/', require('./endpoints/content/app'));
20
+ apiApp.lazyUse('/admin/', require('./endpoints/admin/app'));
21
21
 
22
22
  // Error handling for requests to non-existent API versions
23
23
  apiApp.use(errorHandler.resourceNotFound);
@@ -1,4 +1,4 @@
1
- const debug = require('@tryghost/debug')('web:canary:admin:app');
1
+ const debug = require('@tryghost/debug')('web:endpoints:admin:app');
2
2
  const boolParser = require('express-query-boolean');
3
3
  const express = require('../../../../../shared/express');
4
4
  const bodyParser = require('body-parser');
@@ -10,8 +10,8 @@ const routes = require('./routes');
10
10
  const APIVersionCompatibilityService = require('../../../../services/api-version-compatibility');
11
11
 
12
12
  module.exports = function setupApiApp() {
13
- debug('Admin API canary setup start');
14
- const apiApp = express('canary admin');
13
+ debug('Admin API setup start');
14
+ const apiApp = express('admin api');
15
15
 
16
16
  // API middleware
17
17
 
@@ -37,7 +37,7 @@ module.exports = function setupApiApp() {
37
37
  apiApp.use(APIVersionCompatibilityService.errorHandler);
38
38
  apiApp.use(errorHandler.handleJSONResponse(sentry));
39
39
 
40
- debug('Admin API canary setup end');
40
+ debug('Admin API setup end');
41
41
 
42
42
  return apiApp;
43
43
  };
@@ -33,6 +33,7 @@ const notImplemented = function (req, res, next) {
33
33
  offers: ['GET', 'PUT', 'POST'],
34
34
  newsletters: ['GET', 'PUT', 'POST'],
35
35
  config: ['GET'],
36
+ explore: ['GET'],
36
37
  schedules: ['PUT'],
37
38
  files: ['POST'],
38
39
  media: ['POST'],
@@ -7,7 +7,7 @@ const mw = require('./middleware');
7
7
  const shared = require('../../../shared');
8
8
 
9
9
  module.exports = function apiRoutes() {
10
- const router = express.Router('canary admin');
10
+ const router = express.Router('admin api');
11
11
 
12
12
  // alias delete with del
13
13
  router.del = router.delete;
@@ -20,6 +20,9 @@ module.exports = function apiRoutes() {
20
20
  // ## Configuration
21
21
  router.get('/config', mw.authAdminApi, http(api.config.read));
22
22
 
23
+ // ## Ghost Explore
24
+ router.get('/explore', mw.authAdminApi, http(api.explore.read));
25
+
23
26
  // ## Posts
24
27
  router.get('/posts', mw.authAdminApi, http(api.posts.browse));
25
28
  router.post('/posts', mw.authAdminApi, http(api.posts.add));
@@ -28,6 +31,8 @@ module.exports = function apiRoutes() {
28
31
  router.put('/posts/:id', mw.authAdminApi, http(api.posts.edit));
29
32
  router.del('/posts/:id', mw.authAdminApi, http(api.posts.destroy));
30
33
 
34
+ router.put('/comments/:id', mw.authAdminApi, http(api.comments.edit));
35
+
31
36
  // ## Pages
32
37
  router.get('/pages', mw.authAdminApi, http(api.pages.browse));
33
38
  router.post('/pages', mw.authAdminApi, http(api.pages.add));
@@ -1,4 +1,4 @@
1
- const debug = require('@tryghost/debug')('web:api:canary:content:app');
1
+ const debug = require('@tryghost/debug')('web:api:endpoints:content:app');
2
2
  const boolParser = require('express-query-boolean');
3
3
  const bodyParser = require('body-parser');
4
4
  const express = require('../../../../../shared/express');
@@ -9,8 +9,8 @@ const errorHandler = require('@tryghost/mw-error-handler');
9
9
  const apiVersionCompatibility = require('../../../../services/api-version-compatibility');
10
10
 
11
11
  module.exports = function setupApiApp() {
12
- debug('Content API canary setup start');
13
- const apiApp = express('canary content');
12
+ debug('Content API setup start');
13
+ const apiApp = express('content api');
14
14
 
15
15
  // API middleware
16
16
 
@@ -31,7 +31,7 @@ module.exports = function setupApiApp() {
31
31
  apiApp.use(apiVersionCompatibility.errorHandler);
32
32
  apiApp.use(errorHandler.handleJSONResponse(sentry));
33
33
 
34
- debug('Content API canary setup end');
34
+ debug('Content API setup end');
35
35
 
36
36
  return apiApp;
37
37
  };
@@ -5,7 +5,7 @@ const http = require('../../../../api').shared.http;
5
5
  const mw = require('./middleware');
6
6
 
7
7
  module.exports = function apiRoutes() {
8
- const router = express.Router('canary content');
8
+ const router = express.Router('content api');
9
9
 
10
10
  router.use(cors());
11
11
 
@@ -5,7 +5,7 @@ const jobsService = require('../../../services/jobs');
5
5
 
6
6
  /** A bunch of helper routes for testing purposes */
7
7
  module.exports = function testRoutes() {
8
- const router = express.Router('canary admin');
8
+ const router = express.Router('testmode');
9
9
 
10
10
  router.get('/500', (req, res) => res.sendStatus(500));
11
11
  router.get('/400', (req, res) => res.sendStatus(400));
@@ -0,0 +1 @@
1
+ module.exports = require('./routes');
@@ -0,0 +1,28 @@
1
+ const express = require('../../../shared/express');
2
+ const api = require('../../api').endpoints;
3
+ const http = require('../../api').shared.http;
4
+
5
+ const bodyParser = require('body-parser');
6
+ const membersService = require('../../../server/services/members');
7
+
8
+ module.exports = function apiRoutes() {
9
+ const router = express.Router('comment api');
10
+
11
+ router.use(bodyParser.json({limit: '50mb'}));
12
+
13
+ // Global handling for member session, ensures a member is logged in to the frontend
14
+ router.use(membersService.middleware.loadMemberSession);
15
+
16
+ router.post('/counts', http(api.commentsComments.counts));
17
+
18
+ router.get('/', http(api.commentsComments.browse));
19
+ router.get('/:id', http(api.commentsComments.read));
20
+ router.post('/', http(api.commentsComments.add));
21
+ router.put('/:id', http(api.commentsComments.edit));
22
+ router.delete('/:id', http(api.commentsComments.destroy));
23
+
24
+ router.post('/:id/like', http(api.commentsComments.like));
25
+ router.delete('/:id/like', http(api.commentsComments.unlike));
26
+
27
+ return router;
28
+ };
@@ -12,6 +12,8 @@ const shared = require('../shared');
12
12
  const labs = require('../../../shared/labs');
13
13
  const errorHandler = require('@tryghost/mw-error-handler');
14
14
 
15
+ const commentRouter = require('../comments');
16
+
15
17
  module.exports = function setupMembersApp() {
16
18
  debug('Members App setup start');
17
19
  const membersApp = express('members');
@@ -54,6 +56,9 @@ module.exports = function setupMembersApp() {
54
56
  membersApp.put('/api/subscriptions/:id', (req, res, next) => membersService.api.middleware.updateSubscription(req, res, next));
55
57
  membersApp.post('/api/events', labs.enabledMiddleware('membersActivity'), middleware.loadMemberSession, (req, res, next) => membersService.api.middleware.createEvents(req, res, next));
56
58
 
59
+ // Comments
60
+ membersApp.use('/api/comments', commentRouter());
61
+
57
62
  // API error handling
58
63
  membersApp.use('/api', errorHandler.resourceNotFound);
59
64
  membersApp.use('/api', errorHandler.handleJSONResponse(sentry));
@@ -18,6 +18,7 @@ module.exports = (routerConfig) => {
18
18
  frontendApp.use(shared.middleware.urlRedirects.frontendSSLRedirect);
19
19
 
20
20
  frontendApp.lazyUse('/members', require('../members'));
21
+ frontendApp.lazyUse('/comments', require('../comments'));
21
22
  frontendApp.use('/', require('../../../frontend/web')(routerConfig));
22
23
 
23
24
  return frontendApp;