diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..9f92980 --- /dev/null +++ b/install.sh @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +""" +Wesal Social Media App - Installation Script +This script sets up all necessary configuration files and secrets for the Wesal app. +""" + +import os +import sys +import secrets +import string +import shutil +from pathlib import Path +from typing import Optional + +# ANSI color codes +class Colors: + BLUE = '\033[94m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + BOLD = '\033[1m' + END = '\033[0m' + +def print_ascii_logo(): + """Print the Wesal logo in ASCII art with blue color""" + logo = """ +██╗ ██╗███████╗███████╗ █████╗ ██╗ +██║ ██║██╔════╝██╔════╝██╔══██╗██║ +██║ █╗ ██║█████╗ ███████╗███████║██║ +██║███╗██║██╔══╝ ╚════██║██╔══██║██║ +╚███╔███╔╝███████╗███████║██║ ██║███████╗ + ╚══╝╚══╝ ╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝ + + Installation Script + """ + print(f"{Colors.BLUE}{Colors.BOLD}{logo}{Colors.END}") + +def generate_complex_password(length: int = 20) -> str: + """Generate a complex password with specified requirements""" + # Define character sets + lowercase = string.ascii_lowercase + uppercase = string.ascii_uppercase + digits = string.digits + special_chars = "*@!" + + # Ensure at least one character from each category + password_chars = [ + secrets.choice(lowercase), + secrets.choice(uppercase), + secrets.choice(digits), + secrets.choice(special_chars) + ] + + # Fill the rest randomly + all_chars = lowercase + uppercase + digits + special_chars + for _ in range(length - 4): + password_chars.append(secrets.choice(all_chars)) + + # Shuffle the password + secrets.SystemRandom().shuffle(password_chars) + return ''.join(password_chars) + +def get_user_input(prompt: str, default: Optional[str] = None) -> str: + """Get user input with optional default value""" + if default: + full_prompt = f"{Colors.YELLOW}{prompt} (default: {default}): {Colors.END}" + else: + full_prompt = f"{Colors.YELLOW}{prompt}: {Colors.END}" + + user_input = input(full_prompt).strip() + return user_input if user_input else (default or "") + +def print_success(message: str): + """Print success message in green""" + print(f"{Colors.GREEN}✓ {message}{Colors.END}") + +def print_error(message: str): + """Print error message in red""" + print(f"{Colors.RED}✗ {message}{Colors.END}") + +def print_info(message: str): + """Print info message in blue""" + print(f"{Colors.BLUE}ℹ {message}{Colors.END}") + +def create_env_files(hostname: str, port: str, db_name: str, password: str): + """Create .env and env.properties files""" + # Create backend resources directory if it doesn't exist + resources_dir = Path("backend/src/main/resources") + resources_dir.mkdir(parents=True, exist_ok=True) + + # Prepare the environment variables content + env_content = f"""DB_URL=jdbc:postgresql://{hostname}:{port}/{db_name} +DB_USER=wesaladmin +DB_PASSWORD={password} +DB_DB={db_name}""" + + # Write .env file in TOP LEVEL (root directory) + env_file_path = Path(".env") + with open(env_file_path, 'w') as f: + f.write(env_content) + print_success(f"Created {env_file_path} (top level)") + + # Write env.properties file in resources directory + env_properties_path = resources_dir / "env.properties" + with open(env_properties_path, 'w') as f: + f.write(env_content) + print_success(f"Created {env_properties_path}") + +def setup_database_config(): + """Setup database configuration""" + print(f"\n{Colors.BOLD}📊 Database Configuration{Colors.END}") + print("Setting up PostgreSQL connection parameters...") + + hostname = get_user_input("Database hostname", "db") + port = get_user_input("Database port", "5432") + db_name = get_user_input("Database name", "prod") + + # Ask if user wants to generate password or provide their own + generate_pass = get_user_input("Generate complex password automatically? (y/n)", "y").lower() + + if generate_pass in ['y', 'yes', '']: + password = generate_complex_password() + print_info(f"Generated password: {password}") + else: + password = get_user_input("Enter database password") + if not password: + print_error("Password cannot be empty. Generating one automatically...") + password = generate_complex_password() + print_info(f"Generated password: {password}") + + create_env_files(hostname, port, db_name, password) + return password + +def setup_firebase_credentials(): + """Setup Firebase service account credentials""" + print(f"\n{Colors.BOLD}🔥 Firebase Configuration{Colors.END}") + + # Ask if user needs guidance + need_help = get_user_input("Do you need help finding the Firebase Service Account JSON file? (y/n)", "n").lower() + + if need_help in ['y', 'yes']: + print(f"\n{Colors.BLUE}📋 Steps to get Firebase Service Account JSON:{Colors.END}") + print("1. Go to https://console.firebase.google.com/") + print("2. Select your project") + print("3. Click on the gear icon (Project Settings)") + print("4. Go to the 'Service accounts' tab") + print("5. Click 'Generate new private key'") + print("6. Download the JSON file") + print("7. Save it in the current directory and name it 'firebase-service-account.json'") + print() + + print_info("Please place your Firebase Service Account JSON file in the current directory") + print_info("Name the file: 'firebase-service-account.json'") + + while True: + input(f"{Colors.YELLOW}Press Enter when the file is ready...{Colors.END}") + + firebase_file = Path("firebase-service-account.json") + if firebase_file.exists(): + print_success("Firebase service account file found!") + break + else: + print_error("File 'firebase-service-account.json' not found in current directory.") + retry = get_user_input("Try again? (y/n)", "y").lower() + if retry not in ['y', 'yes', '']: + print_error("Skipping Firebase setup...") + return False + + # Copy to backend resources + backend_resources = Path("backend/src/main/resources") + backend_resources.mkdir(parents=True, exist_ok=True) + backend_firebase_path = backend_resources / "firebase-service-account.json" + + shutil.copy2(firebase_file, backend_firebase_path) + print_success(f"Copied Firebase credentials to {backend_firebase_path}") + + # Copy to frontend directory (the specified path) + frontend_firebase_path = Path("/Users/bubshait/Desktop/My life/2025/SWA/wesal_app/frontend/firebase-service-account.json") + + try: + # Create parent directories if they don't exist + frontend_firebase_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(firebase_file, frontend_firebase_path) + print_success(f"Copied Firebase credentials to {frontend_firebase_path}") + except Exception as e: + print_error(f"Could not copy to frontend directory: {e}") + print_info("You may need to manually copy the file to the frontend directory") + + return True + + + +def print_summary(): + """Print installation summary""" + print(f"\n{Colors.BOLD}{Colors.GREEN}🎉 Installation Complete!{Colors.END}") + print(f"\n{Colors.BOLD}Files created:{Colors.END}") + print("• .env (top level)") + print("• backend/src/main/resources/env.properties") + print("• backend/src/main/resources/firebase-service-account.json") + print(f"\n{Colors.BOLD}Next steps:{Colors.END}") + print("1. Run: docker-compose up --build") + print("2. Access your app at: http://localhost:6060") + print("3. Access Adminer (DB admin) at: http://localhost:8100") + print("4. API will be available at: http://localhost:4044") + print(f"\n{Colors.BLUE}Happy coding! 🚀{Colors.END}") + +def main(): + """Main installation function""" + try: + # Print logo + print_ascii_logo() + + print("Welcome to the Wesal Social Media App installer!") + print("This script will help you set up all necessary configuration files.\n") + + # Setup database + db_password = setup_database_config() + + # Setup Firebase + firebase_success = setup_firebase_credentials() + + # Print summary + print_summary() + + if not firebase_success: + print(f"\n{Colors.YELLOW}⚠️ Note: Firebase setup was skipped. You'll need to set it up manually later.{Colors.END}") + + except KeyboardInterrupt: + print(f"\n{Colors.RED}Installation cancelled by user.{Colors.END}") + sys.exit(1) + except Exception as e: + print_error(f"An error occurred: {e}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file