@evanp/activitypub-bot 0.32.3 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/activitydeliverer.js +18 -2
- package/lib/activitydistributor.js +3 -1
- package/lib/activityhandler.js +15 -17
- package/lib/activitypubclient.js +5 -3
- package/lib/botcontext.js +31 -16
- package/lib/httpsignature.js +2 -1
- package/lib/httpsignatureauthenticator.js +1 -0
- package/lib/keystorage.js +3 -2
- package/lib/migrations/index.js +3 -3
- package/lib/ratelimiter.js +31 -0
- package/lib/remotekeystorage.js +80 -15
- package/lib/routes/collection.js +4 -2
- package/lib/routes/inbox.js +4 -2
- package/lib/routes/object.js +2 -1
- package/lib/routes/sharedinbox.js +4 -2
- package/lib/routes/webfinger.js +22 -0
- package/package.json +1 -1
package/lib/activitydeliverer.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import BotMaker from './botmaker.js'
|
|
2
1
|
import assert from 'node:assert'
|
|
3
|
-
|
|
2
|
+
|
|
4
3
|
import * as ttlcachePkg from '@isaacs/ttlcache'
|
|
5
4
|
|
|
5
|
+
import as2 from './activitystreams.js'
|
|
6
|
+
import BotMaker from './botmaker.js'
|
|
7
|
+
|
|
6
8
|
const TTLCache =
|
|
7
9
|
ttlcachePkg.TTLCache ?? ttlcachePkg.default ?? ttlcachePkg
|
|
8
10
|
|
|
@@ -158,6 +160,20 @@ export class ActivityDeliverer {
|
|
|
158
160
|
}
|
|
159
161
|
}
|
|
160
162
|
}
|
|
163
|
+
|
|
164
|
+
switch (activity.type) {
|
|
165
|
+
case `${NS}Follow`:
|
|
166
|
+
await this.#deliverFollowToAll(activity, bots, deliveredTo)
|
|
167
|
+
break
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async #deliverFollowToAll (activity, bots, deliveredTo) {
|
|
172
|
+
const object = activity.object?.first
|
|
173
|
+
if (object?.id && this.#isLocal(object) && !deliveredTo.has(this.#formatter.unformat(object.id).username)) {
|
|
174
|
+
this.#logger.debug({ object: object.id, activity: activity.id }, 'Follow not yet delivered to object, delivering now')
|
|
175
|
+
await this.#deliverLocalActor(activity, object, bots, deliveredTo)
|
|
176
|
+
}
|
|
161
177
|
}
|
|
162
178
|
|
|
163
179
|
async #isRemoteFollowersCollection (actor, object) {
|
package/lib/activityhandler.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import as2 from './activitystreams.js'
|
|
2
|
-
import { nanoid } from 'nanoid'
|
|
3
1
|
import assert from 'node:assert'
|
|
4
2
|
|
|
3
|
+
import { nanoid } from 'nanoid'
|
|
4
|
+
|
|
5
|
+
import as2 from './activitystreams.js'
|
|
6
|
+
|
|
5
7
|
const AS2 = 'https://www.w3.org/ns/activitystreams#'
|
|
6
8
|
|
|
7
9
|
const THREAD_PROP = 'https://purl.archive.org/socialweb/thread#thread'
|
|
@@ -632,16 +634,15 @@ export class ActivityHandler {
|
|
|
632
634
|
'following',
|
|
633
635
|
actor
|
|
634
636
|
)
|
|
635
|
-
await this.#actorStorage.
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
)
|
|
637
|
+
const followId = await this.#actorStorage.getLastActivity(bot.username, 'Follow', actor)
|
|
638
|
+
if (followId) {
|
|
639
|
+
const followActivity = await this.#objectStorage.read(followId)
|
|
640
|
+
await this.#actorStorage.removeFromCollection(
|
|
641
|
+
bot.username,
|
|
642
|
+
'pendingFollowing',
|
|
643
|
+
followActivity
|
|
644
|
+
)
|
|
645
|
+
}
|
|
645
646
|
}
|
|
646
647
|
}
|
|
647
648
|
|
|
@@ -827,11 +828,9 @@ export class ActivityHandler {
|
|
|
827
828
|
})
|
|
828
829
|
return
|
|
829
830
|
}
|
|
830
|
-
if (
|
|
831
|
-
!(await this.#actorStorage.isInCollection(bot.username, 'followers', actor)) &&
|
|
832
|
-
!(await this.#actorStorage.isInCollection(bot.username, 'pendingFollowers', actor))) {
|
|
831
|
+
if (!(await this.#actorStorage.isInCollection(bot.username, 'followers', actor))) {
|
|
833
832
|
this.#logger.warn({
|
|
834
|
-
msg: 'Undo follow activity from actor not in followers
|
|
833
|
+
msg: 'Undo follow activity from actor not in followers',
|
|
835
834
|
activity: undoActivity.id,
|
|
836
835
|
followActivity: followActivity.id,
|
|
837
836
|
actor: actor.id
|
|
@@ -839,7 +838,6 @@ export class ActivityHandler {
|
|
|
839
838
|
return
|
|
840
839
|
}
|
|
841
840
|
await this.#actorStorage.removeFromCollection(bot.username, 'followers', actor)
|
|
842
|
-
await this.#actorStorage.removeFromCollection(bot.username, 'pendingFollowers', actor)
|
|
843
841
|
this.#logger.debug(
|
|
844
842
|
{ bot: this.#botId(bot), activity: undoActivity.id },
|
|
845
843
|
'Notifying bot of undo follow activity'
|
package/lib/activitypubclient.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import as2 from './activitystreams.js'
|
|
2
|
-
import fetch from 'node-fetch'
|
|
3
1
|
import assert from 'node:assert'
|
|
4
|
-
import createHttpError from 'http-errors'
|
|
5
2
|
import fs from 'node:fs'
|
|
6
3
|
import path from 'node:path'
|
|
7
4
|
import { fileURLToPath } from 'node:url'
|
|
8
5
|
|
|
6
|
+
import fetch from 'node-fetch'
|
|
7
|
+
import createHttpError from 'http-errors'
|
|
8
|
+
|
|
9
|
+
import as2 from './activitystreams.js'
|
|
10
|
+
|
|
9
11
|
const __filename = fileURLToPath(import.meta.url)
|
|
10
12
|
const __dirname = path.dirname(__filename)
|
|
11
13
|
|
package/lib/botcontext.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import { nanoid } from 'nanoid'
|
|
4
4
|
import fetch from 'node-fetch'
|
|
5
5
|
|
|
6
|
+
import as2 from './activitystreams.js'
|
|
7
|
+
|
|
6
8
|
const AS2_TYPES = [
|
|
7
9
|
'application/activity+json',
|
|
8
10
|
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
|
|
@@ -225,8 +227,7 @@ export class BotContext {
|
|
|
225
227
|
assert.ok(actor)
|
|
226
228
|
assert.equal(typeof actor, 'object')
|
|
227
229
|
let activity
|
|
228
|
-
if (await this.#actorStorage.isInCollection(this.#botId, 'following', actor)
|
|
229
|
-
await this.#actorStorage.isInCollection(this.#botId, 'pendingFollowing', actor)) {
|
|
230
|
+
if (await this.#actorStorage.isInCollection(this.#botId, 'following', actor)) {
|
|
230
231
|
const originalId = await this.#actorStorage.getLastActivity(
|
|
231
232
|
this.#botId,
|
|
232
233
|
'Follow',
|
|
@@ -237,17 +238,24 @@ export class BotContext {
|
|
|
237
238
|
}
|
|
238
239
|
activity = await this.#objectStorage.read(originalId)
|
|
239
240
|
} else {
|
|
240
|
-
await this.#actorStorage.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
241
|
+
const pendingId = await this.#actorStorage.getLastActivity(this.#botId, 'Follow', actor)
|
|
242
|
+
if (pendingId) {
|
|
243
|
+
const pendingActivity = await this.#objectStorage.read(pendingId)
|
|
244
|
+
if (await this.#actorStorage.isInCollection(this.#botId, 'pendingFollowing', pendingActivity)) {
|
|
245
|
+
return pendingActivity
|
|
246
|
+
}
|
|
247
|
+
}
|
|
245
248
|
activity = await this.#doActivity({
|
|
246
249
|
type: 'Follow',
|
|
247
250
|
object: actor.id,
|
|
248
251
|
to: actor.id
|
|
249
252
|
})
|
|
250
253
|
await this.#actorStorage.setLastActivity(this.#botId, activity)
|
|
254
|
+
await this.#actorStorage.addToCollection(
|
|
255
|
+
this.#botId,
|
|
256
|
+
'pendingFollowing',
|
|
257
|
+
activity
|
|
258
|
+
)
|
|
251
259
|
}
|
|
252
260
|
|
|
253
261
|
return activity
|
|
@@ -256,11 +264,15 @@ export class BotContext {
|
|
|
256
264
|
async unfollowActor (actor) {
|
|
257
265
|
assert.ok(actor)
|
|
258
266
|
assert.equal(typeof actor, 'object')
|
|
259
|
-
await this.#actorStorage.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
267
|
+
const followId = await this.#actorStorage.getLastActivity(this.#botId, 'Follow', actor)
|
|
268
|
+
if (followId) {
|
|
269
|
+
const followActivity = await this.#objectStorage.read(followId)
|
|
270
|
+
await this.#actorStorage.removeFromCollection(
|
|
271
|
+
this.#botId,
|
|
272
|
+
'pendingFollowing',
|
|
273
|
+
followActivity
|
|
274
|
+
)
|
|
275
|
+
}
|
|
264
276
|
await this.#actorStorage.removeFromCollection(
|
|
265
277
|
this.#botId,
|
|
266
278
|
'following',
|
|
@@ -273,11 +285,14 @@ export class BotContext {
|
|
|
273
285
|
assert.ok(actor)
|
|
274
286
|
assert.equal(typeof actor, 'object')
|
|
275
287
|
await this.#actorStorage.addToCollection(this.#botId, 'blocked', actor)
|
|
288
|
+
const followId = await this.#actorStorage.getLastActivity(this.#botId, 'Follow', actor)
|
|
289
|
+
if (followId) {
|
|
290
|
+
const followActivity = await this.#objectStorage.read(followId)
|
|
291
|
+
await this.#actorStorage.removeFromCollection(this.#botId, 'pendingFollowing', followActivity)
|
|
292
|
+
}
|
|
276
293
|
for (const coll of [
|
|
277
294
|
'following',
|
|
278
|
-
'followers'
|
|
279
|
-
'pendingFollowing',
|
|
280
|
-
'pendingFollowers'
|
|
295
|
+
'followers'
|
|
281
296
|
]) {
|
|
282
297
|
await this.#actorStorage.removeFromCollection(this.#botId, coll, actor)
|
|
283
298
|
}
|
package/lib/httpsignature.js
CHANGED
package/lib/keystorage.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { promisify } from 'util'
|
|
1
|
+
import { promisify } from 'node:util'
|
|
2
2
|
import crypto from 'node:crypto'
|
|
3
|
-
import HumanHasher from 'humanhash'
|
|
4
3
|
import assert from 'node:assert'
|
|
5
4
|
|
|
5
|
+
import HumanHasher from 'humanhash'
|
|
6
|
+
|
|
6
7
|
const generateKeyPair = promisify(crypto.generateKeyPair)
|
|
7
8
|
|
|
8
9
|
export class KeyStorage {
|
package/lib/migrations/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { fileURLToPath } from 'url'
|
|
2
|
-
import { dirname, resolve } from 'path'
|
|
3
|
-
import { readdirSync } from 'fs'
|
|
1
|
+
import { fileURLToPath } from 'node:url'
|
|
2
|
+
import { dirname, resolve } from 'node:path'
|
|
3
|
+
import { readdirSync } from 'node:fs'
|
|
4
4
|
|
|
5
5
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
6
6
|
|
package/lib/ratelimiter.js
CHANGED
|
@@ -55,7 +55,38 @@ export class RateLimiter {
|
|
|
55
55
|
updated_at = CURRENT_TIMESTAMP`,
|
|
56
56
|
{ replacements: [host, remaining, reset] }
|
|
57
57
|
)
|
|
58
|
+
} else if (headers.get('server') === 'Mastodon') {
|
|
59
|
+
const limits = await this.peek(host)
|
|
60
|
+
if (!limits || limits.length === 0 || limits[0].reset < (new Date())) {
|
|
61
|
+
const remaining = 299 // 300 - 1 for the current request
|
|
62
|
+
const reset = new Date(Math.ceil(Date.now() / 300000) * 300000)
|
|
63
|
+
this.#logger.debug({ reset, remaining, host }, 'updating')
|
|
64
|
+
await this.#connection.query(
|
|
65
|
+
`INSERT INTO rate_limit (host, remaining, reset)
|
|
66
|
+
VALUES (?, ?, ?)
|
|
67
|
+
ON CONFLICT (host) DO UPDATE
|
|
68
|
+
SET remaining = EXCLUDED.remaining,
|
|
69
|
+
reset = EXCLUDED.reset,
|
|
70
|
+
updated_at = CURRENT_TIMESTAMP`,
|
|
71
|
+
{ replacements: [host, remaining, reset] }
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async peek (host) {
|
|
78
|
+
const [result] = await this.#connection.query(
|
|
79
|
+
'SELECT remaining, reset FROM rate_limit WHERE host = ?',
|
|
80
|
+
{ replacements: [host] }
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if (result.length === 0) {
|
|
84
|
+
return []
|
|
58
85
|
}
|
|
86
|
+
|
|
87
|
+
const { remaining, reset } = result[0]
|
|
88
|
+
|
|
89
|
+
return [{ policy: 'default', remaining, reset: new Date(reset) }]
|
|
59
90
|
}
|
|
60
91
|
|
|
61
92
|
async #getWaitTime (host, maxWaitTime) {
|
package/lib/remotekeystorage.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import assert from 'node:assert'
|
|
2
|
+
|
|
1
3
|
const SEC_NS = 'https://w3id.org/security#'
|
|
4
|
+
const DEFAULT_NS = '_:'
|
|
2
5
|
|
|
3
6
|
export class RemoteKeyStorage {
|
|
4
7
|
#client = null
|
|
@@ -27,6 +30,10 @@ export class RemoteKeyStorage {
|
|
|
27
30
|
this.debug(`getPublicKey(${id}) - remote not found`)
|
|
28
31
|
return null
|
|
29
32
|
}
|
|
33
|
+
if (!await this.#confirmPublicKey(remote.owner, id)) {
|
|
34
|
+
this.#logger.warn({ owner: remote.owner, id }, 'Mismatched owner and key')
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
30
37
|
await this.#cachePublicKey(id, remote.owner, remote.publicKeyPem)
|
|
31
38
|
return remote
|
|
32
39
|
}
|
|
@@ -55,21 +62,11 @@ export class RemoteKeyStorage {
|
|
|
55
62
|
if (!response) {
|
|
56
63
|
return null
|
|
57
64
|
}
|
|
58
|
-
this.debug(`getRemotePublicKey(${id}) - response: ${
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
owner = response.get(SEC_NS + 'owner')?.first?.id
|
|
64
|
-
publicKeyPem = response.get(SEC_NS + 'publicKeyPem')?.first
|
|
65
|
-
} else if (response.get(SEC_NS + 'publicKey')) {
|
|
66
|
-
this.debug(`getRemotePublicKey(${id}) - publicKey`)
|
|
67
|
-
const publicKey = response.get(SEC_NS + 'publicKey').first
|
|
68
|
-
if (publicKey) {
|
|
69
|
-
owner = publicKey.get(SEC_NS + 'owner')?.first?.id
|
|
70
|
-
publicKeyPem = publicKey.get(SEC_NS + 'publicKeyPem')?.first
|
|
71
|
-
}
|
|
72
|
-
}
|
|
65
|
+
this.debug(`getRemotePublicKey(${id}) - response: ${response.id}`)
|
|
66
|
+
|
|
67
|
+
const owner = this.#getOwner(response)
|
|
68
|
+
const publicKeyPem = this.#getPublicKeyPem(response)
|
|
69
|
+
|
|
73
70
|
if (!owner || !publicKeyPem) {
|
|
74
71
|
return null
|
|
75
72
|
}
|
|
@@ -89,4 +86,72 @@ export class RemoteKeyStorage {
|
|
|
89
86
|
this.#logger.debug(...args)
|
|
90
87
|
}
|
|
91
88
|
}
|
|
89
|
+
|
|
90
|
+
async #confirmPublicKey (owner, id) {
|
|
91
|
+
assert.equal(typeof owner, 'string')
|
|
92
|
+
assert.equal(typeof id, 'string')
|
|
93
|
+
let actor
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
actor = await this.#client.get(owner)
|
|
97
|
+
} catch (err) {
|
|
98
|
+
this.#logger.warn({ err, owner, id }, 'Error getting key owner')
|
|
99
|
+
return false
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const publicKeyId = this.#getPublicKeyId(actor)
|
|
103
|
+
|
|
104
|
+
if (!publicKeyId) {
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return publicKeyId === id
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
#getSecIdProp (obj, prop) {
|
|
112
|
+
assert.strictEqual(typeof obj, 'object')
|
|
113
|
+
assert.strictEqual(typeof prop, 'string')
|
|
114
|
+
let value = obj.get(SEC_NS + prop)
|
|
115
|
+
if (value) {
|
|
116
|
+
return value.first?.id
|
|
117
|
+
}
|
|
118
|
+
value = obj.get(DEFAULT_NS + prop)
|
|
119
|
+
if (value) {
|
|
120
|
+
this.#logger.warn(
|
|
121
|
+
{ objectId: obj.id, prop },
|
|
122
|
+
'security property in default namespace'
|
|
123
|
+
)
|
|
124
|
+
const first = value.first
|
|
125
|
+
return (typeof first === 'string') ? first : first?.id
|
|
126
|
+
}
|
|
127
|
+
return null
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
#getOwner (obj) {
|
|
131
|
+
assert.strictEqual(typeof obj, 'object')
|
|
132
|
+
return this.#getSecIdProp(obj, 'owner')
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
#getPublicKeyPem (obj) {
|
|
136
|
+
assert.strictEqual(typeof obj, 'object')
|
|
137
|
+
const prop = 'publicKeyPem'
|
|
138
|
+
let value = obj.get(SEC_NS + prop)
|
|
139
|
+
if (value) {
|
|
140
|
+
return value.first
|
|
141
|
+
}
|
|
142
|
+
value = obj.get(DEFAULT_NS + prop)
|
|
143
|
+
if (value) {
|
|
144
|
+
this.#logger.warn(
|
|
145
|
+
{ objectId: obj.id, prop },
|
|
146
|
+
'security property in default namespace'
|
|
147
|
+
)
|
|
148
|
+
return value.first
|
|
149
|
+
}
|
|
150
|
+
return null
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#getPublicKeyId (obj) {
|
|
154
|
+
assert.strictEqual(typeof obj, 'object')
|
|
155
|
+
return this.#getSecIdProp(obj, 'publicKey')
|
|
156
|
+
}
|
|
92
157
|
}
|
package/lib/routes/collection.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import assert from 'node:assert'
|
|
2
|
+
|
|
1
3
|
import express from 'express'
|
|
2
|
-
import as2 from '../activitystreams.js'
|
|
3
4
|
import createHttpError from 'http-errors'
|
|
5
|
+
|
|
6
|
+
import as2 from '../activitystreams.js'
|
|
4
7
|
import BotMaker from '../botmaker.js'
|
|
5
|
-
import assert from 'node:assert'
|
|
6
8
|
|
|
7
9
|
const collections = ['outbox', 'liked', 'followers', 'following']
|
|
8
10
|
const router = express.Router()
|
package/lib/routes/inbox.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import http from 'node:http'
|
|
2
|
+
|
|
1
3
|
import express from 'express'
|
|
2
|
-
import as2 from '../activitystreams.js'
|
|
3
4
|
import createHttpError from 'http-errors'
|
|
5
|
+
|
|
6
|
+
import as2 from '../activitystreams.js'
|
|
4
7
|
import BotMaker from '../botmaker.js'
|
|
5
|
-
import http from 'node:http'
|
|
6
8
|
|
|
7
9
|
const router = express.Router()
|
|
8
10
|
|
package/lib/routes/object.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import http from 'node:http'
|
|
2
|
+
|
|
1
3
|
import express from 'express'
|
|
2
|
-
import as2 from '../activitystreams.js'
|
|
3
4
|
import createHttpError from 'http-errors'
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
import as2 from '../activitystreams.js'
|
|
5
7
|
|
|
6
8
|
const router = express.Router()
|
|
7
9
|
|
package/lib/routes/webfinger.js
CHANGED
|
@@ -33,6 +33,26 @@ async function botWebfinger (username, req, res, next) {
|
|
|
33
33
|
})
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
async function profileWebfinger (username, profileUrl, req, res, next) {
|
|
37
|
+
const { formatter, bots } = req.app.locals
|
|
38
|
+
const bot = await BotMaker.makeBot(bots, username)
|
|
39
|
+
if (!bot) {
|
|
40
|
+
return next(createHttpError(404, `No such bot '${username}'`))
|
|
41
|
+
}
|
|
42
|
+
res.status(200)
|
|
43
|
+
res.type('application/jrd+json')
|
|
44
|
+
res.json({
|
|
45
|
+
subject: profileUrl,
|
|
46
|
+
links: [
|
|
47
|
+
{
|
|
48
|
+
rel: 'alternate',
|
|
49
|
+
type: 'application/activity+json',
|
|
50
|
+
href: formatter.format({ username })
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
36
56
|
async function httpsWebfinger (resource, req, res, next) {
|
|
37
57
|
const { formatter } = req.app.locals
|
|
38
58
|
assert.ok(formatter)
|
|
@@ -42,6 +62,8 @@ async function httpsWebfinger (resource, req, res, next) {
|
|
|
42
62
|
const parts = formatter.unformat(resource)
|
|
43
63
|
if (parts.username && !parts.type && !parts.collection) {
|
|
44
64
|
return await botWebfinger(parts.username, req, res, next)
|
|
65
|
+
} else if (parts.username && parts.type === 'profile') {
|
|
66
|
+
return await profileWebfinger(parts.username, resource, req, res, next)
|
|
45
67
|
} else {
|
|
46
68
|
return next(createHttpError(400, `No webfinger lookup for url ${resource}`))
|
|
47
69
|
}
|