312 lines
9.5 KiB
Python
312 lines
9.5 KiB
Python
# python3 by 121
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
' url handlers '
|
|
|
|
import re, time, json, logging, hashlib, base64, asyncio
|
|
|
|
import markdown2
|
|
|
|
from aiohttp import web
|
|
|
|
from coroweb import get, post
|
|
from apis import Page, APIValueError, APIResourceNotFoundError, APIPermissionError, APIError
|
|
|
|
from models import User, Comment, Blog, next_id
|
|
from config import configs
|
|
|
|
COOKIE_NAME = 'awesession'
|
|
_COOKIE_KEY = configs.session.secret
|
|
|
|
def check_admin(request):
|
|
if request.__user__ is None or not request.__user__.admin:
|
|
raise APIPermissionError()
|
|
|
|
def get_page_index(page_str):
|
|
p = 1
|
|
try:
|
|
p = int(page_str)
|
|
except ValueError as e:
|
|
pass
|
|
if p < 1:
|
|
p = 1
|
|
return p
|
|
|
|
def user2cookie(user, max_age):
|
|
'''
|
|
Generate cookie str by user.
|
|
'''
|
|
# build cookie string by: id-expires-sha1
|
|
expires = str(int(time.time() + max_age))
|
|
s = '%s-%s-%s-%s' % (user.id, user.passwd, expires, _COOKIE_KEY)
|
|
L = [user.id, expires, hashlib.sha1(s.encode('utf-8')).hexdigest()]
|
|
return '-'.join(L)
|
|
|
|
def text2html(text):
|
|
lines = map(lambda s: '<p>%s</p>' % s.replace('&', '&').replace('<', '<').replace('>', '>'), filter(lambda s: s.strip() != '', text.split('\n')))
|
|
return ''.join(lines)
|
|
|
|
@asyncio.coroutine
|
|
def cookie2user(cookie_str):
|
|
'''
|
|
Parse cookie and load user if cookie is valid.
|
|
'''
|
|
if not cookie_str:
|
|
return None
|
|
try:
|
|
L = cookie_str.split('-')
|
|
if len(L) != 3:
|
|
return None
|
|
uid, expires, sha1 = L
|
|
if int(expires) < time.time():
|
|
return None
|
|
user = yield from User.find(uid)
|
|
if user is None:
|
|
return None
|
|
s = '%s-%s-%s-%s' % (uid, user.passwd, expires, _COOKIE_KEY)
|
|
if sha1 != hashlib.sha1(s.encode('utf-8')).hexdigest():
|
|
logging.info('invalid sha1')
|
|
return None
|
|
user.passwd = '******'
|
|
return user
|
|
except Exception as e:
|
|
logging.exception(e)
|
|
return None
|
|
|
|
@get('/')
|
|
def index(*, page='1'):
|
|
page_index = get_page_index(page)
|
|
num = yield from Blog.findNumber('count(id)')
|
|
page = Page(num)
|
|
if num == 0:
|
|
blogs = []
|
|
else:
|
|
blogs = yield from Blog.findAll(orderBy='created_at desc', limit=(page.offset, page.limit))
|
|
return {
|
|
'__template__': 'blogs.html',
|
|
'page': page,
|
|
'blogs': blogs
|
|
}
|
|
|
|
@get('/blog/{id}')
|
|
def get_blog(id):
|
|
blog = yield from Blog.find(id)
|
|
comments = yield from Comment.findAll('blog_id=?', [id], orderBy='created_at desc')
|
|
for c in comments:
|
|
c.html_content = text2html(c.content)
|
|
blog.html_content = markdown2.markdown(blog.content)
|
|
return {
|
|
'__template__': 'blog.html',
|
|
'blog': blog,
|
|
'comments': comments
|
|
}
|
|
|
|
@get('/register')
|
|
def register():
|
|
return {
|
|
'__template__': 'register.html'
|
|
}
|
|
|
|
@get('/signin')
|
|
def signin():
|
|
return {
|
|
'__template__': 'signin.html'
|
|
}
|
|
|
|
@post('/api/authenticate')
|
|
def authenticate(*, email, passwd):
|
|
if not email:
|
|
raise APIValueError('email', 'Invalid email.')
|
|
if not passwd:
|
|
raise APIValueError('passwd', 'Invalid password.')
|
|
users = yield from User.findAll('email=?', [email])
|
|
if len(users) == 0:
|
|
raise APIValueError('email', 'Email not exist.')
|
|
user = users[0]
|
|
# check passwd:
|
|
sha1 = hashlib.sha1()
|
|
sha1.update(user.id.encode('utf-8'))
|
|
sha1.update(b':')
|
|
sha1.update(passwd.encode('utf-8'))
|
|
if user.passwd != sha1.hexdigest():
|
|
raise APIValueError('passwd', 'Invalid password.')
|
|
# authenticate ok, set cookie:
|
|
r = web.Response()
|
|
r.set_cookie(COOKIE_NAME, user2cookie(user, 86400), max_age=86400, httponly=True)
|
|
user.passwd = '******'
|
|
r.content_type = 'application/json'
|
|
r.body = json.dumps(user, ensure_ascii=False).encode('utf-8')
|
|
return r
|
|
|
|
@get('/signout')
|
|
def signout(request):
|
|
referer = request.headers.get('Referer')
|
|
r = web.HTTPFound(referer or '/')
|
|
r.set_cookie(COOKIE_NAME, '-deleted-', max_age=0, httponly=True)
|
|
logging.info('user signed out.')
|
|
return r
|
|
|
|
@get('/manage/')
|
|
def manage():
|
|
return 'redirect:/manage/comments'
|
|
|
|
@get('/manage/comments')
|
|
def manage_comments(*, page='1'):
|
|
return {
|
|
'__template__': 'manage_comments.html',
|
|
'page_index': get_page_index(page)
|
|
}
|
|
|
|
@get('/manage/blogs')
|
|
def manage_blogs(*, page='1'):
|
|
return {
|
|
'__template__': 'manage_blogs.html',
|
|
'page_index': get_page_index(page)
|
|
}
|
|
|
|
@get('/manage/blogs/create')
|
|
def manage_create_blog():
|
|
return {
|
|
'__template__': 'manage_blog_edit.html',
|
|
'id': '',
|
|
'action': '/api/blogs'
|
|
}
|
|
|
|
@get('/manage/blogs/edit')
|
|
def manage_edit_blog(*, id):
|
|
return {
|
|
'__template__': 'manage_blog_edit.html',
|
|
'id': id,
|
|
'action': '/api/blogs/%s' % id
|
|
}
|
|
|
|
@get('/manage/users')
|
|
def manage_users(*, page='1'):
|
|
return {
|
|
'__template__': 'manage_users.html',
|
|
'page_index': get_page_index(page)
|
|
}
|
|
|
|
@get('/api/comments')
|
|
def api_comments(*, page='1'):
|
|
page_index = get_page_index(page)
|
|
num = yield from Comment.findNumber('count(id)')
|
|
p = Page(num, page_index)
|
|
if num == 0:
|
|
return dict(page=p, comments=())
|
|
comments = yield from Comment.findAll(orderBy='created_at desc', limit=(p.offset, p.limit))
|
|
return dict(page=p, comments=comments)
|
|
|
|
@post('/api/blogs/{id}/comments')
|
|
def api_create_comment(id, request, *, content):
|
|
user = request.__user__
|
|
if user is None:
|
|
raise APIPermissionError('Please signin first.')
|
|
if not content or not content.strip():
|
|
raise APIValueError('content')
|
|
blog = yield from Blog.find(id)
|
|
if blog is None:
|
|
raise APIResourceNotFoundError('Blog')
|
|
comment = Comment(blog_id=blog.id, user_id=user.id, user_name=user.name, user_image=user.image, content=content.strip())
|
|
yield from comment.save()
|
|
return comment
|
|
|
|
@post('/api/comments/{id}/delete')
|
|
def api_delete_comments(id, request):
|
|
check_admin(request)
|
|
c = yield from Comment.find(id)
|
|
if c is None:
|
|
raise APIResourceNotFoundError('Comment')
|
|
yield from c.remove()
|
|
return dict(id=id)
|
|
|
|
@get('/api/users')
|
|
def api_get_users(*, page='1'):
|
|
page_index = get_page_index(page)
|
|
num = yield from User.findNumber('count(id)')
|
|
p = Page(num, page_index)
|
|
if num == 0:
|
|
return dict(page=p, users=())
|
|
users = yield from User.findAll(orderBy='created_at desc', limit=(p.offset, p.limit))
|
|
for u in users:
|
|
u.passwd = '******'
|
|
return dict(page=p, users=users)
|
|
|
|
_RE_EMAIL = re.compile(r'^[a-z0-9\.\-\_]+\@[a-z0-9\-\_]+(\.[a-z0-9\-\_]+){1,4}$')
|
|
_RE_SHA1 = re.compile(r'^[0-9a-f]{40}$')
|
|
|
|
@post('/api/users')
|
|
def api_register_user(*, email, name, passwd):
|
|
if not name or not name.strip():
|
|
raise APIValueError('name')
|
|
if not email or not _RE_EMAIL.match(email):
|
|
raise APIValueError('email')
|
|
if not passwd or not _RE_SHA1.match(passwd):
|
|
raise APIValueError('passwd')
|
|
users = yield from User.findAll('email=?', [email])
|
|
if len(users) > 0:
|
|
raise APIError('register:failed', 'email', 'Email is already in use.')
|
|
uid = next_id()
|
|
sha1_passwd = '%s:%s' % (uid, passwd)
|
|
user = User(id=uid, name=name.strip(), email=email, passwd=hashlib.sha1(sha1_passwd.encode('utf-8')).hexdigest(), image='https://dn-qiniu-avatar.qbox.me/avatar/%s?d=mm&s=120' % hashlib.md5(email.encode('utf-8')).hexdigest())
|
|
yield from user.save()
|
|
# make session cookie:
|
|
r = web.Response()
|
|
r.set_cookie(COOKIE_NAME, user2cookie(user, 86400), max_age=86400, httponly=True)
|
|
user.passwd = '******'
|
|
r.content_type = 'application/json'
|
|
r.body = json.dumps(user, ensure_ascii=False).encode('utf-8')
|
|
return r
|
|
|
|
@get('/api/blogs')
|
|
def api_blogs(*, page='1'):
|
|
page_index = get_page_index(page)
|
|
num = yield from Blog.findNumber('count(id)')
|
|
p = Page(num, page_index)
|
|
if num == 0:
|
|
return dict(page=p, blogs=())
|
|
blogs = yield from Blog.findAll(orderBy='created_at desc', limit=(p.offset, p.limit))
|
|
return dict(page=p, blogs=blogs)
|
|
|
|
@get('/api/blogs/{id}')
|
|
def api_get_blog(*, id):
|
|
blog = yield from Blog.find(id)
|
|
return blog
|
|
|
|
@post('/api/blogs')
|
|
def api_create_blog(request, *, name, summary, content):
|
|
check_admin(request)
|
|
if not name or not name.strip():
|
|
raise APIValueError('name', 'name cannot be empty.')
|
|
if not summary or not summary.strip():
|
|
raise APIValueError('summary', 'summary cannot be empty.')
|
|
if not content or not content.strip():
|
|
raise APIValueError('content', 'content cannot be empty.')
|
|
blog = Blog(user_id=request.__user__.id, user_name=request.__user__.name, user_image=request.__user__.image, name=name.strip(), summary=summary.strip(), content=content.strip())
|
|
yield from blog.save()
|
|
return blog
|
|
|
|
@post('/api/blogs/{id}')
|
|
def api_update_blog(id, request, *, name, summary, content):
|
|
check_admin(request)
|
|
blog = yield from Blog.find(id)
|
|
if not name or not name.strip():
|
|
raise APIValueError('name', 'name cannot be empty.')
|
|
if not summary or not summary.strip():
|
|
raise APIValueError('summary', 'summary cannot be empty.')
|
|
if not content or not content.strip():
|
|
raise APIValueError('content', 'content cannot be empty.')
|
|
blog.name = name.strip()
|
|
blog.summary = summary.strip()
|
|
blog.content = content.strip()
|
|
yield from blog.update()
|
|
return blog
|
|
|
|
@post('/api/blogs/{id}/delete')
|
|
def api_delete_blog(request, *, id):
|
|
check_admin(request)
|
|
blog = yield from Blog.find(id)
|
|
yield from blog.remove()
|
|
return dict(id=id)
|