mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
267 lines
9.4 KiB
JavaScript
267 lines
9.4 KiB
JavaScript
import { neo4jgraphql } from 'neo4j-graphql-js'
|
|
import fileUpload from './fileUpload'
|
|
import { getNeode } from '../../bootstrap/neo4j'
|
|
import { UserInputError, ForbiddenError } from 'apollo-server'
|
|
import Resolver from './helpers/Resolver'
|
|
import log from './helpers/databaseLogger'
|
|
import createOrUpdateLocations from './users/location'
|
|
|
|
const neode = getNeode()
|
|
|
|
export const getMutedUsers = async context => {
|
|
const { neode } = context
|
|
const userModel = neode.model('User')
|
|
let mutedUsers = neode
|
|
.query()
|
|
.match('user', userModel)
|
|
.where('user.id', context.user.id)
|
|
.relationship(userModel.relationships().get('muted'))
|
|
.to('muted', userModel)
|
|
.return('muted')
|
|
mutedUsers = await mutedUsers.execute()
|
|
mutedUsers = mutedUsers.records.map(r => r.get('muted').properties)
|
|
return mutedUsers
|
|
}
|
|
|
|
export default {
|
|
Query: {
|
|
mutedUsers: async (object, args, context, resolveInfo) => {
|
|
try {
|
|
return getMutedUsers(context)
|
|
} catch (e) {
|
|
throw new UserInputError(e.message)
|
|
}
|
|
},
|
|
User: async (object, args, context, resolveInfo) => {
|
|
const { email } = args
|
|
if (email) {
|
|
let session
|
|
try {
|
|
session = context.driver.session()
|
|
const readTxResult = await session.readTransaction(txc => {
|
|
const result = txc.run(
|
|
`
|
|
MATCH (user:User)-[:PRIMARY_EMAIL]->(e:EmailAddress {email: $args.email})
|
|
RETURN user`,
|
|
{ args },
|
|
)
|
|
return result
|
|
})
|
|
return readTxResult.records.map(r => r.get('user').properties)
|
|
} finally {
|
|
session.close()
|
|
}
|
|
}
|
|
return neo4jgraphql(object, args, context, resolveInfo)
|
|
},
|
|
},
|
|
Mutation: {
|
|
muteUser: async (_parent, params, context, _resolveInfo) => {
|
|
const { user: currentUser } = context
|
|
if (currentUser.id === params.id) return null
|
|
await neode.cypher(
|
|
`
|
|
MATCH(u:User {id: $currentUser.id})-[previousRelationship:FOLLOWS]->(b:User {id: $params.id})
|
|
DELETE previousRelationship
|
|
`,
|
|
{ currentUser, params },
|
|
)
|
|
const [user, mutedUser] = await Promise.all([
|
|
neode.find('User', currentUser.id),
|
|
neode.find('User', params.id),
|
|
])
|
|
await user.relateTo(mutedUser, 'muted')
|
|
return mutedUser.toJson()
|
|
},
|
|
unmuteUser: async (_parent, params, context, _resolveInfo) => {
|
|
const { user: currentUser } = context
|
|
if (currentUser.id === params.id) return null
|
|
await neode.cypher(
|
|
`
|
|
MATCH(u:User {id: $currentUser.id})-[previousRelationship:MUTED]->(b:User {id: $params.id})
|
|
DELETE previousRelationship
|
|
`,
|
|
{ currentUser, params },
|
|
)
|
|
const unmutedUser = await neode.find('User', params.id)
|
|
return unmutedUser.toJson()
|
|
},
|
|
block: async (object, args, context, resolveInfo) => {
|
|
const { user: currentUser } = context
|
|
if (currentUser.id === args.id) return null
|
|
await neode.cypher(
|
|
`
|
|
MATCH(u:User {id: $currentUser.id})-[r:FOLLOWS]->(b:User {id: $args.id})
|
|
DELETE r
|
|
`,
|
|
{ currentUser, args },
|
|
)
|
|
const [user, blockedUser] = await Promise.all([
|
|
neode.find('User', currentUser.id),
|
|
neode.find('User', args.id),
|
|
])
|
|
await user.relateTo(blockedUser, 'blocked')
|
|
return blockedUser.toJson()
|
|
},
|
|
unblock: async (object, args, context, resolveInfo) => {
|
|
const { user: currentUser } = context
|
|
if (currentUser.id === args.id) return null
|
|
await neode.cypher(
|
|
`
|
|
MATCH(u:User {id: $currentUser.id})-[r:BLOCKED]->(b:User {id: $args.id})
|
|
DELETE r
|
|
`,
|
|
{ currentUser, args },
|
|
)
|
|
const blockedUser = await neode.find('User', args.id)
|
|
return blockedUser.toJson()
|
|
},
|
|
UpdateUser: async (_parent, params, context, _resolveInfo) => {
|
|
const { termsAndConditionsAgreedVersion } = params
|
|
if (termsAndConditionsAgreedVersion) {
|
|
const regEx = new RegExp(/^[0-9]+\.[0-9]+\.[0-9]+$/g)
|
|
if (!regEx.test(termsAndConditionsAgreedVersion)) {
|
|
throw new ForbiddenError('Invalid version format!')
|
|
}
|
|
params.termsAndConditionsAgreedAt = new Date().toISOString()
|
|
}
|
|
params = await fileUpload(params, { file: 'avatarUpload', url: 'avatar' })
|
|
const session = context.driver.session()
|
|
|
|
const writeTxResultPromise = session.writeTransaction(async transaction => {
|
|
const updateUserTransactionResponse = await transaction.run(
|
|
`
|
|
MATCH (user:User {id: $params.id})
|
|
SET user += $params
|
|
SET user.updatedAt = toString(datetime())
|
|
RETURN user
|
|
`,
|
|
{ params },
|
|
)
|
|
return updateUserTransactionResponse.records.map(record => record.get('user').properties)
|
|
})
|
|
try {
|
|
const [user] = await writeTxResultPromise
|
|
await createOrUpdateLocations(params.id, params.locationName, session)
|
|
return user
|
|
} catch (error) {
|
|
throw new UserInputError(error.message)
|
|
} finally {
|
|
session.close()
|
|
}
|
|
},
|
|
DeleteUser: async (object, params, context, resolveInfo) => {
|
|
const { resource } = params
|
|
const session = context.driver.session()
|
|
try {
|
|
if (resource && resource.length) {
|
|
await session.writeTransaction(transaction => {
|
|
resource.map(node => {
|
|
return transaction.run(
|
|
`
|
|
MATCH (resource:${node})<-[:WROTE]-(author:User {id: $userId})
|
|
OPTIONAL MATCH (resource)<-[:COMMENTS]-(comment:Comment)
|
|
SET resource.deleted = true
|
|
SET resource.content = 'UNAVAILABLE'
|
|
SET resource.contentExcerpt = 'UNAVAILABLE'
|
|
SET comment.deleted = true
|
|
RETURN author
|
|
`,
|
|
{
|
|
userId: context.user.id,
|
|
},
|
|
)
|
|
})
|
|
})
|
|
}
|
|
|
|
const deleteUserTxResultPromise = session.writeTransaction(async transaction => {
|
|
const deleteUserTransactionResponse = await transaction.run(
|
|
`
|
|
MATCH (user:User {id: $userId})
|
|
SET user.deleted = true
|
|
SET user.name = 'UNAVAILABLE'
|
|
SET user.about = 'UNAVAILABLE'
|
|
WITH user
|
|
OPTIONAL MATCH (user)<-[:BELONGS_TO]-(email:EmailAddress)
|
|
DETACH DELETE email
|
|
WITH user
|
|
OPTIONAL MATCH (user)<-[:OWNED_BY]-(socialMedia:SocialMedia)
|
|
DETACH DELETE socialMedia
|
|
RETURN user
|
|
`,
|
|
{ userId: context.user.id },
|
|
)
|
|
log(deleteUserTransactionResponse)
|
|
return deleteUserTransactionResponse.records.map(record => record.get('user').properties)
|
|
})
|
|
const [user] = await deleteUserTxResultPromise
|
|
return user
|
|
} finally {
|
|
session.close()
|
|
}
|
|
},
|
|
},
|
|
User: {
|
|
email: async (parent, params, context, resolveInfo) => {
|
|
if (typeof parent.email !== 'undefined') return parent.email
|
|
const { id } = parent
|
|
const statement = `MATCH(u:User {id: {id}})-[:PRIMARY_EMAIL]->(e:EmailAddress) RETURN e`
|
|
const result = await neode.cypher(statement, { id })
|
|
const [{ email }] = result.records.map(r => r.get('e').properties)
|
|
return email
|
|
},
|
|
...Resolver('User', {
|
|
undefinedToNull: [
|
|
'actorId',
|
|
'avatar',
|
|
'coverImg',
|
|
'deleted',
|
|
'disabled',
|
|
'locationName',
|
|
'about',
|
|
'termsAndConditionsAgreedVersion',
|
|
'termsAndConditionsAgreedAt',
|
|
'allowEmbedIframes',
|
|
'showShoutsPublicly',
|
|
'locale',
|
|
],
|
|
boolean: {
|
|
followedByCurrentUser:
|
|
'MATCH (this)<-[:FOLLOWS]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
|
|
isBlocked:
|
|
'MATCH (this)<-[:BLOCKED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
|
|
isMuted:
|
|
'MATCH (this)<-[:MUTED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
|
|
},
|
|
count: {
|
|
contributionsCount:
|
|
'-[:WROTE]->(related:Post) WHERE NOT related.disabled = true AND NOT related.deleted = true',
|
|
friendsCount: '<-[:FRIENDS]->(related:User)',
|
|
followingCount: '-[:FOLLOWS]->(related:User)',
|
|
followedByCount: '<-[:FOLLOWS]-(related:User)',
|
|
commentedCount:
|
|
'-[:WROTE]->(c:Comment)-[:COMMENTS]->(related:Post) WHERE NOT related.disabled = true AND NOT related.deleted = true',
|
|
shoutedCount:
|
|
'-[:SHOUTED]->(related:Post) WHERE NOT related.disabled = true AND NOT related.deleted = true',
|
|
badgesCount: '<-[:REWARDED]-(related:Badge)',
|
|
},
|
|
hasOne: {
|
|
invitedBy: '<-[:INVITED]-(related:User)',
|
|
location: '-[:IS_IN]->(related:Location)',
|
|
},
|
|
hasMany: {
|
|
followedBy: '<-[:FOLLOWS]-(related:User)',
|
|
following: '-[:FOLLOWS]->(related:User)',
|
|
friends: '-[:FRIENDS]-(related:User)',
|
|
socialMedia: '<-[:OWNED_BY]-(related:SocialMedia)',
|
|
contributions: '-[:WROTE]->(related:Post)',
|
|
comments: '-[:WROTE]->(related:Comment)',
|
|
shouted: '-[:SHOUTED]->(related:Post)',
|
|
categories: '-[:CATEGORIZED]->(related:Category)',
|
|
badges: '<-[:REWARDED]-(related:Badge)',
|
|
},
|
|
}),
|
|
},
|
|
}
|