From 436eb4410c56dac0e9b1d842d5a8b461a5904ca6 Mon Sep 17 00:00:00 2001 From: Riccardo Date: Tue, 5 Jan 2021 13:58:26 +0100 Subject: [PATCH] Progress with authentication tolen --- client/src/components/App.js | 2 + client/src/components/CreateAppointment.js | 4 +- client/src/components/Header.js | 29 +++++++----- client/src/components/Login.js | 2 +- client/src/index.js | 15 +++++- server/src/models/user.js | 21 +++++++++ server/src/resolvers.js | 54 ++++++++++++++++++++++ server/src/schema.graphql | 45 ++++++++++-------- 8 files changed, 138 insertions(+), 34 deletions(-) create mode 100644 server/src/models/user.js diff --git a/client/src/components/App.js b/client/src/components/App.js index 3b5fcd4..23c8857 100644 --- a/client/src/components/App.js +++ b/client/src/components/App.js @@ -4,6 +4,7 @@ import Header from './Header'; import { Switch, Route } from 'react-router-dom'; // import ProductList from './ProductList'; import AppointmentList from './AppointmentList'; +import Login from './Login'; const App = () => { return ( @@ -14,6 +15,7 @@ const App = () => { {/* */} + diff --git a/client/src/components/CreateAppointment.js b/client/src/components/CreateAppointment.js index dd84c7d..7c2e7c8 100644 --- a/client/src/components/CreateAppointment.js +++ b/client/src/components/CreateAppointment.js @@ -10,8 +10,10 @@ const CREATE_APPOINTMENT_MUTATION = gql` mutation CreateAppointmentMutation( $title: String! $description: String! + $timeStart: String! + $timeEnd: String! ) { - createAppointment(title: $title, description: $description) { + createAppointment(title: $title, description: $description, timeStart: $timeStart, timeEnd: $timeEnd) { id title description diff --git a/client/src/components/Header.js b/client/src/components/Header.js index ae4fbbc..aa16ecb 100644 --- a/client/src/components/Header.js +++ b/client/src/components/Header.js @@ -1,23 +1,30 @@ import React from 'react'; -// import { useHistory } from 'react-router'; +import { useHistory } from 'react-router'; import { Link, withRouter } from 'react-router-dom'; +import { AUTH_TOKEN } from '../constants'; const Header = () => { - // const history = useHistory(); + const history = useHistory(); + const authToken = localStorage.getItem(AUTH_TOKEN); return (
Store!
- - Top list - + Top list
---
- - Create new - + Create new +
+
+ {authToken ? ( +
{ + localStorage.removeItem(AUTH_TOKEN); + history.push(`/`); + }} + >Logout
+ ) : ( + Login + )}
); diff --git a/client/src/components/Login.js b/client/src/components/Login.js index 47ec480..db1e928 100644 --- a/client/src/components/Login.js +++ b/client/src/components/Login.js @@ -12,7 +12,7 @@ const SIGNUP_MUTATION = gql` signup( email: $email password: $password - name: $name + username: $name ) { token } diff --git a/client/src/index.js b/client/src/index.js index da1ddc1..4061cad 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -1,8 +1,10 @@ import React from 'react'; import ReactDOM from 'react-dom'; import './styles/index.css'; +import { AUTH_TOKEN } from './constants'; import App from './components/App'; import { BrowserRouter } from 'react-router-dom'; +import { setContext } from '@apollo/client/link/context'; // import * as serviceWorker from './serviceWorker'; import { @@ -16,8 +18,19 @@ const httpLink = createHttpLink({ uri: 'http://localhost:4000/graphql' }); +const authLink = setContext((_, { headers }) => { + const token = localStorage.getItem(AUTH_TOKEN); + return { + headers: { + ...headers, + authorization: token ? `Bearer ${token}` : '' + } + }; +}); + const client = new ApolloClient({ - link: httpLink, + link: authLink.concat(httpLink), + // link: httpLink, cache: new InMemoryCache() }); diff --git a/server/src/models/user.js b/server/src/models/user.js new file mode 100644 index 0000000..b35e617 --- /dev/null +++ b/server/src/models/user.js @@ -0,0 +1,21 @@ +import mongoose from 'mongoose'; +const Schema = mongoose.Schema; +const UserSchema = new Schema({ + username: { + type: String, + required: true + }, + email: { + type: String, + required: true + }, + password: { + type: String, + required: true + }, + deleted: { + type: Boolean, + required: false + } +}); +export default mongoose.model('user', UserSchema); \ No newline at end of file diff --git a/server/src/resolvers.js b/server/src/resolvers.js index c8a4924..381ef62 100644 --- a/server/src/resolvers.js +++ b/server/src/resolvers.js @@ -1,7 +1,11 @@ // import Appointment from '../../client/src/components/Appointment.js'; import Product from './models/product.js'; import Appointment from './models/appointment.js'; +import User from './models/user.js' // import { createAppointment } from './resolvers/Mutation.js'; +import bcrypt from 'bcryptjs'; +import jwt from 'jsonwebtoken'; +import dotenv from 'dotenv'; export const resolvers = { Query: { @@ -11,8 +15,58 @@ export const resolvers = { async allProducts() { return await Product.find(); }, + async allUsers() { + return await User.find(); + }, }, Mutation: { + async signup(root, { + input + }) { + console.log(input.password); + input.password = await bcrypt.hash(input.password, 10); + + const user = await User.create(input); + + const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET); + + return { + token, + user + }; + }, + + async login(root, { + email, password + }) { + const user = await User.findOne({ + email: email + }); + + if (!user) { + throw new Error('No such user found'); + } + + const pwd = await bcrypt.hash(password, 10); + console.log(pwd); + console.log(user.password); + + const valid = await bcrypt.compare( + password, + user.password + ); + if (!valid) { + throw new Error('Invalid password'); + } + + const token = jwt.sign({ userId: user.id }, APP_SECRET); + + return { + token, + user + }; + }, + async createAppointment(root, { input }) { diff --git a/server/src/schema.graphql b/server/src/schema.graphql index 8049368..8b309ee 100644 --- a/server/src/schema.graphql +++ b/server/src/schema.graphql @@ -8,6 +8,7 @@ type Query { ): Feed! allProducts: [Product] allAppointments: [Appointment] + allUsers: [User] users: [User!]! } @@ -39,9 +40,7 @@ type Mutation { _id: ID! ) : Product signup( - email: String! - password: String! - name: String! + input: UserInput ): AuthPayload login( email: String!, @@ -57,19 +56,26 @@ type Subscription { newFollow: Follow } +#User Schemas +type User { + _id: ID! + username: String! + email: String! + password: String! + # appointments: [Appointment!]! +} +input UserInput{ + username: String! + email: String! + password: String! + # appointments: [Appointment!]! +} type AuthPayload { token: String user: User } -type User { - _id: ID! - name: String! - email: String! - appointments: [Appointment!]! -} - -# Appointment model +# Appointment schemas type Appointment { _id: ID! title: String! @@ -91,8 +97,13 @@ input AppointmentInput { timeStart: Time! timeEnd: Time! } +input AppointmentOrderByInput { + description: Sort + url: Sort + createdAt: Sort +} -# Product model +# Product schemas type Product { _id: ID! title: String! @@ -103,23 +114,17 @@ input ProductInput { qty: Int } +# Follow schemas type Follow { _id: ID! appointment: Appointment! user: User! } - -input AppointmentOrderByInput { - description: Sort - url: Sort - createdAt: Sort -} - +# General-purpose schemas enum Sort { asc desc } - scalar DateTime scalar Time