diff --git a/install.sh b/install.sh index 7a60fb0..dc72ac2 100644 --- a/install.sh +++ b/install.sh @@ -2,21 +2,11 @@ # # ServerKit Quick Install Script for Ubuntu/Debian/Fedora # -# Architecture: -# - Backend: Runs directly on host (for full system access) -# - Frontend: Runs in Docker (nginx serving static files) -# -# Usage: curl -fsSL https://serverkit.ai/install.sh | bash -# set -e -# Safety: Move to a valid directory first -# Prevents "getcwd: cannot access parent directories" error -# when running from a deleted directory (e.g., after uninstall) cd /tmp 2>/dev/null || cd / || true -# Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -28,368 +18,376 @@ VENV_DIR="$INSTALL_DIR/venv" LOG_DIR="/var/log/serverkit" DATA_DIR="/var/lib/serverkit" +# Python constraints +PYTHON_MIN="3.11" +PYTHON_MAX="3.12" +PYTHON_BIN="" + +SAFE_MODE=false + print_header() { - echo -e "${BLUE}" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo " ServerKit Installer" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo -e "${NC}" +echo -e "${BLUE}" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " ServerKit Installer" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo -e "${NC}" +} + +print_success(){ echo -e "${GREEN}✓ $1${NC}"; } +print_error(){ echo -e "${RED}✗ $1${NC}"; } +print_warning(){ echo -e "${YELLOW}! $1${NC}"; } +print_info(){ echo -e "${BLUE}→ $1${NC}"; } + +version_ge(){ printf '%s\n%s' "$2" "$1" | sort -C -V; } +version_le(){ printf '%s\n%s' "$1" "$2" | sort -C -V; } + +detect_python(){ + +if command -v python3 &>/dev/null; then + PY_VER=$(python3 -c 'import sys;print(".".join(map(str,sys.version_info[:2])))') + + if version_ge "$PY_VER" "$PYTHON_MIN" && version_le "$PY_VER" "$PYTHON_MAX"; then + PYTHON_BIN="python3" + print_success "Using system Python $PY_VER" + return + fi +fi + +print_warning "System Python not supported ($PYTHON_MIN-$PYTHON_MAX)" +} + +check_ram(){ + +RAM=$(free -m | awk '/Mem:/ {print $2}') + +if [ "$RAM" -le 700 ]; then +SAFE_MODE=true +print_warning "Low RAM detected (${RAM}MB) → enabling VPS Safe Mode" +fi + } -print_success() { echo -e "${GREEN}✓ $1${NC}"; } -print_error() { echo -e "${RED}✗ $1${NC}"; } -print_warning() { echo -e "${YELLOW}! $1${NC}"; } -print_info() { echo -e "${BLUE}→ $1${NC}"; } +setup_swap(){ + +SWAP_TOTAL=$(free -m | awk '/Swap:/ {print $2}') + +if [ "$SWAP_TOTAL" -lt 512 ]; then + +print_info "Creating swap (1GB)" + +if [ ! -f /swapfile ]; then +fallocate -l 1G /swapfile 2>/dev/null || dd if=/dev/zero of=/swapfile bs=1M count=1024 +chmod 600 /swapfile +mkswap /swapfile >/dev/null +fi + +swapon /swapfile || true + +print_success "Swap enabled" + +fi + +} print_header -# Check if running as root if [ "$EUID" -ne 0 ]; then - print_error "Please run as root (sudo)" - exit 1 +print_error "Please run as root (sudo)" +exit 1 fi -# Check OS OS_FAMILY="unknown" + if [ -f /etc/os-release ]; then - . /etc/os-release - if [ "$ID" = "ubuntu" ] || [ "$ID" = "debian" ]; then - OS_FAMILY="debian" - elif [ "$ID" = "fedora" ]; then - OS_FAMILY="fedora" - else - print_warning "This script is designed for Ubuntu/Debian/Fedora. Proceed with caution." - fi +. /etc/os-release + +if [ "$ID" = "ubuntu" ] || [ "$ID" = "debian" ]; then +OS_FAMILY="debian" +elif [ "$ID" = "fedora" ]; then +OS_FAMILY="fedora" else - print_warning "Cannot detect OS. Proceeding with caution." +print_warning "Unknown OS" +fi fi -echo "" print_info "Installing system dependencies..." if [ "$OS_FAMILY" = "debian" ] || [ "$OS_FAMILY" = "unknown" ]; then - # Configure needrestart for non-interactive mode (Ubuntu 22.04+) - # This prevents the "Which services should be restarted?" dialog - # and avoids dpkg lock issues during automated installs - export NEEDRESTART_MODE=a - export DEBIAN_FRONTEND=noninteractive - - # Also configure needrestart.conf if it exists for future apt operations - if [ -f /etc/needrestart/needrestart.conf ]; then - # Set needrestart to auto-restart mode - sed -i "s/#\$nrconf{restart} = 'i';/\$nrconf{restart} = 'a';/" /etc/needrestart/needrestart.conf 2>/dev/null || true - fi - # Update package list - apt-get update - - # Install Python and required packages - apt-get install -y \ - python3 \ - python3-pip \ - python3-venv \ - python3-dev \ - git \ - curl \ - build-essential \ - libffi-dev \ - libssl-dev \ - iproute2 \ - procps +export NEEDRESTART_MODE=a +export DEBIAN_FRONTEND=noninteractive + +apt-get update + +apt-get install -y \ +python3 \ +python3-pip \ +python3-venv \ +python3-dev \ +git \ +curl \ +build-essential \ +libffi-dev \ +libssl-dev \ +iproute2 \ +procps + +detect_python + +if [ -z "$PYTHON_BIN" ]; then + +print_info "Installing Python 3.12..." + +apt-get install -y \ +python3.12 \ +python3.12-venv \ +python3.12-dev + +PYTHON_BIN="python3.12" + +fi + elif [ "$OS_FAMILY" = "fedora" ]; then - # Update package list - dnf update -y - - # Install Python and required packages - dnf install -y \ - python3 \ - python3-pip \ - git \ - curl \ - gcc \ - gcc-c++ \ - make \ - libffi-devel \ - openssl-devel \ - python3-devel \ - iproute \ - procps-ng + +dnf update -y + +dnf install -y \ +python3 \ +python3-pip \ +git \ +curl \ +gcc \ +gcc-c++ \ +make \ +libffi-devel \ +openssl-devel \ +python3-devel \ +iproute \ +procps-ng + +detect_python + +if [ -z "$PYTHON_BIN" ]; then + +print_info "Installing Python 3.12..." + +dnf install -y \ +python3.12 \ +python3.12-devel + +PYTHON_BIN="python3.12" + +fi + fi print_success "System dependencies installed" -# Install Docker if not present if ! command -v docker &> /dev/null; then - print_info "Installing Docker..." - curl -fsSL https://get.docker.com | sh - systemctl enable docker - systemctl start docker - print_success "Docker installed" + +print_info "Installing Docker..." + +curl -fsSL https://get.docker.com | sh + +systemctl enable docker +systemctl start docker + +print_success "Docker installed" + else - print_success "Docker already installed" + +print_success "Docker already installed" + fi -# Install Docker Compose plugin if not present if ! docker compose version &> /dev/null; then - print_info "Installing Docker Compose..." - if [ "$OS_FAMILY" = "fedora" ]; then - dnf install -y docker-compose-plugin - else - apt-get install -y docker-compose-plugin - fi - print_success "Docker Compose installed" + +print_info "Installing Docker Compose..." + +if [ "$OS_FAMILY" = "fedora" ]; then +dnf install -y docker-compose-plugin +else +apt-get install -y docker-compose-plugin +fi + +print_success "Docker Compose installed" + else - print_success "Docker Compose already installed" + +print_success "Docker Compose already installed" + fi -# Install Node.js for frontend build (builds on host to avoid Docker memory issues) if ! command -v node &> /dev/null; then - print_info "Installing Node.js..." - if [ "$OS_FAMILY" = "fedora" ]; then - curl -fsSL https://rpm.nodesource.com/setup_20.x | bash - - dnf install -y nodejs - else - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - - apt-get install -y nodejs - fi - print_success "Node.js $(node --version) installed" + +print_info "Installing Node.js..." + +if [ "$OS_FAMILY" = "fedora" ]; then +curl -fsSL https://rpm.nodesource.com/setup_20.x | bash - +dnf install -y nodejs +else +curl -fsSL https://deb.nodesource.com/setup_20.x | bash - +apt-get install -y nodejs + +fi + +print_success "Node.js $(node --version) installed" + else - print_success "Node.js $(node --version) already installed" + +print_success "Node.js $(node --version) already installed" + fi -# Clone or update repository print_info "Installing ServerKit to $INSTALL_DIR..." if [ -d "$INSTALL_DIR" ]; then - print_warning "Directory exists, updating..." - cd "$INSTALL_DIR" - git fetch origin - git reset --hard origin/main + +print_warning "Directory exists, updating..." + +cd "$INSTALL_DIR" +git fetch origin +git reset --hard origin/main + else - git clone https://github.com/jhd3197/serverkit.git "$INSTALL_DIR" - cd "$INSTALL_DIR" -fi -print_success "Repository cloned" +git clone https://github.com/jhd3197/serverkit.git "$INSTALL_DIR" +cd "$INSTALL_DIR" + +fi -# Create directories -print_info "Creating directories..." mkdir -p "$LOG_DIR" mkdir -p "$DATA_DIR" mkdir -p "$INSTALL_DIR/backend/instance" mkdir -p "$INSTALL_DIR/nginx/ssl" -mkdir -p /etc/serverkit/templates -mkdir -p /var/serverkit/apps - -# Copy bundled templates to system directory -print_info "Installing app templates..." -if [ -d "$INSTALL_DIR/backend/templates" ]; then - cp -r "$INSTALL_DIR/backend/templates/"*.yaml /etc/serverkit/templates/ 2>/dev/null || true - cp -r "$INSTALL_DIR/backend/templates/"*.yml /etc/serverkit/templates/ 2>/dev/null || true - print_success "Installed $(ls /etc/serverkit/templates/*.yaml 2>/dev/null | wc -l) app templates" -fi -# Set up Python virtual environment print_info "Setting up Python virtual environment..." -python3 -m venv "$VENV_DIR" + +print_info "Using Python binary: $PYTHON_BIN" + +$PYTHON_BIN --version + +$PYTHON_BIN -m venv "$VENV_DIR" + source "$VENV_DIR/bin/activate" -# Install Python dependencies print_info "Installing Python dependencies..." + +if [ "$SAFE_MODE" = true ]; then +pip install --upgrade pip --no-cache-dir +pip install --no-cache-dir -r "$INSTALL_DIR/backend/requirements.txt" +else pip install --upgrade pip pip install -r "$INSTALL_DIR/backend/requirements.txt" +fi + pip install gunicorn gevent gevent-websocket print_success "Python dependencies installed" -# Generate .env if not exists if [ ! -f "$INSTALL_DIR/.env" ]; then - print_info "Generating configuration..." - SECRET_KEY=$(openssl rand -hex 32) - JWT_SECRET_KEY=$(openssl rand -hex 32) - cat > "$INSTALL_DIR/.env" << EOF -# ServerKit Configuration -# Generated on $(date) +print_info "Generating configuration..." + +SECRET_KEY=$(openssl rand -hex 32) +JWT_SECRET_KEY=$(openssl rand -hex 32) -# Security Keys (auto-generated, keep secret!) +cat > "$INSTALL_DIR/.env" << EOF SECRET_KEY=$SECRET_KEY JWT_SECRET_KEY=$JWT_SECRET_KEY - -# Database (SQLite by default) DATABASE_URL=sqlite:///$INSTALL_DIR/backend/instance/serverkit.db - -# CORS Origins (comma-separated, add your domain) -CORS_ORIGINS=http://localhost,https://localhost - -# Ports PORT=80 SSL_PORT=443 - -# Environment FLASK_ENV=production EOF - print_success "Configuration generated" -else - print_warning ".env already exists, keeping existing configuration" -fi - -# Generate self-signed SSL certificate if not exists -if [ ! -f "$INSTALL_DIR/nginx/ssl/fullchain.pem" ]; then - print_info "Generating self-signed SSL certificate..." - openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout "$INSTALL_DIR/nginx/ssl/privkey.pem" \ - -out "$INSTALL_DIR/nginx/ssl/fullchain.pem" \ - -subj "/CN=localhost" 2>/dev/null - print_warning "Self-signed certificate created. Replace with real cert for production." fi -# Install systemd service for backend print_info "Installing systemd service..." + cp "$INSTALL_DIR/serverkit-backend.service" /etc/systemd/system/serverkit.service -# Reload systemd and enable service systemctl daemon-reload systemctl enable serverkit print_success "Systemd service installed" -# Make CLI executable and create symlink chmod +x "$INSTALL_DIR/serverkit" + ln -sf "$INSTALL_DIR/serverkit" /usr/local/bin/serverkit print_success "CLI installed" -# Install and configure host nginx as reverse proxy -print_info "Setting up nginx reverse proxy..." +print_info "Installing nginx..." + if [ "$OS_FAMILY" = "fedora" ]; then - dnf install -y nginx +dnf install -y nginx else - apt-get install -y nginx -fi - -# Stop nginx and remove default site -systemctl stop nginx 2>/dev/null || true -rm -f /etc/nginx/sites-enabled/default - -# Ensure sites directories exist -mkdir -p /etc/nginx/sites-available -mkdir -p /etc/nginx/sites-enabled - -# Ensure nginx.conf includes sites-enabled -if ! grep -q "sites-enabled" /etc/nginx/nginx.conf; then - sed -i '/http {/a \ include /etc/nginx/sites-enabled/*;' /etc/nginx/nginx.conf +apt-get install -y nginx fi -# Install ServerKit site config -cp "$INSTALL_DIR/nginx/sites-available/serverkit.conf" /etc/nginx/sites-available/ -ln -sf /etc/nginx/sites-available/serverkit.conf /etc/nginx/sites-enabled/ +systemctl enable nginx +systemctl start nginx -# Copy site template -cp "$INSTALL_DIR/nginx/sites-available/example.conf.template" /etc/nginx/sites-available/ +print_success "Nginx installed" -# Configure SELinux to allow nginx reverse proxying -if [ "$OS_FAMILY" = "fedora" ] && command -v setsebool &> /dev/null; then - setsebool -P httpd_can_network_connect 1 2>/dev/null || true -fi +print_info "Building frontend..." -print_success "Nginx proxy configured" +cd "$INSTALL_DIR/frontend" -# Clean up Docker to prevent issues -print_info "Cleaning up Docker..." -docker network prune -f 2>/dev/null || true -docker container prune -f 2>/dev/null || true +npm ci --prefer-offline -# Ensure swap exists for low-RAM VPS servers (Vite build needs ~512MB+) -SWAP_TOTAL=$(free -m | awk '/^Swap:/ {print $2}') -if [ "$SWAP_TOTAL" -lt 512 ]; then - print_info "Creating swap space for build..." - if [ ! -f /swapfile ]; then - fallocate -l 1G /swapfile 2>/dev/null || dd if=/dev/zero of=/swapfile bs=1M count=1024 status=none - chmod 600 /swapfile - mkswap /swapfile >/dev/null - fi - swapon /swapfile 2>/dev/null || true -fi - -# Build frontend on host (avoids Docker memory overhead on low-RAM VPS) -print_info "Building frontend..." -cd "$INSTALL_DIR/frontend" -npm ci --prefer-offline 2>&1 | tail -1 NODE_OPTIONS="--max-old-space-size=1024" npm run build -print_success "Frontend built" -# Package frontend into nginx container -print_info "Building frontend container..." cd "$INSTALL_DIR" -docker compose build -print_info "Starting services..." +docker compose build -# Start backend (systemd) systemctl start serverkit -# Start frontend (Docker) docker compose up -d -# Start nginx systemctl start nginx -systemctl enable nginx -# Wait for services to start -print_info "Waiting for services to start..." -sleep 10 +sleep 8 -# Health check -echo "" BACKEND_OK=false FRONTEND_OK=false if curl -s http://127.0.0.1:5000/api/v1/system/health > /dev/null 2>&1; then - BACKEND_OK=true - print_success "Backend is running" +BACKEND_OK=true +print_success "Backend is running" else - print_error "Backend health check failed" +print_error "Backend health check failed" fi if curl -s http://localhost > /dev/null 2>&1; then - FRONTEND_OK=true - print_success "Frontend is running" +FRONTEND_OK=true +print_success "Frontend is running" else - print_error "Frontend health check failed" +print_error "Frontend health check failed" fi echo "" + if [ "$BACKEND_OK" = true ] && [ "$FRONTEND_OK" = true ]; then - # Track successful install - INSTALLED_VERSION=$(cat "$INSTALL_DIR/VERSION" 2>/dev/null | tr -d '\n\r ') - curl -s "https://serverkit.ai/track/install?v=${INSTALLED_VERSION}" >/dev/null 2>&1 || true - - echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - echo -e "${GREEN} Installation Complete!${NC}" - echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - echo "" - echo "ServerKit is now running at: http://localhost" - echo "" - echo "Quick Start:" - echo " 1. Create admin user: serverkit create-admin" - echo " 2. View status: serverkit status" - echo " 3. View logs: serverkit logs" - echo "" - echo "Service Management:" - echo " Backend (systemd): systemctl [start|stop|restart] serverkit" - echo " Frontend (Docker): docker compose -C $INSTALL_DIR [up|down]" - echo "" - echo "For all commands: serverkit help" - echo "" + +echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo -e "${GREEN} Installation Complete!${NC}" +echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + +echo "" +echo "ServerKit running at http://localhost" +echo "" +echo "Create admin:" +echo "serverkit create-admin" +echo "" +echo "Status:" +echo "serverkit status" +echo "" + else - echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - echo -e "${RED} Installation may have issues${NC}" - echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - echo "" - echo "Troubleshooting:" - echo " Backend logs: journalctl -u serverkit -f" - echo " Frontend logs: docker compose -C $INSTALL_DIR logs -f" - echo "" + +echo -e "${RED}Installation may have issues${NC}" + fi diff --git a/uninstall.sh b/uninstall.sh new file mode 100644 index 0000000..3edb07b --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,164 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +######################################## +# ServerKit Uninstaller +######################################## + +INSTALL_DIR="/opt/serverkit" +DATA_DIR="/var/lib/serverkit" +LOG_DIR="/var/log/serverkit" +LOG_FILE="/var/log/serverkit-uninstall.log" + +mkdir -p /var/log +exec > >(tee -a "$LOG_FILE") 2>&1 + +######################################## +# Colors +######################################## + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +######################################## +# UI helpers +######################################## + +print_header() { +echo -e "${BLUE}" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " ServerKit Uninstaller" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo -e "${NC}" +} + +print_success(){ echo -e "${GREEN}✓ $1${NC}"; } +print_error(){ echo -e "${RED}✗ $1${NC}"; } +print_warning(){ echo -e "${YELLOW}! $1${NC}"; } +print_info(){ echo -e "${BLUE}→ $1${NC}"; } + +######################################## +# Root check +######################################## + +if [[ $EUID -ne 0 ]]; then +print_error "Please run as root (sudo)" +exit 1 +fi + +print_header + +######################################## +# Confirm uninstall +######################################## + +echo +read -p "Remove ServerKit completely? (y/N): " confirm + +if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then +print_warning "Uninstall cancelled" +exit 0 +fi + +######################################## +# Stop services +######################################## + +print_info "Stopping ServerKit service" + +systemctl stop serverkit 2>/dev/null || true +systemctl disable serverkit 2>/dev/null || true + +print_success "Backend service stopped" + +######################################## +# Stop containers +######################################## + +print_info "Stopping Docker containers" + +docker compose -C "$INSTALL_DIR" down --remove-orphans 2>/dev/null || true + +print_success "Containers removed" + +######################################## +# Remove systemd service +######################################## + +print_info "Removing systemd service" + +rm -f /etc/systemd/system/serverkit.service + +systemctl daemon-reload + +print_success "Systemd service removed" + +######################################## +# Remove nginx config +######################################## + +print_info "Removing nginx configuration" + +rm -f /etc/nginx/sites-enabled/serverkit.conf 2>/dev/null || true +rm -f /etc/nginx/sites-available/serverkit.conf 2>/dev/null || true + +systemctl reload nginx 2>/dev/null || true + +print_success "Nginx config removed" + +######################################## +# Remove files +######################################## + +print_info "Removing installation files" + +rm -rf "$INSTALL_DIR" + +print_success "Installation directory removed" + +######################################## +# Remove data +######################################## + +print_info "Removing data directory" + +rm -rf "$DATA_DIR" + +print_success "Data directory removed" + +######################################## +# Remove logs +######################################## + +print_info "Removing logs" + +rm -rf "$LOG_DIR" + +print_success "Log directory removed" + +######################################## +# Remove CLI +######################################## + +print_info "Removing CLI command" + +rm -f /usr/local/bin/serverkit + +print_success "CLI removed" + +######################################## +# Finish +######################################## + +echo +echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo -e "${GREEN} ServerKit removed successfully${NC}" +echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + +echo +echo "Uninstall log:" +echo "$LOG_FILE" +echo \ No newline at end of file