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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Usage: `{{asset "css/screen.css"}}`
|
|
3
3
|
//
|
|
4
4
|
// Returns the path to the specified asset.
|
|
5
|
-
const {metaData} = require('../services/proxy');
|
|
5
|
+
const {metaData, urlUtils} = require('../services/proxy');
|
|
6
6
|
const {SafeString} = require('../services/rendering');
|
|
7
7
|
|
|
8
8
|
const errors = require('@tryghost/errors');
|
|
@@ -22,6 +22,14 @@ module.exports = function asset(path, options) {
|
|
|
22
22
|
message: tpl(messages.pathIsRequired)
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
|
+
if (typeof urlUtils.getSiteUrl() !== 'undefined'
|
|
26
|
+
&& typeof urlUtils.getAdminUrl() !== 'undefined'
|
|
27
|
+
&& urlUtils.getSiteUrl() !== urlUtils.getAdminUrl()) {
|
|
28
|
+
const target = new URL(getAssetUrl(path, hasMinFile), urlUtils.getSiteUrl());
|
|
29
|
+
return new SafeString(
|
|
30
|
+
target.href
|
|
31
|
+
);
|
|
32
|
+
}
|
|
25
33
|
|
|
26
34
|
return new SafeString(
|
|
27
35
|
getAssetUrl(path, hasMinFile)
|
|
@@ -5,12 +5,16 @@
|
|
|
5
5
|
const {metaData, settingsCache, config, blogIcon, urlUtils, labs} = require('../services/proxy');
|
|
6
6
|
const {escapeExpression, SafeString} = require('../services/rendering');
|
|
7
7
|
|
|
8
|
+
// BAD REQUIRE
|
|
9
|
+
// @TODO fix this require
|
|
10
|
+
const cardAssetService = require('../services/card-assets');
|
|
11
|
+
|
|
8
12
|
const logging = require('@tryghost/logging');
|
|
9
13
|
const _ = require('lodash');
|
|
10
14
|
const debug = require('@tryghost/debug')('ghost_head');
|
|
11
15
|
const templateStyles = require('./tpl/styles');
|
|
12
16
|
|
|
13
|
-
const getMetaData = metaData
|
|
17
|
+
const {get: getMetaData, getAssetUrl} = metaData;
|
|
14
18
|
|
|
15
19
|
function writeMetaTag(property, content, type) {
|
|
16
20
|
type = type || property.substring(0, 7) === 'twitter' ? 'name' : 'property';
|
|
@@ -193,6 +197,14 @@ module.exports = function ghost_head(options) { // eslint-disable-line camelcase
|
|
|
193
197
|
if (!_.includes(context, 'amp')) {
|
|
194
198
|
head.push(getMembersHelper(options.data));
|
|
195
199
|
|
|
200
|
+
// @TODO do this in a more "frameworky" way
|
|
201
|
+
if (cardAssetService.hasFile('js')) {
|
|
202
|
+
head.push(`<script async src="${getAssetUrl('public/cards.min.js')}"></script>`);
|
|
203
|
+
}
|
|
204
|
+
if (cardAssetService.hasFile('css')) {
|
|
205
|
+
head.push(`<link rel="stylesheet" type="text/css" href="${getAssetUrl('public/cards.min.css')}">`);
|
|
206
|
+
}
|
|
207
|
+
|
|
196
208
|
if (!_.isEmpty(globalCodeinjection)) {
|
|
197
209
|
head.push(globalCodeinjection);
|
|
198
210
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const debug = require('@tryghost/debug')('card-assets');
|
|
2
|
+
const themeEngine = require('../theme-engine');
|
|
3
|
+
|
|
4
|
+
const CardAssetService = require('./service');
|
|
5
|
+
let cardAssetService = new CardAssetService();
|
|
6
|
+
|
|
7
|
+
const initFn = async () => {
|
|
8
|
+
const cardAssetConfig = themeEngine.getActive().config('card_assets');
|
|
9
|
+
debug('initialising with config', cardAssetConfig);
|
|
10
|
+
|
|
11
|
+
await cardAssetService.load(cardAssetConfig);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
module.exports = cardAssetService;
|
|
15
|
+
|
|
16
|
+
module.exports.init = initFn;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
const Minifier = require('@tryghost/minifier');
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs').promises;
|
|
5
|
+
const logging = require('@tryghost/logging');
|
|
6
|
+
|
|
7
|
+
class CardAssetService {
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
// @TODO: use our config paths concept
|
|
10
|
+
this.src = options.src || path.join(__dirname, '../../src/cards');
|
|
11
|
+
this.dest = options.dest || path.join(__dirname, '../../public');
|
|
12
|
+
this.minifier = new Minifier({src: this.src, dest: this.dest});
|
|
13
|
+
|
|
14
|
+
if ('config' in options) {
|
|
15
|
+
this.config = options.config;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
this.files = [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
generateGlobs() {
|
|
22
|
+
// CASE: The theme has asked for all card assets to be included by default
|
|
23
|
+
if (this.config === true) {
|
|
24
|
+
return {
|
|
25
|
+
'cards.min.css': 'css/*.css',
|
|
26
|
+
'cards.min.js': 'js/*.js'
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// CASE: the theme has declared an include directive, we should include exactly these assets
|
|
31
|
+
// Include rules take precedence over exclude rules.
|
|
32
|
+
if (_.has(this.config, 'include')) {
|
|
33
|
+
return {
|
|
34
|
+
'cards.min.css': `css/(${this.config.include.join('|')}).css`,
|
|
35
|
+
'cards.min.js': `js/(${this.config.include.join('|')}).js`
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// CASE: the theme has declared an exclude directive, we should include exactly these assets
|
|
40
|
+
if (_.has(this.config, 'exclude')) {
|
|
41
|
+
return {
|
|
42
|
+
'cards.min.css': `css/!(${this.config.exclude.join('|')}).css`,
|
|
43
|
+
'cards.min.js': `js/!(${this.config.exclude.join('|')}).js`
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// CASE: theme has asked that no assets be included
|
|
48
|
+
// CASE: we didn't understand config, don't do anything
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async minify(globs) {
|
|
53
|
+
try {
|
|
54
|
+
return await this.minifier.minify(globs);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
// @TODO: Convert this back to a proper error once the underlying bug is fixed
|
|
57
|
+
if (err.code === 'EACCES') {
|
|
58
|
+
logging.warn('Ghost was not able to write card asset files due to permissions.');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async clearFiles() {
|
|
64
|
+
this.files = [];
|
|
65
|
+
|
|
66
|
+
// @deprecated switch this to use fs.rm when we drop support for Node v12
|
|
67
|
+
try {
|
|
68
|
+
await fs.unlink(path.join(this.dest, 'cards.min.css'));
|
|
69
|
+
} catch (error) {
|
|
70
|
+
// Don't worry if the file didn't exist or we don't have perms here
|
|
71
|
+
if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
await fs.unlink(path.join(this.dest, 'cards.min.js'));
|
|
78
|
+
} catch (error) {
|
|
79
|
+
// Don't worry if the file didn't exist or we don't have perms here
|
|
80
|
+
if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
hasFile(type) {
|
|
87
|
+
return this.files.indexOf(`cards.min.${type}`) > -1;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* A theme can declare which cards it supports, and we'll do the rest
|
|
92
|
+
*
|
|
93
|
+
* @param {Array|boolean} config
|
|
94
|
+
* @returns
|
|
95
|
+
*/
|
|
96
|
+
async load(config) {
|
|
97
|
+
if (config) {
|
|
98
|
+
this.config = config;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
await this.clearFiles();
|
|
102
|
+
|
|
103
|
+
const globs = this.generateGlobs();
|
|
104
|
+
|
|
105
|
+
this.files = await this.minify(globs) || [];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = CardAssetService;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const defaultConfig = require('./defaults');
|
|
3
|
-
const allowedKeys = ['posts_per_page', 'image_sizes'];
|
|
3
|
+
const allowedKeys = ['posts_per_page', 'image_sizes', 'card_assets'];
|
|
4
4
|
|
|
5
5
|
module.exports.create = function configLoader(packageJson) {
|
|
6
6
|
let config = _.cloneDeep(defaultConfig);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/* style.css */
|
|
2
|
+
|
|
3
|
+
.kg-bookmark-card {
|
|
4
|
+
width: 100%;
|
|
5
|
+
position: relative;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.kg-bookmark-container {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-wrap: wrap;
|
|
11
|
+
flex-direction: row-reverse;
|
|
12
|
+
color: currentColor;
|
|
13
|
+
font-family: inherit;
|
|
14
|
+
text-decoration: none;
|
|
15
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.kg-bookmark-container:hover {
|
|
19
|
+
text-decoration: none;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.kg-bookmark-content {
|
|
23
|
+
flex-basis: 0;
|
|
24
|
+
flex-grow: 999;
|
|
25
|
+
padding: 20px;
|
|
26
|
+
order: 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.kg-bookmark-title {
|
|
30
|
+
font-weight: 600;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.kg-bookmark-metadata,
|
|
34
|
+
.kg-bookmark-description {
|
|
35
|
+
margin-top: .5em;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.kg-bookmark-metadata {
|
|
39
|
+
align-items: center;
|
|
40
|
+
white-space: nowrap;
|
|
41
|
+
overflow: hidden;
|
|
42
|
+
text-overflow: ellipsis;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.kg-bookmark-description {
|
|
46
|
+
display: -webkit-box;
|
|
47
|
+
-webkit-box-orient: vertical;
|
|
48
|
+
-webkit-line-clamp: 2;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.kg-bookmark-icon {
|
|
53
|
+
display: inline-block;
|
|
54
|
+
width: 1em;
|
|
55
|
+
height: 1em;
|
|
56
|
+
vertical-align: text-bottom;
|
|
57
|
+
margin-right: .5em;
|
|
58
|
+
margin-bottom: .05em;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.kg-bookmark-thumbnail {
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-basis: 24rem;
|
|
64
|
+
flex-grow: 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.kg-bookmark-thumbnail img {
|
|
68
|
+
max-width: 100%;
|
|
69
|
+
height: auto;
|
|
70
|
+
vertical-align: bottom;
|
|
71
|
+
object-fit: cover;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.kg-bookmark-author {
|
|
75
|
+
white-space: nowrap;
|
|
76
|
+
text-overflow: ellipsis;
|
|
77
|
+
overflow: hidden;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.kg-bookmark-publisher::before {
|
|
81
|
+
content: "•";
|
|
82
|
+
margin: 0 .5em;
|
|
83
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
.kg-button-card {
|
|
2
|
+
display: flex;
|
|
3
|
+
position: static;
|
|
4
|
+
align-items: center;
|
|
5
|
+
width: 100%;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
a.kg-btn {
|
|
10
|
+
display: flex;
|
|
11
|
+
position: static;
|
|
12
|
+
align-items: center;
|
|
13
|
+
padding: 0 2.0rem;
|
|
14
|
+
height: 4.0rem;
|
|
15
|
+
line-height: 4.0rem;
|
|
16
|
+
font-size: 1.65rem;
|
|
17
|
+
font-weight: 600;
|
|
18
|
+
text-decoration: none;
|
|
19
|
+
border-radius: 5px;
|
|
20
|
+
transition: opacity 0.2s ease-in-out;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
a.kg-btn:hover {
|
|
24
|
+
opacity: 0.85;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
a.kg-btn-accent {
|
|
28
|
+
background-color: var(--ghost-accent-color);
|
|
29
|
+
color: #fff;
|
|
30
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
.kg-gallery-card {
|
|
2
|
+
margin: 0 0 1.5em;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.kg-gallery-card figcaption {
|
|
6
|
+
margin: -1.0em 0 1.5em;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.kg-gallery-container {
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
margin: 1.5em auto;
|
|
13
|
+
max-width: 1040px;
|
|
14
|
+
width: 100vw;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.kg-gallery-row {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: row;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.kg-gallery-image img {
|
|
24
|
+
display: block;
|
|
25
|
+
margin: 0;
|
|
26
|
+
width: 100%;
|
|
27
|
+
height: 100%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.kg-gallery-row:not(:first-of-type) {
|
|
31
|
+
margin: 0.75em 0 0 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.kg-gallery-image:not(:first-of-type) {
|
|
35
|
+
margin: 0 0 0 0.75em;
|
|
36
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
a.kg-nft-card {
|
|
2
|
+
position: static;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex: auto;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
text-decoration: none;
|
|
7
|
+
font-family: -apple-system, BlinkMacSystemFont,
|
|
8
|
+
'avenir next', avenir,
|
|
9
|
+
'helvetica neue', helvetica,
|
|
10
|
+
ubuntu,
|
|
11
|
+
roboto, noto,
|
|
12
|
+
'segoe ui', arial,
|
|
13
|
+
sans-serif;
|
|
14
|
+
font-size: 1.4rem;
|
|
15
|
+
font-weight: 400;
|
|
16
|
+
box-shadow: 0 2px 6px -2px rgb(0 0 0 / 10%), 0 0 1px rgb(0 0 0 / 40%);
|
|
17
|
+
width: 100%;
|
|
18
|
+
max-width: 512px;
|
|
19
|
+
color: #222;
|
|
20
|
+
background: #fff;
|
|
21
|
+
border-radius: 5px;
|
|
22
|
+
transition: none;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
a.kg-nft-card:hover {
|
|
26
|
+
color: #333;
|
|
27
|
+
opacity: 1.0;
|
|
28
|
+
transition: none;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
a.kg-nft-card * {
|
|
32
|
+
position: static;
|
|
33
|
+
line-height: 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.kg-nft-metadata {
|
|
37
|
+
padding: 2.0rem;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.kg-nft-card img {
|
|
41
|
+
min-width: unset;
|
|
42
|
+
max-width: unset;
|
|
43
|
+
margin: -1px;
|
|
44
|
+
border-radius: 5px 5px 0 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.kg-nft-header {
|
|
48
|
+
display: flex;
|
|
49
|
+
justify-content: space-between;
|
|
50
|
+
gap: 20px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.kg-nft-header h4.kg-nft-title {
|
|
54
|
+
font-family: inherit;
|
|
55
|
+
font-size: 1.9rem;
|
|
56
|
+
font-weight: 700;
|
|
57
|
+
min-width: unset;
|
|
58
|
+
max-width: unset;
|
|
59
|
+
margin: 0;
|
|
60
|
+
color: #222;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.kg-nft-header svg {
|
|
64
|
+
width: 100px;
|
|
65
|
+
height: auto;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.kg-nft-creator {
|
|
69
|
+
font-family: inherit;
|
|
70
|
+
margin: 0.8rem 0 0;
|
|
71
|
+
color: #ababab;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.kg-nft-creator span {
|
|
75
|
+
font-weight: 500;
|
|
76
|
+
color: #222;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.kg-nft-card p.kg-nft-description {
|
|
80
|
+
font-family: inherit;
|
|
81
|
+
font-size: 1.4rem;
|
|
82
|
+
line-height: 1.4em;
|
|
83
|
+
margin: 2.0rem 0 0;
|
|
84
|
+
color: #222;
|
|
85
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
var images = document.querySelectorAll('.kg-gallery-image img');
|
|
2
|
+
images.forEach(function (image) {
|
|
3
|
+
var container = image.closest('.kg-gallery-image');
|
|
4
|
+
var width = image.attributes.width.value;
|
|
5
|
+
var height = image.attributes.height.value;
|
|
6
|
+
var ratio = width / height;
|
|
7
|
+
container.style.flex = ratio + ' 1 0%';
|
|
8
|
+
})
|
|
@@ -7,7 +7,8 @@ const urlUtils = require('../../../shared/url-utils');
|
|
|
7
7
|
const tpl = require('@tryghost/tpl');
|
|
8
8
|
|
|
9
9
|
const messages = {
|
|
10
|
-
imageNotFound: 'Image not found'
|
|
10
|
+
imageNotFound: 'Image not found',
|
|
11
|
+
fileNotFound: 'File not found'
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
function createPublicFileMiddleware(file, type, maxAge) {
|
|
@@ -43,6 +44,14 @@ function createPublicFileMiddleware(file, type, maxAge) {
|
|
|
43
44
|
// modify text files before caching+serving to ensure URL placeholders are transformed
|
|
44
45
|
fs.readFile(filePath, (err, buf) => {
|
|
45
46
|
if (err) {
|
|
47
|
+
// Downgrade to a simple 404 if the file didn't exist
|
|
48
|
+
if (err.code === 'ENOENT') {
|
|
49
|
+
err = new errors.NotFoundError({
|
|
50
|
+
message: tpl(messages.fileNotFound),
|
|
51
|
+
code: 'PUBLIC_FILE_NOT_FOUND',
|
|
52
|
+
property: err.path
|
|
53
|
+
});
|
|
54
|
+
}
|
|
46
55
|
return next(err);
|
|
47
56
|
}
|
|
48
57
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('routing');
|
|
2
2
|
|
|
3
3
|
const routing = require('../services/routing');
|
|
4
|
-
// NOTE: temporary import from the frontend, will become a backend service soon
|
|
5
4
|
const urlService = require('../../server/services/url');
|
|
6
5
|
const routeSettings = require('../../server/services/route-settings');
|
|
7
6
|
|
|
@@ -17,11 +17,15 @@ const themeEngine = require('../services/theme-engine');
|
|
|
17
17
|
const themeMiddleware = themeEngine.middleware;
|
|
18
18
|
const membersService = require('../../server/services/members');
|
|
19
19
|
const offersService = require('../../server/services/offers');
|
|
20
|
+
const customRedirects = require('../../server/services/redirects');
|
|
20
21
|
const siteRoutes = require('./routes');
|
|
21
22
|
const shared = require('../../server/web/shared');
|
|
22
23
|
const mw = require('./middleware');
|
|
24
|
+
const labs = require('../../shared/labs');
|
|
23
25
|
|
|
24
26
|
const STATIC_IMAGE_URL_PREFIX = `/${urlUtils.STATIC_IMAGE_URL_PREFIX}`;
|
|
27
|
+
const STATIC_MEDIA_URL_PREFIX = `/${constants.STATIC_MEDIA_URL_PREFIX}`;
|
|
28
|
+
const STATIC_FILES_URL_PREFIX = `/${constants.STATIC_FILES_URL_PREFIX}`;
|
|
25
29
|
|
|
26
30
|
let router;
|
|
27
31
|
|
|
@@ -89,7 +93,7 @@ module.exports = function setupSiteApp(options = {}) {
|
|
|
89
93
|
|
|
90
94
|
// you can extend Ghost with a custom redirects file
|
|
91
95
|
// see https://github.com/TryGhost/Ghost/issues/7707
|
|
92
|
-
|
|
96
|
+
siteApp.use(customRedirects.middleware);
|
|
93
97
|
|
|
94
98
|
// (Optionally) redirect any requests to /ghost to the admin panel
|
|
95
99
|
siteApp.use(mw.redirectGhostToAdmin());
|
|
@@ -106,16 +110,16 @@ module.exports = function setupSiteApp(options = {}) {
|
|
|
106
110
|
siteApp.use(mw.servePublicFile('public/ghost.css', 'text/css', constants.ONE_HOUR_S));
|
|
107
111
|
siteApp.use(mw.servePublicFile('public/ghost.min.css', 'text/css', constants.ONE_YEAR_S));
|
|
108
112
|
|
|
113
|
+
// Card assets
|
|
114
|
+
siteApp.use(mw.servePublicFile('public/cards.min.css', 'text/css', constants.ONE_YEAR_S));
|
|
115
|
+
siteApp.use(mw.servePublicFile('public/cards.min.js', 'text/js', constants.ONE_YEAR_S));
|
|
116
|
+
|
|
109
117
|
// Serve blog images using the storage adapter
|
|
110
118
|
siteApp.use(STATIC_IMAGE_URL_PREFIX, mw.handleImageSizes, storage.getStorage('images').serve());
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
// Else we end up with circular dependencies
|
|
116
|
-
// themeEngine.loadCoreHelpers();
|
|
117
|
-
// themeEngine.registerHandlebarsHelpers();
|
|
118
|
-
// debug('Helpers done');
|
|
119
|
+
// Serve blog media using the storage adapter
|
|
120
|
+
siteApp.use(STATIC_MEDIA_URL_PREFIX, labs.enabledMiddleware('mediaAPI'), storage.getStorage('media').serve());
|
|
121
|
+
// Serve blog files using the storage adapter
|
|
122
|
+
siteApp.use(STATIC_FILES_URL_PREFIX, labs.enabledMiddleware('filesAPI'), storage.getStorage('files').serve());
|
|
119
123
|
|
|
120
124
|
// Global handling for member session, ensures a member is logged in to the frontend
|
|
121
125
|
siteApp.use(membersService.middleware.loadMemberSession);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// # Local File System Storage module
|
|
2
|
+
// The (default) module for storing media, using the local file system
|
|
3
|
+
const config = require('../../../shared/config');
|
|
4
|
+
const constants = require('@tryghost/constants');
|
|
5
|
+
const LocalStorageBase = require('./LocalStorageBase');
|
|
6
|
+
|
|
7
|
+
class LocalFilesStorage extends LocalStorageBase {
|
|
8
|
+
constructor() {
|
|
9
|
+
super({
|
|
10
|
+
storagePath: config.getContentPath('files'),
|
|
11
|
+
siteUrl: config.getSiteUrl(),
|
|
12
|
+
staticFileURLPrefix: constants.STATIC_FILES_URL_PREFIX
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = LocalFilesStorage;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// # Local File System Image Storage module
|
|
2
|
+
// The (default) module for storing images, using the local file system
|
|
3
|
+
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const config = require('../../../shared/config');
|
|
7
|
+
|
|
8
|
+
const urlUtils = require('../../../shared/url-utils');
|
|
9
|
+
const LocalStorageBase = require('./LocalStorageBase');
|
|
10
|
+
|
|
11
|
+
let messages = {
|
|
12
|
+
notFound: 'Image not found',
|
|
13
|
+
notFoundWithRef: 'Image not found: {file}',
|
|
14
|
+
cannotRead: 'Could not read image: {file}'
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
class LocalImagesStorage extends LocalStorageBase {
|
|
18
|
+
constructor() {
|
|
19
|
+
super({
|
|
20
|
+
storagePath: config.getContentPath('images'),
|
|
21
|
+
staticFileURLPrefix: urlUtils.STATIC_IMAGE_URL_PREFIX,
|
|
22
|
+
siteUrl: config.getSiteUrl(),
|
|
23
|
+
errorMessages: messages
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Saves a buffer in the targetPath
|
|
29
|
+
* @param {Buffer} buffer is an instance of Buffer
|
|
30
|
+
* @param {String} targetPath path to which the buffer should be written
|
|
31
|
+
* @returns {Promise<String>} a URL to retrieve the data
|
|
32
|
+
*/
|
|
33
|
+
async saveRaw(buffer, targetPath) {
|
|
34
|
+
const storagePath = path.join(this.storagePath, targetPath);
|
|
35
|
+
const targetDir = path.dirname(storagePath);
|
|
36
|
+
|
|
37
|
+
await fs.mkdirs(targetDir);
|
|
38
|
+
await fs.writeFile(storagePath, buffer);
|
|
39
|
+
|
|
40
|
+
// For local file system storage can use relative path so add a slash
|
|
41
|
+
const fullUrl = (
|
|
42
|
+
urlUtils.urlJoin('/', urlUtils.getSubdir(),
|
|
43
|
+
this.staticFileURLPrefix,
|
|
44
|
+
targetPath)
|
|
45
|
+
).replace(new RegExp(`\\${path.sep}`, 'g'), '/');
|
|
46
|
+
|
|
47
|
+
return fullUrl;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = LocalImagesStorage;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// # Local File System Media Storage module
|
|
2
|
+
// The (default) module for storing media, using the local file system
|
|
3
|
+
const config = require('../../../shared/config');
|
|
4
|
+
const constants = require('@tryghost/constants');
|
|
5
|
+
const LocalStorageBase = require('./LocalStorageBase');
|
|
6
|
+
|
|
7
|
+
const messages = {
|
|
8
|
+
notFound: 'Media file not found',
|
|
9
|
+
notFoundWithRef: 'Media file not found: {file}',
|
|
10
|
+
cannotRead: 'Could not read media file: {file}'
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
class LocalMediaStore extends LocalStorageBase {
|
|
14
|
+
constructor() {
|
|
15
|
+
super({
|
|
16
|
+
storagePath: config.getContentPath('media'),
|
|
17
|
+
staticFileURLPrefix: constants.STATIC_MEDIA_URL_PREFIX,
|
|
18
|
+
siteUrl: config.getSiteUrl(),
|
|
19
|
+
errorMessages: messages
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = LocalMediaStore;
|