This commit is contained in:
2026-07-02 19:20:41 +01:00
parent c1da3abe7d
commit a577296608
13 changed files with 476 additions and 66 deletions
+37 -10
View File
@@ -22,7 +22,7 @@ use tokio::{
use tower_http::cors::CorsLayer;
use uuid::Uuid;
use tracing::{debug, info};
use tracing::{debug, info, warn};
use crate::{
AppState,
@@ -30,12 +30,20 @@ use crate::{
webrtc_ingest::handle_whip_injest,
};
const MAX_USERNAME_LEN: usize = 32;
const MAX_LABEL_LEN: usize = 64;
pub struct HttpServerConfig {
pub signup_code: String,
}
pub struct HttpServer {
pub offer_tx: Sender<(i32, i32, String)>,
pub accept_rx: async_broadcast::InactiveReceiver<(i32, Option<String>)>,
pub appstate: Arc<Mutex<AppState>>,
pub request_count: Mutex<i32>,
pub db: DatabaseConnection,
pub config: HttpServerConfig,
}
impl HttpServer {
@@ -73,7 +81,7 @@ impl HttpServer {
.layer(cors)
.with_state(state);
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
let listener = TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
});
@@ -175,12 +183,18 @@ async fn create_stream_key_handler(
.await
.unwrap()
.len();
if payload.label.is_empty() || payload.label.len() > MAX_LABEL_LEN {
warn!(user_id = auth.0.id, label_len = payload.label.len(), max = MAX_LABEL_LEN, "stream key creation rejected: label length invalid");
return StatusCode::UNPROCESSABLE_ENTITY;
}
if key_amount >= auth.0.stream_key_limit.try_into().unwrap() {
warn!(user_id = auth.0.id, limit = auth.0.stream_key_limit, "stream key limit reached");
return StatusCode::NOT_ACCEPTABLE;
}
let key = stream_key::Entity::create(&state.db, auth.0.id, value, payload.label, false).await;
if let Ok(_key) = key {
if let Ok(ref k) = key {
info!(user_id = auth.0.id, stream_key_id = k.id, label = %k.label, "stream key created");
StatusCode::CREATED
} else {
StatusCode::INTERNAL_SERVER_ERROR
@@ -226,15 +240,17 @@ async fn login_handler(
State(state): State<Arc<HttpServer>>,
Json(payload): Json<LoginForm>,
) -> impl IntoResponse {
if let Ok(x) = users::Entity::find_by_username(&state.db, payload.username).await {
if let Ok(x) = users::Entity::find_by_username(&state.db, payload.username.clone()).await {
if let Some(x) = x {
let pass = verify_password(&payload.password, &x.hashed_password);
if !pass {
warn!(username = %payload.username, "login failed: wrong password");
let mut meow = Response::new("".to_string());
*meow.status_mut() = StatusCode::UNAUTHORIZED;
return meow;
};
let auth = auth_session::Entity::create(&state.db, x.id).await.unwrap(); // This should be ok (hopefully)
info!(user_id = x.id, username = %x.username, "login successful");
let auth = auth_session::Entity::create(&state.db, x.id).await.unwrap();
let token = auth.value;
let mut meow = Response::new("".to_string());
meow.headers_mut().insert(
@@ -246,11 +262,13 @@ async fn login_handler(
*meow.status_mut() = StatusCode::OK;
return meow;
} else {
warn!(username = %payload.username, "login failed: user not found");
let mut meow = Response::new("".to_string());
*meow.status_mut() = StatusCode::UNAUTHORIZED;
return meow;
}
} else {
warn!(username = %payload.username, "login failed: DB error");
let mut meow = Response::new("".to_string());
*meow.status_mut() = StatusCode::UNAUTHORIZED;
return meow;
@@ -268,23 +286,30 @@ async fn create_user_handler(
State(state): State<Arc<HttpServer>>,
Json(payload): Json<CreateUserForm>,
) -> (HeaderMap, StatusCode) {
if payload.ref_token != "TEST" {
if state.config.signup_code.is_empty() || payload.ref_token != state.config.signup_code {
warn!(username = %payload.username, "signup rejected: invalid signup code");
return (HeaderMap::new(), StatusCode::UNAUTHORIZED);
}
if payload.username.is_empty() || payload.username.len() > MAX_USERNAME_LEN {
warn!(username = %payload.username, max = MAX_USERNAME_LEN, "signup rejected: username length invalid");
return (HeaderMap::new(), StatusCode::UNPROCESSABLE_ENTITY);
}
let meow = users::Entity::create(
&state.db,
payload.username,
payload.username.clone(),
hash_password(&payload.password).unwrap(),
)
.await;
// Create session
let session: auth_session::Model = if let Ok(meow) = meow {
auth_session::Entity::create(&state.db, meow.id)
let session: auth_session::Model = if let Ok(ref user) = meow {
info!(user_id = user.id, username = %user.username, "user created");
auth_session::Entity::create(&state.db, user.id)
.await
.unwrap() // This should be ok (hopefully)
.unwrap()
} else {
warn!(username = %payload.username, "user creation failed (likely username conflict)");
return (HeaderMap::new(), StatusCode::CONFLICT);
};
let token = session.value;
@@ -319,6 +344,7 @@ async fn stream_handler(
let stream_key_id = if let Some(id) = stream_key_id {
id
} else {
warn!(slug = %slug, "WHEP request for unknown or inactive stream");
return Response::builder()
.status(StatusCode::NOT_FOUND)
.header("content-type", "application/text")
@@ -326,6 +352,7 @@ async fn stream_handler(
.unwrap();
};
info!(request_id = request_id_clone, slug = %slug, stream_key_id, "WHEP offer received");
let mut accept_rx = state.accept_rx.activate_cloned();
let _ = state
.offer_tx