Enforce explicit cacheInvalidate header (#16826)

refs https://github.com/TryGhost/Team/issues/3005
This commit is contained in:
Michael Barrett 2023-06-07 15:13:45 +01:00 committed by GitHub
parent e0da095c76
commit 9da246ac84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 498 additions and 40 deletions

View File

@ -4,6 +4,9 @@ module.exports = {
docName: 'actions', docName: 'actions',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'page', 'page',
'limit', 'limit',

View File

@ -4,6 +4,9 @@ module.exports = {
docName: 'announcement', docName: 'announcement',
browse: { browse: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
query(frame) { query(frame) {
return announcementBarSettings.getAnnouncementSettings(frame.options.context?.member); return announcementBarSettings.getAnnouncementSettings(frame.options.context?.member);

View File

@ -118,6 +118,9 @@ module.exports = {
}, },
isSetup: { isSetup: {
headers: {
cacheInvalidate: false
},
permissions: false, permissions: false,
async query() { async query() {
const isSetup = await auth.setup.checkIsSetup(); const isSetup = await auth.setup.checkIsSetup();
@ -132,6 +135,9 @@ module.exports = {
}, },
generateResetToken: { generateResetToken: {
headers: {
cacheInvalidate: false
},
validation: { validation: {
docName: 'password_reset' docName: 'password_reset'
}, },
@ -154,6 +160,9 @@ module.exports = {
}, },
resetPassword: { resetPassword: {
headers: {
cacheInvalidate: false
},
validation: { validation: {
docName: 'password_reset', docName: 'password_reset',
data: { data: {
@ -188,6 +197,9 @@ module.exports = {
}, },
acceptInvitation: { acceptInvitation: {
headers: {
cacheInvalidate: false
},
validation: { validation: {
docName: 'invitations' docName: 'invitations'
}, },
@ -204,6 +216,9 @@ module.exports = {
}, },
isInvitation: { isInvitation: {
headers: {
cacheInvalidate: false
},
data: [ data: [
'email' 'email'
], ],
@ -226,6 +241,9 @@ module.exports = {
resetAllPasswords: { resetAllPasswords: {
statusCode: 204, statusCode: 204,
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
async query(frame) { async query(frame) {
await userService.resetAllPasswords(frame.options); await userService.resetAllPasswords(frame.options);

View File

@ -24,6 +24,9 @@ module.exports = {
docName: 'authors', docName: 'authors',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'filter', 'filter',
@ -50,6 +53,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'filter', 'filter',

View File

@ -10,6 +10,9 @@ module.exports = {
docName: 'collections', docName: 'collections',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'limit', 'limit',
'order', 'order',
@ -24,6 +27,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
data: [ data: [
'id' 'id'
], ],
@ -55,7 +61,9 @@ module.exports = {
}, },
edit: { edit: {
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id' 'id'
], ],
@ -95,7 +103,9 @@ module.exports = {
addPost: { addPost: {
docName: 'collection_posts', docName: 'collection_posts',
statusCode: 200, statusCode: 200,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id' 'id'
], ],

View File

@ -6,6 +6,9 @@ module.exports = {
docName: 'comments', docName: 'comments',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'page', 'page',
@ -27,6 +30,9 @@ module.exports = {
}, },
replies: { replies: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'page', 'page',
@ -49,6 +55,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include' 'include'
], ],
@ -68,7 +77,9 @@ module.exports = {
}, },
edit: { edit: {
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id', 'id',
'include' 'include'
@ -91,6 +102,9 @@ module.exports = {
add: { add: {
statusCode: 201, statusCode: 201,
headers: {
cacheInvalidate: false
},
options: [ options: [
'include' 'include'
@ -115,6 +129,9 @@ module.exports = {
destroy: { destroy: {
statusCode: 204, statusCode: 204,
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'id' 'id'
@ -131,6 +148,9 @@ module.exports = {
}, },
counts: { counts: {
headers: {
cacheInvalidate: false
},
permissions: false, permissions: false,
options: [ options: [
'ids' 'ids'
@ -142,6 +162,9 @@ module.exports = {
like: { like: {
statusCode: 204, statusCode: 204,
headers: {
cacheInvalidate: false
},
options: [ options: [
'id' 'id'
], ],
@ -155,6 +178,9 @@ module.exports = {
unlike: { unlike: {
statusCode: 204, statusCode: 204,
headers: {
cacheInvalidate: false
},
options: [ options: [
'id' 'id'
], ],
@ -167,6 +193,9 @@ module.exports = {
report: { report: {
statusCode: 204, statusCode: 204,
headers: {
cacheInvalidate: false
},
options: [ options: [
'id' 'id'
], ],

View File

@ -4,6 +4,9 @@ module.exports = {
docName: 'comments', docName: 'comments',
edit: { edit: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'id' 'id'
], ],

View File

@ -4,6 +4,9 @@ module.exports = {
docName: 'config', docName: 'config',
read: { read: {
headers: {
cacheInvalidate: false
},
permissions: false, permissions: false,
query() { query() {
return publicConfig.config; return publicConfig.config;

View File

@ -4,6 +4,9 @@ module.exports = {
docName: 'custom_theme_settings', docName: 'custom_theme_settings',
browse: { browse: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
query() { query() {
return customThemeSettingsService.api.listSettings(); return customThemeSettingsService.api.listSettings();

View File

@ -12,6 +12,9 @@ module.exports = {
docName: 'db', docName: 'db',
backupContent: { backupContent: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
options: [ options: [
'include', 'include',
@ -48,7 +51,8 @@ module.exports = {
disposition: { disposition: {
type: 'file', type: 'file',
value: () => (exporter.fileName()) value: () => (exporter.fileName())
} },
cacheInvalidate: false
}, },
permissions: true, permissions: true,
async query(frame) { async query(frame) {
@ -103,6 +107,9 @@ module.exports = {
}, },
inlineMedia: { inlineMedia: {
headers: {
cacheInvalidate: false
},
permissions: { permissions: {
method: 'importContent' method: 'importContent'
}, },

View File

@ -11,6 +11,9 @@ module.exports = {
docName: 'email_post', docName: 'email_post',
read: { read: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
options: [ options: [
'include' 'include'

View File

@ -4,6 +4,9 @@ module.exports = {
docName: 'email_previews', docName: 'email_previews',
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'fields', 'fields',
'memberSegment', 'memberSegment',
@ -25,7 +28,9 @@ module.exports = {
}, },
sendTestEmail: { sendTestEmail: {
statusCode: 204, statusCode: 204,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id' 'id'
], ],

View File

@ -16,6 +16,9 @@ module.exports = {
docName: 'emails', docName: 'emails',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'limit', 'limit',
'fields', 'fields',
@ -30,6 +33,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'fields' 'fields'
], ],
@ -57,6 +63,9 @@ module.exports = {
}, },
retry: { retry: {
headers: {
cacheInvalidate: false
},
data: [ data: [
'id' 'id'
], ],
@ -67,6 +76,9 @@ module.exports = {
}, },
browseBatches: { browseBatches: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'limit', 'limit',
'fields', 'fields',
@ -95,6 +107,9 @@ module.exports = {
}, },
browseFailures: { browseFailures: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'limit', 'limit',
'fields', 'fields',
@ -123,6 +138,9 @@ module.exports = {
}, },
analyticsStatus: { analyticsStatus: {
headers: {
cacheInvalidate: false
},
permissions: { permissions: {
method: 'browse' method: 'browse'
}, },
@ -132,6 +150,9 @@ module.exports = {
}, },
scheduleAnalytics: { scheduleAnalytics: {
headers: {
cacheInvalidate: false
},
permissions: { permissions: {
method: 'browse' method: 'browse'
}, },
@ -148,6 +169,9 @@ module.exports = {
}, },
cancelScheduledAnalytics: { cancelScheduledAnalytics: {
headers: {
cacheInvalidate: false
},
permissions: { permissions: {
method: 'browse' method: 'browse'
}, },

View File

@ -4,6 +4,9 @@ module.exports = {
docName: 'explore', docName: 'explore',
read: { read: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
query() { query() {
return exploreService.fetchData(); return exploreService.fetchData();

View File

@ -5,6 +5,9 @@ module.exports = {
add: { add: {
statusCode: 201, statusCode: 201,
headers: {
cacheInvalidate: false
},
validation: { validation: {
data: { data: {
post_id: { post_id: {

View File

@ -4,6 +4,9 @@ module.exports = {
docName: 'files', docName: 'files',
upload: { upload: {
statusCode: 201, statusCode: 201,
headers: {
cacheInvalidate: false
},
permissions: false, permissions: false,
async query(frame) { async query(frame) {
const filePath = await storage.getStorage('files').save({ const filePath = await storage.getStorage('files').save({

View File

@ -26,6 +26,9 @@ const sign = async (claims, options) => {
module.exports = { module.exports = {
docName: 'identities', docName: 'identities',
read: { read: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
async query(frame) { async query(frame) {
const token = await sign({sub: frame.user.get('email')}); const token = await sign({sub: frame.user.get('email')});

View File

@ -8,6 +8,9 @@ module.exports = {
docName: 'images', docName: 'images',
upload: { upload: {
statusCode: 201, statusCode: 201,
headers: {
cacheInvalidate: false
},
permissions: false, permissions: false,
async query(frame) { async query(frame) {
const store = storage.getStorage('images'); const store = storage.getStorage('images');

View File

@ -15,6 +15,9 @@ const integrationsService = getIntegrationsServiceInstance({
module.exports = { module.exports = {
docName: 'integrations', docName: 'integrations',
browse: { browse: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
options: [ options: [
'include', 'include',
@ -32,6 +35,9 @@ module.exports = {
} }
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
data: [ data: [
'id' 'id'
@ -63,6 +69,9 @@ module.exports = {
} }
}, },
edit: { edit: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
data: [ data: [
'name', 'name',
@ -91,6 +100,9 @@ module.exports = {
}, },
add: { add: {
statusCode: 201, statusCode: 201,
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
data: [ data: [
'name', 'name',
@ -125,6 +137,9 @@ module.exports = {
}, },
destroy: { destroy: {
statusCode: 204, statusCode: 204,
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
options: [ options: [
'id' 'id'

View File

@ -15,6 +15,9 @@ module.exports = {
docName: 'invites', docName: 'invites',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'page', 'page',
@ -36,6 +39,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include' 'include'
], ],
@ -64,6 +70,9 @@ module.exports = {
}, },
destroy: { destroy: {
headers: {
cacheInvalidate: false
},
statusCode: 204, statusCode: 204,
options: [ options: [
'include', 'include',
@ -82,6 +91,9 @@ module.exports = {
add: { add: {
statusCode: 201, statusCode: 201,
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'email' 'email'

View File

@ -14,6 +14,9 @@ module.exports = {
docName: 'labels', docName: 'labels',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'filter', 'filter',
@ -36,6 +39,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'filter', 'filter',
@ -69,7 +75,9 @@ module.exports = {
add: { add: {
statusCode: 201, statusCode: 201,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'include' 'include'
], ],
@ -94,7 +102,9 @@ module.exports = {
}, },
edit: { edit: {
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id', 'id',
'include' 'include'

View File

@ -4,6 +4,9 @@ const INVALIDATE_ALL_REDIRECTS = '/r/*';
module.exports = { module.exports = {
docName: 'links', docName: 'links',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'filter' 'filter'
], ],

View File

@ -40,6 +40,9 @@ module.exports = {
docName: 'mail', docName: 'mail',
send: { send: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
query(frame) { query(frame) {
return _private.sendMail(frame.data); return _private.sendMail(frame.data);

View File

@ -5,6 +5,9 @@ module.exports = {
docName: 'media', docName: 'media',
upload: { upload: {
statusCode: 201, statusCode: 201,
headers: {
cacheInvalidate: false
},
permissions: false, permissions: false,
async query(frame) { async query(frame) {
let thumbnailPath = null; let thumbnailPath = null;
@ -22,6 +25,9 @@ module.exports = {
}, },
uploadThumbnail: { uploadThumbnail: {
headers: {
cacheInvalidate: false
},
permissions: false, permissions: false,
data: [ data: [
'url', 'url',

View File

@ -9,6 +9,9 @@ const messages = {
module.exports = { module.exports = {
docName: 'member_signin_urls', docName: 'member_signin_urls',
read: { read: {
headers: {
cacheInvalidate: false
},
data: [ data: [
'id' 'id'
], ],

View File

@ -3,6 +3,9 @@ const membersService = require('../../services/members');
module.exports = { module.exports = {
docName: 'members_stripe_connect', docName: 'members_stripe_connect',
auth: { auth: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
options: [ options: [
'mode' 'mode'

View File

@ -33,6 +33,9 @@ module.exports = {
docName: 'members', docName: 'members',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'limit', 'limit',
'fields', 'fields',
@ -62,7 +65,9 @@ module.exports = {
options: [ options: [
'include' 'include'
], ],
headers: {}, headers: {
cacheInvalidate: false
},
data: [ data: [
'id', 'id',
'email' 'email'
@ -90,7 +95,9 @@ module.exports = {
add: { add: {
statusCode: 201, statusCode: 201,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'send_email', 'send_email',
'email_type' 'email_type'
@ -115,7 +122,9 @@ module.exports = {
edit: { edit: {
statusCode: 200, statusCode: 200,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id' 'id'
], ],
@ -136,7 +145,9 @@ module.exports = {
editSubscription: { editSubscription: {
statusCode: 200, statusCode: 200,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id', 'id',
'subscription_id' 'subscription_id'
@ -196,7 +207,9 @@ module.exports = {
createSubscription: { createSubscription: {
statusCode: 200, statusCode: 200,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id' 'id'
], ],
@ -238,7 +251,9 @@ module.exports = {
destroy: { destroy: {
statusCode: 204, statusCode: 204,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id', 'id',
'cancel' 'cancel'
@ -262,7 +277,9 @@ module.exports = {
bulkDestroy: { bulkDestroy: {
statusCode: 200, statusCode: 200,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'all', 'all',
'filter', 'filter',
@ -290,7 +307,9 @@ module.exports = {
bulkEdit: { bulkEdit: {
statusCode: 200, statusCode: 200,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'all', 'all',
'filter', 'filter',
@ -329,7 +348,8 @@ module.exports = {
const datetime = (new Date()).toJSON().substring(0, 10); const datetime = (new Date()).toJSON().substring(0, 10);
return `members.${datetime}.csv`; return `members.${datetime}.csv`;
} }
} },
cacheInvalidate: false
}, },
response: { response: {
format: 'plain' format: 'plain'
@ -346,6 +366,9 @@ module.exports = {
}, },
importCSV: { importCSV: {
headers: {
cacheInvalidate: false
},
statusCode(result) { statusCode(result) {
if (result && result.meta && result.meta.stats && result.meta.stats.imported !== null) { if (result && result.meta && result.meta.stats && result.meta.stats.imported !== null) {
return 201; return 201;
@ -388,6 +411,9 @@ module.exports = {
}, },
memberStats: { memberStats: {
headers: {
cacheInvalidate: false
},
permissions: { permissions: {
method: 'browse' method: 'browse'
}, },
@ -410,6 +436,9 @@ module.exports = {
}, },
mrrStats: { mrrStats: {
headers: {
cacheInvalidate: false
},
permissions: { permissions: {
method: 'browse' method: 'browse'
}, },
@ -434,6 +463,9 @@ module.exports = {
}, },
activityFeed: { activityFeed: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'limit', 'limit',
'filter' 'filter'

View File

@ -3,6 +3,9 @@ const mentions = require('../../services/mentions');
module.exports = { module.exports = {
docName: 'mentions', docName: 'mentions',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'filter', 'filter',
'fields', 'fields',
@ -20,7 +23,9 @@ module.exports = {
receive: { receive: {
statusCode: 202, // Required for the spec because we don't have a status page statusCode: 202, // Required for the spec because we don't have a status page
headers: {}, headers: {
cacheInvalidate: false
},
options: [], options: [],
permissions: false, permissions: false,
response: { response: {

View File

@ -4,6 +4,9 @@ module.exports = {
docName: 'newsletters', docName: 'newsletters',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'filter', 'filter',
'fields', 'fields',

View File

@ -7,6 +7,9 @@ module.exports = {
docName: 'newsletters', docName: 'newsletters',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'filter', 'filter',
@ -29,6 +32,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'fields', 'fields',
@ -102,6 +108,9 @@ module.exports = {
}, },
verifyPropertyUpdate: { verifyPropertyUpdate: {
headers: {
cacheInvalidate: false
},
permissions: { permissions: {
method: 'edit' method: 'edit'
}, },

View File

@ -7,6 +7,9 @@ module.exports = {
docName: 'notifications', docName: 'notifications',
browse: { browse: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
query(frame) { query(frame) {
return notifications.browse({ return notifications.browse({
@ -25,6 +28,9 @@ module.exports = {
return 200; return 200;
} }
}, },
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
async query(frame) { async query(frame) {
const {allNotifications, notificationsToAdd} = notifications.add({ const {allNotifications, notificationsToAdd} = notifications.add({
@ -45,6 +51,9 @@ module.exports = {
destroy: { destroy: {
statusCode: 204, statusCode: 204,
headers: {
cacheInvalidate: false
},
options: ['notification_id'], options: ['notification_id'],
validation: { validation: {
options: { options: {
@ -76,6 +85,9 @@ module.exports = {
*/ */
destroyAll: { destroyAll: {
statusCode: 204, statusCode: 204,
headers: {
cacheInvalidate: false
},
permissions: { permissions: {
method: 'destroy' method: 'destroy'
}, },

View File

@ -4,6 +4,9 @@ module.exports = {
docName: 'oembed', docName: 'oembed',
read: { read: {
headers: {
cacheInvalidate: false
},
permissions: false, permissions: false,
data: [ data: [
'url', 'url',

View File

@ -10,6 +10,9 @@ module.exports = {
docName: 'offers', docName: 'offers',
read: { read: {
headers: {
cacheInvalidate: false
},
data: ['id'], data: ['id'],
permissions: true, permissions: true,
async query(frame) { async query(frame) {

View File

@ -10,6 +10,9 @@ module.exports = {
docName: 'offers', docName: 'offers',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'filter' 'filter'
], ],
@ -23,6 +26,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
data: ['id'], data: ['id'],
permissions: true, permissions: true,
async query(frame) { async query(frame) {

View File

@ -24,6 +24,9 @@ module.exports = {
docName: 'pages', docName: 'pages',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'filter', 'filter',
@ -56,6 +59,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'fields', 'fields',

View File

@ -14,6 +14,9 @@ const postsService = getPostServiceInstance();
module.exports = { module.exports = {
docName: 'pages', docName: 'pages',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'filter', 'filter',
@ -45,6 +48,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'fields', 'fields',
@ -90,7 +96,9 @@ module.exports = {
add: { add: {
statusCode: 201, statusCode: 201,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'formats', 'formats',
@ -113,9 +121,7 @@ module.exports = {
query(frame) { query(frame) {
return models.Post.add(frame.data.pages[0], frame.options) return models.Post.add(frame.data.pages[0], frame.options)
.then((model) => { .then((model) => {
if (model.get('status') !== 'published') { if (model.get('status') === 'published') {
this.headers.cacheInvalidate = false;
} else {
this.headers.cacheInvalidate = true; this.headers.cacheInvalidate = true;
} }
@ -125,7 +131,9 @@ module.exports = {
}, },
edit: { edit: {
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'id', 'id',
@ -245,7 +253,8 @@ module.exports = {
headers: { headers: {
location: { location: {
resolve: postsService.generateCopiedPostLocationFromUrl resolve: postsService.generateCopiedPostLocationFromUrl
} },
cacheInvalidate: false
}, },
options: [ options: [
'id', 'id',

View File

@ -25,6 +25,9 @@ module.exports = {
docName: 'posts', docName: 'posts',
browse: { browse: {
headers: {
cacheInvalidate: false
},
cache: postsPublicService.api?.cache, cache: postsPublicService.api?.cache,
options: [ options: [
'include', 'include',
@ -58,6 +61,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'fields', 'fields',

View File

@ -24,6 +24,9 @@ const postsService = getPostServiceInstance();
module.exports = { module.exports = {
docName: 'posts', docName: 'posts',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'filter', 'filter',
@ -66,7 +69,8 @@ module.exports = {
const datetime = (new Date()).toJSON().substring(0, 10); const datetime = (new Date()).toJSON().substring(0, 10);
return `post-analytics.${datetime}.csv`; return `post-analytics.${datetime}.csv`;
} }
} },
cacheInvalidate: false
}, },
response: { response: {
format: 'plain' format: 'plain'
@ -83,6 +87,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'fields', 'fields',
@ -118,7 +125,9 @@ module.exports = {
add: { add: {
statusCode: 201, statusCode: 201,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'formats', 'formats',
@ -152,7 +161,9 @@ module.exports = {
}, },
edit: { edit: {
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'id', 'id',
@ -271,7 +282,8 @@ module.exports = {
headers: { headers: {
location: { location: {
resolve: postsService.generateCopiedPostLocationFromUrl resolve: postsService.generateCopiedPostLocationFromUrl
} },
cacheInvalidate: false
}, },
options: [ options: [
'id', 'id',

View File

@ -11,6 +11,9 @@ module.exports = {
docName: 'previews', docName: 'previews',
read: { read: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
options: [ options: [
'include' 'include'

View File

@ -18,7 +18,8 @@ module.exports = {
: 'redirects.yaml'; : 'redirects.yaml';
}); });
} }
} },
cacheInvalidate: false
}, },
permissions: true, permissions: true,
response: { response: {

View File

@ -3,6 +3,9 @@ const models = require('../../models');
module.exports = { module.exports = {
docName: 'roles', docName: 'roles',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'permissions' 'permissions'
], ],

View File

@ -5,7 +5,9 @@ const postSchedulingService = require('../../services/posts/post-scheduling-serv
module.exports = { module.exports = {
docName: 'schedules', docName: 'schedules',
publish: { publish: {
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id', 'id',
'resource' 'resource'
@ -50,6 +52,9 @@ module.exports = {
getScheduled: { getScheduled: {
// NOTE: this method is for internal use only by DefaultScheduler // NOTE: this method is for internal use only by DefaultScheduler
// it is not exposed anywhere! // it is not exposed anywhere!
headers: {
cacheInvalidate: false
},
permissions: false, permissions: false,
validation: { validation: {
options: { options: {

View File

@ -6,6 +6,9 @@ module.exports = {
docName: 'settings', docName: 'settings',
browse: { browse: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
query() { query() {
// @TODO: decouple settings cache from API knowledge // @TODO: decouple settings cache from API knowledge

View File

@ -25,6 +25,9 @@ module.exports = {
docName: 'settings', docName: 'settings',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: ['group'], options: ['group'],
permissions: true, permissions: true,
query(frame) { query(frame) {
@ -33,6 +36,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: ['key'], options: ['key'],
validation: { validation: {
options: { options: {
@ -73,6 +79,9 @@ module.exports = {
disconnectStripeConnectIntegration: { disconnectStripeConnectIntegration: {
statusCode: 204, statusCode: 204,
headers: {
cacheInvalidate: false
},
permissions: { permissions: {
method: 'edit' method: 'edit'
}, },
@ -158,7 +167,8 @@ module.exports = {
disposition: { disposition: {
type: 'yaml', type: 'yaml',
value: 'routes.yaml' value: 'routes.yaml'
} },
cacheInvalidate: false
}, },
response: { response: {
format: 'plain' format: 'plain'

View File

@ -4,6 +4,9 @@ const site = {
docName: 'site', docName: 'site',
read: { read: {
headers: {
cacheInvalidate: false
},
permissions: false, permissions: false,
query() { query() {
return publicConfig.site; return publicConfig.site;

View File

@ -4,6 +4,9 @@ const events = require('../../lib/common/events');
module.exports = { module.exports = {
docName: 'slack', docName: 'slack',
sendTest: { sendTest: {
headers: {
cacheInvalidate: false
},
permissions: false, permissions: false,
query() { query() {
events.emit('slack.test'); events.emit('slack.test');

View File

@ -12,6 +12,9 @@ module.exports = {
docName: 'snippets', docName: 'snippets',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'limit', 'limit',
'order', 'order',
@ -33,7 +36,9 @@ module.exports = {
}, },
read: { read: {
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'formats' 'formats'
], ],
@ -57,7 +62,9 @@ module.exports = {
add: { add: {
statusCode: 201, statusCode: 201,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'formats' 'formats'
], ],
@ -75,7 +82,9 @@ module.exports = {
}, },
edit: { edit: {
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id', 'id',
'formats' 'formats'
@ -104,7 +113,9 @@ module.exports = {
destroy: { destroy: {
statusCode: 204, statusCode: 204,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id' 'id'
], ],

View File

@ -3,6 +3,9 @@ const statsService = require('../../services/stats');
module.exports = { module.exports = {
docName: 'stats', docName: 'stats',
memberCountHistory: { memberCountHistory: {
headers: {
cacheInvalidate: false
},
permissions: { permissions: {
docName: 'members', docName: 'members',
method: 'browse' method: 'browse'
@ -12,6 +15,9 @@ module.exports = {
} }
}, },
mrr: { mrr: {
headers: {
cacheInvalidate: false
},
permissions: { permissions: {
docName: 'members', docName: 'members',
method: 'browse' method: 'browse'
@ -21,6 +27,9 @@ module.exports = {
} }
}, },
subscriptions: { subscriptions: {
headers: {
cacheInvalidate: false
},
permissions: { permissions: {
docName: 'members', docName: 'members',
method: 'browse' method: 'browse'
@ -30,6 +39,9 @@ module.exports = {
} }
}, },
postReferrers: { postReferrers: {
headers: {
cacheInvalidate: false
},
data: [ data: [
'id' 'id'
], ],
@ -42,6 +54,9 @@ module.exports = {
} }
}, },
referrersHistory: { referrersHistory: {
headers: {
cacheInvalidate: false
},
data: [ data: [
'id' 'id'
], ],

View File

@ -14,6 +14,9 @@ module.exports = {
docName: 'tags', docName: 'tags',
browse: { browse: {
headers: {
cacheInvalidate: false
},
cache: tagsPublicService.api?.cache, cache: tagsPublicService.api?.cache,
options: [ options: [
'include', 'include',
@ -38,6 +41,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'filter', 'filter',

View File

@ -13,6 +13,9 @@ module.exports = {
docName: 'tags', docName: 'tags',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'filter', 'filter',
@ -36,6 +39,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'filter', 'filter',
@ -91,7 +97,9 @@ module.exports = {
}, },
edit: { edit: {
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id', 'id',
'include' 'include'

View File

@ -10,6 +10,9 @@ module.exports = {
docName: 'themes', docName: 'themes',
browse: { browse: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
query() { query() {
return themeService.api.getJSON(); return themeService.api.getJSON();
@ -17,6 +20,9 @@ module.exports = {
}, },
readActive: { readActive: {
headers: {
cacheInvalidate: false
},
permissions: true, permissions: true,
async query() { async query() {
let themeName = settingsCache.get('active_theme'); let themeName = settingsCache.get('active_theme');
@ -59,7 +65,9 @@ module.exports = {
}, },
install: { install: {
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'source', 'source',
'ref' 'ref'
@ -94,7 +102,9 @@ module.exports = {
}, },
upload: { upload: {
headers: {}, headers: {
cacheInvalidate: false
},
permissions: { permissions: {
method: 'add' method: 'add'
}, },
@ -125,6 +135,9 @@ module.exports = {
}, },
download: { download: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'name' 'name'
], ],

View File

@ -4,6 +4,9 @@ module.exports = {
docName: 'tiers', docName: 'tiers',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'limit', 'limit',
'fields', 'fields',

View File

@ -4,6 +4,9 @@ module.exports = {
docName: 'tiers', docName: 'tiers',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'limit', 'limit',
'fields', 'fields',
@ -22,6 +25,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
data: [ data: [
'id' 'id'
], ],

View File

@ -45,6 +45,9 @@ module.exports = {
docName: 'users', docName: 'users',
browse: { browse: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'filter', 'filter',
@ -68,6 +71,9 @@ module.exports = {
}, },
read: { read: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'include', 'include',
'filter', 'filter',
@ -103,7 +109,9 @@ module.exports = {
}, },
edit: { edit: {
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id', 'id',
'include' 'include'
@ -166,6 +174,9 @@ module.exports = {
}, },
changePassword: { changePassword: {
headers: {
cacheInvalidate: false
},
validation: { validation: {
docName: 'password', docName: 'password',
data: { data: {
@ -188,6 +199,9 @@ module.exports = {
}, },
transferOwnership: { transferOwnership: {
headers: {
cacheInvalidate: false
},
permissions(frame) { permissions(frame) {
return models.Role.findOne({name: 'Owner'}) return models.Role.findOne({name: 'Owner'})
.then((ownerRole) => { .then((ownerRole) => {
@ -200,6 +214,9 @@ module.exports = {
}, },
readToken: { readToken: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'id' 'id'
], ],
@ -218,6 +235,9 @@ module.exports = {
}, },
regenerateToken: { regenerateToken: {
headers: {
cacheInvalidate: false
},
options: [ options: [
'id' 'id'
], ],

View File

@ -22,7 +22,8 @@ module.exports = {
statusCode: 201, statusCode: 201,
headers: { headers: {
// NOTE: remove if there is ever a 'read' method // NOTE: remove if there is ever a 'read' method
location: false location: false,
cacheInvalidate: false
}, },
options: [], options: [],
data: [], data: [],
@ -33,6 +34,9 @@ module.exports = {
}, },
edit: { edit: {
headers: {
cacheInvalidate: false
},
permissions: { permissions: {
before: (frame) => { before: (frame) => {
if (frame.options.context && frame.options.context.integration && frame.options.context.integration.id) { if (frame.options.context && frame.options.context.integration && frame.options.context.integration.id) {
@ -84,7 +88,9 @@ module.exports = {
destroy: { destroy: {
statusCode: 204, statusCode: 204,
headers: {}, headers: {
cacheInvalidate: false
},
options: [ options: [
'id' 'id'
], ],

View File

@ -0,0 +1,46 @@
const assert = require('assert');
const path = require('path');
const glob = require('glob');
const models = require('../../../core/server/models');
describe('API', function () {
describe('Cache Invalidation', function () {
before(async function () {
// Initialise models - Utilised by various endpoints to reference static fields (i.e models.Post.allowedFormats) when required in
models.init();
});
it('Controller actions explicitly declare cacheInvalidate header', async function () {
const controllersRootPath = path.join(__dirname, '../../../core/server/api/endpoints');
const controllerPaths = glob.sync('*.js', {
cwd: controllersRootPath,
ignore: [
'index.js',
'identities.js' // The identities controller can not be required directly due to requiring other parts of Ghost to have been initialised first
],
realpath: true
});
assert.ok(controllerPaths.length > 0, `No controllers found in ${controllersRootPath}`);
controllerPaths.forEach((controllerPath) => {
const controllerConfig = require(controllerPath);
const ignoreKeys = ['docName'];
Object.keys(controllerConfig).forEach((key) => {
if (ignoreKeys.includes(key) || typeof controllerConfig[key] === 'function') {
return;
}
assert.notEqual(
controllerConfig[key].headers?.cacheInvalidate,
undefined,
`"${key}" action in controller: ${controllerPath} is missing cacheInvalidate header - This needs to be explicitly defined`
);
});
});
});
});
});