1pub mod auth;
7pub mod error;
8pub mod routes;
9pub mod state;
10pub mod ws;
11
12use std::sync::Arc;
13
14use axum::middleware;
15use axum::routing::{delete, get, patch, post};
16use axum::Router;
17use tower_http::cors::CorsLayer;
18use tower_http::trace::TraceLayer;
19
20use crate::state::AppState;
21
22pub fn build_router(state: Arc<AppState>) -> Router {
24 let api = Router::new()
25 .route("/health", get(routes::health::health))
26 .route("/analytics/summary", get(routes::analytics::summary))
28 .route("/analytics/followers", get(routes::analytics::followers))
29 .route(
30 "/analytics/performance",
31 get(routes::analytics::performance),
32 )
33 .route("/analytics/topics", get(routes::analytics::topics))
34 .route(
35 "/analytics/recent-performance",
36 get(routes::analytics::recent_performance),
37 )
38 .route("/approval", get(routes::approval::list_items))
40 .route("/approval/stats", get(routes::approval::stats))
41 .route("/approval/approve-all", post(routes::approval::approve_all))
42 .route("/approval/{id}", patch(routes::approval::edit_item))
43 .route(
44 "/approval/{id}/approve",
45 post(routes::approval::approve_item),
46 )
47 .route("/approval/{id}/reject", post(routes::approval::reject_item))
48 .route("/activity", get(routes::activity::list_activity))
50 .route(
51 "/activity/rate-limits",
52 get(routes::activity::rate_limit_usage),
53 )
54 .route("/replies", get(routes::replies::list_replies))
56 .route(
58 "/content/tweets",
59 get(routes::content::list_tweets).post(routes::content::compose_tweet),
60 )
61 .route(
62 "/content/threads",
63 get(routes::content::list_threads).post(routes::content::compose_thread),
64 )
65 .route("/content/calendar", get(routes::content::calendar))
66 .route("/content/schedule", get(routes::content::schedule))
67 .route("/content/compose", post(routes::content::compose))
68 .route(
69 "/content/scheduled/{id}",
70 patch(routes::content::edit_scheduled).delete(routes::content::cancel_scheduled),
71 )
72 .route(
74 "/content/drafts",
75 get(routes::content::list_drafts).post(routes::content::create_draft),
76 )
77 .route(
78 "/content/drafts/{id}",
79 patch(routes::content::edit_draft).delete(routes::content::delete_draft),
80 )
81 .route(
82 "/content/drafts/{id}/schedule",
83 post(routes::content::schedule_draft),
84 )
85 .route(
86 "/content/drafts/{id}/publish",
87 post(routes::content::publish_draft),
88 )
89 .route(
91 "/targets",
92 get(routes::targets::list_targets).post(routes::targets::add_target),
93 )
94 .route(
95 "/targets/{username}/timeline",
96 get(routes::targets::target_timeline),
97 )
98 .route(
99 "/targets/{username}/stats",
100 get(routes::targets::target_stats),
101 )
102 .route(
103 "/targets/{username}",
104 delete(routes::targets::remove_target),
105 )
106 .route("/strategy/current", get(routes::strategy::current))
108 .route("/strategy/history", get(routes::strategy::history))
109 .route("/strategy/refresh", post(routes::strategy::refresh))
110 .route("/strategy/inputs", get(routes::strategy::inputs))
111 .route("/costs/summary", get(routes::costs::summary))
113 .route("/costs/daily", get(routes::costs::daily))
114 .route("/costs/by-model", get(routes::costs::by_model))
115 .route("/costs/by-type", get(routes::costs::by_type))
116 .route("/costs/x-api/summary", get(routes::costs::x_api_summary))
118 .route("/costs/x-api/daily", get(routes::costs::x_api_daily))
119 .route(
120 "/costs/x-api/by-endpoint",
121 get(routes::costs::x_api_by_endpoint),
122 )
123 .route("/assist/tweet", post(routes::assist::assist_tweet))
125 .route("/assist/reply", post(routes::assist::assist_reply))
126 .route("/assist/thread", post(routes::assist::assist_thread))
127 .route("/assist/improve", post(routes::assist::assist_improve))
128 .route("/assist/topics", get(routes::assist::assist_topics))
129 .route(
130 "/assist/optimal-times",
131 get(routes::assist::assist_optimal_times),
132 )
133 .route("/assist/mode", get(routes::assist::get_mode))
134 .route("/discovery/feed", get(routes::discovery::feed))
136 .route(
137 "/discovery/{tweet_id}/compose-reply",
138 post(routes::discovery::compose_reply),
139 )
140 .route(
141 "/discovery/{tweet_id}/queue-reply",
142 post(routes::discovery::queue_reply),
143 )
144 .route("/media/upload", post(routes::media::upload))
146 .route("/media/file", get(routes::media::serve_file))
147 .route("/settings/status", get(routes::settings::config_status))
149 .route("/settings/init", post(routes::settings::init_settings))
150 .route(
151 "/settings/validate",
152 post(routes::settings::validate_settings),
153 )
154 .route("/settings/defaults", get(routes::settings::get_defaults))
155 .route("/settings/test-llm", post(routes::settings::test_llm))
156 .route(
157 "/settings",
158 get(routes::settings::get_settings).patch(routes::settings::patch_settings),
159 )
160 .route(
162 "/mcp/policy",
163 get(routes::mcp::get_policy).patch(routes::mcp::patch_policy),
164 )
165 .route(
166 "/mcp/telemetry/summary",
167 get(routes::mcp::telemetry_summary),
168 )
169 .route(
170 "/mcp/telemetry/metrics",
171 get(routes::mcp::telemetry_metrics),
172 )
173 .route("/mcp/telemetry/errors", get(routes::mcp::telemetry_errors))
174 .route("/mcp/telemetry/recent", get(routes::mcp::telemetry_recent))
175 .route("/runtime/status", get(routes::runtime::status))
177 .route("/runtime/start", post(routes::runtime::start))
178 .route("/runtime/stop", post(routes::runtime::stop))
179 .route("/ws", get(ws::ws_handler))
181 .layer(middleware::from_fn_with_state(
183 state.clone(),
184 auth::auth_middleware,
185 ));
186
187 Router::new()
188 .nest("/api", api)
189 .layer(CorsLayer::permissive())
190 .layer(TraceLayer::new_for_http())
191 .with_state(state)
192}