diff --git a/crates/api/src/site/admin_allow_instance.rs b/crates/api/src/site/admin_allow_instance.rs new file mode 100644 index 000000000..92de5e22b --- /dev/null +++ b/crates/api/src/site/admin_allow_instance.rs @@ -0,0 +1,46 @@ +use activitypub_federation::config::Data; +use actix_web::web::Json; +use lemmy_api_common::{ + context::LemmyContext, + site::AdminAllowInstanceParams, + utils::is_admin, + LemmyErrorType, + SuccessResponse, +}; +use lemmy_db_schema::source::{ + federation_allowlist::{AdminAllowInstance, AdminAllowInstanceForm}, + instance::Instance, +}; +use lemmy_db_views::structs::LocalUserView; +use lemmy_utils::error::LemmyResult; + +#[tracing::instrument(skip(context))] +pub async fn admin_allow_instance( + data: Json, + local_user_view: LocalUserView, + context: Data, +) -> LemmyResult> { + is_admin(&local_user_view)?; + + let allowlist = Instance::allowlist(&mut context.pool()).await?; + if !allowlist.is_empty() { + return Err( + LemmyErrorType::Unknown("Using allowlist requires that blocklist be empty".to_string()) + .into(), + ); + } + + let instance_block_form = AdminAllowInstanceForm { + instance_id: data.instance_id, + admin_person_id: local_user_view.person.id, + reason: data.reason.clone(), + }; + + if data.allow { + AdminAllowInstance::allow(&mut context.pool(), &instance_block_form).await?; + } else { + AdminAllowInstance::unallow(&mut context.pool(), data.instance_id).await?; + } + + Ok(Json(SuccessResponse::default())) +} diff --git a/crates/api/src/site/admin_block_instance.rs b/crates/api/src/site/admin_block_instance.rs index 81cfa62e6..b11bb0c6e 100644 --- a/crates/api/src/site/admin_block_instance.rs +++ b/crates/api/src/site/admin_block_instance.rs @@ -2,21 +2,34 @@ use activitypub_federation::config::Data; use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, - site::{AdminBlockInstanceParams, AdminBlockInstanceResponse}, + site::AdminBlockInstanceParams, utils::is_admin, + LemmyErrorType, + SuccessResponse, +}; +use lemmy_db_schema::source::{ + federation_blocklist::{AdminBlockInstance, AdminBlockInstanceForm}, + instance::Instance, }; -use lemmy_db_schema::source::federation_blocklist::{AdminBlockInstance, AdminBlockInstanceForm}; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::error::LemmyResult; #[tracing::instrument(skip(context))] -pub async fn block_instance( +pub async fn admin_block_instance( data: Json, local_user_view: LocalUserView, context: Data, -) -> LemmyResult> { +) -> LemmyResult> { is_admin(&local_user_view)?; + let allowlist = Instance::allowlist(&mut context.pool()).await?; + if !allowlist.is_empty() { + return Err( + LemmyErrorType::Unknown("Using blocklist requires that allowlist be empty".to_string()) + .into(), + ); + } + let instance_block_form = AdminBlockInstanceForm { instance_id: data.instance_id, admin_person_id: local_user_view.person.id, @@ -30,7 +43,5 @@ pub async fn block_instance( AdminBlockInstance::unblock(&mut context.pool(), data.instance_id).await?; } - Ok(Json(AdminBlockInstanceResponse { - blocked: data.block, - })) + Ok(Json(SuccessResponse::default())) } diff --git a/crates/api/src/site/mod.rs b/crates/api/src/site/mod.rs index 9b8d8e3e9..52e882bb3 100644 --- a/crates/api/src/site/mod.rs +++ b/crates/api/src/site/mod.rs @@ -1,3 +1,4 @@ +pub mod admin_allow_instance; pub mod admin_block_instance; pub mod federated_instances; pub mod leave_admin; diff --git a/crates/api/src/site/mod_log.rs b/crates/api/src/site/mod_log.rs index 41a7e148b..bbf147666 100644 --- a/crates/api/src/site/mod_log.rs +++ b/crates/api/src/site/mod_log.rs @@ -7,6 +7,7 @@ use lemmy_api_common::{ use lemmy_db_schema::{source::local_site::LocalSite, ModlogActionType}; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_moderator::structs::{ + AdminAllowInstanceView, AdminBlockInstanceView, AdminPurgeCommentView, AdminPurgeCommunityView, @@ -123,6 +124,7 @@ pub async fn get_mod_log( admin_purged_posts, admin_purged_comments, admin_block_instance, + admin_allow_instance, ) = if data.community_id.is_none() { ( match type_ { @@ -169,6 +171,12 @@ pub async fn get_mod_log( } _ => Default::default(), }, + match type_ { + All | AdminAllowInstance if other_person_id.is_none() => { + AdminAllowInstanceView::list(&mut context.pool(), params).await? + } + _ => Default::default(), + }, ) } else { Default::default() @@ -192,5 +200,6 @@ pub async fn get_mod_log( admin_purged_comments, hidden_communities, admin_block_instance, + admin_allow_instance, })) } diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs index 5d4220865..9a0425048 100644 --- a/crates/api_common/src/site.rs +++ b/crates/api_common/src/site.rs @@ -43,6 +43,7 @@ use lemmy_db_views_actor::structs::{ PersonView, }; use lemmy_db_views_moderator::structs::{ + AdminAllowInstanceView, AdminBlockInstanceView, AdminPurgeCommentView, AdminPurgeCommunityView, @@ -185,6 +186,7 @@ pub struct GetModlogResponse { pub admin_purged_comments: Vec, pub hidden_communities: Vec, pub admin_block_instance: Vec, + pub admin_allow_instance: Vec, } #[skip_serializing_none] @@ -678,6 +680,9 @@ pub struct AdminBlockInstanceParams { #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] -pub struct AdminBlockInstanceResponse { - pub blocked: bool, +pub struct AdminAllowInstanceParams { + pub instance_id: InstanceId, + pub allow: bool, + #[cfg_attr(feature = "full", ts(optional))] + pub reason: Option, } diff --git a/crates/db_schema/src/impls/federation_allowlist.rs b/crates/db_schema/src/impls/federation_allowlist.rs index 099e0b231..b448ba24e 100644 --- a/crates/db_schema/src/impls/federation_allowlist.rs +++ b/crates/db_schema/src/impls/federation_allowlist.rs @@ -1,97 +1,48 @@ use crate::{ - schema::federation_allowlist, - source::{ - federation_allowlist::{FederationAllowList, FederationAllowListForm}, - instance::Instance, + newtypes::InstanceId, + schema::{admin_allow_instance, federation_allowlist}, + source::federation_allowlist::{ + AdminAllowInstance, + AdminAllowInstanceForm, + FederationAllowListForm, }, utils::{get_conn, DbPool}, }; -use diesel::{dsl::insert_into, result::Error}; -use diesel_async::{AsyncPgConnection, RunQueryDsl}; +use diesel::{delete, dsl::insert_into, result::Error, ExpressionMethods, QueryDsl}; +use diesel_async::RunQueryDsl; -impl FederationAllowList { - pub async fn replace(pool: &mut DbPool<'_>, list_opt: Option>) -> Result<(), Error> { +impl AdminAllowInstance { + pub async fn allow(pool: &mut DbPool<'_>, form: &AdminAllowInstanceForm) -> Result<(), Error> { let conn = &mut get_conn(pool).await?; conn .build_transaction() .run(|conn| { Box::pin(async move { - if let Some(list) = list_opt { - Self::clear(conn).await?; + insert_into(admin_allow_instance::table) + .values(form) + .execute(conn) + .await?; - for domain in list { - // Upsert all of these as instances - let instance = Instance::read_or_create(&mut conn.into(), domain).await?; - - let form = FederationAllowListForm { - instance_id: instance.id, - updated: None, - }; - insert_into(federation_allowlist::table) - .values(form) - .get_result::(conn) - .await?; - } - Ok(()) - } else { - Ok(()) - } - }) as _ + let form2 = FederationAllowListForm { + instance_id: form.instance_id, + updated: None, + }; + insert_into(federation_allowlist::table) + .values(form2) + .execute(conn) + .await?; + Ok(()) + }) }) .await } - - async fn clear(conn: &mut AsyncPgConnection) -> Result { - diesel::delete(federation_allowlist::table) - .execute(conn) - .await - } -} -#[cfg(test)] -mod tests { - - use crate::{ - source::{federation_allowlist::FederationAllowList, instance::Instance}, - utils::build_db_pool_for_tests, - }; - use diesel::result::Error; - use pretty_assertions::assert_eq; - use serial_test::serial; - - #[tokio::test] - #[serial] - async fn test_allowlist_insert_and_clear() -> Result<(), Error> { - let pool = &build_db_pool_for_tests(); - let pool = &mut pool.into(); - let domains = vec![ - "tld1.xyz".to_string(), - "tld2.xyz".to_string(), - "tld3.xyz".to_string(), - ]; - - let allowed = Some(domains.clone()); - - FederationAllowList::replace(pool, allowed).await?; - - let allows = Instance::allowlist(pool).await?; - let allows_domains = allows - .iter() - .map(|i| i.domain.clone()) - .collect::>(); - - assert_eq!(3, allows.len()); - assert_eq!(domains, allows_domains); - - // Now test clearing them via Some(empty vec) - let clear_allows = Some(Vec::new()); - - FederationAllowList::replace(pool, clear_allows).await?; - let allows = Instance::allowlist(pool).await?; - - assert_eq!(0, allows.len()); - - Instance::delete_all(pool).await?; - + pub async fn unallow(pool: &mut DbPool<'_>, instance_id: InstanceId) -> Result<(), Error> { + let conn = &mut get_conn(pool).await?; + delete( + federation_allowlist::table.filter(federation_allowlist::dsl::instance_id.eq(instance_id)), + ) + .execute(conn) + .await?; Ok(()) } } diff --git a/crates/db_schema/src/lib.rs b/crates/db_schema/src/lib.rs index a67e1b0e3..ad6e93619 100644 --- a/crates/db_schema/src/lib.rs +++ b/crates/db_schema/src/lib.rs @@ -216,6 +216,7 @@ pub enum ModlogActionType { AdminPurgePost, AdminPurgeComment, AdminBlockInstance, + AdminAllowInstance, } #[derive( diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs index 4dd6db601..e714bf1aa 100644 --- a/crates/db_schema/src/schema.rs +++ b/crates/db_schema/src/schema.rs @@ -42,6 +42,16 @@ pub mod sql_types { pub struct RegistrationModeEnum; } +diesel::table! { + admin_allow_instance (id) { + id -> Int4, + instance_id -> Int4, + admin_person_id -> Int4, + reason -> Nullable, + published -> Timestamptz, + } +} + diesel::table! { admin_block_instance (id) { id -> Int4, @@ -943,6 +953,8 @@ diesel::table! { } } +diesel::joinable!(admin_allow_instance -> instance (instance_id)); +diesel::joinable!(admin_allow_instance -> person (admin_person_id)); diesel::joinable!(admin_block_instance -> instance (instance_id)); diesel::joinable!(admin_block_instance -> person (admin_person_id)); diesel::joinable!(admin_purge_comment -> person (admin_person_id)); @@ -1022,6 +1034,7 @@ diesel::joinable!(site_language -> language (language_id)); diesel::joinable!(site_language -> site (site_id)); diesel::allow_tables_to_appear_in_same_query!( + admin_allow_instance, admin_block_instance, admin_purge_comment, admin_purge_community, diff --git a/crates/db_schema/src/source/federation_allowlist.rs b/crates/db_schema/src/source/federation_allowlist.rs index cc66bcad8..56afea4b4 100644 --- a/crates/db_schema/src/source/federation_allowlist.rs +++ b/crates/db_schema/src/source/federation_allowlist.rs @@ -1,9 +1,12 @@ -use crate::newtypes::InstanceId; -#[cfg(feature = "full")] -use crate::schema::federation_allowlist; +use crate::newtypes::{InstanceId, PersonId}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::fmt::Debug; +#[cfg(feature = "full")] +use { + crate::schema::{admin_allow_instance, federation_allowlist}, + ts_rs::TS, +}; #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] #[cfg_attr( @@ -26,7 +29,38 @@ pub struct FederationAllowList { #[derive(Clone, Default)] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = federation_allowlist))] -pub struct FederationAllowListForm { +pub(crate) struct FederationAllowListForm { pub instance_id: InstanceId, pub updated: Option>, } + +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] +#[cfg_attr( + feature = "full", + derive(TS, Queryable, Selectable, Associations, Identifiable) +)] +#[cfg_attr( + feature = "full", + diesel(belongs_to(crate::source::instance::Instance)) +)] +#[cfg_attr(feature = "full", diesel(table_name = admin_allow_instance))] +#[cfg_attr(feature = "full", diesel(primary_key(instance_id)))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +pub struct AdminAllowInstance { + pub id: i32, + pub instance_id: InstanceId, + pub admin_person_id: PersonId, + #[cfg_attr(feature = "full", ts(optional))] + pub reason: Option, + pub published: DateTime, +} + +#[derive(Clone, Default)] +#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = admin_allow_instance))] +pub struct AdminAllowInstanceForm { + pub instance_id: InstanceId, + pub admin_person_id: PersonId, + pub reason: Option, +} diff --git a/crates/db_schema/src/source/federation_blocklist.rs b/crates/db_schema/src/source/federation_blocklist.rs index 18eb9e060..52e8773b2 100644 --- a/crates/db_schema/src/source/federation_blocklist.rs +++ b/crates/db_schema/src/source/federation_blocklist.rs @@ -33,7 +33,7 @@ pub struct FederationBlockList { #[derive(Clone, Default)] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = federation_blocklist))] -pub struct FederationBlockListForm { +pub(crate) struct FederationBlockListForm { pub instance_id: InstanceId, pub updated: Option>, pub block_expires: Option>, diff --git a/crates/db_views_moderator/src/admin_allow_instance.rs b/crates/db_views_moderator/src/admin_allow_instance.rs new file mode 100644 index 000000000..9489e194b --- /dev/null +++ b/crates/db_views_moderator/src/admin_allow_instance.rs @@ -0,0 +1,52 @@ +use crate::structs::{AdminAllowInstanceView, ModlogListParams}; +use diesel::{ + result::Error, + BoolExpressionMethods, + ExpressionMethods, + IntoSql, + JoinOnDsl, + NullableExpressionMethods, + QueryDsl, +}; +use diesel_async::RunQueryDsl; +use lemmy_db_schema::{ + newtypes::PersonId, + schema::{admin_allow_instance, instance, person}, + utils::{get_conn, limit_and_offset, DbPool}, +}; + +impl AdminAllowInstanceView { + pub async fn list(pool: &mut DbPool<'_>, params: ModlogListParams) -> Result, Error> { + let conn = &mut get_conn(pool).await?; + + let admin_person_id_join = params.mod_person_id.unwrap_or(PersonId(-1)); + let show_mod_names = !params.hide_modlog_names; + let show_mod_names_expr = show_mod_names.as_sql::(); + + let admin_names_join = admin_allow_instance::admin_person_id + .eq(person::id) + .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); + let mut query = admin_allow_instance::table + .left_join(person::table.on(admin_names_join)) + .inner_join(instance::table) + .select(( + admin_allow_instance::all_columns, + instance::all_columns, + person::all_columns.nullable(), + )) + .into_boxed(); + + if let Some(admin_person_id) = params.mod_person_id { + query = query.filter(admin_allow_instance::admin_person_id.eq(admin_person_id)); + }; + + let (limit, offset) = limit_and_offset(params.page, params.limit)?; + + query + .limit(limit) + .offset(offset) + .order_by(admin_allow_instance::published.desc()) + .load::(conn) + .await + } +} diff --git a/crates/db_views_moderator/src/admin_block_instance.rs b/crates/db_views_moderator/src/admin_block_instance.rs index e0398e703..3b267bd63 100644 --- a/crates/db_views_moderator/src/admin_block_instance.rs +++ b/crates/db_views_moderator/src/admin_block_instance.rs @@ -46,7 +46,7 @@ impl AdminBlockInstanceView { .limit(limit) .offset(offset) .order_by(admin_block_instance::published.desc()) - .load::(conn) + .load::(conn) .await } } diff --git a/crates/db_views_moderator/src/lib.rs b/crates/db_views_moderator/src/lib.rs index 2da1555b5..5748707c6 100644 --- a/crates/db_views_moderator/src/lib.rs +++ b/crates/db_views_moderator/src/lib.rs @@ -1,4 +1,6 @@ #[cfg(feature = "full")] +pub mod admin_allow_instance; +#[cfg(feature = "full")] pub mod admin_block_instance; #[cfg(feature = "full")] pub mod admin_purge_comment_view; diff --git a/crates/db_views_moderator/src/structs.rs b/crates/db_views_moderator/src/structs.rs index eeffb8a22..bc8a638aa 100644 --- a/crates/db_views_moderator/src/structs.rs +++ b/crates/db_views_moderator/src/structs.rs @@ -5,6 +5,7 @@ use lemmy_db_schema::{ source::{ comment::Comment, community::Community, + federation_allowlist::AdminAllowInstance, federation_blocklist::AdminBlockInstance, instance::Instance, moderator::{ @@ -248,6 +249,19 @@ pub struct AdminBlockInstanceView { pub admin: Option, } +#[skip_serializing_none] +#[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// When an admin purges a post. +pub struct AdminAllowInstanceView { + pub admin_block_instance: AdminAllowInstance, + pub instance: Instance, + #[cfg_attr(feature = "full", ts(optional))] + pub admin: Option, +} + #[skip_serializing_none] #[derive(Debug, Serialize, Deserialize, Clone, Copy)] #[cfg_attr(feature = "full", derive(TS, Queryable))] diff --git a/crates/federate/src/lib.rs b/crates/federate/src/lib.rs index 6324e175d..eed13d36b 100644 --- a/crates/federate/src/lib.rs +++ b/crates/federate/src/lib.rs @@ -201,7 +201,7 @@ mod test { use chrono::DateTime; use lemmy_db_schema::{ source::{ - federation_allowlist::FederationAllowList, + federation_allowlist::{AdminAllowInstance, AdminAllowInstanceForm}, federation_blocklist::{AdminBlockInstance, AdminBlockInstanceForm}, instance::InstanceForm, person::{Person, PersonInsertForm}, @@ -349,13 +349,21 @@ mod test { async fn test_send_manager_allowed() -> LemmyResult<()> { let mut data = TestData::init(1, 1).await?; - let domain = data.instances[0].domain.clone(); - FederationAllowList::replace(&mut data.context.pool(), Some(vec![domain])).await?; + let instance_id = data.instances[0].id; + let form = PersonInsertForm::new("tim".to_string(), String::new(), instance_id); + let person = Person::create(&mut data.context.pool(), &form).await?; + let form = AdminAllowInstanceForm { + instance_id: data.instances[0].id, + admin_person_id: person.id, + reason: None, + }; + AdminAllowInstance::allow(&mut data.context.pool(), &form).await?; data.run().await?; let workers = &data.send_manager.workers; assert_eq!(1, workers.len()); assert!(workers.contains_key(&data.instances[0].id)); + Person::delete(&mut data.context.pool(), person.id).await?; data.cleanup().await?; Ok(()) } diff --git a/migrations/2024-11-19-142005_instance-block-mod-log/down.sql b/migrations/2024-11-19-142005_instance-block-mod-log/down.sql index 98ab9e650..c67409c89 100644 --- a/migrations/2024-11-19-142005_instance-block-mod-log/down.sql +++ b/migrations/2024-11-19-142005_instance-block-mod-log/down.sql @@ -3,3 +3,5 @@ ALTER TABLE federation_blocklist DROP TABLE admin_block_instance; +DROP TABLE admin_allow_instance; + diff --git a/migrations/2024-11-19-142005_instance-block-mod-log/up.sql b/migrations/2024-11-19-142005_instance-block-mod-log/up.sql index 44d6bcbe2..53f91195f 100644 --- a/migrations/2024-11-19-142005_instance-block-mod-log/up.sql +++ b/migrations/2024-11-19-142005_instance-block-mod-log/up.sql @@ -10,3 +10,11 @@ CREATE TABLE admin_block_instance ( published timestamptz NOT NULL DEFAULT now() ); +CREATE TABLE admin_allow_instance ( + id serial PRIMARY KEY, + instance_id int NOT NULL REFERENCES instance (id) ON UPDATE CASCADE ON DELETE CASCADE, + admin_person_id int NOT NULL REFERENCES person (id) ON UPDATE CASCADE ON DELETE CASCADE, + reason text, + published timestamptz NOT NULL DEFAULT now() +); + diff --git a/src/api_routes_http.rs b/src/api_routes_http.rs index 9b6daea81..4a648da00 100644 --- a/src/api_routes_http.rs +++ b/src/api_routes_http.rs @@ -1,4 +1,4 @@ -use actix_web::{guard, web}; +use actix_web::{guard, web::*}; use lemmy_api::{ comment::{ distinguish::distinguish_comment, @@ -76,6 +76,8 @@ use lemmy_api::{ resolve::resolve_pm_report, }, site::{ + admin_allow_instance::admin_allow_instance, + admin_block_instance::admin_block_instance, federated_instances::get_federated_instances, leave_admin::leave_admin, list_all_media::list_all_media, @@ -159,146 +161,146 @@ use lemmy_apub::api::{ use lemmy_routes::images::image_proxy; use lemmy_utils::rate_limit::RateLimitCell; -pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { +pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { cfg.service( - web::scope("/api/v3") - .route("/image_proxy", web::get().to(image_proxy)) + scope("/api/v3") + .route("/image_proxy", get().to(image_proxy)) // Site .service( - web::scope("/site") + scope("/site") .wrap(rate_limit.message()) - .route("", web::get().to(get_site)) + .route("", get().to(get_site)) // Admin Actions - .route("", web::post().to(create_site)) - .route("", web::put().to(update_site)) - .route("/block", web::post().to(user_block_instance)), + .route("", post().to(create_site)) + .route("", put().to(update_site)) + .route("/block", post().to(user_block_instance)), ) .service( - web::resource("/modlog") + resource("/modlog") .wrap(rate_limit.message()) - .route(web::get().to(get_mod_log)), + .route(get().to(get_mod_log)), ) .service( - web::resource("/search") + resource("/search") .wrap(rate_limit.search()) - .route(web::get().to(search)), + .route(get().to(search)), ) .service( - web::resource("/resolve_object") + resource("/resolve_object") .wrap(rate_limit.message()) - .route(web::get().to(resolve_object)), + .route(get().to(resolve_object)), ) // Community .service( - web::resource("/community") + resource("/community") .guard(guard::Post()) .wrap(rate_limit.register()) - .route(web::post().to(create_community)), + .route(post().to(create_community)), ) .service( - web::scope("/community") + scope("/community") .wrap(rate_limit.message()) - .route("", web::get().to(get_community)) - .route("", web::put().to(update_community)) - .route("/random", web::get().to(get_random_community)) - .route("/hide", web::put().to(hide_community)) - .route("/list", web::get().to(list_communities)) - .route("/follow", web::post().to(follow_community)) - .route("/block", web::post().to(block_community)) - .route("/delete", web::post().to(delete_community)) + .route("", get().to(get_community)) + .route("", put().to(update_community)) + .route("/random", get().to(get_random_community)) + .route("/hide", put().to(hide_community)) + .route("/list", get().to(list_communities)) + .route("/follow", post().to(follow_community)) + .route("/block", post().to(block_community)) + .route("/delete", post().to(delete_community)) // Mod Actions - .route("/remove", web::post().to(remove_community)) - .route("/transfer", web::post().to(transfer_community)) - .route("/ban_user", web::post().to(ban_from_community)) - .route("/mod", web::post().to(add_mod_to_community)) + .route("/remove", post().to(remove_community)) + .route("/transfer", post().to(transfer_community)) + .route("/ban_user", post().to(ban_from_community)) + .route("/mod", post().to(add_mod_to_community)) .service( - web::scope("/pending_follows") + scope("/pending_follows") .wrap(rate_limit.message()) - .route("/count", web::get().to(get_pending_follows_count)) - .route("/list", web::get().to(get_pending_follows_list)) - .route("/approve", web::post().to(post_pending_follows_approve)), + .route("/count", get().to(get_pending_follows_count)) + .route("/list", get().to(get_pending_follows_list)) + .route("/approve", post().to(post_pending_follows_approve)), ), ) .service( - web::scope("/federated_instances") + scope("/federated_instances") .wrap(rate_limit.message()) - .route("", web::get().to(get_federated_instances)), + .route("", get().to(get_federated_instances)), ) // Post .service( // Handle POST to /post separately to add the post() rate limitter - web::resource("/post") + resource("/post") .guard(guard::Post()) .wrap(rate_limit.post()) - .route(web::post().to(create_post)), + .route(post().to(create_post)), ) .service( - web::scope("/post") + scope("/post") .wrap(rate_limit.message()) - .route("", web::get().to(get_post)) - .route("", web::put().to(update_post)) - .route("/delete", web::post().to(delete_post)) - .route("/remove", web::post().to(remove_post)) - .route("/mark_as_read", web::post().to(mark_post_as_read)) - .route("/mark_many_as_read", web::post().to(mark_posts_as_read)) - .route("/hide", web::post().to(hide_post)) - .route("/lock", web::post().to(lock_post)) - .route("/feature", web::post().to(feature_post)) - .route("/list", web::get().to(list_posts)) - .route("/like", web::post().to(like_post)) - .route("/like/list", web::get().to(list_post_likes)) - .route("/save", web::put().to(save_post)) - .route("/report", web::post().to(create_post_report)) - .route("/report/resolve", web::put().to(resolve_post_report)) - .route("/report/list", web::get().to(list_post_reports)) - .route("/site_metadata", web::get().to(get_link_metadata)), + .route("", get().to(get_post)) + .route("", put().to(update_post)) + .route("/delete", post().to(delete_post)) + .route("/remove", post().to(remove_post)) + .route("/mark_as_read", post().to(mark_post_as_read)) + .route("/mark_many_as_read", post().to(mark_posts_as_read)) + .route("/hide", post().to(hide_post)) + .route("/lock", post().to(lock_post)) + .route("/feature", post().to(feature_post)) + .route("/list", get().to(list_posts)) + .route("/like", post().to(like_post)) + .route("/like/list", get().to(list_post_likes)) + .route("/save", put().to(save_post)) + .route("/report", post().to(create_post_report)) + .route("/report/resolve", put().to(resolve_post_report)) + .route("/report/list", get().to(list_post_reports)) + .route("/site_metadata", get().to(get_link_metadata)), ) // Comment .service( // Handle POST to /comment separately to add the comment() rate limitter - web::resource("/comment") + resource("/comment") .guard(guard::Post()) .wrap(rate_limit.comment()) - .route(web::post().to(create_comment)), + .route(post().to(create_comment)), ) .service( - web::scope("/comment") + scope("/comment") .wrap(rate_limit.message()) - .route("", web::get().to(get_comment)) - .route("", web::put().to(update_comment)) - .route("/delete", web::post().to(delete_comment)) - .route("/remove", web::post().to(remove_comment)) - .route("/mark_as_read", web::post().to(mark_reply_as_read)) - .route("/distinguish", web::post().to(distinguish_comment)) - .route("/like", web::post().to(like_comment)) - .route("/like/list", web::get().to(list_comment_likes)) - .route("/save", web::put().to(save_comment)) - .route("/list", web::get().to(list_comments)) - .route("/report", web::post().to(create_comment_report)) - .route("/report/resolve", web::put().to(resolve_comment_report)) - .route("/report/list", web::get().to(list_comment_reports)), + .route("", get().to(get_comment)) + .route("", put().to(update_comment)) + .route("/delete", post().to(delete_comment)) + .route("/remove", post().to(remove_comment)) + .route("/mark_as_read", post().to(mark_reply_as_read)) + .route("/distinguish", post().to(distinguish_comment)) + .route("/like", post().to(like_comment)) + .route("/like/list", get().to(list_comment_likes)) + .route("/save", put().to(save_comment)) + .route("/list", get().to(list_comments)) + .route("/report", post().to(create_comment_report)) + .route("/report/resolve", put().to(resolve_comment_report)) + .route("/report/list", get().to(list_comment_reports)), ) // Private Message .service( - web::scope("/private_message") + scope("/private_message") .wrap(rate_limit.message()) - .route("/list", web::get().to(get_private_message)) - .route("", web::post().to(create_private_message)) - .route("", web::put().to(update_private_message)) - .route("/delete", web::post().to(delete_private_message)) - .route("/mark_as_read", web::post().to(mark_pm_as_read)) - .route("/report", web::post().to(create_pm_report)) - .route("/report/resolve", web::put().to(resolve_pm_report)) - .route("/report/list", web::get().to(list_pm_reports)), + .route("/list", get().to(get_private_message)) + .route("", post().to(create_private_message)) + .route("", put().to(update_private_message)) + .route("/delete", post().to(delete_private_message)) + .route("/mark_as_read", post().to(mark_pm_as_read)) + .route("/report", post().to(create_pm_report)) + .route("/report/resolve", put().to(resolve_pm_report)) + .route("/report/list", get().to(list_pm_reports)), ) // User .service( // Account action, I don't like that it's in /user maybe /accounts // Handle /user/register separately to add the register() rate limiter - web::resource("/user/register") + resource("/user/register") .guard(guard::Post()) .wrap(rate_limit.register()) - .route(web::post().to(register)), + .route(post().to(register)), ) // User .service( @@ -306,138 +308,134 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { // TODO: pretty annoying way to apply rate limits for register and login, we should // group them under a common path so that rate limit is only applied once (eg under // /account). - web::resource("/user/login") + resource("/user/login") .guard(guard::Post()) .wrap(rate_limit.register()) - .route(web::post().to(login)), + .route(post().to(login)), ) .service( - web::resource("/user/password_reset") + resource("/user/password_reset") .wrap(rate_limit.register()) - .route(web::post().to(reset_password)), + .route(post().to(reset_password)), ) .service( // Handle captcha separately - web::resource("/user/get_captcha") + resource("/user/get_captcha") .wrap(rate_limit.post()) - .route(web::get().to(get_captcha)), + .route(get().to(get_captcha)), ) .service( - web::resource("/user/export_settings") + resource("/user/export_settings") .wrap(rate_limit.import_user_settings()) - .route(web::get().to(export_settings)), + .route(get().to(export_settings)), ) .service( - web::resource("/user/import_settings") + resource("/user/import_settings") .wrap(rate_limit.import_user_settings()) - .route(web::post().to(import_settings)), + .route(post().to(import_settings)), ) // TODO, all the current account related actions under /user need to get moved here eventually .service( - web::scope("/account") + scope("/account") .wrap(rate_limit.message()) - .route("/list_media", web::get().to(list_media)), + .route("/list_media", get().to(list_media)), ) // User actions .service( - web::scope("/user") + scope("/user") .wrap(rate_limit.message()) - .route("", web::get().to(read_person)) - .route("/mention", web::get().to(list_mentions)) + .route("", get().to(read_person)) + .route("/mention", get().to(list_mentions)) .route( "/mention/mark_as_read", - web::post().to(mark_person_mention_as_read), + post().to(mark_person_mention_as_read), ) - .route("/replies", web::get().to(list_replies)) + .route("/replies", get().to(list_replies)) // Admin action. I don't like that it's in /user - .route("/ban", web::post().to(ban_from_site)) - .route("/banned", web::get().to(list_banned_users)) - .route("/block", web::post().to(block_person)) + .route("/ban", post().to(ban_from_site)) + .route("/banned", get().to(list_banned_users)) + .route("/block", post().to(block_person)) // TODO Account actions. I don't like that they're in /user maybe /accounts - .route("/logout", web::post().to(logout)) - .route("/delete_account", web::post().to(delete_account)) - .route( - "/password_change", - web::post().to(change_password_after_reset), - ) + .route("/logout", post().to(logout)) + .route("/delete_account", post().to(delete_account)) + .route("/password_change", post().to(change_password_after_reset)) // TODO mark_all_as_read feels off being in this section as well - .route( - "/mark_all_as_read", - web::post().to(mark_all_notifications_read), - ) - .route("/save_user_settings", web::put().to(save_user_settings)) - .route("/change_password", web::put().to(change_password)) - .route("/report_count", web::get().to(report_count)) - .route("/unread_count", web::get().to(unread_count)) - .route("/verify_email", web::post().to(verify_email)) - .route("/leave_admin", web::post().to(leave_admin)) - .route("/totp/generate", web::post().to(generate_totp_secret)) - .route("/totp/update", web::post().to(update_totp)) - .route("/list_logins", web::get().to(list_logins)) - .route("/validate_auth", web::get().to(validate_auth)), + .route("/mark_all_as_read", post().to(mark_all_notifications_read)) + .route("/save_user_settings", put().to(save_user_settings)) + .route("/change_password", put().to(change_password)) + .route("/report_count", get().to(report_count)) + .route("/unread_count", get().to(unread_count)) + .route("/verify_email", post().to(verify_email)) + .route("/leave_admin", post().to(leave_admin)) + .route("/totp/generate", post().to(generate_totp_secret)) + .route("/totp/update", post().to(update_totp)) + .route("/list_logins", get().to(list_logins)) + .route("/validate_auth", get().to(validate_auth)), ) // Admin Actions .service( - web::scope("/admin") + scope("/admin") .wrap(rate_limit.message()) - .route("/add", web::post().to(add_admin)) + .route("/add", post().to(add_admin)) .route( "/registration_application/count", - web::get().to(get_unread_registration_application_count), + get().to(get_unread_registration_application_count), ) .route( "/registration_application/list", - web::get().to(list_registration_applications), + get().to(list_registration_applications), ) .route( "/registration_application/approve", - web::put().to(approve_registration_application), + put().to(approve_registration_application), ) .route( "/registration_application", - web::get().to(get_registration_application), + get().to(get_registration_application), ) - .route("/list_all_media", web::get().to(list_all_media)) + .route("/list_all_media", get().to(list_all_media)) .service( - web::scope("/purge") - .route("/person", web::post().to(purge_person)) - .route("/community", web::post().to(purge_community)) - .route("/post", web::post().to(purge_post)) - .route("/comment", web::post().to(purge_comment)), + scope("/purge") + .route("/person", post().to(purge_person)) + .route("/community", post().to(purge_community)) + .route("/post", post().to(purge_post)) + .route("/comment", post().to(purge_comment)), ) .service( - web::scope("/tagline") + scope("/tagline") .wrap(rate_limit.message()) - .route("", web::post().to(create_tagline)) - .route("", web::put().to(update_tagline)) - .route("/delete", web::post().to(delete_tagline)) - .route("/list", web::get().to(list_taglines)), - ), + .route("", post().to(create_tagline)) + .route("", put().to(update_tagline)) + .route("/delete", post().to(delete_tagline)) + .route("/list", get().to(list_taglines)), + ) + .route("block_instance", post().to(admin_block_instance)) + .route("allow_instance", post().to(admin_allow_instance)), ) .service( - web::scope("/custom_emoji") + scope("/custom_emoji") .wrap(rate_limit.message()) - .route("", web::post().to(create_custom_emoji)) - .route("", web::put().to(update_custom_emoji)) - .route("/delete", web::post().to(delete_custom_emoji)) - .route("/list", web::get().to(list_custom_emojis)), + .route("", post().to(create_custom_emoji)) + .route("", put().to(update_custom_emoji)) + .route("/delete", post().to(delete_custom_emoji)) + .route("/list", get().to(list_custom_emojis)), ) .service( - web::scope("/oauth_provider") + scope("/oauth_provider") .wrap(rate_limit.message()) - .route("", web::post().to(create_oauth_provider)) - .route("", web::put().to(update_oauth_provider)) - .route("/delete", web::post().to(delete_oauth_provider)), + .route("", post().to(create_oauth_provider)) + .route("", put().to(update_oauth_provider)) + .route("/delete", post().to(delete_oauth_provider)), ) .service( - web::scope("/oauth") + scope("/oauth") .wrap(rate_limit.register()) - .route("/authenticate", web::post().to(authenticate_with_oauth)), + .route("/authenticate", post().to(authenticate_with_oauth)), ), ); cfg.service( - web::scope("/sitemap.xml") + scope("/sitemap.xml") .wrap(rate_limit.message()) - .route("", web::get().to(get_sitemap)), + .route("", get().to(get_sitemap)), ); }