Files
stack/scripts/setup-wizard.sh
Jason Woltje 6521cba735
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
feat: add flexible docker-compose architecture with profiles
- Add OpenBao services to docker-compose.yml with profiles (openbao, full)
- Add docker-compose.build.yml for local builds vs registry pulls
- Make PostgreSQL and Valkey optional via profiles (database, cache)
- Create example compose files for common deployment scenarios:
  - docker/docker-compose.example.turnkey.yml (all bundled)
  - docker/docker-compose.example.external.yml (all external)
  - docker/docker.example.hybrid.yml (mixed deployment)
- Update documentation:
  - Enhance .env.example with profiles and external service examples
  - Update README.md with deployment mode quick starts
  - Add deployment scenarios to docs/OPENBAO.md
  - Create docker/DOCKER-COMPOSE-GUIDE.md with comprehensive guide
- Clean up repository structure:
  - Move shell scripts to scripts/ directory
  - Move documentation to docs/ directory
  - Move docker compose examples to docker/ directory
- Configure for external Authentik with internal services:
  - Comment out Authentik services (using external OIDC)
  - Comment out unused volumes for disabled services
  - Keep postgres, valkey, openbao as internal services

This provides a flexible deployment architecture supporting turnkey,
production (all external), and hybrid configurations via Docker Compose
profiles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:55:33 -06:00

222 lines
8.4 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
echo "═══════════════════════════════════════════════════════"
echo " Mosaic Stack - Interactive Setup Wizard"
echo "═══════════════════════════════════════════════════════"
echo ""
# Backup existing .env if it exists
if [ -f .env ]; then
BACKUP=".env.bak.$(date +%Y%m%d_%H%M%S)"
cp .env "$BACKUP"
echo "✓ Backed up existing .env to $BACKUP"
echo ""
fi
# Start with .env.example as base
if [ ! -f .env ]; then
cp .env.example .env
fi
# Helper function to update or add env var
update_env() {
local key=$1
local value=$2
if grep -q "^$key=" .env; then
sed -i "s|^$key=.*|$key=$value|" .env
else
echo "$key=$value" >> .env
fi
}
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Domain Configuration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
read -p "Web domain [app.mosaicstack.dev]: " WEB_DOMAIN
WEB_DOMAIN=${WEB_DOMAIN:-app.mosaicstack.dev}
update_env "MOSAIC_WEB_DOMAIN" "$WEB_DOMAIN"
read -p "API domain [api.mosaicstack.dev]: " API_DOMAIN
API_DOMAIN=${API_DOMAIN:-api.mosaicstack.dev}
update_env "MOSAIC_API_DOMAIN" "$API_DOMAIN"
# Update NEXT_PUBLIC_API_URL to use the configured domain
update_env "NEXT_PUBLIC_API_URL" "https://$API_DOMAIN"
update_env "MOSAIC_BASE_URL" "https://$WEB_DOMAIN"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Authentication Configuration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
read -p "Authentik OIDC Issuer URL: " OIDC_ISSUER
if [ -n "$OIDC_ISSUER" ]; then
update_env "OIDC_ISSUER" "$OIDC_ISSUER"
fi
read -p "OIDC Client ID: " OIDC_CLIENT_ID
if [ -n "$OIDC_CLIENT_ID" ]; then
update_env "OIDC_CLIENT_ID" "$OIDC_CLIENT_ID"
fi
read -sp "OIDC Client Secret: " OIDC_CLIENT_SECRET
echo ""
if [ -n "$OIDC_CLIENT_SECRET" ]; then
update_env "OIDC_CLIENT_SECRET" "$OIDC_CLIENT_SECRET"
fi
# Auto-set redirect URI
REDIRECT_URI="https://${API_DOMAIN}/auth/callback/authentik"
update_env "OIDC_REDIRECT_URI" "$REDIRECT_URI"
echo "✓ Set redirect URI to: $REDIRECT_URI"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " AI Provider Configuration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Choose AI provider for orchestrator agents:"
echo " 1) Ollama (local or remote) - Recommended for self-hosted"
echo " 2) Claude API (requires API key)"
echo " 3) OpenAI API (requires API key)"
echo " 4) Skip (configure manually later)"
echo ""
read -p "Select [1]: " AI_CHOICE
AI_CHOICE=${AI_CHOICE:-1}
case $AI_CHOICE in
1)
echo ""
read -p "Ollama endpoint [http://localhost:11434]: " OLLAMA_ENDPOINT
OLLAMA_ENDPOINT=${OLLAMA_ENDPOINT:-http://localhost:11434}
update_env "OLLAMA_ENDPOINT" "$OLLAMA_ENDPOINT"
update_env "OLLAMA_MODE" "remote"
read -p "Ollama model for orchestrator [llama3.1:latest]: " OLLAMA_MODEL
OLLAMA_MODEL=${OLLAMA_MODEL:-llama3.1:latest}
update_env "OLLAMA_MODEL" "$OLLAMA_MODEL"
update_env "AI_PROVIDER" "ollama"
echo "✓ Configured Ollama at $OLLAMA_ENDPOINT"
echo " Note: Claude API key is NOT required for Ollama mode"
;;
2)
echo ""
read -sp "Claude API Key: " CLAUDE_API_KEY
echo ""
if [ -n "$CLAUDE_API_KEY" ]; then
update_env "CLAUDE_API_KEY" "$CLAUDE_API_KEY"
update_env "AI_PROVIDER" "claude"
echo "✓ Configured Claude API"
fi
;;
3)
echo ""
read -sp "OpenAI API Key: " OPENAI_API_KEY
echo ""
if [ -n "$OPENAI_API_KEY" ]; then
update_env "OPENAI_API_KEY" "$OPENAI_API_KEY"
update_env "AI_PROVIDER" "openai"
echo "✓ Configured OpenAI API"
fi
;;
4)
echo "✓ Skipping AI provider configuration"
update_env "AI_PROVIDER" "ollama"
echo " Defaulting to Ollama (configure OLLAMA_ENDPOINT in .env)"
;;
esac
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Security Configuration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Generate secrets if not present or contain REPLACE
echo "Generating security secrets..."
if ! grep -q "^JWT_SECRET=" .env || grep -q "REPLACE" .env; then
JWT_SECRET=$(openssl rand -base64 32)
update_env "JWT_SECRET" "$JWT_SECRET"
echo "✓ Generated JWT_SECRET"
fi
if ! grep -q "^ENCRYPTION_KEY=" .env || grep -q "REPLACE" .env; then
ENCRYPTION_KEY=$(openssl rand -hex 32)
update_env "ENCRYPTION_KEY" "$ENCRYPTION_KEY"
echo "✓ Generated ENCRYPTION_KEY"
fi
if ! grep -q "^BETTER_AUTH_SECRET=" .env || grep -q "REPLACE" .env; then
BETTER_AUTH_SECRET=$(openssl rand -base64 32)
update_env "BETTER_AUTH_SECRET" "$BETTER_AUTH_SECRET"
echo "✓ Generated BETTER_AUTH_SECRET"
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Database Configuration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
if ! grep -q "^POSTGRES_PASSWORD=" .env || grep -q "REPLACE" .env; then
echo "Generate new PostgreSQL password? (recommended for new installations)"
read -p "[y/N]: " GEN_DB_PASS
if [[ $GEN_DB_PASS =~ ^[Yy]$ ]]; then
POSTGRES_PASSWORD=$(openssl rand -base64 24)
update_env "POSTGRES_PASSWORD" "$POSTGRES_PASSWORD"
# Update DATABASE_URL
update_env "DATABASE_URL" "postgresql://mosaic:${POSTGRES_PASSWORD}@postgres:5432/mosaic"
echo "✓ Generated PostgreSQL password"
else
echo "✓ Keeping existing PostgreSQL password"
fi
else
echo "✓ Using existing PostgreSQL password"
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Traefik Configuration"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Use external Traefik instance? [Y/n]: "
read -p "" USE_EXTERNAL_TRAEFIK
USE_EXTERNAL_TRAEFIK=${USE_EXTERNAL_TRAEFIK:-Y}
if [[ $USE_EXTERNAL_TRAEFIK =~ ^[Yy]$ ]]; then
update_env "TRAEFIK_MODE" "upstream"
update_env "TRAEFIK_NETWORK" "traefik-public"
echo "✓ Configured for upstream Traefik"
else
update_env "TRAEFIK_MODE" "bundled"
echo "✓ Configured for bundled Traefik"
fi
echo ""
echo "═══════════════════════════════════════════════════════"
echo " Configuration Complete!"
echo "═══════════════════════════════════════════════════════"
echo ""
echo "Configuration saved to .env"
echo ""
echo "Next steps:"
echo " 1. Review .env file: nano .env"
echo " 2. Deploy stack:"
if [ -f deploy-swarm.sh ]; then
echo " ./deploy-swarm.sh mosaic"
else
echo " docker compose up -d"
fi
echo ""
echo "Services will be available at:"
echo " Web: https://$WEB_DOMAIN"
echo " API: https://$API_DOMAIN"
echo ""