diff --git a/package/secubox/secubox-app-reporter/Makefile b/package/secubox/secubox-app-reporter/Makefile new file mode 100644 index 00000000..8e64eaf7 --- /dev/null +++ b/package/secubox/secubox-app-reporter/Makefile @@ -0,0 +1,51 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=secubox-app-reporter +PKG_VERSION:=1.0.0 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=CyberMind +PKG_LICENSE:=GPL-3.0 + +include $(INCLUDE_DIR)/package.mk + +define Package/secubox-app-reporter + SECTION:=secubox + CATEGORY:=SecuBox + TITLE:=SecuBox Report Generator + DEPENDS:=+secubox-core +curl +msmtp + PKGARCH:=all +endef + +define Package/secubox-app-reporter/description + Generates and distributes SecuBox status reports via HTML and email. + Includes development progress tracking and services distribution status. +endef + +define Package/secubox-app-reporter/conffiles +/etc/config/secubox-reporter +endef + +define Build/Compile +endef + +define Package/secubox-app-reporter/install + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) ./files/etc/config/secubox-reporter $(1)/etc/config/ + + $(INSTALL_DIR) $(1)/etc/cron.d + $(INSTALL_DATA) ./files/etc/cron.d/secubox-reporter $(1)/etc/cron.d/ + + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) ./files/usr/sbin/secubox-reportctl $(1)/usr/sbin/ + + $(INSTALL_DIR) $(1)/usr/share/secubox-reporter/lib + $(INSTALL_DATA) ./files/usr/share/secubox-reporter/lib/*.sh $(1)/usr/share/secubox-reporter/lib/ + + $(INSTALL_DIR) $(1)/usr/share/secubox-reporter/templates + $(INSTALL_DATA) ./files/usr/share/secubox-reporter/templates/*.tpl $(1)/usr/share/secubox-reporter/templates/ + + $(INSTALL_DIR) $(1)/www/reports +endef + +$(eval $(call BuildPackage,secubox-app-reporter)) diff --git a/package/secubox/secubox-app-reporter/files/etc/config/secubox-reporter b/package/secubox/secubox-app-reporter/files/etc/config/secubox-reporter new file mode 100644 index 00000000..05b16dbe --- /dev/null +++ b/package/secubox/secubox-app-reporter/files/etc/config/secubox-reporter @@ -0,0 +1,26 @@ +config global 'global' + option enabled '1' + option theme 'dark' + option output_dir '/www/reports' + option retention_days '30' + +config email 'email' + option enabled '0' + option recipient '' + option smtp_server '' + option smtp_port '587' + option smtp_user '' + option smtp_password '' + option smtp_tls '1' + +config schedule 'schedule' + option dev 'off' + option services 'off' + +config sources 'sources' + option repo_path '/root/secubox-openwrt' + option history_file '.claude/HISTORY.md' + option wip_file '.claude/WIP.md' + option todo_file '.claude/TODO.md' + option roadmap_file '.claude/ROADMAP.md' + option devstatus_file '.claude/DEV-STATUS.md' diff --git a/package/secubox/secubox-app-reporter/files/etc/cron.d/secubox-reporter b/package/secubox/secubox-app-reporter/files/etc/cron.d/secubox-reporter new file mode 100644 index 00000000..86d1569f --- /dev/null +++ b/package/secubox/secubox-app-reporter/files/etc/cron.d/secubox-reporter @@ -0,0 +1,11 @@ +# SecuBox Report Generator - Scheduled Reports +# Managed by: secubox-reportctl schedule + +# Daily development report at 6 AM (disabled by default) +#DAILY_DEV#0 6 * * * root /usr/sbin/secubox-reportctl send dev >/dev/null 2>&1 + +# Weekly services report on Monday at 7 AM (disabled by default) +#WEEKLY_SERVICES#0 7 * * 1 root /usr/sbin/secubox-reportctl send services >/dev/null 2>&1 + +# Monthly full report on 1st at 8 AM (disabled by default) +#MONTHLY_ALL#0 8 1 * * root /usr/sbin/secubox-reportctl send all >/dev/null 2>&1 diff --git a/package/secubox/secubox-app-reporter/files/usr/sbin/secubox-reportctl b/package/secubox/secubox-app-reporter/files/usr/sbin/secubox-reportctl new file mode 100644 index 00000000..06bbe5c1 --- /dev/null +++ b/package/secubox/secubox-app-reporter/files/usr/sbin/secubox-reportctl @@ -0,0 +1,434 @@ +#!/bin/sh +# SecuBox Report Generator +# Generates and distributes status reports via HTML and email +# Copyright (C) 2026 CyberMind.fr + +VERSION="1.0.0" +SCRIPT_DIR="/usr/share/secubox-reporter" +LIB_DIR="$SCRIPT_DIR/lib" +TPL_DIR="$SCRIPT_DIR/templates" +OUTPUT_DIR="/www/reports" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' + +# Load libraries +. /lib/functions.sh +[ -f "$LIB_DIR/collectors.sh" ] && . "$LIB_DIR/collectors.sh" +[ -f "$LIB_DIR/formatters.sh" ] && . "$LIB_DIR/formatters.sh" +[ -f "$LIB_DIR/mailer.sh" ] && . "$LIB_DIR/mailer.sh" + +# Load config +config_load secubox-reporter +config_get OUTPUT_DIR global output_dir "/www/reports" +config_get THEME global theme "dark" +config_get REPO_PATH sources repo_path "/root/secubox-openwrt" + +log_info() { echo -e "${CYAN}[INFO]${NC} $1"; } +log_ok() { echo -e "${GREEN}[OK]${NC} $1"; } +log_err() { echo -e "${RED}[ERROR]${NC} $1" >&2; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } + +usage() { + cat << EOF +SecuBox Report Generator v$VERSION + +Usage: secubox-reportctl [options] + +COMMANDS: + generate Generate report (dev|services|all) + send Generate and email report + schedule Set cron schedule (daily|weekly|off) + status Show generator status + preview Generate and output to stdout + list List generated reports + clean [days] Remove reports older than N days (default: 30) + help Show this help + +REPORT TYPES: + dev Development Status Report (progress, roadmap, health) + services Distribution/Services Status Report (exposures, channels) + all Both reports + +OPTIONS: + --email Also send via email + --theme Theme: dark, light, cyberpunk (default: dark) + --output Custom output path + --json Output status as JSON + +EXAMPLES: + secubox-reportctl generate dev + secubox-reportctl send services + secubox-reportctl schedule dev daily + secubox-reportctl generate all --theme cyberpunk +EOF +} + +# Get hostname +get_hostname() { + uci -q get system.@system[0].hostname 2>/dev/null || hostname +} + +# Get SecuBox version +get_version() { + cat /etc/secubox_version 2>/dev/null || echo "0.19.x" +} + +# Generate development status report +generate_dev_report() { + local output_file="$1" + local theme="${2:-dark}" + + log_info "Generating Development Status Report..." + + mkdir -p "$(dirname "$output_file")" + + local hostname=$(get_hostname) + local version=$(get_version) + local timestamp=$(date '+%Y-%m-%d %H:%M:%S') + local health_score=0 + + # Get health score from RPCD if available + if command -v ubus >/dev/null 2>&1; then + health_score=$(ubus call luci.secubox-core get_full_health_report 2>/dev/null | \ + jsonfilter -e '@.health_score' 2>/dev/null || echo "0") + fi + [ -z "$health_score" ] && health_score=0 + + # Collect data from .claude files + local history_entries="" + local wip_entries="" + local roadmap_data="" + + if [ -d "$REPO_PATH/.claude" ]; then + # Recent history (last 10 dated sections) + history_entries=$(collect_history "$REPO_PATH/.claude/HISTORY.md") + + # WIP items + wip_entries=$(collect_wip "$REPO_PATH/.claude/WIP.md") + + # Roadmap progress + roadmap_data=$(collect_roadmap "$REPO_PATH/.claude/ROADMAP.md") + else + history_entries="

Repository not found at $REPO_PATH

" + wip_entries="

Repository not found

" + roadmap_data="

Repository not found

" + fi + + # Generate HTML from template + if [ -f "$TPL_DIR/dev-status.html.tpl" ]; then + sed -e "s|{{HOSTNAME}}|$hostname|g" \ + -e "s|{{VERSION}}|$version|g" \ + -e "s|{{TIMESTAMP}}|$timestamp|g" \ + -e "s|{{HEALTH_SCORE}}|$health_score|g" \ + -e "s|{{THEME}}|$theme|g" \ + -e "s|{{HISTORY_ENTRIES}}|$history_entries|g" \ + -e "s|{{WIP_ENTRIES}}|$wip_entries|g" \ + -e "s|{{ROADMAP_DATA}}|$roadmap_data|g" \ + "$TPL_DIR/dev-status.html.tpl" > "$output_file" + else + # Fallback inline template + generate_dev_html_inline "$output_file" "$hostname" "$version" "$timestamp" \ + "$health_score" "$history_entries" "$wip_entries" "$roadmap_data" + fi + + log_ok "Generated: $output_file" + echo "$output_file" +} + +# Generate services status report +generate_services_report() { + local output_file="$1" + local theme="${2:-dark}" + + log_info "Generating Services Status Report..." + + mkdir -p "$(dirname "$output_file")" + + local hostname=$(get_hostname) + local timestamp=$(date '+%Y-%m-%d %H:%M:%S') + + # Count services by channel + local tor_count=0 + local dns_count=0 + local mesh_count=0 + + # Tor hidden services + if [ -d /var/lib/tor/hidden_services ]; then + tor_count=$(ls -1 /var/lib/tor/hidden_services 2>/dev/null | wc -l) + fi + + # HAProxy vhosts + dns_count=$(uci show haproxy 2>/dev/null | grep '=vhost$' | wc -l) + + # Mesh services (from P2P if available) + if command -v secubox-p2p >/dev/null 2>&1; then + mesh_count=$(secubox-p2p shared-services 2>/dev/null | wc -l) + fi + + # Collect service details + local tor_services=$(collect_tor_services) + local dns_services=$(collect_dns_services) + local mesh_services=$(collect_mesh_services) + + # Health check + local health_up=0 + local health_total=$((tor_count + dns_count)) + + # Generate HTML + if [ -f "$TPL_DIR/services-status.html.tpl" ]; then + sed -e "s|{{HOSTNAME}}|$hostname|g" \ + -e "s|{{TIMESTAMP}}|$timestamp|g" \ + -e "s|{{TOR_COUNT}}|$tor_count|g" \ + -e "s|{{DNS_COUNT}}|$dns_count|g" \ + -e "s|{{MESH_COUNT}}|$mesh_count|g" \ + -e "s|{{HEALTH_UP}}|$health_up|g" \ + -e "s|{{HEALTH_TOTAL}}|$health_total|g" \ + -e "s|{{TOR_SERVICES}}|$tor_services|g" \ + -e "s|{{DNS_SERVICES}}|$dns_services|g" \ + -e "s|{{MESH_SERVICES}}|$mesh_services|g" \ + -e "s|{{THEME}}|$theme|g" \ + "$TPL_DIR/services-status.html.tpl" > "$output_file" + else + generate_services_html_inline "$output_file" "$hostname" "$timestamp" \ + "$tor_count" "$dns_count" "$mesh_count" \ + "$tor_services" "$dns_services" "$mesh_services" + fi + + log_ok "Generated: $output_file" + echo "$output_file" +} + +# Command: generate +cmd_generate() { + local report_type="$1" + local theme="$THEME" + local output_path="$OUTPUT_DIR" + + shift + while [ $# -gt 0 ]; do + case "$1" in + --theme) theme="$2"; shift 2 ;; + --output) output_path="$2"; shift 2 ;; + *) shift ;; + esac + done + + local timestamp=$(date '+%Y%m%d-%H%M%S') + + case "$report_type" in + dev) + generate_dev_report "$output_path/dev-status-$timestamp.html" "$theme" + ;; + services) + generate_services_report "$output_path/services-status-$timestamp.html" "$theme" + ;; + all) + generate_dev_report "$output_path/dev-status-$timestamp.html" "$theme" + generate_services_report "$output_path/services-status-$timestamp.html" "$theme" + ;; + *) + log_err "Unknown report type: $report_type" + echo "Valid types: dev, services, all" + return 1 + ;; + esac +} + +# Command: send +cmd_send() { + local report_type="$1" + shift + + # Generate report first + local report_file=$(cmd_generate "$report_type" "$@") + + if [ -z "$report_file" ] || [ ! -f "$report_file" ]; then + log_err "Failed to generate report" + return 1 + fi + + # Send via email + local recipient="" + config_get recipient email recipient "" + + if [ -z "$recipient" ]; then + log_warn "No email recipient configured. Report saved to: $report_file" + return 0 + fi + + log_info "Sending report to $recipient..." + + if send_report_email "$report_type" "$(cat "$report_file")" "$recipient"; then + log_ok "Report sent to $recipient" + else + log_err "Failed to send email" + return 1 + fi +} + +# Command: schedule +cmd_schedule() { + local report_type="$1" + local frequency="$2" + local cron_file="/etc/cron.d/secubox-reporter" + + case "$frequency" in + daily) + sed -i "s/^#DAILY_${report_type^^}#//" "$cron_file" 2>/dev/null + uci set secubox-reporter.schedule.$report_type='daily' + ;; + weekly) + sed -i "s/^#WEEKLY_${report_type^^}#//" "$cron_file" 2>/dev/null + uci set secubox-reporter.schedule.$report_type='weekly' + ;; + off) + # Re-comment the lines + sed -i "/secubox-reportctl.*$report_type/s/^[^#]/#DISABLED#/" "$cron_file" 2>/dev/null + uci set secubox-reporter.schedule.$report_type='off' + ;; + *) + log_err "Invalid frequency: $frequency" + echo "Valid: daily, weekly, off" + return 1 + ;; + esac + + uci commit secubox-reporter + /etc/init.d/cron restart 2>/dev/null || true + log_ok "Schedule set: $report_type = $frequency" +} + +# Command: status +cmd_status() { + local json_output=0 + [ "$1" = "--json" ] && json_output=1 + + local hostname=$(get_hostname) + local version=$(get_version) + local dev_schedule=$(uci -q get secubox-reporter.schedule.dev || echo "off") + local services_schedule=$(uci -q get secubox-reporter.schedule.services || echo "off") + local recipient=$(uci -q get secubox-reporter.email.recipient || echo "") + local report_count=$(ls -1 "$OUTPUT_DIR"/*.html 2>/dev/null | wc -l) + + if [ $json_output -eq 1 ]; then + cat << EOF +{"hostname":"$hostname","version":"$version","schedules":{"dev":"$dev_schedule","services":"$services_schedule"},"email_recipient":"$recipient","report_count":$report_count} +EOF + else + echo "SecuBox Report Generator v$VERSION" + echo "==================================" + echo "Hostname: $hostname" + echo "Version: $version" + echo "Output Dir: $OUTPUT_DIR" + echo "Reports: $report_count files" + echo "" + echo "Schedules:" + echo " Dev: $dev_schedule" + echo " Services: $services_schedule" + echo "" + echo "Email: ${recipient:-Not configured}" + fi +} + +# Command: list +cmd_list() { + local json_output=0 + [ "$1" = "--json" ] && json_output=1 + + if [ $json_output -eq 1 ]; then + echo '{"reports":[' + local first=1 + for f in $(ls -1t "$OUTPUT_DIR"/*.html 2>/dev/null); do + [ $first -eq 0 ] && echo "," + first=0 + local name=$(basename "$f") + local size=$(stat -c%s "$f" 2>/dev/null || echo 0) + local mtime=$(stat -c%Y "$f" 2>/dev/null || echo 0) + echo "{\"name\":\"$name\",\"size\":$size,\"mtime\":$mtime}" + done + echo ']}' + else + echo "Generated Reports:" + echo "==================" + ls -lh "$OUTPUT_DIR"/*.html 2>/dev/null || echo "No reports found" + fi +} + +# Command: clean +cmd_clean() { + local days="${1:-30}" + + log_info "Removing reports older than $days days..." + + local count=$(find "$OUTPUT_DIR" -name "*.html" -mtime +$days 2>/dev/null | wc -l) + find "$OUTPUT_DIR" -name "*.html" -mtime +$days -delete 2>/dev/null + + log_ok "Removed $count old reports" +} + +# Command: preview +cmd_preview() { + local report_type="$1" + local tmpfile="/tmp/secubox-report-preview-$$.html" + + case "$report_type" in + dev) + generate_dev_report "$tmpfile" "$THEME" >/dev/null + ;; + services) + generate_services_report "$tmpfile" "$THEME" >/dev/null + ;; + *) + log_err "Unknown report type: $report_type" + return 1 + ;; + esac + + cat "$tmpfile" + rm -f "$tmpfile" +} + +# Main +case "$1" in + generate) + shift + cmd_generate "$@" + ;; + send) + shift + cmd_send "$@" + ;; + schedule) + shift + cmd_schedule "$@" + ;; + status) + shift + cmd_status "$@" + ;; + list) + shift + cmd_list "$@" + ;; + clean) + shift + cmd_clean "$@" + ;; + preview) + shift + cmd_preview "$@" + ;; + help|--help|-h|"") + usage + ;; + *) + log_err "Unknown command: $1" + usage + exit 1 + ;; +esac diff --git a/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/lib/collectors.sh b/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/lib/collectors.sh new file mode 100644 index 00000000..1284b62c --- /dev/null +++ b/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/lib/collectors.sh @@ -0,0 +1,185 @@ +#!/bin/sh +# SecuBox Reporter - Data Collectors +# Extracts data from .claude files and system services + +# Collect recent history entries +collect_history() { + local history_file="$1" + [ ! -f "$history_file" ] && return + + local output="" + local count=0 + local max_entries=10 + local current_date="" + local in_section=0 + + while IFS= read -r line; do + # Match date headers like "### 2026-03-12" + if echo "$line" | grep -qE '^### [0-9]{4}-[0-9]{2}-[0-9]{2}'; then + [ $count -ge $max_entries ] && break + + current_date=$(echo "$line" | sed 's/### //') + output="$output
$current_date
    " + in_section=1 + count=$((count + 1)) + elif [ $in_section -eq 1 ]; then + # Match bullet points + if echo "$line" | grep -qE '^- '; then + local item=$(echo "$line" | sed 's/^- //' | sed 's/\*\*\([^*]*\)\*\*/\1<\/strong>/g') + output="$output
  • $item
  • " + elif echo "$line" | grep -qE '^$'; then + output="$output
" + in_section=0 + fi + fi + done < "$history_file" + + # Close any open section + [ $in_section -eq 1 ] && output="$output" + + echo "$output" +} + +# Collect WIP items +collect_wip() { + local wip_file="$1" + [ ! -f "$wip_file" ] && return + + local output="" + local in_nextup=0 + local in_inprogress=0 + + while IFS= read -r line; do + # Find "Next Up" or "In Progress" sections + if echo "$line" | grep -qiE '^## Next Up|^### Next Up'; then + output="$output

Next Up

    " + in_nextup=1 + in_inprogress=0 + elif echo "$line" | grep -qiE '^## In Progress|^### In Progress'; then + [ $in_nextup -eq 1 ] && output="$output
" + output="$output

In Progress

    " + in_inprogress=1 + in_nextup=0 + elif echo "$line" | grep -qE '^## |^### '; then + # End of section + [ $in_nextup -eq 1 ] && output="$output
" + [ $in_inprogress -eq 1 ] && output="$output" + in_nextup=0 + in_inprogress=0 + elif [ $in_nextup -eq 1 ] || [ $in_inprogress -eq 1 ]; then + if echo "$line" | grep -qE '^[0-9]+\. |^- '; then + local item=$(echo "$line" | sed 's/^[0-9]*\. //' | sed 's/^- //' | sed 's/\*\*\([^*]*\)\*\*/\1<\/strong>/g') + [ -n "$item" ] && output="$output
  • $item
  • " + fi + fi + done < "$wip_file" + + # Close any open sections + [ $in_nextup -eq 1 ] && output="$output" + [ $in_inprogress -eq 1 ] && output="$output" + + echo "$output" +} + +# Collect roadmap progress +collect_roadmap() { + local roadmap_file="$1" + [ ! -f "$roadmap_file" ] && return + + local output="" + local current_version="" + + # Extract version sections with status + while IFS= read -r line; do + if echo "$line" | grep -qE '^### v[0-9]+\.[0-9]+'; then + current_version=$(echo "$line" | grep -oE 'v[0-9]+\.[0-9]+') + local title=$(echo "$line" | sed 's/^### //') + output="$output

    $title

    " + elif echo "$line" | grep -qiE '^\*\*Status:'; then + local status=$(echo "$line" | sed 's/.*Status: *//' | sed 's/\*//g') + local status_class="pending" + echo "$status" | grep -qi "progress" && status_class="inprogress" + echo "$status" | grep -qi "complete\|done" && status_class="complete" + output="$output$status
    " + fi + done < "$roadmap_file" + + echo "$output" +} + +# Collect Tor hidden services +collect_tor_services() { + local output="" + local tor_dir="/var/lib/tor/hidden_services" + + [ ! -d "$tor_dir" ] && echo "

    No Tor services configured

    " && return + + output="" + + for service_dir in "$tor_dir"/*/; do + [ ! -d "$service_dir" ] && continue + local name=$(basename "$service_dir") + local hostname_file="$service_dir/hostname" + + if [ -f "$hostname_file" ]; then + local onion=$(cat "$hostname_file" | head -1) + local port=$(grep -oE '[0-9]+$' "$service_dir/../torrc" 2>/dev/null | head -1) + [ -z "$port" ] && port="80" + output="$output" + fi + done + + output="$output
    ServiceOnion AddressPort
    $name$onion$port
    " + echo "$output" +} + +# Collect DNS/SSL services (HAProxy vhosts) +collect_dns_services() { + local output="" + + output="" + + # Parse HAProxy vhosts from UCI + for vhost in $(uci show haproxy 2>/dev/null | grep '=vhost$' | cut -d'.' -f2 | cut -d'=' -f1); do + local enabled=$(uci -q get haproxy.$vhost.enabled) + [ "$enabled" != "1" ] && continue + + local domain=$(uci -q get haproxy.$vhost.domain) + local backend=$(uci -q get haproxy.$vhost.backend) + local ssl=$(uci -q get haproxy.$vhost.ssl) + + local ssl_icon="❌" + [ "$ssl" = "1" ] && ssl_icon="✅" + + local status_class="running" + local status_text="Active" + + output="$output" + done + + output="$output
    DomainBackendSSLStatus
    $domain$backend$ssl_icon$status_text
    " + echo "$output" +} + +# Collect Mesh services +collect_mesh_services() { + local output="" + + if ! command -v secubox-p2p >/dev/null 2>&1; then + echo "

    P2P mesh not installed

    " + return + fi + + output="" + + # Get shared services from P2P + secubox-p2p shared-services 2>/dev/null | while read line; do + local name=$(echo "$line" | cut -d':' -f1) + local node=$(echo "$line" | cut -d':' -f2) + local port=$(echo "$line" | cut -d':' -f3) + output="$output" + done + + output="$output
    ServiceNodePort
    $name$node$port
    " + echo "$output" +} diff --git a/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/lib/formatters.sh b/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/lib/formatters.sh new file mode 100644 index 00000000..be0f238d --- /dev/null +++ b/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/lib/formatters.sh @@ -0,0 +1,228 @@ +#!/bin/sh +# SecuBox Reporter - HTML Formatters +# Inline HTML generation for reports + +# KissTheme CSS (embedded) +get_kisstheme_css() { + cat << 'CSS' +:root{ + --bg:#0a0a0f;--surface:#12121a;--card:#1a1a2e; + --ink:#f0f2ff;--dim:rgba(240,242,255,.5);--muted:#666; + --primary:#6366f1;--primary-end:#8b5cf6; + --cyan:#06b6d4;--green:#22c55e;--red:#ef4444;--yellow:#f59e0b; + --glass:rgba(255,255,255,.04);--border:rgba(255,255,255,.08); +} +*{margin:0;padding:0;box-sizing:border-box} +body{min-height:100vh;background:var(--bg);color:var(--ink);font-family:"Inter","Segoe UI",system-ui,sans-serif;padding:2rem;line-height:1.6} +.container{max-width:1200px;margin:0 auto} +h1{font-size:2rem;margin-bottom:.5rem;background:linear-gradient(90deg,var(--primary),var(--primary-end),var(--cyan));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text} +h2{font-size:1.25rem;color:var(--cyan);margin:1.5rem 0 1rem;padding-bottom:.5rem;border-bottom:1px solid var(--border)} +h3{font-size:1rem;color:var(--ink);margin-bottom:.5rem} +h4{font-size:.9rem;color:var(--dim);margin-bottom:.5rem} +.header{display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:1rem;margin-bottom:2rem;padding:1.5rem;background:var(--card);border:1px solid var(--border);border-radius:12px} +.header-info{flex:1} +.header-meta{display:flex;gap:1rem;flex-wrap:wrap} +.meta-item{font-size:.75rem;color:var(--muted);padding:.25rem .75rem;background:var(--glass);border-radius:4px} +.score-badge{font-size:2rem;font-weight:700;padding:1rem 1.5rem;background:linear-gradient(135deg,var(--primary),var(--primary-end));border-radius:12px;text-align:center;min-width:100px} +.score-label{font-size:.6rem;text-transform:uppercase;letter-spacing:.1em;opacity:.7;display:block;margin-top:.25rem} +.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:1rem;margin-bottom:2rem} +.stat-badge{background:var(--card);border:1px solid var(--border);border-radius:8px;padding:1rem;text-align:center} +.stat-value{font-size:1.5rem;font-weight:700;background:linear-gradient(90deg,var(--primary),var(--cyan));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text} +.stat-label{font-size:.7rem;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-top:.25rem} +.card{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:1.25rem;margin-bottom:1.5rem} +.card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;padding-bottom:.75rem;border-bottom:1px solid var(--border)} +.card-title{font-size:1rem;font-weight:600;display:flex;align-items:center;gap:.5rem} +.card-title::before{content:"";width:3px;height:1em;background:var(--primary);border-radius:2px} +ul{list-style:none;padding-left:0} +li{padding:.5rem 0;border-bottom:1px solid var(--border);font-size:.9rem} +li:last-child{border-bottom:none} +li strong{color:var(--cyan)} +.history-entry{margin-bottom:1.5rem} +.history-date{font-size:.8rem;color:var(--primary);font-weight:600;margin-bottom:.5rem;padding:.25rem .5rem;background:rgba(99,102,241,.1);border-radius:4px;display:inline-block} +.wip-section{margin-bottom:1rem} +.wip-section h4{color:var(--cyan);font-size:.85rem;margin-bottom:.5rem} +.roadmap-version{display:flex;align-items:center;gap:1rem;padding:.75rem;background:var(--glass);border-radius:8px;margin-bottom:.5rem} +.roadmap-version h4{margin:0;flex:1} +.status-badge{font-size:.65rem;padding:.25rem .5rem;border-radius:4px;text-transform:uppercase;letter-spacing:.05em;font-weight:600} +.status-badge.complete,.status-badge.running{background:rgba(34,197,94,.15);color:var(--green)} +.status-badge.inprogress{background:rgba(99,102,241,.15);color:var(--primary)} +.status-badge.pending{background:rgba(102,102,102,.15);color:var(--muted)} +.services-table{width:100%;border-collapse:collapse;font-size:.85rem} +.services-table th,.services-table td{padding:.75rem;text-align:left;border-bottom:1px solid var(--border)} +.services-table th{color:var(--dim);font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;font-weight:600} +.services-table a{color:var(--cyan);text-decoration:none} +.services-table a:hover{text-decoration:underline} +.services-table code{font-size:.75rem;padding:.15rem .35rem;background:var(--glass);border-radius:3px;font-family:"JetBrains Mono",monospace} +.muted{color:var(--muted);font-style:italic} +footer{margin-top:3rem;text-align:center;color:var(--muted);font-size:.75rem;padding-top:1.5rem;border-top:1px solid var(--border)} +footer a{color:var(--primary)} +@media(max-width:768px){body{padding:1rem}.header{flex-direction:column}.stats-grid{grid-template-columns:repeat(2,1fr)}} +CSS +} + +# Generate development report HTML inline +generate_dev_html_inline() { + local output_file="$1" + local hostname="$2" + local version="$3" + local timestamp="$4" + local health_score="$5" + local history_entries="$6" + local wip_entries="$7" + local roadmap_data="$8" + + cat > "$output_file" << EOF + + + + + +Development Status - $hostname + + + +
    +
    +
    +

    Development Status Report

    +
    + $hostname + v$version + $timestamp +
    +
    +
    + $health_score% + Health Score +
    +
    + +
    +
    +

    Recent Completions

    +
    +
    + $history_entries +
    +
    + +
    +
    +

    Work In Progress

    +
    +
    + $wip_entries +
    +
    + +
    +
    +

    Roadmap Progress

    +
    +
    + $roadmap_data +
    +
    + + +
    + + +EOF +} + +# Generate services report HTML inline +generate_services_html_inline() { + local output_file="$1" + local hostname="$2" + local timestamp="$3" + local tor_count="$4" + local dns_count="$5" + local mesh_count="$6" + local tor_services="$7" + local dns_services="$8" + local mesh_services="$9" + + local total=$((tor_count + dns_count + mesh_count)) + + cat > "$output_file" << EOF + + + + + +Services Status - $hostname + + + +
    +
    +
    +

    Distribution Status Report

    +
    + $hostname + $timestamp +
    +
    +
    + $total + Published Spaces +
    +
    + +
    +
    +
    $tor_count
    +
    Tor Services
    +
    +
    +
    $dns_count
    +
    DNS/SSL Vhosts
    +
    +
    +
    $mesh_count
    +
    Mesh Services
    +
    +
    + +
    +
    +

    Tor Hidden Services

    +
    +
    + $tor_services +
    +
    + +
    +
    +

    DNS/SSL Services

    +
    +
    + $dns_services +
    +
    + +
    +
    +

    Mesh Services

    +
    +
    + $mesh_services +
    +
    + + +
    + + +EOF +} diff --git a/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/lib/mailer.sh b/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/lib/mailer.sh new file mode 100644 index 00000000..dfc4a55a --- /dev/null +++ b/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/lib/mailer.sh @@ -0,0 +1,128 @@ +#!/bin/sh +# SecuBox Reporter - Email Integration +# Sends reports via msmtp or sendmail + +. /lib/functions.sh + +# Send report via email +send_report_email() { + local report_type="$1" + local html_content="$2" + local recipient="$3" + + local smtp_server="" + local smtp_port="" + local smtp_user="" + local smtp_password="" + local smtp_tls="" + + config_load secubox-reporter + config_get smtp_server email smtp_server "" + config_get smtp_port email smtp_port "587" + config_get smtp_user email smtp_user "" + config_get smtp_password email smtp_password "" + config_get smtp_tls email smtp_tls "1" + + [ -z "$recipient" ] && { + echo "ERROR: No email recipient specified" >&2 + return 1 + } + + local hostname=$(uci -q get system.@system[0].hostname || hostname) + local date_str=$(date '+%Y-%m-%d') + + # Format report type for subject + local report_name="Status" + case "$report_type" in + dev) report_name="Development Status" ;; + services) report_name="Services Distribution" ;; + all) report_name="Full Status" ;; + esac + + local subject="[SecuBox] $report_name Report - $hostname - $date_str" + + # Build MIME multipart email + local boundary="SecuBox_Report_$(date +%s)_$$" + + local email_body="MIME-Version: 1.0 +From: SecuBox Reporter +To: $recipient +Subject: $subject +Content-Type: multipart/alternative; boundary=\"$boundary\" + +--$boundary +Content-Type: text/plain; charset=utf-8 + +SecuBox $report_name Report +=========================== + +Generated: $(date) +Hostname: $hostname + +This report contains HTML content. Please view in an HTML-capable email client. + +--$boundary +Content-Type: text/html; charset=utf-8 + +$html_content + +--$boundary--" + + # Try msmtp first, then sendmail + if command -v msmtp >/dev/null 2>&1 && [ -n "$smtp_server" ]; then + # Create temporary msmtp config + local msmtp_conf="/tmp/msmtp-report-$$.conf" + cat > "$msmtp_conf" << EOF +account default +host $smtp_server +port $smtp_port +auth on +user $smtp_user +password $smtp_password +tls $([ "$smtp_tls" = "1" ] && echo "on" || echo "off") +tls_starttls on +tls_certcheck off +from secubox@$hostname +EOF + chmod 600 "$msmtp_conf" + + echo "$email_body" | msmtp -C "$msmtp_conf" "$recipient" 2>/dev/null + local result=$? + + rm -f "$msmtp_conf" + return $result + + elif command -v sendmail >/dev/null 2>&1; then + echo "$email_body" | sendmail -t 2>/dev/null + return $? + + else + echo "ERROR: No mail transport available (msmtp or sendmail required)" >&2 + return 1 + fi +} + +# Test email configuration +test_email() { + local recipient="$1" + [ -z "$recipient" ] && { + config_load secubox-reporter + config_get recipient email recipient "" + } + + [ -z "$recipient" ] && { + echo "ERROR: No recipient configured" >&2 + return 1 + } + + local hostname=$(uci -q get system.@system[0].hostname || hostname) + local test_body="

    SecuBox Email Test

    This is a test email from $hostname.

    Generated: $(date)

    " + + if send_report_email "test" "$test_body" "$recipient"; then + echo "Test email sent to $recipient" + return 0 + else + echo "Failed to send test email" >&2 + return 1 + fi +} diff --git a/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/templates/dev-status.html.tpl b/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/templates/dev-status.html.tpl new file mode 100644 index 00000000..2f8dcf9d --- /dev/null +++ b/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/templates/dev-status.html.tpl @@ -0,0 +1,89 @@ + + + + + +Development Status - {{HOSTNAME}} + + + +
    +
    +
    +

    Development Status Report

    +
    + {{HOSTNAME}} + {{TIMESTAMP}} +
    +
    +
    + {{HEALTH_SCORE}}% + Health Score +
    +
    + +
    +
    +

    Recent Completions

    +
    +
    + {{HISTORY_ENTRIES}} +
    +
    + +
    +
    +

    Work In Progress

    +
    +
    + {{WIP_ENTRIES}} +
    +
    + +
    +
    +

    Roadmap Progress

    +
    +
    + {{ROADMAP_DATA}} +
    +
    + +
    + Generated by SecuBox Report Generator v1.0 +
    +
    + + diff --git a/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/templates/email-wrapper.html.tpl b/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/templates/email-wrapper.html.tpl new file mode 100644 index 00000000..20160416 --- /dev/null +++ b/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/templates/email-wrapper.html.tpl @@ -0,0 +1,38 @@ + + + + + +{{SUBJECT}} + + + + + + +
    + + + + + + + + + + + + + +
    +

    SecuBox Report

    +

    {{HOSTNAME}} | {{TIMESTAMP}}

    +
    +{{CONTENT}} +
    +Generated by SecuBox Report Generator
    +View Dashboard +
    +
    + + diff --git a/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/templates/services-status.html.tpl b/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/templates/services-status.html.tpl new file mode 100644 index 00000000..35d0808c --- /dev/null +++ b/package/secubox/secubox-app-reporter/files/usr/share/secubox-reporter/templates/services-status.html.tpl @@ -0,0 +1,103 @@ + + + + + +Services Status - {{HOSTNAME}} + + + +
    +
    +
    +

    Distribution Status Report

    +
    + {{HOSTNAME}} + {{TIMESTAMP}} +
    +
    +
    + {{HEALTH_UP}}/{{HEALTH_TOTAL}} + Services Up +
    +
    + +
    +
    +
    {{TOR_COUNT}}
    +
    Tor Services
    +
    +
    +
    {{DNS_COUNT}}
    +
    DNS/SSL Vhosts
    +
    +
    +
    {{MESH_COUNT}}
    +
    Mesh Services
    +
    +
    + +
    +
    +

    Tor Hidden Services

    +
    +
    + {{TOR_SERVICES}} +
    +
    + +
    +
    +

    DNS/SSL Services

    +
    +
    + {{DNS_SERVICES}} +
    +
    + +
    +
    +

    Mesh Services

    +
    +
    + {{MESH_SERVICES}} +
    +
    + + +
    + +