#!/bin/bash # Ginxsom Interactive Setup Wizard # Creates signed configuration events for first-run server setup set -e # Configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" CONFIG_PATH="${1:-}" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Helper functions log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; } # Check dependencies check_dependencies() { log_info "Checking dependencies..." local missing_deps=() for cmd in nak jq; do if ! command -v $cmd &> /dev/null; then missing_deps+=("$cmd") fi done if [ ${#missing_deps[@]} -ne 0 ]; then log_error "Missing dependencies: ${missing_deps[*]}" echo "" echo "Please install the missing dependencies:" echo "- nak: https://github.com/fiatjaf/nak" echo "- jq: sudo apt install jq (or equivalent for your system)" exit 1 fi log_success "All dependencies found" } # Validate private key format validate_private_key() { local key="$1" if [[ ! "$key" =~ ^[a-fA-F0-9]{64}$ ]]; then return 1 fi return 0 } # Setup key pairs with user choice setup_keys() { log_info "Setting up cryptographic key pairs..." echo "" echo "=== Admin Key Setup ===" echo "Choose an option for your admin private key:" echo "1. Generate a new random admin key" echo "2. Use an existing admin private key" echo "" while true; do echo -n "Choice (1/2): " read -r ADMIN_KEY_CHOICE case "$ADMIN_KEY_CHOICE" in 1) log_info "Generating new admin key pair..." ADMIN_PRIVKEY=$(nak key generate) ADMIN_PUBKEY=$(echo "$ADMIN_PRIVKEY" | nak key public) log_success "New admin key pair generated" break ;; 2) echo -n "Enter your admin private key (64 hex characters): " read -r ADMIN_PRIVKEY if validate_private_key "$ADMIN_PRIVKEY"; then ADMIN_PUBKEY=$(echo "$ADMIN_PRIVKEY" | nak key public) if [ $? -eq 0 ]; then log_success "Admin private key validated" break else log_error "Invalid private key format or nak error" fi else log_error "Invalid private key format (must be 64 hex characters)" fi ;; *) log_error "Please choose 1 or 2" ;; esac done echo "" echo "=== Server Key Setup ===" echo "Choose an option for your Ginxsom server private key:" echo "1. Generate a new random server key" echo "2. Use an existing server private key" echo "" while true; do echo -n "Choice (1/2): " read -r SERVER_KEY_CHOICE case "$SERVER_KEY_CHOICE" in 1) log_info "Generating new server key pair..." SERVER_PRIVKEY=$(nak key generate) SERVER_PUBKEY=$(echo "$SERVER_PRIVKEY" | nak key public) log_success "New server key pair generated" break ;; 2) echo -n "Enter your server private key (64 hex characters): " read -r SERVER_PRIVKEY if validate_private_key "$SERVER_PRIVKEY"; then SERVER_PUBKEY=$(echo "$SERVER_PRIVKEY" | nak key public) if [ $? -eq 0 ]; then log_success "Server private key validated" break else log_error "Invalid private key format or nak error" fi else log_error "Invalid private key format (must be 64 hex characters)" fi ;; *) log_error "Please choose 1 or 2" ;; esac done echo "" log_success "Key pairs configured:" echo " Admin Public Key: $ADMIN_PUBKEY" echo " Server Public Key: $SERVER_PUBKEY" # Save keys securely echo "ADMIN_PRIVKEY='$ADMIN_PRIVKEY'" > "$PROJECT_ROOT/.admin_keys" echo "ADMIN_PUBKEY='$ADMIN_PUBKEY'" >> "$PROJECT_ROOT/.admin_keys" echo "SERVER_PRIVKEY='$SERVER_PRIVKEY'" >> "$PROJECT_ROOT/.admin_keys" echo "SERVER_PUBKEY='$SERVER_PUBKEY'" >> "$PROJECT_ROOT/.admin_keys" chmod 600 "$PROJECT_ROOT/.admin_keys" log_warning "Keys saved to $PROJECT_ROOT/.admin_keys (keep this file secure!)" } # Collect server configuration collect_configuration() { log_info "Collecting server configuration..." echo "" echo "=== Server Configuration Setup ===" # CDN Origin echo -n "CDN Origin URL (default: http://localhost:9001): " read -r CDN_ORIGIN CDN_ORIGIN="${CDN_ORIGIN:-http://localhost:9001}" # Max file size echo -n "Maximum file size in MB (default: 100): " read -r MAX_SIZE_MB MAX_SIZE_MB="${MAX_SIZE_MB:-100}" MAX_FILE_SIZE=$((MAX_SIZE_MB * 1024 * 1024)) # NIP-94 support echo -n "Enable NIP-94 metadata (y/n, default: y): " read -r ENABLE_NIP94 case "$ENABLE_NIP94" in [Nn]*) NIP94_ENABLED="false" ;; *) NIP94_ENABLED="true" ;; esac # Authentication rules echo -n "Enable authentication rules system (y/n, default: n): " read -r ENABLE_AUTH_RULES case "$ENABLE_AUTH_RULES" in [Yy]*) AUTH_RULES_ENABLED="true" ;; *) AUTH_RULES_ENABLED="false" ;; esac # Cache TTL echo -n "Authentication cache TTL in seconds (default: 300): " read -r CACHE_TTL CACHE_TTL="${CACHE_TTL:-300}" echo "" log_success "Configuration collected:" echo " CDN Origin: $CDN_ORIGIN" echo " Max File Size: ${MAX_SIZE_MB}MB" echo " NIP-94 Enabled: $NIP94_ENABLED" echo " Auth Rules Enabled: $AUTH_RULES_ENABLED" echo " Cache TTL: ${CACHE_TTL}s" } # Create configuration event create_config_event() { log_info "Creating signed configuration event..." local expiration=$(($(date +%s) + 31536000)) # 1 year from now # Create configuration event with all settings CONFIG_EVENT=$(nak event -k 33333 -c "Ginxsom server configuration" \ --tag server_privkey="$SERVER_PRIVKEY" \ --tag cdn_origin="$CDN_ORIGIN" \ --tag max_file_size="$MAX_FILE_SIZE" \ --tag nip94_enabled="$NIP94_ENABLED" \ --tag auth_rules_enabled="$AUTH_RULES_ENABLED" \ --tag auth_cache_ttl="$CACHE_TTL" \ --tag expiration="$expiration" \ --sec "$ADMIN_PRIVKEY") if [ $? -ne 0 ]; then log_error "Failed to create configuration event" exit 1 fi log_success "Configuration event created and signed" } # Save configuration file save_config_file() { local config_file="$1" log_info "Saving configuration to $config_file" # Create directory if it doesn't exist local config_dir=$(dirname "$config_file") mkdir -p "$config_dir" # Save configuration event to file echo "$CONFIG_EVENT" | jq . > "$config_file" if [ $? -ne 0 ]; then log_error "Failed to save configuration file" exit 1 fi chmod 600 "$config_file" log_success "Configuration saved to $config_file" } # Setup database setup_database() { log_info "Setting up database configuration..." local db_path="$PROJECT_ROOT/db/ginxsom.db" if [ ! -f "$db_path" ]; then log_warning "Database not found at $db_path" log_warning "Please ensure the database is initialized before starting the server" return fi # Insert admin configuration into database sqlite3 "$db_path" << EOF INSERT OR REPLACE INTO server_config (key, value, description) VALUES ('admin_pubkey', '$ADMIN_PUBKEY', 'Admin public key from setup wizard'), ('admin_enabled', 'true', 'Enable admin interface'); EOF if [ $? -eq 0 ]; then log_success "Database configuration updated" else log_warning "Failed to update database (this is OK if database doesn't exist yet)" fi } # Display setup summary show_setup_summary() { echo "" echo "=================================================================" echo " GINXSOM SETUP COMPLETE" echo "=================================================================" echo "" log_success "Configuration file created: $CONFIG_PATH" log_success "Admin keys saved: $PROJECT_ROOT/.admin_keys" echo "" echo "Next steps:" echo "1. Start the Ginxsom server:" echo " cd $PROJECT_ROOT" echo " make run" echo "" echo "2. Test admin API access:" echo " source .admin_keys" echo " ./scripts/test_admin.sh" echo "" echo "3. Access web admin (when implemented):" echo " http://localhost:9001/admin" echo "" log_warning "Keep the .admin_keys file secure - it contains your admin private key!" echo "" } # Main setup workflow main() { echo "=== Ginxsom Interactive Setup Wizard ===" echo "" # Validate config path if [ -z "$CONFIG_PATH" ]; then # Determine default config path if [ -n "$XDG_CONFIG_HOME" ]; then CONFIG_PATH="$XDG_CONFIG_HOME/ginxsom/ginxsom_config_event.json" else CONFIG_PATH="$HOME/.config/ginxsom/ginxsom_config_event.json" fi fi log_info "Configuration will be saved to: $CONFIG_PATH" # Check if config already exists if [ -f "$CONFIG_PATH" ]; then log_warning "Configuration file already exists at $CONFIG_PATH" echo -n "Overwrite existing configuration? (y/n): " read -r OVERWRITE case "$OVERWRITE" in [Yy]*) ;; *) log_info "Setup cancelled"; exit 0 ;; esac fi # Run setup steps check_dependencies setup_keys collect_configuration create_config_event save_config_file "$CONFIG_PATH" setup_database show_setup_summary } # Allow sourcing for testing individual functions if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi