1. Alexandr Emelin
  2. tornado-skeleton

Commits

Alexandr Emelin  committed 9cf6331

fc

  • Participants
  • Branches master

Comments (0)

Files changed (11)

File .gitignore

View file
+*.pyc

File app.py

View file
+import os
+import tornado.web
+import tornado.httpserver
+import tornado.ioloop
+import tornado.options
+
+from tornado.options import define, options
+
+from backend.handlers import MainHandler
+from backend.handlers import AuthHandler
+from backend.handlers import GoogleAuthHandler
+from backend.handlers import FacebookAuthHandler
+from backend.handlers import LogoutHandler
+from backend.handlers import ProfileHandler
+from backend.handlers import Http404Handler
+
+from backend.storages import MongoSource
+from backend.storages import RedisSource
+
+
+define("port", default=8000, help="run on the given port", type=int)
+
+
+class Application(tornado.web.Application):
+
+	def __init__(self, settings):
+
+		handlers = [
+			tornado.web.url(r'/', MainHandler, name="main"),
+			tornado.web.url(r'/auth/$', AuthHandler, name="auth"),
+			tornado.web.url(r'/auth/google/$', GoogleAuthHandler, name="auth_google"),
+			tornado.web.url(r'/auth/facebook/$', FacebookAuthHandler, name="auth_facebook"),
+			tornado.web.url(r'/logout/$', LogoutHandler, name="logout"),
+			tornado.web.url(r'/profile/$', ProfileHandler, name="profile"),
+			tornado.web.url(r'.*', Http404Handler, name='http404')
+		]
+
+		tornado.web.Application.__init__(self, handlers, **settings)
+
+
+def run():
+	settings = dict(
+        cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
+        login_url="/auth/",
+        template_path=os.path.join(os.path.dirname(__file__), os.path.join("frontend", "templates")),
+        static_path=os.path.join(os.path.dirname(__file__), os.path.join("frontend", "static")),
+        xsrf_cookies=True,
+        autoescape="xhtml_escape",
+        facebook_api_key="100432673463557",
+        facebook_secret="da194f5e2fca9a4c5ca81ccd09a90580",
+        debug=True,
+        use_mongo=True,
+        use_redis=False
+	)
+	tornado.options.parse_command_line()	
+	app = Application(settings)
+	if settings.get('use_mongo', False):
+		MongoSource.setup(settings)
+	if settings.get('use_redis', False):
+		RedisSource.setup(settings)
+	server = tornado.httpserver.HTTPServer(app)
+	server.listen(options.port)
+	if settings.get('debug', False):
+		tornado.autoreload.start()
+	tornado.ioloop.IOLoop.instance().start()
+
+
+if __name__ == '__main__':
+	run()

File backend/__init__.py

Empty file added.

File backend/handlers.py

View file
+import tornado.web
+import tornado.escape
+import tornado.auth
+
+from backend.storages import MongoSource
+from backend.storages import RedisSource
+
+
+class BaseHandler(tornado.web.RequestHandler, MongoSource, RedisSource):
+
+    def get_current_user(self):
+        user_json = self.get_secure_cookie("user")
+        if not user_json:
+        	return None
+        return tornado.escape.json_decode(user_json)
+
+
+class AuthHandler(BaseHandler):
+
+    def get(self):
+        if self.get_current_user():
+            self.write('already authenticated')
+        else:
+            self.render("accounts/auth.html")
+
+
+class FacebookAuthHandler(BaseHandler, tornado.auth.FacebookGraphMixin):
+
+    @tornado.web.asynchronous
+    def get(self):
+        url = self.request.protocol + "://" + self.request.host + self.reverse_url("auth_facebook")
+        if self.get_argument("code", False):
+            self.get_authenticated_user(
+                redirect_uri=url,
+                client_id=self.settings["facebook_api_key"],
+                client_secret=self.settings["facebook_secret"],
+                code=self.get_argument("code"),
+                callback=self.async_callback(self._on_auth)
+            )
+            return
+        return self.authorize_redirect(redirect_uri=url,
+                          client_id=self.settings["facebook_api_key"],
+                          extra_params={"scope": "read_stream,offline_access"})
+
+    def _on_auth(self, user):
+        if not user:
+            raise tornado.web.HTTPError(500, "Facebook auth failed")
+        user["auth"] = "facebook"
+        print user
+        self.set_secure_cookie("user", tornado.escape.json_encode(user))
+        self.redirect(self.reverse_url("main"))
+
+
+class GoogleAuthHandler(BaseHandler, tornado.auth.GoogleMixin):
+
+    @tornado.web.asynchronous
+    def get(self):
+        if self.get_argument("openid.mode", None):
+            self.get_authenticated_user(self.async_callback(self._on_auth))
+            return
+        self.authenticate_redirect()
+
+    def _on_auth(self, user):
+        if not user:
+            raise tornado.web.HTTPError(500, "Google auth failed")
+        user["auth"] = "google"
+        self.set_secure_cookie("user", tornado.escape.json_encode(user))
+        self.redirect(self.reverse_url("main"))
+
+
+class LogoutHandler(BaseHandler):
+
+    def get(self):
+        self.clear_cookie("user")
+        self.write('Logged out. <a href="%s">Log back in</a>.' % self.reverse_url("auth"))
+
+
+class MainHandler(BaseHandler):
+
+	@tornado.web.authenticated
+	def get(self):
+		res = self.mongo_src.things.find()
+		self.render("base.html")
+
+
+class ProfileHandler(BaseHandler):
+
+    @tornado.web.authenticated
+    def get(self):
+        user = self.get_current_user()
+        self.render("accounts/profile.html", user=user)
+
+
+class Http404Handler(BaseHandler):
+
+    def get(self):
+        self.render("http404.html")

File backend/storages.py

View file
+import pymongo
+import redis
+
+
+class MongoSource(object):
+
+	mongo_src = None
+
+	@classmethod
+	def setup(cls, settings):
+		connection = pymongo.MongoClient("localhost", 27017)
+		cls.mongo_src = connection.test
+
+
+class RedisSource(object):
+
+	redis_src = None
+
+	@classmethod
+	def setup(cls, settings):
+		cls.redis_src = redis.Redis()
+

File frontend/templates/accounts/auth.html

View file
+{% extends '../base.html' %}
+
+{% block content %}
+  <div>
+  	<a href="{{handler.reverse_url("auth_google")}}">{{_("Google")}}</a>
+  	<a href="{{handler.reverse_url("auth_facebook")}}">{{_("Facebook")}}</a>
+  </div>
+{% end %}

File frontend/templates/accounts/profile.html

View file
+{% extends '../base.html' %}
+
+{% block content %}
+  <div>{{_("First Name")}}:&nbsp;{{user.get('first_name', 'Unknown')}}</div>
+  <div>{{_("Last Name")}}:&nbsp;{{user.get('last_name', 'Unknown')}}</div>
+  <div>{{_("Email")}}:&nbsp;{{user.get('email', 'Unknown')}}</div>
+
+  <div>
+  	<a href="{{handler.reverse_url("main")}}">{{_("main page")}}</a>
+  	<a href="{{handler.reverse_url("logout")}}">{{_("logout")}}</a>
+  </div>
+{% end %}

File frontend/templates/base.html

View file
+<!DOCTYPE html>
+<html>
+<head>
+  <title>{% block title %}{% end %}</title>
+  <meta name="description" content=""/>
+  <meta name="author" content=""/>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+  {% include 'staticbase.html' %}
+  {% block extra_head %}{% end %}
+</head>
+<body>
+  <div id="body">
+    {% block body %}
+      <div id="content">
+        {% block content %}
+          {{_("It works!")}}
+        {% end %}
+      </div>
+    {% end %}
+  </div>
+</body>
+</html>

File frontend/templates/http404.html

View file
+{% extends 'base.html' %}
+
+{% block content %}
+	{{_("No such page")}}
+{% end %}

File frontend/templates/staticbase.html

View file
+<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
+<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.2/css/bootstrap-combined.min.css" rel="stylesheet">
+<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.2/js/bootstrap.min.js"></script>

File requirements.txt

View file
+pymongo==2.4.1
+redis==2.7.2
+tornado==2.4.1
+wsgiref==0.1.2