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