228 lines
6.9 KiB
Python
Executable File
228 lines
6.9 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# -*- coding: utf-8
|
|
|
|
# Required modules
|
|
import os
|
|
import inspect
|
|
import random
|
|
import binascii
|
|
from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash
|
|
from functools import wraps
|
|
|
|
# Optionnal modules
|
|
import psycopg2
|
|
from flask_sqlalchemy import SQLAlchemy
|
|
|
|
########################################################################
|
|
# App settings
|
|
########################################################################
|
|
app = Flask(__name__)
|
|
# Path to static files
|
|
app.static_url_path='/static'
|
|
# Set debug mode to False for production
|
|
app.debug = True
|
|
# Various configuration settings belong here (optionnal)
|
|
app.config.from_pyfile('config.local.py')
|
|
# Generate a new key: head -n 40 /dev/urandom | md5sum | cut -d' ' -f1
|
|
app.secret_key = 'ce1d1c9ff0ff388a838b3a1e3207dd27'
|
|
# Feel free to use SQLAlchemy (or not)
|
|
db = SQLAlchemy(app)
|
|
|
|
########################################################################
|
|
# Menu management:
|
|
########################################################################
|
|
|
|
def get_menu(page):
|
|
""" The main menu is a list of lists in the followin format:
|
|
[string caption, string URL endpoint, int 0]
|
|
- The URL end point MUST match the function called by the corresponding
|
|
route.
|
|
- The int 0 is used to determine which menu entry is actally called.
|
|
The value MUST be 0."""
|
|
menu = [['Accueil', '/', 0],
|
|
['Articles', '/articles', 0],
|
|
['Basics', '/basics', 0],
|
|
['Inputs', '/inputs', 0],
|
|
['Ajax', '/ajax', 0],
|
|
['Database', '/database', 0],
|
|
['Todo', '/todo', 0],
|
|
]
|
|
# This is index page
|
|
if page == 'index':
|
|
menu[0][2] = 1
|
|
return menu
|
|
|
|
# Let's look for the actual page
|
|
page = '/%s' % page
|
|
for item in menu:
|
|
if item[1] == page:
|
|
item[2] = 1
|
|
return menu
|
|
# This should never happen
|
|
return menu
|
|
|
|
########################################################################
|
|
# Session management
|
|
########################################################################
|
|
|
|
def sync_session(request, session):
|
|
""" Synchronize cookies with session """
|
|
for key in request.cookies:
|
|
session[key] = request.cookies[key].encode('utf8')
|
|
|
|
def sync_cookies(response, session):
|
|
""" Synchronize session with cookies """
|
|
for key in session:
|
|
response.set_cookie(key, value=str(session[key]))
|
|
|
|
def check_session(func):
|
|
""" Check if the session has required token cookie set.
|
|
If not, redirects to the login page. """
|
|
@wraps(func)
|
|
def check(*args, **kwargs):
|
|
try:
|
|
if session['token'] == request.cookies['token']:
|
|
return func(*args, **kwargs)
|
|
except KeyError:
|
|
return render_template('login.html', message='')
|
|
return check
|
|
|
|
def check_login(login, password):
|
|
""" Puts the login verification code here """
|
|
if login == 'demo' and password == 'demo':
|
|
return True
|
|
return False
|
|
|
|
def gen_token():
|
|
""" Generate a random token to be stored in session and cookie """
|
|
token = binascii.hexlify(os.urandom(42))
|
|
return token
|
|
|
|
########################################################################
|
|
# Routes:
|
|
# -------
|
|
# Except for the index function, the function name MUST have the same
|
|
# name than the URL endpoint to make the menu work properly
|
|
########################################################################
|
|
@app.errorhandler(404)
|
|
def page_not_found(e):
|
|
""" 404 not found """
|
|
return render_template('error.html'), 404
|
|
|
|
@app.route("/login", methods=['GET', 'POST'])
|
|
def login():
|
|
login = request.form.get('login')
|
|
password = request.form.get('password')
|
|
if check_login(login, password):
|
|
# Generate and store a token in session
|
|
token = gen_token()
|
|
session['token'] = token
|
|
# Return user to index page
|
|
page = 'index'
|
|
menu = get_menu(page)
|
|
response = app.make_response(render_template('index.html', menu=menu))
|
|
# Push token to cookie
|
|
sync_cookies(response, session)
|
|
return response
|
|
# Credentials are not valid
|
|
return render_template('login.html', message='Invalid user or password')
|
|
|
|
@app.route("/", methods=['GET', 'POST'])
|
|
@check_session
|
|
def index():
|
|
""" Index page """
|
|
page = inspect.currentframe().f_code.co_name
|
|
menu = get_menu(page)
|
|
return render_template('index.html', menu=menu)
|
|
|
|
@app.route("/articles", methods=['GET', 'POST'])
|
|
@check_session
|
|
def articles():
|
|
""" Arcticles page """
|
|
page = inspect.currentframe().f_code.co_name
|
|
menu = get_menu(page)
|
|
return render_template('articles.html', menu=menu)
|
|
|
|
@app.route("/basics", methods=['GET', 'POST'])
|
|
@check_session
|
|
def basics():
|
|
""" Basics page """
|
|
page = inspect.currentframe().f_code.co_name
|
|
menu = get_menu(page)
|
|
return render_template('basics.html', menu=menu)
|
|
|
|
|
|
@app.route("/inputs", methods=['GET', 'POST'])
|
|
@check_session
|
|
def inputs():
|
|
""" Show the input collection """
|
|
page = inspect.currentframe().f_code.co_name
|
|
menu = get_menu(page)
|
|
return render_template('inputs.html', menu=menu)
|
|
|
|
@app.route("/ajax", methods=['GET', 'POST'])
|
|
@check_session
|
|
def ajax():
|
|
""" Propose various AJAX tests """
|
|
page = inspect.currentframe().f_code.co_name
|
|
menu = get_menu(page)
|
|
return render_template('ajax.html', menu=menu)
|
|
|
|
@app.route("/database", methods=['GET', 'POST'])
|
|
@check_session
|
|
def database():
|
|
""" A blah on using databases """
|
|
page = inspect.currentframe().f_code.co_name
|
|
menu = get_menu(page)
|
|
return render_template('database.html', menu=menu)
|
|
|
|
@app.route("/todo", methods=['GET', 'POST'])
|
|
@check_session
|
|
def todo():
|
|
""" The famous TODO list """
|
|
page = inspect.currentframe().f_code.co_name
|
|
menu = get_menu(page)
|
|
return render_template('todo.html', menu=menu)
|
|
|
|
########################################################################
|
|
# AJAX routes
|
|
########################################################################
|
|
|
|
@app.route("/get_html_from_ajax", methods=['GET', 'POST'])
|
|
@check_session
|
|
def get_html_from_ajax():
|
|
""" Return HTML code to an AJAX request
|
|
It may generate a 404 http error for testing purpose """
|
|
if int(random.random()*10) % 2:
|
|
# Randomlu generate 404 HTTP response
|
|
return render_template('error.html'), 404
|
|
return render_template('ajax_html.html')
|
|
|
|
@app.route("/get_value_from_ajax", methods=['GET', 'POST'])
|
|
@check_session
|
|
def get_value_from_ajax():
|
|
""" Return a randomly generated value to an AJAX request
|
|
It may return an error code for testing purpose """
|
|
err_code = 'TETA_ERR'
|
|
RND = int(random.random()*10)
|
|
if RND % 2:
|
|
# Randomlu generate error
|
|
return err_code
|
|
return str(RND)
|
|
|
|
@app.route("/set_value_from_ajax/<value>", methods=['GET', 'POST'])
|
|
@check_session
|
|
def set_value_from_ajax(value):
|
|
""" Accept a value from an AJAX request
|
|
It may return an error code for testing purpose """
|
|
err_code = 'TETA_ERR'
|
|
if value != 'We Make Porn':
|
|
return 'True'
|
|
return err_code
|
|
|
|
########################################################################
|
|
# Main
|
|
########################################################################
|
|
if __name__ == '__main__':
|
|
app.run(host='0.0.0.0')
|