mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2026-01-20 20:01:25 +00:00
using graphql-yoga and implemented jwt login
This commit is contained in:
parent
e7a59d303a
commit
43707a4fcb
4
.env
4
.env
@ -1,6 +1,10 @@
|
||||
NEO4J_URI=bolt://localhost:7687
|
||||
NEO4J_USER=neo4j
|
||||
NEO4J_PASSWORD=letmein
|
||||
|
||||
GRAPHQL_LISTEN_PORT=4000
|
||||
GRAPHQL_URI=http://localhost:4000
|
||||
CLIENT_URI=http://localhost:3000
|
||||
JWT_SECRET=b/&&7b78BF&fv/Vd
|
||||
|
||||
MOCK=false
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
node_modules/
|
||||
.vscode
|
||||
|
||||
11
package.json
11
package.json
@ -16,20 +16,27 @@
|
||||
"apollo-client": "^2.3.2",
|
||||
"apollo-link-http": "^1.5.4",
|
||||
"apollo-server": "^2.0.4",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"dotenv": "^6.0.0",
|
||||
"graphql-custom-directives": "^0.2.13",
|
||||
"graphql-middleware": "^1.7.6",
|
||||
"graphql-tag": "^2.9.2",
|
||||
"graphql-yoga": "^1.16.2",
|
||||
"jsonwebtoken": "^8.3.0",
|
||||
"lodash": "^4.17.11",
|
||||
"ms": "^2.1.1",
|
||||
"neo4j-driver": "^1.6.1",
|
||||
"neo4j-graphql-js": "^1.0.2",
|
||||
"node-fetch": "^2.1.2",
|
||||
"passport": "^0.4.0",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"slug": "^0.9.1",
|
||||
"trunc-html": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"faker": "^4.1.0",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"nodemon": "^1.17.5"
|
||||
"faker": "^4.1.0",
|
||||
"nodemon": "^1.18.4"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,62 @@
|
||||
// import { neo4jgraphql } from "neo4j-graphql-js"
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import zipObject from 'lodash/zipObject'
|
||||
import generateJwt from './jwt/generateToken'
|
||||
import { fixUrl } from './middleware/fixImageUrlsMiddleware'
|
||||
|
||||
export const typeDefs =
|
||||
fs.readFileSync(process.env.GRAPHQL_SCHEMA || path.join(__dirname, "schema.graphql"))
|
||||
.toString('utf-8')
|
||||
|
||||
export const resolvers = {
|
||||
// Query: {
|
||||
// usersBySubstring: neo4jgraphql
|
||||
// }
|
||||
}
|
||||
Query: {
|
||||
isLoggedIn: (parent, args, { user }) => {
|
||||
console.log(user)
|
||||
return Boolean(user && user.id)
|
||||
}
|
||||
// usersBySubstring: neo4jgraphql
|
||||
},
|
||||
Mutation: {
|
||||
signup: async (parent, { email, password }, { req }) => {
|
||||
// if (data[email]) {
|
||||
// throw new Error('Another User with same email exists.')
|
||||
// }
|
||||
// data[email] = {
|
||||
// password: await bcrypt.hashSync(password, 10),
|
||||
// }
|
||||
|
||||
export const mutations = {
|
||||
return true
|
||||
},
|
||||
login: async (parent, { email, password }, { driver, req, user }) => {
|
||||
// if (user && user.id) {
|
||||
// throw new Error('Already logged in.')
|
||||
// }
|
||||
|
||||
const session = driver.session()
|
||||
const res = await session.run('MATCH (u:User {email: "' + email + '"}) RETURN u.id, u.slug, u.name, u.avatar, u.email, u.password, u.role LIMIT 1')
|
||||
let u = res.records[0]._fields ? zipObject([
|
||||
'id',
|
||||
'slug',
|
||||
'name',
|
||||
'avatar',
|
||||
'email',
|
||||
'password',
|
||||
'role'
|
||||
], res.records[0]._fields) : null
|
||||
if (u) {
|
||||
if (await bcrypt.compareSync(password, u.password)) {
|
||||
delete u.password
|
||||
u.avatar = fixUrl(u.avatar)
|
||||
return Object.assign(u, {
|
||||
token: generateJwt(u)
|
||||
})
|
||||
}
|
||||
throw new Error('Incorrect password.')
|
||||
}
|
||||
|
||||
throw new Error('No Such User exists.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
104
src/index.js
104
src/index.js
@ -1,21 +1,21 @@
|
||||
// import { GraphQLServer } from 'graphql-yoga'
|
||||
import { applyMiddleware } from 'graphql-middleware'
|
||||
import { ApolloServer, makeExecutableSchema } from 'apollo-server'
|
||||
import { GraphQLServer } from 'graphql-yoga'
|
||||
import { makeExecutableSchema } from 'apollo-server'
|
||||
import { augmentSchema } from 'neo4j-graphql-js'
|
||||
import { typeDefs, resolvers } from './graphql-schema'
|
||||
import { v1 as neo4j } from 'neo4j-driver'
|
||||
import passwordMiddleware from './middleware/passwordMiddleware'
|
||||
import softDeleteMiddleware from './middleware/softDeleteMiddleware'
|
||||
import sluggifyMiddleware from './middleware/sluggifyMiddleware'
|
||||
import fixImageUrlsMiddleware from './middleware/fixImageUrlsMiddleware'
|
||||
import excerptMiddleware from './middleware/excerptMiddleware'
|
||||
import dotenv from 'dotenv'
|
||||
import {
|
||||
GraphQLLowerCaseDirective,
|
||||
GraphQLTrimDirective,
|
||||
GraphQLDefaultToDirective
|
||||
} from 'graphql-custom-directives';
|
||||
import faker from 'faker'
|
||||
import mocks from './mocks'
|
||||
import middleware from './middleware'
|
||||
|
||||
import passport from 'passport'
|
||||
import jwtStrategy from './jwt/strategy'
|
||||
import jwt from 'jsonwebtoken'
|
||||
|
||||
// import {
|
||||
// GraphQLLowerCaseDirective,
|
||||
// GraphQLTrimDirective,
|
||||
// GraphQLDefaultToDirective
|
||||
// } from 'graphql-custom-directives';
|
||||
|
||||
dotenv.config()
|
||||
|
||||
@ -25,15 +25,15 @@ const schema = makeExecutableSchema({
|
||||
})
|
||||
|
||||
// augmentSchema will add auto generated mutations based on types in schema
|
||||
const augmentedSchema = augmentSchema(schema)
|
||||
// const augmentedSchema = augmentSchema(schema)
|
||||
|
||||
// add custom directives
|
||||
const directives = [
|
||||
GraphQLLowerCaseDirective,
|
||||
GraphQLTrimDirective,
|
||||
GraphQLDefaultToDirective
|
||||
]
|
||||
augmentedSchema._directives.push.apply(augmentedSchema._directives, directives)
|
||||
// const directives = [
|
||||
// GraphQLLowerCaseDirective,
|
||||
// GraphQLTrimDirective,
|
||||
// GraphQLDefaultToDirective
|
||||
// ]
|
||||
// augmentedSchema._directives.push.apply(augmentedSchema._directives, directives)
|
||||
|
||||
const driver = neo4j.driver(
|
||||
process.env.NEO4J_URI || 'bolt://localhost:7687',
|
||||
@ -46,25 +46,51 @@ const driver = neo4j.driver(
|
||||
const MOCK = (process.env.MOCK === 'true')
|
||||
console.log('MOCK:', MOCK)
|
||||
|
||||
const server = new ApolloServer({
|
||||
context: {
|
||||
driver
|
||||
const server = new GraphQLServer({
|
||||
context: async (req) => {
|
||||
const payload = {
|
||||
driver,
|
||||
user: null,
|
||||
req: req.request
|
||||
}
|
||||
try {
|
||||
const token = payload.req.headers.authorization.replace('Bearer ', '')
|
||||
payload.user = await jwt.verify(token, process.env.JWT_SECRET)
|
||||
} catch (err) {}
|
||||
|
||||
return payload
|
||||
},
|
||||
schema: augmentSchema(schema),
|
||||
tracing: true,
|
||||
schema: applyMiddleware(augmentedSchema, passwordMiddleware, sluggifyMiddleware, excerptMiddleware, fixImageUrlsMiddleware, softDeleteMiddleware),
|
||||
mocks: MOCK ? {
|
||||
User: () => ({
|
||||
name: () => `${faker.name.firstName()} ${faker.name.lastName()}`,
|
||||
email: () => `${faker.internet.email()}`
|
||||
}),
|
||||
Post: () => ({
|
||||
title: () => faker.lorem.lines(1),
|
||||
slug: () => faker.lorem.slug(3),
|
||||
content: () => faker.lorem.paragraphs(5),
|
||||
contentExcerpt: () => faker.lorem.paragraphs(1)
|
||||
})
|
||||
} : false
|
||||
middlewares: middleware,
|
||||
mocks: MOCK ? mocks : false
|
||||
})
|
||||
server.listen().then(({ url }) => {
|
||||
console.log(`Server ready at ${url} 🚀`);
|
||||
|
||||
passport.use('jwt', jwtStrategy())
|
||||
server.express.use(passport.initialize())
|
||||
|
||||
server.express.post('/graphql', passport.authenticate(['jwt'], { session: false }))
|
||||
|
||||
// session middleware
|
||||
// server.express.use(session({
|
||||
// name: 'qid',
|
||||
// secret: process.env.JWT_SECRET,
|
||||
// resave: true,
|
||||
// saveUninitialized: true,
|
||||
// cookie: {
|
||||
// secure: process.env.NODE_ENV === 'production',
|
||||
// maxAge: ms('1d')
|
||||
// }
|
||||
// }))
|
||||
|
||||
const serverConfig = {
|
||||
port: 4000
|
||||
// cors: {
|
||||
// credentials: true,
|
||||
// origin: [process.env.CLIENT_URI] // your frontend url.
|
||||
// }
|
||||
}
|
||||
|
||||
server.start(serverConfig, options => {
|
||||
console.log(`Server ready at ${process.env.GRAPHQL_URI} 🚀`);
|
||||
})
|
||||
|
||||
18
src/jwt/generateToken.js
Normal file
18
src/jwt/generateToken.js
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
import jwt from 'jsonwebtoken'
|
||||
import ms from 'ms'
|
||||
|
||||
// Generate an Access Token for the given User ID
|
||||
export default function generateJwt(user) {
|
||||
console.log('generateJwt', user)
|
||||
const token = jwt.sign(user, process.env.JWT_SECRET, {
|
||||
expiresIn: ms('1d'),
|
||||
issuer: process.env.GRAPHQL_URI,
|
||||
audience: process.env.CLIENT_URI,
|
||||
subject: user.id.toString()
|
||||
})
|
||||
jwt.verify(token, process.env.JWT_SECRET, (err, data) => {
|
||||
console.log('token verification:', err, data)
|
||||
})
|
||||
return token
|
||||
}
|
||||
30
src/jwt/strategy.js
Normal file
30
src/jwt/strategy.js
Normal file
@ -0,0 +1,30 @@
|
||||
import { Strategy } from 'passport-jwt'
|
||||
|
||||
const cookieExtractor = (req) => {
|
||||
var token = null
|
||||
if (req && req.cookies) {
|
||||
token = req.cookies['jwt']
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const options = {
|
||||
jwtFromRequest: cookieExtractor,
|
||||
secretOrKey: process.env.JWT_SECRET,
|
||||
issuer: process.env.GRAPHQL_URI,
|
||||
audience: process.env.CLIENT_URI
|
||||
}
|
||||
|
||||
return new Strategy(options,
|
||||
(JWTPayload, next) => {
|
||||
console.log('JWT Payload Received:', JWTPayload)
|
||||
// usually this would be a database call:
|
||||
// var user = users[_.findIndex(users, {id: JWTPayload.id})]
|
||||
if (true) {
|
||||
next(null, {})
|
||||
} else {
|
||||
next(null, false)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1,21 +1,24 @@
|
||||
|
||||
const replaceURL = (url) => {
|
||||
export const fixUrl = (url) => {
|
||||
return url.replace('https://api-alpha.human-connection.org/uploads', 'http://localhost:3000/uploads')
|
||||
}
|
||||
const fixImageURLs = (result, resolve, root, args, context, info) => {
|
||||
|
||||
if (result && typeof result === 'string' && result.indexOf('https://api-alpha.human-connection.org/uploads') === 0) {
|
||||
result = replaceURL(result)
|
||||
result = fixUrl(result)
|
||||
} else if (result && typeof result === 'object') {
|
||||
Object.keys(result).forEach(key => {
|
||||
result[key] = fixImageURLs(result[key])
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export default {
|
||||
Mutation: async (resolve, root, args, context, info) => {
|
||||
const result = await resolve(root, args, context, info)
|
||||
return fixImageURLs(result, resolve, root, args, context, info)
|
||||
},
|
||||
Query: async (resolve, root, args, context, info) => {
|
||||
let result = await resolve(root, args, context, info)
|
||||
|
||||
|
||||
13
src/middleware/index.js
Normal file
13
src/middleware/index.js
Normal file
@ -0,0 +1,13 @@
|
||||
import passwordMiddleware from './passwordMiddleware'
|
||||
import softDeleteMiddleware from './softDeleteMiddleware'
|
||||
import sluggifyMiddleware from './sluggifyMiddleware'
|
||||
import fixImageUrlsMiddleware from './fixImageUrlsMiddleware'
|
||||
import excerptMiddleware from './excerptMiddleware'
|
||||
|
||||
export default [
|
||||
passwordMiddleware,
|
||||
sluggifyMiddleware,
|
||||
excerptMiddleware,
|
||||
fixImageUrlsMiddleware,
|
||||
softDeleteMiddleware
|
||||
]
|
||||
15
src/mocks/index.js
Normal file
15
src/mocks/index.js
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
import faker from 'faker'
|
||||
|
||||
export default {
|
||||
User: () => ({
|
||||
name: () => `${faker.name.firstName()} ${faker.name.lastName()}`,
|
||||
email: () => `${faker.internet.email()}`
|
||||
}),
|
||||
Post: () => ({
|
||||
title: () => faker.lorem.lines(1),
|
||||
slug: () => faker.lorem.slug(3),
|
||||
content: () => faker.lorem.paragraphs(5),
|
||||
contentExcerpt: () => faker.lorem.paragraphs(1)
|
||||
})
|
||||
}
|
||||
@ -1,3 +1,20 @@
|
||||
type Query {
|
||||
isLoggedIn: Boolean!
|
||||
}
|
||||
type Mutation {
|
||||
login(email: String!, password: String!): LoggedInUser
|
||||
signup(email: String!, password: String!): Boolean!
|
||||
}
|
||||
type LoggedInUser {
|
||||
id: ID!
|
||||
slug: String!
|
||||
name: String!
|
||||
avatar:String!
|
||||
email: String!
|
||||
role: String!,
|
||||
token: String!
|
||||
}
|
||||
|
||||
enum VisibilityEnum {
|
||||
Public
|
||||
Friends
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user