Commit Graph

4329 Commits

Author SHA1 Message Date
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 `'` 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
Nazar Gargol
60ae9e82f9 Fixed integration_id assignment for webhook when creating through API key auth
refs 173e3292fa

- The bug was initially introduced in referenced commit. When request is done with `api_key` context, there should always be an `integration` object associated with it - 71c17539d8/core/server/services/permissions/parse-context.js (L36) . An `id` from `context.integration` not `context.api_key` has to be assigned to newly created webhook!
- The webhooks API is about to be declared stable in upcoming release, so no migration will be done
2020-08-04 16:43:24 +12:00
Gerardo Lopez Dueñas
275bf41ba3
Fixed bookmark card image size in fastmail (#11970)
closes #11907

The image in the bookmark card was being shown out of the bounds of
the card because of a general style `height: auto !important`.

I added a new `max-height` property to the image to avoid exceeding
parent height.
2020-08-03 18:38:45 +01:00
Nazar Gargol
2a4ad0e10f Updated stability index comments around Admin API v2 endpoints
refs 067d2eb614

- v2 API has been in maintenance mode since v3 has been released, updated comments accordingly
2020-08-03 23:46:23 +12:00
Nazar Gargol
067d2eb614 Moved canary/v3 webhooks API to "stable" group
no issue

- Webhooks API has been stabilized with latest changes and there are no breaking changes planned for v3. The change has strictly "informative" purpose
- Changed variable naming from "whitelisted" to "allowlisted" to follow updated naming convention (refs. https://mysqlhighavailability.com/mysql-terminology-updates/)
2020-08-03 23:40:15 +12:00
Nazar Gargol
b76a6a1eee Reduced error level to default for not found post error
refs #12064

- `critical` is meant to be something unpredictable like internal error, something worthy attention, as described in Ignition -3439456d94/README.md (list-of-errors)
- This error level was introduced with - this PR https://github.com/TryGhost/Ghost/pull/9426, but there is no context provided why this specific value was used. Assuming it's an outdated value as 'not found' is nowhere to be treated in any special way
2020-08-03 23:16:53 +12:00
Nazar Gargol
1b449f4f53 🐛 Fixed 500 error in webhooks API when modifying non-existing webhooks
closes #12064

- Handled permission check bug by returning 404, same way it is returned in other permissions related places when handling non-existing resource. Example - 60907a7ae4/core/server/models/relations/authors.js (L355-L358)
2020-08-03 23:08:47 +12:00
Kevin Ansfield
29d94e7814 Fixed mailgun config not allowing custom hosts with ports
no issue

- `mailgun()` expects the `host` option not to include a port but `url.host` will include the port, we instead want to use `url.hostname` which skips the port
2020-07-30 17:28:51 +01:00
Kevin Ansfield
577a934f53
Removed DISTINCT from member labels association query (#12088)
no issue

- bookshelf adds `DISTINCT` to any relation query that does not have an explicit `columns` statement
- when measuring the impact of `DISTINCT` on the eager-loading association query when listing members using `{withRelated: 'labels'}`, it can be 2x slower with no index on the sort_order column or 4x slower with an index on sort_order
2020-07-29 12:50:22 +01:00
Nazar Gargol
ef6586bfdc Fixed failing members import with label associations
no issue

- When processing entries with new labels in parallel Bookshelf relations is trying to create them which caused unique key constraints to fail. To avoid the failure, all labels should be pre-created before proceeding with creating members
2020-07-28 22:37:48 +12:00
Hannah Wolfe
436db4ec3b Updated host limit message to be clearer
- limits are based on total members not number of members that will be emailed
2020-07-26 20:49:30 +01:00
Hannah Wolfe
92446d85ea Changed member limit to be DRY & use raw query
- Member limit code was duplicated in 2 places unnecessarily
- Also used member api code that fetched members and subscriptions fully hyrated when we only need a count
- Using a raw query significantly improves performance here
2020-07-26 20:49:30 +01:00
Kevin Ansfield
14c6968388 Fixed auto-generated label not being attached to members during import
no issue

- label was being created and returned in the response OK but it was missing from the set of labels that get added to members during import
2020-07-26 18:30:58 +01:00
Fabien O'Carroll
c46475f5be Supported optionally cancelling subscriptions on delete
no-issue

This updates the Admin API Member resource to *not* cancel subscriptions
by default, and adds a `cancel` option. This can be used over HTTP by
including a `cancel=true` query parameter.
2020-07-24 16:01:06 +02:00
Fabien O'Carroll
92151a7b5d Added default options to transactional model methods
no-issue

This protects against calling model methods without passing an options
object
2020-07-24 15:31:48 +02:00
Hannah Wolfe
3491e60c9d Added config to send bulk email in testmode
- mailgun has a testmode flag we can use to get email to be accepted but not delivered
- this is useful for developers testing general bulk email code - not for users - so it is only available via config
2020-07-24 11:55:34 +01:00
Fabien 'egg' O'Carroll
5144a0e09c
Updated Member model to cascade on destroy (#12077)
no-issue

Up until now we have left orphaned rows in members_stripe_* tables when
a member is deleted, this updates the destroy method so that we cascade
and remove any MemberStripeCustomer and StripeCustomerSubscription
models related to the Member.

This also adds regression tests for the new functionality as well as to
confirm the existing functionality of cascading to the members_labels
join table

This adds the relations of Subscription->Customer & Customer->Member
2020-07-23 18:21:10 +02:00
Rish
2ac69e637e Added publication icon to members site data
refs https://github.com/TryGhost/members.js/issues/72

- Portal is using using publication logo from settings for signup/signin pages
- Instead, we are switching to using publication icon from settings, which also needs to be passed in site data API
2020-07-23 17:04:44 +05:30