function cmd::logs::show_fw_events() { local filter_ip="${1:-}" filter_name="${2:-}" filter_type="${3:-}" \ limit="${4:-50}" net_file="${5:-}" collapse="${6:-1}" \ since="${7:-}" filter_dest_ip="${8:-}" filter_dest_port="${9:-}" \ sort_order="${10:-desc}" resolved_only="${11:-false}" [[ ! -f "$FW_EVENTS_LOG" ]] && return 0 local data data=$(json::fw_events \ "$FW_EVENTS_LOG" "$filter_ip" "$filter_type" \ "$(ctx::clients)" "${net_file:-}" \ "$limit" "$collapse" "$since" \ "$filter_dest_ip" "$filter_dest_port" \ "$sort_order" \ 2>/dev/null) [[ -z "$data" ]] && return 0 # ── Pass 1: resolve endpoints and measure widths ── local w_client=16 w_dest=20 w_endpoint=0 local resolved_data="" while IFS='|' read -r ts client dest_ip dest_port proto svc count src_endpoint; do [[ -z "$ts" ]] && continue # Measure client (( ${#client} > w_client )) && w_client=${#client} # Build svc_display (for w_dest measurement) local svc_display="" if [[ -n "$svc" ]]; then [[ -n "$dest_port" ]] && svc_display="${svc}/${proto}" \ || svc_display="${svc} (${proto})" else [[ -n "$dest_port" ]] && svc_display="${dest_ip}:${dest_port}/${proto}" \ || svc_display="${dest_ip} (${proto})" fi # Build raw_suffix plain (no ANSI) for w_dest measurement local raw_plain="" if [[ -n "$svc" ]]; then [[ -n "$dest_port" ]] && raw_plain=" (${dest_ip}:${dest_port})" \ || raw_plain=" (${dest_ip})" fi local measure_len if $resolved_only; then measure_len=${#svc_display} else local raw_plain="" [[ -n "$svc" && -n "$dest_port" ]] && raw_plain=" (${dest_ip}:${dest_port})" [[ -n "$svc" && -z "$dest_port" ]] && raw_plain=" (${dest_ip})" measure_len=$(( ${#svc_display} + ${#raw_plain} )) fi (( measure_len > w_dest )) && w_dest=$measure_len # Resolve endpoint once local src_resolved="" if [[ -n "$src_endpoint" ]]; then src_resolved=$(resolve::ip "$src_endpoint" 2>/dev/null || true) [[ "$src_resolved" == "$src_endpoint" ]] && src_resolved="" # Measure endpoint column: raw IP + " → resolved" local ep_display_len=${#src_endpoint} [[ -n "$src_resolved" ]] && ep_display_len=$(( ep_display_len + 4 + ${#src_resolved} )) (( ep_display_len > w_endpoint )) && w_endpoint=$ep_display_len fi if [[ -n "$src_endpoint" ]]; then local ep_measure_len if $resolved_only; then ep_measure_len=${#src_resolved} [[ -z "$src_resolved" ]] && ep_measure_len=${#src_endpoint} else ep_measure_len=${#src_endpoint} [[ -n "$src_resolved" ]] && \ ep_measure_len=$(( ${#src_endpoint} + 4 + ${#src_resolved} )) fi (( ep_measure_len > w_endpoint )) && w_endpoint=$ep_measure_len fi resolved_data+="${ts}|${client}|${dest_ip}|${dest_port}|${proto}|${svc}|${count}|${src_endpoint}|${src_resolved}"$'\n' done <<< "$data" (( w_client += 2 )) (( w_dest += 2 )) [[ "$w_endpoint" -gt 0 ]] && (( w_endpoint += 2 )) # ── Pass 2: render ── ui::logs::fw_section_header while IFS='|' read -r ts client dest_ip dest_port proto svc count src_endpoint src_resolved; do [[ -z "$ts" ]] && continue ui::logs::fw_row "$ts" "$client" "$dest_ip" "$dest_port" \ "$proto" "$svc" "$count" "$w_client" "$w_dest" \ "$src_endpoint" "$src_resolved" "$w_endpoint" "$resolved_only" done <<< "$resolved_data" printf "\n" } function cmd::logs::show_wg_events() { local filter_ip="${1:-}" filter_name="${2:-}" filter_type="${3:-}" \ limit="${4:-50}" collapse="${5:-1}" \ since="${6:-}" filter_event="${7:-}" sort_order="${8:-desc}" \ resolved_only="${9:-false}" [[ ! -f "$WG_EVENTS_LOG" ]] && return 0 local data data=$(json::wg_events \ "$WG_EVENTS_LOG" "$filter_name" "$filter_type" \ "$limit" "$collapse" "$since" "$filter_event" \ "$(ctx::endpoint_cache)" "$sort_order" \ 2>/dev/null) [[ -z "$data" ]] && return 0 local w_client=16 w_endpoint=16 local resolved_data="" while IFS='|' read -r ts client endpoint event count gap_seconds; do [[ -z "$ts" ]] && continue (( ${#client} > w_client )) && w_client=${#client} local resolved="" [[ -n "$endpoint" ]] && resolved=$(resolve::ip "$endpoint" 2>/dev/null || true) [[ "$resolved" == "$endpoint" ]] && resolved="" # In resolved_only mode, use resolved name as display (or raw if unresolved) local ep_display if $resolved_only; then ep_display="${resolved:-$endpoint}" [[ -z "$ep_display" ]] && ep_display="-" else ep_display="${endpoint:--}" fi (( ${#ep_display} > w_endpoint )) && w_endpoint=${#ep_display} # Extra width for annotation when not resolved_only $resolved_only || { [[ -n "$resolved" && -n "$endpoint" ]] && \ w_endpoint=$(( w_endpoint > ${#endpoint} + 4 + ${#resolved} \ ? w_endpoint : ${#endpoint} + 4 + ${#resolved} )) } resolved_data+="${ts}|${client}|${endpoint}|${event}|${count}|${gap_seconds}|${resolved}|${ep_display}"$'\n' done <<< "$data" (( w_client += 2 )) (( w_endpoint += 2 )) $resolved_only || (( w_endpoint += 4 )) # space for annotation ui::logs::wg_section_header while IFS='|' read -r ts client endpoint event count gap_seconds resolved ep_display; do [[ -z "$ts" ]] && continue if $resolved_only; then ui::logs::wg_row "$ts" "$client" "$ep_display" "$event" \ "$count" "$w_client" "$w_endpoint" "$gap_seconds" "" else ui::logs::wg_row "$ts" "$client" "$endpoint" "$event" \ "$count" "$w_client" "$w_endpoint" "$gap_seconds" "$resolved" fi done <<< "$resolved_data" printf "\n" }