diff --git a/commands/activity.command.sh b/commands/activity.command.sh index a2eb16a..207286e 100644 --- a/commands/activity.command.sh +++ b/commands/activity.command.sh @@ -14,6 +14,8 @@ function cmd::activity::on_load() { flag::register --hours flag::register --type flag::register --dropped + + command::mixin json_output } # ============================================ @@ -70,6 +72,11 @@ function cmd::activity::run() { esac done + if command::json; then + cmd::activity::_output_json "$hours" + return 0 + fi + # Resolve peer name if type provided if [[ -n "$filter_peer" && -n "$filter_type" ]]; then filter_peer=$(peers::resolve_and_require "$filter_peer" "$filter_type") || return 1 @@ -188,3 +195,51 @@ function cmd::activity::run() { echo "" } +function cmd::activity::_output_json() { + local hours="${1:-24}" + local data + data=$(json::activity_aggregate \ + "$(ctx::fw_events_log)" "$(ctx::events_log)" \ + "$(config::interface)" "$(ctx::net)" \ + "$(ctx::clients)" "$(ctx::meta)" \ + "$hours" "" "" 2>/dev/null) + + local -a peers=() + local current_peer="" current_services="" + local -a current_svc_list=() + + while IFS='|' read -r record_type rest; do + case "$record_type" in + peer) + # Flush previous peer + if [[ -n "$current_peer" ]]; then + local svc_array + svc_array=$(printf '%s\n' "${current_svc_list[@]:-}" | paste -sd ',' -) + peers+=("${current_peer},\"services\":[${svc_array:-}]}") + current_svc_list=() + fi + local name rx tx drops + IFS='|' read -r name rx tx drops <<< "$rest" + current_peer=$(printf '{"name":"%s","rx":%s,"tx":%s,"drops":%s' \ + "$name" "$rx" "$tx" "$drops") + ;; + service) + local peer dest count + IFS='|' read -r peer dest count <<< "$rest" + current_svc_list+=("$(printf '{"dest":"%s","drops":%s}' "$dest" "$count")") + ;; + esac + done <<< "$data" + + # Flush last peer + if [[ -n "$current_peer" ]]; then + local svc_array + svc_array=$(printf '%s\n' "${current_svc_list[@]:-}" | paste -sd ',' -) + peers+=("${current_peer},\"services\":[${svc_array:-}]}") + fi + + local count=${#peers[@]} + local array + array=$(printf '%s\n' "${peers[@]:-}" | paste -sd ',' -) + printf '{"peers":[%s]}' "${array:-}" | json::envelope "activity" "$count" +} diff --git a/commands/group.command.sh b/commands/group.command.sh index 0fe8fea..7cd4751 100644 --- a/commands/group.command.sh +++ b/commands/group.command.sh @@ -15,6 +15,7 @@ function cmd::group::on_load() { flag::register --force flag::register --all flag::register --dry-run + command::mixin json_output } # ============================================ @@ -83,6 +84,11 @@ function cmd::group::run() { local subcmd="${1:-help}" shift || true + if command::json; then + cmd::group::_output_json + return 0 + fi + case "$subcmd" in list|ls) cmd::group::list "$@" ;; show) cmd::group::show "$@" ;; @@ -908,4 +914,23 @@ function cmd::group::purge_stale() { log::wg_success "${action} ${total_removed} stale peer(s)..." fi fi +} + +function cmd::group::_output_json() { + local groups_dir + groups_dir="$(ctx::groups)" + local data + data=$(json::group_list_data "$groups_dir" "$(ctx::blocks)" "$(ctx::clients)" 2>/dev/null) + + local -a groups=() + while IFS='|' read -r name desc peer_count blocked_count; do + [[ -z "$name" ]] && continue + groups+=("$(printf '{"name":"%s","desc":"%s","peer_count":%s,"blocked_count":%s}' \ + "$name" "$desc" "$peer_count" "$blocked_count")") + done <<< "$data" + + local count=${#groups[@]} + local array + array=$(printf '%s\n' "${groups[@]:-}" | paste -sd ',' -) + printf '{"groups":[%s]}' "${array:-}" | json::envelope "group list" "$count" } \ No newline at end of file diff --git a/commands/identity.command.sh b/commands/identity.command.sh index f498eb0..3f4517f 100644 --- a/commands/identity.command.sh +++ b/commands/identity.command.sh @@ -20,9 +20,7 @@ function cmd::identity::on_load() { flag::register --peer flag::register --dry-run flag::register --force - # rule subcommand flags flag::register --rule - # options subcommand flags flag::register --policy flag::register --set-strict-rule flag::register --unset-strict-rule @@ -31,6 +29,8 @@ function cmd::identity::on_load() { flag::register --field flag::register --value flag::register --migrate + + command::mixin json_output } # ============================================ @@ -78,6 +78,11 @@ function cmd::identity::run() { local subcmd="${1:-list}" shift || true + if command::json && [[ "$subcmd" == "list" ]]; then + cmd::identity::_output_json + return 0 + fi + case "$subcmd" in list) cmd::identity::_list "$@" ;; show) cmd::identity::_show "$@" ;; @@ -565,4 +570,41 @@ function cmd::identity::_options() { if ! $changed; then cmd::identity::_rule_show --name "$name" fi -} \ No newline at end of file +} + +function cmd::identity::_output_json() { + local data + data=$(identity::list_data 2>/dev/null) + + local -a identities=() + while IFS='|' read -r name peer_count types rules policy; do + [[ -z "$name" ]] && continue + + # Build rules array + local rules_json="[]" + if [[ -n "$rules" ]]; then + local rules_array + rules_array=$(echo "$rules" | tr ',' '\n' | \ + while IFS= read -r r; do [[ -n "$r" ]] && printf '"%s",' "$r"; done | sed 's/,$//') + rules_json="[${rules_array}]" + fi + + # Build types array (was comma-separated string) + local types_json="[]" + if [[ -n "$types" ]]; then + local types_array + types_array=$(echo "$types" | tr ',' '\n' | \ + while IFS= read -r t; do [[ -n "$t" ]] && printf '"%s",' "$t"; done | sed 's/,$//') + types_json="[${types_array}]" + fi + + identities+=("$(printf '{"name":"%s","peer_count":%s,"types":%s,"rules":%s,"policy":"%s"}' \ + "$name" "$peer_count" "$types_json" "$rules_json" "$policy")") + done <<< "$data" + + local count=${#identities[@]} + local array + array=$(printf '%s\n' "${identities[@]:-}" | paste -sd ',' -) + printf '{"identities":[%s]}' "${array:-}" | json::envelope "identity list" "$count" +} + \ No newline at end of file diff --git a/commands/mixins/MIXIN_TEMPLATE.mixin.sh b/commands/mixins/MIXIN_TEMPLATE.mixin.sh.template similarity index 100% rename from commands/mixins/MIXIN_TEMPLATE.mixin.sh rename to commands/mixins/MIXIN_TEMPLATE.mixin.sh.template diff --git a/commands/net.command.sh b/commands/net.command.sh index 2045a8e..b36ed01 100644 --- a/commands/net.command.sh +++ b/commands/net.command.sh @@ -8,6 +8,8 @@ function cmd::net::on_load() { flag::register --tag flag::register --detailed flag::register --force + + command::mixin json_output } function cmd::net::help() { @@ -58,6 +60,12 @@ EOF function cmd::net::run() { local subcmd="${1:-list}" shift || true + + if command::json; then + cmd::net::_output_json + return 0 + fi + case "$subcmd" in list) cmd::net::list "$@" ;; show) cmd::net::show "$@" ;; @@ -304,4 +312,31 @@ function cmd::net::rm() { json::net_remove "$(ctx::net)" "$name" log::wg_success "Removed: ${name}" return 0 +} + +function cmd::net::_output_json() { + local net_file + net_file="$(ctx::net)" + local data + data=$(json::net_list "$net_file" 2>/dev/null) + + local -a services=() + while IFS='|' read -r name ip desc tags port_count; do + [[ -z "$name" ]] && continue + # Build tags array + 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 + services+=("$(printf '{"name":"%s","ip":"%s","desc":"%s","tags":%s,"port_count":%s}' \ + "$name" "$ip" "$desc" "$tags_json" "$port_count")") + done <<< "$data" + + local count=${#services[@]} + local array + array=$(printf '%s\n' "${services[@]:-}" | paste -sd ',' -) + printf '{"services":[%s]}' "${array:-}" | json::envelope "net list" "$count" } \ No newline at end of file diff --git a/commands/rule.command.sh b/commands/rule.command.sh index f592d83..e5b2611 100644 --- a/commands/rule.command.sh +++ b/commands/rule.command.sh @@ -31,6 +31,8 @@ function cmd::rule::on_load() { flag::register --force flag::register --type flag::register --all + + command::mixin json_output } # ============================================ @@ -116,6 +118,11 @@ function cmd::rule::run() { local subcmd="${1:-help}" shift || true + if command::json; then + cmd::rule::_output_json + return 0 + fi + case "$subcmd" in list|ls) cmd::rule::list "$@" ;; show|inspect) cmd::rule::show "$@" ;; @@ -671,4 +678,38 @@ function cmd::rule::reapply() { rule::require_exists "$name" || return 1 rule::reapply_all "$name" log::wg_success "Rule '${name}' reapplied" +} + +function cmd::rule::_output_json() { + local rules_dir + rules_dir="$(ctx::rules)" + local data + data=$(json::rule_list_data "$rules_dir" "$(ctx::meta)" 2>/dev/null) + + local -a rules=() + while IFS='|' read -r name desc n_allows n_blocks peer_count extends is_base group; do + [[ -z "$name" ]] && continue + + # Build extends array + local extends_json="[]" + if [[ -n "$extends" ]]; then + local ext_array + ext_array=$(echo "$extends" | tr ',' '\n' | \ + while IFS= read -r e; do [[ -n "$e" ]] && printf '"%s",' "$e"; done | sed 's/,$//') + extends_json="[${ext_array}]" + fi + + # Convert Python bool to JSON bool + local is_base_json="false" + [[ "$is_base" == "True" ]] && is_base_json="true" + + rules+=("$(printf '{"name":"%s","desc":"%s","allows":%s,"blocks":%s,"peer_count":%s,"extends":%s,"is_base":%s,"group":"%s"}' \ + "$name" "$desc" "$n_allows" "$n_blocks" "$peer_count" \ + "$extends_json" "$is_base_json" "$group")") + done <<< "$data" + + local count=${#rules[@]} + local array + array=$(printf '%s\n' "${rules[@]:-}" | paste -sd ',' -) + printf '{"rules":[%s]}' "${array:-}" | json::envelope "rule list" "$count" } \ No newline at end of file