Files
microdao-daarion/docs/tasks/TASK_PHASE7_BACKEND_COMPLETION.md
Apple 3de3c8cb36 feat: Add presence heartbeat for Matrix online status
- matrix-gateway: POST /internal/matrix/presence/online endpoint
- usePresenceHeartbeat hook with activity tracking
- Auto away after 5 min inactivity
- Offline on page close/visibility change
- Integrated in MatrixChatRoom component
2025-11-27 00:19:40 -08:00

417 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TASK_PHASE7_BACKEND_COMPLETION.md
## PHASE 7 — microDAO Console Backend Completion
### Goal
Доробити бекенд для **microdao-service** до production-ready стану:
- повний CRUD для microDAO;
- учасники (members) з ролями;
- проста казна (treasury) з балансами;
- налаштування (settings);
- PDP + Auth перевірки;
- базові NATS-події;
- інтеграція з існуючим фронтендом microDAO Console (MVP вже є).
---
## 0. Вихідні умови (вважати, що вже є)
З попереднього Phase 7 (MVP) вже створено:
- `migrations/008_create_microdao_core.sql` (схема БД);
- `services/microdao-service/main.py` (FastAPI-скелет, health endpoint);
- `services/microdao-service/models.py` (базові Pydantic-схеми);
- `services/microdao-service/requirements.txt`, `Dockerfile`;
- фронтенд:
- `src/api/microdao.ts` (чернетка);
- `src/features/microdao/MicrodaoListPage.tsx`;
- `src/features/microdao/MicrodaoConsolePage.tsx` (MVP з tabs);
- інфраструктура:
- `docker-compose.phase7.yml`;
- `scripts/start-phase7.sh`, `scripts/stop-phase7.sh`.
Цей таск ДОПОВНЮЄ вже створене, НЕ переписує з нуля.
---
## 1. Database: верифікація та дрібний тюнінг
1. Відкрити `migrations/008_create_microdao_core.sql` і переконатися, що там є таблиці:
```sql
microdaos (
id uuid primary key,
external_id text unique not null,
slug text unique not null,
name text not null,
description text,
owner_user_id uuid not null references users(id),
is_active boolean not null default true,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
microdao_members (
id uuid primary key,
microdao_id uuid not null references microdaos(id),
user_id uuid not null references users(id),
role text not null, -- 'owner' | 'admin' | 'member' | 'guest'
joined_at timestamptz not null default now()
);
microdao_treasury (
id uuid primary key,
microdao_id uuid not null references microdaos(id),
token_symbol text not null,
balance numeric(30, 8) not null default 0,
updated_at timestamptz not null default now()
);
microdao_settings (
id uuid primary key,
microdao_id uuid not null references microdaos(id),
key text not null,
value jsonb
);
```
2. Додати індекси, якщо їх ще немає:
```sql
create index if not exists idx_microdao_members_user_id
on microdao_members(user_id);
create index if not exists idx_microdao_members_microdao_id_role
on microdao_members(microdao_id, role);
create index if not exists idx_microdao_treasury_microdao_id
on microdao_treasury(microdao_id);
```
3. Переконатися, що міграція **застосована** до dev-БД.
---
## 2. Repository layer для microDAO
Створити/оновити `services/microdao-service/repository_microdao.py`:
### 2.1. Вважати, що вже є спільний модуль для БД
Подивитися, як це зроблено в `agents-service` / `messaging-service` (наприклад, `database.py` або `db.py` з `async_session` або `Pool`):
* використовувати **той самий підхід** (SQLAlchemy / asyncpg), НЕ вводити новий.
### 2.2. Оголосити інтерфейс
У `repository_microdao.py` реалізувати функції (асинхронні, якщо так прийнято):
```python
# Псевдо-інтерфейс, реалізувати згідно з існуючим стилем проєкту
async def create_microdao(db, *, owner_user_id: uuid.UUID, slug: str, name: str, description: str | None) -> MicrodaoRead: ...
async def update_microdao(db, *, microdao_id: uuid.UUID, data: MicrodaoUpdate) -> MicrodaoRead | None: ...
async def delete_microdao(db, *, microdao_id: uuid.UUID) -> None: ...
async def get_microdao_by_slug(db, slug: str) -> MicrodaoRead | None: ...
async def get_microdao_by_id(db, microdao_id: uuid.UUID) -> MicrodaoRead | None: ...
async def list_microdaos_for_user(db, user_id: uuid.UUID) -> list[MicrodaoRead]: ...
```
### 2.3. Members
```python
async def list_members(db, microdao_id: uuid.UUID) -> list[MicrodaoMember]: ...
async def add_member(db, microdao_id: uuid.UUID, user_id: uuid.UUID, role: str) -> MicrodaoMember: ...
async def remove_member(db, member_id: uuid.UUID) -> None: ...
```
Правила:
* при створенні microDAO — власник автоматично додається в `microdao_members` з `role='owner'`;
* при видаленні microDAO (`delete_microdao`) — або `is_active=false`, або м'яке видалення (краще `is_active=false`).
### 2.4. Treasury
```python
async def get_treasury_items(db, microdao_id: uuid.UUID) -> list[TreasuryItem]: ...
async def apply_treasury_delta(db, microdao_id: uuid.UUID, token_symbol: str, delta: Decimal) -> TreasuryItem: ...
```
* `delta` може бути додатним/від'ємним;
* гарантувати, що `balance` не йде в мінус без крайньої потреби (можна кидати помилку при `balance+delta < 0`).
### 2.5. Settings
```python
async def get_settings(db, microdao_id: uuid.UUID) -> dict[str, Any]: ...
async def upsert_setting(db, microdao_id: uuid.UUID, key: str, value: Any) -> None: ...
```
* повернути `dict[key] = value` для фронтенду.
---
## 3. Pydantic models — models.py
Оновити `services/microdao-service/models.py`:
```python
from datetime import datetime
from decimal import Decimal
from pydantic import BaseModel
class MicrodaoBase(BaseModel):
slug: str
name: str
description: str | None = None
class MicrodaoCreate(MicrodaoBase):
pass
class MicrodaoUpdate(BaseModel):
name: str | None = None
description: str | None = None
is_active: bool | None = None
class MicrodaoRead(MicrodaoBase):
id: str
external_id: str
owner_user_id: str
is_active: bool
created_at: datetime
updated_at: datetime
class MicrodaoMember(BaseModel):
id: str
user_id: str
role: str
joined_at: datetime
class TreasuryItem(BaseModel):
token_symbol: str
balance: Decimal
class MicrodaoSettings(BaseModel):
values: dict[str, object]
```
За потреби вирівняти з уже існуючими типами в проєкті.
---
## 4. Routes: REST API для microDAO
Створити/оновити `services/microdao-service/routes_microdao.py`:
### 4.1. Auth + PDP клієнти
Створити `auth_client.py`, `pdp_client.py` (або використати спільні з інших сервісів, якщо вони вже є).
Мінімум:
```python
async def get_actor_identity(request) -> ActorIdentity: ...
async def check_permission(actor, action: str, resource: dict) -> None:
# кинути HTTPException(403) якщо deny
```
### 4.2. Endpoints
```python
from fastapi import APIRouter, Depends, HTTPException
from .models import MicrodaoCreate, MicrodaoUpdate, MicrodaoRead, MicrodaoMember, TreasuryItem
from . import repository_microdao as repo
router = APIRouter(prefix="/microdao", tags=["microdao"])
```
#### GET `/microdao`
Повертає всі microDAO, де actor є member:
* `actor = get_actor_identity()`
* `repo.list_microdaos_for_user(db, actor.user_id)`
#### POST `/microdao`
Створює новий microDAO:
* PDP: `action="MICRODAO_CREATE"`
* `owner_user_id = actor.user_id`
* виклик `repo.create_microdao(...)`
* автоматично створити запис в `microdao_members` з `role='owner'`.
#### GET `/microdao/{slug}`
* знайти microDAO по `slug`;
* PDP: `action="MICRODAO_READ"`, `resource={"microdao_id": id}`;
* повернути `MicrodaoRead`.
#### PUT `/microdao/{slug}`
* PDP: `action="MICRODAO_MANAGE"`;
* дозволити тільки owner/admin;
* оновити `name/description/is_active`.
#### DELETE `/microdao/{slug}`
* PDP: `action="MICRODAO_MANAGE"`;
* `is_active=false` (soft delete).
---
## 5. Routes: Members / Treasury / Settings
### 5.1. Members
У `routes_members.py` (або в тому ж `routes_microdao.py`, якщо ти тримаєш все разом):
```python
GET /microdao/{slug}/members
POST /microdao/{slug}/members
DELETE /microdao/{slug}/members/{member_id}
```
Правила:
* тільки owner/admin можуть:
* додавати членів;
* видаляти членів;
* змінювати роль (якщо імплементуєш PATCH).
* простий body для POST:
* `user_id: str`
* `role: str`
### 5.2. Treasury
```python
GET /microdao/{slug}/treasury
POST /microdao/{slug}/treasury # delta operation
```
* PDP: `READ_TREASURY` для GET, `MANAGE_TREASURY` для POST.
### 5.3. Settings
```python
GET /microdao/{slug}/settings
POST /microdao/{slug}/settings # { key, value }
```
---
## 6. NATS Events
У `main.py` microdao-service або в окремому модулі:
* Підключитися до NATS (використати той самий клієнт, що в інших сервісах).
* Функція helper:
```python
async def publish_event(subject: str, payload: dict) -> None: ...
```
Викликати:
* при `create_microdao`:
* subject: `microdao.event.created`
* при `update_microdao`:
* `microdao.event.updated`
* при додаванні/видаленні члена:
* `microdao.event.member_added`
* `microdao.event.member_removed`
* при оновленні treasury:
* `microdao.event.treasury_updated`
Payload мінімальний:
```json
{
"microdao_id": "...",
"slug": "daarion-city",
"actor_id": "user:...",
"ts": "2025-11-24T12:00:00Z",
"data": { ... }
}
```
---
## 7. Інтеграція в main.py
Оновити `services/microdao-service/main.py`:
* створити `app = FastAPI(...)`
* підключити `router`:
```python
from .routes_microdao import router as microdao_router
app.include_router(microdao_router)
# за потреби: members_router, treasury_router
```
* додати `/health` endpoint (якщо ще не зроблено).
---
## 8. Frontend: використати реальний бекенд
Оновити `src/api/microdao.ts`:
* `getMyMicrodaos() → GET /microdao`
* `getMicrodao(slug) → GET /microdao/{slug}`
* `createMicrodao(payload) → POST /microdao`
* `getMicrodaoMembers(slug) → GET /microdao/{slug}/members`
* `getMicrodaoTreasury(slug) → GET /microdao/{slug}/treasury`
* `getMicrodaoSettings(slug) → GET /microdao/{slug}/settings`
Потім оновити:
* `MicrodaoListPage.tsx`:
* щоб брав дані з `getMyMicrodaos()`;
* `MicrodaoConsolePage.tsx`:
* Overview → `getMicrodao(slug)`;
* Members tab → `getMicrodaoMembers(slug)`;
* Treasury tab → `getMicrodaoTreasury(slug)`.
---
## 9. Docker / Scripts
Оновити (якщо потрібно):
* `docker-compose.phase7.yml`:
* переконатися, що `microdao-service` піднятий і залежить від Postgres та auth/pdp;
* `scripts/start-phase7.sh`:
* додати команду застосування міграції `008` (як це робиться для інших);
* `scripts/stop-phase7.sh`:
* зупинити microdao-service і пов'язані сервіси.
---
## 10. Acceptance Criteria
Вважати завдання виконаним, якщо:
* [ ] `/microdao` повертає список microDAO, де actor є member;
* [ ] `/microdao` (POST) створює новий microDAO і додає owner в members;
* [ ] `/microdao/{slug}` повертає деталі microDAO;
* [ ] `/microdao/{slug}/members` повертає список учасників;
* [ ] `/microdao/{slug}/treasury` повертає список токенів;
* [ ] PDP блокує доступ до чужих microDAO (403);
* [ ] MicrodaoListPage показує **реальні** microDAO із БД;
* [ ] MicrodaoConsolePage показує **реальні** Overview/Members/Treasury без mock-даних;
* [ ] всі тести/линт проходять успішно.
END OF TASK