feat(deploy): split obsidian stack from main + path-filtered GHA
Obsidian Self-hosted LiveSync (CouchDB) выведен в отдельный docker-compose stack и GHA workflow. Любой стек теперь редеплоится независимо. * docker-compose.obsidian.yml: только couchdb в shared external network * docker-compose.prod.yml: убрана couchdb-секция; caddy подключён к shared network для маршрута obsidian.gendsgn.ru → couchdb:5984 * .github/workflows/deploy-obsidian.yml: новый workflow с path-filters (триггерится только при изменениях obsidian-related файлов) * .github/workflows/deploy.yml: + path-filters (триггерится только при main-related файлах) + создание shared network в bootstrap * docs/obsidian-livesync.md: обновлены инструкции под split-архитектуру После push: 1. ssh gendesign 'docker network create gendesign_shared' (один раз) 2. Прописать COUCHDB_USER/PASSWORD в backend/.env.runtime 3. Добавить DNS A-record obsidian.gendsgn.ru → IP VPS 4. git push → GHA задеплоит обе части независимо Преимущества: - Изменения main не разрывают LiveSync клиентов - Изменения obsidian не рестартуют main - Падение obsidian-стека не влияет на основное приложение
This commit is contained in:
parent
79e2e94e09
commit
b0f5c38894
7 changed files with 550 additions and 3 deletions
97
.github/workflows/deploy-obsidian.yml
vendored
Normal file
97
.github/workflows/deploy-obsidian.yml
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
name: Deploy Obsidian
|
||||
|
||||
# Деплой ТОЛЬКО obsidian-стека (CouchDB).
|
||||
# Триггерится при изменениях:
|
||||
# - docker-compose.obsidian.yml (compose сервиса CouchDB)
|
||||
# - scripts/setup-couchdb.sh (bootstrap)
|
||||
# - docs/obsidian-livesync.md (документация — для history-watcher'а)
|
||||
# - этот workflow
|
||||
#
|
||||
# Не пересобирает никаких Docker-образов (CouchDB официальный с DockerHub).
|
||||
# Не трогает main-стек (backend / frontend / postgres / worker / beat / caddy).
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "docker-compose.obsidian.yml"
|
||||
- "scripts/setup-couchdb.sh"
|
||||
- "docs/obsidian-livesync.md"
|
||||
- ".github/workflows/deploy-obsidian.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: deploy-obsidian
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
deploy-obsidian:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Deploy obsidian stack via SSH
|
||||
uses: appleboy/ssh-action@v1.0.3
|
||||
with:
|
||||
host: ${{ secrets.DEPLOY_HOST }}
|
||||
username: ${{ secrets.DEPLOY_USER }}
|
||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
port: ${{ secrets.DEPLOY_PORT || 22 }}
|
||||
script: |
|
||||
set -euo pipefail
|
||||
cd /opt/gendesign
|
||||
|
||||
# Свежие конфиги из репо
|
||||
git fetch origin main
|
||||
git reset --hard origin/main
|
||||
|
||||
# Создать shared network если её ещё нет (idempotent).
|
||||
# Эта же сеть используется Caddy в main-stack.
|
||||
docker network inspect gendesign_shared >/dev/null 2>&1 \
|
||||
|| docker network create gendesign_shared
|
||||
|
||||
# Стек CouchDB поднимается с собственным project-name,
|
||||
# чтобы main `docker compose ps` не показывал его как часть main.
|
||||
docker compose -p gendesign-obsidian \
|
||||
-f docker-compose.obsidian.yml pull
|
||||
docker compose -p gendesign-obsidian \
|
||||
-f docker-compose.obsidian.yml up -d
|
||||
|
||||
# Bootstrap CouchDB (CORS, db, лимиты) — idempotent
|
||||
if [ -f scripts/setup-couchdb.sh ]; then
|
||||
# Загружаем COUCHDB_PASSWORD из backend/.env.runtime если есть
|
||||
if [ -f backend/.env.runtime ]; then
|
||||
set -a; source backend/.env.runtime; set +a
|
||||
fi
|
||||
# Ждём CouchDB up, потом bootstrap
|
||||
for i in $(seq 1 30); do
|
||||
if docker compose -p gendesign-obsidian \
|
||||
-f docker-compose.obsidian.yml \
|
||||
exec -T couchdb curl -fsS http://localhost:5984/_up >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
COUCHDB_HOST=http://localhost:5984 \
|
||||
COUCHDB_USER="${COUCHDB_USER:-obsidian}" \
|
||||
COUCHDB_PASSWORD="${COUCHDB_PASSWORD:?must be set in backend/.env.runtime}" \
|
||||
docker compose -p gendesign-obsidian \
|
||||
-f docker-compose.obsidian.yml \
|
||||
exec -T couchdb bash -c "$(cat scripts/setup-couchdb.sh)" \
|
||||
|| echo "(bootstrap warnings ignored — script is idempotent)"
|
||||
fi
|
||||
|
||||
# Caddy в main-stack — НЕ перезапускаем тут (другой workflow),
|
||||
# но reload конфига полезен на случай если в Caddyfile добавили
|
||||
# новый obsidian-route только что (главное: image main caddy уже
|
||||
# запущен и подключён к gendesign_shared network).
|
||||
if docker compose -p gendesign -f docker-compose.prod.yml ps caddy --quiet \
|
||||
| grep -q .; then
|
||||
docker compose -p gendesign -f docker-compose.prod.yml \
|
||||
exec -T caddy caddy reload --config /etc/caddy/Caddyfile \
|
||||
|| echo "(caddy reload skipped — main stack not running)"
|
||||
fi
|
||||
|
||||
sleep 3
|
||||
curl -fsS https://obsidian.gendsgn.ru/_up | head -c 200 || true
|
||||
23
.github/workflows/deploy.yml
vendored
23
.github/workflows/deploy.yml
vendored
|
|
@ -1,8 +1,18 @@
|
|||
name: Deploy
|
||||
|
||||
# Деплоится только при изменениях основного стека.
|
||||
# Obsidian-стек (CouchDB) — отдельный workflow `deploy-obsidian.yml`.
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "backend/**"
|
||||
- "frontend/**"
|
||||
- "docker-compose.prod.yml"
|
||||
- "Caddyfile"
|
||||
- ".github/workflows/deploy.yml"
|
||||
# NB: shared compose-fragments (network) — тоже триггерят main
|
||||
# потому что Caddy шарит сеть с obsidian-stack
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
|
|
@ -98,13 +108,20 @@ jobs:
|
|||
mkdir -p backend
|
||||
printf 'SENTRY_RELEASE=%s\n' "$IMAGE_TAG" > backend/.env.runtime
|
||||
|
||||
# Создать external network если её нет (нужна Caddy для маршрута
|
||||
# obsidian.gendsgn.ru → couchdb из отдельного obsidian-stack).
|
||||
docker network inspect gendesign_shared >/dev/null 2>&1 \
|
||||
|| docker network create gendesign_shared
|
||||
|
||||
export IMAGE_TAG="$IMAGE_TAG"
|
||||
docker compose -f docker-compose.prod.yml pull
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
# Project name явно — `gendesign` (по имени папки auto), но
|
||||
# фиксируем для consistency с obsidian-stack (-p gendesign-obsidian).
|
||||
docker compose -p gendesign -f docker-compose.prod.yml pull
|
||||
docker compose -p gendesign -f docker-compose.prod.yml up -d
|
||||
|
||||
# Caddyfile is bind-mounted; up -d won't re-read it. Reload explicitly.
|
||||
# `|| true` so a temporary Caddy hiccup doesn't fail the whole deploy.
|
||||
docker compose -f docker-compose.prod.yml exec -T caddy \
|
||||
docker compose -p gendesign -f docker-compose.prod.yml exec -T caddy \
|
||||
caddy reload --config /etc/caddy/Caddyfile || true
|
||||
|
||||
# Clean up disk: dangling layers + tagged images older than 72h that
|
||||
|
|
|
|||
17
Caddyfile
17
Caddyfile
|
|
@ -27,6 +27,23 @@ www.gendsgn.ru {
|
|||
redir https://gendsgn.ru{uri} permanent
|
||||
}
|
||||
|
||||
# Obsidian Self-hosted LiveSync (CouchDB backend).
|
||||
# Auto-TLS Let's Encrypt. CORS уже включён на стороне CouchDB через bootstrap
|
||||
# (см. scripts/setup-couchdb.sh). Basic-auth — на стороне CouchDB (admin user).
|
||||
#
|
||||
# DNS: A-record obsidian.gendsgn.ru → IP VPS.
|
||||
# Клиенты Obsidian + Self-hosted LiveSync plugin указывают на этот URL.
|
||||
obsidian.gendsgn.ru {
|
||||
encode zstd gzip
|
||||
|
||||
reverse_proxy couchdb:5984 {
|
||||
# Большие документы (vault attachments / images) — увеличиваем timeout
|
||||
transport http {
|
||||
response_header_timeout 120s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Plain HTTP by IP — kept for ssh-tunnel / debugging.
|
||||
# Caddy issues no TLS here (no hostname).
|
||||
:80 {
|
||||
|
|
|
|||
41
docker-compose.obsidian.yml
Normal file
41
docker-compose.obsidian.yml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Obsidian Self-hosted LiveSync stack — CouchDB.
|
||||
#
|
||||
# Деплоится ОТДЕЛЬНО от main стека:
|
||||
# docker compose -f docker-compose.obsidian.yml up -d
|
||||
#
|
||||
# Связан с main-stack через external network `gendesign_shared`. Caddy в
|
||||
# main-stack маршрутизирует obsidian.gendsgn.ru → couchdb:5984 через эту сеть.
|
||||
#
|
||||
# Сеть должна быть создана один раз вручную:
|
||||
# docker network create gendesign_shared
|
||||
# (или используй scripts/setup-obsidian-stack.sh).
|
||||
|
||||
services:
|
||||
couchdb:
|
||||
image: couchdb:3
|
||||
container_name: gendesign-couchdb
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
COUCHDB_USER: ${COUCHDB_USER:-obsidian}
|
||||
COUCHDB_PASSWORD: ${COUCHDB_PASSWORD}
|
||||
volumes:
|
||||
- couchdb_data:/opt/couchdb/data
|
||||
- couchdb_config:/opt/couchdb/etc/local.d
|
||||
expose:
|
||||
- "5984"
|
||||
networks:
|
||||
- shared
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-fsS", "http://localhost:5984/_up"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
couchdb_data:
|
||||
couchdb_config:
|
||||
|
||||
networks:
|
||||
shared:
|
||||
external: true
|
||||
name: gendesign_shared
|
||||
|
|
@ -124,9 +124,22 @@ services:
|
|||
depends_on:
|
||||
- backend
|
||||
- frontend
|
||||
networks:
|
||||
- default
|
||||
# shared — для маршрута obsidian.gendsgn.ru → couchdb (отдельный stack).
|
||||
# Если obsidian-стек не задеплоен, Caddy просто получит 502 на этом маршруте,
|
||||
# main-приложение не страдает.
|
||||
- shared
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
|
||||
networks:
|
||||
# Внешняя сеть, создаётся вне compose (см. docs/obsidian-livesync.md).
|
||||
# Связывает main-stack (Caddy) и obsidian-stack (CouchDB).
|
||||
shared:
|
||||
external: true
|
||||
name: gendesign_shared
|
||||
|
|
|
|||
255
docs/obsidian-livesync.md
Normal file
255
docs/obsidian-livesync.md
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
# Obsidian Self-hosted LiveSync (на CouchDB)
|
||||
|
||||
Single-tenant self-hosted vault на VPS. Multi-device sync через
|
||||
[Self-hosted LiveSync plugin](https://github.com/vrtmrz/obsidian-livesync).
|
||||
|
||||
## Архитектура (split deploy)
|
||||
|
||||
Obsidian-стек **отделён** от основного приложения (backend/frontend/postgres/redis).
|
||||
Каждый деплоится своим GHA workflow, никто не блокирует другого:
|
||||
|
||||
```
|
||||
┌────────────────────────────────────┐
|
||||
[laptop / mobile]──┤ Caddy (main-stack) │
|
||||
│ обслуживает gendsgn.ru + api │
|
||||
│ + reverse_proxy obsidian.* → │
|
||||
│ ───shared network───┐ │
|
||||
└─────────────────────────┼──────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────┐
|
||||
│ CouchDB (obsidian-stack) │
|
||||
│ gendesign_shared network │
|
||||
│ volumes couchdb_data │
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
| | Main stack | Obsidian stack |
|
||||
|---|---|---|
|
||||
| Compose | `docker-compose.prod.yml` | `docker-compose.obsidian.yml` |
|
||||
| Project | `gendesign` (`docker compose -p gendesign`) | `gendesign-obsidian` |
|
||||
| Workflow | `.github/workflows/deploy.yml` | `.github/workflows/deploy-obsidian.yml` |
|
||||
| Триггер push paths | `backend/**`, `frontend/**`, `Caddyfile`, main compose | `docker-compose.obsidian.yml`, `scripts/setup-couchdb.sh`, docs |
|
||||
| Сервисы | backend, frontend, postgres, redis, worker, beat, caddy | couchdb |
|
||||
| Восстанавливается отдельно | ✓ | ✓ |
|
||||
|
||||
**Связь**: внешняя docker-сеть `gendesign_shared` (создаётся вручную или
|
||||
в первом деплое). Caddy в main-stack подключён к ней + к default —
|
||||
маршрут `obsidian.gendsgn.ru` ходит в `couchdb:5984`.
|
||||
|
||||
При отсутствии obsidian-стека main работает без проблем (Caddy просто
|
||||
вернёт 502 на obsidian-route).
|
||||
|
||||
## Deploy
|
||||
|
||||
### 1. DNS
|
||||
|
||||
A-запись:
|
||||
```
|
||||
obsidian.gendsgn.ru. A <VPS_IP>
|
||||
```
|
||||
(тот же IP что `gendsgn.ru`)
|
||||
|
||||
### 2. Secrets на VPS
|
||||
|
||||
Добавить в `backend/.env.runtime` (НЕ в репо!):
|
||||
```bash
|
||||
COUCHDB_USER=obsidian
|
||||
COUCHDB_PASSWORD=<сгенерированный 32-байтный пароль>
|
||||
```
|
||||
|
||||
Сгенерировать пароль:
|
||||
```bash
|
||||
openssl rand -base64 32
|
||||
```
|
||||
|
||||
### 3. Первое создание shared network на VPS (один раз)
|
||||
|
||||
```bash
|
||||
ssh gendesign
|
||||
docker network create gendesign_shared
|
||||
```
|
||||
|
||||
(автоматически создаётся в каждом из двух workflow если её нет, но
|
||||
для первого ручного запуска удобнее самому).
|
||||
|
||||
### 4. Деплой
|
||||
|
||||
Изменения в `docker-compose.obsidian.yml` / `scripts/setup-couchdb.sh` /
|
||||
`docs/obsidian-livesync.md` → GHA триггерит **`deploy-obsidian.yml`**
|
||||
(не трогает main!).
|
||||
|
||||
Изменения в `backend/**`, `frontend/**`, `docker-compose.prod.yml`,
|
||||
`Caddyfile` → триггерит **`deploy.yml`** (main).
|
||||
|
||||
Для ручного старта впервые:
|
||||
```bash
|
||||
git push # любой из 2 workflow задеплоит свою часть
|
||||
```
|
||||
|
||||
Или вручную с VPS:
|
||||
```bash
|
||||
cd /opt/gendesign
|
||||
# main:
|
||||
docker compose -p gendesign -f docker-compose.prod.yml up -d
|
||||
# obsidian:
|
||||
docker compose -p gendesign-obsidian -f docker-compose.obsidian.yml up -d
|
||||
```
|
||||
|
||||
Caddy автоматически получит Let's Encrypt сертификат для `obsidian.gendsgn.ru`
|
||||
при первом обращении (если DNS уже резолвится).
|
||||
|
||||
### 5. Bootstrap CouchDB (один раз, идемпотентный)
|
||||
|
||||
`deploy-obsidian.yml` запускает bootstrap автоматически после `up -d`.
|
||||
Если нужно вручную:
|
||||
|
||||
```bash
|
||||
cd /opt/gendesign
|
||||
source backend/.env.runtime # подгружаем COUCHDB_PASSWORD
|
||||
COUCHDB_HOST=http://localhost:5984 \
|
||||
COUCHDB_PASSWORD="$COUCHDB_PASSWORD" \
|
||||
docker compose -p gendesign-obsidian -f docker-compose.obsidian.yml \
|
||||
exec -T couchdb bash -c "$(cat scripts/setup-couchdb.sh)"
|
||||
```
|
||||
|
||||
Скрипт:
|
||||
- конвертирует CouchDB в "configured single-node" mode
|
||||
- создаёт database `gendesign-vault`
|
||||
- включает CORS (нужно для мобильных Obsidian)
|
||||
- увеличивает лимиты на размер документов (для крупных attachments)
|
||||
- настраивает auth-timeout (для длинных sync-сессий)
|
||||
|
||||
Идемпотентен — повторный запуск безопасен.
|
||||
|
||||
### 5. Проверка работы
|
||||
|
||||
```bash
|
||||
# из любой точки интернета:
|
||||
curl https://obsidian.gendsgn.ru/_up
|
||||
# → {"status":"ok",...}
|
||||
|
||||
curl -u obsidian:<password> https://obsidian.gendsgn.ru/gendesign-vault
|
||||
# → {"db_name":"gendesign-vault","doc_count":0,...}
|
||||
```
|
||||
|
||||
## Настройка клиента (Obsidian Desktop)
|
||||
|
||||
### 1. Установить Obsidian
|
||||
- https://obsidian.md/download
|
||||
|
||||
### 2. Создать vault
|
||||
- "Create new vault" → название "gendesign" → выбрать локальную папку
|
||||
|
||||
### 3. Установить Self-hosted LiveSync plugin
|
||||
- Settings → Community plugins → "Browse"
|
||||
- Search: "Self-hosted LiveSync" (автор: vrtmrz)
|
||||
- Install → Enable
|
||||
|
||||
### 4. Настроить подключение
|
||||
|
||||
Settings → Self-hosted LiveSync → **Setup wizard** (рекомендуется первый раз):
|
||||
|
||||
| Поле | Значение |
|
||||
|---|---|
|
||||
| URI | `https://obsidian.gendsgn.ru` |
|
||||
| Username | `obsidian` (или то что в `COUCHDB_USER`) |
|
||||
| Password | `<COUCHDB_PASSWORD>` |
|
||||
| Database name | `gendesign-vault` |
|
||||
| End-to-End Encryption | **OFF** (см. ниже про trade-off) |
|
||||
|
||||
Затем:
|
||||
- "Test Database Connection" → должно быть зелёное ✓
|
||||
- "Check database configuration" → если требует — выполнить "Fix all"
|
||||
- "Apply settings"
|
||||
- "LiveSync" → "Enable LiveSync"
|
||||
|
||||
### 5. Первичный sync
|
||||
|
||||
При первом подключении vault пустой и в БД ничего нет. После первой
|
||||
правки .md файла LiveSync запушит изменение в CouchDB.
|
||||
|
||||
С другого устройства/у другого пользователя:
|
||||
- Та же установка
|
||||
- При first sync клиент скачает всё содержимое БД в локальный vault
|
||||
|
||||
### 6. Mobile (iOS / Android)
|
||||
|
||||
- Установить **Obsidian** из App Store / Google Play
|
||||
- Создать **пустой** vault
|
||||
- Установить Self-hosted LiveSync (как на desktop)
|
||||
- Те же настройки → первый sync скачает всё с сервера
|
||||
|
||||
⚠️ На iOS: включить **Background App Refresh** для Obsidian, иначе sync только при
|
||||
открытом приложении.
|
||||
|
||||
## End-to-End Encryption (E2EE)
|
||||
|
||||
| | E2EE ON | E2EE OFF (default у нас) |
|
||||
|---|---|---|
|
||||
| Контент в БД | Зашифрован (admin сервера не может прочитать) | Plain text |
|
||||
| Производительность | Тяжелее на клиенте | Быстрее |
|
||||
| Claude (через MCP/HTTP) сможет писать извне? | Нет (нужен passphrase у Claude) | Да |
|
||||
| Восстановление при потере passphrase | Невозможно | Возможно (через CouchDB admin) |
|
||||
|
||||
**Рекомендую OFF** — vault это рабочие заметки, не банковская тайна.
|
||||
HTTPS + basic auth + закрытый DNS защищают от внешнего читателя.
|
||||
|
||||
Если позже потребуется E2EE — переключить в plugin, ребутнуть БД (replicate from scratch).
|
||||
|
||||
## Multi-user
|
||||
|
||||
CouchDB поддерживает per-user permissions, но для простого MVP:
|
||||
- Один shared `gendesign-vault` database
|
||||
- Все пользователи логинятся как `obsidian` (один аккаунт)
|
||||
|
||||
Для разделения (read-only для одних, write — для других) — нужно добавить
|
||||
users в `_users` DB и настроить security object базы:
|
||||
```bash
|
||||
curl -X PUT -u obsidian:PWD https://obsidian.gendsgn.ru/_users/org.couchdb.user:viewer \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"viewer","password":"VIEWER_PWD","roles":[],"type":"user"}'
|
||||
```
|
||||
|
||||
Затем в vault DB:
|
||||
```json
|
||||
PUT /gendesign-vault/_security
|
||||
{
|
||||
"admins": {"names": ["obsidian"], "roles": []},
|
||||
"members": {"names": ["viewer"], "roles": []}
|
||||
}
|
||||
```
|
||||
|
||||
## Backup
|
||||
|
||||
```bash
|
||||
# Snapshot DB → tar.gz на VPS (можно поставить в cron)
|
||||
docker compose exec couchdb tar czf /tmp/vault-backup-$(date +%F).tar.gz /opt/couchdb/data
|
||||
docker cp gendesign-couchdb:/tmp/vault-backup-*.tar.gz /opt/gendesign-backups/
|
||||
```
|
||||
|
||||
Опционально — `_replicate` через CouchDB API на S3-совместимое хранилище.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `Failed to connect` в LiveSync
|
||||
- Проверить DNS: `dig obsidian.gendsgn.ru`
|
||||
- Проверить HTTPS: `curl https://obsidian.gendsgn.ru/_up` (должен вернуть `{"status":"ok"}`)
|
||||
- Проверить пароль: `curl -u obsidian:PWD https://obsidian.gendsgn.ru/gendesign-vault`
|
||||
|
||||
### CORS errors на mobile
|
||||
- Запустить `setup-couchdb.sh` ещё раз (или проверить через `_config/cors/origins`)
|
||||
|
||||
### Большие attachments не синкаются
|
||||
- В LiveSync plugin: "Customization & advanced" → "Chunk size" = 100 KB
|
||||
- На сервере: `couchdb/max_document_size` уже = 50 MB
|
||||
|
||||
### Конфликты при одновременной правке
|
||||
- LiveSync создаёт conflict-маркер в файле
|
||||
- Plugin показывает UI для merge ("Show conflict resolution dialog")
|
||||
|
||||
## Что дальше
|
||||
|
||||
После того как multi-device sync работает:
|
||||
- Решить как Claude пишет в vault: MCP filesystem + bridge / самописный CouchDB-MCP / headless Obsidian
|
||||
- Мигрировать существующий `memory-gendesign.jsonl` knowledge graph → .md файлы в vault
|
||||
107
scripts/setup-couchdb.sh
Normal file
107
scripts/setup-couchdb.sh
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#!/usr/bin/env bash
|
||||
# Bootstrap CouchDB для Obsidian Self-hosted LiveSync.
|
||||
#
|
||||
# Запускается ОДИН РАЗ после первого `docker compose up -d couchdb`.
|
||||
# Идемпотентен — повторный запуск безопасен.
|
||||
#
|
||||
# Что делает:
|
||||
# 1. Конвертирует single-node setup в "single-node configured" (требование CouchDB 3.x)
|
||||
# 2. Включает CORS (нужно для Obsidian мобильного / browser-client)
|
||||
# 3. Увеличивает max_http_request_size до 4 GiB (для крупных attachments)
|
||||
# 4. Создаёт database `gendesign-vault` для самого vault
|
||||
# 5. Создаёт database `_users` (системная, для multi-user в будущем)
|
||||
#
|
||||
# Использование:
|
||||
# COUCHDB_PASSWORD=xxx ./scripts/setup-couchdb.sh
|
||||
# # или на VPS:
|
||||
# docker compose exec couchdb bash -c 'curl ...' # ручной путь
|
||||
#
|
||||
# Требует: curl. Запускать с хоста VPS (couchdb должен быть на 127.0.0.1:5984).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
COUCHDB_HOST="${COUCHDB_HOST:-http://localhost:5984}"
|
||||
COUCHDB_USER="${COUCHDB_USER:-obsidian}"
|
||||
COUCHDB_PASSWORD="${COUCHDB_PASSWORD:?COUCHDB_PASSWORD env var is required}"
|
||||
VAULT_DB="${VAULT_DB:-gendesign-vault}"
|
||||
|
||||
AUTH="-u ${COUCHDB_USER}:${COUCHDB_PASSWORD}"
|
||||
|
||||
echo "=== 0. Wait for CouchDB ready ==="
|
||||
for i in $(seq 1 30); do
|
||||
if curl -fsS "${COUCHDB_HOST}/_up" >/dev/null 2>&1; then
|
||||
echo " CouchDB up after ${i}s"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "=== 1. Convert to 'configured single-node' (idempotent) ==="
|
||||
curl -fsS ${AUTH} -X POST "${COUCHDB_HOST}/_cluster_setup" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"action":"enable_single_node","bind_address":"0.0.0.0","username":"'"${COUCHDB_USER}"'","password":"'"${COUCHDB_PASSWORD}"'"}' \
|
||||
|| echo " (уже configured, OK)"
|
||||
|
||||
echo "=== 2. Системные базы ==="
|
||||
for db in _users _replicator _global_changes; do
|
||||
curl -fsS ${AUTH} -X PUT "${COUCHDB_HOST}/${db}" 2>&1 \
|
||||
| grep -q '"ok":true\|"file_exists"' && echo " ${db}: OK"
|
||||
done
|
||||
|
||||
echo "=== 3. Vault database '${VAULT_DB}' ==="
|
||||
curl -fsS ${AUTH} -X PUT "${COUCHDB_HOST}/${VAULT_DB}" 2>&1 \
|
||||
| grep -q '"ok":true\|"file_exists"' && echo " ${VAULT_DB}: OK"
|
||||
|
||||
echo "=== 4. CORS (для мобильных клиентов) ==="
|
||||
cors_settings=(
|
||||
"httpd/enable_cors true"
|
||||
"cors/origins app://obsidian.md,capacitor://localhost,http://localhost"
|
||||
"cors/credentials true"
|
||||
"cors/methods GET,PUT,POST,HEAD,DELETE"
|
||||
"cors/headers accept,authorization,content-type,origin,referer,x-csrf-token"
|
||||
)
|
||||
for kv in "${cors_settings[@]}"; do
|
||||
key="${kv%% *}"
|
||||
value="${kv#* }"
|
||||
curl -fsS ${AUTH} -X PUT \
|
||||
"${COUCHDB_HOST}/_node/_local/_config/${key}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "\"${value}\"" >/dev/null
|
||||
echo " ${key} = ${value}"
|
||||
done
|
||||
|
||||
echo "=== 5. Размер запросов / chunks (для больших документов LiveSync) ==="
|
||||
curl -fsS ${AUTH} -X PUT \
|
||||
"${COUCHDB_HOST}/_node/_local/_config/chttpd/max_http_request_size" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '"4294967296"' >/dev/null
|
||||
echo " chttpd/max_http_request_size = 4 GiB"
|
||||
|
||||
curl -fsS ${AUTH} -X PUT \
|
||||
"${COUCHDB_HOST}/_node/_local/_config/couchdb/max_document_size" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '"50000000"' >/dev/null
|
||||
echo " couchdb/max_document_size = 50 MB"
|
||||
|
||||
echo "=== 6. Auth settings (CouchDB 3.x требует session timeout для LiveSync) ==="
|
||||
curl -fsS ${AUTH} -X PUT \
|
||||
"${COUCHDB_HOST}/_node/_local/_config/chttpd_auth/require_valid_user" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '"true"' >/dev/null
|
||||
curl -fsS ${AUTH} -X PUT \
|
||||
"${COUCHDB_HOST}/_node/_local/_config/chttpd_auth/timeout" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '"7200"' >/dev/null
|
||||
echo " chttpd_auth: require_valid_user=true, timeout=7200s"
|
||||
|
||||
echo ""
|
||||
echo "=== DONE ==="
|
||||
echo "CouchDB готова к Self-hosted LiveSync."
|
||||
echo ""
|
||||
echo "Клиенту Obsidian → Settings → Self-hosted LiveSync → Remote DB:"
|
||||
echo " URL: https://obsidian.gendsgn.ru"
|
||||
echo " Database: ${VAULT_DB}"
|
||||
echo " User: ${COUCHDB_USER}"
|
||||
echo " Password: <COUCHDB_PASSWORD>"
|
||||
echo ""
|
||||
echo "Дальше: 'Test Database Connection' → 'Apply Settings' → 'Replicate Now'."
|
||||
Loading…
Add table
Reference in a new issue