setup_db.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import pymysql
  2. import getpass
  3. import os
  4. def run_db_setup():
  5. """
  6. This Python script prompts for passwords securely and executes CREATE USER / GRANT
  7. statements to provision the MySQL server dynamically without config files.
  8. """
  9. print("Welcome to Local Food AI Initial Setup.")
  10. print("WARNING: This will configure your MySQL server. You must know the MySQL root password.\n")
  11. root_password = getpass.getpass("Enter the MySQL 'root' password: ")
  12. # Prompt for the new user passwords (so they aren't stored anywhere!)
  13. print("\nPlease define the passwords for the service accounts:")
  14. owner_pass = getpass.getpass(" 1. Enter password for 'db_owner': ")
  15. reader_pass = getpass.getpass(" 2. Enter password for 'db_reader' (Used by Web UI): ")
  16. loader_pass = getpass.getpass(" 3. Enter password for 'db_loader' (Used by Scripts): ")
  17. app_auth_pass = getpass.getpass(" 4. Enter password for 'db_app_auth' (Used for User Login): ")
  18. print("\nConnecting as root to configure server...")
  19. try:
  20. # Connect using the local unix socket which allows seamless auth_socket root login on Ubuntu
  21. conn = pymysql.connect(
  22. unix_socket='/var/run/mysqld/mysqld.sock',
  23. user='root',
  24. password=root_password,
  25. autocommit=True
  26. )
  27. cursor = conn.cursor()
  28. except Exception as e:
  29. print(f"Failed to connect: {e}")
  30. return
  31. queries = [
  32. "CREATE DATABASE IF NOT EXISTS food_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;",
  33. # Owner User
  34. f"CREATE USER IF NOT EXISTS 'db_owner'@'%' IDENTIFIED BY '{owner_pass}';",
  35. "GRANT ALL PRIVILEGES ON food_db.* TO 'db_owner'@'%' WITH GRANT OPTION;",
  36. # Reader User
  37. f"CREATE USER IF NOT EXISTS 'db_reader'@'%' IDENTIFIED BY '{reader_pass}';",
  38. "GRANT USAGE ON *.* TO 'db_reader'@'%';",
  39. # Loader User
  40. f"CREATE USER IF NOT EXISTS 'db_loader'@'%' IDENTIFIED BY '{loader_pass}';",
  41. "GRANT USAGE ON *.* TO 'db_loader'@'%';",
  42. "GRANT FILE ON *.* TO 'db_loader'@'%';",
  43. # App Auth User (PoLP)
  44. f"CREATE USER IF NOT EXISTS 'db_app_auth'@'%' IDENTIFIED BY '{app_auth_pass}';",
  45. "GRANT USAGE ON *.* TO 'db_app_auth'@'%';",
  46. "FLUSH PRIVILEGES;"
  47. ]
  48. for q in queries:
  49. print(f"Executing: {q[:60]}...")
  50. cursor.execute(q)
  51. # Now create the table logic safely
  52. print("\nCreating Tables and Granting Table-Specific Access...")
  53. # 1. Users Table
  54. cursor.execute("""
  55. CREATE TABLE IF NOT EXISTS food_db.users (
  56. id INT AUTO_INCREMENT PRIMARY KEY,
  57. username VARCHAR(100) UNIQUE NOT NULL,
  58. password_hash VARCHAR(255) NOT NULL,
  59. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  60. ) ENGINE=InnoDB;
  61. """)
  62. # 2. Products Table
  63. cursor.execute("""
  64. CREATE TABLE IF NOT EXISTS food_db.products (
  65. code VARCHAR(50) PRIMARY KEY, url TEXT, creator VARCHAR(255), created_t VARCHAR(50),
  66. created_datetime VARCHAR(50), last_modified_t VARCHAR(50), last_modified_datetime VARCHAR(50),
  67. product_name TEXT, generic_name TEXT, quantity VARCHAR(255), packaging TEXT, brands TEXT,
  68. categories TEXT, origins TEXT, labels TEXT, stores TEXT, countries TEXT, ingredients_text TEXT,
  69. allergens TEXT, traces TEXT,
  70. FULLTEXT INDEX ft_idx_search (product_name, ingredients_text)
  71. ) ENGINE=InnoDB;
  72. """)
  73. # Table Context Grants (SoD)
  74. cursor.execute("GRANT SELECT, INSERT, UPDATE ON food_db.users TO 'db_app_auth'@'%';")
  75. cursor.execute("GRANT SELECT ON food_db.products TO 'db_reader'@'%';")
  76. cursor.execute("GRANT SELECT, INSERT, UPDATE, DELETE, DROP, CREATE ON food_db.products TO 'db_loader'@'%';")
  77. cursor.execute("FLUSH PRIVILEGES;")
  78. print("\n✅ Database, Users, and Tables created successfully!")
  79. cursor.close()
  80. conn.close()
  81. print("\n🎉 Setup Complete.")
  82. print("\n!!! IMPORTANT NEXT STEPS !!!")
  83. print("Now, use `mysql_config_editor` to store these passwords locally so the app can use them:")
  84. print(" mysql_config_editor set --login-path=app_reader --host=127.0.0.1 --user=db_reader --password")
  85. print(" mysql_config_editor set --login-path=app_auth --host=127.0.0.1 --user=db_app_auth --password")
  86. if __name__ == "__main__":
  87. run_db_setup()