diff --git a/FlaskWebProject/FlaskWebProject/lewy_api_v1.py b/FlaskWebProject/FlaskWebProject/lewy_api_v1.py index 53ec3f2..e428442 100644 --- a/FlaskWebProject/FlaskWebProject/lewy_api_v1.py +++ b/FlaskWebProject/FlaskWebProject/lewy_api_v1.py @@ -1,4 +1,4 @@ -# API is expected to return: +# API is expected to return a tuple of: # - HTTP status code, # - human-readable status message, # - json with appropriate data @@ -11,6 +11,14 @@ import lewy_db as ldb import lewy_globals def require_authentication(func): + """ + Ten dekorator służy do wymuszenia parametru "token" + podczas obsługi zapytania. Powinien on zostać doklejony + do żądania, np. /api/v1/halt?token=XXX... + Wartość tokenu jest pobierana z pola api_key w config.toml. + Jeżeli skrypt jej tam nie znajdzie, jest generowana losowo + na starcie i drukowana w terminalu. + """ @wraps(func) def wrapper(*args, **kwargs): token = kwargs["r"].args.get('token') @@ -26,19 +34,44 @@ def require_authentication(func): return wrapper def increment_bad_requests(): + """ + Zwiększa globalny, tymczasowy licznik nieprawidłowych zapytań. + """ lewy_globals.apiFailedRequests += 1 def not_implemented(data): + """ + Zwraca kod 501 wraz z endpointem, który wywołał błąd. + + :param data: Ścieżka zapytania + :type data: list + """ # TODO: change list to string -> data, not data[0] return 501, f"not recognised/implemented: {data[0]}", [] +# GET /api/v1 def stub_hello(): + """ + Prosta funkcja witająca użytkowników w /api/v1 + """ return 200, 'hello from v1! stats are at /api/v1/stats', [] def epoch_to_date(epoch): + """ + Zamienia Unix'owy epoch na lokalny czas, + w formacie przypominającym format ISO. + + :param epoch: Epoch - sekundy po 1. stycznia 1970 + :type epoch: int + """ return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(epoch)) +# GET /api/v1/ +# GET /api/v1/stats def stats(): + """ + Zwraca ogólne statystyki serwera. + """ data_to_send = { "start_time": lewy_globals.starttime, "uptime": lewy_globals.getUptime(), @@ -50,11 +83,20 @@ def stats(): } return 200, "ok", data_to_send -def get_matches(): +# GET /api/v1/matches +def get_matches(r): + """ + TODO: Zwraca mecze. + """ pass +# GET /api/v1/debugger_halt?token=XXX... @require_authentication def debugger_halt(r): + """ + Zatrzymuje wykonywanie skryptu, aby pozwolić + administratorowi na wykonywanie dowolnego polecenia z konsoli. + """ if lewy_globals.config['general']['is_proxied']: print(f"{c.WARNING}[{epoch_to_date(time.time())}]{c.ENDC} {r.headers['X-Forwarded-For']} triggered a debugger halt!") else: @@ -63,6 +105,16 @@ def debugger_halt(r): return 200, "ok", [] def lookup(data, request): + """ + Obsługuje zapytania zwrócone do /api/v1/... + + :param data: Lista ze ścieżką zapytania + :type data: list + :param request: Zapytanie + :type request: flask.request + + :returns: Wartość zwróconą przez którąś z przywołanych funkcji. + """ if data == []: return stub_hello() match data[0].lower(): @@ -74,6 +126,8 @@ def lookup(data, request): return stub_hello() case 'halt': return debugger_halt(r = request) + case 'matches': + get_matches(r = request) case _: increment_bad_requests() return not_implemented(data) \ No newline at end of file diff --git a/FlaskWebProject/FlaskWebProject/lewy_globals.py b/FlaskWebProject/FlaskWebProject/lewy_globals.py index 2d9f7af..855f869 100644 --- a/FlaskWebProject/FlaskWebProject/lewy_globals.py +++ b/FlaskWebProject/FlaskWebProject/lewy_globals.py @@ -29,7 +29,7 @@ def safeTraverse(obj: dict, path: list, default=None): finally: return result -def getCommit(): +def getCommit() -> str | None: try: return Repo(search_parent_directories=True).head.object.hexsha except Exception as e: @@ -65,7 +65,7 @@ def ensureRandomlyGeneratedPassword(): print(f"{colors.WARNING}WARNING{colors.ENDC}: Default config populated with one-time, insecure pseudorandom API key: {colors.OKCYAN}{randomly_generated_passcode}{colors.ENDC}.\n" f" The API key is not the Flask debugger PIN. You need to provide a config file for persistence!{colors.ENDL}") -def getConfig(configfile): +def getConfig(configfile: str) -> dict: global randomly_generated_passcode if not os.path.exists(configfile): @@ -82,15 +82,26 @@ def getConfig(configfile): else: return toml.load(configfile) -def setupDb(app, config): +def setupDb(app, config) -> lewy_db.baza: global db db = lewy_db.baza(app, config) return db -def getDb(): +def getDb() -> lewy_db.baza: + """ + Akcesor dla wrappera bazy danych wspólnego dla całego projektu + (klasy baza z lewy_db) + """ return db def setConfig(configfile): + """ + Zapewnia, że konfiguracja nie jest pusta, + nawet, gdy sam plik jest pusty. + + :param configfile: Ścieżka do pliku + :type configfile: str + """ global config config = getConfig(configfile) @@ -100,6 +111,11 @@ def setConfig(configfile): def getHeaders(): + """ + Zwraca hardkodowane nagłówki do scrapowania, bądź te, + z config.toml (o ile użytkownik jakieś podał). + """ + # NOTE: use ESR user-agent # user_agent = 'Mozilla/5.0 (Windows NT 10.0; rv:130.0) Gecko/20100101 Firefox/130.0' user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0' @@ -110,10 +126,18 @@ def getHeaders(): return user_agent def getUptime(): + """ + Zwraca informację o czasie działania serwera. + """ return int(time.time()) - starttime def extractIpAndPortFromPublicUrl() -> tuple: + """ + Pobiera dane z konfiguracji i zwraca + krotkę: adres IP i port. + """ + ip, port = "127.0.0.1", "5000" try: