225 lines
8.1 KiB
Rust
225 lines
8.1 KiB
Rust
use crate::auth::get_auth_user;
|
||
use crate::graphql::types::{blog::*, PaginatedResult, PaginationInput};
|
||
use crate::services::{blog_service::BlogService, casbin_service::CasbinService};
|
||
use async_graphql::{Context, Error as GraphQLError, Object, Result};
|
||
use uuid::Uuid;
|
||
|
||
#[derive(Default)]
|
||
pub struct BlogQuery;
|
||
|
||
#[Object]
|
||
impl BlogQuery {
|
||
async fn blogs(
|
||
&self,
|
||
ctx: &Context<'_>,
|
||
filter: Option<BlogFilterInput>,
|
||
sort: Option<BlogSortInput>,
|
||
pagination: Option<PaginationInput>,
|
||
) -> Result<PaginatedResult<Blog>> {
|
||
let blog_service = ctx.data::<BlogService>()?;
|
||
|
||
// 检查用户权限
|
||
let mut updated_filter = filter.unwrap_or(BlogFilterInput {
|
||
title: None,
|
||
slug: None,
|
||
category_id: None,
|
||
status: None,
|
||
is_featured: None,
|
||
is_active: None,
|
||
tag_ids: None,
|
||
search: None,
|
||
date_from: None,
|
||
date_to: None,
|
||
});
|
||
|
||
// 尝试获取用户信息和权限检查
|
||
match get_auth_user(ctx).await {
|
||
Ok(user) => {
|
||
// 用户已认证,检查是否有读取 blogs 的权限
|
||
let casbin_service = ctx.data::<CasbinService>()?;
|
||
let has_permission = casbin_service
|
||
.can_read(&user.id.to_string(), "blogs")
|
||
.await
|
||
.unwrap_or(false);
|
||
|
||
// 如果没有权限,则只返回非 draft 状态的博客
|
||
if !has_permission {
|
||
// 如果过滤器中没有设置状态,或者状态包含 draft,则排除 draft 状态
|
||
if updated_filter.status.is_none() || updated_filter.status.as_ref() == Some(&"draft".to_string()) {
|
||
updated_filter.status = Some("published".to_string());
|
||
}
|
||
}
|
||
}
|
||
Err(_) => {
|
||
// 用户未认证,只返回已发布的博客
|
||
if updated_filter.status.is_none() || updated_filter.status.as_ref() == Some(&"draft".to_string()) {
|
||
updated_filter.status = Some("published".to_string());
|
||
}
|
||
}
|
||
}
|
||
|
||
let result = blog_service
|
||
.get_blogs(Some(updated_filter), sort, pagination)
|
||
.await
|
||
.map_err(|e| GraphQLError::new(e.to_string()))?;
|
||
|
||
// 转换 model 类型到 GraphQL 类型
|
||
Ok(PaginatedResult::new(
|
||
result.items.into_iter().map(|item| item.into()).collect(),
|
||
result.total,
|
||
result.page,
|
||
result.per_page,
|
||
))
|
||
}
|
||
|
||
/// 根据ID获取博客文章
|
||
async fn blog(&self, ctx: &Context<'_>, id: Uuid) -> Result<Blog> {
|
||
let blog_service = ctx.data::<BlogService>()?;
|
||
let blog = blog_service
|
||
.get_blog_by_id(id)
|
||
.await
|
||
.map_err(|e| GraphQLError::new(e.to_string()))?;
|
||
|
||
// 权限检查:如果是 draft 状态,需要验证用户权限
|
||
if blog.status == "draft" {
|
||
match get_auth_user(ctx).await {
|
||
Ok(user) => {
|
||
let casbin_service = ctx.data::<CasbinService>()?;
|
||
let has_permission = casbin_service
|
||
.can_read(&user.id.to_string(), "blogs")
|
||
.await
|
||
.unwrap_or(false);
|
||
|
||
if !has_permission {
|
||
return Err(GraphQLError::new("Insufficient permissions to access draft content"));
|
||
}
|
||
}
|
||
Err(_) => {
|
||
return Err(GraphQLError::new("Authentication required to access draft content"));
|
||
}
|
||
}
|
||
}
|
||
|
||
Ok(blog.into())
|
||
}
|
||
|
||
/// 根据slug获取博客文章
|
||
async fn blog_by_slug(&self, ctx: &Context<'_>, slug: String) -> Result<Blog> {
|
||
let blog_service = ctx.data::<BlogService>()?;
|
||
let blog = blog_service
|
||
.get_blog_by_slug(&slug)
|
||
.await
|
||
.map_err(|e| GraphQLError::new(e.to_string()))?;
|
||
|
||
// 权限检查:如果是 draft 状态,需要验证用户权限
|
||
if blog.status == "draft" {
|
||
match get_auth_user(ctx).await {
|
||
Ok(user) => {
|
||
let casbin_service = ctx.data::<CasbinService>()?;
|
||
let has_permission = casbin_service
|
||
.can_read(&user.id.to_string(), "blogs")
|
||
.await
|
||
.unwrap_or(false);
|
||
|
||
if !has_permission {
|
||
return Err(GraphQLError::new("Insufficient permissions to access draft content"));
|
||
}
|
||
}
|
||
Err(_) => {
|
||
return Err(GraphQLError::new("Authentication required to access draft content"));
|
||
}
|
||
}
|
||
}
|
||
|
||
Ok(blog.into())
|
||
}
|
||
|
||
async fn blog_detail(&self, ctx: &Context<'_>, id: Uuid) -> Result<BlogDetail> {
|
||
let blog_service = ctx.data::<BlogService>()?;
|
||
let detail = blog_service
|
||
.get_blog_detail(id)
|
||
.await
|
||
.map_err(|e| GraphQLError::new(e.to_string()))?;
|
||
|
||
// 权限检查:如果是 draft 状态,需要验证用户权限
|
||
if detail.blog.status == "draft" {
|
||
match get_auth_user(ctx).await {
|
||
Ok(user) => {
|
||
let casbin_service = ctx.data::<CasbinService>()?;
|
||
let has_permission = casbin_service
|
||
.can_read(&user.id.to_string(), "blogs")
|
||
.await
|
||
.unwrap_or(false);
|
||
|
||
if !has_permission {
|
||
return Err(GraphQLError::new("Insufficient permissions to access draft content"));
|
||
}
|
||
}
|
||
Err(_) => {
|
||
return Err(GraphQLError::new("Authentication required to access draft content"));
|
||
}
|
||
}
|
||
}
|
||
|
||
// 手动转换 BlogDetail,因为它包含嵌套结构
|
||
Ok(BlogDetail {
|
||
blog: detail.blog.into(),
|
||
category: detail.category.map(|c| c.into()),
|
||
tags: detail.tags.into_iter().map(|t| t.into()).collect(),
|
||
})
|
||
}
|
||
|
||
async fn blog_stats(&self, ctx: &Context<'_>) -> Result<BlogStats> {
|
||
let blog_service = ctx.data::<BlogService>()?;
|
||
let stats = blog_service
|
||
.get_blog_stats()
|
||
.await
|
||
.map_err(|e| GraphQLError::new(e.to_string()))?;
|
||
Ok(stats.into())
|
||
}
|
||
|
||
async fn blog_categories(
|
||
&self,
|
||
ctx: &Context<'_>,
|
||
filter: Option<BlogCategoryFilterInput>,
|
||
) -> Result<Vec<BlogCategory>> {
|
||
let blog_service = ctx.data::<BlogService>()?;
|
||
let categories = blog_service
|
||
.get_categories(filter)
|
||
.await
|
||
.map_err(|e| GraphQLError::new(e.to_string()))?;
|
||
Ok(categories.into_iter().map(|c| c.into()).collect())
|
||
}
|
||
|
||
async fn blog_category(&self, ctx: &Context<'_>, id: Uuid) -> Result<BlogCategory> {
|
||
let blog_service = ctx.data::<BlogService>()?;
|
||
let category = blog_service
|
||
.get_category_by_id(id)
|
||
.await
|
||
.map_err(|e| GraphQLError::new(e.to_string()))?;
|
||
Ok(category.into())
|
||
}
|
||
|
||
async fn blog_tags(
|
||
&self,
|
||
ctx: &Context<'_>,
|
||
filter: Option<BlogTagFilterInput>,
|
||
) -> Result<Vec<BlogTag>> {
|
||
let blog_service = ctx.data::<BlogService>()?;
|
||
let tags = blog_service
|
||
.get_tags(filter)
|
||
.await
|
||
.map_err(|e| GraphQLError::new(e.to_string()))?;
|
||
Ok(tags.into_iter().map(|t| t.into()).collect())
|
||
}
|
||
|
||
async fn blog_tag(&self, ctx: &Context<'_>, id: Uuid) -> Result<BlogTag> {
|
||
let blog_service = ctx.data::<BlogService>()?;
|
||
let tag = blog_service
|
||
.get_tag_by_id(id)
|
||
.await
|
||
.map_err(|e| GraphQLError::new(e.to_string()))?;
|
||
Ok(tag.into())
|
||
}
|
||
}
|