cms icon indicating copy to clipboard operation
cms copied to clipboard

[5.x] Fix issue when using Livewire with full measure static caching

Open aerni opened this issue 1 year ago • 9 comments

I've worked on a couple of PR's for Jonas' Livewire addon to enhance support for Statamic's static caching. See https://github.com/jonassiewertsen/statamic-livewire/pull/61 and https://github.com/jonassiewertsen/statamic-livewire/pull/64.

The newly added AssetsReplacer adds support for Livewire's @assets Blade directive, that allows you to push assets into the <head>.

I've previously PR'd support for Livewire and Statamic's full measure static caching in https://github.com/statamic/cms/pull/8762. This script works as expected in general scenarios. However, I've encountered an edge case, where the CSRF token isn't replaced in time before a request to Livewire is made.

This can happen, when a script, added to the <head> with the @assets directive, is making requests to Livewire, e.g. with this.$wire. The issue is that at the time of this request, the CSRF token hasn't been replaced yet, as the nocache script is only executed at the end of the </body>. So this.$wire is making a Livewire request using the STATAMIC_CSRF_TOKEN placeholder as the CSRF token.

This PR resolves this issue by moving the nocache JS to the head before any <link> or <script> so that the CSRF token placeholder is replaced before any other script is executed.

aerni avatar Jun 14 '24 17:06 aerni

Marking this PR as a draft for now. Once the tests are passing, feel free to mark this as "ready for review" and we can take a look. 👍

duncanmcclean avatar Jun 17 '24 12:06 duncanmcclean

I've fixed the tests.

An alternative approach to the code changes could be to simply add the script at the beginning of the <head> instead. I leave this decision up to you.

aerni avatar Jun 17 '24 13:06 aerni

Copied over from Discord:

I am working on integrating Livewire into a full static measure site. When using nocache tags the /!/nocache route will return an CSRF token, which is then replacing any STATAMIC_CSRF_TOKEN placeholder. Only after this point the Livewire components can be used, because they need the CSRF token in order to use the Livewire update route. In case the /!/nocache route takes a lot of time to respond, Livewire cannot be initialized until this point, also no custom alpine code, which don't need this token. What do you think about creating a separate route for getting the CSRF token to ensure a quick initialization, instead of getting the token and nocache contents via one single route, which might need some time to be fetched? The /!/nocache route could therefore omit the CSRF token.

Currently I am using this code to initialize Livewire:

            if (window.livewireScriptConfig?.csrf === 'STATAMIC_CSRF_TOKEN') {
                document.addEventListener('statamic:nocache.replaced', () => Livewire.start());
            } else {
                Livewire.start();
            }

morhi avatar Jun 18 '24 11:06 morhi

That seems like a better approach to me. Even if we move the nocache js to the top of the page, the ajax request might still take longer than expected for a number of reasons. If you start Livewire only after the statamic:nocache.replaced is dispatched, it would work consistently.

jasonvarga avatar Jun 18 '24 12:06 jasonvarga

I'm not sure if it's a good idea to defer Livewire.start() until statamic:nocache.replaced has been dispatched. Livewire can be started from the get-go, as it doesn't depend on the CSRF token. The token is only needed when an update request is sent. Deferring the start of Livewire also defers the start of Alpine, and would generally result in a slower initialization.

The only issue with the CSRF token is that a script might request an update to Livewire before the token has been fetched and replaced.

What do you think about creating a separate route for getting the CSRF token to ensure a quick initialization, instead of getting the token and nocache contents via one single route, which might need some time to be fetched? The /!/nocache route could therefore omit the CSRF token.

@jasonvarga Would this be a better solution? So we could have the CSRF token script in the head and the nocache script at the end of the body?

Also, worth considering; if Livewire allowed hooking into its update cycle, we could maybe add some code to tell Livewire to only execute the update requests once statamic:nocache.replaced has been dispatched. But not sure if this is possible.

aerni avatar Jun 18 '24 13:06 aerni

Ok yeah that's all valid too.

jasonvarga avatar Jun 18 '24 13:06 jasonvarga

The one issue, that we try to solve, are 419 errors if Livewire tries to update without a valid token, right?

morhi avatar Jun 18 '24 14:06 morhi

Yes, that's the issue this PR tries to solve. 😄

aerni avatar Jun 18 '24 14:06 aerni

I did some more testing, and it turns out that this PR works as expected when letting Livewire do its magic script injection, as per https://github.com/jonassiewertsen/statamic-livewire/pull/64.

This PR also reduces the initial load times as mentioned by @morhi. So Livewire can start without having to wait for the CSRF token to be replaced.

However, if you are manually bundling Livewire, you are required to add this code shared above to your bundle:

if (window.livewireScriptConfig?.csrf === 'STATAMIC_CSRF_TOKEN') {
    document.addEventListener('statamic:nocache.replaced', () => Livewire.start());
} else {
    Livewire.start();
}

If you don't, you will still run into a 419 error, as mentioned by @jasonvarga: "Even if we move the nocache js to the top of the page, the ajax request might still take longer than expected for a number of reasons."

I wish we could abstract this away somehow.

aerni avatar Jun 19 '24 14:06 aerni

Ping @jasonvarga

jasonvarga avatar Sep 25 '24 14:09 jasonvarga

This was a breaking change. It's being reverted in #10898.

You'll need to opt into moving the JS into the head by adding 'nocache_js_position' => 'body', to config/statamic/static_caching.php.

jasonvarga avatar Oct 03 '24 14:10 jasonvarga

Oops. Sorry about that!

aerni avatar Oct 03 '24 14:10 aerni

All good. 👍

jasonvarga avatar Oct 03 '24 14:10 jasonvarga