no issues
- with gscan v4.46.0, we introduced a new rule for the custom fonts
feature
- this updates the valid theme zip file to make the theme tests work
with the new rules
no issue
- Currently the prometheus client is only initialized on boot if enabled
via config, but if it's required in other files (i.e. to create a custom
metric) it will be initialized then
- This commit explicitly checks if the prometheus client is enabled via
config before initializing it, thus preventing it from being initialized
when disabled
no ref
Stripe made changes (again) that causes our donation tests to fail. This
round we use an if statement to try to make it more inclusive of cases,
as I've seen them use an accordion button, card button, and no button
all in the past 12h.
no issue
Stripe recently updated their checkout page to use React with Framer
Motion for animations, causing our Playwright tests to intermittently
fail when attempting to click the “Card” payment button. The standard
Playwright `.click()` method was unable to interact with the button
reliably due to animation-related delays, where the button was present
in the DOM but not fully interactable according to Playwright’s strict
visibility checks.
Switching to `dispatchEvent('click')` directly fires the click event on
the button, bypassing Playwright’s visibility and interactability
checks. This ensures the test can proceed without waiting for animations
to fully complete, resolving the issue with the Stripe checkout flow.
ref https://linear.app/ghost/issue/ENG-1749
Batch sending tests were failing with MySQL fairly regularly. It appears
to be a race condition where the listener for the batch sending job
having completed is returning too early, causing the subsequent
Bookshelf data model refresh to happen too soon.
This is a fundamental flaw in the JobManager awaitCompletion handler
(and how the batch sending system interacts with it) as there's no way
to identify one batch from another - they all use the same name, and we
don't pass along any metadata.
ref DES-949
This adds custom fonts feature allowing users to select heading and body fonts for their themes from a curated list. This allows publishers to have more control over their brand, and allows themes to have a wider range of styles to appeal to different audiences.
Without custom fonts support, themes will continue to work as normal, but users won't be able to customize their typography. As for the official themes, all of them will support custom fonts.
---------
Co-authored-by: Aileen Booker <AileenCGN@gmail.com>
ref PLG-227
- Behind flags
- Changed Comments API for members and guests to not return hidden or
removed comments - with the only exception being if a hidden or removed
comment have published replies, in which case it will be greyed out as
per the previous version on the UI.
- Wired up a new admin API endpoint for comment to receive all comments.
It's on par with the members / guests endpoint, with the difference
being that it it shows hidden comment's content, where previously the
html property was nullified.
ref
https://linear.app/ghost/issue/ENG-1592/start-monitoring-connection-pool-utilization-in-ghost
- This commit adds prometheus metrics to the connection pool so we can
start to track connection pool utilization, number of pending acquires,
and also adds some basic SQL query summary metrics like queries per
minute and query duration percentiles.
- The connection pool has now been theorized to be a main constraint of
Ghost for some time, but it's been challenging to get actual visibility
into the state of the connection pool. With this change, we should be
able to directly observe, monitor and alert on the connection pool.
- Updated grafana version to fix a bug in the query editor that was
fixed in 8.3, even though this is a couple versions ahead of production
no issue
- We had reintroduced nodemon in
af0f26c75f (diff-bf18f8caf848e17b35e266db04bcaeaad05a3e5d069846615d2b1260482396e1)
for the docker setup, but it has since caused some issues with the `yarn
dev` script.
- In particular, it was causing a restart while migrations were running
in development, which left a migration lock on and prevented Ghost from
starting.
- This commit removes nodemon and replaces it with node --watch, which
we had been using in the past without issues.
refs https://linear.app/ghost/issue/AP-500
We've got a new @tryghost/activitypub package, which is gonna handle all
of the wiring between Ghost and ActivityPub. Currently that is just the
configuration of webhooks for the internal ActivityPub integration.
All this logic is run on the boot of Ghost, though notably in a
non-blocking manner, it's initialised as part of the background services
so it should not have an effect on the time to serving requests. having
said that - it needs to be defensive against errors, which is why the
entire network request is in a try/catch, as well as lookups for the
integration able to handle missing data.
Unit tests use an in-memory sqlite instance, which means we're testing a
full flow, ideally there would be a way to get the schema from Ghost for
this, but for now this is acceptable IMO.
refs https://linear.app/ghost/issue/AP-500
Rather than having to manually create an integration for communication
with the ActivityPub service, we are going to have an internal
integration which will then be used to handle webhooks between Ghost &
ActivityPub
The 'internal' type has been used to keep it out of the UI/API but
available for all Pro customers, which is necessary during the private
beta.
---------
Co-authored-by: Michael Barrett <mike@ghost.org>
ref https://ghost.org/changelog/internationalization/
- this fixes an error in which changing the language (or sliding the i18n
toggle) didn't correctly update email-service.
---------
Co-authored-by: Sag <guptazy@gmail.com>
ref https://linear.app/tryghost/issue/PLG-230
- adds `in_reply_to_id` to API output
- adds `in_reply_to_snippet` to API output
- dynamically generated from the HTML of the replied-to comment
- excluded if the replied-to comment has been deleted or hidden
- adds `commentSnippet` to `@tryghost/html-to-plaintext`
- skips anchor tag URLs as they won't be useful for snippet purposes
- skips blockquotes so the snippet is more likely to contain the unique content of the replied-to comment when it's quoting a previous comment
- returns a single line (no newline chars)
- allows setting `in_reply_to_id` when creating comments
- id must reference a reply with the same parent
- id must reference a published comment
- adds email notification for the original reply author when their comment is replied to
ref https://linear.app/tryghost/issue/PLG-230
- `comments.in_reply_to_id` will be used to keep a reference to the comment that the new comment was directed at
- used only for replies-to-replies, will be `null` for the top-level parent and `null` for any replies directly to that parent
- technically allows for infinite nesting within a parent comment thread but we won't be using that ability for now
- `comments.parent_id` will be kept as it provides a useful optimisation for loading the top-level comments list
- we're not using `comments.parent_id` for this to keep complexity down and avoid the need for recursive lookups
refs https://linear.app/ghost/issue/AP-500
The logic for generating identity tokens, whilst small, is something
that we don't want to duplicate - as it concerns security & access - so
can easily break interactions between different services. We're gonna
need to use identity tokens as part of the initialisation of the
activitypub service, so this is pulling it out preemptively for that use
case
We shouldn't have logic inside of the endpoint controllers anyway, so
this is kinda general cleanup.
ref
https://linear.app/ghost/issue/ENG-1746/enable-ghost-to-push-metrics-to-a-pushgateway
- We'd like to use prometheus to expose metrics from Ghost, but the
"standard" approach of having prometheus scrape the `/metrics` endpoint
adds some complexity and additional challenges on Pro.
- A suggested simpler alternative is to use a pushgateway, to have Ghost
_push_ metrics to prometheus, rather than have prometheus scrape the
running instances.
- This PR introduces this functionality behind a configuration.
- It also includes a refactor to the current metrics-server
implementation so all the related code for prometheus is colocated, and
the configuration is a bit more organized. `@tryghost/metrics-server`
has been renamed to `@tryghost/prometheus-metrics`, and it now includes
the metrics server and prometheus-client code itself (including the
pushgateway code)
- To enable the prometheus client alone, `prometheus:enabled` must be
true. This will _not_ enable the metrics server or the pushgateway — it
will essentially collect the metrics, but not do anything with them.
- To enable the metrics server, set `prometheus:metrics_server:enabled`
to true. You can also configure the host and port that the metrics
server should export the `/metrics` endpoint on in the
`prometheus:metrics_server` block.
- To enable the pushgateway, set `prometheus:pushgateway:enabled` to
true. You can also configure the pushgateway's `url`, the `interval` it
should push metrics in (in milliseconds) and the `jobName` in the
`prometheus:pushgateway` block.
- we can skip an extra `toJSON` call here by storing the value and
re-using it, which means we can cut down on the number of calls, which
can be particularly heavy when this codepath is hit hard
refs https://github.com/TryGhost/Ghost/issues/19839
refs https://www.sqlite.org/limits.html
SQLite has limit of 500 items in a compound select statement.
This limit could be hit when a complex select statement was being
generated as part of a batch insert statement.
Lowering the batch size will have minimal impact on migration
performance while improving SQLite compatibility.
One of these bulk inserts is confirmed to be affected through the linked
issue.
I didn't confirm if the other two cases would trigger it, but the change
won't hurt there either.
ref https://linear.app/tryghost/issue/ENG-1556/
- added background job queue behind config flags
- when enabled, is only used for the member email analytics updates in
order to speed up the parent job, and take load off of the main process
that is serving requests
The intent here is to decouple certain code paths from the main process where it is unnecessary, or worse, where it's part of the request. Primary use cases are email analytics (particularly the member stats [open rate]) which are not particularly helpful in the period immediately following an email send, while the click traffic and delivered/opened events are.
Related, the email link clicks themselves send off a cascade of events that are quite a burden on the main process currently and are somewhat tied to the request response when they needn't be. We'll be looking to tackle that after some initial testing with the email analytics job.
ref PLG-220
- Added an `orderAttributes` override method to be able to pass
`count__likes` to the `findPage` DB helper.
- Unknowingly, without that override method in the model, it would strip
all 'non-default' queries.
- Adding that means we could remove our custom database queries and use
the regular `findPage` helper that also handles pagination.
no issue
This change adds the ability to pass a desired language/locale into
portal, as a data-locale tag added to the script that loads it.
Falls back to the previous behavior, loading from the site settings, if
no data-locale is present. (And then to 'en', if necessary.) No change
in expected behavior if data-locale is not passed.
This provides an option to run portal in a language other than the
sitewide language.
(ref: https://app.slack.com/client/T025584C4/C018EKC56JF)
- When we added i18n for search we missed gating it behind the i18n flag.
- There aren't that many translations for search yet, so it's likely not many have noticed yet
- We'll remove the flag soon, but until then, adding the flag for consistency :)
no ref
{{ghost_head}} is huge, and some power-users and theme creators want the
ability to customize what it contains. This PR makes it easier for a
theme to write custom schema, or to load a custom version of
portal/comments/search/etc, or to minimize load times by not loading
scripts where they aren't needed, in a theme-specific way.
Because ghost_head is controlled at the theme level, this gives folks in
managed hosting the new ability to load a different version of the
included app scripts (by preventing ghost_head from writing them and
adding them in manually).
Usage example: ` {{ghost_head exclude="search,portal"}} `
(empty array)
No changes to current behavior
search
The built-in sodo-search script
Includes adding the click event listener on buttons, generating the
search index, and the UI.
portal
The portal script
Handles sign-in and sign-up, payments, tips, memberships, etc, and all
the portal data-attributes.
announcement
The announcement bar javascript
If you'd like to use the announcement bar admin settings but not have it
[mess up your CLS
metric](https://www.spectralwebservices.com/blog/announcement-bar-a-review/),
this is for you.
metadata
Skips HTML tags for meta description, favicon, canonical url, robots,
referrer
Important for SEO
schema
The LD+JSON schema
Important for SEO
card_assets
Loads cards.min.css and .js
Needed on any page with a post body, unless your theme replaces them
all. Assets can also be selectively loaded with the [card_assets
override](https://ghost.org/docs/themes/content/?ref=spectralwebservices.com#editor-cards)
comment_counts
Loads the comment_counts helper
Needed if the page is using {{comments}} or data-ghost-comment-count
attribute
social_data
Produces the og: and twitter: attributes for social media sharing and
previews
Required for good social media cards
cta_styles
Removes the call to action (CTA) styles
Used for member signup and CTA cards - may be overwritten by your theme
already
ref https://linear.app/ghost/issue/ONC-506
- adding `context` with the returned API response makes the logged error much more useful as without it we only log the status code which misses any details for why the failure occurred
no issue
This PR adds the ability to translate the strings that appear in the
newsletter as boilerplate text, using i18next.
Variables are in single mustaches ( `{date}` ) in the translation
strings (rather than `{{date}}`), because these strings occur both the
email template.hbs and also .js files. That necessitated a separate
namespace.
This PR also includes changes to the newsletter button ("more like
this", "less like this", "comment") that were previously delivered on
desktop as images that included the text. @sanne-san provided a rework
that removed text-as-image from the desktop buttons, and allows more
shared code between the two layouts, along with making the buttons
translatable.
Example usage - handlebars
```
<h3 class="latest-posts-header">{{t 'Keep reading'}}</h3>
{{{t 'By {authors}' authors=post.authors }}}
```
(NOTE: triple { required because of possible & )
Example usage - javascript
```
getValue: (member) => {
if (member.status === 'comped') {
return t('complimentary');
}
if (this.isMemberTrialing(member)) {
return t('trialing');
}
// other possible statuses: t('free'), t('paid') //
return t(member.status);
}
```
---------
Co-authored-by: Sanne de Vries <sannedv@protonmail.com>
Co-authored-by: Steve Larson <9larsons@gmail.com>
fix https://linear.app/ghost/issue/ENG-1105/settingscacheget-is-slow
- through profiling and flamegraphs, we can see that `_doGet` is one of
the bottlenecks during high traffic times, sometimes taking up to 20%
of the CPU time when hammering Ghost with `wrk`
- this is because, for the majority of settings cache lookup, we're
running `JSON.parse`, which blocks the main thread
- whilst we're only parsing small strings, we're doing it a LOT,
sometimes hundreds of times per request, which adds up
- this code just throws most deserializing at `JSON.parse`, so if we can
stop it from doing that, it'd be a huge win
- my initial attempts here were to convert the _doGet function to a
smarter deserializing, by looking up `cacheEntry.type` and acting
accordingly
- however, it became a bit of a logical nightmare, and difficult to
reason about for now (i still think we should do it)
- therefore, I'm just doing to add a hotpath fix to catch 99% of
usecases, which is checking the type of the cache entry and returning
the value if it's a string
- on a trivial benchmark locally, this causes Ghost to return 30% more
requests per second!!
ref PLG-220
- Improved `getBestComments` service to paginate correctly since we're
using a custom query to determine the top comments that goes beyond the
scope of what `findPage` is capable of.
- Updated CommentsController and CommentsService to support custom order
parameters.
- Added tests