#!/bin/bash # Comprehensive installation script for PunimTag Web # Installs all system dependencies, Python packages, and frontend dependencies set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Project root directory PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$PROJECT_ROOT" echo -e "${BLUE}╔══════════════════════════════════════════════════════════╗${NC}" echo -e "${BLUE}║ PunimTag Web - Comprehensive Installation Script ║${NC}" echo -e "${BLUE}╚══════════════════════════════════════════════════════════╝${NC}" echo "" # Function to check if command exists command_exists() { command -v "$1" >/dev/null 2>&1 } # Function to check Python version check_python_version() { if command_exists python3; then PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') PYTHON_MAJOR=$(echo "$PYTHON_VERSION" | cut -d. -f1) PYTHON_MINOR=$(echo "$PYTHON_VERSION" | cut -d. -f2) if [ "$PYTHON_MAJOR" -lt 3 ] || ([ "$PYTHON_MAJOR" -eq 3 ] && [ "$PYTHON_MINOR" -lt 12 ]); then echo -e "${RED}❌ Python 3.12 or higher is required. Found: Python $PYTHON_VERSION${NC}" return 1 else echo -e "${GREEN}✅ Python $PYTHON_VERSION found${NC}" return 0 fi else echo -e "${RED}❌ Python 3 is not installed${NC}" return 1 fi } # Function to check Node.js version check_node_version() { if command_exists node; then NODE_VERSION=$(node -v | sed 's/v//') NODE_MAJOR=$(echo "$NODE_VERSION" | cut -d. -f1) if [ "$NODE_MAJOR" -lt 18 ]; then echo -e "${RED}❌ Node.js 18 or higher is required. Found: Node.js $NODE_VERSION${NC}" return 1 else echo -e "${GREEN}✅ Node.js $NODE_VERSION found${NC}" return 0 fi else echo -e "${RED}❌ Node.js is not installed${NC}" return 1 fi } # Detect OS detect_os() { if [[ "$OSTYPE" == "linux-gnu"* ]]; then if [ -f /etc/os-release ]; then . /etc/os-release OS=$ID else OS="linux" fi elif [[ "$OSTYPE" == "darwin"* ]]; then OS="macos" else OS="unknown" fi echo "$OS" } # Install system dependencies (Linux only) install_system_dependencies() { local os=$(detect_os) if [[ "$os" != "ubuntu" && "$os" != "debian" ]]; then echo -e "${YELLOW}⚠️ Automatic system dependency installation is only supported on Ubuntu/Debian${NC}" echo -e "${YELLOW} Please install PostgreSQL and Redis manually for other systems${NC}" return 0 fi echo -e "${BLUE}📦 Installing system dependencies...${NC}" # Update package list sudo apt update # Install PostgreSQL if ! command_exists psql; then echo -e "${BLUE} Installing PostgreSQL...${NC}" sudo apt install -y postgresql postgresql-contrib sudo systemctl start postgresql sudo systemctl enable postgresql echo -e "${GREEN} ✅ PostgreSQL installed${NC}" else echo -e "${GREEN} ✅ PostgreSQL already installed${NC}" fi # Install Redis if ! command_exists redis-cli; then echo -e "${BLUE} Installing Redis...${NC}" sudo apt install -y redis-server sudo systemctl start redis-server sudo systemctl enable redis-server echo -e "${GREEN} ✅ Redis installed${NC}" else echo -e "${GREEN} ✅ Redis already installed${NC}" fi # Verify Redis is running if redis-cli ping >/dev/null 2>&1; then echo -e "${GREEN} ✅ Redis is running${NC}" else echo -e "${YELLOW} ⚠️ Redis is not running. Starting Redis...${NC}" sudo systemctl start redis-server || redis-server --daemonize yes fi } # Setup PostgreSQL databases setup_databases() { echo -e "${BLUE}🗄️ Setting up PostgreSQL databases...${NC}" if ! command_exists psql; then echo -e "${YELLOW}⚠️ PostgreSQL is not installed. Skipping database setup.${NC}" echo -e "${YELLOW} Please install PostgreSQL and run: ./scripts/setup_postgresql.sh${NC}" return 0 fi # Start PostgreSQL if not running if ! sudo systemctl is-active --quiet postgresql; then echo -e "${BLUE} Starting PostgreSQL service...${NC}" sudo systemctl start postgresql fi # Create user and databases echo -e "${BLUE} Creating database user and databases...${NC}" sudo -u postgres psql << 'EOF' || true -- Create user if it doesn't exist DO $$ BEGIN IF NOT EXISTS (SELECT FROM pg_user WHERE usename = 'punimtag') THEN CREATE USER punimtag WITH PASSWORD 'punimtag_password'; END IF; END $$; -- Create main database if it doesn't exist SELECT 'CREATE DATABASE punimtag OWNER punimtag' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'punimtag')\gexec -- Create auth database if it doesn't exist SELECT 'CREATE DATABASE punimtag_auth OWNER punimtag' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'punimtag_auth')\gexec -- Grant privileges GRANT ALL PRIVILEGES ON DATABASE punimtag TO punimtag; GRANT ALL PRIVILEGES ON DATABASE punimtag_auth TO punimtag; \q EOF echo -e "${GREEN} ✅ Databases created${NC}" } # Create Python virtual environment setup_python_venv() { echo -e "${BLUE}🐍 Setting up Python virtual environment...${NC}" if [ ! -d "venv" ]; then echo -e "${BLUE} Creating virtual environment...${NC}" python3 -m venv venv echo -e "${GREEN} ✅ Virtual environment created${NC}" else echo -e "${GREEN} ✅ Virtual environment already exists${NC}" fi # Activate virtual environment source venv/bin/activate # Upgrade pip echo -e "${BLUE} Upgrading pip...${NC}" pip install --upgrade pip --quiet echo -e "${GREEN} ✅ Python environment ready${NC}" } # Install Python dependencies install_python_dependencies() { echo -e "${BLUE}📦 Installing Python dependencies...${NC}" if [ ! -f "requirements.txt" ]; then echo -e "${RED}❌ requirements.txt not found${NC}" return 1 fi source venv/bin/activate pip install -r requirements.txt echo -e "${GREEN} ✅ Python dependencies installed${NC}" } # Download DeepFace model weights (optional, but recommended) download_deepface_models() { echo -e "${BLUE}🤖 Downloading DeepFace model weights (optional)...${NC}" source venv/bin/activate # Check if deepface is installed if ! python3 -c "import deepface" 2>/dev/null; then echo -e "${YELLOW} ⚠️ DeepFace not installed, skipping model download${NC}" return 0 fi # Create weights directory mkdir -p ~/.deepface/weights # Check if ArcFace weights already exist if [ -f ~/.deepface/weights/arcface_weights.h5 ]; then echo -e "${GREEN} ✅ ArcFace model weights already exist${NC}" return 0 fi echo -e "${BLUE} Downloading ArcFace model weights (~131MB, this may take a few minutes)...${NC}" # Try to download using wget (more reliable than Python download in worker) if command_exists wget; then if wget -q --show-progress \ https://github.com/serengil/deepface_models/releases/download/v1.0/arcface_weights.h5 \ -O ~/.deepface/weights/arcface_weights.h5; then echo -e "${GREEN} ✅ ArcFace model weights downloaded successfully${NC}" return 0 fi fi # Fallback: try with Python (may fail in some environments) echo -e "${YELLOW} ⚠️ wget failed, trying Python download...${NC}" if python3 << 'PYTHON' import os import sys from pathlib import Path try: from deepface import DeepFace import numpy as np from PIL import Image # Create a dummy image to trigger model download dummy_img = Image.new('RGB', (100, 100), color='black') dummy_array = np.array(dummy_img) # This will trigger model download try: DeepFace.represent( img_path=dummy_array, model_name="ArcFace", detector_backend="retinaface", enforce_detection=False, align=True, ) print("✅ Model weights downloaded successfully") sys.exit(0) except Exception as e: if "arcface_weights" in str(e).lower(): print(f"⚠️ Download failed: {e}") print(" You can download manually later or the worker will try again") sys.exit(1) # Other errors are OK (like no faces found) print("✅ Model weights are available") sys.exit(0) except ImportError: print("⚠️ DeepFace not available") sys.exit(0) except Exception as e: print(f"⚠️ Error: {e}") sys.exit(1) PYTHON then echo -e "${GREEN} ✅ DeepFace models ready${NC}" else echo -e "${YELLOW} ⚠️ Automatic download failed. Models will be downloaded on first use.${NC}" echo -e "${YELLOW} ⚠️ To download manually, run:${NC}" echo -e "${BLUE} mkdir -p ~/.deepface/weights${NC}" echo -e "${BLUE} wget https://github.com/serengil/deepface_models/releases/download/v1.0/arcface_weights.h5 -O ~/.deepface/weights/arcface_weights.h5${NC}" fi } # Install frontend dependencies install_frontend_dependencies() { echo -e "${BLUE}📦 Installing frontend dependencies...${NC}" # Install admin frontend if [ -d "admin-frontend" ]; then echo -e "${BLUE} Installing admin frontend dependencies...${NC}" cd admin-frontend if [ -f "package.json" ]; then npm install echo -e "${GREEN} ✅ Admin frontend dependencies installed${NC}" fi cd .. fi # Install viewer frontend if [ -d "viewer-frontend" ]; then echo -e "${BLUE} Installing viewer frontend dependencies...${NC}" cd viewer-frontend if [ -f "package.json" ]; then npm install echo -e "${GREEN} ✅ Viewer frontend dependencies installed${NC}" fi cd .. fi } # Create .env file if it doesn't exist create_env_file() { echo -e "${BLUE}⚙️ Setting up environment configuration...${NC}" if [ ! -f ".env" ]; then echo -e "${BLUE} Creating .env file...${NC}" cat > .env << 'EOF' # Database (PostgreSQL by default) DATABASE_URL=postgresql+psycopg2://punimtag:punimtag_password@localhost:5432/punimtag # Auth Database (for frontend website user accounts - separate from main database) DATABASE_URL_AUTH=postgresql+psycopg2://punimtag:punimtag_password@localhost:5432/punimtag_auth # JWT Secrets (change in production!) SECRET_KEY=dev-secret-key-change-in-production # Single-user credentials (change in production!) ADMIN_USERNAME=admin ADMIN_PASSWORD=admin # Photo storage directory (default: data/uploads) PHOTO_STORAGE_DIR=data/uploads EOF echo -e "${GREEN} ✅ .env file created${NC}" echo -e "${YELLOW} ⚠️ Please review and update .env file with your configuration${NC}" else echo -e "${GREEN} ✅ .env file already exists${NC}" fi # Create admin frontend .env file if it doesn't exist if [ -d "admin-frontend" ] && [ ! -f "admin-frontend/.env" ]; then echo -e "${BLUE} Creating admin-frontend/.env file...${NC}" cat > admin-frontend/.env << 'EOF' # Backend API URL (must be accessible from browsers) VITE_API_URL=http://127.0.0.1:8000 EOF echo -e "${GREEN} ✅ admin-frontend/.env file created${NC}" elif [ -d "admin-frontend" ]; then echo -e "${GREEN} ✅ admin-frontend/.env file already exists${NC}" fi # Create viewer frontend .env file if it doesn't exist if [ -d "viewer-frontend" ] && [ ! -f "viewer-frontend/.env.local" ]; then echo -e "${BLUE} Creating viewer-frontend/.env.local file...${NC}" cat > viewer-frontend/.env.local << 'EOF' # Database connection (read-only recommended) DATABASE_URL=postgresql://punimtag:punimtag_password@localhost:5432/punimtag # Auth database connection DATABASE_URL_AUTH=postgresql://punimtag:punimtag_password@localhost:5432/punimtag_auth # NextAuth configuration NEXTAUTH_URL=http://localhost:3001 NEXTAUTH_SECRET=dev-secret-key-change-in-production EOF echo -e "${GREEN} ✅ viewer-frontend/.env.local file created${NC}" elif [ -d "viewer-frontend" ]; then echo -e "${GREEN} ✅ viewer-frontend/.env.local file already exists${NC}" fi } # Create data directories create_data_directories() { echo -e "${BLUE}📁 Creating data directories...${NC}" mkdir -p data/uploads mkdir -p data/thumbnails echo -e "${GREEN} ✅ Data directories created${NC}" } # Main installation function main() { echo -e "${BLUE}🔍 Checking prerequisites...${NC}" echo "" # Check Python if ! check_python_version; then echo -e "${RED}❌ Please install Python 3.12 or higher${NC}" exit 1 fi # Check Node.js if ! check_node_version; then echo -e "${RED}❌ Please install Node.js 18 or higher${NC}" exit 1 fi # Check npm if ! command_exists npm; then echo -e "${RED}❌ npm is not installed${NC}" exit 1 else echo -e "${GREEN}✅ npm found${NC}" fi echo "" echo -e "${BLUE}🚀 Starting installation...${NC}" echo "" # Install system dependencies (Linux only) install_system_dependencies echo "" # Setup databases setup_databases echo "" # Setup Python environment setup_python_venv echo "" # Install Python dependencies install_python_dependencies echo "" # Download DeepFace model weights (optional) download_deepface_models echo "" # Install frontend dependencies install_frontend_dependencies echo "" # Create .env files create_env_file echo "" # Create data directories create_data_directories echo "" # Success message echo -e "${GREEN}╔══════════════════════════════════════════════════════════╗${NC}" echo -e "${GREEN}║ ✅ Installation Complete! ║${NC}" echo -e "${GREEN}╚══════════════════════════════════════════════════════════╝${NC}" echo "" echo -e "${BLUE}📋 Next Steps:${NC}" echo "" echo -e "1. ${YELLOW}Review and update .env file${NC} (if needed):" echo -e " ${BLUE} nano .env${NC}" echo "" echo -e "2. ${YELLOW}Activate the virtual environment:${NC}" echo -e " ${BLUE} source venv/bin/activate${NC}" echo "" echo -e "3. ${YELLOW}Start the backend API:${NC}" echo -e " ${BLUE} export PYTHONPATH=$PROJECT_ROOT${NC}" echo -e " ${BLUE} uvicorn backend.app:app --host 127.0.0.1 --port 8000${NC}" echo "" echo -e "4. ${YELLOW}In another terminal, start the admin frontend:${NC}" echo -e " ${BLUE} cd admin-frontend${NC}" echo -e " ${BLUE} npm run dev${NC}" echo "" echo -e "5. ${YELLOW}In another terminal, start the viewer frontend (optional):${NC}" echo -e " ${BLUE} cd viewer-frontend${NC}" echo -e " ${BLUE} npm run dev${NC}" echo "" echo -e "6. ${YELLOW}Access the applications:${NC}" echo -e " ${BLUE} Admin: http://localhost:3000${NC}" echo -e " ${BLUE} Viewer: http://localhost:3001${NC}" echo "" echo -e "${GREEN}For more information, see README.md${NC}" echo "" } # Run main function main