]> Untitled Git - lemmy.git/blob - server/src/api/site.rs
ef1a28287617575a82c23c46faf9e4e986be7314
[lemmy.git] / server / src / api / site.rs
1 use super::*;
2 use diesel::PgConnection;
3 use std::str::FromStr;
4
5 #[derive(Serialize, Deserialize)]
6 pub struct ListCategories {}
7
8 #[derive(Serialize, Deserialize)]
9 pub struct ListCategoriesResponse {
10   categories: Vec<Category>,
11 }
12
13 #[derive(Serialize, Deserialize)]
14 pub struct Search {
15   q: String,
16   type_: String,
17   community_id: Option<i32>,
18   sort: String,
19   page: Option<i64>,
20   limit: Option<i64>,
21   auth: Option<String>,
22 }
23
24 #[derive(Serialize, Deserialize)]
25 pub struct SearchResponse {
26   type_: String,
27   comments: Vec<CommentView>,
28   posts: Vec<PostView>,
29   communities: Vec<CommunityView>,
30   users: Vec<UserView>,
31 }
32
33 #[derive(Serialize, Deserialize)]
34 pub struct GetModlog {
35   mod_user_id: Option<i32>,
36   community_id: Option<i32>,
37   page: Option<i64>,
38   limit: Option<i64>,
39 }
40
41 #[derive(Serialize, Deserialize)]
42 pub struct GetModlogResponse {
43   removed_posts: Vec<ModRemovePostView>,
44   locked_posts: Vec<ModLockPostView>,
45   stickied_posts: Vec<ModStickyPostView>,
46   removed_comments: Vec<ModRemoveCommentView>,
47   removed_communities: Vec<ModRemoveCommunityView>,
48   banned_from_community: Vec<ModBanFromCommunityView>,
49   banned: Vec<ModBanView>,
50   added_to_community: Vec<ModAddCommunityView>,
51   added: Vec<ModAddView>,
52 }
53
54 #[derive(Serialize, Deserialize)]
55 pub struct CreateSite {
56   name: String,
57   description: Option<String>,
58   enable_downvotes: bool,
59   open_registration: bool,
60   enable_nsfw: bool,
61   auth: String,
62 }
63
64 #[derive(Serialize, Deserialize)]
65 pub struct EditSite {
66   name: String,
67   description: Option<String>,
68   enable_downvotes: bool,
69   open_registration: bool,
70   enable_nsfw: bool,
71   auth: String,
72 }
73
74 #[derive(Serialize, Deserialize)]
75 pub struct GetSite {}
76
77 #[derive(Serialize, Deserialize)]
78 pub struct SiteResponse {
79   site: SiteView,
80 }
81
82 #[derive(Serialize, Deserialize)]
83 pub struct GetSiteResponse {
84   site: Option<SiteView>,
85   admins: Vec<UserView>,
86   banned: Vec<UserView>,
87   pub online: usize,
88 }
89
90 #[derive(Serialize, Deserialize)]
91 pub struct TransferSite {
92   user_id: i32,
93   auth: String,
94 }
95
96 impl Perform<ListCategoriesResponse> for Oper<ListCategories> {
97   fn perform(&self, conn: &PgConnection) -> Result<ListCategoriesResponse, Error> {
98     let _data: &ListCategories = &self.data;
99
100     let categories: Vec<Category> = Category::list_all(&conn)?;
101
102     // Return the jwt
103     Ok(ListCategoriesResponse { categories })
104   }
105 }
106
107 impl Perform<GetModlogResponse> for Oper<GetModlog> {
108   fn perform(&self, conn: &PgConnection) -> Result<GetModlogResponse, Error> {
109     let data: &GetModlog = &self.data;
110
111     let removed_posts = ModRemovePostView::list(
112       &conn,
113       data.community_id,
114       data.mod_user_id,
115       data.page,
116       data.limit,
117     )?;
118     let locked_posts = ModLockPostView::list(
119       &conn,
120       data.community_id,
121       data.mod_user_id,
122       data.page,
123       data.limit,
124     )?;
125     let stickied_posts = ModStickyPostView::list(
126       &conn,
127       data.community_id,
128       data.mod_user_id,
129       data.page,
130       data.limit,
131     )?;
132     let removed_comments = ModRemoveCommentView::list(
133       &conn,
134       data.community_id,
135       data.mod_user_id,
136       data.page,
137       data.limit,
138     )?;
139     let banned_from_community = ModBanFromCommunityView::list(
140       &conn,
141       data.community_id,
142       data.mod_user_id,
143       data.page,
144       data.limit,
145     )?;
146     let added_to_community = ModAddCommunityView::list(
147       &conn,
148       data.community_id,
149       data.mod_user_id,
150       data.page,
151       data.limit,
152     )?;
153
154     // These arrays are only for the full modlog, when a community isn't given
155     let (removed_communities, banned, added) = if data.community_id.is_none() {
156       (
157         ModRemoveCommunityView::list(&conn, data.mod_user_id, data.page, data.limit)?,
158         ModBanView::list(&conn, data.mod_user_id, data.page, data.limit)?,
159         ModAddView::list(&conn, data.mod_user_id, data.page, data.limit)?,
160       )
161     } else {
162       (Vec::new(), Vec::new(), Vec::new())
163     };
164
165     // Return the jwt
166     Ok(GetModlogResponse {
167       removed_posts,
168       locked_posts,
169       stickied_posts,
170       removed_comments,
171       removed_communities,
172       banned_from_community,
173       banned,
174       added_to_community,
175       added,
176     })
177   }
178 }
179
180 impl Perform<SiteResponse> for Oper<CreateSite> {
181   fn perform(&self, conn: &PgConnection) -> Result<SiteResponse, Error> {
182     let data: &CreateSite = &self.data;
183
184     let claims = match Claims::decode(&data.auth) {
185       Ok(claims) => claims.claims,
186       Err(_e) => return Err(APIError::err("not_logged_in").into()),
187     };
188
189     if let Err(slurs) = slur_check(&data.name) {
190       return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
191     }
192
193     if let Some(description) = &data.description {
194       if let Err(slurs) = slur_check(description) {
195         return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
196       }
197     }
198
199     let user_id = claims.id;
200
201     // Make sure user is an admin
202     if !UserView::read(&conn, user_id)?.admin {
203       return Err(APIError::err("not_an_admin").into());
204     }
205
206     let site_form = SiteForm {
207       name: data.name.to_owned(),
208       description: data.description.to_owned(),
209       creator_id: user_id,
210       enable_downvotes: data.enable_downvotes,
211       open_registration: data.open_registration,
212       enable_nsfw: data.enable_nsfw,
213       updated: None,
214     };
215
216     match Site::create(&conn, &site_form) {
217       Ok(site) => site,
218       Err(_e) => return Err(APIError::err("site_already_exists").into()),
219     };
220
221     let site_view = SiteView::read(&conn)?;
222
223     Ok(SiteResponse { site: site_view })
224   }
225 }
226
227 impl Perform<SiteResponse> for Oper<EditSite> {
228   fn perform(&self, conn: &PgConnection) -> Result<SiteResponse, Error> {
229     let data: &EditSite = &self.data;
230
231     let claims = match Claims::decode(&data.auth) {
232       Ok(claims) => claims.claims,
233       Err(_e) => return Err(APIError::err("not_logged_in").into()),
234     };
235
236     if let Err(slurs) = slur_check(&data.name) {
237       return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
238     }
239
240     if let Some(description) = &data.description {
241       if let Err(slurs) = slur_check(description) {
242         return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
243       }
244     }
245
246     let user_id = claims.id;
247
248     // Make sure user is an admin
249     if !UserView::read(&conn, user_id)?.admin {
250       return Err(APIError::err("not_an_admin").into());
251     }
252
253     let found_site = Site::read(&conn, 1)?;
254
255     let site_form = SiteForm {
256       name: data.name.to_owned(),
257       description: data.description.to_owned(),
258       creator_id: found_site.creator_id,
259       updated: Some(naive_now()),
260       enable_downvotes: data.enable_downvotes,
261       open_registration: data.open_registration,
262       enable_nsfw: data.enable_nsfw,
263     };
264
265     match Site::update(&conn, 1, &site_form) {
266       Ok(site) => site,
267       Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
268     };
269
270     let site_view = SiteView::read(&conn)?;
271
272     Ok(SiteResponse { site: site_view })
273   }
274 }
275
276 impl Perform<GetSiteResponse> for Oper<GetSite> {
277   fn perform(&self, conn: &PgConnection) -> Result<GetSiteResponse, Error> {
278     let _data: &GetSite = &self.data;
279
280     // It can return a null site in order to redirect
281     let site_view = match Site::read(&conn, 1) {
282       Ok(_site) => Some(SiteView::read(&conn)?),
283       Err(_e) => None,
284     };
285
286     let mut admins = UserView::admins(&conn)?;
287     if site_view.is_some() {
288       let site_creator_id = site_view.to_owned().unwrap().creator_id;
289       let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
290       let creator_user = admins.remove(creator_index);
291       admins.insert(0, creator_user);
292     }
293
294     let banned = UserView::banned(&conn)?;
295
296     Ok(GetSiteResponse {
297       site: site_view,
298       admins,
299       banned,
300       online: 0,
301     })
302   }
303 }
304
305 impl Perform<SearchResponse> for Oper<Search> {
306   fn perform(&self, conn: &PgConnection) -> Result<SearchResponse, Error> {
307     let data: &Search = &self.data;
308
309     let user_id: Option<i32> = match &data.auth {
310       Some(auth) => match Claims::decode(&auth) {
311         Ok(claims) => {
312           let user_id = claims.claims.id;
313           Some(user_id)
314         }
315         Err(_e) => None,
316       },
317       None => None,
318     };
319
320     let sort = SortType::from_str(&data.sort)?;
321     let type_ = SearchType::from_str(&data.type_)?;
322
323     let mut posts = Vec::new();
324     let mut comments = Vec::new();
325     let mut communities = Vec::new();
326     let mut users = Vec::new();
327
328     // TODO no clean / non-nsfw searching rn
329
330     match type_ {
331       SearchType::Posts => {
332         posts = PostQueryBuilder::create(&conn)
333           .sort(&sort)
334           .show_nsfw(true)
335           .for_community_id(data.community_id)
336           .search_term(data.q.to_owned())
337           .my_user_id(user_id)
338           .page(data.page)
339           .limit(data.limit)
340           .list()?;
341       }
342       SearchType::Comments => {
343         comments = CommentQueryBuilder::create(&conn)
344           .sort(&sort)
345           .search_term(data.q.to_owned())
346           .my_user_id(user_id)
347           .page(data.page)
348           .limit(data.limit)
349           .list()?;
350       }
351       SearchType::Communities => {
352         communities = CommunityQueryBuilder::create(&conn)
353           .sort(&sort)
354           .search_term(data.q.to_owned())
355           .page(data.page)
356           .limit(data.limit)
357           .list()?;
358       }
359       SearchType::Users => {
360         users = UserQueryBuilder::create(&conn)
361           .sort(&sort)
362           .search_term(data.q.to_owned())
363           .page(data.page)
364           .limit(data.limit)
365           .list()?;
366       }
367       SearchType::All => {
368         posts = PostQueryBuilder::create(&conn)
369           .sort(&sort)
370           .show_nsfw(true)
371           .for_community_id(data.community_id)
372           .search_term(data.q.to_owned())
373           .my_user_id(user_id)
374           .page(data.page)
375           .limit(data.limit)
376           .list()?;
377
378         comments = CommentQueryBuilder::create(&conn)
379           .sort(&sort)
380           .search_term(data.q.to_owned())
381           .my_user_id(user_id)
382           .page(data.page)
383           .limit(data.limit)
384           .list()?;
385
386         communities = CommunityQueryBuilder::create(&conn)
387           .sort(&sort)
388           .search_term(data.q.to_owned())
389           .page(data.page)
390           .limit(data.limit)
391           .list()?;
392
393         users = UserQueryBuilder::create(&conn)
394           .sort(&sort)
395           .search_term(data.q.to_owned())
396           .page(data.page)
397           .limit(data.limit)
398           .list()?;
399       }
400       SearchType::Url => {
401         posts = PostQueryBuilder::create(&conn)
402           .sort(&sort)
403           .show_nsfw(true)
404           .for_community_id(data.community_id)
405           .url_search(data.q.to_owned())
406           .page(data.page)
407           .limit(data.limit)
408           .list()?;
409       }
410     };
411
412     // Return the jwt
413     Ok(SearchResponse {
414       type_: data.type_.to_owned(),
415       comments,
416       posts,
417       communities,
418       users,
419     })
420   }
421 }
422
423 impl Perform<GetSiteResponse> for Oper<TransferSite> {
424   fn perform(&self, conn: &PgConnection) -> Result<GetSiteResponse, Error> {
425     let data: &TransferSite = &self.data;
426
427     let claims = match Claims::decode(&data.auth) {
428       Ok(claims) => claims.claims,
429       Err(_e) => return Err(APIError::err("not_logged_in").into()),
430     };
431
432     let user_id = claims.id;
433
434     let read_site = Site::read(&conn, 1)?;
435
436     // Make sure user is the creator
437     if read_site.creator_id != user_id {
438       return Err(APIError::err("not_an_admin").into());
439     }
440
441     let site_form = SiteForm {
442       name: read_site.name,
443       description: read_site.description,
444       creator_id: data.user_id,
445       updated: Some(naive_now()),
446       enable_downvotes: read_site.enable_downvotes,
447       open_registration: read_site.open_registration,
448       enable_nsfw: read_site.enable_nsfw,
449     };
450
451     match Site::update(&conn, 1, &site_form) {
452       Ok(site) => site,
453       Err(_e) => return Err(APIError::err("couldnt_update_site").into()),
454     };
455
456     // Mod tables
457     let form = ModAddForm {
458       mod_user_id: user_id,
459       other_user_id: data.user_id,
460       removed: Some(false),
461     };
462
463     ModAdd::create(&conn, &form)?;
464
465     let site_view = SiteView::read(&conn)?;
466
467     let mut admins = UserView::admins(&conn)?;
468     let creator_index = admins
469       .iter()
470       .position(|r| r.id == site_view.creator_id)
471       .unwrap();
472     let creator_user = admins.remove(creator_index);
473     admins.insert(0, creator_user);
474
475     let banned = UserView::banned(&conn)?;
476
477     Ok(GetSiteResponse {
478       site: Some(site_view),
479       admins,
480       banned,
481       online: 0,
482     })
483   }
484 }