#!/usr/bin/env bash # ============================================ # Client Config # ============================================ function peers::create_client_config() { local name="$1" local type="$2" local ip="$3" local allowed_ips="${4:-$(config::allowed_ips_for "$type" "$(config::default_tunnel_for "$type")")}" local conf conf="$(ctx::clients)/${name}.conf" if [[ -f "$conf" ]]; then log::wg_warning "Client config already exists: ${name}" return 1 fi local private_key private_key=$(keys::private "$name") local server_public_key server_public_key=$(config::server_public_key) cat > "$conf" <> "$config" </dev/null)" ]]; then log::wg_list "No clients configured" return 0 fi for conf in "${dir}"/*.conf; do local client_name client_name=$(basename "$conf" .conf) local ip ip=$(grep "^Address" "$conf" | awk '{print $3}' | cut -d'/' -f1) local public_key public_key=$(keys::public "$client_name" 2>/dev/null || echo "unknown") # Determine type from IP local type="unknown" for t in $(config::device_types); do local subnet subnet=$(config::subnet_for "$t") if string::starts_with "$ip" "$subnet"; then type="$t" break fi done printf " %-30s %-15s %-10s %s\n" \ "$client_name" "$ip" "$type" "$public_key" done } function peers::list_by_type() { local filter_type="$1" local dir dir="$(ctx::clients)" for conf in "${dir}"/*.conf; do local client_name client_name=$(basename "$conf" .conf) local ip ip=$(grep "^Address" "$conf" | awk '{print $3}' | cut -d'/' -f1) local subnet subnet=$(config::subnet_for "$filter_type") if string::starts_with "$ip" "$subnet"; then printf " %-30s %-15s\n" "$client_name" "$ip" fi done } function peers::exists_in_server() { local name="$1" grep -q "^# ${name}$" "$(config::config_file)" } function peers::is_blocked() { local name="$1" ! peers::exists_in_server "$name" } # ============================================ # Default Rule # ============================================ function peers::get_type() { local name="$1" local ip ip=$(peers::get_ip "$name") [[ -z "$ip" ]] && echo "unknown" && return local type="unknown" for t in $(config::device_types); do local subnet subnet=$(config::subnet_for "$t") if string::starts_with "$ip" "${subnet}."; then type="$t" break fi done echo "$type" } function peers::default_rule() { local name="$1" local type type=$(peers::get_type "$name") config::is_guest_type "$type" && echo "guest" || echo "user" } function peers::effective_rule() { local name="$1" local rule rule=$(peers::get_meta "$name" "rule") echo "${rule:---}" } # ============================================ # Query # ============================================ function peers::all() { local dir dir="$(ctx::clients)" for conf in "${dir}"/*.conf; do [[ -f "$conf" ]] || continue basename "$conf" .conf done } function peers::with_rule() { local rule="$1" while IFS= read -r name; do local effective effective=$(peers::effective_rule "$name") [[ "$effective" == "$rule" ]] && echo "$name" done < <(peers::all) } function peers::get_ip() { local name="$1" grep "^Address" "$(ctx::clients)/${name}.conf" 2>/dev/null \ | awk '{print $3}' | cut -d'/' -f1 } function peers::find_by_ip() { local target_ip="$1" while IFS= read -r name; do local ip ip=$(peers::get_ip "$name") [[ "$ip" == "$target_ip" ]] && echo "$name" && return 0 done < <(peers::all) } # ============================================ # Name + Type Parsing # ============================================ function peers::resolve_name() { local name="$1" local type="${2:-}" if [[ -n "$type" ]]; then if ! config::is_valid_type "$type"; then log::error "Invalid device type: ${type}" return 1 fi echo "${type}-${name}" else echo "$name" fi } function peers::require_exists() { local name="$1" if [[ ! -f "$(ctx::clients)/${name}.conf" ]]; then log::error "Client not found: ${name}" return 1 fi } function peers::resolve_and_require() { local name="$1" local type="${2:-}" local resolved resolved=$(peers::resolve_name "$name" "$type") || return 1 peers::require_exists "$resolved" || return 1 echo "$resolved" } # ============================================ # Helpers - Meta File # ============================================ function peers::meta_path() { local name="$1" echo "$(ctx::meta)/${name}.meta" } function peers::get_meta() { local name="$1" key="$2" json::get "$(peers::meta_path "$name")" "$key" } function peers::set_meta() { local name="$1" key="$2" value="$3" json::set "$(peers::meta_path "$name")" "$key" "$value" } function peers::remove_meta() { local name="$1" rm -f "$(peers::meta_path "$name")" } # ============================================ # Live Reload # ============================================ function peers::reload() { wg syncconf "$(config::interface)" <(wg-quick strip "$(config::interface)") log::debug "WireGuard config reloaded" }