#!/bin/sh # Стабильный URL для Joplin на телефоне при Android-хотспоте: http://<подсеть>.100:8080 # Alias *.100 на en0 (DHCP primary не трогаем). LaunchAgent поднимает alias после reconnect. # # ./joplin-hotspot-alias.sh status # ./joplin-hotspot-alias.sh ensure # # Passwordless: ./joplin-hotspot-alias-install-sudo.sh set -e CONFIG="${JOPLIN_WEBDAV_CONFIG:-$HOME/.config/joplin-webdav/env}" WIFI_DEV="${WIFI_DEVICE:-en0}" PORT="${JOPLIN_WEBDAV_PORT:-8080}" LOG="$HOME/Library/Logs/joplin-webdav/alias.log" ipv4_subnet() { printf '%s\n' "$1" | awk -F. '{print $1"."$2"."$3}' } ipv4_last_octet() { printf '%s\n' "$1" | awk -F. '{print $4}' } default_gateway() { route -n get default 2>/dev/null | awk '/gateway:/{print $2; exit}' } load_config() { if [ -f "$CONFIG" ]; then # shellcheck disable=SC1090 . "$CONFIG" fi PORT="${JOPLIN_WEBDAV_PORT:-$PORT}" } joplin_phone_ip() { load_config gw="$(default_gateway 2>/dev/null || true)" [ -n "$gw" ] || return 1 subnet="$(ipv4_subnet "$gw")" if [ -n "${JOPLIN_STATIC_IP:-}" ]; then last="$(ipv4_last_octet "$JOPLIN_STATIC_IP")" else last=100 fi printf '%s\n' "${subnet}.${last}" } phone_url() { ip="$(joplin_phone_ip 2>/dev/null || true)" [ -n "$ip" ] || return 1 printf 'http://%s:%s\n' "$ip" "$PORT" } has_ip_on_dev() { ip="$1" dev="$2" ifconfig "$dev" 2>/dev/null | grep -q "inet ${ip} " } log_msg() { mkdir -p "$(dirname "$LOG")" printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >>"$LOG" } run_priv() { if [ "$(id -u)" -eq 0 ]; then "$@" else sudo "$@" fi } write_phone_url_env() { url="$(phone_url 2>/dev/null || true)" [ -n "$url" ] && [ -f "$CONFIG" ] || return 0 if grep -q '^JOPLIN_PHONE_URL=' "$CONFIG" 2>/dev/null; then sed -i '' "s|^JOPLIN_PHONE_URL=.*|JOPLIN_PHONE_URL=$url|" "$CONFIG" 2>/dev/null \ || sed -i "s|^JOPLIN_PHONE_URL=.*|JOPLIN_PHONE_URL=$url|" "$CONFIG" else printf 'JOPLIN_PHONE_URL=%s\n' "$url" >>"$CONFIG" fi } cmd_status() { load_config target="$(joplin_phone_ip 2>/dev/null || true)" primary="$(ipconfig getifaddr "$WIFI_DEV" 2>/dev/null || true)" gw="$(default_gateway 2>/dev/null || true)" url="$(phone_url 2>/dev/null || true)" echo "mode: Android-хотспот (alias *.100)" echo "device: $WIFI_DEV" echo "primary: ${primary:-(none)}" echo "gateway: ${gw:-(none)}" echo "joplin IP: ${target:-(no gateway)}" if [ -n "$target" ] && has_ip_on_dev "$target" "$WIFI_DEV"; then echo "alias: OK" elif [ -n "$target" ] && [ "$primary" = "$target" ]; then echo "alias: OK (primary)" elif [ -n "$target" ]; then echo "alias: MISSING — телефон не достучится" echo " fix: $0 ensure" fi if [ -n "$url" ]; then echo "" echo "URL на телефоне (работает с Android-хотспота):" echo " $url" echo "" echo "⚠ http://*.local:8080 с телефона НЕ работает (Android не резолвит mDNS клиентов хотспота)" fi } cmd_ensure() { target="$(joplin_phone_ip 2>/dev/null || true)" if [ -z "$target" ]; then log_msg "skip: no gateway" exit 0 fi primary="$(ipconfig getifaddr "$WIFI_DEV" 2>/dev/null || true)" if [ -z "$primary" ]; then log_msg "skip: no primary IP" exit 0 fi if [ "$primary" = "$target" ] || has_ip_on_dev "$target" "$WIFI_DEV"; then write_phone_url_env exit 0 fi log_msg "add alias $target on $WIFI_DEV (primary $primary)" run_priv ifconfig "$WIFI_DEV" inet "$target" netmask 255.255.255.0 alias write_phone_url_env echo "alias OK: $target" echo "URL на телефоне: $(phone_url)" } usage() { echo "Usage: $(basename "$0") status|ensure" >&2 exit 1 } case "${1:-ensure}" in status) cmd_status ;; ensure) cmd_ensure ;; -h|--help) usage ;; *) usage ;; esac