unoffical wafrn mirror wafrn.net
atproto social-network activitypub
at testPDSNotExplode 314 lines 11 kB view raw
1import { Job } from 'bullmq' 2import { 3 Blocks, 4 EmojiReaction, 5 FederatedHost, 6 Follows, 7 Mutes, 8 Post, 9 PostMentionsUserRelation, 10 User, 11 UserLikesPostRelations, 12 UserOptions, 13 sequelize 14} from '../../models/index.js' 15import { completeEnvironment } from '../backendOptions.js' 16import { getUserIdFromRemoteId } from '../cacheGetters/getUserIdFromRemoteId.js' 17import { getPetitionSigned } from '../activitypub/getPetitionSigned.js' 18import { processUserEmojis } from '../activitypub/processUserEmojis.js' 19import { fediverseTag } from '../../interfaces/fediverse/tags.js' 20import { logger } from '../logger.js' 21import { redisCache } from '../redis.js' 22import { Op } from 'sequelize' 23import { getDeletedUser } from '../cacheGetters/getDeletedUser.js' 24 25// This function will return userid after processing it. 26async function getRemoteActorIdProcessor(job: Job) { 27 const actorUrl: string = job.data.actorUrl 28 const forceUpdate: boolean = job.data.forceUpdate 29 let res: string | User | undefined | null = await getUserIdFromRemoteId(actorUrl) 30 let url = undefined 31 try { 32 url = new URL(actorUrl) 33 } catch (error) { 34 res = await getDeletedUser() 35 url = undefined 36 logger.debug({ 37 message: `Invalid url ${actorUrl}`, 38 url: actorUrl, 39 stack: new Error().stack 40 }) 41 } 42 if (res === '' || (forceUpdate && url != undefined)) { 43 let federatedHost = await FederatedHost.findOne({ 44 where: sequelize.where( 45 sequelize.fn('lower', sequelize.col('displayName')), 46 url?.host ? url.host.toLowerCase() : '' 47 ) 48 }) 49 const hostBanned = federatedHost?.blocked 50 if (hostBanned) { 51 res = await getDeletedUser() 52 } else { 53 const user = (await User.findByPk(job.data.userId)) as User 54 const userPetition = await getPetitionSigned(user, actorUrl) 55 if (userPetition) { 56 if (!federatedHost && url) { 57 const federatedHostToCreate = { 58 displayName: url.host.toLocaleLowerCase(), 59 publicInbox: userPetition.endpoints?.sharedInbox ? userPetition.endpoints?.sharedInbox : '' 60 } 61 federatedHost = (await FederatedHost.findOrCreate({ where: federatedHostToCreate }))[0] 62 } 63 if (!url || !federatedHost) { 64 logger.warn({ message: 'Url is not valid wtf', trace: new Error().stack }) 65 return await getDeletedUser() 66 } 67 const remoteMentionUrl = typeof userPetition.url === 'string' ? userPetition.url : '' 68 let followers = 0 69 let followed = 0 70 if (userPetition.followers) { 71 const followersPetition = await getPetitionSigned(user, userPetition.followers) 72 if (followersPetition && followersPetition.totalItems) { 73 followers = followersPetition.totalItems 74 } 75 } 76 if (userPetition.following) { 77 const followingPetition = await getPetitionSigned(user, userPetition.following) 78 if (followingPetition && followingPetition.totalItems) { 79 followed = followingPetition.totalItems 80 } 81 } 82 const userData = { 83 hideFollows: false, 84 hideProfileNotLoggedIn: false, 85 url: `@${userPetition.preferredUsername}@${url?.host}`, 86 name: userPetition.name ? userPetition.name : userPetition.preferredUsername, 87 email: null, 88 description: userPetition.summary ? userPetition.summary : '', 89 avatar: userPetition.icon?.url 90 ? userPetition.icon.url 91 : `${completeEnvironment.mediaUrl}/uploads/default.webp`, 92 headerImage: userPetition?.image?.url ? userPetition.image.url.toString() : ``, 93 password: 'NOT_A_WAFRN_USER_NOT_REAL_PASSWORD', 94 publicKey: userPetition.publicKey?.publicKeyPem, 95 remoteInbox: userPetition.inbox, 96 remoteId: actorUrl, 97 activated: true, 98 federatedHostId: federatedHost.id, 99 remoteMentionUrl: remoteMentionUrl, 100 followersCollectionUrl: userPetition.followers, 101 followingCollectionUrl: userPetition.following, 102 isBot: userPetition.type != 'Person', 103 followerCount: followers, 104 followingCount: followed, 105 createdAt: userPetition.published ? new Date(userPetition.published) : new Date(), 106 updatedAt: new Date(), 107 NSFW: false, 108 birthDate: new Date(), 109 userMigratedTo: userPetition.movedTo || '' 110 } 111 federatedHost.publicInbox = userPetition.endpoints?.sharedInbox 112 await federatedHost.save() 113 let userRes 114 const existingUsers = await User.findAll({ 115 where: { 116 [Op.or]: [ 117 sequelize.where(sequelize.fn('lower', sequelize.col('url')), userData.url.toLowerCase()), 118 { 119 remoteId: userData.remoteId 120 } 121 ] 122 } 123 }) 124 if (res) { 125 if (res !== (await getDeletedUser())) { 126 userRes = await User.findByPk(res as string) 127 if (existingUsers.length > 1) { 128 logger.debug({ 129 message: `Multiple fedi users found for ${userData.url} (${userData.remoteId}): ${existingUsers.length}` 130 }) 131 for await (const userWithDuplicatedData of existingUsers.slice(1)) { 132 userWithDuplicatedData.url = userWithDuplicatedData.url + '_DUPLICATED_' + new Date().getTime() 133 userWithDuplicatedData.remoteId = 134 userWithDuplicatedData.remoteId + '_DUPLICATED_' + new Date().getTime() 135 } 136 } 137 if (existingUsers && existingUsers.length > 0 && existingUsers[0] && userRes?.id !== existingUsers[0]?.id) { 138 const existingUser = existingUsers[0] 139 existingUser.activated = false 140 existingUser.remoteId = `${existingUser.remoteId}_OVERWRITTEN_ON${new Date().getTime()}` 141 existingUser.url = `${existingUser.url}_OVERWRITTEN_ON${new Date().getTime()}` 142 await existingUser.save() 143 if (userRes) { 144 const updates = [ 145 Follows.update( 146 { 147 followerId: userRes.id 148 }, 149 { 150 where: { 151 followerId: existingUser.id 152 } 153 } 154 ), 155 Follows.update( 156 { 157 followedId: userRes.id 158 }, 159 { 160 where: { 161 followedId: existingUser.id 162 } 163 } 164 ), 165 Post.update( 166 { 167 userId: userRes.id 168 }, 169 { 170 where: { 171 userId: existingUser.id 172 } 173 } 174 ), 175 UserLikesPostRelations.update( 176 { 177 userId: userRes.id 178 }, 179 { 180 where: { 181 userId: existingUser.id 182 } 183 } 184 ), 185 EmojiReaction.update( 186 { 187 userId: userRes.id 188 }, 189 { 190 where: { 191 userId: existingUser.id 192 } 193 } 194 ), 195 Blocks.update( 196 { 197 blockedId: userRes.id 198 }, 199 { 200 where: { 201 blockedId: existingUser.id 202 } 203 } 204 ), 205 Blocks.update( 206 { 207 blockerId: userRes.id 208 }, 209 { 210 where: { 211 blockerId: existingUser.id 212 } 213 } 214 ), 215 Mutes.update( 216 { 217 muterId: userRes.id 218 }, 219 { 220 where: { 221 muterId: existingUser.id 222 } 223 } 224 ), 225 Mutes.update( 226 { 227 mutedId: userRes.id 228 }, 229 { 230 where: { 231 mutedId: existingUser.id 232 } 233 } 234 ), 235 PostMentionsUserRelation.update( 236 { 237 userId: userRes.id 238 }, 239 { 240 where: { 241 userId: existingUser.id 242 } 243 } 244 ) 245 ] 246 await Promise.all(updates) 247 } 248 await redisCache.del('userRemoteId:' + existingUser.remoteId) 249 } 250 if (userRes) { 251 userRes.set(userData) 252 await userRes.save() 253 } else { 254 redisCache.del('userRemoteId:' + actorUrl.toLocaleLowerCase()) 255 } 256 } 257 } else { 258 if (existingUsers && existingUsers[0]) { 259 existingUsers[0].set(userData) 260 await existingUsers[0].save() 261 } else { 262 userRes = await User.create(userData) 263 } 264 } 265 if ( 266 userRes && 267 userRes.id && 268 userRes.url != completeEnvironment.deletedUser && 269 userPetition && 270 userPetition.attachment && 271 userPetition.attachment.length 272 ) { 273 await UserOptions.destroy({ 274 where: { 275 userId: userRes.id, 276 optionName: { 277 [Op.like]: 'fediverse.public.attachment' 278 } 279 } 280 }) 281 const properties = userPetition.attachment.filter((elem: any) => elem.type === 'PropertyValue') 282 await UserOptions.create({ 283 userId: userRes.id, 284 optionName: `fediverse.public.attachment`, 285 optionValue: JSON.stringify(properties), 286 public: true 287 }) 288 } 289 res = userRes?.id ? userRes.id : await getDeletedUser() 290 try { 291 if (userRes) { 292 const tags = userPetition?.tag 293 ? Array.isArray(userPetition.tag) 294 ? userPetition.tag 295 : [userPetition.tag] 296 : [] 297 const emojis = [...new Set(tags.filter((elem: fediverseTag) => elem.type === 'Emoji'))] 298 await processUserEmojis(userRes, emojis) 299 } 300 } catch (error) { 301 logger.info({ 302 message: `Error processing emojis from user ${userRes?.url}`, 303 error: error, 304 tags: userPetition?.tag, 305 userPetition: userPetition 306 }) 307 } 308 } 309 } 310 } 311 return res 312} 313 314export { getRemoteActorIdProcessor }