- flag::define: variadic constraint args (key:value) instead of bracket string - flag::_parse_constraints_from_args: replaces flag::_parse_and_cache - flag::set_constraint: Option B syntax for post-definition constraints - choices separator: comma (choices:split,full) — no quoting needed - guard against empty _CURRENT_COMMAND in exclusive groups lookup - migrate all commands to new constraint syntax - add helpful error for unknown constraint args
281 lines
No EOL
7.5 KiB
Bash
281 lines
No EOL
7.5 KiB
Bash
#!/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 <<EOF
|
|
Usage: wgctl config --name <name>
|
|
|
|
Show the WireGuard config file for a client.
|
|
|
|
Options:
|
|
--name <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
|
|
# } |