punimtag/install.sh
Tanya 906e2cbe19 feat: Enhance installation script and documentation for Python tkinter support
This commit updates the `install.sh` script to include the installation of Python tkinter, which is required for the native folder picker functionality. Additionally, the README.md is modified to reflect this new requirement, providing installation instructions for various operating systems. The documentation is further enhanced with troubleshooting tips for users encountering issues with the folder picker, ensuring a smoother setup experience.
2026-01-06 12:29:40 -05:00

501 lines
16 KiB
Bash
Executable File

#!/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
# Install Python tkinter (required for native folder picker)
if ! python3 -c "import tkinter" 2>/dev/null; then
echo -e "${BLUE} Installing Python tkinter...${NC}"
sudo apt install -y python3-tk
echo -e "${GREEN} ✅ Python tkinter installed${NC}"
else
echo -e "${GREEN} ✅ Python tkinter 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