Files
openccb/services/lms-service/src/portfolio.rs
T

95 lines
2.9 KiB
Rust

use axum::{
extract::{Path, State},
http::StatusCode,
Json,
};
use common::auth::Claims;
use common::models::{Badge, UserBadge, PublicProfile};
use sqlx::{PgPool, Row};
use uuid::Uuid;
pub async fn get_public_profile(
Path(user_id): Path<Uuid>,
State(pool): State<PgPool>,
) -> Result<Json<PublicProfile>, (StatusCode, String)> {
let user = sqlx::query("SELECT id, full_name, avatar_url, bio, level, xp, is_public_profile FROM users WHERE id = $1")
.bind(user_id)
.fetch_optional(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
.ok_or((StatusCode::NOT_FOUND, "User not found".to_string()))?;
let is_public: bool = user.get("is_public_profile");
if !is_public {
return Err((StatusCode::FORBIDDEN, "This profile is private".to_string()));
}
let badges = sqlx::query_as::<sqlx::Postgres, Badge>(
r#"
SELECT b.* FROM badges b
JOIN user_badges ub ON b.id = ub.badge_id
WHERE ub.user_id = $1
"#
)
.bind(user_id)
.fetch_all(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
let completed_courses: i64 = sqlx::query("SELECT COUNT(*) FROM enrollments WHERE user_id = $1 AND progress_percentage >= 100")
.bind(user_id)
.fetch_one(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
.get(0);
Ok(Json(PublicProfile {
user_id,
full_name: user.get("full_name"),
avatar_url: user.get("avatar_url"),
bio: user.get("bio"),
badges,
level: user.get("level"),
xp: user.get("xp"),
completed_courses_count: completed_courses,
}))
}
pub async fn get_my_badges(
claims: Claims,
State(pool): State<PgPool>,
) -> Result<Json<Vec<Badge>>, (StatusCode, String)> {
let badges = sqlx::query_as::<sqlx::Postgres, Badge>(
r#"
SELECT b.* FROM badges b
JOIN user_badges ub ON b.id = ub.badge_id
WHERE ub.user_id = $1
"#
)
.bind(claims.sub)
.fetch_all(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(Json(badges))
}
pub async fn award_badge(
State(pool): State<PgPool>,
claims: Claims,
Json(payload): Json<UserBadge>,
) -> Result<StatusCode, (StatusCode, String)> {
if claims.role == "student" {
return Err((StatusCode::FORBIDDEN, "Only admins can award badges manually".to_string()));
}
sqlx::query("INSERT INTO user_badges (user_id, badge_id, awarded_at) VALUES ($1, $2, NOW()) ON CONFLICT DO NOTHING")
.bind(payload.user_id)
.bind(payload.badge_id)
.execute(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(StatusCode::CREATED)
}