@evanp/activitypub-bot 0.39.0 → 0.39.1

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.
@@ -4,7 +4,6 @@ import path from 'node:path'
4
4
  import { fileURLToPath } from 'node:url'
5
5
 
6
6
  import fetch from 'node-fetch'
7
- import { ProblemDetailsError } from './errors.js'
8
7
 
9
8
  import as2 from './activitystreams.js'
10
9
  import { SignaturePolicyStorage } from './signaturepolicystorage.js'
@@ -23,6 +22,32 @@ const COLLECTION_TYPES = [
23
22
  `${NS}OrderedCollection`
24
23
  ]
25
24
 
25
+ function normalizeHeaders (headers) {
26
+ if (!headers) {
27
+ return headers
28
+ }
29
+ if (typeof headers.forEach === 'function') {
30
+ const result = {}
31
+ headers.forEach((value, key) => {
32
+ result[key] = value
33
+ })
34
+ return result
35
+ }
36
+ return headers
37
+ }
38
+
39
+ export class ActivityPubClientError extends Error {
40
+ constructor (status, message, { url, method, headers, body } = {}) {
41
+ super(message)
42
+ this.name = 'ActivityPubClientError'
43
+ this.status = status
44
+ this.url = url
45
+ this.method = method
46
+ this.headers = normalizeHeaders(headers)
47
+ this.body = body
48
+ }
49
+ }
50
+
26
51
  export class ActivityPubClient {
27
52
  static #githubUrl = 'https://github.com/evanp/activitypub-bot'
28
53
  static #userAgent = `activitypub.bot/${version} (${ActivityPubClient.#githubUrl})`
@@ -188,10 +213,10 @@ export class ActivityPubClient {
188
213
  { status: res.status, body, url: baseUrl },
189
214
  'Could not fetch url'
190
215
  )
191
- throw new ProblemDetailsError(
192
- 500,
216
+ throw new ActivityPubClientError(
217
+ res.status,
193
218
  `Could not fetch ${baseUrl}`,
194
- { headers: res.headers }
219
+ { url: baseUrl, method, headers: res.headers, body }
195
220
  )
196
221
  }
197
222
 
@@ -297,10 +322,10 @@ export class ActivityPubClient {
297
322
  await this.#limiter.update(hostname, res.headers)
298
323
  this.#logger.debug({ url }, 'Done fetching POST')
299
324
  if (res.status < 200 || res.status > 299) {
300
- throw new ProblemDetailsError(
301
- 500,
325
+ throw new ActivityPubClientError(
326
+ res.status,
302
327
  `Could not post to ${url}`,
303
- { headers: res.headers }
328
+ { url, method, headers: res.headers }
304
329
  )
305
330
  }
306
331
  if (!storedPolicy && lastPolicy) {
package/lib/app.js CHANGED
@@ -9,7 +9,7 @@ import Logger from 'pino'
9
9
  import HTTPLogger from 'pino-http'
10
10
 
11
11
  import { ActivityDistributor } from './activitydistributor.js'
12
- import { ActivityPubClient } from './activitypubclient.js'
12
+ import { ActivityPubClient, ActivityPubClientError } from './activitypubclient.js'
13
13
  import { ActorStorage } from './actorstorage.js'
14
14
  import { BotDataStorage } from './botdatastorage.js'
15
15
  import { KeyStorage } from './keystorage.js'
@@ -255,31 +255,46 @@ export async function makeApp ({ databaseUrl, origin, bots, logLevel = 'silent',
255
255
 
256
256
  app.use((err, req, res, next) => {
257
257
  const { logger } = req.app.locals
258
- let status = 500
259
- if (err.status) {
260
- status = err.status
261
- }
262
- const title = (http.STATUS_CODES[status])
263
- ? http.STATUS_CODES[status]
264
- : 'Unknown Status'
265
-
266
- if (status >= 500 && status < 600) {
267
- logger.error(err)
268
- } else if (status >= 400 && status < 500) {
269
- logger.warn(err)
258
+ if (err instanceof ActivityPubClientError) {
259
+ logger.error({
260
+ err,
261
+ status: err.status,
262
+ url: err.url,
263
+ method: err.method
264
+ }, 'ActivityPub client request failed')
265
+ res.status(500).type('application/problem+json').json({
266
+ type: 'about:blank',
267
+ title: 'Internal Server Error',
268
+ status: 500,
269
+ detail: 'Internal Server Error'
270
+ })
270
271
  } else {
271
- logger.debug(err)
272
- }
273
- const type = err.type || 'about:blank'
274
- const problemTitle = err.title || title
275
- const extra = Object.fromEntries(
276
- Object.entries(err).filter(
277
- ([k]) => !['status', 'type', 'title', 'detail', 'stack'].includes(k)
272
+ let status = 500
273
+ if (err.status) {
274
+ status = err.status
275
+ }
276
+ const title = (http.STATUS_CODES[status])
277
+ ? http.STATUS_CODES[status]
278
+ : 'Unknown Status'
279
+
280
+ if (status >= 500 && status < 600) {
281
+ logger.error(err)
282
+ } else if (status >= 400 && status < 500) {
283
+ logger.warn(err)
284
+ } else {
285
+ logger.debug(err)
286
+ }
287
+ const type = err.type || 'about:blank'
288
+ const problemTitle = err.title || title
289
+ const extra = Object.fromEntries(
290
+ Object.entries(err).filter(
291
+ ([k]) => !['status', 'type', 'title', 'detail', 'stack'].includes(k)
292
+ )
278
293
  )
279
- )
280
- res.status(status)
281
- res.type('application/problem+json')
282
- res.json({ type, title: problemTitle, status, detail: err.message, ...extra })
294
+ res.status(status)
295
+ res.type('application/problem+json')
296
+ res.json({ type, title: problemTitle, status, detail: err.message, ...extra })
297
+ }
283
298
  })
284
299
 
285
300
  app.onIdle = async () => {
@@ -15,7 +15,7 @@ export class HTTPMessageSignature {
15
15
  constructor (logger) {
16
16
  assert.ok(logger)
17
17
  assert.strictEqual(typeof logger, 'object')
18
- this.#logger = logger
18
+ this.#logger = logger.child({ class: this.constructor.name })
19
19
  }
20
20
 
21
21
  keyId (signatureInput) {
@@ -11,7 +11,7 @@ export class RateLimiter {
11
11
  assert.strictEqual(typeof connection, 'object')
12
12
  assert.strictEqual(typeof logger, 'object')
13
13
  this.#connection = connection
14
- this.#logger = logger
14
+ this.#logger = logger.child({ class: this.constructor.name })
15
15
  }
16
16
 
17
17
  async limit (host, maxWaitTime = 30000) {
@@ -15,7 +15,7 @@ export class RemoteObjectCache {
15
15
  assert.ok(connection)
16
16
  assert.ok(logger)
17
17
  this.#connection = connection
18
- this.#logger = logger
18
+ this.#logger = logger.child({ class: this.constructor.name })
19
19
  }
20
20
 
21
21
  async get (id, username) {
@@ -18,7 +18,7 @@ export class SignaturePolicyStorage {
18
18
  assert.ok(logger)
19
19
  assert.strictEqual(typeof logger, 'object')
20
20
  this.#connection = connection
21
- this.#logger = logger
21
+ this.#logger = logger.child({ class: this.constructor.name })
22
22
  }
23
23
 
24
24
  async get (origin) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evanp/activitypub-bot",
3
- "version": "0.39.0",
3
+ "version": "0.39.1",
4
4
  "description": "server-side ActivityPub bot framework",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",