secubox-openwrt/luci-app-system-hub/CODEX-v0.3.5.md
CyberMind-FR 94bc005ec0 docs(system-hub): Add comprehensive v0.3.5 implementation codex
- Complete analysis of current state (80% production-ready)
- Detailed implementation plan for 7 phases over 9 weeks
- Real code examples for diagnostics, remote mgmt, email, reports
- Unit tests and integration test scenarios
- Pre-release checklist and success metrics

Goals:
- Implement diagnostics collection and upload (Phase 1)
- Add RustDesk remote management integration (Phase 2)
- Email notifications with SMTP (Phase 3)
- HTML/PDF report generation (Phase 4)
- Scheduled tasks via cron (Phase 5)
- Enhanced network monitoring (DNS, NTP, firewall) (Phase 6)
- Hostname editing and archive management (Phase 7)

Target: 95-100% feature completion by 2025-01-20
2025-12-28 14:20:32 +01:00

43 KiB

System Hub v0.3.5 Development Codex

Target Version: 0.3.5 Current Version: 0.3.2 Release Date: 2025-01-20 Priority: High - Production Feature Completion Maintainer: CyberMind contact@cybermind.fr


🎯 Mission Statement

Transform luci-app-system-hub from an 80% production-ready monitoring dashboard into a 100% feature-complete system control center by implementing real diagnostics collection, remote management integration, email notifications, report generation, and scheduled task execution.


📊 Current State Analysis (v0.3.2)

What's Production-Ready (80%)

Core Monitoring:

  • Real-time system health monitoring (CPU, memory, disk, temperature)
  • Network status (WAN connectivity, RX/TX bytes)
  • Service enumeration and management (start/stop/restart/enable/disable)
  • System information display (hostname, kernel, uptime, architecture)
  • Auto-refresh polling (30-second intervals)

System Management:

  • Configuration backup/restore via sysupgrade
  • System reboot functionality
  • Log viewing with filtering (logread integration)
  • Storage information display (df command)
  • Settings persistence to UCI

Integration:

  • SecuBox component discovery
  • Theme management
  • Development status widget

What's Placeholder (20%)

Diagnostics (diagnostics.js):

  • Diagnostic collection - UI ready, no backend implementation
  • Quick tests - Hardcoded simulated results
  • Archive generation - No actual file creation
  • Upload to support server - No backend logic

Remote Management (remote.js):

  • RustDesk integration - UI only, no service control
  • Remote session management - Placeholder interface
  • SSH connection automation - Display only

Notifications & Reporting:

  • Email notifications - Button shows "coming soon"
  • PDF export - UI only
  • Report generation - Not implemented

System Configuration:

  • Hostname editing - Notification says "feature coming soon"
  • Scheduled tasks - Toggles saved but no cron execution
  • Upload to support server - Fields saved but no upload

Network Monitoring:

  • DNS server discovery - Hardcoded 8.8.8.8 / 8.8.4.4
  • DNS query statistics - Field displays but no data
  • NTP sync status - Hardcoded placeholder data
  • Firewall rules count - Returns 0, displays placeholder

🚀 Version 0.3.5 Goals

Primary Objectives

  1. Implement Real Diagnostics Collection (Critical - 30% of v0.3.5)

    • Collect system logs, configuration, network info
    • Generate diagnostic archives (.tar.gz)
    • Implement quick diagnostic tests (connectivity, DNS, latency, disk I/O)
    • Upload diagnostics to support server
  2. Implement Remote Management (High - 20% of v0.3.5)

    • RustDesk service integration (install, configure, start/stop)
    • Generate RustDesk ID and password
    • SSH connection string generation
    • Remote session logging
  3. Implement Email Notifications (High - 20% of v0.3.5)

    • SMTP configuration and testing
    • Send health reports via email
    • Send diagnostic archives via email
    • Alert notifications for critical events
  4. Implement Report Generation (Medium - 15% of v0.3.5)

    • Generate HTML/PDF health reports
    • System inventory reports
    • Service status reports
    • Export functionality
  5. Implement Scheduled Tasks (Medium - 10% of v0.3.5)

    • Cron job creation for health reports
    • Weekly backup automation
    • Log cleanup scheduling
    • Diagnostic upload scheduling
  6. Enhanced Network Monitoring (Medium - 5% of v0.3.5)

    • Real DNS server discovery from resolv.conf
    • DNS query statistics via dnsmasq logs
    • NTP sync status from ntpd/chronyd
    • Firewall rules count from iptables

Success Criteria

  • All 14 RPCD methods have real implementations (no placeholders)
  • Diagnostics can be collected, archived, and uploaded
  • Email notifications work with SMTP configuration
  • HTML/PDF reports can be generated and downloaded
  • Scheduled tasks execute via cron jobs
  • Network monitoring shows real DNS/NTP/firewall data
  • RustDesk integration allows remote support access
  • All UI buttons trigger actual backend actions
  • No "coming soon" notifications remain
  • Integration tests pass for all features

🔧 Technical Implementation Plan

Phase 1: Diagnostics Collection (Week 1-2)

Task 1.1: Real Diagnostic Collection

File: root/usr/libexec/rpcd/luci.system-hub Method: collect_diagnostics() (new)

Implementation:

collect_diagnostics() {
    # Read parameters from JSON input
    json_load "$input"
    json_get_var include_logs "include_logs"
    json_get_var include_config "include_config"
    json_get_var include_network "include_network"
    json_get_var anonymize "anonymize"

    # Create temporary directory
    local diag_dir="/tmp/diagnostics-$(date +%s)"
    mkdir -p "$diag_dir"

    # Collect system info
    echo "=== System Information ===" > "$diag_dir/sysinfo.txt"
    uname -a >> "$diag_dir/sysinfo.txt"
    cat /proc/cpuinfo >> "$diag_dir/sysinfo.txt"
    cat /proc/meminfo >> "$diag_dir/sysinfo.txt"
    df -h >> "$diag_dir/sysinfo.txt"
    uptime >> "$diag_dir/sysinfo.txt"

    # Collect logs if requested
    if [ "$include_logs" = "1" ]; then
        logread > "$diag_dir/system.log"
        dmesg > "$diag_dir/kernel.log"
    fi

    # Collect configuration if requested
    if [ "$include_config" = "1" ]; then
        mkdir -p "$diag_dir/config"
        sysupgrade -b "$diag_dir/config/backup.tar.gz" 2>/dev/null

        # Copy sanitized UCI configs
        for conf in /etc/config/*; do
            if [ "$anonymize" = "1" ]; then
                # Remove sensitive data (passwords, keys, IPs)
                grep -v -E "(password|key|secret|ip|addr)" "$conf" > "$diag_dir/config/$(basename $conf)"
            else
                cp "$conf" "$diag_dir/config/"
            fi
        done
    fi

    # Collect network info if requested
    if [ "$include_network" = "1" ]; then
        echo "=== Network Configuration ===" > "$diag_dir/network.txt"
        ip addr >> "$diag_dir/network.txt"
        ip route >> "$diag_dir/network.txt"
        iptables -L -n -v >> "$diag_dir/network.txt"
        cat /etc/resolv.conf >> "$diag_dir/network.txt"

        # Test connectivity
        ping -c 5 8.8.8.8 >> "$diag_dir/network.txt" 2>&1
        ping -c 5 google.com >> "$diag_dir/network.txt" 2>&1
    fi

    # Create archive
    local archive_name="diagnostics-$(hostname)-$(date +%Y%m%d-%H%M%S).tar.gz"
    local archive_path="/tmp/$archive_name"
    tar -czf "$archive_path" -C "$diag_dir" .

    # Calculate size
    local size=$(stat -c%s "$archive_path")

    # Encode to base64 for transmission
    local base64_data=$(base64 "$archive_path")

    # Cleanup
    rm -rf "$diag_dir"

    # Return response
    json_init
    json_add_boolean "success" 1
    json_add_string "filename" "$archive_name"
    json_add_int "size" "$size"
    json_add_string "data" "$base64_data"
    json_dump
    json_cleanup
}

Frontend Integration (diagnostics.js):

handleCollectDiagnostics: function() {
    var options = {
        include_logs: document.querySelector('[name="include_logs"]').checked,
        include_config: document.querySelector('[name="include_config"]').checked,
        include_network: document.querySelector('[name="include_network"]').checked,
        anonymize: document.querySelector('[name="anonymize"]').checked
    };

    return API.collectDiagnostics(options).then(function(result) {
        if (result.success) {
            // Decode base64 and trigger download
            var blob = base64ToBlob(result.data, 'application/gzip');
            var url = URL.createObjectURL(blob);
            var a = document.createElement('a');
            a.href = url;
            a.download = result.filename;
            a.click();
            URL.revokeObjectURL(url);

            ui.addNotification(null, E('p', '✓ Diagnostics collected: ' + result.filename), 'info');
        }
    });
}

Task 1.2: Quick Diagnostic Tests

File: root/usr/libexec/rpcd/luci.system-hub Method: run_diagnostic_test() (new)

Implementation:

run_diagnostic_test() {
    json_load "$input"
    json_get_var test_type "test_type"

    case "$test_type" in
        connectivity)
            # Test WAN connectivity
            if ping -c 1 -W 2 8.8.8.8 >/dev/null 2>&1; then
                local result="✓ WAN connected"
                local status="ok"
            else
                local result="✗ WAN connection failed"
                local status="critical"
            fi

            # Test DNS resolution
            if nslookup google.com >/dev/null 2>&1; then
                result="$result, DNS functional"
            else
                result="$result, DNS failed"
                status="warning"
            fi
            ;;

        dns)
            # DNS resolution test
            local domain="google.com"
            local dns_result=$(nslookup "$domain" 2>&1 | grep "Address" | tail -1 | awk '{print $3}')

            if [ -n "$dns_result" ]; then
                local result="✓ $domain$dns_result"
                local status="ok"
            else
                local result="✗ DNS resolution failed"
                local status="critical"
            fi
            ;;

        latency)
            # Ping latency test
            local ping_result=$(ping -c 5 8.8.8.8 2>&1 | grep "avg" | awk -F'/' '{print $5}')

            if [ -n "$ping_result" ]; then
                local latency_int=$(printf "%.0f" "$ping_result")
                local threshold=100

                if [ "$latency_int" -lt "$threshold" ]; then
                    local result="✓ ${latency_int}ms (threshold: ${threshold}ms)"
                    local status="ok"
                else
                    local result="⚠ ${latency_int}ms (threshold: ${threshold}ms)"
                    local status="warning"
                fi
            else
                local result="✗ Latency test failed"
                local status="critical"
            fi
            ;;

        disk)
            # Disk I/O performance test
            local write_speed=$(dd if=/dev/zero of=/tmp/test.tmp bs=1M count=10 2>&1 | grep "copied" | awk '{print $(NF-1), $NF}')
            local read_speed=$(dd if=/tmp/test.tmp of=/dev/null bs=1M 2>&1 | grep "copied" | awk '{print $(NF-1), $NF}')
            rm -f /tmp/test.tmp

            local result="Write: $write_speed, Read: $read_speed"
            local status="ok"
            ;;

        firewall)
            # Firewall rules count
            local rules_count=$(iptables -L -n | grep -c "^ACCEPT\|^DROP\|^REJECT")

            if [ "$rules_count" -gt 0 ]; then
                local result="✓ $rules_count rules active"
                local status="ok"
            else
                local result="⚠ No firewall rules configured"
                local status="warning"
            fi
            ;;

        wifi)
            # WiFi status
            local radio_count=$(uci show wireless | grep -c "wifi-device")
            local client_count=0

            for iface in /sys/class/net/*; do
                if [ -d "$iface/wireless" ]; then
                    local clients=$(iw dev $(basename $iface) station dump 2>/dev/null | grep -c "Station")
                    client_count=$((client_count + clients))
                fi
            done

            local result="$radio_count radios active, $client_count clients total"
            local status="ok"
            ;;

        *)
            local result="Unknown test type"
            local status="error"
            ;;
    esac

    json_init
    json_add_boolean "success" 1
    json_add_string "result" "$result"
    json_add_string "status" "$status"
    json_dump
    json_cleanup
}

Task 1.3: Upload Diagnostics to Support Server

File: root/usr/libexec/rpcd/luci.system-hub Method: upload_diagnostics() (new)

Implementation:

upload_diagnostics() {
    json_load "$input"
    json_get_var archive_path "archive_path"

    # Load upload settings from UCI
    local upload_url=$(uci -q get system-hub.upload.upload_url)
    local upload_token=$(uci -q get system-hub.upload.upload_token)

    [ -z "$upload_url" ] && {
        json_init
        json_add_boolean "success" 0
        json_add_string "error" "Upload URL not configured"
        json_dump
        json_cleanup
        return 1
    }

    # Upload using curl
    local response=$(curl -s -X POST \
        -H "Authorization: Bearer $upload_token" \
        -F "file=@$archive_path" \
        -F "hostname=$(hostname)" \
        -F "timestamp=$(date +%s)" \
        "$upload_url" 2>&1)

    local curl_exit=$?

    if [ $curl_exit -eq 0 ]; then
        json_init
        json_add_boolean "success" 1
        json_add_string "response" "$response"
        json_dump
        json_cleanup
    else
        json_init
        json_add_boolean "success" 0
        json_add_string "error" "Upload failed: $response"
        json_dump
        json_cleanup
    fi
}

Dependencies:

  • curl package for HTTP uploads
  • ca-certificates for HTTPS support

Phase 2: Remote Management (Week 3)

Task 2.1: RustDesk Service Integration

File: root/usr/libexec/rpcd/luci.system-hub Method: rustdesk_install() (new)

Implementation:

rustdesk_install() {
    # Check if RustDesk is already installed
    if opkg list-installed | grep -q rustdesk; then
        json_init
        json_add_boolean "success" 0
        json_add_string "error" "RustDesk already installed"
        json_dump
        json_cleanup
        return 1
    fi

    # Install RustDesk package
    opkg update
    opkg install rustdesk

    if [ $? -eq 0 ]; then
        json_init
        json_add_boolean "success" 1
        json_add_string "message" "RustDesk installed successfully"
        json_dump
        json_cleanup
    else
        json_init
        json_add_boolean "success" 0
        json_add_string "error" "Installation failed"
        json_dump
        json_cleanup
    fi
}

Method: rustdesk_configure() (new)

rustdesk_configure() {
    json_load "$input"
    json_get_var relay_server "relay_server"
    json_get_var api_server "api_server"
    json_get_var key "key"

    # Create RustDesk configuration
    cat > /etc/rustdesk/config.toml <<EOF
[relay]
server = "$relay_server"

[api]
server = "$api_server"
key = "$key"

[options]
auto_start = true
allow_remote_config = false
EOF

    # Restart service
    /etc/init.d/rustdesk restart

    json_init
    json_add_boolean "success" 1
    json_add_string "message" "RustDesk configured"
    json_dump
    json_cleanup
}

Method: rustdesk_get_id() (new)

rustdesk_get_id() {
    # Get RustDesk ID from service
    local rustdesk_id=$(rustdesk --get-id 2>&1)
    local rustdesk_password=$(rustdesk --password 2>&1)

    json_init
    json_add_boolean "success" 1
    json_add_string "id" "$rustdesk_id"
    json_add_string "password" "$rustdesk_password"
    json_dump
    json_cleanup
}

Method: rustdesk_service_action() (new)

rustdesk_service_action() {
    json_load "$input"
    json_get_var action "action"

    case "$action" in
        start|stop|restart|enable|disable)
            /etc/init.d/rustdesk "$action"
            ;;
        *)
            json_init
            json_add_boolean "success" 0
            json_add_string "error" "Invalid action"
            json_dump
            json_cleanup
            return 1
            ;;
    esac

    json_init
    json_add_boolean "success" 1
    json_add_string "action" "$action"
    json_dump
    json_cleanup
}

Phase 3: Email Notifications (Week 4-5)

Task 3.1: SMTP Configuration

File: root/usr/libexec/rpcd/luci.system-hub Method: configure_smtp() (new)

Implementation:

configure_smtp() {
    json_load "$input"
    json_get_var smtp_server "smtp_server"
    json_get_var smtp_port "smtp_port"
    json_get_var smtp_user "smtp_user"
    json_get_var smtp_password "smtp_password"
    json_get_var from_email "from_email"
    json_get_var use_tls "use_tls"

    # Save to UCI
    uci set system-hub.smtp=smtp
    uci set system-hub.smtp.server="$smtp_server"
    uci set system-hub.smtp.port="$smtp_port"
    uci set system-hub.smtp.user="$smtp_user"
    uci set system-hub.smtp.password="$smtp_password"
    uci set system-hub.smtp.from_email="$from_email"
    uci set system-hub.smtp.use_tls="$use_tls"
    uci commit system-hub

    json_init
    json_add_boolean "success" 1
    json_add_string "message" "SMTP configuration saved"
    json_dump
    json_cleanup
}

Method: test_smtp() (new)

test_smtp() {
    json_load "$input"
    json_get_var to_email "to_email"

    # Load SMTP config from UCI
    local smtp_server=$(uci -q get system-hub.smtp.server)
    local smtp_port=$(uci -q get system-hub.smtp.port)
    local smtp_user=$(uci -q get system-hub.smtp.user)
    local smtp_password=$(uci -q get system-hub.smtp.password)
    local from_email=$(uci -q get system-hub.smtp.from_email)
    local use_tls=$(uci -q get system-hub.smtp.use_tls)

    # Send test email using sendmail or msmtp
    cat > /tmp/test_email.txt <<EOF
From: $from_email
To: $to_email
Subject: SecuBox System Hub - Test Email

This is a test email from SecuBox System Hub.

Hostname: $(hostname)
Date: $(date)
EOF

    if command -v msmtp >/dev/null 2>&1; then
        # Use msmtp
        cat > /tmp/msmtprc <<EOF
account default
host $smtp_server
port $smtp_port
from $from_email
user $smtp_user
password $smtp_password
$([ "$use_tls" = "1" ] && echo "tls on" || echo "tls off")
EOF

        msmtp --file=/tmp/msmtprc "$to_email" < /tmp/test_email.txt
        local exit_code=$?

        rm -f /tmp/msmtprc /tmp/test_email.txt

        if [ $exit_code -eq 0 ]; then
            json_init
            json_add_boolean "success" 1
            json_add_string "message" "Test email sent successfully"
            json_dump
            json_cleanup
        else
            json_init
            json_add_boolean "success" 0
            json_add_string "error" "Failed to send test email"
            json_dump
            json_cleanup
        fi
    else
        json_init
        json_add_boolean "success" 0
        json_add_string "error" "msmtp not installed"
        json_dump
        json_cleanup
    fi
}

Method: send_health_report_email() (new)

send_health_report_email() {
    json_load "$input"
    json_get_var to_email "to_email"

    # Get health data
    local health_json=$(get_health)
    local score=$(echo "$health_json" | jsonfilter -e '@.score')
    local status=$(echo "$health_json" | jsonfilter -e '@.status')

    # Generate email body
    cat > /tmp/health_report.txt <<EOF
From: $(uci -q get system-hub.smtp.from_email)
To: $to_email
Subject: SecuBox Health Report - $(hostname)

System Health Report
====================

Hostname: $(hostname)
Generated: $(date)

Overall Health Score: $score/100
Status: $status

CPU Usage: $(echo "$health_json" | jsonfilter -e '@.cpu.usage')%
Memory Usage: $(echo "$health_json" | jsonfilter -e '@.memory.usage')%
Disk Usage: $(echo "$health_json" | jsonfilter -e '@.disk.usage')%
Temperature: $(echo "$health_json" | jsonfilter -e '@.temperature.value')°C

Network: $(echo "$health_json" | jsonfilter -e '@.network.wan_up' | sed 's/true/Connected/;s/false/Disconnected/')
Services: $(echo "$health_json" | jsonfilter -e '@.services.running') running, $(echo "$health_json" | jsonfilter -e '@.services.failed') failed

Recommendations:
$(echo "$health_json" | jsonfilter -e '@.recommendations[*]' | sed 's/^/- /')

---
Sent from SecuBox System Hub
EOF

    # Send via msmtp
    if command -v msmtp >/dev/null 2>&1; then
        cat > /tmp/msmtprc <<EOF
account default
host $(uci -q get system-hub.smtp.server)
port $(uci -q get system-hub.smtp.port)
from $(uci -q get system-hub.smtp.from_email)
user $(uci -q get system-hub.smtp.user)
password $(uci -q get system-hub.smtp.password)
$([ "$(uci -q get system-hub.smtp.use_tls)" = "1" ] && echo "tls on" || echo "tls off")
EOF

        msmtp --file=/tmp/msmtprc "$to_email" < /tmp/health_report.txt
        local exit_code=$?

        rm -f /tmp/msmtprc /tmp/health_report.txt

        if [ $exit_code -eq 0 ]; then
            json_init
            json_add_boolean "success" 1
            json_add_string "message" "Health report sent to $to_email"
            json_dump
            json_cleanup
        else
            json_init
            json_add_boolean "success" 0
            json_add_string "error" "Failed to send email"
            json_dump
            json_cleanup
        fi
    else
        json_init
        json_add_boolean "success" 0
        json_add_string "error" "msmtp not installed"
        json_dump
        json_cleanup
    fi
}

Dependencies:

  • msmtp package for SMTP email sending
  • ca-certificates for TLS support

Phase 4: Report Generation (Week 6)

Task 4.1: HTML Report Generation

File: root/usr/libexec/rpcd/luci.system-hub Method: generate_html_report() (new)

Implementation:

generate_html_report() {
    # Get health data
    local health_json=$(get_health)
    local sysinfo_json=$(get_system_info)

    # Generate HTML report
    cat > /tmp/system-report.html <<'EOF'
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>SecuBox System Report</title>
    <style>
        body { font-family: 'Inter', sans-serif; max-width: 1200px; margin: 40px auto; padding: 20px; }
        h1 { color: #6366f1; border-bottom: 2px solid #6366f1; padding-bottom: 10px; }
        .metric { display: inline-block; margin: 20px; padding: 20px; border: 1px solid #e2e8f0; border-radius: 8px; }
        .score { font-size: 48px; font-weight: bold; color: #22c55e; }
        .critical { color: #ef4444; }
        table { width: 100%; border-collapse: collapse; margin: 20px 0; }
        th, td { padding: 12px; text-align: left; border-bottom: 1px solid #e2e8f0; }
        th { background: #f8fafc; font-weight: 600; }
    </style>
</head>
<body>
    <h1>SecuBox System Report</h1>
    <p><strong>Hostname:</strong> $(hostname)</p>
    <p><strong>Generated:</strong> $(date)</p>

    <h2>Health Overview</h2>
    <div class="metric">
        <div class="score">$(echo "$health_json" | jsonfilter -e '@.score')/100</div>
        <div>Overall Health</div>
    </div>

    <h2>System Metrics</h2>
    <table>
        <tr><th>Metric</th><th>Value</th><th>Status</th></tr>
        <tr>
            <td>CPU Usage</td>
            <td>$(echo "$health_json" | jsonfilter -e '@.cpu.usage')%</td>
            <td>$(echo "$health_json" | jsonfilter -e '@.cpu.status')</td>
        </tr>
        <tr>
            <td>Memory Usage</td>
            <td>$(echo "$health_json" | jsonfilter -e '@.memory.usage')%</td>
            <td>$(echo "$health_json" | jsonfilter -e '@.memory.status')</td>
        </tr>
        <tr>
            <td>Disk Usage</td>
            <td>$(echo "$health_json" | jsonfilter -e '@.disk.usage')%</td>
            <td>$(echo "$health_json" | jsonfilter -e '@.disk.status')</td>
        </tr>
        <tr>
            <td>Temperature</td>
            <td>$(echo "$health_json" | jsonfilter -e '@.temperature.value')°C</td>
            <td>$(echo "$health_json" | jsonfilter -e '@.temperature.status')</td>
        </tr>
    </table>

    <h2>System Information</h2>
    <table>
        <tr><td>Kernel</td><td>$(echo "$sysinfo_json" | jsonfilter -e '@.kernel')</td></tr>
        <tr><td>Architecture</td><td>$(echo "$sysinfo_json" | jsonfilter -e '@.architecture')</td></tr>
        <tr><td>OpenWrt Version</td><td>$(echo "$sysinfo_json" | jsonfilter -e '@.openwrt_version')</td></tr>
        <tr><td>Uptime</td><td>$(echo "$sysinfo_json" | jsonfilter -e '@.uptime_formatted')</td></tr>
    </table>

    <footer>
        <p style="color: #64748b; font-size: 12px; margin-top: 40px;">
            Generated by SecuBox System Hub v0.3.5
        </p>
    </footer>
</body>
</html>
EOF

    # Encode to base64
    local base64_data=$(base64 /tmp/system-report.html)

    json_init
    json_add_boolean "success" 1
    json_add_string "filename" "system-report-$(date +%Y%m%d-%H%M%S).html"
    json_add_string "data" "$base64_data"
    json_dump
    json_cleanup

    rm -f /tmp/system-report.html
}

Method: generate_pdf_report() (new)

generate_pdf_report() {
    # First generate HTML report
    generate_html_report > /tmp/report.json

    # Extract base64 HTML
    local html_data=$(cat /tmp/report.json | jsonfilter -e '@.data')
    echo "$html_data" | base64 -d > /tmp/system-report.html

    # Convert to PDF using wkhtmltopdf (if installed)
    if command -v wkhtmltopdf >/dev/null 2>&1; then
        wkhtmltopdf /tmp/system-report.html /tmp/system-report.pdf

        # Encode PDF to base64
        local pdf_base64=$(base64 /tmp/system-report.pdf)

        json_init
        json_add_boolean "success" 1
        json_add_string "filename" "system-report-$(date +%Y%m%d-%H%M%S).pdf"
        json_add_string "data" "$pdf_base64"
        json_dump
        json_cleanup

        rm -f /tmp/system-report.html /tmp/system-report.pdf /tmp/report.json
    else
        json_init
        json_add_boolean "success" 0
        json_add_string "error" "wkhtmltopdf not installed"
        json_dump
        json_cleanup
    fi
}

Dependencies:

  • wkhtmltopdf package for PDF generation (optional)

Phase 5: Scheduled Tasks (Week 7)

Task 5.1: Cron Job Management

File: root/usr/libexec/rpcd/luci.system-hub Method: schedule_health_report() (new)

Implementation:

schedule_health_report() {
    json_load "$input"
    json_get_var enabled "enabled"
    json_get_var interval "interval"  # daily, weekly, monthly
    json_get_var email "email"

    # Remove existing cron job
    sed -i '/system-hub-health-report/d' /etc/crontabs/root 2>/dev/null

    if [ "$enabled" = "1" ]; then
        case "$interval" in
            daily)
                local cron_time="0 6 * * *"  # 6 AM daily
                ;;
            weekly)
                local cron_time="0 6 * * 0"  # 6 AM Sunday
                ;;
            monthly)
                local cron_time="0 6 1 * *"  # 6 AM 1st of month
                ;;
            *)
                local cron_time="0 6 * * 0"  # Default to weekly
                ;;
        esac

        # Add cron job
        echo "$cron_time /usr/libexec/system-hub/send-health-report.sh $email # system-hub-health-report" >> /etc/crontabs/root

        # Create helper script
        cat > /usr/libexec/system-hub/send-health-report.sh <<'EOF'
#!/bin/sh
# Send health report via email

to_email="$1"
[ -z "$to_email" ] && exit 1

# Call RPCD method via ubus
ubus call luci.system-hub send_health_report_email "{\"to_email\": \"$to_email\"}"
EOF
        chmod +x /usr/libexec/system-hub/send-health-report.sh

        # Restart cron
        /etc/init.d/cron restart
    fi

    json_init
    json_add_boolean "success" 1
    json_add_string "message" "Health report schedule updated"
    json_dump
    json_cleanup
}

Method: schedule_backup() (new)

schedule_backup() {
    json_load "$input"
    json_get_var enabled "enabled"

    # Remove existing cron job
    sed -i '/system-hub-backup/d' /etc/crontabs/root 2>/dev/null

    if [ "$enabled" = "1" ]; then
        # Weekly backup at 3 AM on Sunday
        local cron_time="0 3 * * 0"

        # Add cron job
        echo "$cron_time /usr/libexec/system-hub/weekly-backup.sh # system-hub-backup" >> /etc/crontabs/root

        # Create helper script
        cat > /usr/libexec/system-hub/weekly-backup.sh <<'EOF'
#!/bin/sh
# Weekly configuration backup

backup_dir="/root/backups"
mkdir -p "$backup_dir"

# Create backup
backup_file="$backup_dir/config-backup-$(date +\%Y\%m\%d-\%H\%M\%S).tar.gz"
sysupgrade -b "$backup_file"

# Keep only last 10 backups
ls -t "$backup_dir"/config-backup-*.tar.gz | tail -n +11 | xargs -r rm
EOF
        chmod +x /usr/libexec/system-hub/weekly-backup.sh

        # Restart cron
        /etc/init.d/cron restart
    fi

    json_init
    json_add_boolean "success" 1
    json_add_string "message" "Backup schedule updated"
    json_dump
    json_cleanup
}

Method: schedule_log_cleanup() (new)

schedule_log_cleanup() {
    json_load "$input"
    json_get_var enabled "enabled"
    json_get_var retention_days "retention_days"

    # Remove existing cron job
    sed -i '/system-hub-log-cleanup/d' /etc/crontabs/root 2>/dev/null

    if [ "$enabled" = "1" ]; then
        # Daily cleanup at 2 AM
        local cron_time="0 2 * * *"

        # Add cron job
        echo "$cron_time /usr/libexec/system-hub/cleanup-logs.sh $retention_days # system-hub-log-cleanup" >> /etc/crontabs/root

        # Create helper script
        cat > /usr/libexec/system-hub/cleanup-logs.sh <<'EOF'
#!/bin/sh
# Cleanup old log files

retention_days="${1:-7}"

# Clean old log files
find /var/log -name "*.log" -mtime +$retention_days -exec rm {} \;
find /tmp -name "*.log" -mtime +$retention_days -exec rm {} \;

# Rotate system log if too large (> 1MB)
logfile="/var/log/messages"
if [ -f "$logfile" ] && [ $(stat -c%s "$logfile") -gt 1048576 ]; then
    mv "$logfile" "$logfile.old"
    touch "$logfile"
fi
EOF
        chmod +x /usr/libexec/system-hub/cleanup-logs.sh

        # Restart cron
        /etc/init.d/cron restart
    fi

    json_init
    json_add_boolean "success" 1
    json_add_string "message" "Log cleanup schedule updated"
    json_dump
    json_cleanup
}

Phase 6: Enhanced Network Monitoring (Week 8)

Task 6.1: Real DNS Server Discovery

File: root/usr/libexec/rpcd/luci.system-hub Method: Update get_health() DNS section

Implementation:

# In get_health() function, replace DNS section with:

# DNS servers from resolv.conf
dns_primary=$(grep "nameserver" /etc/resolv.conf | head -1 | awk '{print $2}')
dns_secondary=$(grep "nameserver" /etc/resolv.conf | sed -n '2p' | awk '{print $2}')

# DNS query statistics from dnsmasq log
if [ -f /var/log/dnsmasq.log ]; then
    dns_queries=$(grep "query" /var/log/dnsmasq.log | wc -l)
else
    # Estimate from dnsmasq cache
    dns_queries=$(dnsmasq --test 2>&1 | grep -o "cache-size=[0-9]*" | cut -d= -f2)
fi

json_add_string "dns_primary" "${dns_primary:-8.8.8.8}"
json_add_string "dns_secondary" "${dns_secondary:-8.8.4.4}"
json_add_int "dns_queries" "${dns_queries:-0}"
json_add_boolean "dns_ok" "$(nslookup google.com >/dev/null 2>&1 && echo 1 || echo 0)"

Task 6.2: Real NTP Sync Status

File: root/usr/libexec/rpcd/luci.system-hub Method: Update get_health() NTP section

Implementation:

# In get_health() function, add NTP section:

# NTP sync status
if command -v ntpd >/dev/null 2>&1; then
    ntp_status=$(ntpq -p 2>/dev/null | grep "^\*" | wc -l)
    ntp_server=$(ntpq -p 2>/dev/null | grep "^\*" | awk '{print $1}' | sed 's/\*//')
    ntp_offset=$(ntpq -p 2>/dev/null | grep "^\*" | awk '{print $9}')
    ntp_last_sync="Just now"
elif command -v chronyd >/dev/null 2>&1; then
    ntp_status=$(chronyc tracking 2>/dev/null | grep "Reference ID" | wc -l)
    ntp_server=$(chronyc sources 2>/dev/null | grep "^\^\*" | awk '{print $2}')
    ntp_offset=$(chronyc tracking 2>/dev/null | grep "System time" | awk '{print $4}')
    ntp_last_sync=$(chronyc sources 2>/dev/null | grep "^\^\*" | awk '{print $5}')
else
    ntp_status=0
    ntp_server="pool.ntp.org"
    ntp_offset="0"
    ntp_last_sync="Unknown"
fi

json_add_string "ntp_server" "$ntp_server"
json_add_string "ntp_offset" "$ntp_offset"
json_add_string "ntp_last_sync" "$ntp_last_sync"
json_add_boolean "ntp_synced" "$ntp_status"

Task 6.3: Real Firewall Rules Count

File: root/usr/libexec/rpcd/luci.system-hub Method: Update get_health() firewall section

Implementation:

# In get_health() function, add firewall section:

# Firewall rules
fw_rules=$(iptables -L -n | grep -c "^ACCEPT\|^DROP\|^REJECT")
fw_input=$(iptables -L INPUT -n | grep "policy" | awk '{print $4}')
fw_forward=$(iptables -L FORWARD -n | grep "policy" | awk '{print $4}')
fw_output=$(iptables -L OUTPUT -n | grep "policy" | awk '{print $4}')

json_add_int "firewall_rules" "$fw_rules"
json_add_string "fw_input" "$fw_input"
json_add_string "fw_forward" "$fw_forward"
json_add_string "fw_output" "$fw_output"

Phase 7: Hostname Editing & Archive Management (Week 9)

Task 7.1: Hostname Editing

File: root/usr/libexec/rpcd/luci.system-hub Method: set_hostname() (new)

Implementation:

set_hostname() {
    json_load "$input"
    json_get_var new_hostname "hostname"

    # Validate hostname
    if ! echo "$new_hostname" | grep -qE '^[a-zA-Z0-9-]+$'; then
        json_init
        json_add_boolean "success" 0
        json_add_string "error" "Invalid hostname format"
        json_dump
        json_cleanup
        return 1
    fi

    # Update /etc/config/system
    uci set system.@system[0].hostname="$new_hostname"
    uci commit system

    # Update /proc/sys/kernel/hostname
    echo "$new_hostname" > /proc/sys/kernel/hostname

    # Update /etc/hosts
    sed -i "s/127.0.1.1.*/127.0.1.1\t$new_hostname/" /etc/hosts

    json_init
    json_add_boolean "success" 1
    json_add_string "hostname" "$new_hostname"
    json_add_string "message" "Hostname updated to $new_hostname"
    json_dump
    json_cleanup
}

Frontend Integration (overview.js):

// Replace "Edit" button click handler:
'click': function() {
    ui.showModal('Edit Hostname', [
        E('p', {}, 'Enter new hostname:'),
        E('input', {
            'type': 'text',
            'class': 'cbi-input-text',
            'id': 'new-hostname',
            'value': self.sysInfo.hostname || '',
            'pattern': '[a-zA-Z0-9-]+',
            'maxlength': '63'
        }),
        E('div', { 'class': 'right' }, [
            E('button', {
                'class': 'btn cbi-button-negative',
                'click': ui.hideModal
            }, 'Cancel'),
            E('button', {
                'class': 'btn cbi-button-positive',
                'click': function() {
                    var newHostname = document.getElementById('new-hostname').value;
                    if (!newHostname.match(/^[a-zA-Z0-9-]+$/)) {
                        ui.addNotification(null, E('p', 'Invalid hostname'), 'error');
                        return;
                    }

                    API.setHostname(newHostname).then(function(result) {
                        if (result.success) {
                            ui.addNotification(null, E('p', '✓ ' + result.message), 'info');
                            ui.hideModal();
                            location.reload();
                        } else {
                            ui.addNotification(null, E('p', '✗ ' + result.error), 'error');
                        }
                    });
                }
            }, 'Save')
        ])
    ]);
}

Task 7.2: Recent Archives List

File: root/usr/libexec/rpcd/luci.system-hub Method: list_recent_archives() (new)

Implementation:

list_recent_archives() {
    local backup_dir="/root/backups"
    mkdir -p "$backup_dir"

    json_init
    json_add_array "archives"

    # List backup files sorted by date
    for archive in $(ls -t "$backup_dir"/config-backup-*.tar.gz 2>/dev/null | head -10); do
        local filename=$(basename "$archive")
        local size=$(stat -c%s "$archive")
        local timestamp=$(stat -c%Y "$archive")
        local date=$(date -d "@$timestamp" "+%Y-%m-%d %H:%M:%S")

        json_add_object
        json_add_string "filename" "$filename"
        json_add_int "size" "$size"
        json_add_string "date" "$date"
        json_add_string "path" "$archive"
        json_close_object
    done

    json_close_array
    json_dump
    json_cleanup
}

🧪 Testing & Validation

Unit Tests

Test File: tests/test-system-hub.sh (new)

#!/bin/sh
# System Hub v0.3.5 Test Suite

test_diagnostics_collection() {
    echo "Testing diagnostics collection..."
    result=$(ubus call luci.system-hub collect_diagnostics '{"include_logs":true,"include_config":true,"include_network":true,"anonymize":false}')
    success=$(echo "$result" | jsonfilter -e '@.success')

    [ "$success" = "true" ] || { echo "FAIL: Diagnostics collection"; return 1; }
    echo "PASS: Diagnostics collected"
}

test_quick_diagnostic_tests() {
    echo "Testing quick diagnostic tests..."

    for test in connectivity dns latency disk firewall wifi; do
        result=$(ubus call luci.system-hub run_diagnostic_test "{\"test_type\":\"$test\"}")
        success=$(echo "$result" | jsonfilter -e '@.success')

        [ "$success" = "true" ] || { echo "FAIL: $test test"; return 1; }
        echo "PASS: $test test"
    done
}

test_smtp_configuration() {
    echo "Testing SMTP configuration..."
    result=$(ubus call luci.system-hub configure_smtp '{"smtp_server":"smtp.gmail.com","smtp_port":"587","smtp_user":"test@example.com","smtp_password":"password","from_email":"test@example.com","use_tls":"1"}')
    success=$(echo "$result" | jsonfilter -e '@.success')

    [ "$success" = "true" ] || { echo "FAIL: SMTP config"; return 1; }
    echo "PASS: SMTP configuration saved"
}

test_html_report_generation() {
    echo "Testing HTML report generation..."
    result=$(ubus call luci.system-hub generate_html_report)
    success=$(echo "$result" | jsonfilter -e '@.success')
    filename=$(echo "$result" | jsonfilter -e '@.filename')

    [ "$success" = "true" ] || { echo "FAIL: HTML report"; return 1; }
    [ -n "$filename" ] || { echo "FAIL: No filename"; return 1; }
    echo "PASS: HTML report generated ($filename)"
}

test_hostname_change() {
    echo "Testing hostname change..."
    old_hostname=$(hostname)
    result=$(ubus call luci.system-hub set_hostname '{"hostname":"test-router"}')
    success=$(echo "$result" | jsonfilter -e '@.success')

    [ "$success" = "true" ] || { echo "FAIL: Hostname change"; return 1; }

    # Restore original hostname
    ubus call luci.system-hub set_hostname "{\"hostname\":\"$old_hostname\"}"
    echo "PASS: Hostname change"
}

test_cron_scheduling() {
    echo "Testing cron scheduling..."
    result=$(ubus call luci.system-hub schedule_health_report '{"enabled":"1","interval":"weekly","email":"admin@example.com"}')
    success=$(echo "$result" | jsonfilter -e '@.success')

    [ "$success" = "true" ] || { echo "FAIL: Cron scheduling"; return 1; }

    # Check if cron job exists
    grep -q "system-hub-health-report" /etc/crontabs/root || { echo "FAIL: Cron job not created"; return 1; }

    echo "PASS: Cron scheduling"
}

test_dns_discovery() {
    echo "Testing DNS server discovery..."
    result=$(ubus call luci.system-hub get_health)
    dns_primary=$(echo "$result" | jsonfilter -e '@.network.dns_primary')

    [ -n "$dns_primary" ] || { echo "FAIL: DNS primary not found"; return 1; }
    echo "PASS: DNS discovery (primary: $dns_primary)"
}

# Run all tests
test_diagnostics_collection
test_quick_diagnostic_tests
test_smtp_configuration
test_html_report_generation
test_hostname_change
test_cron_scheduling
test_dns_discovery

echo ""
echo "Test suite completed."

📋 Pre-Release Checklist

Code Quality

  • All RPCD methods return valid JSON
  • Error handling for missing dependencies
  • Input validation for user-provided data
  • No hardcoded credentials
  • Shellcheck passes on all RPCD scripts
  • No placeholder comments remain

Functionality

  • Diagnostics collection works with all options
  • Quick tests return real results
  • SMTP email sending works
  • HTML/PDF reports generate correctly
  • Cron jobs are created and execute
  • DNS/NTP/firewall data is real
  • Hostname editing works and persists
  • Archive list shows real backup files
  • RustDesk integration installs and configures

Safety

  • No destructive operations without confirmation
  • Config backups created before changes
  • Cron jobs don't overlap or conflict
  • Email passwords stored securely in UCI
  • Diagnostic anonymization removes sensitive data

Documentation

  • README.md updated with new features
  • Dependency list includes new packages
  • Configuration examples provided
  • API documentation for new RPCD methods

🚢 Release Process

Version Bump

# Update Makefile
sed -i 's/PKG_VERSION:=0.3.2/PKG_VERSION:=0.3.5/' luci-app-system-hub/Makefile

# Update README.md
sed -i 's/Version: 0.3.2/Version: 0.3.5/' luci-app-system-hub/README.md

# Update api.js version
sed -i 's/v0.2.2/v0.2.5/' luci-app-system-hub/htdocs/luci-static/resources/system-hub/api.js

Git Workflow

git add luci-app-system-hub/
git commit -m "feat(system-hub): Implement v0.3.5 production features

- Diagnostics: Real collection, archive generation, upload
- Remote: RustDesk service integration and management
- Email: SMTP config, health reports, test emails
- Reports: HTML/PDF generation with system metrics
- Scheduling: Cron jobs for reports, backups, log cleanup
- Network: Real DNS, NTP, firewall monitoring
- Hostname: Edit and persist hostname changes
- Archives: List recent backups with metadata

Closes #XX, #YY, #ZZ"

git tag -a v0.3.5 -m "Release v0.3.5: Feature Completion"
git push origin master --tags

🎓 Implementation Guidelines

Code Style

  • Follow existing RPCD patterns
  • Use jshn.sh for JSON handling
  • Return {"success": true/false} for all methods
  • Log errors to syslog
  • Use UCI for configuration storage

Security Best Practices

  • Validate all user input
  • Escape shell variables: "$var" not $var
  • Use uci -q get to avoid errors
  • Check command existence: command -v cmd >/dev/null
  • Anonymize sensitive data in diagnostics

Performance Considerations

  • Don't block on long-running operations
  • Use background jobs for email/upload
  • Cache diagnostic archives for reuse
  • Clean up temporary files after operations

📊 Success Metrics

Completion Targets:

  • Implementation: 95-100% (up from 80%)
  • Feature Parity: 14/14 RPCD methods fully functional
  • Test Coverage: 90%+ unit tests passing
  • Documentation: 100% of new features documented
  • User Feedback: No "coming soon" notifications

User Impact:

  • One-click diagnostics collection (vs manual log gathering)
  • Automated health reports via email (vs manual checks)
  • Remote support access via RustDesk (vs SSH only)
  • Professional HTML/PDF reports (vs text logs)
  • Scheduled maintenance tasks (vs manual cron editing)

🔗 Dependencies

Required Packages:

  • luci-base
  • rpcd
  • coreutils
  • coreutils-base64

New Optional Packages (v0.3.5):

  • curl - For diagnostics upload
  • msmtp - For SMTP email sending
  • ca-certificates - For HTTPS/TLS support
  • wkhtmltopdf - For PDF report generation (optional)
  • rustdesk - For remote management (optional)

Update Makefile:

LUCI_DEPENDS:=+luci-base +rpcd +coreutils +coreutils-base64 \
    +PACKAGE_luci-app-system-hub-email:msmtp \
    +PACKAGE_luci-app-system-hub-email:ca-certificates \
    +PACKAGE_luci-app-system-hub-remote:rustdesk

Next Steps:

  1. Review this codex with team
  2. Assign tasks to developers
  3. Set up development branches
  4. Begin Phase 1 (Diagnostics) implementation
  5. Weekly progress reviews

Questions/Feedback: Contact maintainers or open GitHub issue


Generated: 2025-12-28 Codex Version: 1.0 Target Release: 2025-01-20