最後活躍 1 month ago

nuno 已修改 1 month ago. 還原成這個修訂版本

1 file changed, 326 insertions

hosts.command.sh(檔案已創建)

@@ -0,0 +1,326 @@
1 + #!/usr/bin/env bash
2 + # hosts.command.sh — manage host/IP display name mappings
3 +
4 + # ============================================
5 + # Lifecycle
6 + # ============================================
7 +
8 + function cmd::hosts::on_load() {
9 + flag::register --ip
10 + flag::register --subnet
11 + flag::register --port
12 + flag::register --name
13 + flag::register --desc
14 + flag::register --tag
15 + flag::register --tags
16 + flag::register --force
17 +
18 + command::mixin json_output
19 + }
20 +
21 + # ============================================
22 + # Help
23 + # ============================================
24 +
25 + function cmd::hosts::help() {
26 + cat <<EOF
27 + Usage: wgctl hosts <subcommand> [options]
28 +
29 + Manage host display names for IP resolution in logs, watch, and activity.
30 + Maps IPs, subnets, and ports to human-readable names.
31 +
32 + Subcommands:
33 + list List all host entries
34 + show --ip <ip> Show host entry details
35 + show --subnet <cidr> Show subnet entry details
36 + show --port <port> Show port entry details
37 + add --ip <ip> --name <name> Add a host entry
38 + add --subnet <cidr> --name <name>
39 + Add a subnet entry
40 + add --port <port> --name <name>
41 + Add a port entry
42 + rm --ip <ip> Remove a host entry
43 + rm --subnet <cidr> Remove a subnet entry
44 + rm --port <port> Remove a port entry
45 +
46 + Options for add:
47 + --ip <ip> IP address to map
48 + --subnet <cidr> Subnet CIDR to map (e.g. 10.0.0.0/24)
49 + --port <port> Port number to map (e.g. 443)
50 + --name <name> Display name (e.g. vodafone-wan)
51 + --desc <description> Optional description
52 + --tag <tag> Tag (repeatable)
53 + --tags <tag1,tag2> Tags (comma-separated)
54 +
55 + Options for rm:
56 + --force Skip confirmation
57 +
58 + Examples:
59 + wgctl hosts list
60 + wgctl hosts add --ip 148.69.46.73 --name vodafone-wan --desc "Vodafone WAN"
61 + wgctl hosts add --ip 94.63.0.129 --name nuno-home --tags home,isp
62 + wgctl hosts add --subnet 10.0.0.0/24 --name lan --desc "Local LAN"
63 + wgctl hosts add --port 443 --name https
64 + wgctl hosts show --ip 148.69.46.73
65 + wgctl hosts rm --ip 148.69.46.73
66 + EOF
67 + }
68 +
69 + # ============================================
70 + # Run
71 + # ============================================
72 +
73 + function cmd::hosts::run() {
74 + local subcmd="${1:-list}"
75 + shift || true
76 +
77 + if command::json; then
78 + cmd::hosts::_output_json
79 + return 0
80 + fi
81 +
82 + case "$subcmd" in
83 + list) cmd::hosts::list "$@" ;;
84 + show) cmd::hosts::show "$@" ;;
85 + add) cmd::hosts::add "$@" ;;
86 + rm|remove|del) cmd::hosts::rm "$@" ;;
87 + help) cmd::hosts::help ;;
88 + *)
89 + log::error "Unknown subcommand: '${subcmd}'"
90 + cmd::hosts::help
91 + return 1 ;;
92 + esac
93 + }
94 +
95 + # ============================================
96 + # List
97 + # ============================================
98 +
99 + function cmd::hosts::list() {
100 + local filter_tag=""
101 +
102 + while [[ $# -gt 0 ]]; do
103 + case "$1" in
104 + --tag) filter_tag="$2"; shift 2 ;;
105 + --help) cmd::hosts::help; return ;;
106 + *) log::error "Unknown flag: $1"; return 1 ;;
107 + esac
108 + done
109 +
110 + local hosts_file
111 + hosts_file="$(ctx::hosts)"
112 +
113 + if [[ ! -f "$hosts_file" ]]; then
114 + log::wg_warning "No hosts configured. Use 'wgctl hosts add' to add one."
115 + return 0
116 + fi
117 +
118 + local data
119 + data=$(json::hosts_list "$hosts_file" 2>/dev/null)
120 + [[ -z "$data" ]] && log::wg_warning "No hosts configured." && return 0
121 +
122 + # Apply tag filter to data first
123 + local filtered_data=""
124 + while IFS='|' read -r type key name desc tags; do
125 + [[ -z "$type" ]] && continue
126 + [[ -n "$filter_tag" && "$tags" != *"$filter_tag"* ]] && continue
127 + filtered_data+="${type}|${key}|${name}|${desc}|${tags}"$'\n'
128 + done <<< "$data"
129 +
130 + [[ -z "$filtered_data" ]] && log::wg_warning "No hosts found." && return 0
131 +
132 + # Measure column widths from filtered data
133 + local w_key=15 w_name=16 w_desc=10
134 + while IFS='|' read -r type key name desc tags; do
135 + [[ -z "$type" ]] && continue
136 + (( ${#key} > w_key )) && w_key=${#key}
137 + (( ${#name} > w_name )) && w_name=${#name}
138 + local desc_len=${#desc}
139 + [[ -z "$desc" ]] && desc_len=1 # "—" = 1 visible char
140 + (( desc_len > w_desc )) && w_desc=$desc_len
141 + done <<< "$filtered_data"
142 + (( w_key += 2 ))
143 + (( w_name += 2 ))
144 + (( w_desc += 2 ))
145 +
146 + log::section "Host Mappings"
147 + echo ""
148 +
149 + if display::is_table "hosts_list"; then
150 + cmd::hosts::_render_table "$data"
151 + return 0
152 + fi
153 +
154 + local last_type="" found=false
155 + while IFS='|' read -r type key name desc tags; do
156 + [[ -z "$type" ]] && continue
157 + found=true
158 +
159 + # Section header when type changes
160 + if [[ "$type" != "$last_type" ]]; then
161 + [[ -n "$last_type" ]] && echo ""
162 + ui::hosts::section_header "$type"
163 + last_type="$type"
164 + fi
165 +
166 + ui::hosts::list_row "$type" "$key" "$name" "$desc" "$tags" \
167 + "$w_key" "$w_name" "$w_desc"
168 +
169 + done <<< "$filtered_data"
170 +
171 + $found || log::wg_warning "No hosts configured."
172 + echo ""
173 + }
174 +
175 + function cmd::hosts::_render_table() {
176 + local data="${1:-}"
177 + [[ -z "$data" ]] && return 0
178 +
179 + ui::hosts::list_header_table
180 + while IFS='|' read -r type key name desc tags; do
181 + [[ -z "$type" ]] && continue
182 + ui::hosts::list_row_table "$type" "$key" "$name" "$desc" "$tags"
183 + done <<< "$data"
184 + }
185 +
186 + # ============================================
187 + # Show
188 + # ============================================
189 +
190 + function cmd::hosts::show() {
191 + local ip="" subnet="" port=""
192 +
193 + while [[ $# -gt 0 ]]; do
194 + case "$1" in
195 + --ip) ip="$2"; shift 2 ;;
196 + --subnet) subnet="$2"; shift 2 ;;
197 + --port) port="$2"; shift 2 ;;
198 + --help) cmd::hosts::help; return ;;
199 + *) log::error "Unknown flag: $1"; return 1 ;;
200 + esac
201 + done
202 +
203 + local key entry_type
204 + if [[ -n "$ip" ]]; then key="$ip"; entry_type="host"; fi
205 + if [[ -n "$subnet" ]]; then key="$subnet"; entry_type="subnet"; fi
206 + if [[ -n "$port" ]]; then key="$port"; entry_type="port"; fi
207 +
208 + [[ -z "$key" ]] && log::error "Specify --ip, --subnet, or --port" && return 1
209 +
210 + hosts::require_exists "$entry_type" "$key" || return 1
211 +
212 + log::section "${entry_type^}: ${key}"
213 + printf "\n"
214 +
215 + while IFS='|' read -r field val; do
216 + case "$field" in
217 + name) ui::row "Name" "${val:-—}" ;;
218 + desc) ui::row "Description" "${val:-—}" ;;
219 + tags) ui::row "Tags" "${val:-—}" ;;
220 + esac
221 + done < <(json::hosts_show "$(ctx::hosts)" "$key" "$entry_type")
222 +
223 + printf "\n"
224 + }
225 +
226 + # ============================================
227 + # Add
228 + # ============================================
229 +
230 + function cmd::hosts::add() {
231 + local ip="" subnet="" port="" name="" desc="" tags=()
232 +
233 + while [[ $# -gt 0 ]]; do
234 + case "$1" in
235 + --ip) ip="$2"; shift 2 ;;
236 + --subnet) subnet="$2"; shift 2 ;;
237 + --port) port="$2"; shift 2 ;;
238 + --name) name="$2"; shift 2 ;;
239 + --desc) desc="$2"; shift 2 ;;
240 + --tag) tags+=("$2"); shift 2 ;;
241 + --tags) IFS=',' read -ra t <<< "$2"; tags+=("${t[@]}"); shift 2 ;;
242 + --help) cmd::hosts::help; return ;;
243 + *) log::error "Unknown flag: $1"; return 1 ;;
244 + esac
245 + done
246 +
247 + [[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1
248 +
249 + local key entry_type
250 + if [[ -n "$ip" ]]; then key="$ip"; entry_type="host"; fi
251 + if [[ -n "$subnet" ]]; then key="$subnet"; entry_type="subnet"; fi
252 + if [[ -n "$port" ]]; then key="$port"; entry_type="port"; fi
253 +
254 + [[ -z "$key" ]] && log::error "Specify --ip, --subnet, or --port" && return 1
255 +
256 + local tags_str
257 + tags_str=$(IFS=','; echo "${tags[*]}")
258 +
259 + json::hosts_add "$(ctx::hosts)" "$entry_type" "$key" "$name" "$desc" "$tags_str"
260 + log::wg_success "Added ${entry_type}: ${key} → ${name}"
261 + }
262 +
263 + # ============================================
264 + # Remove
265 + # ============================================
266 +
267 + function cmd::hosts::rm() {
268 + local ip="" subnet="" port="" force=false
269 +
270 + while [[ $# -gt 0 ]]; do
271 + case "$1" in
272 + --ip) ip="$2"; shift 2 ;;
273 + --subnet) subnet="$2"; shift 2 ;;
274 + --port) port="$2"; shift 2 ;;
275 + --force) force=true; shift ;;
276 + --help) cmd::hosts::help; return ;;
277 + *) log::error "Unknown flag: $1"; return 1 ;;
278 + esac
279 + done
280 +
281 + local key entry_type
282 + if [[ -n "$ip" ]]; then key="$ip"; entry_type="host"; fi
283 + if [[ -n "$subnet" ]]; then key="$subnet"; entry_type="subnet"; fi
284 + if [[ -n "$port" ]]; then key="$port"; entry_type="port"; fi
285 +
286 + [[ -z "$key" ]] && log::error "Specify --ip, --subnet, or --port" && return 1
287 +
288 + hosts::require_exists "$entry_type" "$key" || return 1
289 +
290 + if ! $force; then
291 + read -r -p "Remove ${entry_type} '${key}'? [y/N] " confirm
292 + case "$confirm" in
293 + [yY]*) ;;
294 + *) log::info "Aborted"; return 0 ;;
295 + esac
296 + fi
297 +
298 + json::hosts_remove "$(ctx::hosts)" "$entry_type" "$key"
299 + log::wg_success "Removed ${entry_type}: ${key}"
300 + }
301 +
302 + function cmd::hosts::_output_json() {
303 + local data
304 + data=$(json::hosts_list "$(ctx::hosts)" 2>/dev/null)
305 +
306 + local -a hosts=()
307 + while IFS='|' read -r type ip name desc tags; do
308 + [[ -z "$type" ]] && continue
309 +
310 + local tags_json="[]"
311 + if [[ -n "$tags" ]]; then
312 + local tags_array
313 + tags_array=$(echo "$tags" | tr ',' '\n' | \
314 + while IFS= read -r t; do [[ -n "$t" ]] && printf '"%s",' "$t"; done | sed 's/,$//')
315 + tags_json="[${tags_array}]"
316 + fi
317 +
318 + hosts+=("$(printf '{"type":"%s","ip":"%s","name":"%s","desc":"%s","tags":%s}' \
319 + "$type" "$ip" "$name" "$desc" "$tags_json")")
320 + done <<< "$data"
321 +
322 + local count=${#hosts[@]}
323 + local array
324 + array=$(printf '%s\n' "${hosts[@]:-}" | paste -sd ',' -)
325 + printf '{"hosts":[%s]}' "${array:-}" | json::envelope "hosts list" "$count"
326 + }
上一頁 下一頁