feat: Implement course team management with dedicated UI and API, add course preview token generation, and refactor course settings UI.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use jsonwebtoken::{encode, Header, EncodingKey};
|
||||
use chrono::{Duration, Utc};
|
||||
use jsonwebtoken::{EncodingKey, Header, encode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
use chrono::{Utc, Duration};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Claims {
|
||||
@@ -9,9 +9,15 @@ pub struct Claims {
|
||||
pub org: Uuid,
|
||||
pub exp: i64,
|
||||
pub role: String,
|
||||
pub course_id: Option<Uuid>,
|
||||
pub token_type: Option<String>, // "access", "preview"
|
||||
}
|
||||
|
||||
pub fn create_jwt(user_id: Uuid, organization_id: Uuid, role: &str) -> Result<String, jsonwebtoken::errors::Error> {
|
||||
pub fn create_jwt(
|
||||
user_id: Uuid,
|
||||
organization_id: Uuid,
|
||||
role: &str,
|
||||
) -> Result<String, jsonwebtoken::errors::Error> {
|
||||
let expiration = Utc::now()
|
||||
.checked_add_signed(Duration::hours(24))
|
||||
.expect("valid timestamp")
|
||||
@@ -22,8 +28,41 @@ pub fn create_jwt(user_id: Uuid, organization_id: Uuid, role: &str) -> Result<St
|
||||
org: organization_id,
|
||||
exp: expiration,
|
||||
role: role.to_string(),
|
||||
course_id: None,
|
||||
token_type: Some("access".to_string()),
|
||||
};
|
||||
|
||||
let secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| "secret".to_string());
|
||||
encode(&Header::default(), &claims, &EncodingKey::from_secret(secret.as_ref()))
|
||||
encode(
|
||||
&Header::default(),
|
||||
&claims,
|
||||
&EncodingKey::from_secret(secret.as_ref()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_preview_token(
|
||||
user_id: Uuid,
|
||||
organization_id: Uuid,
|
||||
course_id: Uuid,
|
||||
) -> Result<String, jsonwebtoken::errors::Error> {
|
||||
let expiration = Utc::now()
|
||||
.checked_add_signed(Duration::hours(1))
|
||||
.expect("valid timestamp")
|
||||
.timestamp();
|
||||
|
||||
let claims = Claims {
|
||||
sub: user_id,
|
||||
org: organization_id,
|
||||
exp: expiration,
|
||||
role: "instructor".to_string(),
|
||||
course_id: Some(course_id),
|
||||
token_type: Some("preview".to_string()),
|
||||
};
|
||||
|
||||
let secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| "secret".to_string());
|
||||
encode(
|
||||
&Header::default(),
|
||||
&claims,
|
||||
&EncodingKey::from_secret(secret.as_ref()),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -26,16 +26,27 @@ pub async fn org_extractor_middleware(
|
||||
.and_then(|header: &axum::http::HeaderValue| header.to_str().ok());
|
||||
|
||||
let token = if let Some(token_str) = auth_header.and_then(|s: &str| s.strip_prefix("Bearer ")) {
|
||||
token_str
|
||||
token_str.to_string()
|
||||
} else {
|
||||
return Err(StatusCode::UNAUTHORIZED);
|
||||
// Check for preview_token in query string
|
||||
let query = req.uri().query().unwrap_or_default();
|
||||
let preview_token = query
|
||||
.split('&')
|
||||
.find(|part| part.starts_with("preview_token="))
|
||||
.and_then(|part| part.split('=').nth(1));
|
||||
|
||||
if let Some(token) = preview_token {
|
||||
token.to_string()
|
||||
} else {
|
||||
return Err(StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
};
|
||||
|
||||
// NOTA: El secreto debe venir de una variable de entorno en producción.
|
||||
let secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| "secret".to_string());
|
||||
|
||||
let mut claims = decode::<Claims>(
|
||||
token,
|
||||
&token,
|
||||
&DecodingKey::from_secret(secret.as_ref()),
|
||||
&Validation::default(),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user