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

134 lines
5.0 KiB
PL/PgSQL

-- ============================================================================
-- Migration 010: Living Map Tables
-- Phase 9: Living Map (Full Stack Service)
-- ============================================================================
-- ============================================================================
-- Table: living_map_history
-- Purpose: Event log for all Living Map events
-- ============================================================================
CREATE TABLE IF NOT EXISTS living_map_history (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
event_type TEXT NOT NULL,
payload JSONB NOT NULL,
source_service TEXT,
entity_id TEXT,
entity_type TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Indexes for efficient querying
CREATE INDEX IF NOT EXISTS idx_living_map_history_timestamp
ON living_map_history (timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_living_map_history_event_type
ON living_map_history (event_type);
CREATE INDEX IF NOT EXISTS idx_living_map_history_entity_id
ON living_map_history (entity_id);
CREATE INDEX IF NOT EXISTS idx_living_map_history_entity_type
ON living_map_history (entity_type);
CREATE INDEX IF NOT EXISTS idx_living_map_history_source_service
ON living_map_history (source_service);
-- Composite index for common queries
CREATE INDEX IF NOT EXISTS idx_living_map_history_type_timestamp
ON living_map_history (event_type, timestamp DESC);
COMMENT ON TABLE living_map_history IS 'Event log for Living Map - tracks all network state changes';
COMMENT ON COLUMN living_map_history.event_type IS 'Type of event (e.g., node.metrics.update, agent.status.change)';
COMMENT ON COLUMN living_map_history.payload IS 'Full event data in JSON format';
COMMENT ON COLUMN living_map_history.source_service IS 'Service that generated the event';
COMMENT ON COLUMN living_map_history.entity_id IS 'ID of the entity (for filtering)';
COMMENT ON COLUMN living_map_history.entity_type IS 'Type: city|space|node|agent|dao|microdao';
-- ============================================================================
-- Table: living_map_snapshots (optional, for caching)
-- Purpose: Store periodic full snapshots for fast recovery
-- ============================================================================
CREATE TABLE IF NOT EXISTS living_map_snapshots (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
generated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
snapshot_data JSONB NOT NULL,
version TEXT NOT NULL DEFAULT '1.0',
compressed BOOLEAN DEFAULT FALSE,
size_bytes INTEGER,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_living_map_snapshots_generated_at
ON living_map_snapshots (generated_at DESC);
COMMENT ON TABLE living_map_snapshots IS 'Periodic full state snapshots for fast recovery';
COMMENT ON COLUMN living_map_snapshots.snapshot_data IS 'Complete Living Map state in JSON';
-- ============================================================================
-- Function: Clean old history (retention policy)
-- ============================================================================
CREATE OR REPLACE FUNCTION cleanup_living_map_history()
RETURNS INTEGER AS $$
DECLARE
deleted_count INTEGER;
BEGIN
-- Delete events older than 30 days
DELETE FROM living_map_history
WHERE timestamp < NOW() - INTERVAL '30 days';
GET DIAGNOSTICS deleted_count = ROW_COUNT;
-- Delete old snapshots (keep last 100)
DELETE FROM living_map_snapshots
WHERE id NOT IN (
SELECT id FROM living_map_snapshots
ORDER BY generated_at DESC
LIMIT 100
);
RETURN deleted_count;
END;
$$ LANGUAGE plpgsql;
COMMENT ON FUNCTION cleanup_living_map_history IS 'Cleanup old history and snapshots';
-- ============================================================================
-- Grants
-- ============================================================================
GRANT SELECT, INSERT, DELETE ON living_map_history TO postgres;
GRANT SELECT, INSERT, DELETE ON living_map_snapshots TO postgres;
-- ============================================================================
-- Seed Data: Initial event
-- ============================================================================
INSERT INTO living_map_history (event_type, payload, source_service, entity_type)
VALUES (
'living_map.initialized',
'{"message": "Living Map service initialized", "version": "1.0"}'::jsonb,
'living-map-service',
'system'
) ON CONFLICT DO NOTHING;
-- ============================================================================
-- Migration Complete
-- ============================================================================
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_name = 'living_map_history'
) THEN
RAISE NOTICE 'Migration 010: Living Map Tables created successfully';
ELSE
RAISE EXCEPTION 'Migration 010: Failed to create tables';
END IF;
END $$;