add: implement 9 new scopes, 21 new api endpoints

This commit is contained in:
trisua 2025-06-13 17:47:00 -04:00
parent c3139ef1d2
commit 8f16068a34
14 changed files with 973 additions and 35 deletions

View file

@ -292,6 +292,48 @@ macro_rules! check_user_blocked_or_private {
}
}
};
($user:expr, $other_user:ident, $data:ident, @api) => {
// check if we're blocked
if let Some(ref ua) = $user {
if $data
.get_userblock_by_initiator_receiver($other_user.id, ua.id)
.await
.is_ok()
&& !ua
.permissions
.check(tetratto_core::model::permissions::FinePermission::MANAGE_USERS)
{
return Json(
tetratto_core::model::Error::MiscError("You're blocked".to_string()).into(),
);
}
}
// check for private profile
if $other_user.settings.private_profile {
if let Some(ref ua) = $user {
if (ua.id != $other_user.id)
&& !ua
.permissions
.check(tetratto_core::model::permissions::FinePermission::MANAGE_USERS)
&& $data
.get_userfollow_by_initiator_receiver($other_user.id, ua.id)
.await
.is_err()
{
return Json(
tetratto_core::model::Error::MiscError("Profile is private".to_string())
.into(),
);
}
} else {
return Json(
tetratto_core::model::Error::MiscError("Profile is private".to_string()).into(),
);
}
}
};
}
#[macro_export]
@ -318,4 +360,12 @@ macro_rules! ignore_users_gen {
]
.concat()
};
($user:ident!, #$data:ident) => {
[
$data.get_userblocks_receivers($user.id).await,
$data.get_userblocks_initiator_by_receivers($user.id).await,
]
.concat()
};
}

View file

@ -1,10 +1,19 @@
use crate::{
State, get_user_from_token,
check_user_blocked_or_private, get_user_from_token,
model::{ApiReturn, Error},
routes::pages::PaginatedQuery,
State,
};
use axum::{
extract::{Path, Query},
response::IntoResponse,
Extension, Json,
};
use axum::{Extension, Json, extract::Path, response::IntoResponse};
use axum_extra::extract::CookieJar;
use tetratto_core::model::auth::{FollowResult, IpBlock, Notification, UserBlock, UserFollow};
use tetratto_core::model::{
auth::{FollowResult, IpBlock, Notification, UserBlock, UserFollow},
oauth,
};
/// Toggle following on the given user.
pub async fn follow_request(
@ -13,7 +22,7 @@ pub async fn follow_request(
Extension(data): Extension<State>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageFollowing) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -74,7 +83,7 @@ pub async fn cancel_follow_request(
Path(id): Path<usize>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageFollowing) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -95,7 +104,7 @@ pub async fn accept_follow_request(
Path(id): Path<usize>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageFollowers) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -142,7 +151,7 @@ pub async fn block_request(
Extension(data): Extension<State>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageBlocks) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -205,7 +214,7 @@ pub async fn ip_block_request(
Extension(data): Extension<State>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserCreateIpBlock) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -232,3 +241,67 @@ pub async fn ip_block_request(
}
}
}
/// Get the followers of the given user.
pub async fn followers_request(
jar: CookieJar,
Path(id): Path<usize>,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadProfiles) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
let other_user = match data.get_user_by_id(id).await {
Ok(ua) => ua,
Err(e) => return Json(e.into()),
};
check_user_blocked_or_private!(Some(&user), other_user, data, @api);
match data.get_userfollows_by_receiver(id, 12, props.page).await {
Ok(f) => Json(ApiReturn {
ok: true,
message: "Success".to_string(),
payload: match data.fill_userfollows_with_initiator(f).await {
Ok(f) => Some(data.userfollows_user_filter(&f)),
Err(e) => return Json(e.into()),
},
}),
Err(e) => Json(e.into()),
}
}
/// Get the following of the given user.
pub async fn following_request(
jar: CookieJar,
Path(id): Path<usize>,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadProfiles) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
let other_user = match data.get_user_by_id(id).await {
Ok(ua) => ua,
Err(e) => return Json(e.into()),
};
check_user_blocked_or_private!(Some(&user), other_user, data, @api);
match data.get_userfollows_by_initiator(id, 12, props.page).await {
Ok(f) => Json(ApiReturn {
ok: true,
message: "Success".to_string(),
payload: match data.fill_userfollows_with_receiver(f).await {
Ok(f) => Some(data.userfollows_user_filter(&f)),
Err(e) => return Json(e.into()),
},
}),
Err(e) => Json(e.into()),
}
}

View file

@ -1,12 +1,16 @@
use crate::{
get_user_from_token,
model::{ApiReturn, Error},
routes::api::v1::CreateUserWarning,
routes::{api::v1::CreateUserWarning, pages::PaginatedQuery},
State,
};
use axum::{Extension, Json, extract::Path, response::IntoResponse};
use axum::{
extract::{Path, Query},
response::IntoResponse,
Extension, Json,
};
use axum_extra::extract::CookieJar;
use tetratto_core::model::{auth::UserWarning, permissions::FinePermission};
use tetratto_core::model::{auth::UserWarning, oauth, permissions::FinePermission};
/// Create a new user warning.
pub async fn create_request(
@ -16,7 +20,7 @@ pub async fn create_request(
Json(req): Json<CreateUserWarning>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::ModManageWarnings) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -45,7 +49,7 @@ pub async fn delete_request(
Extension(data): Extension<State>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::ModManageWarnings) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -63,3 +67,58 @@ pub async fn delete_request(
Err(e) => Json(e.into()),
}
}
/// Get all warnings for the given user.
pub async fn on_user_request(
jar: CookieJar,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data, oauth::AppScope::ModManageWarnings) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
if !user.permissions.check(FinePermission::MANAGE_WARNINGS) {
return Json(Error::NotAllowed.into());
}
match data
.get_user_warnings_by_user(user.id, 12, props.page)
.await
{
Ok(w) => Json(ApiReturn {
ok: true,
message: "Success".to_string(),
payload: Some(w),
}),
Err(e) => Json(e.into()),
}
}
/// Get a single warning.
pub async fn get_request(
jar: CookieJar,
Path(id): Path<usize>,
Extension(data): Extension<State>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data, oauth::AppScope::ModManageWarnings) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
if !user.permissions.check(FinePermission::MANAGE_WARNINGS) {
return Json(Error::NotAllowed.into());
}
match data.get_user_warning_by_id(id).await {
Ok(w) => Json(ApiReturn {
ok: true,
message: "Success".to_string(),
payload: Some(w),
}),
Err(e) => Json(e.into()),
}
}

View file

@ -1,5 +1,5 @@
use axum::{
extract::Path,
extract::{Path, Query},
http::{HeaderMap, HeaderValue},
response::IntoResponse,
Extension, Json,
@ -14,11 +14,14 @@ use tetratto_core::model::{
ApiReturn, Error,
};
use crate::{
get_user_from_token,
check_user_blocked_or_private, get_user_from_token,
image::{save_webp_buffer, JsonMultipart},
routes::api::v1::{
CreatePost, CreateRepost, UpdatePostContent, UpdatePostContext, UpdatePostIsOpen,
VoteInPoll,
routes::{
api::v1::{
CreatePost, CreateRepost, UpdatePostContent, UpdatePostContext, UpdatePostIsOpen,
VoteInPoll,
},
pages::{PaginatedQuery, SearchedQuery},
},
State,
};
@ -409,3 +412,550 @@ pub async fn update_is_open_request(
Err(e) => Json(e.into()),
}
}
/// Get posts by the given user.
pub async fn posts_request(
jar: CookieJar,
Path(id): Path<usize>,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> 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()),
};
let other_user = match data.get_user_by_id(id).await {
Ok(ua) => ua,
Err(e) => return Json(e.into()),
};
check_user_blocked_or_private!(Some(&user), other_user, data, @api);
match data
.get_posts_by_user(id, 12, props.page, &Some(user.clone()))
.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 posts in the given community.
pub async fn community_posts_request(
jar: CookieJar,
Path(id): Path<usize>,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> 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_community(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()),
}
}
/// Get replies by the given user.
pub async fn replies_request(
jar: CookieJar,
Path(id): Path<usize>,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> 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()),
};
let other_user = match data.get_user_by_id(id).await {
Ok(ua) => ua,
Err(e) => return Json(e.into()),
};
check_user_blocked_or_private!(Some(&user), other_user, data, @api);
match data
.get_replies_by_user(id, 12, props.page, &Some(user.clone()))
.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 posts (with media) by the given user.
pub async fn posts_with_media_request(
jar: CookieJar,
Path(id): Path<usize>,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> 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()),
};
let other_user = match data.get_user_by_id(id).await {
Ok(ua) => ua,
Err(e) => return Json(e.into()),
};
check_user_blocked_or_private!(Some(&user), other_user, data, @api);
match data
.get_media_posts_by_user(id, 12, props.page, &Some(user.clone()))
.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 posts (searched) by the given user.
pub async fn posts_searched_request(
jar: CookieJar,
Path(id): Path<usize>,
Extension(data): Extension<State>,
Query(props): Query<SearchedQuery>,
) -> 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()),
};
let other_user = match data.get_user_by_id(id).await {
Ok(ua) => ua,
Err(e) => return Json(e.into()),
};
check_user_blocked_or_private!(Some(&user), other_user, data, @api);
match data
.get_posts_by_user_searched(id, 12, props.page, &props.text, &Some(&user))
.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 posts (searched).
pub async fn all_posts_searched_request(
jar: CookieJar,
Extension(data): Extension<State>,
Query(props): Query<SearchedQuery>,
) -> 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()),
};
if !user.permissions.check(FinePermission::SUPPORTER) {
return Json(Error::RequiresSupporter.into());
}
match data.get_posts_searched(12, props.page, &props.text).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 posts (from user communities).
pub async fn from_communities_request(
jar: CookieJar,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> 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_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 posts (from stack).
pub async fn from_stack_request(
jar: CookieJar,
Path(id): Path<usize>,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> 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()),
};
let stack = match data.get_stack_by_id(id).await {
Ok(s) => s,
Err(e) => return Json(e.into()),
};
if stack.owner != user.id && !user.permissions.check(FinePermission::MANAGE_STACKS) {
return Json(Error::NotAllowed.into());
}
match data
.get_posts_from_stack(id, 12, props.page, stack.sort)
.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 posts (by likes).
pub async fn popular_request(
jar: CookieJar,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> 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_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 posts (from any community).
pub async fn all_request(
jar: CookieJar,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> 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_latest_posts(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()),
}
}
/// Get all posts (from following).
pub async fn following_request(
jar: CookieJar,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> 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_from_user_following(user.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()),
}
}
/// Get a single post.
pub async fn get_request(
jar: CookieJar,
Path(id): Path<usize>,
Extension(data): Extension<State>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
if get_user_from_token!(jar, data, oauth::AppScope::UserReadPosts).is_none() {
return Json(Error::NotAllowed.into());
}
match data.get_post_by_id(id).await {
Ok(p) => Json(ApiReturn {
ok: true,
message: "Success".to_string(),
payload: Some(p),
}),
Err(e) => Json(e.into()),
}
}
/// Get replies for the given post.
pub async fn post_replies_request(
jar: CookieJar,
Path(id): Path<usize>,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> 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_replies_by_post(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()),
}
}
/// Get reposts for the given post.
pub async fn reposts_request(
jar: CookieJar,
Path(id): Path<usize>,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> 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_reposts_by_quoting(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()),
}
}
/// Get quotes for the given post.
pub async fn quotes_request(
jar: CookieJar,
Path(id): Path<usize>,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> 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_quoting_posts_by_quoting(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()),
}
}

View file

@ -133,6 +133,60 @@ pub fn routes() -> Router {
"/posts/{id}/open",
post(communities::posts::update_is_open_request),
)
.route(
"/posts/from_user/{id}",
get(communities::posts::posts_request),
)
.route(
"/posts/from_user/{id}/replies",
get(communities::posts::replies_request),
)
.route(
"/posts/from_user/{id}/media",
get(communities::posts::posts_with_media_request),
)
.route(
"/posts/from_user/{id}/searched",
get(communities::posts::posts_searched_request),
)
.route(
"/posts/from_community/{id}",
get(communities::posts::community_posts_request),
)
.route(
"/posts/from_stack/{id}",
get(communities::posts::from_stack_request),
)
.route(
"/posts/searched",
get(communities::posts::all_posts_searched_request),
)
.route(
"/posts/timeline/communities",
get(communities::posts::from_communities_request),
)
.route(
"/posts/timeline/popular",
get(communities::posts::popular_request),
)
.route("/posts/timeline/all", get(communities::posts::all_request))
.route(
"/posts/timeline/following",
get(communities::posts::following_request),
)
.route("/posts/{id}", get(communities::posts::get_request))
.route(
"/posts/{id}/replies",
delete(communities::posts::post_replies_request),
)
.route(
"/posts/{id}/reposts",
delete(communities::posts::reposts_request),
)
.route(
"/posts/{id}/quotes",
delete(communities::posts::quotes_request),
)
// drafts
.route("/drafts", post(communities::drafts::create_request))
.route("/drafts/my", get(communities::drafts::get_drafts_request))
@ -251,12 +305,25 @@ pub fn routes() -> Router {
"/auth/user/{id}/_connect/{stream}/send",
post(auth::profile::post_to_socket_request),
)
.route(
"/auth/user/{id}/following",
get(auth::social::following_request),
)
.route(
"/auth/user/{id}/followers",
get(auth::social::followers_request),
)
// warnings
.route("/warnings/{id}", get(auth::user_warnings::get_request))
.route("/warnings/{id}", post(auth::user_warnings::create_request))
.route(
"/warnings/{id}",
delete(auth::user_warnings::delete_request),
)
.route(
"/warnings/on_user/{id}",
post(auth::user_warnings::on_user_request),
)
// notifications
.route(
"/notifications/my",
@ -275,6 +342,7 @@ pub fn routes() -> Router {
"/notifications/all/read_status",
post(notifications::update_all_read_status_request),
)
.route("/notifications/my", get(notifications::get_list_request))
// community memberships
.route(
"/communities/{id}/join",
@ -304,6 +372,7 @@ pub fn routes() -> Router {
delete(requests::delete_request),
)
.route("/requests/my", delete(requests::delete_all_request))
.route("/requests/my", get(requests::get_list_request))
// connections
.route(
"/auth/user/connections/_data",

View file

@ -1,8 +1,12 @@
use super::UpdateNotificationRead;
use crate::{State, get_user_from_token};
use axum::{Extension, Json, extract::Path, response::IntoResponse};
use crate::{get_user_from_token, routes::pages::PaginatedQuery, State};
use axum::{
extract::{Path, Query},
response::IntoResponse,
Extension, Json,
};
use axum_extra::extract::CookieJar;
use tetratto_core::model::{ApiReturn, Error};
use tetratto_core::model::{oauth, ApiReturn, Error};
pub async fn delete_request(
jar: CookieJar,
@ -10,7 +14,7 @@ pub async fn delete_request(
Path(id): Path<usize>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageNotifications) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -30,7 +34,7 @@ pub async fn delete_all_request(
Extension(data): Extension<State>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageNotifications) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -51,7 +55,7 @@ pub async fn delete_all_by_tag_request(
Path(tag): Path<String>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageNotifications) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -73,7 +77,7 @@ pub async fn update_read_status_request(
Json(req): Json<UpdateNotificationRead>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageNotifications) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -94,7 +98,7 @@ pub async fn update_all_read_status_request(
Json(req): Json<UpdateNotificationRead>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageNotifications) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -108,3 +112,27 @@ pub async fn update_all_read_status_request(
Err(e) => Json(e.into()),
}
}
pub async fn get_list_request(
jar: CookieJar,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadNotifications) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
match data
.get_notifications_by_owner_paginated(user.id, 12, props.page)
.await
{
Ok(l) => Json(ApiReturn {
ok: true,
message: "Success".to_string(),
payload: Some(l),
}),
Err(e) => Json(e.into()),
}
}

View file

@ -1,7 +1,11 @@
use crate::{State, get_user_from_token};
use axum::{Extension, Json, extract::Path, response::IntoResponse};
use crate::{get_user_from_token, routes::pages::PaginatedQuery, State};
use axum::{
extract::{Path, Query},
response::IntoResponse,
Extension, Json,
};
use axum_extra::extract::CookieJar;
use tetratto_core::model::{ApiReturn, Error};
use tetratto_core::model::{oauth, ApiReturn, Error};
pub async fn delete_request(
jar: CookieJar,
@ -9,7 +13,7 @@ pub async fn delete_request(
Path((id, linked_asset)): Path<(usize, usize)>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageRequests) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -29,7 +33,7 @@ pub async fn delete_all_request(
Extension(data): Extension<State>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageRequests) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@ -43,3 +47,27 @@ pub async fn delete_all_request(
Err(e) => Json(e.into()),
}
}
pub async fn get_list_request(
jar: CookieJar,
Extension(data): Extension<State>,
Query(props): Query<PaginatedQuery>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadRequests) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
match data
.get_requests_by_owner_paginated(user.id, 12, props.page)
.await
{
Ok(l) => Json(ApiReturn {
ok: true,
message: "Success".to_string(),
payload: Some(l),
}),
Err(e) => Json(e.into()),
}
}

View file

@ -738,7 +738,7 @@ pub async fn post_request(
// ...
let ignore_users = crate::ignore_users_gen!(user, data);
let feed = match data.0.get_post_comments(post.id, 12, props.page).await {
let feed = match data.0.get_replies_by_post(post.id, 12, props.page).await {
Ok(p) => match data.0.fill_posts(p, &ignore_users, &user).await {
Ok(p) => p,
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),