mod broadcast;
mod config;
mod handler;
mod message;
mod room;
mod state;
use std::net::SocketAddr;
use std::time::Instant;
use axum::routing::{get, post};
use axum::{Json, Router};
use tower_http::cors::CorsLayer;
use tracing::info;
use crate::config::mask_api_key;
use crate::state::AppState;
#[tokio::main]
async fn main() {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "openws_server=info".into()),
)
.init();
let cfg = config::load_config().expect("failed to load configuration");
let state = AppState::new(cfg.clone());
let started_at: Instant = state.started_at;
let app = Router::new()
.route("/ws", get(handler::ws_handler))
.route("/api/broadcast", post(broadcast::broadcast_handler))
.route(
"/health",
{
let state = state.clone();
get(move || {
let state = state.clone();
async move {
let rooms = state.rooms.read().await;
let total_clients: usize = rooms.values().map(|r| r.clients.len()).sum();
Json(serde_json::json!({
"status": "ok",
"rooms": rooms.len(),
"clients": total_clients,
"uptime_secs": started_at.elapsed().as_secs(),
}))
}
})
},
)
.route(
"/stats",
{
let state = state.clone();
get(move || {
let state = state.clone();
async move { Json(state.stats().await) }
})
},
)
.layer(CorsLayer::permissive())
.with_state(state);
let addr = SocketAddr::new(
cfg.server.host.parse().expect("invalid host"),
cfg.server.port,
);
info!(
"openws-server v{} starting on {}:{}",
env!("CARGO_PKG_VERSION"),
cfg.server.host,
cfg.server.port
);
info!(
"limits: max_clients={}, max_rooms={}, max_message_size={}, max_username_len={}",
cfg.limits.max_clients,
cfg.limits.max_rooms,
cfg.limits.max_message_size,
cfg.limits.max_username_len,
);
info!(
"heartbeat: ping_interval={}s, pong_timeout={}s",
cfg.heartbeat.ping_interval_secs, cfg.heartbeat.pong_timeout_secs,
);
info!(
"broadcast api key: {}",
mask_api_key(&cfg.broadcast.api_key)
);
let listener = tokio::net::TcpListener::bind(addr)
.await
.expect("failed to bind to address");
axum::serve(listener, app)
.await
.expect("server error");
}