diff --git a/.envs_example/.local/.front-ms b/.envs_example/.local/.front-ms index 5022d78..27cc603 100644 --- a/.envs_example/.local/.front-ms +++ b/.envs_example/.local/.front-ms @@ -4,4 +4,4 @@ VITE_APP_AUTH_BASE_URL=http://localhost:5000 VITE_APP_RACE_BASE_URL=http://localhost:8000 VITE_APP_RACE_BASE_URL_WS=ws://localhost:8000 -VITE_APP_HOST=http://codequest.online \ No newline at end of file +VITE_APP_HOST=http://localhost diff --git a/.envs_example/.local/.race-ms b/.envs_example/.local/.race-ms index 99a3954..588e2ca 100644 --- a/.envs_example/.local/.race-ms +++ b/.envs_example/.local/.race-ms @@ -1,8 +1 @@ ALLOWED_HOSTS = "http://127.0.0.1:8080,http://127.0.0.1:8000" -PS_URL = "23" -PS_PORT = "2342" -PS_LOGIN = "234" -PS_DB_NAME = "asdf" -PS_PASSWORD = "fsdf" -REDIS_URL = "adsf" -REDIS_PORT = "sfe" \ No newline at end of file diff --git a/.envs_example/.local/.redis b/.envs_example/.local/.redis index 7a98db5..7c230ec 100644 --- a/.envs_example/.local/.redis +++ b/.envs_example/.local/.redis @@ -1,2 +1,2 @@ -REDIS_URL = "adsf" -REDIS_PORT = "sfe" \ No newline at end of file +REDIS_URL = "redis" +REDIS_PORT = "6379" diff --git a/.envs_example/.production/.auth-ms b/.envs_example/.production/.auth-ms index c909c11..976e8ec 100644 --- a/.envs_example/.production/.auth-ms +++ b/.envs_example/.production/.auth-ms @@ -1,2 +1,2 @@ JWT_SECRET_KEY="e7c83a28f8b8d597afedf2134979ed8ae9f472d1" -AUTH_CORS_ORIGINS="http://codequest.online,http://codequest.online:80,http://codequest.online:443,http://codequest.online:8080,http://codequest.online:8000" +AUTH_CORS_ORIGINS="http://codequest.online,http://codequest.online:80,http://codequest.online:8080,http://codequest.online:8000" diff --git a/.envs_example/.production/.front-ms b/.envs_example/.production/.front-ms index 6b31e6e..6795335 100644 --- a/.envs_example/.production/.front-ms +++ b/.envs_example/.production/.front-ms @@ -2,5 +2,6 @@ VITE_APP_VUEX_STRICT=true VITE_APP_AUTH_BASE_URL=http://codequest.online:5000 VITE_APP_RACE_BASE_URL=http://codequest.online:8000 +VITE_APP_RACE_BASE_URL_WS=ws://codequest.online:8000 -VITE_APP_HOST=http://codequest.online \ No newline at end of file +VITE_APP_HOST=http://codequest.online diff --git a/.envs_example/.production/.race-ms b/.envs_example/.production/.race-ms index 99a3954..d84c1ee 100644 --- a/.envs_example/.production/.race-ms +++ b/.envs_example/.production/.race-ms @@ -1,8 +1 @@ -ALLOWED_HOSTS = "http://127.0.0.1:8080,http://127.0.0.1:8000" -PS_URL = "23" -PS_PORT = "2342" -PS_LOGIN = "234" -PS_DB_NAME = "asdf" -PS_PASSWORD = "fsdf" -REDIS_URL = "adsf" -REDIS_PORT = "sfe" \ No newline at end of file +ALLOWED_HOSTS = "*,http://codequest.online,http://codequest.online:80,http://codequest.online:5000,http://codequest.online:8000" diff --git a/.envs_example/.production/.redis b/.envs_example/.production/.redis index 7a98db5..7c230ec 100644 --- a/.envs_example/.production/.redis +++ b/.envs_example/.production/.redis @@ -1,2 +1,2 @@ -REDIS_URL = "adsf" -REDIS_PORT = "sfe" \ No newline at end of file +REDIS_URL = "redis" +REDIS_PORT = "6379" diff --git a/.envs_example/.production_encrypted/.auth-ms b/.envs_example/.production_encrypted/.auth-ms new file mode 100644 index 0000000..1da2a42 --- /dev/null +++ b/.envs_example/.production_encrypted/.auth-ms @@ -0,0 +1,2 @@ +JWT_SECRET_KEY="e7c83a28f8b8d597afedf2134979ed8ae9f472d1" +AUTH_CORS_ORIGINS="https://codequest.online,https://codequest.online/race-ms" diff --git a/.envs_example/.production_encrypted/.front-ms b/.envs_example/.production_encrypted/.front-ms new file mode 100644 index 0000000..2498d87 --- /dev/null +++ b/.envs_example/.production_encrypted/.front-ms @@ -0,0 +1,7 @@ +VITE_APP_VUEX_STRICT=true + +VITE_APP_AUTH_BASE_URL=https://codequest.online/auth-ms +VITE_APP_RACE_BASE_URL=https://codequest.online/race-ms +VITE_APP_RACE_BASE_URL_WS=ws://codequest.online:8000 + +VITE_APP_HOST=https://codequest.online diff --git a/.envs_example/.production_encrypted/.postgres b/.envs_example/.production_encrypted/.postgres new file mode 100644 index 0000000..cb7a53e --- /dev/null +++ b/.envs_example/.production_encrypted/.postgres @@ -0,0 +1,5 @@ +POSTGRES_USER="postgres" +POSTGRES_PASSWORD="paSsw0rdExam91e" +POSTGRES_DB="codequest" + +SQLALCHEMY_DATABASE_URI="postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgresql:5432/${POSTGRES_DB}" \ No newline at end of file diff --git a/.envs_example/.production_encrypted/.race-ms b/.envs_example/.production_encrypted/.race-ms new file mode 100644 index 0000000..363f226 --- /dev/null +++ b/.envs_example/.production_encrypted/.race-ms @@ -0,0 +1 @@ +ALLOWED_HOSTS = "*,https://codequest.online,https://codequest.online/auth-ms,https://codequest.online/race-ms" diff --git a/.envs_example/.production_encrypted/.redis b/.envs_example/.production_encrypted/.redis new file mode 100644 index 0000000..7c230ec --- /dev/null +++ b/.envs_example/.production_encrypted/.redis @@ -0,0 +1,2 @@ +REDIS_URL = "redis" +REDIS_PORT = "6379" diff --git a/.readme-static/competition.png b/.readme-static/competition.png new file mode 100644 index 0000000..dbdc1df Binary files /dev/null and b/.readme-static/competition.png differ diff --git a/.readme-static/learn_fail.png b/.readme-static/learn_fail.png new file mode 100644 index 0000000..9cb1d5b Binary files /dev/null and b/.readme-static/learn_fail.png differ diff --git a/.readme-static/learn_success.png b/.readme-static/learn_success.png new file mode 100644 index 0000000..999ee0c Binary files /dev/null and b/.readme-static/learn_success.png differ diff --git a/.readme-static/main_menu.png b/.readme-static/main_menu.png new file mode 100644 index 0000000..92c7020 Binary files /dev/null and b/.readme-static/main_menu.png differ diff --git a/README.md b/README.md index dd01528..e7dd325 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,41 @@ -## Тема: -### Мини-игры для развития определенных навыков в программировании -> Описание: -> -> Гонки на регулярках. -> Микросервисы бла-бла lorem text... +# CodeQuest +> Проект реализован в рамках development hackathon.
+> Тема: Мини-игры для развития определенных навыков в программировании -## GitFlow: -- В ветке `master` проект готовый к релизу -- В `develop` ведем разработку и вмерживаем ветки фич -- Под каждый сервис своя ветка -(директория сервиса располагается в корневом каталоге проекта) -![gitflow](./.readme-static/gitflow.jpg) +> Авторы: [Дюсенов Асет](https://dyussenov.dev/), [Ким Максим](https://github.com/exynil), [Песков Сергей](https://peskov.dev/) - И не забывайте, 1 готовый блок кода (фича) = 1 коммит. +CodeQuest - игровая платформа представляющая коллекцию игр для разивтия определенных +скиллов в программировании. В качестве mvp представленна игра: **Гонки на регулярках** + -## Setup: +## RegexRace +Регулярки в языках программирования это очень мощный инструмент, +но к сожалению у многих он хромает. + +**RegexRace** имеет 2 режима: +1) Обучающий режим. Блок с теорией позволяющий подятнуть базу по регуляркам + + +2) Рейтинговый режим - позволяет пользователям соревноваться в написании регулярок на перегонки. + + +# Setup: - `git clone https://github.com/codequest-team/codequest.git` - `cd codequest` -- In the root of the project create `.env` file (and substitute your values if necessary): +- copy .envs and substitute your values if necessary: + ```bash + cp -r .envs_example .envs + ``` +- generate docker-compose with `docker-compose.sh` (param `dev` for local lauch) + ```bash + ./docker-compose.sh dev + ``` + or + ```bash + ./docker-compose.sh prod ``` - echo 'JWT_SECRET_KEY="e7c83a28f8b8d597afedf2134979ed8ae9f472d1"' > .env +- run project + ```bash + docker-compose up --build ``` -- `docker-compose up --build` diff --git a/auth-ms/poetry.lock b/auth-ms/poetry.lock index 7bc220f..da30696 100644 --- a/auth-ms/poetry.lock +++ b/auth-ms/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.1 and should not be changed by hand. [[package]] name = "alembic" @@ -241,6 +241,27 @@ files = [ docs = ["Sphinx", "docutils (<0.18)"] test = ["objgraph", "psutil"] +[[package]] +name = "gunicorn" +version = "20.1.0" +description = "WSGI HTTP Server for UNIX" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, + {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, +] + +[package.dependencies] +setuptools = ">=3.0" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + [[package]] name = "isort" version = "5.12.0" @@ -569,6 +590,23 @@ files = [ [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "setuptools" +version = "67.6.1" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-67.6.1-py3-none-any.whl", hash = "sha256:e728ca814a823bf7bf60162daf9db95b93d532948c4c0bea762ce62f60189078"}, + {file = "setuptools-67.6.1.tar.gz", hash = "sha256:257de92a9d50a60b8e22abfcbb771571fde0dbf3ec234463212027a4eeecbe9a"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "six" version = "1.16.0" @@ -704,4 +742,4 @@ watchdog = ["watchdog"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "dbccbcb54f85d3d0a6cb8158d839a77d5a6bcfccf8f735d8bf9d44ea51302e1f" +content-hash = "bb597b39ae9cbcbaad4012a1afc27b27d0cc3a25f77bf487a601ebe470c90022" diff --git a/auth-ms/pyproject.toml b/auth-ms/pyproject.toml index b5eba52..2cd0a55 100644 --- a/auth-ms/pyproject.toml +++ b/auth-ms/pyproject.toml @@ -15,6 +15,7 @@ flask-cors = "^3.0.10" flask-sqlalchemy = "^3.0.3" psycopg2 = "^2.9.5" flask-migrate = "^4.0.4" +gunicorn = "^20.1.0" [tool.poetry.group.dev.dependencies] diff --git a/auth-ms/services.py b/auth-ms/services.py deleted file mode 100644 index c543f58..0000000 --- a/auth-ms/services.py +++ /dev/null @@ -1,162 +0,0 @@ -from datetime import datetime -from functools import wraps -from typing import Literal, TypedDict - -import jwt # type: ignore -from flask import jsonify, request - -from app import app, db -from models import User - - -class JwtAccessPayload(TypedDict): - id: str - username: str - exp: datetime - token_type: Literal["access"] - - -class JwtRefreshPayload(TypedDict): - id: str - exp: datetime - token_type: Literal["refresh"] - - -def get_user_by_username(username) -> User: - return User.query.filter(User.username == username.lower()).first() - - -def get_user_by_id(id) -> User: - return User.query.filter(User.id == id).first() - - -def update_refresh_token(id, refresh_token) -> bool: - try: - user = get_user_by_id(id) - user.refresh_token = refresh_token - db.session.add(user) - db.session.commit() - return True - except Exception as e: - print("", e) - return False - - -def remove_refresh_token(id) -> tuple[dict, int]: - try: - user = get_user_by_id(id) - if user: - user.refresh_token = None - db.session.add(user) - db.session.commit() - return {"message": "Refresh token was deleted"}, 200 - except Exception as e: - print("", e) - return {"message": "failed to update update entry in db"}, 500 - - -def create_user(user: User) -> tuple[dict, int]: - """Returns jwt-tokens, or mistakes""" - try: - is_user_exists = db.session.query( - db.exists().where(User.username == user.username) - ).scalar() - if is_user_exists: - return {"message": "This username is already taken"}, 409 - - db.session.add(user) - db.session.commit() - - json_answer, status_code = generate_tokens(user) - return json_answer, status_code - - except Exception as e: - print("", e) - return {"message": "failed to write the user to the db"}, 500 - - -def _generate_access_token(user: User) -> str: - payload: JwtAccessPayload = { - "id": str(user.id), - "username": user.username, - "exp": datetime.utcnow() + app.config["JWT_ACCESS_EXPIRATION_DELTA"], - "token_type": "access", - } - return jwt.encode( - payload, - app.config["SECRET_KEY"], - algorithm="HS256", - ) - - -def _generate_refresh_token(user: User) -> str: - payload: JwtRefreshPayload = { - "id": str(user.id), - "exp": datetime.utcnow() + app.config["JWT_REFRESH_EXPIRATION_DELTA"], - "token_type": "refresh", - } - return jwt.encode( - payload, - app.config["SECRET_KEY"], - algorithm="HS256", - ) - - -def generate_tokens(user: User) -> tuple[dict, int]: - access_token = _generate_access_token(user) - refresh_token = _generate_refresh_token(user) - - is_result_success = update_refresh_token(user.id, refresh_token) - - if is_result_success: - return { - "access_token": access_token, - "refresh_token": refresh_token, - }, 200 - return {"message": "Can't update refresh_token in db"}, 500 - - -def refresh_tokens(refresh_token) -> tuple[dict, int]: - if not refresh_token: - return {"message": "Refresh token is missing!"}, 401 - - try: - payload = jwt.decode( - refresh_token, app.config["SECRET_KEY"], algorithms=["HS256"] - ) - user = get_user_by_id(payload.get("id")) - if ( - payload.get("token_type") != "refresh" - or user.refresh_token != refresh_token - ): - return {"message": "Refresh token is invalid!"}, 401 - except jwt.ExpiredSignatureError: - return {"message": "Refresh token has expired!"}, 401 - except jwt.InvalidTokenError: - return {"message": "Refresh token is invalid!"}, 401 - - json_answer, status_code = generate_tokens(user) - return json_answer, status_code - - -def token_required(func): - @wraps(func) - def wrapper(*args, **kwargs): - access_token = request.headers.get("Authorization") - if not access_token: - return jsonify({"message": "Access token is missing!"}), 401 - - try: - payload = jwt.decode( - access_token, app.config["SECRET_KEY"], algorithms=["HS256"] - ) - if payload.get("token_type") != "access": - return jsonify({"message": "Access token is invalid!"}), 401 - except jwt.ExpiredSignatureError: - return jsonify({"message": "Access token has expired!"}), 401 - except jwt.InvalidTokenError: - return jsonify({"message": "Access token is invalid!"}), 401 - - return func(payload.get("id"), *args, **kwargs) - - return wrapper diff --git a/auth-ms/services/__init__.py b/auth-ms/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth-ms/services/jwt_service.py b/auth-ms/services/jwt_service.py new file mode 100644 index 0000000..983a970 --- /dev/null +++ b/auth-ms/services/jwt_service.py @@ -0,0 +1,122 @@ +from datetime import datetime +from functools import wraps +from typing import Literal, TypedDict + +import jwt # type: ignore +from flask import jsonify, request + +from app import app +from models import User + +from .user_service import get_user_by_id, update_refresh_token + + +class JwtPayload(TypedDict): + id: str + username: str + nickname: str + is_guest: bool + exp: datetime + token_type: Literal["access", "refresh"] + + +def _generate_a_token(user, is_guest, exp_delta, token_type) -> str: + payload: JwtPayload = { + "id": str(user.id), + "username": user.username, + "nickname": user.nickname, + "is_guest": is_guest, + "exp": datetime.utcnow() + exp_delta, + "token_type": token_type, + } + return jwt.encode( + payload, + app.config["SECRET_KEY"], + algorithm="HS256", + ) + + +def generate_tokens(user: User, is_guest: bool) -> tuple[dict, int]: + access_token = _generate_a_token( + user=user, + is_guest=is_guest, + exp_delta=app.config["JWT_ACCESS_EXPIRATION_DELTA"], + token_type="access", + ) + refresh_token = _generate_a_token( + user=user, + is_guest=is_guest, + exp_delta=app.config["JWT_REFRESH_EXPIRATION_DELTA"], + token_type="refresh", + ) + tokens = {"access_token": access_token, "refresh_token": refresh_token} + + if is_guest: + return tokens, 200 + else: + is_result_success = update_refresh_token(user.id, refresh_token) + if not is_result_success: + return {"message": "Can't update refresh_token in db"}, 500 + + return tokens, 200 + + +def refresh_tokens(refresh_token) -> tuple[dict, int]: + if not refresh_token: + return {"message": "Refresh token is missing!"}, 401 + + try: + payload = jwt.decode( + refresh_token, app.config["SECRET_KEY"], algorithms=["HS256"] + ) + except jwt.ExpiredSignatureError: + return {"message": "Refresh token has expired!"}, 401 + except jwt.InvalidTokenError: + return {"message": "Refresh token is invalid!"}, 401 + + if payload.get("token_type") != "refresh": + return {"message": "Refresh token is invalid!"}, 401 + is_guest = payload.get("is_guest") + if is_guest: + user = User( + id=payload.get("id"), + username=payload.get("username"), + nickname=payload.get("nickname"), + ) + else: + user = get_user_by_id(payload.get("id")) + if user.refresh_token != refresh_token: + return {"message": "Refresh token is invalid!"}, 401 + + json_answer, status_code = generate_tokens(user, is_guest) + return json_answer, status_code + + +def token_required(func): + @wraps(func) + def wrapper(*args, **kwargs): + access_token = request.headers.get("Authorization") + if not access_token: + return jsonify({"message": "Access token is missing!"}), 401 + + try: + payload = jwt.decode( + access_token, app.config["SECRET_KEY"], algorithms=["HS256"] + ) + except jwt.ExpiredSignatureError: + return jsonify({"message": "Access token has expired!"}), 401 + except jwt.InvalidTokenError: + return jsonify({"message": "Access token is invalid!"}), 401 + + if payload.get("token_type") != "access": + return jsonify({"message": "Access token is invalid!"}), 401 + + user = User( + id=payload.get("id"), + username=payload.get("username"), + nickname=payload.get("nickname"), + ) + + return func(user, *args, **kwargs) + + return wrapper diff --git a/auth-ms/services/user_service.py b/auth-ms/services/user_service.py new file mode 100644 index 0000000..cef0baa --- /dev/null +++ b/auth-ms/services/user_service.py @@ -0,0 +1,105 @@ +import random +import re +import string +import uuid + +from app import db +from models import User + + +def get_user_by_username(username) -> User: + return User.query.filter(User.username == username.lower()).first() + + +def get_user_by_id(id) -> User: + return User.query.filter(User.id == id).first() + + +def remove_refresh_token(id) -> tuple[dict, int]: + try: + user = get_user_by_id(id) + if user: + user.refresh_token = None + db.session.add(user) + db.session.commit() + return {"message": "Refresh token was deleted"}, 200 + except Exception as e: + print("", e) + return {"message": "failed to update update entry in db"}, 500 + + +def update_refresh_token(id, refresh_token) -> bool: + try: + user = get_user_by_id(id) + if user: + user.refresh_token = refresh_token + db.session.add(user) + db.session.commit() + return True + return False + except Exception as e: + print("", e) + return False + + +from .jwt_service import generate_tokens + + +def create_user(username, nickname, password: str) -> tuple[dict, int]: + """Returns jwt-tokens, or mistakes""" + if not username or not password: + return {"message": "Username or password is missing"}, 401 + + if not _is_valid_username(username): + return { + "message": "Usernames with the `guest-` prefix are forbidden" + }, 409 + + try: + is_user_exists = db.session.query( + db.exists().where(User.username == username) + ).scalar() + if is_user_exists: + return {"message": "This username is already taken"}, 409 + + user = User(username=username, nickname=nickname, password=password) + db.session.add(user) + db.session.commit() + + json_answer, status_code = generate_tokens(user=user, is_guest=False) + return json_answer, status_code + + except Exception as e: + print("", e) + return {"message": "failed to write the user to the db"}, 500 + + +def create_guest(nickname: str) -> tuple[dict, int]: + user = User( + id=uuid.uuid4(), + username=_generate_guest_username(), + nickname=nickname, + ) + json_answer, status_code = generate_tokens(user=user, is_guest=True) + return json_answer, status_code + + +def _is_valid_username(username: str): + """Usernames with the `guest-` prefix are forbidden""" + + if re.match("^guest-.*", username): + return False + return True + + +def _generate_guest_username() -> str: + random_string = _get_random_string(5) + return f"guest-{random_string}" + + +def _get_random_string(size=5) -> str: + chars = string.ascii_lowercase + string.digits + random_string = "" + for i in range(size): + random_string += random.choice(chars) + return random_string diff --git a/auth-ms/views.py b/auth-ms/views.py index dc2e283..ee5758d 100644 --- a/auth-ms/views.py +++ b/auth-ms/views.py @@ -1,27 +1,38 @@ from flask import jsonify, request from app import app -from models import User -from services import ( +from services.user_service import ( + create_guest, create_user, - generate_tokens, - get_user_by_id, get_user_by_username, remove_refresh_token, +) +from services.jwt_service import ( + generate_tokens, refresh_tokens, token_required, ) +@app.route("/get-guest", methods=["POST"]) +def get_guest(): + """Makes user with random data and returns tokens""" + + json_answer, status_code = create_guest(request.form.get("nickname")) + return jsonify(json_answer), status_code + + @app.route("/login", methods=["POST"]) def login(): - # Find user in database + """Authenticates and returns tokens""" + username = request.form.get("username") password = request.form.get("password") + user = get_user_by_username(username) if user and user.verify_password(password): - json_answer, status_code = generate_tokens(user) + json_answer, status_code = generate_tokens(user=user, is_guest=False) return jsonify(json_answer), status_code else: return jsonify({"message": "Invalid username or password"}), 401 @@ -29,18 +40,20 @@ def login(): @app.route("/signup", methods=["POST"]) def signup(): - user = User( + """Creates a user and returns tokens""" + + json_answer, status_code = create_user( username=request.form.get("username"), nickname=request.form.get("nickname"), password=request.form.get("password"), ) - - json_answer, status_code = create_user(user) return jsonify(json_answer), status_code @app.route("/refresh") def refresh(): + """Returns new tokens or mistakes""" + refresh_token = request.headers.get("Authorization") json_answer, status_code = refresh_tokens(refresh_token) return jsonify(json_answer), status_code @@ -48,7 +61,7 @@ def refresh(): @app.route("/verify") @token_required -def verify(user_id): +def verify(user): """Verifies if the token is valid""" return jsonify({"message": "Access token is valid"}), 200 @@ -56,18 +69,16 @@ def verify(user_id): @app.route("/get-credentials") @token_required -def get_credentials(user_id): - """decode token and return content""" - - user = get_user_by_id(user_id) +def get_credentials(user): + """Decodes token and return user's credentials""" return jsonify({"username": user.username, "nickname": user.nickname}), 200 @app.route("/logout") @token_required -def logout(user_id): - """decode token and return content""" +def logout(user): + """Deletes refresh_token from database""" - json_answer, status_code = remove_refresh_token(user_id) + json_answer, status_code = remove_refresh_token(user.id) return jsonify(json_answer), status_code diff --git a/docker-compose.sh b/docker-compose.sh index d8123cd..0272abb 100755 --- a/docker-compose.sh +++ b/docker-compose.sh @@ -22,11 +22,10 @@ DEV=$( ports: - 5432:5432 - auth-ms: container_name: 'auth-ms' build: ./auth-ms/ - command: python main.py + command: gunicorn main:app --bind 0.0.0.0:5000 restart: always env_file: - ./.envs/.local/.auth-ms @@ -39,6 +38,7 @@ DEV=$( build: ./race-ms working_dir: /race-ms env_file: + - ./.envs/.local/.auth-ms - ./.envs/.local/.race-ms - ./.envs/.local/.postgres - ./.envs/.local/.redis @@ -73,14 +73,125 @@ EOF PROD=$( cat < diff --git a/race-ms/main.py b/race-ms/main.py index f1b5434..0d55483 100644 --- a/race-ms/main.py +++ b/race-ms/main.py @@ -17,8 +17,8 @@ version='1.0.0', ) -#origins = settings.ALLOWED_HOSTS.split(',') -origins = ['*'] +origins = settings.ALLOWED_HOSTS.split(',') +# origins = ['*'] app.add_middleware( CORSMiddleware,