Flask app that routes based on subdomain - python

I want to have my top-level domain as a portal for various subdomains that correspond to different sections of my site. example.com should route to a welcome.html template. eggs.example.com should route to an "eggs" subsection or application of the site. How would I achieve this in Flask?

#app.route() takes a subdomain argument to specify what subdomain the route is matched on. Blueprint also takes a subdomain argument to set subdomain matching for all routes in a blueprint.
You must set app.config['SERVER_NAME'] to the base domain so Flask knows what to match against. You will also need to specify the port, unless your app is running on port 80 or 443 (i.e in production).
As of Flask 1.0 you must also set subdomain_matching=True when creating the app object.
from flask import Flask
app = Flask(__name__, subdomain_matching=True)
app.config['SERVER_NAME'] = "example.com:5000"
#app.route("/")
def index():
return "example.com"
#app.route("/", subdomain="eggs")
def egg_index():
return "eggs.example.com"
ham = Blueprint("ham", __name__, subdomain="ham")
#ham.route("/")
def index():
return "ham.example.com"
app.register_blueprint(ham)
When running locally, you'll need to edit your computer's hosts file (/etc/hosts on Unix) so that it will know how to route the subdomains, since the domains don't actually exist locally.
127.0.0.1 localhost example.com eggs.example.com ham.example.com
Remember to still specify the port in the browser, http://example.com:5000, http://eggs.example.com:5000, etc.
Similarly, when deploying to production, you'll need to configure DNS so that the subdomains route to the same host as the base name, and configure the web server to route all those names to the app.
Remember, all Flask routes are really instances of werkzeug.routing.Rule. Consulting Werkzeug's documentation for Rule will show you quite a few things that routes can do that Flask's documentation glosses over (since it is already well documented by Werkzeug).

Related

Multiple Python Flask apps on single apache server losing sessions when session.clear is called on one of the apps

I have a couple of python 3.6 Flask apps running on my apache server using WSGI.
There are 2 different apps running on the same apache server:
www.example.com/lodge
www.example.com/dashboard
Both apps have a unique app.secret_key
The /dashboard app is a flask app with its own set of routes:
/dashboard/login
/dashboard/orders
/dashboard/staff
The login route calls session.clear() and lets the user enter their login information. A logged in token then gets stored in a session variable.
Both the /dashboard/orders and dashboard/staff routes have a decorator which checks for the existence of the logged in token in session and redirects to the login route if it does not exists.
The /lodge app is another simple Flask app with its own routes:
/lodge/welcome
/lodge/personal
/lodge/review
/lodge/confirmation
The welcome route also calls session.clear() and then displays a webform. When the user submits the webform, the personal route is called which stores those webform input values into session.
The issue that I am having is if I navigate to www.example.com/dashboard/login and login, I can then flick between the staff and orders routes no problems at all, however when I then open a new tab and go to www.example.com/lodge/welcome (which then calls session.clear) and then reopen the dashboard tab and try to go to the staff or orders route, I get redirected back to the login route as the session has been cleared.
httpd.conf:
<VirtualHost *:80>
WSGIScriptAlias /newapp "c:/lodge/lodge.wsgi"
<Directory "c:/lodge">
Require all granted
</Directory>
WSGIScriptAlias /dashboard "c:/dashboard/dashboard.wsgi"
<Directory "c:/dashboard">
Require all granted
</Directory>
</VirtualHost>
Side note, this does not happen if I access the dashboard app on http://example.com/dashboard and the lodge app on http://www.example.com/lodge
Answering my own question here.
I achieved multiple applications on the same apache server quite easily by changing the app configs for both apps. No virtual enviroments or tinkering with wsgi scripts needed!
Lodge app:
app = Flask(__name__)
app.config.from_object(__name__)
app.config.update(
SESSION_COOKIE_NAME = 'session_lodge',
SESSION_COOKIE_PATH = '/lodge/'
)
Dashboard app:
app = Flask(__name__)
app.config.from_object(__name__)
app.config.update(
SESSION_COOKIE_NAME = 'session_db',
SESSION_COOKIE_PATH = '/dashboard/'
)
The answeres by #m-dennis and #burhan-khalid porivded some insight to the problem so thanks for that!
Having multiple sub domains was not an option for me and I encountered the same issue when having both apps run in their own virtual enviroments.
Side note, this does not happen if I access the dashboard app on http://example.com/dashboard and the lodge app on http://www.example.com/lodge
This is actually the reason why you are seeing this behavior. Cookies are bound to domains and not paths.
Once you set a cookie at example.com, it is valid for all links, paths and URLs for that domain. It is not valid for www.example.com - this explains why it works if you run one of your apps on a different subdomain.
So what you are seeing is the proper behavior.
Try to use virtual environments. It seems that your flask apps running in one python execution thread. Here you can find, how to run your app(s) in a virtual environment.

Flask building urls for api and view

I am writing a flask application with angularjs frontend. Templates aren't rendered from flask yet, they are delivered as static files. My api endpoint is as following:
#route('/projects', method=['GET'])
def rest_projects(self):
"""TODO: CRUD operation
"""
Project = Model.get('project.project')
return jsonify([project.serialize() for project in Project.search([])])
url_for works perfect with above endpoint but is there any way it can build urls for js templates also?. Say if url has api prefixed ie: /api/tasks, it comes to this handler otherwise deliver template. Right now I am using nginx to achieve that but is there anything wrong in my design which is restricting me to do this from flask only.
Flask is not intended to serve static files and you are better off keeping your files served by nginx for performance reasons. You can serve static files however using send_static_file() or send_from_directory methods.
http://flask.pocoo.org/docs/0.10/api/

Serve static files with Eve

I am running Flask and Eve on localhost at a same time. The Flask app serves static files and makes requests to the Eve app to get some data. I want to run Eve only, without a separate Flask app. How can I serve static files with Eve?
A better approach will be to prefix the /api for all REST APIs. This can be done by adding URL_PREFIX="api" in settings.py.
By doing this whenever there is request to /, Eve(Flask) will not return the resource catalog instead returns the page as given in run.py.
To serve static content add route decorators accordingly in run.py,
#app.route('/')
def index():
return app.send_static_file('index.html')
app.run(host="0.0.0.0", debug=True)
Eve is a Flask application (a subclass) so as a general rule everything that works with Flask works with Eve too. You could register a blueprint, or add new routes.
Also see this answer for a link to a working example: Servicing html requests with Eve
try set import_name arg for Eve:
app = Eve(import_name=__name__)

Inheriting configuration from parent app in Flask

I have an application that is constructed as follows:
app = Flask(__name__)
app.wsgi_app = DispatcherMiddleware(frontend.create_app(), {
'/api': api.create_app()
})
app.config['DATABASE'] = db
I want to access the same database in both the frontend and api apps, but when I run something like current_app.config['DATABASE'] in Blueprints registered to api, that raises KeyError: 'DATABASE'. Is it possible to inherit configurations so that tests, etc. need only modify the top-level abstraction? Thanks.
Simply change your create_app methods on frontend and api to take a configuration dictionary and use flask.Config to create a configuration with all the properties of app.config (loadable from environmental variables, etc.):
from flask import Config
from werkzeug.wsgi import DispatcherMiddlware
config = Config()
config.from_envvar("YOUR_PROGRAM_CONFIG")
app = DispatcherMiddlware(frontend.create_app(config), {
'/api': api.create_app(config)
})
Then you can merge the provided config with each of your app's configurations:
def create_app(config):
app = Flask(__name__)
app.config.update(config)
# Potentially, load another config
app.config.from_envvar("YOUR_PROGRAM_CONFIG_FRONTEND", silent=True)
return app
Note that there is no need to create an app to route to other apps - the dispatcher middleware can already do that for you.
Also note that if you are already using blueprints it may make more sense to simply ship the blueprints and compose them on a single application, rather than using dispatcher middleware (depending, of course on how complex your apps are).

How do I manipulate subdomains with Pyramid's url dispatch? [duplicate]

I'm looking to have multiple domains and subdomains on a single Pyramid instance. However, I can't seem to find any documentation on it. The last question referred to a glossary with very little information and no examples. Do any of you have any examples or can direct me to better documentation?
Pyramid is just a WSGI application. This means it's dependent on the HTTP_HOST environ key (set by the Host header) to determine the host of the application. It's all relative. Point-being that Pyramid has no restrictions on what it can accept, thus the world is your oyster and you can set it up to limit content to various domains however you'd like. This of course starts with what hosts your webserver is configured to feed to your application.
Assuming you're using URL dispatch, you might want to design some custom route predicates that check the request.host value for whatever you'd like. Returning False from that predicate will prevent that route from ever matching a request to that host.
This is a large topic, so it might help if you give some more specifics. For example, since Pyramid is relative, any URL you may want to generate from 'example.com' to redirect someone to 'sub.example.com' will need to be done via a pregenerator.
def pregen(request, elements, kw):
kw['_app_url'] = 'http://sub.example.com'
return elements, kw
def req_sub(info, request):
return request.host.startswith('sub')
config.add_route('sub_only', '/',
custom_predicates=(req_sub,),
pregenerator=pregen)
config.add_route('foo', '/foo')
config.add_view(view, route_name-'foo')
def view(request):
# redirect the user to "http://sub.example.com", regardless of whether
# request.host is "example.com" or "sub.example.com"
return HTTPFound(request.route_url('sub_only'))
If you have control over your hosting environment, I would strongly suggest keeping the domain stuff out of pyramid and handling it with a proxy server such as apache mod proxy, routing to subdomains in pyramid. Then you can easily switch any of the domain name to view routing without having anything fragile (like domain names) in your pyramid code. Your app code will be much cleaner this way, and far easier to change later.
Here's an Apache example of two domains going to one pyramid app, assuming we are serving the pyramid app somehow or another on port 5001 (gunicorn or whatever you want).
<VirtualHost *:80>
ServerName domain_2.com
ProxyPreserveHost On
# send all request to our app at /app1/*
ProxyPass / http://127.0.0.1:5001/app_1/
ProxyPassReverse / http://127.0.0.1:5001/app_1/
</VirtualHost>
<VirtualHost *:80>
ServerName domain_2.com
ProxyPreserveHost On
# send all request to our app at /app2/*
ProxyPass / http://127.0.0.1:5001/app_2/
ProxyPassReverse / http://127.0.0.1:5001/app_2/
</VirtualHost>
And here's an example of one domain going to several pyramid instances:
<VirtualHost *:80>
ServerName mydomain.com
ProxyPreserveHost On
# admin go to manager app on 5001
ProxyPass /media/manager/ http://127.0.0.1:5001/ retry=5
ProxyPassReverse /media/manager/ http://127.0.0.1:5001/
# downloads from server app on 5002
ProxyPass /media/server/ http://127.0.0.1:5002/ retry=5
ProxyPassReverse /media/server/ http://127.0.0.1:5002/
</VirtualHost>

Categories