Explorar o código

TG-3: Docker Setup and DB Creation

lanfr144 hai 4 semanas
pai
achega
ae711f7d4c
Modificáronse 4 ficheiros con 279 adicións e 0 borrados
  1. 59 0
      deploy.sh
  2. 92 0
      init.sql
  3. 25 0
      my.cnf
  4. 103 0
      setup_db.py

+ 59 - 0
deploy.sh

@@ -0,0 +1,59 @@
+#!/bin/bash
+# -----------------------------------------------------------------------------
+# Naked Environment Deployment Script for Ubuntu 24.04
+# Run this script to seamlessly fully provision the server for the AI Web App.
+# -----------------------------------------------------------------------------
+
+set -e
+
+echo "=========================================================="
+echo " Starting Full Environment Deployment for Ubuntu 24.04..."
+echo "=========================================================="
+
+# 1. Update system packages
+echo ""
+echo "[1/6] Updating APT cache and installing system dependencies (GCC, Python, MySQL Server)..."
+sudo apt update
+sudo apt install -y build-essential gcc python3-venv python3-dev python3-pip curl mysql-server
+
+# 2. Install Ollama natively
+echo ""
+echo "[2/6] Installing Ollama Engine..."
+curl -fsSL https://ollama.com/install.sh | sh
+
+# 3. Secure and setup MySQL Server configuration
+echo ""
+echo "[3/6] Configuring MySQL Server settings (Local infile & validation policies)..."
+# We inject the provided my.cnf configuration directly into the MySQL system config
+if [ -f "./my.cnf" ]; then
+    sudo cp ./my.cnf /etc/mysql/conf.d/custom_ai_app.cnf
+    echo "Custom MySQL configurations applied from my.cnf."
+else
+    echo "Warning: my.cnf not found in the current directory."
+fi
+
+sudo systemctl restart mysql
+echo "MySQL Service restarted."
+
+# 4. Set up Python Virtual Environment (PEP 668 compliant)
+echo ""
+echo "[4/6] Setting up Python 3 Virtual Environment ('venv')..."
+python3 -m venv venv
+# From here on, we temporarily export the paths to use the new virtual env directly
+export PATH="$PWD/venv/bin:$PATH"
+
+# 5. Install Required Python Libraries
+echo ""
+echo "[5/6] Installing Python dependencies via pip inside venv..."
+pip install --upgrade pip
+pip install pandas pymysql myloginpath streamlit ollama bcrypt
+
+echo ""
+echo "=========================================================="
+echo " 🎉 Environment Deployment Complete! "
+echo "=========================================================="
+echo ""
+echo "Next steps:"
+echo "1. Activate your virtual environment manually:  source venv/bin/activate"
+echo "2. Check your config.ini file details."
+echo "3. Run your setup script to configure database users:  python setup_db.py"

+ 92 - 0
init.sql

@@ -0,0 +1,92 @@
+-- ---------------------------------------------------------
+-- Initial Database and User Setup (Run as MySQL Root)
+-- ---------------------------------------------------------
+CREATE DATABASE IF NOT EXISTS food_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- 1. Create the Owner User
+-- Has full rights and can grant privileges to others.
+CREATE USER IF NOT EXISTS 'db_owner'@'%' IDENTIFIED BY 'owner_pass';
+GRANT ALL PRIVILEGES ON food_db.* TO 'db_owner'@'%' WITH GRANT OPTION;
+
+-- 2. Create the Reader User
+-- Has only connect and read permissions.
+CREATE USER IF NOT EXISTS 'db_reader'@'%' IDENTIFIED BY 'reader_pass';
+GRANT USAGE ON *.* TO 'db_reader'@'%';
+
+-- 3. Create the Loader User
+-- Has connect and data manipulation permissions to load files.
+CREATE USER IF NOT EXISTS 'db_loader'@'%' IDENTIFIED BY 'loader_pass';
+GRANT USAGE ON *.* TO 'db_loader'@'%';
+GRANT FILE ON *.* TO 'db_loader'@'%'; -- Essential for LOAD DATA INFILE from any directory
+
+-- 4. Create the App Auth User
+-- Segregation of Duties: Handles only users table for web application routing.
+CREATE USER IF NOT EXISTS 'db_app_auth'@'%' IDENTIFIED BY 'app_auth_placeholder_pass';
+-- Note: Replace 'app_auth_placeholder_pass' later outside this script.
+GRANT USAGE ON *.* TO 'db_app_auth'@'%';
+
+FLUSH PRIVILEGES;
+
+
+-- ---------------------------------------------------------
+-- Table Creation & Grants (Logically executed by db_owner)
+-- ---------------------------------------------------------
+USE food_db;
+
+-- NOTE: The syntax you provided (`read_csv_auto`) is specific to DuckDB!
+-- MySQL does NOT support `read_csv_auto()` to dynamically create tables from CSV.
+-- In MySQL, you MUST define the table schema first, and then use LOAD DATA INFILE.
+-- Here is the MySQL equivalent process:
+
+-- Step A.1: Create Web Users Table
+CREATE TABLE IF NOT EXISTS users (
+    id INT AUTO_INCREMENT PRIMARY KEY,
+    username VARCHAR(100) UNIQUE NOT NULL,
+    password_hash VARCHAR(255) NOT NULL,
+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+) ENGINE=InnoDB;
+
+GRANT SELECT, INSERT, UPDATE ON food_db.users TO 'db_app_auth'@'%';
+FLUSH PRIVILEGES;
+
+-- Step A.2: Create the table with known columns (Example structure for OpenFoodFacts)
+CREATE TABLE IF NOT EXISTS products (
+    code VARCHAR(50) PRIMARY KEY,
+    url TEXT,
+    creator VARCHAR(255),
+    created_t VARCHAR(50),
+    created_datetime VARCHAR(50),
+    last_modified_t VARCHAR(50),
+    last_modified_datetime VARCHAR(50),
+    product_name TEXT,
+    generic_name TEXT,
+    quantity VARCHAR(255),
+    packaging TEXT,
+    brands TEXT,
+    categories TEXT,
+    origins TEXT,
+    labels TEXT,
+    stores TEXT,
+    countries TEXT,
+    ingredients_text TEXT,
+    allergens TEXT,
+    traces TEXT,
+    
+    -- Add FULLTEXT index for context search on ingredients and products
+    FULLTEXT INDEX ft_idx_search (product_name, ingredients_text)
+) ENGINE=InnoDB;
+
+-- Step B: The Owner grants explicit privileges to the Reader and Loader
+GRANT SELECT ON food_db.products TO 'db_reader'@'%';
+GRANT SELECT, INSERT, UPDATE, DELETE, DROP, CREATE ON food_db.products TO 'db_loader'@'%';
+FLUSH PRIVILEGES;
+
+-- Step C: The Loader user would then run this MySQL command to import:
+/*
+LOAD DATA INFILE '/path/to/en.openfoodfacts.org.products.converted.csv'
+INTO TABLE products
+FIELDS TERMINATED BY '\t'
+ENCLOSED BY ''
+LINES TERMINATED BY '\n'
+IGNORE 1 ROWS;
+*/

+ 25 - 0
my.cnf

@@ -0,0 +1,25 @@
+[mysqld]
+# ---------------------------------------------------------
+# MySQL Configuration Settings
+# ---------------------------------------------------------
+
+# 1. Allow loading files from ANY directory
+# Setting secure_file_priv to empty string removes all directory restrictions 
+# for the LOAD DATA INFILE command.
+secure_file_priv = ""
+
+# Enable local infile on the server side
+local_infile = 1
+
+# 2. Allow any password (disabling strict password requirements)
+# Note: For MySQL 8+, the variable uses a dot (validate_password.policy).
+# For MySQL 5.7, it uses an underscore (validate_password_policy).
+validate_password.policy = LOW
+validate_password.length = 1
+validate_password.mixed_case_count = 0
+validate_password.number_count = 0
+validate_password.special_char_count = 0
+
+[mysql]
+# Enable local infile on the client side
+local_infile = 1

+ 103 - 0
setup_db.py

@@ -0,0 +1,103 @@
+import pymysql
+import getpass
+import os
+
+def run_db_setup():
+    """ 
+    This Python script prompts for passwords securely and executes CREATE USER / GRANT 
+    statements to provision the MySQL server dynamically without config files.
+    """
+    print("Welcome to Local Food AI Initial Setup.")
+    print("WARNING: This will configure your MySQL server. You must know the MySQL root password.\n")
+    
+    root_password = getpass.getpass("Enter the MySQL 'root' password: ")
+    
+    # Prompt for the new user passwords (so they aren't stored anywhere!)
+    print("\nPlease define the passwords for the service accounts:")
+    owner_pass = getpass.getpass("  1. Enter password for 'db_owner': ")
+    reader_pass = getpass.getpass("  2. Enter password for 'db_reader' (Used by Web UI): ")
+    loader_pass = getpass.getpass("  3. Enter password for 'db_loader' (Used by Scripts): ")
+    app_auth_pass = getpass.getpass("  4. Enter password for 'db_app_auth' (Used for User Login): ")
+
+    print("\nConnecting as root to configure server...")
+    try:
+        conn = pymysql.connect(
+            host='127.0.0.1',  # Local host execution context
+            user='root',
+            password=root_password,
+            autocommit=True
+        )
+        cursor = conn.cursor()
+    except Exception as e:
+        print(f"Failed to connect: {e}")
+        return
+
+    queries = [
+        "CREATE DATABASE IF NOT EXISTS food_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;",
+        
+        # Owner User
+        f"CREATE USER IF NOT EXISTS 'db_owner'@'%' IDENTIFIED BY '{owner_pass}';",
+        "GRANT ALL PRIVILEGES ON food_db.* TO 'db_owner'@'%' WITH GRANT OPTION;",
+        
+        # Reader User
+        f"CREATE USER IF NOT EXISTS 'db_reader'@'%' IDENTIFIED BY '{reader_pass}';",
+        "GRANT USAGE ON *.* TO 'db_reader'@'%';",
+        
+        # Loader User
+        f"CREATE USER IF NOT EXISTS 'db_loader'@'%' IDENTIFIED BY '{loader_pass}';",
+        "GRANT USAGE ON *.* TO 'db_loader'@'%';",
+        "GRANT FILE ON *.* TO 'db_loader'@'%';",  
+        
+        # App Auth User (PoLP)
+        f"CREATE USER IF NOT EXISTS 'db_app_auth'@'%' IDENTIFIED BY '{app_auth_pass}';",
+        "GRANT USAGE ON *.* TO 'db_app_auth'@'%';",
+        
+        "FLUSH PRIVILEGES;"
+    ]
+
+    for q in queries:
+        print(f"Executing: {q[:60]}...")
+        cursor.execute(q)
+
+    # Now create the table logic safely
+    print("\nCreating Tables and Granting Table-Specific Access...")
+    
+    # 1. Users Table
+    cursor.execute("""
+    CREATE TABLE IF NOT EXISTS food_db.users (
+        id INT AUTO_INCREMENT PRIMARY KEY,
+        username VARCHAR(100) UNIQUE NOT NULL,
+        password_hash VARCHAR(255) NOT NULL,
+        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+    ) ENGINE=InnoDB;
+    """)
+    # 2. Products Table
+    cursor.execute("""
+    CREATE TABLE IF NOT EXISTS food_db.products (
+        code VARCHAR(50) PRIMARY KEY, url TEXT, creator VARCHAR(255), created_t VARCHAR(50), 
+        created_datetime VARCHAR(50), last_modified_t VARCHAR(50), last_modified_datetime VARCHAR(50), 
+        product_name TEXT, generic_name TEXT, quantity VARCHAR(255), packaging TEXT, brands TEXT, 
+        categories TEXT, origins TEXT, labels TEXT, stores TEXT, countries TEXT, ingredients_text TEXT, 
+        allergens TEXT, traces TEXT, 
+        FULLTEXT INDEX ft_idx_search (product_name, ingredients_text)
+    ) ENGINE=InnoDB;
+    """)
+    
+    # Table Context Grants (SoD)
+    cursor.execute("GRANT SELECT, INSERT, UPDATE ON food_db.users TO 'db_app_auth'@'%';")
+    cursor.execute("GRANT SELECT ON food_db.products TO 'db_reader'@'%';")
+    cursor.execute("GRANT SELECT, INSERT, UPDATE, DELETE, DROP, CREATE ON food_db.products TO 'db_loader'@'%';")
+    cursor.execute("FLUSH PRIVILEGES;")
+
+    print("\n✅ Database, Users, and Tables created successfully!")
+    cursor.close()
+    conn.close()
+
+    print("\n🎉 Setup Complete.")
+    print("\n!!! IMPORTANT NEXT STEPS !!!")
+    print("Now, use `mysql_config_editor` to store these passwords locally so the app can use them:")
+    print("  mysql_config_editor set --login-path=app_reader --host=127.0.0.1 --user=db_reader --password")
+    print("  mysql_config_editor set --login-path=app_auth --host=127.0.0.1 --user=db_app_auth --password")
+
+if __name__ == "__main__":
+    run_db_setup()