diff --git a/commands/hosts.command.sh b/commands/hosts.command.sh index 55e5e7b..cd9e10c 100644 --- a/commands/hosts.command.sh +++ b/commands/hosts.command.sh @@ -14,6 +14,8 @@ function cmd::hosts::on_load() { flag::register --tag flag::register --tags flag::register --force + + command::mixin json_output } # ============================================ @@ -71,6 +73,12 @@ EOF 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 "$@" ;; @@ -288,4 +296,30 @@ function cmd::hosts::rm() { 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" } \ No newline at end of file diff --git a/commands/policy.command.sh b/commands/policy.command.sh index 649a540..db2927a 100644 --- a/commands/policy.command.sh +++ b/commands/policy.command.sh @@ -27,6 +27,8 @@ function cmd::policy::on_load() { flag::register --desc flag::register --field flag::register --value + + command::mixin json_output } # ============================================ @@ -79,6 +81,11 @@ function cmd::policy::run() { local subcmd="${1:-list}" shift || true + if command::json; then + cmd::policy::_output_json + return 0 + fi + case "$subcmd" in list) cmd::policy::_list "$@" ;; show) cmd::policy::_show "$@" ;; @@ -244,4 +251,28 @@ function cmd::policy::_set() { json::policy_set_field "$(ctx::policies)" "$name" "$field" "$value" log::ok "Policy '${name}': ${field} = ${value}" +} + +function cmd::policy::_output_json() { + local data + data=$(policy::list_data 2>/dev/null) + + local -a policies=() + while IFS='|' read -r name tunnel_mode default_rule strict_rule auto_apply desc; do + [[ -z "$name" ]] && continue + + local strict_json="false" + [[ "$strict_rule" == "true" ]] && strict_json="true" + local auto_json="true" + [[ "$auto_apply" == "false" ]] && auto_json="false" + + policies+=("$(printf '{"name":"%s","tunnel_mode":"%s","default_rule":"%s","strict_rule":%s,"auto_apply":%s,"desc":"%s"}' \ + "$name" "$tunnel_mode" "${default_rule:-}" \ + "$strict_json" "$auto_json" "$desc")") + done <<< "$data" + + local count=${#policies[@]} + local array + array=$(printf '%s\n' "${policies[@]:-}" | paste -sd ',' -) + printf '{"policies":[%s]}' "${array:-}" | json::envelope "policy list" "$count" } \ No newline at end of file diff --git a/commands/subnet.command.sh b/commands/subnet.command.sh index 0a41956..af7e494 100644 --- a/commands/subnet.command.sh +++ b/commands/subnet.command.sh @@ -22,6 +22,8 @@ function cmd::subnet::on_load() { flag::register --desc flag::register --group flag::register --new-name + + command::mixin json_output } # ============================================ @@ -65,6 +67,11 @@ function cmd::subnet::run() { local subcmd="${1:-list}" shift || true + if command::json; then + cmd::subnet::_output_json + return 0 + fi + case "$subcmd" in list) cmd::subnet::_list "$@" ;; show) cmd::subnet::_show "$@" ;; @@ -291,4 +298,26 @@ function cmd::subnet::_validate_cidr() { log::error "Invalid CIDR format: '${cidr}'" return 1 fi +} + +function cmd::subnet::_output_json() { + local data + data=$(subnet::list_data 2>/dev/null) + + local -a subnets=() + while IFS='|' read -r type cidr display_name tunnel_mode desc is_group group_parent; do + [[ -z "$type" ]] && continue + + local is_group_json="false" + [[ "$is_group" == "true" ]] && is_group_json="true" + + subnets+=("$(printf '{"type":"%s","cidr":"%s","display_name":"%s","tunnel_mode":"%s","desc":"%s","is_group":%s,"group_parent":"%s"}' \ + "$type" "$cidr" "$display_name" "$tunnel_mode" \ + "$desc" "$is_group_json" "${group_parent:-}")") + done <<< "$data" + + local count=${#subnets[@]} + local array + array=$(printf '%s\n' "${subnets[@]:-}" | paste -sd ',' -) + printf '{"subnets":[%s]}' "${array:-}" | json::envelope "subnet list" "$count" } \ No newline at end of file diff --git a/commands/test/integration.sh b/commands/test/integration.sh index 9aac2cb..d440c66 100644 --- a/commands/test/integration.sh +++ b/commands/test/integration.sh @@ -132,6 +132,7 @@ function cmd::test::run_all_integration_sections() { cmd::test::section_net cmd::test::section_subnet cmd::test::section_identity + cmd::test::section_activity cmd::test::section_hosts cmd::test::section_peer_cmd cmd::test::section_group_purge @@ -180,6 +181,7 @@ function cmd::test::section_rules() { cmd::test::run_cmd "rule show --name guest" "Description" rule show --name guest cmd::test::run_cmd "rule show --name user" "Description" rule show --name user cmd::test::run_cmd "rule show --name admin" "Description" rule show --name admin + cmd::test::run_cmd "rule list --json" '"rules":' rule list --json cmd::test::run_cmd_fails "rule show nonexistent" rule show --name nonexistent } @@ -187,6 +189,7 @@ function cmd::test::section_groups() { test::section "Groups" cmd::test::run_cmd "group list" "Groups" group list cmd::test::run_cmd "group show --name family" "Peers:" group show --name family + cmd::test::run_cmd "group list --json" '"groups":' group list --json cmd::test::run_cmd_fails "group show nonexistent" group show --name nonexistent } @@ -238,8 +241,9 @@ function cmd::test::section_net() { cmd::test::run_cmd "net add port again" "Added" net add --name test-svc:web --port 9999:tcp cmd::test::run_cmd "net rm all ports" "Removed" net rm --name test-svc:ports --force cmd::test::run_cmd "net rm service" "Removed" net rm --name test-svc --force - cmd::test::run_cmd_fails "net show nonexistent" net show --name nonexistent-svc - cmd::test::run_cmd_fails "net add port no service" net add --name nonexistent:web --port 80:tcp + cmd::test::run_cmd "net list --json" '"services":' net list --json + cmd::test::run_cmd_fails "net show nonexistent" net show --name nonexistent-svc + cmd::test::run_cmd_fails "net add port no service" net add --name nonexistent:web --port 80:tcp } function cmd::test::section_subnet() { @@ -271,9 +275,14 @@ function cmd::test::section_identity() { cmd::test::run_cmd "identity list" "" identity list cmd::test::run_cmd "identity migrate --dry-run" "Dry run" identity migrate --dry-run cmd::test::run_cmd "identity show nuno" "nuno" identity show --name nuno + cmd::test::run_cmd "identity list --json" '"identities":' identity list --json cmd::test::run_cmd_fails "identity show nonexistent" identity show --name nonexistent } +function cmd::test::section_activity() { + cmd::test::run_cmd "activity --json" '"peers":' activity --json +} + function cmd::test::section_hosts() { test::section "Hosts"