Prepare to implement scanner
This commit is contained in:
parent
02e657328f
commit
286cfa992b
|
@ -14,6 +14,7 @@
|
|||
"author": "William Lyon",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/node": "^7.5.0",
|
||||
"@babel/plugin-transform-spread": "^7.2.2",
|
||||
"apollo-boost": "^0.4.3",
|
||||
"apollo-cache-inmemory": "^1.5.1",
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import { PubSub } from 'apollo-server'
|
||||
|
||||
export const EVENT_SCANNER_PROGRESS = 'SCANNER_PROGRESS'
|
||||
|
||||
class PhotoScanner {
|
||||
constructor(driver) {
|
||||
this.driver = driver
|
||||
this.isRunning = false
|
||||
this.pubsub = new PubSub()
|
||||
}
|
||||
|
||||
async scanAll() {
|
||||
this.isRunning = true
|
||||
|
||||
this.pubsub.publish(EVENT_SCANNER_PROGRESS, {
|
||||
scannerStatusUpdate: {
|
||||
progress: 0,
|
||||
finished: false,
|
||||
error: false,
|
||||
errorMessage: '',
|
||||
},
|
||||
})
|
||||
|
||||
let session = this.driver.session()
|
||||
|
||||
session
|
||||
.run(
|
||||
'MATCH (u:User) return u.id AS id, u.username AS username, u.rootPath as path'
|
||||
)
|
||||
.subscribe({
|
||||
onNext: function(record) {
|
||||
const username = record.get('username')
|
||||
const id = record.get('id')
|
||||
const path = record.get('path')
|
||||
|
||||
if (!path) {
|
||||
console.log(`User ${username}, has no root path, skipping`)
|
||||
return
|
||||
}
|
||||
|
||||
this.scanUser(id)
|
||||
|
||||
console.log(`Scanning ${username}...`)
|
||||
},
|
||||
onCompleted: () => {
|
||||
session.close()
|
||||
this.isRunning = false
|
||||
console.log('Done scanning')
|
||||
|
||||
this.pubsub.publish(EVENT_SCANNER_PROGRESS, {
|
||||
scannerStatusUpdate: {
|
||||
progress: 100,
|
||||
finished: true,
|
||||
error: false,
|
||||
errorMessage: '',
|
||||
},
|
||||
})
|
||||
},
|
||||
onError: error => {
|
||||
console.error(error)
|
||||
|
||||
this.pubsub.publish(EVENT_SCANNER_PROGRESS, {
|
||||
scannerStatusUpdate: {
|
||||
progress: 0,
|
||||
finished: false,
|
||||
error: true,
|
||||
errorMessage: error.message,
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async scanUser(id) {}
|
||||
}
|
||||
|
||||
export default PhotoScanner
|
|
@ -1,13 +0,0 @@
|
|||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
/*
|
||||
* Check for GRAPHQL_SCHEMA environment variable to specify schema file
|
||||
* fallback to schema.graphql if GRAPHQL_SCHEMA environment variable is not set
|
||||
*/
|
||||
|
||||
export const typeDefs = fs
|
||||
.readFileSync(
|
||||
process.env.GRAPHQL_SCHEMA || path.join(__dirname, "schema.graphql")
|
||||
)
|
||||
.toString("utf-8");
|
|
@ -1,10 +1,15 @@
|
|||
import { typeDefs } from './graphql-schema'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { ApolloServer } from 'apollo-server-express'
|
||||
import express from 'express'
|
||||
import bodyParser from 'body-parser'
|
||||
import { v1 as neo4j } from 'neo4j-driver'
|
||||
import { makeAugmentedSchema } from 'neo4j-graphql-js'
|
||||
import dotenv from 'dotenv'
|
||||
import http from 'http'
|
||||
import PhotoScanner from './Scanner'
|
||||
|
||||
import { getUserFromToken, getTokenFromBearer } from './token'
|
||||
|
||||
// set environment variables from ../.env
|
||||
dotenv.config()
|
||||
|
@ -20,7 +25,14 @@ app.use(bodyParser.json())
|
|||
* https://grandstack.io/docs/neo4j-graphql-js-api.html#makeaugmentedschemaoptions-graphqlschema
|
||||
*/
|
||||
|
||||
import users from './resolvers/users'
|
||||
const typeDefs = fs
|
||||
.readFileSync(
|
||||
process.env.GRAPHQL_SCHEMA || path.join(__dirname, 'schema.graphql')
|
||||
)
|
||||
.toString('utf-8')
|
||||
|
||||
import usersResolver from './resolvers/users'
|
||||
import scannerResolver from './resolvers/scanner'
|
||||
|
||||
const schema = makeAugmentedSchema({
|
||||
typeDefs,
|
||||
|
@ -30,10 +42,17 @@ const schema = makeAugmentedSchema({
|
|||
hasRole: true,
|
||||
},
|
||||
mutation: false,
|
||||
query: {
|
||||
exclude: ['ScannerResult', 'AuthorizeResult', 'Subscription'],
|
||||
},
|
||||
},
|
||||
resolvers: {
|
||||
Mutation: {
|
||||
...users.mutation,
|
||||
...usersResolver.mutation,
|
||||
...scannerResolver.mutation,
|
||||
},
|
||||
Subscription: {
|
||||
...scannerResolver.subscription,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -51,6 +70,8 @@ const driver = neo4j.driver(
|
|||
)
|
||||
)
|
||||
|
||||
const scanner = new PhotoScanner(driver)
|
||||
|
||||
/*
|
||||
* Create a new ApolloServer instance, serving the GraphQL schema
|
||||
* created using makeAugmentedSchema above and injecting the Neo4j driver
|
||||
|
@ -58,22 +79,50 @@ const driver = neo4j.driver(
|
|||
* generated resolvers to connect to the database.
|
||||
*/
|
||||
const server = new ApolloServer({
|
||||
context: ({ req }) => Object.assign(req, { driver }),
|
||||
context: async function({ req }) {
|
||||
let user = null
|
||||
|
||||
if (req && req.headers.authorization) {
|
||||
const token = getTokenFromBearer(req.headers.authorization)
|
||||
user = await getUserFromToken(token, driver)
|
||||
}
|
||||
|
||||
return { ...req, driver, scanner, user }
|
||||
},
|
||||
schema: schema,
|
||||
introspection: true,
|
||||
playground: true,
|
||||
subscriptions: {
|
||||
onConnect: async (connectionParams, webSocket) => {
|
||||
const token = getTokenFromBearer(connectionParams.Authorization)
|
||||
const user = await getUserFromToken(token, driver)
|
||||
|
||||
return {
|
||||
token,
|
||||
user,
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Specify port and path for GraphQL endpoint
|
||||
const port = process.env.GRAPHQL_LISTEN_PORT || 4001
|
||||
const path = '/graphql'
|
||||
const graphPath = '/graphql'
|
||||
|
||||
/*
|
||||
* Optionally, apply Express middleware for authentication, etc
|
||||
* This also also allows us to specify a path for the GraphQL endpoint
|
||||
*/
|
||||
server.applyMiddleware({ app, path })
|
||||
server.applyMiddleware({ app, graphPath })
|
||||
|
||||
app.listen({ port, path }, () => {
|
||||
console.log(`GraphQL server ready at http://localhost:${port}${path}`)
|
||||
const httpServer = http.createServer(app)
|
||||
server.installSubscriptionHandlers(httpServer)
|
||||
|
||||
httpServer.listen({ port, graphPath }, () => {
|
||||
console.log(
|
||||
`🚀 GraphQL endpoint ready at http://localhost:${port}${server.graphqlPath}`
|
||||
)
|
||||
console.log(
|
||||
`🚀 Subscriptions ready at ws://localhost:${port}${server.subscriptionsPath}`
|
||||
)
|
||||
})
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import { EVENT_SCANNER_PROGRESS } from '../scanner'
|
||||
|
||||
const mutation = {
|
||||
async scanAll(root, args, ctx, info) {
|
||||
if (ctx.scanner.isRunning) {
|
||||
return {
|
||||
finished: false,
|
||||
error: true,
|
||||
errorMessage: 'Scanner already running',
|
||||
}
|
||||
}
|
||||
|
||||
ctx.scanner.scanAll()
|
||||
|
||||
return {
|
||||
finished: false,
|
||||
error: false,
|
||||
progress: 0,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const subscription = {
|
||||
scannerStatusUpdate: {
|
||||
subscribe(root, args, ctx, info) {
|
||||
return ctx.scanner.pubsub.asyncIterator([EVENT_SCANNER_PROGRESS])
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
mutation,
|
||||
subscription,
|
||||
}
|
|
@ -36,12 +36,12 @@ const mutation = {
|
|||
let { username, password } = args
|
||||
|
||||
let session = ctx.driver.session()
|
||||
let result = await session.run(
|
||||
let findResult = await session.run(
|
||||
'MATCH (usr:User {username: {username} }) RETURN usr',
|
||||
{ username }
|
||||
)
|
||||
|
||||
if (result.records.length > 0) {
|
||||
if (findResult.records.length > 0) {
|
||||
return {
|
||||
success: false,
|
||||
status: 'Username is already taken',
|
||||
|
@ -49,17 +49,21 @@ const mutation = {
|
|||
}
|
||||
}
|
||||
|
||||
await session.run(
|
||||
'CREATE (n:User { username: {username}, password: {password}, id: {id} }) return n',
|
||||
const registerResult = await session.run(
|
||||
'CREATE (n:User { username: {username}, password: {password}, id: {id} }) return n.id',
|
||||
{ username, password, id: uuid() }
|
||||
)
|
||||
|
||||
let id = registerResult.records[0].get('n.id')
|
||||
|
||||
const token = jwt.sign({ id }, process.env.JWT_SECRET)
|
||||
|
||||
session.close()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
status: 'User created',
|
||||
token: 'yay',
|
||||
token: token,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -36,9 +36,22 @@ type AuthorizeResult {
|
|||
token: String
|
||||
}
|
||||
|
||||
type ScannerResult {
|
||||
finished: Boolean!
|
||||
error: Boolean!
|
||||
errorMessage: String
|
||||
progress: Float
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
scannerStatusUpdate: ScannerResult
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
authorizeUser(username: String!, password: String!): AuthorizeResult!
|
||||
registerUser(username: String!, password: String!): AuthorizeResult!
|
||||
|
||||
scanAll: ScannerResult! @isAuthenticated
|
||||
}
|
||||
|
||||
type Query {
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import jwt from 'jsonwebtoken'
|
||||
|
||||
export const getUserFromToken = async function(token, driver) {
|
||||
const tokenContent = jwt.verify(token, process.env.JWT_SECRET)
|
||||
const userId = tokenContent.id
|
||||
|
||||
const session = driver.session()
|
||||
|
||||
const userResult = await session.run(
|
||||
'MATCH (u:User {id: {userId}}) RETURN u',
|
||||
{
|
||||
userId,
|
||||
}
|
||||
)
|
||||
|
||||
if (userResult.records.length == 0) {
|
||||
throw new Error(`User doesn't exist anymore`)
|
||||
}
|
||||
|
||||
const user = userResult.records[0].toObject()
|
||||
|
||||
session.close()
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
export const getTokenFromBearer = bearer => {
|
||||
let token = bearer
|
||||
|
||||
if (!token) {
|
||||
throw new Error('Missing auth token')
|
||||
}
|
||||
|
||||
if (!token.toLowerCase().startsWith('bearer ')) {
|
||||
throw new Error('Invalid auth token')
|
||||
}
|
||||
|
||||
token = token.substr(7)
|
||||
|
||||
return token
|
||||
}
|
Loading…
Reference in New Issue