JustArchiNET / ASF-ui

The official web interface for ASF
Apache License 2.0
271 stars 38 forks source link

Can't see log page when use cloudflare in Proxied mode #1567

Closed cyb233 closed 2 years ago

cyb233 commented 2 years ago

Checklist

ASF version

Latest stable release

ASF-ui version

892742c

Bug description

when I use host with cloud flarein Proxied mode, and enter the log page, it will throw exception: TypeError: Cannot read properties of undefined (reading 'replace'), and it will no problem without cloud flare. image see also: https://github.com/JustArchiNET/ArchiSteamFarm/discussions/2563

it will happen when set cloud flare in Development Mode and solved when set the host DNS only. image it's only happen in log page

Expected behavior

I exprcted that I can see the log, and protect my server by cloud flare.

Actual behavior

when I protect my server by cloud flare, I can't see any log.

Steps to reproduce

when use cloud flare to set a DNS Record in Proxied mode: image

Possible reason/solution

it's only happenned in cloud flare with Proxied, I'm not sure if the problem is cloud flare or the framework.

Can you help us with this bug report?

Somehow, I can test and offer feedback, but can't code

Global ASF.json config file

No response

BotName.json config of all affected bot instances

No response

Additional info

if it can be resolved, I have time to test, and if not, I want know why.

cyb233 commented 2 years ago

maybe I found why exception: window.__BASE_URL__ is undefined. how to fix it? image left proxied by cf and another not

Aareksio commented 2 years ago

The error is pointing to window.__BASE_URL__ being undefined indeed. This variable is set in 2 places:

Second path is activated when the main scripts fail to load from expected path. First path is activated when the main script loads from expected path AND it did not fail before.

Either way, I see no way how this code could not be set by the time we attempt to connect websocket.

To give more insight, I need you to verify neither of those paths activated. To do this, please open www/index.html and add a console.log statements in highlighted places:

image image

If none of those happens to fire when CF's proxy is enabled, I'd appriciate if you could show full rendered HTML (right click > View Source > copy whole source). I suspect CloudFlare may be doing some magic behind the scenes which prevents the script from working in predictable manner.


My bets what is going on:

  1. CloudFlare is using server-push (or early hints) to send the script along with initial document. This leads to <script src="main.js"/> being evaluated before inline script due to it's higher placement in HTML. I think this may be the case, as given there shouldn't be async or defer modifiers, browser have no reason to skip evaluating available script right away.

image

  1. CloudFlare is inlining the main.js script, the result is ultimately the same as above, the main script is parsed before inline script mounts load watcher and onload is never captured.

image

(options on screenshots may not represent the actual CF features at fault, but you can try turning them off)

mrskizzex commented 2 years ago

image image I'm going to be unhelpful and say a classic: works for me.

cyb233 commented 2 years ago

it seems realy caused by Rocket Loader™, it works well when I turn off it. thx.

I think it can be added into faq.

Aareksio commented 2 years ago

@cyb233 Could you please provide the rendered HTML for reference? I am positive we could adjust our code to work with Rocket Loader :)

cyb233 commented 2 years ago

with Rocket Loader or without?

cyb233 commented 2 years ago

souce file: index.txt view-source without Rocket Loader: index_view-source_without_RL.txt view-source with Rocket Loader: (why github said We don’t support that file type. I jist want upload txt file)

<!doctype html>
<html>
<head>
<title>ASF</title>
<meta charset="utf-8">
<meta name="robots" content="noindex">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600" rel="stylesheet">
<link href="/manifest.json" rel="manifest" crossorigin="use-credentials">
<link href="/images/logo.png" rel="shortcut icon">
<script defer="defer" src="/scripts/main.5ed0908.bundle.js" integrity="sha256-9JCW9sijsEmadSNVqk+HoLPjyfCDMsAvKgzrHhfYvEA= sha384-1Xs/h7NuREr2Qv9B32I34OZo0CPYmk/jzlp8pACL+PNCXeOiLSoUl1+Zr/03/kLE" crossorigin="anonymous" type="8c362c86887f4ed57e9e8237-text/javascript"></script>
<script async src='/cdn-cgi/challenge-platform/h/g/scripts/invisible.js?ts=1651795200'></script></head>
<body>
<div id="app"></div>
<script type="8c362c86887f4ed57e9e8237-text/javascript">
        console.log("Change success #1");
        const mainScript = document.querySelector('script[src^="/scripts/main"]');
        mainScript.onerror = createErrorHandler(window.location.pathname, mainScript.getAttribute('src'), mainScript.getAttribute('integrity'));
        mainScript.onload = createLoadHandler(mainScript.onerror);

        function addScript(path, integrity) {
            const scriptElement = document.createElement('script');
            scriptElement.src = path;
            scriptElement.async = true;

            if (integrity) {
                scriptElement.integrity = integrity;
                scriptElement.crossOrigin = 'anonymous';
            }

            document.head.appendChild(scriptElement);
            return scriptElement;
        }

        function stripLastPart(path) {
            const lastIndexOfSlash = path.lastIndexOf('/');
            return path.slice(0, lastIndexOfSlash);
        }

        function createLoadHandler(onError) {
            return function() {
                console.log("Load Handler fired");
                // onLoad is fired when script is loaded. We do not want to be fooled by server returning index.html
                // Check if the script is indeed working can be achieved by testing __ASF_UI_LOADED__ property set in index.js
                if (!window.__ASF_UI_LOADED__) return onError();

                // It is possible we loaded main script on first attempt
                if (!window.__BASE_PATH__) {
                    // Other parts of the app use __BASE_PATH__ and __BASE_URL__, set it anyway
                    window.__BASE_URL__ = window.location.origin + '/';
                    window.__BASE_PATH__ = '/';

                    // We do not need to update resources, as the default path is already good
                    return;
                }

                document.querySelector('link[rel="shortcut icon"]').setAttribute('href', `${__BASE_PATH__}images/logo.png`);
                document.querySelector('link[rel="manifest"]').setAttribute('href', `${__BASE_PATH__}manifest.json`);
            };
        }

        function createErrorHandler(path, scriptPath, integrity) {
            return function() {
                console.log("Load Handler fired");
                // We are likely working with /<prefix>. Possible <browser path>:
                // a) We are on /<prefix>
                // b) We are on nested page /<prefix>/<page>
                // c) We are on deeply nested page /<prefix>/<page A>/<page B>
                // Either way, we need to load script from /<prefix>/<script path>

                // We start from <browser path> and work recursively by striping
                // parts after last "/" character:
                // 1. /<prefix>/<page A>/<page B>
                // 2. /<prefix>/<page A>
                // 3. /<prefix>

                const normalizedPath = path.endsWith('/') ? path.slice(0, -1) : path;
                if (!normalizedPath.length) return; // Give up

                // These properties needs to be set properly prior to script execution
                // Otherwise we will be downloading script chunks from base (incorrect) path
                window.__BASE_URL__ = window.location.href.replace(window.location.pathname, normalizedPath + '/');
                window.__BASE_PATH__ = normalizedPath + '/';

                // Add new script tag using new path
                const newScriptPath = normalizedPath + scriptPath;
                const newScript = addScript(newScriptPath, integrity);

                // We found the path! Replace other resources
                newScript.onerror = createErrorHandler(stripLastPart(normalizedPath), scriptPath, integrity);
                newScript.onload = createLoadHandler(newScript.onerror);
            };
        }
    </script>
<script src="/cdn-cgi/scripts/7d0fa10a/cloudflare-static/rocket-loader.min.js" data-cf-settings="8c362c86887f4ed57e9e8237-|49" defer=""></script><script type="text/javascript">(function(){window['__CF$cv$params']={r:'706d6fb0e8867cf1',m:'3TjaQTEm0_onhLIkmBVGfRXusY3PHsPXzCZ0Y3mMMw8-1651795233-0-AaxiOjn9fIm9ddZSuCX48bEj0SoTGvdTLC4CLt6F7f+co9DV0VHnEhC/p0KfgRyYey0Cdms4+L/slDTx3YssNPQoluL5+JSNGJsTPcSxHRnjn0Z0vV/FutJzJuxKN1E4zw==',s:[0xc13ceb3c74,0x69fea67a9a],u:'/cdn-cgi/challenge-platform/h/g'}})();</script></body>
</html>