Progress with authentication tolen
This commit is contained in:
@@ -4,6 +4,7 @@ import Header from './Header';
|
|||||||
import { Switch, Route } from 'react-router-dom';
|
import { Switch, Route } from 'react-router-dom';
|
||||||
// import ProductList from './ProductList';
|
// import ProductList from './ProductList';
|
||||||
import AppointmentList from './AppointmentList';
|
import AppointmentList from './AppointmentList';
|
||||||
|
import Login from './Login';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return (
|
return (
|
||||||
@@ -14,6 +15,7 @@ const App = () => {
|
|||||||
{/* <Route exact path="/" component={ProductList} /> */}
|
{/* <Route exact path="/" component={ProductList} /> */}
|
||||||
<Route exact path="/" component={AppointmentList} />
|
<Route exact path="/" component={AppointmentList} />
|
||||||
<Route exact path="/create" component={CreateAppointment} />
|
<Route exact path="/create" component={CreateAppointment} />
|
||||||
|
<Route exact path="/login" component={Login} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ const CREATE_APPOINTMENT_MUTATION = gql`
|
|||||||
mutation CreateAppointmentMutation(
|
mutation CreateAppointmentMutation(
|
||||||
$title: String!
|
$title: String!
|
||||||
$description: String!
|
$description: String!
|
||||||
|
$timeStart: String!
|
||||||
|
$timeEnd: String!
|
||||||
) {
|
) {
|
||||||
createAppointment(title: $title, description: $description) {
|
createAppointment(title: $title, description: $description, timeStart: $timeStart, timeEnd: $timeEnd) {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
description
|
description
|
||||||
|
|||||||
@@ -1,23 +1,30 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
// import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
import { Link, withRouter } from 'react-router-dom';
|
import { Link, withRouter } from 'react-router-dom';
|
||||||
|
import { AUTH_TOKEN } from '../constants';
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
// const history = useHistory();
|
const history = useHistory();
|
||||||
|
const authToken = localStorage.getItem(AUTH_TOKEN);
|
||||||
return (
|
return (
|
||||||
<div className="flex pa1 justify-between nowrap orange">
|
<div className="flex pa1 justify-between nowrap orange">
|
||||||
<div className="flex flex-fixed black">
|
<div className="flex flex-fixed black">
|
||||||
<div className="fw7 mr1">Store!</div>
|
<div className="fw7 mr1">Store!</div>
|
||||||
<Link to="/" className="ml1 no-underline black">
|
<Link to="/" className="ml1 no-underline black">Top list</Link>
|
||||||
Top list
|
|
||||||
</Link>
|
|
||||||
<div className="ml1"> --- </div>
|
<div className="ml1"> --- </div>
|
||||||
<Link
|
<Link to="/create" className="ml1 no-underline black">Create new</Link>
|
||||||
to="/create"
|
</div>
|
||||||
className="ml1 no-underline black"
|
<div className="flex flex-fixed">
|
||||||
>
|
{authToken ? (
|
||||||
Create new
|
<div className="ml1 pointer black"
|
||||||
</Link>
|
onClick={() => {
|
||||||
|
localStorage.removeItem(AUTH_TOKEN);
|
||||||
|
history.push(`/`);
|
||||||
|
}}
|
||||||
|
>Logout</div>
|
||||||
|
) : (
|
||||||
|
<Link to="/login" className="ml1 no-underline black">Login</Link>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const SIGNUP_MUTATION = gql`
|
|||||||
signup(
|
signup(
|
||||||
email: $email
|
email: $email
|
||||||
password: $password
|
password: $password
|
||||||
name: $name
|
username: $name
|
||||||
) {
|
) {
|
||||||
token
|
token
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import './styles/index.css';
|
import './styles/index.css';
|
||||||
|
import { AUTH_TOKEN } from './constants';
|
||||||
import App from './components/App';
|
import App from './components/App';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
import { setContext } from '@apollo/client/link/context';
|
||||||
// import * as serviceWorker from './serviceWorker';
|
// import * as serviceWorker from './serviceWorker';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -16,8 +18,19 @@ const httpLink = createHttpLink({
|
|||||||
uri: 'http://localhost:4000/graphql'
|
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({
|
const client = new ApolloClient({
|
||||||
link: httpLink,
|
link: authLink.concat(httpLink),
|
||||||
|
// link: httpLink,
|
||||||
cache: new InMemoryCache()
|
cache: new InMemoryCache()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
21
server/src/models/user.js
Normal file
21
server/src/models/user.js
Normal file
@@ -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);
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
// import Appointment from '../../client/src/components/Appointment.js';
|
// import Appointment from '../../client/src/components/Appointment.js';
|
||||||
import Product from './models/product.js';
|
import Product from './models/product.js';
|
||||||
import Appointment from './models/appointment.js';
|
import Appointment from './models/appointment.js';
|
||||||
|
import User from './models/user.js'
|
||||||
// import { createAppointment } from './resolvers/Mutation.js';
|
// import { createAppointment } from './resolvers/Mutation.js';
|
||||||
|
import bcrypt from 'bcryptjs';
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
|
||||||
export const resolvers = {
|
export const resolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
@@ -11,8 +15,58 @@ export const resolvers = {
|
|||||||
async allProducts() {
|
async allProducts() {
|
||||||
return await Product.find();
|
return await Product.find();
|
||||||
},
|
},
|
||||||
|
async allUsers() {
|
||||||
|
return await User.find();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Mutation: {
|
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, {
|
async createAppointment(root, {
|
||||||
input
|
input
|
||||||
}) {
|
}) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ type Query {
|
|||||||
): Feed!
|
): Feed!
|
||||||
allProducts: [Product]
|
allProducts: [Product]
|
||||||
allAppointments: [Appointment]
|
allAppointments: [Appointment]
|
||||||
|
allUsers: [User]
|
||||||
users: [User!]!
|
users: [User!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,9 +40,7 @@ type Mutation {
|
|||||||
_id: ID!
|
_id: ID!
|
||||||
) : Product
|
) : Product
|
||||||
signup(
|
signup(
|
||||||
email: String!
|
input: UserInput
|
||||||
password: String!
|
|
||||||
name: String!
|
|
||||||
): AuthPayload
|
): AuthPayload
|
||||||
login(
|
login(
|
||||||
email: String!,
|
email: String!,
|
||||||
@@ -57,19 +56,26 @@ type Subscription {
|
|||||||
newFollow: Follow
|
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 {
|
type AuthPayload {
|
||||||
token: String
|
token: String
|
||||||
user: User
|
user: User
|
||||||
}
|
}
|
||||||
|
|
||||||
type User {
|
# Appointment schemas
|
||||||
_id: ID!
|
|
||||||
name: String!
|
|
||||||
email: String!
|
|
||||||
appointments: [Appointment!]!
|
|
||||||
}
|
|
||||||
|
|
||||||
# Appointment model
|
|
||||||
type Appointment {
|
type Appointment {
|
||||||
_id: ID!
|
_id: ID!
|
||||||
title: String!
|
title: String!
|
||||||
@@ -91,8 +97,13 @@ input AppointmentInput {
|
|||||||
timeStart: Time!
|
timeStart: Time!
|
||||||
timeEnd: Time!
|
timeEnd: Time!
|
||||||
}
|
}
|
||||||
|
input AppointmentOrderByInput {
|
||||||
|
description: Sort
|
||||||
|
url: Sort
|
||||||
|
createdAt: Sort
|
||||||
|
}
|
||||||
|
|
||||||
# Product model
|
# Product schemas
|
||||||
type Product {
|
type Product {
|
||||||
_id: ID!
|
_id: ID!
|
||||||
title: String!
|
title: String!
|
||||||
@@ -103,23 +114,17 @@ input ProductInput {
|
|||||||
qty: Int
|
qty: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Follow schemas
|
||||||
type Follow {
|
type Follow {
|
||||||
_id: ID!
|
_id: ID!
|
||||||
appointment: Appointment!
|
appointment: Appointment!
|
||||||
user: User!
|
user: User!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# General-purpose schemas
|
||||||
input AppointmentOrderByInput {
|
|
||||||
description: Sort
|
|
||||||
url: Sort
|
|
||||||
createdAt: Sort
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Sort {
|
enum Sort {
|
||||||
asc
|
asc
|
||||||
desc
|
desc
|
||||||
}
|
}
|
||||||
|
|
||||||
scalar DateTime
|
scalar DateTime
|
||||||
scalar Time
|
scalar Time
|
||||||
|
|||||||
Reference in New Issue
Block a user