Self-hosted, federated location sharing app and server that prioritizes user privacy and security
end-to-end-encryption
location-sharing
privacy
self-hosted
federated
1use std::{collections::HashMap, sync::Arc};
2
3use axum::{Router, routing::post};
4use nanoid::nanoid;
5use std::collections::HashSet;
6use tokio::sync::Mutex;
7use tower_http::cors::CorsLayer;
8use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
9
10mod auth;
11mod handlers;
12mod log;
13mod types;
14
15use handlers::*;
16use types::*;
17
18use crate::auth::auth_test;
19use crate::log::log_req_res_bodies;
20
21#[tokio::main]
22async fn main() {
23 tracing_subscriber::registry()
24 .with(
25 tracing_subscriber::EnvFilter::try_from_default_env()
26 .unwrap_or_else(|_| "info,tower_http=info,axum=info".into()),
27 )
28 .with(tracing_subscriber::fmt::layer())
29 .init();
30
31 // TODO: should this be inside an Arc?
32 let state = AppState {
33 users: Arc::new(Mutex::new(Vec::new())),
34 signup_keys: Arc::new(Mutex::new(HashSet::new())),
35 friend_requests: Arc::new(Mutex::new(HashSet::new())),
36 links: Arc::new(Mutex::new(HashSet::new())),
37 positions: Arc::new(Mutex::new(HashMap::new())),
38 admin_id: Arc::new(Mutex::new(None)),
39 ring_buffer_cap: 5,
40 };
41
42 // Until we have disk saves, always generate a admin signup key since there will be no admin set at launch
43 let admin_signup_key = nanoid!(5);
44 println!("Admin signup key: {admin_signup_key}");
45 println!("http://127.0.0.1:3000");
46 state.signup_keys.lock().await.insert(admin_signup_key);
47
48 // build our application with a route
49 let app = Router::new()
50 .route("/", post(|| async { "You just sent a POST to /" })) // for testing
51 .route("/create-account", post(create_user))
52 .route("/generate-signup-key", post(generate_signup_key))
53 .route("/request-friend-request", post(request_friend_request))
54 .route(
55 "/is-friend-request-accepted",
56 post(is_friend_request_accepted),
57 )
58 .route("/send-pings", post(send_pings))
59 .route("/get-pings", post(get_pings))
60 .with_state(state.clone())
61 .layer(axum::middleware::from_fn_with_state(state, auth_test))
62 .layer(axum::middleware::from_fn(log_req_res_bodies))
63 .layer(CorsLayer::permissive());
64
65 let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
66 axum::serve(listener, app).await.unwrap();
67}
68
69// TODO: potential security risk of returning error messages directly to the user. nice for debugging tho :p