diff --git a/server/Cargo.lock b/server/Cargo.lock index 1e0e04f86..44fb405ea 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -1549,7 +1549,9 @@ dependencies = [ "bcrypt", "chrono", "diesel", + "lazy_static", "log", + "regex", "serde 1.0.114", "serde_json", "sha2", diff --git a/server/lemmy_db/Cargo.toml b/server/lemmy_db/Cargo.toml index f10f217ef..6d342c1e7 100644 --- a/server/lemmy_db/Cargo.toml +++ b/server/lemmy_db/Cargo.toml @@ -13,4 +13,6 @@ strum_macros = "0.18.0" log = "0.4.0" sha2 = "0.9" bcrypt = "0.8.0" -url = { version = "2.1.1", features = ["serde"] } \ No newline at end of file +url = { version = "2.1.1", features = ["serde"] } +lazy_static = "1.3.0" +regex = "1.3.5" diff --git a/server/lemmy_db/src/lib.rs b/server/lemmy_db/src/lib.rs index 2eead841d..cca2994b8 100644 --- a/server/lemmy_db/src/lib.rs +++ b/server/lemmy_db/src/lib.rs @@ -2,9 +2,12 @@ pub extern crate diesel; #[macro_use] pub extern crate strum_macros; +#[macro_use] +pub extern crate lazy_static; pub extern crate bcrypt; pub extern crate chrono; pub extern crate log; +pub extern crate regex; pub extern crate serde; pub extern crate serde_json; pub extern crate sha2; @@ -12,6 +15,7 @@ pub extern crate strum; use chrono::NaiveDateTime; use diesel::{dsl::*, result::Error, *}; +use regex::Regex; use serde::{Deserialize, Serialize}; use std::{env, env::VarError}; @@ -172,10 +176,19 @@ pub fn naive_now() -> NaiveDateTime { chrono::prelude::Utc::now().naive_utc() } +pub fn is_email_regex(test: &str) -> bool { + EMAIL_REGEX.is_match(test) +} + +lazy_static! { + static ref EMAIL_REGEX: Regex = + Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap(); +} + #[cfg(test)] mod tests { use super::fuzzy_search; - use crate::get_database_url_from_env; + use crate::{get_database_url_from_env, is_email_regex}; use diesel::{Connection, PgConnection}; pub fn establish_unpooled_connection() -> PgConnection { @@ -194,4 +207,10 @@ mod tests { let test = "This is a fuzzy search"; assert_eq!(fuzzy_search(test), "%This%is%a%fuzzy%search%".to_string()); } + + #[test] + fn test_email() { + assert!(is_email_regex("gush@gmail.com")); + assert!(!is_email_regex("nada_neutho")); + } } diff --git a/server/lemmy_db/src/user.rs b/server/lemmy_db/src/user.rs index 718067f99..e53890770 100644 --- a/server/lemmy_db/src/user.rs +++ b/server/lemmy_db/src/user.rs @@ -1,4 +1,5 @@ use crate::{ + is_email_regex, naive_now, schema::{user_, user_::dsl::*}, Crud, @@ -125,9 +126,18 @@ impl User_ { use crate::schema::user_::dsl::*; user_.filter(actor_id.eq(object_id)).first::(conn) } -} -impl User_ { + pub fn find_by_email_or_username( + conn: &PgConnection, + username_or_email: &str, + ) -> Result { + if is_email_regex(username_or_email) { + Self::find_by_email(conn, username_or_email) + } else { + Self::find_by_username(conn, username_or_email) + } + } + pub fn find_by_username(conn: &PgConnection, username: &str) -> Result { user_.filter(name.eq(username)).first::(conn) } diff --git a/server/lemmy_utils/Cargo.toml b/server/lemmy_utils/Cargo.toml index fed22f585..9685c0eda 100644 --- a/server/lemmy_utils/Cargo.toml +++ b/server/lemmy_utils/Cargo.toml @@ -19,4 +19,4 @@ serde_json = { version = "1.0.52", features = ["preserve_order"]} comrak = "0.7" lazy_static = "1.3.0" openssl = "0.10" -url = { version = "2.1.1", features = ["serde"] } \ No newline at end of file +url = { version = "2.1.1", features = ["serde"] } diff --git a/server/lemmy_utils/src/lib.rs b/server/lemmy_utils/src/lib.rs index d88335e2d..6d851b033 100644 --- a/server/lemmy_utils/src/lib.rs +++ b/server/lemmy_utils/src/lib.rs @@ -44,10 +44,6 @@ pub fn convert_datetime(datetime: NaiveDateTime) -> DateTime { DateTime::::from_utc(datetime, *now.offset()) } -pub fn is_email_regex(test: &str) -> bool { - EMAIL_REGEX.is_match(test) -} - pub fn remove_slurs(test: &str) -> String { SLUR_REGEX.replace_all(test, "*removed*").to_string() } @@ -165,7 +161,6 @@ pub fn is_valid_post_title(title: &str) -> bool { #[cfg(test)] mod tests { use crate::{ - is_email_regex, is_valid_community_name, is_valid_post_title, is_valid_username, @@ -185,12 +180,6 @@ mod tests { assert_eq!(mentions[1].domain, "lemmy-alpha:8540".to_string()); } - #[test] - fn test_email() { - assert!(is_email_regex("gush@gmail.com")); - assert!(!is_email_regex("nada_neutho")); - } - #[test] fn test_valid_register_username() { assert!(is_valid_username("Hello_98")); diff --git a/server/src/api/claims.rs b/server/src/api/claims.rs index eec9d1a71..9118714ba 100644 --- a/server/src/api/claims.rs +++ b/server/src/api/claims.rs @@ -1,7 +1,7 @@ use diesel::{result::Error, PgConnection}; use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation}; use lemmy_db::{user::User_, Crud}; -use lemmy_utils::{is_email_regex, settings::Settings}; +use lemmy_utils::settings::Settings; use serde::{Deserialize, Serialize}; type Jwt = String; @@ -54,18 +54,6 @@ impl Claims { .unwrap() } - // TODO: move these into user? - pub fn find_by_email_or_username( - conn: &PgConnection, - username_or_email: &str, - ) -> Result { - if is_email_regex(username_or_email) { - User_::find_by_email(conn, username_or_email) - } else { - User_::find_by_username(conn, username_or_email) - } - } - pub fn find_by_jwt(conn: &PgConnection, jwt: &str) -> Result { let claims: Claims = Claims::decode(&jwt).expect("Invalid token").claims; User_::read(&conn, claims.id) diff --git a/server/src/api/user.rs b/server/src/api/user.rs index 2e6218239..71fecea78 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -110,7 +110,6 @@ pub struct GetUserDetailsResponse { moderates: Vec, comments: Vec, posts: Vec, - admins: Vec, // TODO why is this necessary, just use GetSite } #[derive(Serialize, Deserialize)] @@ -276,7 +275,7 @@ impl Perform for Oper { // Fetch that username / email let username_or_email = data.username_or_email.clone(); let user = match blocking(pool, move |conn| { - Claims::find_by_email_or_username(conn, &username_or_email) + User_::find_by_email_or_username(conn, &username_or_email) }) .await? { @@ -643,14 +642,6 @@ impl Perform for Oper { }) .await??; - let site_creator_id = - blocking(pool, move |conn| Site::read(conn, 1).map(|s| s.creator_id)).await??; - - let mut admins = blocking(pool, move |conn| UserView::admins(conn)).await??; - let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap(); - let creator_user = admins.remove(creator_index); - admins.insert(0, creator_user); - // If its not the same user, remove the email, and settings // TODO an if let chain would be better here, but can't figure it out // TODO separate out settings into its own thing @@ -665,7 +656,6 @@ impl Perform for Oper { moderates, comments, posts, - admins, }) } } diff --git a/server/src/apub/user.rs b/server/src/apub/user.rs index 2a98670ce..403c4d4a8 100644 --- a/server/src/apub/user.rs +++ b/server/src/apub/user.rs @@ -1,5 +1,4 @@ use crate::{ - api::claims::Claims, apub::{ activities::send_activity, create_apub_response, @@ -253,7 +252,7 @@ pub async fn get_apub_user_http( ) -> Result, LemmyError> { let user_name = info.into_inner().user_name; let user = blocking(&db, move |conn| { - Claims::find_by_email_or_username(conn, &user_name) + User_::find_by_email_or_username(conn, &user_name) }) .await??; let u = user.to_apub(&db).await?; diff --git a/ui/src/components/user-details.tsx b/ui/src/components/user-details.tsx index 49b9589e7..5f2346a24 100644 --- a/ui/src/components/user-details.tsx +++ b/ui/src/components/user-details.tsx @@ -1,7 +1,7 @@ import { Component, linkEvent } from 'inferno'; import { WebSocketService, UserService } from '../services'; import { Subscription } from 'rxjs'; -import { retryWhen, delay, take, last } from 'rxjs/operators'; +import { retryWhen, delay, take } from 'rxjs/operators'; import { i18n } from '../i18next'; import { UserOperation, @@ -16,7 +16,6 @@ import { CommentResponse, BanUserResponse, PostResponse, - AddAdminResponse, } from '../interfaces'; import { wsJsonToRes, @@ -41,6 +40,7 @@ interface UserDetailsProps { enableNsfw: boolean; view: UserDetailsView; onPageChange(page: number): number | any; + admins: Array; } interface UserDetailsState { @@ -49,7 +49,6 @@ interface UserDetailsState { comments: Array; posts: Array; saved?: Array; - admins: Array; } export class UserDetails extends Component { @@ -63,7 +62,6 @@ export class UserDetails extends Component { comments: [], posts: [], saved: [], - admins: [], }; this.subscription = WebSocketService.Instance.subject @@ -152,7 +150,7 @@ export class UserDetails extends Component { {i.type === 'posts' ? ( { ) : ( {
{ {this.state.posts.map(post => ( { follows: data.follows, moderates: data.moderates, posts: data.posts, - admins: data.admins, }); } else if (res.op == UserOperation.CreateCommentLike) { const data = res.data as CommentResponse; @@ -298,11 +295,6 @@ export class UserDetails extends Component { posts: this.state.posts, comments: this.state.comments, }); - } else if (res.op == UserOperation.AddAdmin) { - const data = res.data as AddAdminResponse; - this.setState({ - admins: data.admins, - }); } } } diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index 468d29801..8ff5c3929 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -13,9 +13,9 @@ import { DeleteAccountForm, WebSocketJsonResponse, GetSiteResponse, - Site, UserDetailsView, UserDetailsResponse, + AddAdminResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { @@ -54,7 +54,7 @@ interface UserState { deleteAccountLoading: boolean; deleteAccountShowConfirm: boolean; deleteAccountForm: DeleteAccountForm; - site: Site; + siteRes: GetSiteResponse; } interface UserProps { @@ -114,19 +114,24 @@ export class User extends Component { deleteAccountForm: { password: null, }, - site: { - id: undefined, - name: undefined, - creator_id: undefined, - published: undefined, - creator_name: undefined, - number_of_users: undefined, - number_of_posts: undefined, - number_of_comments: undefined, - number_of_communities: undefined, - enable_downvotes: undefined, - open_registration: undefined, - enable_nsfw: undefined, + siteRes: { + admins: [], + banned: [], + online: undefined, + site: { + id: undefined, + name: undefined, + creator_id: undefined, + published: undefined, + creator_name: undefined, + number_of_users: undefined, + number_of_posts: undefined, + number_of_comments: undefined, + number_of_communities: undefined, + enable_downvotes: undefined, + open_registration: undefined, + enable_nsfw: undefined, + }, }, }; @@ -201,7 +206,7 @@ export class User extends Component { // Couldnt get a refresh working. This does for now. location.reload(); } - document.title = `/u/${this.state.username} - ${this.state.site.name}`; + document.title = `/u/${this.state.username} - ${this.state.siteRes.site.name}`; setupTippy(); } @@ -236,8 +241,9 @@ export class User extends Component { sort={SortType[this.state.sort]} page={this.state.page} limit={fetchLimit} - enableDownvotes={this.state.site.enable_downvotes} - enableNsfw={this.state.site.enable_nsfw} + enableDownvotes={this.state.siteRes.site.enable_downvotes} + enableNsfw={this.state.siteRes.site.enable_nsfw} + admins={this.state.siteRes.admins} view={this.state.view} onPageChange={this.handlePageChange} /> @@ -637,7 +643,7 @@ export class User extends Component { />
- {this.state.site.enable_nsfw && ( + {this.state.siteRes.site.enable_nsfw && (
{ this.context.router.history.push('/'); } else if (res.op == UserOperation.GetSite) { const data = res.data as GetSiteResponse; - this.setState({ - site: data.site, - }); + this.state.siteRes = data; + this.setState(this.state); + } else if (res.op == UserOperation.AddAdmin) { + const data = res.data as AddAdminResponse; + this.state.siteRes.admins = data.admins; + this.setState(this.state); } } }