ghost 4.41.2 → 4.42.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/boot.js +24 -6
- package/core/built/assets/ghost-dark-a93afb20027060d760ac6d78f115a76f.css +1 -0
- package/core/built/assets/{ghost.min-8e2e6c7a01fde044c566c1650a36bfc2.js → ghost.min-20096eef632760c3a2906e243adbd24b.js} +1035 -841
- package/core/built/assets/ghost.min-ce35ef1b76d9a943ab912c076773b132.css +1 -0
- package/core/built/assets/{vendor.min-9094db77ba3190cb10876f8e42e1d90d.js → vendor.min-21f79c68a284acb1b70039f3f63e5507.js} +68 -68
- package/core/built/assets/{vendor.min-2c8ad32b7960bb605ebc20097fee5ebd.css → vendor.min-ba66b98f7c24fa40e061c7ffc94f4e23.css} +214 -0
- package/core/frontend/helpers/price.js +1 -0
- package/core/frontend/web/middleware/error-handler.js +5 -3
- package/core/server/api/canary/email-preview.js +2 -1
- package/core/server/api/canary/{email.js → emails.js} +0 -0
- package/core/server/api/canary/index.js +11 -3
- package/core/server/api/canary/{memberSigninUrls.js → member-signin-urls.js} +0 -1
- package/core/server/api/canary/{membersStripeConnect.js → members-stripe-connect.js} +0 -0
- package/core/server/api/canary/members.js +0 -45
- package/core/server/api/canary/newsletters.js +45 -0
- package/core/server/api/canary/stats.js +14 -0
- package/core/server/api/canary/utils/serializers/output/authentication.js +4 -0
- package/core/server/api/canary/utils/serializers/output/default.js +35 -0
- package/core/server/api/canary/utils/serializers/output/email-previews.js +7 -0
- package/core/server/api/canary/utils/serializers/output/index.js +18 -42
- package/core/server/api/canary/utils/serializers/output/mappers/authors.js +1 -0
- package/core/server/api/canary/utils/serializers/output/mappers/index.js +2 -1
- package/core/server/api/canary/utils/serializers/output/mappers/integrations.js +1 -1
- package/core/server/api/canary/utils/serializers/output/mappers/snippets.js +36 -0
- package/core/server/api/canary/utils/serializers/output/members-stripe-connect.js +6 -0
- package/core/server/api/canary/utils/serializers/output/members.js +2 -2
- package/core/server/api/canary/utils/serializers/output/oembed.js +2 -2
- package/core/server/api/canary/utils/serializers/output/offers.js +8 -0
- package/core/server/api/canary/utils/serializers/output/redirects.js +2 -2
- package/core/server/api/canary/utils/serializers/output/schedules.js +2 -2
- package/core/server/api/canary/utils/serializers/output/session.js +9 -0
- package/core/server/api/canary/utils/serializers/output/settings.js +64 -37
- package/core/server/api/canary/utils/serializers/output/slack.js +9 -0
- package/core/server/api/canary/utils/serializers/output/themes.js +3 -24
- package/core/server/api/canary/utils/serializers/output/users.js +0 -23
- package/core/server/api/shared/serializers/handle.js +25 -11
- package/core/server/data/exporter/table-lists.js +1 -0
- package/core/server/data/migrations/utils.js +1 -1
- package/core/server/data/migrations/versions/4.42/2022-03-21-17-17-add.js +20 -0
- package/core/server/data/migrations/versions/4.42/2022-03-30-15-44-add-newsletter-permissions.js +28 -0
- package/core/server/data/schema/commands.js +13 -13
- package/core/server/data/schema/schema.js +18 -0
- package/core/server/models/newsletter.js +9 -0
- package/core/server/services/mega/template.js +25 -13
- package/core/server/services/members/service.js +2 -1
- package/core/server/services/offers/service.js +11 -8
- package/core/server/services/stats/index.js +1 -0
- package/core/server/services/stats/lib/members-stats-service.js +165 -0
- package/core/server/services/stats/service.js +6 -0
- package/core/server/services/themes/validate.js +4 -3
- package/core/server/services/webhooks/webhooks-service.js +3 -1
- package/core/server/web/admin/app.js +8 -0
- package/core/server/web/admin/views/default-prod.html +5 -5
- package/core/server/web/admin/views/default.html +5 -5
- package/core/server/web/api/canary/admin/routes.js +8 -2
- package/core/shared/config/defaults.json +2 -2
- package/core/shared/config/env/config.development.json +26 -0
- package/core/shared/config/env/config.production.json +21 -0
- package/core/shared/config/env/config.testing-mysql.json +59 -0
- package/core/shared/config/env/config.testing.json +58 -0
- package/core/shared/labs.js +3 -1
- package/core/shared/settings-cache/cache.js +1 -1
- package/package.json +62 -62
- package/yarn.lock +1586 -1757
- package/.c8rc.json +0 -34
- package/.eslintrc.js +0 -118
- package/core/built/assets/ghost-dark-6fbe502f2bb2cde92e15b2f1a9da57a0.css +0 -1
- package/core/built/assets/ghost.min-09301e5bd933cf6d24368e98a4d898a9.css +0 -1
- package/core/server/api/canary/utils/serializers/output/actions.js +0 -13
- package/core/server/api/canary/utils/serializers/output/authors.js +0 -21
- package/core/server/api/canary/utils/serializers/output/email-preview.js +0 -7
- package/core/server/api/canary/utils/serializers/output/emails.js +0 -22
- package/core/server/api/canary/utils/serializers/output/identities.js +0 -7
- package/core/server/api/canary/utils/serializers/output/integrations.js +0 -34
- package/core/server/api/canary/utils/serializers/output/invites.js +0 -24
- package/core/server/api/canary/utils/serializers/output/labels.js +0 -25
- package/core/server/api/canary/utils/serializers/output/mappers/labels.js +0 -4
- package/core/server/api/canary/utils/serializers/output/member-signin_urls.js +0 -7
- package/core/server/api/canary/utils/serializers/output/snippets.js +0 -97
- package/core/server/api/canary/utils/serializers/output/tags.js +0 -25
- package/core/server/api/canary/utils/serializers/output/webhooks.js +0 -15
- package/jsconfig.json +0 -13
|
@@ -19,6 +19,7 @@ const VerificationTrigger = require('@tryghost/verification-trigger');
|
|
|
19
19
|
const DomainEvents = require('@tryghost/domain-events');
|
|
20
20
|
const {LastSeenAtUpdater} = require('@tryghost/members-events-service');
|
|
21
21
|
const events = require('../../lib/common/events');
|
|
22
|
+
const DatabaseInfo = require('@tryghost/database-info');
|
|
22
23
|
|
|
23
24
|
const messages = {
|
|
24
25
|
noLiveKeysInDevelopment: 'Cannot use live stripe keys in development. Please restart in production mode.',
|
|
@@ -37,7 +38,7 @@ const membersConfig = new MembersConfigProvider({
|
|
|
37
38
|
const membersStats = new MembersStats({
|
|
38
39
|
db: db,
|
|
39
40
|
settingsCache: settingsCache,
|
|
40
|
-
isSQLite:
|
|
41
|
+
isSQLite: DatabaseInfo.isSQLite(db.knex)
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
let membersApi;
|
|
@@ -5,19 +5,20 @@ const config = require('../../../shared/config');
|
|
|
5
5
|
const urlUtils = require('../../../shared/url-utils');
|
|
6
6
|
const models = require('../../models');
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
permanentMaxAge: config.get('caching:customRedirects:maxAge'),
|
|
10
|
-
getSubdirectoryURL: (pathname) => {
|
|
11
|
-
return urlUtils.urlJoin(urlUtils.getSubdir(), pathname);
|
|
12
|
-
}
|
|
13
|
-
});
|
|
8
|
+
let redirectManager;
|
|
14
9
|
|
|
15
10
|
module.exports = {
|
|
16
11
|
async init() {
|
|
12
|
+
redirectManager = new DynamicRedirectManager({
|
|
13
|
+
permanentMaxAge: config.get('caching:customRedirects:maxAge'),
|
|
14
|
+
getSubdirectoryURL: (pathname) => {
|
|
15
|
+
return urlUtils.urlJoin(urlUtils.getSubdir(), pathname);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
17
18
|
const offersModule = OffersModule.create({
|
|
18
19
|
OfferModel: models.Offer,
|
|
19
20
|
OfferRedemptionModel: models.OfferRedemption,
|
|
20
|
-
redirectManager
|
|
21
|
+
redirectManager
|
|
21
22
|
});
|
|
22
23
|
|
|
23
24
|
this.api = offersModule.api;
|
|
@@ -27,5 +28,7 @@ module.exports = {
|
|
|
27
28
|
|
|
28
29
|
api: null,
|
|
29
30
|
|
|
30
|
-
middleware
|
|
31
|
+
get middleware() {
|
|
32
|
+
return redirectManager.handleRequest;
|
|
33
|
+
}
|
|
31
34
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./service');
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
const {DateTime} = require('luxon');
|
|
2
|
+
|
|
3
|
+
class MembersStatsService {
|
|
4
|
+
constructor({db}) {
|
|
5
|
+
this.db = db;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get the current total members grouped by status
|
|
10
|
+
* @returns {Promise<TotalMembersByStatus>}
|
|
11
|
+
*/
|
|
12
|
+
async getCount() {
|
|
13
|
+
const knex = this.db.knex;
|
|
14
|
+
const rows = await knex('members')
|
|
15
|
+
.select('status')
|
|
16
|
+
.select(knex.raw('COUNT(id) AS total'))
|
|
17
|
+
.groupBy('status');
|
|
18
|
+
|
|
19
|
+
const paidEvent = rows.find(c => c.status === 'paid');
|
|
20
|
+
const freeEvent = rows.find(c => c.status === 'free');
|
|
21
|
+
const compedEvent = rows.find(c => c.status === 'comped');
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
paid: paidEvent ? paidEvent.total : 0,
|
|
25
|
+
free: freeEvent ? freeEvent.total : 0,
|
|
26
|
+
comped: compedEvent ? compedEvent.total : 0
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get the member deltas by status for all days (from new to old)
|
|
32
|
+
* @returns {Promise<MemberStatusDelta[]>} The deltas of paid, free and comped users per day, sorted from new to old
|
|
33
|
+
*/
|
|
34
|
+
async fetchAllStatusDeltas() {
|
|
35
|
+
const knex = this.db.knex;
|
|
36
|
+
const rows = await knex('members_status_events')
|
|
37
|
+
.select(knex.raw('DATE(created_at) as date'))
|
|
38
|
+
.select(knex.raw(`SUM(
|
|
39
|
+
CASE WHEN to_status='paid' THEN 1
|
|
40
|
+
ELSE 0 END
|
|
41
|
+
) as paid_subscribed`))
|
|
42
|
+
.select(knex.raw(`SUM(
|
|
43
|
+
CASE WHEN from_status='paid' THEN 1
|
|
44
|
+
ELSE 0 END
|
|
45
|
+
) as paid_canceled`))
|
|
46
|
+
.select(knex.raw(`SUM(
|
|
47
|
+
CASE WHEN to_status='comped' THEN 1
|
|
48
|
+
WHEN from_status='comped' THEN -1
|
|
49
|
+
ELSE 0 END
|
|
50
|
+
) as comped_delta`))
|
|
51
|
+
.select(knex.raw(`SUM(
|
|
52
|
+
CASE WHEN to_status='free' THEN 1
|
|
53
|
+
WHEN from_status='free' THEN -1
|
|
54
|
+
ELSE 0 END
|
|
55
|
+
) as free_delta`))
|
|
56
|
+
.groupByRaw('DATE(created_at)')
|
|
57
|
+
.orderByRaw('DATE(created_at) DESC');
|
|
58
|
+
return rows;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Returns a list of the total members by status for each day, including the paid deltas paid_subscribed and paid_canceled
|
|
63
|
+
* @returns {Promise<CountHistory>}
|
|
64
|
+
*/
|
|
65
|
+
async getCountHistory() {
|
|
66
|
+
const rows = await this.fetchAllStatusDeltas();
|
|
67
|
+
|
|
68
|
+
// Fetch current total amounts and start counting from there
|
|
69
|
+
const totals = await this.getCount();
|
|
70
|
+
let {paid, free, comped} = totals;
|
|
71
|
+
|
|
72
|
+
// Get today in UTC (default timezone for Luxon)
|
|
73
|
+
const today = DateTime.local().toISODate();
|
|
74
|
+
|
|
75
|
+
const cumulativeResults = [];
|
|
76
|
+
for (const row of rows) {
|
|
77
|
+
// Convert JSDates to YYYY-MM-DD (in UTC)
|
|
78
|
+
const date = DateTime.fromJSDate(row.date).toISODate();
|
|
79
|
+
if (date > today) {
|
|
80
|
+
// Skip results that are in the future (fix for invalid events)
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
cumulativeResults.unshift({
|
|
84
|
+
date,
|
|
85
|
+
paid,
|
|
86
|
+
free,
|
|
87
|
+
comped,
|
|
88
|
+
|
|
89
|
+
// Deltas
|
|
90
|
+
paid_subscribed: row.paid_subscribed,
|
|
91
|
+
paid_canceled: row.paid_canceled
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Update current counts
|
|
95
|
+
paid = Math.max(0, paid - row.paid_subscribed + row.paid_canceled);
|
|
96
|
+
free = Math.max(0, free - row.free_delta);
|
|
97
|
+
comped = Math.max(0, comped - row.comped_delta);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Always make sure we have at least one result
|
|
101
|
+
if (cumulativeResults.length === 0) {
|
|
102
|
+
cumulativeResults.push({
|
|
103
|
+
date: today,
|
|
104
|
+
paid,
|
|
105
|
+
free,
|
|
106
|
+
comped,
|
|
107
|
+
|
|
108
|
+
// Deltas
|
|
109
|
+
paid_subscribed: 0,
|
|
110
|
+
paid_canceled: 0
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
data: cumulativeResults,
|
|
116
|
+
meta: {
|
|
117
|
+
pagination: {
|
|
118
|
+
page: 1,
|
|
119
|
+
limit: 'all',
|
|
120
|
+
pages: 1,
|
|
121
|
+
total: cumulativeResults.length,
|
|
122
|
+
next: null,
|
|
123
|
+
prev: null
|
|
124
|
+
},
|
|
125
|
+
totals
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = MembersStatsService;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @typedef MemberStatusDelta
|
|
135
|
+
* @type {Object}
|
|
136
|
+
* @property {Date} date
|
|
137
|
+
* @property {number} paid_subscribed Paid members that subscribed on this day
|
|
138
|
+
* @property {number} paid_canceled Paid members that canceled on this day
|
|
139
|
+
* @property {number} comped_delta Total net comped members on this day
|
|
140
|
+
* @property {number} free_delta Total net members on this day
|
|
141
|
+
*/
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @typedef TotalMembersByStatus
|
|
145
|
+
* @type {Object}
|
|
146
|
+
* @property {number} paid Total paid members
|
|
147
|
+
* @property {number} free Total free members
|
|
148
|
+
* @property {number} comped Total comped members
|
|
149
|
+
*/
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @typedef {Object} TotalMembersByStatusItem
|
|
153
|
+
* @property {string} date In YYYY-MM-DD format
|
|
154
|
+
* @property {number} paid Total paid members
|
|
155
|
+
* @property {number} free Total free members
|
|
156
|
+
* @property {number} comped Total comped members
|
|
157
|
+
* @property {number} paid_subscribed Paid members that subscribed on this day
|
|
158
|
+
* @property {number} paid_canceled Paid members that canceled on this day
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @typedef {Object} CountHistory
|
|
163
|
+
* @property {TotalMembersByStatusItem[]} data List of the total members by status for each day, including the paid deltas paid_subscribed and paid_canceled
|
|
164
|
+
* @property {Object} meta
|
|
165
|
+
*/
|
|
@@ -22,26 +22,27 @@ const check = async function check(theme, isZip) {
|
|
|
22
22
|
debug('Begin: Check');
|
|
23
23
|
// gscan can slow down boot time if we require on boot, for now nest the require.
|
|
24
24
|
const gscan = require('gscan');
|
|
25
|
+
const checkedVersion = 'v4';
|
|
25
26
|
let checkedTheme;
|
|
26
27
|
|
|
27
28
|
if (isZip) {
|
|
28
29
|
debug('zip mode');
|
|
29
30
|
checkedTheme = await gscan.checkZip(theme, {
|
|
30
31
|
keepExtractedDir: true,
|
|
31
|
-
checkVersion:
|
|
32
|
+
checkVersion: checkedVersion,
|
|
32
33
|
labs: labs.getAll()
|
|
33
34
|
});
|
|
34
35
|
} else {
|
|
35
36
|
debug('non-zip mode');
|
|
36
37
|
checkedTheme = await gscan.check(theme.path, {
|
|
37
|
-
checkVersion:
|
|
38
|
+
checkVersion: checkedVersion,
|
|
38
39
|
labs: labs.getAll()
|
|
39
40
|
});
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
checkedTheme = gscan.format(checkedTheme, {
|
|
43
44
|
onlyFatalErrors: config.get('env') === 'production',
|
|
44
|
-
checkVersion:
|
|
45
|
+
checkVersion: checkedVersion
|
|
45
46
|
});
|
|
46
47
|
|
|
47
48
|
debug('End: Check');
|
|
@@ -32,7 +32,9 @@ class WebhooksService {
|
|
|
32
32
|
const newWebhook = await this.WebhookModel.add(data.webhooks[0], options);
|
|
33
33
|
return newWebhook;
|
|
34
34
|
} catch (error) {
|
|
35
|
-
if (error.errno === 1452
|
|
35
|
+
if (error.errno === 1452
|
|
36
|
+
|| (error.code === 'SQLITE_CONSTRAINT' && /SQLITE_CONSTRAINT: FOREIGN KEY constraint failed/.test(error.message))
|
|
37
|
+
|| (error.code === 'SQLITE_CONSTRAINT_FOREIGNKEY')) {
|
|
36
38
|
throw new ValidationError({
|
|
37
39
|
message: tpl(messages.nonExistingIntegrationIdProvided.message, {
|
|
38
40
|
key: 'integration_id'
|
|
@@ -46,6 +46,14 @@ module.exports = function setupAdminApp() {
|
|
|
46
46
|
// Finally, routing
|
|
47
47
|
adminApp.get('*', require('./controller'));
|
|
48
48
|
|
|
49
|
+
adminApp.use((err, req, res, next) => {
|
|
50
|
+
if (err.statusCode && err.statusCode === 404) {
|
|
51
|
+
// Remove 404 errors for next middleware to inject
|
|
52
|
+
next();
|
|
53
|
+
} else {
|
|
54
|
+
next(err);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
49
57
|
adminApp.use(errorHandler.pageNotFound);
|
|
50
58
|
adminApp.use(errorHandler.handleHTMLResponse(sentry));
|
|
51
59
|
|
|
@@ -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%2F%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%224.
|
|
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%2F%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%224.42%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%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" />
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
</style>
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
<link rel="stylesheet" href="assets/vendor.min-
|
|
41
|
-
<link rel="stylesheet" href="assets/ghost.min-
|
|
40
|
+
<link rel="stylesheet" href="assets/vendor.min-ba66b98f7c24fa40e061c7ffc94f4e23.css">
|
|
41
|
+
<link rel="stylesheet" href="assets/ghost.min-ce35ef1b76d9a943ab912c076773b132.css" title="light">
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
<script src="assets/vendor.min-
|
|
60
|
-
<script src="assets/ghost.min-
|
|
59
|
+
<script src="assets/vendor.min-21f79c68a284acb1b70039f3f63e5507.js"></script>
|
|
60
|
+
<script src="assets/ghost.min-20096eef632760c3a2906e243adbd24b.js"></script>
|
|
61
61
|
|
|
62
62
|
</body>
|
|
63
63
|
</html>
|
|
@@ -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%2F%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%224.
|
|
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%2F%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%224.42%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22moment%22%3A%7B%22includeTimezone%22%3A%22all%22%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" />
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
</style>
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
<link rel="stylesheet" href="assets/vendor.min-
|
|
41
|
-
<link rel="stylesheet" href="assets/ghost.min-
|
|
40
|
+
<link rel="stylesheet" href="assets/vendor.min-ba66b98f7c24fa40e061c7ffc94f4e23.css">
|
|
41
|
+
<link rel="stylesheet" href="assets/ghost.min-ce35ef1b76d9a943ab912c076773b132.css" title="light">
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
<div id="ember-basic-dropdown-wormhole"></div>
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
<script src="assets/vendor.min-
|
|
60
|
-
<script src="assets/ghost.min-
|
|
59
|
+
<script src="assets/vendor.min-21f79c68a284acb1b70039f3f63e5507.js"></script>
|
|
60
|
+
<script src="assets/ghost.min-20096eef632760c3a2906e243adbd24b.js"></script>
|
|
61
61
|
|
|
62
62
|
</body>
|
|
63
63
|
</html>
|
|
@@ -113,8 +113,6 @@ module.exports = function apiRoutes() {
|
|
|
113
113
|
|
|
114
114
|
router.get('/members/stats/count', mw.authAdminApi, http(api.members.memberStats));
|
|
115
115
|
router.get('/members/stats/mrr', mw.authAdminApi, http(api.members.mrrStats));
|
|
116
|
-
router.get('/members/stats/subscribers', mw.authAdminApi, http(api.members.subscriberStats));
|
|
117
|
-
router.get('/members/stats/gross_volume', mw.authAdminApi, http(api.members.grossVolumeStats));
|
|
118
116
|
|
|
119
117
|
router.get('/members/events', mw.authAdminApi, http(api.members.activityFeed));
|
|
120
118
|
|
|
@@ -139,6 +137,9 @@ module.exports = function apiRoutes() {
|
|
|
139
137
|
|
|
140
138
|
router.get('/members/:id/signin_urls', mw.authAdminApi, http(api.memberSigninUrls.read));
|
|
141
139
|
|
|
140
|
+
// ## Stats
|
|
141
|
+
router.get('/stats/member_count', mw.authAdminApi, http(api.stats.memberCountHistory));
|
|
142
|
+
|
|
142
143
|
// ## Labels
|
|
143
144
|
router.get('/labels', mw.authAdminApi, http(api.labels.browse));
|
|
144
145
|
router.get('/labels/:id', mw.authAdminApi, http(api.labels.read));
|
|
@@ -290,6 +291,7 @@ module.exports = function apiRoutes() {
|
|
|
290
291
|
router.get('/actions', mw.authAdminApi, http(api.actions.browse));
|
|
291
292
|
|
|
292
293
|
// ## Email Preview
|
|
294
|
+
// @TODO: rename to email_previews in 5.0
|
|
293
295
|
router.get('/email_preview/posts/:id', mw.authAdminApi, http(api.email_preview.read));
|
|
294
296
|
router.post('/email_preview/posts/:id', mw.authAdminApi, http(api.email_preview.sendTestEmail));
|
|
295
297
|
|
|
@@ -309,5 +311,9 @@ module.exports = function apiRoutes() {
|
|
|
309
311
|
router.get('/custom_theme_settings', mw.authAdminApi, http(api.customThemeSettings.browse));
|
|
310
312
|
router.put('/custom_theme_settings', mw.authAdminApi, http(api.customThemeSettings.edit));
|
|
311
313
|
|
|
314
|
+
router.get('/newsletters', mw.authAdminApi, http(api.newsletters.browse));
|
|
315
|
+
router.post('/newsletters', mw.authAdminApi, http(api.newsletters.add));
|
|
316
|
+
router.put('/newsletters/:id', mw.authAdminApi, http(api.newsletters.edit));
|
|
317
|
+
|
|
312
318
|
return router;
|
|
313
319
|
};
|
|
@@ -128,8 +128,8 @@
|
|
|
128
128
|
"emailAnalytics": true
|
|
129
129
|
},
|
|
130
130
|
"portal": {
|
|
131
|
-
"url": "https://unpkg.com/@tryghost/portal@~1.
|
|
132
|
-
"version": "1.
|
|
131
|
+
"url": "https://unpkg.com/@tryghost/portal@~1.18.0/umd/portal.min.js",
|
|
132
|
+
"version": "1.18"
|
|
133
133
|
},
|
|
134
134
|
"tenor": {
|
|
135
135
|
"publicReadOnlyApiKey": null,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"url": "http://localhost:2368",
|
|
3
|
+
"database": {
|
|
4
|
+
"client": "sqlite3",
|
|
5
|
+
"connection": {
|
|
6
|
+
"filename": "content/data/ghost-dev.db"
|
|
7
|
+
},
|
|
8
|
+
"debug": false
|
|
9
|
+
},
|
|
10
|
+
"paths": {
|
|
11
|
+
"contentPath": "content/"
|
|
12
|
+
},
|
|
13
|
+
"privacy": {
|
|
14
|
+
"useRpcPing": false,
|
|
15
|
+
"useUpdateCheck": true
|
|
16
|
+
},
|
|
17
|
+
"useMinFiles": false,
|
|
18
|
+
"caching": {
|
|
19
|
+
"theme": {
|
|
20
|
+
"maxAge": 0
|
|
21
|
+
},
|
|
22
|
+
"admin": {
|
|
23
|
+
"maxAge": 0
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"database": {
|
|
3
|
+
"client": "mysql",
|
|
4
|
+
"connection": {
|
|
5
|
+
"host" : "127.0.0.1",
|
|
6
|
+
"user" : "root",
|
|
7
|
+
"password" : "",
|
|
8
|
+
"database" : "ghost"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"paths": {
|
|
12
|
+
"contentPath": "content/"
|
|
13
|
+
},
|
|
14
|
+
"logging": {
|
|
15
|
+
"level": "info",
|
|
16
|
+
"rotation": {
|
|
17
|
+
"enabled": true
|
|
18
|
+
},
|
|
19
|
+
"transports": ["file", "stdout"]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"url": "http://127.0.0.1:2369",
|
|
3
|
+
"server": {
|
|
4
|
+
"port": 2369
|
|
5
|
+
},
|
|
6
|
+
"database": {
|
|
7
|
+
"client": "mysql",
|
|
8
|
+
"connection": {
|
|
9
|
+
"host" : "127.0.0.1",
|
|
10
|
+
"user" : "root",
|
|
11
|
+
"password" : "",
|
|
12
|
+
"database" : "ghost_testing"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"logging": {
|
|
16
|
+
"level": "fatal"
|
|
17
|
+
},
|
|
18
|
+
"spam": {
|
|
19
|
+
"user_login": {
|
|
20
|
+
"minWait": 600000,
|
|
21
|
+
"maxWait": 604800000,
|
|
22
|
+
"freeRetries": 3
|
|
23
|
+
},
|
|
24
|
+
"user_reset": {
|
|
25
|
+
"minWait": 3600000,
|
|
26
|
+
"maxWait": 3600000,
|
|
27
|
+
"lifetime": 3600,
|
|
28
|
+
"freeRetries": 4
|
|
29
|
+
},
|
|
30
|
+
"global_reset": {
|
|
31
|
+
"minWait": 3600000,
|
|
32
|
+
"maxWait": 3600000,
|
|
33
|
+
"lifetime": 3600,
|
|
34
|
+
"freeRetries":4
|
|
35
|
+
},
|
|
36
|
+
"global_block": {
|
|
37
|
+
"minWait": 3600000,
|
|
38
|
+
"maxWait": 3600000,
|
|
39
|
+
"lifetime": 3600,
|
|
40
|
+
"freeRetries":4
|
|
41
|
+
},
|
|
42
|
+
"private_block": {
|
|
43
|
+
"minWait": 3600000,
|
|
44
|
+
"maxWait": 3600000,
|
|
45
|
+
"lifetime": 3600,
|
|
46
|
+
"freeRetries":99
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"privacy": {
|
|
50
|
+
"useTinfoil": true,
|
|
51
|
+
"useStructuredData": true
|
|
52
|
+
},
|
|
53
|
+
"useMinFiles": false,
|
|
54
|
+
"paths": {
|
|
55
|
+
"fixtures": "test/utils/fixtures/fixtures",
|
|
56
|
+
"defaultSettings": "test/utils/fixtures/default-settings.json",
|
|
57
|
+
"urlCache": "test/utils/fixtures/urls"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"url": "http://127.0.0.1:2369",
|
|
3
|
+
"database": {
|
|
4
|
+
"client": "sqlite3",
|
|
5
|
+
"connection": {
|
|
6
|
+
"filename": "/tmp/ghost-test.db"
|
|
7
|
+
},
|
|
8
|
+
"useNullAsDefault": true,
|
|
9
|
+
"debug": false
|
|
10
|
+
},
|
|
11
|
+
"server": {
|
|
12
|
+
"port": 2369
|
|
13
|
+
},
|
|
14
|
+
"logging": {
|
|
15
|
+
"level": "fatal"
|
|
16
|
+
},
|
|
17
|
+
"spam": {
|
|
18
|
+
"user_login": {
|
|
19
|
+
"minWait": 600000,
|
|
20
|
+
"maxWait": 604800000,
|
|
21
|
+
"freeRetries": 3
|
|
22
|
+
},
|
|
23
|
+
"user_reset": {
|
|
24
|
+
"minWait": 3600000,
|
|
25
|
+
"maxWait": 3600000,
|
|
26
|
+
"lifetime": 3600,
|
|
27
|
+
"freeRetries": 4
|
|
28
|
+
},
|
|
29
|
+
"global_reset": {
|
|
30
|
+
"minWait": 3600000,
|
|
31
|
+
"maxWait": 3600000,
|
|
32
|
+
"lifetime": 3600,
|
|
33
|
+
"freeRetries":4
|
|
34
|
+
},
|
|
35
|
+
"global_block": {
|
|
36
|
+
"minWait": 3600000,
|
|
37
|
+
"maxWait": 3600000,
|
|
38
|
+
"lifetime": 3600,
|
|
39
|
+
"freeRetries":4
|
|
40
|
+
},
|
|
41
|
+
"private_block": {
|
|
42
|
+
"minWait": 3600000,
|
|
43
|
+
"maxWait": 3600000,
|
|
44
|
+
"lifetime": 3600,
|
|
45
|
+
"freeRetries":99
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"privacy": {
|
|
49
|
+
"useTinfoil": true,
|
|
50
|
+
"useStructuredData": true
|
|
51
|
+
},
|
|
52
|
+
"useMinFiles": false,
|
|
53
|
+
"paths": {
|
|
54
|
+
"fixtures": "test/utils/fixtures/fixtures",
|
|
55
|
+
"defaultSettings": "test/utils/fixtures/default-settings.json",
|
|
56
|
+
"urlCache": "test/utils/fixtures/urls"
|
|
57
|
+
}
|
|
58
|
+
}
|
package/core/shared/labs.js
CHANGED
|
@@ -28,7 +28,7 @@ const doGet = (key, options) => {
|
|
|
28
28
|
|
|
29
29
|
// Don't try to resolve to the value of the setting
|
|
30
30
|
if (options && options.resolve === false) {
|
|
31
|
-
return settingsCache[key]
|
|
31
|
+
return settingsCache[key];
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// Default behaviour is to try to resolve the value and return that
|