# 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) ```yaml # /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) ```yaml # /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 ```caddy 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 (получает автоматически). ## Команды применения ```bash # 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 ```bash 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.