unoffical wafrn mirror wafrn.net
atproto social-network activitypub
at angular21 134 lines 4.1 kB view raw
1import { SignedRequest } from '../../interfaces/fediverse/signedRequest.js' 2import { Response } from 'express' 3import { getPostAndUserFromPostId } from '../cacheGetters/getPostAndUserFromPostId.js' 4import { getFollowerRemoteIds } from '../cacheGetters/getFollowerRemoteIds.js' 5import { logger } from '../logger.js' 6import { postToJSONLD } from './postToJSONLD.js' 7import { getRemoteActor } from './getRemoteActor.js' 8import { Queue } from 'bullmq' 9import { completeEnvironment } from '../backendOptions.js' 10import { FederatedHost, Follows, User } from '../../models/index.js' 11import { Op } from 'sequelize' 12import { Privacy } from '../../models/post.js' 13 14const processPostViewQueue = new Queue('processRemoteView', { 15 connection: completeEnvironment.bullmqConnection, 16 defaultJobOptions: { 17 removeOnComplete: true, 18 attempts: 3, 19 backoff: { 20 type: 'exponential', 21 delay: 25000 22 }, 23 removeOnFail: true 24 } 25}) 26 27async function handlePostRequest(req: SignedRequest, res: Response) { 28 if (req.params?.id) { 29 const cachePost = await getPostAndUserFromPostId(req.params.id) 30 const post = cachePost.data 31 if (post) { 32 // we get remote user async-ly 33 const fediData = req.fediData as { 34 fediHost: string 35 remoteUserUrl: string 36 valid: boolean 37 } 38 const user = post.user 39 if (user.isRemoteUser) { 40 // EXTERNAL USER 41 if (post.remotePostId) { 42 res.redirect(post.remotePostId) 43 } else { 44 // bsky post 45 res.sendStatus(404) 46 } 47 return 48 } 49 if (user.banned) { 50 res.sendStatus(410) 51 return 52 } 53 const remoteActor = await getRemoteActor(fediData.remoteUserUrl, cachePost.data.user, false) 54 if (!remoteActor) { 55 logger.debug({ 56 message: `remote actor not found`, 57 fedidata: fediData 58 }) 59 return res.sendStatus(500) 60 } else { 61 const federatedHost = await remoteActor.getFederatedHost() 62 await processPostViewQueue.add('processPost', { 63 postId: post.id, 64 federatedHostId: federatedHost && federatedHost.publicInbox ? federatedHost.id : '', 65 userId: federatedHost?.publicInbox ? '' : remoteActor.id 66 }) 67 } 68 if (post.privacy === Privacy.DirectMessage) { 69 res.sendStatus(403) 70 return 71 } 72 if (post.privacy === Privacy.FollowersOnly) { 73 const followerIds = await getFollowerRemoteIds(user.id) 74 try { 75 if (remoteActor) { 76 const followerServers = ( 77 await User.findAll({ 78 include: [FederatedHost], 79 where: { 80 id: { 81 [Op.in]: ( 82 await Follows.findAll({ 83 where: { 84 followedId: user.id 85 } 86 }) 87 ).map((elem: any) => elem.followerId) 88 } 89 } 90 }) 91 ).map((elem: any) => elem.federatedHostId) 92 if ( 93 !(followerIds.includes(remoteActor.remoteId) || followerServers.includes(remoteActor.federatedHostId)) 94 ) { 95 res.sendStatus(403) 96 return 97 } 98 } else { 99 res.sendStatus(403) 100 return 101 } 102 } catch (error) { 103 logger.warn({ 104 message: 'Error on post', 105 postId: post.id, 106 fediData: req.fediData, 107 user: user.id, 108 error: error 109 }) 110 res.sendStatus(500) 111 return 112 } 113 } 114 const response = await postToJSONLD(post.id) 115 if (!response) { 116 return res.sendStatus(404) 117 } 118 res.set({ 119 'content-type': 'application/activity+json' 120 }) 121 res.send({ 122 ...response.object, 123 '@context': response['@context'] 124 }) 125 } else { 126 res.sendStatus(404) 127 } 128 } else { 129 res.sendStatus(404) 130 } 131 res.end() 132} 133 134export { handlePostRequest }