1use std::net::SocketAddr;
3use std::time::Duration;
4
5use axum::Router;
6use axum::extract::DefaultBodyLimit;
7use axum::http::StatusCode;
8use axum::middleware as axum_mw;
9use axum::routing::{delete, get, post};
10use tower_http::cors::CorsLayer;
11use tower_http::timeout::TimeoutLayer;
12use tracing::info;
13
14use crate::middleware::auth::auth_middleware;
15use crate::middleware::pretty::pretty_json_middleware;
16use crate::routes::{
17 attachment, chat, facetime, general, handle, icloud, message, server, webhook,
18};
19use crate::state::AppState;
20
21pub fn build_router(state: AppState) -> Router {
23 let protected = Router::new()
25 .route("/api/v1/ping", get(general::ping))
27 .route("/api/v1/server/info", get(server::get_info))
29 .route("/api/v1/server/logs", get(server::get_logs))
30 .route("/api/v1/server/permissions", get(server::get_permissions))
31 .route(
32 "/api/v1/server/statistics/totals",
33 get(server::get_stat_totals),
34 )
35 .route(
36 "/api/v1/server/statistics/media",
37 get(server::get_stat_media),
38 )
39 .route(
40 "/api/v1/server/statistics/media/chat",
41 get(server::get_stat_media_by_chat),
42 )
43 .route("/api/v1/webhook", get(webhook::get_webhooks))
45 .route("/api/v1/handle/count", get(handle::count))
47 .route("/api/v1/handle/query", post(handle::query))
48 .route("/api/v1/handle/{guid}", get(handle::find))
49 .route("/api/v1/handle/{guid}/focus", get(handle::get_focus_status))
50 .route(
51 "/api/v1/handle/availability/imessage",
52 get(handle::get_imessage_availability).post(handle::post_imessage_availability),
53 )
54 .route(
55 "/api/v1/handle/availability/facetime",
56 get(handle::get_facetime_availability).post(handle::post_facetime_availability),
57 )
58 .route("/api/v1/chat/count", get(chat::count))
60 .route("/api/v1/chat/query", post(chat::query))
61 .route("/api/v1/chat/new", post(chat::create))
62 .route("/api/v1/chat/{guid}/message", get(chat::get_messages))
63 .route(
64 "/api/v1/chat/{guid}",
65 get(chat::find).put(chat::update).delete(chat::delete_chat),
66 )
67 .route("/api/v1/chat/{guid}/read", post(chat::mark_read))
68 .route("/api/v1/chat/{guid}/unread", post(chat::mark_unread))
69 .route("/api/v1/chat/{guid}/leave", post(chat::leave))
70 .route(
71 "/api/v1/chat/{guid}/typing",
72 post(chat::start_typing).delete(chat::stop_typing),
73 )
74 .route(
75 "/api/v1/chat/{guid}/participant/add",
76 post(chat::add_participant),
77 )
78 .route(
79 "/api/v1/chat/{guid}/participant/remove",
80 post(chat::remove_participant),
81 )
82 .route(
83 "/api/v1/chat/{guid}/participant",
84 post(chat::add_participant).delete(chat::remove_participant_delete),
85 )
86 .route(
87 "/api/v1/chat/{guid}/icon",
88 get(chat::get_icon)
89 .post(chat::set_icon)
90 .delete(chat::remove_icon),
91 )
92 .route(
93 "/api/v1/chat/{guid}/share/contact/status",
94 get(chat::share_contact_status),
95 )
96 .route(
97 "/api/v1/chat/{guid}/share/contact",
98 post(chat::share_contact),
99 )
100 .route(
101 "/api/v1/chat/{guid}/{messageGuid}",
102 delete(chat::delete_message),
103 )
104 .route("/api/v1/message/count", get(message::count))
106 .route("/api/v1/message/count/updated", get(message::count_updated))
107 .route("/api/v1/message/count/me", get(message::sent_count))
108 .route("/api/v1/message/query", post(message::query))
109 .route("/api/v1/message/text", post(message::send_text))
110 .route("/api/v1/message/attachment", post(message::send_attachment))
111 .route(
112 "/api/v1/message/attachment/chunk",
113 post(message::send_attachment_chunk),
114 )
115 .route("/api/v1/message/multipart", post(message::send_multipart))
116 .route("/api/v1/message/react", post(message::send_reaction))
117 .route("/api/v1/message/{guid}/edit", post(message::edit_message))
118 .route(
119 "/api/v1/message/{guid}/unsend",
120 post(message::unsend_message),
121 )
122 .route(
123 "/api/v1/message/{guid}/notify",
124 post(message::notify_message),
125 )
126 .route(
127 "/api/v1/message/{guid}/embedded-media",
128 get(message::get_embedded_media),
129 )
130 .route("/api/v1/message/{guid}", get(message::find))
131 .route("/api/v1/attachment/count", get(attachment::count))
133 .route("/api/v1/attachment/upload", post(attachment::upload))
134 .route(
135 "/api/v1/attachment/{guid}/download/force",
136 get(attachment::force_download),
137 )
138 .route(
139 "/api/v1/attachment/{guid}/download",
140 get(attachment::download),
141 )
142 .route(
143 "/api/v1/attachment/{guid}/live",
144 get(attachment::download_live),
145 )
146 .route(
147 "/api/v1/attachment/{guid}/blurhash",
148 get(attachment::blurhash),
149 )
150 .route("/api/v1/attachment/{guid}", get(attachment::find))
151 .route("/api/v1/icloud/account", get(icloud::get_account_info))
153 .route("/api/v1/icloud/account/alias", post(icloud::change_alias))
154 .route("/api/v1/icloud/contact", get(icloud::get_contact_card))
155 .route(
156 "/api/v1/icloud/findmy/devices",
157 get(icloud::get_findmy_devices),
158 )
159 .route(
160 "/api/v1/icloud/findmy/devices/refresh",
161 post(icloud::refresh_findmy_devices),
162 )
163 .route(
164 "/api/v1/icloud/findmy/friends",
165 get(icloud::get_findmy_friends),
166 )
167 .route(
168 "/api/v1/icloud/findmy/friends/refresh",
169 post(icloud::refresh_findmy_friends),
170 )
171 .route("/api/v1/facetime/session", post(facetime::create_session))
173 .route(
174 "/api/v1/facetime/answer/{call_uuid}",
175 post(facetime::answer_call),
176 )
177 .route(
178 "/api/v1/facetime/leave/{call_uuid}",
179 post(facetime::leave_call),
180 )
181 .layer(axum_mw::from_fn_with_state(state.clone(), auth_middleware))
183 .with_state(state.clone());
184
185 Router::new()
187 .merge(protected)
188 .layer(axum_mw::from_fn(pretty_json_middleware))
190 .layer(CorsLayer::permissive())
192 .layer(TimeoutLayer::with_status_code(
194 StatusCode::REQUEST_TIMEOUT,
195 Duration::from_secs(300),
196 ))
197 .layer(DefaultBodyLimit::max(1024 * 1024 * 1024))
199 .fallback(fallback_handler)
201}
202
203async fn fallback_handler() -> (axum::http::StatusCode, &'static str) {
205 (axum::http::StatusCode::NOT_FOUND, "Not Found")
206}
207
208pub async fn start_server(state: AppState) -> anyhow::Result<()> {
210 let port = state.config.socket_port;
211 let app = build_router(state);
212
213 let addr = SocketAddr::from(([127, 0, 0, 1], port));
214 let listener = tokio::net::TcpListener::bind(addr).await?;
215 info!("Successfully started HTTP server on {addr}");
216 axum::serve(listener, app).await?;
217
218 Ok(())
219}