diff options
Diffstat (limited to 'api/user')
| -rw-r--r-- | api/user/avatar.py | 37 | ||||
| -rw-r--r-- | api/user/games.py | 116 | ||||
| -rw-r--r-- | api/user/info.py | 145 | ||||
| -rw-r--r-- | api/user/password.py | 13 | ||||
| -rw-r--r-- | api/user/preferences.py | 51 | ||||
| -rw-r--r-- | api/user/status.py | 20 | 
6 files changed, 221 insertions, 161 deletions
| diff --git a/api/user/avatar.py b/api/user/avatar.py index b4edeed..1b5500a 100644 --- a/api/user/avatar.py +++ b/api/user/avatar.py @@ -10,29 +10,34 @@ default_avatar = open("database/avatars/default.png", "rb").read()  avatar = Blueprint('avatar', __name__) -@avatar.route('/avatar', methods = ["GET"]) + +@avatar.route('/avatar', methods=["GET"])  def get_avatar(): -    token = request.cookies.get("token") or "" -    login = token_login(token) or "" +	token = request.cookies.get("token") or "" +	login = token_login(token) or "" + +	user_id = request.args.get("id") or login +	if not user_id: return "", 400 +	if not valid_user_id(user_id): return "", 403 -    user_id = request.args.get("id") or login -    if not user_id: return "", 400 -    if not valid_user_id(user_id): return "", 403 +	avatar_path = f"database/avatars/{user_id}.png" +	avatar = "" +	if exists(avatar_path): +		avatar = open(avatar_path, "rb").read() +	return Response(avatar or default_avatar, 200, mimetype="image/png") -    avatar_path = f"database/avatars/{user_id}.png" -    avatar = "" -    if exists(avatar_path): -        avatar = open(avatar_path, "rb").read() -    return Response(avatar or default_avatar, 200, mimetype="image/png") -@avatar.route('/avatar', methods = ["POST"]) #TODO: pillow image size validation (client side resize) +@avatar.route( +    '/avatar', methods=["POST"] +)  #TODO: pillow image size validation (client side resize)  @auth_required("user")  def update_avatar(login): -    if not request.data: return "", 400 +	if not request.data: return "", 400 -    open(f"database/avatars/{login}.png", "wb").write(decode(request.data, "base64")) +	open(f"database/avatars/{login}.png", +	     "wb").write(decode(request.data, "base64")) -    return "", 200 +	return "", 200 -dynamic_route = ["/user", avatar] +dynamic_route = ["/user", avatar] diff --git a/api/user/games.py b/api/user/games.py index 3936566..83d721a 100644 --- a/api/user/games.py +++ b/api/user/games.py @@ -8,69 +8,89 @@ from ruleset import resolve_ruleset  from game.info import format_game  import json +  # get total game outcome amount for user -def sum_games(user_id): #! SANITIZE USER_ID FIRST -    wld_querys = [' '.join([ -        "select count(game_id)", -        "from games", -        "where", -        f"player_{x[0]}_id = \"{user_id}\" and", -        f"outcome = \"{x[1]}\"", -    ]) for x in [(1, "w"), (1, "l"), (2, "w"), (2, "l")]] -    wld_querys.insert(0, ' '.join([ -        "select count(game_id)", -        "from games", -        "where", -        f"(player_1_id = \"{user_id}\" or player_2_id = \"{user_id}\") and", -        "outcome = \"d\"", -    ])) - -    big_query = "select " + ", ".join([f"({query})" for query in wld_querys]) - -    results = cursor.execute(big_query).fetchone() - -    # win and lose are calculated from user_id's perspective (player_1_id, player_2_id in db) -    return { -            "draw": results[0], -            "win": results[1] + results[4], -            "lose": results[2] + results[3], -            "games": reduce(lambda a, b: a + b, results) -    } +def sum_games(user_id):  #! SANITIZE USER_ID FIRST +	wld_querys = [ +	    ' '.join( +	        [ +	            "select count(game_id)", +	            "from games", +	            "where", +	            f"player_{x[0]}_id = \"{user_id}\" and", +	            f"outcome = \"{x[1]}\"", +	        ] +	    ) for x in [(1, "w"), (1, "l"), (2, "w"), (2, "l")] +	] +	wld_querys.insert( +	    0, ' '.join( +	        [ +	            "select count(game_id)", +	            "from games", +	            "where", +	            f"(player_1_id = \"{user_id}\" or player_2_id = \"{user_id}\") and", +	            "outcome = \"d\"", +	        ] +	    ) +	) + +	big_query = "select " + ", ".join([f"({query})" for query in wld_querys]) + +	results = cursor.execute(big_query).fetchone() + +	# win and lose are calculated from user_id's perspective (player_1_id, player_2_id in db) +	return { +	    "draw": results[0], +	    "win": results[1] + results[4], +	    "lose": results[2] + results[3], +	    "games": reduce(lambda a, b: a + b, results) +	} +  # get `count` games that `user_id` participated in, sorted by newest game  def fetch_games(user_id, count): -    game_ids = cursor.execute("select game_id from games where player_1_id = ? or player_2_id = ? order by created desc", [user_id, user_id]).fetchmany(count) -    export = [] +	game_ids = cursor.execute( +	    "select game_id from games where player_1_id = ? or player_2_id = ? order by created desc", +	    [user_id, user_id] +	).fetchmany(count) +	export = [] + +	for game_id in game_ids: +		export.append(format_game(game_id[0], user_id)) -    for game_id in game_ids: -        export.append(format_game(game_id[0], user_id)) +	return export -    return export  games = Blueprint('games', __name__) -@games.route('/games', methods = ['GET', 'POST']) + +@games.route('/games', methods=['GET', 'POST'])  def index(): -    data_string = request.data or "{}" -    data = json.loads(data_string) +	data_string = request.data or "{}" +	data = json.loads(data_string) + +	user_id = data.get("id") or "" +	token = request.cookies.get("token") or "" -    user_id = data.get("id") or "" -    token = request.cookies.get("token") or "" +	if not user_id and \ +                         not token: +		return "", 400 -    if not user_id and \ -       not token: -           return "", 400 +	if token and not user_id: +		user_id = token_login(token) -    if token and not user_id: -        user_id = token_login(token) +	if not cursor.execute( +	    "select user_id from users where user_id = ?", [user_id] +	).fetchone(): +		return "", 403 -    if not cursor.execute("select user_id from users where user_id = ?", [user_id]).fetchone(): return "", 403 +	export = {} +	merge( +	    export, {"totals": sum_games(user_id)}, +	    {"games": fetch_games(user_id, 20)} +	) -    export = {} -    merge(export, -          {"totals": sum_games(user_id)}, -          {"games": fetch_games(user_id, 20)}) +	return export, 200 -    return export, 200  dynamic_route = ["/user", games] diff --git a/api/user/info.py b/api/user/info.py index be48ef1..de0d2e8 100644 --- a/api/user/info.py +++ b/api/user/info.py @@ -4,84 +4,103 @@ from auth.login_token import token_login  from rating import get_rating  import json +  # check if user_id exists in database  def valid_user_id(user_id): -    query = cursor.execute("select user_id from users where user_id = ?", [user_id]).fetchone() -    return bool(query) +	query = cursor.execute( +	    "select user_id from users where user_id = ?", [user_id] +	).fetchone() +	return bool(query) +  # get relation to user_2_id from user_1_id's perspective  def get_relation_to(user_1_id, user_2_id): -    relation = cursor.execute("select * from social where " + \ -            "(user_1_id = ? and user_2_id = ?) or " + \ -            "(user_1_id = ? and user_2_id = ?)", [user_1_id, user_2_id, user_2_id, user_1_id]).fetchone() -    if not relation: return "none" -    if relation[2] == "friendship": return "friends" -    if relation[2] == "outgoing" and relation[0] == user_1_id: return "outgoing" -    if relation[2] == "outgoing" and relation[1] == user_1_id: return "incoming" -    if relation[2] == "block" and relation[0] == user_1_id: return "blocked" -    return "none" +	relation = cursor.execute("select * from social where " + \ +                                    "(user_1_id = ? and user_2_id = ?) or " + \ +                                    "(user_1_id = ? and user_2_id = ?)", [user_1_id, user_2_id, user_2_id, user_1_id]).fetchone() +	if not relation: return "none" +	if relation[2] == "friendship": return "friends" +	if relation[2] == "outgoing" and relation[0] == user_1_id: +		return "outgoing" +	if relation[2] == "outgoing" and relation[1] == user_1_id: +		return "incoming" +	if relation[2] == "block" and relation[0] == user_1_id: return "blocked" +	return "none" +  # get users friend count  def count_friends(user_id): -    query = cursor.execute("select type from social where (user_1_id = ? or user_2_id = ?) and type = \"friendship\"", [user_id, user_id]).fetchall() -    return len(query) #FIXME: use SQL count() instead of python's len() +	query = cursor.execute( +	    "select type from social where (user_1_id = ? or user_2_id = ?) and type = \"friendship\"", +	    [user_id, user_id] +	).fetchall() +	return len(query)  #FIXME: use SQL count() instead of python's len() +  # get user/info of `user_id` as `viewer` (id) -def format_user(user_id, viewer = ''): -    user = cursor.execute("select " + ", ".join([ -        "username", -        "user_id", -        "country", -        "registered", -        "status", -    ]) + " from users where user_id = ?", [user_id]).fetchone() -    formatted_user = { -        "username": user[0], -        "id": user[1], -        "country": user[2], -        "registered": user[3], -        "status": user[4], -        "friends": count_friends(user_id), -        "rating": get_rating(user_id), #TODO: calculate rating based on game analysis -    } -    if viewer: -        #FIXME: validate viewer id? -        formatted_user["relation"] = get_relation_to(viewer, user_id) -    return formatted_user +def format_user(user_id, viewer=''): +	user = cursor.execute( +	    "select " + ", ".join( +	        [ +	            "username", +	            "user_id", +	            "country", +	            "registered", +	            "status", +	        ] +	    ) + " from users where user_id = ?", [user_id] +	).fetchone() +	formatted_user = { +	    "username": user[0], +	    "id": user[1], +	    "country": user[2], +	    "registered": user[3], +	    "status": user[4], +	    "friends": count_friends(user_id), +	    "rating": +	    get_rating(user_id),  #TODO: calculate rating based on game analysis +	} +	if viewer: +		#FIXME: validate viewer id? +		formatted_user["relation"] = get_relation_to(viewer, user_id) +	return formatted_user +  info = Blueprint('info', __name__) +  # view own user/info if no user_id or username is provided and is logged in,  # else view user/info of user with user_id = `user_id` or username = `username` -@info.route('/info', methods = ['GET', 'POST']) +@info.route('/info', methods=['GET', 'POST'])  def index(): -    data_string = request.data or "{}" -    data = json.loads(data_string) - -    username = data.get("username") or "" -    user_id = data.get("id") or "" -    token = request.cookies.get("token") or "" -    viewer = "" - -    if not username and \ -       not user_id and \ -       not token: -           return "", 400 - -    if username: -        temp_user_id = cursor.execute("select user_id from users where username = ?", [username]).fetchone() -        if len(temp_user_id) > 0: user_id = temp_user_id - -    if token: -        self_id = token_login(token) -        if not (username or user_id): -            user_id = self_id -        if user_id: -            viewer = self_id - -    if user_id and not valid_user_id(user_id): return "", 403 -    user = format_user(user_id, viewer) - -    return user, 200 +	data_string = request.data or "{}" +	data = json.loads(data_string) + +	username = data.get("username") or "" +	user_id = data.get("id") or "" +	token = request.cookies.get("token") or "" +	viewer = "" + +	if all(not v for v in [username, user_id, token]): +		return "", 400 + +	if username: +		temp_user_id = cursor.execute( +		    "select user_id from users where username = ?", [username] +		).fetchone() +		if len(temp_user_id) > 0: user_id = temp_user_id + +	if token: +		self_id = token_login(token) +		if not (username or user_id): +			user_id = self_id +		if user_id: +			viewer = self_id + +	if user_id and not valid_user_id(user_id): return "", 403 +	user = format_user(user_id, viewer) + +	return user, 200 +  dynamic_route = ["/user", info] diff --git a/api/user/password.py b/api/user/password.py index 0c1cb70..ff52ba4 100644 --- a/api/user/password.py +++ b/api/user/password.py @@ -3,16 +3,17 @@ from db import cursor  password = Blueprint('password', __name__) +  # this endpoint is unfinished  @password.route('/password')  def index(): -    data = request.get_json() +	data = request.get_json() -    if not data["password"] or \ -       not data["newPassword"]: -        return "", 400 +	if not data["password"] or \ +                         not data["newPassword"]: +		return "", 400 -    return {}, 200 +	return {}, 200 -dynamic_route = ["/user", password] +dynamic_route = ["/user", password] diff --git a/api/user/preferences.py b/api/user/preferences.py index d4e27c9..2feaade 100644 --- a/api/user/preferences.py +++ b/api/user/preferences.py @@ -4,38 +4,49 @@ from ruleset import resolve_ruleset  from hierarchy import auth_required  import json +  # fill missing dict keys in preferences object  def format_preferences(prefs): -    return { -            "darkMode": prefs.get("darkMode") or False, -            "ruleset": resolve_ruleset(json.dumps(prefs.get("ruleset") or {}) or "default"), -            "userColors": { -                "diskA": prefs.get("userColors", {}).get("diskA") or "", -                "diskB": prefs.get("userColors", {}).get("diskB") or "", -                "background": prefs.get("userColors", {}).get("background") or "" -                } -            } +	return { +	    "darkMode": +	    prefs.get("darkMode") or False, +	    "ruleset": +	    resolve_ruleset(json.dumps(prefs.get("ruleset") or {}) or "default"), +	    "userColors": { +	        "diskA": prefs.get("userColors", {}).get("diskA") or "", +	        "diskB": prefs.get("userColors", {}).get("diskB") or "", +	        "background": prefs.get("userColors", {}).get("background") or "" +	    } +	} +  preferences = Blueprint('preferences', __name__) -@preferences.route('/preferences', methods = ["GET"]) + +@preferences.route('/preferences', methods=["GET"])  @auth_required("user")  def get_preferences(login): -    user_prefs = cursor.execute("select preferences from users where user_id = ?", [login]).fetchone() -    return { "preferences": format_preferences(json.loads(user_prefs[0])) }, 200 +	user_prefs = cursor.execute( +	    "select preferences from users where user_id = ?", [login] +	).fetchone() +	return {"preferences": format_preferences(json.loads(user_prefs[0]))}, 200 -@preferences.route('/preferences', methods = ["POST"]) + +@preferences.route('/preferences', methods=["POST"])  @auth_required("user")  def index(login): -    data = request.get_json() -    new_preferences = data.get("newPreferences") or "" +	data = request.get_json() +	new_preferences = data.get("newPreferences") or "" -    formatted_json = format_preferences(new_preferences) +	formatted_json = format_preferences(new_preferences) -    cursor.execute("update users set preferences = ? where user_id = ?", [json.dumps(formatted_json), login]) -    connection.commit() +	cursor.execute( +	    "update users set preferences = ? where user_id = ?", +	    [json.dumps(formatted_json), login] +	) +	connection.commit() -    return "", 200 +	return "", 200 -dynamic_route = ["/user", preferences] +dynamic_route = ["/user", preferences] diff --git a/api/user/status.py b/api/user/status.py index 3adb013..e762d12 100644 --- a/api/user/status.py +++ b/api/user/status.py @@ -5,17 +5,21 @@ import json  status = Blueprint('user_status', __name__) -@status.route('/status', methods = ['POST']) + +@status.route('/status', methods=['POST'])  @auth_required("user")  def index(user_id): -    data = request.get_json() -    status = data.get("status") or "" -    if not status: return "", 400 +	data = request.get_json() +	status = data.get("status") or "" +	if not status: return "", 400 -    cursor.execute("update users set status = ? where user_id = ?", [status[0:200], user_id]) -    connection.commit() +	cursor.execute( +	    "update users set status = ? where user_id = ?", +	    [status[0:200], user_id] +	) +	connection.commit() -    return "", 200 +	return "", 200 -dynamic_route = ["/user", status] +dynamic_route = ["/user", status] |