very early session and registration support
This commit is contained in:
parent
91fcbb7101
commit
c843f444e7
14 changed files with 317 additions and 67 deletions
|
@ -2,33 +2,26 @@ import flask
|
||||||
from flask import request
|
from flask import request
|
||||||
import functools
|
import functools
|
||||||
import time
|
import time
|
||||||
import uuid
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
def _generate_session_token():
|
import jsonify
|
||||||
token = str(uuid.uuid4())
|
|
||||||
#print('MAKE SESSION', token)
|
|
||||||
return token
|
|
||||||
|
|
||||||
def give_session_token(function):
|
|
||||||
@functools.wraps(function)
|
|
||||||
def wrapped(*args, **kwargs):
|
|
||||||
# Inject new token so the function doesn't know the difference
|
|
||||||
token = request.cookies.get('etiquette_session', None)
|
|
||||||
if not token:
|
|
||||||
token = _generate_session_token()
|
|
||||||
request.cookies = dict(request.cookies)
|
|
||||||
request.cookies['etiquette_session'] = token
|
|
||||||
|
|
||||||
ret = function(*args, **kwargs)
|
def required_fields(fields):
|
||||||
|
'''
|
||||||
# Send the token back to the client
|
Declare that the endpoint requires certain POST body fields. Without them,
|
||||||
if not isinstance(ret, flask.Response):
|
we respond with 400 and a message.
|
||||||
ret = flask.Response(ret)
|
'''
|
||||||
ret.set_cookie('etiquette_session', value=token, max_age=60)
|
def with_required_fields(function):
|
||||||
|
@functools.wraps(function)
|
||||||
return ret
|
def wrapped(*args, **kwargs):
|
||||||
return wrapped
|
if not all(field in request.form for field in fields):
|
||||||
|
response = {'error': 'Required fields: %s' % ', '.join(fields)}
|
||||||
|
response = jsonify.make_json_response(response, status=400)
|
||||||
|
return response
|
||||||
|
return function(*args, **kwargs)
|
||||||
|
return wrapped
|
||||||
|
return with_required_fields
|
||||||
|
|
||||||
def not_implemented(function):
|
def not_implemented(function):
|
||||||
'''
|
'''
|
||||||
|
|
157
etiquette.py
157
etiquette.py
|
@ -12,6 +12,7 @@ import exceptions
|
||||||
import helpers
|
import helpers
|
||||||
import jsonify
|
import jsonify
|
||||||
import phototagger
|
import phototagger
|
||||||
|
import sessions
|
||||||
|
|
||||||
# pip install
|
# pip install
|
||||||
# https://raw.githubusercontent.com/voussoir/else/master/_voussoirkit/voussoirkit.zip
|
# https://raw.githubusercontent.com/voussoir/else/master/_voussoirkit/voussoirkit.zip
|
||||||
|
@ -27,6 +28,7 @@ site.debug = True
|
||||||
|
|
||||||
P = phototagger.PhotoDB()
|
P = phototagger.PhotoDB()
|
||||||
|
|
||||||
|
session_manager = sessions.SessionManager()
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
@ -34,6 +36,9 @@ P = phototagger.PhotoDB()
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
def back_url():
|
||||||
|
return request.args.get('goto') or request.referrer or '/'
|
||||||
|
|
||||||
def create_tag(easybake_string):
|
def create_tag(easybake_string):
|
||||||
notes = P.easybake(easybake_string)
|
notes = P.easybake(easybake_string)
|
||||||
notes = [{'action': action, 'tagname': tagname} for (action, tagname) in notes]
|
notes = [{'action': action, 'tagname': tagname} for (action, tagname) in notes]
|
||||||
|
@ -60,12 +65,6 @@ def delete_synonym(synonym):
|
||||||
master_tag.remove_synonym(synonym)
|
master_tag.remove_synonym(synonym)
|
||||||
return {'action':'delete_synonym', 'synonym': synonym}
|
return {'action':'delete_synonym', 'synonym': synonym}
|
||||||
|
|
||||||
def make_json_response(j, *args, **kwargs):
|
|
||||||
dumped = json.dumps(j)
|
|
||||||
response = flask.Response(dumped, *args, **kwargs)
|
|
||||||
response.headers['Content-Type'] = 'application/json;charset=utf-8'
|
|
||||||
return response
|
|
||||||
|
|
||||||
def P_album(albumid):
|
def P_album(albumid):
|
||||||
try:
|
try:
|
||||||
return P.get_album(albumid)
|
return P.get_album(albumid)
|
||||||
|
@ -159,11 +158,82 @@ def send_file(filepath):
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
|
|
||||||
@site.route('/')
|
@site.route('/')
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def root():
|
def root():
|
||||||
motd = random.choice(P.config['motd_strings'])
|
motd = random.choice(P.config['motd_strings'])
|
||||||
return flask.render_template('root.html', motd=motd)
|
session = session_manager.get(request)
|
||||||
|
return flask.render_template('root.html', motd=motd, session=session)
|
||||||
|
|
||||||
|
@site.route('/login', methods=['GET'])
|
||||||
|
@session_manager.give_token
|
||||||
|
def get_login():
|
||||||
|
session = session_manager.get(request)
|
||||||
|
return flask.render_template('login.html', session=session)
|
||||||
|
|
||||||
|
@site.route('/register', methods=['GET'])
|
||||||
|
def get_register():
|
||||||
|
return flask.redirect('/login')
|
||||||
|
|
||||||
|
@site.route('/login', methods=['POST'])
|
||||||
|
@session_manager.give_token
|
||||||
|
@decorators.required_fields(['username', 'password'])
|
||||||
|
def post_login():
|
||||||
|
if session_manager.get(request):
|
||||||
|
flask.abort(403, 'You\'re already signed in.')
|
||||||
|
|
||||||
|
username = request.form['username']
|
||||||
|
password = request.form['password']
|
||||||
|
user = P.get_user(username=username)
|
||||||
|
try:
|
||||||
|
user = P.login(user.id, password)
|
||||||
|
except exceptions.WrongLogin:
|
||||||
|
flask.abort(422, 'Wrong login.')
|
||||||
|
session = sessions.Session(request, user)
|
||||||
|
session_manager.add(session)
|
||||||
|
response = flask.Response('redirect', status=302, headers={'Location': '/'})
|
||||||
|
return response
|
||||||
|
|
||||||
|
@site.route('/register', methods=['POST'])
|
||||||
|
@session_manager.give_token
|
||||||
|
@decorators.required_fields(['username', 'password_1', 'password_2'])
|
||||||
|
def post_register():
|
||||||
|
if session_manager.get(request):
|
||||||
|
flask.abort(403, 'You\'re already signed in.')
|
||||||
|
|
||||||
|
username = request.form['username']
|
||||||
|
password_1 = request.form['password_1']
|
||||||
|
password_2 = request.form['password_2']
|
||||||
|
|
||||||
|
if password_1 != password_2:
|
||||||
|
flask.abort(422, 'Passwords do not match.')
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = P.register_user(username, password_1)
|
||||||
|
except exceptions.UsernameTooShort as e:
|
||||||
|
flask.abort(422, 'Username shorter than minimum of %d' % P.config['min_username_length'])
|
||||||
|
except exceptions.UsernameTooLong as e:
|
||||||
|
flask.abort(422, 'Username longer than maximum of %d' % P.config['max_username_length'])
|
||||||
|
except exceptions.InvalidUsernameChars as e:
|
||||||
|
flask.abort(422, 'Username contains invalid characters %s' % e.args[0])
|
||||||
|
except exceptions.PasswordTooShort as e:
|
||||||
|
flask.abort(422, 'Password is shorter than minimum of %d' % P.config['min_password_length'])
|
||||||
|
except exceptions.UserExists as e:
|
||||||
|
flask.abort(422, 'User %s already exists' % e.args[0])
|
||||||
|
|
||||||
|
session = sessions.Session(request, user)
|
||||||
|
session_manager.add(session)
|
||||||
|
response = flask.Response('redirect', status=302, headers={'Location': '/'})
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@site.route('/logout', methods=['GET', 'POST'])
|
||||||
|
@session_manager.give_token
|
||||||
|
def logout():
|
||||||
|
session_manager.remove(request)
|
||||||
|
response = flask.Response('redirect', status=302, headers={'Location': back_url()})
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
@site.route('/favicon.ico')
|
@site.route('/favicon.ico')
|
||||||
|
@ -182,22 +252,24 @@ def get_album_core(albumid):
|
||||||
return album
|
return album
|
||||||
|
|
||||||
@site.route('/album/<albumid>')
|
@site.route('/album/<albumid>')
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def get_album_html(albumid):
|
def get_album_html(albumid):
|
||||||
album = get_album_core(albumid)
|
album = get_album_core(albumid)
|
||||||
|
session = session_manager.get(request)
|
||||||
response = flask.render_template(
|
response = flask.render_template(
|
||||||
'album.html',
|
'album.html',
|
||||||
album=album,
|
album=album,
|
||||||
photos=album['photos'],
|
photos=album['photos'],
|
||||||
|
session=session,
|
||||||
view=request.args.get('view', 'grid'),
|
view=request.args.get('view', 'grid'),
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@site.route('/album/<albumid>.json')
|
@site.route('/album/<albumid>.json')
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def get_album_json(albumid):
|
def get_album_json(albumid):
|
||||||
album = get_album_core(albumid)
|
album = get_album_core(albumid)
|
||||||
return make_json_response(album)
|
return jsonify.make_json_response(album)
|
||||||
|
|
||||||
|
|
||||||
@site.route('/album/<albumid>.tar')
|
@site.route('/album/<albumid>.tar')
|
||||||
|
@ -218,21 +290,24 @@ def get_albums_core():
|
||||||
return albums
|
return albums
|
||||||
|
|
||||||
@site.route('/albums')
|
@site.route('/albums')
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def get_albums_html():
|
def get_albums_html():
|
||||||
albums = get_albums_core()
|
albums = get_albums_core()
|
||||||
return flask.render_template('albums.html', albums=albums)
|
session = session_manager.get(request)
|
||||||
|
return flask.render_template('albums.html', albums=albums, session=session)
|
||||||
|
|
||||||
@site.route('/albums.json')
|
@site.route('/albums.json')
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def get_albums_json():
|
def get_albums_json():
|
||||||
albums = get_albums_core()
|
albums = get_albums_core()
|
||||||
return make_json_response(albums)
|
return jsonify.make_json_response(albums)
|
||||||
|
|
||||||
|
|
||||||
@site.route('/bookmarks')
|
@site.route('/bookmarks')
|
||||||
|
@session_manager.give_token
|
||||||
def get_bookmarks():
|
def get_bookmarks():
|
||||||
return flask.render_template('bookmarks.html')
|
session = session_manager.get(request)
|
||||||
|
return flask.render_template('bookmarks.html', session=session)
|
||||||
|
|
||||||
|
|
||||||
@site.route('/file/<photoid>')
|
@site.route('/file/<photoid>')
|
||||||
|
@ -268,20 +343,20 @@ def get_photo_core(photoid):
|
||||||
return photo
|
return photo
|
||||||
|
|
||||||
@site.route('/photo/<photoid>', methods=['GET'])
|
@site.route('/photo/<photoid>', methods=['GET'])
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def get_photo_html(photoid):
|
def get_photo_html(photoid):
|
||||||
photo = get_photo_core(photoid)
|
photo = get_photo_core(photoid)
|
||||||
photo['tags'].sort(key=lambda x: x['qualified_name'])
|
photo['tags'].sort(key=lambda x: x['qualified_name'])
|
||||||
return flask.render_template('photo.html', photo=photo)
|
session = session_manager.get(request)
|
||||||
|
return flask.render_template('photo.html', photo=photo, session=session)
|
||||||
|
|
||||||
@site.route('/photo/<photoid>.json', methods=['GET'])
|
@site.route('/photo/<photoid>.json', methods=['GET'])
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def get_photo_json(photoid):
|
def get_photo_json(photoid):
|
||||||
photo = get_photo_core(photoid)
|
photo = get_photo_core(photoid)
|
||||||
photo = make_json_response(photo)
|
photo = jsonify.make_json_response(photo)
|
||||||
return photo
|
return photo
|
||||||
|
|
||||||
|
|
||||||
def get_search_core():
|
def get_search_core():
|
||||||
#print(request.args)
|
#print(request.args)
|
||||||
|
|
||||||
|
@ -418,11 +493,12 @@ def get_search_core():
|
||||||
return final_results
|
return final_results
|
||||||
|
|
||||||
@site.route('/search')
|
@site.route('/search')
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def get_search_html():
|
def get_search_html():
|
||||||
search_results = get_search_core()
|
search_results = get_search_core()
|
||||||
search_kwargs = search_results['search_kwargs']
|
search_kwargs = search_results['search_kwargs']
|
||||||
qualname_map = search_results['qualname_map']
|
qualname_map = search_results['qualname_map']
|
||||||
|
session = session_manager.get(request)
|
||||||
response = flask.render_template(
|
response = flask.render_template(
|
||||||
'search.html',
|
'search.html',
|
||||||
next_page_url=search_results['next_page_url'],
|
next_page_url=search_results['next_page_url'],
|
||||||
|
@ -430,13 +506,14 @@ def get_search_html():
|
||||||
photos=search_results['photos'],
|
photos=search_results['photos'],
|
||||||
qualname_map=json.dumps(qualname_map),
|
qualname_map=json.dumps(qualname_map),
|
||||||
search_kwargs=search_kwargs,
|
search_kwargs=search_kwargs,
|
||||||
|
session=session,
|
||||||
total_tags=search_results['total_tags'],
|
total_tags=search_results['total_tags'],
|
||||||
warns=search_results['warns'],
|
warns=search_results['warns'],
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@site.route('/search.json')
|
@site.route('/search.json')
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def get_search_json():
|
def get_search_json():
|
||||||
search_results = get_search_core()
|
search_results = get_search_core()
|
||||||
#search_kwargs = search_results['search_kwargs']
|
#search_kwargs = search_results['search_kwargs']
|
||||||
|
@ -445,7 +522,7 @@ def get_search_json():
|
||||||
include_qualname_map = helpers.truthystring(include_qualname_map)
|
include_qualname_map = helpers.truthystring(include_qualname_map)
|
||||||
if not include_qualname_map:
|
if not include_qualname_map:
|
||||||
search_results.pop('qualname_map')
|
search_results.pop('qualname_map')
|
||||||
return make_json_response(search_results)
|
return jsonify.make_json_response(search_results)
|
||||||
|
|
||||||
|
|
||||||
@site.route('/static/<filename>')
|
@site.route('/static/<filename>')
|
||||||
|
@ -468,18 +545,19 @@ def get_tags_core(specific_tag=None):
|
||||||
|
|
||||||
@site.route('/tags')
|
@site.route('/tags')
|
||||||
@site.route('/tags/<specific_tag>')
|
@site.route('/tags/<specific_tag>')
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def get_tags_html(specific_tag=None):
|
def get_tags_html(specific_tag=None):
|
||||||
tags = get_tags_core(specific_tag)
|
tags = get_tags_core(specific_tag)
|
||||||
return flask.render_template('tags.html', tags=tags)
|
session = session_manager.get(request)
|
||||||
|
return flask.render_template('tags.html', tags=tags, session=session)
|
||||||
|
|
||||||
@site.route('/tags.json')
|
@site.route('/tags.json')
|
||||||
@site.route('/tags/<specific_tag>.json')
|
@site.route('/tags/<specific_tag>.json')
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def get_tags_json(specific_tag=None):
|
def get_tags_json(specific_tag=None):
|
||||||
tags = get_tags_core(specific_tag)
|
tags = get_tags_core(specific_tag)
|
||||||
tags = [t[0] for t in tags]
|
tags = [t[0] for t in tags]
|
||||||
return make_json_response(tags)
|
return jsonify.make_json_response(tags)
|
||||||
|
|
||||||
|
|
||||||
@site.route('/thumbnail/<photoid>')
|
@site.route('/thumbnail/<photoid>')
|
||||||
|
@ -495,7 +573,7 @@ def get_thumbnail(photoid):
|
||||||
|
|
||||||
@site.route('/album/<albumid>', methods=['POST'])
|
@site.route('/album/<albumid>', methods=['POST'])
|
||||||
@site.route('/album/<albumid>.json', methods=['POST'])
|
@site.route('/album/<albumid>.json', methods=['POST'])
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def post_edit_album(albumid):
|
def post_edit_album(albumid):
|
||||||
'''
|
'''
|
||||||
Edit the album's title and description.
|
Edit the album's title and description.
|
||||||
|
@ -512,18 +590,18 @@ def post_edit_album(albumid):
|
||||||
tag = P_tag(tag)
|
tag = P_tag(tag)
|
||||||
except exceptions.NoSuchTag:
|
except exceptions.NoSuchTag:
|
||||||
response = {'error': 'That tag doesnt exist', 'tagname': tag}
|
response = {'error': 'That tag doesnt exist', 'tagname': tag}
|
||||||
return make_json_response(response, status=404)
|
return jsonify.make_json_response(response, status=404)
|
||||||
recursive = request.form.get('recursive', False)
|
recursive = request.form.get('recursive', False)
|
||||||
recursive = helpers.truthystring(recursive)
|
recursive = helpers.truthystring(recursive)
|
||||||
album.add_tag_to_all(tag, nested_children=recursive)
|
album.add_tag_to_all(tag, nested_children=recursive)
|
||||||
response['action'] = action
|
response['action'] = action
|
||||||
response['tagname'] = tag.name
|
response['tagname'] = tag.name
|
||||||
return make_json_response(response)
|
return jsonify.make_json_response(response)
|
||||||
|
|
||||||
|
|
||||||
@site.route('/photo/<photoid>', methods=['POST'])
|
@site.route('/photo/<photoid>', methods=['POST'])
|
||||||
@site.route('/photo/<photoid>.json', methods=['POST'])
|
@site.route('/photo/<photoid>.json', methods=['POST'])
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def post_edit_photo(photoid):
|
def post_edit_photo(photoid):
|
||||||
'''
|
'''
|
||||||
Add and remove tags from photos.
|
Add and remove tags from photos.
|
||||||
|
@ -548,22 +626,22 @@ def post_edit_photo(photoid):
|
||||||
tag = P.get_tag(tag)
|
tag = P.get_tag(tag)
|
||||||
except exceptions.NoSuchTag:
|
except exceptions.NoSuchTag:
|
||||||
response = {'error': 'That tag doesnt exist', 'tagname': tag}
|
response = {'error': 'That tag doesnt exist', 'tagname': tag}
|
||||||
return make_json_response(response, status=404)
|
return jsonify.make_json_response(response, status=404)
|
||||||
|
|
||||||
method(tag)
|
method(tag)
|
||||||
response['action'] = action
|
response['action'] = action
|
||||||
#response['tagid'] = tag.id
|
#response['tagid'] = tag.id
|
||||||
response['tagname'] = tag.name
|
response['tagname'] = tag.name
|
||||||
return make_json_response(response)
|
return jsonify.make_json_response(response)
|
||||||
|
|
||||||
|
|
||||||
@site.route('/tags', methods=['POST'])
|
@site.route('/tags', methods=['POST'])
|
||||||
@decorators.give_session_token
|
@session_manager.give_token
|
||||||
def post_edit_tags():
|
def post_edit_tags():
|
||||||
'''
|
'''
|
||||||
Create and delete tags and synonyms.
|
Create and delete tags and synonyms.
|
||||||
'''
|
'''
|
||||||
print(request.form)
|
#print(request.form)
|
||||||
status = 200
|
status = 200
|
||||||
if 'create_tag' in request.form:
|
if 'create_tag' in request.form:
|
||||||
action = 'create_tag'
|
action = 'create_tag'
|
||||||
|
@ -605,6 +683,13 @@ def post_edit_tags():
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@site.route('/apitest')
|
||||||
|
@session_manager.give_token
|
||||||
|
def apitest():
|
||||||
|
response = flask.Response('testing')
|
||||||
|
response.set_cookie('etiquette_session', 'don\'t overwrite me')
|
||||||
|
return response
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
#site.run(threaded=True)
|
#site.run(threaded=True)
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -2,10 +2,10 @@ import datetime
|
||||||
import math
|
import math
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
import os
|
||||||
|
import warnings
|
||||||
|
|
||||||
import constants
|
import constants
|
||||||
import exceptions
|
import exceptions
|
||||||
import warnings
|
|
||||||
|
|
||||||
from voussoirkit import bytestring
|
from voussoirkit import bytestring
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
|
import flask
|
||||||
import helpers
|
import helpers
|
||||||
|
import json
|
||||||
|
|
||||||
|
def make_json_response(j, *args, **kwargs):
|
||||||
|
dumped = json.dumps(j)
|
||||||
|
response = flask.Response(dumped, *args, **kwargs)
|
||||||
|
response.headers['Content-Type'] = 'application/json;charset=utf-8'
|
||||||
|
return response
|
||||||
|
|
||||||
def album(a, minimal=False):
|
def album(a, minimal=False):
|
||||||
j = {
|
j = {
|
||||||
|
|
79
sessions.py
Normal file
79
sessions.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import flask
|
||||||
|
from flask import request
|
||||||
|
import functools
|
||||||
|
import helpers
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
def _generate_token():
|
||||||
|
token = str(uuid.uuid4())
|
||||||
|
#print('MAKE SESSION', token)
|
||||||
|
return token
|
||||||
|
|
||||||
|
def _normalize_token(token):
|
||||||
|
if isinstance(token, flask.Request):
|
||||||
|
token = token.cookies.get('etiquette_session', None)
|
||||||
|
|
||||||
|
class SessionManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.sessions = {}
|
||||||
|
|
||||||
|
def add(self, session):
|
||||||
|
self.sessions[session.token] = session
|
||||||
|
|
||||||
|
def get(self, token):
|
||||||
|
token = _normalize_token(token)
|
||||||
|
return self.sessions.get(token, None)
|
||||||
|
|
||||||
|
def give_token(self, function):
|
||||||
|
'''
|
||||||
|
This decorator ensures that the user has an `etiquette_session` cookie
|
||||||
|
before reaching the request handler.
|
||||||
|
If the user does not have the cookie, they are given one.
|
||||||
|
If they do, its lifespan is reset.
|
||||||
|
'''
|
||||||
|
@functools.wraps(function)
|
||||||
|
def wrapped(*args, **kwargs):
|
||||||
|
# Inject new token so the function doesn't know the difference
|
||||||
|
token = request.cookies.get('etiquette_session', None)
|
||||||
|
if not token:
|
||||||
|
token = _generate_token()
|
||||||
|
request.cookies = dict(request.cookies)
|
||||||
|
request.cookies['etiquette_session'] = token
|
||||||
|
|
||||||
|
response = function(*args, **kwargs)
|
||||||
|
|
||||||
|
if not isinstance(response, flask.Response):
|
||||||
|
response = flask.Response(response)
|
||||||
|
|
||||||
|
# Send the token back to the client
|
||||||
|
# but only if the endpoint didn't manually set the cookie.
|
||||||
|
for (headerkey, value) in response.headers:
|
||||||
|
if headerkey == 'Set-Cookie' and value.startswith('etiquette_session='):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
response.set_cookie('etiquette_session', value=token, max_age=86400)
|
||||||
|
self.maintain(token)
|
||||||
|
|
||||||
|
return response
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
def maintain(self, token):
|
||||||
|
session = self.get(token)
|
||||||
|
if session:
|
||||||
|
session.maintain()
|
||||||
|
|
||||||
|
def remove(self, token):
|
||||||
|
token = _normalize_token(token)
|
||||||
|
if token in self.sessions:
|
||||||
|
self.sessions.pop(token)
|
||||||
|
|
||||||
|
class Session:
|
||||||
|
def __init__(self, request, user):
|
||||||
|
self.token = _normalize_token(request)
|
||||||
|
self.user = user
|
||||||
|
self.ip_address = request.remote_addr
|
||||||
|
self.user_agent = request.headers.get('User-Agent', '')
|
||||||
|
self.last_activity = int(helpers.now())
|
||||||
|
|
||||||
|
def maintain(self):
|
||||||
|
self.last_activity = int(helpers.now())
|
|
@ -22,7 +22,7 @@ p
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{{header.make_header()}}
|
{{header.make_header(session=session)}}
|
||||||
<div id="content_body">
|
<div id="content_body">
|
||||||
<h2>{{album["title"]}}</h2>
|
<h2>{{album["title"]}}</h2>
|
||||||
<p>{{album["description"]}}</p>
|
<p>{{album["description"]}}</p>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{{header.make_header()}}
|
{{header.make_header(session=session)}}
|
||||||
<div id="content_body">
|
<div id="content_body">
|
||||||
{% for album in albums %}
|
{% for album in albums %}
|
||||||
{% if album["title"] %}
|
{% if album["title"] %}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{{header.make_header()}}
|
{{header.make_header(session=session)}}
|
||||||
<div id="content_body">
|
<div id="content_body">
|
||||||
<a href="/search?has_tags=no&orderby=random-desc&mimetype=image">Needs tagging</a>
|
<a href="/search?has_tags=no&orderby=random-desc&mimetype=image">Needs tagging</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
{% macro make_header() %}
|
{% macro make_header(session) %}
|
||||||
<div id="header">
|
<div id="header">
|
||||||
<a class="header_element" href="/">Etiquette</a>
|
<a class="header_element" href="/">Etiquette</a>
|
||||||
<a class="header_element" href="/search">Search</a>
|
<a class="header_element" href="/search">Search</a>
|
||||||
<a class="header_element" href="/tags">Tags</a>
|
<a class="header_element" href="/tags">Tags</a>
|
||||||
|
{% if session %}
|
||||||
|
<a class="header_element" href="/user/{{session.user.username}}">{{session.user.username}}</a>
|
||||||
|
<a class="header_element" href="/logout" style="flex:0">Logout</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="header_element" href="/login">Log in</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
74
templates/login.html
Normal file
74
templates/login.html
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
<!DOCTYPE html5>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
{% import "header.html" as header %}
|
||||||
|
<title>Login/Register</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="stylesheet" href="/static/common.css">
|
||||||
|
<script src="/static/common.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#content_body
|
||||||
|
{
|
||||||
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
#login_register_box
|
||||||
|
{
|
||||||
|
margin: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
form
|
||||||
|
{
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 25px;
|
||||||
|
margin: 25px;
|
||||||
|
border: 1px black solid;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
form > *
|
||||||
|
{
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
input
|
||||||
|
{
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
button
|
||||||
|
{
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{{header.make_header(session=session)}}
|
||||||
|
<div id="content_body">
|
||||||
|
<div id="login_register_box">
|
||||||
|
<form id="login_form" action="/login" method="post">
|
||||||
|
<span>Log in</span>
|
||||||
|
<input type="text" name="username" placeholder="username">
|
||||||
|
<input type="password" name="password" placeholder="password">
|
||||||
|
<button type="submit">Log in</button>
|
||||||
|
</form>
|
||||||
|
<form id="register_form" action="/register" method="post">
|
||||||
|
<span>Register</span>
|
||||||
|
<input type="text" name="username" placeholder="username">
|
||||||
|
<input type="password" name="password_1" placeholder="password">
|
||||||
|
<input type="password" name="password_2" placeholder="password again">
|
||||||
|
<button type="submit">Register</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
</script>
|
||||||
|
</html>
|
|
@ -87,7 +87,7 @@
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{{header.make_header()}}
|
{{header.make_header(session=session)}}
|
||||||
<div id="content_body">
|
<div id="content_body">
|
||||||
<div id="left">
|
<div id="left">
|
||||||
<div id="editor_area">
|
<div id="editor_area">
|
||||||
|
|
|
@ -34,6 +34,11 @@ a:hover
|
||||||
<a href="/tags">Browse tags</a>
|
<a href="/tags">Browse tags</a>
|
||||||
<a href="/albums">Browse albums</a>
|
<a href="/albums">Browse albums</a>
|
||||||
<a href="/bookmarks">Bookmarks</a>
|
<a href="/bookmarks">Bookmarks</a>
|
||||||
|
{% if session %}
|
||||||
|
<a href="/user/{{session.user.username}}">{{session.user.username}}</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="/login">Log in</a>
|
||||||
|
{% endif %}
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,7 @@ form
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{{header.make_header()}}
|
{{header.make_header(session=session)}}
|
||||||
<div id="error_message_area">
|
<div id="error_message_area">
|
||||||
{% for warn in warns %}
|
{% for warn in warns %}
|
||||||
<span class="search_warning">{{warn}}</span>
|
<span class="search_warning">{{warn}}</span>
|
||||||
|
|
|
@ -59,7 +59,7 @@ body
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{{header.make_header()}}
|
{{header.make_header(session=session)}}
|
||||||
<div id="content_body">
|
<div id="content_body">
|
||||||
<div id="left">
|
<div id="left">
|
||||||
<ul>
|
<ul>
|
||||||
|
|
Loading…
Reference in a new issue