ghost 4.21.0 → 4.22.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 +6 -0
- package/Gruntfile.js +2 -0
- package/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/built/screen.css.map +1 -1
- package/content/themes/casper/assets/css/screen.css +263 -50
- package/content/themes/casper/default.hbs +12 -3
- package/content/themes/casper/index.hbs +25 -23
- package/content/themes/casper/package.json +91 -2
- package/content/themes/casper/partials/post-card.hbs +1 -1
- package/content/themes/casper/post.hbs +18 -14
- package/content/themes/casper/yarn.lock +245 -192
- package/core/boot.js +8 -0
- package/core/bridge.js +14 -0
- package/core/built/assets/{chunk.3.065ee3c3bdf674bd81a4.js → chunk.3.324fd0cc598c73650219.js} +59 -59
- package/core/built/assets/{ghost-dark-1328db4a7dd128305646305a8731bcfe.css → ghost-dark-39fb496d051565531062d7e047d1c0b1.css} +1 -1
- package/core/built/assets/{ghost.min-5abc69c04ad1d5301a857e01009b9c05.css → ghost.min-4207edfc1ae0a3f9f6505ca00d20b0c0.css} +1 -1
- package/core/built/assets/{ghost.min-6c546c322127ae6d1d1b0ddbf34be75b.js → ghost.min-7da921f6c6cac3fe10da1ba104575440.js} +1775 -1897
- package/core/built/assets/{vendor.min-c6ef90bfd7eff256e10b85583bfe9a74.js → vendor.min-413f887176a041e6dbf88214ca9a7481.js} +6849 -6688
- package/core/frontend/helpers/asset.js +9 -1
- package/core/frontend/helpers/ghost_head.js +13 -1
- package/core/frontend/services/card-assets/index.js +16 -0
- package/core/frontend/services/card-assets/service.js +109 -0
- package/core/frontend/services/theme-engine/config/defaults.json +4 -1
- package/core/frontend/services/theme-engine/config/index.js +1 -1
- package/core/frontend/src/cards/css/bookmark.css +83 -0
- package/core/frontend/src/cards/css/button.css +30 -0
- package/core/frontend/src/cards/css/callout.css +12 -0
- package/core/frontend/src/cards/css/gallery.css +36 -0
- package/core/frontend/src/cards/css/nft.css +85 -0
- package/core/frontend/src/cards/js/gallery.js +8 -0
- package/core/frontend/web/middleware/serve-public-file.js +10 -1
- package/core/frontend/web/routes.js +0 -1
- package/core/frontend/web/site.js +13 -9
- package/core/server/adapters/storage/LocalFilesStorage.js +17 -0
- package/core/server/adapters/storage/LocalImagesStorage.js +51 -0
- package/core/server/adapters/storage/LocalMediaStorage.js +24 -0
- package/core/server/adapters/storage/{LocalFileStorage.js → LocalStorageBase.js} +64 -51
- package/core/server/adapters/storage/index.js +1 -1
- package/core/server/adapters/storage/utils.js +2 -2
- package/core/server/api/canary/files.js +19 -0
- package/core/server/api/canary/index.js +8 -0
- package/core/server/api/canary/media.js +42 -0
- package/core/server/api/canary/oembed.js +3 -0
- package/core/server/api/canary/redirects.js +1 -6
- package/core/server/api/canary/utils/serializers/input/index.js +4 -0
- package/core/server/api/canary/utils/serializers/input/media.js +8 -0
- package/core/server/api/canary/utils/serializers/input/pages.js +8 -0
- package/core/server/api/canary/utils/serializers/output/config.js +21 -14
- package/core/server/api/canary/utils/serializers/output/files.js +27 -0
- package/core/server/api/canary/utils/serializers/output/index.js +8 -0
- package/core/server/api/canary/utils/serializers/output/media.js +37 -0
- package/core/server/api/canary/utils/validators/input/files.js +7 -0
- package/core/server/api/canary/utils/validators/input/index.js +8 -0
- package/core/server/api/canary/utils/validators/input/media.js +11 -0
- package/core/server/api/v2/redirects.js +1 -6
- package/core/server/api/v3/members.js +5 -1
- package/core/server/api/v3/redirects.js +1 -6
- package/core/server/data/migrations/utils.js +55 -16
- package/core/server/data/migrations/versions/4.22/01-add-is-launch-complete-setting.js +8 -0
- package/core/server/data/migrations/versions/4.22/02-update-launch-complete-setting-from-user-data.js +39 -0
- package/core/server/data/schema/default-settings.json +8 -0
- package/core/server/frontend/ghost.min.css +1 -1
- package/core/server/lib/image/blog-icon.js +2 -4
- package/core/server/lib/image/image-size.js +1 -1
- package/core/server/services/limits.js +3 -6
- package/core/server/services/mega/template.js +62 -1
- package/core/server/services/nft-oembed.js +71 -0
- package/core/server/services/oembed.js +145 -110
- package/core/server/services/offers/service.js +1 -31
- package/core/server/services/public-config/config.js +2 -1
- package/core/server/services/redirects/api.js +270 -0
- package/core/server/services/redirects/index.js +27 -12
- package/core/server/services/stripe/index.js +4 -2
- package/core/server/services/themes/ThemeStorage.js +5 -5
- package/core/server/services/url/Resource.js +1 -1
- package/core/server/services/url/Resources.js +28 -21
- package/core/server/services/url/UrlService.js +66 -8
- package/core/server/services/url/Urls.js +7 -2
- package/core/server/services/url/index.js +8 -1
- 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/canary/admin/routes.js +28 -4
- package/core/server/web/api/middleware/cors.js +7 -7
- package/core/server/web/api/middleware/upload.js +117 -10
- package/core/server/web/members/app.js +1 -1
- package/core/server/web/shared/middlewares/index.js +0 -4
- package/core/shared/config/defaults.json +5 -1
- package/core/shared/config/helpers.js +4 -0
- package/core/shared/config/overrides.json +8 -0
- package/core/shared/labs.js +12 -3
- package/package.json +28 -27
- package/urls.json +597 -0
- package/yarn.lock +972 -941
- package/core/built/assets/img/themes/Editorial-a25a4a34c04dedd858bd5e05ef388b1c.jpg +0 -0
- package/core/built/assets/img/themes/Massively-06edf00108429f7fb8e65f190fba34fe.jpg +0 -0
- package/core/server/services/redirects/settings.js +0 -234
- package/core/server/web/shared/middlewares/custom-redirects.js +0 -128
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
// # Local File
|
|
2
|
-
// The (default) module for storing
|
|
1
|
+
// # Local File Base Storage module
|
|
2
|
+
// The (default) module for storing files using the local file system
|
|
3
3
|
const serveStatic = require('../../../shared/express').static;
|
|
4
4
|
|
|
5
5
|
const fs = require('fs-extra');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const Promise = require('bluebird');
|
|
8
8
|
const moment = require('moment');
|
|
9
|
-
const config = require('../../../shared/config');
|
|
10
9
|
const tpl = require('@tryghost/tpl');
|
|
11
10
|
const logging = require('@tryghost/logging');
|
|
12
11
|
const errors = require('@tryghost/errors');
|
|
@@ -15,74 +14,87 @@ const urlUtils = require('../../../shared/url-utils');
|
|
|
15
14
|
const StorageBase = require('ghost-storage-base');
|
|
16
15
|
|
|
17
16
|
const messages = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
notFound: 'File not found',
|
|
18
|
+
notFoundWithRef: 'File not found: {file}',
|
|
19
|
+
cannotRead: 'Could not read file: {file}',
|
|
20
|
+
invalidUrlParameter: `The URL "{url}" is not a valid URL for this site.`
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
class
|
|
24
|
-
constructor() {
|
|
25
|
-
super();
|
|
26
|
-
|
|
27
|
-
this.storagePath = config.getContentPath('images');
|
|
28
|
-
}
|
|
29
|
-
|
|
23
|
+
class LocalStorageBase extends StorageBase {
|
|
30
24
|
/**
|
|
31
|
-
*
|
|
32
|
-
* @param {
|
|
33
|
-
* @param {String}
|
|
34
|
-
* @
|
|
25
|
+
*
|
|
26
|
+
* @param {Object} options
|
|
27
|
+
* @param {String} options.storagePath
|
|
28
|
+
* @param {String} options.siteUrl
|
|
29
|
+
* @param {String} [options.staticFileURLPrefix]
|
|
30
|
+
* @param {Object} [options.errorMessages]
|
|
31
|
+
* @param {String} [options.errorMessages.notFound]
|
|
32
|
+
* @param {String} [options.errorMessages.notFoundWithRef]
|
|
33
|
+
* @param {String} [options.errorMessages.cannotRead]
|
|
35
34
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const targetDir = path.dirname(storagePath);
|
|
39
|
-
|
|
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'), '/');
|
|
35
|
+
constructor({storagePath, staticFileURLPrefix, siteUrl, errorMessages}) {
|
|
36
|
+
super();
|
|
49
37
|
|
|
50
|
-
|
|
38
|
+
this.storagePath = storagePath;
|
|
39
|
+
this.staticFileURLPrefix = staticFileURLPrefix;
|
|
40
|
+
this.siteUrl = siteUrl;
|
|
41
|
+
this.staticFileUrl = `${siteUrl}${staticFileURLPrefix}`;
|
|
42
|
+
this.errorMessages = errorMessages || messages;
|
|
51
43
|
}
|
|
52
44
|
|
|
53
45
|
/**
|
|
54
|
-
* Saves the
|
|
55
|
-
* -
|
|
56
|
-
* - returns a promise which ultimately returns the full url to the uploaded image
|
|
46
|
+
* Saves the file to storage (the file system)
|
|
47
|
+
* - returns a promise which ultimately returns the full url to the uploaded file
|
|
57
48
|
*
|
|
58
|
-
* @param {StorageBase.Image}
|
|
49
|
+
* @param {StorageBase.Image} file
|
|
59
50
|
* @param {String} targetDir
|
|
60
51
|
* @returns {Promise<String>}
|
|
61
52
|
*/
|
|
62
|
-
async save(
|
|
53
|
+
async save(file, targetDir) {
|
|
63
54
|
let targetFilename;
|
|
64
55
|
|
|
65
56
|
// NOTE: the base implementation of `getTargetDir` returns the format this.storagePath/YYYY/MM
|
|
66
57
|
targetDir = targetDir || this.getTargetDir(this.storagePath);
|
|
67
58
|
|
|
68
|
-
const filename = await this.getUniqueFileName(
|
|
59
|
+
const filename = await this.getUniqueFileName(file, targetDir);
|
|
69
60
|
|
|
70
61
|
targetFilename = filename;
|
|
71
62
|
await fs.mkdirs(targetDir);
|
|
72
63
|
|
|
73
|
-
await fs.copy(
|
|
64
|
+
await fs.copy(file.path, targetFilename);
|
|
74
65
|
|
|
75
66
|
// The src for the image must be in URI format, not a file system path, which in Windows uses \
|
|
76
67
|
// For local file system storage can use relative path so add a slash
|
|
77
68
|
const fullUrl = (
|
|
78
|
-
urlUtils.urlJoin('/',
|
|
79
|
-
urlUtils.
|
|
69
|
+
urlUtils.urlJoin('/',
|
|
70
|
+
urlUtils.getSubdir(),
|
|
71
|
+
this.staticFileURLPrefix,
|
|
80
72
|
path.relative(this.storagePath, targetFilename))
|
|
81
73
|
).replace(new RegExp(`\\${path.sep}`, 'g'), '/');
|
|
82
74
|
|
|
83
75
|
return fullUrl;
|
|
84
76
|
}
|
|
85
77
|
|
|
78
|
+
/**
|
|
79
|
+
*
|
|
80
|
+
* @param {String} url full url under which the stored content is served, result of save method
|
|
81
|
+
* @returns {String} path under which the content is stored
|
|
82
|
+
*/
|
|
83
|
+
urlToPath(url) {
|
|
84
|
+
let filePath;
|
|
85
|
+
|
|
86
|
+
if (url.match(this.staticFileUrl)) {
|
|
87
|
+
filePath = url.replace(this.staticFileUrl, '');
|
|
88
|
+
filePath = path.join(this.storagePath, filePath);
|
|
89
|
+
} else {
|
|
90
|
+
throw new errors.IncorrectUsageError({
|
|
91
|
+
message: tpl(messages.invalidUrlParameter, {url})
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return filePath;
|
|
96
|
+
}
|
|
97
|
+
|
|
86
98
|
exists(fileName, targetDir) {
|
|
87
99
|
const filePath = path.join(targetDir || this.storagePath, fileName);
|
|
88
100
|
|
|
@@ -103,7 +115,7 @@ class LocalFileStore extends StorageBase {
|
|
|
103
115
|
* @returns {serveStaticContent}
|
|
104
116
|
*/
|
|
105
117
|
serve() {
|
|
106
|
-
const {storagePath} = this;
|
|
118
|
+
const {storagePath, errorMessages} = this;
|
|
107
119
|
|
|
108
120
|
return function serveStaticContent(req, res, next) {
|
|
109
121
|
const startedAtMoment = moment();
|
|
@@ -114,14 +126,14 @@ class LocalFileStore extends StorageBase {
|
|
|
114
126
|
maxAge: constants.ONE_YEAR_MS,
|
|
115
127
|
fallthrough: false,
|
|
116
128
|
onEnd: () => {
|
|
117
|
-
logging.info('
|
|
129
|
+
logging.info('LocalStorageBase.serve', req.path, moment().diff(startedAtMoment, 'ms') + 'ms');
|
|
118
130
|
}
|
|
119
131
|
}
|
|
120
132
|
)(req, res, (err) => {
|
|
121
133
|
if (err) {
|
|
122
134
|
if (err.statusCode === 404) {
|
|
123
135
|
return next(new errors.NotFoundError({
|
|
124
|
-
message: tpl(
|
|
136
|
+
message: tpl(errorMessages.notFound),
|
|
125
137
|
code: 'STATIC_FILE_NOT_FOUND',
|
|
126
138
|
property: err.path
|
|
127
139
|
}));
|
|
@@ -144,16 +156,17 @@ class LocalFileStore extends StorageBase {
|
|
|
144
156
|
}
|
|
145
157
|
|
|
146
158
|
/**
|
|
147
|
-
*
|
|
159
|
+
* @param {String} filePath
|
|
148
160
|
* @returns {Promise.<*>}
|
|
149
161
|
*/
|
|
150
|
-
delete() {
|
|
151
|
-
|
|
162
|
+
async delete(fileName, targetDir) {
|
|
163
|
+
const filePath = path.join(targetDir, fileName);
|
|
164
|
+
return await fs.remove(filePath);
|
|
152
165
|
}
|
|
153
166
|
|
|
154
167
|
/**
|
|
155
|
-
* Reads bytes from disk for a target
|
|
156
|
-
* - path of target
|
|
168
|
+
* Reads bytes from disk for a target file
|
|
169
|
+
* - path of target file (without content path!)
|
|
157
170
|
*
|
|
158
171
|
* @param options
|
|
159
172
|
*/
|
|
@@ -171,7 +184,7 @@ class LocalFileStore extends StorageBase {
|
|
|
171
184
|
if (err.code === 'ENOENT' || err.code === 'ENOTDIR') {
|
|
172
185
|
return reject(new errors.NotFoundError({
|
|
173
186
|
err: err,
|
|
174
|
-
message: tpl(
|
|
187
|
+
message: tpl(this.errorMessages.notFoundWithRef, {file: options.path})
|
|
175
188
|
}));
|
|
176
189
|
}
|
|
177
190
|
|
|
@@ -185,7 +198,7 @@ class LocalFileStore extends StorageBase {
|
|
|
185
198
|
|
|
186
199
|
return reject(new errors.GhostError({
|
|
187
200
|
err: err,
|
|
188
|
-
message: tpl(
|
|
201
|
+
message: tpl(this.errorMessages.cannotRead, {file: options.path})
|
|
189
202
|
}));
|
|
190
203
|
}
|
|
191
204
|
|
|
@@ -195,4 +208,4 @@ class LocalFileStore extends StorageBase {
|
|
|
195
208
|
}
|
|
196
209
|
}
|
|
197
210
|
|
|
198
|
-
module.exports =
|
|
211
|
+
module.exports = LocalStorageBase;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const adapterManager = require('../../services/adapter-manager');
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @param {'images'|'
|
|
4
|
+
* @param {'images'|'media'|'files'} [feature] - name for the "feature" to enable through adapter, e.g.: images or media storage
|
|
5
5
|
* @returns {Object} adapter instance
|
|
6
6
|
*/
|
|
7
7
|
function getStorage(feature) {
|
|
@@ -12,7 +12,7 @@ const urlUtils = require('../../../shared/url-utils');
|
|
|
12
12
|
* @description Takes a url or filepath and returns a filepath with is readable
|
|
13
13
|
* for the local file storage.
|
|
14
14
|
*/
|
|
15
|
-
exports.
|
|
15
|
+
exports.getLocalImagesStoragePath = function getLocalImagesStoragePath(imagePath) {
|
|
16
16
|
// The '/' in urlJoin is necessary to add the '/' to `content/images`, if no subdirectory is setup
|
|
17
17
|
const urlRegExp = new RegExp(`^${urlUtils.urlJoin(
|
|
18
18
|
urlUtils.urlFor('home', true),
|
|
@@ -43,7 +43,7 @@ exports.getLocalFileStoragePath = function getLocalFileStoragePath(imagePath) {
|
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
45
|
exports.isLocalImage = function isLocalImage(imagePath) {
|
|
46
|
-
const localImagePath = this.
|
|
46
|
+
const localImagePath = this.getLocalImagesStoragePath(imagePath);
|
|
47
47
|
|
|
48
48
|
if (localImagePath !== imagePath) {
|
|
49
49
|
return true;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const storage = require('../../adapters/storage');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
docName: 'files',
|
|
5
|
+
upload: {
|
|
6
|
+
statusCode: 201,
|
|
7
|
+
permissions: false,
|
|
8
|
+
async query(frame) {
|
|
9
|
+
const filePath = await storage.getStorage('files').save({
|
|
10
|
+
name: frame.file.originalname,
|
|
11
|
+
path: frame.file.path
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
filePath
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -105,6 +105,14 @@ module.exports = {
|
|
|
105
105
|
return shared.pipeline(require('./images'), localUtils);
|
|
106
106
|
},
|
|
107
107
|
|
|
108
|
+
get media() {
|
|
109
|
+
return shared.pipeline(require('./media'), localUtils);
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
get files() {
|
|
113
|
+
return shared.pipeline(require('./files'), localUtils);
|
|
114
|
+
},
|
|
115
|
+
|
|
108
116
|
get tags() {
|
|
109
117
|
return shared.pipeline(require('./tags'), localUtils);
|
|
110
118
|
},
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const storage = require('../../adapters/storage');
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
docName: 'media',
|
|
6
|
+
upload: {
|
|
7
|
+
statusCode: 201,
|
|
8
|
+
permissions: false,
|
|
9
|
+
async query(frame) {
|
|
10
|
+
let thumbnailPath = null;
|
|
11
|
+
if (frame.files.thumbnail && frame.files.thumbnail[0]) {
|
|
12
|
+
thumbnailPath = await storage.getStorage('media').save(frame.files.thumbnail[0]);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const filePath = await storage.getStorage('media').save(frame.files.file[0]);
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
filePath,
|
|
19
|
+
thumbnailPath
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
uploadThumbnail: {
|
|
25
|
+
permissions: false,
|
|
26
|
+
options: [
|
|
27
|
+
'url'
|
|
28
|
+
],
|
|
29
|
+
async query(frame) {
|
|
30
|
+
const mediaStorage = storage.getStorage('media');
|
|
31
|
+
const targetDir = path.dirname(mediaStorage.urlToPath(frame.data.url));
|
|
32
|
+
|
|
33
|
+
// NOTE: need to cleanup otherwise the parent media name won't match thumb name
|
|
34
|
+
// due to "unique name" generation during save
|
|
35
|
+
if (mediaStorage.exists(frame.file.name, targetDir)) {
|
|
36
|
+
await mediaStorage.delete(frame.file.name, targetDir);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return await mediaStorage.save(frame.file, targetDir);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -3,6 +3,9 @@ const externalRequest = require('../../lib/request-external');
|
|
|
3
3
|
|
|
4
4
|
const OEmbed = require('../../services/oembed');
|
|
5
5
|
const oembed = new OEmbed({config, externalRequest});
|
|
6
|
+
const NFT = require('../../services/nft-oembed');
|
|
7
|
+
const nft = new NFT();
|
|
8
|
+
oembed.registerProvider(nft);
|
|
6
9
|
|
|
7
10
|
module.exports = {
|
|
8
11
|
docName: 'oembed',
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
|
|
3
|
-
const web = require('../../web');
|
|
4
3
|
const redirects = require('../../services/redirects');
|
|
5
4
|
|
|
6
5
|
module.exports = {
|
|
@@ -42,11 +41,7 @@ module.exports = {
|
|
|
42
41
|
cacheInvalidate: true
|
|
43
42
|
},
|
|
44
43
|
query(frame) {
|
|
45
|
-
return redirects.api.setFromFilePath(frame.file.path, frame.file.ext)
|
|
46
|
-
.then(() => {
|
|
47
|
-
// CASE: trigger that redirects are getting re-registered
|
|
48
|
-
web.shared.middlewares.customRedirects.reload();
|
|
49
|
-
});
|
|
44
|
+
return redirects.api.setFromFilePath(frame.file.path, frame.file.ext);
|
|
50
45
|
}
|
|
51
46
|
}
|
|
52
47
|
};
|
|
@@ -102,6 +102,13 @@ const forceStatusFilter = (frame) => {
|
|
|
102
102
|
}
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
+
const transformPageVisibilityFilters = (frame) => {
|
|
106
|
+
if (frame.data.pages[0].visibility === 'filter' && frame.data.pages[0].visibility_filter) {
|
|
107
|
+
frame.data.pages[0].visibility = frame.data.pages[0].visibility_filter;
|
|
108
|
+
}
|
|
109
|
+
delete frame.data.pages[0].visibility_filter;
|
|
110
|
+
};
|
|
111
|
+
|
|
105
112
|
module.exports = {
|
|
106
113
|
browse(apiConfig, frame) {
|
|
107
114
|
debug('browse');
|
|
@@ -180,6 +187,7 @@ module.exports = {
|
|
|
180
187
|
});
|
|
181
188
|
}
|
|
182
189
|
|
|
190
|
+
transformPageVisibilityFilters(frame);
|
|
183
191
|
handlePostsMeta(frame);
|
|
184
192
|
defaultFormat(frame);
|
|
185
193
|
defaultRelations(frame);
|
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
|
+
const labs = require('../../../../../../shared/labs');
|
|
2
3
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:config');
|
|
3
4
|
|
|
4
5
|
module.exports = {
|
|
5
6
|
all(data, apiConfig, frame) {
|
|
6
7
|
debug('all');
|
|
7
8
|
|
|
9
|
+
const keys = [
|
|
10
|
+
'version',
|
|
11
|
+
'environment',
|
|
12
|
+
'database',
|
|
13
|
+
'mail',
|
|
14
|
+
'useGravatar',
|
|
15
|
+
'labs',
|
|
16
|
+
'clientExtensions',
|
|
17
|
+
'enableDeveloperExperiments',
|
|
18
|
+
'stripeDirect',
|
|
19
|
+
'mailgunIsConfigured',
|
|
20
|
+
'emailAnalytics',
|
|
21
|
+
'hostSettings'
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
if (labs.isSet('gifsCard')) {
|
|
25
|
+
keys.push('tenorApiKey');
|
|
26
|
+
}
|
|
27
|
+
|
|
8
28
|
frame.response = {
|
|
9
|
-
config: _.pick(data,
|
|
10
|
-
'version',
|
|
11
|
-
'environment',
|
|
12
|
-
'database',
|
|
13
|
-
'mail',
|
|
14
|
-
'useGravatar',
|
|
15
|
-
'labs',
|
|
16
|
-
'clientExtensions',
|
|
17
|
-
'enableDeveloperExperiments',
|
|
18
|
-
'stripeDirect',
|
|
19
|
-
'mailgunIsConfigured',
|
|
20
|
-
'emailAnalytics',
|
|
21
|
-
'hostSettings'
|
|
22
|
-
])
|
|
29
|
+
config: _.pick(data, keys)
|
|
23
30
|
};
|
|
24
31
|
}
|
|
25
32
|
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const config = require('../../../../../../shared/config');
|
|
2
|
+
const {STATIC_FILES_URL_PREFIX} = require('@tryghost/constants');
|
|
3
|
+
|
|
4
|
+
function getURL(urlPath) {
|
|
5
|
+
const media = new RegExp('^' + config.getSubdir() + '/' + STATIC_FILES_URL_PREFIX);
|
|
6
|
+
const absolute = media.test(urlPath) ? true : false;
|
|
7
|
+
|
|
8
|
+
if (absolute) {
|
|
9
|
+
// Remove the sub-directory from the URL because ghostConfig will add it back.
|
|
10
|
+
urlPath = urlPath.replace(new RegExp('^' + config.getSubdir()), '');
|
|
11
|
+
const baseUrl = config.getSiteUrl().replace(/\/$/, '');
|
|
12
|
+
urlPath = baseUrl + urlPath;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return urlPath;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
upload({filePath}, apiConfig, frame) {
|
|
20
|
+
return frame.response = {
|
|
21
|
+
files: [{
|
|
22
|
+
url: getURL(filePath),
|
|
23
|
+
ref: frame.data.ref || null
|
|
24
|
+
}]
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const config = require('../../../../../../shared/config');
|
|
2
|
+
const {STATIC_MEDIA_URL_PREFIX} = require('@tryghost/constants');
|
|
3
|
+
|
|
4
|
+
function getURL(urlPath) {
|
|
5
|
+
const media = new RegExp('^' + config.getSubdir() + '/' + STATIC_MEDIA_URL_PREFIX);
|
|
6
|
+
const absolute = media.test(urlPath) ? true : false;
|
|
7
|
+
|
|
8
|
+
if (absolute) {
|
|
9
|
+
// Remove the sub-directory from the URL because ghostConfig will add it back.
|
|
10
|
+
urlPath = urlPath.replace(new RegExp('^' + config.getSubdir()), '');
|
|
11
|
+
const baseUrl = config.getSiteUrl().replace(/\/$/, '');
|
|
12
|
+
urlPath = baseUrl + urlPath;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return urlPath;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
upload({filePath, thumbnailPath}, apiConfig, frame) {
|
|
20
|
+
return frame.response = {
|
|
21
|
+
media: [{
|
|
22
|
+
url: getURL(filePath),
|
|
23
|
+
thumbnail_url: getURL(thumbnailPath),
|
|
24
|
+
ref: frame.data.ref || null
|
|
25
|
+
}]
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
uploadThumbnail(path, apiConfig, frame) {
|
|
30
|
+
return frame.response = {
|
|
31
|
+
media: [{
|
|
32
|
+
url: getURL(path),
|
|
33
|
+
ref: frame.data.ref || null
|
|
34
|
+
}]
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const limitService = require('../../../../../services/limits');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
async upload(apiConfig, frame) {
|
|
5
|
+
await limitService.errorIfIsOverLimit('uploads', {currentCount: frame.file.size});
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
async uploadThumbnail(apiConfig, frame) {
|
|
9
|
+
await limitService.errorIfIsOverLimit('uploads', {currentCount: frame.file.size});
|
|
10
|
+
}
|
|
11
|
+
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const web = require('../../web');
|
|
2
1
|
const redirects = require('../../services/redirects');
|
|
3
2
|
|
|
4
3
|
module.exports = {
|
|
@@ -23,11 +22,7 @@ module.exports = {
|
|
|
23
22
|
cacheInvalidate: true
|
|
24
23
|
},
|
|
25
24
|
query(frame) {
|
|
26
|
-
return redirects.api.setFromFilePath(frame.file.path)
|
|
27
|
-
.then(() => {
|
|
28
|
-
// CASE: trigger that redirects are getting re-registered
|
|
29
|
-
web.shared.middlewares.customRedirects.reload();
|
|
30
|
-
});
|
|
25
|
+
return redirects.api.setFromFilePath(frame.file.path);
|
|
31
26
|
}
|
|
32
27
|
}
|
|
33
28
|
};
|
|
@@ -154,7 +154,11 @@ module.exports = {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
if (frame.options.send_email) {
|
|
157
|
-
await membersService.api.sendEmailWithMagicLink({
|
|
157
|
+
await membersService.api.sendEmailWithMagicLink({
|
|
158
|
+
email: member.get('email'), requestedType: frame.options.email_type, options: {
|
|
159
|
+
forceEmailType: true
|
|
160
|
+
}
|
|
161
|
+
});
|
|
158
162
|
}
|
|
159
163
|
|
|
160
164
|
return member;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
|
|
3
|
-
const web = require('../../web');
|
|
4
3
|
const redirects = require('../../services/redirects');
|
|
5
4
|
|
|
6
5
|
module.exports = {
|
|
@@ -42,11 +41,7 @@ module.exports = {
|
|
|
42
41
|
cacheInvalidate: true
|
|
43
42
|
},
|
|
44
43
|
query(frame) {
|
|
45
|
-
return redirects.api.setFromFilePath(frame.file.path, frame.file.ext)
|
|
46
|
-
.then(() => {
|
|
47
|
-
// CASE: trigger that redirects are getting re-registered
|
|
48
|
-
web.shared.middlewares.customRedirects.reload();
|
|
49
|
-
});
|
|
44
|
+
return redirects.api.setFromFilePath(frame.file.path, frame.file.ext);
|
|
50
45
|
}
|
|
51
46
|
}
|
|
52
47
|
};
|