Commit Graph

4746 Commits

Author SHA1 Message Date
Kevin Ansfield
c40a8e978d Fixed incorrect knex syntax
refs 166d072cd4

- accidentally committed an earlier attempt 🤦🏻‍♂️ updated with the valid syntax
2020-12-02 14:53:10 +00:00
Kevin Ansfield
166d072cd4 Fixed email analytics job not being registered when creating an email
no issue

- job registration was checking for submitted emails in it's email count but the job registration method is called as soon as an email is created meaning the email has a status of 'pending' which prevented the analytics job from being started until a second email was sent
2020-12-02 14:47:18 +00:00
Kevin Ansfield
f802128cfc
Added emailAnalytics config feature flag (#12443)
no issue

- email analytics may be desirable to fully switch off in certain circumstances, when that happens we want to prevent related background jobs from running and expose the feature flag via the config endpoint in the Admin API so that clients can adjust accordingly
2020-12-02 13:22:12 +00:00
Kevin Ansfield
2951eb9eaf Added missing moment require
refs 92ef83c61a
2020-12-02 12:15:27 +00:00
Kevin Ansfield
92ef83c61a Adjusted email jobs registration to check for emails newer than 30 days
no issue

- if emails are older than 30 days we wouldn't be able to fetch any analytics for them and if a site used emails in the past but is no longer using them it doesn't make sense to keep potentially expensive background worker threads spinning up
2020-12-02 12:14:22 +00:00
Daniel Lockyer
947603d0e3
Added original error to thrown error in mega code
- the original error is not propagated up with the error we throw, so it
  can sometimes be difficult to see what's going on down the line
2020-12-02 09:04:20 +00:00
Kevin Ansfield
d675278b0b
Prevented scheduling of recurring analytics jobs when not using emails (#12441)
no issue

- recurring jobs spin up worker threads which can be quite CPU intensive even when not performing much processing, this can be problematic in environments where there are many Ghost instances running
- updated the email job scheduling to be skipped on bootup when there are no emails in the database and to be started when the first email is created as long as we're not in testing env
- increase analytics job schedule from every 2 minutes to every 5 minutes to help spread the load further across instances
2020-12-02 08:17:44 +00:00
Kevin Ansfield
249fd4f06a Fixed email count check in email-analytics service
no issue

- raw knex `.count()` does not return a straight number, we need to handle an array of rowDataPacket objects
2020-12-01 15:02:04 +00:00
Kevin Ansfield
641be8fec3 Adjusted email analytics job scheduling to avoid API spikes
no issue

- typically cron/later schedules will schedule for :00 on the minute which would create API spikes with every members-email-using Ghost site hitting the API at the same time
- adjusted the scheduling to use cron syntax with job runs every 2 minutes on 1,3,5... or 2,4,6... and a random seconds value to smooth usage across sites
2020-12-01 11:27:34 +00:00
Kevin Ansfield
db7fff0b2a Fixed misleading emailAnalytics.fetchLatest debug statement 2020-12-01 10:16:41 +00:00
Kevin Ansfield
d604f96a9c Added no-emails guard check when fetching latest email events
no issue

- replicates the same guard used in `fetchLatest()` to prevent contacting Mailgun when there are no emails in the database
2020-12-01 10:15:31 +00:00
Kevin Ansfield
b35eef1b02 Added kill-switch config for email analytics background jobs
no issue

- it's possible background jobs may cause unintended side-effects so it's useful to have a kill-switch to disable them individually to keep sites working
2020-12-01 09:10:59 +00:00
Daniel Lockyer
8f86885132 Fixed typo in v2 oembed API
- canary uses `maxwidth` and it's the correct parameter to use
2020-12-01 08:59:41 +00:00
Scott Cabot
da5586e5aa Fixed user import with no created_at date breaking graph
- users imported from CSV with no created_at date where having their created_at date being stored as an int rather than a datetime.
- this was causing parsing issues with the graph so this commit fixes the formatting
2020-11-30 11:21:22 +00:00
Scott Cabot
1e5d201835 🐛 Fixed flat member chart for GMT-X timezones
closes #12083

- fixes a parsing issue where negative offset values were incorrectly having the + sign added regardless of actual offset for sqlite databases.
- for mysql databases absolute values of offset were taken with sign applied where appropriate to stop issues where both hours and minutes could be negative which would cause both an issue with offsets that could present as -2:30 and by the look of the code also trigger extra padding to result in -2:-030 rather than the expected -2:30
2020-11-30 11:21:22 +00:00
Naz
52bcf6086a Fixed malformatted JSDoc {Object} parameters
no issue

- Removed † symbol from the source code
- Not sure how this symbol got into the source code but it definitely should not be here
2020-11-30 17:18:13 +13:00
Naz
eb52cb06bb Improved threading performance on non-SQLite clients
refs https://github.com/TryGhost/Ghost/pull/12431

- child_process are more expensive to run comparing to worker_threads (ref. https://wanago.io/2019/05/20/node-js-typescript-14-performance-hooks/)
- limiting child process only to sqlite backed instances allows high load servers running with MySQL (most usecases) to continue using worker_threads
2020-11-30 13:27:40 +13:00
Kevin Ansfield
a29ac2691a
Fixed sqlite3 errors when email analytics jobs run (#12431)
no issue

- the 4.2.0 version of `sqlite3` that we're using is not compatible with `worker_threads`
- 5.0.0 should add support this but there are other errors
- 5.0.1 is released but not published (https://github.com/mapbox/node-sqlite3/issues/1386)
2020-11-26 15:12:12 +00:00
Kevin Ansfield
dcafebe379 Fixed linting 2020-11-26 13:11:18 +00:00
Kevin Ansfield
717543835c
Added email analytics service (#12393)
no issue

- added `EmailAnalyticsService`
  - `.fetchAll()` grabs and processes all available events
  - `.fetchLatest()` grabs and processes all events since the last seen event timestamp
  - `EventProcessor` passed event objects and updates `email_recipients` or `members` records depending on the event being analytics or list hygiene
    - always returns a `EventProcessingResult` instance so that progress can be tracked and merged across individual events, batches (pages of events), and total runs
    - adds email_id and member_id to the returned result where appropriate so that the stats aggregator can limit processing to data that has changed
    - sets `email_recipients.{delivered_at, opened_at, failed_at}` for analytics events
    - sets `members.subscribed = false` for permanent failure/unsubscribed/complained list hygiene events
  - `StatsAggregator` takes an `EventProcessingResult`-like object containing arrays of email ids and member ids on which to aggregate statistics.
  - jobs for `fetch-latest` and `fetch-all` ready for use with the JobsService
- added `initialiseRecurringJobs()` function to Ghost bootup procedure that schedules the email analytics "fetch latest" job to run every minute
2020-11-26 13:09:38 +00:00
Kevin Ansfield
0c59b948fa
Added migrations for email analytics (#12387)
no issue

- cleans up unused tables `emails.{meta,stats}`
- adds timestamp columns `email_recipients.{delivered_at,opened_at,failed_at}` that can be used for event timelines and basic stats aggregation
  - indexed because we want to sort by these columns to find the "latest event" when limiting Mailgun events API requests
- adds aggregated stats columns `emails.{delivered_count,opened_count,failed_count}`
- adds a composite index on `email_recipients.[email_id,member_email]` to dramatically speed up `email_recipient` update queries when processing events
  - modifies the db initialisation to support an `'@@INDEXES@@'` key in table schema definition for composite indexes
2020-11-25 17:48:24 +00:00
Fabien 'egg' O'Carroll
cbaf6e5a74
🐛 Fixed backwards compatibility for newsletters (#12422)
closes https://github.com/TryGhost/Ghost/issues/12416

This fixes compatibility for the `send_email_when_published` option for
the Posts API.

The model layer only allows setting the `email_recipient_filter` column
when the `status` is being changed. Because of this we need to withhold
the `status` change until after we have determined the
`email_recipient_filter`.
2020-11-25 13:58:21 +00:00
Daniel Lockyer
d14f174f35 Refactored 'add column' migrations to use helper
- this removes a lot of code duplication and streamlines how we do
  things across the codebase
2020-11-25 13:56:10 +00:00
Daniel Lockyer
5c3439dc86 Refactored 'add table' migrations to use helper
- this removes a lot of code duplication and streamlines how we do
  things across the codebase
2020-11-25 13:56:10 +00:00
Daniel Lockyer
ed2a21bef4 Added JSDoc comments into package-json lib 2020-11-25 11:29:04 +00:00
Daniel Lockyer
90f7cf9e67 Reverted "Refactored remaining function in package-json lib to use async-await"
- this reverts commit d4b15141a0.
- reverting until I can figure out why the tests are broken
2020-11-25 10:57:55 +00:00
Daniel Lockyer
d4b15141a0 Refactored remaining function in package-json lib to use async-await
- this helps simplify the code and gets rid of Promise chaining
- apparently I can't easily use an async function within filter, so I've
  left it for now
2020-11-25 10:44:15 +00:00
Fabien 'egg' O'Carroll
18b87d9734
Included all subscriptions in stripeSubscriptions (#12414)
refs https://github.com/TryGhost/Ghost/issues/12256

We no longer want to filter out cancelled subscriptions, so we are able
to remove the whereIn clause of the relation.

* Fixed paid flag on member
* Fixed content gating for members

Now that the subscriptions for a member include all of them, we must
explicitly check that the member has an active subscription in order to
consider them "paid"
2020-11-25 10:39:07 +00:00
Daniel Lockyer
ac17b6bc35 Refactored package-json lib to use more async-await
- this helps get rid of all the promise chaining and indentation,
  resulting in cleaner code
2020-11-25 09:19:35 +00:00
Daniel Lockyer
4378435e12 Refactored package-json lib into a class
- this helps bring all the code together so we can extract it in the
  future
- turning it into a class also lets us easily inject the i18n instance
  and store it locally
2020-11-25 09:19:35 +00:00
Kevin Ansfield
47e81e3ca1 Fixed error when creating emails when track opens is disabled
no issue

- ensure `email.track_opens` is a boolean rather than `null`
2020-11-25 08:08:15 +00:00
Naz
0e78f2ed63 Improved job queue logging
no issue

- Makes debugging scheduled jobs easier by listing running/scheduled job names
2020-11-25 13:28:13 +13:00
Fabien 'egg' O'Carroll
217f069085
Included cancellation_reason in subscription serialization (#12411)
refs #12403 

This new property needed to be exposed in serialization so that Ghost-Admin
can use it via the API.
2020-11-23 21:19:27 +00:00
Kevin Ansfield
7bbbeeb5b0 Fixed linting 2020-11-23 19:58:34 +00:00
Kevin Ansfield
a8d8598003 Updated mega service to use open tracking from settings
refs https://github.com/TryGhost/Ghost/issues/12390

- switched from enabling open tracking based on `enableDeveloperExperiments` to using the track-opens setting
2020-11-23 18:53:55 +00:00
Kevin Ansfield
ea77f3f349
Added "email_track_opens" setting (#12404)
refs https://github.com/TryGhost/Ghost/issues/12390

- adds "email_track_opens" setting to the `email` group, defaulting to `true`
2020-11-23 18:36:12 +00:00
Kevin Ansfield
4c96aa5c95 Fixed bulk email only having 'bulk-email' tag in certain circumstances
no issue

- the `'bulk-email`' tag was only being added to bulk emails if another more specific tag was set up via config
- we always want the `'bulk-email'` tag to be present for better event filtering
2020-11-23 18:34:17 +00:00
Fabien 'egg' O'Carroll
47e266e6d3
Added cancellation_reason column to stripe subscriptions (#12405)
refs #12403 

This will allow us to store the reason for each individual subscription
cancellation.
2020-11-23 15:36:14 +00:00
Peter Zimon
bf9820f97d Fixed sans-serif link style
no refs.
- fixed regression that links in emails with sans-serif setting wasn't underlined
2020-11-23 15:50:02 +01:00
Fabien 'egg' O'Carroll
f8b617af64
Supported redirecting externally after signup (#12391)
refs #12391

Adds support for redirecting to external URL's after successful signup for members.
2020-11-23 09:36:45 +00:00
Naz
5c4e884070 Added support for graceful job shutdown and worker threads
refs #12402

- With bumped version of job-manager it offloads job procesing into separate worker thread. Having jobs run out of main Ghost process even loop allows for safe job execution, which does not block Ghost from serving requests or performing other functions without a delay
- Added experimental data access to 'testmode' jobs. This should serve as an illustration of how to access data from the job layer
2020-11-23 17:17:49 +13:00
Naz
6ba6e9c307 Removed unused authors input serializer
refs https://github.com/TryGhost/Ghost/issues/10354

- The serializer wasn't hooked up properly during the implementation (1a4497fc9a). It is not possible to hook it back in now as that would introduce breaking change to now stable v2 API.
- v3 (canary) has is properly hooked in, so there should not be a problem going forward
2020-11-23 11:35:02 +13:00
Thibaut Patel
bda4e0da70 Fixed Personal Tokens migration file name
no ref
2020-11-20 10:35:31 +01:00
Thibaut Patel
5fefa9fe96 Disabled Personal Tokens of inactive/locked users
no issue
2020-11-20 09:53:18 +01:00
Thibaut Patel
d48febbbd8 Added Personal Token removal when a user is deleted
no issue
2020-11-20 09:53:18 +01:00
Thibaut Patel
4edccfd2f7 Added the Admin API and tests for Personal Tokens.
no issue
2020-11-20 09:53:18 +01:00
Thibaut Patel
be4146e324 Enabled authentication using an api key with a user_id
no issue

- Enables the Personal Tokens feature.
2020-11-20 09:53:18 +01:00
Thibaut Patel
214ed405cc Added the user_id field to the api_keys table
no issue

- Migration related to the Personal Tokens feature.
2020-11-20 09:53:18 +01:00
Fabien 'egg' O'Carroll
38d19c999d
Removed search params from custom signup redirects (#12386)
refs #12366

This means that custom redirects will not trigger the built in notifications handled by Portal.
2020-11-19 14:47:08 +00:00
Daniel Lockyer
49197a6597 Merged 3.38.2 into master
* origin/3.38.2:
  v3.38.2
  Updated Ghost-Admin to v3.38.2
  🐛 Added missing Bluebird require in v2 API
2020-11-19 13:43:18 +00:00
Daniel Lockyer
c72ecf6a96 🐛 Added missing Bluebird require in v2 API
- this was missed in the original pass when removing the global Promise
  override
2020-11-19 13:36:15 +00:00
Fabien 'egg' O'Carroll
9ba3e96790
Redirected to custom path on successful signup (#12372)
refs #12366 

This implements redirection based on the settings for successful member sign up!
- Removes support for redirecting to `req.path` afterwards, this was never used and
  we now have a more configurable implementation.
- Retains redirection to the homepage for unsuccessful sign up (invalid/expired token)
2020-11-19 09:58:32 +00:00
Fabien 'egg' O'Carroll
2600a20f54
Added new settings for members signup redirects (#12370)
refs #12366

Adds two new settings for redirecting members after signup, based on if
they're paid or free, defaulting to the current redirect of "/"
2020-11-17 17:17:54 +00:00
Fabien 'egg' O'Carroll
8ad995203e
🐛 Fixed migration for SQLite3 (#12371)
no-issue

The current version of SQLite3 bundled with Ghost supports a maximum of 999
variables, we use one variable for the SET value and so we're left with 998 for the
WHERE IN clause values.

refs: https://forum.ghost.org/t/unable-to-start-ghost-3-38-0-locally/18322
2020-11-17 16:39:42 +00:00
Fabien 'egg' O'Carroll
bfc152bede
Fixed handling of email_recipient_filter option (#12369)
no-issue

This logic would assume that the option was always passed at the point
of publishing the post, which is not the case for scheduled posts.

Instead of setting the property to 'none' when the option is not
present, we take the approach of ONLY setting the propery when
1. It is present and not 'none'
2. The post is being published or scheduled

This means that scheduled posts will have the property set correctly,
and any future publishing will leave the it in the original state
2020-11-17 11:00:03 +00:00
Rish
f53ab0f52b Updated default config for newsletter badge
no refs

- Migrations were using previous default for badge which was set to `false`
- Default config for badge was `false` which caused migration to switch off the badge, updated to `true`
2020-11-17 16:06:31 +05:30
Daniel Lockyer
33bc3c9011
Refactored column migrations to use helpers (#12360)
- these helpers remove a lot of the duplicated code that we had when doing up/down column migrations, and provides a much shorter way of doing this in the future
2020-11-16 11:55:51 +00:00
Peter Zimon
02d78a89aa Udpated email template footer spacings
no refs.
- updated padding between Ghost badge and rest of the footer
2020-11-16 12:31:37 +01:00
Rish
2d04bfbac9 Enabled newsletter badge by default
no issue

Updated newsletter badge to be shown by default which was previously set to be hidden. Users can still control the visibility via Email settings in Ghost-Admin
2020-11-16 15:55:05 +05:30
Peter Zimon
cfac18cb32 Newsletter email template refinements
no refs.
- updated color styles, links for footer text
- refined spacings for email footer paragraphs
2020-11-13 16:58:56 +01:00
Fabien 'egg' O'Carroll
3f9053ae94
Added setting for newsletter footer content (#12359)
no-issue

We want to give users to ability to customise the content of their newsletter, and the first step
toward that is a setting in which we can store text or html to embed in the template
2020-11-13 13:18:47 +00:00
Fabien 'egg' O'Carroll
4604ba1587
Fixed backward compatibility for send_email_when_published (#12357)
no-issue

* Handled send_email_when_published in Posts API

This restores backwards compatibility of the Posts API allowing existing
clients to continue to use the `send_email_when_published` flag. This
change uses two edits, which is unfortunate. The reason being is that
this is an API compatibility issue, not a model issue, so we shouldn't
introduce code to the model layer to handle it. The visibility property
of the model is used to determine how to fall back, and because it can
be left out of the API request, and relies on a default in the settings,
we require that the model decide on the `visibility` before we run our
fallback logic (or we duplicate the `visibility` default at the cost of
maintenance in the future)

* Dropped send_email_when_published column from posts

Since this column is not used any more, we can drop it from the table.
We include an extra migration to repopulate the column in the event of
a rollback

* Updated importer to handle send_email_when_published

Because we currently export this value from Ghost, we should correctly
import it. This follows the same logic as the migrations for this value.

* Included send_email_when_published in API response

As our v3 API documentation includes `send_email_when_published` we must
retain backward compatibility by calculating the property.

* Fixed fields filter with send_email_when_published

* Added safety checks to frame properties

Some parts of the code pass a manually created "frame" which is missing
lots of properties, so we check for the existence of all of them before
using them.

* Fixed 3.1 migration to include columnDefinition

We require that migrations have all the information they need contained
within them as they run in an unknown state of the codebase, which could
be from the commit they are introduced, to any future commit. In this
case the column definition is removed from the schema in 3.38 and the
migration would fail when run in this version or later.
2020-11-11 13:03:41 +00:00
Peter Zimon
215bfd0a7a
Added newsletter design settings (#12352)
refs https://github.com/TryGhost/Ghost/issues/12355

- Adds new default settings for newsletter customisations - `newsletter_show_badge`, `newsletter_show_header` and `newsletter_body_font_category`
- Adds migrations to update group for new settings
- Add migration to update settings based on existing config value for newsletter settings
- Passes new newsletter settings to newsletter template and updates design based on them
- Fix tests
2020-11-11 18:26:11 +05:30
Naz
5b6fe31002 Added support for jobs defined as modules
no issue

- These changes should allow easy testing of scheduled jobs and provide an implementation example for future jobs within Ghost
- Added experimental support to run jobs based on functions defined in modules
- Extendeded testmode "schedule" endpoint with an optional job name parameter which picks up job from "jobs" folder
- Adjusted scheduleJob method use and passed "schedule" as first parameter
- Bumped job-manager version to allow for all new functionality
2020-11-10 17:29:35 +13:00
Naz
d6505899a7 Moved testmode routes into their own folder
no issue

- Living in separate folder will allow adding more testmode specific modules (for example test jobs)
- Mimicks the folder structure of how v2/canary routes are structured better
2020-11-10 15:01:28 +13:00
Fabien 'egg' O'Carroll
6140a98351
Updated newsletter functionality to use email_recipient_filter (#12343)
no-issue

* Used email_recipient_filter in MEGA

This officially decouples the newsletter recipients from the post
visibility allowing us to send emails to free members only

* Supported enum for send_email_when_published in model

This allows us to migrate from the previously used boolean to an enum
when we eventually rename the email_recipient_filter column to
send_email_when_published

* Updated the posts API to handle email_recipient_filter

We now no longer rely on the send_email_when_published property to send
newsletters, meaning we can remove the column and start cleaning up the
new columns name

* Handled draft status changes when emails not sent

We want to reset any concept of sending an email when a post is
transition to the draft status, if and only if, and email has not
already been sent. If an email has been sent, we should leave the email
related fields as they were.

* Removed send_email_when_published from add method

This is not supported at the model layer

* Removed email_recipient_filter from v2&Content API

This should not be exposed on previous api versions, or publicly

* Removed reference to send_email_when_published

This allows us to move completely to the email_recipient_filter
property, keeping the code clean and allowing us to delete the
send_email_when_published column in the database. We plan to then
migrate _back_ to the send_email_when_published name at both the
database and api level.
2020-11-06 17:32:23 +00:00
Fabien O'Carroll
44b43a4755 Fixed error with removal of global.Promise override
refs #12182

These were missed in the cleanup and were causing errors.
2020-11-06 16:49:10 +00:00
Fabien O'Carroll
0ce8e3171b Bound SSOAdapter methods to their instance
no-issue

This allows custom SSO adapters to store instance config and refer to
in their methods
2020-11-05 13:08:11 +00:00
Kevin Ansfield
aface9ed4c Enabled Mailgun open tracking when dev experiments is enabled
no issue

- set `emails.track_opens` to `true` when the `enableDeveloperExperiments` flag is set
- update mailgun bulk-email provider to pass the open-tracking header to Mailgun when the email's `track_opens` flag is set
2020-11-05 12:55:08 +00:00
Daniel Lockyer
c8f23873c6 Fixed wording on warning message
- derp
2020-11-05 12:18:28 +00:00
Daniel Lockyer
d8fdf2052c Added check for missing DB data
- this check should never really be hit unless the DB has missing data,
  but the code tries to read from an undefined variable and crashes, which we
  can protect against
- this can be triggered by doing a `knex-migrator init` followed by a
  `knex-migrator rollback --v 3.0 --force`
2020-11-05 12:13:58 +00:00
Daniel Lockyer
a3c8b032de Added missing Bluebird requires
- 504509bb6 removed the global override for Promise
- there are a bunch of places in code that use Bluebird Promise methods,
  but Bluebird wasn't being imported in these places
- this would have thrown errors all over the place
2020-11-05 11:55:14 +00:00
Kevin Ansfield
d41c2f8446
Added migration for emails.track_opens column (#12331)
no issue

- tracking of bulk email opens can be enabled/disabled over time, if we're calculating analytics for emails we don't want emails which didn't have tracking enabled skewing the results so we need a record of whether tracking was enabled for each email
2020-11-05 11:39:53 +00:00
Naz
c7541a1fe0 Added scheduled job support in testmode
no issue

- We need an ability to early test scheduled jobs to refine the new feature and it's API. Should be used with caution
- To schedule an example scheduled job every 30 seconds run following request: `curl http://localhost:2368/ghost/api/schedule/every%2030%20seconds`
2020-11-05 17:46:57 +13:00
Naz
f69526c140 Fixed error logged for absent redirects config
refs #11085

- Incorrect usage error was logged to the output when there was no recirecst configuration file present in the system. Previously an empty string was returned in such situation, resulting in "ENOENT" error, which was ignored through special handling.
- The fix resembles logic in redirects async getter function where empty array is returned when the config file does not exits.
- Attempting to read unexistent config should not ever happen and will be handled on the config service layer, this is why special "ENOENT" handling has been removed
2020-11-05 12:45:02 +13:00
Fabien O'Carroll
601b6ce174 Added migration to populate recipient_filter
no-issue

This populates the recipient_filter column for existing emails
based on the visibility of the related post at the time of the
migration.
2020-11-04 16:29:54 +00:00
Fabien O'Carroll
918c721bd1 Added recipient_filter column to emails table
no-issue

This column will allow us to store the canonical recipient filter on the
email resource giving us a detailed log of which members an email was
intended for
2020-11-04 16:29:54 +00:00
Fabien O'Carroll
dd0f80c9fa Added migration to populate email_recipient_filter
no-issue

This populates the email_recipient_filter column for existing posts
based on 1. if they were sent as an email and 2. their visibility.
2020-11-04 16:29:54 +00:00
Fabien O'Carroll
3b0a84b33b Added email_recipient_filter column to posts table
no-issue

This column will allow us to decouple the recipients of newsletter from
the `visibility` of a post, allowing us to send emails to specifically
free members, or to send paid posts as newsletters to all members.
2020-11-04 16:29:54 +00:00
Daniel Lockyer
9b45ea6f7d Added original error to logged error message
- without this, the original error stacktrace is hidden which makes
  issues harder to track down
2020-11-04 14:00:59 +00:00
Kukhyeon Heo
504509bb67
Removed global.Promise override (#12182)
closed #11943 

* Remove global.Promise
* Fix brute-knex bluebird error.
* Fix api-acceptance tests.
* Fix unit tests
2020-11-04 10:55:47 +00:00
Naz
e3e15646eb Updated redirect error message
refs #11085

- The logic which uses this error deals with an input yaml file thus the change.
- Related discussion here - https://github.com/TryGhost/Ghost/pull/12187#discussion_r508144220
2020-11-04 12:26:22 +13:00
Kukhyeon Heo
5715aa2155
Added .yaml format support in redirects configuration (#12187)
closes #11085

- Ghost has been using YAML format for other configurations (e.g. routes). The plan is to move to this format for all user-edited settings files. By default JSON format is still used in Ghost Admin API v2/v3, but will be changed to YAML in API v4. Check referenced issue for more context.
- New format supports all the features available before. The main noticeable change is the structure of config file. It is now grouped by redirect HTTP code instead of specifying `"permanent": true | false` attribute for each config property. Example format for YAML config:
```
302:
  /from-url/: /to-url/

301:
  /category/([a-z0-9\-]+)/i: /tag/$1/
  /v([0-9\.]+)/docs/([a-z0-9\-]+)/i: /docs/$2/
```
- Added 2 new endpoints: `POST redirects/upload` and `GET redirects/download`. These serve as an alias to current GET/POST `/redirects/json. "upload/download" naming pattern is introduced to match the convention with other resources that can be uploaded and downloaded (images, themes etc.). `/redirects/json`  endpoints will be removed in Admin API v4
- The parsing code from `custom-redirects.js` has been moved to `frontend/services/redirects/settings.js`. This location is more appropriate for this logic and eventually `custom-redirects.js` middlewear might be moved into "frontend" as this middlewear plays a role mostly effecting that area.
2020-11-04 12:08:32 +13:00
Rishabh Garg
8ad11fe082
Enabled Portal (#12317)
no refs

[Portal](https://github.com/TryGhost/Portal) is a new drop-in script to make the bulk of Ghost membership features work on any theme out of the box, which was under a developer flag so far. This release removes the flag for Portal and makes it included as default for any members-enabled Ghost site. The Portal script is backward compatible with old public members script and existing Members-enabled themes should notice no change.

- Removes Portal config flag as Portal is now enabled by default
- Removes old members script as Portal is backward compatible with it
- Changes `{{content}}` helper to show default CTA in case of restricted content access
- `accent_color` setting is no more behind the dev experiment flag and included by default
- Adds migration to switch off Portal button setting for all existing sites which don't have Portal enabled in beta
2020-11-03 14:36:21 +05:30
Naz
4ebebd12d3 Removed unneeded parameter
no issue

- There is no second parameter in Promise constructor. Didn't serve any purpose as per comment - bbbd011074 (r41413465)
2020-11-03 13:38:08 +13:00
Naz
7326241718 🐛 Fixed server error for repeated order query parameter
closes #12263

- Express parses repeated query parameters as an array (req.query properties). Because there is no clear reason on why not to support this behavior extended order parameter parsing logic to handle arrays. This follows the rule of "liberal inputs, conservative outputs"
- Example supported query string for ordering can now look like: `?order=featured&order=published_at asc`, the priority of the order stays the same with the most significant appearing first and least significant last
2020-11-03 00:15:24 +13:00
naz
a2bfef53de
🐛 Fixed post resource filtering by posts_meta table fields (#12307)
refs #11572

- Filtering by fields coming from posts_meta table did not work for post resources. This was due to lack of support for these types of operations on NQL layer. The approach taken here is using same way filtering was done for many:many relations and generates a `WHERE IN` filtering clause. In the future we could look into adding preloading of 1:1 relations which should allow getting rid of `WHERE IN` in favor of `JOIN` and filtering directly by field names. 
- Changed structure of `EXPANSIONS` filter configuration. Current approach was based on "bag of all the things". Such structure will become problematic as more fields are added. For example, adding all the fields from 1:1 relation posts:posts_meta might collide with any other relations that would have similar naming like meta_description from tags table (if it were was added).
- Bumped nql version to 0.5.0. This adds filtering support to 1:1 relations
- Added filter expansions which can be unique per model Previous approach with single global expansions lookup wasn't working in case different models would need to declare expansion for same field names. Having a `filterExpansion` method per model works in a similar convention other filter related model methods do (e.g. enforcedFilters, defaultFilters)
2020-11-02 12:53:57 +13:00
Kevin Ansfield
7a74b78940 Included email-id as user variable when sending bulk emails via mailgun
no issue

- adding user variables via the mailgun API when sending emails means that events related to email have those variables attached to them
- adding the `email.id` value to user variables means we can easily associate mailgun events to emails, otherwise we'd have look up the batch via `email_batches.provider_id` then use a join to get back to the associated email
2020-10-29 16:37:42 +00:00
Daniel Lockyer
85da9bdeb2 Updated Ghost versioning lib to include all prerelease IDs
- currently if Ghost has a version of something like
  `3.37.0-pre.abc.def`, Ghost will return `3.37.0-pre.abc` as the full
  version
- this hides parts of the version which are useful for debugging
- this commit updates the logic to join together all prerelease elements
  so we keep the full string
2020-10-29 12:44:55 +00:00
Rish
c4c6bdf353 Updated mail template for member email change
no issue

The email change verification template was using the same as for `subscribe`, which did not have the right messaging that needs to be communicated about the action thats happening in this case. Updates the email template to same as what we use for email verification for support/newsletter address
2020-10-29 17:40:19 +05:30
Rish
746ccbd7ed Added custom action param for magic links
closes https://github.com/TryGhost/Ghost/issues/12253

- Allows using custom action param for requests from Portal by using a new `requestSrc` option that is passed down when a request for magic link is made via Portal
2020-10-29 13:42:52 +05:30
Rishabh Garg
0ce5f87616
🐛 Added past_due and unpaid subscriptions for members (#12301)
refs https://github.com/TryGhost/Ghost/issues/12256 , https://github.com/TryGhost/Ghost/issues/12255

Currently when listing subscriptions for Members in both the Admin and the Theme, we only show the subscriptions which have a status of trialing or active.

Based on discussion, the `unpaid` and `past_due` states on Stripe also represent owner's intention of considering a subscription as active instead of `cancelled`, so we allow any subscriptions under these 2 states to be also listed for a member and consider them as `paid`.

- Subscriptions will go into a past_due state if the payment is missed, this should be considered a grace period where the member still has access.

- After this the subscriptions will either go to the unpaid or the cancelled state - this can be configured on an account by account basis in the Stripe dashboard. `unpaid` is considered as an intention to keep the subscription to allow for re-activation later.
2020-10-27 15:19:48 +05:30
Peter Zimon
43b7c7b344
Updated members admin redirect to new page(#12296)
no refs.

- Members settings were moved to a new page [here](https://github.com/TryGhost/Ghost-Admin/pull/1736)
- updated members related redirect for from/newsletter email address change to point to new page
2020-10-22 16:10:25 +05:30
Rishabh Garg
7182efdb88
🐛 Fixed special chars in single use token (#12290)
no refs

- The token generation logic for single use token was replacing only the first instance of + or / to make the token URL safe, instead of replacing all instances which caused a bug where token was not validated properly in case it included multiple + or / in it.

- The fix ensures replacing all the + or / in the token with URL safe _ or - so it can be properly validated via magic link
2020-10-20 11:49:20 +05:30
naz
8ddf83f3c5
Fixed "no-shadow" linting error in server modules (#12287)
refs 143921948d

- Continuation of changes started in referenced commit
2020-10-20 12:02:56 +13:00
naz
38b7f8e311
Fixed "no-shadow" linting error in server/data modules (#12288)
refs 143921948d

- Continuation of changes started in referenced commit
2020-10-20 11:56:46 +13:00
Kevin Ansfield
d08b586781 🐛 Fixed width/height attributes in gallery cards not matching resized images
closes https://github.com/TryGhost/Ghost/issues/11548

- bumps @tryghost/kg-default-cards
  - introduces a breaking change where `imageOptimization` is passed as an options argument instead of separating out individual config options
  - fixes width/height attributes for images in gallery cards being larger than the max default width used when resizing images
2020-10-19 17:05:57 +01:00
Kevin Ansfield
3148d6a946
Moved definition of default max image width to config (#12289)
no issue

- centralises definition of max width and allows customisation if needed
- allows for passing of the config value through to rendering libraries
2020-10-19 08:56:18 +01:00
Kevin Ansfield
16bfb3fa41 Added basic CRUD admin API for snippets
no issue

- standard browse/read/add/edit/destroy API endpoints for snippets resource
- updates `@tryghost/admin-api-schema` dependency to version that includes snippet definition and schemas
2020-10-16 18:02:58 +01:00
Kevin Ansfield
e5a92be932 Fixed typo in snippets permission fixtures
refs https://github.com/TryGhost/Ghost/pull/12283

- `delete` -> `destroy` for action type
2020-10-16 17:46:31 +01:00
Kevin Ansfield
15afed4b81
Added snippets permissions migration and fixtures (#12283)
no issue

- all staff users can browse/read snippets so they're usable in the editor for everyone
- only administrators, editors, and admin integrations are able to create/edit/delete snippets
2020-10-16 12:53:09 +01:00
Kevin Ansfield
74269070dd
Added migration for snippets table creation (#12282)
no issue

- minimal table structure required for the first iteration of content snippets
- snippets are stored pieces of re-usable content that could effectively be entire posts so the `mobiledoc` field length matches the `posts.mobiledoc` field length
2020-10-16 12:52:06 +01:00
Rish
a5a7515fe8 Fixed typo in private setting import
no refs

Last commit introduced a small typo in how private setting is ignored during import
2020-10-15 15:55:24 +05:30
Rish
3e0948d04e Ignored member email settings in import
no issue

- Member email settings - newsletter and support - was previously being set to null for all cases, which can lead to `null` values being set for those addresses on a successful import whereas the expected behavior is to retain the old values.
- Adds the 2 email settings to ignored settings list so they are not changed during import process
2020-10-15 15:32:05 +05:30
Rish
54e638310c Added default support address for member site data
no issue

- In a case where support address is null, member's site data won't load as there was no fallback.
- The fix adds default `noreply` as the support address for site data
2020-10-15 14:44:16 +05:30
Fabien O'Carroll
a9b3d83e00 Loaded Action model after all other models
no-issue

The Action model loops through all registered models when the file is
loaded, by loading the model last, we ensure that it can read all
models, rather than an arbitrary selection which come before it.
2020-10-14 15:11:45 +01:00
Fabien O'Carroll
0fd0527da0 Fixed creation of EmailBatch and EmailRecipient collections
no-issue
2020-10-14 15:11:45 +01:00
naz
392140cb36
Allowed for comped field when creating a member through Members API (#12278)
closes #12273

- `comped` field has been allowed when editing a member or importing from a CSV. There has been a usecase (Zapier Integration) for API client to create a member with "Complimentary" plan, which made this change necessary
- Previously the logic for comped field was to skip and continue member record creation if Stripe was not connected. Now we throw an error - same as the one we have been throwing before when stripe_customer_id field was passed in. The implication of this change is that we won't be creating any record now if comped === true and Stripe is disabled. 
- Bumped admin-api-schema-package. Contains `comped` schema change so this field gets passed through to controller
2020-10-14 13:24:09 +13:00
Kevin Ansfield
8ad384d7f8 🐛 Fixed scheduled post emails pointing at /404/ for the "view online" link
no issue

When scheduling a post to publish+send the "view online" link was pointing at https://site.com/404/ rather than the published post's url.

The problem occurred because the `/schedules/` endpoint wraps it's post read+edit calls in a transaction. Context:
- when a post is published with with the "send email" option the email record is immediately generated and added to the API response, as part of the email record generation we render the email content including fetching the url for the "view online" link
- urls for all resources are handled by our `url` service, that service updates it's internal cache based upon model events such as the "edited" event triggered when a post is published
- if the posts API controller is given a transaction, the email record is also generated inside of that transaction however at this point the `url` service will not have been updated because the post record hasn't been committed meaning it has no available url for the post

Fix:
- removed the `models.Base.transaction()` wrapper around the post read+update in the `/schedules/` API controllers
- we don't need a transaction here. It was added as protection against another write request coming in between the `/schedules/` controller reading a post and publishing a post but we already have protection against that in the form of collision detection - if a write request comes in and commits between the schedules controller reading the post and updating it, the scheduler's update call will fail with a collision error at which point the scheduler itself should retry the request which could then publish the post successfully if everything else is in order
2020-10-08 10:26:49 +01:00
Kevin Ansfield
9cbeb74db0
🐛 Fixed broken embeds cards when pasting links to Wordpress sites (#12262)
closes https://github.com/TryGhost/Ghost/issues/12260

- if a card type was not explicitly chosen (i.e. a url was pasted into the editor) then abort fetching the oembed endpoint if we detect it's a `wp-json` oembed and return a bookmark card payload instead
- cleaned up an unused argument in the internal `fetchBookmarkData()` method
2020-10-06 08:44:03 +01:00
Kevin Ansfield
cde364bf27 🐛 Fixed email card replacements showing raw replacement text in emails
closes https://github.com/TryGhost/Ghost/issues/12257

- there was a destructuring problem introduced in the recent email refactor which meant the correct replacement data was not being passed over to the Mailgun provider when sending email
2020-10-05 17:24:48 +01:00
Kevin Ansfield
4f211d025d Fixed members with multiple subscriptions receiving multiple newsletters
closes https://github.com/TryGhost/Ghost/issues/12259

- adds a `DISTINCT` to the query used to fetch member rows when generating an email recipient list
- this increases query time 2.7s vs 1.6s locally with ~94k paid members but once the `members.paid` column is implemented this slow query can be removed
2020-10-05 16:53:35 +01:00
Naz
39e403d176 🐛 Fixed meta attributes calculation on post preview
closes https://github.com/TryGhost/Ghost/issues/12247

- Internal preview controller was lacking "mapping" call to post object which handled not only missing meta attribute information but lots of other mappings (e.g. users, tags, etc.)
- Have added a regression test to catch issues like this in the future
2020-10-05 17:52:40 +13:00
Kevin Ansfield
8f3ab3c535 🐛 Fixed email showing as success when an email batch fails to send
no issue

- fixed passing of errors up through send/processBatch/processEmail
- fixed errant overwrite of email status with a "submitted" status after a failure had occurred
2020-10-02 14:26:57 +01:00
Kevin Ansfield
b5ffb38726 Fixed partial email batch/recipient records when email creation fails
no issue

- wrap email batch/recipient record creation in a transaction so if an error occurs during creation we're not left with a partially created batch/recipient set in the database
2020-10-02 13:47:14 +01:00
Kevin Ansfield
0f30b9f6a0 🐛 Fixed email not showing as failed if error occurs when preparing email
no issue

- if an error occurred whilst creating email batch/recipient records the email status was never updated and was left in the 'pending' status
- adjusted the error handling to update the email status and record the error message if such a scenario occurs
2020-10-02 12:40:49 +01:00
Kevin Ansfield
7b789e1cd5 🐛 Fixed newsletters being sent to Stripe customer emails in place of member emails
no issue

- the paid-member SQL query that is obtained using `models.Member.getFilteredCollectionQuery({paid: true})` can return multiple columns with the same name (eg, `email`, `name`), when that happens the last column with duplicate names "wins" and it's value is used in the resulting knex row instance
- in the `mega` service when fetching email recipient rows we ran into this problem, to avoid it we adjust the query to explicitly select only the data from the `members` table
2020-10-02 12:17:17 +01:00
Kevin Ansfield
82126f29e6 Added guard for member rows having missing data when creating email recipients
no issue

- we've had an issue with emails failing due to unexpectedly missing data when inserting email recipient rows
- added a validation check before adding recipient details along with a log so that invalid data can be investigated
2020-10-02 11:29:10 +01:00
Rish
58fda5bad2 Allowed accent color setting for site with portal flag
no issue

- We had previously allowed accent_color setting for member site settings behind portal flag, but Ghost Admin also needs the public site setting with accent color to correctly reflect the accent color when flag is switched on
- Removes deletion of accent color setting when behind the Portal flag OR dev experiment flag
2020-10-02 15:30:54 +05:30
Kevin Ansfield
defb43fe7a 🐛 Fixed newsletters emails having no subject
no issue

- subject was not being picked out of the message data when passing over to the mailgun-js send method
2020-10-01 19:51:01 +01:00
Kevin Ansfield
e34acc31c5 🐛 Fixed newsletter email sending
no issue

- there was an typo in the recent email sending refactor that resulted in `Error: 'to' parameter is missing` errors when sending email previews and bulk emails
2020-10-01 19:03:57 +01:00
Fabien 'egg' O'Carroll
ce14d8113a
🐛 Fixed members form missing data-members-name (#12250)
refs #12249

This was incorrectly assuming the presence of the data-members-name
element in the document. By guarding against it and defaulting to
undefined, we fallback to the existing behaviour when the element is not
present.
2020-10-01 18:05:23 +01:00
Fabien 'egg' O'Carroll
244704156c
Updated all Origin header checks to handle 'null' (#12246)
closes #12244

As per RFC 6454 the Origin header MUST be set to the string 'null' when
in a "privacy-sensitive" context. We were not handling this string and
this was causing errors. This commit updates all checks of the 'Origin'
header to treat the value 'null' as if the header was not present.

ref: https://tools.ietf.org/html/rfc6454#section-7.3
2020-10-01 09:37:22 +01:00
Rishabh Garg
eb4933d27e
Added extra settings to member auth emails (#12238)
no issue

- Adds accent color, site domain and url to member auth emails for customization
2020-10-01 12:31:36 +05:30
Fabien O'Carroll
fd5194ea46 Updated magic link expiry to 24 hours
no-issue

As discussed with @JohnONolan 24 hours is the preferred expiry for magic links
2020-09-30 10:53:35 +01:00
Fabien 'egg' O'Carroll
c19d282a51
Ensured Admin API members resource only returns known fields (#12240)
refs #12055

As part of the work in TryGhost/Members#206 we load the stripeCustomers relation on the member model, and we do not want this to be part of the API response. The changes here include a refactor but the main thing is that the serialized object is explicit and does not include unexpected or unknown fields.

* Moved mapMember out of mapper file

This cleans up the serializer a bit by keeping it's functionality all in
one place, rather than a shared mapper file

* Refactored members controller to return models

Previously the controller was calling toJSON, which is serialization,
this updates the controller to only deal with models, leaving all of the
serialization to the serializer!

* Refactored members serializer

This adds typings to all of the methods/functions in the serializer, as
well as making the serializating explicit, rather than returning the
result of toJSON, we explicitly set the properties we expect to be on
the output object. This protects us against accidental API changes in
the future.
2020-09-30 10:22:22 +01:00
naz
071bf8edb5
Extracted Admin API JSON schemas into @tryghost/admin-api-schema (#12237)
closes https://github.com/TryGhost/Ghost/issues/10628

- JSON Schemas were extracted into a separate module to allow other clients to reuse them (for example documentation). Having them in a separate package also slims down the amount of code needed to be maintained in the core.
- Updated canary API input validators to use admin-api-schema module
- Removed canary schemas that moved into admin-api-schema package
- Updated v2 API input validators to use admin-api-schema package
- Removed v2 schemas that moved into admin-api-schema package
- Updated tests to contain needed information in apiConfig to pick up correct validation
- Added @tryghost/admin-api-schema package dependency
2020-09-30 16:25:12 +13:00
Kevin Ansfield
932def7e6d Fixed replacement strings showing when viewing sent emails
refs https://github.com/TryGhost/Ghost/pull/12232

When viewing sent emails in Ghost's admin area, it displays the `html` field directly from the `email` relation loaded with the post. Since the `mega` refactor we now store raw content in that field rather than sanitized "preview" content so it's necessary to modify the API output to match the old behaviour.

- use the API output serializers to parse replacements in email content and replace with the desired fallback or empty string
2020-09-29 17:39:22 +01:00
Kevin Ansfield
474e6c4c45 Refactor mega service to use stored email content and batch/recipient records
no issue

- store raw content in email record
  - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed
- split post email serializer into separate serialization and replacement parsing functions
  - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements
  - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content
- move mailgun-specific functionality into the mailgun provider
  - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service
  - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place
  - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows
- `bulk-email` service split into three methods
  - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails
  - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently
  - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed
  - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible
- refactored `mega` service
  - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats
    - used for generating email content before storing in the email table, and when sending test emails
    - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData`
  - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method
  - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service
  - member row fetching extracted into a separate function and used by `createEmailBatches`
  - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-29 17:17:54 +01:00
Kevin Ansfield
d34a3263e8 Store email batch and recipient records when sending newsletters (#12195)
requires https://github.com/TryGhost/Ghost/pull/12192

- added initial `EmailBatch` and `EmailRecipient` model definitions with defaults and relationships
- added missing `post` relationship function to email model
- fetch member list without bookshelf
    - bookshelf can add around 3x overhead when fetching the members list for an email
    - we don't need full members at this point, only having the data is fine
    - if we need full models later on we can push the model hydration into background jobs where recipient batches are fetched ready for an email to be sent
    - bookshelf model instantiation of many models blocks the event loop, using knex directly keeps concurrent requests fast
    - adds `getFilteredCollectionQuery` method to base model to facilitate getting a knex query based on our normal model filters along with transaction/forUpdate applied
- store recipient list before sending email
    - chunk already-fetched members list into batches and insert records into the `email_recipients` table via knex
    - chunked into batches of 1000 to match the number of emails that Mailgun accepts in a single API request but this may not be the absolute fastest batch size for recipient insertion:
        | Batch size | Batch time | Total time |
        | ---------- | ---------- | ---------- |
        |        500 |       20ms |     4142ms |
        |       1000 |       50ms |     4651ms |
        |       5000 |      170ms |     3540ms |
        |      10000 |      370ms |     3684ms |
    - create an email_batch record before inserting recipient rows so we can effeciently fetch recipients by batch and store the overall batch status
2020-09-29 17:17:54 +01:00
Rish
8e0976fc8f Allowed accent color in members site data
no issue

- The accent_color setting was being removed from members site data when behind portal flag
- The accent color setting is now allowed in members site data for all cases as it doesn't make any sense to remove it specifically from here where we already have all the other Portal settings included which is a dev/portal flag feature anyways
2020-09-29 17:23:33 +05:30
Fabien 'egg' O'Carroll
854a41e556
🐛 Fixed CSV import json-schema email validation (#12239)
no-issue

By using the "email" validation, we were validating emails in CSV
imports using a different validator to the rest of the API. AJV's built
in email validation was failing on emails with "special" characters,
such as letters with an umlaut above them.

This commit brings the validation for CSV imports in line with the rest
of the API.
2020-09-29 12:51:35 +01:00
Peter Zimon
4e45373730 Updated Webhook API error message
refs 6f1abc610a

- updated missing Webhook API error message when integration is missing
2020-09-28 13:24:47 +02:00
Naz
50f30c4f66 Fixed minor formatting in i18n file 2020-09-28 14:21:29 +13:00
Rish
03968219a7 🐛 Fixed email verification mails not sent
no issue

- Email ownership verification emails for support/from address was not using the updated magic link service syntax
2020-09-24 23:39:25 +05:30
Kevin Ansfield
1dfaf8c5f8
Added migration to store from/reply-to values in email table (#12204)
no issue

The email table should be a reference for all data that was used when sending an email. From and Reply-to addresses can change over time and we don't have any other reference for their value at the time of sending an email so we should store them alongside the email content.

- schema updated with `from` and `reply_to` columns
- both are set to `nullable` because we don't have historic data (can be populated and changed in later migrations if needed)
- neither `from` or `reply_to` have `isEmail` validations because they can have name+email in an email-specific format
- will help keep concerns separated in the future. `mega` service can deal with all of the email contents/properties, and the `bulk-email` service's concerns are then only email sending and any provider-specific needs
2020-09-24 08:20:10 +01:00
Naz
43153ba31e Disabled changing webhook's integration_id through PUT API
refs #12033

- Allowing to change parent integration opens up possible security holes and has no clear usecase at the moment. After a webhook record is created it should not be possible to change parent integration.
- Had do partially duplicate JSON schema definition from webhooks definition as there is no proper composition technique available in current version of JSON Schema.
2020-09-24 16:24:02 +12:00
Naz
6f1abc610a Added check for parent integration_id when creating a webhook
refs https://github.com/TryGhost/Ghost/issues/12033
refs https://github.com/TryGhost/Ghost/issues/10567

- Creating a webhook without valid parent integration leads to orphaned webhook records, which shoult not ever happen
- This scenario is only possible for non-integration authentication,
because in case of integration being authenticated it's id is
automatically assigned to creatd webhook
2020-09-24 15:09:51 +12:00
naz
bbcc83dadb
Added support for ordering Post API resources by fields coming form posts_meta table (#12226)
refs #11729

- When ordering is done by fields from a relation (like post's `meta_title` that comes form `posts_meta` table), Bookshelf does not include those relations in the original query which caused errors. To support this usecase added a mechanism to detect fields from a relation and load those relations into query. 
- Extended ordering to include table name in ordered field name.  The information about the table name is needed to avoid using `tableName` within pagination plugin and gives path to having other than original table ordering fields (e.g. order by posts_meta table fields)
- Added test case to check ordering on posts_meta fields
- Added support for "eager loading" relations. Allows to extend query builder object with joins to related tables,
which could be used in ordering (possibly in filtering later). Bookshelf does not support ordering/filtering by proprieties coming from relations, that's why this kind of plugin and query expansion is needed
- Added note about lack of support for child relations with same property names.
2020-09-24 13:32:40 +12:00
Kukhyeon Heo
635d26469f
Fixed settings reinit to only emit changed settings events (#12184)
closes #12038

Previously we were emitting changed events for _all_ settings which would
cause any listeners for those to be triggered, this ensures that listeners are
only triggered if the corresponding setting, _did_ in fact change.
2020-09-23 14:35:03 +01:00
Fabien O'Carroll
171908cefa Added support for filtering Members CSV exports
no-issue

Adding these options to the endpoint will pass them to the model layer,
allowing us to filter members CSV exports
2020-09-23 14:29:15 +01:00
Léo Bourrel
87855075c0
Added support for data-members-name in themes (#12191)
no-issue

This change allows themes to collect member names on signup with use of the
`data-members-name` attribute on an input element in the `data-members-form`
2020-09-23 13:59:26 +01:00
Nazar Gargol
84c8bcc457 Extracted ordering into separate bookshelf plugin
refs #11729

- Having a plugin allows to reason about ordering better and gives way
to further extraction away form the core.
- Just like with filtering, ordering falls into similar category of having effect on knex'es query builder object extension through additional statements - ORDER BY in this case.
- Because there were bugs associated with use of permittedAttributes inside of `parseOrderOption` method, introduced separate `orderAttributes` function which returns only those fields which are orderable (https://github.com/TryGhost/Ghost/issues/11729#issuecomment-685740836)
2020-09-22 16:27:12 +12:00
Nazar Gargol
32b37d7ba8 Improved error messaging for password reset process
refs #11878

- When password reset link is invalid previous messaging left the user
without clear information about why the reset failed and what they could do about it.
- Updated messaging around password reset tokens including detection of
when password token has invalid structure, has expired or has already
been used
2020-09-22 15:45:19 +12:00
Kevin Ansfield
6dc8d91ace Revert "Store email batch and recipient records when sending newsletters (#12195)"
This reverts commit 80af56b530.

- reverting temporarily so that all associated functionality can be merged in a single release
- creating email batch/recipient records without using them would cause inconsistent data
2020-09-21 17:02:59 +01:00
Fabien 'egg' O'Carroll
39dd492927
Updated magic link expiry information to 4 hours (#12224)
no-issue

Since TryGhost/Ghost#12218 magic links now have
an expiry of 4 hours.
2020-09-21 16:08:21 +01:00
Kristian Freeman
aca94e65c3
Added support for Stripe promo codes in config (#12149)
closes #12111 

* Added default value in defaults.json
* Passed config to members-api module

refs: https://github.com/TryGhost/Members/pull/194
2020-09-21 13:15:41 +01:00
Fabien 'egg' O'Carroll
7c5a3bb537
Updated magic links to use shorter, single us, longer lived tokens (#12218)
no-issue

* Added SingleUseTokenProvider to members service

This implements the TokenProvider interface required by members-api to
generate magic links. It handles checking if the token is expired and
pulls out any associated data.

Future improvments may include the email in the error for expired
tokens, which would make resending a token simpler.

* Passed SingleUseTokenProvider to members-api

This sets up the members-api module to use the new single use tokens

* Installed @tryghost/members-api@0.30.0

This includes the change to allow us to pass a token provider to the members-api
2020-09-18 17:32:18 +01:00
Fabien 'egg' O'Carroll
812e4b682f
Added SingleUseToken model (#12215)
no-issue

This is a model for the tokens table, which handles the single use
aspect by customising the `findOne` method to automatically destroy the
model after reading from it
2020-09-18 15:05:56 +01:00
Fabien O'Carroll
fe1e480e2e Updated getMagicLink call to handle Promise return
no-issue

refs: https://github.com/TryGhost/Members/commit/63942f03

The above commit updated all magic-link methods to be async, this change
ensures that we will handle a Promise return when it's updated in Ghost.
`await` has no effect on non Promise return values so it's safe to add
this now.
2020-09-18 10:49:18 +01:00
Fabien O'Carroll
a862a58b5f Updated the tokens table to have 32 char tokens
no-issue

After discussion with Matt, we decided that 192 bits for the token is a
good number, as it has no padding when base64 encoded and is more secure
than 128 bits, whilst still a managable size.
2020-09-17 17:33:43 +01:00
Fabien O'Carroll
01e62e3d79 Added tokens table to the database
no-issue

This is a table to store single use tokens for use in magic links, the
columns are as simple as possible at the moment and are designed as:
id - standard ObjectID like all of our tables
token - 128bit base64 encoded string
data - arbitrary data to store against the token
created_at - timestamp to allow for expiry to be implemented for tokens
2020-09-17 16:08:35 +01:00
Fabien O'Carroll
ed7adf16a2 Added migration util for adding new tables
no-issue

This pulls out some commonly used logic into a util for future use
2020-09-17 16:08:35 +01:00
Rish
ac146f1e3b Updated ownership verification email flow to use different From address
no issue

- In a recent change to ownership verification email flow, we changed the FROM address of ownership verification mails to use the same email as the one we are verifying, aka TO address.
- Email clients like Gmail flags off such emails as possible spam
- Fix updates the `FROM` address to `noreply@domain.com` where domain.com is domain for TO address
-  In case the TO is already noreply@domain.com, we use no-reply@domain.com to bypass the same address restriction.
2020-09-17 19:52:51 +05:30
Rish
7a3839fb14 Added new portal config flag
no issue

- The new Portal config flag allows switching on Portal conditionally with config
- The dev experiment flag still works for enabling Portal
- The flag currently defaults to `false` as Portal is still a beta feature and switched off by default
- We expose it on the admin api config endpoint so that the Ghost-Admin client can use it to conditionally render Portal settings
2020-09-17 19:40:34 +05:30
Rish
49abeaf398 Added blog domain for default support address
no issue

- Members site data was not appending blog domain for default support address which is `noreply`
- The change allows Portal to use default support address correctly
2020-09-17 12:33:16 +05:30
Nazar Gargol
46932c6d75 Removed unused messages from i18n file
no issue

- These messages are never used throughout the system. Removed to keep things tidy.
2020-09-16 15:46:22 +12:00
Kevin Ansfield
80af56b530
Store email batch and recipient records when sending newsletters (#12195)
requires https://github.com/TryGhost/Ghost/pull/12192

- added initial `EmailBatch` and `EmailRecipient` model definitions with defaults and relationships
- added missing `post` relationship function to email model
- fetch member list without bookshelf
    - bookshelf can add around 3x overhead when fetching the members list for an email
    - we don't need full members at this point, only having the data is fine
    - if we need full models later on we can push the model hydration into background jobs where recipient batches are fetched ready for an email to be sent
    - bookshelf model instantiation of many models blocks the event loop, using knex directly keeps concurrent requests fast
- store recipient list before sending email
    - chunk already-fetched members list into batches and insert records into the `email_recipients` table via knex
    - chunked into batches of 1000 to match the number of emails that Mailgun accepts in a single API request but this may not be the absolute fastest batch size for recipient insertion:
        | Batch size | Batch time | Total time |
        | ---------- | ---------- | ---------- |
        |        500 |       20ms |     4142ms |
        |       1000 |       50ms |     4651ms |
        |       5000 |      170ms |     3540ms |
        |      10000 |      370ms |     3684ms |
    - create an email_batch record before inserting recipient rows so we can effeciently fetch recipients by batch and store the overall batch status
2020-09-14 15:40:00 +01:00
Kevin Ansfield
76c1b60a4d
Added schema+migration for email_{batches,recipients} tables (#12192)
no issue

We want to store a list of recipients for each bulk email so that we have a consistent set of data that background processing/sending jobs can work from without worrying about moving large data sets around or member data changing mid-send.

- `email_batches` table acts as a join table with status for email<->email_recipient
  - stores a provider-specific ID that we get back when submitting a batch for sending to the bulk email provider
  - `status` allows for batch-specific status updates and picking up where we left off when submitting batches if needed
  - explicitly tying a list of email recipients to a batch allows for partial retries
- `email_recipients` table acts as a join table for email<->member
  - `member_id` does not have a foreign key constraint because members can be deleted but does have an index so that we can efficiently query which emails a member has received
  - stores static copies of the member info present at the time of sending an email for consistency in background jobs and auditing/historical data
2020-09-14 12:21:58 +01:00
naz
cbdc91ce48
Added Location header to API's POST request responses (#12186)
refs #2635

- Adds 'Location' header to endpoints which create new resources and have corresponding `GET` endpoint as speced in JSON API - https://jsonapi.org/format/#crud-creating-responses-201. Specifically:
    /posts/
    /pages/
    /integrations/
    /tags/
    /members/
    /labels/
    /notifications/
    /invites/

- Adding the header should allow for better resource discoverability and improved logging readability
- Added `url` property to the frame constructor. Data in `url` should give enough information  to later build up the `Location` header URL for created resource.
- Added Location header to headers handler. The Location value is built up from a combination of request URL and the id that is present in the response for the resource. The header is automatically added to requests coming to `add` controller methods which return `id` property in the frame result
- Excluded Webhooks API  as there is no "GET" endpoint available to fetch the resource
2020-09-14 22:33:37 +12:00
Talha
afa976066a
🐛 Fixed table constraint error when updating member's email with an already existing email (#12178)
closes #12045

- When member's email is updated to an already existing email of different member it caused table's unique constraint error, which was not handled properly. 
- Added handling for this error similar to one in members `add` method.
2020-09-10 16:03:57 +12:00
Nazar Gargol
5582d030e3 Added routes.yaml content checksum storage to the db
closes #11999

- When the routes.yaml file changes (manually or through API) we need
to store a checksum to be able to optimize routes reloads in the future
- Added mechanism to detect differences between stored and current routes.yaml hash value
- Added routes.yaml sync on server boot
- Added routes.yaml handling in controllers
- Added routes hash synchronization method in core settings. It lives in core settings
as it needs access to model layer. To avoid coupling with the frontend settings it accepts
a function which has to resolve to a routes hash
- Added note about settings validation side-effect. It mutates input!
- Added async check for currently loaded routes hash
- Extended frontend settings loader with async loader. The default behavior of the loader is
to load settings syncronously for reasons spelled in 0ac19dcf84
To avoid blocking the eventloop added async loading method
- Refactored frontend setting loader for reusability of  settings file path
- Added integrity check test for routes.yaml file
2020-09-10 10:54:57 +12:00
Talha
c8a6939b2e
🐛 Fixed error caused by accepting invitation with existing email (#12172)
closes #12060

- A 500 error what happening when invited user provided an email that is associated with an existing user
- Additional validation for existing email address was added to prevent invalid data hitting db constraint error
2020-09-09 11:58:53 +12:00
Rish
3c7a95aefa Added members support address to site data
refs https://github.com/TryGhost/members.js/issues/90

- Includes new members_support_address in member site data endpoint for Portal
2020-09-08 20:48:57 +05:30
naz
fcc6401445
Added routes_hash setting (#12171)
refs #11999

- The `routes_hash` setting will be used during the boot process to update the hash
of currently loaded routes.yaml file in case it's different from last restart
2020-09-07 21:54:55 +12:00
Nazar Gargol
4606c93e4f Refactored headers function to use async/await
no issue

- The async/await syntax makes it easier to reason about the code. Because adding 'Location' header is in the works it's a prep-work in a sense
2020-09-07 15:36:06 +12:00
Rish
2d2fa1a0ba Fixed missing subscription data in member update endpoint
closes https://github.com/TryGhost/members.js/issues/94

- The member-api package was recently updated to work directly with models and needs explicit `withRelated` options to attack relations
- Without options, the endpoint was returning the default member data without subscriptions attached, which in Portal showed paid member as free
- Fix updates the middleware for updating member data to correctly pass the relations needed to populate the member
2020-09-04 17:11:04 +05:30
Rish
dd6ac57aca Fixed missing domain for default support address
no issue

- By default for new sites, support address is set same as from address to `noreply` , with full email address using the domain for `@`
- For newsletter emails, the support address was missing the default site domain to be added to address if its `noreply`
- Fix updates the support address to use the same format as from address and add relevant domain for default case
2020-09-03 16:34:47 +05:30
Kevin Ansfield
936d7a315a 🐛 Fixed incorrect status used for trial subscription query
refs 96c18edc82

- replaced `trialed` with `trialing` to correctly match the status used in Stripe
2020-09-02 13:07:48 +01:00
Rish
696e60dd51 🐛 Fixed missing member email on unsubscribe page
no refs

- The `update` method in members-api package was edited to return Model object instead of JSON directly - TryGhost/Members@a28bcc5
- This unsubscribe handler was returning the raw member object returned from `update` method, which is now a model object and not able to access `member.email`
- Fix updates the unsubscribe request handler to return the member JSON again
2020-09-02 12:03:10 +05:30
Kevin Ansfield
96c18edc82 🐛 Fixed members with Stripe trials not receiving paid-members newsletters
no issue

- members who have trial subscriptions added directly via Stripe will have a status of `"trialed"` in their Ghost subscription
- the `paid: true` filter was not taking that into account meaning trial users were not receiving newsletters sent to paid members even though they have a "paid" subscription
2020-09-02 00:34:26 +01:00
Rish
806d0f8e21 Updated sender address for ownership verification emails to new address
no issue

- We used existing "from" address as sender for mails sent to new email address for verification, but that breaks the flow to update if the current "from" address has DMARC policy set.
- This updates the flow to always send the ownership verification email TO the new address and FROM the new address which both verifies the email deliverability for new address and ownership
2020-08-31 18:09:38 +05:30
Rish
8d022ecfb5 Updated newsletter emails to include reply-to address
no issue

- The newsletter emails are sent out with `from` address as sender
- The new `members_reply_address` setting is now used to set reply-to address for emails, which can be either newsletter or support address
2020-08-31 18:09:38 +05:30
Rish
ca8b77182c Updated sender address for member auth emails to support address
no issue

- Member auth emails were previously using the `from` address as sender
- New `members_support_address` was introduced with default as original "from" address
- Auth emails use the new support address as sender
2020-08-31 18:09:38 +05:30
Rish
cbb3664bdd Added handling for new members support/reply email addresses
no issue

- Updated magic link generation and validation methods for email update API to handle new support address
- Updated importer to ignore the new support address as it can only be updated via verification
- Updated members service to listen on settings edit for new support/reply address fields as well
- Updated tests to include the new settings
2020-08-31 18:09:38 +05:30
Rishabh Garg
bc2f6eb8e9 Added migrations for support and reply email address setting (#12163)
no issue

- Added default settings for the two new setting fields - `members_support_address` and `members_reply_address`
- Added migrations for setting group for new email settings
- Migration sets current from address as new support address default
- Added migration to set new support address same as from address
- Updated tests for new settings
- `members_support_address` - How members can reach for help with their account, public setting
- `members_reply_address` - Where you receive responses to newsletters
2020-08-31 17:16:53 +05:30
Rish
8cf9dc05e3 Revert "Added migrations for support and reply email address setting (#12163)"
This reverts commit 2216019a86.
2020-08-31 14:25:29 +05:30
Nazar Gargol
eb8c0fcff9 🐛 Fixed Tags API v2 to return posts count
closes #12167

- Tags API v2 was ignoring `count.posts` include parameter.
- Regression was introduced with a3f693b472
- Introduced regression tests across all Content API versions to avoid similar bug in the future
2020-08-31 18:46:35 +12:00
Rishabh Garg
2216019a86
Added migrations for support and reply email address setting (#12163)
no issue

- Added default settings for the two new setting fields - `members_support_address` and `members_reply_address`
- Added migrations for setting group for new email settings
- Migration sets current from address as new support address default
- Added migration to set new support address same as from address
- Updated tests for new settings
- `members_support_address` - How members can reach for help with their account
- `members_reply_address` - Where you receive responses to newsletters
2020-08-27 20:27:50 +05:30
Nazar Gargol
e8248cdc9e Refactored members imporeter in preparation for jobs
no issue

- This refactor extracts labels related code into a separate module for easier reuse by the "job-aware" batched importer
2020-08-27 21:47:00 +12:00
Nazar Gargol
262d33b73c Improved import_label creation logic
no issue

- When an import was done and there were no "global labels" present Ghost created generic `import-[data]` label which later helped to find a specific batch of imported data
- It did not make sense to create such generic label when user provided their own unique label
- The rules that work now are:
1. When there is no global provided Ghost generates on and removes it in case there are no imported records
2. When there is a unique new global label provided no new label is generated, but the label stays even if there are no imported records
2020-08-27 19:07:27 +12:00
Kevin Ansfield
f30d3cd2c2 Switched to bookshelf count in getTotalMembers()
no issue

- tested performance between knex raw, knex `count()` and bookshelf `count()` and found no difference over 1000 iterations of each (each ~19,500ms +- 500ms for 104k members locally)
- switched to using bookshelf as the code is the simplest
2020-08-27 01:52:36 +01:00
Kevin Ansfield
a995e9cc89 Allowed custom and search queries to be used with .findAll()
no issue

- extract filtering of an collection into a separate function
- use extracted function in `findAll()` so that it's query behaviour matches `findPage()`
2020-08-27 01:09:07 +01:00
Kevin Ansfield
5e64f113d5 Skipped separate count query in .findPage() for limit="all" requests
no issue

- for large result sets or complex queries the count query itself can be quite time consuming
- when `limit: 'all'` is passed as an option there's no need to perform a separate count query because we can determine the pagination data from the final result set
- skipped count query when `limit: 'all'` option is present
- re-ordered comments to be closer to the code they reference (ie, why we have our own count query instead of Bookshelf's `.count()`
2020-08-27 01:09:07 +01:00
Nazar Gargol
9fe5007929 Removed date handling validation in members importer logic
no issue

- This is handled on input sanitization layer with date
format check in JSON schema validation, so there's no need to do this
check again in the importer.
2020-08-26 17:25:24 +12:00
Nazar Gargol
544382b651 Updated boolean values validations for members uploads
no issue

- When the value is part of `enum` the `null` value is no longer
recognized by `null` type in JSON schema validator.
2020-08-26 17:23:36 +12:00
Nazar Gargol
95044e3ba0 Added error handling for failed member imports
no issue

- When bulk insert fails there is no transactional logic to revert
related records form being inserted. Also, previously there were no
attempts to "retry" the insert.
- To avoid complex retry logic, an iterative one-by-one insert retry
approach was taken. If this becomes a bottleneck in the future, the
retry algorithm could be improved.
- To avoid a lot of code duplication refactored model's `bulkAdd` & `bulkDestroy`
methods to use 'bulk-operations' module.
- Updated error handling and logging for bulk delete operations. It's very
unlikely for error to happen here,  but still need to make sure there is
a proper logging in place to trace back the failure.
- Added debug logs. This should improve debugging experience and
performance measurements.
- Added handling for unrecognized errors. Handling inspired by current unrecognized
error handling by ghost importer -10e5d5f3d4/core/server/data/importer/importers/data/base.js (L148-L154)
2020-08-26 17:11:35 +12:00
Nazar Gargol
45bca2b104 Fixed linting for await statements
refs 9d65de4b10
2020-08-25 19:30:02 +12:00
Nazar Gargol
9d65de4b10 Removed created import labels when no records were imported
no issue

- When no members are succesfully imported through CSV import process the import label should not be created. Otherwise after multiple failed attempts to import there are orphaned labels in the system
2020-08-25 19:23:05 +12:00
Kevin Ansfield
e07e0a17c6 🐛 Fixed bookmark card display in member emails when using Gmail+Chrome
no issue

- updates `@tryghost/kg-default-cards` which contains two fixes
  - removes email-specific output being added to post html (had no visual impact due to use of conditional comments but keeps rendered html smaller+cleaner)
  - adds a background-url style to the thumbnail container to give two options for styling
- updates member email template styling to hide the `<img>` element in bookmark cards and use a background image instead to get consistent rendering across email clients
2020-08-24 18:21:09 +01:00
Peter Zimon
75f5ab637d Added minor member newsletter customisations via config
no issue
- adds a `members:emailTemplate` config object
  - `showSiteHeader` - defaults to `true`, shows the site title and icon in member emails
  - `showPoweredBy` - defaults to `false`, adds a "Publish with Ghost" button to member email footer
- updates member newsletter email template with hideable site header and "powered by" badge
2020-08-24 10:44:22 +01:00
Rish
297b63edce 🐛 Fixed response for members API update endpoint
closes https://github.com/TryGhost/members.js/issues/87

- The `update` method in members-api package was edited to return Model object instead of JSON directly [here](a28bcc5b2a)
- This caused the update member API on member endpoint to return partial response only as most properties couldn't be fetched
- Fix updates the middleware to correctly call `toJSON` before formatting response
2020-08-24 13:38:21 +05:30
Nazar Gargol
91d1417df2 Fixed email newsletter error when post has no content
closes #12158
2020-08-24 19:46:46 +12:00
Daniel Lockyer
0ab190e83c Merged 3.30.2 into master
* 3.30.2:
  Bumped @tryghost/members-api to 0.28.1 in lockfile
  Bumbed @tryghost/members-api to 0.28.1
  🐛 Fixed unable to delete member when stripe is connected
2020-08-21 12:06:01 +01:00
Rish
87e8298ca7 🐛 Fixed unable to delete member when stripe is connected
closes https://github.com/TryGhost/Ghost/issues/12150

- The `destroy` method on members-api method was called incorrectly with not enough params
- It expects both `data` and `options` passed down [here](https://github.com/TryGhost/Members/blob/master/packages/members-api/lib/users.js#L14)
- Missing `options` in method call throws error as we read `cancelStripeSubscriptions` property on options if stripe is connected
- Fix passes both data and options to the destroy method
2020-08-21 11:13:07 +01:00
Rishabh Garg
c7f3837c54
Added admin endpoint for editing member subscription (#12145)
refs #12127

- Adds new `editSubscription` endpoint for members admin API which allows updating individual subscription for a member - `PUT /members/:id/subscriptions/:subscription_id/`
- `editSubscription` has same permissions as member's `edit` endpoint
- Currently allows toggling of cancellation at period end for an active subscription
2020-08-20 17:28:11 +05:30
Nazar Gargol
5670d99f46 Improved error handling for batch deleted records
no issue

- Similar handling to one introduced in 8418c829de
- Having granular tracking for failed to remove id's would make it possible to return more specific errors to the client
2020-08-20 19:11:41 +12:00
Nazar Gargol
8418c829de Improved error handling for batch inserted records
no issue

- Similar to 3a594ce22e this adds error handling to batch operations done outside models
2020-08-20 19:01:17 +12:00
Nazar Gargol
f8adb4a672 Refactored bulk insert/delete operations into separate module
no issue

- Moved bulk db operations outside of importer module to create clearer separation of responsibilities
2020-08-20 18:42:38 +12:00
naz
3a594ce22e
Improved error handling for batch inserted member records (#12146)
no issue

- When batch insert fails handling should be more granular and aim to retry and insert as many records from the batch as possible.  
- Added retry logic for failed member's batch inserts. It's a sequential insert for each record in the batch. This implementation was chosen to keep it as simple as possible
- Added filtering of "toCreate" records when member fails to insert. We should not try inserting related members_labels/members_stripe_customers/members_stripe_customer_subscriptions records because they would definitely fail insertion without associated member record
2020-08-20 17:41:47 +12:00
Nazar Gargol
2e769e3122 Fixed handling for Stripe connected members import
no issue

- When stripe is disconnected and there are Stripe-connected records present in imported set they should not be processed and proper error should be thrown
2020-08-20 17:08:19 +12:00
Nazar Gargol
4498b4624a Fixed label serialization in members bulk importer
no issue

- After a refactor logic was missing trimming logic and handling for empty labels
2020-08-20 16:43:42 +12:00
Nazar Gargol
32fd913ac3 Fixed default subscribed value for member model in the importer
no issue

- The default value should not be a string but rather a boolean
2020-08-19 23:42:48 +12:00
Kevin Ansfield
d8e319af88 🐛 Fixed card spacing and caption styling for member emails in Outlook
closes https://github.com/TryGhost/Ghost/issues/12139

- once the email content has been rendered in the post serializer, perform some whole-content transformation of `figure` and `figcaption` to `div` using cheerio
- juiced will have already inlined the elements styles so there's no need to adjust the template's stylesheet
2020-08-19 12:34:14 +01:00
Nazar Gargol
616767d0ec Fixed members bulk import's createdBy field assignment
no issue

- Method signature has been changed with cada88975a, the call needed an update as well
2020-08-19 17:14:45 +12:00
Jeremy Davidson
681c96b5e9
🐛 Fixed bookmark card author/publisher spacing in member emails (#12134)
closes #12078

- Root cause was that pseudo class .kg-bookmark-author:after was not getting inlined to email newsletter or its preview.
  - That is where the margin and bullet-point content are added between author and publisher.
- Dependency juice supports an option inlinePseudoElements which is false by default.
- Fix was to set inlinePseudoElements to true when serializing post email.

Co-authored-by: Jeremy Davidson <jeremy@crossingcontour.com>
2020-08-18 16:18:44 +01:00
Hannah Wolfe
9f291b2d10 Improved testmode logging of shutdown status
- testmode allows us to test Ghost's behaviour with long running requests and jobs
- moved all the testmode logging to only happen if Ghost starts successfully to make it clearer what is happening
- added a (currently very dirty) bit of code to fetch the jobService and output the queue status giving us a similar
     output for both open connections and jobs
- all of this allows for debugging exactly what Ghost is doing whilst it is processing a shutdown request
2020-08-18 15:48:42 +01:00
Kevin Ansfield
cefcdad090 🐛 Fixed emails appearing very wide in Outlook and improved email image sizes
closes https://github.com/TryGhost/Ghost/issues/11536

- bumps `@tryghost/kg-default-cards`
  - image and gallery cards now output `width/height` attributes on `img` elements with a max width of 600px
  - uses resized images where possible to keep email weight down
- adds `height: auto` style to image card images so that the `height` attribute does not cause distortion at smaller screen widths
2020-08-18 15:33:54 +01:00
Fabien 'egg' O'Carroll
cada88975a
Updated bulk importer to improve performance (#12128)
no-issue

* Added bulkAdd method to Member,Customer&Subscription model
  This allows us to keep the db access in the model layer
* Updated @tryghost/members-api to 0.27.2
  This includes fixes for rate-limiting of requests, and exposes necessary
  Stripe methods for creating customers and complimentary subscriptions,
  without affecting the database.
* Refactored importer to parallelise tasks where possible
  By parallelising our tasks we are able to improve the speed at which the
  entire import completes.
2020-08-18 13:39:45 +01:00
Rishabh Garg
2bac2c67cc
Granted Admin users ability to impersonate member (#12132)
refs #12126

- Adds migration to add impersonation permission to administrators
- Adds default permission fixture to allow administrators to read member impersonation urls
- Allows administrators to create member impersonation magic links
2020-08-18 14:17:56 +05:30
Nazar Gargol
6ca51eae29 Fixed member import csv validator for labels
no issue

- Labels can have a null value when there is none defined in the imported file. Such records fails for "string" type in JSON schema
2020-08-18 15:25:04 +12:00
Kevin Ansfield
752c1022f9 🐛 Fixed failing migration from <2.34 to 3.29
closes https://github.com/TryGhost/Ghost/issues/12129

Migrations were failing when upgrading from a version that didn't include stripe member subscriptions table. The `up` migration was checking for the existence of a unique index that was added to the schema in 3.29 but it was using the wrong variable name which meant that it would never return true for an existing index. For most migrations this worked because the index existed but when through 2.34 the table was created from scratch and did included the index at that point.

- fixed variable name and re-ordered variable assignment for better code locality that would have made the typo more visible
2020-08-17 18:31:00 +01:00
naz
51c9a50c4f
Added validation layer to members import endpoint (#12131)
no issue

- Additional validation is needed for imported data because in case of bulk insertions (through knex) we bypass model layer validation - this could lead to invalid data in the database, which would be hard to fix.
- Chose validation method we use for other endpoints - through JSON Schema. It proved to be very performant (200ms overhead for 50k records). When comparing it with iterative method (validating each record separately) this was adding about 17s of overhead.
- Refactored returned values from "sanitizeInput" method to encapsulate more logic so that the caller doesn't have to calculate amount of invalid records and deal with error types
- Whole sanitizeInput method could now be easily extracted into separate module (somewhere close to members importer)
- Bumped members-csv package. It is meant to handle empty string values - '' and null, which should allow validating member records more consistently!
2020-08-17 23:28:57 +12:00
Daniel Lockyer
046b5540fe Removed workaround for intermittent ghost-server logging
- upon further testing, this started to not work again so I'm removing
  this horrible hack
- it could be Bunyan playing around with us, and the server is actually
  shutting down
2020-08-13 18:45:51 +01:00
Daniel Lockyer
d4f1274ba5 Returned empty promises in ghost-server
- I have no idea why this is required and I really don't like it
- this change looks superfluous but I had problems with Ghost not
  displaying shutting down messages properly and this is the only
  thing that got it working
- the async-awaits were getting stuck in `await this._cleanup()`,
  and then wouldn't enter into the `finally` block
- oddly, this was only reproducible when a job was running, it would
  shut down normally otherwise
- if I find the solution in the mean time, I'll happily get rid of this
2020-08-13 17:26:33 +01:00
Nazar Gargol
ef41c57974 Added test coverage for invalid member import emails
no issue

- This test should serve as a control for new batched importer
2020-08-13 22:14:52 +12:00
Nazar Gargol
a1e6b8f898 Updated members import sanitization
no issue

- Updated sanitization logic to be self contained and return sanitieze input along with error stats
- This should give a nice place for validations to fit in
2020-08-13 22:12:20 +12:00
Nazar Gargol
4126187df3 Fixed parameter naming for members importer 2020-08-13 21:08:34 +12:00
Nazar Gargol
d2fbe327e4 Update Stripe concurrency in members importer
no issue

- There were many failed import records due to rate-limit errors. With concurrency of 9 imports go through with 100% success
- Would need to verify these limits with live API to make the most of it
2020-08-13 21:04:57 +12:00
Nazar Gargol
78b4dff656 Moved batching logic inside the members importer module
no issue

- This way importer is more self contained and controller logic doesn't have to know about batch sizes and other unecessary variables
2020-08-13 20:31:11 +12:00
Kevin Ansfield
c434666ba2 🐛 Removed [http://url/] output in member email preview text
no issue

- we output the post excerpt in a hidden div in the email template so that email clients pick it up as the "preview" text when listing emails
- when no custom excerpt is provided the preview text is grabbed from post.excerpt which is the first 500 chars of the post.plaintext value
- post.plaintext formats links as "Link [http://url/]" which is unwanted in html email previews

- add a basic replacement to the post email serializer to remove any `[http://url/]` occurrences from the post excerpt before rendering the email content
2020-08-12 20:14:06 +01:00
Kevin Ansfield
163092f377 🐛 Fixed apostrophes not displaying correctly in Outlook for member emails
refs https://github.com/TryGhost/Ghost/issues/11536

- Outlook supports `&#39;` as a special char for apostrophes but not `&#apos;` which is what cheerio/juiced render
- adds a basic string placement to the email serializer to switch to the older style of special char
2020-08-12 20:01:42 +01:00
Kevin Ansfield
c7ff4c9e93 Moved email sending to the background job queue
no issue

- moves the meat of `pendingEmailHandler()` code into a new function `sendEmailJob()` that is passed over to the new job service
- lets the server keep processing email generation and sending when it receives a shutdown request rather than halting processing mid-send and ending up in a partial state
2020-08-12 17:02:14 +01:00
Fabien 'egg' O'Carroll
1294e3f92c
Replaced all usage of member models with members-api (#12117)
no-issue

* Added stripeSubscriptions relation to member model

This allows us to fetch the subscriptions for a member via standard
model usage, e.g. `withRelated: ['stripeSubscriptions']` rather than
offloading to loops and `decorateWithSubscriptions` functions, this is
more performant and less non-standard than the existing method.

* Updated serialize methods to match existing format

The current usage of `decorateWithSubscriptions` and the usage of
members throughout the codebase has a subscriptions array on a stripe
object on the member, this ensures that when we serialize members to
JSON that we are using the same format.

There is definitely room to change this in future, but this is an
attempt to create as few breaking changes as possible.

* Installed @tryghost/members-api@0.26.0

This includes the required API changes so that everywhere can use
members-api directly rather than models and/or helper methods
2020-08-12 14:17:44 +01:00
Nazar Gargol
c696d715c1 Extracted batched member import into separate module
no issue

- The code in controller was becoming hard to reason about.
- Having a single module shows exactly how many dependencies are there to do an import for single batch.
- Having a separate module would make it easier to extract into it's own package in Members monorepo
2020-08-12 20:18:30 +12:00
Nazar Gargol
7d3f6e32ca Addes sort order support for imported members
no issue

- Member's labels have to have sort_order assigned when added/edited. This was lacking from batched importer.
- Implementation is based on logic used in model's base - e484709e73/core/server/models/base/index.js (L81-L86)
2020-08-12 16:34:15 +12:00
Hannah Wolfe
bbbd011074 Added job-manager & wired up shutdown and testmode
- Bottom line - we need to manage shutting down gracefully when doing long-running tasks
- To achieve that, we're going to use job queues

In this commit:
- added new @tryghost/job-manager dependency
- added a minimal job service, that handles in passing things like logging and (maybe later) config
- job service is wired up to server shutdown, so that the queue finishes before the server exits
- also added a new job endpoint to testmode so that it's easy to test job behaviour without needing to do real work
2020-08-11 21:31:34 +01:00
Hannah Wolfe
624206b6d7 Improved ghost-server with async/await
- Using consistent patterns for shutdown and stop
- Make it clear what each method does
- Use async/await to make the code more readable and simple
- This lays groundwork for having more cleanup tasks in stop than just server.stop()
2020-08-11 19:42:37 +01:00
Daniel Lockyer
5b471e1bbe Extracted promise libs and history into @tryghost/promise
- deleted files under `core/server/lib/promise` and related test files
- added `@tryghost/promise` as a dependency
- fixed all local requires to point to the new package
2020-08-11 18:44:21 +01:00
Daniel Lockyer
c9a5b28669 Extracted core/server/lib/security to @tryghost/security package
- code and tests were extracted out to this package
- deletes these files
- replaces all local requires, and adds it as a dependency
2020-08-11 14:06:50 +01:00
Daniel Lockyer
8799feb801 Replaced constants file with @tryghost/constants
- extracted constants file into a new package
- replaced all local requires of the file with new package
2020-08-11 12:51:16 +01:00
Hannah Wolfe
e84621d6ef 🐛 Readded missing server.start event
closes #12118

- server.start was mistakenly removed in 71f02d25e9
- it is used for loading themes (and other things) and is critical
- added tests to prevent this regressing again in future
2020-08-11 10:09:24 +01:00
Nazar Gargol
8a7e00c413 Enabled batched members import method through enableDeveloperExperiments flag
no issue

- Allows early testing of batched import method
2020-08-11 18:52:37 +12:00
Nazar Gargol
bbcc0f5178 Added batched members import API method
no issue

- New Member API batched import is meant to be a substitution to current import
with improved performance while keeping same behaviore. Current
import processes 1 record at a time using internal API calls and times
out consistently when large number of members has to be imported (~10k
records without Stripe).
- New import's aim is to improve performance and process >50K
records without timing out both with and without Stripe connected
members
- Batched import can be conceptually devided into 3 stages which have
their own ways to improve performance:
  1. labels - can be at current performance as number of
labels is usually small, but could also be improved through batching
  2. member records + member<->labels relations - these could
be performed as batched inserts into the database
  3. Stripe connections - most challanging bottleneck to solve because
API request are slow by it's nature and have to deal with rate limits of
Stripe's API itself
- It's a heavy WIP, with lots of known pitfalls which are marked with
TODOs. Will be solved iteratively through time untill the method can be
declared stable
- The new batched import method will be hidden behind 'enableDeveloperExperiments' flag to
allow early testing
2020-08-11 18:31:31 +12:00
Hannah Wolfe
b0512f2c25 Added a server testmode to help test behaviour
- A simple way to test behaviours without having to do complex interactions to e.g. generate errors or slow requests
- Makes it easier to test the new shutdown behaviour, among other things
2020-08-10 16:38:49 +01:00
Hannah Wolfe
a9c6e081cf Added an additional log to notify shutdown start
- there can now be quite a big delay between SIGINT/TERM being received and shutdown finishing
- add an extra log message to acknowledge the SIGINT/TERM to facilitate debugging and just be clear
2020-08-10 14:53:05 +01:00
Hannah Wolfe
19e3b70c7a Added stoppable for graceful shutdown of requests
- stopppable is a dependency that handles closing connections properly, which server.close does not
    - active connections are allowed to complete what they are doing
    - idle connections are closed
    - no new connections are allowed
- we call stoppable in stop() instead of server.close so that idle connections don't hold the server open
- calling await stop() from shutdown then ensures that we have a consistent experience of stop
- all together this allows ghost to shutdown gracefully when there are long-running requests
- @TODO: handle graceful shutdown of long-running processes
- @TODO: consider do we need to send 503s whilst the server is shutting down?
2020-08-10 11:46:36 +01:00
Hannah Wolfe
e72cc193c6 Refactored GhostServer to use a class
- use more up-to-date and less confusing syntax
- makes it easier to use async/await as well
2020-08-10 08:53:09 +01:00
Hannah Wolfe
2289b7c0f7 Moved sig event and stop message handling
- none of this should be inside the start message handling
- move to inside the start function, where it's clearer and makes more sense
2020-08-10 08:53:09 +01:00
Hannah Wolfe
31981d086b Updated stop messages to have a consistent interface
- changed method to logStopMessages, as we use start and stop, not start and shutdown
- changed logStopMesasges to output the "proper" messages and use this method consistently - the closing connections message isn't really useful
- changed uptime message to always be output cos I can't see a case where there isn't interesting/useful
2020-08-10 08:53:09 +01:00
Hannah Wolfe
f8cdaf0c24 Removed unused connection handling + restart method
- Connection handling is legacy code added in 1438278ce4
- Although we were tracking all connections in memory, we weren't actually closing any because stop isn't called
- This (and restart) were both added as part of the now long-deprecated system for using Ghost directly as an npm module
- If we want to close connections cleanly, we should use a tool to do this
2020-08-09 17:31:01 +01:00
Hannah Wolfe
71f02d25e9 Refactored server announce functions to be clearer
- the announce functions exist for the purpose of communicating with Ghost CLI
- the functions were called announceServerStart and announceServerStopped, which implies they tell Ghost CLI when the server starts and stops
- however, the true intention / purpose of these functions is to:
    - either tell Ghost CLI when Ghost has successfully booted (e.g. is ready to serve requests)
    - or tell Ghost CLI when the server failed to boot, and report the error so that Ghost CLI can communicate it to the user
- therefore, I've refactored the old functions into 1 function to make it clearer they do the same job, but with 2 different states
- also added some tests :D
2020-08-09 17:25:15 +01:00
Hannah Wolfe
c577007afe Update dependency @tryghost/bootstrap-socket to v0.2.0
- includes API changes
2020-08-09 17:22:27 +01:00
Hannah Wolfe
b765a30ee9 Moved bootstap-socket code to @tryghost/bootstrap-socket
- this code has zero deps and is much easier to reason about standalone
2020-08-07 18:43:46 +01:00
Hannah Wolfe
b09504e7cc Moved bootstrap socket logic out of server
- this logic is a dependency rather than part of the server code
- moved it out ready to be moved elsewhere, doesn't need to be here
2020-08-07 16:27:45 +01:00
Hannah Wolfe
028d3fc88b Added missing debug info for IPC on start
refs 022a433e56

- noticed I missed adding debug info in one place (not that it's used, just for consistency)
- also made the SIGINT/TERM code slightly more readable
2020-08-06 20:39:49 +01:00
Kevin Ansfield
2efcf94645
Improved performance of sending newsletter emails (#12091)
no-issue

- switch from `membersService.api.members.list` to using bookshelf `Member.findPage()` with the `{paid: true}` filter to avoid per-member queries (N+1) to decorate members with subscriptions and a heavy post-fetch filter via `contentGating`
- add concurrency to the Mailgun API requests in `bulk-email` service to reduce overall time submitting API requests
- add debug statements with timing output for easier measurements
2020-08-06 15:19:39 +02:00
Kevin Ansfield
490f9787fa
Added migration to create indexes and constraints for member tables (#12108)
no issue

For large numbers of members we're unable to perform queries for free/paid members in a reasonable time without indexes. Bulk delete is also very slow when looping through bookshelf models and having bookshelf manage deletion of child records, adding `ON DELETE CASCADE` to the foreign key indexes moves child deletion to the DB level and allows for performant bulk delete queries.

- migrations only run on mysql because sqlite does not support altering tables
- adds foreign key indexes and constraints with 'ON DELETE CASCADE' for members_labels, members_stripe_customers, and members_stripe_customers_subscriptions
2020-08-06 14:57:05 +02:00
Fabien 'egg' O'Carroll
d3384975da
Cleaned up members_stripe_* tables on MySQL (#12103)
refs #12100

For performance reasons we want to add foreign key and unique constraints
to the members_stripe_* tables so we can utilised cascading deletes and 
joins across the tables when querying.

In order to do this we must first ensure that:
- There are no duplicate entries in the `subscription_id` or `customer_id` columns
- There are no orphaned rows in the subscription or customers tables

If the first is not true, the unique constraint will fail, and if the second is not true,
the foreign key constraint will fail.

As we are only adding the indexes to existing MySQL databases at this point, the
cleanup migrations will also only be done for existing MySQL databases too.

The migrations for removing orphaned rows splits the deletion into a `SELECT`
followed by a `WHERE IN` to avoid the database "optimising" the query into a
`JOIN` which ends up taking much longer due to the lack of indexes.
2020-08-06 11:13:41 +02:00
Fabien 'egg' O'Carroll
d15446593a
Added support for ON DELETE CASCADE to the schema (#12105)
no-issue

We are in the process of creating migrations to add foreign key constraints
and cascading deletes to the members_stripe_* tables to make listing members
and deleting members faster. As well as the migrations we need to update the
database schema so that new installations have the correct indexes and constraints.

Co-authored-by: Kevin Ansfield <kevin@lookingsideways.co.uk>
2020-08-05 13:20:30 +02:00