Permalink
Browse files

Add Login/Logout/Signup support

Signed-off-by: Wenwei Cai <stanley.w.cai@gmail.com>
  • Loading branch information...
1 parent 1aa4fd3 commit 7356f32204e404ce84dfe05d1e9cc37326863b76 @swcai committed May 3, 2011
Showing with 221 additions and 36 deletions.
  1. +3 −0 README
  2. BIN bookmarks.db
  3. +132 −36 bookmarks.py
  4. +14 −0 database.py
  5. BIN database.pyc
  6. +9 −0 scheme.sql
  7. +45 −0 templates/login_or_signup.html
  8. +18 −0 templates/main.html
View
@@ -0,0 +1,3 @@
+My exercise for REST API - a clone of instapaper
+
+May 4 - Add login/logout/signup support
View
Binary file not shown.
View
@@ -1,4 +1,19 @@
#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2011 Stanley Cai
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
import json
import time
@@ -19,11 +34,15 @@
BOOKMARKS_DB = database.Connection("bookmarks.db")
class Application(tornado.web.Application):
+
def __init__(self):
handlers = [
- (r"/", RootHandler),
+ (r"/", MainHandler),
(r"/bookmarks/([0-9]*)", BookmarkHandler),
(r"/upload", UploadHandler),
+ (r"/auth/login", AuthLoginHandler),
+ (r"/auth/logout", AuthLogoutHandler),
+ (r"/auth/signup", AuthSignupHandler),
]
settings = dict(
cookie_secret="32oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=", # todo update cookie_secret
@@ -35,88 +54,165 @@ def __init__(self):
tornado.web.Application.__init__(self, handlers, **settings)
class BaseHandler(tornado.web.RequestHandler):
+ def is_valid_username(self, name):
+ return True
+
+ def is_valid_email(self, email):
+ return True
+
+ def is_valid_password(self, password):
+ return True
+
def get_current_user(self):
- user_json = self.get_secure_cookie("user")
- if not user_json:
+ user_id = self.get_secure_cookie("user")
+ if not user_id:
return None
- return tornado.escape.json_decode(user_json)
+ else:
+ return BOOKMARKS_DB.get("SELECT * FROM users WHERE id = %d" % int(user_id))
+
+class AuthSignupHandler(BaseHandler):
+ def post(self):
+ username = self.get_argument("username")
+ email = self.get_argument("email")
+ password = self.get_argument("password")
-class RootHandler(BaseHandler):
+ if not self.is_valid_username(username) or \
+ not self.is_valid_email(email) or \
+ not self.is_valid_password(password):
+ self.render("login_or_signup.html", error_message = "Invalid email or user name")
+ else:
+ user = BOOKMARKS_DB.get("SELECT * FROM users WHERE email=\"%s\"" %
+ email)
+ if not user:
+ BOOKMARKS_DB.execute( "INSERT INTO users (username, email, password) \
+ VALUES (\"%s\", \"%s\", \"%s\")" % (username, email, password))
+ user = BOOKMARKS_DB.get("SELECT * FROM users WHERE email=\"%s\"" %
+ email)
+ BOOKMARKS_DB.commit()
+ self.set_secure_cookie("user", str(user.id))
+ self.redirect("/")
+ else:
+ self.render("login_or_signup.html", error_message = "This email is registered.")
+
def get(self):
- entries = []
- results = BOOKMARKS_DB.query('SELECT * FROM bookmarks')
- for record in results:
- entries.append((record.title, record.url, record.last_modified))
- self.render("view.html", entries = entries)
+ user = self.get_current_user()
+ if user:
+ self.clear_secure_cookie("user")
+ self.render("login_or_signup.html")
+
+class AuthLoginHandler(BaseHandler):
+ def post(self):
+ username_or_email = self.get_argument("username_or_email")
+ password = self.get_argument("password")
+ if not self.is_valid_username(username_or_email) and \
+ not self.is_valid_email(username_or_email):
+ self.render("login_or_signup.html", error_message = "Invalid email or username")
+ else:
+ user = BOOKMARKS_DB.get("SELECT * FROM users WHERE email=\"%s\" and password=\"%s\"" %
+ (username_or_email, password))
+ if not user:
+ user = BOOKMARKS_DB.get("SELECT * FROM users WHERE username=\"%s\" and password=\"%s\"" %
+ (username_or_email, password))
+ if not user:
+ self.render("login_or_signup.html", error_message = "No such user")
+ return
+ self.set_secure_cookie("user", str(user.id))
+ self.redirect("/")
+
+ def get(self):
+ user = self.get_current_user()
+ if user:
+ self.clear_secure_cookie("user")
+ self.render("login_or_signup.html") # need render this page?
+
+class AuthLogoutHandler(BaseHandler):
+ def get(self):
+ user = self.get_current_user()
+ if user:
+ self.clear_cookie("user")
+ self.redirect("/")
+
+class MainHandler(BaseHandler):
+ def get(self):
+ user = self.get_current_user()
+ if not user:
+ self.render("login_or_signup.html")
+ else:
+ entries = []
+ results = BOOKMARKS_DB.query("SELECT * FROM bookmarks where user_id=\"%s\"" % user.id)
+ for record in results:
+ entries.append((record.title, record.url, record.last_modified))
+ self.render("main.html", entries = entries)
class UploadHandler(BaseHandler):
def get(self):
- title = self.get_argument('title')
- url = self.get_argument('url')
- date = int(time.time())
- record = BOOKMARKS_DB.get('SELECT * FROM bookmarks WHERE title=\"%s\" and url=\"%s\"' % (title, url))
- if record is None:
- BOOKMARKS_DB.execute('INSERT INTO bookmarks (title, url, last_modified) VALUES (\"%s\", \"%s\", \"%d\")' % (title, url, date))
- self.write("Updated")
+ user = self.get_current_user()
+ if not user:
+ self.render("login_or_signup.html")
else:
- BOOKMARKS_DB.execute('UPDATE bookmarks SET last_modified=\"%d\" WHERE title=\"%s\" and url=\"%s\"' % (date, title, url))
- self.write("Saved")
- BOOKMARKS_DB.commit()
+ title = self.get_argument('title')
+ url = self.get_argument('url')
+ date = int(time.time())
+ record = BOOKMARKS_DB.get('SELECT * FROM bookmarks WHERE title=\"%s\" and url=\"%s\"' % (title, url))
+ if record is None:
+ BOOKMARKS_DB.execute('INSERT INTO bookmarks (title, url, last_modified) VALUES (\"%s\", \"%s\", \"%d\")' % (title, url, date))
+ self.write("Updated")
+ else:
+ BOOKMARKS_DB.execute('UPDATE bookmarks SET last_modified=\"%d\" WHERE title=\"%s\" and url=\"%s\"' % (date, title, url))
+ self.write("Saved")
+ BOOKMARKS_DB.commit()
class BookmarkHandler(BaseHandler):
- def get(self, bookmark_id):
+ ''' REST style API...No test at all.'''
+ def get(self, user_id, bookmark_id):
try:
index = int(bookmark_id)
record = BOOKMARKS_DB.get('SELECT * FROM bookmarks WHERE id=\"%d\"' % index)
if record is None:
raise Exception()
- # could add "auto-remove" function
-
- self.redirect(record.url)
+ self.write(json.dumps((record.title, record.url, record.last_modified)))
except ValueError:
results = []
records = BOOKMARKS_DB.query('SELECT * FROM bookmarks')
for record in records:
results.append((record.title, record.url, record.last_modified))
self.write(json.dumps(results))
- except:
- self.write("Invalid bookmark_id")
- def put(self, bookmark_id):
+ def put(self, user_id, bookmark_id):
title, url = json.loads(self.request.body)
date = int(time.time())
try:
index = int(bookmark_id)
record = BOOKMARKS_DB.get('SELECT * FROM bookmarks WHERE id=\"%d\"' % index)
- if record is None:
- raise ValueError()
- BOOKMARKS_DB.execute('UPDATE bookmarks SET title=\"%s\", url=\"%s\", last_modified=\"%d\" WHERE id = \"%d\"' % (title, url, date, index))
+ if record is not None:
+ BOOKMARKS_DB.execute('UPDATE bookmarks SET title=\"%s\", url=\"%s\", last_modified=\"%d\" WHERE id = \"%d\"' % (title, url, date, index))
except ValueError:
BOOKMARKS_DB.execute('INSERT INTO bookmarks (title, url, last_modified) VALUES (\"%s\", \"%s\", \"%d\")' % (title, url, date))
finally:
BOOKMARKS_DB.commit()
-
- def delete(self, bookmark_id):
+
+ def delete(self, user_id, bookmark_id):
try:
index = int(bookmark_id)
BOOKMARKS_DB.execute('DELETE FROM bookmarks WHERE id=\"%d\"' % index)
BOOKMARKS_DB.commit()
except ValueError:
self.write("Invalid bookmark_id")
- def post(self, bookmark_id):
- self.write("Not support yet")
-
+
class EntryModule(tornado.web.UIModule):
+
def render(self, entry, isAdmin=False):
return self.render_string("modules/entry.html", entry=entry, isAdmin=isAdmin)
-
+
+
def main():
tornado.options.parse_command_line()
http_server = tornado.httpserver.HTTPServer(Application())
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
+
if __name__ == "__main__":
main()
View
@@ -1,6 +1,20 @@
#!/usr/bin/env python
#
# Modified and simplified from Tornado project
+#
+# Copyright 2011 Facebook
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
"""A lightweight wrapper around sqlite3."""
View
Binary file not shown.
View
@@ -1,7 +1,16 @@
+DROP TABLE IF EXISTS users;
+CREATE TABLE users (
+ id integer NOT NULL PRIMARY KEY AUTOINCREMENT,
+ email text NOT NULL,
+ username text NOT NULL,
+ password text NOT NULL
+);
+
DROP TABLE IF EXISTS bookmarks;
CREATE TABLE bookmarks (
id integer NOT NULL PRIMARY KEY AUTOINCREMENT,
title text NOT NULL,
url text NOT NULL,
+ user_id integer NOT NULL,
last_modified long NOT NULL
);
@@ -0,0 +1,45 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+ <meta http-equiv="content-type" content="text/html;charset=iso-8859-1" />
+ <title>Bookmarks</title>
+</head>
+
+<body class="login" onLoad="document.forms[0].elements[0].focus();">
+<div class="Container">
+ <div id="Dialog">
+ <form action="/auth/login" method="post">
+ <dl>
+ <dt>Username or Email:</dt>
+ <dd><input name="username_or_email" type="username" value="" /></dd>
+ <dt>Password:</dt>
+ <dd>
+ <input id="password" name="password" type="password" />
+
+ </dd>
+ </dl>
+ <input type="submit" value="Login" class="submit"/>
+ </form>
+ <form action="/auth/signup" method="post">
+ <dl>
+ <dt>Username:</dt>
+ <dd><input id="username" name="username" type="username" value="" /></dd>
+ <dt>Email:</dt>
+ <dd><input id="email" name="email" type="email" value="" /></dd>
+
+ <dt>Password:</dt>
+ <dd>
+ <input id="password" name="password" type="password" />
+
+ </dd>
+ <input type="submit" value="Sign Up" class="submit"/>
+ </form>
+
+ </div>
+</div>
+
+</body>
+</html>
View
@@ -0,0 +1,18 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <title>My bookmarks</title>
+ <link rel="stylesheet" href="{{ static_url("bookmark.css") }}" type="text/css"/>
+ {% block head %}{% end %}
+ </head>
+ <body>
+ <div class="body">
+ <div class="header">
+ <h1>Bookmarks</h1>
+ </div>
+ <div class="content">{% block body %}{% end %}</div>
+ </div>
+ {% block bottom %}{% end %}
+ </body>
+</html>

0 comments on commit 7356f32

Please sign in to comment.