#!/usr/bin/env python3 """ Master User Creation Script for Positeasy Canteen Automation System. This script creates or updates a master administrator user in the database with username 'admin' and password 'admin', and synchronizes the application configuration. """ import sys import os import re import argparse import bcrypt import psycopg2 # Default Configuration DEFAULT_USERNAME = "admin" DEFAULT_PASSWORD = "admin" DEFAULT_NAME = "Admin Master" DEFAULT_ROLE = "MASTER" DEFAULT_DB_HOST = "localhost" DEFAULT_DB_PORT = 5432 DEFAULT_DB_NAME = "positeasy" DEFAULT_DB_USER = "postgres" DEFAULT_DB_PASS = "sidharth" ALL_PERMISSIONS = [ "dashboard", "sale", "customers", "purchases", "inventory", "expense", "reports", "stores", "table", "wallet", "promotions", "feedback" ] def hash_password(password: str) -> str: """Hash password using bcrypt compatible with Spring Security BCryptPasswordEncoder.""" salt = bcrypt.gensalt(rounds=10) hashed = bcrypt.hashpw(password.encode('utf-8'), salt) return hashed.decode('utf-8') def parse_application_properties(prop_path: str) -> dict: """Parse db credentials from backend application.properties if available.""" config = {} if not os.path.exists(prop_path): return config with open(prop_path, 'r', encoding='utf-8') as f: for line in f: line = line.strip() if line and not line.startswith('#') and '=' in line: key, value = line.split('=', 1) config[key.strip()] = value.strip() return config def update_application_properties(prop_path: str, username: str, password: str) -> bool: """Update app.master.username and app.master.password in application.properties.""" if not os.path.exists(prop_path): print(f"āš ļø [Config] File not found: {prop_path}") return False with open(prop_path, 'r', encoding='utf-8') as f: content = f.read() # Update or add app.master.username and app.master.password new_user_line = f"app.master.username=${{MASTER_USER:{username}}}" new_pass_line = f"app.master.password=${{MASTER_PASSWORD:{password}}}" if re.search(r'^app\.master\.username=.*$', content, flags=re.MULTILINE): content = re.sub(r'^app\.master\.username=.*$', new_user_line, content, flags=re.MULTILINE) else: content += f"\n{new_user_line}" if re.search(r'^app\.master\.password=.*$', content, flags=re.MULTILINE): content = re.sub(r'^app\.master\.password=.*$', new_pass_line, content, flags=re.MULTILINE) else: content += f"\n{new_pass_line}" with open(prop_path, 'w', encoding='utf-8') as f: f.write(content) print(f"āœ… [Config] Updated {prop_path} with default master credentials ({username}/{password}).") return True def update_frontend_demo_credentials(app_tsx_path: str, username: str, password: str) -> bool: """Update UI demo credentials displayed in api-dashboard if file exists.""" if not os.path.exists(app_tsx_path): return False with open(app_tsx_path, 'r', encoding='utf-8') as f: content = f.read() old_pattern = r'Default Admin:\s*[^<]*\s*/\s*Password:\s*[^<]*' new_text = f'Default Admin: {username} / Password: {password}' if re.search(old_pattern, content): content = re.sub(old_pattern, new_text, content) with open(app_tsx_path, 'w', encoding='utf-8') as f: f.write(content) print(f"āœ… [UI] Updated demo credentials in {app_tsx_path}.") return True return False def create_or_update_master_user(db_params: dict, username: str, password: str, name: str) -> bool: """Insert or update master user in PostgreSQL database.""" print(f"\nšŸ”Œ Connecting to PostgreSQL database '{db_params['dbname']}' on {db_params['host']}:{db_params['port']}...") try: conn = psycopg2.connect(**db_params) conn.autocommit = False cur = conn.cursor() # Check if system_users table exists cur.execute(""" SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_name = 'system_users' ); """) table_exists = cur.fetchone()[0] if not table_exists: print("āŒ Table 'system_users' does not exist in the database. Has backend schema initialized?") conn.close() return False # Check if master user with this username (stored in email column) exists cur.execute("SELECT id, name, role FROM system_users WHERE email = %s;", (username,)) row = cur.fetchone() hashed_pass = hash_password(password) if row: user_id = row[0] print(f"šŸ”„ Existing master user found (ID: {user_id}). Updating credentials...") cur.execute(""" UPDATE system_users SET name = %s, password = %s, role = %s, view_only = false WHERE id = %s; """, (name, hashed_pass, DEFAULT_ROLE, user_id)) else: print(f"āž• Creating new master user '{username}'...") cur.execute(""" INSERT INTO system_users (name, email, password, role, view_only) VALUES (%s, %s, %s, %s, false) RETURNING id; """, (name, username, hashed_pass, DEFAULT_ROLE)) user_id = cur.fetchone()[0] # Reset permissions for this user cur.execute("DELETE FROM system_user_permissions WHERE user_id = %s;", (user_id,)) for perm in ALL_PERMISSIONS: cur.execute(""" INSERT INTO system_user_permissions (user_id, permission) VALUES (%s, %s); """, (user_id, perm)) conn.commit() # Verification query cur.execute(""" SELECT u.id, u.name, u.email, u.role, count(p.permission) FROM system_users u LEFT JOIN system_user_permissions p ON u.id = p.user_id WHERE u.id = %s GROUP BY u.id, u.name, u.email, u.role; """, (user_id,)) ver = cur.fetchone() cur.close() conn.close() print("\n" + "=" * 50) print("šŸŽ‰ MASTER USER CREATED / UPDATED SUCCESSFULLY") print("=" * 50) print(f" User ID: {ver[0]}") print(f" Name: {ver[1]}") print(f" Username: {ver[2]}") print(f" Password: {password}") print(f" Role: {ver[3]}") print(f" Permissions: {ver[4]} granted ({', '.join(ALL_PERMISSIONS[:4])}...)") print("=" * 50 + "\n") return True except Exception as e: print(f"āŒ Database error: {e}") return False def main(): parser = argparse.ArgumentParser(description="Create or update master user for Positeasy Canteen Automation.") parser.add_argument("-u", "--username", default=DEFAULT_USERNAME, help="Master username (default: admin)") parser.add_argument("-p", "--password", default=DEFAULT_PASSWORD, help="Master password (default: admin)") parser.add_argument("-n", "--name", default=DEFAULT_NAME, help="Display name for master user") parser.add_argument("--host", default=DEFAULT_DB_HOST, help="PostgreSQL host (default: localhost)") parser.add_argument("--port", type=int, default=DEFAULT_DB_PORT, help="PostgreSQL port (default: 5432)") parser.add_argument("--dbname", default=DEFAULT_DB_NAME, help="PostgreSQL database name (default: positeasy)") parser.add_argument("--dbuser", default=DEFAULT_DB_USER, help="PostgreSQL database user (default: postgres)") parser.add_argument("--dbpass", default=DEFAULT_DB_PASS, help="PostgreSQL database password") args = parser.parse_args() project_root = os.path.dirname(os.path.abspath(__file__)) prop_path = os.path.join(project_root, "backend", "src", "main", "resources", "application.properties") app_tsx_path = os.path.join(project_root, "api-dashboard", "src", "App.tsx") db_params = { "host": args.host, "port": args.port, "dbname": args.dbname, "user": args.dbuser, "password": args.dbpass } print("šŸš€ Starting Master User setup script...") # 1. Database creation / update db_success = create_or_update_master_user(db_params, args.username, args.password, args.name) # 2. Sync application.properties update_application_properties(prop_path, args.username, args.password) # 3. Sync frontend dashboard UI update_frontend_demo_credentials(app_tsx_path, args.username, args.password) if db_success: print("✨ All steps completed successfully! You can now log in with:") print(f" Username: {args.username}") print(f" Password: {args.password}") else: print("āš ļø Database operation failed or incomplete. Check database connectivity.") if __name__ == "__main__": main()