diff --git a/.env.tmp b/.env.tmp new file mode 100644 index 0000000..d121f85 --- /dev/null +++ b/.env.tmp @@ -0,0 +1,2 @@ +KEY= # Key for JWT token +POSTGRES_URL=postgresql:// diff --git a/api/auth.py b/api/auth.py new file mode 100644 index 0000000..b62c6e5 --- /dev/null +++ b/api/auth.py @@ -0,0 +1,35 @@ +from flask import Blueprint, request, jsonify +from model.user import User +from db.repositories.user_repository import UserRepository # FIXME: authenticate_user as get_user +from utils.token import generateKey as getToken + +auth = Blueprint("auth", __name__) + + +@auth.route('/login', methods = ['POST']) +def login(): + if request.is_json: + req = request.json + + email = req.get('email') + password = req.get('password') + + if not email or not password: + return "Request must have email and password", 400 + + if len(email.strip()) < 4 or '@' not in email or '.' not in email: + return "Email is incorrect", 400 + + if len(password.strip()) < 8: + return "Password is too short", 400 + + user = UserRepository().authenticate_user(email, password) + if not user: + return "Wrong credentials", 400 + + token = getToken(user) + + return jsonify({'token': token, 'user': {'id': user.id, 'name': user.name, 'role': user.role}}) + + else: + return "Request is not a json", 400 diff --git a/api/loginapi.py b/api/loginapi.py deleted file mode 100644 index 299d109..0000000 --- a/api/loginapi.py +++ /dev/null @@ -1,13 +0,0 @@ -from flask import Blueprint, request - -from model import user - -loginBP = Blueprint("loginapi", __name__) - -@loginBP.route('/api/login', methods = ['POST']) -def login(): - email = request.form['email'] - password = request.form['password'] - #if(isvalid(email, password)): - us = user.initialize(email, password) - return us.toJSON() diff --git a/app.py b/app.py index 90e60b3..0ccbfa1 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,11 @@ +from sys import exit from flask import Flask -from api.loginapi import loginBP +from api.auth import auth +from utils.loadDotEnv import initializeENV + +if not initializeENV(): + exit(-1) app = Flask(__name__) -app.register_blueprint(loginBP) +app.register_blueprint(auth, url_prefix='/api/auth') diff --git a/db/repositories/ai_prediction_repository.py b/db/repositories/ai_prediction_repository.py index 11fde40..176fd9c 100644 --- a/db/repositories/ai_prediction_repository.py +++ b/db/repositories/ai_prediction_repository.py @@ -1,11 +1,9 @@ from typing import List, Optional from datetime import datetime, date -import logging +from loguru import logger from db.connection import get_connection from model.ai_prediction import AIPrediction -logger = logging.getLogger(__name__) - class AIPredictionsRepository: def get_all(self) -> List[AIPrediction]: try: @@ -77,12 +75,14 @@ class AIPredictionsRepository: logger.error(f"Ошибка получения последних прогнозов по товарам: {e}") return [] - def create_prediction(self, - product_id: str, - prediction_date: date, - days_until_stockout: int, - recommended_order: int, - confidence_score: float) -> Optional[int]: + def create_prediction( + self, + product_id: str, + prediction_date: date, + days_until_stockout: int, + recommended_order: int, + confidence_score: float + ) -> Optional[int]: try: with get_connection() as conn: with conn.cursor() as cur: diff --git a/db/repositories/inventory_repository.py b/db/repositories/inventory_repository.py index b514acb..c6873a1 100644 --- a/db/repositories/inventory_repository.py +++ b/db/repositories/inventory_repository.py @@ -1,12 +1,10 @@ # db/repositories/inventory_repository.py from typing import List, Optional, Tuple from datetime import datetime -import logging +from loguru import logger from db.connection import get_connection from model.inventory import InventoryRecord -logger = logging.getLogger(__name__) - class InventoryRepository: def get_all(self) -> List[InventoryRecord]: try: @@ -48,15 +46,17 @@ class InventoryRepository: logger.error(f"Ошибка получения записи инвентаризации {record_id}: {e}") return None - def create_record(self, - robot_id: str, - product_id: str, - quantity: int, - zone: str, - row_number: int, - shelf_number: int, - status: str, - scanned_at: datetime) -> Optional[int]: + def create_record( + self, + robot_id: str, + product_id: str, + quantity: int, + zone: str, + row_number: int, + shelf_number: int, + status: str, + scanned_at: datetime + ) -> Optional[int]: try: with get_connection() as conn: with conn.cursor() as cur: diff --git a/db/repositories/product_repository.py b/db/repositories/product_repository.py index 6de73b8..7fc3cbb 100644 --- a/db/repositories/product_repository.py +++ b/db/repositories/product_repository.py @@ -1,10 +1,8 @@ from typing import List, Optional -import logging +from loguru import logger from db.connection import get_connection from model.product import Product -logger = logging.getLogger(__name__) - class ProductRepository: def get_all(self) -> List[Product]: try: diff --git a/db/repositories/robot_repository.py b/db/repositories/robot_repository.py index 3092502..3ae1aff 100644 --- a/db/repositories/robot_repository.py +++ b/db/repositories/robot_repository.py @@ -1,10 +1,8 @@ from typing import List, Optional -import logging +from loguru import logger from db.connection import get_connection from model.robot import Robot -logger = logging.getLogger(__name__) - class RobotRepository: def get_all(self) -> List[Robot]: try: @@ -43,13 +41,15 @@ class RobotRepository: logger.error(f"Ошибка получения робота {robot_id}: {e}") return None - def update_robot(self, - robot_id: str, - status: str = None, - battery_level: int = None, - current_zone: str = None, - current_row: int = None, - current_shelf: int = None) -> bool: + def update_robot( + self, + robot_id: str, + status: str = None, + battery_level: int = None, + current_zone: str = None, + current_row: int = None, + current_shelf: int = None + ) -> bool: try: with get_connection() as conn: with conn.cursor() as cur: @@ -131,10 +131,12 @@ class RobotRepository: logger.error(f"Ошибка получения роботов в зоне {zone}: {e}") return [] - def create_robot(self, - robot_id: str, - status: str = 'active', - battery_level: int = 100) -> bool: + def create_robot( + self, + robot_id: str, + status: str = 'active', + battery_level: int = 100 + ) -> bool: try: with get_connection() as conn: with conn.cursor() as cur: diff --git a/db/repositories/user_repository.py b/db/repositories/user_repository.py index e6307fb..a5c88f2 100644 --- a/db/repositories/user_repository.py +++ b/db/repositories/user_repository.py @@ -1,10 +1,8 @@ from typing import List, Optional -import logging +from loguru import logger from model.user import User from db.connection import get_connection -logger = logging.getLogger(__name__) - class UserRepository: def get_all(self) -> List[User]: try: @@ -57,11 +55,13 @@ class UserRepository: logger.error(f"Ошибка получения пользователя по email {email}: {e}") return None - def create_user(self, - email: str, - password_hash: str, - name: str, - role: str) -> Optional[User]: + def create_user( + self, + email: str, + password_hash: str, + name: str, + role: str + ) -> Optional[User]: try: with get_connection() as conn: with conn.cursor() as cur: @@ -166,6 +166,9 @@ class UserRepository: return False def authenticate_user(self, email: str, password_hash: str) -> Optional[User]: + if not self.user_exists(email): + return + try: with get_connection() as conn: with conn.cursor() as cur: @@ -183,24 +186,6 @@ class UserRepository: logger.error(f"Ошибка аутентификации пользователя {email}: {e}") return None - def is_valid_authenticate(self, email: str, password_hash: str) -> bool: - try: - with get_connection() as conn: - with conn.cursor() as cur: - cur.execute(""" - SELECT 1 FROM users - WHERE email = %s AND password_hash = %s - """, (email, password_hash)) - is_valid = cur.fetchone() is not None - if is_valid: - logger.debug(f"Валидные учетные данные для пользователя {email}") - else: - logger.warning(f"Невалидные учетные данные для пользователя {email}") - return is_valid - except Exception as e: - logger.error(f"Ошибка проверки учетных данных пользователя {email}: {e}") - return False - def user_exists(self, email: str) -> bool: try: with get_connection() as conn: diff --git a/dependencies.txt b/dependencies.txt index 5758f3e..69c3592 100644 --- a/dependencies.txt +++ b/dependencies.txt @@ -1,4 +1,5 @@ flask==3.1.2 python-dotenv -psycopg-binary +psycopg2-binary pyjwt +loguru diff --git a/utils/loadDotEnv.py b/utils/loadDotEnv.py index 1edf728..0d6c510 100644 --- a/utils/loadDotEnv.py +++ b/utils/loadDotEnv.py @@ -1,12 +1,14 @@ import os from dotenv import load_dotenv +from loguru import logger as log -def initializeENV(): - dotenv_path = '../.env' - if os.path.exists(dotenv_path): - load_dotenv(dotenv_path) - print('.env is loaded') - return 1 +DOTENV_PATH = '.env' + +def initializeENV() -> bool: + if os.path.exists(DOTENV_PATH): + load_dotenv(DOTENV_PATH) + log.info('.env is loaded') + return True else: - print('.env isn`t loaded') - return 0 + log.error('.env isn`t loaded') + return False diff --git a/utils/token.py b/utils/token.py index 5a3468d..d14e602 100644 --- a/utils/token.py +++ b/utils/token.py @@ -1,7 +1,18 @@ import jwt import os +from time import time +from model.user import User -def generateKey(email, passwd): +def generateKey(user: User) -> dict: key = os.getenv('KEY') - encoded = jwt.encode({f"{email}": f"{passwd}"}, key, algorithm="HS256") + encoded = jwt.encode( + { + 'id': user.id, + 'name': user.name, + 'role': user.role, + 'iat': time() + }, + key, + algorithm="HS256" + ) return encoded