- display.module.sh: style toggle per view (compact/table) - display.json: default config with all views set to compact - ctx::display: points to .wgctl/config/display.json - list: _render_table with dynamic widths, colors, shared row_color/status_color - group/identity/net/hosts/activity: _render_table added - rule/subnet/policy: table UI functions + _render_table - ui::peer::status_color: \033[2m for offline (dimmer, more readable) - note: individual table layout refinements pending cleanup pass - note: configurable colors per field deferred to display config v2
326 lines
No EOL
9.5 KiB
Bash
326 lines
No EOL
9.5 KiB
Bash
#!/usr/bin/env bash
|
|
# hosts.command.sh — manage host/IP display name mappings
|
|
|
|
# ============================================
|
|
# Lifecycle
|
|
# ============================================
|
|
|
|
function cmd::hosts::on_load() {
|
|
flag::register --ip
|
|
flag::register --subnet
|
|
flag::register --port
|
|
flag::register --name
|
|
flag::register --desc
|
|
flag::register --tag
|
|
flag::register --tags
|
|
flag::register --force
|
|
|
|
command::mixin json_output
|
|
}
|
|
|
|
# ============================================
|
|
# Help
|
|
# ============================================
|
|
|
|
function cmd::hosts::help() {
|
|
cat <<EOF
|
|
Usage: wgctl hosts <subcommand> [options]
|
|
|
|
Manage host display names for IP resolution in logs, watch, and activity.
|
|
Maps IPs, subnets, and ports to human-readable names.
|
|
|
|
Subcommands:
|
|
list List all host entries
|
|
show --ip <ip> Show host entry details
|
|
show --subnet <cidr> Show subnet entry details
|
|
show --port <port> Show port entry details
|
|
add --ip <ip> --name <name> Add a host entry
|
|
add --subnet <cidr> --name <name>
|
|
Add a subnet entry
|
|
add --port <port> --name <name>
|
|
Add a port entry
|
|
rm --ip <ip> Remove a host entry
|
|
rm --subnet <cidr> Remove a subnet entry
|
|
rm --port <port> Remove a port entry
|
|
|
|
Options for add:
|
|
--ip <ip> IP address to map
|
|
--subnet <cidr> Subnet CIDR to map (e.g. 10.0.0.0/24)
|
|
--port <port> Port number to map (e.g. 443)
|
|
--name <name> Display name (e.g. vodafone-wan)
|
|
--desc <description> Optional description
|
|
--tag <tag> Tag (repeatable)
|
|
--tags <tag1,tag2> Tags (comma-separated)
|
|
|
|
Options for rm:
|
|
--force Skip confirmation
|
|
|
|
Examples:
|
|
wgctl hosts list
|
|
wgctl hosts add --ip 148.69.46.73 --name vodafone-wan --desc "Vodafone WAN"
|
|
wgctl hosts add --ip 94.63.0.129 --name nuno-home --tags home,isp
|
|
wgctl hosts add --subnet 10.0.0.0/24 --name lan --desc "Local LAN"
|
|
wgctl hosts add --port 443 --name https
|
|
wgctl hosts show --ip 148.69.46.73
|
|
wgctl hosts rm --ip 148.69.46.73
|
|
EOF
|
|
}
|
|
|
|
# ============================================
|
|
# Run
|
|
# ============================================
|
|
|
|
function cmd::hosts::run() {
|
|
local subcmd="${1:-list}"
|
|
shift || true
|
|
|
|
if command::json; then
|
|
cmd::hosts::_output_json
|
|
return 0
|
|
fi
|
|
|
|
case "$subcmd" in
|
|
list) cmd::hosts::list "$@" ;;
|
|
show) cmd::hosts::show "$@" ;;
|
|
add) cmd::hosts::add "$@" ;;
|
|
rm|remove|del) cmd::hosts::rm "$@" ;;
|
|
help) cmd::hosts::help ;;
|
|
*)
|
|
log::error "Unknown subcommand: '${subcmd}'"
|
|
cmd::hosts::help
|
|
return 1 ;;
|
|
esac
|
|
}
|
|
|
|
# ============================================
|
|
# List
|
|
# ============================================
|
|
|
|
function cmd::hosts::list() {
|
|
local filter_tag=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--tag) filter_tag="$2"; shift 2 ;;
|
|
--help) cmd::hosts::help; return ;;
|
|
*) log::error "Unknown flag: $1"; return 1 ;;
|
|
esac
|
|
done
|
|
|
|
local hosts_file
|
|
hosts_file="$(ctx::hosts)"
|
|
|
|
if [[ ! -f "$hosts_file" ]]; then
|
|
log::wg_warning "No hosts configured. Use 'wgctl hosts add' to add one."
|
|
return 0
|
|
fi
|
|
|
|
local data
|
|
data=$(json::hosts_list "$hosts_file" 2>/dev/null)
|
|
[[ -z "$data" ]] && log::wg_warning "No hosts configured." && return 0
|
|
|
|
# Apply tag filter to data first
|
|
local filtered_data=""
|
|
while IFS='|' read -r type key name desc tags; do
|
|
[[ -z "$type" ]] && continue
|
|
[[ -n "$filter_tag" && "$tags" != *"$filter_tag"* ]] && continue
|
|
filtered_data+="${type}|${key}|${name}|${desc}|${tags}"$'\n'
|
|
done <<< "$data"
|
|
|
|
[[ -z "$filtered_data" ]] && log::wg_warning "No hosts found." && return 0
|
|
|
|
# Measure column widths from filtered data
|
|
local w_key=15 w_name=16 w_desc=10
|
|
while IFS='|' read -r type key name desc tags; do
|
|
[[ -z "$type" ]] && continue
|
|
(( ${#key} > w_key )) && w_key=${#key}
|
|
(( ${#name} > w_name )) && w_name=${#name}
|
|
local desc_len=${#desc}
|
|
[[ -z "$desc" ]] && desc_len=1 # "—" = 1 visible char
|
|
(( desc_len > w_desc )) && w_desc=$desc_len
|
|
done <<< "$filtered_data"
|
|
(( w_key += 2 ))
|
|
(( w_name += 2 ))
|
|
(( w_desc += 2 ))
|
|
|
|
log::section "Host Mappings"
|
|
echo ""
|
|
|
|
if display::is_table "hosts_list"; then
|
|
cmd::hosts::_render_table "$data"
|
|
return 0
|
|
fi
|
|
|
|
local last_type="" found=false
|
|
while IFS='|' read -r type key name desc tags; do
|
|
[[ -z "$type" ]] && continue
|
|
found=true
|
|
|
|
# Section header when type changes
|
|
if [[ "$type" != "$last_type" ]]; then
|
|
[[ -n "$last_type" ]] && echo ""
|
|
ui::hosts::section_header "$type"
|
|
last_type="$type"
|
|
fi
|
|
|
|
ui::hosts::list_row "$type" "$key" "$name" "$desc" "$tags" \
|
|
"$w_key" "$w_name" "$w_desc"
|
|
|
|
done <<< "$filtered_data"
|
|
|
|
$found || log::wg_warning "No hosts configured."
|
|
echo ""
|
|
}
|
|
|
|
function cmd::hosts::_render_table() {
|
|
local data="${1:-}"
|
|
[[ -z "$data" ]] && return 0
|
|
|
|
ui::hosts::list_header_table
|
|
while IFS='|' read -r type key name desc tags; do
|
|
[[ -z "$type" ]] && continue
|
|
ui::hosts::list_row_table "$type" "$key" "$name" "$desc" "$tags"
|
|
done <<< "$data"
|
|
}
|
|
|
|
# ============================================
|
|
# Show
|
|
# ============================================
|
|
|
|
function cmd::hosts::show() {
|
|
local ip="" subnet="" port=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--ip) ip="$2"; shift 2 ;;
|
|
--subnet) subnet="$2"; shift 2 ;;
|
|
--port) port="$2"; shift 2 ;;
|
|
--help) cmd::hosts::help; return ;;
|
|
*) log::error "Unknown flag: $1"; return 1 ;;
|
|
esac
|
|
done
|
|
|
|
local key entry_type
|
|
if [[ -n "$ip" ]]; then key="$ip"; entry_type="host"; fi
|
|
if [[ -n "$subnet" ]]; then key="$subnet"; entry_type="subnet"; fi
|
|
if [[ -n "$port" ]]; then key="$port"; entry_type="port"; fi
|
|
|
|
[[ -z "$key" ]] && log::error "Specify --ip, --subnet, or --port" && return 1
|
|
|
|
hosts::require_exists "$entry_type" "$key" || return 1
|
|
|
|
log::section "${entry_type^}: ${key}"
|
|
printf "\n"
|
|
|
|
while IFS='|' read -r field val; do
|
|
case "$field" in
|
|
name) ui::row "Name" "${val:-—}" ;;
|
|
desc) ui::row "Description" "${val:-—}" ;;
|
|
tags) ui::row "Tags" "${val:-—}" ;;
|
|
esac
|
|
done < <(json::hosts_show "$(ctx::hosts)" "$key" "$entry_type")
|
|
|
|
printf "\n"
|
|
}
|
|
|
|
# ============================================
|
|
# Add
|
|
# ============================================
|
|
|
|
function cmd::hosts::add() {
|
|
local ip="" subnet="" port="" name="" desc="" tags=()
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--ip) ip="$2"; shift 2 ;;
|
|
--subnet) subnet="$2"; shift 2 ;;
|
|
--port) port="$2"; shift 2 ;;
|
|
--name) name="$2"; shift 2 ;;
|
|
--desc) desc="$2"; shift 2 ;;
|
|
--tag) tags+=("$2"); shift 2 ;;
|
|
--tags) IFS=',' read -ra t <<< "$2"; tags+=("${t[@]}"); shift 2 ;;
|
|
--help) cmd::hosts::help; return ;;
|
|
*) log::error "Unknown flag: $1"; return 1 ;;
|
|
esac
|
|
done
|
|
|
|
[[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1
|
|
|
|
local key entry_type
|
|
if [[ -n "$ip" ]]; then key="$ip"; entry_type="host"; fi
|
|
if [[ -n "$subnet" ]]; then key="$subnet"; entry_type="subnet"; fi
|
|
if [[ -n "$port" ]]; then key="$port"; entry_type="port"; fi
|
|
|
|
[[ -z "$key" ]] && log::error "Specify --ip, --subnet, or --port" && return 1
|
|
|
|
local tags_str
|
|
tags_str=$(IFS=','; echo "${tags[*]}")
|
|
|
|
json::hosts_add "$(ctx::hosts)" "$entry_type" "$key" "$name" "$desc" "$tags_str"
|
|
log::wg_success "Added ${entry_type}: ${key} → ${name}"
|
|
}
|
|
|
|
# ============================================
|
|
# Remove
|
|
# ============================================
|
|
|
|
function cmd::hosts::rm() {
|
|
local ip="" subnet="" port="" force=false
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--ip) ip="$2"; shift 2 ;;
|
|
--subnet) subnet="$2"; shift 2 ;;
|
|
--port) port="$2"; shift 2 ;;
|
|
--force) force=true; shift ;;
|
|
--help) cmd::hosts::help; return ;;
|
|
*) log::error "Unknown flag: $1"; return 1 ;;
|
|
esac
|
|
done
|
|
|
|
local key entry_type
|
|
if [[ -n "$ip" ]]; then key="$ip"; entry_type="host"; fi
|
|
if [[ -n "$subnet" ]]; then key="$subnet"; entry_type="subnet"; fi
|
|
if [[ -n "$port" ]]; then key="$port"; entry_type="port"; fi
|
|
|
|
[[ -z "$key" ]] && log::error "Specify --ip, --subnet, or --port" && return 1
|
|
|
|
hosts::require_exists "$entry_type" "$key" || return 1
|
|
|
|
if ! $force; then
|
|
read -r -p "Remove ${entry_type} '${key}'? [y/N] " confirm
|
|
case "$confirm" in
|
|
[yY]*) ;;
|
|
*) log::info "Aborted"; return 0 ;;
|
|
esac
|
|
fi
|
|
|
|
json::hosts_remove "$(ctx::hosts)" "$entry_type" "$key"
|
|
log::wg_success "Removed ${entry_type}: ${key}"
|
|
}
|
|
|
|
function cmd::hosts::_output_json() {
|
|
local data
|
|
data=$(json::hosts_list "$(ctx::hosts)" 2>/dev/null)
|
|
|
|
local -a hosts=()
|
|
while IFS='|' read -r type ip name desc tags; do
|
|
[[ -z "$type" ]] && continue
|
|
|
|
local tags_json="[]"
|
|
if [[ -n "$tags" ]]; then
|
|
local tags_array
|
|
tags_array=$(echo "$tags" | tr ',' '\n' | \
|
|
while IFS= read -r t; do [[ -n "$t" ]] && printf '"%s",' "$t"; done | sed 's/,$//')
|
|
tags_json="[${tags_array}]"
|
|
fi
|
|
|
|
hosts+=("$(printf '{"type":"%s","ip":"%s","name":"%s","desc":"%s","tags":%s}' \
|
|
"$type" "$ip" "$name" "$desc" "$tags_json")")
|
|
done <<< "$data"
|
|
|
|
local count=${#hosts[@]}
|
|
local array
|
|
array=$(printf '%s\n' "${hosts[@]:-}" | paste -sd ',' -)
|
|
printf '{"hosts":[%s]}' "${array:-}" | json::envelope "hosts list" "$count"
|
|
} |