feat: Implement AI-driven lesson summaries, automate quiz generation, add gamification base, and introduce Studio organization management.
This commit is contained in:
@@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
use chrono::{Utc, Duration};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Claims {
|
||||
pub sub: Uuid,
|
||||
pub org: Uuid,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use axum::{
|
||||
async_trait,
|
||||
extract::FromRequestParts,
|
||||
http::{request::Parts, Request, StatusCode},
|
||||
extract::{FromRequestParts, Request},
|
||||
http::{request::Parts, StatusCode},
|
||||
middleware::Next,
|
||||
response::Response,
|
||||
};
|
||||
@@ -17,16 +16,16 @@ pub struct OrgContext {
|
||||
}
|
||||
|
||||
/// Middleware que valida el token JWT y extrae el `organization_id`.
|
||||
pub async fn org_extractor_middleware<B>(
|
||||
mut req: Request<B>,
|
||||
next: Next<B>,
|
||||
pub async fn org_extractor_middleware(
|
||||
mut req: Request,
|
||||
next: Next,
|
||||
) -> Result<Response, StatusCode> {
|
||||
let auth_header = req
|
||||
.headers()
|
||||
.get("authorization")
|
||||
.and_then(|header| header.to_str().ok());
|
||||
.and_then(|header: &axum::http::HeaderValue| header.to_str().ok());
|
||||
|
||||
let token = if let Some(token_str) = auth_header.and_then(|s| s.strip_prefix("Bearer ")) {
|
||||
let token = if let Some(token_str) = auth_header.and_then(|s: &str| s.strip_prefix("Bearer ")) {
|
||||
token_str
|
||||
} else {
|
||||
return Err(StatusCode::UNAUTHORIZED);
|
||||
@@ -43,17 +42,31 @@ pub async fn org_extractor_middleware<B>(
|
||||
.map_err(|_| StatusCode::UNAUTHORIZED)?
|
||||
.claims;
|
||||
|
||||
// Insertamos el contexto en las extensiones de la petición.
|
||||
// Insertamos el contexto y las claims en las extensiones de la petición.
|
||||
req.extensions_mut().insert(OrgContext { id: claims.org });
|
||||
req.extensions_mut().insert(claims);
|
||||
|
||||
Ok(next.run(req).await)
|
||||
}
|
||||
|
||||
impl<S> FromRequestParts<S> for Claims
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = (StatusCode, &'static str);
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
||||
parts.extensions.get::<Claims>().cloned().ok_or((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Claims no encontradas. ¿El middleware está configurado?",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Extractor de Axum para acceder fácilmente al `OrgContext` en los handlers.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Org(pub OrgContext);
|
||||
|
||||
#[async_trait]
|
||||
impl<S> FromRequestParts<S> for Org
|
||||
where
|
||||
S: Send + Sync,
|
||||
|
||||
@@ -33,6 +33,7 @@ pub struct Lesson {
|
||||
pub title: String,
|
||||
pub content_type: String,
|
||||
pub content_url: Option<String>,
|
||||
pub summary: Option<String>,
|
||||
pub transcription: Option<serde_json::Value>,
|
||||
pub metadata: Option<serde_json::Value>,
|
||||
pub grading_category_id: Option<Uuid>,
|
||||
@@ -128,6 +129,14 @@ pub struct UserResponse {
|
||||
pub role: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, sqlx::FromRow, Clone)]
|
||||
pub struct Organization {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AuthResponse {
|
||||
pub user: UserResponse,
|
||||
|
||||
Reference in New Issue
Block a user