Files
email-assistant/openspec/changes/local-calendar-tasks/design.md
T

5.3 KiB

Design: Локальные сервисы календаря (Radicale) и задач (Vikunja)

Архитектура (по требованию пользователя)

ВСЕ сервисы работают в отдельных Docker-контейнерах. Полный стек изолирован в docker compose; никаких systemd-сервисов для Radicale/Vikunja.

┌────────────────────────────────────────────┐
│ bigbox (docker)                             │
│  ┌──────────────┐   ┌───────────────────┐  │
│  │ radicale     │   │ vikunja           │  │
│  │ :5232 CalDAV │   │ :3456 WebUI+API   │  │
│  │              │   │ └─ postgres (:5433)│ │
│  └──────────────┘   └───────────────────┘  │
└────────────────────────────────────────────┘
        │                        │
        └── Caddy reverse proxy (TLS) → Android DAVx5

Сервисы и файлы

Сервис Образ Порт Каталог Данные
Radicale tobymossman/radicale 5232 /opt/hermes/radicale/ volume radicale-data (коллекции .xand)
Vikunja vikunja/vikunja:latest 3456 /opt/hermes/vikunja/ volume vikunja-files, vikunja-db
PostgreSQL postgres:16-alpine 5433 (внутр.) — volume vikunja-db

Radicale (docker)

# /opt/hermes/radicale/docker-compose.yml
services:
  radicale:
    image: tobyworrall/radicale:latest
    container_name: radicale
    ports:
      - "5232:5232"
    volumes:
      - ./data:/data
      - ./config:/config
    environment:
      - RADICALE_APPS=caldav
    restart: unless-stopped
  • Auth: HTTP Basic. Пользователи в config (htpasswd-файл users)
  • Коллекции: /data/collections/ (календари .xand, задачи .ics)
  • Создание коллекций: через DAVx5/curl PUT, или вручную скриптом (radicale_collections.sh создаёт «Личный», «Рабочий», «Задачи»)

Vikunja (docker)

# /opt/hermes/vikunja/docker-compose.yml
services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: [REDACTED]
      POSTGRES_DB: vikunja
    volumes:
      - vikunja-db:/var/lib/postgresql/data
    restart: unless-stopped

  vikunja:
    image: vikunja/vikunja:latest
    depends_on: [db]
    environment:
      VIKUNJA_DATABASE_TYPE: postgres
      VIKUNJA_DATABASE_HOST: db
      VIKUNJA_DATABASE_DATABASE: vikunja
      VIKUNJA_DATABASE_USERNAME: vikunja
      VIKUNJA_DATABASE_PASSWORD: [REDACTED]
      VIKUNJA_SERVICE_JWTSECRET: [REDACTED]
      VIKUNJA_SERVICE_FRONTENDURL: https://tasks.nixg.ru
      VIKUNJA_SERVICE_PUBLICURL: https://tasks.nixg.ru
    ports:
      - "3456:80"
    volumes:
      - vikunja-files:/app/files
    restart: unless-stopped
  • Порт: 3456 (netbox на 8080 — не конфликтует)
  • Postgres: внутренний контейнер на 5433 (не публикуем наружу)
  • Данные: volumes vikunja-db + vikunja-files
  • Логин: первый зарегистрированный пользователь становится админом
  • API: /api/v1/ — токен в Настройки → API tokens

Caddy reverse proxy (TLS для Android)

Radicale (DAVx5) и Vikunja доступны снаружи через Caddy (он уже есть в /opt/gitea.nixg.ru). Домены:

  • cal.nixg.ru → Radicale :5232
  • tasks.nixg.ru → Vikunja :3456
cal.nixg.ru {
    reverse_proxy 127.0.0.1:5232
}
tasks.nixg.ru {
    reverse_proxy 127.0.0.1:3456
}

TLS — Let's Encrypt через Caddy (получает автоматически).

Команды применения

# Radicale
mkdir -p /opt/hermes/radicale/{config,data}
cd /opt/hermes/radicale && docker compose up -d

# Vikunja
mkdir -p /opt/hermes/vikunja
cd /opt/hermes/vikunja && docker compose up -d

# Проверка
curl -i -X PROPFIND http://127.0.0.1:5232/ -u estorozhenko:...
curl http://127.0.0.1:3456/api/v1/info

Верификация (GIVEN/WHEN/THEN → команды)

  1. Radicale: curl -s -o /dev/null -w '%{http_code}' -X PROPFIND http://127.0.0.1:5232/ -u estorozhenko:... → 207
  2. Vikunja: curl -s http://127.0.0.1:3456/api/v1/info → JSON 200
  3. API-токен: создать задачу → 201
  4. Android/DAVx5: событие с телефона появляется в Radicale

Rollback

cd /opt/hermes/vikunja && docker compose down -v   # удалить всё, включая volume
cd /opt/hermes/radicale && docker compose down -v
rm -rf /opt/hermes/vikunja /opt/hermes/radicale
# убрать строки из Caddy и перезапустить его (внешне, sudo systemctl restart)

Секреты

Пароли/токены — только в .env файлах сервисов ([REDACTED] в доках), НЕ в git.