How to deploy static website connecting to Django RESTful API? - django

First of all, google or SO search didn't help me: lots of tips regarding django's staticfiles, which I believe are not relevant here.
I have inherited a project consisting of:
Django backend in form of API returning JSON responses only;
standard Swampdragon deployment pushing realtime updates to frontend; very little configuration has been done here;
Frontend webapp built on Backbone and marionette.js, compiled and minified by Grunt.
My problem is: the frontend needs to know addresses for swampdragon and django servers; right now those values are hardcoded, so there is for example a Backbone model with lines like:
url: function() {
return App.BACKEND_URL+'settings/map';
}
Why hardcoded: backend can be served on any port or have a subdomain to itself; frontend is static and normally would be simply thrown into /var/www (for Apache) or would use some very simple nginx config. Both will be served from the same place, but there is no guarantee the port numbers or subdomains would match.
Idea number 1: try to guess what BACKEND_URL is from javascript, by taking window.location.host and appending standard port. That's hackish and error prone.
Idea number 2: move frontend to Django and make it ask for swampdragon credentials (they would be sent in the context of home view). Problem with that is, the frontend files are compiled by grunt. So where Django would kindly expect something like:
<script src="{% static 'scripts/vendor/modernizr.js' %}"></script>
I actually have
<script src="scripts/vendor/a8bcb0b6.modernizr.js"></script>
Where 'a8bcb0b6' is grunt's hash/version number and will be regenerated during next minification/build. Do I need to add additional logic to get rid of such stuff and copy grunt's output directory to django's static and template dirs?
Or is there another way to make this work, the right one, I am missing?

Your architecture is already clean, no need to make Django know about grunt or serve static files, and no need to use JS hacks to guess port numbers
Reverse Proxy
Use a reverse proxy like nginx or any other web server you like as a front end to both the static files and the REST API.
In computer networks, a reverse proxy is a type of proxy server that
retrieves resources on behalf of a client from one or more servers.
These resources are then returned to the client as though they
originated from the proxy server itself. (Wikipedia)
I will outline the important aspects without going into too much detail:
URL for the REST API
We make configs so that nginx will forward the API requests to Django
location /api {
proxy_pass http://127.0.0.1:8000; # assumes Django listens here
proxy_set_header Host $http_host; # preserve host info
}
So the above assumes your Django REST is mapped to /api and runs on port 8000 (e.g. you can run gunicorn on that port, or any other server you like)
http://nginx.org/en/docs/http/ngx_http_proxy_module.html
URL for our front end app
Next nginx will serve the static files that come out of grunt, by simply pointing it to the static folder
location / { alias /app/static/; }
The above assumes your static resources are in /app/static/ folder (like index.html, your CSS, JS etc). So this is primarily to load your BackboneJS app.
Django static files
Next step is not required, but if you have static files that you use with the Django app (static files that are generated with ./manage.py collectstatic, e.g. the django admin or the UI of Django REST Framework etc), simply map according to your Django settings.py STATIC_URL and STATIC_ROOT
location /static { alias /app/django_static_root/; }
/static and django_static_root being the STATIC_URL and STATIC_ROOT respectively
To sum up
So e.g. when you hit example.com/, nginx simply serves up the static files, then when a JS script makes REST call to /api, it gets trapped in the /api nginx location and gets forwarded to Django
End result is, example.com/ and example.com/api both hit the same front end web server, which proxies them to the right places
So there you have it, reserve proxying solves your ports and subdomain issues (and many others, like slow static files from Django and same-origin policies in web browsers and firewalls not liking anything besides default HTTP and HTTPS ports)

Related

Multiple netlify static sites served under a single cloudfront domain path

I have multiple netlify static sites that I want to serve in their entirety under a path of a cloudfront domain. However, resources that don't reside at the root of the netlify app are not being served (raise a 404)
i.e.
https://netlify1-abcd123.netlify.app/
/
/page1
/page1/subpage1
/page2
/images/logo.png
/stylesheets/styles.css
https://netlify2-defg456.netlify.app/
/
/netlify2-page1
/netlify2-page1/subpage1
/netlify2-page2
/netlify2-images/logo.png
/netlify2-stylesheets/styles.css
and I want these to be served under www.mydomain.com/netflify1 and www.mydomain.com/netlify2. The root of www.mydomain.com is currently served via an S3 static site.
I have configured rewrite rules in netlify.toml for each site (changing as appropriate to netlify2) as below:
[[redirects]]
from = "/netlify1/*"
to = "https://netlify1-abcd123.netlify.app/:splat"
status = 200
force = true
...and then added an Origin to Cloudfront which has the Origin Domain Name of https://netlify1-abcd123.netlify.app/ - all other Origin settings are the defaults.
I've then configured a Behaviour with a Path Pattern netlify1 that points to the netlify1 origin. Note that I cannot use the Default behaviour as the main index of www.mydomain.com is served via a static site hosted on S3.
This all works perfectly fine when serving the root / of my netlify app. I can navigate to www.mydomain.com/netlify1 and see the home page of my netlify app. However, anything that is not on the / of netlify doesn't get served.
I can see from the developer tools that a request is being made to www.mydomain.com/images/logo.png and www.mydomain.com/stylesheets/styles.css when trying to load the images and the styles. This returns a 404 because (I presume) they are not getting picked up by any Cloudfront Behaviour or Netlify rewrite rules.
So my question is. How can I make images, stylesheets and any page not on the root of the netlify apps work correctly? I can make it work by manually added every single potential path as a Behaviour on Cloudfront (i.e. configure a Behaviour with a Path Pattern of /images/* to point to the netlify1 Origin) - but that doesn't seem scalable or even correct.
I don't know whether I need to somehow rewrite all my URLs on the netlify app during building so that they become /netlify1/images and /netlify/page1 rather than /images and /page1 (the netlify apps are docs built with Sphinx docs), or whether this is something that needs to be handled via a Rewrite Rule in Lambda#Edge. I'm completely stumped.

How to Configure django_plotly_dash to serve assets from custom url

I have an hybrid app with most pages being on vuejs but some legacy code pages are still being served through dash-django app.
previously it was wholly a dash app.
on the production environment this is being served through an nginx with dedicated static directory and a reverse proxy.
The app is being served on a subdomain.
So the url looks like :
abd.com/subdom/route_to_app_endpoints
where abd.com is set to serve nginx files from base directory
and abd.com/subdom is reverse proxy to serve files from port 8000 on which the django application is running.
The problem I'm facing is when the app loads the base url i tries to load the components and the layout from is hitting the root directory :
eg for
abd.com/.django_plotly_dash/app/FileUpload/_dash-layout and it
gives 404 not found. This is what it tries by default .
whereas if i request
abd.com/subdom/django_plotly_dash/app/FileUpload/_dash-layout my
browser gives a nice output ..
I tried setting :
PLOTLY_DASH = {
"requests_pathname_prefix" :"/subdom/"
}
in the settings.py but still it is unable to route properly.

Browserify + WebStorm debug breaks routing in React-Router v4 BrowserRouter

I am writing a single page app with React for educational purposes. My React-Router v4 BrowserRouter handles client side routing correctly on CodeSandbox but not locally. In this case, the local server is the webstorm built-in devserver. HashRouter works locally but BrowserRouter does not.
Functioning properly: https://codesandbox.io/s/j71nwp9469
You are likely serving your app on the built-in webserver (localhost:63342), right? Internal web server returns 404 when using 'absolute' URLs (the ones starting with slash) as it serves files from localhost:port/project_name and not from localhost:port. That's why you have to make sure to change all URLs from absolute to the relative ones.
There is no way to set up the internal webserver to use project root as server document root. But you can configure it to use URLs like http://<host name>:<port> where the 'host name' is a name specified in hosts file, like 127.0.0.1 myhostName. See https://youtrack.jetbrains.com/issue/WEB-8988#comment=27-577559.
The solution was to understand how push state routing and the history API works. It is necessary to proxy requests through the index page when serving Single Page Applications that utilize the HTML5 History API.
The Webstorm dev server is not expected to include this feature, therefore the mention of Webstorm in this thread was a mistake.
There are multiple libraries of < 20 lines which do this for us, or it can easily be hand coded.

Service Worker served from different port on a local machine

I'm trying to develop a PWA for our sites. In production and staging, we serve everything from one domain. However, in development on a local machine we serve HTML from one port using Django server eg
http://localhost:8000
And the assets (including JS) using Grunt server from another port:
http://localhost:8001
The problem is that the scope of the service workers is therefore only limited to assets, which is useless, I want to offline-cache pages on the 8000-port origin.
I have somewhat been able to go around this by serving the service worker as a custom view in Django:
# urls.py
url(r'^(?P<scope>.*)sw\.js', service_worker_handler)
# views.py
def service_worker_handler(request, scope='/'):
return HttpResponse(render_to_string('assets/sw.js', {
'scope': scope,
}), content_type="application/x-javascript")
However, I do not think this is a good solution. This code sets up custom routing rules which are not necessary for production at all.
What I'm looking for is a local fix using a proxy, or something else that would let me serve the service worker with grunt like all the other assets.
I believe this resource can be of help to you: https://googlechrome.github.io/samples/service-worker/foreign-fetch/
Basically you host the Service worker on the port 8001 and the server handles it as shown in the example.
Then you fetch it from there.
The Foreign fetch is described more in details here: https://developers.google.com/web/updates/2016/09/foreign-fetch

ember cli url not working on server with manual return key (versus link-to)

An ember cli site was deployed onto a server and it works fine. Links via {{link-to}} all work beautifully.
BUT, when a user (me that is) manually enters a url and hits return. then the site is not found.
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
even changing a parameter of a working url (initially navigated to thru link-to)
http://site/start/0/length/30
and simply backspacing, changing the 30 to 20 and hit return
http://site/start/0/length/20
its a no go
localhost:4200 doesn't have this issue.
has anyone observed this vicious behaviour.
i actually need it for a callback redirect for oauth. but then noticed than any manually entered urls dont function.
It is because your server (IIS?) is trying to access the full path requested by your browser (eg /start/0/length/30), and not finding a valid file on disk returns a 404.
So, you need to configure your web server to proxy/rewrite the requests to the proper location. Assuming you are deploying your application in your "root" directory, the proper location is /index.html (the file ember-cli creates).
Unfortunately, I can't help you with IIS, but I can provide you with the proper configuration for nginx:
location / {
try_files $uri $uri/ /index.html;
}
This says "If the requested URI doesn't exist, instead respond with the /index.html file".
When you are using ember server on localhost:4200 you don't have the same problem because it is automatically doing something similar transparently.
If you are serving this up from any web server that isn't the built in Ember, ie non local server, you need to have a wildcard rule that returns your Ember app's index.html file for anything below your websites base url. If you only have your base url return the index.html file, then your webserver is confused by the unrecognized url and thinks it has nothing to return. If your rule, though,
for baseUrl/* returns index.html, your Ember app will then run the correct route hooks to establish the app context
this is a dupe and the question is
How to run emberJS application in IIS?
the easy answer is set locationType: hash in ember-cli's environment.config file (copied from accepted answer)
that will introduce a '#' in the url but doesnt require an IIS change.
var ENV = {
...
locationType: 'hash'
... };