#!/bin/bash # Stoatchat Setup Script # Automated deployment of Revolt chat backend for st.vish.gg set -euo pipefail # Configuration STOATCHAT_DIR="/opt/stoatchat" DOMAIN="st.vish.gg" REPO_URL="https://github.com/stoatchat/stoatchat.git" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Logging functions log_info() { echo -e "${BLUE}[INFO]${NC} $1" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } # Check if running as root check_root() { if [[ $EUID -eq 0 ]]; then log_error "This script should not be run as root" exit 1 fi } # Check system requirements check_requirements() { log_info "Checking system requirements..." # Check OS if [[ ! -f /etc/os-release ]]; then log_error "Cannot determine OS version" exit 1 fi source /etc/os-release if [[ "$ID" != "ubuntu" && "$ID" != "debian" ]]; then log_warning "This script is designed for Ubuntu/Debian. Proceeding anyway..." fi # Check available memory local mem_gb=$(free -g | awk '/^Mem:/{print $2}') if [[ $mem_gb -lt 4 ]]; then log_warning "Less than 4GB RAM detected. Stoatchat may not perform well." fi # Check available disk space local disk_gb=$(df -BG / | awk 'NR==2{print $4}' | sed 's/G//') if [[ $disk_gb -lt 20 ]]; then log_error "Less than 20GB free disk space. Cannot proceed." exit 1 fi log_success "System requirements check passed" } # Install system dependencies install_dependencies() { log_info "Installing system dependencies..." sudo apt update sudo apt install -y \ curl \ wget \ git \ build-essential \ pkg-config \ libssl-dev \ ca-certificates \ gnupg \ lsb-release \ jq # Install Docker if not present if ! command -v docker &> /dev/null; then log_info "Installing Docker..." curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo usermod -aG docker $USER rm get-docker.sh log_success "Docker installed" else log_info "Docker already installed" fi # Install Docker Compose if not present if ! command -v docker-compose &> /dev/null; then log_info "Installing Docker Compose..." sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose log_success "Docker Compose installed" else log_info "Docker Compose already installed" fi # Install mise (Rust toolchain manager) if ! command -v mise &> /dev/null; then log_info "Installing mise..." curl https://mise.run | sh echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc export PATH="$HOME/.local/bin:$PATH" log_success "mise installed" else log_info "mise already installed" fi log_success "Dependencies installed" } # Clone and setup stoatchat setup_stoatchat() { log_info "Setting up Stoatchat..." # Create directory sudo mkdir -p $STOATCHAT_DIR sudo chown $USER:$USER $STOATCHAT_DIR # Clone repository if [[ ! -d "$STOATCHAT_DIR/.git" ]]; then log_info "Cloning Stoatchat repository..." git clone $REPO_URL $STOATCHAT_DIR else log_info "Updating Stoatchat repository..." cd $STOATCHAT_DIR git pull origin main fi cd $STOATCHAT_DIR # Setup LiveKit configuration if [[ ! -f "livekit.yml" ]]; then log_info "Creating LiveKit configuration..." cp livekit.example.yml livekit.yml # Update LiveKit config with domain sed -i "s/localhost:7880/voice.$DOMAIN/g" livekit.yml sed -i "s/redis_host: localhost/redis_host: localhost/g" livekit.yml sed -i "s/redis_port: 6379/redis_port: 6380/g" livekit.yml fi # Create production configuration log_info "Creating production configuration..." cat > Revolt.overrides.toml << EOF [api] url = "https://api.$DOMAIN" [events] url = "wss://events.$DOMAIN" [autumn] url = "https://files.$DOMAIN" [january] url = "https://proxy.$DOMAIN" [livekit] url = "wss://voice.$DOMAIN" [database] mongodb = "mongodb://localhost:27017/revolt" [redis] url = "redis://localhost:6380" [s3] endpoint = "http://localhost:14009" access_key_id = "minioadmin" secret_access_key = "minioadmin" bucket = "revolt-files" region = "us-east-1" [rabbitmq] url = "amqp://guest:guest@localhost:5672" [email] smtp_host = "smtp.gmail.com" smtp_port = 587 smtp_username = "your-email@example.com" smtp_password = "REDACTED_PASSWORD" from_address = "your-email@example.com" smtp_tls = true [features] registration = true email_verification = false invite_only = false EOF log_success "Stoatchat setup completed" } # Start supporting services start_infrastructure() { log_info "Starting supporting services..." cd $STOATCHAT_DIR # Start Docker services docker-compose up -d # Wait for services to be ready log_info "Waiting for services to be ready..." sleep 30 # Check service health local services=("database" "redis" "minio" "rabbitmq") for service in "${services[@]}"; do if docker-compose ps | grep -q "stoatchat-$service.*Up"; then log_success "$service is running" else log_error "$service failed to start" docker-compose logs stoatchat-$service exit 1 fi done log_success "Infrastructure services started" } # Build stoatchat build_stoatchat() { log_info "Building Stoatchat..." cd $STOATCHAT_DIR # Activate mise environment export PATH="$HOME/.local/bin:$PATH" eval "$(mise activate bash)" # Build the project if mise run build; then log_success "Stoatchat built successfully" else log_error "Failed to build Stoatchat" exit 1 fi } # Start stoatchat services start_stoatchat_services() { log_info "Starting Stoatchat services..." cd $STOATCHAT_DIR # Create logs directory mkdir -p logs # Start services in background export PATH="$HOME/.local/bin:$PATH" eval "$(mise activate bash)" mise service:api > logs/api.log 2>&1 & echo $! > logs/api.pid mise service:events > logs/events.log 2>&1 & echo $! > logs/events.pid mise service:files > logs/files.log 2>&1 & echo $! > logs/files.pid mise service:proxy > logs/proxy.log 2>&1 & echo $! > logs/proxy.pid mise service:gifbox > logs/gifbox.log 2>&1 & echo $! > logs/gifbox.pid mise service:pushd > logs/pushd.log 2>&1 & echo $! > logs/pushd.pid mise service:crond > logs/crond.log 2>&1 & echo $! > logs/crond.pid # Wait for services to start sleep 10 # Check if services are running local ports=(14702 14703 14704 14705 14706) for port in "${ports[@]}"; do if ss -tlnp | grep -q ":$port "; then log_success "Service on port $port is running" else log_warning "Service on port $port may not be ready yet" fi done log_success "Stoatchat services started" } # Test the installation test_installation() { log_info "Testing installation..." # Test API endpoint if curl -s http://localhost:14702/0.8/ | jq -e '.revolt' > /dev/null; then log_success "API is responding correctly" else log_error "API is not responding" return 1 fi # Test file service if curl -s http://localhost:14704/ | jq -e '.autumn' > /dev/null; then log_success "File service is responding correctly" else log_error "File service is not responding" return 1 fi log_success "Installation test passed" } # Create systemd services create_systemd_services() { log_info "Creating systemd services..." # Create stoatchat user service sudo tee /etc/systemd/system/stoatchat.service > /dev/null << EOF [Unit] Description=Stoatchat (Revolt Chat Backend) After=network.target docker.service Requires=docker.service [Service] Type=forking User=$USER WorkingDirectory=$STOATCHAT_DIR Environment=PATH=$HOME/.local/bin:/usr/local/bin:/usr/bin:/bin ExecStart=$STOATCHAT_DIR/scripts/start-services.sh ExecStop=$STOATCHAT_DIR/scripts/stop-services.sh Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF # Create start script cat > $STOATCHAT_DIR/scripts/start-services.sh << 'EOF' #!/bin/bash cd /opt/stoatchat export PATH="$HOME/.local/bin:$PATH" eval "$(mise activate bash)" # Start infrastructure docker-compose up -d # Wait for infrastructure sleep 30 # Start stoatchat services mkdir -p logs mise service:api > logs/api.log 2>&1 & echo $! > logs/api.pid mise service:events > logs/events.log 2>&1 & echo $! > logs/events.pid mise service:files > logs/files.log 2>&1 & echo $! > logs/files.pid mise service:proxy > logs/proxy.log 2>&1 & echo $! > logs/proxy.pid mise service:gifbox > logs/gifbox.log 2>&1 & echo $! > logs/gifbox.pid mise service:pushd > logs/pushd.log 2>&1 & echo $! > logs/pushd.pid mise service:crond > logs/crond.log 2>&1 & echo $! > logs/crond.pid EOF # Create stop script cat > $STOATCHAT_DIR/scripts/stop-services.sh << 'EOF' #!/bin/bash cd /opt/stoatchat # Stop stoatchat services if [[ -f logs/api.pid ]]; then kill $(cat logs/api.pid) 2>/dev/null || true; fi if [[ -f logs/events.pid ]]; then kill $(cat logs/events.pid) 2>/dev/null || true; fi if [[ -f logs/files.pid ]]; then kill $(cat logs/files.pid) 2>/dev/null || true; fi if [[ -f logs/proxy.pid ]]; then kill $(cat logs/proxy.pid) 2>/dev/null || true; fi if [[ -f logs/gifbox.pid ]]; then kill $(cat logs/gifbox.pid) 2>/dev/null || true; fi if [[ -f logs/pushd.pid ]]; then kill $(cat logs/pushd.pid) 2>/dev/null || true; fi if [[ -f logs/crond.pid ]]; then kill $(cat logs/crond.pid) 2>/dev/null || true; fi # Stop infrastructure docker-compose down EOF # Make scripts executable chmod +x $STOATCHAT_DIR/scripts/*.sh # Enable service sudo systemctl daemon-reload sudo systemctl enable stoatchat.service log_success "Systemd services created" } # Print final instructions print_final_instructions() { log_success "Stoatchat installation completed!" echo "" echo "🎉 Installation Summary:" echo " • Stoatchat installed in: $STOATCHAT_DIR" echo " • Domain configured for: $DOMAIN" echo " • Services running on ports: 14702-14706" echo "" echo "🔧 Next Steps:" echo " 1. Set up Gmail App Password:" echo " - Go to Google Account settings" echo " - Enable 2-Factor Authentication" echo " - Generate App Password for 'Mail'" echo " - Update GMAIL_APP_PASSWORD_REQUIRED in Revolt.overrides.toml" echo "" echo " 2. Configure Cloudflare Tunnel for external access:" echo " - api.$DOMAIN → localhost:14702" echo " - events.$DOMAIN → localhost:14703" echo " - files.$DOMAIN → localhost:14704" echo " - proxy.$DOMAIN → localhost:14705" echo "" echo " 3. Set up the web client at $DOMAIN" echo "" echo " 4. Configure LiveKit for voice chat (optional)" echo "" echo "📊 Service Management:" echo " • Start: sudo systemctl start stoatchat" echo " • Stop: sudo systemctl stop stoatchat" echo " • Status: sudo systemctl status stoatchat" echo " • Logs: journalctl -u stoatchat -f" echo "" echo "🔍 Manual Service Management:" echo " • View logs: tail -f $STOATCHAT_DIR/logs/*.log" echo " • Test API: curl http://localhost:14702/0.8/" echo " • Check ports: ss -tlnp | grep revolt" echo "" echo "📚 Documentation: $STOATCHAT_DIR/README.md" echo "" } # Main execution main() { log_info "Starting Stoatchat installation for $DOMAIN" check_root check_requirements install_dependencies setup_stoatchat start_infrastructure build_stoatchat start_stoatchat_services if test_installation; then create_systemd_services print_final_instructions else log_error "Installation test failed. Please check the logs." exit 1 fi } # Run main function main "$@"