ghost 5.17.1 → 5.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/components/tryghost-adapter-manager-5.18.0.tgz +0 -0
- package/components/{tryghost-api-framework-5.17.1.tgz → tryghost-api-framework-5.18.0.tgz} +0 -0
- package/components/{tryghost-api-version-compatibility-service-5.17.1.tgz → tryghost-api-version-compatibility-service-5.18.0.tgz} +0 -0
- package/components/tryghost-bootstrap-socket-5.18.0.tgz +0 -0
- package/components/tryghost-constants-5.18.0.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.18.0.tgz +0 -0
- package/components/tryghost-domain-events-5.18.0.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.18.0.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.18.0.tgz +0 -0
- package/components/tryghost-email-content-generator-5.18.0.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.18.0.tgz +0 -0
- package/components/tryghost-extract-api-key-5.18.0.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.18.0.tgz +0 -0
- package/components/tryghost-job-manager-5.18.0.tgz +0 -0
- package/components/{tryghost-link-redirects-5.17.1.tgz → tryghost-link-redirects-5.18.0.tgz} +0 -0
- package/components/tryghost-link-replacer-5.18.0.tgz +0 -0
- package/components/tryghost-link-tracking-5.18.0.tgz +0 -0
- package/components/tryghost-magic-link-5.18.0.tgz +0 -0
- package/components/tryghost-mailgun-client-5.18.0.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.18.0.tgz +0 -0
- package/components/{tryghost-member-attribution-5.17.1.tgz → tryghost-member-attribution-5.18.0.tgz} +0 -0
- package/components/tryghost-member-events-5.18.0.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.18.0.tgz +0 -0
- package/components/tryghost-members-api-5.18.0.tgz +0 -0
- package/components/tryghost-members-csv-5.18.0.tgz +0 -0
- package/components/tryghost-members-events-service-5.18.0.tgz +0 -0
- package/components/tryghost-members-importer-5.18.0.tgz +0 -0
- package/components/tryghost-members-offers-5.18.0.tgz +0 -0
- package/components/tryghost-members-payments-5.18.0.tgz +0 -0
- package/components/tryghost-members-ssr-5.18.0.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.18.0.tgz +0 -0
- package/components/tryghost-minifier-5.18.0.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.18.0.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.18.0.tgz +0 -0
- package/components/{tryghost-mw-error-handler-5.17.1.tgz → tryghost-mw-error-handler-5.18.0.tgz} +0 -0
- package/components/tryghost-mw-session-from-token-5.18.0.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.18.0.tgz +0 -0
- package/components/tryghost-mw-vhost-5.18.0.tgz +0 -0
- package/components/{tryghost-oembed-service-5.17.1.tgz → tryghost-oembed-service-5.18.0.tgz} +0 -0
- package/components/{tryghost-package-json-5.17.1.tgz → tryghost-package-json-5.18.0.tgz} +0 -0
- package/components/tryghost-referrers-5.18.0.tgz +0 -0
- package/components/tryghost-security-5.18.0.tgz +0 -0
- package/components/{tryghost-session-service-5.17.1.tgz → tryghost-session-service-5.18.0.tgz} +0 -0
- package/components/tryghost-settings-path-manager-5.18.0.tgz +0 -0
- package/components/tryghost-staff-service-5.18.0.tgz +0 -0
- package/components/tryghost-stats-service-5.18.0.tgz +0 -0
- package/components/{tryghost-update-check-service-5.17.1.tgz → tryghost-update-check-service-5.18.0.tgz} +0 -0
- package/components/tryghost-verification-trigger-5.18.0.tgz +0 -0
- package/components/{tryghost-version-notifications-data-service-5.17.1.tgz → tryghost-version-notifications-data-service-5.18.0.tgz} +0 -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 +18 -21
- package/content/themes/casper/package.json +1 -1
- package/core/built/admin/assets/{chunk.143.a252ca5afc89e52c049a.js → chunk.143.6d23a3157dae7a9c899d.js} +6 -6
- package/core/built/admin/assets/{chunk.174.37fefc669899f0fd0064.js → chunk.174.e997dfffceeaa0ce4636.js} +155 -154
- package/core/built/admin/assets/{chunk.178.23e9f243eba5320fe15b.js → chunk.178.52a9ca26217a593eda67.js} +4 -4
- package/core/built/admin/assets/{chunk.579.a9bccec4d650a7be727a.js → chunk.427.4483d5bbdaf2a65888ac.js} +8969 -8890
- package/core/built/admin/assets/{chunk.579.a9bccec4d650a7be727a.js.LICENSE.txt → chunk.427.4483d5bbdaf2a65888ac.js.LICENSE.txt} +0 -0
- package/core/built/admin/assets/{ghost-fa7772f8aa2522b3935efed71fa75619.js → ghost-7e6e9479705e7e772bb5ea3b6476cd52.js} +2801 -2758
- package/core/built/admin/assets/ghost-dark-a41f7645a406e0df78b7152f3f805e66.css +1 -0
- package/core/built/admin/assets/ghost-ff0bee94743aa886ce35305a5b46fac3.css +1 -0
- package/core/built/admin/assets/{vendor-733135cd6cbca8126c6fa223d63a5bf3.css → vendor-3e6947aa681f0fb82b193090e520dc73.css} +24 -92
- package/core/built/admin/assets/{vendor-325e038b8609f0979f6578cae7a87f9e.js → vendor-4da5d2584fbe1442e25e4271a5513f1c.js} +545 -546
- package/core/built/admin/index.html +7 -7
- package/core/frontend/public/ghost.min.css +1 -1
- package/core/frontend/web/middleware/static-theme.js +2 -0
- package/core/server/api/endpoints/member-signin-urls.js +1 -1
- package/core/server/api/endpoints/utils/serializers/input/posts.js +1 -6
- package/core/server/api/endpoints/utils/serializers/output/mappers/posts.js +8 -15
- package/core/server/services/members/middleware.js +1 -0
- package/core/server/services/webhooks/serialize.js +1 -1
- package/core/server/web/admin/app.js +2 -0
- package/core/server/web/api/endpoints/admin/routes.js +1 -2
- package/core/server/web/members/app.js +9 -1
- package/core/server/web/shared/middleware/api/spam-prevention.js +65 -9
- package/core/server/web/shared/middleware/brute.js +8 -0
- package/core/shared/config/defaults.json +8 -2
- package/core/shared/labs.js +3 -3
- package/package.json +101 -101
- package/yarn.lock +4473 -1242
- package/components/tryghost-adapter-manager-5.17.1.tgz +0 -0
- package/components/tryghost-bootstrap-socket-5.17.1.tgz +0 -0
- package/components/tryghost-constants-5.17.1.tgz +0 -0
- package/components/tryghost-custom-theme-settings-service-5.17.1.tgz +0 -0
- package/components/tryghost-domain-events-5.17.1.tgz +0 -0
- package/components/tryghost-email-analytics-provider-mailgun-5.17.1.tgz +0 -0
- package/components/tryghost-email-analytics-service-5.17.1.tgz +0 -0
- package/components/tryghost-email-content-generator-5.17.1.tgz +0 -0
- package/components/tryghost-express-dynamic-redirects-5.17.1.tgz +0 -0
- package/components/tryghost-extract-api-key-5.17.1.tgz +0 -0
- package/components/tryghost-html-to-plaintext-5.17.1.tgz +0 -0
- package/components/tryghost-job-manager-5.17.1.tgz +0 -0
- package/components/tryghost-link-replacer-5.17.1.tgz +0 -0
- package/components/tryghost-link-tracking-5.17.1.tgz +0 -0
- package/components/tryghost-magic-link-5.17.1.tgz +0 -0
- package/components/tryghost-mailgun-client-5.17.1.tgz +0 -0
- package/components/tryghost-member-analytics-service-5.17.1.tgz +0 -0
- package/components/tryghost-member-events-5.17.1.tgz +0 -0
- package/components/tryghost-members-analytics-ingress-5.17.1.tgz +0 -0
- package/components/tryghost-members-api-5.17.1.tgz +0 -0
- package/components/tryghost-members-csv-5.17.1.tgz +0 -0
- package/components/tryghost-members-events-service-5.17.1.tgz +0 -0
- package/components/tryghost-members-importer-5.17.1.tgz +0 -0
- package/components/tryghost-members-offers-5.17.1.tgz +0 -0
- package/components/tryghost-members-payments-5.17.1.tgz +0 -0
- package/components/tryghost-members-ssr-5.17.1.tgz +0 -0
- package/components/tryghost-members-stripe-service-5.17.1.tgz +0 -0
- package/components/tryghost-minifier-5.17.1.tgz +0 -0
- package/components/tryghost-mw-api-version-mismatch-5.17.1.tgz +0 -0
- package/components/tryghost-mw-cache-control-5.17.1.tgz +0 -0
- package/components/tryghost-mw-session-from-token-5.17.1.tgz +0 -0
- package/components/tryghost-mw-update-user-last-seen-5.17.1.tgz +0 -0
- package/components/tryghost-mw-vhost-5.17.1.tgz +0 -0
- package/components/tryghost-referrers-5.17.1.tgz +0 -0
- package/components/tryghost-security-5.17.1.tgz +0 -0
- package/components/tryghost-settings-path-manager-5.17.1.tgz +0 -0
- package/components/tryghost-staff-service-5.17.1.tgz +0 -0
- package/components/tryghost-stats-service-5.17.1.tgz +0 -0
- package/components/tryghost-verification-trigger-5.17.1.tgz +0 -0
- package/core/built/admin/assets/ghost-597fb8e8b1b91dd0ac4d9f2d75bd67fb.css +0 -1
- package/core/built/admin/assets/ghost-dark-e4ccecd9903d35d360d71fe859cbb3bf.css +0 -1
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<title>Ghost Admin</title>
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.
|
|
11
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%225.18%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%7D" />
|
|
12
12
|
|
|
13
13
|
<meta name="HandheldFriendly" content="True" />
|
|
14
14
|
<meta name="MobileOptimized" content="320" />
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
}
|
|
37
37
|
</style>
|
|
38
38
|
|
|
39
|
-
<link integrity="" rel="stylesheet" href="assets/vendor-
|
|
40
|
-
<link integrity="" rel="stylesheet" href="assets/ghost-
|
|
39
|
+
<link integrity="" rel="stylesheet" href="assets/vendor-3e6947aa681f0fb82b193090e520dc73.css">
|
|
40
|
+
<link integrity="" rel="stylesheet" href="assets/ghost-ff0bee94743aa886ce35305a5b46fac3.css" title="light">
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
</head>
|
|
@@ -56,9 +56,9 @@
|
|
|
56
56
|
|
|
57
57
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
58
58
|
|
|
59
|
-
<script src="assets/vendor-
|
|
60
|
-
<script src="assets/chunk.
|
|
61
|
-
<script src="assets/chunk.143.
|
|
62
|
-
<script src="assets/ghost-
|
|
59
|
+
<script src="assets/vendor-4da5d2584fbe1442e25e4271a5513f1c.js"></script>
|
|
60
|
+
<script src="assets/chunk.427.4483d5bbdaf2a65888ac.js"></script>
|
|
61
|
+
<script src="assets/chunk.143.6d23a3157dae7a9c899d.js"></script>
|
|
62
|
+
<script src="assets/ghost-7e6e9479705e7e772bb5ea3b6476cd52.js"></script>
|
|
63
63
|
</body>
|
|
64
64
|
</html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{
|
|
1
|
+
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-family:sans-serif}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.black{color:#15171a}.darkgrey{color:#394047}.midgrey{color:#7c8b9a}.lightgrey{color:#ced4d9}.blue{color:#14b8ff}.red{color:#f50b23}.orange{color:#ffb41f}.green{color:#30cf43}.darkgrey-hover:hover{color:#394047}.midgrey-hover:hover{color:#7c8b9a}.lightgrey-hover:hover{color:#ced4d9}.blue-hover:hover{color:#14b8ff}.red-hover:hover{color:#f50b23}.orange-hover:hover{color:#ffb41f}.green-hover:hover{color:#30cf43}*,:after,:before{box-sizing:border-box}html{-webkit-tap-highlight-color:rgba(0,0,0,0);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:62.5%;letter-spacing:.2px;line-height:1.65;overflow:hidden}body,html{height:100%;width:100%}body{color:#343f44;font-size:1.4rem;overflow:auto;overflow-x:hidden}.gh-view{-ms-flex-positive:1;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;flex-grow:1}h1,h2{color:#343f44;font-size:2.9rem;line-height:1.15em;margin:0 0 .3em;text-indent:-1px;text-rendering:optimizeLegibility}@media (max-width:500px){h1{font-size:2.4rem}}.gh-input{-webkit-appearance:none;border:1px solid #d6e3eb;border-radius:4px;color:#4b5b62;display:block;font-size:1.6rem;font-weight:300;height:40px;line-height:1em;padding:10px 12px;transition:border-color .15s linear;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;width:100%}.gh-input:focus{border-color:#b4cbda;outline:0}.gh-btn{fill:#829aa8;-webkit-font-smoothing:subpixel-antialiased;border:1px solid #d6e3eb;border-radius:5px;color:#829aa8;display:inline-block;outline:none;text-decoration:none!important;transition:all .2s ease;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.gh-btn span{border-radius:4px;display:block;font-size:1.3rem;font-weight:400;height:33px;letter-spacing:.2px;line-height:33px;padding:0 12px;text-align:center}.gh-btn:hover{border-color:#b4cbda}.gh-btn-hover-blue:hover{border-color:#3eb0ef;color:#3eb0ef}.gh-btn-blue{fill:#fff;background:linear-gradient(#3da1d6,#2288bf);border:0;box-shadow:0 1px 0 rgba(0,0,0,.12);color:#fff;padding:1px;text-shadow:0 -1px 0 rgba(0,0,0,.1);transition:none!important}.gh-btn-blue span{background:linear-gradient(#4ab6f0,#2fa5e4 60%,#2fa5e4 90%,#38a9e5);box-shadow:inset 0 1px 0 hsla(0,0%,100%,.1)}.gh-btn-blue:active,.gh-btn-blue:focus{background:#1e78a9}.gh-btn-blue:active span,.gh-btn-blue:focus span{background:#29a0e0;box-shadow:none}.gh-btn-block{display:block;width:100%}.gh-input-icon{display:block;position:relative}.gh-input-icon svg{fill:color(var(--midgrey) l(15%));height:14px;left:10px;position:absolute;top:50%;transform:translateY(-7px);width:auto;z-index:2}.gh-input-icon input{padding-left:35px}.gh-app{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;height:100%;overflow:hidden}.gh-viewport{max-height:100%;overflow:hidden}.gh-main,.gh-viewport{-ms-flex-positive:1;display:-ms-flexbox;display:flex;flex-grow:1}.gh-main{background:#fff;overflow-y:auto;position:relative}.gh-flow{background:linear-gradient(315deg,#efefef,#fff);min-height:100%;overflow-y:auto}.gh-flow,.gh-flow-content-wrap{display:flex;flex-direction:column;flex-grow:1}.gh-flow-content-wrap{align-items:center;flex-shrink:0;justify-content:center;margin:0 24px;padding-bottom:8vh}.gh-flow-content-wrap .site-icon{border-radius:3px;height:70px;width:70px}.gh-flow-content{background:#fff;border-radius:3px;box-shadow:0 2.8px 2.2px rgba(0,0,0,.02),0 6.7px 5.3px rgba(0,0,0,.02),0 12.5px 10px rgba(0,0,0,.02),0 22.3px 17.9px rgba(0,0,0,.03),0 41.8px 33.4px rgba(0,0,0,.03),0 100px 80px rgba(0,0,0,.05);color:var(--darkgrey);display:flex;flex-direction:column;font-size:1.9rem;font-weight:300;line-height:1.5em;margin:4rem 0 6rem;max-width:520px;padding:40px;width:100%}.gh-flow-content.unsubscribe{align-items:center;justify-content:center;margin:4rem 0;max-width:560px;min-height:200px;text-align:center}@media (max-width:500px){.gh-flow-content{background:transparent;box-shadow:none;padding:0}}.gh-flow-content header{align-items:center;display:flex;flex-direction:column}.gh-flow-content h1{color:#15171a;font-size:4.1rem;font-weight:700;line-height:1.15em;margin-bottom:24px}.gh-flow-content.unsubscribe h1{font-size:3.2rem}@media (max-width:600px){.gh-flow-content h1,.gh-flow-content.unsubscribe h1{font-size:6vw}}.gh-flow-content.unsubscribe p{color:#394047;font-size:1.8rem;margin:0 0 .4em}@media (max-width:500px){.gh-flow-content.unsubscribe p{font-size:1.6rem;line-height:1.5}}.gh-flow-content .gh-btn{display:block;margin:20px auto 0;max-width:400px}.gh-flow-content .form-group{margin-bottom:2.5rem;position:relative}.gh-flow-content .form-group.error .gh-input{border-color:#f50b23;box-shadow:0 0 0 3px rgba(239,24,24,.15)}.gh-flow-content .main-error{color:#7c8b9a;font-size:1.35rem;font-weight:400;margin:0;position:absolute;right:0;text-align:center;user-select:text}.gh-flow-em{font-weight:500}.unsubscribe-footer{font-size:1.5rem;text-align:center}@media (max-width:500px){.unsubscribe-footer{font-size:1.4rem;line-height:1.4em;padding:0 24px}}.unsubscribe-footer p{color:#7c8b9a;margin:0 0 .4rem}.unsubscribe-footer a{color:#15171a;text-decoration:none}.unsubscribe-footer a:hover{text-decoration:underline}.gh-signin{margin-bottom:1.5rem}.gh-signin .gh-input,.gh-signin .gh-input:-webkit-autofill:first-line{border-radius:8px;font-size:1.8rem;height:54px;padding:12px 16px}.gh-signin .gh-input::placeholder{color:#abb4be;font-weight:400;opacity:1}.gh-signin .gh-input::-webkit-input-placeholder{color:#abb4be;font-weight:400}.gh-signin .gh-input:-ms-input-placeholder{color:#abb4be;font-weight:400}.gh-signin .gh-input::-moz-placeholder{color:#abb4be;font-weight:400;opacity:1}.gh-signin .gh-input:focus{border-color:#30cf43;box-shadow:0 0 0 3px rgba(26,170,96,.15)}.gh-signin .gh-btn{-webkit-font-smoothing:subpixel-antialiased;background:#15171a;border-radius:8px;font-weight:300;height:54px;line-height:54px;margin:0;margin-top:32px;max-width:unset;transition:all .4s ease;width:100%}.gh-signin .gh-btn span{color:#fff;font-size:1.8rem}.error-content{flex-grow:1;justify-content:center;padding:8vw;user-select:text}.error-content,.error-details{align-items:center;display:flex}.error-details{margin-bottom:4rem}.error-ghost{height:115px;margin:15px}@media (max-width:630px){.error-ghost{display:none}}.error-code{color:#c5d2d9;font-size:10vw;font-weight:600;letter-spacing:-.4vw;line-height:.9em;margin:0}.error-description{border:none;color:#54666d;font-size:2.3rem;font-weight:300;line-height:1.3em;margin:0;padding:0}.error-message{align-items:center;display:flex;flex-direction:column;margin:15px}.error-message a{font-size:1.4rem;line-height:1;margin:8px 0}.error-link{background-color:transparent;color:#5ba4e5;text-decoration:none;transition:background .3s,color .3s}.error-stack{background-color:hsla(0,0%,100%,.3);margin:1rem auto;max-width:800px;padding:2rem}.error-stack-list{list-style-type:none;margin:0;padding:0}.error-stack-list li{display:block}.error-stack-list li:before{color:#bbb;content:"\21AA";display:inline-block;font-size:1.2rem;margin-right:.5rem}.error-stack-function{font-weight:700}
|
|
@@ -32,6 +32,8 @@ function forwardToExpressStatic(req, res, next) {
|
|
|
32
32
|
|
|
33
33
|
const configMaxAge = config.get('caching:theme:maxAge');
|
|
34
34
|
|
|
35
|
+
// @NOTE: the maxAge config passed below are in milliseconds and the config
|
|
36
|
+
// is specified in seconds. See https://github.com/expressjs/serve-static/issues/150 for more context
|
|
35
37
|
express.static(themeEngine.getActive().path, {
|
|
36
38
|
maxAge: (configMaxAge || configMaxAge === 0) ? configMaxAge : constants.ONE_YEAR_MS
|
|
37
39
|
}
|
|
@@ -6,7 +6,6 @@ const localUtils = require('../../index');
|
|
|
6
6
|
const mobiledoc = require('../../../../../lib/mobiledoc');
|
|
7
7
|
const postsMetaSchema = require('../../../../../data/schema').tables.posts_meta;
|
|
8
8
|
const clean = require('./utils/clean');
|
|
9
|
-
const labs = require('../../../../../../shared/labs');
|
|
10
9
|
|
|
11
10
|
function removeSourceFormats(frame) {
|
|
12
11
|
if (frame.options.formats?.includes('mobiledoc') || frame.options.formats?.includes('lexical')) {
|
|
@@ -25,11 +24,7 @@ function defaultRelations(frame) {
|
|
|
25
24
|
return false;
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.signups', 'count.paid_conversions', 'count.clicks'];
|
|
30
|
-
} else {
|
|
31
|
-
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.signups', 'count.paid_conversions'];
|
|
32
|
-
}
|
|
27
|
+
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.signups', 'count.paid_conversions', 'count.clicks'];
|
|
33
28
|
}
|
|
34
29
|
|
|
35
30
|
function setDefaultOrder(frame) {
|
|
@@ -10,7 +10,6 @@ const extraAttrs = require('../utils/extra-attrs');
|
|
|
10
10
|
const gating = require('../utils/post-gating');
|
|
11
11
|
const url = require('../utils/url');
|
|
12
12
|
|
|
13
|
-
const labs = require('../../../../../../../shared/labs');
|
|
14
13
|
const utils = require('../../../index');
|
|
15
14
|
|
|
16
15
|
const postsMetaSchema = require('../../../../../../data/schema').tables.posts_meta;
|
|
@@ -110,20 +109,14 @@ module.exports = async (model, frame, options = {}) => {
|
|
|
110
109
|
});
|
|
111
110
|
}
|
|
112
111
|
|
|
113
|
-
if (
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (!labs.isSet('memberAttribution') && !labs.isSet('emailClicks')) {
|
|
126
|
-
delete jsonModel.count;
|
|
112
|
+
if (jsonModel.email && jsonModel.count) {
|
|
113
|
+
jsonModel.email.opened_count = Math.min(
|
|
114
|
+
Math.max(
|
|
115
|
+
jsonModel.email.opened_count || 0,
|
|
116
|
+
jsonModel.count.clicks || 0
|
|
117
|
+
),
|
|
118
|
+
jsonModel.email.email_count
|
|
119
|
+
);
|
|
127
120
|
}
|
|
128
121
|
|
|
129
122
|
return jsonModel;
|
|
@@ -157,6 +157,7 @@ const createSessionFromMagicLink = async function (req, res, next) {
|
|
|
157
157
|
try {
|
|
158
158
|
const member = await membersService.ssr.exchangeTokenForSession(req, res);
|
|
159
159
|
spamPrevention.membersAuth().reset(req.ip, `${member.email}login`);
|
|
160
|
+
// Note: don't reset 'member_login', or that would give an easy way around user enumeration by logging in to a manually created account
|
|
160
161
|
const subscriptions = member && member.subscriptions || [];
|
|
161
162
|
|
|
162
163
|
const action = req.query.action;
|
|
@@ -13,7 +13,7 @@ module.exports = (event, model) => {
|
|
|
13
13
|
ops.push(() => {
|
|
14
14
|
let frame = {options: {previous: false, context: {user: true}}};
|
|
15
15
|
|
|
16
|
-
// NOTE: below options are lost
|
|
16
|
+
// @NOTE: below options are lost during event processing, a more holistic approach would be
|
|
17
17
|
// to pass them somehow along with the model
|
|
18
18
|
if (['posts', 'pages'].includes(docName)) {
|
|
19
19
|
frame.options.formats = ['mobiledoc', 'html', 'plaintext'];
|
|
@@ -20,6 +20,8 @@ module.exports = function setupAdminApp() {
|
|
|
20
20
|
// @NOTE: when we start working on HTTP/3 optimizations the immutable headers
|
|
21
21
|
// produced below should be split into separate 'Cache-Control' entry.
|
|
22
22
|
// For reference see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#validation_2
|
|
23
|
+
// @NOTE: the maxAge config passed below are in milliseconds and the config
|
|
24
|
+
// is specified in seconds. See https://github.com/expressjs/serve-static/issues/150 for more context
|
|
23
25
|
adminApp.use('/assets', serveStatic(
|
|
24
26
|
path.join(config.get('paths').adminAssets, 'assets'), {
|
|
25
27
|
maxAge: (configMaxAge || configMaxAge === 0) ? configMaxAge : constants.ONE_YEAR_MS,
|
|
@@ -3,7 +3,6 @@ const api = require('../../../../api').endpoints;
|
|
|
3
3
|
const {http} = require('@tryghost/api-framework');
|
|
4
4
|
const apiMw = require('../../middleware');
|
|
5
5
|
const mw = require('./middleware');
|
|
6
|
-
const labs = require('../../../../../shared/labs');
|
|
7
6
|
|
|
8
7
|
const shared = require('../../../shared');
|
|
9
8
|
|
|
@@ -310,7 +309,7 @@ module.exports = function apiRoutes() {
|
|
|
310
309
|
router.put('/newsletters/verifications/', mw.authAdminApi, http(api.newsletters.verifyPropertyUpdate));
|
|
311
310
|
router.put('/newsletters/:id', mw.authAdminApi, http(api.newsletters.edit));
|
|
312
311
|
|
|
313
|
-
router.get('/links',
|
|
312
|
+
router.get('/links', mw.authAdminApi, http(api.links.browse));
|
|
314
313
|
|
|
315
314
|
return router;
|
|
316
315
|
};
|
|
@@ -48,7 +48,15 @@ module.exports = function setupMembersApp() {
|
|
|
48
48
|
membersApp.delete('/api/session', middleware.deleteSession);
|
|
49
49
|
|
|
50
50
|
// NOTE: this is wrapped in a function to ensure we always go via the getter
|
|
51
|
-
membersApp.post(
|
|
51
|
+
membersApp.post(
|
|
52
|
+
'/api/send-magic-link',
|
|
53
|
+
bodyParser.json(),
|
|
54
|
+
// Prevent brute forcing email addresses (user enumeration)
|
|
55
|
+
shared.middleware.brute.membersAuthEnumeration,
|
|
56
|
+
// Prevent brute forcing passwords for the same email address
|
|
57
|
+
shared.middleware.brute.membersAuth,
|
|
58
|
+
(req, res, next) => membersService.api.middleware.sendMagicLink(req, res, next)
|
|
59
|
+
);
|
|
52
60
|
membersApp.post('/api/create-stripe-checkout-session', (req, res, next) => membersService.api.middleware.createCheckoutSession(req, res, next));
|
|
53
61
|
membersApp.post('/api/create-stripe-update-session', (req, res, next) => membersService.api.middleware.createCheckoutSetupSession(req, res, next));
|
|
54
62
|
membersApp.put('/api/subscriptions/:id', (req, res, next) => membersService.api.middleware.updateSubscription(req, res, next));
|
|
@@ -5,7 +5,7 @@ const errors = require('@tryghost/errors');
|
|
|
5
5
|
const config = require('../../../../../shared/config');
|
|
6
6
|
const tpl = require('@tryghost/tpl');
|
|
7
7
|
const logging = require('@tryghost/logging');
|
|
8
|
-
|
|
8
|
+
let spam = config.get('spam') || {};
|
|
9
9
|
|
|
10
10
|
const messages = {
|
|
11
11
|
forgottenPasswordEmail: {
|
|
@@ -22,13 +22,13 @@ const messages = {
|
|
|
22
22
|
},
|
|
23
23
|
tooManyAttempts: 'Too many attempts.'
|
|
24
24
|
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
let spamPrivateBlock = spam.private_block || {};
|
|
26
|
+
let spamGlobalBlock = spam.global_block || {};
|
|
27
|
+
let spamGlobalReset = spam.global_reset || {};
|
|
28
|
+
let spamUserReset = spam.user_reset || {};
|
|
29
|
+
let spamUserLogin = spam.user_login || {};
|
|
30
|
+
let spamMemberLogin = spam.member_login || {};
|
|
31
|
+
let spamContentApiKey = spam.content_api_key || {};
|
|
32
32
|
|
|
33
33
|
let store;
|
|
34
34
|
let memoryStore;
|
|
@@ -37,6 +37,7 @@ let globalResetInstance;
|
|
|
37
37
|
let globalBlockInstance;
|
|
38
38
|
let userLoginInstance;
|
|
39
39
|
let membersAuthInstance;
|
|
40
|
+
let membersAuthEnumerationInstance;
|
|
40
41
|
let userResetInstance;
|
|
41
42
|
let contentApiKeyInstance;
|
|
42
43
|
|
|
@@ -152,6 +153,39 @@ const membersAuth = () => {
|
|
|
152
153
|
return membersAuthInstance;
|
|
153
154
|
};
|
|
154
155
|
|
|
156
|
+
/**
|
|
157
|
+
* This one should have higher limits because it checks across all email addresses
|
|
158
|
+
*/
|
|
159
|
+
const membersAuthEnumeration = () => {
|
|
160
|
+
const ExpressBrute = require('express-brute');
|
|
161
|
+
const BruteKnex = require('brute-knex');
|
|
162
|
+
const db = require('../../../../data/db');
|
|
163
|
+
|
|
164
|
+
store = store || new BruteKnex({
|
|
165
|
+
tablename: 'brute',
|
|
166
|
+
createTable: false,
|
|
167
|
+
knex: db.knex
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (!membersAuthEnumerationInstance) {
|
|
171
|
+
membersAuthEnumerationInstance = new ExpressBrute(store,
|
|
172
|
+
extend({
|
|
173
|
+
attachResetToRequest: true,
|
|
174
|
+
failCallback(req, res, next, nextValidRequestDate) {
|
|
175
|
+
return next(new errors.TooManyRequestsError({
|
|
176
|
+
message: `Too many different sign-in attempts try again in ${moment(nextValidRequestDate).fromNow(true)}`,
|
|
177
|
+
context: tpl(messages.tooManySigninAttempts.context),
|
|
178
|
+
help: tpl(messages.tooManySigninAttempts.context)
|
|
179
|
+
}));
|
|
180
|
+
},
|
|
181
|
+
handleStoreError: handleStoreError
|
|
182
|
+
}, pick(spamMemberLogin, spamConfigKeys))
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return membersAuthEnumerationInstance;
|
|
187
|
+
};
|
|
188
|
+
|
|
155
189
|
// Stops login attempts for a user+IP pair with an increasing time period starting from 10 minutes
|
|
156
190
|
// and rising to a week in a fibonnaci sequence
|
|
157
191
|
// The user+IP count is reset when on successful login
|
|
@@ -281,7 +315,29 @@ module.exports = {
|
|
|
281
315
|
globalReset: globalReset,
|
|
282
316
|
userLogin: userLogin,
|
|
283
317
|
membersAuth: membersAuth,
|
|
318
|
+
membersAuthEnumeration: membersAuthEnumeration,
|
|
284
319
|
userReset: userReset,
|
|
285
320
|
privateBlog: privateBlog,
|
|
286
|
-
contentApiKey: contentApiKey
|
|
321
|
+
contentApiKey: contentApiKey,
|
|
322
|
+
reset: () => {
|
|
323
|
+
store = undefined;
|
|
324
|
+
memoryStore = undefined;
|
|
325
|
+
privateBlogInstance = undefined;
|
|
326
|
+
globalResetInstance = undefined;
|
|
327
|
+
globalBlockInstance = undefined;
|
|
328
|
+
userLoginInstance = undefined;
|
|
329
|
+
membersAuthInstance = undefined;
|
|
330
|
+
membersAuthEnumerationInstance = undefined;
|
|
331
|
+
userResetInstance = undefined;
|
|
332
|
+
contentApiKeyInstance = undefined;
|
|
333
|
+
|
|
334
|
+
spam = config.get('spam') || {};
|
|
335
|
+
spamPrivateBlock = spam.private_block || {};
|
|
336
|
+
spamGlobalBlock = spam.global_block || {};
|
|
337
|
+
spamGlobalReset = spam.global_reset || {};
|
|
338
|
+
spamUserReset = spam.user_reset || {};
|
|
339
|
+
spamUserLogin = spam.user_login || {};
|
|
340
|
+
spamMemberLogin = spam.member_login || {};
|
|
341
|
+
spamContentApiKey = spam.content_api_key || {};
|
|
342
|
+
}
|
|
287
343
|
};
|
|
@@ -84,6 +84,7 @@ module.exports = {
|
|
|
84
84
|
},
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
+
* Block too many password guesses for the same email address
|
|
87
88
|
*/
|
|
88
89
|
membersAuth(req, res, next) {
|
|
89
90
|
return spamPrevention.membersAuth().getMiddleware({
|
|
@@ -96,5 +97,12 @@ module.exports = {
|
|
|
96
97
|
return _next();
|
|
97
98
|
}
|
|
98
99
|
})(req, res, next);
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Blocks user enumeration
|
|
104
|
+
*/
|
|
105
|
+
membersAuthEnumeration(req, res, next) {
|
|
106
|
+
return spamPrevention.membersAuthEnumeration().prevent(req, res, next);
|
|
99
107
|
}
|
|
100
108
|
};
|
|
@@ -99,6 +99,12 @@
|
|
|
99
99
|
"maxWait": 86400000,
|
|
100
100
|
"lifetime": 3600,
|
|
101
101
|
"freeRetries": 99
|
|
102
|
+
},
|
|
103
|
+
"member_login": {
|
|
104
|
+
"minWait": 600000,
|
|
105
|
+
"maxWait": 43200000,
|
|
106
|
+
"lifetime": 43200,
|
|
107
|
+
"freeRetries": 8
|
|
102
108
|
}
|
|
103
109
|
},
|
|
104
110
|
"caching": {
|
|
@@ -152,7 +158,7 @@
|
|
|
152
158
|
},
|
|
153
159
|
"portal": {
|
|
154
160
|
"url": "https://cdn.jsdelivr.net/ghost/portal@~{version}/umd/portal.min.js",
|
|
155
|
-
"version": "2.
|
|
161
|
+
"version": "2.14"
|
|
156
162
|
},
|
|
157
163
|
"sodoSearch": {
|
|
158
164
|
"url": "https://cdn.jsdelivr.net/ghost/sodo-search@~{version}/umd/sodo-search.min.js",
|
|
@@ -162,7 +168,7 @@
|
|
|
162
168
|
"comments": {
|
|
163
169
|
"url": "https://cdn.jsdelivr.net/ghost/comments-ui@~{version}/umd/comments-ui.min.js",
|
|
164
170
|
"styles": "https://cdn.jsdelivr.net/ghost/comments-ui@~{version}/umd/main.css",
|
|
165
|
-
"version": "0.10
|
|
171
|
+
"version": "0.10"
|
|
166
172
|
},
|
|
167
173
|
"editor": {
|
|
168
174
|
"url": "https://unpkg.com/@tryghost/koenig-lexical@~{version}/dist/koenig-lexical.umd.js",
|
package/core/shared/labs.js
CHANGED
|
@@ -19,8 +19,7 @@ const GA_FEATURES = [
|
|
|
19
19
|
'freeTrial',
|
|
20
20
|
'compExpiring',
|
|
21
21
|
'searchHelper',
|
|
22
|
-
'emailAlerts'
|
|
23
|
-
'emailClicks'
|
|
22
|
+
'emailAlerts'
|
|
24
23
|
];
|
|
25
24
|
|
|
26
25
|
// NOTE: this allowlist is meant to be used to filter out any unexpected
|
|
@@ -34,7 +33,8 @@ const ALPHA_FEATURES = [
|
|
|
34
33
|
'urlCache',
|
|
35
34
|
'beforeAfterCard',
|
|
36
35
|
'sourceAttribution',
|
|
37
|
-
'lexicalEditor'
|
|
36
|
+
'lexicalEditor',
|
|
37
|
+
'exploreApp'
|
|
38
38
|
];
|
|
39
39
|
|
|
40
40
|
module.exports.GA_KEYS = [...GA_FEATURES];
|