Загрузка данных
#!/bin/zsh
# noteindex — SSH-туннель к https://noteindex.ru/ на macOS
# Использование: noteindex start | noteindex stop | noteindex status
VPS="91.218.115.87"
SSH_USER="user"
LOCAL_PORT=443
REMOTE="127.0.0.1:443"
HOSTS_LINE="127.0.0.1 noteindex.ru"
HOSTS_MARK="# noteindex-tunnel"
PID_FILE="${HOME}/.noteindex-tunnel.pid"
# Не использовать «ssh -N -L» — между -N и -L в процессе есть -f и -o
SSH_MATCH="${LOCAL_PORT}:${REMOTE} ${SSH_USER}@${VPS}"
hosts_has_entry() {
grep -qE '^\s*127\.0\.0\.1\s+noteindex\.ru(\s|$)' /etc/hosts 2>/dev/null
}
hosts_add() {
if hosts_has_entry; then
echo "hosts: запись noteindex.ru уже есть"
return 0
fi
echo "${HOSTS_LINE} ${HOSTS_MARK}" | sudo tee -a /etc/hosts >/dev/null
echo "hosts: добавлено → 127.0.0.1 noteindex.ru"
}
hosts_remove() {
if ! hosts_has_entry; then
echo "hosts: записи noteindex.ru нет"
return 0
fi
sudo sed -i '' '/noteindex\.ru/d' /etc/hosts
echo "hosts: запись noteindex.ru удалена"
}
tunnel_pid() {
sudo pgrep -f "${SSH_MATCH}" 2>/dev/null | head -1
}
tunnel_listening() {
sudo lsof -nP -iTCP:"${LOCAL_PORT}" -sTCP:LISTEN 2>/dev/null | grep -q '[s]sh'
}
tunnel_running() {
tunnel_listening || [[ -n "$(tunnel_pid)" ]]
}
cmd_start() {
if tunnel_running; then
echo "Туннель уже запущен (PID $(tunnel_pid))"
hosts_add
echo "Откройте: https://noteindex.ru/"
return 0
fi
hosts_add
echo "Запуск SSH-туннеля (sudo — пароль Mac; затем пароль root на VPS)…"
if ! sudo ssh -N -f \
-o ExitOnForwardFailure=yes \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-L "${LOCAL_PORT}:${REMOTE}" \
"${SSH_USER}@${VPS}"; then
echo "Ошибка: ssh не подключился. Проверьте пароль и: sudo route delete -host ${VPS}" >&2
exit 1
fi
sleep 2
pid="$(tunnel_pid)"
if [[ -z "${pid}" ]] && tunnel_listening; then
pid="$(sudo lsof -t -iTCP:"${LOCAL_PORT}" -sTCP:LISTEN 2>/dev/null | head -1)"
fi
if [[ -z "${pid}" ]]; then
echo "Ошибка: туннель не слушает порт ${LOCAL_PORT}." >&2
echo "Проверьте: sudo lsof -i :${LOCAL_PORT}" >&2
exit 1
fi
echo "${pid}" > "${PID_FILE}"
echo "Туннель запущен (PID ${pid})"
echo "Откройте: https://noteindex.ru/"
echo "Остановка: noteindex stop"
}
cmd_stop() {
local pid=""
if [[ -f "${PID_FILE}" ]]; then
pid="$(cat "${PID_FILE}")"
sudo kill "${pid}" 2>/dev/null || true
rm -f "${PID_FILE}"
fi
while read -r p; do
[[ -n "${p}" ]] && sudo kill "${p}" 2>/dev/null || true
done < <(sudo pgrep -f "${SSH_MATCH}" 2>/dev/null || true)
sleep 0.5
if tunnel_running; then
echo "Предупреждение: туннель ещё активен — noteindex stop ещё раз или Activity Monitor" >&2
else
echo "Туннель остановлен"
fi
hosts_remove
}
cmd_status() {
if tunnel_running; then
echo "Туннель: запущен (PID $(tunnel_pid))"
else
echo "Туннель: не запущен"
fi
if hosts_has_entry; then
echo "hosts: 127.0.0.1 noteindex.ru — есть"
else
echo "hosts: записи noteindex.ru нет"
fi
}
cmd_usage() {
cat <<'EOF'
noteindex — SSH-туннель к https://noteindex.ru/ (macOS)
Использование:
noteindex start включить (/etc/hosts + туннель в фоне)
noteindex stop остановить туннель и убрать запись из hosts
noteindex status проверить туннель и hosts
Сокращения: start|up, stop|down|off
После start: https://noteindex.ru/
EOF
}
case "${1:-}" in
start|up)
cmd_start
;;
stop|down|off)
cmd_stop
;;
status)
cmd_status
;;
help|-h|--help)
cmd_usage
;;
"")
cmd_usage
;;
*)
echo "Неизвестная команда: ${1}" >&2
echo "Справка: noteindex help" >&2
exit 1
;;
esac