#!/usr/bin/env bash # ============================================ # Lifecycle # ============================================ function cmd::config::on_load() { flag::register --name flag::register --type flag::register --force flag::register --dry-run } # ============================================ # Help # ============================================ function cmd::config::help() { cat < Show the WireGuard config file for a client. Options: --name Client name (e.g. phone-nuno) Examples: wgctl config --name phone-nuno wgctl config --name laptop-nuno EOF } # ============================================ # Run # ============================================ function cmd::config::run() { local subcmd="${1:-show}" # If first arg is a flag, treat as 'show' subcommand if [[ "$subcmd" == --* ]]; then subcmd="show" else shift || true fi case "$subcmd" in show) cmd::config::_show "$@" ;; migrate) cmd::config::migrate "$@" ;; help) cmd::config::help ;; *) log::error "Unknown subcommand: '${subcmd}'" cmd::config::help return 1 ;; esac } # ============================================ # Show # ============================================ function cmd::config::_show() { local name="" type="" while [[ $# -gt 0 ]]; do case "$1" in --name) name="$2"; shift 2 ;; --type) type="$2"; shift 2 ;; --help) cmd::config::help; return ;; *) log::error "Unknown flag: $1"; return 1 ;; esac done [[ -z "$name" ]] && log::error "Missing required flag: --name" && return 1 name=$(peers::resolve_and_require "$name" "$type") || return 1 local conf conf="$(ctx::clients)/${name}.conf" log::section "Client Config: ${name}" cat "$conf" } # ============================================ # Migrate # ============================================ function cmd::config::migrate() { local force=false dry_run=false while [[ $# -gt 0 ]]; do case "$1" in --force) force=true; shift ;; --dry-run) dry_run=true; shift ;; --help) cmd::config::help; return ;; *) log::error "Unknown flag: $1"; return 1 ;; esac done local wgctl_dir wgctl_dir="$(ctx::wgctl)" local config_dir="${wgctl_dir}/config" local data_dir="${wgctl_dir}/data" local legacy_conf="${wgctl_dir}/wgctl.conf" local json_conf="${config_dir}/wgctl.json" # Check if already migrated if [[ -f "$json_conf" && ! -f "$legacy_conf" ]]; then log::wg_warning "Already migrated to new config structure" return 0 fi log::section "wgctl Config Migration" printf "\n" printf " This will:\n" printf " 1. Create %s/config/ and %s/data/\n" "$wgctl_dir" "$wgctl_dir" printf " 2. Convert wgctl.conf → wgctl.json\n" printf " 3. Move data files to data/\n\n" if ! $force && ! $dry_run; then read -r -p " Proceed? [y/N] " confirm case "$confirm" in [yY]*) ;; *) log::info "Aborted"; return 0 ;; esac fi local do="" $dry_run && do="echo [dry-run]" # 1. Create directories $dry_run || mkdir -p "$config_dir" "$data_dir" $dry_run && printf " Would create: %s/config/\n" "$wgctl_dir" $dry_run && printf " Would create: %s/data/\n" "$wgctl_dir" # 2. Convert wgctl.conf → wgctl.json if [[ -f "$legacy_conf" ]]; then if ! $dry_run; then config::_convert_to_json "$legacy_conf" "$json_conf" fi printf " %s wgctl.conf → config/wgctl.json\n" "$($dry_run && echo '[dry-run]' || echo '✓')" else log::wg_warning "No wgctl.conf found — creating default wgctl.json" $dry_run || config::_write_default_json "$json_conf" fi # 3. Move data files local -a data_files=( "hosts.json" "services.json" "subnets.json" "policies.json" ) local -a data_dirs=( "rules" "identities" "groups" "blocks" "meta" "peer-history" ) for f in "${data_files[@]}"; do if [[ -f "${wgctl_dir}/${f}" ]]; then $dry_run || mv "${wgctl_dir}/${f}" "${data_dir}/${f}" printf " %s %s → data/%s\n" \ "$($dry_run && echo '[dry-run]' || echo '✓')" "$f" "$f" fi done for d in "${data_dirs[@]}"; do if [[ -d "${wgctl_dir}/${d}" ]]; then $dry_run || mv "${wgctl_dir}/${d}" "${data_dir}/${d}" printf " %s %s/ → data/%s/\n" \ "$($dry_run && echo '[dry-run]' || echo '✓')" "$d" "$d" fi done # 4. Remove legacy conf after successful migration if ! $dry_run && [[ -f "$legacy_conf" ]]; then mv "$legacy_conf" "${legacy_conf}.bak" printf " ✓ wgctl.conf → wgctl.conf.bak (backup)\n" fi printf "\n" $dry_run && log::wg_warning "Dry run — no changes made" \ || log::wg_success "Migration complete" } # function config::_convert_to_json() { # local legacy_file="$1" output_file="$2" # # Read legacy conf into variables # local wg_interface="wg0" wg_endpoint="" wg_dns="10.0.0.103" # local wg_dns_fallback="" wg_port="51820" wg_subnet="10.1.0.0/16" # local wg_lan="10.0.0.0/24" wg_hs_check="300" date_format="eu" # while IFS='=' read -r key value || [[ -n "$key" ]]; do # [[ "$key" =~ ^[[:space:]]*# ]] && continue # [[ -z "${key// }" ]] && continue # key="${key// /}" # value="${value// /}" # case "$key" in # WG_INTERFACE) wg_interface="$value" ;; # WG_ENDPOINT) wg_endpoint="$value" ;; # WG_DNS) wg_dns="$value" ;; # WG_DNS_FALLBACK) wg_dns_fallback="$value" ;; # WG_PORT) wg_port="$value" ;; # WG_SUBNET) wg_subnet="$value" ;; # WG_LAN) wg_lan="$value" ;; # WG_HANDSHAKE_CHECK_TIME_SEC) wg_hs_check="$value" ;; # DATE_FORMAT) date_format="$value" ;; # esac # done < "$legacy_file" # # Build fallback DNS array # local dns_fallback_json="[]" # if [[ -n "$wg_dns_fallback" ]]; then # local fallback_array # fallback_array=$(echo "$wg_dns_fallback" | tr ',' '\n' | \ # while IFS= read -r s; do # s="${s// /}" # [[ -n "$s" ]] && printf '"%s",' "$s" # done | sed 's/,$//') # dns_fallback_json="[${fallback_array}]" # fi # mkdir -p "$(dirname "$output_file")" # cat > "$output_file" << JSON # { # "wireguard": { # "interface": "${wg_interface}", # "endpoint": "${wg_endpoint}", # "port": ${wg_port}, # "subnet": "${wg_subnet}", # "lan": "${wg_lan}" # }, # "dns": { # "primary": "${wg_dns}", # "fallback": ${dns_fallback_json} # }, # "handshake": { # "check_interval_sec": ${wg_hs_check} # }, # "activity": { # "total": {"low": 1000000, "medium": 10000000, "high": 100000000}, # "current": {"low": 1000000, "medium": 10000000, "high": 100000000} # }, # "display": { # "date_format": "${date_format}" # } # } # JSON # } # function config::_write_default_json() { # local output_file="$1" # mkdir -p "$(dirname "$output_file")" # cat > "$output_file" << 'JSON' # { # "wireguard": { # "interface": "wg0", # "endpoint": "", # "port": 51820, # "subnet": "10.1.0.0/16", # "lan": "10.0.0.0/24" # }, # "dns": { # "primary": "10.0.0.103", # "fallback": [] # }, # "handshake": { # "check_interval_sec": 300 # }, # "activity": { # "total": {"low": 1000000, "medium": 10000000, "high": 100000000}, # "current": {"low": 1000000, "medium": 10000000, "high": 100000000} # }, # "display": { # "date_format": "eu" # } # } # JSON # }