ghost 4.41.3 → 4.42.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 (55) hide show
  1. package/core/built/assets/ghost-dark-97613c037232aba4490489431ce170ca.css +1 -0
  2. package/core/built/assets/{ghost.min-1abf114ca26a71e8e1f09054f3592614.js → ghost.min-20096eef632760c3a2906e243adbd24b.js} +400 -323
  3. package/core/built/assets/ghost.min-c08ce1872f0e09edb63eb13c43606d18.css +1 -0
  4. package/core/built/assets/{vendor.min-9094db77ba3190cb10876f8e42e1d90d.js → vendor.min-21f79c68a284acb1b70039f3f63e5507.js} +68 -68
  5. package/core/built/assets/{vendor.min-2c8ad32b7960bb605ebc20097fee5ebd.css → vendor.min-ba66b98f7c24fa40e061c7ffc94f4e23.css} +214 -0
  6. package/core/frontend/helpers/price.js +1 -0
  7. package/core/server/api/canary/email-preview.js +2 -1
  8. package/core/server/api/canary/{email.js → emails.js} +0 -0
  9. package/core/server/api/canary/index.js +9 -1
  10. package/core/server/api/canary/members.js +0 -45
  11. package/core/server/api/canary/newsletters.js +45 -0
  12. package/core/server/api/canary/stats.js +14 -0
  13. package/core/server/api/canary/utils/serializers/output/email-previews.js +7 -0
  14. package/core/server/api/canary/utils/serializers/output/index.js +2 -22
  15. package/core/server/api/canary/utils/serializers/output/mappers/index.js +1 -0
  16. package/core/server/api/canary/utils/serializers/output/mappers/snippets.js +36 -0
  17. package/core/server/api/canary/utils/serializers/output/members.js +0 -2
  18. package/core/server/api/canary/utils/serializers/output/oembed.js +2 -2
  19. package/core/server/api/canary/utils/serializers/output/redirects.js +2 -2
  20. package/core/server/api/canary/utils/serializers/output/schedules.js +2 -2
  21. package/core/server/api/canary/utils/serializers/output/slack.js +2 -2
  22. package/core/server/api/canary/utils/serializers/output/themes.js +2 -2
  23. package/core/server/api/canary/utils/serializers/output/users.js +0 -23
  24. package/core/server/api/shared/serializers/handle.js +25 -26
  25. package/core/server/data/exporter/table-lists.js +1 -0
  26. package/core/server/data/migrations/utils.js +1 -1
  27. package/core/server/data/migrations/versions/4.42/2022-03-21-17-17-add.js +20 -0
  28. package/core/server/data/migrations/versions/4.42/2022-03-30-15-44-add-newsletter-permissions.js +28 -0
  29. package/core/server/data/schema/commands.js +13 -13
  30. package/core/server/data/schema/schema.js +18 -0
  31. package/core/server/models/newsletter.js +9 -0
  32. package/core/server/services/mega/template.js +25 -13
  33. package/core/server/services/members/service.js +2 -1
  34. package/core/server/services/stats/index.js +1 -0
  35. package/core/server/services/stats/lib/members-stats-service.js +165 -0
  36. package/core/server/services/stats/service.js +6 -0
  37. package/core/server/services/webhooks/webhooks-service.js +3 -1
  38. package/core/server/web/admin/views/default-prod.html +5 -5
  39. package/core/server/web/admin/views/default.html +5 -5
  40. package/core/server/web/api/canary/admin/routes.js +8 -2
  41. package/core/shared/config/defaults.json +2 -2
  42. package/core/shared/config/env/config.development.json +26 -0
  43. package/core/shared/config/env/config.production.json +21 -0
  44. package/core/shared/config/env/config.testing-mysql.json +59 -0
  45. package/core/shared/config/env/config.testing.json +58 -0
  46. package/package.json +38 -38
  47. package/yarn.lock +510 -654
  48. package/core/built/assets/ghost-dark-146c4c688b47d45c4aa018ee0f79cebc.css +0 -1
  49. package/core/built/assets/ghost.min-a73b150c7eecc4641d377cc73fb5eecd.css +0 -1
  50. package/core/server/api/canary/utils/serializers/output/email-preview.js +0 -10
  51. package/core/server/api/canary/utils/serializers/output/emails.js +0 -22
  52. package/core/server/api/canary/utils/serializers/output/identities.js +0 -7
  53. package/core/server/api/canary/utils/serializers/output/member-signin-urls.js +0 -7
  54. package/core/server/api/canary/utils/serializers/output/snippets.js +0 -107
  55. package/core/server/api/canary/utils/serializers/output/webhooks.js +0 -15
@@ -1202,6 +1202,220 @@ ol .occluded-content {
1202
1202
  height: 0;
1203
1203
  }
1204
1204
 
1205
+ :root {
1206
+ /* The named -duration and -delay variables will be lowered to near zero when using the setupPromiseModals test helper */
1207
+ --epm-animation-backdrop-in-duration: 0.3s;
1208
+ --epm-animation-backdrop-out-duration: 0.18s;
1209
+ --epm-animation-modal-in-duration: 0.3s;
1210
+ --epm-animation-modal-out-duration: 0.18s;
1211
+ --epm-animation-backdrop-in-delay: 0s;
1212
+ --epm-animation-backdrop-out-delay: 0s;
1213
+ --epm-animation-modal-in-delay: 0s;
1214
+ --epm-animation-modal-out-delay: 0s;
1215
+ --epm-animation-backdrop-in: var(--epm-animation-backdrop-in-duration) ease var(--epm-animation-backdrop-in-delay) forwards epm-backdrop-in;
1216
+ --epm-animation-backdrop-out: var(--epm-animation-backdrop-out-duration) ease var(--epm-animation-backdrop-out-delay) forwards epm-backdrop-out;
1217
+ --epm-animation-modal-in: var(--epm-animation-modal-in-duration) ease-out var(--epm-animation-modal-in-delay) forwards epm-modal-in;
1218
+ --epm-animation-modal-out: var(--epm-animation-modal-out-duration) ease-out var(--epm-animation-modal-out-delay) forwards epm-modal-out;
1219
+ --epm-backdrop-background: #2d3748CD;
1220
+ }
1221
+
1222
+ @media (prefers-reduced-motion: reduce) {
1223
+ :root {
1224
+ --epm-animation-backdrop-in-duration: 0s;
1225
+ --epm-animation-backdrop-out-duration: 0s;
1226
+ --epm-animation-modal-in-duration: 0s;
1227
+ --epm-animation-modal-out-duration: 0s;
1228
+ --epm-animation-backdrop-in-delay: 0s;
1229
+ --epm-animation-backdrop-out-delay: 0s;
1230
+ --epm-animation-modal-in-delay: 0s;
1231
+ --epm-animation-modal-out-delay: 0s;
1232
+ }
1233
+ }
1234
+
1235
+ .epm-scrolling-disabled {
1236
+ overflow: hidden;
1237
+ }
1238
+
1239
+ .epm-backdrop,
1240
+ .epm-modal-container {
1241
+ position: fixed;
1242
+ top: 0;
1243
+ right: 0;
1244
+ bottom: 0;
1245
+ left: 0;
1246
+ }
1247
+
1248
+ .epm-backdrop {
1249
+ background-color: #2d3748CD;
1250
+ background-color: var(--epm-backdrop-background);
1251
+ opacity: 0;
1252
+ -webkit-animation: 0.3s ease 0s forwards epm-backdrop-in;
1253
+ animation: 0.3s ease 0s forwards epm-backdrop-in;
1254
+ -webkit-animation: var(--epm-animation-backdrop-in);
1255
+ animation: var(--epm-animation-backdrop-in);
1256
+ -webkit-animation-delay: 0s;
1257
+ animation-delay: 0s;
1258
+ -webkit-animation-delay: var(--epm-animation-backdrop-in-delay);
1259
+ animation-delay: var(--epm-animation-backdrop-in-delay);
1260
+ -webkit-animation-duration: 0.3s;
1261
+ animation-duration: 0.3s;
1262
+ -webkit-animation-duration: var(--epm-animation-backdrop-in-duration);
1263
+ animation-duration: var(--epm-animation-backdrop-in-duration);
1264
+ }
1265
+
1266
+ .epm-modal-container {
1267
+ display: flex;
1268
+ align-items: center;
1269
+ justify-content: center;
1270
+ overflow: auto;
1271
+ }
1272
+
1273
+ .epm-animating .epm-modal-container {
1274
+ overflow: unset;
1275
+ }
1276
+
1277
+ .epm-modal {
1278
+ margin: auto;
1279
+ transform: translate(0, -30vh) scale(1.1);
1280
+ opacity: 0;
1281
+ -webkit-animation: 0.3s ease-out 0s forwards epm-modal-in;
1282
+ animation: 0.3s ease-out 0s forwards epm-modal-in;
1283
+ -webkit-animation: var(--epm-animation-modal-in);
1284
+ animation: var(--epm-animation-modal-in);
1285
+ -webkit-animation-delay: 0s;
1286
+ animation-delay: 0s;
1287
+ -webkit-animation-delay: var(--epm-animation-modal-in-delay);
1288
+ animation-delay: var(--epm-animation-modal-in-delay);
1289
+ -webkit-animation-duration: 0.3s;
1290
+ animation-duration: 0.3s;
1291
+ -webkit-animation-duration: var(--epm-animation-modal-in-duration);
1292
+ animation-duration: var(--epm-animation-modal-in-duration);
1293
+ -webkit-overflow-scrolling: touch; /* momentum-based scrolling for Safari on iOS */
1294
+ }
1295
+
1296
+ .epm-backdrop.epm-out {
1297
+ opacity: 1;
1298
+ -webkit-animation: 0.18s ease 0s forwards epm-backdrop-out;
1299
+ animation: 0.18s ease 0s forwards epm-backdrop-out;
1300
+ -webkit-animation: var(--epm-animation-backdrop-out);
1301
+ animation: var(--epm-animation-backdrop-out);
1302
+ -webkit-animation-delay: 0s;
1303
+ animation-delay: 0s;
1304
+ -webkit-animation-delay: var(--epm-animation-backdrop-out-delay);
1305
+ animation-delay: var(--epm-animation-backdrop-out-delay);
1306
+ -webkit-animation-duration: 0.18s;
1307
+ animation-duration: 0.18s;
1308
+ -webkit-animation-duration: var(--epm-animation-backdrop-out-duration);
1309
+ animation-duration: var(--epm-animation-backdrop-out-duration);
1310
+ pointer-events: none;
1311
+ }
1312
+
1313
+ .epm-modal.epm-out {
1314
+ transform: translate(0, 0) scale(1);
1315
+ opacity: 1;
1316
+ -webkit-animation: 0.18s ease-out 0s forwards epm-modal-out;
1317
+ animation: 0.18s ease-out 0s forwards epm-modal-out;
1318
+ -webkit-animation: var(--epm-animation-modal-out);
1319
+ animation: var(--epm-animation-modal-out);
1320
+ -webkit-animation-delay: 0s;
1321
+ animation-delay: 0s;
1322
+ -webkit-animation-delay: var(--epm-animation-modal-out-delay);
1323
+ animation-delay: var(--epm-animation-modal-out-delay);
1324
+ -webkit-animation-duration: 0.18s;
1325
+ animation-duration: 0.18s;
1326
+ -webkit-animation-duration: var(--epm-animation-modal-out-duration);
1327
+ animation-duration: var(--epm-animation-modal-out-duration);
1328
+ pointer-events: none;
1329
+ }
1330
+
1331
+ @-webkit-keyframes epm-backdrop-in {
1332
+ 0% {
1333
+ opacity: 0;
1334
+ }
1335
+ 100% {
1336
+ opacity: 1;
1337
+ }
1338
+ }
1339
+
1340
+ @keyframes epm-backdrop-in {
1341
+ 0% {
1342
+ opacity: 0;
1343
+ }
1344
+ 100% {
1345
+ opacity: 1;
1346
+ }
1347
+ }
1348
+
1349
+ @-webkit-keyframes epm-backdrop-out {
1350
+ 0% {
1351
+ opacity: 1;
1352
+ }
1353
+ 100% {
1354
+ opacity: 0;
1355
+ }
1356
+ }
1357
+
1358
+ @keyframes epm-backdrop-out {
1359
+ 0% {
1360
+ opacity: 1;
1361
+ }
1362
+ 100% {
1363
+ opacity: 0;
1364
+ }
1365
+ }
1366
+
1367
+ @-webkit-keyframes epm-modal-in {
1368
+ 0% {
1369
+ transform: translate(0, -30vh) scale(1.1);
1370
+ opacity: 0;
1371
+ }
1372
+ 72% {
1373
+ transform: translate(0, 0) scale(0.99);
1374
+ opacity: 1;
1375
+ }
1376
+ 100% {
1377
+ transform: translate(0, 0) scale(1);
1378
+ opacity: 1;
1379
+ }
1380
+ }
1381
+
1382
+ @keyframes epm-modal-in {
1383
+ 0% {
1384
+ transform: translate(0, -30vh) scale(1.1);
1385
+ opacity: 0;
1386
+ }
1387
+ 72% {
1388
+ transform: translate(0, 0) scale(0.99);
1389
+ opacity: 1;
1390
+ }
1391
+ 100% {
1392
+ transform: translate(0, 0) scale(1);
1393
+ opacity: 1;
1394
+ }
1395
+ }
1396
+
1397
+ @-webkit-keyframes epm-modal-out {
1398
+ 0% {
1399
+ transform: translate(0, 0) scale(1);
1400
+ opacity: 1;
1401
+ }
1402
+ 100% {
1403
+ transform: translate(0, -10vh) scale(0.8);
1404
+ opacity: 0;
1405
+ }
1406
+ }
1407
+
1408
+ @keyframes epm-modal-out {
1409
+ 0% {
1410
+ transform: translate(0, 0) scale(1);
1411
+ opacity: 1;
1412
+ }
1413
+ 100% {
1414
+ transform: translate(0, -10vh) scale(0.8);
1415
+ opacity: 0;
1416
+ }
1417
+ }
1418
+
1205
1419
  .ember-tooltip-base {
1206
1420
  display: none;
1207
1421
  height: 0;
@@ -58,6 +58,7 @@ module.exports = function price(planOrAmount, options) {
58
58
  }
59
59
  options = options || {};
60
60
  options.hash = options.hash || {};
61
+ // NOTE: potentially breaking place once site.lang is removed in favor of site.locale
61
62
  const {currency, numberFormat = 'short', currencyFormat = 'symbol', locale = _.get(options, 'data.site.lang', 'en')} = options.hash;
62
63
  if (plan) {
63
64
  return formatter({
@@ -12,7 +12,8 @@ const emailPreview = new mega.EmailPreview({
12
12
  });
13
13
 
14
14
  module.exports = {
15
- docName: 'email_preview',
15
+ // @TODO make the route match the resource name in 5.0
16
+ docName: 'email_previews',
16
17
 
17
18
  read: {
18
19
  options: [
File without changes
@@ -158,7 +158,7 @@ module.exports = {
158
158
  },
159
159
 
160
160
  get emails() {
161
- return shared.pipeline(require('./email'), localUtils);
161
+ return shared.pipeline(require('./emails'), localUtils);
162
162
  },
163
163
 
164
164
  get site() {
@@ -169,6 +169,10 @@ module.exports = {
169
169
  return shared.pipeline(require('./snippets'), localUtils);
170
170
  },
171
171
 
172
+ get stats() {
173
+ return shared.pipeline(require('./stats'), localUtils);
174
+ },
175
+
172
176
  get customThemeSettings() {
173
177
  return shared.pipeline(require('./custom-theme-settings'), localUtils);
174
178
  },
@@ -177,6 +181,10 @@ module.exports = {
177
181
  return require('./utils/serializers');
178
182
  },
179
183
 
184
+ get newsletters() {
185
+ return shared.pipeline(require('./newsletters'), localUtils);
186
+ },
187
+
180
188
  /**
181
189
  * Content API Controllers
182
190
  *
@@ -452,51 +452,6 @@ module.exports = {
452
452
  };
453
453
  }
454
454
  },
455
- subscriberStats: {
456
- permissions: {
457
- method: 'browse'
458
- },
459
- async query() {
460
- const statsData = await membersService.api.events.getSubscriptions();
461
- const totalSubscriptions = (_.last(statsData) && _.last(statsData).subscribed) || 0;
462
- statsData.forEach((d) => {
463
- d.date = moment(d.date).format('YYYY-MM-DD');
464
- });
465
- return {
466
- resource: 'subscribers',
467
- total: totalSubscriptions,
468
- data: statsData.map((d) => {
469
- return Object.assign({}, {
470
- date: moment(d.date).format('YYYY-MM-DD'),
471
- value: d.subscribed
472
- });
473
- })
474
- };
475
- }
476
- },
477
- grossVolumeStats: {
478
- permissions: {
479
- method: 'browse'
480
- },
481
- async query() {
482
- const volumeData = await membersService.api.events.getVolume();
483
- const volumeStats = Object.keys(volumeData).map((curr) => {
484
- return {
485
- currency: curr,
486
- data: volumeData[curr].map((d) => {
487
- return Object.assign({}, {
488
- date: moment(d.date).format('YYYY-MM-DD'),
489
- value: d.volume
490
- });
491
- })
492
- };
493
- });
494
- return {
495
- resource: 'gross-volume',
496
- data: volumeStats
497
- };
498
- }
499
- },
500
455
 
501
456
  activityFeed: {
502
457
  options: [
@@ -0,0 +1,45 @@
1
+ const models = require('../../models');
2
+
3
+ module.exports = {
4
+ docName: 'newsletters',
5
+
6
+ browse: {
7
+ options: [
8
+ 'filter',
9
+ 'fields',
10
+ 'limit',
11
+ 'order',
12
+ 'page'
13
+ ],
14
+ permissions: true,
15
+ query(frame) {
16
+ return models.Newsletter.findPage(frame.options);
17
+ }
18
+ },
19
+
20
+ add: {
21
+ statusCode: 201,
22
+ permissions: true,
23
+ async query(frame) {
24
+ return models.Newsletter.add(frame.data.newsletters[0], frame.options);
25
+ }
26
+ },
27
+
28
+ edit: {
29
+ headers: {},
30
+ options: [
31
+ 'id'
32
+ ],
33
+ validation: {
34
+ options: {
35
+ id: {
36
+ required: true
37
+ }
38
+ }
39
+ },
40
+ permissions: true,
41
+ async query(frame) {
42
+ return models.Newsletter.edit(frame.data.newsletters[0], frame.options);
43
+ }
44
+ }
45
+ };
@@ -0,0 +1,14 @@
1
+ const statsService = require('../../services/stats');
2
+
3
+ module.exports = {
4
+ docName: 'stats',
5
+ memberCountHistory: {
6
+ permissions: {
7
+ docName: 'members',
8
+ method: 'browse'
9
+ },
10
+ async query() {
11
+ return await statsService.members.getCountHistory();
12
+ }
13
+ }
14
+ };
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ // @TODO remove for 5.0
3
+ // This should be a default serializer, not a passthrough, as per the read endpoint
4
+ sendTestEmail(data, apiConfig, frame) {
5
+ frame.response = data;
6
+ }
7
+ };
@@ -41,10 +41,6 @@ module.exports = {
41
41
  return require('./schedules');
42
42
  },
43
43
 
44
- get webhooks() {
45
- return require('./webhooks');
46
- },
47
-
48
44
  get posts() {
49
45
  return require('./posts');
50
46
  },
@@ -73,14 +69,6 @@ module.exports = {
73
69
  return require('./tiers');
74
70
  },
75
71
 
76
- get member_signin_urls() {
77
- return require('./member-signin-urls');
78
- },
79
-
80
- get identities() {
81
- return require('./identities');
82
- },
83
-
84
72
  get images() {
85
73
  return require('./images');
86
74
  },
@@ -121,16 +109,8 @@ module.exports = {
121
109
  return require('./site');
122
110
  },
123
111
 
124
- get email_preview() {
125
- return require('./email-preview');
126
- },
127
-
128
- get emails() {
129
- return require('./emails');
130
- },
131
-
132
- get snippets() {
133
- return require('./snippets');
112
+ get email_previews() {
113
+ return require('./email-previews');
134
114
  },
135
115
 
136
116
  get custom_theme_settings() {
@@ -7,6 +7,7 @@ module.exports = {
7
7
  pages: require('./pages'),
8
8
  posts: require('./posts'),
9
9
  settings: require('./settings'),
10
+ snippets: require('./snippets'),
10
11
  tags: require('./tags'),
11
12
  users: require('./users')
12
13
  };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @param {import('bookshelf').Model} snippet
3
+ * @param {Frame} frame
4
+ *
5
+ * @returns {SerializedSnippet}
6
+ */
7
+ module.exports = (snippet, frame) => {
8
+ const json = snippet.toJSON(frame.options);
9
+
10
+ return {
11
+ id: json.id,
12
+ name: json.name,
13
+ // @ts-ignore
14
+ mobiledoc: json.mobiledoc,
15
+ created_at: json.created_at,
16
+ updated_at: json.updated_at,
17
+ created_by: json.created_by,
18
+ updated_by: json.updated_by
19
+ };
20
+ };
21
+
22
+ /**
23
+ * @typedef {Object} SerializedSnippet
24
+ * @prop {string} id
25
+ * @prop {string=} name
26
+ * @prop {string=} mobiledoc
27
+ * @prop {string} created_at
28
+ * @prop {string} updated_at
29
+ * @prop {string} created_by
30
+ * @prop {string} updated_by
31
+ */
32
+
33
+ /**
34
+ * @typedef {Object<string, any>} Frame
35
+ * @prop {Object} options
36
+ */
@@ -20,8 +20,6 @@ module.exports = {
20
20
  importCSV: createSerializer('importCSV', passthrough),
21
21
  memberStats: createSerializer('memberStats', passthrough),
22
22
  mrrStats: createSerializer('mrrStats', passthrough),
23
- subscriberStats: createSerializer('subscriberStats', passthrough),
24
- grossVolumeStats: createSerializer('grossVolumeStats', passthrough),
25
23
  activityFeed: createSerializer('activityFeed', passthrough)
26
24
  };
27
25
 
@@ -1,8 +1,8 @@
1
1
  const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:oembed');
2
2
 
3
3
  module.exports = {
4
- all(res, apiConfig, frame) {
4
+ all(data, apiConfig, frame) {
5
5
  debug('all');
6
- frame.response = res;
6
+ frame.response = data;
7
7
  }
8
8
  };
@@ -1,5 +1,5 @@
1
1
  module.exports = {
2
- all(response, apiConfig, frame) {
3
- frame.response = response;
2
+ all(data, apiConfig, frame) {
3
+ frame.response = data;
4
4
  }
5
5
  };
@@ -1,5 +1,5 @@
1
1
  module.exports = {
2
- all(model, apiConfig, frame) {
3
- frame.response = model;
2
+ all(data, apiConfig, frame) {
3
+ frame.response = data;
4
4
  }
5
5
  };
@@ -1,9 +1,9 @@
1
1
  const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:slack');
2
2
 
3
3
  module.exports = {
4
- all(themes, apiConfig, frame) {
4
+ all(data, apiConfig, frame) {
5
5
  debug('all');
6
6
 
7
- frame.response = themes;
7
+ frame.response = data;
8
8
  }
9
9
  };
@@ -1,9 +1,9 @@
1
1
  const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:themes');
2
2
 
3
3
  module.exports = {
4
- all(themes, apiConfig, frame) {
4
+ all(data, apiConfig, frame) {
5
5
  debug('all');
6
6
 
7
- frame.response = themes;
7
+ frame.response = data;
8
8
  }
9
9
  };
@@ -1,34 +1,11 @@
1
1
  const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:users');
2
2
  const tpl = require('@tryghost/tpl');
3
- const mappers = require('./mappers');
4
3
 
5
4
  const messages = {
6
5
  pwdChangedSuccessfully: 'Password changed successfully.'
7
6
  };
8
7
 
9
8
  module.exports = {
10
- browse(models, apiConfig, frame) {
11
- debug('browse');
12
-
13
- frame.response = {
14
- users: models.data.map(model => mappers.users(model, frame)),
15
- meta: models.meta
16
- };
17
- },
18
-
19
- read(model, apiConfig, frame) {
20
- debug('read');
21
-
22
- frame.response = {
23
- users: [mappers.users(model, frame)]
24
- };
25
- },
26
-
27
- edit() {
28
- debug('edit');
29
- this.read(...arguments);
30
- },
31
-
32
9
  destroy(filename, apiConfig, frame) {
33
10
  debug('destroy');
34
11
 
@@ -67,6 +67,19 @@ module.exports.input = (apiConfig, apiSerializers, frame) => {
67
67
  return sequence(tasks);
68
68
  };
69
69
 
70
+ const getBestMatchSerializer = function (apiSerializers, docName, method) {
71
+ if (apiSerializers[docName] && apiSerializers[docName][method]) {
72
+ debug(`Calling ${docName}.${method}`);
73
+ return apiSerializers[docName][method].bind(apiSerializers[docName]);
74
+ } else if (apiSerializers[docName] && apiSerializers[docName].all) {
75
+ debug(`Calling ${docName}.all`);
76
+ return apiSerializers[docName].all.bind(apiSerializers[docName]);
77
+ }
78
+
79
+ debug(`Returning as-is`);
80
+ return false;
81
+ };
82
+
70
83
  /**
71
84
  * @description Shared output serialization handler.
72
85
  *
@@ -101,33 +114,19 @@ module.exports.output = (response = {}, apiConfig, apiSerializers, frame) => {
101
114
  });
102
115
  }
103
116
 
104
- // CASE: custom serializer exists
105
- if (apiSerializers[apiConfig.docName]) {
106
- if (apiSerializers[apiConfig.docName].all) {
107
- tasks.push(function serialiseCustomAll() {
108
- return apiSerializers[apiConfig.docName].all(response, apiConfig, frame);
109
- });
110
- }
117
+ const customSerializer = getBestMatchSerializer(apiSerializers, apiConfig.docName, apiConfig.method);
118
+ const defaultSerializer = getBestMatchSerializer(apiSerializers, 'default', apiConfig.method);
111
119
 
112
- if (apiSerializers[apiConfig.docName][apiConfig.method]) {
113
- tasks.push(function serialiseCustomMethod() {
114
- return apiSerializers[apiConfig.docName][apiConfig.method](response, apiConfig, frame);
115
- });
116
- }
117
-
118
- // CASE: Fall back to default serializer
119
- } else if (apiSerializers.default) {
120
- if (apiSerializers.default.all) {
121
- tasks.push(function serializeDefaultAll() {
122
- return apiSerializers.default.all(response, apiConfig, frame);
123
- });
124
- }
125
-
126
- if (apiSerializers.default[apiConfig.method]) {
127
- tasks.push(function serializeDefaultMethod() {
128
- return apiSerializers.default[apiConfig.method](response, apiConfig, frame);
129
- });
130
- }
120
+ if (customSerializer) {
121
+ // CASE: custom serializer exists
122
+ tasks.push(function doCustomSerializer() {
123
+ return customSerializer(response, apiConfig, frame);
124
+ });
125
+ } else if (defaultSerializer) {
126
+ // CASE: Fall back to default serializer
127
+ tasks.push(function doDefaultSerializer() {
128
+ return defaultSerializer(response, apiConfig, frame);
129
+ });
131
130
  }
132
131
 
133
132
  if (apiSerializers.all && apiSerializers.all.after) {
@@ -15,6 +15,7 @@ const BACKUP_TABLES = [
15
15
  'members_stripe_customers_subscriptions',
16
16
  'migrations',
17
17
  'migrations_lock',
18
+ 'newsletters',
18
19
  'oauth',
19
20
  'permissions',
20
21
  'permissions_roles',
@@ -178,7 +178,7 @@ function addPermissionToRole(config) {
178
178
  return;
179
179
  }
180
180
 
181
- logging.warn(`Adding permission(${config.permission}) to role(${config.role})`);
181
+ logging.info(`Adding permission(${config.permission}) to role(${config.role})`);
182
182
  await connection('permissions_roles').insert({
183
183
  id: ObjectId().toHexString(),
184
184
  permission_id: permission.id,