301 lines
10 KiB
Bash
Executable File
301 lines
10 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# DictIA — Main setup script
|
|
#
|
|
# Interactive installer that detects hardware and configures the appropriate
|
|
# deployment profile (cloud, local-cpu, local-gpu).
|
|
#
|
|
# Usage:
|
|
# bash deployment/setup.sh # Interactive mode
|
|
# bash deployment/setup.sh --profile cloud # Non-interactive
|
|
# bash deployment/setup.sh --profile local-gpu
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
PROFILE=""
|
|
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--profile=*) PROFILE="${arg#*=}" ;;
|
|
--profile) shift_next=true ;;
|
|
*)
|
|
if [ "${shift_next:-false}" = true ]; then
|
|
PROFILE="$arg"
|
|
shift_next=false
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# --- Colors ---
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m'
|
|
|
|
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
|
|
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
|
err() { echo -e "${RED}[ERROR]${NC} $*"; }
|
|
|
|
echo
|
|
echo -e "${CYAN}========================================${NC}"
|
|
echo -e "${CYAN} DictIA — Setup${NC}"
|
|
echo -e "${CYAN}========================================${NC}"
|
|
echo
|
|
|
|
# ==========================================================================
|
|
# 1. Hardware Detection
|
|
# ==========================================================================
|
|
info "Detecting hardware..."
|
|
|
|
# Docker
|
|
if command -v docker &>/dev/null && docker info &>/dev/null; then
|
|
DOCKER_VERSION=$(docker --version | grep -oP '\d+\.\d+\.\d+' | head -1)
|
|
ok "Docker $DOCKER_VERSION"
|
|
else
|
|
err "Docker not found or not running."
|
|
echo " Install Docker: https://docs.docker.com/engine/install/"
|
|
exit 1
|
|
fi
|
|
|
|
# Docker Compose
|
|
if docker compose version &>/dev/null; then
|
|
COMPOSE_VERSION=$(docker compose version --short 2>/dev/null || echo "unknown")
|
|
ok "Docker Compose $COMPOSE_VERSION"
|
|
else
|
|
err "Docker Compose not found."
|
|
echo " Docker Compose V2 is required (comes with Docker Desktop or docker-compose-plugin)"
|
|
exit 1
|
|
fi
|
|
|
|
# GPU
|
|
HAS_GPU=false
|
|
if command -v nvidia-smi &>/dev/null; then
|
|
GPU_NAME=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1 || echo "")
|
|
if [ -n "$GPU_NAME" ]; then
|
|
HAS_GPU=true
|
|
ok "NVIDIA GPU: $GPU_NAME"
|
|
# Check nvidia-container-toolkit
|
|
if docker info 2>/dev/null | grep -qi nvidia; then
|
|
ok "nvidia-container-toolkit detected"
|
|
else
|
|
warn "nvidia-container-toolkit not detected. Required for local-gpu profile."
|
|
echo " Install: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html"
|
|
fi
|
|
fi
|
|
else
|
|
info "No NVIDIA GPU detected"
|
|
fi
|
|
|
|
# RAM
|
|
if command -v free &>/dev/null; then
|
|
RAM_GB=$(free -g | awk '/Mem:/{print $2}')
|
|
info "RAM: ${RAM_GB}GB"
|
|
fi
|
|
|
|
# Disk
|
|
DISK_AVAIL=$(df -h "$PROJECT_DIR" 2>/dev/null | awk 'NR==2{print $4}')
|
|
info "Disk available: $DISK_AVAIL"
|
|
|
|
echo
|
|
|
|
# ==========================================================================
|
|
# 2. Profile Selection
|
|
# ==========================================================================
|
|
if [ -z "$PROFILE" ]; then
|
|
echo -e "${CYAN}Select deployment profile:${NC}"
|
|
echo
|
|
echo " 1) cloud — VPS with ASR Proxy (GCP GPU on demand)"
|
|
echo " Best for: remote servers, pay-per-use GPU"
|
|
echo
|
|
echo " 2) local-gpu — Local NVIDIA GPU for transcription"
|
|
echo " Best for: dedicated GPU server, fastest"
|
|
if [ "$HAS_GPU" = false ]; then
|
|
echo -e " ${YELLOW}(No GPU detected on this machine)${NC}"
|
|
fi
|
|
echo
|
|
echo " 3) local-cpu — CPU-only transcription (slow)"
|
|
echo " Best for: testing, low-volume usage"
|
|
echo
|
|
read -rp "Choice [1-3]: " CHOICE
|
|
case "$CHOICE" in
|
|
1) PROFILE="cloud" ;;
|
|
2) PROFILE="local-gpu" ;;
|
|
3) PROFILE="local-cpu" ;;
|
|
*) err "Invalid choice"; exit 1 ;;
|
|
esac
|
|
fi
|
|
|
|
COMPOSE_FILE="$SCRIPT_DIR/docker/docker-compose.$PROFILE.yml"
|
|
if [ ! -f "$COMPOSE_FILE" ]; then
|
|
err "Compose file not found: $COMPOSE_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
ok "Profile: $PROFILE"
|
|
echo
|
|
|
|
# ==========================================================================
|
|
# 3. Generate .env
|
|
# ==========================================================================
|
|
ENV_FILE="$PROJECT_DIR/.env"
|
|
|
|
if [ -f "$ENV_FILE" ]; then
|
|
warn ".env already exists. Keeping existing configuration."
|
|
echo " To reconfigure, delete .env and re-run setup."
|
|
else
|
|
info "Generating .env..."
|
|
|
|
# Generate secret key
|
|
SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))" 2>/dev/null \
|
|
|| openssl rand -hex 32 2>/dev/null \
|
|
|| head -c 64 /dev/urandom | xxd -p | head -c 64)
|
|
|
|
# Prompt for admin credentials
|
|
read -rp "Admin username [admin]: " ADMIN_USER
|
|
ADMIN_USER="${ADMIN_USER:-admin}"
|
|
read -rp "Admin email [admin@example.com]: " ADMIN_EMAIL
|
|
ADMIN_EMAIL="${ADMIN_EMAIL:-admin@example.com}"
|
|
read -rsp "Admin password: " ADMIN_PASS
|
|
echo
|
|
ADMIN_PASS="${ADMIN_PASS:-changeme}"
|
|
|
|
# Prompt for text model API key
|
|
echo
|
|
info "DictIA needs a text/LLM API key for summaries, titles, and chat."
|
|
echo " Recommended: OpenRouter (https://openrouter.ai) — access to many models"
|
|
read -rp "Text model API key (or press Enter to skip): " TEXT_API_KEY
|
|
TEXT_API_KEY="${TEXT_API_KEY:-your_openrouter_api_key}"
|
|
|
|
# HuggingFace token for diarization
|
|
if [ "$PROFILE" != "cloud" ]; then
|
|
echo
|
|
info "For speaker diarization, a HuggingFace token is needed."
|
|
echo " Get one at: https://huggingface.co/settings/tokens"
|
|
echo " Accept model: https://huggingface.co/pyannote/speaker-diarization-3.1"
|
|
read -rp "HuggingFace token (or press Enter to skip): " HF_TOKEN
|
|
HF_TOKEN="${HF_TOKEN:-}"
|
|
else
|
|
HF_TOKEN=""
|
|
fi
|
|
|
|
# Write .env
|
|
cp "$SCRIPT_DIR/docker/.env.example" "$ENV_FILE"
|
|
sed -i "s|SECRET_KEY=.*|SECRET_KEY=$SECRET_KEY|" "$ENV_FILE"
|
|
sed -i "s|DICTIA_PROFILE=.*|DICTIA_PROFILE=$PROFILE|" "$ENV_FILE"
|
|
sed -i "s|ADMIN_USERNAME=.*|ADMIN_USERNAME=$ADMIN_USER|" "$ENV_FILE"
|
|
sed -i "s|ADMIN_EMAIL=.*|ADMIN_EMAIL=$ADMIN_EMAIL|" "$ENV_FILE"
|
|
sed -i "s|ADMIN_PASSWORD=.*|ADMIN_PASSWORD=$ADMIN_PASS|" "$ENV_FILE"
|
|
sed -i "s|TEXT_MODEL_API_KEY=.*|TEXT_MODEL_API_KEY=$TEXT_API_KEY|" "$ENV_FILE"
|
|
sed -i "s|HF_TOKEN=.*|HF_TOKEN=$HF_TOKEN|" "$ENV_FILE"
|
|
|
|
ok ".env generated"
|
|
fi
|
|
echo
|
|
|
|
# ==========================================================================
|
|
# 4. Create data directories
|
|
# ==========================================================================
|
|
info "Creating data directories..."
|
|
mkdir -p "$PROJECT_DIR/data/uploads" "$PROJECT_DIR/data/instance"
|
|
ok "data/uploads and data/instance created"
|
|
echo
|
|
|
|
# ==========================================================================
|
|
# 5. Profile-specific setup
|
|
# ==========================================================================
|
|
case "$PROFILE" in
|
|
cloud)
|
|
info "Cloud profile — setting up ASR Proxy..."
|
|
if [ -f "$SCRIPT_DIR/asr-proxy/setup.sh" ]; then
|
|
echo " Run the ASR proxy setup separately:"
|
|
echo " bash $SCRIPT_DIR/asr-proxy/setup.sh"
|
|
fi
|
|
echo
|
|
info "Setting up iptables rules..."
|
|
if [ -f "$SCRIPT_DIR/security/iptables-rules.sh" ] && [ "$(id -u)" -eq 0 ]; then
|
|
bash "$SCRIPT_DIR/security/iptables-rules.sh"
|
|
else
|
|
echo " Run as root: sudo bash $SCRIPT_DIR/security/iptables-rules.sh"
|
|
fi
|
|
echo
|
|
info "Setting up Tailscale Serve..."
|
|
if command -v tailscale &>/dev/null; then
|
|
echo " Run: bash $SCRIPT_DIR/config/tailscale/setup-serve.sh"
|
|
else
|
|
warn "Tailscale not installed."
|
|
echo " Install: curl -fsSL https://tailscale.com/install.sh | sh"
|
|
fi
|
|
;;
|
|
local-gpu)
|
|
info "Local GPU profile — verifying NVIDIA runtime..."
|
|
if docker info 2>/dev/null | grep -qi nvidia; then
|
|
ok "NVIDIA Docker runtime available"
|
|
# Quick GPU test
|
|
if docker run --rm --gpus all nvidia/cuda:12.0-base nvidia-smi &>/dev/null; then
|
|
ok "GPU test passed"
|
|
else
|
|
warn "GPU test failed. Check nvidia-container-toolkit installation."
|
|
fi
|
|
else
|
|
err "NVIDIA Docker runtime not found."
|
|
echo " Install nvidia-container-toolkit and restart Docker."
|
|
echo " https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html"
|
|
fi
|
|
;;
|
|
local-cpu)
|
|
warn "CPU-only transcription is significantly slower than GPU."
|
|
echo " Expect ~10x real-time (1h audio = ~10h processing)."
|
|
echo " Consider local-gpu or cloud profile for better performance."
|
|
;;
|
|
esac
|
|
|
|
echo
|
|
|
|
# ==========================================================================
|
|
# 6. Build and start
|
|
# ==========================================================================
|
|
info "Building DictIA Docker image..."
|
|
cd "$PROJECT_DIR"
|
|
docker build -t innova-ai/dictia:latest .
|
|
ok "Image built"
|
|
|
|
echo
|
|
info "Starting DictIA ($PROFILE profile)..."
|
|
docker compose -f "$COMPOSE_FILE" up -d
|
|
ok "Containers started"
|
|
|
|
# ==========================================================================
|
|
# 7. Health check
|
|
# ==========================================================================
|
|
echo
|
|
info "Waiting for DictIA to become healthy..."
|
|
RETRIES=30
|
|
for i in $(seq 1 $RETRIES); do
|
|
if curl -sf -o /dev/null -m 5 http://localhost:8899/health 2>/dev/null; then
|
|
ok "DictIA is healthy!"
|
|
break
|
|
fi
|
|
if [ "$i" -eq "$RETRIES" ]; then
|
|
warn "Health check timeout. Check logs: docker compose -f $COMPOSE_FILE logs"
|
|
fi
|
|
sleep 5
|
|
done
|
|
|
|
echo
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo -e "${GREEN} DictIA is ready!${NC}"
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo
|
|
echo " App: http://localhost:8899"
|
|
echo " Profile: $PROFILE"
|
|
echo " Compose: $COMPOSE_FILE"
|
|
echo
|
|
echo " Tools:"
|
|
echo " Update: bash deployment/tools/update.sh"
|
|
echo " Backup: bash deployment/tools/backup.sh"
|
|
echo " Health check: bash deployment/tools/health-check.sh"
|
|
echo
|