Celso
Celso

Reputation: 306

Dash app deployed on an Apache2 system never lets user past 'Loading...' because it cannot seem to find its dash .js files

I am trying serve a (python3.8.2) dash application using an Apache2 server and mod_wsgi, but the application stops on the "Loading..." screen. The server returns 404s for each of dash's .js scripts. I've had no problems when using dash's built in local server... but I'm not trying to serve this locally.

I'd love some advice on where I'm going wrong, and how to get this particular app up and running.

Symptoms

I am able to serve both a simple python "hello world" and a simple flask app, using my setup. However, when creating a simple 'dash' app...

[Error] Failed to load resource: the server responded with a status of 404 (Not Found) ([email protected]_6_0m1598005008.8.7.min.js, line 0)
... followed by 12 more similar error messages...
[Error] Failed to load resource: the server responded with a status of 404 (Not Found) (dash_renderer.v1_6_0m1598005008.min.js, line 0)
[Error] ReferenceError: Can't find variable: DashRenderer
    Global Code (hazcurves:28:103)

my apache access log shows a series of GET requests that got 404's (here's one example):

... "GET /_dash-component-suites/dash_renderer/dash_renderer.v1_6_0m1598005008.min.js HTTP/1.1" 404 495 "http://efehr-dev.ethz.ch/hazcurves/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15"

Additional observations

When I compared my served html to the html from a correctly functioning Dash app, I notice a difference in the .js names. It looks like mine are being mangled somehow.

Functioning version, from https://dash-gallery.plotly.host/dash-wind-streaming/

# Excerpt from functioning app's "footer" section
<footer>
  <script id="_dash-config" type="application/json">{"url_base_pathname": null, "requests_pathname_prefix": "/dash-wind-streaming/", "ui": false, "props_check": false, "show_undo_redo": false}</script>

  # [CGR] then there are  several components here that are variations of the following
  <script src="/dash-wind-streaming/_dash-component-suites/dash_renderer/dash_renderer.min.js?v=1.0.0&m=1583853516"></script>

  <script id="_dash-renderer" type="application/javascript">
        var renderer = new DashRenderer();
        </script>

My app's Footer makes the request look scrambled, with query parameters absorbed into the filename.

<footer>
  <script id="_dash-config" type="application/json">{"url_base_pathname": null, "requests_pathname_prefix": "/", "ui": false, "props_check": false, "show_undo_redo": false, "suppress_callback_exceptions": false, "update_title": "Updating..."}</script>
   
  # [CGR] then there are  several components here that are variations of the following
  <script src="/_dash-component-suites/dash_html_components/dash_html_components.v1_0_3m1598005008.min.js"></script>
  <script src="/_dash-component-suites/dash_renderer/dash_renderer.v1_6_0m1598005008.min.js"></script>
  <script id="_dash-renderer" type="application/javascript">var renderer = new DashRenderer();</script>
        </footer>

Now, these .js files [the unscrambled versions] DO exist, and _dash-component-suites would seem to map to my .local/lib/python3.8/site-packages folder.

So, from what I've shown above, I have .../dash_renderer/dash_renderer.min.js and .../dash_html_components.min.js

My system:

A clean system with Ubuntu 20.04.1 LTS , Apache 2.4 with mod_wsgi installed.
Python 3.8.2 has the following relevant packages installed [using sudo pip3] :

dash==1.14.0
dash-core-components==1.10.2
dash-html-components==1.0.3
dash-renderer==1.6.0
dash-table==4.9.0
Flask==1.1.2
Flask-Compres=1.5.0

Here is my app.wsgi:

from flask import Flask
server = Flask(__name__)

import dash
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash(server=server) 
app.css.config.serve_locally = True  
app.scripts.config.serve_locally = True
app.layout = html.Div(children=["hello"])
application = app.server

I have been able to run this using the built-in server, serving it on a local server.

My site configuration file:

<VirtualHost *:80>
        ServerAdmin webmaster@localhost
        DocumentRoot /home/username/public_html

        WSGIDaemonProcess /home/username/public_html/myapp.wsgi processes=2 threads=15 display-name=%{GROUP}
        WSGIScriptAlias /hazcurves /home/username/public_html/myapp.wsgi

        <Directory /home/username/public_html>
                AllowOverride all
                Options Indexes FollowSymLinks
                <Limit GET POST OPTIONS>
                        Require all granted
                </Limit>
        </Directory>


        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Some of the things I'll admit to trying

After attempting to shoehorn advice from websites that are tangentially relevant towards this question, I'm starting to feel like a monkey banging on keys --- which feels, um wrong (and insecure)

final caveat: this is my first foray into managing an app on the server side of the world.

Upvotes: 4

Views: 3342

Answers (1)

Celso
Celso

Reputation: 306

I am happy to report I eventually figured it out, and that my applications are running great now. Here's what I learned.

I had to be mindful of the following when calling dash.Dash(...) Instead of what I had written, I needed:

dash.Dash(__name__, requests_pathname_prefix='/hazcurves/')

The important lessons are as folows:

  1. Do not overwrite the server variable. Somehow I was loosing any configurations I had created.
  2. requests_pathname_prefix needed to match the URL alias (starting end ending with slashes).

Red herrings (Things that did not actually matter):

  1. creating a Flask instance first
  2. configurations with respect to serving locally
app.css.config.serve_locally = True  
app.scripts.config.serve_locally = True

So, here are excerpts from my three files of interest:

mysite.conf : /etc/apache2/sites-available/mysite.conf

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /home/myusername/public_html


    # ---- Hazard Curves App ---- #
    # using WSGIProcessGroup within Location allows multiple apps to run
    # without interfering with each other

    WSGIDaemonProcess curves/wsgi.py processes=2 threads=15 display-name=%{GROUP}
    WSGIScriptAlias /curves /home/myusername/public_html/curves/wsgi.py
    <Location /curves>
        WSGIProcessGroup curves/wsgi.py
    </Location>
</VirtualHost>

wsgi.py : ~/public_html/curves/wsgi.py

#!/usr/bin/python3

from myapp import app
application = app.server

myapp.py : ~/public_html/curves/myapp.py

import dash
import dash_html_components as html

app = dash.Dash(__name__, requests_pathname_prefix='/curves/')

app.title = "Some Test"
app.layout = html.Div([html.H6("Hello World")])

if __name__ == '__main__':
    app.run_server(port=8051, debug=True) # unimportant in this context

Upvotes: 8

Related Questions