backend: implement users methods according to frontend

This commit is contained in:
tylen 2025-10-31 17:29:33 +02:00
parent 92c76d7155
commit 3f074e895d
5 changed files with 113 additions and 269 deletions

View File

@ -1,3 +1,4 @@
flask==3.0.2 flask==3.0.2
mysql-connector-python==9.4.0 mysql-connector-python==9.4.0
python_dotenv==1.1.1 python_dotenv==1.1.1
Flask-CORS==6.0.1

View File

@ -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

View File

@ -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 from enum import Enum
import sys import sys
import mysql.connector import mysql.connector
@ -14,32 +6,20 @@ import os
STARTUP_TABLE_CREATION_QUERIES = { STARTUP_TABLE_CREATION_QUERIES = {
"users": """CREATE TABLE IF NOT EXISTS users ( "users": """CREATE TABLE IF NOT EXISTS users (
Name varchar(255), Name varchar(255),
Attendance varchar(255), Attendance bool,
HasCar bool Password VARCHAR(2048)
);""", );""",
"car": """CREATE TABLE IF NOT EXISTS car ( "sessions": """CREATE TABLE IF NOT EXISTS sessions (
Name varchar(255), Token VARCHAR(2048),
Car varchar(255), Name 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)
);""", );""",
} }
class Severity(Enum): class Severity(Enum):
INFO = "INFO" INFO = "INFO"
WARNING = "WARNING" WARNING = "WARNING"
ERROR = "ERROR" ERROR = "ERROR"
class DBClient: class DBClient:
def __init__(self): def __init__(self):
self.db_server = os.environ.get('DB_SERVER') self.db_server = os.environ.get('DB_SERVER')
@ -48,16 +28,18 @@ class DBClient:
self.password = os.environ.get('ROOT_PWD') self.password = os.environ.get('ROOT_PWD')
self.database = os.environ.get('DB_NAME') self.database = os.environ.get('DB_NAME')
if not self.db_server: self.validate_env_variables() # Check for required environment variables
self.error("Environment variable 'DB_SERVER' is not set.") self.connection = self.open()
if not self.db_port: self.cursor = self.connection.cursor()
self.error("Environment variable 'DB_PORT' is not set.")
if not self.password: self.initialize_database()
self.error("Environment variable 'ROOT_PWD' is not set.")
if not self.database:
self.error("Environment variable 'DB_NAME' is not set.")
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, host=self.db_server,
port=self.db_port, port=self.db_port,
user=self.user, user=self.user,
@ -65,51 +47,39 @@ class DBClient:
database=self.database 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): def close(self):
self.cursor.close() self.cursor.close()
self.connection.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): def info(self, message):
self.message(severity=Severity.INFO, message=message) self.message(severity=Severity.INFO, message=message)
def warning(self, message): def warning(self, message):
self.message(severity=Severity.WARNING, message=message) self.message(severity=Severity.WARNING, message=message)
def error(self, message): def error(self, message):
self.message(severity=Severity.ERROR, message=message) self.message(severity=Severity.ERROR, message=message)
sys.exit(1)
def message(self, severity, message): def message(self, severity, message):
print(f'DBClient [{severity.value}]: {message}') print(f'DBClient [{severity.value}]: {message}')

View File

@ -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 import Flask, request, jsonify
from flask_cors import CORS
from dotenv import load_dotenv from dotenv import load_dotenv
from db_client import DBClient from db_client import DBClient
from car import registerCarEndpoints
from user import registerUserEndpoints from user import registerUserEndpoints
from suggestions import registerSuggestionsEndpoints
load_dotenv() load_dotenv()
app = Flask(__name__) 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() database = DBClient()
registerCarEndpoints(app=app, database=database)
registerUserEndpoints(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__": if __name__ == "__main__":
app.run(debug=True) app.run(debug=True)

View File

@ -6,106 +6,78 @@ user.py is a source for all user endpoints.
''' '''
from flask import request, jsonify from flask import request, jsonify
import os
def registerUserEndpoints(app, database): def registerUserEndpoints(app, database):
@app.route('/users', methods=['GET']) @app.route('/users/isSet', methods=['GET'])
def get_users(): def user_is_set():
query = f'SELECT * from users' user_name = request.args.get('userName')
users = database.query(query_str=query) try:
if not users: query = "SELECT * FROM users WHERE Name=%s"
return jsonify({"message": "No users exist"}), 404 result = database.query(query, params=(user_name,))
response = {} return jsonify(bool(result and result[0][2])), 200
for user in users: except mysql.connector.Error as err:
if len(user) != 3: # Log the error or handle it as necessary
return jsonify({'error': 'User data is corrupted'}), 500 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({ @app.route('/users/createPassword', methods=['POST'])
"name": user[0], def create_password():
"attendance": user[1], data = request.json
"has_car": bool(user[2]) user_name = data.get('userName')
}) password = data.get('password')
return jsonify(response), 200
@app.route('/user', methods=['GET']) # Check if the user already exists
def get_user(): query = "SELECT * FROM users WHERE Name=%s"
if not request.is_json: result = database.query(query, params=(user_name,))
return jsonify({'error': 'Request must contain JSON data'}), 400
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'): # Generate a session token
return jsonify({'error': 'Request must contain name field'}), 400 token = os.urandom(16).hex()
session_query = "INSERT INTO sessions (Token, Name) VALUES (%s, %s)"
query = f'SELECT * from users WHERE name = %s' database.query(session_query, params=(token,user_name))
output = database.query(query_str=query, params=(data['name'],))
if not output: return jsonify(success=True, token=token), 201 # Return success with token
return jsonify({"message": "No user by that name exist"}), 404 except Exception as e:
user = output[0] return jsonify(success=False, message='Ошибка при создании пароля: ' + str(e)), 500
if len(user) != 3:
return jsonify({'error': 'User data is corrupted'}), 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() @app.route('/login', methods=['POST'])
if not data.get('name') or not data.get('attendance') or data.get('has_car') is None: def login():
return jsonify({'error': 'JSON must contain user fields'}), 400 data = request.json
user_name = data.get('userName')
query = 'SELECT * from users WHERE name = %s' password = data.get('password')
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']))
database.commit() query = "SELECT * FROM users WHERE Name=%s AND Password=%s"
return jsonify({"message": "user added", "user": data}), 200 result = database.query(query, params=(user_name, password))
@app.route('/user', methods=['UPDATE'])
def update_user():
if not request.is_json:
return jsonify({'error': 'Request must contain JSON data'}), 400
data = request.get_json() if result:
if not data.get('name') or not data.get('attendance') or data.get('has_car') is None: token = os.urandom(16).hex() # Example token generation
return jsonify({'error': 'JSON must contain user fields'}), 400 session_query = "INSERT INTO sessions (Token, Name) VALUES (%s, %s)"
database.query(session_query, params=(token, user_name))
query = 'SELECT * from users WHERE name = %s' return jsonify(success=True, token=token), 200
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']))
database.commit() return jsonify(success=False), 401
return jsonify({"message": "user modified", "user": data}), 200
@app.route('/user', methods=['DELETE']) @app.route('/login/validateToken', methods=['POST'])
def delete_user(): def validate_token():
if not request.is_json: data = request.json
return jsonify({'error': 'Request must contain JSON data'}), 400 token = data.get('token')
user_name = data.get('userName')
data = request.get_json() query = "SELECT * FROM sessions WHERE Token=%s AND Name=%s"
if not data.get('name'): try:
return jsonify({'error': 'JSON must contain persons name to delete'}), 400 result = database.query(query, params=(token, user_name))
return jsonify(tokenValid=bool(result)), 200
query = 'SELECT * from users WHERE name = %s' except Exception as e:
output = database.query(query_str=query, params=(data['name'],)) return jsonify(success=False, message=str(e)), 500
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