]> Untitled Git - lemmy.git/blob - src/api_routes_http.rs
Remove SendActivity and Perform traits, rely on channel (#3596)
[lemmy.git] / src / api_routes_http.rs
1 use actix_web::{guard, web, Error, HttpResponse, Result};
2 use lemmy_api::Perform;
3 use lemmy_api_common::{
4   comment::{
5     CreateComment,
6     CreateCommentLike,
7     CreateCommentReport,
8     DeleteComment,
9     DistinguishComment,
10     EditComment,
11     GetComment,
12     ListCommentReports,
13     RemoveComment,
14     ResolveCommentReport,
15     SaveComment,
16   },
17   community::{
18     AddModToCommunity,
19     BanFromCommunity,
20     BlockCommunity,
21     CreateCommunity,
22     DeleteCommunity,
23     EditCommunity,
24     FollowCommunity,
25     HideCommunity,
26     ListCommunities,
27     RemoveCommunity,
28     TransferCommunity,
29   },
30   context::LemmyContext,
31   custom_emoji::{CreateCustomEmoji, DeleteCustomEmoji, EditCustomEmoji},
32   person::{
33     AddAdmin,
34     BanPerson,
35     BlockPerson,
36     ChangePassword,
37     DeleteAccount,
38     GetBannedPersons,
39     GetCaptcha,
40     GetPersonMentions,
41     GetReplies,
42     GetReportCount,
43     GetUnreadCount,
44     Login,
45     MarkAllAsRead,
46     MarkCommentReplyAsRead,
47     MarkPersonMentionAsRead,
48     PasswordChangeAfterReset,
49     PasswordReset,
50     Register,
51     SaveUserSettings,
52     VerifyEmail,
53   },
54   post::{
55     CreatePostLike,
56     CreatePostReport,
57     DeletePost,
58     EditPost,
59     FeaturePost,
60     GetPost,
61     GetSiteMetadata,
62     ListPostReports,
63     LockPost,
64     MarkPostAsRead,
65     RemovePost,
66     ResolvePostReport,
67     SavePost,
68   },
69   private_message::{
70     CreatePrivateMessage,
71     CreatePrivateMessageReport,
72     DeletePrivateMessage,
73     EditPrivateMessage,
74     GetPrivateMessages,
75     ListPrivateMessageReports,
76     MarkPrivateMessageAsRead,
77     ResolvePrivateMessageReport,
78   },
79   site::{
80     ApproveRegistrationApplication,
81     CreateSite,
82     EditSite,
83     GetFederatedInstances,
84     GetModlog,
85     GetSite,
86     GetUnreadRegistrationApplicationCount,
87     LeaveAdmin,
88     ListRegistrationApplications,
89     PurgeComment,
90     PurgeCommunity,
91     PurgePerson,
92     PurgePost,
93   },
94 };
95 use lemmy_api_crud::{post::create::create_post, PerformCrud};
96 use lemmy_apub::{
97   api::{
98     list_comments::list_comments,
99     list_posts::list_posts,
100     read_community::read_community,
101     read_person::read_person,
102     resolve_object::resolve_object,
103     search::search,
104   },
105   SendActivity,
106 };
107 use lemmy_utils::{rate_limit::RateLimitCell, spawn_try_task, SYNCHRONOUS_FEDERATION};
108 use serde::Deserialize;
109
110 pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
111   cfg.service(
112     web::scope("/api/v3")
113       // Site
114       .service(
115         web::scope("/site")
116           .wrap(rate_limit.message())
117           .route("", web::get().to(route_get_crud::<GetSite>))
118           // Admin Actions
119           .route("", web::post().to(route_post_crud::<CreateSite>))
120           .route("", web::put().to(route_post_crud::<EditSite>)),
121       )
122       .service(
123         web::resource("/modlog")
124           .wrap(rate_limit.message())
125           .route(web::get().to(route_get::<GetModlog>)),
126       )
127       .service(
128         web::resource("/search")
129           .wrap(rate_limit.search())
130           .route(web::get().to(search)),
131       )
132       .service(
133         web::resource("/resolve_object")
134           .wrap(rate_limit.message())
135           .route(web::get().to(resolve_object)),
136       )
137       // Community
138       .service(
139         web::resource("/community")
140           .guard(guard::Post())
141           .wrap(rate_limit.register())
142           .route(web::post().to(route_post_crud::<CreateCommunity>)),
143       )
144       .service(
145         web::scope("/community")
146           .wrap(rate_limit.message())
147           .route("", web::get().to(read_community))
148           .route("", web::put().to(route_post_crud::<EditCommunity>))
149           .route("/hide", web::put().to(route_post::<HideCommunity>))
150           .route("/list", web::get().to(route_get_crud::<ListCommunities>))
151           .route("/follow", web::post().to(route_post::<FollowCommunity>))
152           .route("/block", web::post().to(route_post::<BlockCommunity>))
153           .route(
154             "/delete",
155             web::post().to(route_post_crud::<DeleteCommunity>),
156           )
157           // Mod Actions
158           .route(
159             "/remove",
160             web::post().to(route_post_crud::<RemoveCommunity>),
161           )
162           .route("/transfer", web::post().to(route_post::<TransferCommunity>))
163           .route("/ban_user", web::post().to(route_post::<BanFromCommunity>))
164           .route("/mod", web::post().to(route_post::<AddModToCommunity>)),
165       )
166       .service(
167         web::scope("/federated_instances")
168           .wrap(rate_limit.message())
169           .route("", web::get().to(route_get::<GetFederatedInstances>)),
170       )
171       // Post
172       .service(
173         // Handle POST to /post separately to add the post() rate limitter
174         web::resource("/post")
175           .guard(guard::Post())
176           .wrap(rate_limit.post())
177           .route(web::post().to(create_post)),
178       )
179       .service(
180         web::scope("/post")
181           .wrap(rate_limit.message())
182           .route("", web::get().to(route_get_crud::<GetPost>))
183           .route("", web::put().to(route_post_crud::<EditPost>))
184           .route("/delete", web::post().to(route_post_crud::<DeletePost>))
185           .route("/remove", web::post().to(route_post_crud::<RemovePost>))
186           .route(
187             "/mark_as_read",
188             web::post().to(route_post::<MarkPostAsRead>),
189           )
190           .route("/lock", web::post().to(route_post::<LockPost>))
191           .route("/feature", web::post().to(route_post::<FeaturePost>))
192           .route("/list", web::get().to(list_posts))
193           .route("/like", web::post().to(route_post::<CreatePostLike>))
194           .route("/save", web::put().to(route_post::<SavePost>))
195           .route("/report", web::post().to(route_post::<CreatePostReport>))
196           .route(
197             "/report/resolve",
198             web::put().to(route_post::<ResolvePostReport>),
199           )
200           .route("/report/list", web::get().to(route_get::<ListPostReports>))
201           .route(
202             "/site_metadata",
203             web::get().to(route_get::<GetSiteMetadata>),
204           ),
205       )
206       // Comment
207       .service(
208         // Handle POST to /comment separately to add the comment() rate limitter
209         web::resource("/comment")
210           .guard(guard::Post())
211           .wrap(rate_limit.comment())
212           .route(web::post().to(route_post_crud::<CreateComment>)),
213       )
214       .service(
215         web::scope("/comment")
216           .wrap(rate_limit.message())
217           .route("", web::get().to(route_get_crud::<GetComment>))
218           .route("", web::put().to(route_post_crud::<EditComment>))
219           .route("/delete", web::post().to(route_post_crud::<DeleteComment>))
220           .route("/remove", web::post().to(route_post_crud::<RemoveComment>))
221           .route(
222             "/mark_as_read",
223             web::post().to(route_post::<MarkCommentReplyAsRead>),
224           )
225           .route(
226             "/distinguish",
227             web::post().to(route_post::<DistinguishComment>),
228           )
229           .route("/like", web::post().to(route_post::<CreateCommentLike>))
230           .route("/save", web::put().to(route_post::<SaveComment>))
231           .route("/list", web::get().to(list_comments))
232           .route("/report", web::post().to(route_post::<CreateCommentReport>))
233           .route(
234             "/report/resolve",
235             web::put().to(route_post::<ResolveCommentReport>),
236           )
237           .route(
238             "/report/list",
239             web::get().to(route_get::<ListCommentReports>),
240           ),
241       )
242       // Private Message
243       .service(
244         web::scope("/private_message")
245           .wrap(rate_limit.message())
246           .route("/list", web::get().to(route_get_crud::<GetPrivateMessages>))
247           .route("", web::post().to(route_post_crud::<CreatePrivateMessage>))
248           .route("", web::put().to(route_post_crud::<EditPrivateMessage>))
249           .route(
250             "/delete",
251             web::post().to(route_post_crud::<DeletePrivateMessage>),
252           )
253           .route(
254             "/mark_as_read",
255             web::post().to(route_post::<MarkPrivateMessageAsRead>),
256           )
257           .route(
258             "/report",
259             web::post().to(route_post::<CreatePrivateMessageReport>),
260           )
261           .route(
262             "/report/resolve",
263             web::put().to(route_post::<ResolvePrivateMessageReport>),
264           )
265           .route(
266             "/report/list",
267             web::get().to(route_get::<ListPrivateMessageReports>),
268           ),
269       )
270       // User
271       .service(
272         // Account action, I don't like that it's in /user maybe /accounts
273         // Handle /user/register separately to add the register() rate limitter
274         web::resource("/user/register")
275           .guard(guard::Post())
276           .wrap(rate_limit.register())
277           .route(web::post().to(route_post_crud::<Register>)),
278       )
279       .service(
280         // Handle captcha separately
281         web::resource("/user/get_captcha")
282           .wrap(rate_limit.post())
283           .route(web::get().to(route_get::<GetCaptcha>)),
284       )
285       // User actions
286       .service(
287         web::scope("/user")
288           .wrap(rate_limit.message())
289           .route("", web::get().to(read_person))
290           .route("/mention", web::get().to(route_get::<GetPersonMentions>))
291           .route(
292             "/mention/mark_as_read",
293             web::post().to(route_post::<MarkPersonMentionAsRead>),
294           )
295           .route("/replies", web::get().to(route_get::<GetReplies>))
296           // Admin action. I don't like that it's in /user
297           .route("/ban", web::post().to(route_post::<BanPerson>))
298           .route("/banned", web::get().to(route_get::<GetBannedPersons>))
299           .route("/block", web::post().to(route_post::<BlockPerson>))
300           // Account actions. I don't like that they're in /user maybe /accounts
301           .route("/login", web::post().to(route_post::<Login>))
302           .route(
303             "/delete_account",
304             web::post().to(route_post_crud::<DeleteAccount>),
305           )
306           .route(
307             "/password_reset",
308             web::post().to(route_post::<PasswordReset>),
309           )
310           .route(
311             "/password_change",
312             web::post().to(route_post::<PasswordChangeAfterReset>),
313           )
314           // mark_all_as_read feels off being in this section as well
315           .route(
316             "/mark_all_as_read",
317             web::post().to(route_post::<MarkAllAsRead>),
318           )
319           .route(
320             "/save_user_settings",
321             web::put().to(route_post::<SaveUserSettings>),
322           )
323           .route(
324             "/change_password",
325             web::put().to(route_post::<ChangePassword>),
326           )
327           .route("/report_count", web::get().to(route_get::<GetReportCount>))
328           .route("/unread_count", web::get().to(route_get::<GetUnreadCount>))
329           .route("/verify_email", web::post().to(route_post::<VerifyEmail>))
330           .route("/leave_admin", web::post().to(route_post::<LeaveAdmin>)),
331       )
332       // Admin Actions
333       .service(
334         web::scope("/admin")
335           .wrap(rate_limit.message())
336           .route("/add", web::post().to(route_post::<AddAdmin>))
337           .route(
338             "/registration_application/count",
339             web::get().to(route_get::<GetUnreadRegistrationApplicationCount>),
340           )
341           .route(
342             "/registration_application/list",
343             web::get().to(route_get::<ListRegistrationApplications>),
344           )
345           .route(
346             "/registration_application/approve",
347             web::put().to(route_post::<ApproveRegistrationApplication>),
348           )
349           .service(
350             web::scope("/purge")
351               .route("/person", web::post().to(route_post::<PurgePerson>))
352               .route("/community", web::post().to(route_post::<PurgeCommunity>))
353               .route("/post", web::post().to(route_post::<PurgePost>))
354               .route("/comment", web::post().to(route_post::<PurgeComment>)),
355           ),
356       )
357       .service(
358         web::scope("/custom_emoji")
359           .wrap(rate_limit.message())
360           .route("", web::post().to(route_post_crud::<CreateCustomEmoji>))
361           .route("", web::put().to(route_post_crud::<EditCustomEmoji>))
362           .route(
363             "/delete",
364             web::post().to(route_post_crud::<DeleteCustomEmoji>),
365           ),
366       ),
367   );
368 }
369
370 async fn perform<'a, Data>(
371   data: Data,
372   context: web::Data<LemmyContext>,
373   apub_data: activitypub_federation::config::Data<LemmyContext>,
374 ) -> Result<HttpResponse, Error>
375 where
376   Data: Perform
377     + SendActivity<Response = <Data as Perform>::Response>
378     + Clone
379     + Deserialize<'a>
380     + Send
381     + 'static,
382 {
383   let res = data.perform(&context).await?;
384   let res_clone = res.clone();
385   let fed_task = async move { SendActivity::send_activity(&data, &res_clone, &apub_data).await };
386   if *SYNCHRONOUS_FEDERATION {
387     fed_task.await?;
388   } else {
389     spawn_try_task(fed_task);
390   }
391   Ok(HttpResponse::Ok().json(&res))
392 }
393
394 async fn route_get<'a, Data>(
395   data: web::Query<Data>,
396   context: web::Data<LemmyContext>,
397   apub_data: activitypub_federation::config::Data<LemmyContext>,
398 ) -> Result<HttpResponse, Error>
399 where
400   Data: Perform
401     + SendActivity<Response = <Data as Perform>::Response>
402     + Clone
403     + Deserialize<'a>
404     + Send
405     + 'static,
406 {
407   perform::<Data>(data.0, context, apub_data).await
408 }
409
410 async fn route_post<'a, Data>(
411   data: web::Json<Data>,
412   context: web::Data<LemmyContext>,
413   apub_data: activitypub_federation::config::Data<LemmyContext>,
414 ) -> Result<HttpResponse, Error>
415 where
416   Data: Perform
417     + SendActivity<Response = <Data as Perform>::Response>
418     + Clone
419     + Deserialize<'a>
420     + Send
421     + 'static,
422 {
423   perform::<Data>(data.0, context, apub_data).await
424 }
425
426 async fn perform_crud<'a, Data>(
427   data: Data,
428   context: web::Data<LemmyContext>,
429   apub_data: activitypub_federation::config::Data<LemmyContext>,
430 ) -> Result<HttpResponse, Error>
431 where
432   Data: PerformCrud
433     + SendActivity<Response = <Data as PerformCrud>::Response>
434     + Clone
435     + Deserialize<'a>
436     + Send
437     + 'static,
438 {
439   let res = data.perform(&context).await?;
440   let res_clone = res.clone();
441   let fed_task = async move { SendActivity::send_activity(&data, &res_clone, &apub_data).await };
442   if *SYNCHRONOUS_FEDERATION {
443     fed_task.await?;
444   } else {
445     spawn_try_task(fed_task);
446   }
447   Ok(HttpResponse::Ok().json(&res))
448 }
449
450 async fn route_get_crud<'a, Data>(
451   data: web::Query<Data>,
452   context: web::Data<LemmyContext>,
453   apub_data: activitypub_federation::config::Data<LemmyContext>,
454 ) -> Result<HttpResponse, Error>
455 where
456   Data: PerformCrud
457     + SendActivity<Response = <Data as PerformCrud>::Response>
458     + Clone
459     + Deserialize<'a>
460     + Send
461     + 'static,
462 {
463   perform_crud::<Data>(data.0, context, apub_data).await
464 }
465
466 async fn route_post_crud<'a, Data>(
467   data: web::Json<Data>,
468   context: web::Data<LemmyContext>,
469   apub_data: activitypub_federation::config::Data<LemmyContext>,
470 ) -> Result<HttpResponse, Error>
471 where
472   Data: PerformCrud
473     + SendActivity<Response = <Data as PerformCrud>::Response>
474     + Clone
475     + Deserialize<'a>
476     + Send
477     + 'static,
478 {
479   perform_crud::<Data>(data.0, context, apub_data).await
480 }