magnetar/src/api_v1/user.rs

264 lines
8.4 KiB
Rust

use crate::model::processing::notification::NotificationModel;
use crate::model::processing::user::{UserBorrowedData, UserModel, UserShapedData};
use crate::model::PackingContext;
use crate::service::MagnetarService;
use crate::web::auth::{AuthenticatedUser, MaybeUser};
use crate::web::pagination::Pagination;
use crate::web::{ApiError, ArgumentOutOfRange, ObjectNotFound};
use axum::extract::{Path, Query, State};
use axum::Json;
use itertools::Itertools;
use magnetar_common::util::lenient_parse_tag_decode;
use magnetar_sdk::endpoints::user::{
GetFollowRequestsSelf, GetFollowersById, GetFollowersSelf, GetFollowingById, GetFollowingSelf,
GetManyUsersById, GetNotifications, GetUserByAcct, GetUserById, GetUserSelf, ManyUsersByIdReq,
NotificationsReq,
};
use magnetar_sdk::endpoints::{Req, Res};
use magnetar_sdk::types::notification::NotificationType;
use std::collections::HashMap;
use std::sync::Arc;
use strum::IntoEnumIterator;
#[tracing::instrument(err, skip_all)]
pub async fn handle_user_info_self(
Query(req): Query<Req<GetUserSelf>>,
State(service): State<Arc<MagnetarService>>,
AuthenticatedUser(user): AuthenticatedUser,
) -> Result<Json<Res<GetUserSelf>>, ApiError> {
let ctx = PackingContext::new(service, Some(user.clone())).await?;
let user = UserModel
.self_full_from_base(
&ctx,
&UserBorrowedData {
user: user.as_ref(),
profile: None,
avatar: None,
banner: None,
},
&req,
)
.await?;
Ok(Json(user))
}
#[tracing::instrument(err, skip_all)]
pub async fn handle_user_info(
Path(id): Path<String>,
Query(req): Query<Req<GetUserById>>,
State(service): State<Arc<MagnetarService>>,
MaybeUser(self_user): MaybeUser,
) -> Result<Json<Res<GetUserById>>, ApiError> {
let ctx = PackingContext::new(service.clone(), self_user).await?;
let user_model = service
.db
.get_user_by_id(&id)
.await?
.ok_or(ObjectNotFound(id))?;
let user = UserModel
.foreign_full_from_base(
&ctx,
&UserBorrowedData {
user: &user_model,
profile: None,
avatar: None,
banner: None,
},
&req,
)
.await?;
Ok(Json(user))
}
#[tracing::instrument(err, skip_all)]
pub async fn handle_user_info_by_acct(
Path(tag_str): Path<String>,
Query(req): Query<Req<GetUserByAcct>>,
State(service): State<Arc<MagnetarService>>,
MaybeUser(self_user): MaybeUser,
) -> Result<Json<Res<GetUserByAcct>>, ApiError> {
let mut tag = lenient_parse_tag_decode(&tag_str)?;
if matches!(&tag.host, Some(host) if host.to_string() == service.config.networking.host) {
tag.host = None;
}
let ctx = PackingContext::new(service.clone(), self_user).await?;
let user_model = service
.db
.get_user_by_tag(tag.name.as_ref(), tag.host.as_ref())
.await?
.ok_or(ObjectNotFound(tag_str))?;
let user = UserModel
.foreign_full_from_base(
&ctx,
&UserBorrowedData {
user: &user_model,
profile: None,
avatar: None,
banner: None,
},
&req,
)
.await?;
Ok(Json(user))
}
#[tracing::instrument(err, skip_all)]
pub async fn handle_user_by_id_many(
Query(ManyUsersByIdReq { id }): Query<Req<GetManyUsersById>>,
State(service): State<Arc<MagnetarService>>,
MaybeUser(user): MaybeUser,
) -> Result<Json<Res<GetManyUsersById>>, ApiError> {
if id.len() >= 100 {
return Err(ArgumentOutOfRange(stringify!(id).to_string()).into());
}
let users = service
.db
.get_many_users_by_id(&id.iter().cloned().sorted().dedup().collect::<Vec<_>>())
.await?;
let ctx = PackingContext::new(service, user.clone()).await?;
let user_model = UserModel;
let user_data = users
.iter()
.map(|user| UserBorrowedData {
user,
profile: None,
avatar: None,
banner: None,
})
.map(|u| Box::new(u) as Box<dyn UserShapedData>)
.collect::<Vec<_>>();
let users_proc = user_model
.pack_many_base(&ctx, &user_data.iter().map(Box::as_ref).collect::<Vec<_>>())
.await?
.into_iter()
.map(|u| (u.id.0.id.clone(), u))
.collect::<HashMap<_, _>>();
let users_ordered = id
.iter()
.map(|ident| users_proc.get(ident).cloned())
.collect::<Vec<_>>();
Ok(Json(users_ordered))
}
#[tracing::instrument(err, skip_all)]
pub async fn handle_notifications(
Query(NotificationsReq {
ref exclude_types,
include_types,
unread_only,
}): Query<Req<GetNotifications>>,
State(service): State<Arc<MagnetarService>>,
AuthenticatedUser(user): AuthenticatedUser,
mut pagination: Pagination,
) -> Result<(Pagination, Json<Res<GetNotifications>>), ApiError> {
let notification_types = include_types
.unwrap_or_else(|| NotificationType::iter().collect::<Vec<_>>())
.iter()
.filter(|t| {
exclude_types.is_none() || !exclude_types.as_ref().is_some_and(|tt| tt.contains(t))
})
.copied()
.collect::<Vec<_>>();
let ctx = PackingContext::new(service, Some(user.clone())).await?;
let notification_model = NotificationModel;
let notifications = notification_model
.get_notifications(
&ctx,
&user.id,
&notification_types,
unread_only.unwrap_or_default(),
&mut pagination,
)
.await?;
Ok((pagination, Json(notifications)))
}
#[tracing::instrument(err, skip_all)]
pub async fn handle_following_self(
Query(_): Query<Req<GetFollowingSelf>>,
State(service): State<Arc<MagnetarService>>,
AuthenticatedUser(user): AuthenticatedUser,
mut pagination: Pagination,
) -> Result<(Pagination, Json<Res<GetFollowingSelf>>), ApiError> {
let ctx = PackingContext::new(service, Some(user.clone())).await?;
let user_model = UserModel;
let users = user_model
.get_followees(&ctx, &user.id, &mut pagination)
.await?;
Ok((pagination, Json(users)))
}
#[tracing::instrument(err, skip_all)]
pub async fn handle_followers_self(
Query(_): Query<Req<GetFollowersSelf>>,
State(service): State<Arc<MagnetarService>>,
AuthenticatedUser(user): AuthenticatedUser,
mut pagination: Pagination,
) -> Result<(Pagination, Json<Res<GetFollowersSelf>>), ApiError> {
let ctx = PackingContext::new(service, Some(user.clone())).await?;
let user_model = UserModel;
let users = user_model
.get_followers(&ctx, &user.id, &mut pagination)
.await?;
Ok((pagination, Json(users)))
}
#[tracing::instrument(err, skip_all)]
pub async fn handle_following(
Query(_): Query<Req<GetFollowingById>>,
Path(id): Path<String>,
State(service): State<Arc<MagnetarService>>,
MaybeUser(user): MaybeUser,
mut pagination: Pagination,
) -> Result<(Pagination, Json<Res<GetFollowingById>>), ApiError> {
let ctx = PackingContext::new(service, user.clone()).await?;
let user_model = UserModel;
let users = user_model.get_followees(&ctx, &id, &mut pagination).await?;
Ok((pagination, Json(users)))
}
#[tracing::instrument(err, skip_all)]
pub async fn handle_followers(
Query(_): Query<Req<GetFollowersById>>,
Path(id): Path<String>,
State(service): State<Arc<MagnetarService>>,
MaybeUser(user): MaybeUser,
mut pagination: Pagination,
) -> Result<(Pagination, Json<Res<GetFollowingById>>), ApiError> {
let ctx = PackingContext::new(service, user.clone()).await?;
let user_model = UserModel;
let users = user_model.get_followers(&ctx, &id, &mut pagination).await?;
Ok((pagination, Json(users)))
}
#[tracing::instrument(err, skip_all)]
pub async fn handle_follow_requests_self(
Query(_): Query<Req<GetFollowRequestsSelf>>,
State(service): State<Arc<MagnetarService>>,
AuthenticatedUser(user): AuthenticatedUser,
mut pagination: Pagination,
) -> Result<(Pagination, Json<Res<GetFollowingById>>), ApiError> {
let ctx = PackingContext::new(service, Some(user.clone())).await?;
let user_model = UserModel;
let users = user_model
.get_follow_requests(&ctx, &user.id, &mut pagination)
.await?;
Ok((pagination, Json(users)))
}