Sfoglia il codice sorgente

user component done.

Tomi Cvetic 5 anni fa
parent
commit
61f7a5d7e1

+ 10 - 0
backend/package-lock.json

@@ -706,6 +706,11 @@
         "tweetnacl": "^0.14.3"
       }
     },
+    "bcryptjs": {
+      "version": "2.4.3",
+      "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+      "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
+    },
     "bignumber.js": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz",
@@ -1365,6 +1370,11 @@
       "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
       "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
     },
+    "crypto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz",
+      "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig=="
+    },
     "crypto-random-string": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",

+ 2 - 0
backend/package.json

@@ -4,7 +4,9 @@
   "description": "",
   "main": "index.js",
   "dependencies": {
+    "bcryptjs": "^2.4.3",
     "cookie-parser": "^1.4.4",
+    "crypto": "^1.0.1",
     "dotenv": "^7.0.0",
     "express-fileupload": "^1.1.4",
     "graphql-yoga": "^1.17.4",

+ 2 - 1
backend/schema.graphql

@@ -1,10 +1,11 @@
 type Query {
   projects: [Project]!,
   connectionCommand(connectionId: String!, command: String!): String!
+  me: User!
 }
 
 type Mutation {
-  createUser(name: String!, email: String!): User!
+  createUser(name: String!, email: String!, abbreviation: String!, password: String!): User!
 }
 
 type User {

+ 43 - 2
backend/src/resolvers.js

@@ -1,11 +1,52 @@
 const { forwardTo } = require('prisma-binding')
+const bcrypt = require('bcryptjs')
+const jwt = require('jsonwebtoken')
+const { randomBytes } = require('crypto')
+const { promisify } = require('util')
 
 const Query = {
   projects: forwardTo('db'),
-  connectionCommand: (parent, args, context, info) => 'Hello!'
+  connectionCommand: (parent, args, context, info) => 'Hello!',
+  me: (parent, args, context, info) => {
+    if (!context.request.userId) throw new Error('Not logged in.')
+    return context.db.query.user({ where: { id: context.request.userId } }, info)
+  }
 }
 
-const Mutations = {}
+const Mutations = {
+  createUser: async (parent, args, context, info) => {
+    const email = args.email.toLowerCase()
+    const password = await bcrypt.hash(args.password, 10)
+    const user = await context.db.mutation.createUser({
+      data: {
+        ...args,
+        email,
+        password
+      }
+    },
+      info
+    )
+    const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET)
+    context.response.cookie('token', token, {
+      httpOnly: true,
+      maxAge: 7 * 24 * 3600 * 1000
+    })
+    return user
+  },
+  userLogin: async (parent, args, context, info) => {
+    const { email, password } = args
+    const user = await context.db.query.user({ where: { email } })
+    if (!user) throw new Error('User not found')
+    const valid = await bcrypt.compare(password, user.password)
+    if (!valid) throw new Error('Invalid password')
+    const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET)
+    context.response.cookie('token', token, {
+      httpOnly: true,
+      maxAge: 7 * 24 * 3600 * 1000
+    })
+    return user
+  }
+}
 
 const resolvers = {
   Query

+ 25 - 0
frontend/components/User.js

@@ -0,0 +1,25 @@
+import { Query } from 'react-apollo'
+import gql from 'graphql-tag'
+
+const CURRENT_USER = gql`
+  query {
+    me {
+      id
+      email
+      name
+      abbreviation
+    }
+  }
+`
+
+const User = props => {
+  console.log(props)
+  return (
+    <Query {...props} query={CURRENT_USER}>
+      {payload => props.children(payload)}
+    </Query>
+  )
+}
+
+export default User
+export { CURRENT_USER }

+ 2 - 1
frontend/components/UserCreate.js

@@ -1,6 +1,7 @@
 import React from 'react'
 import gql from 'graphql-tag'
 import { Mutation } from 'react-apollo'
+import { CURRENT_USER } from './User'
 
 const USER_CREATE = gql`
   mutation USER_CREATE($email: String!, $name: String!, $abbreviation: String!, $password: String!) {
@@ -29,7 +30,7 @@ class UserCreate extends React.Component {
     <Mutation
       mutation={USER_CREATE}
       variables={this.state}
-      refetchQueries={[{ query: USER_CREATE }]}
+      refetchQueries={[{ query: CURRENT_USER }]}
     >
       {(userCreate, { error, loading }) => (
         <form

+ 64 - 0
frontend/components/UserLogin.js

@@ -0,0 +1,64 @@
+import React from 'react'
+import { Mutation } from 'react-apollo'
+import gql from 'graphql-tag'
+import { CURRENT_USER } from './User'
+
+const USER_LOGIN = gql`
+  mutation USER_LOGIN($email: String!, $password: String!) {
+    userLogin(email: $email, password: $password) {
+      id
+      email
+      name
+      abbreviation
+    }
+  }
+`
+
+const CURRENT_USER = gql`
+  query CURRENT_USER() {
+    currentUser() {
+      
+    }
+  }
+`
+
+class UserLogin extends React.Component {
+  state = {
+    email: '',
+    password: ''
+  }
+
+  handleInput = event => {
+    this.setState({ [event.target.name]: event.target.value })
+  }
+
+  render() {
+    return (
+      <Mutation
+        mutation={USER_LOGIN}
+        variables={this.state}
+        refetchQueries={[{ query: CURRENT_USER }]}
+      >
+        {(userLogin, { error, loading }) => (
+          <form
+            method="POST"
+            onSubmit={event => {
+              event.preventDefault()
+              await userLogin()
+              this.setState({ email: '', password: '' })
+            }}
+          >
+            <fieldset disabled={loading}>
+              <h2>Log in</h2>
+              <input type="email" name="email" id="email" placeholder="" value={this.state.email} onChange={this.handleInput} />
+              <label htmlFor="email"></label>
+              <input type="password" name="password" id="password" placeholder="" value={this.state.email} onChange={this.handleInput} />
+              <label htmlFor="password"></label>
+              <button type="submit">Log in</button>
+            </fieldset>
+          </form>
+        )}
+      </Mutation>
+    )
+  }
+}

+ 21 - 0
frontend/components/UserLogout.js

@@ -0,0 +1,21 @@
+import React from 'react'
+import { Mutation } from 'react-apollo'
+import gql from 'graphql-tag'
+import { CURRENT_USER } from './User'
+
+const USER_LOGOUT = gql`
+  mutation USER_LOGOUT {
+    userLogout
+  }
+`
+
+const UserLogout = props => (
+  <Mutation
+    mutation={USER_LOGOUT}
+    refetchQueries={[{ query: CURRENT_USER }]}
+  >
+    {userLogout => <button onClick={userLogout}>Log out</button>}
+  </Mutation>
+)
+
+export default UserLogout

+ 10 - 0
frontend/pages/user.js

@@ -0,0 +1,10 @@
+import React from 'react'
+import User from '../components/User'
+
+const UserPage = props => (
+  <User>
+    <div>Secret content!!!</div>
+  </User>
+)
+
+export default UserPage