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