kaboot/scripts/setup-prod.sh
2026-01-15 21:05:49 -07:00

290 lines
9.7 KiB
Bash
Executable file

#!/bin/bash
set -e
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
BOLD='\033[1m'
NC='\033[0m'
print_header() {
echo ""
echo -e "${BLUE}${BOLD}════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE}${BOLD} Kaboot Production Setup${NC}"
echo -e "${BLUE}${BOLD}════════════════════════════════════════════════════════════${NC}"
echo ""
}
print_step() {
echo -e "${GREEN}${BOLD}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_header
KABOOT_DOMAIN=""
AUTH_DOMAIN=""
GEMINI_API_KEY=""
while [[ $# -gt 0 ]]; do
case $1 in
--domain)
KABOOT_DOMAIN="$2"
shift 2
;;
--auth-domain)
AUTH_DOMAIN="$2"
shift 2
;;
--gemini-key)
GEMINI_API_KEY="$2"
shift 2
;;
--help|-h)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --domain DOMAIN Main application domain (e.g., kaboot.example.com)"
echo " --auth-domain DOMAIN Authentication domain (e.g., auth.example.com)"
echo " --gemini-key KEY Gemini API key for system AI (optional)"
echo " --help, -h Show this help message"
echo ""
echo "If options are not provided, you will be prompted for them."
exit 0
;;
*)
print_error "Unknown option: $1"
exit 1
;;
esac
done
if [ ! -f ".env.example" ]; then
print_error "Error: .env.example not found. Run this script from the project root."
exit 1
fi
if [ -f ".env" ]; then
print_warning ".env file already exists."
read -p "Overwrite existing configuration? (y/N): " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborting. Existing configuration preserved."
exit 0
fi
fi
echo -e "${BOLD}Domain Configuration${NC}"
echo "────────────────────────────────────────────────────────────"
if [ -z "$KABOOT_DOMAIN" ]; then
read -p "Enter your main domain (e.g., kaboot.example.com): " KABOOT_DOMAIN
fi
if [ -z "$KABOOT_DOMAIN" ]; then
print_error "Domain is required."
exit 1
fi
if [ -z "$AUTH_DOMAIN" ]; then
DEFAULT_AUTH="auth.${KABOOT_DOMAIN}"
read -p "Enter your auth domain [$DEFAULT_AUTH]: " AUTH_DOMAIN
AUTH_DOMAIN=${AUTH_DOMAIN:-$DEFAULT_AUTH}
fi
echo ""
echo -e "${BOLD}System AI Configuration (Optional)${NC}"
echo "────────────────────────────────────────────────────────────"
echo "You can provide a Gemini API key to enable AI quiz generation"
echo "for all users without requiring them to set up their own key."
echo -e "${YELLOW}Note: This key will be embedded in the frontend and visible to users.${NC}"
echo ""
if [ -z "$GEMINI_API_KEY" ]; then
read -p "Enter Gemini API key (or press Enter to skip): " GEMINI_API_KEY
fi
echo ""
print_step "Generating secrets..."
PG_PASS=$(openssl rand -base64 36 | tr -d '\n' | tr -d '/')
AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -d '\n' | tr -d '/')
AUTHENTIK_BOOTSTRAP_PASSWORD=$(openssl rand -base64 24 | tr -d '\n' | tr -d '/')
AUTHENTIK_BOOTSTRAP_TOKEN=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(openssl rand -base64 36 | tr -d '\n' | tr -d '/')
print_success "Secrets generated"
print_step "Creating .env file..."
cat > .env << EOF
# Kaboot Production Configuration
# Generated by setup-prod.sh on $(date)
# Database
PG_PASS=${PG_PASS}
PG_USER=authentik
PG_DB=authentik
# Authentik Secrets
AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY}
AUTHENTIK_BOOTSTRAP_PASSWORD=${AUTHENTIK_BOOTSTRAP_PASSWORD}
AUTHENTIK_BOOTSTRAP_TOKEN=${AUTHENTIK_BOOTSTRAP_TOKEN}
AUTHENTIK_ERROR_REPORTING=false
# Backend Encryption
ENCRYPTION_KEY=${ENCRYPTION_KEY}
# OIDC Configuration (Production)
OIDC_ISSUER=https://${AUTH_DOMAIN}/application/o/kaboot/
OIDC_JWKS_URI=https://${AUTH_DOMAIN}/application/o/kaboot/jwks/
# Security
CORS_ORIGIN=https://${KABOOT_DOMAIN}
NODE_ENV=production
LOG_REQUESTS=true
# System AI (optional - server-side quiz generation)
GEMINI_API_KEY=${GEMINI_API_KEY}
EOF
print_success "Created .env"
print_step "Creating Caddyfile..."
cat > Caddyfile << EOF
# Kaboot Production Caddyfile
# Generated by setup-prod.sh on $(date)
${KABOOT_DOMAIN} {
root * /srv/frontend
file_server
try_files {path} /index.html
handle /api/* {
reverse_proxy kaboot-backend:3001
}
handle /health {
reverse_proxy kaboot-backend:3001
}
}
${AUTH_DOMAIN} {
reverse_proxy authentik-server:9000 {
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-Host {host}
transport http {
keepalive 30s
}
}
}
EOF
print_success "Created Caddyfile"
print_step "Creating production Authentik blueprint..."
BLUEPRINT_DIR="authentik/blueprints"
PROD_BLUEPRINT="${BLUEPRINT_DIR}/kaboot-setup-production.yaml"
DEV_BLUEPRINT="${BLUEPRINT_DIR}/kaboot-setup.yaml"
if [ -f "${BLUEPRINT_DIR}/kaboot-setup-production.yaml.example" ]; then
sed "s/kaboot.example.com/${KABOOT_DOMAIN}/g; s/auth.example.com/${AUTH_DOMAIN}/g" \
"${BLUEPRINT_DIR}/kaboot-setup-production.yaml.example" > "$PROD_BLUEPRINT"
print_success "Created production blueprint"
else
print_warning "Production blueprint example not found, skipping..."
fi
if [ -f "$DEV_BLUEPRINT" ]; then
rm "$DEV_BLUEPRINT"
print_success "Removed development blueprint"
fi
print_step "Installing frontend dependencies..."
npm install --silent 2>/dev/null || npm install
print_success "Dependencies installed"
print_step "Building frontend with production URLs..."
VITE_API_URL="https://${KABOOT_DOMAIN}" \
VITE_BACKEND_URL="https://${KABOOT_DOMAIN}" \
VITE_AUTHENTIK_URL="https://${AUTH_DOMAIN}" \
VITE_OIDC_CLIENT_ID="kaboot-spa" \
VITE_OIDC_APP_SLUG="kaboot" \
npm run build --silent 2>/dev/null || npm run build
if [ -n "$GEMINI_API_KEY" ]; then
print_success "System AI will be available (key configured for backend)"
else
print_warning "No Gemini API key provided - users must configure their own"
fi
print_success "Frontend built"
echo ""
echo -e "${GREEN}${BOLD}════════════════════════════════════════════════════════════${NC}"
echo -e "${GREEN}${BOLD} Setup Complete!${NC}"
echo -e "${GREEN}${BOLD}════════════════════════════════════════════════════════════${NC}"
echo ""
echo -e "${BOLD}Configuration Summary${NC}"
echo "────────────────────────────────────────────────────────────"
echo -e " Main Domain: ${BLUE}https://${KABOOT_DOMAIN}${NC}"
echo -e " Auth Domain: ${BLUE}https://${AUTH_DOMAIN}${NC}"
if [ -n "$GEMINI_API_KEY" ]; then
echo -e " System AI: ${GREEN}Enabled${NC}"
else
echo -e " System AI: ${YELLOW}Disabled (users need own API key)${NC}"
fi
echo ""
echo -e "${BOLD}Authentik Admin${NC}"
echo "────────────────────────────────────────────────────────────"
echo -e " Admin URL: ${BLUE}https://${AUTH_DOMAIN}/if/admin/${NC}"
echo -e " Username: ${YELLOW}akadmin${NC}"
echo -e " Password: ${YELLOW}${AUTHENTIK_BOOTSTRAP_PASSWORD}${NC}"
echo ""
echo -e "${RED}${BOLD}⚠ SAVE THESE CREDENTIALS - They won't be shown again!${NC}"
echo ""
echo -e "${BOLD}Files Created${NC}"
echo "────────────────────────────────────────────────────────────"
echo " .env - Environment variables"
echo " Caddyfile - Reverse proxy config"
echo " authentik/blueprints/kaboot-setup-production.yaml"
echo " dist/ - Built frontend"
echo ""
echo -e "${BOLD}Next Steps${NC}"
echo "────────────────────────────────────────────────────────────"
echo ""
echo " 1. Ensure DNS records point to this server:"
echo -e " ${KABOOT_DOMAIN}${BLUE}<your-server-ip>${NC}"
echo -e " ${AUTH_DOMAIN}${BLUE}<your-server-ip>${NC}"
echo ""
echo " 2. Start the production stack:"
echo -e " ${YELLOW}docker compose -f docker-compose.prod.yml -f docker-compose.caddy.yml up -d${NC}"
echo ""
echo " 3. Wait for services to start (~60 seconds for Authentik)"
echo ""
echo " 4. Verify all services are running:"
echo -e " ${YELLOW}docker compose -f docker-compose.prod.yml -f docker-compose.caddy.yml ps${NC}"
echo ""
echo " 5. Check Authentik blueprint was applied:"
echo -e " ${YELLOW}docker compose -f docker-compose.prod.yml logs authentik-server | grep -i blueprint${NC}"
echo ""
echo " 6. Access your app at:"
echo -e " ${BLUE}https://${KABOOT_DOMAIN}${NC}"
echo ""