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` ведем разработку и вмерживаем ветки фич
-- Под каждый сервис своя ветка
-(директория сервиса располагается в корневом каталоге проекта)
-
+> Авторы: [Дюсенов Асет](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,