From 5f344aca861a55b69365a94173dbd06929477620 Mon Sep 17 00:00:00 2001 From: Ethan Clay <ethanclay2017@gmail.com> Date: Fri, 14 Feb 2025 12:40:57 +0000 Subject: [PATCH] Add SQL on inital load, depending on init.sql file --- app/__init__.py | 61 +++++++++++++++++++++++++++++++++++++----- app/admin/routes.py | 19 +++++++++++++ app/models/listings.py | 3 +++ docker-compose.yml | 1 - 4 files changed, 76 insertions(+), 8 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index fa82092..1cbd84a 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -9,6 +9,8 @@ from dotenv import load_dotenv from app.logger import auth_logger from functools import wraps import os +import pymysql +from sqlalchemy.sql import text # Initialize extensions db = SQLAlchemy() @@ -28,11 +30,55 @@ def permission_required(permission): return decorated_function return decorator - super_admin_permission = Permission(RoleNeed('super-admin')) admin_permission = Permission(RoleNeed('admin')) user_permission = Permission(RoleNeed('user')) + +def create_database_if_not_exists(db_host, db_user, db_password, db_name): + # Connect using Root to create schema if doesn't exist and then create user for that schema + connection = pymysql.connect( + host=db_host, + user='root', + password=db_password + ) + + try: + with connection.cursor() as cursor: + cursor.execute(f"CREATE DATABASE IF NOT EXISTS {db_name}") + # Create the user from .env details + cursor.execute(f"CREATE USER IF NOT EXISTS '{db_user}'@'%' IDENTIFIED BY '{db_password}'") + cursor.execute(f"GRANT ALL PRIVILEGES ON {db_name}.* TO '{db_user}'@'%'") + cursor.execute(f"FLUSH PRIVILEGES") + connection.commit() + finally: + connection.close() + + # Reconnect using user in .env to prevent permission issues + connection = pymysql.connect( + host=db_host, + user=db_user, + password=db_password, + database=db_name + ) + + try: + with connection.cursor() as cursor: + # Check if tables exist + cursor.execute("SHOW TABLES;") + tables = cursor.fetchall() + if not tables: + with open('sql-setup/init.sql', 'r') as file: + sql_commands = file.read().split(';') + for command in sql_commands: + if command.strip(): + cursor.execute(command) + connection.commit() + finally: + connection.close() + + + def create_app(config_class=Config): app = Flask(__name__) app.config.from_object(config_class) @@ -48,9 +94,12 @@ def create_app(config_class=Config): db_password = os.getenv("DATABASE_PASSWORD") db_name = os.getenv("DATABASE_NAME") + create_database_if_not_exists(db_host, db_user, db_password, db_name) + app.config['SECRET_KEY'] = os.getenv('SECRET_KEY') app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{db_user}:{db_password}@{db_host}/{db_name}" + # Initialize extensions with the app db.init_app(app) migrate.init_app(app, db) @@ -75,7 +124,6 @@ def create_app(config_class=Config): else: auth_logger.debug(f'No role found for user {identity.user.username}.') - # Add global template variables @app.context_processor def set_global_html_variable_values(): @@ -99,13 +147,13 @@ def create_app(config_class=Config): 'user_permission': g.user_permission, 'super_admin_permission': g.super_admin_permission } - + # @app.errorhandler(Exception) # def handle_exception(e): # app.logger.error(f"Unhandled exception: {e}") # session['error_message'] = str(e) # return redirect(url_for('errors.quandary')) - + @app.errorhandler(403) def handle_exception(e): app.logger.error(f"Unhandled exception: {e}") @@ -133,10 +181,9 @@ def create_app(config_class=Config): g.admin_permission = admin_permission g.is_admin = True - login_manager.login_view = 'profile.login' - - return app + login_manager.login_view = 'profile.login' + return app @login_manager.user_loader def load_user(user_id): diff --git a/app/admin/routes.py b/app/admin/routes.py index 8e4775e..9ca30fe 100644 --- a/app/admin/routes.py +++ b/app/admin/routes.py @@ -4,6 +4,7 @@ from app import admin_permission, permission_required, super_admin_permission from app.models import Listings, ListingImages, User from app.admin import bp from app.main.utils import generate_time_options +from sqlalchemy.sql import text @bp.route('/home') @@ -296,3 +297,21 @@ def delete_image(image_id): except Exception as e: db.session.rollback() return jsonify({'success': False, 'error': str(e)}), 500 + + +@bp.route('/init/database', methods=['GET']) +def check_database_exists(): + try: + if Listings.check_table_exists(): + flash ("Database already exists, 'error") + else: + raise Exception('Schema exists but database does not') + except: + with open('sql-setup/init.sql', 'r') as file: + sql_commands = file.read().split(';') + for command in sql_commands: + if command.strip(): + db.session.execute(text(command)) + + db.session.commit() + flash ("Database initialised", 'success') diff --git a/app/models/listings.py b/app/models/listings.py index 7c00b96..7d44f1a 100644 --- a/app/models/listings.py +++ b/app/models/listings.py @@ -80,3 +80,6 @@ class Listings(db.Model): 'business_fair_cost': self.business_fair_cost } + @classmethod + def check_table_exists(cls): + return cls.get_top_listings(1) diff --git a/docker-compose.yml b/docker-compose.yml index 33e6fe1..8b59898 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,7 +31,6 @@ services: MYSQL_PASSWORD: ${DATABASE_PASSWORD} volumes: - mysql_data:/var/lib/mysql - - ./sql-setup/init.sql:/docker-entrypoint-initdb.d/init.sql networks: - network -- GitLab