Merge pull request #1 from ThomasRubini/server

Start of the server
This commit is contained in:
Djalim Simaila 2022-11-29 15:16:16 +01:00 committed by GitHub
commit 8f2a4cea04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 252 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
**/__pycache__
instance/

4
app.py Normal file
View File

@ -0,0 +1,4 @@
from truthseeker import app # the variable 'app' is detected by `flask run`
if __name__ == "__main__":
app.run()

27
truthseeker/__init__.py Normal file
View File

@ -0,0 +1,27 @@
import flask
import os
from truthseeker.routes import routes_api, routes_ui
app = flask.Flask("truthseeker")
def set_secret(app):
if os.path.isfile("instance/secret.txt"):
f = open("instance/secret.txt", "r")
app.config["SECRET_KEY"] = f.read()
f.close()
print("Read secret from secret.txt !")
else:
import secrets
app.config["SECRET_KEY"] = secrets.token_hex()
f = open("instance/secret.txt", "w")
f.write(app.config["SECRET_KEY"])
f.close()
print("Generated secret and wrote to secret.txt !")
set_secret(app)
app.register_blueprint(routes_api.routes_api, url_prefix="/api/v1")
app.register_blueprint(routes_ui.routes_ui, url_prefix="/")

View File

@ -0,0 +1,118 @@
import string
import random
import jwt
from datetime import datetime, timedelta
import truthseeker
# Map of all actively running games
# games_list["game.game_id"]-> game info linked to that id
games_list = {}
def random_string(length: int) ->str:
"""
This function create a random string as long as the lint passed as
parameter
: param length: the lenght of the random string
: type length : int
: return : a random string
: return type : string
"""
return "".join(random.choice(string.ascii_letters) for _ in range(length))
class Member:
"""
stores information related to the member of a given game
Member.username : The username of this member
Member.socker : The reference to the socket to talk to this member
"""
def __init__(self, username):
self.username = username
self.socket = None
def __str__(self) -> str:
return "Member[username={}]".format(self.username)
def __repr__(self) -> str:
return self.__str__()
class Game:
"""
The game info class stores all information linked to a active game
Game.game_id : str, the game identifier of the game
Game.owner : Member, the game identifier of the game
Game.members : Member[], the members of the game
"""
def __init__(self):
self.game_id = None
self.owner = None
self.members = []
def _gen_jwt(self, username, owner):
return jwt.encode(
payload={
"game_type": "multi",
"game_id": self.game_id,
"username": username,
"owner": owner,
"exp": datetime.utcnow() + timedelta(hours = 1) # handled automatically on jwt.decode
},
key=truthseeker.app.config["SECRET_KEY"],
algorithm="HS256"
)
def set_owner(self, username):
self.owner = Member(username)
self.members.append(self.owner)
return self.owner, self._gen_jwt(username, owner=True)
def add_member(self, username):
member = Member(username)
self.members.append(member)
return member, self._gen_jwt(username, owner=False)
def __str__(self) -> str:
return "Game[game_id={}, owner={}, members={}]".format(self.game_id, self.owner, self.members)
def __repr__(self) -> str:
return self.__str__()
def create_game():
"""
This function creates a new game by creating a Game object and stores
it into the games_list dictionnary
: return : a new Game
: return type : Game
"""
game = Game()
game.game_id = random_string(6)
game.start_token = random_string(64)
games_list[game.game_id] = game
#TODO ADD A WEBSOCKET IF THE GAME IS KNOWN TO BE MULTIPLAYER
return game
def get_game(game_id):
if game_id in games_list:
return games_list[game_id]
else:
return None
def get_game_info(game_id):
"""
This function retrieve a the Game object linked to the game_id
passed as parametter
: param game_id : the lenght of the random string
: type game_id : str
: return : The Game Object linked to the game_id
: return type : Game
"""
if game_id in games_list:
return games_list[game_id]
else:
return None

View File

@ -0,0 +1,91 @@
import flask
import jwt
import truthseeker
from truthseeker.logic import game_logic
from functools import wraps
routes_api = flask.Blueprint("api", __name__)
# Auth decorator
def jwt_required(f):
@wraps(f)
def decorator(*args, **kwargs):
jwt_str = flask.request.args.get("jwt")
if not jwt_str:
return {"status": "Error, JWT token missing"}, 401
try:
claims = jwt.decode(jwt_str, truthseeker.app.config['SECRET_KEY'], algorithms=['HS256'])
except jwt.exceptions.InvalidTokenError as e:
print("Caught exception while decoding JWT token :", e)
return {"status": "Error, invalid JWT"}, 401
return f(claims, *args, **kwargs)
return decorator
@routes_api.route("/createGame")
def create_game():
username = flask.request.args.get("username")
if username==None:
return {"status": "error, username not set"}
response = {}
response["status"] = "ok"
game = game_logic.create_game()
response["game_id"] = game.game_id
owner, owner_jwt = game.set_owner(username=username)
response["jwt"] = owner_jwt
return response
@routes_api.route("/joinGame")
def join_game():
game_id = flask.request.args.get("game_id")
username = flask.request.args.get("username")
if game_id==None or username==None:
return {"status": "error, username or game id not set"}
game = game_logic.get_game(game_id)
if game == None:
return {"status": "error, game does not exist"}
member, member_jwt = game.add_member(username)
response = {}
response["status"] = "ok"
response["jwt"] = member_jwt
return response
@routes_api.route("/getGameInfo")
def get_game_info(): # DEPRECATED, SHOULD BE REMOVED
response = {}
game_id = flask.request.args.get("game_id")
if game_id == None:
response["status"] = "No 'game_id' argument"
return response
game = game_logic.get_game_info(game_id)
if game == None:
response["status"] = "Game {} does not exist".format(game_id)
return response
else:
response["status"] = "ok"
response["game_id"] = game_id
response["token"] = game.start_token
return response
@routes_api.route("/startGame")
@jwt_required
def start_game(claims):
if not claims["owner"]:
return {"status": "Error, you are not the owner of this game"}
if game_logic.get_game(claims["game_id"]) == None:
return {"status": "Error, this game doesn't exist"}
return {"status": "ok"}

View File

@ -0,0 +1,10 @@
import flask
from truthseeker.logic import game_logic
routes_ui = flask.Blueprint("ui", __name__)
@routes_ui.route("/")
def hello():
return "Hello World!"