mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
c6621dc17d
refs https://github.com/TryGhost/Team/issues/584 The current support email verification flow uses an API endpoint as verification URL inside the emails. This is a bad pattern, and also has the side effect that it shows a JSON error if something goes wrong. To fix this, this commit updates the whole flow to use the same pattern as newsletters: - You can update the `members_support_address` setting directly via the edit endpoint of settings. - Changes to that (and future 'guarded' email properties) are blocked and generate verification emails automatically. - When an email verification has been sent, the meta property `sent_email_verification` is set. Other changes: - Underlying, the implementation of email verificaton has moved from the (old) members service to the settings BREAD service. This makes it easier to add extra email addresses in settings later on that are not related to 'members'. - Now you can update the `members_support_address` by updating the settings directly, so the `updateMembersEmail` endpoint has been deprecated and is mapped to the new behaviour. - The SingleUseTokenProvider threw a `UnauthorizedError` error if a token was expired or invalid. Those errors are caught by the admin app, and causes it to do a page reload (making the error message and modals invisible). To fix that, I've swapped it with a validation error. Future changes: - Existing emails that have been sent 24h before this change is applied, still use the `validateMembersEmailUpdate` API endpoint. This endpoint has not been removed for now, to not break those emails. In a future release, we should remove this. Changes to admin: https://github.com/TryGhost/Admin/pull/2426 |
||
---|---|---|
.. | ||
endpoints | ||
shared | ||
index.js | ||
README.md |
API
Stages
Each request goes through the following stages:
- input validation
- input serialisation
- permissions
- query
- output serialisation
The framework we are building pipes a request through these stages in respect of the API controller configuration.
Frame
Is a class, which holds all the information for request processing. We pass this instance by reference. Each function can modify the original instance. No need to return the class instance.
Structure
{
original: Object,
options: Object,
data: Object,
user: Object,
file: Object,
files: Array
}
Example
{
original: {
include: 'tags'
},
options: {
withRelated: ['tags']
},
data: {
posts: []
}
}
API Controller
A controller is no longer just a function, it's a set of configurations.
Structure
edit: function || object
edit: {
headers: object,
options: Array,
data: Array,
validation: object | function,
permissions: boolean | object | function,
query: function
}
Examples
edit: {
headers: {
cacheInvalidate: true
},
// Allowed url/query params
options: ['include']
// Url/query param validation configuration
validation: {
options: {
include: {
required: true,
values: ['tags']
}
}
},
permissions: true,
// Returns a model response!
query(frame) {
return models.Post.edit(frame.data, frame.options);
}
}
read: {
// Allowed url/query params, which will be remembered inside `frame.data`
// This is helpful for READ requests e.g. `model.findOne(frame.data, frame.options)`.
// Our model layer requires sending the where clauses as first parameter.
data: ['slug']
validation: {
data: {
slug: {
values: ['eins']
}
}
},
permissions: true,
query(frame) {
return models.Post.findOne(frame.data, frame.options);
}
}
edit: {
validation() {
// custom validation, skip framework
},
permissions: {
unsafeAttrs: ['author']
},
query(frame) {
return models.Post.edit(frame.data, frame.options);
}
}