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

12 KiB
Raw Blame History

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 і переконатися, що там є таблиці:
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
);
  1. Додати індекси, якщо їх ще немає:
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);
  1. Переконатися, що міграція застосована до 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 реалізувати функції (асинхронні, якщо так прийнято):

# Псевдо-інтерфейс, реалізувати згідно з існуючим стилем проєкту

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

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

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

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:

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 (або використати спільні з інших сервісів, якщо вони вже є).

Мінімум:

async def get_actor_identity(request) -> ActorIdentity: ...
async def check_permission(actor, action: str, resource: dict) -> None:
    # кинути HTTPException(403) якщо deny

4.2. Endpoints

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, якщо ти тримаєш все разом):

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

GET  /microdao/{slug}/treasury
POST /microdao/{slug}/treasury   # delta operation
  • PDP: READ_TREASURY для GET, MANAGE_TREASURY для POST.

5.3. Settings

GET  /microdao/{slug}/settings
POST /microdao/{slug}/settings   # { key, value }

6. NATS Events

У main.py microdao-service або в окремому модулі:

  • Підключитися до NATS (використати той самий клієнт, що в інших сервісах).
  • Функція helper:
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 мінімальний:

{
  "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:
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