diff --git a/.env b/.env index a686c0b..fbf1c1f 100644 --- a/.env +++ b/.env @@ -2,6 +2,20 @@ # CLASSROOM COPILOT - ENVIRONMENT CONFIGURATION ############################################################# +## ===================================================== +## DOCKER INITIALIZATION CONFIGURATION +## ===================================================== +# Set RUN_INIT=true to run initialization tasks on container startup +# Set to false or remove to skip initialization (for subsequent deployments) +RUN_INIT=true + +# INIT_MODE options: +# - infra: Infrastructure only (Neo4j schema, calendar, Supabase buckets) +# - full: Full setup including demo school and users (infra → demo-school → demo-users → gais-data) +# - infra,demo-school,demo-users: Custom combination (comma-separated) +# - infra,gais-data: Infrastructure + GAIS data import +INIT_MODE=full + ## ===================================================== ## APP INFORMATION & METADATA ## ===================================================== @@ -35,7 +49,7 @@ ADMIN_WORKER_EMAIL=kcar@kevlarai.com ## ===================================================== ## SUPABASE DATABASE CONFIGURATION ## ===================================================== -SUPABASE_URL=http://supa.classroomcopilot.ai +SUPABASE_URL=https://supa.classroomcopilot.ai ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU POSTGRES_PASSWORD=your-super-secret-and-long-postgres-password @@ -56,7 +70,7 @@ PORT_NEO4J_HTTPS=7473 ## ===================================================== ## OLLAMA AI SERVICE CONFIGURATION ## ===================================================== -HOST_OLLAMA=http://localhost +HOST_OLLAMA=http://ollama.kevlarai.com PORT_OLLAMA=11434 OLLAMA_MODEL=qwen2.5-coder:32b @@ -77,7 +91,7 @@ GOOGLE_CLIENT_SECRETS_FILE=Users/kcar/ClassroomCopilot/backend/app/secrets/googl ## DOCUMENT PROCESSING SERVICES ## ===================================================== # External Service Endpoints -TIKA_URL=http://ubuntu-ct-tika:9998 +TIKA_URL=https://tika.kevlarai.com TIKA_TIMEOUT=300 DOCLING_URL=http://ubuntu-server:5001 @@ -184,10 +198,10 @@ DOCLING_VLM_DO_PICTURE_DESCRIPTION=true ## ===================================================== ## APPLICATION DOMAINS & URLS ## ===================================================== -VITE_APP_URL=http://localhost:3000 -APP_API_URL=http://localhost:{UVICORN_PORT} -APP_GRAPH_URL=http://localhost:7474 -APP_BOLT_URL=bolt://localhost:7687 +VITE_APP_URL=https://app.classroomcopilot.ai +APP_API_URL=https://api.classroomcopilot.ai +APP_GRAPH_URL=https://graph.classroomcopilot.ai +APP_BOLT_URL=bolt://bolt.classroomcopilot.ai ## ===================================================== ## REDIS CONFIGURATION & ENVIRONMENT ISOLATION @@ -234,7 +248,7 @@ UPLOAD_STATUS_POLLING_INTERVAL=5 # Status polling interval (seconds) ## ===================================================== ## CORS & SECURITY SETTINGS ## ===================================================== -CORS_SITE_URL=http://localhost:5173,http://localhost:8000,http://127.0.0.1:8000 +CORS_SITE_URL=https://app.classroomcopilot.ai,https://api.classroomcopilot.ai,https://graph.classroomcopilot.ai CORS_GRAPH_URL={APP_GRAPH_URL} CORS_API_URL={APP_API_URL} diff --git a/Dockerfile b/Dockerfile index 080ae92..f85c9bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,8 +13,9 @@ RUN pip install --no-cache-dir -r requirements.txt # Copy application code COPY . . -# Make startup script executable +# Make startup scripts executable RUN chmod +x start.sh +RUN chmod +x docker-entrypoint.sh # Set environment variables ENV PYTHONPATH=/app @@ -24,8 +25,8 @@ ENV UVICORN_WORKERS=4 # Expose port EXPOSE 8000 -# Use the new startup script in production mode -CMD ["./start.sh", "prod"] +# Use the production entrypoint script (supports initialization) +ENTRYPOINT ["./docker-entrypoint.sh"] # Alternative: Run initialization first, then production server # CMD ["sh", "-c", "./start.sh init && ./start.sh prod"] diff --git a/FIRST_RUN_SETUP.md b/FIRST_RUN_SETUP.md new file mode 100644 index 0000000..c1671bd --- /dev/null +++ b/FIRST_RUN_SETUP.md @@ -0,0 +1,128 @@ +# First Run Setup Guide + +This guide shows how to enable full initialization including demo users and schools on first deployment. + +## Quick Start: Full Setup with Demo Data + +### Option 1: Using .env file (Recommended) + +Add these lines to your `.env` file: + +```bash +RUN_INIT=true +INIT_MODE=full +``` + +Then start: +```bash +docker compose up --build +``` + +This will run: +1. ✅ Infrastructure setup (Neo4j schema, calendar, Supabase buckets) +2. ✅ Demo school creation (KevlarAI) +3. ✅ Demo users creation +4. ✅ GAIS data import (Edubase, etc.) + +### Option 2: Environment Variable Override + +```bash +RUN_INIT=true INIT_MODE=full docker compose up --build +``` + +### Option 3: Custom Combination + +If you want infrastructure + demo data but skip GAIS import: + +```bash +# In .env or as environment variable +RUN_INIT=true +INIT_MODE=infra,demo-school,demo-users +``` + +## Available INIT_MODE Options + +| Mode | What It Does | +|------|-------------| +| `infra` | Infrastructure only (Neo4j schema, calendar, Supabase buckets) | +| `demo-school` | Creates demo school (KevlarAI) | +| `demo-users` | Creates demo users | +| `gais-data` | Imports GAIS data (Edubase, etc.) | +| `full` | **All of the above** (infra → demo-school → demo-users → gais-data) | +| `infra,demo-school,demo-users` | Custom: Infrastructure + demo data (no GAIS) | +| `infra,gais-data` | Infrastructure + GAIS data (no demo data) | + +## Examples + +### Full setup with everything (recommended for first run) +```bash +RUN_INIT=true +INIT_MODE=full +``` + +### Infrastructure + demo data only (no GAIS import) +```bash +RUN_INIT=true +INIT_MODE=infra,demo-school,demo-users +``` + +### Infrastructure only (production, no demo data) +```bash +RUN_INIT=true +INIT_MODE=infra +``` + +### Infrastructure + GAIS data (production with public school data) +```bash +RUN_INIT=true +INIT_MODE=infra,gais-data +``` + +## After First Run + +Once initialization is complete, you can disable automatic initialization for subsequent deployments: + +```bash +# In .env file +RUN_INIT=false +``` + +Or simply remove/comment out the `RUN_INIT` line. The backend will start normally without running initialization tasks. + +## Manual Re-initialization + +If you need to re-run initialization later: + +```bash +# Run specific mode +docker compose exec backend python3 main.py --mode infra +docker compose exec backend python3 main.py --mode demo-school +docker compose exec backend python3 main.py --mode demo-users + +# Or use the helper script +docker compose exec backend ./init-production.sh full +``` + +## Troubleshooting + +### Check if initialization ran +```bash +docker compose logs backend | grep -i "initialization\|infra\|demo" +``` + +### Re-run initialization +If initialization failed or you need to re-run it: +```bash +# Set in .env temporarily +RUN_INIT=true +INIT_MODE=full + +# Rebuild and start +docker compose up --build +``` + +### Skip initialization on startup +```bash +RUN_INIT=false docker compose up --build +``` + diff --git a/PRODUCTION_INIT.md b/PRODUCTION_INIT.md new file mode 100644 index 0000000..e1e58f2 --- /dev/null +++ b/PRODUCTION_INIT.md @@ -0,0 +1,173 @@ +# Production Initialization Guide + +This guide explains how to run initialization tasks (Supabase table setup, Neo4j schema, etc.) in production environments. + +## Overview + +In development, you use `./start.sh` to run initialization tasks. In production (Docker), you have several options: + +## Option 1: Automatic Initialization on Startup (Recommended for First Deploy) + +Set environment variables to run initialization when the container starts: + +```bash +# In your .env file or docker-compose.yml +RUN_INIT=true +INIT_MODE=infra # or: infra,gais-data or full +``` + +Then start normally: +```bash +docker compose up --build +``` + +The backend container will: +1. Run the specified initialization tasks +2. Start the production server + +**Note:** This runs init every time the container starts. For subsequent deployments, use Option 2 or 3. + +## Option 2: Run Initialization as Separate Service (One-Time) + +Use the dedicated init service: + +```bash +# Run infrastructure setup +docker compose --profile init up init + +# Or run multiple tasks +INIT_MODE=infra,gais-data docker compose --profile init up init +``` + +This runs once and exits. The backend service doesn't depend on it, so you can run it independently. + +## Option 3: Manual Initialization (Recommended for Updates) + +Run initialization tasks manually using the helper script: + +```bash +# On the host machine +./init-production.sh infra + +# Or using Docker Compose directly +docker compose run --rm backend python3 main.py --mode infra +``` + +### Available Initialization Modes + +- **`infra`** - Essential setup: + - Neo4j schema and constraints + - Calendar structure + - Supabase storage buckets + +- **`demo-school`** - Creates demo school (KevlarAI) + - Usually only needed for development/demo environments + +- **`demo-users`** - Creates demo users + - Usually only needed for development/demo environments + +- **`gais-data`** - Imports GAIS data (Edubase, etc.) + - Public school database imports + - May be needed in production + +- **`full`** - Runs all initialization tasks in order + - infra → demo-school → demo-users → gais-data + +## Option 4: Run Inside Running Container + +If your container is already running: + +```bash +# Execute init command inside running container +docker compose exec backend python3 main.py --mode infra + +# Or use the helper script +docker compose exec backend ./init-production.sh infra +``` + +## Recommended Production Workflow + +### First Deployment + +1. **Initial Setup:** + ```bash + # Set in .env or docker-compose.yml + RUN_INIT=true + INIT_MODE=infra + + # Start services + docker compose up --build + ``` + +2. **Import Data (if needed):** + ```bash + docker compose exec backend python3 main.py --mode gais-data + ``` + +### Subsequent Deployments + +1. **Normal startup (no init):** + ```bash + # Ensure RUN_INIT is false or not set + docker compose up --build + ``` + +2. **Run init only when needed:** + ```bash + # When schema changes or new buckets needed + docker compose exec backend python3 main.py --mode infra + ``` + +## Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `RUN_INIT` | `false` | Set to `true` to run initialization on container startup | +| `INIT_MODE` | `infra` | Comma-separated list of modes: `infra`, `demo-school`, `demo-users`, `gais-data`, or `full` | + +## Examples + +### Run only infrastructure setup +```bash +INIT_MODE=infra docker compose up --build +``` + +### Run infrastructure and GAIS data import +```bash +INIT_MODE=infra,gais-data docker compose up --build +``` + +### Run full initialization (including demo data) +```bash +INIT_MODE=full docker compose up --build +``` + +### Run init separately before starting backend +```bash +# Run init +INIT_MODE=infra docker compose --profile init up init + +# Start backend (init already done) +docker compose up backend +``` + +## Troubleshooting + +### Init fails on startup +If initialization fails, the container will exit. Check logs: +```bash +docker compose logs backend +``` + +### Need to re-run init +Simply run the init command again - most tasks are idempotent: +```bash +docker compose exec backend python3 main.py --mode infra +``` + +### Skip init on startup +Ensure `RUN_INIT` is not set or is `false`: +```bash +RUN_INIT=false docker compose up --build +``` + diff --git a/docker-compose.yml b/docker-compose.yml index 21a9786..2134126 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,6 +15,28 @@ services: timeout: 3s retries: 5 + # Initialization service - runs setup tasks before backend starts + init: + build: + context: . + dockerfile: Dockerfile + container_name: api-init + env_file: + - .env + environment: + - REDIS_HOST=redis + - RUN_INIT=true + - INIT_MODE=${INIT_MODE:-infra} # Set via .env or override: INIT_MODE=infra,gais-data + - INIT_ONLY=true # Exit after init, don't start server + command: ["./docker-entrypoint.sh", "init-only"] + depends_on: + redis: + condition: service_healthy + networks: + - kevlarai-network + profiles: + - init # Only run when explicitly requested: docker compose --profile init up + backend: container_name: api build: @@ -24,6 +46,8 @@ services: - .env environment: - REDIS_HOST=redis + - RUN_INIT=${RUN_INIT:-false} # Set to 'true' to run init on startup + - INIT_MODE=${INIT_MODE:-infra} # Which init tasks to run ports: - 8000:8000 depends_on: diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..57c4676 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,108 @@ +#!/bin/bash +# Production entrypoint script for ClassroomCopilot API +# Supports running initialization tasks before starting the server + +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 + +print_status() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +print_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Check if we should run initialization +RUN_INIT="${RUN_INIT:-false}" +INIT_MODE="${INIT_MODE:-infra}" # Default to 'infra', can be 'infra', 'full', or comma-separated list + +# If RUN_INIT is true, run initialization tasks +if [ "$RUN_INIT" = "true" ]; then + print_status "Running initialization tasks (mode: $INIT_MODE)..." + + # Split INIT_MODE by comma if it contains multiple modes + IFS=',' read -ra MODES <<< "$INIT_MODE" + + for mode in "${MODES[@]}"; do + mode=$(echo "$mode" | xargs) # Trim whitespace + print_status "Running initialization mode: $mode" + + case "$mode" in + "infra") + print_status "Setting up infrastructure (Neo4j schema, calendar, Supabase buckets)..." + python3 main.py --mode infra || { + print_error "Infrastructure setup failed!" + exit 1 + } + print_success "Infrastructure setup completed" + ;; + "demo-school") + print_status "Creating demo school..." + python3 main.py --mode demo-school || { + print_error "Demo school creation failed!" + exit 1 + } + print_success "Demo school creation completed" + ;; + "demo-users") + print_status "Creating demo users..." + python3 main.py --mode demo-users || { + print_error "Demo users creation failed!" + exit 1 + } + print_success "Demo users creation completed" + ;; + "gais-data") + print_status "Importing GAIS data..." + python3 main.py --mode gais-data || { + print_error "GAIS data import failed!" + exit 1 + } + print_success "GAIS data import completed" + ;; + "full") + print_status "Running full initialization..." + python3 main.py --mode infra || exit 1 + python3 main.py --mode demo-school || exit 1 + python3 main.py --mode demo-users || exit 1 + python3 main.py --mode gais-data || exit 1 + print_success "Full initialization completed" + ;; + *) + print_warning "Unknown initialization mode: $mode (skipping)" + ;; + esac + done + + print_success "All initialization tasks completed" + + # If this is the init service (not backend), exit after init + if [ "$1" = "init-only" ] || [ -n "$INIT_ONLY" ]; then + print_status "Initialization complete - exiting (init-only mode)" + exit 0 + fi +fi + +# Start the production server (unless init-only mode) +if [ "$1" != "init-only" ] && [ -z "$INIT_ONLY" ]; then + print_status "Starting production server..." + exec ./start.sh prod +else + print_status "Init-only mode - not starting server" +fi + diff --git a/init-production.sh b/init-production.sh new file mode 100755 index 0000000..e689595 --- /dev/null +++ b/init-production.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Helper script to run initialization tasks in production +# Usage: ./init-production.sh [mode] +# Modes: infra, demo-school, demo-users, gais-data, full + +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 + +print_status() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +print_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +MODE=${1:-infra} + +print_status "Running production initialization: $MODE" + +# Check if running in Docker +if [ -f /.dockerenv ] || [ -n "$DOCKER_CONTAINER" ]; then + print_status "Running inside Docker container" + python3 main.py --mode "$MODE" +else + print_status "Running on host - using Docker Compose" + + # Run init using docker compose + docker compose run --rm \ + -e RUN_INIT=true \ + -e INIT_MODE="$MODE" \ + backend python3 main.py --mode "$MODE" +fi + +print_success "Initialization completed: $MODE" +