Recently I have been using Flask to write this site. During the process I came across many small issues which took long time, and which should not, had I known about them in advance. Well, if you are also proceeding to writing your first 2000-line-scale Hackathon site, this blog might save you some time.
There are a few notes worth writing down.
- Frontend: Use template or not
- Backend: Deploying to AWS EC2 and RDS
- Flask/Werkzeug: Scaling it up to enable multi-threadding
- Flask: Submitting forms with data;
- Practical problem: security
- Practical problem: log-in control
- Notes on redirecting
- Apache: How to log and debug?
- Discuss, Debug, and Deploy
- Acknowledgements
Overview
First let us go over the functinoality and structure of this website. For functionality,
It is hosted on AWS EC2, using Flask 0.11.1 (requiring Python 2.7.6) as backend, connecting an MySQL instance which is hosted on Amazon Relational Database Service (RDS).
Frontend
There are many excellent frontend frameworks out there. React Native
might be the choice if you want a corresponding mobile app. Vue.js
or Angular.js
might be a choice if you want a user’s panel with rich support in operation, with codes being modular. For me this is not a site requiring too many functionalities so I used neither and went with pure HTML/CSS/JS instead.
Deployment to AWS EC2 and RDS
AWS is used by the most people in the world, so I used it. There are three steps in deployment: EC2, RDS, and DNS.
- EC2: (Other choices include Heroku, Google Cloud Engine, DigitalOcean, etc). Amazon Web Service has pretty good documentation on how to launch an instance of EC2, and how to connect by
ssh
-ing into it. You can also use a FTP cilent like FileZilla or PuTTY. Note that an EC2 instance closes all its ports by default so you have to create a security group and add some rule allowing some incoming and outgoing traffics. - RDS: (Other choices include Google Cloud Database, MongoDB Lab, etc) On AWS RDS, the security group of EC2 can be assigned. Also, to test your website, MySQL PC client can be used via
mysql -h [endpoint-name] -P 3306 -u [user_name] -p
. Note thatuser_name
can be manually added from RDS panel so you don’t have toINSERT TO mysql USER ()
, orGRANT PRIVILLEGE
, andFLUSH PRICILLEGE
. - DNS: (You have to buy a domain name and configure the A* record if you don’t want the domain name be something like
http://52.123.123.123
) You can set up a custom DNS using AWS Route 53 following this documentation and this video.
Flask/Werkzeug: Scaling it up to enable multi-threadding
Flask is single-threadded by default. There are many ways to run it concurrently, depending on where you are running it on:
- Local machine. If your
__init__.py
file contains aif __name__=="__main__"
block then you can setapp.run(threaded=True)
in it. If not then, in addition to running it usingexport FLASK_APP=__init__.py
thenflask run
, how to make it multi-threadded in this way? - Deployment. Add this lineinto the configuration file
1
WSGIDaemonProcess site-name user=www-data group=www-data threads=5 home=/var/www/site-name/
sitename.conf
placed in/etc/apache2/sites-available/
.
Flask: Submitting forms with data
There are generally three methods of submitting forms: (1)pure HTML form or (2) HTML form with ajax call, or (3) HTML Button with ajax call.
(1) Pure HTML form is the easiest way. You don’t even need JavaScript codes. Just using CSS to make the form look nice is enough. This is however the least flexible choice, since you can do no checking upon the content, the format, or anything else in the form. Also the URL is changed compulsorily to the one specified in the action
attribute after the request is fired.
(2) HTML form with AJAX call enables you to check the validity of the form and display some messages if something happens. For example, incorrect password, etc. Two things are to be noted here:
- First, according to the W3C doc, the
name
field should be filled to enable data be able to be read from server. - Second, two methods can turn the form into data easily. In both methods, the
data
variable is to be passed into thedata
field of thejQuery ajax()
call. One isvar data = $('#form-name').serialize();
. The other isvar data = new FormData($('#form-name')[0])
. Be careful that if you are using the latter method, the form in HTML should have attributeenctype='multipart/form-data'
, and the ajax call should be specified asprocessData: false
andcontentType: false
, so that the file and form data can be processed at the same time, and that they can be accessible viarequest.files['file_field_name']
and sth likerequest.form['firstname']
respectively.
(3) Using a<button>
as the submit button ditches the HTML<form>
element. This should be fine but you cannot utilize the one-sentence form-data-collection method.
Practical Problem: Security
Database Interference and SQL Injection
Security is very important when it comes to form submission. Since users have access to your database they can potentially do anything to disrupt it via SQL injection. This blog may be useful for securing your site against SQL injection.
As a summary, SQL injection may happen whenever there is an input being used to construct dynamic SQL statements. Counteractions include: limiting the allowed characters in input fields, parameterizing SQL queries, restricting the user’s privilleges, and using stored procedures.
Password Communication
Also because a Hackathon website requires people to register (apply) using a password, it is crucial that the password is not leaked. However, a web application has at least three security leaks:
(1) The front end is all open-sourced. Clicking F12
in Chrome, for example, grants you the access to all the secrets of the front-end codes, from HTML to CSS, from DOM to resources.
(2) The URL exposes some hint about the server. For example, passing data through a GET
request erializes the data and appends to the URL after an ?
. By the way, data transferred via URL in this way can be read in JavaScript using window.location.search
.
(3) Moreover, someone may spy your line and know the information you are communicating.
Malicious Behaviors
Users, theoretically, may do anything crazy to destroy your website. Some things they may do include:
- Flood your database by automatically filling out the forms.
- Simultaneously submitting queries to make the server too busy to serve other people. (DoS attack)
Practical problem: Log-in Control
If a website needs to provide different contents to different users, the log-in mechanism is necessary. The usual way to design a login-register system goes as following:
- A page for log in. If the account exists, redirect user to a logged-in page.
- A page for registration. The user account is created upon a valid submission of the registration form.
I was using another design. There is only one entrance for either login or register. So the users do not need to differentiate between login and register at the first step - that is the only entrance of the website. I set up: - A page for both login and register. If the account does not exist, create it and redirect user to filling out the remaining parts of the registration form.
- A page for successfully logged in users.
There is a drawback for this design though. Some users does not complete their registration and quit. This adds a lot of “zombie” accounts to the website and I have to manually delete them. Traditional login-register system avoids this issue.
Notes on Redirecting
There are two ways to redirect: through server or through JavaScript.
- Via server (Flask as example):
return redirect('hello')
wherehello
comes from the function under the route you want to redirect to:Note 1:1
2
3@app.route('your_destination_route')
def hello(params):
passyour_destination_route
always starts with a forward slash (‘/‘).
Note 2: Sometimes the website is trapped in a page saying ‘Redirecting… If you are not automatically redirected, click some link’. This happens when an AJAX request is lodging. Check if this AJAX call involves database query (which usually takes a long time). If yes, one of the solutions is to use JavaScript-patient-redirection instead. Another method, however, is to manually provide a redirect page to the site and, again, brutally redirect to the new page without the AJAX being finished. - Via JavaScript:
Check everything you want to check, and thenwindow.location='your_destination_route';
. This method is equivalent (and is a short-hand) to setting thewindow.location.pathname
which is actually the thing you are setting.
Apache: How to log and debug?
On my machine, the print
commands in Flask apps print to Apache’s error log file located in /var/log/apache2/error.log
. If, however, you run with flask run
, then the print sentence prints onto the standard output.
Discuss, Debug, and Deploy
The production of a website is a collaborative work, so communication is crutial. Especially communication specifying the functionalities of the site saves time for your revision. Ambiguous discussions during the product designing phase almost always results in the product not satisfying the customer’s requirement, which means, additional work.
Acknowledgements
This website cannot be made to stable so fast without the collaborative effort of the great IEEE University of Toronto student chapter. Joanna designed the background pictures, Danny, Jane and Barry did many testings to ensure the quality. Other people in the electronic chapter all contributed to make the website to a flagship tier. Thank you all very much!