Ocelot-Social/src/graphql-schema.js

220 lines
8.0 KiB
JavaScript

import fs from 'fs'
import path from 'path'
import bcrypt from 'bcryptjs'
import generateJwt from './jwt/generateToken'
import uuid from 'uuid/v4'
import { fixUrl } from './middleware/fixImageUrlsMiddleware'
import { AuthenticationError } from 'apollo-server'
import { neo4jgraphql } from 'neo4j-graphql-js'
import { activityPub } from './activitypub/ActivityPub'
import as from 'activitystrea.ms'
/*
import as from 'activitystrea.ms'
import request from 'request'
*/
const debug = require('debug')('backend:schema')
export const typeDefs =
fs.readFileSync(process.env.GRAPHQL_SCHEMA || path.join(__dirname, 'schema.graphql'))
.toString('utf-8')
export const query = (cypher, session) => {
return new Promise((resolve, reject) => {
let data = []
session
.run(cypher)
.subscribe({
onNext: function (record) {
let item = {}
record.keys.forEach(key => {
item[key] = record.get(key)
})
data.push(item)
},
onCompleted: function () {
session.close()
resolve(data)
},
onError: function (error) {
reject(error)
}
})
})
}
const queryOne = (cypher, session) => {
return new Promise((resolve, reject) => {
query(cypher, session)
.then(res => {
resolve(res.length ? res.pop() : {})
})
.catch(err => {
reject(err)
})
})
}
export const resolvers = {
Query: {
isLoggedIn: (parent, args, { driver, user }) => {
return Boolean(user && user.id)
},
statistics: async (parent, args, { driver, user }) => {
return new Promise(async (resolve) => {
const session = driver.session()
const queries = {
countUsers: 'MATCH (r:User) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countUsers',
countPosts: 'MATCH (r:Post) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countPosts',
countComments: 'MATCH (r:Comment) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countComments',
countNotifications: 'MATCH (r:Notification) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countNotifications',
countOrganizations: 'MATCH (r:Organization) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countOrganizations',
countProjects: 'MATCH (r:Project) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countProjects',
countInvites: 'MATCH (r:Invite) WHERE r.wasUsed <> true OR NOT exists(r.wasUsed) RETURN COUNT(r) AS countInvites',
countFollows: 'MATCH (:User)-[r:FOLLOWS]->(:User) RETURN COUNT(r) AS countFollows',
countShouts: 'MATCH (:User)-[r:SHOUTED]->(:Post) RETURN COUNT(r) AS countShouts'
}
let data = {
countUsers: (await queryOne(queries.countUsers, session)).countUsers.low,
countPosts: (await queryOne(queries.countPosts, session)).countPosts.low,
countComments: (await queryOne(queries.countComments, session)).countComments.low,
countNotifications: (await queryOne(queries.countNotifications, session)).countNotifications.low,
countOrganizations: (await queryOne(queries.countOrganizations, session)).countOrganizations.low,
countProjects: (await queryOne(queries.countProjects, session)).countProjects.low,
countInvites: (await queryOne(queries.countInvites, session)).countInvites.low,
countFollows: (await queryOne(queries.countFollows, session)).countFollows.low,
countShouts: (await queryOne(queries.countShouts, session)).countShouts.low
}
resolve(data)
})
}
// 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),
// }
return true
},
login: async (parent, { email, password }, { driver, req, user }) => {
// if (user && user.id) {
// throw new Error('Already logged in.')
// }
const session = driver.session()
return session.run(
'MATCH (user:User {email: $userEmail}) ' +
'RETURN user {.id, .slug, .name, .avatar, .locationName, .about, .email, .password, .role} as user LIMIT 1', {
userEmail: email
})
.then(async (result) => {
session.close()
const [currentUser] = await result.records.map(function (record) {
return record.get('user')
})
if (currentUser && await bcrypt.compareSync(password, currentUser.password)) {
delete currentUser.password
currentUser.avatar = fixUrl(currentUser.avatar)
return Object.assign(currentUser, {
token: generateJwt(currentUser)
})
} else throw new AuthenticationError('Incorrect email address or password.')
})
},
report: async (parent, { resource, description }, { driver, req, user }, resolveInfo) => {
const contextId = uuid()
const session = driver.session()
const data = {
id: contextId,
type: resource.type,
createdAt: (new Date()).toISOString(),
description: resource.description
}
await session.run(
'CREATE (r:Report $report) ' +
'RETURN r.id, r.type, r.description', {
report: data
}
)
let contentType
switch (resource.type) {
case 'post':
case 'contribution':
contentType = 'Post'
break
case 'comment':
contentType = 'Comment'
break
case 'user':
contentType = 'User'
break
}
await session.run(
`MATCH (author:User {id: $userId}), (context:${contentType} {id: $resourceId}), (report:Report {id: $contextId}) ` +
'MERGE (report)<-[:REPORTED]-(author) ' +
'MERGE (context)<-[:REPORTED]-(report) ' +
'RETURN context', {
resourceId: resource.id,
userId: user.id,
contextId: contextId
}
)
session.close()
// TODO: output Report compatible object
return data
},
CreatePost: async (object, params, ctx, resolveInfo) => {
params.activityId = uuid()
const result = await neo4jgraphql(object, params, ctx, resolveInfo, false)
debug(`user = ${JSON.stringify(ctx.user, null, 2)}`)
const session = ctx.driver.session()
const author = await session.run(
'MATCH (author:User {slug: $slug}), (post:Post {id: $postId}) ' +
'MERGE (post)<-[:WROTE]-(author) ' +
'RETURN author', {
slug: ctx.user.slug,
postId: result.id
})
// debug(`result = ${JSON.stringify(author, null, 2)}`)
debug(`actorId = ${author.records[0]._fields[0].properties.actorId}`)
if (Array.isArray(author.records) && author.records.length > 0) {
const actorId = author.records[0]._fields[0].properties.actorId
const createActivity = await new Promise((resolve, reject) => {
as.create()
.id(`${actorId}/status/${params.activityId}`)
.actor(`${actorId}`)
.object(
as.article()
.id(`${actorId}/status/${result.id}`)
.content(result.content)
.to('https://www.w3.org/ns/activitystreams#Public')
.publishedNow()
.attributedTo(`${actorId}`)
).prettyWrite((err, doc) => {
if (err) {
reject(err)
} else {
debug(doc)
const parsedDoc = JSON.parse(doc)
parsedDoc.send = true
resolve(JSON.stringify(parsedDoc))
}
})
})
try {
await activityPub.sendActivity(createActivity)
} catch (e) {
debug(`error sending post activity = ${JSON.stringify(e, null, 2)}`)
}
}
}
}
}