#!/bin/bash # # ServerKit CLI - Management tool for ServerKit # # Architecture: # - Backend: systemd service (runs directly on host) # - Frontend: Docker container (nginx) # # Usage: serverkit [options] # set -e # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # Configuration INSTALL_DIR="${SERVERKIT_DIR:-/opt/serverkit}" VENV_DIR="$INSTALL_DIR/venv" BACKEND_SERVICE="serverkit" FRONTEND_CONTAINER="serverkit-frontend" # Helper functions print_header() { echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${BLUE} ServerKit CLI${NC}" echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${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}"; } check_root() { if [ "$EUID" -ne 0 ]; then print_error "This command requires root privileges. Run with sudo." exit 1 fi } check_installed() { if [ ! -d "$INSTALL_DIR" ]; then print_error "ServerKit is not installed in $INSTALL_DIR" print_info "Run the install script first" exit 1 fi } run_cli() { # Run CLI commands directly on host using the virtual environment cd "$INSTALL_DIR/backend" source "$VENV_DIR/bin/activate" python cli.py "$@" } # Commands cmd_help() { print_header echo "" echo "Usage: serverkit [options]" echo "" echo "Service Commands:" echo " start Start all services" echo " stop Stop all services" echo " restart Restart all services" echo " status Show service status" echo " logs [service] View logs (backend, frontend, or all)" echo " update Update to latest version" echo " uninstall Uninstall ServerKit" echo "" echo "User Management:" echo " create-admin Create a new admin user" echo " reset-password Reset a user's password" echo " unlock-user Unlock a locked user account" echo " list-users List all users" echo " make-admin Promote user to admin" echo " deactivate-user Deactivate a user account" echo " activate-user Activate a user account" echo "" echo "Database Commands:" echo " init-db Initialize the database" echo " backup-db Backup the database" echo " restore-db Restore database from backup" echo "" echo "Utility Commands:" echo " generate-keys Generate secure keys for .env" echo " config Edit configuration" echo "" echo "Examples:" echo " sudo serverkit start" echo " sudo serverkit create-admin" echo " serverkit logs backend" echo "" } cmd_start() { check_root check_installed print_info "Starting ServerKit..." # Start backend (systemd) systemctl start $BACKEND_SERVICE # Start frontend (Docker) cd "$INSTALL_DIR" docker compose up -d sleep 3 print_success "ServerKit started" cmd_status_brief } cmd_stop() { check_root check_installed print_info "Stopping ServerKit..." # Stop backend (systemd) systemctl stop $BACKEND_SERVICE || true # Stop frontend (Docker) cd "$INSTALL_DIR" docker compose down || true print_success "ServerKit stopped" } cmd_restart() { check_root check_installed print_info "Restarting ServerKit..." # Restart backend (systemd) systemctl restart $BACKEND_SERVICE # Restart frontend (Docker) cd "$INSTALL_DIR" docker compose restart sleep 3 print_success "ServerKit restarted" cmd_status_brief } cmd_status_brief() { # Quick status check echo "" # Backend status if systemctl is-active --quiet $BACKEND_SERVICE; then print_success "Backend: running" else print_error "Backend: stopped" fi # Frontend status if docker ps --format '{{.Names}}' | grep -q $FRONTEND_CONTAINER; then print_success "Frontend: running" else print_error "Frontend: stopped" fi # API health if curl -s http://127.0.0.1:5000/api/v1/system/health > /dev/null 2>&1; then print_success "API: healthy" else print_warning "API: not responding" fi } cmd_status() { check_installed print_header echo "" echo "Backend Service (systemd):" echo "─────────────────────────────" systemctl status $BACKEND_SERVICE --no-pager -l 2>/dev/null || print_warning "Backend service not found" echo "" echo "Frontend Container (Docker):" echo "─────────────────────────────" cd "$INSTALL_DIR" docker compose ps 2>/dev/null || print_warning "Frontend container not found" echo "" cmd_status_brief } cmd_logs() { check_installed service="${1:-all}" case "$service" in backend) journalctl -u $BACKEND_SERVICE -f ;; frontend) cd "$INSTALL_DIR" docker compose logs -f ;; all|*) print_info "Showing backend logs (Ctrl+C to switch to frontend)..." echo "" # Show both in split view isn't easy in bash, so show backend first journalctl -u $BACKEND_SERVICE -n 50 --no-pager echo "" print_info "Showing frontend logs..." echo "" cd "$INSTALL_DIR" docker compose logs --tail=50 echo "" print_info "For live logs, use: serverkit logs backend OR serverkit logs frontend" ;; esac } cmd_update() { check_root check_installed print_header echo "" print_info "Updating ServerKit..." cd "$INSTALL_DIR" # Store current version CURRENT_VERSION=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") # Stop services print_info "Stopping services..." systemctl stop $BACKEND_SERVICE || true docker compose down || true # Pull latest changes print_info "Pulling latest changes..." git fetch origin git reset --hard origin/main NEW_VERSION=$(git rev-parse --short HEAD) # Update Python dependencies print_info "Updating Python dependencies..." source "$VENV_DIR/bin/activate" pip install -r "$INSTALL_DIR/backend/requirements.txt" --quiet # Rebuild frontend print_info "Rebuilding frontend..." docker compose build # Reload systemd in case service file changed systemctl daemon-reload # Restart services print_info "Starting services..." systemctl start $BACKEND_SERVICE docker compose up -d sleep 5 echo "" print_success "ServerKit updated!" echo " Previous version: $CURRENT_VERSION" echo " Current version: $NEW_VERSION" echo "" cmd_status_brief } cmd_uninstall() { check_root print_header echo "" print_warning "This will remove ServerKit and all data!" read -p "Are you sure? (y/N) " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then print_info "Stopping services..." systemctl stop $BACKEND_SERVICE 2>/dev/null || true systemctl disable $BACKEND_SERVICE 2>/dev/null || true cd "$INSTALL_DIR" 2>/dev/null && docker compose down -v 2>/dev/null || true print_info "Removing files..." rm -f /etc/systemd/system/serverkit.service rm -f /usr/local/bin/serverkit rm -rf "$INSTALL_DIR" rm -rf /var/log/serverkit rm -rf /var/lib/serverkit systemctl daemon-reload print_success "ServerKit uninstalled" fi } cmd_create_admin() { check_installed print_info "Creating admin user..." run_cli create-admin } cmd_reset_password() { check_installed print_info "Resetting password..." run_cli reset-password } cmd_unlock_user() { check_installed run_cli unlock-user } cmd_list_users() { check_installed run_cli list-users } cmd_make_admin() { check_installed run_cli make-admin } cmd_deactivate_user() { check_installed run_cli deactivate-user } cmd_activate_user() { check_installed run_cli activate-user } cmd_init_db() { check_installed run_cli init-db } cmd_backup_db() { check_installed BACKUP_DIR="$INSTALL_DIR/backups" BACKUP_FILE="$BACKUP_DIR/serverkit-$(date +%Y%m%d-%H%M%S).db" DB_FILE="$INSTALL_DIR/backend/instance/serverkit.db" mkdir -p "$BACKUP_DIR" if [ -f "$DB_FILE" ]; then cp "$DB_FILE" "$BACKUP_FILE" print_success "Database backed up to $BACKUP_FILE" else print_error "Database file not found: $DB_FILE" exit 1 fi } cmd_restore_db() { check_root check_installed BACKUP_DIR="$INSTALL_DIR/backups" DB_FILE="$INSTALL_DIR/backend/instance/serverkit.db" if [ -z "$1" ]; then echo "Available backups:" ls -la "$BACKUP_DIR"/*.db 2>/dev/null || echo "No backups found" echo "" read -p "Enter backup file path: " BACKUP_FILE else BACKUP_FILE="$1" fi if [ ! -f "$BACKUP_FILE" ]; then print_error "Backup file not found: $BACKUP_FILE" exit 1 fi print_warning "This will overwrite the current database!" read -p "Continue? (y/N) " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then # Stop backend before restore systemctl stop $BACKEND_SERVICE cp "$BACKUP_FILE" "$DB_FILE" # Restart backend systemctl start $BACKEND_SERVICE print_success "Database restored" fi } cmd_generate_keys() { print_header echo "" echo "Add these to your .env file:" echo "" SECRET_KEY=$(openssl rand -hex 32) JWT_SECRET_KEY=$(openssl rand -hex 32) echo "SECRET_KEY=$SECRET_KEY" echo "JWT_SECRET_KEY=$JWT_SECRET_KEY" echo "" } cmd_config() { check_installed ${EDITOR:-nano} "$INSTALL_DIR/.env" print_info "Restart ServerKit to apply changes: sudo serverkit restart" } # Main case "${1:-help}" in help|--help|-h) cmd_help ;; start) cmd_start ;; stop) cmd_stop ;; restart) cmd_restart ;; status) cmd_status ;; logs) cmd_logs "$2" ;; update) cmd_update ;; uninstall) cmd_uninstall ;; create-admin) cmd_create_admin ;; reset-password) cmd_reset_password ;; unlock-user) cmd_unlock_user ;; list-users) cmd_list_users ;; make-admin) cmd_make_admin ;; deactivate-user) cmd_deactivate_user ;; activate-user) cmd_activate_user ;; init-db) cmd_init_db ;; backup-db) cmd_backup_db ;; restore-db) cmd_restore_db "$2" ;; generate-keys) cmd_generate_keys ;; config) cmd_config ;; *) print_error "Unknown command: $1" echo "Run 'serverkit help' for usage" exit 1 ;; esac