mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
241 lines
7.9 KiB
JavaScript
241 lines
7.9 KiB
JavaScript
// import { extractDomainFromUrl, signAndSend } from './utils'
|
|
import { extractNameFromId, signAndSend } from './utils'
|
|
import { isPublicAddressed } from './utils/activity'
|
|
// import { isPublicAddressed, sendAcceptActivity, sendRejectActivity } from './utils/activity'
|
|
import request from 'request'
|
|
// import as from 'activitystrea.ms'
|
|
import NitroDataSource from './NitroDataSource'
|
|
import router from './routes'
|
|
import Collections from './Collections'
|
|
import { v4 as uuid } from 'uuid'
|
|
import CONFIG from '../config'
|
|
const debug = require('debug')('ea')
|
|
|
|
let activityPub = null
|
|
|
|
export { activityPub }
|
|
|
|
export default class ActivityPub {
|
|
constructor(activityPubEndpointUri, internalGraphQlUri) {
|
|
this.endpoint = activityPubEndpointUri
|
|
this.dataSource = new NitroDataSource(internalGraphQlUri)
|
|
this.collections = new Collections(this.dataSource)
|
|
}
|
|
|
|
static init(server) {
|
|
if (!activityPub) {
|
|
activityPub = new ActivityPub(CONFIG.CLIENT_URI, CONFIG.GRAPHQL_URI)
|
|
|
|
// integrate into running graphql express server
|
|
server.express.set('ap', activityPub)
|
|
server.express.use(router)
|
|
console.log('-> ActivityPub middleware added to the graphql express server') // eslint-disable-line no-console
|
|
} else {
|
|
console.log('-> ActivityPub middleware already added to the graphql express server') // eslint-disable-line no-console
|
|
}
|
|
}
|
|
|
|
// handleFollowActivity(activity) {
|
|
// debug(`inside FOLLOW ${activity.actor}`)
|
|
// const toActorName = extractNameFromId(activity.object)
|
|
// const fromDomain = extractDomainFromUrl(activity.actor)
|
|
// const dataSource = this.dataSource
|
|
|
|
// return new Promise((resolve, reject) => {
|
|
// request(
|
|
// {
|
|
// url: activity.actor,
|
|
// headers: {
|
|
// Accept: 'application/activity+json',
|
|
// },
|
|
// },
|
|
// async (err, response, toActorObject) => {
|
|
// if (err) return reject(err)
|
|
// // save shared inbox
|
|
// toActorObject = JSON.parse(toActorObject)
|
|
// await this.dataSource.addSharedInboxEndpoint(toActorObject.endpoints.sharedInbox)
|
|
|
|
// const followersCollectionPage = await this.dataSource.getFollowersCollectionPage(
|
|
// activity.object,
|
|
// )
|
|
|
|
// const followActivity = as
|
|
// .follow()
|
|
// .id(activity.id)
|
|
// .actor(activity.actor)
|
|
// .object(activity.object)
|
|
|
|
// // add follower if not already in collection
|
|
// if (followersCollectionPage.orderedItems.includes(activity.actor)) {
|
|
// debug('follower already in collection!')
|
|
// debug(`inbox = ${toActorObject.inbox}`)
|
|
// resolve(
|
|
// sendRejectActivity(followActivity, toActorName, fromDomain, toActorObject.inbox),
|
|
// )
|
|
// } else {
|
|
// followersCollectionPage.orderedItems.push(activity.actor)
|
|
// }
|
|
// debug(`toActorObject = ${toActorObject}`)
|
|
// toActorObject =
|
|
// typeof toActorObject !== 'object' ? JSON.parse(toActorObject) : toActorObject
|
|
// debug(`followers = ${JSON.stringify(followersCollectionPage.orderedItems, null, 2)}`)
|
|
// debug(`inbox = ${toActorObject.inbox}`)
|
|
// debug(`outbox = ${toActorObject.outbox}`)
|
|
// debug(`followers = ${toActorObject.followers}`)
|
|
// debug(`following = ${toActorObject.following}`)
|
|
|
|
// try {
|
|
// await dataSource.saveFollowersCollectionPage(followersCollectionPage)
|
|
// debug('follow activity saved')
|
|
// resolve(
|
|
// sendAcceptActivity(followActivity, toActorName, fromDomain, toActorObject.inbox),
|
|
// )
|
|
// } catch (e) {
|
|
// debug('followers update error!', e)
|
|
// resolve(
|
|
// sendRejectActivity(followActivity, toActorName, fromDomain, toActorObject.inbox),
|
|
// )
|
|
// }
|
|
// },
|
|
// )
|
|
// })
|
|
// }
|
|
|
|
handleUndoActivity(activity) {
|
|
debug('inside UNDO')
|
|
switch (activity.object.type) {
|
|
case 'Follow': {
|
|
const followActivity = activity.object
|
|
return this.dataSource.undoFollowActivity(followActivity.actor, followActivity.object)
|
|
}
|
|
case 'Like': {
|
|
return this.dataSource.deleteShouted(activity)
|
|
}
|
|
}
|
|
}
|
|
|
|
handleCreateActivity(activity) {
|
|
debug('inside create')
|
|
switch (activity.object.type) {
|
|
case 'Note': {
|
|
const articleObject = activity.object
|
|
if (articleObject.inReplyTo) {
|
|
return this.dataSource.createComment(activity)
|
|
} else {
|
|
return this.dataSource.createPost(activity)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
handleDeleteActivity(activity) {
|
|
debug('inside delete')
|
|
switch (activity.object.type) {
|
|
case 'Article':
|
|
case 'Note':
|
|
return this.dataSource.deletePost(activity)
|
|
default:
|
|
}
|
|
}
|
|
|
|
handleUpdateActivity(activity) {
|
|
debug('inside update')
|
|
switch (activity.object.type) {
|
|
case 'Note':
|
|
case 'Article':
|
|
return this.dataSource.updatePost(activity)
|
|
default:
|
|
}
|
|
}
|
|
|
|
handleLikeActivity(activity) {
|
|
// TODO differ if activity is an Article/Note/etc.
|
|
return this.dataSource.createShouted(activity)
|
|
}
|
|
|
|
handleDislikeActivity(activity) {
|
|
// TODO differ if activity is an Article/Note/etc.
|
|
return this.dataSource.deleteShouted(activity)
|
|
}
|
|
|
|
async handleAcceptActivity(activity) {
|
|
debug('inside accept')
|
|
switch (activity.object.type) {
|
|
case 'Follow': {
|
|
const followObject = activity.object
|
|
const followingCollectionPage = await this.collections.getFollowingCollectionPage(
|
|
followObject.actor,
|
|
)
|
|
followingCollectionPage.orderedItems.push(followObject.object)
|
|
await this.dataSource.saveFollowingCollectionPage(followingCollectionPage)
|
|
}
|
|
}
|
|
}
|
|
|
|
getActorObject(url) {
|
|
return new Promise((resolve, reject) => {
|
|
request(
|
|
{
|
|
url: url,
|
|
headers: {
|
|
Accept: 'application/json',
|
|
},
|
|
},
|
|
(err, response, body) => {
|
|
if (err) {
|
|
reject(err)
|
|
}
|
|
resolve(JSON.parse(body))
|
|
},
|
|
)
|
|
})
|
|
}
|
|
|
|
generateStatusId(slug) {
|
|
return `https://${this.host}/activitypub/users/${slug}/status/${uuid()}`
|
|
}
|
|
|
|
async sendActivity(activity) {
|
|
delete activity.send
|
|
const fromName = extractNameFromId(activity.actor)
|
|
if (Array.isArray(activity.to) && isPublicAddressed(activity)) {
|
|
debug('is public addressed')
|
|
const sharedInboxEndpoints = await this.dataSource.getSharedInboxEndpoints()
|
|
// serve shared inbox endpoints
|
|
sharedInboxEndpoints.map((sharedInbox) => {
|
|
return this.trySend(activity, fromName, new URL(sharedInbox).host, sharedInbox)
|
|
})
|
|
activity.to = activity.to.filter((recipient) => {
|
|
return !isPublicAddressed({ to: recipient })
|
|
})
|
|
// serve the rest
|
|
activity.to.map(async (recipient) => {
|
|
debug('serve rest')
|
|
const actorObject = await this.getActorObject(recipient)
|
|
return this.trySend(activity, fromName, new URL(recipient).host, actorObject.inbox)
|
|
})
|
|
} else if (typeof activity.to === 'string') {
|
|
debug('is string')
|
|
const actorObject = await this.getActorObject(activity.to)
|
|
return this.trySend(activity, fromName, new URL(activity.to).host, actorObject.inbox)
|
|
} else if (Array.isArray(activity.to)) {
|
|
activity.to.map(async (recipient) => {
|
|
const actorObject = await this.getActorObject(recipient)
|
|
return this.trySend(activity, fromName, new URL(recipient).host, actorObject.inbox)
|
|
})
|
|
}
|
|
}
|
|
|
|
async trySend(activity, fromName, host, url, tries = 5) {
|
|
try {
|
|
return await signAndSend(activity, fromName, host, url)
|
|
} catch (e) {
|
|
if (tries > 0) {
|
|
setTimeout(function () {
|
|
return this.trySend(activity, fromName, host, url, --tries)
|
|
}, 20000)
|
|
}
|
|
}
|
|
}
|
|
}
|