use axum::{ extract::{Path, Query}, http::{HeaderMap, HeaderValue}, response::IntoResponse, Extension, Json, }; use axum_extra::extract::CookieJar; use tetratto_core::model::{ addr::RemoteAddr, auth::{AchievementName, IpBlock}, communities::{CommunityReadAccess, Question}, oauth, permissions::FinePermission, ApiReturn, Error, }; use crate::{ get_user_from_token, image::JsonMultipart, routes::{api::v1::CreateQuestion, pages::PaginatedQuery}, State, }; pub async fn create_request( jar: CookieJar, headers: HeaderMap, Extension(data): Extension, JsonMultipart(drawings, req): JsonMultipart, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = get_user_from_token!(jar, data, oauth::AppScope::UserCreateQuestions); if req.is_global && user.is_none() { return Json(Error::NotAllowed.into()); } // get real ip let real_ip = headers .get(data.0.0.security.real_ip_header.to_owned()) .unwrap_or(&HeaderValue::from_static("")) .to_str() .unwrap_or("") .to_string(); // check for ip ban if data .get_ipban_by_addr(RemoteAddr::from(real_ip.as_str())) .await .is_ok() { return Json(Error::NotAllowed.into()); } // award achievement if let Some(ref user) = user { let mut user = user.clone(); if let Err(e) = data .add_achievement(&mut user, AchievementName::CreateQuestion.into()) .await { return Json(e.into()); } if drawings.len() > 0 { if let Err(e) = data .add_achievement(&mut user, AchievementName::CreateDrawing.into()) .await { return Json(e.into()); } } } // ... let mut props = Question::new( if let Some(ref ua) = user { ua.id } else { 0 }, match req.receiver.parse::() { Ok(x) => x, Err(e) => return Json(Error::MiscError(e.to_string()).into()), }, req.content, req.is_global, real_ip, ); if !req.community.is_empty() { props.is_global = true; props.receiver = 0; props.community = match req.community.parse::() { Ok(x) => x, Err(e) => return Json(Error::MiscError(e.to_string()).into()), } } match data .create_question(props, drawings.iter().map(|x| x.to_vec()).collect()) .await { Ok(id) => Json(ApiReturn { ok: true, message: "Question created".to_string(), payload: Some(id.to_string()), }), Err(e) => Json(e.into()), } } pub async fn delete_request( jar: CookieJar, Extension(data): Extension, Path(id): Path, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = match get_user_from_token!(jar, data, oauth::AppScope::UserDeleteQuestions) { Some(ua) => ua, None => return Json(Error::NotAllowed.into()), }; match data.delete_question(id, &user).await { Ok(_) => Json(ApiReturn { ok: true, message: "Question deleted".to_string(), payload: (), }), Err(e) => Json(e.into()), } } pub async fn ip_block_request( jar: CookieJar, Extension(data): Extension, Path(id): Path, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = match get_user_from_token!(jar, data, oauth::AppScope::UserCreateIpBlock) { Some(ua) => ua, None => return Json(Error::NotAllowed.into()), }; // get question let question = match data.get_question_by_id(id).await { Ok(q) => q, Err(e) => return Json(e.into()), }; // check for an existing ip block if data .get_ipblock_by_initiator_receiver(user.id, &question.ip) .await .is_ok() { return Json(Error::NotAllowed.into()); } // create ip block match data .create_ipblock(IpBlock::new(user.id, question.ip)) .await { Ok(_) => Json(ApiReturn { ok: true, message: "IP blocked".to_string(), payload: (), }), Err(e) => Json(e.into()), } } /// Get questions by the current user. pub async fn outbox_request( jar: CookieJar, Path(id): Path, Extension(data): Extension, Query(props): Query, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadQuestions) { Some(ua) => ua, None => return Json(Error::NotAllowed.into()), }; if user.id != id && !user.permissions.check(FinePermission::MANAGE_QUESTIONS) { return Json(Error::NotAllowed.into()); } match data .get_questions_by_owner_paginated(id, 12, props.page) .await { Ok(questions) => { let ignore_users = crate::ignore_users_gen!(user!, #data); Json(ApiReturn { ok: true, message: "Success".to_string(), payload: match data.fill_questions(questions, &ignore_users).await { Ok(l) => Some(data.questions_owner_filter(&l)), Err(e) => return Json(e.into()), }, }) } Err(e) => Json(e.into()), } } /// Get questions in the given community. pub async fn community_questions_request( jar: CookieJar, Path(id): Path, Extension(data): Extension, Query(props): Query, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadQuestions) { Some(ua) => ua, None => return Json(Error::NotAllowed.into()), }; let community = match data.get_community_by_id(id).await { Ok(c) => c, Err(e) => return Json(e.into()), }; if community.read_access == CommunityReadAccess::Joined { if data .get_membership_by_owner_community_no_void(user.id, community.id) .await .is_err() { return Json(Error::NotAllowed.into()); } } match data.get_questions_by_community(id, 12, props.page).await { Ok(questions) => { let ignore_users = crate::ignore_users_gen!(user!, #data); Json(ApiReturn { ok: true, message: "Success".to_string(), payload: match data.fill_questions(questions, &ignore_users).await { Ok(l) => Some(data.questions_owner_filter(&l)), Err(e) => return Json(e.into()), }, }) } Err(e) => Json(e.into()), } } /// Get all questions (from user communities). pub async fn from_communities_request( jar: CookieJar, Extension(data): Extension, Query(props): Query, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadQuestions) { Some(ua) => ua, None => return Json(Error::NotAllowed.into()), }; match data .get_questions_from_user_communities(user.id, 12, props.page) .await { Ok(questions) => { let ignore_users = crate::ignore_users_gen!(user!, #data); Json(ApiReturn { ok: true, message: "Success".to_string(), payload: match data.fill_questions(questions, &ignore_users).await { Ok(l) => Some(data.questions_owner_filter(&l)), Err(e) => return Json(e.into()), }, }) } Err(e) => Json(e.into()), } } /// Get all posts (by likes). pub async fn popular_request( jar: CookieJar, Extension(data): Extension, Query(props): Query, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadQuestions) { Some(ua) => ua, None => return Json(Error::NotAllowed.into()), }; match data.get_popular_posts(12, props.page, 604_800_000).await { Ok(posts) => { let ignore_users = crate::ignore_users_gen!(user!, #data); Json(ApiReturn { ok: true, message: "Success".to_string(), payload: match data .fill_posts_with_community(posts, user.id, &ignore_users, &Some(user.clone())) .await { Ok(l) => data.posts_owner_filter( &data.posts_muted_phrase_filter(&l, Some(&user.settings.muted)), ), Err(e) => return Json(e.into()), }, }) } Err(e) => Json(e.into()), } } /// Get all questions (from any community). pub async fn all_request( jar: CookieJar, Extension(data): Extension, Query(props): Query, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadQuestions) { Some(ua) => ua, None => return Json(Error::NotAllowed.into()), }; match data.get_latest_global_questions(12, props.page).await { Ok(questions) => { let ignore_users = crate::ignore_users_gen!(user!, #data); Json(ApiReturn { ok: true, message: "Success".to_string(), payload: match data.fill_questions(questions, &ignore_users).await { Ok(l) => Some(data.questions_owner_filter(&l)), Err(e) => return Json(e.into()), }, }) } Err(e) => Json(e.into()), } } /// Get all questions (from following). pub async fn following_request( jar: CookieJar, Extension(data): Extension, Query(props): Query, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadQuestions) { Some(ua) => ua, None => return Json(Error::NotAllowed.into()), }; match data .get_questions_from_user_following(user.id, 12, props.page) .await { Ok(questions) => { let ignore_users = crate::ignore_users_gen!(user!, #data); Json(ApiReturn { ok: true, message: "Success".to_string(), payload: match data.fill_questions(questions, &ignore_users).await { Ok(l) => Some(data.questions_owner_filter(&l)), Err(e) => return Json(e.into()), }, }) } Err(e) => Json(e.into()), } } /// Get a single question. pub async fn get_request( jar: CookieJar, Path(id): Path, Extension(data): Extension, ) -> impl IntoResponse { let data = &(data.read().await).0; if get_user_from_token!(jar, data, oauth::AppScope::UserReadQuestions).is_none() { return Json(Error::NotAllowed.into()); } match data.get_question_by_id(id).await { Ok(p) => Json(ApiReturn { ok: true, message: "Success".to_string(), payload: Some(p), }), Err(e) => Json(e.into()), } } /// Get answers for the given question. pub async fn answers_request( jar: CookieJar, Path(id): Path, Extension(data): Extension, Query(props): Query, ) -> impl IntoResponse { let data = &(data.read().await).0; let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadPosts) { Some(ua) => ua, None => return Json(Error::NotAllowed.into()), }; match data.get_posts_by_question(id, 12, props.page).await { Ok(posts) => { let ignore_users = crate::ignore_users_gen!(user!, #data); Json(ApiReturn { ok: true, message: "Success".to_string(), payload: match data .fill_posts_with_community(posts, user.id, &ignore_users, &Some(user.clone())) .await { Ok(l) => data.posts_owner_filter( &data.posts_muted_phrase_filter(&l, Some(&user.settings.muted)), ), Err(e) => return Json(e.into()), }, }) } Err(e) => Json(e.into()), } }