initial-frontend-work #2
@ -24,13 +24,20 @@ pub enum ApiResponseVariant {
 | 
			
		||||
    //    Flash(Flash<Redirect>)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize)]
 | 
			
		||||
pub struct UserWithToken {
 | 
			
		||||
    #[serde(flatten)]
 | 
			
		||||
    pub user: schema::User,
 | 
			
		||||
    pub jwt: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug)]
 | 
			
		||||
struct ApiResponse {
 | 
			
		||||
    result: Cow<'static, str>,
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    message: Option<Cow<'static, str>>,
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    jwt: Option<Cow<'static, str>>,
 | 
			
		||||
    user: Option<UserWithToken>,
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    gamenights: Option<Vec<schema::GameNight>>,
 | 
			
		||||
}
 | 
			
		||||
@ -42,7 +49,7 @@ impl ApiResponse {
 | 
			
		||||
    const SUCCES: Self = Self {
 | 
			
		||||
        result: Self::SUCCES_RESULT,
 | 
			
		||||
        message: None,
 | 
			
		||||
        jwt: None,
 | 
			
		||||
        user: None,
 | 
			
		||||
        gamenights: None,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@ -50,16 +57,19 @@ impl ApiResponse {
 | 
			
		||||
        Self {
 | 
			
		||||
            result: Self::FAILURE_RESULT,
 | 
			
		||||
            message: Some(Cow::Owned(message)),
 | 
			
		||||
            jwt: None,
 | 
			
		||||
            user: None,
 | 
			
		||||
            gamenights: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn login_response(jwt: String) -> Self {
 | 
			
		||||
    fn login_response(user: schema::User, jwt: String) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            result: Self::SUCCES_RESULT,
 | 
			
		||||
            message: None,
 | 
			
		||||
            jwt: Some(Cow::Owned(jwt)),
 | 
			
		||||
            user: Some(UserWithToken {
 | 
			
		||||
                user: user,
 | 
			
		||||
                jwt: jwt
 | 
			
		||||
            }),
 | 
			
		||||
            gamenights: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -68,7 +78,7 @@ impl ApiResponse {
 | 
			
		||||
        Self {
 | 
			
		||||
            result: Self::SUCCES_RESULT,
 | 
			
		||||
            message: None,
 | 
			
		||||
            jwt: None,
 | 
			
		||||
            user: None,
 | 
			
		||||
            gamenights: Some(gamenights),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -190,10 +200,11 @@ pub async fn login_post_json(
 | 
			
		||||
                ))));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let user = login_result.user.unwrap();
 | 
			
		||||
            let my_claims = Claims {
 | 
			
		||||
                exp: Utc::now().timestamp() + chrono::Duration::days(7).num_seconds(),
 | 
			
		||||
                uid: login_result.id.unwrap(),
 | 
			
		||||
                role: login_result.role.unwrap(),
 | 
			
		||||
                uid: user.id,
 | 
			
		||||
                role: user.role,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            let secret = &config.inner().jwt_secret;
 | 
			
		||||
@ -202,7 +213,7 @@ pub async fn login_post_json(
 | 
			
		||||
                &my_claims,
 | 
			
		||||
                &EncodingKey::from_secret(secret.as_bytes()),
 | 
			
		||||
            ) {
 | 
			
		||||
                Ok(token) => ApiResponseVariant::Value(json!(ApiResponse::login_response(token))),
 | 
			
		||||
                Ok(token) => ApiResponseVariant::Value(json!(ApiResponse::login_response(user, token))),
 | 
			
		||||
                Err(error) => {
 | 
			
		||||
                    ApiResponseVariant::Value(json!(ApiResponse::error(error.to_string())))
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -175,25 +175,19 @@ pub async fn login(conn: DbConn, login: Login) -> Result<LoginResult, DatabaseEr
 | 
			
		||||
            .verify_password(&login.password.as_bytes(), &parsed_hash)
 | 
			
		||||
            .is_ok()
 | 
			
		||||
        {
 | 
			
		||||
            let role: Role = match user::table
 | 
			
		||||
                .filter(user::id.eq(id))
 | 
			
		||||
                .select(user::role)
 | 
			
		||||
                .first(c)
 | 
			
		||||
            {
 | 
			
		||||
                Ok(role) => role,
 | 
			
		||||
                Err(error) => return Err(DatabaseError::Query(error.to_string())),
 | 
			
		||||
            let user: User = match user::table.find(id).first(c) {
 | 
			
		||||
                Ok(user) => user,
 | 
			
		||||
                Err(error) => return Err(DatabaseError::Query(error.to_string()))
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            Ok(LoginResult {
 | 
			
		||||
                result: true,
 | 
			
		||||
                id: Some(id),
 | 
			
		||||
                role: Some(role),
 | 
			
		||||
                user : Some(user)
 | 
			
		||||
            })
 | 
			
		||||
        } else {
 | 
			
		||||
            Ok(LoginResult {
 | 
			
		||||
                result: false,
 | 
			
		||||
                id: None,
 | 
			
		||||
                role: None,
 | 
			
		||||
                user: None
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
@ -251,7 +245,7 @@ pub async fn run_migrations(rocket: Rocket<Build>) -> Rocket<Build> {
 | 
			
		||||
    rocket
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize, DbEnum, Clone)]
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize, DbEnum, Clone, Copy)]
 | 
			
		||||
pub enum Role {
 | 
			
		||||
    Admin,
 | 
			
		||||
    User,
 | 
			
		||||
@ -318,6 +312,5 @@ pub struct Login {
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug)]
 | 
			
		||||
pub struct LoginResult {
 | 
			
		||||
    pub result: bool,
 | 
			
		||||
    pub id: Option<i32>,
 | 
			
		||||
    pub role: Option<Role>,
 | 
			
		||||
    pub user: Option<User>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,3 @@
 | 
			
		||||
import logo from './logo.svg';
 | 
			
		||||
import './App.css';
 | 
			
		||||
import React, { useState, useEffect } from 'react';
 | 
			
		||||
import MenuBar from './components/MenuBar';
 | 
			
		||||
@ -6,10 +5,11 @@ import Login from './components/Login';
 | 
			
		||||
import Gamenights from './components/Gamenights'
 | 
			
		||||
import AddGameNight from './components/AddGameNight'
 | 
			
		||||
 | 
			
		||||
const localStorageUserKey = 'user';
 | 
			
		||||
 | 
			
		||||
function App() {
 | 
			
		||||
 | 
			
		||||
  const [user, setUser] = useState(null);
 | 
			
		||||
  const [token, setToken] = useState(null);
 | 
			
		||||
  const [gamenights, setGamenights] = useState([]);
 | 
			
		||||
  const [flashData, setFlashData] = useState({});
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,8 @@ function App() {
 | 
			
		||||
      .then(response => response.json())
 | 
			
		||||
      .then(data => {
 | 
			
		||||
        if(data.result === "Ok") {
 | 
			
		||||
          setToken(data.jwt);
 | 
			
		||||
          setUser(data.user);
 | 
			
		||||
          localStorage.setItem(localStorageUserKey, JSON.stringify(data.user));
 | 
			
		||||
        } else {
 | 
			
		||||
          setFlashData({
 | 
			
		||||
            type: "Error",
 | 
			
		||||
@ -34,17 +35,17 @@ function App() {
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleAddGameNight = (input) => {
 | 
			
		||||
    if (token !== null) {
 | 
			
		||||
    if (user !== null) {
 | 
			
		||||
      const requestOptions = {
 | 
			
		||||
        method: 'POST',
 | 
			
		||||
        headers: {
 | 
			
		||||
          'Content-Type': 'application/json',
 | 
			
		||||
          'Authorization': `Bearer ${token}`
 | 
			
		||||
          'Authorization': `Bearer ${user.jwt}`
 | 
			
		||||
        },
 | 
			
		||||
        body: JSON.stringify(input)
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      fetch('api/gamenight', requestOptions)
 | 
			
		||||
      return fetch('api/gamenight', requestOptions)
 | 
			
		||||
        .then(response => response.json())
 | 
			
		||||
        .then(data => {
 | 
			
		||||
          if(data.result !== "Ok") {
 | 
			
		||||
@ -52,24 +53,25 @@ function App() {
 | 
			
		||||
              type: "Error",
 | 
			
		||||
              message: data.message
 | 
			
		||||
            });
 | 
			
		||||
            return false;
 | 
			
		||||
          } else {
 | 
			
		||||
            setToken(token);
 | 
			
		||||
            setUser({ ...user });
 | 
			
		||||
            return true;
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
  }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const onLogout = () => {
 | 
			
		||||
    setUser(null);
 | 
			
		||||
    setToken(null);
 | 
			
		||||
  }
 | 
			
		||||
    localStorage.removeItem(localStorageUserKey);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (token !== null) {
 | 
			
		||||
    if (user !== null) {
 | 
			
		||||
      const requestOptions = {
 | 
			
		||||
        method: 'GET',
 | 
			
		||||
        headers: { 'Authorization': `Bearer ${token}` },
 | 
			
		||||
        headers: { 'Authorization': `Bearer ${user.jwt}` },
 | 
			
		||||
      };
 | 
			
		||||
      fetch('api/gamenights', requestOptions)
 | 
			
		||||
        .then(response => response.json())
 | 
			
		||||
@ -84,9 +86,13 @@ function App() {
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  }, [user])
 | 
			
		||||
 | 
			
		||||
  if(token === null) {
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setUser(JSON.parse(localStorage.getItem(localStorageUserKey)));
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  if(user === null) {
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="App">
 | 
			
		||||
        <Login onChange={handleLogin}/>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import React, { useState } from 'react';
 | 
			
		||||
import React, { useEffect, useState } from 'react';
 | 
			
		||||
 | 
			
		||||
function AddGameNight(props) {
 | 
			
		||||
  const [expanded, setExpanded] = useState(false);
 | 
			
		||||
@ -9,6 +9,12 @@ function AddGameNight(props) {
 | 
			
		||||
    props.onChange({
 | 
			
		||||
      game: gameName,
 | 
			
		||||
      datetime: date
 | 
			
		||||
    }).then((result) => {
 | 
			
		||||
      if(result) {
 | 
			
		||||
        setExpanded(false);
 | 
			
		||||
        setGameName("");
 | 
			
		||||
        setDate(null);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    event.preventDefault();
 | 
			
		||||
  };
 | 
			
		||||
@ -21,6 +27,13 @@ function AddGameNight(props) {
 | 
			
		||||
    setDate(event.target.value);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if(!expanded) {
 | 
			
		||||
      setGameName("");
 | 
			
		||||
      setDate(null);
 | 
			
		||||
    }
 | 
			
		||||
  }, [expanded]);
 | 
			
		||||
 | 
			
		||||
  if(expanded) {
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="Add-GameNight">
 | 
			
		||||
@ -32,20 +45,20 @@ function AddGameNight(props) {
 | 
			
		||||
            <input id="gamename" name="gamename" type="text"
 | 
			
		||||
              value={gameName}
 | 
			
		||||
              onChange={handleNameChange} />
 | 
			
		||||
            <label for="date">date:</label>
 | 
			
		||||
            <input id="date" name="date" type="date"
 | 
			
		||||
            <label for="datetime">date:</label>
 | 
			
		||||
            <input id="datetime" name="datetime" type="datetime-local"
 | 
			
		||||
              value={date}
 | 
			
		||||
              onChange={handleDateChange} />
 | 
			
		||||
  
 | 
			
		||||
            <input type="submit" value="Submit" />
 | 
			
		||||
          </fieldset>
 | 
			
		||||
        </form>
 | 
			
		||||
        <button onClick={() => {setExpanded(false)}}>Expand</button>
 | 
			
		||||
        <button onClick={() => setExpanded(false)}>Discard</button>
 | 
			
		||||
      </div>        
 | 
			
		||||
    );
 | 
			
		||||
  } else {
 | 
			
		||||
    return (
 | 
			
		||||
      <button onClick={() => {setExpanded(true)}}>Expand</button>
 | 
			
		||||
      <button onClick={() => setExpanded(true)}>Expand</button>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,14 +2,10 @@ import React from 'react';
 | 
			
		||||
 | 
			
		||||
function Gamenights(props) {
 | 
			
		||||
 | 
			
		||||
  console.log(props.gamenights);
 | 
			
		||||
 | 
			
		||||
  let gamenights = props.gamenights.map(g => 
 | 
			
		||||
    (<li>{g.game}</li>)
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  console.log(gamenights);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <ul>
 | 
			
		||||
      {gamenights}
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ function MenuBar(props) {
 | 
			
		||||
        <a>Gamenight</a>
 | 
			
		||||
      </li>
 | 
			
		||||
      <li>
 | 
			
		||||
        <a>User: {props.user}</a>
 | 
			
		||||
        <a>User: {props.user.username}</a>
 | 
			
		||||
      </li>
 | 
			
		||||
      <li>
 | 
			
		||||
        <button onClick={props.onLogout}>Logout</button>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user