Skip to main content

convergio_reports/
ext.rs

1//! Extension trait implementation for convergio-reports.
2
3use std::sync::Arc;
4
5use convergio_db::pool::ConnPool;
6use convergio_types::extension::{AppContext, Extension, Health, McpToolDef, Metric, Migration};
7use convergio_types::manifest::{Capability, Manifest, ModuleKind};
8
9use crate::routes::{reports_routes, ReportsState};
10
11pub struct ReportsExtension {
12    pool: ConnPool,
13}
14
15impl ReportsExtension {
16    pub fn new(pool: ConnPool) -> Self {
17        Self { pool }
18    }
19
20    fn state(&self) -> Arc<ReportsState> {
21        Arc::new(ReportsState {
22            pool: self.pool.clone(),
23        })
24    }
25}
26
27impl Extension for ReportsExtension {
28    fn manifest(&self) -> Manifest {
29        Manifest {
30            id: "convergio-reports".to_string(),
31            description: "Convergio Think Tank — professional report generation service"
32                .to_string(),
33            version: env!("CARGO_PKG_VERSION").to_string(),
34            kind: ModuleKind::Extension,
35            provides: vec![
36                Capability {
37                    name: "reports".to_string(),
38                    version: "1.0.0".to_string(),
39                    description: "CTT research report generation".to_string(),
40                },
41                Capability {
42                    name: "reports-api".to_string(),
43                    version: "1.0.0".to_string(),
44                    description: "REST API for report CRUD and generation".to_string(),
45                },
46            ],
47            requires: vec![],
48            agent_tools: vec![],
49            required_roles: vec!["orchestrator".into(), "all".into()],
50        }
51    }
52
53    fn routes(&self, _ctx: &AppContext) -> Option<axum::Router> {
54        Some(reports_routes(self.state()))
55    }
56
57    fn migrations(&self) -> Vec<Migration> {
58        vec![Migration {
59            version: 1,
60            description: "reports table",
61            up: "CREATE TABLE IF NOT EXISTS reports (\
62                    id TEXT PRIMARY KEY,\
63                    org_id TEXT NOT NULL DEFAULT 'convergio.io',\
64                    topic TEXT NOT NULL,\
65                    report_type TEXT NOT NULL,\
66                    format TEXT NOT NULL DEFAULT 'markdown',\
67                    status TEXT NOT NULL DEFAULT 'pending',\
68                    audience TEXT,\
69                    depth TEXT NOT NULL DEFAULT 'standard',\
70                    content_md TEXT,\
71                    pdf_path TEXT,\
72                    sources_json TEXT,\
73                    metadata_json TEXT,\
74                    extra_context TEXT,\
75                    error TEXT,\
76                    created_at TEXT NOT NULL DEFAULT (datetime('now')),\
77                    completed_at TEXT\
78                );\
79                CREATE INDEX IF NOT EXISTS idx_reports_org \
80                    ON reports(org_id);\
81                CREATE INDEX IF NOT EXISTS idx_reports_status \
82                    ON reports(status);\
83                CREATE INDEX IF NOT EXISTS idx_reports_type \
84                    ON reports(report_type);",
85        }]
86    }
87
88    fn health(&self) -> Health {
89        match self.pool.get() {
90            Ok(_) => Health::Ok,
91            Err(e) => Health::Degraded {
92                reason: format!("db: {e}"),
93            },
94        }
95    }
96
97    fn metrics(&self) -> Vec<Metric> {
98        let count: f64 = self
99            .pool
100            .get()
101            .ok()
102            .and_then(|c| {
103                c.query_row("SELECT COUNT(*) FROM reports", [], |r| r.get::<_, i64>(0))
104                    .ok()
105            })
106            .unwrap_or(0) as f64;
107        vec![Metric {
108            name: "reports_total".to_string(),
109            value: count,
110            labels: vec![],
111        }]
112    }
113
114    fn mcp_tools(&self) -> Vec<McpToolDef> {
115        crate::mcp_defs::reports_tools()
116    }
117}