diff --git a/first_time_install.sh b/first_time_install.sh index 09b55d09..ffaf021b 100644 --- a/first_time_install.sh +++ b/first_time_install.sh @@ -239,11 +239,20 @@ echo "" if [ "$ASSUME_YES" = "1" ]; then echo "Non-interactive mode: proceeding with installation." else - read -p "Do you want to proceed with the installation? (y/N): " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Installation cancelled." - exit 0 + # Check if stdin is available (not running via pipe/curl) + if [ -t 0 ]; then + read -p "Do you want to proceed with the installation? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Installation cancelled." + exit 0 + fi + else + # Non-interactive mode but ASSUME_YES not set - exit with error + echo "✗ Non-interactive mode detected but ASSUME_YES not set." >&2 + echo " Please run with -y flag or set LEDMATRIX_ASSUME_YES=1" >&2 + echo " Example: sudo ./first_time_install.sh -y" >&2 + exit 1 fi fi @@ -973,28 +982,38 @@ if [ -f "$PROJECT_ROOT_DIR/scripts/install/install_wifi_monitor.sh" ]; then if [ ! -f "/etc/systemd/system/ledmatrix-wifi-monitor.service" ] || [ "$NEEDS_UPDATE" = true ]; then echo "Installing/updating WiFi monitor service..." - bash "$PROJECT_ROOT_DIR/scripts/install/install_wifi_monitor.sh" + # Run install script but don't fail installation if it errors (WiFi monitor is optional) + if bash "$PROJECT_ROOT_DIR/scripts/install/install_wifi_monitor.sh"; then + echo "✓ WiFi monitor service installation completed" + else + INSTALL_EXIT_CODE=$? + echo "⚠ WiFi monitor service installation returned exit code $INSTALL_EXIT_CODE" + echo " Continuing installation - WiFi monitor is optional and can be installed later" + fi + fi # Harden service file permissions (if service was created) if [ -f "/etc/systemd/system/ledmatrix-wifi-monitor.service" ]; then chown root:root "/etc/systemd/system/ledmatrix-wifi-monitor.service" || true chmod 644 "/etc/systemd/system/ledmatrix-wifi-monitor.service" || true systemctl daemon-reload || true - fi - - # Check if service was installed successfully - if systemctl list-unit-files | grep -q "ledmatrix-wifi-monitor.service"; then - echo "✓ WiFi monitor service installed" - # Check if service is running - if systemctl is-active --quiet ledmatrix-wifi-monitor.service 2>/dev/null; then - echo "✓ WiFi monitor service is running" + # Check if service was installed successfully + if systemctl list-unit-files | grep -q "ledmatrix-wifi-monitor.service"; then + echo "✓ WiFi monitor service installed" + + # Check if service is running + if systemctl is-active --quiet ledmatrix-wifi-monitor.service 2>/dev/null; then + echo "✓ WiFi monitor service is running" + else + echo "⚠ WiFi monitor service installed but not running (may need required packages)" + fi else - echo "⚠ WiFi monitor service installed but not running (may need required packages)" + echo "⚠ WiFi monitor service file exists but not registered with systemd" fi else - echo "⚠ WiFi monitor service installation may have failed" - fi + echo "⚠ WiFi monitor service file not created (installation may have failed)" + echo " You can install it later by running: sudo ./scripts/install/install_wifi_monitor.sh" fi else echo "⚠ install_wifi_monitor.sh not found; skipping WiFi monitor installation" diff --git a/scripts/install/debug_install.sh b/scripts/install/debug_install.sh new file mode 100755 index 00000000..7f094217 --- /dev/null +++ b/scripts/install/debug_install.sh @@ -0,0 +1,85 @@ +#!/bin/bash +# Quick diagnostic script to check why first_time_install.sh is failing +# Run this on the Pi: bash debug_install.sh + +echo "=== Diagnostic Script for Installation Failure ===" +echo "" + +echo "1. Checking if running as root:" +if [ "$EUID" -eq 0 ]; then + echo " ✓ Running as root (EUID=$EUID)" +else + echo " ✗ NOT running as root (EUID=$EUID, user=$(whoami))" +fi +echo "" + +echo "2. Checking if first_time_install.sh exists:" +if [ -f "./first_time_install.sh" ]; then + echo " ✓ Found ./first_time_install.sh" + echo " Checking if executable:" + if [ -x "./first_time_install.sh" ]; then + echo " ✓ Is executable" + else + echo " ✗ NOT executable (fix with: chmod +x first_time_install.sh)" + fi +else + echo " ✗ NOT found in current directory" + echo " Current directory: $(pwd)" +fi +echo "" + +echo "3. Testing argument passing with -y flag:" +echo " Running: bash ./first_time_install.sh -y --help 2>&1 | head -20" +if [ -f "./first_time_install.sh" ]; then + bash ./first_time_install.sh -y --help 2>&1 | head -20 || echo " ✗ Script failed or not found" +else + echo " ✗ first_time_install.sh not found" +fi +echo "" + +echo "4. Checking environment variable:" +echo " LEDMATRIX_ASSUME_YES=${LEDMATRIX_ASSUME_YES:-not set}" +echo " Testing with env: env LEDMATRIX_ASSUME_YES=1 bash -c 'echo ASSUME_YES would be set'" +env LEDMATRIX_ASSUME_YES=1 bash -c 'echo " ASSUME_YES would be: ${LEDMATRIX_ASSUME_YES:-not set}"' +echo "" + +echo "5. Testing sudo with arguments:" +echo " Command: sudo -E env LEDMATRIX_ASSUME_YES=1 bash ./first_time_install.sh -y --help 2>&1 | head -20" +if [ -f "./first_time_install.sh" ]; then + sudo -E env LEDMATRIX_ASSUME_YES=1 bash ./first_time_install.sh -y --help 2>&1 | head -20 || echo " ✗ Sudo command failed" +else + echo " ✗ first_time_install.sh not found" +fi +echo "" + +echo "6. Checking /tmp permissions:" +echo " /tmp is writable: $([ -w /tmp ] && echo 'YES' || echo 'NO')" +echo " /tmp permissions: $(stat -c '%a' /tmp 2>/dev/null || echo 'unknown')" +echo " TMPDIR: ${TMPDIR:-not set}" +echo "" + +echo "7. Checking stdin/TTY:" +if [ -t 0 ]; then + echo " ✓ stdin is a TTY (interactive)" +else + echo " ✗ stdin is NOT a TTY (non-interactive/pipe)" + echo " This is expected when running via curl | bash" +fi +echo "" + +echo "8. Latest installation log:" +# Determine project root directory (parent of scripts/install/) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" +LOG_DIR="$PROJECT_ROOT_DIR/logs" +LOG_FILE=$(ls -t "$LOG_DIR"/first_time_install_*.log 2>/dev/null | head -1) +if [ -n "$LOG_FILE" ]; then + echo " Found: $LOG_FILE" + echo " Last 30 lines:" + tail -30 "$LOG_FILE" | sed 's/^/ /' +else + echo " No log files found in $LOG_DIR/" +fi +echo "" + +echo "=== Diagnostic Complete ===" diff --git a/scripts/install/install_wifi_monitor.sh b/scripts/install/install_wifi_monitor.sh index 58855ebe..5d224c03 100755 --- a/scripts/install/install_wifi_monitor.sh +++ b/scripts/install/install_wifi_monitor.sh @@ -40,20 +40,28 @@ if ! command -v nmcli >/dev/null 2>&1 && ! command -v iwlist >/dev/null 2>&1; th fi if [ ${#MISSING_PACKAGES[@]} -gt 0 ]; then - echo "⚠ The following packages are required for WiFi setup:" + echo "Installing required packages for WiFi setup:" for pkg in "${MISSING_PACKAGES[@]}"; do echo " - $pkg" done echo "" - read -p "Install these packages now? (y/N): " -n 1 -r - echo "" - if [[ $REPLY =~ ^[Yy]$ ]]; then - sudo apt update - sudo apt install -y "${MISSING_PACKAGES[@]}" - echo "✓ Packages installed" + + # Install packages automatically (no prompt) + # Use apt directly if running as root, otherwise use sudo + if [ "$EUID" -eq 0 ]; then + apt update || echo "⚠ apt update failed, continuing anyway..." + apt install -y "${MISSING_PACKAGES[@]}" || { + echo "⚠ Package installation failed, but continuing with WiFi monitor setup" + echo " You may need to install packages manually: apt install -y ${MISSING_PACKAGES[*]}" + } else - echo "⚠ Skipping package installation. WiFi setup may not work correctly." + sudo apt update || echo "⚠ apt update failed, continuing anyway..." + sudo apt install -y "${MISSING_PACKAGES[@]}" || { + echo "⚠ Package installation failed, but continuing with WiFi monitor setup" + echo " You may need to install packages manually: sudo apt install -y ${MISSING_PACKAGES[*]}" + } fi + echo "✓ Package installation completed" fi # Create service file with correct paths @@ -81,7 +89,11 @@ WantedBy=multi-user.target EOF ) -echo "$SERVICE_FILE_CONTENT" | sudo tee /etc/systemd/system/ledmatrix-wifi-monitor.service > /dev/null +if [ "$EUID" -eq 0 ]; then + echo "$SERVICE_FILE_CONTENT" | tee /etc/systemd/system/ledmatrix-wifi-monitor.service > /dev/null +else + echo "$SERVICE_FILE_CONTENT" | sudo tee /etc/systemd/system/ledmatrix-wifi-monitor.service > /dev/null +fi # Check WiFi connection status before enabling service echo "" @@ -142,36 +154,52 @@ if [ "$WIFI_CONNECTED" = false ] && [ "$ETHERNET_CONNECTED" = false ]; then echo " 2. Or connect via Ethernet cable" echo " 3. Or proceed with installation - you can connect to LEDMatrix-Setup AP after reboot" echo "" - if [ -z "${ASSUME_YES:-}" ] && [ -z "${LEDMATRIX_ASSUME_YES:-}" ]; then - read -p "Continue with WiFi monitor installation? (y/N): " -n 1 -r - echo "" - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Installation cancelled. Connect to WiFi/Ethernet and run this script again." - exit 0 - fi - fi + echo "Proceeding with WiFi monitor installation..." + echo " (WiFi monitor will enable AP mode if no network connection is detected)" fi # Reload systemd echo "" echo "Reloading systemd..." -sudo systemctl daemon-reload +if [ "$EUID" -eq 0 ]; then + systemctl daemon-reload +else + sudo systemctl daemon-reload +fi # Enable and start the service echo "Enabling WiFi monitor service to start on boot..." -sudo systemctl enable ledmatrix-wifi-monitor.service +if [ "$EUID" -eq 0 ]; then + systemctl enable ledmatrix-wifi-monitor.service +else + sudo systemctl enable ledmatrix-wifi-monitor.service +fi echo "Starting WiFi monitor service..." -sudo systemctl start ledmatrix-wifi-monitor.service +if [ "$EUID" -eq 0 ]; then + systemctl start ledmatrix-wifi-monitor.service || echo "⚠ Failed to start service (may start on reboot)" +else + sudo systemctl start ledmatrix-wifi-monitor.service || echo "⚠ Failed to start service (may start on reboot)" +fi # Check service status echo "" echo "Checking service status..." -if sudo systemctl is-active --quiet ledmatrix-wifi-monitor.service; then +if [ "$EUID" -eq 0 ]; then + SYSTEMCTL_CMD="systemctl" +else + SYSTEMCTL_CMD="sudo systemctl" +fi + +if $SYSTEMCTL_CMD is-active --quiet ledmatrix-wifi-monitor.service 2>/dev/null; then echo "✓ WiFi monitor service is running" else echo "⚠ WiFi monitor service failed to start. Check logs with:" - echo " sudo journalctl -u ledmatrix-wifi-monitor -n 50" + if [ "$EUID" -eq 0 ]; then + echo " journalctl -u ledmatrix-wifi-monitor -n 50" + else + echo " sudo journalctl -u ledmatrix-wifi-monitor -n 50" + fi fi echo "" diff --git a/scripts/install/one-shot-install.sh b/scripts/install/one-shot-install.sh index abe5456c..6d9ebc02 100755 --- a/scripts/install/one-shot-install.sh +++ b/scripts/install/one-shot-install.sh @@ -29,6 +29,7 @@ on_error() { echo " - Verify sudo access: sudo -v" >&2 echo " - Check disk space: df -h /" >&2 echo " - If APT lock error: sudo dpkg --configure -a" >&2 + echo " - If /tmp permission error: sudo chmod 1777 /tmp" >&2 echo " - Wait a few minutes and try again" >&2 echo "" >&2 echo "This script is safe to run multiple times. You can re-run it to continue." >&2 @@ -105,10 +106,7 @@ check_network() { print_error "No internet connectivity detected" echo "" - echo "Please ensure your Raspberry Pi is connected to the internet:" - echo " 1. Check WiFi/Ethernet connection" - echo " 2. Test manually: ping -c1 8.8.8.8" - echo " 3. Then re-run this installation script" + echo "Please ensure your Raspberry Pi is connected to the internet and try again." exit 1 } @@ -140,49 +138,17 @@ check_disk_space() { fi } -# Check for curl or wget, install if missing -ensure_download_tool() { - CURRENT_STEP="Download tool check" - if command -v curl >/dev/null 2>&1; then - print_success "curl is available" - return 0 - fi - - if command -v wget >/dev/null 2>&1; then - print_success "wget is available" - return 0 - fi - - print_warning "Neither curl nor wget found, installing curl..." - - # Try to install curl (may fail if not sudo, but we'll check sudo next) - if command -v apt-get >/dev/null 2>&1; then - print_step "Installing curl..." - if [ "$EUID" -eq 0 ]; then - retry apt-get update - retry apt-get install -y curl - print_success "curl installed successfully" - else - print_error "Need sudo to install curl. Please run: sudo apt-get update && sudo apt-get install -y curl" - echo "Then re-run this installation script." - exit 1 - fi - else - print_error "Cannot install curl: apt-get not available" - exit 1 - fi -} - -# Check and elevate to sudo if needed +# Ensure sudo access check_sudo() { - CURRENT_STEP="Privilege check" + CURRENT_STEP="Sudo access check" + print_step "Checking sudo access..." + + # Check if running as root if [ "$EUID" -eq 0 ]; then - print_success "Running with root privileges" + print_success "Running as root" return 0 fi - print_warning "Script needs administrator privileges" - # Check if sudo is available if ! command -v sudo >/dev/null 2>&1; then print_error "sudo is not available and script is not running as root" @@ -205,19 +171,24 @@ check_sudo() { print_success "Sudo access confirmed" } -# Check if running on Raspberry Pi (warning only, don't fail) -check_raspberry_pi() { - CURRENT_STEP="Hardware check" - if [ -r /proc/device-tree/model ]; then - DEVICE_MODEL=$(tr -d '\0' /dev/null || true else - print_warning "Not running on Raspberry Pi hardware: $DEVICE_MODEL" - print_warning "LED matrix functionality requires Raspberry Pi hardware" + sudo chmod 1777 /tmp 2>/dev/null || true fi - else - print_warning "Could not detect device model (continuing anyway)" + fi + + # Ensure TMPDIR is set correctly + if [ -z "${TMPDIR:-}" ] || [ ! -w "${TMPDIR:-/tmp}" ]; then + export TMPDIR=/tmp fi } @@ -226,84 +197,37 @@ main() { print_step "LED Matrix One-Shot Installation" echo "This script will:" - echo " 1. Check system prerequisites" - echo " 2. Install required system packages" - echo " 3. Clone or update the LEDMatrix repository" - echo " 4. Run the full installation script" + echo " 1. Check prerequisites (network, disk space, sudo)" + echo " 2. Install system dependencies (git, python3, build tools)" + echo " 3. Clone the LEDMatrix repository" + echo " 4. Run the first-time installation script" echo "" - # Prerequisites checks + # Check prerequisites check_network check_disk_space - check_raspberry_pi - ensure_download_tool check_sudo + # Note: /tmp permissions are checked and fixed inline before running first_time_install.sh + # (only if actually wrong, not preemptively) - # Install system prerequisites - CURRENT_STEP="System package installation" - print_step "Installing system prerequisites..." - - # Update package list - print_success "Updating package list..." - if [ "$EUID" -eq 0 ]; then - retry apt-get update - else - retry sudo apt-get update - fi - - # Install required packages - PACKAGES=( - "git" - "python3-pip" - "cython3" - "build-essential" - "python3-dev" - "python3-pillow" - "scons" - ) - - print_success "Installing required packages..." - for pkg in "${PACKAGES[@]}"; do - print_success "Installing $pkg..." - if [ "$EUID" -eq 0 ]; then - retry apt-get install -y "$pkg" - else - retry sudo apt-get install -y "$pkg" - fi - done - - # Repository cloning/updating - CURRENT_STEP="Repository setup" - print_step "Setting up LEDMatrix repository..." - - REPO_DIR="$HOME/LEDMatrix" + # Determine repository location + REPO_DIR="${HOME}/LEDMatrix" REPO_URL="https://github.com/ChuckBuilds/LEDMatrix.git" + CURRENT_STEP="Repository setup" + print_step "Setting up repository..." + + # Check if directory exists and handle accordingly if [ -d "$REPO_DIR" ]; then - print_warning "Directory $REPO_DIR already exists" - - # Check if it's a valid git repository if [ -d "$REPO_DIR/.git" ]; then - print_success "Valid git repository found, updating..." + print_warning "Repository already exists at $REPO_DIR" + print_warning "Pulling latest changes..." cd "$REPO_DIR" - - # Check if we can pull (may fail if there are local changes) - if git fetch >/dev/null 2>&1 && git status >/dev/null 2>&1; then - # Check for local modifications - if [ -z "$(git status --porcelain)" ]; then - print_success "Pulling latest changes..." - retry git pull || print_warning "Could not pull latest changes (continuing with existing code)" - else - print_warning "Repository has local modifications, skipping pull" - print_warning "Using existing repository state" - fi + if git pull origin main >/dev/null 2>&1; then + print_success "Repository updated successfully" else - print_warning "Git repository appears corrupted or has issues" - print_warning "Attempting to re-clone..." - cd "$HOME" - rm -rf "$REPO_DIR" - print_success "Cloning fresh repository..." - retry git clone "$REPO_URL" "$REPO_DIR" + print_warning "Git pull failed, but continuing with existing repository" + print_warning "You may have local changes or the repository may be on a different branch" fi else print_warning "Directory exists but is not a git repository" @@ -344,14 +268,35 @@ main() { print_success "Starting main installation (this may take 10-30 minutes)..." echo "" - # Execute with proper error handling + # Execute with proper error handling and non-interactive mode # Temporarily disable errexit to capture exit code instead of exiting immediately set +e - # Use sudo if we're not root, otherwise run directly + + # Check /tmp permissions - only fix if actually wrong (common in automated scenarios) + # When running manually, /tmp usually has correct permissions (1777) + TMP_PERMS=$(stat -c '%a' /tmp 2>/dev/null || echo "unknown") + if [ "$TMP_PERMS" != "1777" ] && [ "$TMP_PERMS" != "unknown" ]; then + CURRENT_STEP="Fixing /tmp permissions" + print_warning "/tmp has incorrect permissions ($TMP_PERMS), fixing to 1777..." + if [ "$EUID" -eq 0 ]; then + chmod 1777 /tmp 2>/dev/null || print_warning "Failed to fix /tmp permissions, continuing anyway..." + else + sudo chmod 1777 /tmp 2>/dev/null || print_warning "Failed to fix /tmp permissions, continuing anyway..." + fi + fi + + # Execute main installation script with non-interactive mode + CURRENT_STEP="Main installation" + export TMPDIR=/tmp if [ "$EUID" -eq 0 ]; then - bash ./first_time_install.sh + # Run in non-interactive mode with ASSUME_YES (both -y flag and env var for safety) + export LEDMATRIX_ASSUME_YES=1 + bash ./first_time_install.sh -y else - sudo bash ./first_time_install.sh + # Pass both -y flag AND environment variable for non-interactive mode + # This ensures it works even if the script re-executes itself with sudo + # Also ensure stdin is properly handled for non-interactive mode + sudo -E env TMPDIR=/tmp LEDMATRIX_ASSUME_YES=1 bash ./first_time_install.sh -y {combined_value}") diff --git a/web_interface/static/v3/plugins_manager.js b/web_interface/static/v3/plugins_manager.js index 2f13ac25..7702addf 100644 --- a/web_interface/static/v3/plugins_manager.js +++ b/web_interface/static/v3/plugins_manager.js @@ -2475,7 +2475,7 @@ function flattenConfig(obj, prefix = '') { // Helper function to render a single item in an array of objects function renderArrayObjectItem(fieldId, fullKey, itemProperties, itemValue, index, itemsSchema) { const item = itemValue || {}; - const itemId = `${fieldId}_item_${index}`; + const itemId = `${escapeAttribute(fieldId)}_item_${index}`; let html = `
`; // Render each property of the object @@ -2502,26 +2502,31 @@ function renderArrayObjectItem(fieldId, fullKey, itemProperties, itemValue, inde html += `

${escapeHtml(propDescription)}

`; } const uploadConfig = propSchema['x-upload-config'] || {}; - const pluginId = uploadConfig.plugin_id || (typeof currentPluginConfig !== 'undefined' ? currentPluginConfig?.pluginId : null) || (typeof window.currentPluginConfig !== 'undefined' ? window.currentPluginConfig?.pluginId : null) || 'ledmatrix-news'; + // Remove hardcoded fallback - require explicit pluginId to avoid surprising defaults + const pluginId = uploadConfig.plugin_id || (typeof currentPluginConfig !== 'undefined' ? currentPluginConfig?.pluginId : null) || (typeof window.currentPluginConfig !== 'undefined' ? window.currentPluginConfig?.pluginId : null) || null; const logoValue = propValue || {}; // Display existing logo if present, but disable upload functionality + // Store file metadata in data-file-data attribute for serialization if (logoValue.path) { + // Use base64 encoding for JSON in data attributes to safely handle all characters + const fileDataJson = JSON.stringify(logoValue); + const fileDataBase64 = btoa(unescape(encodeURIComponent(fileDataJson))); html += ` -
+
- Logo + Logo File upload not yet available for array items
`; } else { html += ` -
+

File upload functionality for array items is coming soon

@@ -2535,43 +2540,46 @@ function renderArrayObjectItem(fieldId, fullKey, itemProperties, itemValue, inde html += ` `; } else { // Regular text/string input html += ` -
`; }); + // Use schema-driven label for remove button, fallback to generic "Remove item" + const removeLabel = itemsSchema['x-removeLabel'] || 'Remove item'; html += `
`; @@ -3017,6 +3025,7 @@ function generateFieldHtml(key, prop, value, prefix = '') {