1 import * as Bluebird from 'bluebird'
2 import { Transaction } from 'sequelize'
3 import * as url from 'url'
4 import { ActivityPubActor } from '../../../shared/models/activitypub'
5 import { doRequest, logger, retryTransactionWrapper } from '../../helpers'
6 import { isRemoteAccountValid } from '../../helpers/custom-validators/activitypub'
7 import { ACTIVITY_PUB, sequelizeTypescript } from '../../initializers'
8 import { AccountModel } from '../../models/account/account'
9 import { ServerModel } from '../../models/server/server'
11 async function getOrCreateAccountAndServer (accountUrl: string) {
12 let account = await AccountModel.loadByUrl(accountUrl)
14 // We don't have this account in our database, fetch it on remote
16 account = await fetchRemoteAccount(accountUrl)
17 if (account === undefined) throw new Error('Cannot fetch remote account.')
20 arguments: [ account ],
21 errorMessage: 'Cannot save account and server with many retries.'
23 account = await retryTransactionWrapper(saveAccountAndServerIfNotExist, options)
29 function saveAccountAndServerIfNotExist (account: AccountModel, t?: Transaction): Bluebird<AccountModel> | Promise<AccountModel> {
30 if (t !== undefined) {
33 return sequelizeTypescript.transaction(t => {
38 async function save (t: Transaction) {
39 const accountHost = url.parse(account.url).host
41 const serverOptions = {
50 const [ server ] = await ServerModel.findOrCreate(serverOptions)
52 // Save our new account in database
53 account.set('serverId', server.id)
54 account = await account.save({ transaction: t })
60 async function fetchRemoteAccount (accountUrl: string) {
65 'Accept': ACTIVITY_PUB.ACCEPT_HEADER
69 logger.info('Fetching remote account %s.', accountUrl)
73 requestResult = await doRequest(options)
75 logger.warn('Cannot fetch remote account %s.', accountUrl, err)
79 const accountJSON: ActivityPubActor = JSON.parse(requestResult.body)
80 if (isRemoteAccountValid(accountJSON) === false) {
81 logger.debug('Remote account JSON is not valid.', { accountJSON })
85 const followersCount = await fetchAccountCount(accountJSON.followers)
86 const followingCount = await fetchAccountCount(accountJSON.following)
88 return new AccountModel({
89 uuid: accountJSON.uuid,
90 name: accountJSON.preferredUsername,
92 publicKey: accountJSON.publicKey.publicKeyPem,
94 followersCount: followersCount,
95 followingCount: followingCount,
96 inboxUrl: accountJSON.inbox,
97 outboxUrl: accountJSON.outbox,
98 sharedInboxUrl: accountJSON.endpoints.sharedInbox,
99 followersUrl: accountJSON.followers,
100 followingUrl: accountJSON.following
105 getOrCreateAccountAndServer,
107 saveAccountAndServerIfNotExist
110 // ---------------------------------------------------------------------------
112 async function fetchAccountCount (url: string) {
120 requestResult = await doRequest(options)
122 logger.warn('Cannot fetch remote account count %s.', url, err)
126 return requestResult.totalItems ? requestResult.totalItems : 0