Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Day 6 #2

Merged
merged 6 commits into from
Oct 19, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions www/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Configuration
"""

from transwarp.db import Dict

import config_default


def merge(defaults, override):
"""
合并override 和 default 配置文档,返回字典
"""
r = {}
for k, v in defaults.iteritems():
if k in override:
if isinstance(v, dict):
r[k] = merge(v, override[k])
else:
r[k] = override[k]
else:
r[k] = v
return r


def toDict(d):
"""
将一个字典对象转换成 一个Dict对象
"""
D = Dict()
for k, v in d.iteritems():
D[k] = toDict(v) if isinstance(v, dict) else v
return D

configs = config_default.configs



try:
import config_override
configs = merge(configs, config_override.configs)
except ImportError:
pass

configs = toDict(configs)
20 changes: 20 additions & 0 deletions www/config_default.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-


"""
Default configurations.
"""

configs = {
'db': {
'host': '127.0.0.1',
'port': 3306,
'user': 'www-data',
'password': 'www-data',
'database': 'awesome'
},
'session': {
'secret': 'AwEsOmE'
}
}
13 changes: 13 additions & 0 deletions www/config_override.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

'''
Override configurations.
'''


configs = {
'db': {
'host': '127.0.0.1'
}
}
138 changes: 138 additions & 0 deletions www/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os, re, time, base64, hashlib, logging

from transwarp.web import get, post, ctx, view, interceptor, HttpError

from apis import api, APIError, APIValueError, APIPermissionError, APIResourceNotFoundError

from models import User, Blog, Comment
from config import configs

_COOKIE_NAME = 'awesession'
_COOKIE_KEY = configs.session.secret

def make_signed_cookie(id, password, max_age):
# build cookie string by: id-expires-md5
expires = str(int(time.time() + (max_age or 86400)))
L = [id, expires, hashlib.md5('%s-%s-%s-%s' % (id, password, expires, _COOKIE_KEY)).hexdigest()]
return '-'.join(L)

def parse_signed_cookie(cookie_str):
try:
L = cookie_str.split('-')
if len(L) != 3:
return None
id, expires, md5 = L
if int(expires) < time.time():
return None
user = User.get(id)
if user is None:
return None
if md5 != hashlib.md5('%s-%s-%s-%s' % (id, user.password, expires, _COOKIE_KEY)).hexdigest():
return None
return user
except:
return None

def check_admin():
user = ctx.request.user
if user and user.admin:
return
raise APIPermissionError('No permission.')

@interceptor('/')
def user_interceptor(next):
logging.info('try to bind user from session cookie...')
user = None
cookie = ctx.request.cookies.get(_COOKIE_NAME)
if cookie:
logging.info('parse session cookie...')
user = parse_signed_cookie(cookie)
if user:
logging.info('bind user <%s> to session...' % user.email)
ctx.request.user = user
return next()

@interceptor('/manage/')
def manage_interceptor(next):
user = ctx.request.user
if user and user.admin:
return next()
raise HttpError.seeother('/signin')

@view('blogs.html')
@get('/')
def index():
blogs = Blog.find_all()
return dict(blogs=blogs, user=ctx.request.user)

@view('signin.html')
@get('/signin')
def signin():
return dict()

@get('/signout')
def signout():
ctx.response.delete_cookie(_COOKIE_NAME)
raise HttpError.seeother('/')

@api
@post('/api/authenticate')
def authenticate():
i = ctx.request.input(remember='')
email = i.email.strip().lower()
password = i.password
remember = i.remember
user = User.find_first('where email=?', email)
if user is None:
raise APIError('auth:failed', 'email', 'Invalid email.')
elif user.password != password:
raise APIError('auth:failed', 'password', 'Invalid password.')
# make session cookie:
max_age = 604800 if remember=='true' else None
cookie = make_signed_cookie(user.id, user.password, max_age)
ctx.response.set_cookie(_COOKIE_NAME, cookie, max_age=max_age)
user.password = '******'
return user

_RE_EMAIL = re.compile(r'^[a-z0-9\.\-\_]+\@[a-z0-9\-\_]+(\.[a-z0-9\-\_]+){1,4}$')
_RE_MD5 = re.compile(r'^[0-9a-f]{32}$')

@api
@post('/api/users')
def register_user():
i = ctx.request.input(name='', email='', password='')
name = i.name.strip()
email = i.email.strip().lower()
password = i.password
if not name:
raise APIValueError('name')
if not email or not _RE_EMAIL.match(email):
raise APIValueError('email')
if not password or not _RE_MD5.match(password):
raise APIValueError('password')
user = User.find_first('where email=?', email)
if user:
raise APIError('register:failed', 'email', 'Email is already in use.')
user = User(name=name, email=email, password=password, image='http://www.gravatar.com/avatar/%s?d=mm&s=120' % hashlib.md5(email).hexdigest())
user.insert()
# make session cookie:
cookie = make_signed_cookie(user.id, user.password, None)
ctx.response.set_cookie(_COOKIE_NAME, cookie)
return user

@view('register.html')
@get('/register')
def register():
return dict()

@api
@get('/api/users')
def api_get_users():
users = User.find_by('order by created_at desc')
for u in users:
u.password = '******'
return dict(users=users)
51 changes: 51 additions & 0 deletions www/wsgiapp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

__author__ = 'Michael Liao'

'''
A WSGI application entry.
'''

import logging; logging.basicConfig(level=logging.INFO)

import os, time
from datetime import datetime

from transwarp import db
from transwarp.web import WSGIApplication, Jinja2TemplateEngine

from config import configs

def datetime_filter(t):
delta = int(time.time() - t)
if delta < 60:
return u'1分钟前'
if delta < 3600:
return u'%s分钟前' % (delta // 60)
if delta < 86400:
return u'%s小时前' % (delta // 3600)
if delta < 604800:
return u'%s天前' % (delta // 86400)
dt = datetime.fromtimestamp(t)
return u'%s年%s月%s日' % (dt.year, dt.month, dt.day)

# init db:
db.create_engine(**configs.db)

# init wsgi app:
wsgi = WSGIApplication(os.path.dirname(os.path.abspath(__file__)))

template_engine = Jinja2TemplateEngine(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates'))
template_engine.add_filter('datetime', datetime_filter)

wsgi.template_engine = template_engine

import urls

wsgi.add_interceptor(urls.user_interceptor)
wsgi.add_interceptor(urls.manage_interceptor)
wsgi.add_module(urls)

if __name__ == '__main__':
wsgi.run(9000, host='0.0.0.0')