From 19c9683e2126c68cb9d231293162c756d69c51fb Mon Sep 17 00:00:00 2001 From: Wes Bos Date: Thu, 8 Feb 2018 10:41:16 -0500 Subject: WIP --- backend/README.md | 4 + backend/database/datamodel.graphql | 2 +- backend/database/prisma.yml | 2 +- backend/src/generated/prisma.graphql | 44 +- backend/src/resolvers/Mutation.js | 31 +- backend/src/resolvers/Query.js | 7 +- backend/src/schema.graphql | 5 +- backend/src/utils.js | 10 + frontend/components/Header.js | 16 +- frontend/components/Item.js | 114 ++-- frontend/components/Items.js | 56 +- frontend/components/Page.js | 40 +- frontend/components/Pagination.js | 16 +- frontend/components/RequestReset.js | 71 +++ frontend/components/Signin.js | 76 +++ frontend/components/Signout.js | 20 + frontend/components/Signup.js | 130 +++-- frontend/components/SingleItem.js | 26 +- frontend/components/TakeMyMoney.js | 24 +- frontend/components/UpdateItem.js | 67 ++- frontend/enhancers/enhancers.js | 36 +- frontend/lib/initApollo.js | 11 +- frontend/package-lock.json | 1047 +++++++++++++++++----------------- frontend/package.json | 3 +- frontend/pages/admin/update.js | 7 +- frontend/pages/signup.js | 24 +- frontend/queries/index.js | 71 +-- 27 files changed, 1097 insertions(+), 863 deletions(-) create mode 100644 frontend/components/RequestReset.js create mode 100644 frontend/components/Signin.js create mode 100644 frontend/components/Signout.js diff --git a/backend/README.md b/backend/README.md index 4acc044..7687fd4 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1 +1,5 @@ What are all these files?! + +What are the steps: + +1. diff --git a/backend/database/datamodel.graphql b/backend/database/datamodel.graphql index c383b23..40dd30c 100644 --- a/backend/database/datamodel.graphql +++ b/backend/database/datamodel.graphql @@ -16,7 +16,7 @@ type User { posts: [Post!]! website: String age: Int - dummy: String + resetToken: String } type Item { diff --git a/backend/database/prisma.yml b/backend/database/prisma.yml index ce652ef..70e10f4 100644 --- a/backend/database/prisma.yml +++ b/backend/database/prisma.yml @@ -1,5 +1,5 @@ # the name for the service (will be part of the service's HTTP endpoint) -service: sick-fits +service: sick-fitz # the cluster and stage the service is deployed to stage: ${env:PRISMA_STAGE} diff --git a/backend/src/generated/prisma.graphql b/backend/src/generated/prisma.graphql index e59a1c9..5e2d471 100644 --- a/backend/src/generated/prisma.graphql +++ b/backend/src/generated/prisma.graphql @@ -30,7 +30,7 @@ type User implements Node { posts(where: PostWhereInput, orderBy: PostOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Post!] website: String age: Int - dummy: String + resetToken: String } @@ -421,7 +421,7 @@ input UserCreateInput { name: String! website: String age: Int - dummy: String + resetToken: String posts: PostCreateManyWithoutAuthorInput } @@ -436,7 +436,7 @@ input UserCreateWithoutPostsInput { name: String! website: String age: Int - dummy: String + resetToken: String } type UserEdge { @@ -457,8 +457,8 @@ enum UserOrderByInput { website_DESC age_ASC age_DESC - dummy_ASC - dummy_DESC + resetToken_ASC + resetToken_DESC updatedAt_ASC updatedAt_DESC createdAt_ASC @@ -472,7 +472,7 @@ type UserPreviousValues { name: String! website: String age: Int - dummy: String + resetToken: String } type UserSubscriptionPayload { @@ -498,7 +498,7 @@ input UserUpdateInput { name: String website: String age: Int - dummy: String + resetToken: String posts: PostUpdateManyWithoutAuthorInput } @@ -517,7 +517,7 @@ input UserUpdateWithoutPostsDataInput { name: String website: String age: Int - dummy: String + resetToken: String } input UserUpdateWithoutPostsInput { @@ -612,20 +612,20 @@ input UserWhereInput { age_lte: Int age_gt: Int age_gte: Int - dummy: String - dummy_not: String - dummy_in: [String!] - dummy_not_in: [String!] - dummy_lt: String - dummy_lte: String - dummy_gt: String - dummy_gte: String - dummy_contains: String - dummy_not_contains: String - dummy_starts_with: String - dummy_not_starts_with: String - dummy_ends_with: String - dummy_not_ends_with: String + resetToken: String + resetToken_not: String + resetToken_in: [String!] + resetToken_not_in: [String!] + resetToken_lt: String + resetToken_lte: String + resetToken_gt: String + resetToken_gte: String + resetToken_contains: String + resetToken_not_contains: String + resetToken_starts_with: String + resetToken_not_starts_with: String + resetToken_ends_with: String + resetToken_not_ends_with: String posts_every: PostWhereInput posts_some: PostWhereInput posts_none: PostWhereInput diff --git a/backend/src/resolvers/Mutation.js b/backend/src/resolvers/Mutation.js index fbae6fe..95be561 100644 --- a/backend/src/resolvers/Mutation.js +++ b/backend/src/resolvers/Mutation.js @@ -2,6 +2,8 @@ const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const { forwardTo } = require('prisma-binding'); const { getUserId, Context } = require('../utils'); +const { randomBytes } = require('crypto'); +const { promisify } = require('util'); const mutations = { // Signup Mutations @@ -17,7 +19,7 @@ const mutations = { }; }, - async login(parent, { email, password }, ctx, info) { + async signin(parent, { email, password }, ctx, info) { const user = await ctx.db.query.user({ where: { email } }); if (!user) { throw new Error(`No such user found for email: ${email}`); @@ -40,8 +42,7 @@ const mutations = { }, async deleteItem(parent, args, ctx, info) { - console.log('gonna delete one'); - console.log(args); + // TODO - handle auth for deleting an item return ctx.db.mutation.deleteItem( { where: { @@ -53,7 +54,6 @@ const mutations = { }, async updateItem(parent, args, ctx, info) { - console.log('Updating an item'); const updates = { ...args }; delete updates.id; return ctx.db.mutation.updateItem({ @@ -123,9 +123,30 @@ const mutations = { text: args.text, }, }); - console.log(updatedPost); return updatedPost; }, + + // Send password request + async requestReset(parent, args, ctx, info) { + // 1. find if there is a user with that email + const user = await ctx.db.query.user({ where: { email: args.email } }); + + if (!user) { + throw new Error(`No user with the email ${args.email}`); + } + console.log(user); + // 2. Set a reset token, and a reset date + // const resetToken = await promisify(randomBytes)(20); + // const resetTokenExpiry = Date.now() + 3600000; // 1 hour from now + // console.log({ resetToken, resetTokenExpiry }); + // const res = await ctx.db.mutation.updateUser({ + // where: { email: args.email }, + // data: { resetToken, resetTokenExpiry }, + // }); + + // console.log(res); + // 3. Send them their token via email + }, }; module.exports = mutations; diff --git a/backend/src/resolvers/Query.js b/backend/src/resolvers/Query.js index a2eb749..e6ea299 100644 --- a/backend/src/resolvers/Query.js +++ b/backend/src/resolvers/Query.js @@ -1,4 +1,4 @@ -const { getUserId, Context } = require('../utils'); +const { getUserId, Context, checkForUserId } = require('../utils'); const { forwardTo } = require('prisma-binding'); const Query = { @@ -31,6 +31,11 @@ const Query = { }, me(parent, args, ctx, info) { + const Authorization = ctx.request.get('Authorization'); + if (!Authorization || Authorization === 'null') { + console.log('Authorization is null'); + return null; + } const id = getUserId(ctx); return ctx.db.query.user({ where: { id } }, info); }, diff --git a/backend/src/schema.graphql b/backend/src/schema.graphql index 993236e..8a2fa23 100644 --- a/backend/src/schema.graphql +++ b/backend/src/schema.graphql @@ -26,7 +26,8 @@ type Query { type Mutation { signup(email: String!, password: String!, name: String!): AuthPayload! - login(email: String!, password: String!): AuthPayload! + signin(email: String!, password: String!): AuthPayload! + requestReset(email: String!): User createDraft(title: String!, text: String!): Post! publish(id: ID!): Post! deletePost(id: ID!): Post! @@ -41,7 +42,6 @@ type AuthPayload { user: User! } -# THe user definition overrides type User { id: ID! email: String! @@ -49,4 +49,5 @@ type User { name: String! posts: [Post!]! age: Int + resetToken: String } diff --git a/backend/src/utils.js b/backend/src/utils.js index 35587db..642c04a 100644 --- a/backend/src/utils.js +++ b/backend/src/utils.js @@ -11,6 +11,15 @@ function getUserId(ctx) { throw new AuthError(); } +function checkForUserId(ctx) { + const Authorization = ctx.request.get('Authorization'); + if (Authorization) { + const token = Authorization.replace('Bearer ', ''); + const { userId } = jwt.verify(token, process.env.APP_SECRET); + return userId; + } +} + class AuthError extends Error { constructor() { super('Not authorized'); @@ -20,4 +29,5 @@ class AuthError extends Error { module.exports = { getUserId, AuthError, + checkForUserId, }; diff --git a/frontend/components/Header.js b/frontend/components/Header.js index 5624fb5..5e1a620 100644 --- a/frontend/components/Header.js +++ b/frontend/components/Header.js @@ -6,6 +6,7 @@ import Login from './LoginAuth0'; import Search from './Search'; import NProgress from 'nprogress'; import Router from 'next/router'; +import Signout from './Signout'; Router.onRouteChangeStart = url => { console.log(`Loading: ${url}`); @@ -23,20 +24,17 @@ class Header extends Component { } render() { - // const user = this.props.currentUserQuery.user || {}; - // const { email = '' } = user; return (
- {/*

- Signed in as {email} -

*/} + {this.props.currentUser.me ? this.props.currentUser.me.email : 'Not Signed in'} + {/* */} - + {/* */}
); } } -const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUserQuery' }); -// export default compose(userEnhancer)(Header); -export default Header; +const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUser' }); +export default compose(userEnhancer)(Header); +// export default Header; diff --git a/frontend/components/Item.js b/frontend/components/Item.js index eb19918..d19af0e 100644 --- a/frontend/components/Item.js +++ b/frontend/components/Item.js @@ -1,10 +1,13 @@ -import styled from "styled-components"; -import slugify from "slugify"; -import { Link } from "../routes"; -import AddToCart from "./AddToCart"; -import makeImage from "../lib/image"; -import TakeMyMoney from "./TakeMyMoney"; -import formatMoney from "../lib/formatMoney"; +import React from 'react'; +import styled from 'styled-components'; +import slugify from 'slugify'; +import { compose } from 'react-apollo'; +import { Link } from '../routes'; +import AddToCart from './AddToCart'; +import makeImage from '../lib/image'; +import TakeMyMoney from './TakeMyMoney'; +import formatMoney from '../lib/formatMoney'; +import { removeItemMutation } from '../enhancers/enhancers'; const Item = styled.div` background: #f3f3f3; @@ -14,60 +17,53 @@ const Item = styled.div` } `; -const ItemComponent = ({ item }) => ( - - {item.image ? ( - {item.title} - ) : null} -

- - {item.title} - -

+class ItemComponent extends React.Component { + removeItem = () => { + this.props.removeItem({ variables: { id: this.props.item.id } }); + }; + render() { + const item = this.props.item; + return ( + + {item.image ? {item.title} : null} +

+ + {item.title} + +

-

{item.description}

- {/* { +

{item.description}

- - Edit ✏️ - + + Edit ✏️ + - } */} + + + - - - - - - -
-); + {/* */} + +
+ ); + } +} -export default ItemComponent; +export default compose(removeItemMutation)(ItemComponent); diff --git a/frontend/components/Items.js b/frontend/components/Items.js index 3e2c738..a3f98c9 100644 --- a/frontend/components/Items.js +++ b/frontend/components/Items.js @@ -1,11 +1,11 @@ -import { Component } from "react"; -import { withApollo, graphql, compose } from "react-apollo"; -import styled from "styled-components"; -import Pagination from "./Pagination"; -import Item from "./Item"; -import { itemEnhancer } from "../enhancers/enhancers"; +import { Component } from 'react'; +import { withApollo, graphql, compose } from 'react-apollo'; +import styled from 'styled-components'; +import Pagination from './Pagination'; +import Item from './Item'; +import { itemEnhancer } from '../enhancers/enhancers'; -import { ALL_ITEMS_QUERY, DELETE_ITEM_MUTATION } from "../queries"; +import { ALL_ITEMS_QUERY, DELETE_ITEM_MUTATION } from '../queries'; const Title = styled.h1` font-size: 10px; @@ -19,7 +19,6 @@ const Items = styled.div` class ItemList extends Component { componentDidMount() { - console.log(this.props.allItemsQuery); this.prefetchNextItems(this.props.page); } @@ -36,56 +35,35 @@ class ItemList extends Component { this.props.client.query({ query: ALL_ITEMS_QUERY, variables: { - skip: page * 3 - 3 - } + skip: page * 3 - 3, + }, }); }; render() { + console.log(this.props); // 1 - if (this.props.allItemsQuery && this.props.allItemsQuery.loading) { + if (this.props.itemsQuery && this.props.itemsQuery.loading) { return
Loading
; } // 2 - if (this.props.allItemsQuery && this.props.allItemsQuery.error) { - console.log(this.props.allItemsQuery.error); + if (this.props.itemsQuery && this.props.itemsQuery.error) { + console.log(this.props.itemsQuery.error); return
Error
; } - + console.log(this.props); // 3 - const itemsToRender = this.props.allItemsQuery.items; + const itemsToRender = this.props.itemsQuery.items; return (
Items For Sale - - {itemsToRender.map((item, i) => )} - + {itemsToRender.map((item, i) => )}
); } } -// 1 - -// We export the graphQL HOC - this will fetch the data and inject it into the ItemList compeont via props - -const deleteItemEnhancer = graphql(DELETE_ITEM_MUTATION, { - name: "removeItemMutation", - options: { - update: (proxy, { data: { deleteItem } }) => { - // grab the data from our cache - const data = proxy.readQuery({ query: ALL_ITEMS_QUERY }); - - // filter out the deleted item - data.allItems = data.allItems.filter(item => item.id !== deleteItem.id); - - // and then "set state" (update cache), so it will update wherever we have used this data on the page - proxy.writeQuery({ query: ALL_ITEMS_QUERY, data }); - } - } -}); - -export default withApollo(compose(itemEnhancer, deleteItemEnhancer)(ItemList)); +export default withApollo(compose(itemEnhancer)(ItemList)); diff --git a/frontend/components/Page.js b/frontend/components/Page.js index ceb0939..291bdcd 100644 --- a/frontend/components/Page.js +++ b/frontend/components/Page.js @@ -1,8 +1,26 @@ -import styled from "styled-components"; -import Header from "./Header"; -import Meta from "./Meta"; -import Nav from "./Nav"; -import CartList from "./CartList"; +import styled from 'styled-components'; +import Header from './Header'; +import Meta from './Meta'; +import Nav from './Nav'; +import CartList from './CartList'; +import { Component } from 'react'; + +class ErrorBoundary extends Component { + state = { + error: null, + errorInfo: null, + }; + componentDidCatch(error, errorInfo) { + console.log('Caught an erorrror!'); + this.setState({ error, errorInfo }); + } + render() { + if (this.state.error) { + return

Shit! AN error!

; + } + return this.props.children; + } +} const StyledPage = styled.div` font-family: sans-serif; @@ -13,11 +31,13 @@ const StyledPage = styled.div` const Page = ({ children }) => ( - -