feat: add IPTV export functionality with M3U and XMLTV generation, including UI components for export dialog
This commit is contained in:
@@ -39,6 +39,9 @@ impl FromRequestParts<AppState> for CurrentUser {
|
||||
}
|
||||
|
||||
/// Optional current user — returns None instead of error when auth is missing/invalid.
|
||||
///
|
||||
/// Checks `Authorization: Bearer <token>` first; falls back to `?token=<jwt>` query param
|
||||
/// so IPTV clients and direct stream links work without custom headers.
|
||||
pub struct OptionalCurrentUser(pub Option<User>);
|
||||
|
||||
impl FromRequestParts<AppState> for OptionalCurrentUser {
|
||||
@@ -50,7 +53,21 @@ impl FromRequestParts<AppState> for OptionalCurrentUser {
|
||||
) -> Result<Self, Self::Rejection> {
|
||||
#[cfg(feature = "auth-jwt")]
|
||||
{
|
||||
return Ok(OptionalCurrentUser(try_jwt_auth(parts, state).await.ok()));
|
||||
// Try Authorization header first
|
||||
if let Ok(user) = try_jwt_auth(parts, state).await {
|
||||
return Ok(OptionalCurrentUser(Some(user)));
|
||||
}
|
||||
// Fall back to ?token= query param
|
||||
let query_token = parts.uri.query().and_then(|q| {
|
||||
q.split('&')
|
||||
.find(|seg| seg.starts_with("token="))
|
||||
.map(|seg| seg[6..].to_owned())
|
||||
});
|
||||
if let Some(token) = query_token {
|
||||
let user = validate_jwt_token(&token, state).await.ok();
|
||||
return Ok(OptionalCurrentUser(user));
|
||||
}
|
||||
return Ok(OptionalCurrentUser(None));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "auth-jwt"))]
|
||||
@@ -61,7 +78,7 @@ impl FromRequestParts<AppState> for OptionalCurrentUser {
|
||||
}
|
||||
}
|
||||
|
||||
/// Authenticate using JWT Bearer token
|
||||
/// Authenticate using JWT Bearer token from the `Authorization` header.
|
||||
#[cfg(feature = "auth-jwt")]
|
||||
async fn try_jwt_auth(parts: &mut Parts, state: &AppState) -> Result<User, ApiError> {
|
||||
use axum::http::header::AUTHORIZATION;
|
||||
@@ -79,6 +96,12 @@ async fn try_jwt_auth(parts: &mut Parts, state: &AppState) -> Result<User, ApiEr
|
||||
ApiError::Unauthorized("Authorization header must use Bearer scheme".to_string())
|
||||
})?;
|
||||
|
||||
validate_jwt_token(token, state).await
|
||||
}
|
||||
|
||||
/// Validate a raw JWT string and return the corresponding `User`.
|
||||
#[cfg(feature = "auth-jwt")]
|
||||
pub(crate) async fn validate_jwt_token(token: &str, state: &AppState) -> Result<User, ApiError> {
|
||||
let validator = state
|
||||
.jwt_validator
|
||||
.as_ref()
|
||||
|
||||
Reference in New Issue
Block a user