diff --git a/.gitignore b/.gitignore index 70ba85b..bddaeda 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .env __pycache__/ *.py[cod] +.vscode/* \ No newline at end of file diff --git a/client/src/App.js b/client/src/App.js index 281352b..e34fe38 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -1,30 +1,62 @@ -import React from 'react'; +import React, {useEffect, useState} from 'react'; import { BrowserRouter, Switch, Route, NavLink } from 'react-router-dom'; import UserList from './components/UsersList'; - +import UserForm from './components/UserForm'; +import AuthContext from './auth' function App() { + const [fetchWithCSRF, setFetchWithCSRF] = useState(() => fetch); + const authContextValue = { + fetchWithCSRF, + }; + useEffect(() => { + async function restoreCSRF() { + const response = await fetch('/api/csrf/restore', { + method: 'GET', + credentials: 'include' + }); + if (response.ok) { + const authData = await response.json(); + setFetchWithCSRF(() => { + return (resource, init) => { + if (init.headers) { + init.headers['X-CSRFToken'] = authData.csrf_token; + } else { + init.headers = { + 'X-CSRFToken': authData.csrf_token + } + } + return fetch(resource, init); + } + }); + } + } + restoreCSRF(); + }, []); return ( - - - - - - - - -

My Home Page

-
-
-
+ + + + + + + + + +

My Home Page

+
+
+
+
); } -export default App; +export default App; \ No newline at end of file diff --git a/client/src/auth.js b/client/src/auth.js new file mode 100644 index 0000000..082e4fe --- /dev/null +++ b/client/src/auth.js @@ -0,0 +1,5 @@ +import React from 'react'; + +const AuthContext = React.createContext({}); + +export default AuthContext; \ No newline at end of file diff --git a/client/src/components/User.js b/client/src/components/User.js index 7684d78..ad115f5 100644 --- a/client/src/components/User.js +++ b/client/src/components/User.js @@ -1,11 +1,17 @@ -import React from 'react'; - +import React, {} from 'react'; +import { Link } from 'react-router-dom'; function User(props) { return ( <> Username: {props.user.username}
Email: {props.user.email}
+ Edit
); diff --git a/client/src/components/UserForm.js b/client/src/components/UserForm.js new file mode 100644 index 0000000..21a62c4 --- /dev/null +++ b/client/src/components/UserForm.js @@ -0,0 +1,56 @@ +import React, {useState, useContext} from 'react'; +import AuthContext from '../auth' + +function UserForm(props) { + const { id } = props.location.state.user; + console.log(id) + const [username, setUsername] = useState(props.location.state.user.username); + const [email, setEmail] = useState(props.location.state.user.email); + + const [errors, setErrors] = useState([]); + const {fetchWithCSRF} = useContext(AuthContext); + + const submitForm = (e) => { + e.preventDefault(); + + async function updateUser() { + const response = await fetchWithCSRF(`/api/users/${id}`, { + method: 'POST', + headers: { + "Content-Type": "application/json", + }, + credentials: 'include', + body: JSON.stringify({ + username, + email + }) + }); + + const responseData = await response.json(); + if (!response.ok) { + setErrors(responseData.errors); + } + } + updateUser(); + } + return ( +
+ {errors.length ? errors.map((err) =>
  • {err}
  • ) : ''} +
    + +
    + setEmail(e.target.value)} name="email" /> +
    +
    +
    + +
    + setUsername(e.target.value)} name="username" /> +
    +
    + + +
    + ); +} +export default UserForm; \ No newline at end of file diff --git a/client/src/components/UsersList.js b/client/src/components/UsersList.js index c458245..2090077 100644 --- a/client/src/components/UsersList.js +++ b/client/src/components/UsersList.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, createContext } from 'react'; import User from './User'; @@ -13,7 +13,6 @@ function UsersList (props) { } fetchData(); }, []); - const userComponents = users.map((user) => ) return ( <> diff --git a/starter_app/__init__.py b/starter_app/__init__.py index edb283d..f1d5b49 100644 --- a/starter_app/__init__.py +++ b/starter_app/__init__.py @@ -12,6 +12,7 @@ app = Flask(__name__) app.config.from_object(Config) +CSRFProtect(app) app.register_blueprint(user_routes, url_prefix='/api/users') db.init_app(app) @@ -34,3 +35,6 @@ def react_root(path): if path == 'favicon.ico': return app.send_static_file('favicon.ico') return app.send_static_file('index.html') +@app.route('/api/csrf/restore') +def restore_csrf(): + return {'csrf_token': generate_csrf()} diff --git a/starter_app/api/user_routes.py b/starter_app/api/user_routes.py index b6fd740..2838a11 100644 --- a/starter_app/api/user_routes.py +++ b/starter_app/api/user_routes.py @@ -3,7 +3,13 @@ user_routes = Blueprint('users', __name__) + @user_routes.route('/') def index(): - response = User.query.all() - return { "users": [user.to_dict() for user in response]} \ No newline at end of file + response = User.query.all() + return {"users": [user.to_dict() for user in response]} + + +@user_routes.route('/', methods=['GET', 'POST']) +def user_detail(id): + return {} \ No newline at end of file