]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/lib/activitypub/account.ts
Add follow tests
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / account.ts
1 import * as Bluebird from 'bluebird'
2 import * as url from 'url'
3 import { ActivityPubActor } from '../../../shared/models/activitypub/activitypub-actor'
4 import { isRemoteAccountValid } from '../../helpers/custom-validators/activitypub/account'
5 import { retryTransactionWrapper } from '../../helpers/database-utils'
6 import { logger } from '../../helpers/logger'
7 import { doRequest } from '../../helpers/requests'
8 import { ACTIVITY_PUB } from '../../initializers/constants'
9 import { database as db } from '../../initializers/database'
10 import { AccountInstance } from '../../models/account/account-interface'
11 import { Transaction } from 'sequelize'
12
13 async function getOrCreateAccountAndServer (accountUrl: string) {
14 let account = await db.Account.loadByUrl(accountUrl)
15
16 // We don't have this account in our database, fetch it on remote
17 if (!account) {
18 account = await fetchRemoteAccount(accountUrl)
19 if (account === undefined) throw new Error('Cannot fetch remote account.')
20
21 const options = {
22 arguments: [ account ],
23 errorMessage: 'Cannot save account and server with many retries.'
24 }
25 account = await retryTransactionWrapper(saveAccountAndServerIfNotExist, options)
26 }
27
28 return account
29 }
30
31 function saveAccountAndServerIfNotExist (account: AccountInstance, t?: Transaction): Bluebird<AccountInstance> | Promise<AccountInstance> {
32 if (t !== undefined) {
33 return save(t)
34 } else {
35 return db.sequelize.transaction(t => {
36 return save(t)
37 })
38 }
39
40 async function save (t: Transaction) {
41 const accountHost = url.parse(account.url).host
42
43 const serverOptions = {
44 where: {
45 host: accountHost
46 },
47 defaults: {
48 host: accountHost
49 },
50 transaction: t
51 }
52 const [ server ] = await db.Server.findOrCreate(serverOptions)
53
54 // Save our new account in database
55 account.set('serverId', server.id)
56 account = await account.save({ transaction: t })
57
58 return account
59 }
60 }
61
62 async function fetchRemoteAccount (accountUrl: string) {
63 const options = {
64 uri: accountUrl,
65 method: 'GET',
66 headers: {
67 'Accept': ACTIVITY_PUB.ACCEPT_HEADER
68 }
69 }
70
71 logger.info('Fetching remote account %s.', accountUrl)
72
73 let requestResult
74 try {
75 requestResult = await doRequest(options)
76 } catch (err) {
77 logger.warn('Cannot fetch remote account %s.', accountUrl, err)
78 return undefined
79 }
80
81 const accountJSON: ActivityPubActor = JSON.parse(requestResult.body)
82 if (isRemoteAccountValid(accountJSON) === false) {
83 logger.debug('Remote account JSON is not valid.', { accountJSON })
84 return undefined
85 }
86
87 const followersCount = await fetchAccountCount(accountJSON.followers)
88 const followingCount = await fetchAccountCount(accountJSON.following)
89
90 const account = db.Account.build({
91 uuid: accountJSON.uuid,
92 name: accountJSON.preferredUsername,
93 url: accountJSON.url,
94 publicKey: accountJSON.publicKey.publicKeyPem,
95 privateKey: null,
96 followersCount: followersCount,
97 followingCount: followingCount,
98 inboxUrl: accountJSON.inbox,
99 outboxUrl: accountJSON.outbox,
100 sharedInboxUrl: accountJSON.endpoints.sharedInbox,
101 followersUrl: accountJSON.followers,
102 followingUrl: accountJSON.following
103 })
104
105 return account
106 }
107
108 export {
109 getOrCreateAccountAndServer,
110 fetchRemoteAccount,
111 saveAccountAndServerIfNotExist
112 }
113
114 // ---------------------------------------------------------------------------
115
116 async function fetchAccountCount (url: string) {
117 const options = {
118 uri: url,
119 method: 'GET'
120 }
121
122 let requestResult
123 try {
124 requestResult = await doRequest(options)
125 } catch (err) {
126 logger.warn('Cannot fetch remote account count %s.', url, err)
127 return undefined
128 }
129
130 return requestResult.totalItems ? requestResult.totalItems : 0
131 }