1507 lines
93 KiB
HTML
1507 lines
93 KiB
HTML
|
||
<!doctype html>
|
||
<html lang="en" class="no-js">
|
||
<head>
|
||
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
|
||
|
||
|
||
<link rel="canonical" href="https://IvanTytar.github.io/microdao-daarion/tasks/TASK_PHASE8_DAO_DASHBOARD/">
|
||
|
||
|
||
|
||
|
||
<link rel="icon" href="../../assets/images/favicon.png">
|
||
<meta name="generator" content="mkdocs-1.5.3, mkdocs-material-9.5.18">
|
||
|
||
|
||
|
||
<title>TASK_PHASE8_DAO_DASHBOARD.md - DAARION Documentation</title>
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="../../assets/stylesheets/main.66ac8b77.min.css">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
|
||
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
|
||
|
||
|
||
|
||
<script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce((e,_)=>(e<<5)-e+_.charCodeAt(0),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</head>
|
||
|
||
|
||
<body dir="ltr">
|
||
|
||
|
||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||
<label class="md-overlay" for="__drawer"></label>
|
||
<div data-md-component="skip">
|
||
|
||
|
||
<a href="#task_phase8_dao_dashboardmd" class="md-skip">
|
||
Skip to content
|
||
</a>
|
||
|
||
</div>
|
||
<div data-md-component="announce">
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<header class="md-header md-header--shadow" data-md-component="header">
|
||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||
<a href="../.." title="DAARION Documentation" class="md-header__button md-logo" aria-label="DAARION Documentation" data-md-component="logo">
|
||
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||
|
||
</a>
|
||
<label class="md-header__button md-icon" for="__drawer">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg>
|
||
</label>
|
||
<div class="md-header__title" data-md-component="header-title">
|
||
<div class="md-header__ellipsis">
|
||
<div class="md-header__topic">
|
||
<span class="md-ellipsis">
|
||
DAARION Documentation
|
||
</span>
|
||
</div>
|
||
<div class="md-header__topic" data-md-component="header-topic">
|
||
<span class="md-ellipsis">
|
||
|
||
TASK_PHASE8_DAO_DASHBOARD.md
|
||
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<script>var media,input,key,value,palette=__md_get("__palette");if(palette&&palette.color){"(prefers-color-scheme)"===palette.color.media&&(media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']"),palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent"));for([key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||
|
||
|
||
|
||
<label class="md-header__button md-icon" for="__search">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||
</label>
|
||
<div class="md-search" data-md-component="search" role="dialog">
|
||
<label class="md-search__overlay" for="__search"></label>
|
||
<div class="md-search__inner" role="search">
|
||
<form class="md-search__form" name="search">
|
||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||
<label class="md-search__icon md-icon" for="__search">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg>
|
||
</label>
|
||
<nav class="md-search__options" aria-label="Search">
|
||
|
||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg>
|
||
</button>
|
||
</nav>
|
||
|
||
</form>
|
||
<div class="md-search__output">
|
||
<div class="md-search__scrollwrap" data-md-scrollfix>
|
||
<div class="md-search-result" data-md-component="search-result">
|
||
<div class="md-search-result__meta">
|
||
Initializing search
|
||
</div>
|
||
<ol class="md-search-result__list" role="presentation"></ol>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
</nav>
|
||
|
||
</header>
|
||
|
||
<div class="md-container" data-md-component="container">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<main class="md-main" data-md-component="main">
|
||
<div class="md-main__inner md-grid">
|
||
|
||
|
||
|
||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||
<div class="md-sidebar__scrollwrap">
|
||
<div class="md-sidebar__inner">
|
||
|
||
|
||
|
||
|
||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||
<label class="md-nav__title" for="__drawer">
|
||
<a href="../.." title="DAARION Documentation" class="md-nav__button md-logo" aria-label="DAARION Documentation" data-md-component="logo">
|
||
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54Z"/></svg>
|
||
|
||
</a>
|
||
DAARION Documentation
|
||
</label>
|
||
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../public/" class="md-nav__link">
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Home
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../public/getting-started/" class="md-nav__link">
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Getting Started
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../public/architecture-overview/" class="md-nav__link">
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Architecture
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../public/daiS_daos_overview/" class="md-nav__link">
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
DAIS & DAOS
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" >
|
||
|
||
|
||
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="">
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Internal
|
||
</span>
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_5">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
Internal
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_1" >
|
||
|
||
|
||
<label class="md-nav__link" for="__nav_5_1" id="__nav_5_1_label" tabindex="0">
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Infra
|
||
</span>
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_1_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_5_1">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
Infra
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../internal/infra/INFRA_AUTOMATION_PACK_V1/" class="md-nav__link">
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Infra Automation Pack v1
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../internal/infra/monitoring_overview/" class="md-nav__link">
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Monitoring Overview
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../internal/infra/nodes_registry_v0/" class="md-nav__link">
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Nodes Registry v0
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_2" >
|
||
|
||
|
||
<label class="md-nav__link" for="__nav_5_2" id="__nav_5_2_label" tabindex="0">
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Specs
|
||
</span>
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_2_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_5_2">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
Specs
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../internal/specs/matrix_presence_aggregator/" class="md-nav__link">
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Matrix Presence Aggregator
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../internal/specs/city_map_spec/" class="md-nav__link">
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
City Map Spec
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../internal/specs/node_join_protocol_draft/" class="md-nav__link">
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Node Join Protocol (Draft)
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||
<div class="md-sidebar__scrollwrap">
|
||
<div class="md-sidebar__inner">
|
||
|
||
|
||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-nav__title" for="__toc">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
Table of contents
|
||
</label>
|
||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#phase-8-dao-dashboard-governance-treasury-voting" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
PHASE 8 — DAO Dashboard (Governance + Treasury + Voting)
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="PHASE 8 — DAO Dashboard (Governance + Treasury + Voting)">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#goal" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
Goal
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#0" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
0. Вихідні умови
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#1-database-dao-core-schema" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
1. Database: DAO Core Schema
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="1. Database: DAO Core Schema">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#11" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
1.1. Таблиці
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#2-backend-dao-service-fastapi" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
2. Backend: dao-service (FastAPI)
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="2. Backend: dao-service (FastAPI)">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#21-modelspy" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
2.1. models.py
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#3-repository-layer" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
3. Repository layer
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="3. Repository layer">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#31-repository_daopy" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
3.1. repository_dao.py
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#32-repository_proposalspy" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
3.2. repository_proposals.py
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#33-repository_votespy" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
3.3. repository_votes.py
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#4-governance-engine" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
4. Governance Engine
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#5-nats-events" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
5. NATS Events
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#6-routes-fastapi" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
6. Routes (FastAPI)
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="6. Routes (FastAPI)">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#61-authpdp" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
6.1. Auth/PDP
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#62-dao-routes" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
6.2. DAO Routes
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#63-members-routes" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
6.3. Members Routes
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#64-treasury-routes" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
6.4. Treasury Routes
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#65-proposals-votes" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
6.5. Proposals & Votes
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#7-frontend-dao-dashboard" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
7. Frontend: DAO Dashboard
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="7. Frontend: DAO Dashboard">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#71-api-client" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
7.1. API Client
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#72" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
7.2. Сторінки
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="7.2. Сторінки">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#dao" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
/dao
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#daoslug" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
/dao/:slug
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#8-websocket-live-updates-mvp" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
8. WebSocket / Live Updates (MVP)
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#9-microdao-console" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
9. Інтеграція з microDAO Console
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#10-docker-scripts" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
10. Docker / Scripts
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#11-acceptance-criteria" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
11. Acceptance Criteria
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<div class="md-content" data-md-component="content">
|
||
<article class="md-content__inner md-typeset">
|
||
|
||
|
||
|
||
|
||
<h1 id="task_phase8_dao_dashboardmd">TASK_PHASE8_DAO_DASHBOARD.md<a class="headerlink" href="#task_phase8_dao_dashboardmd" title="Permanent link">¶</a></h1>
|
||
<h2 id="phase-8-dao-dashboard-governance-treasury-voting">PHASE 8 — DAO Dashboard (Governance + Treasury + Voting)<a class="headerlink" href="#phase-8-dao-dashboard-governance-treasury-voting" title="Permanent link">¶</a></h2>
|
||
<h3 id="goal">Goal<a class="headerlink" href="#goal" title="Permanent link">¶</a></h3>
|
||
<p>Завершити <strong>DAO-рівень governance</strong> поверх вже готового microDAO Console:</p>
|
||
<ul>
|
||
<li>створити <strong>dao-service</strong> (бекенд) з повним CRUD;</li>
|
||
<li>додати <strong>governance models</strong> (simple / quadratic / delegated);</li>
|
||
<li>реалізувати <strong>proposals + votes + treasury</strong>;</li>
|
||
<li>інтегрувати з <strong>PDP/Auth</strong> (Phase 4);</li>
|
||
<li>зробити <strong>DAO Dashboard UI</strong> (frontend);</li>
|
||
<li>підключити <strong>NATS-події</strong> для живого оновлення.</li>
|
||
</ul>
|
||
<p>Фінальний результат:<br />
|
||
DAO Dashboard, який показує стан DAO (учасники, казна, пропозиції, голосування) і дозволяє керувати governance.</p>
|
||
<hr />
|
||
<h2 id="0">0. Вихідні умови<a class="headerlink" href="#0" title="Permanent link">¶</a></h2>
|
||
<p>Вважати, що в репозиторії вже є:</p>
|
||
<ul>
|
||
<li><strong>Phase 1–7 завершені</strong> (Messenger, Agents, LLM, Security, Passkey, Agent Hub, microDAO Console);</li>
|
||
<li>База даних (PostgreSQL) з таблицями <code>users</code>, <code>microdaos</code>, <code>microdao_members</code>, <code>microdao_treasury</code>, <code>microdao_settings</code>;</li>
|
||
<li><strong>auth-service</strong>, <strong>pdp-service</strong>, <strong>usage-engine</strong>, <strong>messaging-service</strong>, <strong>agents-service</strong>, <strong>microdao-service</strong>;</li>
|
||
<li>Frontend (React/TS), з:</li>
|
||
<li><code>MicrodaoListPage.tsx</code>, <code>MicrodaoConsolePage.tsx</code>;</li>
|
||
<li>auth/pdp інтеграцією;</li>
|
||
<li>Agent Hub UI.</li>
|
||
</ul>
|
||
<p>Цей таск додає <strong>новий шар DAO</strong> поверх існуючих microDAO.</p>
|
||
<hr />
|
||
<h2 id="1-database-dao-core-schema">1. Database: DAO Core Schema<a class="headerlink" href="#1-database-dao-core-schema" title="Permanent link">¶</a></h2>
|
||
<p>Створити нову міграцію:</p>
|
||
<p><code>migrations/009_create_dao_core.sql</code></p>
|
||
<h3 id="11">1.1. Таблиці<a class="headerlink" href="#11" title="Permanent link">¶</a></h3>
|
||
<div class="codehilite"><pre><span></span><code><span class="c1">-- 1) DAO (верхній рівень governance)</span>
|
||
<span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">dao</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">primary</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">gen_random_uuid</span><span class="p">(),</span>
|
||
<span class="w"> </span><span class="n">slug</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">unique</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">description</span><span class="w"> </span><span class="nb">text</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">microdao_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">microdaos</span><span class="p">(</span><span class="n">id</span><span class="p">),</span>
|
||
<span class="w"> </span><span class="n">owner_user_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">users</span><span class="p">(</span><span class="n">id</span><span class="p">),</span>
|
||
<span class="w"> </span><span class="n">governance_model</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="s1">'simple'</span><span class="p">,</span><span class="w"> </span><span class="c1">-- 'simple' | 'quadratic' | 'delegated'</span>
|
||
<span class="w"> </span><span class="n">voting_period_seconds</span><span class="w"> </span><span class="nb">integer</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="mi">604800</span><span class="p">,</span><span class="w"> </span><span class="c1">-- 7 днів</span>
|
||
<span class="w"> </span><span class="n">quorum_percent</span><span class="w"> </span><span class="nb">integer</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="mi">20</span><span class="p">,</span><span class="w"> </span><span class="c1">-- 20%</span>
|
||
<span class="w"> </span><span class="n">is_active</span><span class="w"> </span><span class="nb">boolean</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="k">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">created_at</span><span class="w"> </span><span class="n">timestamptz</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">now</span><span class="p">(),</span>
|
||
<span class="w"> </span><span class="n">updated_at</span><span class="w"> </span><span class="n">timestamptz</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">now</span><span class="p">()</span>
|
||
<span class="p">);</span>
|
||
|
||
<span class="k">create</span><span class="w"> </span><span class="k">index</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">idx_dao_microdao_id</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">dao</span><span class="p">(</span><span class="n">microdao_id</span><span class="p">);</span>
|
||
<span class="k">create</span><span class="w"> </span><span class="k">index</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">idx_dao_owner_user_id</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">dao</span><span class="p">(</span><span class="n">owner_user_id</span><span class="p">);</span>
|
||
|
||
|
||
<span class="c1">-- 2) DAO Members (над microdao_members)</span>
|
||
<span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">dao_members</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">primary</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">gen_random_uuid</span><span class="p">(),</span>
|
||
<span class="w"> </span><span class="n">dao_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">dao</span><span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="k">delete</span><span class="w"> </span><span class="k">cascade</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">user_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">users</span><span class="p">(</span><span class="n">id</span><span class="p">),</span>
|
||
<span class="w"> </span><span class="k">role</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="p">,</span><span class="w"> </span><span class="c1">-- 'owner' | 'admin' | 'member' | 'guest'</span>
|
||
<span class="w"> </span><span class="n">joined_at</span><span class="w"> </span><span class="n">timestamptz</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">now</span><span class="p">()</span>
|
||
<span class="p">);</span>
|
||
|
||
<span class="k">create</span><span class="w"> </span><span class="k">index</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">idx_dao_members_user_id</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">dao_members</span><span class="p">(</span><span class="n">user_id</span><span class="p">);</span>
|
||
<span class="k">create</span><span class="w"> </span><span class="k">index</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">idx_dao_members_dao_id_role</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">dao_members</span><span class="p">(</span><span class="n">dao_id</span><span class="p">,</span><span class="w"> </span><span class="k">role</span><span class="p">);</span>
|
||
|
||
|
||
<span class="c1">-- 3) DAO Treasury (агрегований шар над microdao_treasury, але на рівні DAO)</span>
|
||
<span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">dao_treasury</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">primary</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">gen_random_uuid</span><span class="p">(),</span>
|
||
<span class="w"> </span><span class="n">dao_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">dao</span><span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="k">delete</span><span class="w"> </span><span class="k">cascade</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">token_symbol</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">contract_address</span><span class="w"> </span><span class="nb">text</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">balance</span><span class="w"> </span><span class="nb">numeric</span><span class="p">(</span><span class="mi">30</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">)</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">updated_at</span><span class="w"> </span><span class="n">timestamptz</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">now</span><span class="p">()</span>
|
||
<span class="p">);</span>
|
||
|
||
<span class="k">create</span><span class="w"> </span><span class="k">unique</span><span class="w"> </span><span class="k">index</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">uq_dao_treasury_token</span>
|
||
<span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">dao_treasury</span><span class="p">(</span><span class="n">dao_id</span><span class="p">,</span><span class="w"> </span><span class="n">token_symbol</span><span class="p">);</span>
|
||
|
||
|
||
<span class="c1">-- 4) DAO Proposals</span>
|
||
<span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">dao_proposals</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">primary</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">gen_random_uuid</span><span class="p">(),</span>
|
||
<span class="w"> </span><span class="n">dao_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">dao</span><span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="k">delete</span><span class="w"> </span><span class="k">cascade</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">slug</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">description</span><span class="w"> </span><span class="nb">text</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">created_by_user_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">users</span><span class="p">(</span><span class="n">id</span><span class="p">),</span>
|
||
<span class="w"> </span><span class="n">created_at</span><span class="w"> </span><span class="n">timestamptz</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">now</span><span class="p">(),</span>
|
||
<span class="w"> </span><span class="n">start_at</span><span class="w"> </span><span class="n">timestamptz</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">end_at</span><span class="w"> </span><span class="n">timestamptz</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">status</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="s1">'draft'</span><span class="p">,</span><span class="w"> </span><span class="c1">-- 'draft' | 'active' | 'passed' | 'rejected' | 'executed'</span>
|
||
<span class="w"> </span><span class="n">governance_model_override</span><span class="w"> </span><span class="nb">text</span><span class="p">,</span><span class="w"> </span><span class="c1">-- optional override</span>
|
||
<span class="w"> </span><span class="n">quorum_percent_override</span><span class="w"> </span><span class="nb">integer</span>
|
||
<span class="p">);</span>
|
||
|
||
<span class="k">create</span><span class="w"> </span><span class="k">unique</span><span class="w"> </span><span class="k">index</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">uq_dao_proposals_slug</span>
|
||
<span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">dao_proposals</span><span class="p">(</span><span class="n">dao_id</span><span class="p">,</span><span class="w"> </span><span class="n">slug</span><span class="p">);</span>
|
||
|
||
|
||
<span class="c1">-- 5) DAO Votes</span>
|
||
<span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">dao_votes</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">primary</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">gen_random_uuid</span><span class="p">(),</span>
|
||
<span class="w"> </span><span class="n">proposal_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">dao_proposals</span><span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="k">delete</span><span class="w"> </span><span class="k">cascade</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">voter_user_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">users</span><span class="p">(</span><span class="n">id</span><span class="p">),</span>
|
||
<span class="w"> </span><span class="n">vote_value</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="p">,</span><span class="w"> </span><span class="c1">-- 'yes' | 'no' | 'abstain'</span>
|
||
<span class="w"> </span><span class="n">weight</span><span class="w"> </span><span class="nb">numeric</span><span class="p">(</span><span class="mi">30</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">)</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="p">,</span><span class="w"> </span><span class="c1">-- actual weight after applying model</span>
|
||
<span class="w"> </span><span class="n">raw_power</span><span class="w"> </span><span class="nb">numeric</span><span class="p">(</span><span class="mi">30</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">),</span><span class="w"> </span><span class="c1">-- до обробки</span>
|
||
<span class="w"> </span><span class="n">created_at</span><span class="w"> </span><span class="n">timestamptz</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">now</span><span class="p">()</span>
|
||
<span class="p">);</span>
|
||
|
||
<span class="k">create</span><span class="w"> </span><span class="k">index</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">idx_dao_votes_proposal_id</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">dao_votes</span><span class="p">(</span><span class="n">proposal_id</span><span class="p">);</span>
|
||
<span class="k">create</span><span class="w"> </span><span class="k">unique</span><span class="w"> </span><span class="k">index</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">uq_dao_votes_proposal_voter</span>
|
||
<span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">dao_votes</span><span class="p">(</span><span class="n">proposal_id</span><span class="p">,</span><span class="w"> </span><span class="n">voter_user_id</span><span class="p">);</span>
|
||
|
||
|
||
<span class="c1">-- 6) DAO Roles (додатковий шар, якщо потрібні нестандартні ролі)</span>
|
||
<span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">dao_roles</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">primary</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">gen_random_uuid</span><span class="p">(),</span>
|
||
<span class="w"> </span><span class="n">dao_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">dao</span><span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="k">delete</span><span class="w"> </span><span class="k">cascade</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">code</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">description</span><span class="w"> </span><span class="nb">text</span>
|
||
<span class="p">);</span>
|
||
|
||
<span class="k">create</span><span class="w"> </span><span class="k">unique</span><span class="w"> </span><span class="k">index</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">uq_dao_roles_code</span>
|
||
<span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">dao_roles</span><span class="p">(</span><span class="n">dao_id</span><span class="p">,</span><span class="w"> </span><span class="n">code</span><span class="p">);</span>
|
||
|
||
|
||
<span class="c1">-- 7) DAO Role Assignments</span>
|
||
<span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">dao_role_assignments</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">primary</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">gen_random_uuid</span><span class="p">(),</span>
|
||
<span class="w"> </span><span class="n">dao_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">dao</span><span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="k">delete</span><span class="w"> </span><span class="k">cascade</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">user_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">users</span><span class="p">(</span><span class="n">id</span><span class="p">),</span>
|
||
<span class="w"> </span><span class="n">role_code</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">assigned_at</span><span class="w"> </span><span class="n">timestamptz</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">now</span><span class="p">()</span>
|
||
<span class="p">);</span>
|
||
|
||
<span class="k">create</span><span class="w"> </span><span class="k">index</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">idx_dao_role_assignments_user</span>
|
||
<span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">dao_role_assignments</span><span class="p">(</span><span class="n">user_id</span><span class="p">);</span>
|
||
|
||
|
||
<span class="c1">-- 8) DAO Audit Log</span>
|
||
<span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">dao_audit_log</span><span class="w"> </span><span class="p">(</span>
|
||
<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">primary</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">gen_random_uuid</span><span class="p">(),</span>
|
||
<span class="w"> </span><span class="n">dao_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">dao</span><span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="k">delete</span><span class="w"> </span><span class="k">cascade</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">actor_user_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">references</span><span class="w"> </span><span class="n">users</span><span class="p">(</span><span class="n">id</span><span class="p">),</span>
|
||
<span class="w"> </span><span class="n">event_type</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">event_payload</span><span class="w"> </span><span class="n">jsonb</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">created_at</span><span class="w"> </span><span class="n">timestamptz</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="n">now</span><span class="p">()</span>
|
||
<span class="p">);</span>
|
||
|
||
<span class="k">create</span><span class="w"> </span><span class="k">index</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">exists</span><span class="w"> </span><span class="n">idx_dao_audit_log_dao_id</span>
|
||
<span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">dao_audit_log</span><span class="p">(</span><span class="n">dao_id</span><span class="p">);</span>
|
||
</code></pre></div>
|
||
|
||
<p>Перевірити, що міграція застосовується без помилок.</p>
|
||
<hr />
|
||
<h2 id="2-backend-dao-service-fastapi">2. Backend: <code>dao-service</code> (FastAPI)<a class="headerlink" href="#2-backend-dao-service-fastapi" title="Permanent link">¶</a></h2>
|
||
<p>Створити новий сервіс:</p>
|
||
<p><code>services/dao-service/</code>:</p>
|
||
<ul>
|
||
<li><code>main.py</code></li>
|
||
<li><code>models.py</code></li>
|
||
<li><code>repository_dao.py</code></li>
|
||
<li><code>repository_proposals.py</code></li>
|
||
<li><code>repository_votes.py</code></li>
|
||
<li><code>governance_engine.py</code></li>
|
||
<li><code>nats_events.py</code></li>
|
||
<li><code>auth_client.py</code> / <code>pdp_client.py</code> (як thin-обгортки над існуючими)</li>
|
||
<li><code>requirements.txt</code></li>
|
||
<li><code>Dockerfile</code></li>
|
||
<li><code>README.md</code></li>
|
||
</ul>
|
||
<h3 id="21-modelspy">2.1. models.py<a class="headerlink" href="#21-modelspy" title="Permanent link">¶</a></h3>
|
||
<p>Оголосити Pydantic-схеми (адаптувати до стилю проєкту):</p>
|
||
<div class="codehilite"><pre><span></span><code><span class="kn">from</span><span class="w"> </span><span class="nn">datetime</span><span class="w"> </span><span class="kn">import</span> <span class="n">datetime</span>
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">decimal</span><span class="w"> </span><span class="kn">import</span> <span class="n">Decimal</span>
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">pydantic</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseModel</span>
|
||
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">DaoBase</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||
<span class="n">slug</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">description</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">DaoCreate</span><span class="p">(</span><span class="n">DaoBase</span><span class="p">):</span>
|
||
<span class="n">governance_model</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="n">voting_period_seconds</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="n">quorum_percent</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">DaoUpdate</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="n">description</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="n">governance_model</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="n">voting_period_seconds</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="n">quorum_percent</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="n">is_active</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">DaoRead</span><span class="p">(</span><span class="n">DaoBase</span><span class="p">):</span>
|
||
<span class="nb">id</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">microdao_id</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">owner_user_id</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">governance_model</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">voting_period_seconds</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">quorum_percent</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">is_active</span><span class="p">:</span> <span class="nb">bool</span>
|
||
<span class="n">created_at</span><span class="p">:</span> <span class="n">datetime</span>
|
||
<span class="n">updated_at</span><span class="p">:</span> <span class="n">datetime</span>
|
||
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">DaoMember</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||
<span class="nb">id</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">user_id</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">role</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">joined_at</span><span class="p">:</span> <span class="n">datetime</span>
|
||
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">DaoTreasuryItem</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||
<span class="n">token_symbol</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">contract_address</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="n">balance</span><span class="p">:</span> <span class="n">Decimal</span>
|
||
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">ProposalBase</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||
<span class="n">slug</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">title</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">description</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">ProposalCreate</span><span class="p">(</span><span class="n">ProposalBase</span><span class="p">):</span>
|
||
<span class="n">start_at</span><span class="p">:</span> <span class="n">datetime</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
<span class="n">end_at</span><span class="p">:</span> <span class="n">datetime</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">ProposalRead</span><span class="p">(</span><span class="n">ProposalBase</span><span class="p">):</span>
|
||
<span class="nb">id</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">dao_id</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">created_by_user_id</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">created_at</span><span class="p">:</span> <span class="n">datetime</span>
|
||
<span class="n">start_at</span><span class="p">:</span> <span class="n">datetime</span> <span class="o">|</span> <span class="kc">None</span>
|
||
<span class="n">end_at</span><span class="p">:</span> <span class="n">datetime</span> <span class="o">|</span> <span class="kc">None</span>
|
||
<span class="n">status</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">governance_model_override</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span>
|
||
<span class="n">quorum_percent_override</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="kc">None</span>
|
||
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">VoteCreate</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||
<span class="n">vote_value</span><span class="p">:</span> <span class="nb">str</span> <span class="c1"># 'yes' | 'no' | 'abstain'</span>
|
||
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">VoteRead</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||
<span class="nb">id</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">proposal_id</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">voter_user_id</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">vote_value</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">weight</span><span class="p">:</span> <span class="n">Decimal</span>
|
||
<span class="n">raw_power</span><span class="p">:</span> <span class="n">Decimal</span> <span class="o">|</span> <span class="kc">None</span>
|
||
<span class="n">created_at</span><span class="p">:</span> <span class="n">datetime</span>
|
||
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">DaoOverview</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||
<span class="n">dao</span><span class="p">:</span> <span class="n">DaoRead</span>
|
||
<span class="n">members_count</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">active_proposals_count</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">treasury_items</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">DaoTreasuryItem</span><span class="p">]</span>
|
||
</code></pre></div>
|
||
|
||
<hr />
|
||
<h2 id="3-repository-layer">3. Repository layer<a class="headerlink" href="#3-repository-layer" title="Permanent link">¶</a></h2>
|
||
<h3 id="31-repository_daopy">3.1. <code>repository_dao.py</code><a class="headerlink" href="#31-repository_daopy" title="Permanent link">¶</a></h3>
|
||
<p>Реалізувати:</p>
|
||
<div class="codehilite"><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_dao_for_user</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">user_id</span><span class="p">:</span> <span class="n">uuid</span><span class="o">.</span><span class="n">UUID</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="n">DaoRead</span><span class="p">]:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_dao_by_slug</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">slug</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">DaoRead</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_dao</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">microdao_id</span><span class="p">,</span> <span class="n">owner_user_id</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="n">DaoCreate</span><span class="p">)</span> <span class="o">-></span> <span class="n">DaoRead</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_dao</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">dao_id</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="n">DaoUpdate</span><span class="p">)</span> <span class="o">-></span> <span class="n">DaoRead</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">soft_delete_dao</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">dao_id</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_members</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">dao_id</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="n">DaoMember</span><span class="p">]:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">add_member</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">dao_id</span><span class="p">,</span> <span class="n">user_id</span><span class="p">,</span> <span class="n">role</span><span class="p">)</span> <span class="o">-></span> <span class="n">DaoMember</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">remove_member</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">member_id</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_treasury_items</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">dao_id</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="n">DaoTreasuryItem</span><span class="p">]:</span> <span class="o">...</span>
|
||
</code></pre></div>
|
||
|
||
<h3 id="32-repository_proposalspy">3.2. <code>repository_proposals.py</code><a class="headerlink" href="#32-repository_proposalspy" title="Permanent link">¶</a></h3>
|
||
<div class="codehilite"><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_proposals</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">dao_id</span><span class="p">:</span> <span class="n">uuid</span><span class="o">.</span><span class="n">UUID</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="n">ProposalRead</span><span class="p">]:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_proposal</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">proposal_id</span><span class="p">:</span> <span class="n">uuid</span><span class="o">.</span><span class="n">UUID</span><span class="p">)</span> <span class="o">-></span> <span class="n">ProposalRead</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_proposal_by_slug</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">dao_id</span><span class="p">,</span> <span class="n">slug</span><span class="p">)</span> <span class="o">-></span> <span class="n">ProposalRead</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_proposal</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">dao_id</span><span class="p">,</span> <span class="n">created_by_user_id</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="n">ProposalCreate</span><span class="p">)</span> <span class="o">-></span> <span class="n">ProposalRead</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_proposal_status</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">proposal_id</span><span class="p">,</span> <span class="n">status</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">ProposalRead</span> <span class="o">|</span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
|
||
</code></pre></div>
|
||
|
||
<h3 id="33-repository_votespy">3.3. <code>repository_votes.py</code><a class="headerlink" href="#33-repository_votespy" title="Permanent link">¶</a></h3>
|
||
<div class="codehilite"><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">list_votes_for_proposal</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">proposal_id</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="n">VoteRead</span><span class="p">]:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_or_update_vote</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">proposal_id</span><span class="p">,</span> <span class="n">voter_user_id</span><span class="p">,</span> <span class="n">vote_value</span><span class="p">,</span> <span class="n">weight</span><span class="p">,</span> <span class="n">raw_power</span><span class="p">)</span> <span class="o">-></span> <span class="n">VoteRead</span><span class="p">:</span> <span class="o">...</span>
|
||
</code></pre></div>
|
||
|
||
<hr />
|
||
<h2 id="4-governance-engine">4. Governance Engine<a class="headerlink" href="#4-governance-engine" title="Permanent link">¶</a></h2>
|
||
<p>Файл: <code>services/dao-service/governance_engine.py</code></p>
|
||
<p>Реалізувати три моделі:</p>
|
||
<div class="codehilite"><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">calculate_voting_power_simple</span><span class="p">(</span><span class="n">actor</span><span class="p">,</span> <span class="n">dao</span><span class="p">:</span> <span class="n">DaoRead</span><span class="p">)</span> <span class="o">-></span> <span class="n">Decimal</span><span class="p">:</span>
|
||
<span class="c1"># 1 user = 1 голос</span>
|
||
<span class="k">return</span> <span class="n">Decimal</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
||
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">calculate_voting_power_quadratic</span><span class="p">(</span><span class="n">actor</span><span class="p">,</span> <span class="n">dao</span><span class="p">:</span> <span class="n">DaoRead</span><span class="p">,</span> <span class="n">base_power</span><span class="p">:</span> <span class="n">Decimal</span><span class="p">)</span> <span class="o">-></span> <span class="n">Decimal</span><span class="p">:</span>
|
||
<span class="c1"># weight = sqrt(base_power)</span>
|
||
<span class="c1"># base_power може бути, наприклад, кількістю токенів з treasury або окремої таблиці</span>
|
||
<span class="kn">from</span><span class="w"> </span><span class="nn">decimal</span><span class="w"> </span><span class="kn">import</span> <span class="n">Decimal</span><span class="p">,</span> <span class="n">getcontext</span>
|
||
<span class="n">getcontext</span><span class="p">()</span><span class="o">.</span><span class="n">prec</span> <span class="o">=</span> <span class="mi">28</span>
|
||
<span class="k">return</span> <span class="n">base_power</span><span class="o">.</span><span class="n">sqrt</span><span class="p">()</span>
|
||
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">calculate_voting_power_delegated</span><span class="p">(</span><span class="n">actor</span><span class="p">,</span> <span class="n">dao</span><span class="p">:</span> <span class="n">DaoRead</span><span class="p">,</span> <span class="n">delegation_graph</span><span class="p">)</span> <span class="o">-></span> <span class="n">Decimal</span><span class="p">:</span>
|
||
<span class="c1"># з урахуванням делегацій</span>
|
||
<span class="o">...</span>
|
||
</code></pre></div>
|
||
|
||
<p>Також функцію <strong>обчислення результату</strong>:</p>
|
||
<div class="codehilite"><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">evaluate_proposal_outcome</span><span class="p">(</span>
|
||
<span class="n">dao</span><span class="p">:</span> <span class="n">DaoRead</span><span class="p">,</span>
|
||
<span class="n">proposal</span><span class="p">:</span> <span class="n">ProposalRead</span><span class="p">,</span>
|
||
<span class="n">votes</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">VoteRead</span><span class="p">]</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">dict</span><span class="p">:</span>
|
||
<span class="c1"># рахуємо total weight, yes/no/abstain, quorum, чи passed</span>
|
||
<span class="o">...</span>
|
||
</code></pre></div>
|
||
|
||
<hr />
|
||
<h2 id="5-nats-events">5. NATS Events<a class="headerlink" href="#5-nats-events" title="Permanent link">¶</a></h2>
|
||
<p>Файл: <code>services/dao-service/nats_events.py</code></p>
|
||
<p>Функція:</p>
|
||
<div class="codehilite"><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">publish_event</span><span class="p">(</span><span class="n">subject</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">payload</span><span class="p">:</span> <span class="nb">dict</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span> <span class="o">...</span>
|
||
</code></pre></div>
|
||
|
||
<p>Викликати з backend:</p>
|
||
<ul>
|
||
<li>при створенні DAO:
|
||
<code>dao.event.created</code></li>
|
||
<li>при оновленні DAO:
|
||
<code>dao.event.updated</code></li>
|
||
<li>при оновленні treasury:
|
||
<code>dao.event.treasury_updated</code></li>
|
||
<li>при створенні пропозиції:
|
||
<code>dao.event.proposal_created</code></li>
|
||
<li>при активації/закритті пропозиції:
|
||
<code>dao.event.proposal_status_changed</code></li>
|
||
<li>при голосуванні:
|
||
<code>dao.event.vote_cast</code></li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="6-routes-fastapi">6. Routes (FastAPI)<a class="headerlink" href="#6-routes-fastapi" title="Permanent link">¶</a></h2>
|
||
<p>У <code>services/dao-service/main.py</code> створити FastAPI app з router'ами:</p>
|
||
<h3 id="61-authpdp">6.1. Auth/PDP<a class="headerlink" href="#61-authpdp" title="Permanent link">¶</a></h3>
|
||
<p>Створити <code>auth_client.py</code>, <code>pdp_client.py</code> (як у інших сервісах):</p>
|
||
<div class="codehilite"><pre><span></span><code><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_actor_identity</span><span class="p">(</span><span class="n">request</span><span class="p">)</span> <span class="o">-></span> <span class="n">ActorIdentity</span><span class="p">:</span> <span class="o">...</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">check_permission</span><span class="p">(</span><span class="n">actor</span><span class="p">,</span> <span class="n">action</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">resource</span><span class="p">:</span> <span class="nb">dict</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="c1"># кинути HTTPException(403) якщо deny</span>
|
||
</code></pre></div>
|
||
|
||
<h3 id="62-dao-routes">6.2. DAO Routes<a class="headerlink" href="#62-dao-routes" title="Permanent link">¶</a></h3>
|
||
<p>Файл: <code>routes_dao.py</code>:</p>
|
||
<div class="codehilite"><pre><span></span><code><span class="n">router</span> <span class="o">=</span> <span class="n">APIRouter</span><span class="p">(</span><span class="n">prefix</span><span class="o">=</span><span class="s2">"/dao"</span><span class="p">,</span> <span class="n">tags</span><span class="o">=</span><span class="p">[</span><span class="s2">"dao"</span><span class="p">])</span>
|
||
</code></pre></div>
|
||
|
||
<p>Endpoints:</p>
|
||
<ol>
|
||
<li>
|
||
<p><code>GET /dao</code></p>
|
||
</li>
|
||
<li>
|
||
<p>повертає DAO, де actor є членом:</p>
|
||
</li>
|
||
<li>
|
||
<p><code>list_dao_for_user(db, actor.user_id)</code></p>
|
||
</li>
|
||
<li>
|
||
<p><code>POST /dao</code></p>
|
||
</li>
|
||
<li>
|
||
<p>PDP: <code>DAO_CREATE</code></p>
|
||
</li>
|
||
<li>body: <code>DaoCreate</code></li>
|
||
<li>потрібно вказати <code>microdao_id</code> (як поле або через контекст)</li>
|
||
<li>
|
||
<p>створюємо DAO, додаємо owner у <code>dao_members</code> з роллю <code>owner</code>.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>GET /dao/{slug}</code></p>
|
||
</li>
|
||
<li>
|
||
<p>PDP: <code>DAO_READ</code></p>
|
||
</li>
|
||
<li>
|
||
<p>повертає <code>DaoRead</code> + агреговану інформацію (можна через <code>DaoOverview</code>).</p>
|
||
</li>
|
||
<li>
|
||
<p><code>PUT /dao/{slug}</code></p>
|
||
</li>
|
||
<li>
|
||
<p>PDP: <code>DAO_MANAGE</code></p>
|
||
</li>
|
||
<li>
|
||
<p>оновити <code>name/description/governance_model/...</code>.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>DELETE /dao/{slug}</code></p>
|
||
</li>
|
||
<li>
|
||
<p>PDP: <code>DAO_MANAGE</code></p>
|
||
</li>
|
||
<li><code>is_active=false</code>.</li>
|
||
</ol>
|
||
<h3 id="63-members-routes">6.3. Members Routes<a class="headerlink" href="#63-members-routes" title="Permanent link">¶</a></h3>
|
||
<p><code>GET /dao/{slug}/members</code>
|
||
<code>POST /dao/{slug}/members</code>
|
||
<code>DELETE /dao/{slug}/members/{memberId}</code></p>
|
||
<h3 id="64-treasury-routes">6.4. Treasury Routes<a class="headerlink" href="#64-treasury-routes" title="Permanent link">¶</a></h3>
|
||
<p><code>GET /dao/{slug}/treasury</code>
|
||
<code>POST /dao/{slug}/treasury</code> (delta-операція: <code>token_symbol</code>, <code>delta</code>)</p>
|
||
<h3 id="65-proposals-votes">6.5. Proposals & Votes<a class="headerlink" href="#65-proposals-votes" title="Permanent link">¶</a></h3>
|
||
<p><code>GET /dao/{slug}/proposals</code>
|
||
<code>POST /dao/{slug}/proposals</code>
|
||
<code>GET /dao/{slug}/proposals/{proposalSlug}</code>
|
||
<code>POST /dao/{slug}/proposals/{proposalSlug}/activate</code>
|
||
<code>POST /dao/{slug}/proposals/{proposalSlug}/close</code></p>
|
||
<p><code>GET /dao/{slug}/proposals/{proposalSlug}/votes</code>
|
||
<code>POST /dao/{slug}/proposals/{proposalSlug}/votes</code> (create/update vote)</p>
|
||
<hr />
|
||
<h2 id="7-frontend-dao-dashboard">7. Frontend: DAO Dashboard<a class="headerlink" href="#7-frontend-dao-dashboard" title="Permanent link">¶</a></h2>
|
||
<p>У фронтенді створити структуру:</p>
|
||
<p><code>src/api/dao.ts</code>
|
||
<code>src/features/dao/DaoListPage.tsx</code>
|
||
<code>src/features/dao/DaoDashboardPage.tsx</code>
|
||
<code>src/features/dao/components/...</code></p>
|
||
<h3 id="71-api-client">7.1. API Client<a class="headerlink" href="#71-api-client" title="Permanent link">¶</a></h3>
|
||
<p>У <code>src/api/dao.ts</code>:</p>
|
||
<div class="codehilite"><pre><span></span><code><span class="k">export</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">getMyDaos</span><span class="p">()</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">DaoSummary</span><span class="p">[]</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span>
|
||
<span class="k">export</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">getDao</span><span class="p">(</span><span class="nx">slug</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">DaoOverview</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span>
|
||
<span class="k">export</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">createDao</span><span class="p">(</span><span class="nx">payload</span><span class="o">:</span><span class="w"> </span><span class="kt">DaoCreatePayload</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">DaoRead</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span>
|
||
<span class="k">export</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">getDaoMembers</span><span class="p">(</span><span class="nx">slug</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">DaoMember</span><span class="p">[]</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span>
|
||
<span class="k">export</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">getDaoTreasury</span><span class="p">(</span><span class="nx">slug</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">DaoTreasuryItem</span><span class="p">[]</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span>
|
||
<span class="k">export</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">getDaoProposals</span><span class="p">(</span><span class="nx">slug</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">DaoProposal</span><span class="p">[]</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span>
|
||
<span class="k">export</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">createDaoProposal</span><span class="p">(</span><span class="nx">slug</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">payload</span><span class="o">:</span><span class="w"> </span><span class="kt">ProposalCreatePayload</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">DaoProposal</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span>
|
||
<span class="k">export</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">getDaoProposal</span><span class="p">(</span><span class="nx">slug</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">proposalSlug</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">DaoProposalDetail</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span>
|
||
<span class="k">export</span><span class="w"> </span><span class="k">async</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">castVote</span><span class="p">(</span><span class="nx">slug</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">proposalSlug</span><span class="o">:</span><span class="w"> </span><span class="kt">string</span><span class="p">,</span><span class="w"> </span><span class="nx">payload</span><span class="o">:</span><span class="w"> </span><span class="kt">VotePayload</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="nb">Promise</span><span class="o"><</span><span class="nx">VoteRead</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<p>Типи — у цьому ж файлі або в <code>types/dao.ts</code>.</p>
|
||
<h3 id="72">7.2. Сторінки<a class="headerlink" href="#72" title="Permanent link">¶</a></h3>
|
||
<h4 id="dao"><code>/dao</code><a class="headerlink" href="#dao" title="Permanent link">¶</a></h4>
|
||
<p><code>DaoListPage.tsx</code>:</p>
|
||
<ul>
|
||
<li>
|
||
<p>список DAO картками:</p>
|
||
</li>
|
||
<li>
|
||
<p>назва</p>
|
||
</li>
|
||
<li>опис</li>
|
||
<li>governance модель</li>
|
||
<li>кількість учасників, активних пропозицій</li>
|
||
<li>кнопка "Створити DAO" (dialog → createDao)</li>
|
||
</ul>
|
||
<h4 id="daoslug"><code>/dao/:slug</code><a class="headerlink" href="#daoslug" title="Permanent link">¶</a></h4>
|
||
<p><code>DaoDashboardPage.tsx</code>:</p>
|
||
<p>Tabs:</p>
|
||
<ul>
|
||
<li>
|
||
<p>Overview:</p>
|
||
</li>
|
||
<li>
|
||
<p>загальна інформація</p>
|
||
</li>
|
||
<li>короткі stats (members, proposals, treasury)</li>
|
||
<li>
|
||
<p>Proposals:</p>
|
||
</li>
|
||
<li>
|
||
<p>список пропозицій</p>
|
||
</li>
|
||
<li>кнопка "Створити пропозицію"</li>
|
||
<li>статуси, дедлайни</li>
|
||
<li>
|
||
<p>Proposal detail:</p>
|
||
</li>
|
||
<li>
|
||
<p>окрема панель (може бути drawer/side-panel або окрема сторінка)</p>
|
||
</li>
|
||
<li>кнопка Vote (Yes/No/Abstain)</li>
|
||
<li>показ quorum, результатів</li>
|
||
<li>
|
||
<p>Treasury:</p>
|
||
</li>
|
||
<li>
|
||
<p>список токенів, баланси</p>
|
||
</li>
|
||
<li>простий графік</li>
|
||
<li>
|
||
<p>Members:</p>
|
||
</li>
|
||
<li>
|
||
<p>таблиця учасників</p>
|
||
</li>
|
||
<li>роль</li>
|
||
<li>
|
||
<p>Activity:</p>
|
||
</li>
|
||
<li>
|
||
<p>стрічка подій DAO (з <code>dao_audit_log</code> або NATS)</p>
|
||
</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="8-websocket-live-updates-mvp">8. WebSocket / Live Updates (MVP)<a class="headerlink" href="#8-websocket-live-updates-mvp" title="Permanent link">¶</a></h2>
|
||
<p>Опційно для цієї фази (якщо є час):</p>
|
||
<ul>
|
||
<li>у <code>dao-service</code>:</li>
|
||
</ul>
|
||
<div class="codehilite"><pre><span></span><code><span class="nd">@router</span><span class="o">.</span><span class="n">websocket</span><span class="p">(</span><span class="s2">"/ws/dao-events"</span><span class="p">)</span>
|
||
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">dao_events_ws</span><span class="p">(</span><span class="n">ws</span><span class="p">:</span> <span class="n">WebSocket</span><span class="p">):</span>
|
||
<span class="c1"># підписка на NATS dao.event.* і пуш у клієнт</span>
|
||
<span class="o">...</span>
|
||
</code></pre></div>
|
||
|
||
<ul>
|
||
<li>
|
||
<p>на фронті <code>useDaoEvents(slug)</code>:</p>
|
||
</li>
|
||
<li>
|
||
<p>фільтрувати тільки події конкретного DAO;</p>
|
||
</li>
|
||
<li>оновлювати списки proposals/treasury.</li>
|
||
</ul>
|
||
<p>Якщо часу мало — можна залишити це на наступну фазу, але місце під WS варто зарезервувати.</p>
|
||
<hr />
|
||
<h2 id="9-microdao-console">9. Інтеграція з microDAO Console<a class="headerlink" href="#9-microdao-console" title="Permanent link">¶</a></h2>
|
||
<p>У <code>MicrodaoConsolePage.tsx</code> додати:</p>
|
||
<ul>
|
||
<li>
|
||
<p>секцію <code>Governance</code>:</p>
|
||
</li>
|
||
<li>
|
||
<p>якщо для даного microDAO існує DAO → кнопка:</p>
|
||
<ul>
|
||
<li>"Відкрити DAO Dashboard"</li>
|
||
<li><code>navigate('/dao/' + daoSlug)</code></li>
|
||
<li>
|
||
<p>якщо не існує DAO → кнопка:</p>
|
||
</li>
|
||
<li>
|
||
<p>"Створити DAO Governance"</p>
|
||
</li>
|
||
<li>виклик <code>createDao({ microdaoId, slug, name, ... })</code>
|
||
(можна автогенерувати slug з microDAO slug).</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="10-docker-scripts">10. Docker / Scripts<a class="headerlink" href="#10-docker-scripts" title="Permanent link">¶</a></h2>
|
||
<p>Оновити:</p>
|
||
<ul>
|
||
<li>
|
||
<p><code>docker-compose.phase8.yml</code> (або доповнити існуючий compose):</p>
|
||
</li>
|
||
<li>
|
||
<p><code>dao-service</code> (порт, наприклад, 7016)</p>
|
||
</li>
|
||
<li>
|
||
<p><code>scripts/start-phase8.sh</code>:</p>
|
||
</li>
|
||
<li>
|
||
<p>застосування <code>009</code> міграції;</p>
|
||
</li>
|
||
<li>запуск <code>dao-service</code> разом з іншими сервісами.</li>
|
||
</ul>
|
||
<hr />
|
||
<h2 id="11-acceptance-criteria">11. Acceptance Criteria<a class="headerlink" href="#11-acceptance-criteria" title="Permanent link">¶</a></h2>
|
||
<p>Вважати Phase 8 виконаною, якщо:</p>
|
||
<ul>
|
||
<li>[ ] <code>009_create_dao_core.sql</code> застосовується без помилок;</li>
|
||
<li>[ ] Запущено <code>dao-service</code> з <code>/health</code> endpoint;</li>
|
||
<li>[ ] <code>GET /dao</code> повертає DAO, де actor є членом;</li>
|
||
<li>[ ] <code>POST /dao</code> створює DAO, додає owner у members і публікує <code>dao.event.created</code>;</li>
|
||
<li>[ ] <code>GET /dao/{slug}</code> повертає overview DAO (включно з members_count, active_proposals_count, treasury_items);</li>
|
||
<li>[ ] <code>POST /dao/{slug}/proposals</code> створює пропозицію, <code>dao.event.proposal_created</code> публікується;</li>
|
||
<li>[ ] <code>POST /dao/{slug}/proposals/{proposalSlug}/votes</code> створює/оновлює голос;</li>
|
||
<li>[ ] у фронтенді <code>/dao</code> показує реальні DAO з БД;</li>
|
||
<li>[ ] у фронтенді <code>/dao/:slug</code> показує Overview/Proposals/Treasury/Members з реальних endpoint'ів (без mock);</li>
|
||
<li>[ ] PDP блокує доступ до DAO, де actor не є членом (403).</li>
|
||
</ul>
|
||
<p>END OF TASK</p>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</article>
|
||
</div>
|
||
|
||
|
||
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
|
||
</div>
|
||
|
||
</main>
|
||
|
||
<footer class="md-footer">
|
||
|
||
<div class="md-footer-meta md-typeset">
|
||
<div class="md-footer-meta__inner md-grid">
|
||
<div class="md-copyright">
|
||
|
||
|
||
Made with
|
||
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
|
||
Material for MkDocs
|
||
</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
</div>
|
||
<div class="md-dialog" data-md-component="dialog">
|
||
<div class="md-dialog__inner md-typeset"></div>
|
||
</div>
|
||
|
||
|
||
<script id="__config" type="application/json">{"base": "../..", "features": ["navigation.sections", "navigation.instant", "content.code.copy"], "search": "../../assets/javascripts/workers/search.b8dbb3d2.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script>
|
||
|
||
|
||
<script src="../../assets/javascripts/bundle.3220b9d7.min.js"></script>
|
||
|
||
|
||
</body>
|
||
</html> |