Compare commits

...

19 Commits

Author SHA1 Message Date
9e218fe87d vault backup: 2026-04-09 21:30:33 2026-04-09 21:30:33 +03:00
5b1191b398 vault backup: 2026-04-09 21:10:02 2026-04-09 21:10:02 +03:00
1a878d5e98 vault backup: 2026-04-07 14:24:28 2026-04-07 14:24:28 +03:00
134a142774 vault backup: 2026-04-07 11:53:46 2026-04-07 11:53:46 +03:00
0d25cbe8b8 vault backup: 2026-04-05 15:15:22 2026-04-05 15:15:22 +03:00
d3e783f512 vault backup: 2026-04-05 00:48:47 2026-04-05 00:48:47 +03:00
Cosmo
e2cbac051a design: анализ Ultrahuman Ring PRO для вдохновения Pulse 2026-04-02 15:42:49 +00:00
43e271733b vault backup: 2026-04-02 13:59:18 2026-04-02 13:59:19 +03:00
Cosmo
20e1f3fa6c docs: инфраструктура VM Сервисы + архитектура pulse-api и pulse-web 2026-04-02 10:43:44 +00:00
Cosmo
d4f3a10d0d docs: Marzban — план действий при блокировке VPN 2026-04-01 14:09:18 +00:00
ec5f55a897 vault backup: 2026-04-01 14:45:03 2026-04-01 14:45:03 +03:00
cfaf1e8f45 vault backup: 2026-04-01 13:05:39 2026-04-01 13:05:39 +03:00
0f31a7eee6 vault backup: 2026-04-01 12:50:06 2026-04-01 12:50:06 +03:00
f2a00f328f vault backup: 2026-04-01 12:37:58 2026-04-01 12:37:58 +03:00
1ab4f4412d vault backup: 2026-04-01 12:17:06 2026-04-01 12:17:06 +03:00
7c043afd4f vault backup: 2026-04-01 11:23:38 2026-04-01 11:23:38 +03:00
Cosmo
7b2415eeda feat: Cosmo Studio plan — пиксельный офис агентов 2026-03-27 17:58:59 +00:00
b1220f1d2e vault backup: 2026-03-26 14:02:52 2026-03-26 14:02:52 +03:00
d953f87c92 vault backup: 2026-03-26 12:38:28 2026-03-26 12:38:28 +03:00
42 changed files with 3089 additions and 2 deletions

View File

@@ -0,0 +1,126 @@
# BPMSoft CRM — RAG поиск по коду
Семантический поиск по кодовой базе проекта `C:\Work\BPMSoft\crm`.
Стек: **Qdrant** (векторная БД) + **Ollama nomic-embed-text** (эмбеддинги) + **MCP сервер** для Claude Code + **веб-интерфейс**.
---
## Инфраструктура
| Компонент | Адрес | Описание |
| ---------------- | --------------------------------- | ------------------------ |
| Qdrant | `http://localhost:6333` | Векторная БД, Docker |
| Qdrant Dashboard | `http://localhost:6333/dashboard` | Веб-интерфейс Qdrant |
| Ollama | `http://localhost:11434` | Локальные эмбеддинги |
| Веб-поиск | `http://localhost:8765` | Интерфейс поиска по коду |
Файлы проекта: `C:\Work\BPMSoft\crm\.rag\`
---
## Команды
### Запустить Qdrant (Docker)
```bash
docker start qdrant
```
> Контейнер настроен на `restart=always` — стартует автоматически вместе с Docker.
### Запустить веб-интерфейс поиска
```bash
python C:\Work\BPMSoft\crm\.rag\search_server.py
```
Открыть в браузере: `http://localhost:8765`
### Инкрементальное обновление индекса (только изменённые файлы)
```bash
python C:\Work\BPMSoft\crm\.rag\index_incremental.py
```
Запускать после изменения/добавления файлов в проекте. Работает быстро — обрабатывает только изменённые файлы.
### Полная переиндексация (редко, при крупных изменениях)
```bash
python C:\Work\BPMSoft\crm\.rag\index_codebase.py --reindex
```
> Занимает несколько часов — пересоздаёт всю коллекцию с нуля.
---
## Что индексируется
### C# (`BPMSoft.Configuration`)
- Методы, конструкторы → тип `method`
- Классы, структуры → тип `class` / `struct`
- Интерфейсы → тип `interface`
- Enum'ы → тип `enum`
- XML doc-комментарии включены в контекст
### JavaScript (`BPMSoft.Configuration`)
- ExtJS BPMSoft схемы (`/Schemas/*.js`) → тип `extjs_schema`
- Методы внутри схем → тип `extjs_method`
- Mixins схем → тип `extjs_mixins`
- Angular/src файлы (`/Files/src/js/`) → `function`, `js_class`, `js_method`
- Autogenerated JS платформы (`/Autogenerated/Src/`)
### Исключено из индекса
- `SvnPackages/` — SVN пакеты
- `node_modules/`, `bin/`, `obj/`, `dist/`
- `*.min.js` — минифицированные файлы
- Angular build cache `/.nx/`
---
## MCP сервер для Claude Code
Зарегистрирован автоматически. Доступные инструменты:
| Инструмент | Описание |
|------------|----------|
| `search_codebase` | Семантический поиск по запросу |
| `find_symbol` | Поиск класса/метода по имени |
| `find_in_namespace` | Все символы в namespace или по пути |
### Регистрация вручную (если слетела)
```bash
claude mcp add --transport stdio crm-rag -- cmd /c python C:\Work\BPMSoft\crm\.rag\mcp_server.py
```
### Проверить статус
```bash
claude mcp list
```
---
## Файлы
```
C:\Work\BPMSoft\crm\.rag\
├── index_codebase.py # Полная индексация
├── index_incremental.py # Инкрементальное обновление
├── mcp_server.py # MCP сервер для Claude Code
├── search_server.py # Веб-сервер поиска (FastAPI)
├── search_ui.html # Интерфейс поиска
├── file_hashes.json # Хеши файлов для инкрементальной индексации
└── qdrant_storage/ # Данные Qdrant (Docker volume)
```
---
## Зависимости Python
```bash
pip install qdrant-client tree-sitter tree-sitter-c-sharp tree-sitter-javascript requests fastapi uvicorn mcp
```
---
## Управление Docker контейнером
```bash
docker ps | grep qdrant # статус
docker stop qdrant # остановить
docker start qdrant # запустить
docker logs qdrant # логи
docker restart qdrant # перезапустить
```

0
Companies.md Normal file
View File

202
Dev/Cosmo Studio.md Normal file
View File

@@ -0,0 +1,202 @@
# Cosmo Studio — Пиксельный офис агентов
> Веб-дашборд в реальном времени: Cosmo, Люся и Claude Coding как пиксельные персонажи в виртуальном офисе.
## Концепция
Открываешь браузер → видишь офис. Я пишу тебе ответ → мой персонаж печатает за компьютером. Агент закончил работу → идёт на диван в зону отдыха. Люся думает → её персонаж чешет голову.
**Стек:**
- Frontend: React + Canvas 2D (пиксель-арт офис)
- Backend: Node.js + WebSocket (мост между OpenClaw и браузером)
- Хостинг: VM Сервисы → `studio.digital-home.site`
- Спрайты: из репозитория AgentRoom (MIT лицензия)
---
## Этапы
### Этап 0 — Подготовка (1-2 дня)
**0.1 Открыть OpenClaw API для VM Сервисов**
- Сейчас `gateway.bind = "lan"` — уже слушает на LAN интерфейсе (192.168.31.103:18789)
- VM Сервисы (192.168.31.60) должна быть в той же сети → проверить пинг
- Если не достучаться: включить `gateway.bind = "lan"` явно + проверить firewall
- Аутентификация: токен уже настроен в конфиге
**0.2 Скачать спрайты из AgentRoom**
Репозиторий: https://github.com/liuyixin-louis/agentroom
Нужные файлы:
- `agentroom-visual/public/assets/` — тайлсеты офиса (SkyOffice: FloorAndGround, Modern_Office)
- `agentroom-visual/public/assets/characters/` — спрайты персонажей (JIK-A-4 Metro City)
- Каждый персонаж: 4 направления × 3 кадра = 12 спрайтов (idle, walk, type)
**0.3 Назначить персонажей агентам**
| Агент | Персонаж | Цвет |
|-------|----------|------|
| Cosmo (main) | Синий character | #3B82F6 |
| Люся (wife) | Розовый character | #EC4899 |
| Claude Coding | Зелёный character | #10B981 |
| Claude Research | Фиолетовый character | #8B5CF6 |
---
### Этап 1 — OpenClaw Bridge (2-3 дня)
**Задача:** Node.js сервис на VM Сервисы, который подключается к OpenClaw WebSocket API и транслирует события агентов.
**Структура:** `/opt/digital-home/cosmo-studio/bridge/`
```
bridge/
├── index.js # WebSocket клиент к OpenClaw
├── state.js # Состояние всех агентов
├── server.js # WebSocket сервер → браузер
└── parser.js # Разбор JSONL событий
```
**Логика определения состояния агента:**
OpenClaw sessions API: `GET http://192.168.31.103:18789/sessions`
WebSocket events: `ws://192.168.31.103:18789`
Из JSONL событий:
```
toolCall → agent = "typing" (пишет)
toolResult → agent = "reading" (читает/думает)
text block → agent = "sending" (отправляет)
session idle → agent = "idle" (свободен)
session running → agent = "working"
```
**Состояния персонажа:**
- `idle` → сидит за столом, анимация дыхания
- `typing` → руки на клавиатуре, быстрая анимация
- `thinking` → смотрит в экран, медленная анимация
- `walking` → движется по офису (BFS pathfinding)
- `break` → на диване в Break Room
- `waiting` → speech bubble "⏳"
---
### Этап 2 — Офисный движок (3-4 дня)
**Задача:** React + Canvas 2D рендерер с тайлсетами и персонажами.
**Структура:** `/opt/digital-home/cosmo-studio/frontend/`
```
src/
├── engine/
│ ├── Renderer.ts # Canvas 2D loop (requestAnimationFrame)
│ ├── TilesetManager.ts # Загрузка и рендер тайлов
│ ├── Character.ts # Спрайт + анимация + позиция
│ ├── Pathfinding.ts # BFS по офисной сетке
│ └── OfficeLayout.ts # Карта офиса (столы, диваны, зоны)
├── components/
│ ├── Office.tsx # Основной Canvas компонент
│ ├── AgentCard.tsx # Карточка агента (статус, токены)
│ └── EventFeed.tsx # Лента событий (последние действия)
├── hooks/
│ └── useAgentEvents.ts # WebSocket → состояния агентов
└── App.tsx
```
**Офисная карта (32×24 тайла, каждый 32px = 1024×768px):**
```
┌─────────────────────────────────┐
│ [💻 Desk Cosmo] [💻 Desk Люся] │ ← Work Room
│ [💻 Desk Coding] [💻 Desk Res] │
├─────────────────────────────────┤
│ [🛋️ Couch] [🌱 Plant] [TV] │ ← Break Room
└─────────────────────────────────┘
```
**Логика переходов:**
- Агент стал `working` → персонаж идёт к своему столу (BFS)
- Агент стал `idle` → персонаж идёт в Break Room
- Агент `typing` → анимация печати
- Два агента встретились → speech bubble "👋"
---
### Этап 3 — UI панели (1-2 дня)
**Боковая панель:**
- Список агентов: имя + статус + текущее действие
- Токены: потрачено сегодня, неделя
- Последнее сообщение (первые 60 символов)
**Нижняя панель — лента событий:**
```
17:42 Cosmo → отвечает Даниилу (telegram)
17:41 Coding → читает файл main.go
17:40 Люся → обрабатывает heartbeat
```
**Верхняя панель:**
- Дата/время
- Кнопка "Open in Telegram" → deep link
- Индикатор здоровья системы
---
### Этап 4 — Деплой (1 день)
```yaml
# /opt/digital-home/cosmo-studio/docker-compose.yml
services:
cosmo-studio:
build: .
ports:
- "3400:3400"
environment:
- OPENCLAW_URL=ws://192.168.31.103:18789
- OPENCLAW_TOKEN=${OPENCLAW_TOKEN}
networks:
- services_proxy
restart: unless-stopped
networks:
services_proxy:
external: true
```
NPM proxy host: `studio.digital-home.site``cosmo-studio:3400`
---
## Что нужно решить заранее
1. **OpenClaw WebSocket API** — какие именно события транслирует, нужно изучить документацию `/home/daniil/.npm-global/lib/node_modules/openclaw/docs`
2. **Firewall между VPS и VM Сервисы** — сейчас VM Сервисы не видит gateway. Вариант: открыть порт 18789 в ufw на VPS, или использовать SSH tunnel.
3. **Спрайты** — нужно клонировать репо и выбрать конкретные файлы из `agentroom-visual/public/assets/`
4. **Аутентификация дашборда** — добавить basic auth через NPM или JWT
---
## Оценка времени
| Этап | Работа |
|------|--------|
| 0. Подготовка | 1-2 дня |
| 1. Bridge сервис | 2-3 дня |
| 2. Офисный движок | 3-4 дня |
| 3. UI панели | 1-2 дня |
| 4. Деплой | 1 день |
| **Итого** | **~2 недели** |
---
## Статус
- [ ] Этап 0: Подготовка
- [ ] Этап 1: Bridge сервис
- [ ] Этап 2: Офисный движок
- [ ] Этап 3: UI панели
- [ ] Этап 4: Деплой
*Создано: 2026-03-27*

View File

@@ -42,8 +42,8 @@ Dart — типизированный язык, похожий на гибрид
### Что изучить
- [ ] Синтаксис: переменные, типы, `final`/`const`, `late`
- [ ] Null safety (`?`, `!`, `??`, `?.`)
- [x] Синтаксис: переменные, типы, `final`/`const`, `late` ✅ 2026-03-26
- [x] Null safety (`?`, `!`, `??`, `?.`) ✅ 2026-03-26
- [ ] Классы, наследование, mixins, abstract classes
- [ ] Extensions (привет, C# extension methods)
- [ ] Enums (enhanced enums с методами — Dart 3)

92
Sing-box.md Normal file
View File

@@ -0,0 +1,92 @@
1. Конфиг
```json
{
"log": {
"level": "info"
},
"dns": {
"servers": [
{
"tag": "google",
"address": "https://8.8.8.8/dns-query",
"address_strategy": "ipv4_only",
"detour": "proxy"
}
],
"strategy": "ipv4_only"
},
"inbounds": [
{
"type": "tun",
"tag": "tun-in",
"interface_name": "tun0",
"address": "172.19.0.1/30",
"mtu": 1400,
"auto_route": true,
"strict_route": false,
"stack": "system",
"route_exclude_address": [
"192.168.31.0/24",
"195.135.254.66/32",
"100.64.0.0/10"
]
}
],
"outbounds": [
{
"type": "vless",
"tag": "proxy",
"server": "195.135.254.66",
"server_port": 8888,
"uuid": "96639c6e-553f-4389-95d6-e9e3e808f22f",
"tls": {
"enabled": true,
"server_name": "www.microsoft.com",
"utls": {
"enabled": true,
"fingerprint": "chrome"
},
"reality": {
"enabled": true,
"public_key": "da-vsnYvc3G8nSguWmo0ort1nWBWcFs2nWY_kAcdYjI",
"short_id": "a1b2c3d4"
}
}
},
{
"type": "direct",
"tag": "direct"
}
],
"route": {
"rules": [
{
"protocol": "dns",
"action": "hijack-dns"
},
{
"ip_cidr": [
"192.168.31.0/24",
"100.64.0.0/10"
],
"outbound": "direct"
}
],
"final": "proxy",
"auto_detect_interface": true
}
}
```
2. Команды
```sh
sudo systemctl start sing-box\nsleep 3\ncurl --max-time 5 http://34.160.111.145 -H "Host: ifconfig.me"
sudo ip route add 195.135.254.66/32 via 192.168.31.1 dev ens18 2>/dev/null true
curl --max-time 5 ifconfig.me
sudo systemctl restart sing-box\nsleep 2 sudo ip route add 195.135.254.66/32 via 192.168.31.1 dev ens18 2>/dev/null true\n sudo ip route add 100.64.0.0/10 via 192.168.31.1 dev ens18 2>/dev/null true\n sleep 3\n journalctl -u sing-box -n 10 --no-pager\n curl --max-time 5 ifconfig.me
sudo iptables -t mangle -A POSTROUTING -o ens18 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1000\nsudo iptables -t mangle -A PREROUTING -i ens18 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1000\ncurl --max-time 5 http://34.160.111.145 -H "Host: ifconfig.me"
sudo systemctl stop sing-box
curl --max-time 5 http://34.160.111.145 -H "Host: ifconfig.me"
sudo tcpdump -i ens18 -n 'host 195.135.254.66' -c 10 &\nsleep 1\ncurl --max-time 3 http://34.160.111.145 -H "Host: ifconfig.me"
sudo systemctl start sing-box\nsleep 2\nsudo tcpdump -i ens18 -n 'host 195.135.254.66' -c 10 &\nsleep 1\ncurl --max-time 3 http://34.160.111.145 -H "Host: ifconfig.me"
sudo systemctl stop sing-box
```

View File

@@ -0,0 +1,219 @@
# Marzban — план действий при блокировке
> Инфраструктура: VDS Рига `195.135.254.66`, панель `https://daniilvds.duckdns.org:2083/dashboard/`
> Логин: `admin` / `Marzban2026!`
---
## 🚨 Быстрая смена порта
### 1. Меняем порт в Marzban (xray конфиг)
Через браузер:
```
https://daniilvds.duckdns.org:2083/dashboard/
→ Core Settings → Xray Config
→ Меняем "port": 8888 на новый (например 9443)
→ Save
```
Или через API (с любой машины):
```bash
TOKEN=$(curl -s -X POST https://daniilvds.duckdns.org:2083/api/admin/token \
-d "username=admin&password=Marzban2026!" \
-H "Content-Type: application/x-www-form-urlencoded" | python3 -c "import json,sys; print(json.load(sys.stdin)['access_token'])")
# Получить текущий конфиг
curl -s https://daniilvds.duckdns.org:2083/api/core/config \
-H "Authorization: Bearer $TOKEN"
# Применить изменённый конфиг (меняешь port в JSON и применяешь)
curl -s -X PUT https://daniilvds.duckdns.org:2083/api/core/config \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ ... новый конфиг ... }'
```
### 2. Пробрасываем новый порт в Docker
```bash
ssh root@195.135.254.66
# Редактируем compose
nano /opt/marzban/docker-compose.yml
# Меняем "8888:8888" → "9443:9443"
cd /opt/marzban && docker compose up -d marzban
```
### 3. Обновляем ключи пользователям
Ключи пользователей **обновляются автоматически** — порт меняется во всех ссылках.
Заходишь в панель → выбираешь пользователя → копируешь новую ссылку → отправляешь.
Или через API:
```bash
curl -s https://daniilvds.duckdns.org:2083/api/user/USERNAME \
-H "Authorization: Bearer $TOKEN"
# В поле "links" будет актуальная ссылка с новым портом
```
---
## 👥 Управление пользователями
### Создать пользователя
```bash
curl -s -X POST https://daniilvds.duckdns.org:2083/api/user \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "vasya",
"proxies": {"vless": {}},
"inbounds": {"VLESS_REALITY": []}
}'
```
### Получить ссылку пользователя
```bash
curl -s https://daniilvds.duckdns.org:2083/api/user/vasya \
-H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['links'][0])"
```
### Удалить пользователя (отозвать доступ)
```bash
curl -s -X DELETE https://daniilvds.duckdns.org:2083/api/user/vasya \
-H "Authorization: Bearer $TOKEN"
```
### Список всех пользователей
```bash
curl -s "https://daniilvds.duckdns.org:2083/api/users" \
-H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; [print(u['username'], u['status']) for u in json.load(sys.stdin)['users']]"
```
---
## 🔌 Добавить новый протокол (inbound)
Через **Core Settings → Xray Config** в панели добавляешь новый блок в `inbounds`.
### Shadowsocks (простой, хорошо работает через мобильный)
```json
{
"tag": "SHADOWSOCKS",
"listen": "0.0.0.0",
"port": 8388,
"protocol": "shadowsocks",
"settings": {
"clients": [],
"network": "tcp,udp"
}
}
```
### VLESS + WebSocket + TLS (обходит DPI через CDN/Cloudflare)
```json
{
"tag": "VLESS_WS",
"listen": "0.0.0.0",
"port": 8889,
"protocol": "vless",
"settings": {
"clients": [],
"decryption": "none"
},
"streamSettings": {
"network": "ws",
"security": "none",
"wsSettings": {
"path": "/vpn"
}
}
}
```
После добавления inbound — в настройках пользователя выбрать новый inbound.
---
## ⚡ Устойчивые порты (реже блокируют)
| Порт | Почему безопасен |
|------|-----------------|
| 443 | HTTPS — блокировка убьёт половину интернета |
| 80 | HTTP — аналогично |
| 2083 | cPanel SSL — используется хостингами |
| 2087 | cPanel — аналогично |
| 8443 | Альтернативный HTTPS |
**Переехать на 443:**
1. Остановить Outline/shadowbox: `docker stop shadowbox`
2. Сменить порт Marzban на 443 (через панель + compose)
---
## 🔄 Полезные команды на сервере
```bash
ssh root@195.135.254.66
# Статус контейнеров
cd /opt/marzban && docker compose ps
# Логи Marzban
docker logs marzban-marzban-1 --tail 50
# Логи Caddy (SSL)
docker logs marzban-caddy-1 --tail 20
# Перезапуск Marzban
docker compose restart marzban
# Перезапуск всего стека
docker compose restart
# Обновить образ Marzban
docker compose pull && docker compose up -d
# Занятые порты
ss -tlnp | grep -E "8888|2053|443|80"
```
---
## 🔑 Reality — что делать если начали детектировать
Reality маскируется под настоящий TLS-хендшейк к `www.microsoft.com`. Если заблокировали:
1. Сменить `dest` на другой популярный сайт (например `www.google.com:443` или `www.apple.com:443`)
2. Сгенерировать новую пару ключей:
```bash
docker exec marzban-marzban-1 xray x25519
```
3. Обновить `privateKey`, `publicKey`, `shortIds` в Core Settings
4. Все пользователи получат новые ссылки автоматически (pubkey меняется в ссылке)
---
## 📱 Приложения для клиентов
| Платформа | Приложение | Ссылка |
|-----------|-----------|--------|
| iOS | Karing | https://apps.apple.com/app/karing/id6472431552 |
| Android | Hiddify | https://play.google.com/store/apps/details?id=app.hiddify.com |
| Windows | Hiddify | https://github.com/hiddify/hiddify-app/releases/latest |
| macOS | Hiddify | https://github.com/hiddify/hiddify-app/releases/latest |
---
## 📋 Страница для раздачи ключей
`https://vpn.digital-home.site/admin?key=mysecret2026`
Вставляешь vless:// ссылку из Marzban → получаешь ссылку на 24 часа для друга.
---
*Обновлено: 01.04.2026*

View File

@@ -0,0 +1,111 @@
```
{
"log": {
"loglevel": "warning"
},
"routing": {
"rules": [
{
"ip": [
"geoip:cloudflare"
],
"outboundTag": "DIRECT",
"type": "field"
}
]
},
"inbounds": [
{
"tag": "VLESS_REALITY",
"listen": "0.0.0.0",
"port": 2083,
"protocol": "vless",
"settings": {
"clients": [],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"show": false,
"dest": "www.apple.com:443",
"xver": 0,
"serverNames": [
"www.apple.com"
],
"privateKey": "sIiJLOrd5kisiC4Fx3OXBKqEinrtYa5vt4CalThTvkI",
"shortIds": [
"fb8382e98f461945",
"a1b2c3d4"
]
}
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "DIRECT"
},
{
"protocol": "blackhole",
"tag": "BLOCK"
}
]
}
```
```
{
"log": {
"loglevel": "warning"
},
"routing": {
"rules": [
{
"ip": [
"geoip:private"
],
"outboundTag": "BLOCK",
"type": "field"
}
]
},
"inbounds": [
{
"tag": "Shadowsocks TCP",
"listen": "0.0.0.0",
"port": 1080,
"protocol": "shadowsocks",
"settings": {
"clients": [],
"network": "tcp,udp"
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "DIRECT"
},
{
"protocol": "blackhole",
"tag": "BLOCK"
}
]
}
```
```
id leadNumber creator
```

303
Без названия.md Normal file
View File

@@ -0,0 +1,303 @@
cat .openclaw/openclaw.json  8.07s   :daniil-openclaw 
{
"meta": {
"lastTouchedVersion": "2026.3.24",
"lastTouchedAt": "2026-04-04T21:30:41.300Z"
},
"env": {
"MOONSHOT_API_KEY": "sk-TOKEN"
},
"wizard": {
"lastRunAt": "2026-03-31T11:19:16.350Z",
"lastRunVersion": "2026.3.24",
"lastRunCommand": "doctor",
"lastRunMode": "local"
},
"auth": {
"profiles": {
"anthropic:default": {
"provider": "anthropic",
"mode": "token"
}
}
},
"acp": {
"enabled": true,
"dispatch": {
"enabled": true
},
"backend": "acpx",
"defaultAgent": "claude",
"allowedAgents": [
"codex",
"claude",
"opencode",
"gemini"
],
"stream": {
"coalesceIdleMs": 5000,
"maxChunkChars": 16000
},
"runtime": {
"ttlMinutes": 120
}
},
"models": {
"providers": {
"moonshot": {
"baseUrl": "https://api.moonshot.ai/v1",
"apiKey": "${MOONSHOT_API_KEY}",
"api": "openai-completions",
"models": [
{
"id": "kimi-k2.5",
"name": "Kimi K2.5",
"reasoning": false,
"input": [
"text",
"image"
],
"contextWindow": 262144,
"maxTokens": 262144
},
{
"id": "kimi-k2-thinking",
"name": "Kimi K2 Thinking",
"reasoning": true,
"input": [
"text"
],
"contextWindow": 262144,
"maxTokens": 262144
}
]
}
}
},
"agents": {
"defaults": {
"model": {
"primary": "anthropic/claude-sonnet-4-6",
"fallbacks": [
"anthropic/claude-haiku-4-5"
]
},
"models": {
"anthropic/claude-sonnet-4-6": {
"alias": "Sonnet 4.6"
},
"anthropic/claude-opus-4-6": {
"alias": "Opus 4.6"
},
"anthropic/claude-haiku-4-5": {
"alias": "Haiku 4.5"
},
"openai/gpt-5.4-mini": {
"alias": "GPT-5.4 Mini"
},
"openai/gpt-5.4": {
"alias": "GPT-5.4"
},
"openai/gpt-5.4-pro": {
"alias": "GPT-5.4 Pro"
},
"moonshot/kimi-k2.5": {
"alias": "Kimi K2.5"
}
},
"workspace": "/home/daniil/.openclaw/workspace",
"compaction": {
"mode": "safeguard"
},
"maxConcurrent": 4,
"subagents": {
"maxConcurrent": 8
}
},
"list": [
{
"id": "main",
"default": true,
"name": "Cosmo",
"workspace": "/home/daniil/.openclaw/workspace"
},
{
"id": "wife",
"name": "Ассистент",
"workspace": "/home/daniil/.openclaw/workspace-wife"
}
]
},
"tools": {
"web": {
"search": {
"enabled": true,
"provider": "brave",
"maxResults": 5
}
},
"media": {
"audio": {
"enabled": true,
"models": [
{
"provider": "groq",
"model": "whisper-large-v3-turbo"
}
]
}
},
"exec": {
"notifyOnExit": false
}
},
"bindings": [
{
"agentId": "wife",
"match": {
"channel": "telegram",
"accountId": "wife"
}
},
{
"type": "route",
"agentId": "main",
"match": {
"channel": "telegram",
"accountId": "daniil",
"peer": {
"kind": "direct",
"id": "398382229"
}
}
}
],
"messages": {
"ackReactionScope": "group-mentions",
"tts": {
"auto": "inbound",
"provider": "microsoft",
"edge": {
"enabled": true,
"voice": "ru-RU-DmitryNeural",
"lang": "ru-RU",
"outputFormat": "audio-24khz-48kbitrate-mono-mp3"
},
"microsoft": {
"enabled": true,
"voice": "ru-RU-DmitryNeural",
"lang": "ru-RU",
"outputFormat": "audio-24khz-48kbitrate-mono-mp3",
"pitch": "+0%",
"rate": "+0%"
}
}
},
"commands": {
"native": "auto",
"nativeSkills": "auto",
"restart": true,
"ownerDisplay": "raw"
},
"hooks": {
"internal": {
"enabled": true,
"entries": {
"command-logger": {
"enabled": true
},
"session-memory": {
"enabled": true
}
}
}
},
"channels": {
"telegram": {
"enabled": true,
"dmPolicy": "pairing",
"groupAllowFrom": [
"398382229"
],
"groupPolicy": "allowlist",
"streaming": "partial",
"actions": {
"reactions": true
},
"accounts": {
"daniil": {
"name": "Cosmo (Даниил)",
"dmPolicy": "pairing",
"botToken": "8527290610:TOKEN",
"allowFrom": [
"398382229"
],
"groupPolicy": "allowlist",
"streaming": "off",
"threadBindings": {
"spawnAcpSessions": false
}
},
"wife": {
"name": "Бот жены",
"dmPolicy": "allowlist",
"botToken": "8351034602:TOKEN",
"allowFrom": [
"197366320"
],
"groupPolicy": "allowlist",
"streaming": "off"
}
}
}
},
"gateway": {
"port": 18789,
"mode": "local",
"bind": "lan",
"controlUi": {
"allowedOrigins": [
"http://localhost:18789",
"http://127.0.0.1:18789",
"http://192.168.31.103:18789",
"http://192.168.31.95:18789"
]
},
"auth": {
"mode": "token",
"token": "TOKEN"
},
"tailscale": {
"mode": "off",
"resetOnExit": false
}
},
"skills": {
"install": {
"nodeManager": "npm"
}
},
"plugins": {
"entries": {
"telegram": {
"enabled": true
},
"brave": {
"enabled": true,
"config": {
"webSearch": {
"apiKey": "KEY"
}
}
},
"acpx": {
"enabled": true,
"config": {
"permissionMode": "approve-all",
"command": "/home/daniil/bin/acpx-wrapper.sh",
"expectedVersion": "any"
}
}
}
}
}

View File

@@ -0,0 +1,137 @@
# Ultrahuman Ring PRO — Анализ дизайна и UX
> Источник: https://www.ultrahuman.com/global/ring-pro/
> Сохранено: 2026-04-02
> Цель: вдохновение для редизайна Pulse App
---
## 🎨 Визуальный дизайн сайта
### Общий стиль
- **Тёмная тема** как основная — почти чёрный фон (`#0a0a0a`, `#111`), текст белый
- **Акцентный цвет** — насыщенный тёмно-зелёный / teal (`#00c896` примерно), используется для иконок, индикаторов, меток состояния
- **Типографика** — крупные, жирные заголовки (видимо sans-serif, похоже на Inter/Neue Haas), огромный кегль на hero-секциях
- **Контраст** — очень высокий, метрики выделяются большими цифрами на тёмном фоне
### Фотографии и визуалы
- Телефон в руке — реалистичные 3D-рендеры, не скриншоты
- Фоновые фото людей в движении — размытые, тёмные, атмосферные (оранжевые, синие градиенты)
- Каждая функция — отдельная «карточка» с реальным скриншотом приложения + фото/паттерн на фоне
- Использование градиентных blob-овалов как фоновых элементов (зелёные, синие, розовые свечения)
### Компоненты
- **Таб-бар** внизу экрана телефона — горизонтальный, полупрозрачный тёмный фон, активный таб белый жирный
- **Карточки метрик** — скруглённые углы, плотный padding, маленький лейбл сверху + большое число
- **Индикаторы статуса** — цветные теги (зелёный «Excellent», красный «Needs attention»)
- **Мини-графики** — sparkline прямо в карточке, тонкие линии
- **Прогресс-бар** — тонкий, цветной (зелёный/красный)
---
## 📱 UX и структура приложения
### Навигация
Четыре основных раздела в таб-баре:
| Таб | Содержимое |
|-----|-----------|
| **Sleep** | Sleep Index, стадии сна, REM/Deep/Light, детальный timeline |
| **Vitals** | HR, RHR, HRV, VO2 Max, Temp, Cardio Age |
| **PowerPlugs** | Маркетплейс плагинов (Women's Health, Respiratory, Heart) |
| **Longevity** | Ultra Age = Brain Age + Pulse Age + Blood Age |
### Ключевые экраны
#### Dashboard / Longevity
- Центральный элемент — **большое число** (возраст: 32, с маркером "+3 years older")
- Под ним — объяснение одной фразой: *"You're currently aging at a faster aging pace than expected for your age"*
- Contributors: три субметрики (Brain Age 32, Pulse Age 28, Blood Age 31) с иконками
#### Sleep
- Score крупно (74), под ним timeline-график сна по стадиям
- Стадии: Awake, REM, Light Sleep, Deep Sleep — цветовая кодировка
- Мини-гистограмма активности по ночи
#### Vitals
- Сетка карточек 2×n
- Каждая карточка: лейбл + значение + delta (`+2`) + статус (Within typical range) + мини-sparkline
- Метрики: Heart Rate, Resting HR, HRV, VO2 Max, Temp Deviation, Cardio Age
#### PowerPlugs
- App Store-стиль: карточки с gradient-обложками
- Каждый плагин: иконка + название + категория + кнопка Get / Installed
---
## 💡 Фичи для вдохновения в Pulse
### Что можно взять напрямую
| Идея | Где в Pulse | Сложность |
|------|------------|-----------|
| **Readiness Score как центральный элемент** — большое число, одна фраза-объяснение | Health Dashboard | Низкая — данные уже есть |
| **Contributors под скором** — разбивка на 3 субметрики (сон, HRV, RHR) | Health Dashboard | Низкая |
| **Цветовые статусы** — Excellent / Good / Needs attention | Везде | Низкая |
| **Sparkline в карточке** — мини-график прямо рядом с числом | Health, Финансы | Средняя |
| **Tab-bar снизу** — Sleep / Vitals / Habits / Finance | Главная навигация | Низкая |
| **Timeline сна** — гистограмма по часам с фазами | Sleep раздел | Средняя |
| **Delta значений**`+2 bpm vs yesterday` рядом с числом | Все метрики | Низкая |
### Концепции посложнее
| Идея | Описание | Сложность |
|------|---------|-----------|
| **"Biological Age" аналог** | Индекс долгосрочного здоровья на основе сна + HRV + активности | Высокая |
| **PowerPlugs аналог** | Маркетплейс трекеров/модулей (привычки, финансы, здоровье) | Высокая |
| **AI-объяснения** (как Jade) | *"Low glymphatic clearance is slowing mental recovery"* — причина + рекомендация | Высокая |
| **Stress Rhythm** | Трекинг стресса по HRV в течение дня | Высокая (нет датчика) |
---
## 🖌️ Дизайн-система для адаптации в Pulse
### Цвета
```
Фон: #0d0d0d — #111111
Карточки: #1a1a1a — #1e1e1e
Акцент: #00c896 (teal/зелёный) — уже близко к нашему Deep Teal
Хорошо: #00c896 green
Внимание: #ff4d4d red
Нейтральный: #888888 gray
Текст: #ffffff primary, #aaaaaa secondary
```
### Типографика
- Заголовки: очень крупные (48px+), жирные, Inter или аналог
- Метрики: 3248px, bold
- Лейблы: 1012px, uppercase, letter-spacing
- Описание: 14px, regular, серый
### Карточки
```
border-radius: 1620px
padding: 1620px
background: rgba(255,255,255,0.05) или #1e1e1e
```
---
## 📋 Приоритетный план для Pulse
**Фаза 1 (быстрые wins):**
1. Редизайн Health Dashboard — большой Readiness Score + 3 contributors
2. Добавить цветовые статусы к метрикам (Excellent / Needs attention)
3. Delta значений (`+2 vs yesterday`) везде
**Фаза 2 (средний срок):**
4. Sparklines в карточках метрик
5. Timeline-график сна по фазам
6. Tab-bar навигация (Sleep / Vitals / Habits / Finance)
**Фаза 3 (амбициозно):**
7. AI-интерпретации метрик (Cosmo объясняет что значит HRV = 45)
8. "Pulse Age" — агрегированный индекс здоровья
---
*Сохранено Cosmo, 02.04.2026*

View File

@@ -0,0 +1,337 @@
# pulse-api — Архитектура
**Репозиторий:** `https://git.digital-home.site/daniil/pulse-api`
**Go module:** `github.com/daniil/homelab-api`
**URL:** `https://api.digital-home.site`
**Dev:** `http://192.168.31.60:8081`
## Общая архитектура
Классический Go REST API с разделением на слои:
```
cmd/api/main.go ← точка входа, роутер, инициализация
internal/
config/ ← загрузка env-переменных
repository/ ← работа с БД (SQL-запросы)
service/ ← бизнес-логика
handler/ ← HTTP-хендлеры (request/response)
model/ ← структуры данных (Go structs + JSON)
middleware/ ← JWT-аутентификация
bot/ ← Telegram бот
scheduler/ ← cron-задачи (напоминания)
```
**Стек:**
- Router: `go-chi/chi v5`
- ORM: `jmoiron/sqlx` (raw SQL + named queries)
- БД: PostgreSQL 16
- JWT: `golang-jwt/jwt v5`
- Telegram: `go-telegram-bot-api v5`
- Cron: `robfig/cron v3`
- Email: Resend API (`service/email.go`)
- Крипто: `golang.org/x/crypto` (bcrypt для паролей)
## Структура папок
```
pulse-api/
├── cmd/api/main.go # Точка входа: роутер, DI, запуск
├── internal/
│ ├── bot/
│ │ ├── bot.go # Инициализация, Start(), SendMessage()
│ │ └── handlers.go # Команды и callback кнопки
│ ├── config/
│ │ └── config.go # Config struct + Load() из env
│ ├── handler/
│ │ ├── auth.go # Register, Login, Refresh, Me, VerifyEmail...
│ │ ├── tasks.go # CRUD задач + complete/uncomplete
│ │ ├── habits.go # CRUD привычек + логи + статистика
│ │ ├── habit_freeze.go # Заморозка привычек
│ │ ├── finance.go # Категории и транзакции финансов
│ │ ├── savings.go # Накопления (категории, участники, планы)
│ │ ├── interest.go # Начисление процентов
│ │ ├── profile.go # Профиль пользователя
│ │ └── health.go # GET /health
│ ├── middleware/
│ │ └── auth.go # JWT Bearer middleware
│ ├── model/
│ │ ├── user.go # User, RegisterRequest, LoginRequest...
│ │ ├── task.go # Task, CreateTaskRequest...
│ │ ├── habit.go # Habit, HabitLog, HabitStats...
│ │ ├── habit_freeze.go # HabitFreeze
│ │ ├── finance.go # FinanceCategory, FinanceTransaction...
│ │ ├── savings.go # SavingsCategory, SavingsTransaction...
│ │ └── email.go # Email templates
│ ├── repository/
│ │ ├── db.go # NewDB() + RunMigrations()
│ │ ├── user.go # UserRepository
│ │ ├── task.go # TaskRepository
│ │ ├── habit.go # HabitRepository
│ │ ├── habit_freeze.go # HabitFreezeRepository
│ │ ├── finance.go # FinanceRepository
│ │ ├── savings.go # SavingsRepository
│ │ └── email_token.go # EmailTokenRepository
│ ├── service/
│ │ ├── auth.go # AuthService: register/login/JWT
│ │ ├── habit.go # HabitService
│ │ ├── task.go # TaskService
│ │ ├── finance.go # FinanceService
│ │ ├── interest.go # Начисление процентов
│ │ └── email.go # EmailService (Resend)
│ └── scheduler/
│ └── scheduler.go # Cron-напоминания через Telegram
├── docs/
│ └── SAVINGS.md
├── go.mod
├── Dockerfile
└── docker-compose.yml
```
## API Эндпоинты
### Публичные (без авторизации)
| Метод | Путь | Описание |
|-------|------|----------|
| GET | `/health` | Health check |
| POST | `/auth/register` | Регистрация: `{email, username, password}` |
| POST | `/auth/login` | Логин: `{email, password}``{access_token, refresh_token, user}` |
| POST | `/auth/refresh` | Обновление токена: `{refresh_token}` |
| POST | `/auth/verify-email` | Подтверждение email: `{token}` |
| POST | `/auth/resend-verification` | Повторная отправка: `{email}` |
| POST | `/auth/forgot-password` | Сброс пароля: `{email}` |
| POST | `/auth/reset-password` | Новый пароль: `{token, new_password}` |
### Авторизованные (Bearer JWT)
#### Аутентификация/Профиль
| Метод | Путь | Описание |
|-------|------|----------|
| GET | `/auth/me` | Текущий пользователь |
| PUT | `/auth/me` | Обновить профиль |
| PUT | `/auth/password` | Сменить пароль: `{old_password, new_password}` |
| GET | `/profile` | Профиль |
| PUT | `/profile` | Обновить профиль |
#### Задачи
| Метод | Путь | Описание |
|-------|------|----------|
| GET | `/tasks` | Список задач. Query: `?completed=true/false` |
| GET | `/tasks/today` | Задачи на сегодня |
| POST | `/tasks` | Создать: `{title, description?, icon?, color?, due_date?, priority?, reminder_time?, is_recurring?, recurrence_type?, recurrence_interval?, recurrence_end_date?}` |
| GET | `/tasks/{id}` | Получить задачу |
| PUT | `/tasks/{id}` | Обновить задачу |
| DELETE | `/tasks/{id}` | Удалить задачу |
| POST | `/tasks/{id}/complete` | Отметить выполненной |
| POST | `/tasks/{id}/uncomplete` | Снять отметку |
#### Привычки
| Метод | Путь | Описание |
|-------|------|----------|
| GET | `/habits` | Список привычек |
| POST | `/habits` | Создать: `{name, description?, color?, icon?, frequency, target_days?, target_count?, reminder_time?, start_date?}` |
| GET | `/habits/{id}` | Получить привычку |
| PUT | `/habits/{id}` | Обновить |
| DELETE | `/habits/{id}` | Удалить |
| POST | `/habits/{id}/log` | Отметить: `{date?, count?, note?}` |
| GET | `/habits/{id}/logs` | История отметок |
| DELETE | `/habits/{id}/logs/{logId}` | Удалить отметку |
| GET | `/habits/stats` | Общая статистика |
| GET | `/habits/{id}/stats` | Статистика привычки |
| GET | `/habits/{id}/freezes` | Заморозки привычки |
| POST | `/habits/{id}/freezes` | Создать заморозку |
| DELETE | `/habits/{id}/freezes/{freezeId}` | Удалить заморозку |
#### Финансы
| Метод | Путь | Описание |
|-------|------|----------|
| GET | `/finance/categories` | Категории расходов/доходов |
| POST | `/finance/categories` | Создать: `{name, emoji?, type, budget?, color?, sort_order?}` |
| PUT | `/finance/categories/{id}` | Обновить |
| DELETE | `/finance/categories/{id}` | Удалить |
| GET | `/finance/transactions` | Транзакции. Query: `?month=&year=` |
| POST | `/finance/transactions` | Создать: `{category_id, type, amount, description?, date}` |
| PUT | `/finance/transactions/{id}` | Обновить |
| DELETE | `/finance/transactions/{id}` | Удалить |
| GET | `/finance/summary` | Сводка: баланс, доходы, расходы, по категориям |
| GET | `/finance/analytics` | Аналитика: тренды по месяцам |
#### Накопления
| Метод | Путь | Описание |
|-------|------|----------|
| GET | `/savings/categories` | Категории накоплений |
| POST | `/savings/categories` | Создать категорию |
| GET | `/savings/categories/{id}` | Получить |
| PUT | `/savings/categories/{id}` | Обновить |
| DELETE | `/savings/categories/{id}` | Удалить |
| GET | `/savings/categories/{id}/members` | Участники |
| POST | `/savings/categories/{id}/members` | Добавить участника |
| DELETE | `/savings/categories/{id}/members/{userId}` | Удалить участника |
| GET | `/savings/categories/{id}/recurring-plans` | Регулярные планы |
| POST | `/savings/categories/{id}/recurring-plans` | Создать план |
| PUT | `/savings/recurring-plans/{planId}` | Обновить план |
| DELETE | `/savings/recurring-plans/{planId}` | Удалить план |
| GET | `/savings/transactions` | Транзакции накоплений |
| POST | `/savings/transactions` | Создать транзакцию |
| GET | `/savings/transactions/{id}` | Получить |
| PUT | `/savings/transactions/{id}` | Обновить |
| DELETE | `/savings/transactions/{id}` | Удалить |
| GET | `/savings/stats` | Статистика |
## Модели данных
### User
```go
type User struct {
ID int64
Email string
Username string
PasswordHash string // bcrypt, в JSON скрыто
EmailVerified bool
TelegramChatID *int64 // nullable
NotificationsEnabled bool
Timezone string
MorningReminderTime string // "09:00"
EveningReminderTime string // "21:00"
CreatedAt, UpdatedAt time.Time
}
```
### Task
```go
type Task struct {
ID, UserID int64
Title, Description string
Icon, Color string
DueDate *string // "2026-01-01"
Priority int // 1=низкий, 2=средний, 3=высокий
ReminderTime *string // "19:00"
Completed bool // производное от CompletedAt
// Повторяющиеся задачи:
IsRecurring bool
RecurrenceType *string // "daily", "weekly", "monthly"
RecurrenceInterval int
RecurrenceEndDate *string
ParentTaskID *int64
CreatedAt, UpdatedAt time.Time
}
```
### Habit
```go
type Habit struct {
ID, UserID int64
Name, Description string
Color, Icon string
Frequency string // "daily", "weekly", "custom"
TargetDays []int // дни недели (0=вс...6=сб)
TargetCount int
ReminderTime *string // "19:00"
StartDate *string
IsArchived bool
CreatedAt, UpdatedAt time.Time
}
type HabitLog struct {
ID, HabitID, UserID int64
Date time.Time
Count int
Note string
CreatedAt time.Time
}
```
### FinanceCategory / FinanceTransaction
```go
type FinanceCategory struct {
ID, UserID int64
Name, Emoji string
Type string // "income" | "expense"
Budget *float64
Color string
SortOrder int
CreatedAt time.Time
}
type FinanceTransaction struct {
ID, UserID, CategoryID int64
Type string // "income" | "expense"
Amount float64
Description string
Date time.Time
CreatedAt time.Time
CategoryName, CategoryEmoji string // из JOIN
}
```
## Аутентификация
- **JWT HS256** с двумя типами токенов: `access` (короткий) и `refresh` (длинный)
- Middleware `Authenticate` парсит `Authorization: Bearer <token>`, проверяет `type == "access"`
- UserID извлекается из claims и помещается в context: `GetUserID(ctx)`
- Пароли хешируются bcrypt
- Email-верификация при регистрации через Resend API
- Password reset — одноразовые токены в таблице `email_tokens`
## Telegram Бот
**Команды:**
| Команда | Действие |
|---------|---------|
| `/start` | Показать Chat ID (для привязки к аккаунту Pulse) |
| `/tasks` | Список задач на сегодня с inline-кнопками |
| `/habits` | Привычки на сегодня с inline-кнопками |
| `/done <id>` или `/done_<id>` | Отметить задачу выполненной |
| `/check <id>` или `/check_<id>` | Отметить привычку |
| `/help` | Справка |
**Callback-кнопки:**
- `donetask_<id>` — выполнить задачу
- `deltask_<id>` — удалить задачу
- `checkhabit_<id>` — отметить привычку сегодня
- `checkhabit_<id>_yesterday` — отметить привычку за вчера
**Привязка:** пользователь вводит `/start`, получает Chat ID, вставляет его в настройки Pulse → `PUT /profile {telegram_chat_id: ...}`
## Scheduler (cron-напоминания)
`internal/scheduler/scheduler.go` — использует `robfig/cron`:
- Утренние напоминания (время из настроек пользователя)
- Вечерние напоминания
- Напоминания о задачах по `reminder_time`
- Напоминания о привычках по `reminder_time`
## Конфигурация (env переменные)
| Переменная | Описание | Default |
|-----------|----------|---------|
| `DATABASE_URL` | PostgreSQL DSN | `postgres://homelab:homelab@db:5432/homelab` |
| `JWT_SECRET` | Секрет для JWT | `change-me-in-production` |
| `PORT` | Порт сервера | `8080` |
| `RESEND_API_KEY` | API ключ Resend (email) | — |
| `FROM_EMAIL` | Email отправителя | `noreply@digital-home.site` |
| `FROM_NAME` | Имя отправителя | `Homelab` |
| `APP_URL` | Публичный URL приложения | `https://api.digital-home.site` |
| `TELEGRAM_BOT_TOKEN` | Токен Telegram бота | — |
## Где искать что
| Задача | Файл |
|--------|------|
| Добавить новый эндпоинт | `handler/<domain>.go` + роут в `cmd/api/main.go` |
| Изменить модель/таблицу | `model/<domain>.go` + `repository/db.go` (migrations) |
| Логика уведомлений | `internal/scheduler/scheduler.go` |
| Telegram команды | `internal/bot/handlers.go` |
| Финансы (категории/транзакции) | `handler/finance.go`, `service/finance.go`, `repository/finance.go` |
| Привычки | `handler/habits.go`, `service/habit.go`, `repository/habit.go` |
| Задачи | `handler/tasks.go`, `service/task.go`, `repository/task.go` |
| Накопления | `handler/savings.go`, `repository/savings.go` |
| Email отправка | `service/email.go` |
| JWT/Auth | `service/auth.go`, `middleware/auth.go` |
| Конфиг | `config/config.go` |

View File

@@ -0,0 +1,263 @@
# pulse-web — Архитектура
**Репозиторий:** `https://git.digital-home.site/daniil/pulse-web`
**URL:** `https://pulse.digital-home.site`
**Dev:** `http://192.168.31.60:5174`
**Storybook:** `http://192.168.31.60:6006`
## Общая архитектура
React SPA (Single Page Application):
```
src/
api/ ← API-функции (axios, по доменам)
store/ ← State management (Zustand)
contexts/ ← React Context (тема)
pages/ ← Страницы (маршруты)
components/ ← Переиспользуемые компоненты
main.jsx ← Точка входа (BrowserRouter + ThemeContext)
App.jsx ← Роутер (Routes/Route)
index.css ← Глобальные стили (Tailwind)
```
**Стек:**
- Framework: React 18
- Bundler: Vite 5
- Роутинг: React Router DOM 6
- State: Zustand 4
- HTTP: Axios (с interceptors для JWT refresh)
- UI: Tailwind CSS 3
- Анимации: Framer Motion 11
- Иконки: Lucide React
- Графики: Recharts 2
- Утилиты дат: date-fns 3
- Компонент-либа: Storybook 8
- Тесты: Vitest + Testing Library
## Структура папок
```
pulse-web/
├── src/
│ ├── api/
│ │ ├── client.js # Axios instance + JWT interceptor
│ │ ├── auth.js # (в store/auth.js)
│ │ ├── tasks.js # tasksApi: list, today, create, complete...
│ │ ├── habits.js # habitsApi: list, create, log, stats...
│ │ ├── finance.js # financeApi: categories, transactions, summary
│ │ ├── savings.js # savingsApi: categories, transactions, stats
│ │ └── profile.js # profileApi: get, update
│ ├── store/
│ │ └── auth.js # useAuthStore (Zustand): user, login, logout
│ ├── contexts/
│ │ └── ThemeContext.jsx # ThemeProvider: light/dark, localStorage
│ ├── pages/
│ │ ├── Home.jsx # Главная страница (дашборд)
│ │ ├── Tracker.jsx # Трекер (таб: привычки/задачи/статистика)
│ │ ├── Habits.jsx # Страница привычек (встраивается в Tracker)
│ │ ├── Tasks.jsx # Страница задач (встраивается в Tracker)
│ │ ├── Stats.jsx # Статистика (встраивается в Tracker)
│ │ ├── Finance.jsx # Финансы (таб: обзор/транзакции/аналитика/категории)
│ │ ├── Savings.jsx # Накопления
│ │ ├── Settings.jsx # Настройки пользователя
│ │ ├── Login.jsx # Вход
│ │ ├── Register.jsx # Регистрация
│ │ ├── ForgotPassword.jsx # Сброс пароля
│ │ ├── ResetPassword.jsx # Новый пароль (по токену из email)
│ │ └── VerifyEmail.jsx # Подтверждение email
│ ├── components/
│ │ ├── Navigation.jsx # Нижняя навигация (fixed bottom)
│ │ ├── CreateTaskModal.jsx
│ │ ├── EditTaskModal.jsx
│ │ ├── CreateHabitModal.jsx
│ │ ├── EditHabitModal.jsx
│ │ ├── LogHabitModal.jsx
│ │ └── finance/
│ │ ├── FinanceDashboard.jsx # Обзор месяца
│ │ ├── TransactionList.jsx # Список транзакций
│ │ ├── FinanceAnalytics.jsx # Графики трендов
│ │ ├── CategoriesManager.jsx # Управление категориями
│ │ └── AddTransactionModal.jsx
│ ├── App.jsx # Routes + ProtectedRoute/PublicRoute
│ ├── main.jsx # ReactDOM.render + Providers
│ └── index.css # Tailwind + кастомные стили
├── public/
├── package.json
├── vite.config.js
├── tailwind.config.js
├── nginx.conf
└── Dockerfile
```
## Страницы / Роуты
| Путь | Компонент | Защита | Описание |
|------|-----------|--------|----------|
| `/login` | `Login` | Public only | Форма входа |
| `/register` | `Register` | Public only | Форма регистрации |
| `/forgot-password` | `ForgotPassword` | Public only | Запрос сброса пароля |
| `/verify-email` | `VerifyEmail` | Нет | Подтверждение email по токену |
| `/reset-password` | `ResetPassword` | Нет | Установка нового пароля |
| `/` | `Home` | Protected | Главная: дашборд |
| `/tracker` | `Tracker` | Protected | Трекер привычек/задач/статистики |
| `/habits` | → `/tracker` | Protected | Редирект на трекер |
| `/tasks` | → `/tracker` | Protected | Редирект на трекер |
| `/stats` | → `/tracker` | Protected | Редирект на трекер |
| `/savings` | `Savings` | Protected | Накопления |
| `/settings` | `Settings` | Protected | Настройки аккаунта |
| `*` | → `/` | — | Любой неизвестный → главная |
### Tracker (вкладки)
`/tracker` содержит 3 вкладки:
1. **Привычки**`Habits` компонент
2. **Задачи**`Tasks` компонент
3. **Статистика**`Stats` компонент
### Finance (вкладки)
`/finance` (доступна через роутер если добавить) содержит 4 вкладки:
1. **Обзор**`FinanceDashboard`
2. **Транзакции**`TransactionList`
3. **Аналитика**`FinanceAnalytics`
4. **Категории**`CategoriesManager`
> ⚠️ Страница Finance рендерится, но **нет роута** в App.jsx. Доступ только если добавить `<Route path="/finance">`.
## Основные компоненты
| Компонент | Файл | Назначение |
|-----------|------|------------|
| `Navigation` | components/Navigation.jsx | Нижняя навбар (Главная, Трекер, Накопления, Настройки). Показывает Finance только для `user.id === 1` (owner) |
| `ProtectedRoute` | App.jsx | Редиректит на `/login` если не авторизован |
| `PublicRoute` | App.jsx | Редиректит на `/` если уже авторизован |
| `FinanceDashboard` | finance/ | Обзор месяца: баланс, доходы/расходы, по категориям, дневной график |
| `TransactionList` | finance/ | Список транзакций с фильтрацией |
| `FinanceAnalytics` | finance/ | Recharts: тренды по месяцам |
| `CategoriesManager` | finance/ | CRUD категорий |
| `AddTransactionModal` | finance/ | Модал добавления транзакции |
| `CreateTaskModal` | components/ | Создание задачи |
| `EditTaskModal` | components/ | Редактирование задачи |
| `CreateHabitModal` | components/ | Создание привычки |
| `LogHabitModal` | components/ | Отметка привычки |
## API вызовы
### Axios Client (`src/api/client.js`)
```js
const api = axios.create({ baseURL: VITE_API_URL })
// Request interceptor: добавляет Bearer токен из localStorage
api.interceptors.request Authorization: Bearer <access_token>
// Response interceptor: при 401 делает refresh и повторяет запрос
api.interceptors.response POST /auth/refresh обновляет tokens в localStorage
```
### API модули
```js
// tasks.js
tasksApi.list(completed?) GET /tasks
tasksApi.today() GET /tasks/today
tasksApi.create(data) POST /tasks
tasksApi.complete(id) POST /tasks/{id}/complete
tasksApi.uncomplete(id) POST /tasks/{id}/uncomplete
tasksApi.update(id, data) PUT /tasks/{id}
tasksApi.delete(id) DELETE /tasks/{id}
// habits.js
habitsApi.list() GET /habits
habitsApi.create(data) POST /habits
habitsApi.log(id, data) POST /habits/{id}/log
habitsApi.stats() GET /habits/stats
habitsApi.habitStats(id) GET /habits/{id}/stats
// finance.js
financeApi.getCategories() GET /finance/categories
financeApi.createCategory(data) POST /finance/categories
financeApi.getTransactions(m, y) GET /finance/transactions?month=&year=
financeApi.createTransaction(data) POST /finance/transactions
financeApi.getSummary(m, y) GET /finance/summary
financeApi.getAnalytics() GET /finance/analytics
// savings.js
savingsApi.getCategories() GET /savings/categories
savingsApi.createCategory(data) POST /savings/categories
savingsApi.getTransactions() GET /savings/transactions
savingsApi.getStats() GET /savings/stats
// profile.js
profileApi.get() GET /profile
profileApi.update(data) PUT /profile
```
## State Management
**Zustand** — единственный store: `src/store/auth.js`
```js
useAuthStore = {
user: null | User,
isLoading: boolean,
isAuthenticated: boolean,
initialize() // Вызывается в App.jsx useEffect: GET /auth/me
login(email, password) // POST /auth/login, сохраняет tokens
register(email, username, password)
logout() // Чистит localStorage + state
}
```
**React Context:**
- `ThemeContext` (`contexts/ThemeContext.jsx`) — dark/light mode, сохраняется в localStorage
**Нет Redux / React Query** — данные загружаются локально в компонентах через `useState + useEffect`.
## Основные зависимости
| Пакет | Версия | Назначение |
|-------|--------|------------|
| react | 18.2 | UI framework |
| react-router-dom | 6.22 | Роутинг |
| zustand | 4.5 | State management |
| axios | 1.6 | HTTP клиент |
| tailwindcss | 3.4 | CSS utility framework |
| framer-motion | 11 | Анимации |
| lucide-react | 0.312 | Иконки |
| recharts | 2.12 | Графики |
| date-fns | 3.3 | Утилиты дат |
| clsx | 2.1 | Условные классы |
| storybook | 8.5 | UI компонент-браузер (dev) |
| vitest | 4 | Тесты |
## Конфигурация
| Env переменная | Описание | Default |
|---------------|----------|---------|
| `VITE_API_URL` | URL backend API | `https://api.digital-home.site` |
## Где искать что
| Задача | Файл |
|--------|------|
| **Новая страница** | `src/pages/NewPage.jsx` + роут в `App.jsx` + ссылка в `Navigation.jsx` |
| **Новый компонент** | `src/components/NewComponent.jsx` |
| **Новый API вызов** | `src/api/<domain>.js` (добавить метод) |
| **Финансы: UI** | `src/pages/Finance.jsx`, `src/components/finance/` |
| **Финансы: API** | `src/api/finance.js` |
| **Привычки: UI** | `src/pages/Habits.jsx`, `CreateHabitModal`, `LogHabitModal` |
| **Привычки: API** | `src/api/habits.js` |
| **Задачи: UI** | `src/pages/Tasks.jsx`, `CreateTaskModal`, `EditTaskModal` |
| **Задачи: API** | `src/api/tasks.js` |
| **Авторизация** | `src/store/auth.js`, `src/pages/Login.jsx` |
| **Глобальные стили** | `src/index.css` + `tailwind.config.js` |
| **Темная тема** | `src/contexts/ThemeContext.jsx` |
| **Нижняя навигация** | `src/components/Navigation.jsx` |
## Заметки
- `Finance` страница не добавлена в роутер и навигацию для обычных пользователей — только для owner (id=1)
- Legacy роуты `/habits`, `/tasks`, `/stats` → редиректят на `/tracker`
- Токены хранятся в `localStorage`: `access_token`, `refresh_token`
- Auto-refresh токена при 401 ответе (в axios interceptor)

View File

@@ -0,0 +1,112 @@
# Сервисы VM 192.168.31.60
Все Docker-сервисы развёрнуты в `/opt/digital-home/` на VM Сервисы.
Общая сеть: `services_proxy` (создана в `services/docker-compose.yml`).
Внешний трафик: Nginx Proxy Manager → домены `*.digital-home.site`.
## Сводная таблица
| Сервис | Папка | Домен | Порты (внешние) | Статус |
|--------|-------|-------|-----------------|--------|
| Nginx Proxy Manager | services/ | — (точка входа) | 80, 81, 443 | ✅ Up |
| Gitea | gitea/ | git.digital-home.site | — (NPM) | ✅ Up |
| Gitea Runner | gitea-runner/ | — | — | ✅ Up |
| Homepage | homepage/ | home.digital-home.site | — (NPM) | ✅ Up |
| Jellyfin | media/ | — | — (NPM) | ✅ Up |
| qBittorrent | media/ | — | 6881 | ❌ Stopped |
| Nextcloud | nextcloud/ | cloud.digital-home.site | 5433 (db) | ✅ Up |
| Immich | photo/ | — | — (NPM) | ✅ Up |
| Portainer | portainer/ | portainer.digital-home.site | — (NPM) | ✅ Up |
| Uptime Kuma | uptime-kuma/ | — | — (NPM) | ✅ Up |
| Vaultwarden | vault/ | vault.digital-home.site | — (NPM) | ✅ Up |
| Vikunja | vikunja/ | tasks.digital-home.site | — (NPM) | ✅ Up |
| Menu Bot | bots/menu/ | — | — | ❓ Unknown |
| Review Bot | review-bot/ | — | 3300 | ✅ Up |
| Cosmo Studio | cosmo-studio/ | — | — (NPM) | ✅ Up |
| Pulse API | homelab-api/ | api.digital-home.site | 8081 (dev) | ✅ Up |
| Pulse Web | pulse-web/ | pulse.digital-home.site | 5174 (dev) | ✅ Up |
| Storybook | storybook/ | — | 6006 | ✅ Up |
| Health Webhook | health-webhook/ | health.digital-home.site | 3200 | ✅ Up |
| IT Tools | it-tools/ | — | — (NPM) | ✅ Up |
| Savings | savings/ | — | — (NPM) | ❌ Stopped |
| AdventureLog | adventurelog/ | — | — | ❌ Stopped |
| LangLearn | langlearn/ | — | — | ❌ Stopped |
| Lingua Learn | lingua-learn/ | — | — | ❌ Stopped |
| My Game (Своя Игра) | my-game/ | — | — | ❌ Stopped |
| Japan App | japan-app/ | — | — | ❌ Stopped |
| VPN Invite | vpn-invite/ | — | 3500 | ✅ Up |
| VPN Router | vpn-router/ | — | host network | ❌ Stopped |
| Webhook Deploy | webhook-deploy/ | — | 9000 | ❓ Unknown |
## Запущенные сервисы (docker ps)
Статус на момент документирования (02.04.2026):
```
homelab-api Up 19 hours
vpn-invite Up 21 hours
cosmo-studio (x2) Up 3 days
uptime-kuma Up 5 days (healthy)
jellyfin Up 6 days (healthy)
gitea-runner Up 6 days
review-bot Up 6 days
health-webhook Up 7 days
pulse-web Up 12 days
pulse-web-dev Up 4 weeks
pulse-api-dev Up 4 weeks
storybook Up 4 weeks
nginx_proxy_manager Up 26 hours
homelab-db Up 4 weeks (healthy)
it-tools Up 4 weeks
homepage Up 4 weeks (healthy)
immich_server Up 4 weeks (healthy)
immich_redis Up 4 weeks
immich_postgres Up 4 weeks
immich_ml Up 4 weeks (healthy)
vikunja Up 4 weeks
vikunja_db Up 4 weeks
vaultwarden Up 4 weeks (healthy)
portainer Up 4 weeks
nextcloud_app Up 4 weeks
nextcloud_db Up 4 weeks
nextcloud_redis Up 4 weeks
gitea Up 4 weeks
```
## Архитектура сети
```
Интернет
Nginx Proxy Manager (80/443)
↓ (services_proxy network)
┌───────────────────────────────┐
Все сервисы в одной сети │
│ services_proxy │
│ │
│ git.* → gitea:3000 │
│ home.* → homepage:3000 │
│ cloud.* → nextcloud_app:80 │
│ vault.* → vaultwarden:80 │
│ tasks.* → vikunja:3456 │
│ portainer.* → portainer:9000 │
│ api.* → homelab-api:8080 │
│ pulse.* → pulse-web:80 │
│ health.* → health-webhook:3200│
└───────────────────────────────┘
```
## Паттерн добавления сервиса
```bash
mkdir /opt/digital-home/<name>
cd /opt/digital-home/<name>
# docker-compose.yml с сетью:
networks:
services_proxy:
external: true
docker compose up -d
# Добавить proxy host в NPM UI (порт 81)
```

View File

@@ -0,0 +1,38 @@
# AdventureLog
## Назначение
Приложение для логирования путешествий и приключений. Ведение дневника поездок с геолокацией.
## Контейнеры
| Имя | Образ |
|-----|-------|
| adventurelog-frontend | ghcr.io/seanmorley15/adventurelog-frontend:latest |
| adventurelog-backend | ghcr.io/seanmorley15/adventurelog-backend:latest |
| adventurelog-db | postgis/postgis:16-3.5 |
## Порты
Нет внешних портов (через NPM).
## Volumes
| Volume | Контейнер | Путь контейнера | Содержимое |
|--------|-----------|----------------|------------|
| postgres_data | adventurelog-db | /var/lib/postgresql/data | БД PostGIS |
| adventurelog_media | adventurelog-backend | /code/media/ | Медиа-файлы |
## Домен
Через NPM (не указан явно).
## Сети
- `services_proxy` (external)
- `default` (internal между frontend и backend)
## Env переменные
Из `env_file: .env` (ключи неизвестны).
## Статус
**Не запущен** (не найден в `docker ps`)
## Заметки
- Использует PostGIS (PostgreSQL с геопространственными расширениями)

View File

@@ -0,0 +1,42 @@
# Menu Bot (bots/menu)
## Назначение
Telegram-бот для отображения меню / навигации. Написан на Go.
## Контейнеры
| Имя | Образ |
|-----|-------|
| menu-bot | local build (Go) |
## Порты
Нет внешних портов.
## Volumes
| Путь хоста | Путь контейнера | Содержимое |
|-----------|----------------|------------|
| ./data | /root/data | Данные бота |
## Домен
Нет.
## Сети
Нет явной сети (использует `env_file: .env`).
## Env переменные
Из `.env` файла (ключи неизвестны — файл не читался).
## Статус
**Статус неизвестен** (не найден в `docker ps`)
## Структура проекта
```
bots/menu/
├── cmd/ # Точка входа
├── internal/ # Логика бота
├── data/ # Данные
├── Dockerfile
├── docker-compose.yml
└── go.mod
```

View File

@@ -0,0 +1,36 @@
# Cosmo Studio
## Назначение
Web-интерфейс для OpenClaw AI-ассистента (Cosmo). Bridge-сервис соединяется с OpenClaw instance, frontend предоставляет UI для взаимодействия.
## Контейнеры
| Имя | Образ |
|-----|-------|
| cosmo-studio-bridge-1 | local build (Dockerfile) |
| cosmo-studio-frontend-1 | local build (Dockerfile.frontend) |
## Порты
| Контейнер | Внутренний | Назначение |
|-----------|-----------|------------|
| bridge | 3401-3402 | WebSocket bridge |
| frontend | 80 | Web UI |
## Volumes
Нет persistent volumes (конфиг через env).
## Домен
Через NPM (не указан явно).
## Сети
- `services_proxy` (external)
- `internal` (bridge между bridge и frontend)
## Env переменные
- `OPENCLAW_URL` (ws://192.168.31.103:18789)
- `OPENCLAW_TOKEN`
- `SSH_KEY_PATH`
## Статус
**Запущен** (Up 3 days)

View File

@@ -0,0 +1,40 @@
# Gitea Actions Runner
## Назначение
CI/CD runner для выполнения Gitea Actions workflows. Запускает задачи сборки, тестирования и деплоя при push/PR в Gitea.
## Контейнеры
| Имя | Образ |
|-----|-------|
| gitea-runner | gitea-runner-runner (local build) |
## Порты
Нет внешних портов.
## Volumes
| Путь хоста | Путь контейнера | Содержимое |
|-----------|----------------|------------|
| /var/run/docker.sock | /var/run/docker.sock | Docker socket (для запуска контейнеров) |
| runner_data | /data | Данные runner'а |
| ./config.yaml | /config/config.yaml | Конфиг runner'а |
## Домен
Нет.
## Сети
- `services_proxy` (external)
## Env переменные
- `GITEA_INSTANCE_URL`
- `GITEA_RUNNER_REGISTRATION_TOKEN`
- `GITEA_RUNNER_NAME`
- `CONFIG_FILE`
## Статус
**Запущен** (Up 6 days)
## Заметки
- `privileged: true` — нужно для запуска вложенных Docker-контейнеров в workflows
- Подключается к Gitea по `http://gitea:3000`

View File

@@ -0,0 +1,47 @@
# Gitea
## Назначение
Self-hosted Git-хостинг. Хранит репозитории проектов (pulse-api, pulse-web, obsidian и другие). Поддерживает Gitea Actions для CI/CD.
## Контейнеры
| Имя | Образ |
|-----|-------|
| gitea | gitea/gitea:latest |
## Порты
| Внутренний | Протокол | Назначение |
|-----------|---------|------------|
| 3000 | HTTP | Web UI + API |
| 22 | TCP | SSH для git |
(Порты открыты только в сети, внешний доступ через NPM)
## Volumes
| Путь хоста | Путь контейнера | Содержимое |
|-----------|----------------|------------|
| ./data | /data | Репозитории, конфиги, БД (SQLite) |
| /etc/timezone | /etc/timezone | Временная зона (ro) |
| /etc/localtime | /etc/localtime | Локальное время (ro) |
## Домен
`https://git.digital-home.site`
## Сети
- `gitea_backend` (internal)
- `services_proxy` (external, для NPM)
## Env переменные
- `USER_UID`
- `USER_GID`
- `GITEA__server__DOMAIN`
- `GITEA__server__ROOT_URL`
## Статус
**Запущен** (Up 4 weeks)
## Связанные сервисы
- `gitea-runner` — CI/CD runner для Gitea Actions
- `review-bot` — AI ревью PR через webhook

View File

@@ -0,0 +1,37 @@
# Health Webhook
## Назначение
Webhook-сервис для проверки здоровья / health check инфраструктуры. Node.js сервис с JWT-авторизацией, возможно интеграция с email-уведомлениями через Resend.
## Контейнеры
| Имя | Образ |
|-----|-------|
| health-webhook | local build (Node.js) |
## Порты
| Внешний | Внутренний | Назначение |
|---------|-----------|------------|
| 3200 | 3200 | HTTP API |
## Volumes
| Путь хоста | Путь контейнера | Содержимое |
|-----------|----------------|------------|
| ./data | /app/data | Данные сервиса |
## Домен
`https://health.digital-home.site`
## Сети
- `services_proxy` (external)
## Env переменные
- `JWT_SECRET`
- `NODE_ENV`
- `RESEND_API_KEY`
- `APP_URL`
## Статус
**Запущен** (Up 7 days)

View File

@@ -0,0 +1,40 @@
# Homepage
## Назначение
Стартовая страница / дашборд домашней лаборатории. Показывает все сервисы, их статусы, виджеты (погода, календарь и т.д.).
## Контейнеры
| Имя | Образ |
|-----|-------|
| homepage | ghcr.io/gethomepage/homepage:latest |
## Порты
| Внутренний | Назначение |
|-----------|------------|
| 3000 | Web UI |
## Volumes
| Путь хоста | Путь контейнера | Содержимое |
|-----------|----------------|------------|
| ./config | /app/config | YAML-конфиги (services, widgets, bookmarks) |
| ./icons | /app/public/icons | Пользовательские иконки |
| /var/run/docker.sock | /var/run/docker.sock | Docker socket (для статусов контейнеров, ro) |
## Домен
`https://home.digital-home.site`
## Сети
- `homepage_backend` (internal)
- `services_proxy` (external, для NPM)
## Env переменные
- `PUID`
- `PGID`
- `TZ`
- `HOMEPAGE_ALLOWED_HOSTS`
## Статус
**Запущен** (Up 4 weeks, healthy)

View File

@@ -0,0 +1,31 @@
# IT Tools
## Назначение
Коллекция онлайн-инструментов для разработчиков: конверторы форматов, генераторы, кодировщики, хэши и т.д.
## Контейнеры
| Имя | Образ |
|-----|-------|
| it-tools | corentinth/it-tools:latest |
## Порты
| Внутренний | Назначение |
|-----------|------------|
| 80 | Web UI (через NPM) |
## Volumes
Нет.
## Домен
Через NPM (не указан явно).
## Сети
- `services_proxy` (external)
## Env переменные
Нет.
## Статус
**Запущен** (Up 4 weeks)

View File

@@ -0,0 +1,31 @@
# Japan App
## Назначение
PWA-гид по Японии. Содержит список мест (92+), расписание поездки, карты.
## Контейнеры
| Имя | Образ |
|-----|-------|
| japan-app | local build |
## Порты
Нет (через NPM).
## Volumes
Нет.
## Домен
Через NPM (не указан явно).
## Сети
- `services_proxy` (external)
## Env переменные
Нет.
## Статус
**Не запущен** (не найден в `docker ps`)
## Заметки
- `watchtower.enable=false` — не обновляется автоматически

View File

@@ -0,0 +1,39 @@
# LangLearn
## Назначение
Приложение для изучения языков. Go backend + React frontend с PostgreSQL.
## Контейнеры
| Имя | Образ |
|-----|-------|
| langlearn-backend | local build (Go) |
| langlearn-frontend | local build (React) |
| langlearn-db | postgres:16-alpine |
## Порты
| Контейнер | Внутренний | Назначение |
|-----------|-----------|------------|
| backend | 8080 | REST API (через NPM) |
| frontend | — | Web UI (через NPM) |
## Volumes
| Volume | Контейнер | Путь контейнера | Содержимое |
|--------|-----------|----------------|------------|
| pgdata | db | /var/lib/postgresql/data | БД |
## Домен
Через NPM (не указан явно).
## Сети
- `services_proxy` (external)
- `internal` (между backend и db)
## Env переменные
- `DATABASE_URL`
- `PORT`
## Статус
**Не запущен** (не найден в `docker ps`)

View File

@@ -0,0 +1,34 @@
# Lingua Learn
## Назначение
Приложение для изучения языков (альтернативная версия). Node.js backend + React frontend.
## Контейнеры
| Имя | Образ |
|-----|-------|
| lingua-learn-client | local build (React) |
| lingua-learn-server | local build (Node.js) |
| lingua-learn-db | postgres:16-alpine |
## Порты
Нет внешних портов (через NPM).
## Volumes
| Volume | Контейнер | Путь контейнера | Содержимое |
|--------|-----------|----------------|------------|
| postgres_data | db | /var/lib/postgresql/data | БД |
## Домен
Через NPM (не указан явно).
## Сети
- `services_proxy` (external)
## Env переменные
- `DATABASE_URL`
- `NODE_ENV`
## Статус
**Не запущен** (не найден в `docker ps`)

View File

@@ -0,0 +1,50 @@
# Media (Jellyfin + qBittorrent)
## Назначение
Медиа-стек: скачивание торрентов (qBittorrent) и стриминг медиа-контента (Jellyfin).
## Контейнеры
| Имя | Образ |
|-----|-------|
| jellyfin | jellyfin/jellyfin:latest |
| qbittorrent | lscr.io/linuxserver/qbittorrent:latest |
## Порты
| Контейнер | Внешний | Внутренний | Назначение |
|-----------|---------|-----------|------------|
| qbittorrent | 6881 | 6881 | BitTorrent TCP |
| qbittorrent | 6881/udp | 6881/udp | BitTorrent UDP |
| jellyfin | — | 8096 | Web UI (через NPM) |
| qbittorrent | — | 8080 | Web UI (через NPM) |
## Volumes
| Путь хоста | Контейнер | Путь контейнера | Содержимое |
|-----------|-----------|----------------|------------|
| ./qbittorrent/config | qbittorrent | /config | Конфиг qBittorrent |
| ./downloads | qbittorrent | /downloads | Загруженные файлы |
| ./jellyfin/config | jellyfin | /config | Конфиг Jellyfin |
| ./jellyfin/cache | jellyfin | /cache | Кэш Jellyfin |
| ./downloads | jellyfin | /data/downloads | Медиа-файлы (ro) |
| ../nextcloud/nextcloud_data/data | jellyfin | /data/nextcloud | Файлы Nextcloud (ro) |
## Домен
Через NPM (не указан явно в конфиге).
## Сети
- `media_backend` (internal)
- `services_proxy` (external)
## Env переменные
- `PUID`, `PGID`, `TZ` (оба контейнера)
- `WEBUI_PORT` (qbittorrent)
## Статус
- ✅ jellyfin: **Запущен** (Up 6 days, healthy)
- ❌ qbittorrent: **Не запущен**
## Заметки
- Jellyfin имеет доступ к файлам Nextcloud через bind mount
- Закомментированная секция GPU-транскодинга для Intel

View File

@@ -0,0 +1,41 @@
# My Game (Своя игра)
## Назначение
Веб-версия игры "Своя Игра" (аналог Jeopardy). Node.js/Express backend + React frontend + MongoDB.
## Контейнеры
| Имя | Образ |
|-----|-------|
| svoya-igra-client | local build (React) |
| svoya-igra-server | local build (Node.js) |
| svoya-igra-mongodb | mongo:7 |
## Порты
| Контейнер | Внутренний | Назначение |
|-----------|-----------|------------|
| server | 3001 | REST API (через NPM) |
## Volumes
| Volume | Контейнер | Путь контейнера | Содержимое |
|--------|-----------|----------------|------------|
| mongodb_data | mongodb | /data/db | MongoDB данные |
| ./server/public | server | /app/public | Статичные файлы |
## Домен
Через NPM (не указан явно).
## Сети
- `services_proxy` (external)
- `internal` (между server и mongodb)
## Env переменные
- `PORT`
- `MONGODB_URI`
- `NODE_ENV`
- `CLIENT_URL`
## Статус
**Не запущен** (не найден в `docker ps`)

View File

@@ -0,0 +1,49 @@
# Nextcloud
## Назначение
Облачное хранилище файлов (self-hosted аналог Google Drive). Синхронизация файлов, фото, документов.
## Контейнеры
| Имя | Образ |
|-----|-------|
| nextcloud_app | nextcloud:latest |
| nextcloud_db | postgres:15-alpine |
| nextcloud_redis | redis:7-alpine |
## Порты
| Контейнер | Внешний | Внутренний | Назначение |
|-----------|---------|-----------|------------|
| nextcloud_db | 5433 | 5432 | PostgreSQL (для прямого доступа) |
| nextcloud_app | — | 80 | Web UI (через NPM) |
## Volumes
| Путь хоста | Контейнер | Путь контейнера | Содержимое |
|-----------|-----------|----------------|------------|
| ./db_data | nextcloud_db | /var/lib/postgresql/data | БД PostgreSQL |
| ./redis_data | nextcloud_redis | /data | Redis данные |
| ./nextcloud_data | nextcloud_app | /var/www/html | Файлы Nextcloud, данные пользователей |
## Домен
`https://cloud.digital-home.site`
## Сети
- `nextcloud_backend` (internal)
- `services_proxy` (external)
## Env переменные
- `POSTGRES_HOST`, `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`
- `NEXTCLOUD_ADMIN_USER`, `NEXTCLOUD_ADMIN_PASSWORD`
- `OVERWRITEPROTOCOL`, `OVERWRITEHOST`
- `NEXTCLOUD_TRUSTED_PROXIES`, `TRUSTED_DOMAINS`
- `REDIS_HOST`
- `PHP_UPLOAD_LIMIT`, `PHP_MEMORY_LIMIT`
## Статус
**Все контейнеры запущены** (Up 4 weeks)
## Заметки
- Файлы Nextcloud монтируются в Jellyfin и Immich для просмотра медиа
- Лимит загрузки файлов: 2048 МБ

View File

@@ -0,0 +1,50 @@
# Immich (photo)
## Назначение
Self-hosted Google Photos альтернатива. Автозагрузка фото с телефона, ML-распознавание лиц и объектов, умный поиск.
## Контейнеры
| Имя | Образ |
|-----|-------|
| immich_server | ghcr.io/immich-app/immich-server:release |
| immich_machine_learning | ghcr.io/immich-app/immich-machine-learning:release |
| immich_redis | redis:7-alpine |
| immich_postgres | tensorchord/pgvecto-rs:pg14-v0.2.0 |
## Порты
| Контейнер | Внутренний | Назначение |
|-----------|-----------|------------|
| immich_server | 2283 | Web UI (через NPM) |
| immich_redis | 6379 | Redis |
| immich_postgres | 5432 | PostgreSQL + pgvecto-rs |
## Volumes
| Путь хоста | Контейнер | Путь контейнера | Содержимое |
|-----------|-----------|----------------|------------|
| ./upload | immich_server | /usr/src/app/upload | Загруженные фото |
| ./model-cache | immich_ml | /cache | ML-модели |
| ./pgdata | immich_postgres | /var/lib/postgresql/data | БД |
| ../nextcloud/nextcloud_data/data | immich_server | /mnt/nextcloud | Файлы Nextcloud (ro) |
| /etc/localtime | immich_server | /etc/localtime | Время (ro) |
## Домен
Через NPM (не указан явно).
## Сети
- `photo_backend` (internal)
- `services_proxy` (external, только immich_server)
## Env переменные
- `DB_HOSTNAME`, `DB_USERNAME`, `DB_PASSWORD`, `DB_DATABASE_NAME`
- `REDIS_HOSTNAME`
- `POSTGRES_PASSWORD`, `POSTGRES_USER`, `POSTGRES_DB`
## Статус
**Все контейнеры запущены** (Up 4 weeks, immich_server healthy)
## Заметки
- Использует `pgvecto-rs` — PostgreSQL с расширением для векторного поиска (нужно для ML)
- immich_machine_learning работает только во внутренней сети

View File

@@ -0,0 +1,38 @@
# Portainer
## Назначение
Web UI для управления Docker. Просмотр контейнеров, образов, volumes, сетей. Визуальный мониторинг всей Docker-инфраструктуры.
## Контейнеры
| Имя | Образ |
|-----|-------|
| portainer | portainer/portainer-ce:latest |
## Порты
| Внутренний | Назначение |
|-----------|------------|
| 8000 | Edge agent |
| 9000 | HTTP Web UI |
| 9443 | HTTPS Web UI |
## Volumes
| Путь хоста | Путь контейнера | Содержимое |
|-----------|----------------|------------|
| /var/run/docker.sock | /var/run/docker.sock | Docker socket |
| ./data | /data | Данные Portainer (пользователи, настройки) |
## Домен
`https://portainer.digital-home.site`
## Сети
- `portainer_backend` (internal)
- `services_proxy` (external)
## Env переменные
Нет (кроме `security_opt: no-new-privileges:true`).
## Статус
**Запущен** (Up 4 weeks)

View File

@@ -0,0 +1,49 @@
# Pulse API (homelab-api)
## Назначение
Backend API для приложения Pulse — трекер привычек, задач, финансов, накоплений. Go REST API + Telegram бот.
## Контейнеры
| Имя | Образ |
|-----|-------|
| homelab-api | local build (Go) |
| homelab-db | postgres:16-alpine |
| pulse-api-dev | local build (dev instance, порт 8081) |
## Порты
| Контейнер | Внешний | Внутренний | Назначение |
|-----------|---------|-----------|------------|
| homelab-api | — | 8080 | REST API (через NPM → api.digital-home.site) |
| pulse-api-dev | 8081 | 8080 | Dev-инстанс API |
| homelab-db | — | 5432 | PostgreSQL |
## Volumes
| Volume | Путь контейнера | Содержимое |
|--------|----------------|------------|
| postgres_data | /var/lib/postgresql/data | БД |
## Домен
`https://api.digital-home.site`
## Сети
- `services_proxy` (external)
- `internal` (между api и db)
## Env переменные
- `DATABASE_URL`
- `JWT_SECRET`
- `PORT`
- `RESEND_API_KEY`
- `FROM_EMAIL`
- `FROM_NAME`
- `APP_URL`
- `TELEGRAM_BOT_TOKEN`
## Статус
**Запущен** (homelab-api: Up 19 hours, homelab-db: Up 4 weeks, pulse-api-dev: Up 4 weeks)
## Подробная документация
→ [[pulse-api]]

View File

@@ -0,0 +1,36 @@
# Pulse Web
## Назначение
Frontend React-приложение для Pulse. Трекер привычек, задач, финансов, накоплений.
## Контейнеры
| Имя | Образ |
|-----|-------|
| pulse-web | local build (React/Vite) |
| pulse-web-dev | local build (dev instance, порт 5174) |
## Порты
| Контейнер | Внешний | Внутренний | Назначение |
|-----------|---------|-----------|------------|
| pulse-web | — | 80 | Prod (через NPM → pulse.digital-home.site) |
| pulse-web-dev | 5174 | 80 | Dev-инстанс |
## Volumes
Нет persistent volumes.
## Домен
`https://pulse.digital-home.site`
## Сети
- `services_proxy` (external)
## Env переменные
- `VITE_API_URL`
## Статус
**Запущен** (pulse-web: Up 12 days, pulse-web-dev: Up 4 weeks)
## Подробная документация
→ [[pulse-web]]

View File

@@ -0,0 +1,46 @@
# Review Bot
## Назначение
AI-бот для автоматического code review Pull Request'ов в Gitea. Получает вебхуки от Gitea, клонирует репозиторий, анализирует изменения через Anthropic Claude и оставляет комментарии.
## Контейнеры
| Имя | Образ |
|-----|-------|
| review-bot | local build (Node.js) |
## Порты
| Внешний | Внутренний | Назначение |
|---------|-----------|------------|
| 3300 | 3300 | HTTP webhook endpoint |
## Volumes
| Volume | Путь контейнера | Содержимое |
|--------|----------------|------------|
| review-bot-logs | /var/log/review-bot | Логи ревью |
## Домен
Нет (доступен только изнутри сети).
## Сети
- `services_proxy` (external)
## Env переменные
- `GITEA_URL`
- `GITEA_TOKEN`
- `WEBHOOK_SECRET`
- `ANTHROPIC_URL` (прокси через AI-proxy на 192.168.31.103:3301)
- `PROXY_KEY`
- `LOG_FILE`
- `CLONE_BASE_DIR`
- `PORT`
## Статус
**Запущен** (Up 6 days)
## Заметки
- Использует AI-proxy (`192.168.31.103:3301`) т.к. Anthropic API key работает только с основной машины OpenClaw
- Вебхук настроен для репозиториев pulse-api и pulse-web
- Директория для клонирования: `/tmp/review-bot-clones`

View File

@@ -0,0 +1,52 @@
# Savings (семейные накопления)
## Назначение
Приложение для совместного управления накоплениями. Go backend + React frontend. Поддержка категорий накоплений, участников, регулярных планов, начисления процентов.
## Контейнеры
| Имя | Образ |
|-----|-------|
| savings-backend | local build (Go) |
| savings-frontend | local build (React) |
## Порты
| Контейнер | Внутренний | Назначение |
|-----------|-----------|------------|
| savings-backend | 9090 | REST API (через NPM) |
| savings-frontend | — | Web UI (через NPM) |
## Volumes
| Путь хоста | Контейнер | Путь контейнера | Содержимое |
|-----------|-----------|----------------|------------|
| ./savings.db | backend | /root/savings.db | SQLite БД |
## Домен
Через NPM (не указан явно).
## Сети
- `services_proxy` (external)
## Env переменные
**Backend:**
- `DB_NAME`, `DB_TYPE`
- `JWT_SECRET`, `JWT_EXPIRES_IN`
- `PORT`, `GIN_MODE`
- `FRONTEND_URL`
- `INTEREST_CALCULATION_ENABLED`, `INTEREST_CALCULATION_INTERVAL`
- `SMTP_HOST`, `SMTP_PORT`, `SMTP_USERNAME`, `SMTP_PASSWORD`
- `FROM_EMAIL`, `FROM_NAME`, `EMAIL_ENABLED`
**Frontend (build args):**
- `REACT_APP_API_URL`
- `REACT_APP_APP_NAME`
- `REACT_APP_VERSION`
## Статус
**Не запущен** (не найден в `docker ps`)
## Заметки
- Использует SQLite (не PostgreSQL) — данные в одном файле `savings.db`
- Встроенный расчёт процентов по расписанию

View File

@@ -0,0 +1,41 @@
# Nginx Proxy Manager (services)
## Назначение
Обратный прокси-сервер. Управляет входящим трафиком (HTTP/HTTPS), SSL-сертификатами (Let's Encrypt), маршрутизирует запросы к контейнерам по доменам `*.digital-home.site`.
## Контейнеры
| Имя | Образ |
|-----|-------|
| nginx_proxy_manager | jc21/nginx-proxy-manager:latest |
## Порты
| Внешний | Внутренний | Назначение |
|---------|-----------|------------|
| 80 | 80 | HTTP |
| 81 | 81 | Web UI (управление NPM) |
| 443 | 443 | HTTPS |
## Volumes
| Volume | Путь | Содержимое |
|--------|------|------------|
| ./data | /data | Конфиги прокси-хостов, БД |
| ./letsencrypt | /etc/letsencrypt | SSL-сертификаты Let's Encrypt |
## Домен
Нет (сам является точкой входа). UI доступен напрямую через порт 81.
## Сети
- `services_proxy` (создаётся здесь как `proxy`, используется всеми другими сервисами)
## Env переменные
Нет конфигурационных env-переменных.
## Статус
**Запущен** (Up 26 hours)
## Заметки
- Это корневой сервис инфраструктуры — все остальные подключаются к сети `services_proxy`
- Управление через UI на порту 81 (только из локальной сети)

View File

@@ -0,0 +1,31 @@
# Storybook
## Назначение
Storybook для разработки UI-компонентов pulse-web. Изолированная среда для разработки и документирования React-компонентов.
## Контейнеры
| Имя | Образ |
|-----|-------|
| storybook | local build |
## Порты
| Внешний | Внутренний | Назначение |
|---------|-----------|------------|
| 6006 | 80 | Storybook UI |
## Volumes
Нет.
## Домен
Нет (доступен напрямую по порту 6006).
## Сети
- `services_proxy` (external)
## Env переменные
Нет.
## Статус
**Запущен** (Up 4 weeks)

View File

@@ -0,0 +1,35 @@
# Uptime Kuma
## Назначение
Мониторинг доступности сервисов. Следит за uptime всех хостов и URL, отправляет уведомления при падении.
## Контейнеры
| Имя | Образ |
|-----|-------|
| uptime-kuma | louislam/uptime-kuma:1 |
## Порты
| Внутренний | Назначение |
|-----------|------------|
| 3001 | Web UI (через NPM) |
## Volumes
| Путь хоста | Путь контейнера | Содержимое |
|-----------|----------------|------------|
| ./data | /app/data | БД мониторинга (SQLite), конфиги |
## Домен
Через NPM (не указан явно).
## Сети
- `uptime_backend` (internal)
- `services_proxy` (external)
## Env переменные
Нет.
## Статус
**Запущен** (Up 5 days, healthy)

View File

@@ -0,0 +1,38 @@
# Vaultwarden (vault)
## Назначение
Self-hosted менеджер паролей, совместимый с клиентами Bitwarden. Хранит пароли, заметки, карты в зашифрованном виде.
## Контейнеры
| Имя | Образ |
|-----|-------|
| vaultwarden | vaultwarden/server:latest |
## Порты
| Внутренний | Назначение |
|-----------|------------|
| 80 | Web UI (через NPM) |
## Volumes
| Путь хоста | Путь контейнера | Содержимое |
|-----------|----------------|------------|
| ./data | /data | Зашифрованная БД паролей |
## Домен
`https://vault.digital-home.site`
## Сети
- `vault_backend` (internal)
- `services_proxy` (external)
## Env переменные
- `DOMAIN`
- `LOG_LEVEL`
- `EXTENDED_LOGGING`
- (закомментированы: `SIGNUPS_ALLOWED`, `ADMIN_TOKEN`, SMTP-настройки)
## Статус
**Запущен** (Up 4 weeks, healthy)

View File

@@ -0,0 +1,48 @@
# Vikunja
## Назначение
Self-hosted менеджер задач (аналог Todoist/Trello). Используется для личных задач, проектов, канбан-досок.
## Контейнеры
| Имя | Образ |
|-----|-------|
| vikunja | vikunja/vikunja:latest |
| vikunja_db | postgres:15-alpine |
## Порты
| Контейнер | Внутренний | Назначение |
|-----------|-----------|------------|
| vikunja | 3456 | Web UI (через NPM) |
| vikunja_db | 5432 | PostgreSQL |
## Volumes
| Путь хоста | Контейнер | Путь контейнера | Содержимое |
|-----------|-----------|----------------|------------|
| ./db | vikunja_db | /var/lib/postgresql/data | БД задач |
| ./files | vikunja | /app/vikunja/files | Вложения к задачам |
## Домен
`https://tasks.digital-home.site`
## Сети
- `vikunja_backend` (internal)
- `services_proxy` (external)
## Env переменные
- `VIKUNJA_DATABASE_TYPE`, `VIKUNJA_DATABASE_HOST`, `VIKUNJA_DATABASE_DATABASE`
- `VIKUNJA_DATABASE_USER`, `VIKUNJA_DATABASE_PASSWORD`
- `VIKUNJA_SERVICE_PUBLICURL`
- `VIKUNJA_SERVICE_JWTSECRET`
- `VIKUNJA_SERVICE_ENABLEREGISTRATION`
- `VIKUNJA_SERVICE_ENABLETASKATTACHMENTS`
- `VIKUNJA_SERVICE_TIMEZONE`
## Статус
**Запущен** (Up 4 weeks)
## Заметки
- API токен для автоматизации: `tk_03787e3778789fd5bfaff0542a8dd9390aae0f82`
- Проекты: Inbox(1), Личное(3), Dev(4), Япония(5), Финансы(6)

View File

@@ -0,0 +1,37 @@
# VPN Invite
## Назначение
Сервис для управления приглашениями в VPN. Node.js приложение с SQLite.
## Контейнеры
| Имя | Образ |
|-----|-------|
| vpn-invite-vpn-invite-1 | node:20-alpine |
## Порты
| Внутренний | Назначение |
|-----------|------------|
| 3500 | HTTP API (через NPM) |
## Volumes
| Volume | Путь контейнера | Содержимое |
|--------|----------------|------------|
| vpn-invite-data | /data | SQLite БД инвайтов |
| ./ | /app | Код приложения |
## Домен
Через NPM (не указан явно).
## Сети
- `services_proxy` (external)
## Env переменные
- `ADMIN_KEY`
- `PORT`
- `DB_PATH`
## Статус
**Запущен** (Up 21 hours)

View File

@@ -0,0 +1,35 @@
# VPN Router (sing-box)
## Назначение
VPN-маршрутизатор на базе sing-box. Управляет VPN-трафиком, поддерживает разные протоколы.
## Контейнеры
| Имя | Образ |
|-----|-------|
| vpn-router | ghcr.io/sagernet/sing-box:v1.10.7 |
## Порты
`network_mode: host` — использует все порты хоста напрямую.
## Volumes
| Путь хоста | Путь контейнера | Содержимое |
|-----------|----------------|------------|
| ./config | /etc/sing-box | Конфиг sing-box (config.json) |
## Домен
Нет (работает на уровне сети).
## Сети
`network_mode: host` (нет Docker-сетей).
## Env переменные
Нет.
## Статус
**Не запущен** (не найден в `docker ps`)
## Заметки
- `cap_add: NET_ADMIN` — нужно для управления сетевыми интерфейсами
- Конфиг в `./config/config.json`

View File

@@ -0,0 +1,40 @@
# Webhook Deploy
## Назначение
Python-сервер для автоматического деплоя через вебхуки. Получает HTTP-запрос → запускает Docker Compose команды для обновления сервисов.
## Контейнеры
| Имя | Образ |
|-----|-------|
| deploy-webhook | python:3.12-alpine |
## Порты
| Внешний | Внутренний | Назначение |
|---------|-----------|------------|
| 9000 | 9000 | HTTP webhook endpoint |
## Volumes
| Путь хоста | Путь контейнера | Содержимое |
|-----------|----------------|------------|
| ./server.py | /app/server.py | Python код сервера |
| /var/run/docker.sock | /var/run/docker.sock | Docker socket |
| /opt/digital-home | /opt/digital-home | Все проекты (для деплоя) |
| /usr/bin/docker | /usr/bin/docker | Docker CLI бинарник |
## Домен
Нет (внутренний доступ).
## Сети
- `services_proxy` (external)
## Env переменные
Нет (конфиг в `server.py`).
## Статус
**Статус неизвестен** (не найден в `docker ps`)
## Заметки
- Имеет доступ к Docker socket и всем проектам — может деплоить любой сервис

View File

@@ -0,0 +1,12 @@
Пользователи:
- Даня +
- Света +
- Юлиана +
- Виталя +
- Коля +
- Паша (Виталя) +
- Юля Ш.
- Юля М.
- Даша
- Ксюша +

View File

@@ -0,0 +1,42 @@
Основной объект
Дата старта
Дата завершения
Период - Q1, Q2, ..., Year
Родитель - Year
Деталь
Команда
План (в млн)
Основной объект
--------
Q1 2026
01.01.2026
31.03.2026
Q1 2026
2026
Деталь
DFS | 20 | Q1 2026 | 50
DTI | 30 | Q1 2026
KPI | 20 | Q1 2026
-----
Q2 2026
01.01.2026
31.03.2026
Q1 2026
2026
Деталь
DFS | 20 | Q2 2026
DTI | 30 | Q2 2026
2026
DFS | 20+ 30 + 40 + 50 |