- matrix-gateway: POST /internal/matrix/presence/online endpoint - usePresenceHeartbeat hook with activity tracking - Auto away after 5 min inactivity - Offline on page close/visibility change - Integrated in MatrixChatRoom component
290 lines
12 KiB
PL/PgSQL
290 lines
12 KiB
PL/PgSQL
-- ============================================================================
|
||
-- Migration 009: DAO Core Tables
|
||
-- Phase 8: DAO Dashboard (Governance + Treasury + Voting)
|
||
-- ============================================================================
|
||
|
||
-- ============================================================================
|
||
-- Table: dao
|
||
-- Purpose: DAO entities with governance configuration
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS dao (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
slug TEXT NOT NULL UNIQUE,
|
||
name TEXT NOT NULL,
|
||
description TEXT,
|
||
microdao_id UUID NOT NULL REFERENCES microdaos(id) ON DELETE CASCADE,
|
||
owner_user_id UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
|
||
governance_model TEXT NOT NULL DEFAULT 'simple', -- 'simple' | 'quadratic' | 'delegated'
|
||
voting_period_seconds INTEGER NOT NULL DEFAULT 604800, -- 7 days
|
||
quorum_percent INTEGER NOT NULL DEFAULT 20, -- 20%
|
||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
);
|
||
|
||
CREATE INDEX idx_dao_microdao_id ON dao(microdao_id);
|
||
CREATE INDEX idx_dao_owner_user_id ON dao(owner_user_id);
|
||
CREATE INDEX idx_dao_is_active ON dao(is_active);
|
||
|
||
COMMENT ON TABLE dao IS 'DAO entities with governance configuration';
|
||
COMMENT ON COLUMN dao.governance_model IS 'simple, quadratic, or delegated voting';
|
||
COMMENT ON COLUMN dao.voting_period_seconds IS 'Default voting period for proposals';
|
||
COMMENT ON COLUMN dao.quorum_percent IS 'Minimum participation percentage for valid vote';
|
||
|
||
-- ============================================================================
|
||
-- Table: dao_members
|
||
-- Purpose: DAO membership with roles
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS dao_members (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
dao_id UUID NOT NULL REFERENCES dao(id) ON DELETE CASCADE,
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
role TEXT NOT NULL, -- 'owner' | 'admin' | 'member' | 'guest'
|
||
joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
UNIQUE(dao_id, user_id)
|
||
);
|
||
|
||
CREATE INDEX idx_dao_members_user_id ON dao_members(user_id);
|
||
CREATE INDEX idx_dao_members_dao_id ON dao_members(dao_id);
|
||
CREATE INDEX idx_dao_members_dao_id_role ON dao_members(dao_id, role);
|
||
|
||
COMMENT ON TABLE dao_members IS 'DAO membership with roles';
|
||
COMMENT ON COLUMN dao_members.role IS 'owner, admin, member, guest';
|
||
|
||
-- ============================================================================
|
||
-- Table: dao_treasury
|
||
-- Purpose: Token balances for DAO treasury
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS dao_treasury (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
dao_id UUID NOT NULL REFERENCES dao(id) ON DELETE CASCADE,
|
||
token_symbol TEXT NOT NULL,
|
||
contract_address TEXT,
|
||
balance NUMERIC(30, 8) NOT NULL DEFAULT 0,
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
UNIQUE(dao_id, token_symbol)
|
||
);
|
||
|
||
CREATE INDEX idx_dao_treasury_dao_id ON dao_treasury(dao_id);
|
||
|
||
COMMENT ON TABLE dao_treasury IS 'Token balances for DAO treasury';
|
||
COMMENT ON COLUMN dao_treasury.balance IS 'Token balance with 8 decimal precision';
|
||
COMMENT ON COLUMN dao_treasury.contract_address IS 'Optional smart contract address';
|
||
|
||
-- ============================================================================
|
||
-- Table: dao_proposals
|
||
-- Purpose: Governance proposals for voting
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS dao_proposals (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
dao_id UUID NOT NULL REFERENCES dao(id) ON DELETE CASCADE,
|
||
slug TEXT NOT NULL,
|
||
title TEXT NOT NULL,
|
||
description TEXT,
|
||
created_by_user_id UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
start_at TIMESTAMPTZ,
|
||
end_at TIMESTAMPTZ,
|
||
status TEXT NOT NULL DEFAULT 'draft', -- 'draft' | 'active' | 'passed' | 'rejected' | 'executed'
|
||
governance_model_override TEXT,
|
||
quorum_percent_override INTEGER,
|
||
UNIQUE(dao_id, slug)
|
||
);
|
||
|
||
CREATE INDEX idx_dao_proposals_dao_id ON dao_proposals(dao_id);
|
||
CREATE INDEX idx_dao_proposals_status ON dao_proposals(status);
|
||
CREATE INDEX idx_dao_proposals_created_by ON dao_proposals(created_by_user_id);
|
||
|
||
COMMENT ON TABLE dao_proposals IS 'Governance proposals for voting';
|
||
COMMENT ON COLUMN dao_proposals.status IS 'draft, active, passed, rejected, executed';
|
||
COMMENT ON COLUMN dao_proposals.governance_model_override IS 'Override DAO default governance model';
|
||
|
||
-- ============================================================================
|
||
-- Table: dao_votes
|
||
-- Purpose: Individual votes on proposals
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS dao_votes (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
proposal_id UUID NOT NULL REFERENCES dao_proposals(id) ON DELETE CASCADE,
|
||
voter_user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
vote_value TEXT NOT NULL, -- 'yes' | 'no' | 'abstain'
|
||
weight NUMERIC(30, 8) NOT NULL, -- actual weight after applying governance model
|
||
raw_power NUMERIC(30, 8), -- raw voting power before governance model
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
UNIQUE(proposal_id, voter_user_id)
|
||
);
|
||
|
||
CREATE INDEX idx_dao_votes_proposal_id ON dao_votes(proposal_id);
|
||
CREATE INDEX idx_dao_votes_voter_user_id ON dao_votes(voter_user_id);
|
||
|
||
COMMENT ON TABLE dao_votes IS 'Individual votes on proposals';
|
||
COMMENT ON COLUMN dao_votes.vote_value IS 'yes, no, abstain';
|
||
COMMENT ON COLUMN dao_votes.weight IS 'Calculated weight after governance model';
|
||
COMMENT ON COLUMN dao_votes.raw_power IS 'Raw voting power before calculation';
|
||
|
||
-- ============================================================================
|
||
-- Table: dao_roles
|
||
-- Purpose: Custom roles for DAO
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS dao_roles (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
dao_id UUID NOT NULL REFERENCES dao(id) ON DELETE CASCADE,
|
||
code TEXT NOT NULL,
|
||
name TEXT NOT NULL,
|
||
description TEXT,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
UNIQUE(dao_id, code)
|
||
);
|
||
|
||
CREATE INDEX idx_dao_roles_dao_id ON dao_roles(dao_id);
|
||
|
||
COMMENT ON TABLE dao_roles IS 'Custom roles for DAO';
|
||
COMMENT ON COLUMN dao_roles.code IS 'Unique role code (e.g., treasury_manager)';
|
||
|
||
-- ============================================================================
|
||
-- Table: dao_role_assignments
|
||
-- Purpose: Custom role assignments to users
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS dao_role_assignments (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
dao_id UUID NOT NULL REFERENCES dao(id) ON DELETE CASCADE,
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||
role_code TEXT NOT NULL,
|
||
assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
UNIQUE(dao_id, user_id, role_code)
|
||
);
|
||
|
||
CREATE INDEX idx_dao_role_assignments_user_id ON dao_role_assignments(user_id);
|
||
CREATE INDEX idx_dao_role_assignments_dao_id ON dao_role_assignments(dao_id);
|
||
|
||
COMMENT ON TABLE dao_role_assignments IS 'Custom role assignments to users';
|
||
|
||
-- ============================================================================
|
||
-- Table: dao_audit_log
|
||
-- Purpose: Audit log for all DAO actions
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS dao_audit_log (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
dao_id UUID NOT NULL REFERENCES dao(id) ON DELETE CASCADE,
|
||
actor_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||
event_type TEXT NOT NULL,
|
||
event_payload JSONB,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
);
|
||
|
||
CREATE INDEX idx_dao_audit_log_dao_id ON dao_audit_log(dao_id);
|
||
CREATE INDEX idx_dao_audit_log_created_at ON dao_audit_log(created_at DESC);
|
||
CREATE INDEX idx_dao_audit_log_event_type ON dao_audit_log(event_type);
|
||
|
||
COMMENT ON TABLE dao_audit_log IS 'Audit log for all DAO actions';
|
||
COMMENT ON COLUMN dao_audit_log.event_type IS 'Type of event (created, updated, voted, etc.)';
|
||
|
||
-- ============================================================================
|
||
-- Update Trigger: dao.updated_at
|
||
-- ============================================================================
|
||
|
||
CREATE OR REPLACE FUNCTION update_dao_updated_at()
|
||
RETURNS TRIGGER AS $$
|
||
BEGIN
|
||
NEW.updated_at = NOW();
|
||
RETURN NEW;
|
||
END;
|
||
$$ LANGUAGE plpgsql;
|
||
|
||
CREATE TRIGGER trigger_update_dao_updated_at
|
||
BEFORE UPDATE ON dao
|
||
FOR EACH ROW
|
||
EXECUTE FUNCTION update_dao_updated_at();
|
||
|
||
-- ============================================================================
|
||
-- Seed Data: Sample DAO for DAARION microDAO
|
||
-- ============================================================================
|
||
|
||
-- Create DAO for DAARION microDAO
|
||
INSERT INTO dao (slug, name, description, microdao_id, owner_user_id, governance_model, quorum_percent)
|
||
SELECT
|
||
'daarion-governance',
|
||
'DAARION Governance',
|
||
'Децентралізоване управління екосистемою DAARION',
|
||
m.id,
|
||
m.owner_user_id,
|
||
'simple',
|
||
20
|
||
FROM microdaos m
|
||
WHERE m.external_id = 'microdao:daarion'
|
||
LIMIT 1
|
||
ON CONFLICT (slug) DO NOTHING;
|
||
|
||
-- Add owner as DAO member
|
||
INSERT INTO dao_members (dao_id, user_id, role)
|
||
SELECT
|
||
d.id,
|
||
d.owner_user_id,
|
||
'owner'
|
||
FROM dao d
|
||
WHERE d.slug = 'daarion-governance'
|
||
ON CONFLICT (dao_id, user_id) DO NOTHING;
|
||
|
||
-- Initialize DAO treasury with DAARION token
|
||
INSERT INTO dao_treasury (dao_id, token_symbol, balance)
|
||
SELECT
|
||
d.id,
|
||
'DAARION',
|
||
1000000.0
|
||
FROM dao d
|
||
WHERE d.slug = 'daarion-governance'
|
||
ON CONFLICT (dao_id, token_symbol) DO NOTHING;
|
||
|
||
-- Create sample proposal
|
||
INSERT INTO dao_proposals (dao_id, slug, title, description, created_by_user_id, status, start_at, end_at)
|
||
SELECT
|
||
d.id,
|
||
'proposal-1-funding',
|
||
'Фінансування розвитку Agent Hub',
|
||
'Пропозиція виділити 10,000 DAARION токенів на розвиток Agent Hub у Q1 2025',
|
||
d.owner_user_id,
|
||
'active',
|
||
NOW(),
|
||
NOW() + INTERVAL '7 days'
|
||
FROM dao d
|
||
WHERE d.slug = 'daarion-governance'
|
||
ON CONFLICT (dao_id, slug) DO NOTHING;
|
||
|
||
-- ============================================================================
|
||
-- Grants
|
||
-- ============================================================================
|
||
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON dao TO postgres;
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON dao_members TO postgres;
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON dao_treasury TO postgres;
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON dao_proposals TO postgres;
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON dao_votes TO postgres;
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON dao_roles TO postgres;
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON dao_role_assignments TO postgres;
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON dao_audit_log TO postgres;
|
||
|
||
-- ============================================================================
|
||
-- Migration Complete
|
||
-- ============================================================================
|
||
|
||
DO $$
|
||
BEGIN
|
||
IF EXISTS (
|
||
SELECT 1 FROM information_schema.tables
|
||
WHERE table_name IN ('dao', 'dao_members', 'dao_treasury', 'dao_proposals', 'dao_votes')
|
||
) THEN
|
||
RAISE NOTICE 'Migration 009: DAO Core Tables created successfully';
|
||
ELSE
|
||
RAISE EXCEPTION 'Migration 009: Failed to create tables';
|
||
END IF;
|
||
END $$;
|
||
|