subdomain dispatcher
This commit is contained in:
parent
27ced9d90e
commit
2906b79dfc
33
app.py
33
app.py
|
@ -1,8 +1,35 @@
|
|||
from gevent import monkey, pywsgi
|
||||
monkey.patch_all()
|
||||
import logging
|
||||
from realms import app, config
|
||||
from realms import create_app, config
|
||||
from threading import Lock
|
||||
|
||||
class SubdomainDispatcher(object):
|
||||
|
||||
def __init__(self, domain, create_app):
|
||||
self.domain = domain
|
||||
self.create_app = create_app
|
||||
self.lock = Lock()
|
||||
self.instances = {}
|
||||
|
||||
def get_application(self, host):
|
||||
host = host.split(':')[0]
|
||||
assert host.endswith(self.domain), 'Configuration error'
|
||||
subdomain = host[:-len(self.domain)].rstrip('.')
|
||||
with self.lock:
|
||||
app = self.instances.get(subdomain)
|
||||
if app is None:
|
||||
app = self.create_app(subdomain)
|
||||
self.instances[subdomain] = app
|
||||
return app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
app = self.get_application(environ['HTTP_HOST'])
|
||||
return app(environ, start_response)
|
||||
|
||||
|
||||
def make_app(subdomain):
|
||||
return create_app(subdomain)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.logger.setLevel(logging.INFO)
|
||||
app = SubdomainDispatcher(config.domain, make_app)
|
||||
pywsgi.WSGIServer(('', config.port), app).serve_forever()
|
||||
|
|
|
@ -2,19 +2,17 @@ import logging
|
|||
import os
|
||||
import time
|
||||
|
||||
import redis
|
||||
import rethinkdb as rdb
|
||||
from flask import Flask, request, render_template, url_for, redirect, flash, session
|
||||
from flask.ext.bcrypt import Bcrypt
|
||||
from flask.ext.login import LoginManager, login_user, logout_user
|
||||
from flask.ext.assets import Environment
|
||||
from recaptcha.client import captcha
|
||||
from werkzeug.routing import BaseConverter
|
||||
|
||||
from session import RedisSessionInterface
|
||||
import config
|
||||
from wiki import Wiki
|
||||
from util import to_canonical, remove_ext, mkdir_safe, gravatar_url
|
||||
from models import Site, User, CurrentUser
|
||||
|
||||
|
||||
class RegexConverter(BaseConverter):
|
||||
|
@ -22,48 +20,6 @@ class RegexConverter(BaseConverter):
|
|||
super(RegexConverter, self).__init__(url_map)
|
||||
self.regex = items[0]
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.update(config.flask)
|
||||
app.debug = (config.ENV is not 'PROD')
|
||||
app.secret_key = config.secret_key
|
||||
app.static_path = os.sep + 'static'
|
||||
app.session_interface = RedisSessionInterface()
|
||||
app.url_map.converters['regex'] = RegexConverter
|
||||
|
||||
bcrypt = Bcrypt(app)
|
||||
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
|
||||
assets = Environment(app)
|
||||
assets.url = app.static_url_path
|
||||
assets.directory = app.static_folder
|
||||
|
||||
cache = redis.StrictRedis(host=config.cache['host'], port=config.cache['port'])
|
||||
|
||||
conn = rdb.connect(config.db['host'], config.db['port'], db=config.db['dbname'])
|
||||
|
||||
if not config.db['dbname'] in rdb.db_list().run(conn) and config.ENV is not 'PROD':
|
||||
# Create default db and repo
|
||||
print "Creating DB %s" % config.db['dbname']
|
||||
rdb.db_create(config.db['dbname']).run(conn)
|
||||
for tbl in ['sites', 'users', 'pages']:
|
||||
rdb.table_create(tbl).run(conn)
|
||||
|
||||
main_repo_dir = config.repos['main']
|
||||
repo_dir = config.repos['dir']
|
||||
|
||||
# This is down here because of dependencies above
|
||||
from models import Site, User, CurrentUser
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return CurrentUser(user_id)
|
||||
|
||||
|
||||
w = Wiki(main_repo_dir)
|
||||
|
||||
|
||||
def redirect_url():
|
||||
return request.args.get('next') or request.referrer or url_for('index')
|
||||
|
@ -73,167 +29,197 @@ def validate_captcha():
|
|||
response = captcha.submit(
|
||||
request.form['recaptcha_challenge_field'],
|
||||
request.form['recaptcha_response_field'],
|
||||
app.config['RECAPTCHA_PRIVATE_KEY'],
|
||||
config.flask['RECAPTCHA_PRIVATE_KEY'],
|
||||
request.remote_addr)
|
||||
return response.is_valid
|
||||
|
||||
@app.template_filter('datetime')
|
||||
def _jinja2_filter_datetime(ts):
|
||||
return time.strftime('%b %d, %Y %I:%M %p', time.localtime(ts))
|
||||
|
||||
def create_app(subdomain=None):
|
||||
app = Flask(__name__)
|
||||
app.config.update(config.flask)
|
||||
app.debug = (config.ENV is not 'PROD')
|
||||
app.secret_key = config.secret_key
|
||||
app.static_path = os.sep + 'static'
|
||||
app.session_interface = RedisSessionInterface()
|
||||
app.url_map.converters['regex'] = RegexConverter
|
||||
|
||||
bcrypt = Bcrypt(app)
|
||||
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return CurrentUser(user_id)
|
||||
|
||||
assets = Environment(app)
|
||||
assets.url = app.static_url_path
|
||||
assets.directory = app.static_folder
|
||||
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
return render_template('errors/404.html'), 404
|
||||
main_repo_dir = config.repos['main']
|
||||
repo_dir = config.repos['dir']
|
||||
|
||||
w = Wiki(main_repo_dir) if not subdomain else Wiki(repo_dir + "/" + subdomain)
|
||||
|
||||
|
||||
@app.errorhandler(500)
|
||||
def page_error(e):
|
||||
logging.exception(e)
|
||||
return render_template('errors/500.html'), 500
|
||||
@app.template_filter('datetime')
|
||||
def _jinja2_filter_datetime(ts):
|
||||
return time.strftime('%b %d, %Y %I:%M %p', time.localtime(ts))
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def root():
|
||||
return render('home')
|
||||
#return redirect('/home')
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
return render_template('errors/404.html'), 404
|
||||
|
||||
|
||||
@app.route("/account/")
|
||||
def account():
|
||||
return render_template('account/index.html')
|
||||
@app.errorhandler(500)
|
||||
def page_error(e):
|
||||
logging.exception(e)
|
||||
return render_template('errors/500.html'), 500
|
||||
|
||||
|
||||
@app.route("/_new/", methods=['GET', 'POST'])
|
||||
def new_wiki():
|
||||
if request.method == 'POST':
|
||||
# TODO validate wiki name
|
||||
wiki_name = request.form['name']
|
||||
s = Site()
|
||||
if s.get_by_name(wiki_name):
|
||||
flash("Site already exists")
|
||||
return redirect(redirect_url())
|
||||
@app.route("/")
|
||||
def root():
|
||||
return render('home')
|
||||
#return redirect('/home')
|
||||
|
||||
|
||||
@app.route("/account/")
|
||||
def account():
|
||||
return render_template('account/index.html')
|
||||
|
||||
|
||||
@app.route("/_new/", methods=['GET', 'POST'])
|
||||
def new_wiki():
|
||||
if request.method == 'POST':
|
||||
# TODO validate wiki name
|
||||
wiki_name = request.form['name']
|
||||
s = Site()
|
||||
if s.get_by_name(wiki_name):
|
||||
flash("Site already exists")
|
||||
return redirect(redirect_url())
|
||||
else:
|
||||
Wiki(repo_dir + "/" + wiki_name)
|
||||
return redirect('http://%s.%s' % (wiki_name, config.hostname))
|
||||
else:
|
||||
Wiki(repo_dir + "/" + wiki_name)
|
||||
return redirect('http://%s.%s' % (wiki_name, config.hostname))
|
||||
else:
|
||||
return render_template('_new/index.html')
|
||||
return render_template('_new/index.html')
|
||||
|
||||
|
||||
@app.route("/logout/")
|
||||
def logout():
|
||||
logout_user()
|
||||
del session['user']
|
||||
return redirect(url_for('root'))
|
||||
@app.route("/logout/")
|
||||
def logout():
|
||||
logout_user()
|
||||
del session['user']
|
||||
return redirect(url_for('root'))
|
||||
|
||||
@app.route("/commit/<sha>/<name>")
|
||||
def commit_sha(name, sha):
|
||||
cname = to_canonical(name)
|
||||
@app.route("/commit/<sha>/<name>")
|
||||
def commit_sha(name, sha):
|
||||
cname = to_canonical(name)
|
||||
|
||||
data = w.get_page(cname, sha=sha)
|
||||
if data:
|
||||
return render_template('page/page.html', page=data)
|
||||
else:
|
||||
return redirect('/create/'+cname)
|
||||
|
||||
|
||||
@app.route("/compare/<name>/<regex('[^.]+'):fsha><regex('\.{2,3}'):dots><regex('.+'):lsha>")
|
||||
def compare(name, fsha, dots, lsha):
|
||||
diff = w.compare(name, fsha, lsha)
|
||||
return render_template('page/compare.html', name=name, diff=diff)
|
||||
|
||||
|
||||
@app.route("/register", methods=['GET', 'POST'])
|
||||
def register():
|
||||
if request.method == 'POST':
|
||||
user = User()
|
||||
if user.get_by_email(request.form['email']):
|
||||
flash('Email is already taken')
|
||||
return redirect('/register')
|
||||
if user.get_by_username(request.form['username']):
|
||||
flash('Username is already taken')
|
||||
return redirect('/register')
|
||||
|
||||
email = request.form['email'].lower()
|
||||
# Create user and login
|
||||
u = User.create(email=email,
|
||||
username=request.form['username'],
|
||||
password=bcrypt.generate_password_hash(request.form['password']),
|
||||
avatar=gravatar_url(email))
|
||||
login_user(CurrentUser(u.id))
|
||||
return redirect("/")
|
||||
else:
|
||||
return render_template('account/register.html')
|
||||
|
||||
|
||||
@app.route("/login", methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
if User.auth(request.form['email'], request.form['password']):
|
||||
return redirect("/")
|
||||
else:
|
||||
flash("Email or Password invalid")
|
||||
return redirect("/login")
|
||||
else:
|
||||
return render_template('account/login.html')
|
||||
|
||||
|
||||
@app.route("/history/<name>")
|
||||
def history(name):
|
||||
history = w.get_history(name)
|
||||
return render_template('page/history.html', name=name, history=history)
|
||||
|
||||
|
||||
@app.route("/edit/<name>", methods=['GET', 'POST'])
|
||||
def edit(name):
|
||||
data = w.get_page(name)
|
||||
cname = to_canonical(name)
|
||||
if request.method == 'POST':
|
||||
edit_cname = to_canonical(request.form['name'])
|
||||
if edit_cname != cname:
|
||||
w.rename_page(cname, edit_cname)
|
||||
w.write_page(edit_cname, request.form['content'], message=request.form['message'])
|
||||
return redirect("/" + edit_cname)
|
||||
else:
|
||||
data = w.get_page(cname, sha=sha)
|
||||
if data:
|
||||
name = remove_ext(data['name'])
|
||||
content = data['data']
|
||||
return render_template('page/edit.html', name=name, content=content)
|
||||
return render_template('page/page.html', page=data)
|
||||
else:
|
||||
return redirect('/create/'+cname)
|
||||
|
||||
|
||||
@app.route("/delete/<name>", methods=['POST'])
|
||||
def delete(name):
|
||||
pass
|
||||
@app.route("/compare/<name>/<regex('[^.]+'):fsha><regex('\.{2,3}'):dots><regex('.+'):lsha>")
|
||||
def compare(name, fsha, dots, lsha):
|
||||
diff = w.compare(name, fsha, lsha)
|
||||
return render_template('page/compare.html', name=name, diff=diff)
|
||||
|
||||
|
||||
@app.route("/create/", methods=['GET', 'POST'])
|
||||
@app.route("/create/<name>", methods=['GET', 'POST'])
|
||||
def create(name=None):
|
||||
cname = ""
|
||||
if name:
|
||||
@app.route("/register", methods=['GET', 'POST'])
|
||||
def register():
|
||||
if request.method == 'POST':
|
||||
user = User()
|
||||
if user.get_by_email(request.form['email']):
|
||||
flash('Email is already taken')
|
||||
return redirect('/register')
|
||||
if user.get_by_username(request.form['username']):
|
||||
flash('Username is already taken')
|
||||
return redirect('/register')
|
||||
|
||||
email = request.form['email'].lower()
|
||||
# Create user and login
|
||||
u = User.create(email=email,
|
||||
username=request.form['username'],
|
||||
password=bcrypt.generate_password_hash(request.form['password']),
|
||||
avatar=gravatar_url(email))
|
||||
login_user(CurrentUser(u.id))
|
||||
return redirect("/")
|
||||
else:
|
||||
return render_template('account/register.html')
|
||||
|
||||
|
||||
@app.route("/login", methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
if User.auth(request.form['email'], request.form['password']):
|
||||
return redirect("/")
|
||||
else:
|
||||
flash("Email or Password invalid")
|
||||
return redirect("/login")
|
||||
else:
|
||||
return render_template('account/login.html')
|
||||
|
||||
|
||||
@app.route("/history/<name>")
|
||||
def history(name):
|
||||
history = w.get_history(name)
|
||||
return render_template('page/history.html', name=name, history=history)
|
||||
|
||||
|
||||
@app.route("/edit/<name>", methods=['GET', 'POST'])
|
||||
def edit(name):
|
||||
data = w.get_page(name)
|
||||
cname = to_canonical(name)
|
||||
if w.get_page(cname):
|
||||
# Page exists, edit instead
|
||||
return redirect("/edit/" + cname)
|
||||
if request.method == 'POST':
|
||||
w.write_page(request.form['name'], request.form['content'], message=request.form['message'], create=True)
|
||||
return redirect("/" + cname)
|
||||
else:
|
||||
return render_template('page/edit.html', name=cname, content="")
|
||||
if request.method == 'POST':
|
||||
edit_cname = to_canonical(request.form['name'])
|
||||
if edit_cname != cname:
|
||||
w.rename_page(cname, edit_cname)
|
||||
w.write_page(edit_cname, request.form['content'], message=request.form['message'])
|
||||
return redirect("/" + edit_cname)
|
||||
else:
|
||||
if data:
|
||||
name = remove_ext(data['name'])
|
||||
content = data['data']
|
||||
return render_template('page/edit.html', name=name, content=content)
|
||||
else:
|
||||
return redirect('/create/'+cname)
|
||||
|
||||
|
||||
@app.route("/<name>")
|
||||
def render(name):
|
||||
cname = to_canonical(name)
|
||||
if cname != name:
|
||||
return redirect('/' + cname)
|
||||
@app.route("/delete/<name>", methods=['POST'])
|
||||
def delete(name):
|
||||
pass
|
||||
|
||||
data = w.get_page(cname)
|
||||
if data:
|
||||
return render_template('page/page.html', name=cname, page=data)
|
||||
else:
|
||||
return redirect('/create/'+cname)
|
||||
|
||||
import ratelimit
|
||||
@app.route("/create/", methods=['GET', 'POST'])
|
||||
@app.route("/create/<name>", methods=['GET', 'POST'])
|
||||
def create(name=None):
|
||||
cname = ""
|
||||
if name:
|
||||
cname = to_canonical(name)
|
||||
if w.get_page(cname):
|
||||
# Page exists, edit instead
|
||||
return redirect("/edit/" + cname)
|
||||
if request.method == 'POST':
|
||||
w.write_page(request.form['name'], request.form['content'], message=request.form['message'], create=True)
|
||||
return redirect("/" + cname)
|
||||
else:
|
||||
return render_template('page/edit.html', name=cname, content="")
|
||||
|
||||
|
||||
@app.route("/<name>")
|
||||
def render(name):
|
||||
cname = to_canonical(name)
|
||||
if cname != name:
|
||||
return redirect('/' + cname)
|
||||
|
||||
data = w.get_page(cname)
|
||||
if data:
|
||||
return render_template('page/page.html', name=cname, page=data)
|
||||
else:
|
||||
return redirect('/create/'+cname)
|
||||
|
||||
return app
|
|
@ -1,9 +1,25 @@
|
|||
import rethinkdb as rdb
|
||||
import bcrypt
|
||||
import redis
|
||||
from flask import session
|
||||
from flask.ext.login import login_user
|
||||
from rethinkORM import RethinkModel
|
||||
from realms import conn, bcrypt
|
||||
from realms import config
|
||||
|
||||
# Default DB connection
|
||||
conn = rdb.connect(config.db['host'], config.db['port'], db=config.db['dbname'])
|
||||
|
||||
# Default Cache connection
|
||||
cache = redis.StrictRedis(host=config.cache['host'], port=config.cache['port'])
|
||||
|
||||
|
||||
def init_db():
|
||||
if not config.db['dbname'] in rdb.db_list().run(conn) and config.ENV is not 'PROD':
|
||||
# Create default db and repo
|
||||
print "Creating DB %s" % config.db['dbname']
|
||||
rdb.db_create(config.db['dbname']).run(conn)
|
||||
for tbl in ['sites', 'users', 'pages']:
|
||||
rdb.table_create(tbl).run(conn)
|
||||
|
||||
def to_dict(cur, first=False):
|
||||
ret = []
|
||||
|
@ -83,7 +99,7 @@ class User(BaseModel):
|
|||
if not data:
|
||||
return False
|
||||
|
||||
if bcrypt.check_password_hash(data['password'], password):
|
||||
if bcrypt.checkpw(password, data['password']):
|
||||
login_user(CurrentUser(data['id']))
|
||||
session['user'] = data
|
||||
return True
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import time
|
||||
from functools import update_wrapper
|
||||
from flask import request, g
|
||||
from realms import app, cache
|
||||
from realms import app
|
||||
from models import cache
|
||||
|
||||
|
||||
class RateLimit(object):
|
||||
|
|
Loading…
Reference in a new issue