feat: add installation script
This commit is contained in:
parent
3b50c997fa
commit
02323106de
236
install.sh
Normal file
236
install.sh
Normal file
@ -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()
|
||||||
Loading…
Reference in New Issue
Block a user