Files
tuliprox/backend/src/api/endpoints/websocket_api.rs
T

286 lines
10 KiB
Rust
Raw Normal View History

use crate::api::endpoints::v1_api::create_status_check;
2025-07-24 14:27:57 +02:00
use crate::api::model::AppState;
use crate::api::model::EventMessage;
2025-07-29 10:44:55 +02:00
use crate::auth::{verify_token_admin, verify_token_user};
use axum::extract::ws::CloseFrame;
2025-08-31 15:26:02 +02:00
use axum::{extract::ws::{Message, WebSocket, WebSocketUpgrade},response::IntoResponse};
2025-07-21 22:17:57 +02:00
use log::{error, info};
2025-07-29 18:14:43 +02:00
use shared::model::{ProtocolHandler, ProtocolHandlerMemory, ProtocolMessage, UserRole, WsCloseCode, PROTOCOL_VERSION};
use std::sync::Arc;
2025-08-05 14:20:13 +02:00
use shared::utils::{concat_path_leading_slash};
2025-07-21 22:17:57 +02:00
// WebSocket upgrade handler
async fn websocket_handler(
axum::extract::State(app_state): axum::extract::State<Arc<AppState>>,
2025-07-24 14:27:57 +02:00
ws: WebSocketUpgrade,
) -> impl IntoResponse {
2025-07-21 22:17:57 +02:00
info!("Websocket connected");
ws.on_upgrade(move |socket| handle_socket(socket, app_state, false))
}
// WebSocket upgrade handler
async fn websocket_handler_auth(
axum::extract::State(app_state): axum::extract::State<Arc<AppState>>,
2025-07-24 14:27:57 +02:00
ws: WebSocketUpgrade,
) -> impl IntoResponse {
2025-07-21 22:17:57 +02:00
info!("Websocket connected");
ws.on_upgrade(move |socket| handle_socket(socket, app_state, true))
}
pub fn ws_api_register(web_auth_enabled: bool, web_ui_path: &str) -> axum::Router<Arc<AppState>> {
if web_auth_enabled {
2025-07-24 14:27:57 +02:00
axum::Router::new().route(
2025-08-05 14:20:13 +02:00
&concat_path_leading_slash(web_ui_path, "ws"),
2025-07-24 14:27:57 +02:00
axum::routing::get(websocket_handler_auth),
)
2025-07-21 22:17:57 +02:00
} else {
2025-07-24 14:27:57 +02:00
axum::Router::new().route(
2025-08-05 14:20:13 +02:00
&concat_path_leading_slash(web_ui_path, "ws"),
2025-07-24 14:27:57 +02:00
axum::routing::get(websocket_handler),
)
2025-07-21 22:17:57 +02:00
}
}
#[inline]
fn verify_auth_admin_token(auth_token: &str, secret_key: Option<&Vec<u8>>) -> bool {
match secret_key.as_ref() {
None => false,
2025-07-24 14:27:57 +02:00
Some(key) => verify_token_admin(auth_token, key.as_slice()),
}
}
2025-07-29 10:44:55 +02:00
#[inline]
fn verify_auth_user_token(auth_token: &str, secret_key: Option<&Vec<u8>>) -> bool {
match secret_key.as_ref() {
None => false,
Some(key) => verify_token_user(auth_token, key.as_slice()),
}
}
2025-07-22 17:37:56 +02:00
fn get_secret_key(app_state: &AppState, auth: bool) -> Option<Vec<u8>> {
if !auth {
return None;
}
2025-07-24 14:27:57 +02:00
app_state
.app_config
.config
.load()
.web_ui
.as_ref()
2025-07-22 17:37:56 +02:00
.and_then(|c| c.auth.as_ref())
.map(|c| {
let secret_key: &[u8] = c.secret.as_ref();
secret_key.to_vec()
})
}
2025-07-24 14:27:57 +02:00
async fn handle_handshake(msg: Message, socket: &mut WebSocket, version: u8) -> Result<(), String> {
2025-07-22 17:37:56 +02:00
if let Message::Binary(bytes) = msg {
if bytes.len() == 1 {
let client_version = bytes[0];
if client_version == version {
2025-07-24 14:27:57 +02:00
socket
.send(Message::binary(bytes))
.await
.map_err(|e| e.to_string())?;
2025-07-22 17:37:56 +02:00
return Ok(());
}
2025-08-02 12:37:13 +02:00
error!("Protocol Version mismatch: server={version}, client={client_version}");
2025-07-22 17:37:56 +02:00
}
}
2025-07-24 14:27:57 +02:00
let _ = socket
.send(Message::Close(Some(CloseFrame {
code: WsCloseCode::Protocol.code(),
reason: "Unsupported protocol".into(),
})))
.await;
2025-07-22 17:37:56 +02:00
Err("Protocol version mismatch".into())
}
async fn handle_protocol_message(
msg: Message,
2025-07-29 10:44:55 +02:00
mem: &mut ProtocolHandlerMemory,
2025-07-22 17:37:56 +02:00
app_state: &Arc<AppState>,
auth: bool,
secret_key: Option<&Vec<u8>>,
2025-07-23 12:49:43 +02:00
) -> Option<ProtocolMessage> {
2025-07-22 17:37:56 +02:00
if let Message::Binary(bytes) = msg {
match ProtocolMessage::from_bytes(bytes) {
2025-07-29 10:44:55 +02:00
Ok(ProtocolMessage::Auth(auth_token)) => {
mem.token = None;
2025-08-31 15:26:02 +02:00
if !auth || verify_auth_admin_token(&auth_token, secret_key) {
2025-07-29 18:14:43 +02:00
mem.role = UserRole::Admin;
2025-07-29 10:44:55 +02:00
mem.token = Some(auth_token);
Some(ProtocolMessage::Authorized)
} else if verify_auth_user_token(&auth_token, secret_key) {
2025-07-29 18:14:43 +02:00
mem.role = UserRole::User;
2025-07-29 10:44:55 +02:00
mem.token = Some(auth_token);
Some(ProtocolMessage::Authorized)
} else {
Some(ProtocolMessage::Unauthorized)
}
},
2025-07-22 17:37:56 +02:00
Ok(ProtocolMessage::StatusRequest(auth_token)) => {
if !auth || verify_auth_admin_token(&auth_token, secret_key) {
2025-07-29 18:14:43 +02:00
mem.role = UserRole::Admin;
2025-07-29 10:44:55 +02:00
mem.token = Some(auth_token);
2025-07-24 14:27:57 +02:00
let status = create_status_check(app_state).await;
Some(ProtocolMessage::StatusResponse(status))
2025-07-23 12:49:43 +02:00
} else {
Some(ProtocolMessage::Unauthorized)
2025-07-22 17:37:56 +02:00
}
}
Ok(_) => {
error!("Unexpected protocol message after handshake");
2025-07-23 12:49:43 +02:00
None
2025-07-22 17:37:56 +02:00
}
Err(e) => {
error!("Invalid websocket message: {e}");
2025-07-24 14:27:57 +02:00
Some(ProtocolMessage::Error(format!(
"Invalid websocket message: {e}"
)))
2025-07-22 17:37:56 +02:00
}
}
2025-07-23 12:49:43 +02:00
} else {
None
2025-07-22 17:37:56 +02:00
}
}
async fn handle_incoming_message(
result: Result<Message, axum::Error>,
socket: &mut WebSocket,
handler: &mut ProtocolHandler,
app_state: &Arc<AppState>,
auth: bool,
secret_key: Option<&Vec<u8>>,
) -> Result<(), String> {
let msg = result.map_err(|e| e.to_string())?;
match handler {
ProtocolHandler::Version(version) => {
handle_handshake(msg, socket, *version).await?;
2025-07-29 10:44:55 +02:00
*handler = ProtocolHandler::Default(ProtocolHandlerMemory::default());
2025-07-22 17:37:56 +02:00
Ok(())
2025-07-24 14:27:57 +02:00
}
2025-07-29 10:44:55 +02:00
ProtocolHandler::Default(mem) => {
let msg = handle_protocol_message(msg, mem, app_state, auth, secret_key).await;
2025-07-23 12:49:43 +02:00
match msg {
2025-07-24 14:27:57 +02:00
None => Ok(()),
2025-07-23 12:49:43 +02:00
Some(protocol_msg) => {
let bytes = match protocol_msg.to_bytes() {
Ok(bytes) => bytes,
2025-07-24 14:27:57 +02:00
Err(err) => ProtocolMessage::Error(err.to_string())
.to_bytes()
.map_err(|e| e.to_string())?,
2025-07-23 12:49:43 +02:00
};
2025-07-24 14:27:57 +02:00
Ok(socket
.send(Message::Binary(bytes))
.await
.map_err(|e| e.to_string())?)
2025-07-23 12:49:43 +02:00
}
}
2025-07-24 14:27:57 +02:00
}
2025-07-22 17:37:56 +02:00
}
}
2025-07-29 10:44:55 +02:00
async fn handle_event_message(socket: &mut WebSocket, event: EventMessage, handler: &ProtocolHandler) -> Result<(), String> {
match handler {
ProtocolHandler::Version(_) => {},
ProtocolHandler::Default(mem) => {
2025-07-29 18:14:43 +02:00
if mem.role.is_admin() {
2025-07-29 10:44:55 +02:00
match event {
2025-08-06 11:28:01 +02:00
EventMessage::ServerError(error) => {
let msg = ProtocolMessage::ServerError(error)
.to_bytes()
.map_err(|e| e.to_string())?;
socket
.send(Message::Binary(msg))
.await
.map_err(|e| format!("Server Error event: {e} "))?;
}
2025-10-24 18:36:13 +02:00
EventMessage::ActiveUser(event) => {
let msg = ProtocolMessage::ActiveUserResponse(event)
2025-07-29 10:44:55 +02:00
.to_bytes()
.map_err(|e| e.to_string())?;
socket
.send(Message::Binary(msg))
.await
.map_err(|e| format!("Active user connection change event: {e} "))?;
}
2025-08-01 21:03:50 +02:00
EventMessage::ActiveProvider(provider, connections) => {
2025-07-29 10:44:55 +02:00
let msg = ProtocolMessage::ActiveProviderResponse(provider, connections)
.to_bytes()
.map_err(|e| e.to_string())?;
socket
.send(Message::Binary(msg))
.await
.map_err(|e| format!("Provider connection change event: {e} "))?;
}
2025-08-01 21:03:50 +02:00
EventMessage::ConfigChange(config) => {
let msg = ProtocolMessage::ConfigChangeResponse(config)
.to_bytes()
.map_err(|e| e.to_string())?;
socket
.send(Message::Binary(msg))
.await
.map_err(|e| format!("Configuration files change event: {e} "))?;
}
2025-08-03 10:24:23 +02:00
EventMessage::PlaylistUpdate(state) => {
let msg = ProtocolMessage::PlaylistUpdateResponse(state)
.to_bytes()
.map_err(|e| e.to_string())?;
socket
.send(Message::Binary(msg))
.await
.map_err(|e| format!("Playlist update event: {e} "))?;
}
EventMessage::PlaylistUpdateProgress(target, msg) => {
let msg = ProtocolMessage::PlaylistUpdateProgressResponse(target, msg)
.to_bytes()
.map_err(|e| e.to_string())?;
socket
.send(Message::Binary(msg))
.await
.map_err(|e| format!("Playlist update progress event: {e} "))?;
}
2025-07-29 10:44:55 +02:00
}
}
2025-07-22 17:37:56 +02:00
}
}
2025-07-29 10:44:55 +02:00
Ok(())
2025-07-22 17:37:56 +02:00
}
2025-07-21 22:17:57 +02:00
// WebSocket communication logic
async fn handle_socket(mut socket: WebSocket, app_state: Arc<AppState>, auth: bool) {
2025-07-22 17:37:56 +02:00
let secret_key = get_secret_key(&app_state, auth);
2025-07-21 22:17:57 +02:00
2025-07-22 17:37:56 +02:00
let mut event_rx = app_state.event_manager.get_event_channel();
2025-07-21 22:17:57 +02:00
let mut handler = ProtocolHandler::Version(PROTOCOL_VERSION);
loop {
tokio::select! {
maybe_msg = socket.recv() => {
2025-07-22 17:37:56 +02:00
if let Some(msg) = maybe_msg {
if let Err(e) = handle_incoming_message(msg, &mut socket, &mut handler, &app_state, auth, secret_key.as_ref()).await {
error!("WebSocket message handling error: {e}");
2025-07-21 22:17:57 +02:00
break;
}
2025-07-22 17:37:56 +02:00
} else {
break;
2025-07-21 22:17:57 +02:00
}
}
2025-07-22 17:37:56 +02:00
Ok(event) = event_rx.recv() => {
2025-07-29 10:44:55 +02:00
if let Err(e) = handle_event_message(&mut socket, event, &handler).await {
2025-10-16 11:58:40 +02:00
error!("Failed to send ws event: {e}");
2025-07-22 17:37:56 +02:00
break;
2025-07-21 22:17:57 +02:00
}
}
}
}
2025-07-24 14:27:57 +02:00
}