ghost 4.36.3 → 4.38.1

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 (98) hide show
  1. package/.c8rc.json +10 -0
  2. package/content/themes/casper/LICENSE +1 -1
  3. package/content/themes/casper/README.md +1 -1
  4. package/content/themes/casper/assets/built/global.css +1 -1
  5. package/content/themes/casper/assets/built/global.css.map +1 -1
  6. package/content/themes/casper/assets/built/screen.css +1 -1
  7. package/content/themes/casper/assets/built/screen.css.map +1 -1
  8. package/content/themes/casper/assets/css/global.css +14 -6
  9. package/content/themes/casper/assets/css/screen.css +9 -1
  10. package/content/themes/casper/package.json +2 -2
  11. package/content/themes/casper/partials/post-card.hbs +1 -1
  12. package/content/themes/casper/post.hbs +18 -19
  13. package/content/themes/casper/yarn.lock +186 -217
  14. package/core/built/assets/ghost-dark-9f760f16230b8bc52e188d6ce28516b0.css +1 -0
  15. package/core/built/assets/{ghost.min-801697772dc605c0dae0abfec54ec591.js → ghost.min-6386b02480494a69c3bfe66206754836.js} +375 -312
  16. package/core/built/assets/ghost.min-f4c59dd57a2136df8b0a34f87c099034.css +1 -0
  17. package/core/built/assets/icons/eye.svg +4 -1
  18. package/core/built/assets/icons/member-add.svg +3 -0
  19. package/core/built/assets/icons/pin.svg +4 -1
  20. package/core/built/assets/{vendor.min-2313642ee897688be83924a38d5e62f1.js → vendor.min-c814d3c4b3f543c4cd5ef3aacd0fc645.js} +40 -36
  21. package/core/frontend/helpers/excerpt.js +7 -4
  22. package/core/frontend/helpers/get.js +4 -0
  23. package/core/frontend/helpers/match.js +12 -0
  24. package/core/frontend/helpers/prev_post.js +11 -1
  25. package/core/frontend/helpers/tiers.js +59 -0
  26. package/core/frontend/helpers/tpl/content-cta.hbs +1 -1
  27. package/core/frontend/services/routing/router-manager.js +1 -1
  28. package/core/frontend/web/site.js +10 -0
  29. package/core/server/api/canary/authentication.js +2 -0
  30. package/core/server/api/canary/index.js +8 -0
  31. package/core/server/api/canary/members.js +2 -1
  32. package/core/server/api/canary/products.js +3 -6
  33. package/core/server/api/canary/tiers-public.js +34 -0
  34. package/core/server/api/canary/tiers.js +120 -0
  35. package/core/server/api/canary/utils/serializers/input/index.js +4 -0
  36. package/core/server/api/canary/utils/serializers/input/tiers.js +36 -0
  37. package/core/server/api/canary/utils/serializers/output/email-posts.js +7 -1
  38. package/core/server/api/canary/utils/serializers/output/index.js +4 -0
  39. package/core/server/api/canary/utils/serializers/output/members.js +5 -0
  40. package/core/server/api/canary/utils/serializers/output/pages.js +9 -2
  41. package/core/server/api/canary/utils/serializers/output/posts.js +8 -2
  42. package/core/server/api/canary/utils/serializers/output/preview.js +7 -1
  43. package/core/server/api/canary/utils/serializers/output/products.js +3 -1
  44. package/core/server/api/canary/utils/serializers/output/tiers.js +212 -0
  45. package/core/server/api/canary/utils/serializers/output/utils/mapper.js +17 -7
  46. package/core/server/api/canary/utils/validators/input/index.js +4 -0
  47. package/core/server/api/canary/utils/validators/input/tiers.js +6 -0
  48. package/core/server/api/v2/settings.js +2 -1
  49. package/core/server/data/db/connection.js +3 -2
  50. package/core/server/data/migrations/init/1-create-tables.js +4 -1
  51. package/core/server/data/migrations/versions/3.29/01-remove-duplicate-subscriptions.js +2 -1
  52. package/core/server/data/migrations/versions/3.29/02-remove-duplicate-customers.js +2 -1
  53. package/core/server/data/migrations/versions/3.29/03-remove-orphaned-customers.js +2 -1
  54. package/core/server/data/migrations/versions/3.29/04-remove-orphaned-subscriptions.js +2 -1
  55. package/core/server/data/migrations/versions/3.29/05-add-member-constraints.js +3 -2
  56. package/core/server/data/migrations/versions/3.39/06-add-email-recipient-index.js +4 -3
  57. package/core/server/data/migrations/versions/4.0/14-remove-orphaned-stripe-records.js +2 -1
  58. package/core/server/data/migrations/versions/4.0/26-add-cascade-on-delete.js +2 -1
  59. package/core/server/data/migrations/versions/4.0/29-fix-foreign-key-for-members-stripe-customers-subscriptions.js +2 -1
  60. package/core/server/data/migrations/versions/4.1/02-add-unique-constraint-for-member-stripe-tables.js +2 -1
  61. package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +3 -2
  62. package/core/server/data/migrations/versions/4.33/2022-01-18-09-07-remove-duplicate-offer-redemptions.js +2 -2
  63. package/core/server/data/migrations/versions/4.35/2022-02-01-11-48-update-email-recipient-filter-column-type.js +2 -1
  64. package/core/server/data/migrations/versions/4.35/2022-02-01-12-03-update-recipient-filter-column-type.js +2 -1
  65. package/core/server/data/migrations/versions/4.37/2022-02-21-09-53-backfill-members-last-seen-at-column.js +32 -0
  66. package/core/server/data/migrations/versions/4.38/2022-03-01-08-46-add-visibility-to-tiers.js +11 -0
  67. package/core/server/data/migrations/versions/4.38/2022-03-03-16-12-add-visibility-to-tiers.js +8 -0
  68. package/core/server/data/migrations/versions/4.38/2022-03-03-16-17-drop-tiers-visible-column.js +7 -0
  69. package/core/server/data/schema/clients/index.js +1 -1
  70. package/core/server/data/schema/clients/mysql.js +4 -4
  71. package/core/server/data/schema/commands.js +61 -70
  72. package/core/server/data/schema/{default-settings.json → default-settings/default-settings.json} +0 -0
  73. package/core/server/data/schema/default-settings/index.js +6 -0
  74. package/core/server/data/schema/fixtures/fixtures.json +4 -2
  75. package/core/server/data/schema/schema.js +7 -0
  76. package/core/server/models/product.js +2 -1
  77. package/core/server/services/auth/api-key/admin.js +15 -6
  78. package/core/server/services/auth/setup.js +13 -1
  79. package/core/server/services/email-analytics/lib/event-processor.js +18 -1
  80. package/core/server/services/members/middleware.js +4 -1
  81. package/core/server/services/members/service.js +21 -8
  82. package/core/server/services/route-settings/index.js +1 -1
  83. package/core/server/web/admin/views/default-prod.html +4 -4
  84. package/core/server/web/admin/views/default.html +4 -4
  85. package/core/server/web/api/app.js +3 -0
  86. package/core/server/web/api/canary/admin/middleware.js +2 -0
  87. package/core/server/web/api/canary/admin/routes.js +7 -0
  88. package/core/server/web/api/canary/content/routes.js +1 -0
  89. package/core/server/web/members/app.js +1 -1
  90. package/core/server/web/shared/middleware/uncapitalise.js +2 -2
  91. package/core/shared/config/defaults.json +3 -2
  92. package/core/shared/config/overrides.json +1 -1
  93. package/core/shared/config/utils.js +5 -1
  94. package/core/shared/labs.js +4 -1
  95. package/package.json +62 -61
  96. package/yarn.lock +729 -614
  97. package/core/built/assets/ghost-dark-25e568b14d76f6754fa8279cceb265ba.css +0 -1
  98. package/core/built/assets/ghost.min-75ed7451ca633bae1b345eb57e2c28e0.css +0 -1
@@ -1,4 +1,5 @@
1
1
  const logging = require('@tryghost/logging');
2
+ const DatabaseInfo = require('@tryghost/database-info');
2
3
 
3
4
  module.exports = {
4
5
  config: {
@@ -6,7 +7,7 @@ module.exports = {
6
7
  },
7
8
 
8
9
  async up({transacting: knex}) {
9
- if (knex.client.config.client !== 'mysql') {
10
+ if (!DatabaseInfo.isMySQL(knex)) {
10
11
  logging.warn('Skipping cleanup of duplicate subscriptions - database is not MySQL');
11
12
  return;
12
13
  }
@@ -1,4 +1,5 @@
1
1
  const logging = require('@tryghost/logging');
2
+ const DatabaseInfo = require('@tryghost/database-info');
2
3
 
3
4
  module.exports = {
4
5
  config: {
@@ -6,7 +7,7 @@ module.exports = {
6
7
  },
7
8
 
8
9
  async up({transacting: knex}) {
9
- if (knex.client.config.client !== 'mysql') {
10
+ if (!DatabaseInfo.isMySQL(knex)) {
10
11
  logging.warn('Skipping cleanup of duplicate customers - database is not MySQL');
11
12
  return;
12
13
  }
@@ -1,4 +1,5 @@
1
1
  const logging = require('@tryghost/logging');
2
+ const DatabaseInfo = require('@tryghost/database-info');
2
3
 
3
4
  module.exports = {
4
5
  config: {
@@ -6,7 +7,7 @@ module.exports = {
6
7
  },
7
8
 
8
9
  async up({transacting: knex}) {
9
- if (knex.client.config.client !== 'mysql') {
10
+ if (!DatabaseInfo.isMySQL(knex)) {
10
11
  logging.warn('Skipping cleanup of orphaned customers - database is not MySQL');
11
12
  return;
12
13
  }
@@ -1,4 +1,5 @@
1
1
  const logging = require('@tryghost/logging');
2
+ const DatabaseInfo = require('@tryghost/database-info');
2
3
 
3
4
  module.exports = {
4
5
  config: {
@@ -6,7 +7,7 @@ module.exports = {
6
7
  },
7
8
 
8
9
  async up({transacting: knex}) {
9
- if (knex.client.config.client !== 'mysql') {
10
+ if (!DatabaseInfo.isMySQL(knex)) {
10
11
  logging.warn('Skipping cleanup of orphaned subscriptions - database is not MySQL');
11
12
  return;
12
13
  }
@@ -1,4 +1,5 @@
1
1
  const logging = require('@tryghost/logging');
2
+ const DatabaseInfo = require('@tryghost/database-info');
2
3
 
3
4
  module.exports = {
4
5
  config: {
@@ -6,7 +7,7 @@ module.exports = {
6
7
  },
7
8
 
8
9
  async up({transacting: knex}) {
9
- if (knex.client.config.client !== 'mysql') {
10
+ if (!DatabaseInfo.isMySQL(knex)) {
10
11
  return logging.warn('Skipping member tables index creation - database is not MySQL');
11
12
  }
12
13
 
@@ -91,7 +92,7 @@ module.exports = {
91
92
  },
92
93
 
93
94
  async down({transacting: knex}) {
94
- if (knex.client.config.client !== 'mysql') {
95
+ if (!DatabaseInfo.isMySQL(knex)) {
95
96
  return logging.warn('Skipping member tables index removal - database is not MySQL');
96
97
  }
97
98
 
@@ -1,11 +1,12 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const {createNonTransactionalMigration} = require('../../utils');
3
+ const DatabaseInfo = require('@tryghost/database-info');
3
4
 
4
5
  module.exports = createNonTransactionalMigration(
5
6
  async function up(knex) {
6
7
  let hasIndex = false;
7
8
 
8
- if (knex.client.config.client === 'sqlite3') {
9
+ if (DatabaseInfo.isSQLite(knex)) {
9
10
  const result = await knex.raw(`select * from sqlite_master where type = 'index' and tbl_name = 'email_recipients' and name = 'email_recipients_email_id_member_email_index'`);
10
11
  hasIndex = result.length !== 0;
11
12
  } else {
@@ -27,7 +28,7 @@ module.exports = createNonTransactionalMigration(
27
28
  async function down(knex) {
28
29
  let missingIndex = false;
29
30
 
30
- if (knex.client.config.client === 'sqlite3') {
31
+ if (DatabaseInfo.isSQLite(knex)) {
31
32
  const result = await knex.raw(`select * from sqlite_master where type = 'index' and tbl_name = 'email_recipients' and name = 'email_recipients_email_id_member_email_index'`);
32
33
  missingIndex = result.length === 0;
33
34
  } else {
@@ -42,7 +43,7 @@ module.exports = createNonTransactionalMigration(
42
43
 
43
44
  logging.info('Dropping composite index on email_recipients for [email_id, member_email]');
44
45
 
45
- if (knex.client.config.client === 'mysql') {
46
+ if (DatabaseInfo.isMySQL(knex)) {
46
47
  await knex.schema.table('email_recipients', (table) => {
47
48
  table.dropForeign('email_id');
48
49
  table.dropIndex(['email_id', 'member_email']);
@@ -1,8 +1,9 @@
1
1
  const {createIrreversibleMigration} = require('../../utils');
2
2
  const logging = require('@tryghost/logging');
3
+ const DatabaseInfo = require('@tryghost/database-info');
3
4
 
4
5
  module.exports = createIrreversibleMigration(async function up(connection) {
5
- if (connection.client.config.client === 'mysql') {
6
+ if (DatabaseInfo.isMySQL(connection)) {
6
7
  logging.info('Skipping removal of orphaned stripe records for MySQL');
7
8
  return;
8
9
  }
@@ -1,9 +1,10 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const {createIrreversibleMigration} = require('../../utils');
3
3
  const {addForeign, dropForeign} = require('../../../schema/commands');
4
+ const DatabaseInfo = require('@tryghost/database-info');
4
5
 
5
6
  module.exports = createIrreversibleMigration(async (knex) => {
6
- if (knex.client.config.client !== 'sqlite3') {
7
+ if (!DatabaseInfo.isSQLite(knex)) {
7
8
  return logging.warn('Skipping adding "on delete cascade" - database is not SQLite3');
8
9
  }
9
10
 
@@ -1,9 +1,10 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const {createIrreversibleMigration} = require('../../utils');
3
3
  const {addForeign, dropForeign} = require('../../../schema/commands');
4
+ const DatabaseInfo = require('@tryghost/database-info');
4
5
 
5
6
  module.exports = createIrreversibleMigration(async (knex) => {
6
- if (knex.client.config.client !== 'sqlite3') {
7
+ if (!DatabaseInfo.isSQLite(knex)) {
7
8
  return logging.warn('Skipping fixing foreign key for members_stripe_customers_subscriptions - database is not SQLite3');
8
9
  }
9
10
 
@@ -1,10 +1,11 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const {createTransactionalMigration} = require('../../utils');
3
3
  const {addUnique} = require('../../../schema/commands');
4
+ const DatabaseInfo = require('@tryghost/database-info');
4
5
 
5
6
  module.exports = createTransactionalMigration(
6
7
  async function up(connection) {
7
- if (connection.client.config.client !== 'sqlite3') {
8
+ if (!DatabaseInfo.isSQLite(connection)) {
8
9
  return logging.warn('Skipping adding unique constraint for members_stripe_customers_subscriptions and members_stripe_customers - database is not SQLite3');
9
10
  }
10
11
 
@@ -1,6 +1,7 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const {createNonTransactionalMigration} = require('../../utils');
3
3
  const {addUnique} = require('../../../schema/commands');
4
+ const DatabaseInfo = require('@tryghost/database-info');
4
5
 
5
6
  module.exports = createNonTransactionalMigration(
6
7
  async function up(knex) {
@@ -14,7 +15,7 @@ module.exports = createNonTransactionalMigration(
14
15
  table.string('portal_title', 191).nullable();
15
16
  });
16
17
 
17
- if (knex.client.config.client === 'sqlite3') {
18
+ if (DatabaseInfo.isSQLite(knex)) {
18
19
  // eslint-disable-next-line no-restricted-syntax
19
20
  for (const column of ['name', 'code', 'stripe_coupon_id']) {
20
21
  await addUnique('offers', column, knex);
@@ -32,7 +33,7 @@ module.exports = createNonTransactionalMigration(
32
33
  table.string('portal_title', 191).notNullable();
33
34
  });
34
35
 
35
- if (knex.client.config.client === 'sqlite3') {
36
+ if (DatabaseInfo.isSQLite(knex)) {
36
37
  // eslint-disable-next-line no-restricted-syntax
37
38
  for (const column of ['name', 'code', 'stripe_coupon_id']) {
38
39
  await addUnique('offers', column, knex);
@@ -1,10 +1,10 @@
1
1
  const logging = require('@tryghost/logging');
2
-
3
2
  const {createTransactionalMigration} = require('../../utils');
3
+ const DatabaseInfo = require('@tryghost/database-info');
4
4
 
5
5
  module.exports = createTransactionalMigration(
6
6
  async function up(knex) {
7
- if (knex.client.config.client !== 'mysql') {
7
+ if (!DatabaseInfo.isMySQL(knex)) {
8
8
  logging.warn('Skipping cleanup of duplicate offer redemptions - database is not MySQL');
9
9
  return;
10
10
  }
@@ -1,9 +1,10 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const {createNonTransactionalMigration} = require('../../utils');
3
+ const DatabaseInfo = require('@tryghost/database-info');
3
4
 
4
5
  module.exports = createNonTransactionalMigration(
5
6
  async function up(knex) {
6
- if (knex.client.config.client === 'sqlite3') {
7
+ if (DatabaseInfo.isSQLite(knex)) {
7
8
  logging.warn('Skipping migration for SQLite3');
8
9
  return;
9
10
  }
@@ -1,9 +1,10 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const {createNonTransactionalMigration} = require('../../utils');
3
+ const DatabaseInfo = require('@tryghost/database-info');
3
4
 
4
5
  module.exports = createNonTransactionalMigration(
5
6
  async function up(knex) {
6
- if (knex.client.config.client === 'sqlite3') {
7
+ if (DatabaseInfo.isSQLite(knex)) {
7
8
  logging.warn('Skipping migration for SQLite3');
8
9
  return;
9
10
  }
@@ -0,0 +1,32 @@
1
+ const logging = require('@tryghost/logging');
2
+ const {createTransactionalMigration} = require('../../utils');
3
+ const DatabaseInfo = require('@tryghost/database-info');
4
+
5
+ module.exports = createTransactionalMigration(
6
+ async function up(knex) {
7
+ if (DatabaseInfo.isSQLite(knex)) {
8
+ logging.warn('Skipping migration for SQLite3');
9
+ return;
10
+ }
11
+ logging.info('Backfilling the members.last_seen_at column from members_login_events.');
12
+ await knex.raw(`
13
+ UPDATE members
14
+ INNER JOIN (SELECT member_id as id, MAX(created_at) as last_seen_at
15
+ FROM members_login_events
16
+ GROUP BY member_id) as logins ON logins.id = members.id
17
+ SET
18
+ members.last_seen_at = logins.last_seen_at
19
+ WHERE
20
+ members.last_seen_at IS NULL
21
+ OR members.last_seen_at < logins.last_seen_at
22
+ `);
23
+ },
24
+ async function down(knex) {
25
+ if (DatabaseInfo.isSQLite(knex)) {
26
+ logging.warn('Skipping migration for SQLite3');
27
+ return;
28
+ }
29
+ logging.info('Rolling back the backfilling of the members.last_seen_at column from members_login_events.');
30
+ await knex('members').update({last_seen_at: null});
31
+ }
32
+ );
@@ -0,0 +1,11 @@
1
+ const {createAddColumnMigration} = require('../../utils');
2
+
3
+ module.exports = createAddColumnMigration('products', 'visible', {
4
+ type: 'boolean',
5
+ nullable: false,
6
+ defaultTo: false
7
+ });
8
+
9
+ module.exports.up = async () => {
10
+ // noop - column will be replaced with `visibility` instead
11
+ };
@@ -0,0 +1,8 @@
1
+ const {createAddColumnMigration} = require('../../utils');
2
+
3
+ module.exports = createAddColumnMigration('products', 'visibility', {
4
+ type: 'string',
5
+ maxlength: 50,
6
+ nullable: false,
7
+ defaultTo: 'none'
8
+ });
@@ -0,0 +1,7 @@
1
+ const {createDropColumnMigration} = require('../../utils');
2
+
3
+ module.exports = createDropColumnMigration('products', 'visible', {});
4
+
5
+ module.exports.down = async () => {
6
+ // noop - column was replaced by `visibility` instead
7
+ };
@@ -3,5 +3,5 @@ const mysql = require('./mysql');
3
3
 
4
4
  module.exports = {
5
5
  sqlite3: sqlite3,
6
- mysql: mysql
6
+ mysql2: mysql
7
7
  };
@@ -1,8 +1,8 @@
1
1
  const _ = require('lodash');
2
2
  const db = require('../../../data/db');
3
3
 
4
- const doRawAndFlatten = function doRaw(query, transaction, flattenFn) {
5
- return (transaction || db.knex).raw(query).then(function (response) {
4
+ const doRawAndFlatten = function doRaw(query, transaction = db.knex, flattenFn) {
5
+ return transaction.raw(query).then(function (response) {
6
6
  return _.flatten(flattenFn(response));
7
7
  });
8
8
  };
@@ -31,8 +31,8 @@ const getColumns = function getColumns(table, transaction) {
31
31
  // a wrong datatype in schema.js some installations using mysql could have been created using the
32
32
  // data type text instead of mediumtext.
33
33
  // For details see: https://github.com/TryGhost/Ghost/issues/1947
34
- const checkPostTable = function checkPostTable(transaction) {
35
- return (transaction || db.knex).raw('SHOW FIELDS FROM posts where Field ="html" OR Field = "markdown"').then(function (response) {
34
+ const checkPostTable = function checkPostTable(transaction = db.knex) {
35
+ return transaction.raw('SHOW FIELDS FROM posts where Field ="html" OR Field = "markdown"').then(function (response) {
36
36
  return _.flatten(_.map(response[0], function (entry) {
37
37
  if (entry.Type.toLowerCase() !== 'mediumtext') {
38
38
  return (transaction || db.knex).raw('ALTER TABLE posts MODIFY ' + entry.Field + ' MEDIUMTEXT');
@@ -4,6 +4,7 @@ const logging = require('@tryghost/logging');
4
4
  const errors = require('@tryghost/errors');
5
5
  const tpl = require('@tryghost/tpl');
6
6
  const db = require('../db');
7
+ const DatabaseInfo = require('@tryghost/database-info');
7
8
  const schema = require('./schema');
8
9
  const clients = require('./clients');
9
10
 
@@ -58,14 +59,14 @@ function addTableColumn(tableName, table, columnName, columnSpec = schema[tableN
58
59
  }
59
60
  }
60
61
 
61
- function addColumn(tableName, column, transaction, columnSpec) {
62
- return (transaction || db.knex).schema.table(tableName, function (table) {
62
+ function addColumn(tableName, column, transaction = db.knex, columnSpec) {
63
+ return transaction.schema.table(tableName, function (table) {
63
64
  addTableColumn(tableName, table, column, columnSpec);
64
65
  });
65
66
  }
66
67
 
67
- function dropColumn(tableName, column, transaction) {
68
- return (transaction || db.knex).schema.table(tableName, function (table) {
68
+ function dropColumn(tableName, column, transaction = db.knex) {
69
+ return transaction.schema.table(tableName, function (table) {
69
70
  table.dropColumn(column);
70
71
  });
71
72
  }
@@ -77,11 +78,11 @@ function dropColumn(tableName, column, transaction) {
77
78
  * @param {string|[string]} columns - column(s) to form unique constraint with
78
79
  * @param {import('knex')} transaction - connection object containing knex reference
79
80
  */
80
- async function addUnique(tableName, columns, transaction) {
81
+ async function addUnique(tableName, columns, transaction = db.knex) {
81
82
  try {
82
83
  logging.info(`Adding unique constraint for: ${columns} in table ${tableName}`);
83
84
 
84
- return await (transaction || db.knex).schema.table(tableName, function (table) {
85
+ return await transaction.schema.table(tableName, function (table) {
85
86
  table.unique(columns);
86
87
  });
87
88
  } catch (err) {
@@ -104,11 +105,11 @@ async function addUnique(tableName, columns, transaction) {
104
105
  * @param {string|[string]} columns - column(s) unique constraint was formed
105
106
  * @param {import('knex')} transaction - connection object containing knex reference
106
107
  */
107
- async function dropUnique(tableName, columns, transaction) {
108
+ async function dropUnique(tableName, columns, transaction = db.knex) {
108
109
  try {
109
110
  logging.info(`Dropping unique constraint for: ${columns} in table: ${tableName}`);
110
111
 
111
- return await (transaction || db.knex).schema.table(tableName, function (table) {
112
+ return await transaction.schema.table(tableName, function (table) {
112
113
  table.dropUnique(columns);
113
114
  });
114
115
  } catch (err) {
@@ -134,17 +135,14 @@ async function dropUnique(tableName, columns, transaction) {
134
135
  * @param {string} configuration.toColumn - column of the table to point the foreign key to
135
136
  * @param {import('knex')} configuration.transaction - connection object containing knex reference
136
137
  */
137
- async function hasForeignSQLite({fromTable, fromColumn, toTable, toColumn, transaction}) {
138
- const knex = (transaction || db.knex);
139
- const client = knex.client.config.client;
140
-
141
- if (client !== 'sqlite3') {
138
+ async function hasForeignSQLite({fromTable, fromColumn, toTable, toColumn, transaction = db.knex}) {
139
+ if (!DatabaseInfo.isSQLite(transaction)) {
142
140
  throw new errors.InternalServerError({
143
141
  message: tpl(messages.hasForeignSQLite3)
144
142
  });
145
143
  }
146
144
 
147
- const foreignKeys = await knex.raw(`PRAGMA foreign_key_list('${fromTable}');`);
145
+ const foreignKeys = await transaction.raw(`PRAGMA foreign_key_list('${fromTable}');`);
148
146
 
149
147
  const hasForeignKey = foreignKeys.some(foreignKey => foreignKey.table === toTable && foreignKey.from === fromColumn && foreignKey.to === toColumn);
150
148
 
@@ -162,9 +160,8 @@ async function hasForeignSQLite({fromTable, fromColumn, toTable, toColumn, trans
162
160
  * @param {Boolean} configuration.cascadeDelete - adds the "on delete cascade" option if true
163
161
  * @param {import('knex')} configuration.transaction - connection object containing knex reference
164
162
  */
165
- async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDelete = false, transaction}) {
166
- const isSQLite = db.knex.client.config.client === 'sqlite3';
167
- if (isSQLite) {
163
+ async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDelete = false, transaction = db.knex}) {
164
+ if (DatabaseInfo.isSQLite(transaction)) {
168
165
  const foreignKeyExists = await hasForeignSQLite({fromTable, fromColumn, toTable, toColumn, transaction});
169
166
  if (foreignKeyExists) {
170
167
  logging.warn(`Skipped adding foreign key from ${fromTable}.${fromColumn} to ${toTable}.${toColumn} - foreign key already exists`);
@@ -176,14 +173,14 @@ async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDele
176
173
 
177
174
  //disable and re-enable foreign key checks on sqlite because of https://github.com/knex/knex/issues/4155
178
175
  let foreignKeysEnabled;
179
- if (isSQLite) {
176
+ if (DatabaseInfo.isSQLite(transaction)) {
180
177
  foreignKeysEnabled = await db.knex.raw('PRAGMA foreign_keys;');
181
178
  if (foreignKeysEnabled[0].foreign_keys) {
182
179
  await db.knex.raw('PRAGMA foreign_keys = OFF;');
183
180
  }
184
181
  }
185
182
 
186
- await (transaction || db.knex).schema.table(fromTable, function (table) {
183
+ await transaction.schema.table(fromTable, function (table) {
187
184
  if (cascadeDelete) {
188
185
  table.foreign(fromColumn).references(`${toTable}.${toColumn}`).onDelete('CASCADE');
189
186
  } else {
@@ -191,13 +188,13 @@ async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDele
191
188
  }
192
189
  });
193
190
 
194
- if (isSQLite) {
191
+ if (DatabaseInfo.isSQLite(transaction)) {
195
192
  if (foreignKeysEnabled[0].foreign_keys) {
196
193
  await db.knex.raw('PRAGMA foreign_keys = ON;');
197
194
  }
198
195
  }
199
196
  } catch (err) {
200
- if (err.code === 'ER_DUP_KEY') {
197
+ if (err.code === 'ER_DUP_KEY' || err.code === 'ER_FK_DUP_KEY' || err.code === 'ER_FK_DUP_NAME') {
201
198
  logging.warn(`Skipped adding foreign key from ${fromTable}.${fromColumn} to ${toTable}.${toColumn} - foreign key already exists`);
202
199
  return;
203
200
  }
@@ -215,9 +212,8 @@ async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDele
215
212
  * @param {string} configuration.toColumn - column of the table to point the foreign key to
216
213
  * @param {import('knex')} configuration.transaction - connection object containing knex reference
217
214
  */
218
- async function dropForeign({fromTable, fromColumn, toTable, toColumn, transaction}) {
219
- const isSQLite = db.knex.client.config.client === 'sqlite3';
220
- if (isSQLite) {
215
+ async function dropForeign({fromTable, fromColumn, toTable, toColumn, transaction = db.knex}) {
216
+ if (DatabaseInfo.isSQLite(transaction)) {
221
217
  const foreignKeyExists = await hasForeignSQLite({fromTable, fromColumn, toTable, toColumn, transaction});
222
218
  if (!foreignKeyExists) {
223
219
  logging.warn(`Skipped dropping foreign key from ${fromTable}.${fromColumn} to ${toTable}.${toColumn} - foreign key does not exist`);
@@ -229,18 +225,18 @@ async function dropForeign({fromTable, fromColumn, toTable, toColumn, transactio
229
225
 
230
226
  //disable and re-enable foreign key checks on sqlite because of https://github.com/knex/knex/issues/4155
231
227
  let foreignKeysEnabled;
232
- if (isSQLite) {
228
+ if (DatabaseInfo.isSQLite(transaction)) {
233
229
  foreignKeysEnabled = await db.knex.raw('PRAGMA foreign_keys;');
234
230
  if (foreignKeysEnabled[0].foreign_keys) {
235
231
  await db.knex.raw('PRAGMA foreign_keys = OFF;');
236
232
  }
237
233
  }
238
234
 
239
- await (transaction || db.knex).schema.table(fromTable, function (table) {
235
+ await transaction.schema.table(fromTable, function (table) {
240
236
  table.dropForeign(fromColumn);
241
237
  });
242
238
 
243
- if (isSQLite) {
239
+ if (DatabaseInfo.isSQLite(transaction)) {
244
240
  if (foreignKeysEnabled[0].foreign_keys) {
245
241
  await db.knex.raw('PRAGMA foreign_keys = ON;');
246
242
  }
@@ -260,17 +256,14 @@ async function dropForeign({fromTable, fromColumn, toTable, toColumn, transactio
260
256
  * @param {string} tableName - name of the table to check primary key constraint on
261
257
  * @param {import('knex')} transaction - connection object containing knex reference
262
258
  */
263
- async function hasPrimaryKeySQLite(tableName, transaction) {
264
- const knex = (transaction || db.knex);
265
- const client = knex.client.config.client;
266
-
267
- if (client !== 'sqlite3') {
259
+ async function hasPrimaryKeySQLite(tableName, transaction = db.knex) {
260
+ if (!DatabaseInfo.isSQLite(transaction)){
268
261
  throw new errors.InternalServerError({
269
262
  message: tpl(messages.hasPrimaryKeySQLiteError)
270
263
  });
271
264
  }
272
265
 
273
- const rawConstraints = await knex.raw(`PRAGMA index_list('${tableName}');`);
266
+ const rawConstraints = await transaction.raw(`PRAGMA index_list('${tableName}');`);
274
267
  const tablePrimaryKey = rawConstraints.find(c => c.origin === 'pk');
275
268
 
276
269
  return tablePrimaryKey;
@@ -283,9 +276,8 @@ async function hasPrimaryKeySQLite(tableName, transaction) {
283
276
  * @param {string|[string]} columns - column(s) to form primary key constraint with
284
277
  * @param {import('knex')} transaction - connection object containing knex reference
285
278
  */
286
- async function addPrimaryKey(tableName, columns, transaction) {
287
- const isSQLite = db.knex.client.config.client === 'sqlite3';
288
- if (isSQLite) {
279
+ async function addPrimaryKey(tableName, columns, transaction = db.knex) {
280
+ if (DatabaseInfo.isSQLite(transaction)) {
289
281
  const primaryKeyExists = await hasPrimaryKeySQLite(tableName, transaction);
290
282
  if (primaryKeyExists) {
291
283
  logging.warn(`Primary key constraint for: ${columns} already exists for table: ${tableName}`);
@@ -294,7 +286,7 @@ async function addPrimaryKey(tableName, columns, transaction) {
294
286
  }
295
287
  try {
296
288
  logging.info(`Adding primary key constraint for: ${columns} in table ${tableName}`);
297
- return await (transaction || db.knex).schema.table(tableName, function (table) {
289
+ return await transaction.schema.table(tableName, function (table) {
298
290
  table.primary(columns);
299
291
  });
300
292
  } catch (err) {
@@ -307,37 +299,36 @@ async function addPrimaryKey(tableName, columns, transaction) {
307
299
  }
308
300
 
309
301
  /**
310
- * https://github.com/tgriesser/knex/issues/1303
311
- * createTableIfNotExists can throw error if indexes are already in place
302
+ * Adds a table according to the provided spec, or falls back to the current schema
303
+ *
304
+ * NOTE: this function does NOT check if the table already exists - use the migration
305
+ * utils if you want that
306
+ *
307
+ * @param {String} table - name of the table to create
308
+ * @param {import('knex').Transaction} transaction - connection to the DB
309
+ * @param {Object} [tableSpec] - table schema to generate table with
312
310
  */
313
- function createTable(table, transaction, tableSpec = schema[table]) {
314
- return (transaction || db.knex).schema.hasTable(table)
315
- .then(function (exists) {
316
- if (exists) {
317
- return;
318
- }
319
-
320
- return (transaction || db.knex).schema.createTable(table, function (t) {
321
- Object.keys(tableSpec)
322
- .filter(column => !(column.startsWith('@@')))
323
- .forEach(column => addTableColumn(table, t, column, tableSpec[column]));
324
-
325
- if (tableSpec['@@INDEXES@@']) {
326
- tableSpec['@@INDEXES@@'].forEach(index => t.index(index));
327
- }
328
- if (tableSpec['@@UNIQUE_CONSTRAINTS@@']) {
329
- tableSpec['@@UNIQUE_CONSTRAINTS@@'].forEach(unique => t.unique(unique));
330
- }
331
- });
332
- });
311
+ function createTable(table, transaction = db.knex, tableSpec = schema[table]) {
312
+ return transaction.schema.createTable(table, function (t) {
313
+ Object.keys(tableSpec)
314
+ .filter(column => !(column.startsWith('@@')))
315
+ .forEach(column => addTableColumn(table, t, column, tableSpec[column]));
316
+
317
+ if (tableSpec['@@INDEXES@@']) {
318
+ tableSpec['@@INDEXES@@'].forEach(index => t.index(index));
319
+ }
320
+ if (tableSpec['@@UNIQUE_CONSTRAINTS@@']) {
321
+ tableSpec['@@UNIQUE_CONSTRAINTS@@'].forEach(unique => t.unique(unique));
322
+ }
323
+ });
333
324
  }
334
325
 
335
- function deleteTable(table, transaction) {
336
- return (transaction || db.knex).schema.dropTableIfExists(table);
326
+ function deleteTable(table, transaction = db.knex) {
327
+ return transaction.schema.dropTableIfExists(table);
337
328
  }
338
329
 
339
- function getTables(transaction) {
340
- const client = (transaction || db.knex).client.config.client;
330
+ function getTables(transaction = db.knex) {
331
+ const client = transaction.client.config.client;
341
332
 
342
333
  if (_.includes(_.keys(clients), client)) {
343
334
  return clients[client].getTables(transaction);
@@ -346,8 +337,8 @@ function getTables(transaction) {
346
337
  return Promise.reject(tpl(messages.noSupportForDatabase, {client: client}));
347
338
  }
348
339
 
349
- function getIndexes(table, transaction) {
350
- const client = (transaction || db.knex).client.config.client;
340
+ function getIndexes(table, transaction = db.knex) {
341
+ const client = transaction.client.config.client;
351
342
 
352
343
  if (_.includes(_.keys(clients), client)) {
353
344
  return clients[client].getIndexes(table, transaction);
@@ -356,8 +347,8 @@ function getIndexes(table, transaction) {
356
347
  return Promise.reject(tpl(messages.noSupportForDatabase, {client: client}));
357
348
  }
358
349
 
359
- function getColumns(table, transaction) {
360
- const client = (transaction || db.knex).client.config.client;
350
+ function getColumns(table, transaction = db.knex) {
351
+ const client = transaction.client.config.client;
361
352
 
362
353
  if (_.includes(_.keys(clients), client)) {
363
354
  return clients[client].getColumns(table);
@@ -366,10 +357,10 @@ function getColumns(table, transaction) {
366
357
  return Promise.reject(tpl(messages.noSupportForDatabase, {client: client}));
367
358
  }
368
359
 
369
- function checkTables(transaction) {
370
- const client = (transaction || db.knex).client.config.client;
360
+ function checkTables(transaction = db.knex) {
361
+ const client = transaction.client.config.client;
371
362
 
372
- if (client === 'mysql') {
363
+ if (DatabaseInfo.isMySQL(transaction)) {
373
364
  return clients[client].checkPostTable();
374
365
  }
375
366
  }
@@ -0,0 +1,6 @@
1
+ const config = require('../../../../shared/config');
2
+
3
+ const defaultSettingsPath = config.get('paths').defaultSettings;
4
+ const defaultSettings = require(defaultSettingsPath);
5
+
6
+ module.exports = defaultSettings;