Add Object B and self-growth canons with Cursor ledger rules; refresh connectivity and VPN runbooks.
Track day metrics/anamnesis HARD rules under .cursor/rules and personal docs so each screening updates days/{KIN}, dynamics, and big-data ledger.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,212 +1,137 @@
|
||||
# Cursor IDE — работа из РФ и обход PING timeout
|
||||
# Cursor IDE — сеть, прокси, Agent
|
||||
|
||||
> **Проблема:** из России Cursor показывает `PING timed out`, Agent не отвечает.
|
||||
> **Временный обход:** Disable Http2 в настройках — помогает, но всё сильно замедляется.
|
||||
> **Рекомендуемое решение:** SOCKS-прокси через SSH-туннель на сервер с нормальным доступом (DO / RU VPS), без отключения HTTP/2.
|
||||
> **Смена страны / «Cursor не подключается», а интернет живой:** почти всегда мёртвый SOCKS в `settings.json` (`127.0.0.1:1090` + `http.proxySupport: override`).
|
||||
> **Фикс:** снять proxy → Cmd+Q. Подробно: **[RUNBOOK.md](./RUNBOOK.md)** (логи 2026-07, диагностика).
|
||||
|
||||
**Связанные гайды:** [PROXY_GUIDE.md](../connectivity/PROXY_GUIDE.md), [BUILD_AAB_FROM_RUSSIA_GUIDE.md](../connectivity/BUILD_AAB_FROM_RUSSIA_GUIDE.md) — тот же паттерн «туннель через прокси-сервер». **Выход через DO (как VPN на 209.38.32.21):** [docs/connectivity/vpn/README.md](../connectivity/vpn/README.md). **Windows:** [WINDOWS_CURSOR_TUNNEL.md](../connectivity/WINDOWS_CURSOR_TUNNEL.md) и скрипт `scripts/cursor-socks-tunnel.ps1`.
|
||||
> **Из РФ / DPI:** SOCKS через SSH на **10809** (не 1090). Disable Http2 — только временный костыль.
|
||||
|
||||
**Связанные:** [PROXY_GUIDE.md](../../connectivity/PROXY_GUIDE.md), [SSH_EXIT_MODES.md](../../connectivity/vpn/SSH_EXIT_MODES.md), [WINDOWS_CURSOR_TUNNEL.md](../../connectivity/WINDOWS_CURSOR_TUNNEL.md).
|
||||
|
||||
---
|
||||
|
||||
## Содержание
|
||||
|
||||
1. [Симптомы и причина](#-симптомы-и-причина)
|
||||
2. [Обход 1: Disable Http2 (простой, но медленный)](#-обход-1-disable-http2-простой-но-медленный)
|
||||
3. [Обход 2: SOCKS через SSH-туннель (рекомендуется)](#-обход-2-socks-через-ssh-туннель-рекомендуется)
|
||||
4. [Проверка и диагностика](#-проверка-и-диагностика)
|
||||
5. [Troubleshooting](#-troubleshooting)
|
||||
6. [Ссылки](#-ссылки)
|
||||
1. [Показательный инцидент: SOCKS 1090](#показательный-инцидент-socks-1090)
|
||||
2. [Симптомы из РФ (PING timeout)](#симптомы-из-рф-ping-timeout)
|
||||
3. [Обход 1: Disable Http2](#обход-1-disable-http2)
|
||||
4. [Обход 2: SOCKS 10809](#обход-2-socks-10809)
|
||||
5. [Проверка и troubleshooting](#проверка-и-troubleshooting)
|
||||
6. [Ссылки](#ссылки)
|
||||
|
||||
---
|
||||
|
||||
## Симптомы и причина
|
||||
## Показательный инцидент: SOCKS 1090
|
||||
|
||||
### Что видно в Cursor
|
||||
**Контекст:** после смены страны Cursor «не подключался». Интернет, DNS и HTTP/2 — **исправны**.
|
||||
|
||||
- В чате с Agent: **Request ID: … [unavailable] PING timed out**, `_he: [unavailable] PING timed out`.
|
||||
- Стек указывает на `workbench.desktop.main.js`, `streamFromAgentBackend` / `getAgentStreamResponse` — обрыв связи с бэкендом агента.
|
||||
**Ошибка в логах / Network Diagnostics (единственная настоящая причина):**
|
||||
|
||||
### Почему так происходит
|
||||
```
|
||||
Failed to establish a socket connection to proxies: SOCKS 127.0.0.1:1090
|
||||
```
|
||||
|
||||
1. **HTTP/2 и маршрутизация**
|
||||
Cursor использует HTTP/2 для стриминга к `*.cursor.sh` (в т.ч. `agent.api5.cursor.sh`). В части сетей (в т.ч. из РФ) запросы уходят в CDN (например Cloudflare) с неудачной конфигурацией SSL/HTTP2 или блокировкой по DPI, из‑за чего соединение обрывается или уходит в таймаут.
|
||||
**Источник** — не macOS и не Cursor API:
|
||||
|
||||
2. **Disable Http2**
|
||||
Отключение HTTP/2 переводит трафик на HTTP/1.1 и часто убирает обрывы, но увеличивает нагрузку и задержки — «всё становится медленным», возможны дополнительные таймауты агента.
|
||||
```json
|
||||
"http.proxy": "socks5://127.0.0.1:1090",
|
||||
"http.proxySupport": "override"
|
||||
```
|
||||
|
||||
3. **Идея нормального обхода**
|
||||
Пропускать только трафик Cursor через сервер с хорошим доступом к Cursor API (VPS в DO, РФ и т.п.). До этого сервера — SSH (обычно не режется). От сервера до `*.cursor.sh` — уже «нормальный» канал, HTTP/2 работает без костылей.
|
||||
`override` заставляет Cursor **всегда** ходить через локальный SOCKS, даже когда порт **1090 никто не слушает**.
|
||||
|
||||
**Что помогло:** отключение прокси в Cursor (`http.proxySupport: "off"`, убрать `http.proxy`) + полный перезапуск.
|
||||
|
||||
**Правило:** при прямом интернете — **без** жёсткого proxy в settings. Прокси включать только пока жив туннель на **10809**.
|
||||
|
||||
Полный чеклист команд и выдержки из логов за 2 суток: [RUNBOOK.md](./RUNBOOK.md).
|
||||
|
||||
---
|
||||
|
||||
## Обход 1: Disable Http2 (простой, но медленный)
|
||||
## Симптомы из РФ (PING timeout)
|
||||
|
||||
Если нужно быстро восстановить работу без настройки туннеля:
|
||||
|
||||
1. **Cursor → Settings** (Cmd+, / Ctrl+,).
|
||||
2. Поиск: **HTTP**.
|
||||
3. Включить **Cursor > General: Disable Http2**.
|
||||
|
||||
Минус: выше задержки и нагрузка, возможны таймауты агента при длинных ответах. Для постоянной работы из РФ предпочтительно [Обход 2](#-обход-2-socks-через-ssh-туннель-рекомендуется).
|
||||
- Chat / Agent: `PING timed out`, Request ID unavailable.
|
||||
- Часто помогает временный **Disable Http2**, но всё тормозит.
|
||||
- Нормальный путь: SOCKS **10809** через RU→DO (см. ниже).
|
||||
|
||||
---
|
||||
|
||||
## Обход 2: SOCKS через SSH-туннель (рекомендуется)
|
||||
## Обход 1: Disable Http2
|
||||
|
||||
Трафик Cursor идёт в интернет через ваш VPS. До VPS — SSH (один порт), с VPS — обычный доступ к Cursor API, HTTP/2 остаётся включённым.
|
||||
1. Settings → поиск **HTTP** → **Disable Http2** = on.
|
||||
2. Минус: медленнее, больше таймаутов агента.
|
||||
|
||||
### Схема
|
||||
Если Diagnostics показывает **HTTP/2: Success** (как вне РФ) — параметр лучше **выключить** (`false` / удалить ключ).
|
||||
|
||||
---
|
||||
|
||||
## Обход 2: SOCKS 10809
|
||||
|
||||
```
|
||||
Cursor → SOCKS 127.0.0.1:10809 → SSH → RU hsites-ahau → DO 209.38.32.21
|
||||
↓
|
||||
HTTPS/HTTP2 → *.cursor.sh
|
||||
Cursor → socks5://127.0.0.1:10809 → SSH → RU (hsites-ahau) → DO → *.cursor.sh
|
||||
```
|
||||
|
||||
При **`CURSOR_SOCKS_VIA_DO=1`** (по умолчанию) — **только** цепочка **Mac → RU → DO** (`telegram-socks-via-do`). Если **`hsites-ahau`** недоступен, скрипт **завершается с ошибкой** (прямой Mac→DO для SOCKS не поднимается). Публичный IP снаружи — **DO** (шаблон `Host telegram-socks-via-do` в [PROXY_GUIDE.md](../connectivity/PROXY_GUIDE.md)).
|
||||
|
||||
На стороне DO не нужен отдельный прокси-сервис — только SSH и те же `Host`, что для Telegram.
|
||||
|
||||
### Предположения
|
||||
|
||||
- В **`~/.ssh/config`** задан **`telegram-socks-via-do`** с **`ProxyJump hsites-ahau`** (шаблон в [PROXY_GUIDE.md](../connectivity/PROXY_GUIDE.md)).
|
||||
- Локально свободен порт **10809** (не путать с **1081** для Telegram).
|
||||
|
||||
### Шаг 1: Поднять SOCKS-туннель
|
||||
|
||||
Рекомендуется скрипт (выход через DO по умолчанию):
|
||||
### Поднять туннель
|
||||
|
||||
```bash
|
||||
./docs/cursor/scripts/cursor-socks-tunnel.sh start
|
||||
./docs/apps/cursor/scripts/cursor-socks-tunnel.sh start
|
||||
./docs/apps/cursor/scripts/cursor-socks-tunnel.sh status
|
||||
```
|
||||
|
||||
Переменные: **`CURSOR_SOCKS_PORT`** (по умолчанию 10809), **`CURSOR_SOCKS_VIA_DO=0`** — только RU (`CURSOR_TUNNEL_HOST`, по умолчанию `hsites-ahau`).
|
||||
Или: `./scripts/vpn/exit-via-ru-do.sh` (см. [SSH_EXIT_MODES.md](../../connectivity/vpn/SSH_EXIT_MODES.md)).
|
||||
|
||||
Вручную (выход с DO, как у скрипта):
|
||||
Порт по умолчанию: **10809**. Не путать с **1081** (Telegram) и устаревшим **1090**.
|
||||
|
||||
```bash
|
||||
pkill -f "ssh.*-D 10809" 2>/dev/null
|
||||
ssh -D 10809 -f -N -o ServerAliveInterval=60 -o ServerAliveCountMax=4 telegram-socks-via-do
|
||||
```
|
||||
|
||||
Только RU (без DO в цепочке выхода):
|
||||
|
||||
```bash
|
||||
CURSOR_SOCKS_VIA_DO=0 ./docs/cursor/scripts/cursor-socks-tunnel.sh start
|
||||
# или: ssh -D 10809 -f -N hsites-ahau
|
||||
```
|
||||
|
||||
Проверка: туннель держится, пока сессия не разорвана; см. [Проверка и диагностика](#-проверка-и-диагностика).
|
||||
|
||||
### Шаг 2: Настроить Cursor на использование SOCKS
|
||||
|
||||
1. **Cursor → Settings** (Cmd+, / Ctrl+,), поиск: **proxy**.
|
||||
2. Заполнить:
|
||||
- **Http: Proxy** — `socks5://127.0.0.1:10809`
|
||||
- **Http: Proxy Strict SSL** — при необходимости отключить только если заведомо знаете, что за прокси (для одного своего VPS обычно не требуется).
|
||||
|
||||
Или в `settings.json` (Cursor: Open User Settings (JSON)):
|
||||
### Настройки Cursor (только пока туннель жив)
|
||||
|
||||
```json
|
||||
{
|
||||
"http.proxy": "socks5://127.0.0.1:10809",
|
||||
"https.proxy": "socks5://127.0.0.1:10809"
|
||||
"http.proxySupport": "override",
|
||||
"http.disableHttp2": false
|
||||
}
|
||||
```
|
||||
|
||||
### Шаг 3: Не отключать HTTP/2
|
||||
### Вернуться на локальный IP
|
||||
|
||||
**Cursor > General: Disable Http2** — выключить (оставить HTTP/2 включённым). Трафик к Cursor API идёт с DO (или с RU, если включён только RU-туннель).
|
||||
```bash
|
||||
./scripts/vpn/exit-via-local.sh
|
||||
```
|
||||
|
||||
### Шаг 4: Запуск Cursor при уже поднятом туннеле
|
||||
Затем в settings:
|
||||
|
||||
Порядок каждый раз:
|
||||
```json
|
||||
{
|
||||
"http.proxy": "",
|
||||
"http.proxySupport": "off"
|
||||
}
|
||||
```
|
||||
|
||||
1. В терминале: `./docs/cursor/scripts/cursor-socks-tunnel.sh start` (или ручная команда из шага 1).
|
||||
2. Запуск Cursor как обычно.
|
||||
|
||||
Чтобы не забывать туннель, можно завести скрипт запуска (см. [Скрипт запуска с туннелем](#скрипт-запуска-с-туннелем)).
|
||||
Cmd+Q → снова открыть Cursor.
|
||||
|
||||
---
|
||||
|
||||
## Проверка и диагностика
|
||||
|
||||
### Туннель поднят
|
||||
## Проверка и troubleshooting
|
||||
|
||||
```bash
|
||||
# Должен слушать 10809
|
||||
lsof -i :10809
|
||||
# или
|
||||
nc -z 127.0.0.1 10809 && echo "OK"
|
||||
# Кто слушает SOCKS
|
||||
lsof -nP -iTCP:10809 -sTCP:LISTEN
|
||||
lsof -nP -iTCP:1090 -sTCP:LISTEN || echo "1090 free (ожидаемо без старого прокси)"
|
||||
|
||||
# API через туннель
|
||||
curl -x socks5h://127.0.0.1:10809 -sI --max-time 15 https://agent.api5.cursor.sh | head -5
|
||||
|
||||
# Мёртвый 1090 в профиле
|
||||
grep -n 'http.proxy\|1090\|proxySupport' ~/Library/Application\ Support/Cursor/User/settings.json
|
||||
|
||||
# Логи
|
||||
grep -R "SOCKS 127.0.0.1" ~/Library/Application\ Support/Cursor/logs --include='*.log' 2>/dev/null | tail -10
|
||||
```
|
||||
|
||||
### Доступ к Cursor API через SOCKS (curl)
|
||||
|
||||
```bash
|
||||
curl -x socks5h://127.0.0.1:10809 -sI https://agent.api5.cursor.sh 2>&1 | head -5
|
||||
```
|
||||
|
||||
Успех: в ответе есть HTTP/2 или заголовки от сервера. Ошибка соединения или таймаут — туннель не работает или порт занят.
|
||||
|
||||
### Логи Cursor
|
||||
|
||||
При проблемах с подключением:
|
||||
|
||||
- **macOS:** `~/Library/Application Support/Cursor/logs/main.log`
|
||||
- Искать по `ERROR`, `PING`, `timeout`, `HTTP2`, `ECONNREFUSED`, `ETIMEDOUT`.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### После включения прокси Cursor вообще не подключается
|
||||
|
||||
- Убедиться, что туннель запущен: `lsof -i :10809`.
|
||||
- Проверить доступ через SOCKS: `curl -x socks5h://127.0.0.1:10809 -sI https://agent.api5.cursor.sh`.
|
||||
- Временно в настройках Cursor убрать `http.proxy` / `https.proxy` и проверить без прокси (например, с Disable Http2) — если так работает, проблема в туннеле или порте.
|
||||
|
||||
### PING timed out остаётся даже через прокси
|
||||
|
||||
- Перезапустить туннель и Cursor (полностью закрыть приложение и открыть снова).
|
||||
- В настройках Cursor убедиться, что **Disable Http2** выключен (HTTP/2 включён).
|
||||
- Проверить, что в настройках указан именно `socks5://127.0.0.1:10809` (без опечаток, порт совпадает с `-D 10809`).
|
||||
|
||||
### Порт 10809 занят
|
||||
|
||||
Выбрать другой порт, например 10810:
|
||||
|
||||
```bash
|
||||
ssh -D 10810 -f -N hsites-ahau
|
||||
```
|
||||
|
||||
И в настройках Cursor указать `socks5://127.0.0.1:10810`.
|
||||
|
||||
### Туннель рвётся при долгой неактивности
|
||||
|
||||
Поддерживать соединение помогут опции SSH:
|
||||
|
||||
```bash
|
||||
ssh -D 10809 -f -N -o ServerAliveInterval=30 -o ServerAliveCountMax=6 hsites-ahau
|
||||
```
|
||||
|
||||
При необходимости можно вынести эту команду в скрипт или systemd/supervisor на стороне клиента.
|
||||
|
||||
---
|
||||
|
||||
## Скрипт запуска туннеля
|
||||
|
||||
В репозитории есть скрипт для поднятия/остановки SOCKS-туннеля:
|
||||
|
||||
```bash
|
||||
# Из корня репо
|
||||
./docs/cursor/scripts/cursor-socks-tunnel.sh start # поднять туннель
|
||||
./docs/cursor/scripts/cursor-socks-tunnel.sh status # проверить
|
||||
./docs/cursor/scripts/cursor-socks-tunnel.sh stop # остановить
|
||||
```
|
||||
|
||||
Переменные окружения: `CURSOR_SOCKS_PORT` (10809), **`CURSOR_SOCKS_VIA_DO`** (по умолчанию **1** — выход через DO), при **`CURSOR_SOCKS_VIA_DO=0`** — `CURSOR_TUNNEL_HOST` (по умолчанию `hsites-ahau`).
|
||||
|
||||
**Запуск Cursor после туннеля (macOS):** после `./docs/cursor/scripts/cursor-socks-tunnel.sh start` откройте Cursor как обычно; при настроенных `http.proxy`/`https.proxy` трафик пойдёт через SOCKS.
|
||||
| Симптом | Действие |
|
||||
|---------|----------|
|
||||
| `SOCKS 127.0.0.1:1090` / API FAIL при живом интернете | [RUNBOOK.md](./RUNBOOK.md) — снять proxy |
|
||||
| Proxy на 10809, но Cursor мёртв | `cursor-socks-tunnel.sh status` / `exit-via-ru-do.sh` |
|
||||
| PING timeout из РФ без прокси | Туннель 10809 или временно Disable Http2 |
|
||||
|
||||
---
|
||||
|
||||
@@ -214,12 +139,12 @@ ssh -D 10809 -f -N -o ServerAliveInterval=30 -o ServerAliveCountMax=6 hsites-aha
|
||||
|
||||
| Документ | Назначение |
|
||||
|----------|------------|
|
||||
| [PROXY_GUIDE.md](../connectivity/PROXY_GUIDE.md) | Прокси ru.hunab.app, SSH ProxyCommand для scp/ssh на прод |
|
||||
| [WINDOWS_CURSOR_TUNNEL.md](../connectivity/WINDOWS_CURSOR_TUNNEL.md) | Cursor из РФ на **Windows**: туннель, скрипт .ps1, автозапуск |
|
||||
| [BUILD_AAB_FROM_RUSSIA_GUIDE.md](../connectivity/BUILD_AAB_FROM_RUSSIA_GUIDE.md) | Сборка AAB из РФ через Maven proxy + SSH-туннель |
|
||||
| [Cursor Forum — HTTP/2 network error](https://forum.cursor.com/t/http2-network-error-with-cursor-ide-and-cursor-cli/147318) | Обсуждение ошибок HTTP/2 и региональной маршрутизации |
|
||||
| [Cursor Forum — Agent timeouts when HTTP2 disabled](https://forum.cursor.com/t/agent-timeouts-much-more-when-http2-disabled/76517) | Таймауты агента при отключённом HTTP/2 |
|
||||
| **[RUNBOOK.md](./RUNBOOK.md)** | RCA SOCKS 1090, чеклист, логи |
|
||||
| [SSH_EXIT_MODES.md](../../connectivity/vpn/SSH_EXIT_MODES.md) | local / ru / ru-do |
|
||||
| [PROXY_EMERGENCY.md](../../connectivity/PROXY_EMERGENCY.md) | Экстренный SSH / SOCKS |
|
||||
| [WINDOWS_CURSOR_TUNNEL.md](../../connectivity/WINDOWS_CURSOR_TUNNEL.md) | Windows |
|
||||
| [scripts/cursor-socks-tunnel.sh](./scripts/cursor-socks-tunnel.sh) | Туннель macOS |
|
||||
|
||||
---
|
||||
|
||||
*Последнее обновление: 2026-04-08*
|
||||
*Обновлено: 2026-07-12 — инцидент смены страны / SOCKS 1090.*
|
||||
|
||||
176
docs/apps/cursor/RUNBOOK.md
Normal file
176
docs/apps/cursor/RUNBOOK.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# Runbook: Cursor не подключается (Agent / Chat / API)
|
||||
|
||||
Симптом: сеть на Mac **работает**, браузер открывает сайты, DNS OK, но Cursor — Agent, Chat, Marketplace, auth — падают или «не подключается» после смены страны / сети.
|
||||
|
||||
---
|
||||
|
||||
## Показательная диагностика (2026-07, смена страны → ID)
|
||||
|
||||
### Что показала Network Diagnostics Cursor
|
||||
|
||||
| Проверка | Результат |
|
||||
|----------|-----------|
|
||||
| Интернет | OK |
|
||||
| DNS | OK |
|
||||
| HTTP/2 | OK |
|
||||
| SSL / API / Chat / Agent | FAIL |
|
||||
|
||||
### Единственная настоящая ошибка в логах
|
||||
|
||||
```
|
||||
Failed to establish a socket connection to proxies: SOCKS 127.0.0.1:1090
|
||||
```
|
||||
|
||||
За **2 суток** (сессии `20260709T150616`, `20260712T121524`, `20260712T122718`) — **тысячи** повторов этой ошибки в:
|
||||
|
||||
- `Cursor Structured Logs*.log`
|
||||
- `renderer.log`
|
||||
- `cursor-sentry-events*.log`
|
||||
- Agent Exec / Indexing & Retrieval
|
||||
|
||||
Типичные вызовы: `refreshAuthentication`, `getEmailAndSignUpType`, `GetEffectiveUserPlugins`, `GetGithubInstallations`, unary AI connect.
|
||||
|
||||
### Причина (не сеть и не «Индонезия»)
|
||||
|
||||
В `~/Library/Application Support/Cursor/User/settings.json` было жёстко:
|
||||
|
||||
```json
|
||||
"http.proxy": "socks5://127.0.0.1:1090",
|
||||
"http.proxySupport": "override"
|
||||
```
|
||||
|
||||
`override` = **игнорировать системный прокси macOS** и всегда гнать HTTP Cursor через указанный SOCKS.
|
||||
|
||||
После переезда локальный SOCKS на **1090** (старый SSH Dynamic Forward / clash / xray и т.п.) **не слушал**, порт пуст → весь API Cursor мёртв, хотя прямой интернет живой.
|
||||
|
||||
Подтверждение источника:
|
||||
|
||||
```bash
|
||||
grep -R "127.0.0.1:1090" ~/Library/Application\ Support/Cursor 2>/dev/null
|
||||
# → User/settings.json: "http.proxy": "socks5://127.0.0.1:1090"
|
||||
```
|
||||
|
||||
**Отключение прокси в Cursor помогло** — это и есть фикс.
|
||||
|
||||
---
|
||||
|
||||
## Быстрый фикс (локальный выход, другая страна)
|
||||
|
||||
1. Открыть **Cursor → Settings → Open User Settings (JSON)** или:
|
||||
|
||||
```bash
|
||||
open -a "Cursor" "$HOME/Library/Application Support/Cursor/User/settings.json"
|
||||
```
|
||||
|
||||
2. Убрать / заменить:
|
||||
|
||||
```json
|
||||
"http.proxy": "",
|
||||
"http.proxySupport": "off"
|
||||
```
|
||||
|
||||
Либо **удалить** ключи `http.proxy` и `http.proxySupport` целиком.
|
||||
|
||||
3. **Cmd+Q** (полностью закрыть Cursor) → запустить снова.
|
||||
|
||||
4. Опционально: если Network Diagnostics показывает **HTTP/2: Success**, убрать костыль:
|
||||
|
||||
```json
|
||||
"http.disableHttp2": false
|
||||
```
|
||||
|
||||
(или удалить ключ) — раньше `true` ставили как обход для РФ; вне РФ он часто не нужен.
|
||||
|
||||
5. Проверить, что **1090 никто не слушает** (ожидаемо пусто, если прокси не нужен):
|
||||
|
||||
```bash
|
||||
lsof -nP -iTCP:1090 -sTCP:LISTEN || echo "1090 free"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Кто подсовывает SOCKS — чеклист (2 минуты)
|
||||
|
||||
```bash
|
||||
# 1. Env (обычно пусто — Cursor берёт settings.json)
|
||||
env | egrep -i 'proxy|http_proxy|https_proxy|all_proxy|no_proxy' || echo "(none)"
|
||||
|
||||
# 2. Явный 1090 в профиле Cursor
|
||||
grep -R "127.0.0.1:1090" ~/Library/Application\ Support/Cursor 2>/dev/null | grep -v Cache_Data
|
||||
|
||||
# 3. settings.json
|
||||
cat ~/Library/Application\ Support/Cursor/User/settings.json
|
||||
|
||||
# 4. Системный прокси macOS (не источник, если http.proxySupport=override)
|
||||
networksetup -getsocksfirewallproxy "Wi-Fi"
|
||||
networksetup -getwebproxy "Wi-Fi"
|
||||
networksetup -getsecurewebproxy "Wi-Fi"
|
||||
networksetup -getautoproxyurl "Wi-Fi"
|
||||
|
||||
# 5. Слушает ли кто-то 1090
|
||||
lsof -nP -iTCP:1090 -sTCP:LISTEN || echo "(nothing on 1090)"
|
||||
|
||||
# 6. Логи за сегодня
|
||||
grep -R "SOCKS 127.0.0.1:1090" ~/Library/Application\ Support/Cursor/logs --include='*.log' 2>/dev/null | tail -5
|
||||
```
|
||||
|
||||
| Источник | Роль |
|
||||
|----------|------|
|
||||
| **`settings.json` → `http.proxy` + `override`** | Главный виновник (этот инцидент) |
|
||||
| Env `ALL_PROXY` / `HTTPS_PROXY` | Редко; Cursor может подхватить при запуске из терминала |
|
||||
| Системный SOCKS / PAC | Только если `http.proxySupport` не `override` / не `off` |
|
||||
| Порт 1090 слушает процесс | Нужен **только** когда осознанно гоните Cursor через локальный SOCKS |
|
||||
|
||||
---
|
||||
|
||||
## Правило после смены страны / сети
|
||||
|
||||
| Сеть | Cursor proxy |
|
||||
|------|----------------|
|
||||
| **Прямой интернет работает** (ID, EU, …) | `http.proxySupport: off`, **без** `http.proxy` |
|
||||
| **Нужен RU→DO** (из РФ / DPI) | Поднять SOCKS **10809**, затем `socks5://127.0.0.1:10809` + `override` **только пока** туннель жив |
|
||||
|
||||
**Не** держать мёртвый `127.0.0.1:1090` (или любой порт) в settings навсегда.
|
||||
|
||||
Рекомендуемый порт для Cursor-туннеля в этом репо: **10809** (см. [README.md](./README.md)), не 1090.
|
||||
|
||||
Включить на время:
|
||||
|
||||
```json
|
||||
"http.proxy": "socks5://127.0.0.1:10809",
|
||||
"http.proxySupport": "override"
|
||||
```
|
||||
|
||||
Выключить:
|
||||
|
||||
```json
|
||||
"http.proxy": "",
|
||||
"http.proxySupport": "off"
|
||||
```
|
||||
|
||||
Скрипты выхода: [SSH_EXIT_MODES.md](../../connectivity/vpn/SSH_EXIT_MODES.md) — после `exit-via-local.sh` **обязательно** снять proxy в Cursor.
|
||||
|
||||
---
|
||||
|
||||
## Симптом → действие
|
||||
|
||||
| Симптом | Действие |
|
||||
|---------|----------|
|
||||
| Diagnostics: DNS/HTTP2 OK, API FAIL; лог `SOCKS 127.0.0.1:…` | Снять `http.proxy` / `override` → Cmd+Q |
|
||||
| `lsof :1090` пусто + proxy в settings | То же — прокси прописан, слушателя нет |
|
||||
| Нужен выход с IP DO | `exit-via-ru-do.sh` + proxy на **10809**, не 1090 |
|
||||
| После фикса всё ещё FAIL | Новый Network Diagnostics + свежие логи без строки SOCKS |
|
||||
|
||||
---
|
||||
|
||||
## Логи
|
||||
|
||||
```
|
||||
~/Library/Application Support/Cursor/logs/<timestamp>/
|
||||
```
|
||||
|
||||
Искать: `SOCKS 127.0.0.1`, `Failed to establish a socket connection to proxies`, `ConnectError`.
|
||||
|
||||
---
|
||||
|
||||
*Инцидент зафиксирован: 2026-07-12. Отключение прокси в settings.json — рабочий фикс.*
|
||||
71
docs/apps/git/README.md
Normal file
71
docs/apps/git/README.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Git / Gitea — репозиторий local_machine
|
||||
|
||||
Документация и скрипты этой машины в self-hosted **Gitea**. Обычная работа — коммиты и push; из РФ часто нужен SSH или прокси.
|
||||
|
||||
| Документ | Назначение |
|
||||
|----------|------------|
|
||||
| **[RUNBOOK.md](./RUNBOOK.md)** | Push не идёт, таймаут, auth, bundle — пошагово |
|
||||
| [docs/git/LOCAL_MACHINE_GITEA_SETUP.md](../../git/LOCAL_MACHINE_GITEA_SETUP.md) | Первый пуш, `.gitignore` |
|
||||
| [docs/git/GITEA_COMPLETE_GUIDE.md](../../git/GITEA_COMPLETE_GUIDE.md) | Полный справочник Gitea (сервер, SSL, админ) |
|
||||
|
||||
---
|
||||
|
||||
## Константы
|
||||
|
||||
| Что | Значение |
|
||||
|-----|----------|
|
||||
| Репозиторий | `hunabgit/local_machine` |
|
||||
| Веб | https://gitea.hunab.app/hunabgit/local_machine |
|
||||
| Ветка | `main` |
|
||||
| SSH | `ssh://git@gitea.hunab.app:2223/hunabgit/local_machine.git` |
|
||||
| HTTPS | `https://gitea.hunab.app/hunabgit/local_machine.git` |
|
||||
| Ключ (прод / Gitea) | `~/.ssh/hunab_deploy_key` |
|
||||
| RU-прокси (SSH hop) | `hsites-ahau` (149.154.64.19) |
|
||||
|
||||
---
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
### Первый раз (репо ещё не на Gitea)
|
||||
|
||||
```bash
|
||||
cd /Users/eternal/code/local_machine
|
||||
# 1. На https://gitea.hunab.app → New Repository → local_machine (пустой)
|
||||
bash scripts/deployment/gitea/setup-and-push.sh
|
||||
```
|
||||
|
||||
### Обычный коммит и push
|
||||
|
||||
```bash
|
||||
cd /Users/eternal/code/local_machine
|
||||
git status
|
||||
git add …
|
||||
git commit -m "…"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
### Push из РФ (если `git push` зависает)
|
||||
|
||||
```bash
|
||||
cd /Users/eternal/code/local_machine
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh --dry-run
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh
|
||||
```
|
||||
|
||||
Подробно: [RUNBOOK.md](./RUNBOOK.md).
|
||||
|
||||
---
|
||||
|
||||
## Скрипты
|
||||
|
||||
| Скрипт | Когда |
|
||||
|--------|--------|
|
||||
| `scripts/deployment/gitea/setup-and-push.sh` | Первый push, init git |
|
||||
| `scripts/deployment/gitea/git-push-gitea-via-proxy.sh` | Push через RU → DO (SSH / HTTPS / bundle) |
|
||||
|
||||
---
|
||||
|
||||
## Связанное
|
||||
|
||||
- [docs/connectivity/PROXY_GUIDE.md](../../connectivity/PROXY_GUIDE.md) — SSH `hsites-ahau`, `hunab-prod`
|
||||
- [docs/connectivity/PROXY_EMERGENCY.md](../../connectivity/PROXY_EMERGENCY.md) — если «всё слетело»
|
||||
338
docs/apps/git/RUNBOOK.md
Normal file
338
docs/apps/git/RUNBOOK.md
Normal file
@@ -0,0 +1,338 @@
|
||||
# Runbook: Git / Gitea — push local_machine
|
||||
|
||||
Симптом: **не удаётся запушить** в Gitea, `git push` зависает, auth error, «repository not found», или нужен первый push.
|
||||
|
||||
Репозиторий: **local_machine** → https://gitea.hunab.app/hunabgit/local_machine
|
||||
Корень на Mac: `/Users/eternal/code/local_machine`
|
||||
|
||||
---
|
||||
|
||||
## Архитектура (кратко)
|
||||
|
||||
```
|
||||
Mac (local_machine) ──git push──► Gitea (209.38.32.21 / gitea.hunab.app)
|
||||
PostgreSQL, контейнер gitea, SSH :2223
|
||||
```
|
||||
|
||||
**Из РФ** прямой HTTPS/SSH часто нестабилен → цепочка **Mac → RU (`hsites-ahau`) → DO (209.38.32.21)** или bundle на зеркало **206.189.35.205**.
|
||||
|
||||
| Компонент | Значение |
|
||||
|-----------|----------|
|
||||
| Remote `origin` | SSH `:2223` или HTTPS (см. `git remote -v`) |
|
||||
| Ветка | `main` |
|
||||
| SSH ключ Gitea | `~/.ssh/hunab_deploy_key` |
|
||||
| SSH hop RU | `hsites-ahau`, `~/.ssh/id_ed25519` |
|
||||
| Скрипт push через прокси | `scripts/deployment/gitea/git-push-gitea-via-proxy.sh` |
|
||||
|
||||
Полный справочник сервера: [GITEA_COMPLETE_GUIDE.md](../../git/GITEA_COMPLETE_GUIDE.md).
|
||||
|
||||
---
|
||||
|
||||
## Шаг 0: диагностика (2 минуты)
|
||||
|
||||
Из корня **local_machine**:
|
||||
|
||||
```bash
|
||||
cd /Users/eternal/code/local_machine
|
||||
git status
|
||||
git remote -v
|
||||
git branch -vv
|
||||
git log -1 --oneline
|
||||
git fetch origin 2>&1 | tail -5
|
||||
```
|
||||
|
||||
| Проверка | OK | Проблема |
|
||||
|----------|-----|----------|
|
||||
| `git status` | на нужной ветке, осмысленные изменения | не в корне репо |
|
||||
| `origin` | SSH `2223` или HTTPS gitea.hunab.app | нет remote → `setup-and-push.sh` |
|
||||
| `git fetch origin` | без ошибки | сеть / auth → §2–§4 |
|
||||
| Gitea в браузере | https://gitea.hunab.app открывается | сеть / DNS → connectivity |
|
||||
|
||||
**SSH до Gitea (прямой):**
|
||||
|
||||
```bash
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=10 -p 2223 -i ~/.ssh/hunab_deploy_key git@gitea.hunab.app
|
||||
# ожидаем приветствие Gitea или "Hi there, ..." / exit 1 — не timeout
|
||||
```
|
||||
|
||||
**SSH через RU (как в proxy-скрипте):**
|
||||
|
||||
```bash
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=8 hsites-ahau "echo RU_OK"
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=15 -p 2223 \
|
||||
-i ~/.ssh/hunab_deploy_key \
|
||||
-o ProxyJump=hsites-ahau \
|
||||
git@209.38.32.21 "echo GITEA_SSH_OK"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Правильные remote URL
|
||||
|
||||
| Режим | URL |
|
||||
|-------|-----|
|
||||
| **SSH (предпочтительно)** | `ssh://git@gitea.hunab.app:2223/hunabgit/local_machine.git` |
|
||||
| HTTPS | `https://gitea.hunab.app/hunabgit/local_machine.git` |
|
||||
|
||||
Переключить:
|
||||
|
||||
```bash
|
||||
git remote set-url origin ssh://git@gitea.hunab.app:2223/hunabgit/local_machine.git
|
||||
# или
|
||||
git remote set-url origin https://gitea.hunab.app/hunabgit/local_machine.git
|
||||
```
|
||||
|
||||
Клонирование:
|
||||
|
||||
```bash
|
||||
git clone ssh://git@gitea.hunab.app:2223/hunabgit/local_machine.git
|
||||
# или
|
||||
git clone https://gitea.hunab.app/hunabgit/local_machine.git
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Симптом → причина → действие
|
||||
|
||||
### 1. Репозиторий ещё не на Gitea / «repository not found»
|
||||
|
||||
**Причина:** на сервере нет `hunabgit/local_machine`.
|
||||
|
||||
**Действие:**
|
||||
|
||||
1. https://gitea.hunab.app → **New Repository** → Name: `local_machine` → **пустой** (без README).
|
||||
2. Или API: `GITEA_TOKEN=… bash scripts/deployment/gitea/setup-and-push.sh --create-only`
|
||||
3. `bash scripts/deployment/gitea/setup-and-push.sh` или `git push -u origin main`
|
||||
|
||||
---
|
||||
|
||||
### 2. `git push` зависает / timeout (типично из РФ)
|
||||
|
||||
**Причина:** HTTPS или прямой SSH до DO режется или медленный маршрут.
|
||||
|
||||
**Действие A — SSH на 2223:**
|
||||
|
||||
```bash
|
||||
git remote set-url origin ssh://git@gitea.hunab.app:2223/hunabgit/local_machine.git
|
||||
GIT_SSH_COMMAND="ssh -i ~/.ssh/hunab_deploy_key -o ConnectTimeout=30" git push origin main
|
||||
```
|
||||
|
||||
**Действие B — скрипт через прокси (рекомендуется из РФ):**
|
||||
|
||||
```bash
|
||||
cd /Users/eternal/code/local_machine
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh --dry-run
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh
|
||||
```
|
||||
|
||||
Скрипт по порядку: **SSH через `hsites-ahau`** → HTTPS (короткий timeout) → **bundle** (scp + `docker exec gitea`).
|
||||
|
||||
Переменные при необходимости:
|
||||
|
||||
```bash
|
||||
PROXY_SERVER=hsites-ahau \
|
||||
TARGET_SSH_KEY=~/.ssh/hunab_deploy_key \
|
||||
GITEA_SERVER=206.189.35.205 \
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. `Permission denied (publickey)` / auth failed
|
||||
|
||||
**Причина:** неверный ключ или ключ не добавлен в Gitea / `~/.ssh/config`.
|
||||
|
||||
**Действие:**
|
||||
|
||||
```bash
|
||||
ssh-add -l
|
||||
ls -la ~/.ssh/hunab_deploy_key ~/.ssh/id_ed25519
|
||||
ssh -v -p 2223 -i ~/.ssh/hunab_deploy_key git@gitea.hunab.app 2>&1 | tail -20
|
||||
```
|
||||
|
||||
- В Gitea: **Settings → SSH / GPG Keys** — публичный ключ с Mac.
|
||||
- В `~/.ssh/config` блок для `gitea.hunab.app` с `IdentityFile ~/.ssh/hunab_deploy_key`, `Port 2223`.
|
||||
- Через прокси: ключ **`hunab_deploy_key`** на целевой хост, **`id_ed25519`** до RU.
|
||||
|
||||
SSH recovery: [PROXY_EMERGENCY.md](../../connectivity/PROXY_EMERGENCY.md).
|
||||
|
||||
---
|
||||
|
||||
### 4. `rejected (non-fast-forward)` / нужен pull
|
||||
|
||||
**Причина:** на Gitea есть коммиты, которых нет локально.
|
||||
|
||||
**Действие:**
|
||||
|
||||
```bash
|
||||
git fetch origin
|
||||
git log --oneline HEAD..origin/main # что на сервере
|
||||
git pull --rebase origin main # или merge — по политике команды
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**Не делать** `git push --force` на `main` без явного запроса.
|
||||
|
||||
---
|
||||
|
||||
### 5. Push прошёл, но в Gitea старый коммит (bundle)
|
||||
|
||||
**Причина:** использовался bundle; локальный `origin/main` не обновился.
|
||||
|
||||
**Действие:**
|
||||
|
||||
```bash
|
||||
git fetch origin
|
||||
git log -1 origin/main
|
||||
git log -1 main
|
||||
```
|
||||
|
||||
Если расходятся — повторить push или `git fetch` после успешного bundle (скрипт делает `git update-ref` при совпадении).
|
||||
|
||||
---
|
||||
|
||||
### 6. Случайно пытаюсь закоммитить секреты
|
||||
|
||||
**Причина:** `.env`, ключи, VPN `.conf` с приватными ключами.
|
||||
|
||||
**Действие:**
|
||||
|
||||
```bash
|
||||
git status
|
||||
git diff --cached
|
||||
```
|
||||
|
||||
Не коммитить: `~/.config/joplin-webdav/env`, `*.pem`, `*.key`, `docs/connectivity/vpn/*.conf`, `temp/` с личными данными. См. корневой `.gitignore`.
|
||||
|
||||
Если уже в индексе: `git reset HEAD <file>`.
|
||||
|
||||
---
|
||||
|
||||
### 7. «Not a git repository» / push из не того каталога
|
||||
|
||||
**Причина:** команды не из `/Users/eternal/code/local_machine`.
|
||||
|
||||
```bash
|
||||
cd /Users/eternal/code/local_machine
|
||||
git rev-parse --show-toplevel
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. Нет интернета / RU недоступен
|
||||
|
||||
Push невозможен, пока не восстановлена сеть.
|
||||
|
||||
```bash
|
||||
curl -sS --max-time 8 https://gitea.hunab.app
|
||||
ssh -o ConnectTimeout=6 hsites-ahau "echo RU_OK"
|
||||
```
|
||||
|
||||
Hotspot / Wi‑Fi: [HOTSPOT_NETWORK_EMERGENCY.md](../../connectivity/HOTSPOT_NETWORK_EMERGENCY.md).
|
||||
Прокси RU→DO: `./scripts/vpn/exit-via-ru-do.sh` — **после** базового интернета.
|
||||
|
||||
---
|
||||
|
||||
## Процедуры
|
||||
|
||||
### Ежедневный push (нормальная сеть)
|
||||
|
||||
```bash
|
||||
cd /Users/eternal/code/local_machine
|
||||
git status
|
||||
git add -p # или конкретные файлы
|
||||
git commit -m "Кратко: зачем изменение."
|
||||
git push origin main
|
||||
```
|
||||
|
||||
Коммиты создавать **только по запросу** (локальная политика). Не пушить без явного согласия, если так принято в workflow.
|
||||
|
||||
---
|
||||
|
||||
### Первый push новой машины
|
||||
|
||||
```bash
|
||||
cd /Users/eternal/code/local_machine
|
||||
git clone ssh://git@gitea.hunab.app:2223/hunabgit/local_machine.git
|
||||
# или уже есть папка:
|
||||
bash scripts/deployment/gitea/setup-and-push.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Push только через прокси (рабочий режим из РФ)
|
||||
|
||||
```bash
|
||||
cd /Users/eternal/code/local_machine
|
||||
git add … && git commit -m "…"
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh --dry-run
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh
|
||||
```
|
||||
|
||||
`--dry-run` показывает число коммитов к отправке, **ничего не меняет**.
|
||||
|
||||
---
|
||||
|
||||
### После смены hotspot / VPN
|
||||
|
||||
1. Проверить интернет и RU: `ssh hsites-ahau "echo OK"`.
|
||||
2. `git fetch origin` — если OK, обычный `git push`.
|
||||
3. Если fetch/push timeout → `git-push-gitea-via-proxy.sh`.
|
||||
|
||||
---
|
||||
|
||||
## Скрипты
|
||||
|
||||
| Скрипт | Назначение |
|
||||
|--------|------------|
|
||||
| `scripts/deployment/gitea/setup-and-push.sh` | `git init`, remote, первый commit+push; `--create-only` + `GITEA_TOKEN` |
|
||||
| `scripts/deployment/gitea/git-push-gitea-via-proxy.sh` | Push main: SSH→HTTPS→bundle; `--dry-run [branch]` |
|
||||
|
||||
Безопасность proxy-скрипта: **не** делает `reset --hard`, **не** `--force` push; только обновляет `origin/main` после успеха.
|
||||
|
||||
---
|
||||
|
||||
## `.gitignore` (напоминание)
|
||||
|
||||
Коммитить **можно:** `docs/`, `scripts/`, примеры конфигов без секретов.
|
||||
|
||||
**Нельзя:** `.env`, ключи, `temp/` с дампами, личные CSV, VPN conf с ключами, `~/.config/*` секреты.
|
||||
|
||||
---
|
||||
|
||||
## Логи и отладка
|
||||
|
||||
```bash
|
||||
GIT_TRACE=1 GIT_SSH_COMMAND="ssh -v" git push origin main 2>&1 | tail -50
|
||||
```
|
||||
|
||||
Bundle-артефакты: `temp/gitea-push-*.bundle` (скрипт удаляет после успеха).
|
||||
|
||||
Gitea UI: **Commits** на https://gitea.hunab.app/hunabgit/local_machine/commits/branch/main
|
||||
|
||||
---
|
||||
|
||||
## Связанные документы
|
||||
|
||||
| Документ | Зачем |
|
||||
|----------|--------|
|
||||
| [README.md](./README.md) | Оглавление apps/git |
|
||||
| [LOCAL_MACHINE_GITEA_SETUP.md](../../git/LOCAL_MACHINE_GITEA_SETUP.md) | Первичная настройка |
|
||||
| [GITEA_COMPLETE_GUIDE.md](../../git/GITEA_COMPLETE_GUIDE.md) | Сервер, SSL, bundle детали |
|
||||
| [PROXY_GUIDE.md](../../connectivity/PROXY_GUIDE.md) | SSH config, hsites-ahau |
|
||||
| [PROXY_EMERGENCY.md](../../connectivity/PROXY_EMERGENCY.md) | Восстановление SSH |
|
||||
|
||||
---
|
||||
|
||||
## История типовых инцидентов
|
||||
|
||||
| Ситу | Симптом | Фикс |
|
||||
|------|---------|------|
|
||||
| — | HTTPS push hang | SSH `:2223` или `git-push-gitea-via-proxy.sh` |
|
||||
| — | repository not found | Создать repo на Gitea UI |
|
||||
| — | Permission denied | `hunab_deploy_key` в Gitea + SSH config |
|
||||
| 2026-03 | Первый push local_machine | `setup-and-push.sh` |
|
||||
|
||||
---
|
||||
|
||||
*Проверка remote: `git remote -v`. Push из РФ: `bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh --dry-run`.*
|
||||
39
docs/apps/joplin/scripts/joplin-ensure-sync.sh
Executable file
39
docs/apps/joplin/scripts/joplin-ensure-sync.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh
|
||||
# Подготовить Mac к синку Joplin с телефона (одна команда).
|
||||
# ./joplin-ensure-sync.sh
|
||||
#
|
||||
# Mac = клиент Wi‑Fi хотспота телефона. Primary IP *.100 (не alias).
|
||||
|
||||
set -e
|
||||
|
||||
ROOT="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
|
||||
|
||||
echo "=== Joplin: подготовка синка ==="
|
||||
|
||||
if ! pgrep -f "rclone serve webdav" >/dev/null 2>&1; then
|
||||
echo "→ запуск WebDAV..."
|
||||
"$ROOT/joplin-webdav-serve.sh"
|
||||
else
|
||||
echo "→ WebDAV уже запущен"
|
||||
fi
|
||||
|
||||
if [ -x "$ROOT/joplin-hotspot-alias.sh" ]; then
|
||||
if ! "$ROOT/joplin-hotspot-alias.sh" repair 2>/dev/null; then
|
||||
if ! "$ROOT/joplin-hotspot-alias.sh" ensure 2>/dev/null; then
|
||||
echo ""
|
||||
echo "⚠ Нужен sudo один раз:"
|
||||
echo " ./docs/apps/joplin/scripts/joplin-hotspot-alias-install-sudo.sh"
|
||||
echo " ./docs/apps/joplin/scripts/joplin-hotspot-alias.sh repair"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
"$ROOT/joplin-webdav-status.sh"
|
||||
|
||||
echo ""
|
||||
echo "=== На телефоне ==="
|
||||
echo "1. Mac должен быть на Wi‑Fi хотспота этого телефона (не домашний Wi‑Fi)"
|
||||
echo "2. Joplin → WebDAV → URL из блока выше (*.100)"
|
||||
echo "3. Браузер на телефоне → тот же URL → логин за 2–3 сек"
|
||||
echo "4. Синхронизировать"
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
# Одноразово: passwordless sudo только для joplin-hotspot-alias.sh ensure
|
||||
# Одноразово: passwordless sudo для joplin-hotspot-alias.sh (setmanual *.100, dhcp)
|
||||
set -e
|
||||
|
||||
SCRIPT="$(CDPATH= cd -- "$(dirname "$0")" && pwd)/joplin-hotspot-alias.sh"
|
||||
@@ -12,8 +12,8 @@ TMP="$(mktemp)"
|
||||
trap 'rm -f "$TMP"' EXIT
|
||||
|
||||
cat >"$TMP" <<EOF
|
||||
# Joplin WebDAV: alias *.100 на en0 после reconnect хотспота (только этот скрипт)
|
||||
${USER_NAME} ALL=(ALL) NOPASSWD: ${SCRIPT} ensure
|
||||
# Joplin WebDAV: primary *.100 на Wi-Fi (setmanual) + dhcp (только этот скрипт)
|
||||
${USER_NAME} ALL=(ALL) NOPASSWD: ${SCRIPT}, ${SCRIPT} ensure, ${SCRIPT} ensure --force, ${SCRIPT} repair, ${SCRIPT} dhcp
|
||||
EOF
|
||||
|
||||
echo "Будет создан $DEST:"
|
||||
@@ -22,4 +22,4 @@ echo ""
|
||||
sudo cp "$TMP" "$DEST"
|
||||
sudo chmod 440 "$DEST"
|
||||
sudo visudo -c -f "$DEST"
|
||||
echo "OK. LaunchAgent сможет поднимать alias без пароля."
|
||||
echo "OK. LaunchAgent сможет выставлять *.100 без пароля."
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
# LaunchAgent: каждые 60 с проверять alias *.100 (нужен joplin-hotspot-alias-install-sudo.sh).
|
||||
# LaunchAgent: каждые 60 с проверять primary *.100 (нужен joplin-hotspot-alias-install-sudo.sh).
|
||||
set -e
|
||||
|
||||
ROOT="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#!/bin/sh
|
||||
# Стабильный URL для Joplin на телефоне при Android-хотспоте: http://<подсеть>.100:8080
|
||||
# Alias *.100 на en0 (DHCP primary не трогаем). LaunchAgent поднимает alias после reconnect.
|
||||
# Стабильный URL для Joplin: http://<подсеть>.100:8080
|
||||
# Primary IP *.100 через networksetup (Android видит primary, alias — часто нет).
|
||||
#
|
||||
# ./joplin-hotspot-alias.sh status
|
||||
# ./joplin-hotspot-alias.sh ensure
|
||||
# ./joplin-hotspot-alias.sh ensure|repair
|
||||
# ./joplin-hotspot-alias.sh dhcp # вернуть DHCP (перед домашним Wi‑Fi)
|
||||
#
|
||||
# Passwordless: ./joplin-hotspot-alias-install-sudo.sh
|
||||
|
||||
@@ -11,8 +12,10 @@ set -e
|
||||
|
||||
CONFIG="${JOPLIN_WEBDAV_CONFIG:-$HOME/.config/joplin-webdav/env}"
|
||||
WIFI_DEV="${WIFI_DEVICE:-en0}"
|
||||
WIFI_SERVICE="${WIFI_SERVICE:-Wi-Fi}"
|
||||
PORT="${JOPLIN_WEBDAV_PORT:-8080}"
|
||||
LOG="$HOME/Library/Logs/joplin-webdav/alias.log"
|
||||
NS="/usr/sbin/networksetup"
|
||||
|
||||
ipv4_subnet() {
|
||||
printf '%s\n' "$1" | awk -F. '{print $1"."$2"."$3}'
|
||||
@@ -59,19 +62,34 @@ has_ip_on_dev() {
|
||||
ifconfig "$dev" 2>/dev/null | grep -q "inet ${ip} "
|
||||
}
|
||||
|
||||
# Убрать *.100 alias с других подсетей (после смены хотспота / Wi‑Fi)
|
||||
prune_stale_aliases() {
|
||||
target="$1"
|
||||
dev="$2"
|
||||
ifconfig "$dev" 2>/dev/null | awk '/inet / {print $2}' | while read -r ip; do
|
||||
case "$ip" in
|
||||
*.*.*.100)
|
||||
[ "$ip" = "$target" ] && continue
|
||||
ifconfig "$dev" -alias "$ip" 2>/dev/null || true
|
||||
log_msg "removed stale alias $ip"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
is_android_hotspot_gw() {
|
||||
gw="$1"
|
||||
case "$gw" in
|
||||
10.*|172.20.10.*|192.168.43.*|192.168.44.*|192.168.49.*) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
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
|
||||
@@ -90,31 +108,45 @@ cmd_status() {
|
||||
gw="$(default_gateway 2>/dev/null || true)"
|
||||
url="$(phone_url 2>/dev/null || true)"
|
||||
|
||||
echo "mode: Android-хотспот (alias *.100)"
|
||||
echo "mode: static primary *.100 ($WIFI_SERVICE)"
|
||||
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)"
|
||||
if [ -n "$gw" ] && ! is_android_hotspot_gw "$gw"; then
|
||||
echo "network: домашний/роутер Wi‑Fi (не Android-хотспот)"
|
||||
echo " → телефон должен быть в той же Wi‑Fi сети, что Mac"
|
||||
else
|
||||
echo "network: похоже на Android-хотспот"
|
||||
fi
|
||||
|
||||
if [ -n "$target" ]; then
|
||||
ifconfig "$WIFI_DEV" 2>/dev/null | awk -v t="$target" '/inet / { if ($2 ~ /\.100$/ && $2 != t) print "stale ifconfig: " $2 " (repair)" }'
|
||||
fi
|
||||
|
||||
if [ -n "$target" ] && [ "$primary" = "$target" ]; then
|
||||
echo "static IP: OK (primary = $target)"
|
||||
elif [ -n "$target" ]; then
|
||||
echo "alias: MISSING — телефон не достучится"
|
||||
echo " fix: $0 ensure"
|
||||
echo "static IP: MISSING (primary $primary, нужен $target)"
|
||||
echo " fix: $0 repair"
|
||||
fi
|
||||
|
||||
if [ -n "$url" ]; then
|
||||
echo ""
|
||||
echo "URL на телефоне (работает с Android-хотспота):"
|
||||
echo "URL на телефоне:"
|
||||
echo " $url"
|
||||
echo ""
|
||||
echo "Проверка: браузер НА ТЕЛЕФОНЕ → URL → запрос логина за 2–3 сек"
|
||||
echo "Если браузер висит — телефон не видит Mac (не Joplin)"
|
||||
echo ""
|
||||
echo "⚠ http://*.local:8080 с телефона НЕ работает (Android не резолвит mDNS клиентов хотспота)"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_ensure() {
|
||||
force=0
|
||||
[ "${1:-}" = --force ] && force=1
|
||||
target="$(joplin_phone_ip 2>/dev/null || true)"
|
||||
if [ -z "$target" ]; then
|
||||
log_msg "skip: no gateway"
|
||||
@@ -125,25 +157,56 @@ cmd_ensure() {
|
||||
log_msg "skip: no primary IP"
|
||||
exit 0
|
||||
fi
|
||||
if [ "$primary" = "$target" ] || has_ip_on_dev "$target" "$WIFI_DEV"; then
|
||||
write_phone_url_env
|
||||
if [ "$primary" = "$target" ] && [ "$force" -eq 0 ]; then
|
||||
[ "$(id -u)" -ne 0 ] && 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)"
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
log_msg "setmanual $target (was $primary)"
|
||||
sudo_arg=ensure
|
||||
[ "$force" -eq 1 ] && sudo_arg="ensure --force"
|
||||
# shellcheck disable=SC2086
|
||||
if ! sudo -n "$0" $sudo_arg; then
|
||||
echo "static IP FAILED: нужен sudo." >&2
|
||||
echo " один раз: ./docs/apps/joplin/scripts/joplin-hotspot-alias-install-sudo.sh" >&2
|
||||
echo " затем: $0 repair" >&2
|
||||
exit 1
|
||||
fi
|
||||
write_phone_url_env
|
||||
echo "static IP OK: $target"
|
||||
echo "URL на телефоне: $(phone_url)"
|
||||
exit 0
|
||||
fi
|
||||
gw="$(default_gateway 2>/dev/null || true)"
|
||||
[ -n "$gw" ] || { log_msg "skip: no gateway"; exit 1; }
|
||||
prune_stale_aliases "$target" "$WIFI_DEV"
|
||||
log_msg "networksetup -setmanual $target gw $gw [root]"
|
||||
"$NS" -setmanual "$WIFI_SERVICE" "$target" "255.255.255.255.0" "$gw"
|
||||
}
|
||||
|
||||
cmd_dhcp() {
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
if ! sudo -n "$0" dhcp; then
|
||||
echo "Нужен sudo: sudo $NS -setdhcp \"$WIFI_SERVICE\"" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Wi‑Fi → DHCP"
|
||||
exit 0
|
||||
fi
|
||||
"$NS" -setdhcp "$WIFI_SERVICE"
|
||||
log_msg "setdhcp $WIFI_SERVICE"
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "Usage: $(basename "$0") status|ensure" >&2
|
||||
echo "Usage: $(basename "$0") status|ensure|repair|dhcp" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
case "${1:-ensure}" in
|
||||
status) cmd_status ;;
|
||||
ensure) cmd_ensure ;;
|
||||
ensure) cmd_ensure "${2:-}" ;;
|
||||
repair) "$0" ensure --force ;;
|
||||
dhcp) cmd_dhcp ;;
|
||||
-h|--help) usage ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
|
||||
@@ -56,16 +56,52 @@ if [ -f "$settings" ]; then
|
||||
| while read -r u; do echo "settings.json: $u"; done
|
||||
fi
|
||||
|
||||
log="$HOME/Library/Logs/joplin-webdav/rclone.log"
|
||||
|
||||
echo ""
|
||||
echo "=== Сеть: Mac и телефон в одной LAN? ==="
|
||||
gw=$(route -n get default 2>/dev/null | awk '/gateway:/{print $2; exit}')
|
||||
ip=$(ipconfig getifaddr en0 2>/dev/null || true)
|
||||
case "$gw" in
|
||||
192.168.1.*|192.168.0.*)
|
||||
peers=$(arp -an 2>/dev/null | grep "$gw" | awk '{print $2}' | tr -d '()' | grep -vE "^${gw%.*}\.(1|255)$|^$ip$" | head -3)
|
||||
if [ -z "$peers" ]; then
|
||||
echo "⚠ Mac на домашнем Wi‑Fi ($ip), других устройств в LAN не видно"
|
||||
echo " Телефон, скорее всего, НЕ в этой Wi‑Fi → синк невозможен"
|
||||
echo " Вариант A: Mac подключить к хотспоту телефона"
|
||||
echo " Вариант B: телефон подключить к той же Wi‑Fi, что Mac"
|
||||
else
|
||||
echo "Mac на Wi‑Fi роутера ($ip). Телефон должен быть в той же Wi‑Fi."
|
||||
fi
|
||||
;;
|
||||
10.*|172.20.10.*|192.168.43.*|192.168.44.*)
|
||||
echo "Mac на хотспоте ($ip). URL на телефоне — из блока выше (*.100)."
|
||||
if [ -n "$gw" ] && [ -f "$log" ] && ! grep -q "from ${gw}:" "$log" 2>/dev/null; then
|
||||
echo "⚠ В логе rclone ещё не было запросов с телефона ($gw)"
|
||||
echo " Проверьте URL в браузере НА ТЕЛЕФОНЕ (не только в Joplin)"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
[ -n "$gw" ] && echo "gateway: $gw, Mac: ${ip:-?}"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "=== rclone (запросы с телефона?) ==="
|
||||
log="$HOME/Library/Logs/joplin-webdav/rclone.log"
|
||||
gw=$(route -n get default 2>/dev/null | awk '/gateway:/{print $2; exit}')
|
||||
if [ -f "$log" ]; then
|
||||
phone_lines=$(grep -E 'from 10\.|from 192\.168\.' "$log" 2>/dev/null | tail -3)
|
||||
if [ -n "$phone_lines" ]; then
|
||||
phone_lines=$(grep -E 'from 10\.|from 192\.168\.' "$log" 2>/dev/null | grep -v '127.0.0.1' | tail -5)
|
||||
if [ -n "$gw" ] && grep -q "from ${gw}:" "$log" 2>/dev/null; then
|
||||
echo "OK: в логе есть запросы с телефона/шлюза ($gw)"
|
||||
grep "from ${gw}:" "$log" 2>/dev/null | tail -2
|
||||
elif [ -n "$phone_lines" ]; then
|
||||
echo "$phone_lines"
|
||||
echo "⚠ нет запросов с шлюза ${gw:-?} — возможно только тесты с Mac"
|
||||
else
|
||||
echo "нет запросов с LAN (только 127.0.0.1?) — телефон не достучался"
|
||||
echo "нет запросов с LAN — телефон не достучался до rclone"
|
||||
fi
|
||||
echo ""
|
||||
echo "Смотреть live: tail -f $log | grep -v 127.0.0.1"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
Reference in New Issue
Block a user