yolo
This commit is contained in:
parent
99ce4acd00
commit
98c753aba6
|
@ -1,13 +1,20 @@
|
|||
from gevent import monkey, wsgi
|
||||
from gevent import wsgi
|
||||
from realms import config, app, manager
|
||||
|
||||
monkey.patch_all()
|
||||
|
||||
|
||||
@manager.command
|
||||
def server(port=10000):
|
||||
print "Server started (%s)" % config.ENV
|
||||
wsgi.WSGIServer(('', int(port)), app).serve_forever()
|
||||
|
||||
|
||||
@manager.command
|
||||
def init_db():
|
||||
from realms import db
|
||||
import realms.models
|
||||
print "Init DB"
|
||||
db.drop_all()
|
||||
db.create_all()
|
||||
|
||||
if __name__ == '__main__':
|
||||
manager.run()
|
|
@ -1,32 +1,46 @@
|
|||
# Monkey patch stdlib.
|
||||
import gevent.monkey
|
||||
gevent.monkey.patch_all(aggressive=False)
|
||||
|
||||
# Set default encoding to UTF-8
|
||||
import sys
|
||||
|
||||
reload(sys)
|
||||
# noinspection PyUnresolvedReferences
|
||||
sys.setdefaultencoding('utf-8')
|
||||
|
||||
# Silence Sentry and Requests.
|
||||
import logging
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
logging.getLogger('raven').setLevel(logging.WARNING)
|
||||
logging.getLogger('requests').setLevel(logging.WARNING)
|
||||
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
|
||||
from flask import Flask, request, render_template, url_for, redirect, session, g
|
||||
import httplib
|
||||
import traceback
|
||||
from flask import Flask, request, render_template, url_for, redirect, session, flash, g
|
||||
from flask.ctx import _AppCtxGlobals
|
||||
from flask.ext.script import Manager
|
||||
from flask.ext.login import LoginManager, login_required
|
||||
from flask.ext.assets import Environment, Bundle
|
||||
from werkzeug.routing import BaseConverter
|
||||
from werkzeug.utils import cached_property
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
from realms import config
|
||||
from realms.lib.services import db
|
||||
from realms.lib.ratelimit import get_view_rate_limit, ratelimiter
|
||||
from realms.lib.session import RedisSessionInterface
|
||||
from realms.lib.wiki import Wiki
|
||||
from realms.lib.util import to_canonical, remove_ext, mkdir_safe, gravatar_url
|
||||
from realms.lib.services import db
|
||||
from realms.models import User, CurrentUser
|
||||
from realms.lib.util import to_canonical, remove_ext, mkdir_safe, gravatar_url, to_dict
|
||||
from realms.models import User, CurrentUser, Site
|
||||
|
||||
|
||||
sites = {}
|
||||
|
||||
|
||||
class Site(object):
|
||||
wiki = None
|
||||
|
||||
|
||||
class AppCtxGlobals(_AppCtxGlobals):
|
||||
|
||||
@cached_property
|
||||
|
@ -40,7 +54,7 @@ class AppCtxGlobals(_AppCtxGlobals):
|
|||
return False
|
||||
|
||||
if not sites.get(subdomain):
|
||||
sites[subdomain] = Site()
|
||||
sites[subdomain] = to_dict(Site.get_by_name(subdomain))
|
||||
sites[subdomain].wiki = Wiki("%s/%s" % (config.REPO_DIR, subdomain))
|
||||
|
||||
return sites[subdomain]
|
||||
|
@ -149,9 +163,11 @@ app.url_map.converters['regex'] = RegexConverter
|
|||
app.url_map.strict_slashes = False
|
||||
app.debug = True
|
||||
|
||||
# Flask-SQLAlchemy
|
||||
db.init_app(app)
|
||||
|
||||
manager = Manager(app)
|
||||
|
||||
# Flask extension objects
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = 'auth.login'
|
||||
|
@ -161,6 +177,38 @@ login_manager.login_view = 'auth.login'
|
|||
def load_user(user_id):
|
||||
return CurrentUser(user_id)
|
||||
|
||||
|
||||
def error_handler(e):
|
||||
try:
|
||||
if isinstance(e, HTTPException):
|
||||
status_code = e.code
|
||||
message = e.description if e.description != type(e).description else None
|
||||
tb = None
|
||||
else:
|
||||
status_code = httplib.INTERNAL_SERVER_ERROR
|
||||
message = None
|
||||
tb = traceback.format_exc() if g.current_user.staff else None
|
||||
|
||||
if request.is_xhr or request.accept_mimetypes.best in ['application/json', 'text/javascript']:
|
||||
response = {
|
||||
'message': message,
|
||||
'traceback': tb,
|
||||
}
|
||||
else:
|
||||
response = render_template('errors/error.html',
|
||||
title=httplib.responses[status_code],
|
||||
status_code=status_code,
|
||||
message=message,
|
||||
traceback=tb)
|
||||
except HTTPException as e2:
|
||||
return error_handler(e2)
|
||||
|
||||
return response, status_code
|
||||
|
||||
for status_code in httplib.responses:
|
||||
if status_code >= 400:
|
||||
app.register_error_handler(status_code, error_handler)
|
||||
|
||||
assets = Environment()
|
||||
assets.init_app(app)
|
||||
if config.ENV is 'PROD':
|
||||
|
@ -221,16 +269,25 @@ def page_not_found(e):
|
|||
return render_template('errors/404.html'), 404
|
||||
|
||||
|
||||
@app.errorhandler(500)
|
||||
def page_error(e):
|
||||
logging.exception(e)
|
||||
return render_template('errors/500.html'), 500
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def root():
|
||||
return redirect(url_for(config.ROOT_ENDPOINT))
|
||||
|
||||
@app.route("/new/", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def new():
|
||||
if request.method == 'POST':
|
||||
site_name = to_canonical(request.form['name'])
|
||||
|
||||
if Site.get_by_name(site_name):
|
||||
flash("Site already exists")
|
||||
return redirect(redirect_url())
|
||||
else:
|
||||
Site.create(name=site_name, founder=g.current_user.id)
|
||||
return redirect('http://%s.%s' % (site_name, config.HOSTNAME))
|
||||
else:
|
||||
return render_template('wiki/new.html')
|
||||
|
||||
|
||||
@app.route("/_account/")
|
||||
@login_required
|
||||
|
@ -240,5 +297,3 @@ def account():
|
|||
if 'devserver' not in sys.argv or os.environ.get('WERKZEUG_RUN_MAIN'):
|
||||
app.discover()
|
||||
|
||||
print app.url_map
|
||||
|
||||
|
|
|
@ -2,10 +2,9 @@ import socket
|
|||
|
||||
HOSTNAME = socket.gethostname()
|
||||
|
||||
DOMAIN = 'realms.dev'
|
||||
ENV = 'DEV'
|
||||
|
||||
DB_URI = 'postgresql://realms:dbpassword@localhost:5432/realms'
|
||||
SQLALCHEMY_DATABASE_URI = 'postgresql://deploy:dbpassword@localhost:5432/realms'
|
||||
|
||||
REDIS_HOST = '127.0.0.1'
|
||||
REDIS_PORT = 6379
|
||||
|
@ -17,7 +16,6 @@ REPO_MAIN_NAME = '_'
|
|||
REPO_FORBIDDEN_NAMES = ['api', 'www']
|
||||
REPO_ENABLE_SUBDOMAIN = True
|
||||
|
||||
|
||||
RECAPTCHA_PUBLIC_KEY = '6LfoxeESAAAAAGNaeWnISh0GTgDk0fBnr6Bo2Tfk'
|
||||
RECAPTCHA_PRIVATE_KEY = '6LfoxeESAAAAABFzdCs0hNIIyeb42mofV-Ndd2_2'
|
||||
RECAPTCHA_OPTIONS = {'theme': 'clean'}
|
||||
|
@ -31,10 +29,12 @@ MODULES = [
|
|||
]
|
||||
|
||||
if ENV is 'PROD':
|
||||
SERVER_NAME = 'realms.io'
|
||||
#SERVER_NAME = 'realms.io'
|
||||
PORT = 80
|
||||
DOMAIN = 'realms.io'
|
||||
else:
|
||||
DEBUG = True
|
||||
ASSETS_DEBUG = True
|
||||
SERVER_NAME = 'realms.dev:8000'
|
||||
#SERVER_NAME = 'realms.dev:8000'
|
||||
DOMAIN = 'realms.dev'
|
||||
PORT = 8000
|
|
@ -1,10 +1,8 @@
|
|||
import redis
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
# Default DB connection
|
||||
from flask.ext.sqlalchemy import SQLAlchemy
|
||||
from realms import config
|
||||
|
||||
db = create_engine(config.DB_URI, encoding='utf8', echo=True)
|
||||
db = SQLAlchemy()
|
||||
|
||||
# Default Cache connection
|
||||
cache = redis.StrictRedis(host=config.REDIS_HOST, port=config.REDIS_PORT)
|
|
@ -10,6 +10,34 @@ from realms import config
|
|||
from realms.lib.services import cache
|
||||
|
||||
|
||||
class AttrDict(dict):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AttrDict, self).__init__(*args, **kwargs)
|
||||
self.__dict__ = self
|
||||
|
||||
|
||||
def to_json(data):
|
||||
return json.dumps(to_dict(data), separators=(',', ':'))
|
||||
|
||||
|
||||
def to_dict(data):
|
||||
|
||||
if not data:
|
||||
return AttrDict()
|
||||
|
||||
def row2dict(row):
|
||||
d = AttrDict()
|
||||
for column in row.__table__.columns:
|
||||
d[column.name] = getattr(row, column.name)
|
||||
|
||||
return d
|
||||
|
||||
if isinstance(data, list):
|
||||
return [row2dict(x) for x in data]
|
||||
else:
|
||||
return row2dict(data)
|
||||
|
||||
|
||||
def cache_it(fn):
|
||||
def wrap(*args, **kw):
|
||||
key = "%s:%s" % (args[0].table, args[1])
|
||||
|
@ -40,26 +68,6 @@ def cache_it(fn):
|
|||
return wrap
|
||||
|
||||
|
||||
def to_json(res, first=False):
|
||||
"""
|
||||
Jsonify query result.
|
||||
"""
|
||||
res = to_dict(res, first)
|
||||
return json.dumps(res, separators=(',', ':'))
|
||||
|
||||
|
||||
def to_dict(cur, first=False):
|
||||
if not cur:
|
||||
return None
|
||||
ret = []
|
||||
for row in cur:
|
||||
ret.append(row)
|
||||
if ret and first:
|
||||
return ret[0]
|
||||
else:
|
||||
return ret
|
||||
|
||||
|
||||
def validate_captcha():
|
||||
response = captcha.submit(
|
||||
request.form['recaptcha_challenge_field'],
|
||||
|
|
|
@ -63,11 +63,6 @@ class Wiki():
|
|||
|
||||
self.path = path
|
||||
|
||||
@staticmethod
|
||||
def is_registered(name):
|
||||
s = Site()
|
||||
return True if s.get_by_name(name) else False
|
||||
|
||||
def revert_page(self, name, commit_sha, message, username):
|
||||
page = self.get_page(name, commit_sha)
|
||||
if not page:
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
import bcrypt
|
||||
from sqlalchemy import Column, Integer, String, Time
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from flask import session, flash
|
||||
from flask.ext.login import login_user, logout_user
|
||||
from realms.lib.util import gravatar_url, to_dict
|
||||
from realms.lib.services import db
|
||||
from realms.lib.util import gravatar_url, to_dict
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
class ModelMixin(object):
|
||||
def __getitem__(self, k):
|
||||
return self.__getattribute__(k)
|
||||
|
||||
@classmethod
|
||||
def create(cls, **kwargs):
|
||||
obj = cls(**kwargs)
|
||||
db.session.add(obj)
|
||||
db.session.commit()
|
||||
return obj
|
||||
|
||||
|
||||
class CurrentUser():
|
||||
|
@ -15,8 +24,7 @@ class CurrentUser():
|
|||
def __init__(self, id):
|
||||
self.id = id
|
||||
if id:
|
||||
user = User()
|
||||
session['user'] = user.get_by_id(id)
|
||||
session['user'] = to_dict(User.query.filter_by(id=id).first())
|
||||
|
||||
def get_id(self):
|
||||
return self.id
|
||||
|
@ -38,28 +46,38 @@ class CurrentUser():
|
|||
return None
|
||||
|
||||
|
||||
class Site(Base):
|
||||
class Site(ModelMixin, db.Model):
|
||||
__tablename__ = 'sites'
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String(100))
|
||||
pages = Column(Integer)
|
||||
views = Column(Integer)
|
||||
created = Column(Time)
|
||||
founder = Column(Integer)
|
||||
created_at = Column(Time)
|
||||
updated_at = Column(Time)
|
||||
|
||||
@classmethod
|
||||
def get_by_name(cls, name):
|
||||
return Site.query.filter_by(name=name).first()
|
||||
|
||||
|
||||
class User(Base):
|
||||
class User(db.Model, ModelMixin):
|
||||
__tablename__ = 'users'
|
||||
id = Column(Integer, primary_key=True)
|
||||
username = Column(String(100))
|
||||
email = Column(String(255))
|
||||
avatar = Column(String(255))
|
||||
password = Column(String(255))
|
||||
joined = Column(Time)
|
||||
created_at = Column(Time)
|
||||
updated_at = Column(Time)
|
||||
|
||||
def get_by_email(self, email):
|
||||
return to_dict(self.get_one(email, 'email'), True)
|
||||
@classmethod
|
||||
def get_by_email(cls, email):
|
||||
return User.query.filter_by(email=email).first()
|
||||
|
||||
def get_by_username(self, username):
|
||||
return to_dict(self.get_one(username, 'username'), True)
|
||||
@classmethod
|
||||
def get_by_username(cls, username):
|
||||
return User.query.filter_by(username=username).first()
|
||||
|
||||
def login(self, login, password):
|
||||
pass
|
||||
|
@ -84,6 +102,7 @@ class User(Base):
|
|||
if user.get_by_email(email):
|
||||
flash('Email is already taken')
|
||||
return False
|
||||
|
||||
if user.get_by_username(username):
|
||||
flash('Username is already taken')
|
||||
return False
|
||||
|
@ -93,7 +112,6 @@ class User(Base):
|
|||
username=username,
|
||||
password=bcrypt.hashpw(password, bcrypt.gensalt(10)),
|
||||
avatar=gravatar_url(email))
|
||||
|
||||
User.login(u.id)
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -1,30 +1,10 @@
|
|||
from flask import g, render_template, request, redirect, Blueprint, flash, url_for
|
||||
from flask.ext.login import login_required
|
||||
from realms import redirect_url, config
|
||||
from realms.lib.util import to_canonical, remove_ext
|
||||
from realms.lib.wiki import Wiki
|
||||
from realms.models import Site
|
||||
|
||||
blueprint = Blueprint('wiki', __name__, url_prefix='/wiki')
|
||||
|
||||
|
||||
@blueprint.route("/new/", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def new():
|
||||
if request.method == 'POST':
|
||||
wiki_name = to_canonical(request.form['name'])
|
||||
|
||||
if Wiki.is_registered(wiki_name):
|
||||
flash("Site already exists")
|
||||
return redirect(redirect_url())
|
||||
else:
|
||||
s = Site()
|
||||
s.create(name=wiki_name, repo=wiki_name, founder=g.current_user.get('id'))
|
||||
return redirect('http://%s.%s' % (wiki_name, config.HOSTNAME))
|
||||
else:
|
||||
return render_template('wiki/new.html')
|
||||
|
||||
|
||||
@blueprint.route("/_commit/<sha>/<name>")
|
||||
def commit(name, sha):
|
||||
cname = to_canonical(name)
|
||||
|
|
17
realms/templates/errors/error.html
Normal file
17
realms/templates/errors/error.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block title %}{{ title|escape }}{% endblock %}
|
||||
{% block scripts %}
|
||||
{% if traceback %}
|
||||
<link rel="stylesheet" href="{{ static_url('vendor/highlightjs/default.css') }}" />
|
||||
<script src="{{ static_url('vendor/highlightjs/highlight.pack.js') }}"></script>
|
||||
<script>hljs.initHighlightingOnLoad();</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-header"><h1>{{ title }} <small>{{ status_code }}</small></h1></div>
|
||||
{% if message %}<div class="alert alert-error">{{ message|escape }}</div>{% endif %}
|
||||
{% if traceback %}<pre><code>{{ traceback|escape }}</code></pre>{% endif %}
|
||||
<a href="{{ url_for('home.index') }}" class="btn btn-danger btn-large">← Back to Safety</a>
|
||||
<hr />
|
||||
<div class="help-block">Think you know what happened? Please <a href="mailto:support@realms.io">email</a> us.</div>
|
||||
{% endblock %}
|
|
@ -48,9 +48,6 @@
|
|||
<li><a href="{{ url_for('wiki.edit', name=name) }}">Edit Page</a></li>
|
||||
<li><a href="{{ url_for('wiki.history', name=name) }}">History</a></li>
|
||||
{% endif %}
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-header">Site Options</li>
|
||||
<li><a href="{{ url_for('wiki.new') }}">Create New Site</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ Flask-Assets==0.8
|
|||
Flask-Bcrypt==0.5.2
|
||||
Flask-Login==0.2.7
|
||||
Flask-Script==0.6.3
|
||||
Flask-SQLAlchemy==1.0
|
||||
beautifulsoup4==4.3.2
|
||||
boto==2.17.0
|
||||
closure==20121212
|
||||
|
@ -16,7 +17,6 @@ pyzmq==14.0.0
|
|||
recaptcha==1.0rc1
|
||||
recaptcha-client==1.0.6
|
||||
redis==2.8.0
|
||||
rethinkdb==1.10.0-0
|
||||
simplejson==3.3.1
|
||||
sockjs-tornado==1.0.0
|
||||
supervisor==3.0
|
||||
|
|
Loading…
Reference in a new issue