ghost 4.19.1 → 4.20.3
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.
- package/.eslintrc.js +9 -8
- package/Gruntfile.js +1 -1
- package/PRIVACY.md +3 -0
- package/content/adapters/README.md +2 -2
- package/core/boot.js +4 -4
- package/core/bridge.js +9 -1
- package/core/built/assets/{chunk.3.0778d8e4d707d2a625f1.js → chunk.3.777d43e2ce954ba8b2f5.js} +1 -1
- package/core/built/assets/codemirror/{codemirror-21a09582262987037db73b152fb35f7c.js → codemirror-d25c379b87ec8b33d54ac7149bc0b6ae.js} +14 -14
- package/core/built/assets/ghost-dark-20e2892d4f30d0d1183c9ac725ea37d0.css +1 -0
- package/core/built/assets/{ghost.min-102753ec485602c8fe80d60a1750bf84.js → ghost.min-07b6a50c54b3e2e190332c28c7255d2f.js} +525 -340
- package/core/built/assets/ghost.min-57e46fd3b1145ecf2cbd185a13611f3b.css +1 -0
- package/core/built/assets/icons/arrow-left-small.svg +0 -4
- package/core/built/assets/img/footer-marketplace-bg-572b6c6486a7e26316954d599eaa9f30.png +0 -0
- package/core/built/assets/img/marketing/offers-1-f2e1b653c4d5bb90eea9d7a2862530f9.jpg +0 -0
- package/core/built/assets/img/marketing/offers-2-28a225d34cc39d133748431536961d00.jpg +0 -0
- package/core/built/assets/img/marketing/offers-3-2094c91ab21a16c37fbe6ec16c140160.jpg +0 -0
- package/core/built/assets/img/themes/Casper-c7e784d7188cc5d7f097d9b6c97b0263.jpg +0 -0
- package/core/built/assets/simplemde/{simplemde-232f69d126310434489071a1891e6d8b.js → simplemde-3ffc0ec9e9fecf29b9a499db678c9e65.js} +14 -14
- package/core/built/assets/{vendor.min-0916203b598271a795909e8e0b1c16c2.js → vendor.min-af502ac4142871500fc424f6a5a254ec.js} +1046 -1043
- package/core/frontend/apps/amp/lib/router.js +1 -1
- package/core/frontend/meta/author-url.js +1 -1
- package/core/frontend/meta/url.js +1 -1
- package/core/{server → frontend}/public/favicon.ico +0 -0
- package/core/{server → frontend}/public/ghost.css +0 -0
- package/core/{server → frontend}/public/ghost.min.css +0 -0
- package/core/{server → frontend}/public/robots.txt +0 -0
- package/core/{server → frontend}/public/sitemap.xsl +0 -0
- package/core/frontend/services/proxy.js +1 -1
- package/core/frontend/services/routing/CollectionRouter.js +3 -49
- package/core/frontend/services/routing/ParentRouter.js +1 -4
- package/core/frontend/services/routing/StaticPagesRouter.js +3 -5
- package/core/frontend/services/routing/StaticRoutesRouter.js +4 -6
- package/core/frontend/services/routing/TaxonomyRouter.js +4 -5
- package/core/frontend/services/routing/controllers/collection.js +2 -2
- package/core/frontend/services/routing/controllers/email-post.js +2 -2
- package/core/frontend/services/routing/controllers/entry.js +2 -2
- package/core/frontend/services/routing/controllers/preview.js +2 -2
- package/core/frontend/services/routing/index.js +6 -12
- package/core/frontend/services/routing/registry.js +13 -0
- package/core/frontend/services/routing/router-manager.js +185 -0
- package/core/frontend/services/rss/generate-feed.js +2 -2
- package/core/frontend/services/theme-engine/i18n/i18n.js +267 -28
- package/core/frontend/services/theme-engine/i18n/index.js +1 -1
- package/core/frontend/services/theme-engine/i18n/theme-i18n.js +73 -0
- package/core/frontend/web/index.js +1 -0
- package/core/{server/web/site → frontend/web}/middleware/handle-image-sizes.js +4 -4
- package/core/{server/web/site → frontend/web}/middleware/index.js +0 -0
- package/core/{server/web/site → frontend/web}/middleware/redirect-ghost-to-admin.js +3 -3
- package/core/{server/web/site → frontend/web}/middleware/serve-favicon.js +6 -6
- package/core/{server/web/site → frontend/web}/middleware/serve-public-file.js +2 -2
- package/core/{server/web/site → frontend/web}/middleware/static-theme.js +3 -3
- package/core/{server/web/site → frontend/web}/routes.js +5 -4
- package/core/{server/web/site/app.js → frontend/web/site.js} +12 -16
- package/core/server/adapters/storage/LocalFileStorage.js +35 -39
- package/core/server/adapters/storage/index.js +12 -2
- package/core/server/api/canary/images.js +1 -1
- package/core/server/api/canary/offers.js +19 -0
- package/core/server/api/canary/utils/serializers/output/settings.js +2 -3
- package/core/server/api/canary/utils/serializers/output/utils/url.js +1 -1
- package/core/server/api/v2/images.js +1 -1
- package/core/server/api/v2/utils/serializers/output/utils/url.js +1 -1
- package/core/server/api/v3/images.js +1 -1
- package/core/server/api/v3/utils/serializers/output/settings.js +2 -3
- package/core/server/api/v3/utils/serializers/output/utils/url.js +1 -1
- package/core/server/data/importer/handlers/image.js +1 -1
- package/core/server/data/importer/importers/image.js +1 -1
- package/core/server/data/migrations/init/1-create-tables.js +7 -8
- package/core/server/data/migrations/init/2-create-fixtures.js +8 -8
- package/core/server/data/migrations/versions/4.20/01-remove-offer-redemptions-table.js +19 -0
- package/core/server/data/migrations/versions/4.20/02-remove-offers-table.js +30 -0
- package/core/server/data/migrations/versions/4.20/03-add-offers-table.js +21 -0
- package/core/server/data/migrations/versions/4.20/04-add-offer-redemptions-table.js +9 -0
- package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +41 -0
- package/core/server/data/schema/fixtures/utils.js +150 -143
- package/core/server/data/schema/schema.js +4 -3
- package/core/server/frontend/ghost.min.css +1 -0
- package/core/server/lib/image/image-size.js +2 -2
- package/core/server/lib/mobiledoc.js +3 -2
- package/core/server/models/action.js +7 -4
- package/core/server/models/base/plugins/overrides.js +19 -6
- package/core/server/models/index.js +4 -46
- package/core/server/models/member.js +5 -0
- package/core/server/models/user.js +2 -1
- package/core/server/overrides.js +6 -2
- package/core/server/services/adapter-manager/config.js +1 -0
- package/core/server/services/adapter-manager/index.js +9 -5
- package/core/server/services/adapter-manager/options-resolver.js +18 -0
- package/core/server/services/bulk-email/mailgun.js +1 -1
- package/core/server/services/mega/post-email-serializer.js +2 -2
- package/core/server/services/members/api.js +1 -3
- package/core/server/services/members/emails/signin.js +1 -1
- package/core/server/services/members/emails/signup.js +1 -1
- package/core/server/services/members/emails/subscribe.js +1 -1
- package/core/server/services/members/service.js +2 -1
- package/core/server/services/offers/service.js +1 -1
- package/core/server/services/route-settings/route-settings.js +1 -1
- package/core/server/services/settings/index.js +3 -1
- package/core/server/services/settings/settings-bread-service.js +42 -20
- package/core/server/services/slack.js +1 -1
- package/core/server/services/themes/activate.js +2 -2
- package/core/server/services/themes/activation-bridge.js +6 -6
- package/core/server/services/themes/storage.js +1 -1
- package/core/{frontend → server}/services/url/Queue.js +0 -0
- package/core/{frontend → server}/services/url/Resource.js +0 -0
- package/core/{frontend → server}/services/url/Resources.js +2 -2
- package/core/{frontend → server}/services/url/UrlGenerator.js +14 -14
- package/core/{frontend → server}/services/url/UrlService.js +12 -15
- package/core/{frontend → server}/services/url/Urls.js +1 -1
- package/core/{frontend → server}/services/url/configs/canary.js +0 -0
- package/core/{frontend → server}/services/url/configs/v2.js +0 -0
- package/core/{frontend → server}/services/url/configs/v3.js +0 -0
- package/core/{frontend → server}/services/url/configs/v4.js +0 -0
- package/core/{frontend → server}/services/url/index.js +0 -0
- package/core/server/services/xmlrpc.js +1 -1
- package/core/server/update-check.js +3 -3
- package/core/server/web/admin/controller.js +11 -0
- package/core/server/web/admin/views/default-prod.html +4 -4
- package/core/server/web/admin/views/default.html +4 -4
- package/core/server/web/api/app.js +8 -9
- package/core/server/web/oauth/app.js +4 -2
- package/core/server/web/parent/backend.js +3 -3
- package/core/server/web/parent/frontend.js +2 -2
- package/core/server/web/shared/middlewares/custom-redirects.js +0 -8
- package/core/server/web/shared/middlewares/maintenance.js +1 -1
- package/core/server/web/well-known.js +10 -10
- package/core/shared/config/overrides.json +1 -1
- package/core/shared/express.js +10 -0
- package/core/shared/html-to-plaintext.js +2 -2
- package/core/shared/labs.js +14 -5
- package/package.json +45 -43
- package/yarn.lock +649 -284
- package/core/built/assets/ghost-dark-da8e8eba130fb52f97494e51850d1045.css +0 -1
- package/core/built/assets/ghost.min-0d8f19623e9f077351bce453034daf4d.css +0 -1
- package/core/frontend/services/routing/bootstrap.js +0 -134
- package/core/server/public/404-ghost.png +0 -0
- package/core/server/public/404-ghost@2x.png +0 -0
- package/core/server/web/site/index.js +0 -1
- package/core/shared/i18n/i18n.js +0 -312
- package/core/shared/i18n/index.js +0 -6
|
@@ -29,27 +29,25 @@ class LocalFileStore extends StorageBase {
|
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Saves a buffer in the targetPath
|
|
32
|
-
*
|
|
33
|
-
*
|
|
32
|
+
* @param {Buffer} buffer is an instance of Buffer
|
|
33
|
+
* @param {String} targetPath path to which the buffer should be written
|
|
34
|
+
* @returns {Promise<String>} a URL to retrieve the data
|
|
34
35
|
*/
|
|
35
|
-
saveRaw(buffer, targetPath) {
|
|
36
|
+
async saveRaw(buffer, targetPath) {
|
|
36
37
|
const storagePath = path.join(this.storagePath, targetPath);
|
|
37
38
|
const targetDir = path.dirname(storagePath);
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return fullUrl;
|
|
52
|
-
});
|
|
40
|
+
await fs.mkdirs(targetDir);
|
|
41
|
+
await fs.writeFile(storagePath, buffer);
|
|
42
|
+
|
|
43
|
+
// For local file system storage can use relative path so add a slash
|
|
44
|
+
const fullUrl = (
|
|
45
|
+
urlUtils.urlJoin('/', urlUtils.getSubdir(),
|
|
46
|
+
urlUtils.STATIC_IMAGE_URL_PREFIX,
|
|
47
|
+
targetPath)
|
|
48
|
+
).replace(new RegExp(`\\${path.sep}`, 'g'), '/');
|
|
49
|
+
|
|
50
|
+
return fullUrl;
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
/**
|
|
@@ -57,34 +55,32 @@ class LocalFileStore extends StorageBase {
|
|
|
57
55
|
* - image is the express image object
|
|
58
56
|
* - returns a promise which ultimately returns the full url to the uploaded image
|
|
59
57
|
*
|
|
60
|
-
* @param image
|
|
61
|
-
* @param targetDir
|
|
62
|
-
* @returns {
|
|
58
|
+
* @param {StorageBase.Image} image
|
|
59
|
+
* @param {String} targetDir
|
|
60
|
+
* @returns {Promise<String>}
|
|
63
61
|
*/
|
|
64
|
-
save(image, targetDir) {
|
|
62
|
+
async save(image, targetDir) {
|
|
65
63
|
let targetFilename;
|
|
66
64
|
|
|
67
65
|
// NOTE: the base implementation of `getTargetDir` returns the format this.storagePath/YYYY/MM
|
|
68
66
|
targetDir = targetDir || this.getTargetDir(this.storagePath);
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return Promise.reject(e);
|
|
87
|
-
});
|
|
68
|
+
const filename = await this.getUniqueFileName(image, targetDir);
|
|
69
|
+
|
|
70
|
+
targetFilename = filename;
|
|
71
|
+
await fs.mkdirs(targetDir);
|
|
72
|
+
|
|
73
|
+
await fs.copy(image.path, targetFilename);
|
|
74
|
+
|
|
75
|
+
// The src for the image must be in URI format, not a file system path, which in Windows uses \
|
|
76
|
+
// For local file system storage can use relative path so add a slash
|
|
77
|
+
const fullUrl = (
|
|
78
|
+
urlUtils.urlJoin('/', urlUtils.getSubdir(),
|
|
79
|
+
urlUtils.STATIC_IMAGE_URL_PREFIX,
|
|
80
|
+
path.relative(this.storagePath, targetFilename))
|
|
81
|
+
).replace(new RegExp(`\\${path.sep}`, 'g'), '/');
|
|
82
|
+
|
|
83
|
+
return fullUrl;
|
|
88
84
|
}
|
|
89
85
|
|
|
90
86
|
exists(fileName, targetDir) {
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
const adapterManager = require('../../services/adapter-manager');
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* @param {'images'|'videos'|'audios'} [feature] - name for the "feature" to enable through adapter, e.g.: images or videos storage
|
|
5
|
+
* @returns {Object} adapter instance
|
|
6
|
+
*/
|
|
7
|
+
function getStorage(feature) {
|
|
8
|
+
let adapterName = 'storage';
|
|
9
|
+
|
|
10
|
+
if (feature) {
|
|
11
|
+
adapterName += `:${feature}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return adapterManager.getAdapter(adapterName);
|
|
5
15
|
}
|
|
6
16
|
|
|
7
17
|
module.exports.getStorage = getStorage;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
const tpl = require('@tryghost/tpl');
|
|
2
|
+
const errors = require('@tryghost/errors');
|
|
1
3
|
const offersService = require('../../services/offers');
|
|
2
4
|
|
|
5
|
+
const messages = {
|
|
6
|
+
offerNotFound: 'Offer not found.'
|
|
7
|
+
};
|
|
8
|
+
|
|
3
9
|
module.exports = {
|
|
4
10
|
docName: 'offers',
|
|
5
11
|
|
|
@@ -21,6 +27,12 @@ module.exports = {
|
|
|
21
27
|
permissions: true,
|
|
22
28
|
async query(frame) {
|
|
23
29
|
const offer = await offersService.api.getOffer(frame.data);
|
|
30
|
+
if (!offer) {
|
|
31
|
+
throw new errors.NotFoundError({
|
|
32
|
+
message: tpl(messages.offerNotFound)
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
frame.response = {
|
|
25
37
|
offers: [offer]
|
|
26
38
|
};
|
|
@@ -38,6 +50,13 @@ module.exports = {
|
|
|
38
50
|
...frame.data.offers[0],
|
|
39
51
|
id: frame.options.id
|
|
40
52
|
});
|
|
53
|
+
|
|
54
|
+
if (!offer) {
|
|
55
|
+
throw new errors.NotFoundError({
|
|
56
|
+
message: tpl(messages.offerNotFound)
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
41
60
|
frame.response = {
|
|
42
61
|
offers: [offer]
|
|
43
62
|
};
|
|
@@ -55,9 +55,8 @@ module.exports = {
|
|
|
55
55
|
this.browse(...arguments);
|
|
56
56
|
},
|
|
57
57
|
|
|
58
|
-
edit(
|
|
59
|
-
|
|
60
|
-
this.browse(settingsKeyedJSON, apiConfig, frame);
|
|
58
|
+
edit() {
|
|
59
|
+
this.browse(...arguments);
|
|
61
60
|
},
|
|
62
61
|
|
|
63
62
|
download(bytes, apiConfig, frame) {
|
|
@@ -55,9 +55,8 @@ module.exports = {
|
|
|
55
55
|
this.browse(...arguments);
|
|
56
56
|
},
|
|
57
57
|
|
|
58
|
-
edit(
|
|
59
|
-
|
|
60
|
-
this.browse(settingsKeyedJSON, apiConfig, frame);
|
|
58
|
+
edit() {
|
|
59
|
+
this.browse(...arguments);
|
|
61
60
|
},
|
|
62
61
|
|
|
63
62
|
download(bytes, apiConfig, frame) {
|
|
@@ -13,7 +13,7 @@ ImageHandler = {
|
|
|
13
13
|
directories: ['images', 'content'],
|
|
14
14
|
|
|
15
15
|
loadFile: function (files, baseDir) {
|
|
16
|
-
const store = storage.getStorage();
|
|
16
|
+
const store = storage.getStorage('images');
|
|
17
17
|
const baseDirRegex = baseDir ? new RegExp('^' + baseDir + '/') : new RegExp('');
|
|
18
18
|
|
|
19
19
|
const imageFolderRegexes = _.map(urlUtils.STATIC_IMAGE_URL_PREFIX.split('/'), function (dir) {
|
|
@@ -64,7 +64,7 @@ ImageImporter = {
|
|
|
64
64
|
return importData;
|
|
65
65
|
},
|
|
66
66
|
doImport: function (imageData) {
|
|
67
|
-
const store = storage.getStorage();
|
|
67
|
+
const store = storage.getStorage('images');
|
|
68
68
|
|
|
69
69
|
return Promise.map(imageData, function (image) {
|
|
70
70
|
return store.save(image, image.targetDir).then(function (result) {
|
|
@@ -4,27 +4,26 @@ const schema = require('../../schema').tables;
|
|
|
4
4
|
const logging = require('@tryghost/logging');
|
|
5
5
|
const schemaTables = Object.keys(schema);
|
|
6
6
|
|
|
7
|
-
module.exports.up =
|
|
7
|
+
module.exports.up = async (options) => {
|
|
8
8
|
const connection = options.connection;
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
await Promise.mapSeries(schemaTables, async (table) => {
|
|
11
11
|
logging.info('Creating table: ' + table);
|
|
12
|
-
|
|
12
|
+
await commands.createTable(table, connection);
|
|
13
13
|
});
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
17
|
@TODO: This works, but is very dangerous in the current state of the knex-migrator v3.
|
|
19
|
-
@TODO:
|
|
20
|
-
module.exports.down =
|
|
18
|
+
@TODO: Decide if we should enable or delete this
|
|
19
|
+
module.exports.down = async (options) => {
|
|
21
20
|
var connection = options.connection;
|
|
22
21
|
|
|
23
22
|
// Reference between tables!
|
|
24
23
|
schemaTables.reverse();
|
|
25
|
-
|
|
24
|
+
await Promise.mapSeries(schemaTables, async (table) => {
|
|
26
25
|
logging.info('Drop table: ' + table);
|
|
27
|
-
|
|
26
|
+
await commands.deleteTable(table, connection);
|
|
28
27
|
});
|
|
29
28
|
};
|
|
30
29
|
*/
|
|
@@ -7,20 +7,20 @@ module.exports.config = {
|
|
|
7
7
|
transaction: true
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
module.exports.up =
|
|
10
|
+
module.exports.up = async (options) => {
|
|
11
11
|
const localOptions = _.merge({
|
|
12
12
|
context: {internal: true},
|
|
13
13
|
migrating: true
|
|
14
14
|
}, options);
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
await Promise.mapSeries(fixtures.models, async (model) => {
|
|
17
17
|
logging.info('Model: ' + model.name);
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
await fixtures.utils.addFixturesForModel(model, localOptions);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
await Promise.mapSeries(fixtures.relations, async (relation) => {
|
|
23
|
+
logging.info('Relation: ' + relation.from.model + ' to ' + relation.to.model);
|
|
24
|
+
await fixtures.utils.addFixturesForRelation(relation, localOptions);
|
|
25
25
|
});
|
|
26
26
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const utils = require('../../utils');
|
|
2
|
+
|
|
3
|
+
const migration = utils.addTable('offer_redemptions', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
offer_id: {type: 'string', maxlength: 24, nullable: false, references: 'offers.id', cascadeDelete: true},
|
|
6
|
+
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
7
|
+
subscription_id: {type: 'string', maxlength: 24, nullable: false, references: 'members_stripe_customers_subscriptions.id', cascadeDelete: true}
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// This reverses an "addTable" migration so that we
|
|
11
|
+
// drop the table going forwards and re-add it going back
|
|
12
|
+
const up = migration.down;
|
|
13
|
+
const down = migration.up;
|
|
14
|
+
|
|
15
|
+
migration.up = up;
|
|
16
|
+
migration.down = down;
|
|
17
|
+
|
|
18
|
+
module.exports = migration;
|
|
19
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const utils = require('../../utils');
|
|
2
|
+
|
|
3
|
+
const migration = utils.addTable('offers', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
active: {type: 'boolean', nullable: false, defaultTo: true},
|
|
6
|
+
name: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
7
|
+
code: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
8
|
+
product_id: {type: 'string', maxlength: 24, nullable: false, references: 'products.id'},
|
|
9
|
+
stripe_coupon_id: {type: 'string', maxlength: 255, nullable: false, unique: true},
|
|
10
|
+
interval: {type: 'string', maxlength: 50, nullable: false},
|
|
11
|
+
currency: {type: 'string', maxlength: 50, nullable: true},
|
|
12
|
+
discount_type: {type: 'string', maxlength: 50, nullable: false},
|
|
13
|
+
discount_amount: {type: 'integer', nullable: false},
|
|
14
|
+
duration: {type: 'string', maxlength: 50, nullable: false},
|
|
15
|
+
duration_in_months: {type: 'integer', nullable: true},
|
|
16
|
+
portal_title: {type: 'string', maxlength: 191, nullable: false},
|
|
17
|
+
portal_description: {type: 'string', maxlength: 2000, nullable: true},
|
|
18
|
+
created_at: {type: 'dateTime', nullable: false},
|
|
19
|
+
updated_at: {type: 'dateTime', nullable: true}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// This reverses an "addTable" migration so that we
|
|
23
|
+
// drop the table going forwards and re-add it going back
|
|
24
|
+
const up = migration.down;
|
|
25
|
+
const down = migration.up;
|
|
26
|
+
|
|
27
|
+
migration.up = up;
|
|
28
|
+
migration.down = down;
|
|
29
|
+
|
|
30
|
+
module.exports = migration;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const utils = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = utils.addTable('offers', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
active: {type: 'boolean', nullable: false, defaultTo: true},
|
|
6
|
+
name: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
7
|
+
code: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
8
|
+
product_id: {type: 'string', maxlength: 24, nullable: false, references: 'products.id'},
|
|
9
|
+
stripe_coupon_id: {type: 'string', maxlength: 255, nullable: true, unique: true},
|
|
10
|
+
interval: {type: 'string', maxlength: 50, nullable: false},
|
|
11
|
+
currency: {type: 'string', maxlength: 50, nullable: true},
|
|
12
|
+
discount_type: {type: 'string', maxlength: 50, nullable: false},
|
|
13
|
+
discount_amount: {type: 'integer', nullable: false},
|
|
14
|
+
duration: {type: 'string', maxlength: 50, nullable: false},
|
|
15
|
+
duration_in_months: {type: 'integer', nullable: true},
|
|
16
|
+
portal_title: {type: 'string', maxlength: 191, nullable: false},
|
|
17
|
+
portal_description: {type: 'string', maxlength: 2000, nullable: true},
|
|
18
|
+
created_at: {type: 'dateTime', nullable: false},
|
|
19
|
+
updated_at: {type: 'dateTime', nullable: true}
|
|
20
|
+
});
|
|
21
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const {addTable} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = addTable('offer_redemptions', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
offer_id: {type: 'string', maxlength: 24, nullable: false, references: 'offers.id', cascadeDelete: true},
|
|
6
|
+
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
7
|
+
subscription_id: {type: 'string', maxlength: 24, nullable: false, references: 'members_stripe_customers_subscriptions.id', cascadeDelete: true},
|
|
8
|
+
created_at: {type: 'dateTime', nullable: false}
|
|
9
|
+
});
|
package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
const {createNonTransactionalMigration} = require('../../utils');
|
|
3
|
+
const {addUnique} = require('../../../schema/commands');
|
|
4
|
+
|
|
5
|
+
module.exports = createNonTransactionalMigration(
|
|
6
|
+
async function up(knex) {
|
|
7
|
+
logging.info('Dropping NOT NULL constraint for: portal_title in table: offers');
|
|
8
|
+
|
|
9
|
+
await knex.schema.table('offers', function (table) {
|
|
10
|
+
table.dropColumn('portal_title');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
await knex.schema.table('offers', function (table) {
|
|
14
|
+
table.string('portal_title', 191).nullable();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
if (knex.client.config.client === 'sqlite3') {
|
|
18
|
+
for (const column of ['name', 'code', 'stripe_coupon_id']) {
|
|
19
|
+
await addUnique('offers', column, knex);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
async function down(knex) {
|
|
24
|
+
logging.info('Adding NOT NULL constraint for: portal_title in table: offers');
|
|
25
|
+
|
|
26
|
+
await knex.schema.table('offers', function (table) {
|
|
27
|
+
table.dropColumn('portal_title');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await knex.schema.table('offers', function (table) {
|
|
31
|
+
table.string('portal_title', 191).notNullable();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (knex.client.config.client === 'sqlite3') {
|
|
35
|
+
for (const column of ['name', 'code', 'stripe_coupon_id']) {
|
|
36
|
+
await addUnique('offers', column, knex);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
|