From 3f074e895d1f00bf19fdbb89d7e7bff6812dff44 Mon Sep 17 00:00:00 2001 From: tylen Date: Fri, 31 Oct 2025 17:29:33 +0200 Subject: [PATCH] backend: implement users methods according to frontend --- backend/requirements.txt | 3 +- backend/src/car.py | 93 ----------------------- backend/src/db_client.py | 112 +++++++++++----------------- backend/src/server.py | 20 ++--- backend/src/user.py | 154 ++++++++++++++++----------------------- 5 files changed, 113 insertions(+), 269 deletions(-) delete mode 100644 backend/src/car.py diff --git a/backend/requirements.txt b/backend/requirements.txt index 228de33..4b49415 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,3 +1,4 @@ flask==3.0.2 mysql-connector-python==9.4.0 -python_dotenv==1.1.1 \ No newline at end of file +python_dotenv==1.1.1 +Flask-CORS==6.0.1 \ No newline at end of file diff --git a/backend/src/car.py b/backend/src/car.py deleted file mode 100644 index 4b4bac3..0000000 --- a/backend/src/car.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -''' -car.py is a source for all car endpoints. -''' - -from flask import request, jsonify - -def registerCarEndpoints(app, database): - @app.route('/car', methods=['GET']) - def get_car(): - if not request.is_json: - return jsonify({'error': 'Request must contain JSON data'}), 400 - - data = request.get_json() - - if not data.get('name'): - return jsonify({'error': 'Request must contain name field'}), 400 - - query = f'SELECT * from car WHERE name = %s' - output = database.query(query_str=query, params=(data['name'],)) - if not output: - return jsonify({"message": "No car by that name exist"}), 404 - car = output[0] - if len(car) != 3: - return jsonify({'error': 'Car data is corrupted'}), 500 - - response = { - "name": car[0], - "car": car[1], - "freeCarSpaces": car[2] - } - return jsonify(response), 200 - - @app.route('/car', methods=['POST']) - def add_car(): - if not request.is_json: - return jsonify({'error': 'Request must contain JSON data'}), 400 - - data = request.get_json() - if not data.get('name') or not data.get('car') or data.get('spaces') is None: - return jsonify({'error': 'JSON must contain car and name fields'}), 400 - - query = 'SELECT * from car WHERE name = %s' - output = database.query(query_str=query, params=(data['name'],)) - if output: - return jsonify({'error': 'A person has a car already'}), 409 - - query = 'INSERT into car (Name, Car, FreeCarSpaces) VALUES (%s, %s, %s)' - output = database.query(query_str=query, params=(data['name'],data['car'],data['spaces'])) - - database.commit() - return jsonify({"message": "car added", "car": data}), 200 - - @app.route('/car', methods=['UPDATE']) - def update_car(): - if not request.is_json: - return jsonify({'error': 'Request must contain JSON data'}), 400 - - data = request.get_json() - if not data.get('name') or not data.get('car') or data.get('spaces') is None: - return jsonify({'error': 'JSON must contain car,name,space fields'}), 400 - - query = 'SELECT * from car WHERE name = %s' - output = database.query(query_str=query, params=(data['name'],)) - if not output: - return jsonify({'error': 'Such car does not exist. Add it first'}), 409 - - query = 'UPDATE car SET Name = %s, Car = %s, FreeCarSpaces = %s' - output = database.query(query_str=query, params=(data['name'],data['car'],data['spaces'])) - - database.commit() - return jsonify({"message": "car modified", "car": data}), 200 - - @app.route('/car', methods=['DELETE']) - def delete_car(): - if not request.is_json: - return jsonify({'error': 'Request must contain JSON data'}), 400 - - data = request.get_json() - if not data.get('name'): - return jsonify({'error': 'JSON must contain persons name whose car to delete'}), 400 - - query = 'SELECT * from car WHERE name = %s' - output = database.query(query_str=query, params=(data['name'],)) - if not output: - return jsonify({'error': 'Such person does not have a car'}), 409 - - query = 'DELETE FROM car WHERE Name = %s' - output = database.query(query_str=query, params=(data['name'],)) - database.commit() - return jsonify({"message": "car deleted"}), 200 \ No newline at end of file diff --git a/backend/src/db_client.py b/backend/src/db_client.py index a9850f0..bf8a8fb 100644 --- a/backend/src/db_client.py +++ b/backend/src/db_client.py @@ -1,11 +1,3 @@ - -#!/usr/bin/env python -# encoding: utf-8 - -''' -db_client.py is the module for managing teh Dungeon's database services. -''' - from enum import Enum import sys import mysql.connector @@ -14,32 +6,20 @@ import os STARTUP_TABLE_CREATION_QUERIES = { "users": """CREATE TABLE IF NOT EXISTS users ( Name varchar(255), - Attendance varchar(255), - HasCar bool + Attendance bool, + Password VARCHAR(2048) );""", - "car": """CREATE TABLE IF NOT EXISTS car ( - Name varchar(255), - Car varchar(255), - FreeCarSpaces tinyint(1) - );""", - "suggestions": """CREATE TABLE IF NOT EXISTS suggestions ( - id INT PRIMARY KEY AUTO_INCREMENT, - Name VARCHAR(255), - Suggestion VARCHAR(2048) - );""", - "passengers": """CREATE TABLE IF NOT EXISTS passengers ( - Name varchar(255), - Car varchar(255) + "sessions": """CREATE TABLE IF NOT EXISTS sessions ( + Token VARCHAR(2048), + Name varchar(255) );""", } - class Severity(Enum): INFO = "INFO" WARNING = "WARNING" ERROR = "ERROR" - class DBClient: def __init__(self): self.db_server = os.environ.get('DB_SERVER') @@ -48,16 +28,18 @@ class DBClient: self.password = os.environ.get('ROOT_PWD') self.database = os.environ.get('DB_NAME') - if not self.db_server: - self.error("Environment variable 'DB_SERVER' is not set.") - if not self.db_port: - self.error("Environment variable 'DB_PORT' is not set.") - if not self.password: - self.error("Environment variable 'ROOT_PWD' is not set.") - if not self.database: - self.error("Environment variable 'DB_NAME' is not set.") + self.validate_env_variables() # Check for required environment variables + self.connection = self.open() + self.cursor = self.connection.cursor() + + self.initialize_database() - self.connection = mysql.connector.connect( + def validate_env_variables(self): + if not self.db_server or not self.db_port or not self.password or not self.database: + self.error("Missing one or more environment variables.") + + def open(self): + return mysql.connector.connect( host=self.db_server, port=self.db_port, user=self.user, @@ -65,51 +47,39 @@ class DBClient: database=self.database ) - self.cursor = self.connection.cursor() - self.initialize_database() - self.commit() - - def create_database(self, db_name): - query = f"CREATE DATABASE IF NOT EXISTS `{db_name}`;" - self.cursor.execute(query) - - def switch_database(self, db_name): - self.connection.database = db_name - - def initialize_database(self): - self.create_database(self.database) - self.switch_database(self.database) - self.query(STARTUP_TABLE_CREATION_QUERIES['users']) - self.query(STARTUP_TABLE_CREATION_QUERIES['car']) - self.query(STARTUP_TABLE_CREATION_QUERIES['suggestions']) - self.query(STARTUP_TABLE_CREATION_QUERIES['passengers']) - - def query(self, query_str, quiet=False, params=None): - self.info(f'Executing query: {query_str}') - self.cursor.execute(query_str, params) - if quiet: - return [] - return self.cursor.fetchall() - - def commit(self): - self.info('Commiting actions to DB') - self.connection.commit() - def close(self): self.cursor.close() self.connection.close() - + + def initialize_database(self): + self.query(STARTUP_TABLE_CREATION_QUERIES['users']) + self.query(STARTUP_TABLE_CREATION_QUERIES['sessions']) + + def query(self, query_str, params=None): + try: + self.info(f'Executing query: {query_str}') + self.cursor.execute(query_str, params) + + if 'SELECT' in query_str: + return self.cursor.fetchall() # Return results for SELECT queries + else: + self.commit() # Commit if it's a non-SELECT query + except Exception as e: + self.error(f"Query failed: {str(e)}") + + def commit(self): + self.info('Committing actions to DB') + self.connection.commit() + def info(self, message): self.message(severity=Severity.INFO, message=message) - - + def warning(self, message): self.message(severity=Severity.WARNING, message=message) - + def error(self, message): self.message(severity=Severity.ERROR, message=message) - sys.exit(1) - + def message(self, severity, message): print(f'DBClient [{severity.value}]: {message}') - + diff --git a/backend/src/server.py b/backend/src/server.py index 91735a7..2384d22 100644 --- a/backend/src/server.py +++ b/backend/src/server.py @@ -6,27 +6,21 @@ server.py is the main source file for the Dungeon's backend service. ''' from flask import Flask, request, jsonify +from flask_cors import CORS from dotenv import load_dotenv from db_client import DBClient -from car import registerCarEndpoints from user import registerUserEndpoints -from suggestions import registerSuggestionsEndpoints load_dotenv() app = Flask(__name__) +app.config['JSON_AS_ASCII'] = False # Ensures non-ASCII characters are preserved +allowed_origins = [ + "https://nyipyatki.davydovcloud.com", + "https://nyipyatki-dev.davydovcloud.com", +] +CORS(app, resources={r"*": {"origins": allowed_origins}}) # Only allow example.com database = DBClient() -registerCarEndpoints(app=app, database=database) registerUserEndpoints(app=app, database=database) -registerSuggestionsEndpoints(app=app, database=database) - -@app.route('/login', methods=['POST']) -def login(): - if request.is_json: - return jsonify({"hello": "user"}), 200 - else: - return jsonify({'error': 'Request must contain JSON data'}), 400 - - if __name__ == "__main__": app.run(debug=True) \ No newline at end of file diff --git a/backend/src/user.py b/backend/src/user.py index 167250d..a4ca84f 100644 --- a/backend/src/user.py +++ b/backend/src/user.py @@ -6,106 +6,78 @@ user.py is a source for all user endpoints. ''' from flask import request, jsonify +import os def registerUserEndpoints(app, database): - @app.route('/users', methods=['GET']) - def get_users(): - query = f'SELECT * from users' - users = database.query(query_str=query) - if not users: - return jsonify({"message": "No users exist"}), 404 - response = {} - for user in users: - if len(user) != 3: - return jsonify({'error': 'User data is corrupted'}), 500 + @app.route('/users/isSet', methods=['GET']) + def user_is_set(): + user_name = request.args.get('userName') + try: + query = "SELECT * FROM users WHERE Name=%s" + result = database.query(query, params=(user_name,)) + return jsonify(bool(result and result[0][2])), 200 + except mysql.connector.Error as err: + # Log the error or handle it as necessary + app.logger.error(f"Error: {err}") + return jsonify({"error": "Database error occurred"}), 500 + except Exception as e: + # Handle unexpected errors + app.logger.error(f"Unexpected error: {e}") + return jsonify({"error": "Internal server error"}), 500 # Check if password exists - response.update({ - "name": user[0], - "attendance": user[1], - "has_car": bool(user[2]) - }) - return jsonify(response), 200 + @app.route('/users/createPassword', methods=['POST']) + def create_password(): + data = request.json + user_name = data.get('userName') + password = data.get('password') - @app.route('/user', methods=['GET']) - def get_user(): - if not request.is_json: - return jsonify({'error': 'Request must contain JSON data'}), 400 + # Check if the user already exists + query = "SELECT * FROM users WHERE Name=%s" + result = database.query(query, params=(user_name,)) + + if result: + return jsonify(success=False, message='Пользователь уже создан'), 400 - data = request.get_json() + query = "INSERT INTO users (Name, Password) VALUES (%s, %s)" + + try: + database.query(query, params=(user_name, password)) - if not data.get('name'): - return jsonify({'error': 'Request must contain name field'}), 400 - - query = f'SELECT * from users WHERE name = %s' - output = database.query(query_str=query, params=(data['name'],)) - if not output: - return jsonify({"message": "No user by that name exist"}), 404 - user = output[0] - if len(user) != 3: - return jsonify({'error': 'User data is corrupted'}), 500 + # Generate a session token + token = os.urandom(16).hex() + session_query = "INSERT INTO sessions (Token, Name) VALUES (%s, %s)" + database.query(session_query, params=(token,user_name)) + + return jsonify(success=True, token=token), 201 # Return success with token + except Exception as e: + return jsonify(success=False, message='Ошибка при создании пароля: ' + str(e)), 500 - response = { - "name": user[0], - "attendance": user[1], - "has_car": bool(user[2]) - } - return jsonify(response), 200 - - @app.route('/user', methods=['POST']) - def add_user(): - if not request.is_json: - return jsonify({'error': 'Request must contain JSON data'}), 400 - data = request.get_json() - if not data.get('name') or not data.get('attendance') or data.get('has_car') is None: - return jsonify({'error': 'JSON must contain user fields'}), 400 - - query = 'SELECT * from users WHERE name = %s' - output = database.query(query_str=query, params=(data['name'],)) - if output: - return jsonify({'error': 'A person already exists'}), 409 - - query = 'INSERT into users (Name, Attendance, HasCar) VALUES (%s, %s, %s)' - output = database.query(query_str=query, params=(data['name'],data['attendance'],data['has_car'])) + @app.route('/login', methods=['POST']) + def login(): + data = request.json + user_name = data.get('userName') + password = data.get('password') - database.commit() - return jsonify({"message": "user added", "user": data}), 200 - - @app.route('/user', methods=['UPDATE']) - def update_user(): - if not request.is_json: - return jsonify({'error': 'Request must contain JSON data'}), 400 + query = "SELECT * FROM users WHERE Name=%s AND Password=%s" + result = database.query(query, params=(user_name, password)) - data = request.get_json() - if not data.get('name') or not data.get('attendance') or data.get('has_car') is None: - return jsonify({'error': 'JSON must contain user fields'}), 400 - - query = 'SELECT * from users WHERE name = %s' - output = database.query(query_str=query, params=(data['name'],)) - if not output: - return jsonify({'error': 'Such user does not exist. Add it first'}), 409 - - query = 'UPDATE user SET Name = %s, Attendance = %s, HasCar = %s' - output = database.query(query_str=query, params=(data['name'],data['attendance'],data['has_car'])) + if result: + token = os.urandom(16).hex() # Example token generation + session_query = "INSERT INTO sessions (Token, Name) VALUES (%s, %s)" + database.query(session_query, params=(token, user_name)) + return jsonify(success=True, token=token), 200 - database.commit() - return jsonify({"message": "user modified", "user": data}), 200 + return jsonify(success=False), 401 - @app.route('/user', methods=['DELETE']) - def delete_user(): - if not request.is_json: - return jsonify({'error': 'Request must contain JSON data'}), 400 - - data = request.get_json() - if not data.get('name'): - return jsonify({'error': 'JSON must contain persons name to delete'}), 400 - - query = 'SELECT * from users WHERE name = %s' - output = database.query(query_str=query, params=(data['name'],)) - if not output: - return jsonify({'error': 'Such person does not exist'}), 409 - - query = 'DELETE FROM users WHERE Name = %s' - output = database.query(query_str=query, params=(data['name'],)) - database.commit() - return jsonify({"message": "user deleted"}), 200 \ No newline at end of file + @app.route('/login/validateToken', methods=['POST']) + def validate_token(): + data = request.json + token = data.get('token') + user_name = data.get('userName') + query = "SELECT * FROM sessions WHERE Token=%s AND Name=%s" + try: + result = database.query(query, params=(token, user_name)) + return jsonify(tokenValid=bool(result)), 200 + except Exception as e: + return jsonify(success=False, message=str(e)), 500