fix: fix Turnstile widget not rendering behind Cloudflare Rocket Loader

Load Turnstile api.js statically in <head> with data-cfasync="false"
to bypass Rocket Loader interference. Use turnstile.render() API to
manually render widget after site key is fetched, instead of relying
on dynamic script loading and Vue data-bind attributes.
This commit is contained in:
Sora39831 2026-04-03 02:46:20 +08:00
parent f026afbc17
commit de6131aeac

View file

@ -1,4 +1,5 @@
{{ template "page/head_start" .}} {{ template "page/head_start" .}}
<script data-cfasync="false" src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
{{ template "page/head_end" .}} {{ template "page/head_end" .}}
{{ template "page/body_start" .}} {{ template "page/body_start" .}}
@ -118,7 +119,7 @@
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<div class="cf-turnstile-wrapper"> <div class="cf-turnstile-wrapper">
<div class="cf-turnstile" :data-sitekey="turnstileSiteKey" data-callback="onTurnstileCallback" :data-size="turnstileSize"></div> <div class="cf-turnstile" id="turnstile-widget"></div>
</div> </div>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
@ -149,10 +150,6 @@
<script> <script>
var turnstileToken = ''; var turnstileToken = '';
function onTurnstileCallback(token) {
turnstileToken = token;
}
const app = new Vue({ const app = new Vue({
delimiters: ['[[', ']]'], delimiters: ['[[', ']]'],
el: '#app', el: '#app',
@ -172,7 +169,7 @@
this.twoFactorEnable = await this.getTwoFactorEnable(); this.twoFactorEnable = await this.getTwoFactorEnable();
this.turnstileSiteKey = await this.getTurnstileSiteKey(); this.turnstileSiteKey = await this.getTurnstileSiteKey();
if (this.turnstileSiteKey) { if (this.turnstileSiteKey) {
this.loadTurnstileScript(); this.renderTurnstile();
} }
}, },
computed: { computed: {
@ -214,17 +211,19 @@
this.regUser = { username: "", password: "", confirmPassword: "" }; this.regUser = { username: "", password: "", confirmPassword: "" };
turnstileToken = ''; turnstileToken = '';
if (window.turnstile) { if (window.turnstile) {
turnstile.reset('.cf-turnstile'); turnstile.reset('#turnstile-widget');
} }
} }
this.loadingStates.registerSpinning = false; this.loadingStates.registerSpinning = false;
}, },
loadTurnstileScript() { renderTurnstile() {
const script = document.createElement('script'); if (window.turnstile) {
script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js'; turnstile.render('#turnstile-widget', {
script.async = true; sitekey: this.turnstileSiteKey,
script.defer = true; callback: function(token) { turnstileToken = token; },
document.head.appendChild(script); size: this.turnstileSize,
});
}
}, },
async getTurnstileSiteKey() { async getTurnstileSiteKey() {
const msg = await HttpUtil.post('/getTurnstileSiteKey'); const msg = await HttpUtil.post('/getTurnstileSiteKey');