Some GetUserDetails cleanup.

This commit is contained in:
Dessalines 2020-07-20 15:32:15 -04:00
parent 613b462662
commit ca7d2feedb
11 changed files with 78 additions and 78 deletions

2
server/Cargo.lock generated vendored
View file

@ -1549,7 +1549,9 @@ dependencies = [
"bcrypt", "bcrypt",
"chrono", "chrono",
"diesel", "diesel",
"lazy_static",
"log", "log",
"regex",
"serde 1.0.114", "serde 1.0.114",
"serde_json", "serde_json",
"sha2", "sha2",

View file

@ -14,3 +14,5 @@ log = "0.4.0"
sha2 = "0.9" sha2 = "0.9"
bcrypt = "0.8.0" bcrypt = "0.8.0"
url = { version = "2.1.1", features = ["serde"] } url = { version = "2.1.1", features = ["serde"] }
lazy_static = "1.3.0"
regex = "1.3.5"

View file

@ -2,9 +2,12 @@
pub extern crate diesel; pub extern crate diesel;
#[macro_use] #[macro_use]
pub extern crate strum_macros; pub extern crate strum_macros;
#[macro_use]
pub extern crate lazy_static;
pub extern crate bcrypt; pub extern crate bcrypt;
pub extern crate chrono; pub extern crate chrono;
pub extern crate log; pub extern crate log;
pub extern crate regex;
pub extern crate serde; pub extern crate serde;
pub extern crate serde_json; pub extern crate serde_json;
pub extern crate sha2; pub extern crate sha2;
@ -12,6 +15,7 @@ pub extern crate strum;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, *}; use diesel::{dsl::*, result::Error, *};
use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{env, env::VarError}; use std::{env, env::VarError};
@ -172,10 +176,19 @@ pub fn naive_now() -> NaiveDateTime {
chrono::prelude::Utc::now().naive_utc() 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)] #[cfg(test)]
mod tests { mod tests {
use super::fuzzy_search; 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}; use diesel::{Connection, PgConnection};
pub fn establish_unpooled_connection() -> PgConnection { pub fn establish_unpooled_connection() -> PgConnection {
@ -194,4 +207,10 @@ mod tests {
let test = "This is a fuzzy search"; let test = "This is a fuzzy search";
assert_eq!(fuzzy_search(test), "%This%is%a%fuzzy%search%".to_string()); 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"));
}
} }

View file

@ -1,4 +1,5 @@
use crate::{ use crate::{
is_email_regex,
naive_now, naive_now,
schema::{user_, user_::dsl::*}, schema::{user_, user_::dsl::*},
Crud, Crud,
@ -125,9 +126,18 @@ impl User_ {
use crate::schema::user_::dsl::*; use crate::schema::user_::dsl::*;
user_.filter(actor_id.eq(object_id)).first::<Self>(conn) user_.filter(actor_id.eq(object_id)).first::<Self>(conn)
} }
}
impl User_ { pub fn find_by_email_or_username(
conn: &PgConnection,
username_or_email: &str,
) -> Result<Self, Error> {
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_, Error> { pub fn find_by_username(conn: &PgConnection, username: &str) -> Result<User_, Error> {
user_.filter(name.eq(username)).first::<User_>(conn) user_.filter(name.eq(username)).first::<User_>(conn)
} }

View file

@ -44,10 +44,6 @@ pub fn convert_datetime(datetime: NaiveDateTime) -> DateTime<FixedOffset> {
DateTime::<FixedOffset>::from_utc(datetime, *now.offset()) DateTime::<FixedOffset>::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 { pub fn remove_slurs(test: &str) -> String {
SLUR_REGEX.replace_all(test, "*removed*").to_string() SLUR_REGEX.replace_all(test, "*removed*").to_string()
} }
@ -165,7 +161,6 @@ pub fn is_valid_post_title(title: &str) -> bool {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
is_email_regex,
is_valid_community_name, is_valid_community_name,
is_valid_post_title, is_valid_post_title,
is_valid_username, is_valid_username,
@ -185,12 +180,6 @@ mod tests {
assert_eq!(mentions[1].domain, "lemmy-alpha:8540".to_string()); 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] #[test]
fn test_valid_register_username() { fn test_valid_register_username() {
assert!(is_valid_username("Hello_98")); assert!(is_valid_username("Hello_98"));

View file

@ -1,7 +1,7 @@
use diesel::{result::Error, PgConnection}; use diesel::{result::Error, PgConnection};
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation}; use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation};
use lemmy_db::{user::User_, Crud}; use lemmy_db::{user::User_, Crud};
use lemmy_utils::{is_email_regex, settings::Settings}; use lemmy_utils::settings::Settings;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
type Jwt = String; type Jwt = String;
@ -54,18 +54,6 @@ impl Claims {
.unwrap() .unwrap()
} }
// TODO: move these into user?
pub fn find_by_email_or_username(
conn: &PgConnection,
username_or_email: &str,
) -> Result<User_, Error> {
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<User_, Error> { pub fn find_by_jwt(conn: &PgConnection, jwt: &str) -> Result<User_, Error> {
let claims: Claims = Claims::decode(&jwt).expect("Invalid token").claims; let claims: Claims = Claims::decode(&jwt).expect("Invalid token").claims;
User_::read(&conn, claims.id) User_::read(&conn, claims.id)

View file

@ -110,7 +110,6 @@ pub struct GetUserDetailsResponse {
moderates: Vec<CommunityModeratorView>, moderates: Vec<CommunityModeratorView>,
comments: Vec<CommentView>, comments: Vec<CommentView>,
posts: Vec<PostView>, posts: Vec<PostView>,
admins: Vec<UserView>, // TODO why is this necessary, just use GetSite
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -276,7 +275,7 @@ impl Perform for Oper<Login> {
// Fetch that username / email // Fetch that username / email
let username_or_email = data.username_or_email.clone(); let username_or_email = data.username_or_email.clone();
let user = match blocking(pool, move |conn| { 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? .await?
{ {
@ -643,14 +642,6 @@ impl Perform for Oper<GetUserDetails> {
}) })
.await??; .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 // 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 an if let chain would be better here, but can't figure it out
// TODO separate out settings into its own thing // TODO separate out settings into its own thing
@ -665,7 +656,6 @@ impl Perform for Oper<GetUserDetails> {
moderates, moderates,
comments, comments,
posts, posts,
admins,
}) })
} }
} }

View file

@ -1,5 +1,4 @@
use crate::{ use crate::{
api::claims::Claims,
apub::{ apub::{
activities::send_activity, activities::send_activity,
create_apub_response, create_apub_response,
@ -253,7 +252,7 @@ pub async fn get_apub_user_http(
) -> Result<HttpResponse<Body>, LemmyError> { ) -> Result<HttpResponse<Body>, LemmyError> {
let user_name = info.into_inner().user_name; let user_name = info.into_inner().user_name;
let user = blocking(&db, move |conn| { 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??; .await??;
let u = user.to_apub(&db).await?; let u = user.to_apub(&db).await?;

View file

@ -1,7 +1,7 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take, last } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { import {
UserOperation, UserOperation,
@ -16,7 +16,6 @@ import {
CommentResponse, CommentResponse,
BanUserResponse, BanUserResponse,
PostResponse, PostResponse,
AddAdminResponse,
} from '../interfaces'; } from '../interfaces';
import { import {
wsJsonToRes, wsJsonToRes,
@ -41,6 +40,7 @@ interface UserDetailsProps {
enableNsfw: boolean; enableNsfw: boolean;
view: UserDetailsView; view: UserDetailsView;
onPageChange(page: number): number | any; onPageChange(page: number): number | any;
admins: Array<UserView>;
} }
interface UserDetailsState { interface UserDetailsState {
@ -49,7 +49,6 @@ interface UserDetailsState {
comments: Array<Comment>; comments: Array<Comment>;
posts: Array<Post>; posts: Array<Post>;
saved?: Array<Post>; saved?: Array<Post>;
admins: Array<UserView>;
} }
export class UserDetails extends Component<UserDetailsProps, UserDetailsState> { export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
@ -63,7 +62,6 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
comments: [], comments: [],
posts: [], posts: [],
saved: [], saved: [],
admins: [],
}; };
this.subscription = WebSocketService.Instance.subject this.subscription = WebSocketService.Instance.subject
@ -152,7 +150,7 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
{i.type === 'posts' ? ( {i.type === 'posts' ? (
<PostListing <PostListing
post={i.data as Post} post={i.data as Post}
admins={this.state.admins} admins={this.props.admins}
showCommunity showCommunity
enableDownvotes={this.props.enableDownvotes} enableDownvotes={this.props.enableDownvotes}
enableNsfw={this.props.enableNsfw} enableNsfw={this.props.enableNsfw}
@ -160,7 +158,7 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
) : ( ) : (
<CommentNodes <CommentNodes
nodes={[{ comment: i.data as Comment }]} nodes={[{ comment: i.data as Comment }]}
admins={this.state.admins} admins={this.props.admins}
noIndent noIndent
showContext showContext
enableDownvotes={this.props.enableDownvotes} enableDownvotes={this.props.enableDownvotes}
@ -177,7 +175,7 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
<div> <div>
<CommentNodes <CommentNodes
nodes={commentsToFlatNodes(this.state.comments)} nodes={commentsToFlatNodes(this.state.comments)}
admins={this.state.admins} admins={this.props.admins}
noIndent noIndent
showContext showContext
enableDownvotes={this.props.enableDownvotes} enableDownvotes={this.props.enableDownvotes}
@ -192,7 +190,7 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
{this.state.posts.map(post => ( {this.state.posts.map(post => (
<PostListing <PostListing
post={post} post={post}
admins={this.state.admins} admins={this.props.admins}
showCommunity showCommunity
enableDownvotes={this.props.enableDownvotes} enableDownvotes={this.props.enableDownvotes}
enableNsfw={this.props.enableNsfw} enableNsfw={this.props.enableNsfw}
@ -252,7 +250,6 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
follows: data.follows, follows: data.follows,
moderates: data.moderates, moderates: data.moderates,
posts: data.posts, posts: data.posts,
admins: data.admins,
}); });
} else if (res.op == UserOperation.CreateCommentLike) { } else if (res.op == UserOperation.CreateCommentLike) {
const data = res.data as CommentResponse; const data = res.data as CommentResponse;
@ -298,11 +295,6 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
posts: this.state.posts, posts: this.state.posts,
comments: this.state.comments, comments: this.state.comments,
}); });
} else if (res.op == UserOperation.AddAdmin) {
const data = res.data as AddAdminResponse;
this.setState({
admins: data.admins,
});
} }
} }
} }

View file

@ -13,9 +13,9 @@ import {
DeleteAccountForm, DeleteAccountForm,
WebSocketJsonResponse, WebSocketJsonResponse,
GetSiteResponse, GetSiteResponse,
Site,
UserDetailsView, UserDetailsView,
UserDetailsResponse, UserDetailsResponse,
AddAdminResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { import {
@ -54,7 +54,7 @@ interface UserState {
deleteAccountLoading: boolean; deleteAccountLoading: boolean;
deleteAccountShowConfirm: boolean; deleteAccountShowConfirm: boolean;
deleteAccountForm: DeleteAccountForm; deleteAccountForm: DeleteAccountForm;
site: Site; siteRes: GetSiteResponse;
} }
interface UserProps { interface UserProps {
@ -114,19 +114,24 @@ export class User extends Component<any, UserState> {
deleteAccountForm: { deleteAccountForm: {
password: null, password: null,
}, },
site: { siteRes: {
id: undefined, admins: [],
name: undefined, banned: [],
creator_id: undefined, online: undefined,
published: undefined, site: {
creator_name: undefined, id: undefined,
number_of_users: undefined, name: undefined,
number_of_posts: undefined, creator_id: undefined,
number_of_comments: undefined, published: undefined,
number_of_communities: undefined, creator_name: undefined,
enable_downvotes: undefined, number_of_users: undefined,
open_registration: undefined, number_of_posts: undefined,
enable_nsfw: 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<any, UserState> {
// Couldnt get a refresh working. This does for now. // Couldnt get a refresh working. This does for now.
location.reload(); location.reload();
} }
document.title = `/u/${this.state.username} - ${this.state.site.name}`; document.title = `/u/${this.state.username} - ${this.state.siteRes.site.name}`;
setupTippy(); setupTippy();
} }
@ -236,8 +241,9 @@ export class User extends Component<any, UserState> {
sort={SortType[this.state.sort]} sort={SortType[this.state.sort]}
page={this.state.page} page={this.state.page}
limit={fetchLimit} limit={fetchLimit}
enableDownvotes={this.state.site.enable_downvotes} enableDownvotes={this.state.siteRes.site.enable_downvotes}
enableNsfw={this.state.site.enable_nsfw} enableNsfw={this.state.siteRes.site.enable_nsfw}
admins={this.state.siteRes.admins}
view={this.state.view} view={this.state.view}
onPageChange={this.handlePageChange} onPageChange={this.handlePageChange}
/> />
@ -637,7 +643,7 @@ export class User extends Component<any, UserState> {
/> />
</div> </div>
</div> </div>
{this.state.site.enable_nsfw && ( {this.state.siteRes.site.enable_nsfw && (
<div class="form-group"> <div class="form-group">
<div class="form-check"> <div class="form-check">
<input <input
@ -1063,9 +1069,12 @@ export class User extends Component<any, UserState> {
this.context.router.history.push('/'); this.context.router.history.push('/');
} else if (res.op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
const data = res.data as GetSiteResponse; const data = res.data as GetSiteResponse;
this.setState({ this.state.siteRes = data;
site: data.site, 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);
} }
} }
} }