mirror of
https://github.com/orris-inc/orris.git
synced 2026-05-06 21:44:01 +08:00
- Add RuleSetEntry value object for remote rule-set sources in sing-box route configuration, replacing inline geoip/geosite with rule_set references - Add rule-set entry serialization in DTO and persistence mapper layers - Add username/version fields to OutboundDTO for SOCKS/HTTP auth support - Update nginx reverse proxy examples with WebSocket upgrade headers - Fix GitHub OAuth to fall back to login when display name is empty - Redesign OAuth success/error HTML pages with dark mode support, cookie-based auth flow, and auto-close with manual fallback
1274 lines
40 KiB
Bash
1274 lines
40 KiB
Bash
#!/bin/bash
|
|
# Orris One-Click Installation Script
|
|
# Usage:
|
|
# Install: curl -fsSL https://raw.githubusercontent.com/orris-inc/orris/main/install.sh | bash
|
|
# Update: curl -fsSL https://raw.githubusercontent.com/orris-inc/orris/main/install.sh | bash -s -- update
|
|
# Or locally: ./install.sh update
|
|
|
|
set -e
|
|
|
|
# Command (install or update)
|
|
ACTION="${1:-install}"
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Default values
|
|
INSTALL_DIR="${INSTALL_DIR:-./orris}"
|
|
DOMAIN="${DOMAIN:-}"
|
|
ADMIN_EMAIL="${ADMIN_EMAIL:-}"
|
|
ADMIN_PASSWORD="${ADMIN_PASSWORD:-}"
|
|
|
|
# Installation mode and component flags
|
|
INSTALL_MODE="" # full or custom
|
|
USE_BUILTIN_CADDY="yes"
|
|
USE_BUILTIN_MYSQL="yes"
|
|
USE_BUILTIN_REDIS="yes"
|
|
|
|
# External service configs
|
|
EXT_MYSQL_HOST=""
|
|
EXT_MYSQL_PORT="3306"
|
|
EXT_MYSQL_USER="root"
|
|
EXT_MYSQL_PASSWORD=""
|
|
EXT_MYSQL_DATABASE="orris"
|
|
|
|
EXT_REDIS_HOST=""
|
|
EXT_REDIS_PORT="6379"
|
|
EXT_REDIS_PASSWORD=""
|
|
|
|
print_banner() {
|
|
echo -e "${BLUE}"
|
|
echo " ___ ____ ____ ___ ____ "
|
|
echo " / _ \| _ \| _ \|_ _|/ ___| "
|
|
echo "| | | | |_) | |_) || | \___ \ "
|
|
echo "| |_| | _ <| _ < | | ___) |"
|
|
echo " \___/|_| \_\_| \_\___||____/ "
|
|
echo -e "${NC}"
|
|
echo -e "${GREEN}Orris One-Click Installation${NC}"
|
|
echo ""
|
|
}
|
|
|
|
log_info() {
|
|
echo -e "${GREEN}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
check_command() {
|
|
if ! command -v "$1" &> /dev/null; then
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
detect_os() {
|
|
if [ -f /etc/os-release ]; then
|
|
. /etc/os-release
|
|
OS=$ID
|
|
OS_VERSION=$VERSION_ID
|
|
elif [ "$(uname)" == "Darwin" ]; then
|
|
OS="macos"
|
|
else
|
|
OS="unknown"
|
|
fi
|
|
}
|
|
|
|
install_docker() {
|
|
detect_os
|
|
|
|
case "$OS" in
|
|
ubuntu|debian)
|
|
log_info "Installing Docker on $OS..."
|
|
sudo apt-get update
|
|
sudo apt-get install -y ca-certificates curl gnupg
|
|
sudo install -m 0755 -d /etc/apt/keyrings
|
|
curl -fsSL https://download.docker.com/linux/$OS/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
|
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
|
echo \
|
|
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$OS \
|
|
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
|
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
|
sudo apt-get update
|
|
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
|
sudo systemctl start docker
|
|
sudo systemctl enable docker
|
|
# Add current user to docker group
|
|
sudo usermod -aG docker $USER
|
|
log_info "Docker installed successfully."
|
|
log_warn "You may need to log out and back in for docker group changes to take effect."
|
|
;;
|
|
centos|rhel|fedora|rocky|almalinux|alinux)
|
|
log_info "Installing Docker on $OS..."
|
|
sudo yum install -y yum-utils
|
|
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
|
|
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
|
sudo systemctl start docker
|
|
sudo systemctl enable docker
|
|
sudo usermod -aG docker $USER
|
|
log_info "Docker installed successfully."
|
|
log_warn "You may need to log out and back in for docker group changes to take effect."
|
|
;;
|
|
macos)
|
|
log_error "Docker Desktop is required for macOS."
|
|
log_error "Please download and install from: https://www.docker.com/products/docker-desktop/"
|
|
log_error "After installation, start Docker Desktop and run this script again."
|
|
exit 1
|
|
;;
|
|
*)
|
|
log_warn "Automatic Docker installation not supported for $OS."
|
|
log_info "Attempting to install using official convenience script..."
|
|
curl -fsSL https://get.docker.com | sudo sh
|
|
sudo systemctl start docker
|
|
sudo systemctl enable docker
|
|
sudo usermod -aG docker $USER
|
|
log_info "Docker installed successfully."
|
|
log_warn "You may need to log out and back in for docker group changes to take effect."
|
|
;;
|
|
esac
|
|
}
|
|
|
|
check_dependencies() {
|
|
log_info "Checking dependencies..."
|
|
|
|
# Check if Docker is installed
|
|
if ! check_command "docker"; then
|
|
log_warn "Docker is not installed."
|
|
read -p "Would you like to install Docker automatically? [Y/n] " INSTALL_DOCKER < /dev/tty
|
|
INSTALL_DOCKER="${INSTALL_DOCKER:-Y}"
|
|
if [[ "$INSTALL_DOCKER" =~ ^[Yy]$ ]]; then
|
|
install_docker
|
|
# Re-check after installation
|
|
if ! check_command "docker"; then
|
|
log_error "Docker installation failed. Please install manually."
|
|
exit 1
|
|
fi
|
|
else
|
|
log_error "Docker is required. Please install it first."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Check for docker compose (v2) or docker-compose (v1)
|
|
if docker compose version &> /dev/null; then
|
|
DOCKER_COMPOSE="docker compose"
|
|
elif command -v docker-compose &> /dev/null; then
|
|
DOCKER_COMPOSE="docker-compose"
|
|
else
|
|
log_error "Docker Compose is not installed."
|
|
log_error "If you just installed Docker, please log out and back in, then run this script again."
|
|
exit 1
|
|
fi
|
|
|
|
# Check if Docker daemon is running
|
|
if ! docker info &> /dev/null; then
|
|
log_error "Docker daemon is not running."
|
|
log_info "Attempting to start Docker..."
|
|
if command -v systemctl &> /dev/null; then
|
|
sudo systemctl start docker
|
|
sleep 3
|
|
if ! docker info &> /dev/null; then
|
|
log_error "Failed to start Docker. Please start it manually."
|
|
exit 1
|
|
fi
|
|
else
|
|
log_error "Please start Docker manually and run this script again."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
log_info "Dependencies check passed."
|
|
}
|
|
|
|
generate_secret() {
|
|
openssl rand -base64 32 2>/dev/null || head -c 32 /dev/urandom | base64
|
|
}
|
|
|
|
select_install_mode() {
|
|
echo ""
|
|
echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${CYAN}║ Select Installation Mode ║${NC}"
|
|
echo -e "${CYAN}╠════════════════════════════════════════════════════════════╣${NC}"
|
|
echo -e "${CYAN}║ 1) Full Installation (Recommended) ║${NC}"
|
|
echo -e "${CYAN}║ - Includes: Caddy (reverse proxy), MySQL, Redis ║${NC}"
|
|
echo -e "${CYAN}║ - Best for: New deployments, quick setup ║${NC}"
|
|
echo -e "${CYAN}║ ║${NC}"
|
|
echo -e "${CYAN}║ 2) Custom Installation ║${NC}"
|
|
echo -e "${CYAN}║ - Choose which components to include ║${NC}"
|
|
echo -e "${CYAN}║ - Use your own reverse proxy, MySQL, or Redis ║${NC}"
|
|
echo -e "${CYAN}║ - Best for: Existing infrastructure, advanced users ║${NC}"
|
|
echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
|
|
while true; do
|
|
read -p "Select mode [1/2] (default: 1): " MODE_CHOICE < /dev/tty
|
|
MODE_CHOICE="${MODE_CHOICE:-1}"
|
|
case "$MODE_CHOICE" in
|
|
1)
|
|
INSTALL_MODE="full"
|
|
USE_BUILTIN_CADDY="yes"
|
|
USE_BUILTIN_MYSQL="yes"
|
|
USE_BUILTIN_REDIS="yes"
|
|
log_info "Full installation mode selected."
|
|
break
|
|
;;
|
|
2)
|
|
INSTALL_MODE="custom"
|
|
select_components
|
|
break
|
|
;;
|
|
*)
|
|
log_error "Invalid choice. Please enter 1 or 2."
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
select_components() {
|
|
echo ""
|
|
log_info "Custom Installation - Select Components"
|
|
echo "For each component, choose whether to use the built-in Docker service"
|
|
echo "or configure an external service."
|
|
echo ""
|
|
|
|
# Caddy (Reverse Proxy)
|
|
echo -e "${YELLOW}━━━ Reverse Proxy (Caddy) ━━━${NC}"
|
|
echo " 1) Use built-in Caddy (automatic HTTPS, easy setup)"
|
|
echo " 2) Use external reverse proxy (Nginx, Traefik, etc.)"
|
|
read -p " Choice [1/2] (default: 1): " CADDY_CHOICE < /dev/tty
|
|
CADDY_CHOICE="${CADDY_CHOICE:-1}"
|
|
if [ "$CADDY_CHOICE" == "2" ]; then
|
|
USE_BUILTIN_CADDY="no"
|
|
echo ""
|
|
log_warn "You'll need to configure your reverse proxy manually."
|
|
log_warn "Backend API runs on port 8080, Frontend on port 3000."
|
|
fi
|
|
echo ""
|
|
|
|
# MySQL
|
|
echo -e "${YELLOW}━━━ MySQL Database ━━━${NC}"
|
|
echo " 1) Use built-in MySQL (Docker container)"
|
|
echo " 2) Use external MySQL (existing server, RDS, etc.)"
|
|
read -p " Choice [1/2] (default: 1): " MYSQL_CHOICE < /dev/tty
|
|
MYSQL_CHOICE="${MYSQL_CHOICE:-1}"
|
|
if [ "$MYSQL_CHOICE" == "2" ]; then
|
|
USE_BUILTIN_MYSQL="no"
|
|
prompt_external_mysql
|
|
fi
|
|
echo ""
|
|
|
|
# Redis
|
|
echo -e "${YELLOW}━━━ Redis Cache ━━━${NC}"
|
|
echo " 1) Use built-in Redis (Docker container)"
|
|
echo " 2) Use external Redis (existing server, ElastiCache, etc.)"
|
|
read -p " Choice [1/2] (default: 1): " REDIS_CHOICE < /dev/tty
|
|
REDIS_CHOICE="${REDIS_CHOICE:-1}"
|
|
if [ "$REDIS_CHOICE" == "2" ]; then
|
|
USE_BUILTIN_REDIS="no"
|
|
prompt_external_redis
|
|
fi
|
|
echo ""
|
|
|
|
# Summary
|
|
echo -e "${CYAN}━━━ Configuration Summary ━━━${NC}"
|
|
echo " Caddy: $([ "$USE_BUILTIN_CADDY" == "yes" ] && echo "Built-in" || echo "External")"
|
|
echo " MySQL: $([ "$USE_BUILTIN_MYSQL" == "yes" ] && echo "Built-in" || echo "External ($EXT_MYSQL_HOST)")"
|
|
echo " Redis: $([ "$USE_BUILTIN_REDIS" == "yes" ] && echo "Built-in" || echo "External ($EXT_REDIS_HOST)")"
|
|
echo ""
|
|
}
|
|
|
|
prompt_external_mysql() {
|
|
echo ""
|
|
log_info "Configure External MySQL Connection"
|
|
read -p " MySQL Host: " EXT_MYSQL_HOST < /dev/tty
|
|
read -p " MySQL Port [3306]: " EXT_MYSQL_PORT < /dev/tty
|
|
EXT_MYSQL_PORT="${EXT_MYSQL_PORT:-3306}"
|
|
read -p " MySQL User [root]: " EXT_MYSQL_USER < /dev/tty
|
|
EXT_MYSQL_USER="${EXT_MYSQL_USER:-root}"
|
|
read -s -p " MySQL Password: " EXT_MYSQL_PASSWORD < /dev/tty
|
|
echo ""
|
|
read -p " MySQL Database [orris]: " EXT_MYSQL_DATABASE < /dev/tty
|
|
EXT_MYSQL_DATABASE="${EXT_MYSQL_DATABASE:-orris}"
|
|
|
|
# Validate connection
|
|
log_info "Testing MySQL connection..."
|
|
if check_command "mysql"; then
|
|
if mysql -h "$EXT_MYSQL_HOST" -P "$EXT_MYSQL_PORT" -u "$EXT_MYSQL_USER" -p"$EXT_MYSQL_PASSWORD" -e "SELECT 1" &>/dev/null; then
|
|
log_info "MySQL connection successful."
|
|
else
|
|
log_warn "Could not connect to MySQL. Please verify your credentials."
|
|
log_warn "Installation will continue, but you may need to fix the connection later."
|
|
fi
|
|
else
|
|
log_warn "mysql client not installed, skipping connection test."
|
|
fi
|
|
}
|
|
|
|
prompt_external_redis() {
|
|
echo ""
|
|
log_info "Configure External Redis Connection"
|
|
read -p " Redis Host: " EXT_REDIS_HOST < /dev/tty
|
|
read -p " Redis Port [6379]: " EXT_REDIS_PORT < /dev/tty
|
|
EXT_REDIS_PORT="${EXT_REDIS_PORT:-6379}"
|
|
read -s -p " Redis Password (leave empty if none): " EXT_REDIS_PASSWORD < /dev/tty
|
|
echo ""
|
|
|
|
# Validate connection
|
|
log_info "Testing Redis connection..."
|
|
if check_command "redis-cli"; then
|
|
if [ -n "$EXT_REDIS_PASSWORD" ]; then
|
|
REDIS_TEST=$(redis-cli -h "$EXT_REDIS_HOST" -p "$EXT_REDIS_PORT" -a "$EXT_REDIS_PASSWORD" ping 2>/dev/null)
|
|
else
|
|
REDIS_TEST=$(redis-cli -h "$EXT_REDIS_HOST" -p "$EXT_REDIS_PORT" ping 2>/dev/null)
|
|
fi
|
|
if [ "$REDIS_TEST" == "PONG" ]; then
|
|
log_info "Redis connection successful."
|
|
else
|
|
log_warn "Could not connect to Redis. Please verify your configuration."
|
|
log_warn "Installation will continue, but you may need to fix the connection later."
|
|
fi
|
|
else
|
|
log_warn "redis-cli not installed, skipping connection test."
|
|
fi
|
|
}
|
|
|
|
prompt_config() {
|
|
echo ""
|
|
log_info "Basic Configuration"
|
|
echo "Press Enter to use default values (shown in brackets)"
|
|
echo ""
|
|
|
|
# Domain
|
|
if [ -z "$DOMAIN" ]; then
|
|
read -p "Domain name (leave empty for localhost): " DOMAIN < /dev/tty
|
|
fi
|
|
|
|
# Admin email
|
|
if [ -z "$ADMIN_EMAIL" ]; then
|
|
read -p "Admin email: " ADMIN_EMAIL < /dev/tty
|
|
fi
|
|
|
|
# Admin password
|
|
if [ -z "$ADMIN_PASSWORD" ]; then
|
|
read -s -p "Admin password (min 8 chars): " ADMIN_PASSWORD < /dev/tty
|
|
echo ""
|
|
fi
|
|
|
|
# Generate secrets
|
|
JWT_SECRET=$(generate_secret)
|
|
FORWARD_SECRET=$(generate_secret)
|
|
|
|
# Only generate DB password if using built-in MySQL
|
|
if [ "$USE_BUILTIN_MYSQL" == "yes" ]; then
|
|
DB_PASSWORD=$(generate_secret | tr -dc 'a-zA-Z0-9' | head -c 16)
|
|
else
|
|
DB_PASSWORD="$EXT_MYSQL_PASSWORD"
|
|
fi
|
|
}
|
|
|
|
create_install_dir() {
|
|
log_info "Creating installation directory: $INSTALL_DIR"
|
|
mkdir -p "$INSTALL_DIR"
|
|
cd "$INSTALL_DIR"
|
|
mkdir -p configs/sub
|
|
|
|
# Only create data directories for built-in services
|
|
if [ "$USE_BUILTIN_MYSQL" == "yes" ]; then
|
|
mkdir -p data/mysql
|
|
fi
|
|
if [ "$USE_BUILTIN_REDIS" == "yes" ]; then
|
|
mkdir -p data/redis
|
|
fi
|
|
}
|
|
|
|
download_files() {
|
|
log_info "Downloading configuration files..."
|
|
|
|
REPO_URL="https://raw.githubusercontent.com/orris-inc/orris/main"
|
|
|
|
# Download .env.example
|
|
curl -fsSL "$REPO_URL/.env.example" -o .env.example
|
|
|
|
# Download Caddyfile (only if using built-in Caddy)
|
|
if [ "$USE_BUILTIN_CADDY" == "yes" ]; then
|
|
curl -fsSL "$REPO_URL/Caddyfile" -o Caddyfile
|
|
fi
|
|
|
|
# Download subscription template
|
|
curl -fsSL "$REPO_URL/configs/sub/custom.clash.yaml" -o configs/sub/custom.clash.yaml 2>/dev/null || true
|
|
|
|
log_info "Configuration files downloaded."
|
|
}
|
|
|
|
generate_docker_compose() {
|
|
log_info "Generating docker-compose.yml..."
|
|
|
|
# Start with header and Caddy (if using built-in)
|
|
cat > docker-compose.yml << 'EOF'
|
|
version: '3.8'
|
|
|
|
services:
|
|
EOF
|
|
|
|
# Add Caddy service first if using built-in
|
|
if [ "$USE_BUILTIN_CADDY" == "yes" ]; then
|
|
cat >> docker-compose.yml << 'EOF'
|
|
caddy:
|
|
image: caddy:2-alpine
|
|
container_name: orris_caddy
|
|
restart: unless-stopped
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
volumes:
|
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
|
- caddy_data:/data
|
|
- caddy_config:/config
|
|
depends_on:
|
|
- frontend
|
|
- orris
|
|
|
|
EOF
|
|
fi
|
|
|
|
# Add frontend service
|
|
cat >> docker-compose.yml << 'EOF'
|
|
frontend:
|
|
image: ghcr.io/orris-inc/orris-frontend:latest
|
|
container_name: orris_frontend
|
|
restart: unless-stopped
|
|
EOF
|
|
|
|
if [ "$USE_BUILTIN_CADDY" == "yes" ]; then
|
|
cat >> docker-compose.yml << 'EOF'
|
|
expose:
|
|
- "80"
|
|
EOF
|
|
else
|
|
cat >> docker-compose.yml << 'EOF'
|
|
ports:
|
|
- "3000:80"
|
|
EOF
|
|
fi
|
|
|
|
cat >> docker-compose.yml << 'EOF'
|
|
depends_on:
|
|
- orris
|
|
|
|
EOF
|
|
|
|
# Add orris service
|
|
cat >> docker-compose.yml << 'EOF'
|
|
orris:
|
|
image: ghcr.io/orris-inc/orris:latest
|
|
container_name: orris_app
|
|
restart: unless-stopped
|
|
EOF
|
|
|
|
if [ "$USE_BUILTIN_CADDY" == "yes" ]; then
|
|
cat >> docker-compose.yml << 'EOF'
|
|
expose:
|
|
- "8080"
|
|
EOF
|
|
else
|
|
cat >> docker-compose.yml << 'EOF'
|
|
ports:
|
|
- "8080:8080"
|
|
EOF
|
|
fi
|
|
|
|
cat >> docker-compose.yml << 'EOF'
|
|
volumes:
|
|
- ./configs:/app/configs:ro
|
|
env_file:
|
|
- .env
|
|
EOF
|
|
|
|
# Add depends_on for built-in services
|
|
if [ "$USE_BUILTIN_MYSQL" == "yes" ] || [ "$USE_BUILTIN_REDIS" == "yes" ]; then
|
|
echo " depends_on:" >> docker-compose.yml
|
|
if [ "$USE_BUILTIN_MYSQL" == "yes" ]; then
|
|
cat >> docker-compose.yml << 'EOF'
|
|
mysql:
|
|
condition: service_healthy
|
|
EOF
|
|
fi
|
|
if [ "$USE_BUILTIN_REDIS" == "yes" ]; then
|
|
cat >> docker-compose.yml << 'EOF'
|
|
redis:
|
|
condition: service_healthy
|
|
EOF
|
|
fi
|
|
fi
|
|
|
|
# Add MySQL service if using built-in
|
|
if [ "$USE_BUILTIN_MYSQL" == "yes" ]; then
|
|
cat >> docker-compose.yml << 'EOF'
|
|
|
|
mysql:
|
|
image: mysql:8.0
|
|
container_name: orris_mysql
|
|
restart: unless-stopped
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
MYSQL_ROOT_PASSWORD: ${ORRIS_DATABASE_PASSWORD:-password}
|
|
MYSQL_DATABASE: ${ORRIS_DATABASE_DATABASE:-orris}
|
|
MYSQL_USER: ${ORRIS_DATABASE_USERNAME:-orris}
|
|
MYSQL_PASSWORD: ${ORRIS_DATABASE_PASSWORD:-password}
|
|
ports:
|
|
- "${ORRIS_DATABASE_PORT:-3306}:3306"
|
|
volumes:
|
|
- ./data/mysql:/var/lib/mysql
|
|
healthcheck:
|
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
|
timeout: 20s
|
|
retries: 10
|
|
EOF
|
|
fi
|
|
|
|
# Add Redis service if using built-in
|
|
if [ "$USE_BUILTIN_REDIS" == "yes" ]; then
|
|
cat >> docker-compose.yml << 'EOF'
|
|
|
|
redis:
|
|
image: redis:7-alpine
|
|
container_name: orris_redis
|
|
restart: unless-stopped
|
|
ports:
|
|
- "${ORRIS_REDIS_PORT:-6379}:6379"
|
|
volumes:
|
|
- ./data/redis:/data
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "ping"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
command: redis-server --appendonly yes
|
|
EOF
|
|
fi
|
|
|
|
# Add volumes section
|
|
if [ "$USE_BUILTIN_CADDY" == "yes" ]; then
|
|
cat >> docker-compose.yml << 'EOF'
|
|
|
|
volumes:
|
|
caddy_data:
|
|
caddy_config:
|
|
EOF
|
|
fi
|
|
|
|
log_info "docker-compose.yml generated."
|
|
}
|
|
|
|
configure_env() {
|
|
log_info "Configuring environment variables..."
|
|
|
|
cp .env.example .env
|
|
|
|
# Set production mode
|
|
sed -i.bak 's/ORRIS_SERVER_MODE=debug/ORRIS_SERVER_MODE=release/' .env
|
|
|
|
# Configure database connection
|
|
if [ "$USE_BUILTIN_MYSQL" == "yes" ]; then
|
|
sed -i.bak "s|ORRIS_DATABASE_HOST=localhost|ORRIS_DATABASE_HOST=mysql|" .env
|
|
sed -i.bak "s|ORRIS_DATABASE_PASSWORD=password|ORRIS_DATABASE_PASSWORD=$DB_PASSWORD|" .env
|
|
else
|
|
sed -i.bak "s|ORRIS_DATABASE_HOST=localhost|ORRIS_DATABASE_HOST=$EXT_MYSQL_HOST|" .env
|
|
sed -i.bak "s|ORRIS_DATABASE_PORT=3306|ORRIS_DATABASE_PORT=$EXT_MYSQL_PORT|" .env
|
|
sed -i.bak "s|ORRIS_DATABASE_USER=root|ORRIS_DATABASE_USER=$EXT_MYSQL_USER|" .env
|
|
sed -i.bak "s|ORRIS_DATABASE_PASSWORD=password|ORRIS_DATABASE_PASSWORD=$EXT_MYSQL_PASSWORD|" .env
|
|
sed -i.bak "s|ORRIS_DATABASE_NAME=orris|ORRIS_DATABASE_NAME=$EXT_MYSQL_DATABASE|" .env
|
|
fi
|
|
|
|
# Configure Redis connection
|
|
if [ "$USE_BUILTIN_REDIS" == "yes" ]; then
|
|
sed -i.bak "s|ORRIS_REDIS_HOST=localhost|ORRIS_REDIS_HOST=redis|" .env
|
|
else
|
|
sed -i.bak "s|ORRIS_REDIS_HOST=localhost|ORRIS_REDIS_HOST=$EXT_REDIS_HOST|" .env
|
|
sed -i.bak "s|ORRIS_REDIS_PORT=6379|ORRIS_REDIS_PORT=$EXT_REDIS_PORT|" .env
|
|
if [ -n "$EXT_REDIS_PASSWORD" ]; then
|
|
sed -i.bak "s|ORRIS_REDIS_PASSWORD=|ORRIS_REDIS_PASSWORD=$EXT_REDIS_PASSWORD|" .env
|
|
fi
|
|
fi
|
|
|
|
# Set JWT secret
|
|
sed -i.bak "s|ORRIS_AUTH_JWT_SECRET=change-me-in-production|ORRIS_AUTH_JWT_SECRET=$JWT_SECRET|" .env
|
|
|
|
# Set forward token secret
|
|
sed -i.bak "s|ORRIS_FORWARD_TOKEN_SIGNING_SECRET=change-me-in-production|ORRIS_FORWARD_TOKEN_SIGNING_SECRET=$FORWARD_SECRET|" .env
|
|
|
|
# Set admin credentials
|
|
if [ -n "$ADMIN_EMAIL" ]; then
|
|
sed -i.bak "s|ORRIS_ADMIN_EMAIL=|ORRIS_ADMIN_EMAIL=$ADMIN_EMAIL|" .env
|
|
fi
|
|
if [ -n "$ADMIN_PASSWORD" ]; then
|
|
sed -i.bak "s|ORRIS_ADMIN_PASSWORD=|ORRIS_ADMIN_PASSWORD=$ADMIN_PASSWORD|" .env
|
|
fi
|
|
|
|
# Set domain-related configs
|
|
if [ -n "$DOMAIN" ]; then
|
|
sed -i.bak "s|ORRIS_SERVER_BASE_URL=|ORRIS_SERVER_BASE_URL=https://$DOMAIN/api|" .env
|
|
sed -i.bak "s|ORRIS_SERVER_ALLOWED_ORIGINS=|ORRIS_SERVER_ALLOWED_ORIGINS=https://$DOMAIN|" .env
|
|
sed -i.bak "s|ORRIS_SERVER_FRONTEND_CALLBACK_URL=|ORRIS_SERVER_FRONTEND_CALLBACK_URL=https://$DOMAIN/auth/callback|" .env
|
|
sed -i.bak "s|ORRIS_SUBSCRIPTION_BASE_URL=|ORRIS_SUBSCRIPTION_BASE_URL=https://$DOMAIN/api|" .env
|
|
sed -i.bak "s/ORRIS_AUTH_COOKIE_SECURE=false/ORRIS_AUTH_COOKIE_SECURE=true/" .env
|
|
sed -i.bak "s|ORRIS_AUTH_COOKIE_DOMAIN=|ORRIS_AUTH_COOKIE_DOMAIN=$DOMAIN|" .env
|
|
else
|
|
sed -i.bak "s|ORRIS_SERVER_BASE_URL=|ORRIS_SERVER_BASE_URL=http://localhost/api|" .env
|
|
sed -i.bak "s|ORRIS_SERVER_ALLOWED_ORIGINS=|ORRIS_SERVER_ALLOWED_ORIGINS=http://localhost|" .env
|
|
sed -i.bak "s|ORRIS_SERVER_FRONTEND_CALLBACK_URL=|ORRIS_SERVER_FRONTEND_CALLBACK_URL=http://localhost/auth/callback|" .env
|
|
sed -i.bak "s|ORRIS_SUBSCRIPTION_BASE_URL=|ORRIS_SUBSCRIPTION_BASE_URL=http://localhost/api|" .env
|
|
fi
|
|
|
|
# Clean up backup files
|
|
rm -f .env.bak
|
|
|
|
log_info "Environment configured."
|
|
}
|
|
|
|
configure_caddy() {
|
|
# Skip if not using built-in Caddy
|
|
if [ "$USE_BUILTIN_CADDY" != "yes" ]; then
|
|
return
|
|
fi
|
|
|
|
if [ -n "$DOMAIN" ]; then
|
|
log_info "Configuring Caddy for domain: $DOMAIN"
|
|
|
|
cat > Caddyfile << EOF
|
|
# Caddyfile for Orris
|
|
# Automatic HTTPS enabled for $DOMAIN
|
|
|
|
$DOMAIN {
|
|
# API routes -> backend service
|
|
handle /api/* {
|
|
uri strip_prefix /api
|
|
reverse_proxy orris:8080
|
|
}
|
|
|
|
# All other routes -> frontend service
|
|
handle {
|
|
reverse_proxy frontend:80
|
|
}
|
|
}
|
|
EOF
|
|
else
|
|
log_info "Configuring Caddy for localhost (HTTP only)"
|
|
|
|
cat > Caddyfile << EOF
|
|
# Caddyfile for Orris
|
|
# HTTP mode for localhost
|
|
|
|
:80 {
|
|
# API routes -> backend service
|
|
handle /api/* {
|
|
uri strip_prefix /api
|
|
reverse_proxy orris:8080
|
|
}
|
|
|
|
# All other routes -> frontend service
|
|
handle {
|
|
reverse_proxy frontend:80
|
|
}
|
|
}
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
start_services() {
|
|
log_info "Starting services..."
|
|
|
|
$DOCKER_COMPOSE pull
|
|
$DOCKER_COMPOSE up -d
|
|
|
|
log_info "Waiting for services to be ready..."
|
|
sleep 10
|
|
}
|
|
|
|
run_migrations() {
|
|
log_info "Running database migrations..."
|
|
|
|
# Wait for MySQL to be fully ready
|
|
if [ "$USE_BUILTIN_MYSQL" == "yes" ]; then
|
|
for i in {1..30}; do
|
|
if docker exec orris_mysql mysqladmin ping -h localhost -u root -p"$DB_PASSWORD" &>/dev/null; then
|
|
break
|
|
fi
|
|
echo -n "."
|
|
sleep 2
|
|
done
|
|
echo ""
|
|
else
|
|
# For external MySQL, wait for app to be ready
|
|
for i in {1..30}; do
|
|
if docker exec orris_app /app/orris migrate status &>/dev/null; then
|
|
break
|
|
fi
|
|
echo -n "."
|
|
sleep 2
|
|
done
|
|
echo ""
|
|
fi
|
|
|
|
# Run migrations
|
|
docker exec orris_app /app/orris migrate up
|
|
|
|
log_info "Database migrations completed."
|
|
|
|
# Restart orris app to trigger admin user seeding
|
|
# seedAdminUser runs on server startup but the first attempt fails because
|
|
# the users table does not exist yet (migrations had not been applied).
|
|
log_info "Restarting orris to apply initial configuration..."
|
|
$DOCKER_COMPOSE restart orris
|
|
sleep 5
|
|
}
|
|
|
|
print_reverse_proxy_examples() {
|
|
local PROXY_DOMAIN="${DOMAIN:-example.com}"
|
|
local USE_HTTPS="yes"
|
|
if [ -z "$DOMAIN" ]; then
|
|
USE_HTTPS="no"
|
|
fi
|
|
|
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
echo -e "${CYAN} Reverse Proxy Configuration Examples${NC}"
|
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
echo ""
|
|
echo " Backend API: http://localhost:8080"
|
|
echo " Frontend: http://localhost:3000"
|
|
echo " Routing: /api/* -> Backend, /* -> Frontend"
|
|
echo ""
|
|
|
|
# Nginx configuration
|
|
echo -e "${YELLOW}▸ Nginx Configuration${NC}"
|
|
echo ""
|
|
if [ "$USE_HTTPS" == "yes" ]; then
|
|
cat << EOF
|
|
server {
|
|
listen 80;
|
|
server_name $PROXY_DOMAIN;
|
|
return 301 https://\$server_name\$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name $PROXY_DOMAIN;
|
|
|
|
ssl_certificate /path/to/cert.pem;
|
|
ssl_certificate_key /path/to/key.pem;
|
|
|
|
# API routes -> backend
|
|
location /api/ {
|
|
rewrite ^/api/(.*)\$ /\$1 break;
|
|
proxy_pass http://127.0.0.1:8080;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
}
|
|
|
|
# All other routes -> frontend
|
|
location / {
|
|
proxy_pass http://127.0.0.1:3000;
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
}
|
|
}
|
|
EOF
|
|
else
|
|
cat << EOF
|
|
server {
|
|
listen 80;
|
|
server_name localhost;
|
|
|
|
# API routes -> backend
|
|
location /api/ {
|
|
rewrite ^/api/(.*)\$ /\$1 break;
|
|
proxy_pass http://127.0.0.1:8080;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
}
|
|
|
|
# All other routes -> frontend
|
|
location / {
|
|
proxy_pass http://127.0.0.1:3000;
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
}
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}▸ Caddy Configuration${NC}"
|
|
echo ""
|
|
if [ "$USE_HTTPS" == "yes" ]; then
|
|
cat << EOF
|
|
$PROXY_DOMAIN {
|
|
handle /api/* {
|
|
uri strip_prefix /api
|
|
reverse_proxy 127.0.0.1:8080
|
|
}
|
|
|
|
handle {
|
|
reverse_proxy 127.0.0.1:3000
|
|
}
|
|
}
|
|
EOF
|
|
else
|
|
cat << EOF
|
|
:80 {
|
|
handle /api/* {
|
|
uri strip_prefix /api
|
|
reverse_proxy 127.0.0.1:8080
|
|
}
|
|
|
|
handle {
|
|
reverse_proxy 127.0.0.1:3000
|
|
}
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}▸ Traefik Configuration (docker-compose labels)${NC}"
|
|
echo ""
|
|
cat << EOF
|
|
# Add to orris service in docker-compose.yml:
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.orris-api.rule=Host(\`$PROXY_DOMAIN\`) && PathPrefix(\`/api\`)"
|
|
- "traefik.http.routers.orris-api.middlewares=strip-api"
|
|
- "traefik.http.middlewares.strip-api.stripprefix.prefixes=/api"
|
|
- "traefik.http.services.orris-api.loadbalancer.server.port=8080"
|
|
|
|
# Add to frontend service in docker-compose.yml:
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.orris-frontend.rule=Host(\`$PROXY_DOMAIN\`)"
|
|
- "traefik.http.services.orris-frontend.loadbalancer.server.port=80"
|
|
EOF
|
|
|
|
echo ""
|
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
echo ""
|
|
|
|
# Save config examples to files
|
|
save_proxy_config_files "$PROXY_DOMAIN" "$USE_HTTPS"
|
|
}
|
|
|
|
save_proxy_config_files() {
|
|
local PROXY_DOMAIN="$1"
|
|
local USE_HTTPS="$2"
|
|
|
|
mkdir -p ./proxy-examples
|
|
|
|
# Save Nginx config
|
|
if [ "$USE_HTTPS" == "yes" ]; then
|
|
cat > ./proxy-examples/nginx.conf << EOF
|
|
server {
|
|
listen 80;
|
|
server_name $PROXY_DOMAIN;
|
|
return 301 https://\$server_name\$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name $PROXY_DOMAIN;
|
|
|
|
ssl_certificate /path/to/cert.pem;
|
|
ssl_certificate_key /path/to/key.pem;
|
|
|
|
# API routes -> backend (WebSocket/SSE supported)
|
|
location /api/ {
|
|
rewrite ^/api/(.*)\$ /\$1 break;
|
|
proxy_pass http://127.0.0.1:8080;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
}
|
|
|
|
# All other routes -> frontend
|
|
location / {
|
|
proxy_pass http://127.0.0.1:3000;
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
}
|
|
}
|
|
EOF
|
|
else
|
|
cat > ./proxy-examples/nginx.conf << EOF
|
|
server {
|
|
listen 80;
|
|
server_name localhost;
|
|
|
|
# API routes -> backend (WebSocket/SSE supported)
|
|
location /api/ {
|
|
rewrite ^/api/(.*)\$ /\$1 break;
|
|
proxy_pass http://127.0.0.1:8080;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
}
|
|
|
|
# All other routes -> frontend
|
|
location / {
|
|
proxy_pass http://127.0.0.1:3000;
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
}
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
# Save Caddy config
|
|
if [ "$USE_HTTPS" == "yes" ]; then
|
|
cat > ./proxy-examples/Caddyfile << EOF
|
|
$PROXY_DOMAIN {
|
|
handle /api/* {
|
|
uri strip_prefix /api
|
|
reverse_proxy 127.0.0.1:8080
|
|
}
|
|
|
|
handle {
|
|
reverse_proxy 127.0.0.1:3000
|
|
}
|
|
}
|
|
EOF
|
|
else
|
|
cat > ./proxy-examples/Caddyfile << EOF
|
|
:80 {
|
|
handle /api/* {
|
|
uri strip_prefix /api
|
|
reverse_proxy 127.0.0.1:8080
|
|
}
|
|
|
|
handle {
|
|
reverse_proxy 127.0.0.1:3000
|
|
}
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
log_info "Proxy configuration examples saved to ./proxy-examples/"
|
|
}
|
|
|
|
print_summary() {
|
|
echo ""
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo -e "${GREEN} Installation Complete!${NC}"
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo ""
|
|
|
|
if [ -n "$DOMAIN" ]; then
|
|
echo -e " URL: ${BLUE}https://$DOMAIN${NC}"
|
|
else
|
|
if [ "$USE_BUILTIN_CADDY" == "yes" ]; then
|
|
echo -e " URL: ${BLUE}http://localhost${NC}"
|
|
else
|
|
echo -e " Frontend: ${BLUE}http://localhost:3000${NC}"
|
|
echo -e " Backend: ${BLUE}http://localhost:8080${NC}"
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$ADMIN_EMAIL" ]; then
|
|
echo -e " Admin: ${BLUE}$ADMIN_EMAIL${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e " ${CYAN}Components:${NC}"
|
|
echo " Caddy: $([ "$USE_BUILTIN_CADDY" == "yes" ] && echo "Built-in (Docker)" || echo "External")"
|
|
echo " MySQL: $([ "$USE_BUILTIN_MYSQL" == "yes" ] && echo "Built-in (Docker)" || echo "External ($EXT_MYSQL_HOST)")"
|
|
echo " Redis: $([ "$USE_BUILTIN_REDIS" == "yes" ] && echo "Built-in (Docker)" || echo "External ($EXT_REDIS_HOST)")"
|
|
|
|
echo ""
|
|
echo " Useful commands:"
|
|
echo " cd $INSTALL_DIR"
|
|
echo " $DOCKER_COMPOSE ps # View status"
|
|
echo " $DOCKER_COMPOSE logs -f # View logs"
|
|
echo " $DOCKER_COMPOSE down # Stop services"
|
|
echo " ./install.sh update # Update to latest version"
|
|
echo ""
|
|
|
|
if [ "$USE_BUILTIN_CADDY" != "yes" ]; then
|
|
log_warn "Remember to configure your reverse proxy!"
|
|
echo ""
|
|
print_reverse_proxy_examples
|
|
fi
|
|
|
|
if [ -n "$DOMAIN" ] && [ "$USE_BUILTIN_CADDY" == "yes" ]; then
|
|
log_warn "Make sure your DNS is pointing to this server!"
|
|
log_warn "Caddy will automatically obtain SSL certificates."
|
|
fi
|
|
|
|
echo ""
|
|
log_info "For more information, visit: https://github.com/orris-inc/orris"
|
|
}
|
|
|
|
# Uninstall function - stops services and optionally removes data
|
|
do_uninstall() {
|
|
echo -e "${BLUE}"
|
|
echo " ___ ____ ____ ___ ____ "
|
|
echo " / _ \| _ \| _ \|_ _|/ ___| "
|
|
echo "| | | | |_) | |_) || | \___ \ "
|
|
echo "| |_| | _ <| _ < | | ___) |"
|
|
echo " \___/|_| \_\_| \_\___||____/ "
|
|
echo -e "${NC}"
|
|
echo -e "${RED}Orris Uninstall${NC}"
|
|
echo ""
|
|
|
|
# Check if we're in the right directory
|
|
if [ ! -f "docker-compose.yml" ]; then
|
|
log_error "docker-compose.yml not found in current directory."
|
|
log_error "Please run this command from your Orris installation directory."
|
|
exit 1
|
|
fi
|
|
|
|
# Check for docker compose
|
|
if docker compose version &> /dev/null; then
|
|
DOCKER_COMPOSE="docker compose"
|
|
elif command -v docker-compose &> /dev/null; then
|
|
DOCKER_COMPOSE="docker-compose"
|
|
else
|
|
log_error "Docker Compose is not installed."
|
|
exit 1
|
|
fi
|
|
|
|
echo -e "${YELLOW}WARNING: This will stop and remove all Orris services.${NC}"
|
|
echo ""
|
|
read -p "Are you sure you want to uninstall Orris? [y/N] " CONFIRM < /dev/tty
|
|
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
|
|
log_info "Uninstall cancelled."
|
|
exit 0
|
|
fi
|
|
|
|
echo ""
|
|
log_info "Stopping services..."
|
|
$DOCKER_COMPOSE down
|
|
|
|
# Ask about removing volumes (Docker volumes)
|
|
echo ""
|
|
read -p "Remove Docker volumes (caddy_data, caddy_config)? [y/N] " REMOVE_VOLUMES < /dev/tty
|
|
if [[ "$REMOVE_VOLUMES" =~ ^[Yy]$ ]]; then
|
|
log_info "Removing Docker volumes..."
|
|
$DOCKER_COMPOSE down -v
|
|
fi
|
|
|
|
# Ask about removing data directory
|
|
echo ""
|
|
echo -e "${YELLOW}Data directories contain:${NC}"
|
|
echo " - ./data/mysql (database files)"
|
|
echo " - ./data/redis (cache data)"
|
|
echo ""
|
|
read -p "Remove data directories? This will DELETE ALL DATA! [y/N] " REMOVE_DATA < /dev/tty
|
|
if [[ "$REMOVE_DATA" =~ ^[Yy]$ ]]; then
|
|
read -p "Type 'DELETE' to confirm data deletion: " CONFIRM_DELETE < /dev/tty
|
|
if [ "$CONFIRM_DELETE" == "DELETE" ]; then
|
|
log_info "Removing data directories..."
|
|
rm -rf ./data/mysql ./data/redis
|
|
log_info "Data directories removed."
|
|
else
|
|
log_info "Data deletion cancelled."
|
|
fi
|
|
fi
|
|
|
|
# Ask about removing configuration files
|
|
echo ""
|
|
read -p "Remove configuration files (.env, docker-compose.yml, Caddyfile)? [y/N] " REMOVE_CONFIG < /dev/tty
|
|
if [[ "$REMOVE_CONFIG" =~ ^[Yy]$ ]]; then
|
|
log_info "Removing configuration files..."
|
|
rm -f .env docker-compose.yml Caddyfile .env.example
|
|
rm -rf configs
|
|
log_info "Configuration files removed."
|
|
fi
|
|
|
|
# Ask about removing the entire installation directory
|
|
CURRENT_DIR=$(pwd)
|
|
echo ""
|
|
read -p "Remove entire installation directory ($CURRENT_DIR)? [y/N] " REMOVE_ALL < /dev/tty
|
|
if [[ "$REMOVE_ALL" =~ ^[Yy]$ ]]; then
|
|
read -p "Type 'REMOVE' to confirm directory deletion: " CONFIRM_REMOVE < /dev/tty
|
|
if [ "$CONFIRM_REMOVE" == "REMOVE" ]; then
|
|
log_info "Removing installation directory..."
|
|
cd ..
|
|
rm -rf "$CURRENT_DIR"
|
|
log_info "Installation directory removed."
|
|
else
|
|
log_info "Directory deletion cancelled."
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo -e "${GREEN} Uninstall Complete!${NC}"
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo ""
|
|
log_info "Orris has been uninstalled."
|
|
|
|
# Check if Docker images should be removed
|
|
echo ""
|
|
read -p "Remove Orris Docker images? [y/N] " REMOVE_IMAGES < /dev/tty
|
|
if [[ "$REMOVE_IMAGES" =~ ^[Yy]$ ]]; then
|
|
log_info "Removing Docker images..."
|
|
docker rmi ghcr.io/orris-inc/orris:latest 2>/dev/null || true
|
|
docker rmi ghcr.io/orris-inc/orris-frontend:latest 2>/dev/null || true
|
|
docker rmi caddy:2-alpine 2>/dev/null || true
|
|
docker rmi mysql:8.0 2>/dev/null || true
|
|
docker rmi redis:7-alpine 2>/dev/null || true
|
|
log_info "Docker images removed."
|
|
fi
|
|
}
|
|
|
|
# Update function - pulls latest images, restarts services, and runs migrations
|
|
do_update() {
|
|
echo -e "${BLUE}"
|
|
echo " ___ ____ ____ ___ ____ "
|
|
echo " / _ \| _ \| _ \|_ _|/ ___| "
|
|
echo "| | | | |_) | |_) || | \___ \ "
|
|
echo "| |_| | _ <| _ < | | ___) |"
|
|
echo " \___/|_| \_\_| \_\___||____/ "
|
|
echo -e "${NC}"
|
|
echo -e "${GREEN}Orris Update${NC}"
|
|
echo ""
|
|
|
|
check_dependencies
|
|
|
|
# Check if we're in the right directory (has docker-compose.yml)
|
|
if [ ! -f "docker-compose.yml" ]; then
|
|
log_error "docker-compose.yml not found in current directory."
|
|
log_error "Please run this command from your Orris installation directory."
|
|
exit 1
|
|
fi
|
|
|
|
log_info "Pulling latest images..."
|
|
$DOCKER_COMPOSE pull
|
|
|
|
log_info "Restarting services with new images..."
|
|
$DOCKER_COMPOSE up -d
|
|
|
|
log_info "Waiting for services to be ready..."
|
|
sleep 10
|
|
|
|
# Wait for app container to be healthy
|
|
for i in {1..30}; do
|
|
if docker exec orris_app /app/orris migrate status &>/dev/null; then
|
|
break
|
|
fi
|
|
echo -n "."
|
|
sleep 2
|
|
done
|
|
echo ""
|
|
|
|
log_info "Running database migrations..."
|
|
docker exec orris_app /app/orris migrate up
|
|
|
|
echo ""
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo -e "${GREEN} Update Complete!${NC}"
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo ""
|
|
echo " Useful commands:"
|
|
echo " $DOCKER_COMPOSE ps # View status"
|
|
echo " $DOCKER_COMPOSE logs -f # View logs"
|
|
echo ""
|
|
log_info "Update completed successfully!"
|
|
}
|
|
|
|
# Main installation flow
|
|
do_install() {
|
|
print_banner
|
|
check_dependencies
|
|
select_install_mode
|
|
prompt_config
|
|
create_install_dir
|
|
download_files
|
|
generate_docker_compose
|
|
configure_env
|
|
configure_caddy
|
|
start_services
|
|
run_migrations
|
|
print_summary
|
|
}
|
|
|
|
# Show usage
|
|
show_usage() {
|
|
echo "Usage: $0 [command]"
|
|
echo ""
|
|
echo "Commands:"
|
|
echo " install Install Orris (default)"
|
|
echo " update Update Orris to the latest version"
|
|
echo " uninstall Uninstall Orris and optionally remove data"
|
|
echo " help Show this help message"
|
|
echo ""
|
|
echo "Installation Modes:"
|
|
echo " Full: All components included (Caddy, MySQL, Redis)"
|
|
echo " Custom: Choose which components to use (external DB/Redis supported)"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 # Install Orris"
|
|
echo " $0 install # Install Orris"
|
|
echo " $0 update # Update existing installation"
|
|
echo " $0 uninstall # Uninstall Orris"
|
|
echo ""
|
|
echo "Remote usage:"
|
|
echo " curl -fsSL https://raw.githubusercontent.com/orris-inc/orris/main/install.sh | bash"
|
|
echo " curl -fsSL https://raw.githubusercontent.com/orris-inc/orris/main/install.sh | bash -s -- update"
|
|
echo " curl -fsSL https://raw.githubusercontent.com/orris-inc/orris/main/install.sh | bash -s -- uninstall"
|
|
}
|
|
|
|
# Main entry point
|
|
case "$ACTION" in
|
|
install)
|
|
do_install
|
|
;;
|
|
update)
|
|
do_update
|
|
;;
|
|
uninstall|remove)
|
|
do_uninstall
|
|
;;
|
|
help|--help|-h)
|
|
show_usage
|
|
;;
|
|
*)
|
|
log_error "Unknown command: $ACTION"
|
|
show_usage
|
|
exit 1
|
|
;;
|
|
esac
|