summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--backend/src/index.js25
-rw-r--r--backend/src/resolvers/Mutation.js26
-rw-r--r--frontend/components/DeleteItem.js5
-rw-r--r--frontend/components/Signin.js80
-rw-r--r--frontend/lib/withData.js36
5 files changed, 96 insertions, 76 deletions
diff --git a/backend/src/index.js b/backend/src/index.js
index df5eb29..4f98c52 100644
--- a/backend/src/index.js
+++ b/backend/src/index.js
@@ -1,9 +1,30 @@
+const jwt = require('jsonwebtoken');
const createServer = require('./createServer');
const server = createServer();
-server.express.use((req, res, next, db) => {
- console.log('MIDDLEWARE!');
+// 1. Check JWT
+server.express.use((req, res, next) => {
+ const Authorization = req.get('Authorization');
+ if (Authorization) {
+ const token = Authorization.replace('Bearer ', '');
+ const { userId } = jwt.verify(token, process.env.APP_SECRET);
+ req.userId = userId;
+ }
+ next();
+});
+
+// 2. Get User from their ID
+server.express.use(async (req, res, next) => {
+ if (!req.userId) return next();
+ const user = await server.context().db.query.user(
+ { where: { id: req.userId } },
+ `
+ { id, permissions, email, name }
+ `
+ );
+ req.user = user;
+ console.log(req.user);
next();
});
diff --git a/backend/src/resolvers/Mutation.js b/backend/src/resolvers/Mutation.js
index 35037b8..9127b04 100644
--- a/backend/src/resolvers/Mutation.js
+++ b/backend/src/resolvers/Mutation.js
@@ -29,6 +29,7 @@ const mutations = {
async signin(parent, { email, password }, ctx, info) {
const user = await ctx.db.query.user({ where: { email } });
+ console.log(user);
if (!user) {
throw new Error(`No such user found for email: ${email}`);
}
@@ -64,16 +65,18 @@ const mutations = {
},
async deleteItem(parent, args, ctx, info) {
- // TODO - handle auth for deleting an item
- // You Should Either Own this item, or have CAN_DELETE in roles
- return ctx.db.mutation.deleteItem(
- {
- where: {
- id: args.id,
- },
- },
- info
- );
+ const where = {
+ id: args.id,
+ };
+ // 1. find the item
+ const item = await ctx.db.query.item({ where }, `{ user {id}, title, id, description }`);
+ // 2. Make sure they own it, or are an admin
+ if (item.user.id !== ctx.request.user.id || !ctx.request.user.permissions.includes('ADMIN')) {
+ throw new Error("You aren't allowed to delete that item!");
+ }
+
+ // You Should Either Own this item, or have ITEMDELETE in roles
+ return ctx.db.mutation.deleteItem({ where }, info);
},
async updateItem(parent, args, ctx, info) {
@@ -204,12 +207,15 @@ const mutations = {
info
);
},
+
// delete that cart item
async removeFromCart(parent, args, ctx, info) {
+ // TODO: add userId to where
return ctx.db.mutation.deleteCartItem({
where: { id: args.id },
});
},
+
async createOrder(parent, args, ctx, info) {
const userId = getUserId(ctx);
const user = await ctx.db.query.user(
diff --git a/frontend/components/DeleteItem.js b/frontend/components/DeleteItem.js
index 30b2bf7..8609ca7 100644
--- a/frontend/components/DeleteItem.js
+++ b/frontend/components/DeleteItem.js
@@ -2,6 +2,7 @@ import React from 'react';
import { Mutation } from 'react-apollo';
import PropTypes from 'prop-types';
import { REMOVE_ITEM_MUTATION, ALL_ITEMS_QUERY } from '../queries/index';
+import Error from './ErrorMessage';
class DeleteItem extends React.Component {
static propTypes = {
@@ -20,7 +21,7 @@ class DeleteItem extends React.Component {
render() {
return (
<Mutation mutation={REMOVE_ITEM_MUTATION} variables={{ id: this.props.id }} update={this.update}>
- {removeItem => (
+ {(removeItem, { error }) => (
<button
onClick={() => {
if (confirm('Are you sure you want to delete this item?')) {
@@ -28,7 +29,7 @@ class DeleteItem extends React.Component {
}
}}
>
- &times; Delete item
+ {error ? error.message : '× Delete Item'}
</button>
)}
</Mutation>
diff --git a/frontend/components/Signin.js b/frontend/components/Signin.js
index eeff57c..e701e94 100644
--- a/frontend/components/Signin.js
+++ b/frontend/components/Signin.js
@@ -1,8 +1,9 @@
import React, { Component } from 'react';
-import { Mutation, Query } from 'react-apollo';
+import { Mutation, Query, ApolloConsumer } from 'react-apollo';
import { SIGNIN_MUTATION, CURRENT_USER_QUERY } from '../queries';
import Error from './ErrorMessage';
import Form from './styles/Form';
+import { client } from '../lib/withData';
class Signin extends Component {
state = {
@@ -10,18 +11,11 @@ class Signin extends Component {
password: 'abc123',
};
- loginUser = async (e, signin, refetchUser) => {
+ loginUser = async (e, signin, client) => {
e.preventDefault();
const res = await signin();
localStorage.setItem('token', res.data.signin.token);
- await refetchUser();
- // TODO refetch current user query
- };
-
- update = (proxy, payload) => {
- const data = proxy.readQuery({ query: CURRENT_USER_QUERY });
- data.me = payload.data.signin.user;
- proxy.writeQuery({ query: CURRENT_USER_QUERY, data });
+ client.query({ query: CURRENT_USER_QUERY, fetchPolicy: 'network-only' });
};
saveToState = e => {
@@ -32,44 +26,40 @@ class Signin extends Component {
render() {
// TODO / ASK - do I really have to wrap this in a query just to access the refetch function
return (
- <Query query={CURRENT_USER_QUERY}>
- {({ refetch }) => (
- <Mutation mutation={SIGNIN_MUTATION} variables={this.state}>
- {(signin, { data, loading, error }) => (
- <Form onSubmit={e => this.loginUser(e, signin, refetch)}>
- <Error error={error} />
- <fieldset disabled={loading} aria-busy={loading}>
- <label htmlFor="email">
- Email
- <input
- value={this.state.email}
- onChange={this.saveToState}
- name="email"
- type="text"
- placeholder="email"
- />
- </label>
+ <Mutation mutation={SIGNIN_MUTATION} variables={this.state}>
+ {(signin, { data, loading, error }) => (
+ <Form onSubmit={e => this.loginUser(e, signin, client)}>
+ <Error error={error} />
+ <fieldset disabled={loading} aria-busy={loading}>
+ <label htmlFor="email">
+ Emailx
+ <input
+ value={this.state.email}
+ onChange={this.saveToState}
+ name="email"
+ type="text"
+ placeholder="email"
+ />
+ </label>
- <label htmlFor="password">
- Password
- <input
- type="password"
- name="password"
- id="password"
- className="password"
- placeholder="password"
- value={this.state.password}
- onChange={this.saveToState}
- />
- </label>
+ <label htmlFor="password">
+ Password
+ <input
+ type="password"
+ name="password"
+ id="password"
+ className="password"
+ placeholder="password"
+ value={this.state.password}
+ onChange={this.saveToState}
+ />
+ </label>
- <button type="submit">Sign In!</button>
- </fieldset>
- </Form>
- )}
- </Mutation>
+ <button type="submit">Sign In!</button>
+ </fieldset>
+ </Form>
)}
- </Query>
+ </Mutation>
);
}
}
diff --git a/frontend/lib/withData.js b/frontend/lib/withData.js
index 1a152a3..5c4614f 100644
--- a/frontend/lib/withData.js
+++ b/frontend/lib/withData.js
@@ -3,25 +3,27 @@ import ApolloClient from 'apollo-boost';
// can also be a function that accepts a `headers` object (SSR only) and returns a config
-export default withApollo({
- client: new ApolloClient({
- uri: 'http://localhost:4444',
- // cache: new InMemoryCache().restore(initialState || {}),
- ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
- request: operation => {
- console.log(operation);
- if (typeof localStorage !== 'undefined' && localStorage.getItem('token')) {
- console.log(`Bearer ${localStorage.getItem('token')}`);
- operation.setContext({
- headers: {
- authorization: `Bearer ${localStorage.getItem('token')}`,
- },
- });
- }
- },
- }),
+const client = new ApolloClient({
+ uri: 'http://localhost:4444',
+ // cache: new InMemoryCache().restore(initialState || {}),
+ ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
+ request: operation => {
+ console.log(operation);
+ if (typeof localStorage !== 'undefined' && localStorage.getItem('token')) {
+ console.log(`Bearer ${localStorage.getItem('token')}`);
+ operation.setContext({
+ headers: {
+ authorization: `Bearer ${localStorage.getItem('token')}`,
+ },
+ });
+ }
+ },
});
+export { client };
+
+export default withApollo({ client });
+
// OPTION 2
// import { withData } from 'next-apollo';