diff --git a/src/app.ts b/src/app.ts index 78747f62..9acc5f9b 100644 --- a/src/app.ts +++ b/src/app.ts @@ -43,6 +43,8 @@ import { } from './dispatchers'; import { inboxHandler, postPublishedWebhook, followAction } from './handlers'; +import * as path from 'path'; +import { RequestRecorder } from 'requestrecorder'; if (process.env.SENTRY_DSN) { Sentry.init({ dsn: process.env.SENTRY_DSN }); @@ -206,6 +208,13 @@ app.use(async (ctx, next) => { await next(); }); +if (process.env.RECORD_INBOX_REQUESTS) { + const filePath = path.join(import.meta.dirname, '../', 'inbox-requests.json'); + console.log(`Recording to ${filePath}`); + const requestRecorder = await RequestRecorder.create(filePath); + app.post('/.ghost/activitypub/inbox/:handle', requestRecorder.honoMiddleware.bind(requestRecorder)); +} + /** Custom API routes */ app.get('/ping', (ctx) => { diff --git a/src/requestrecorder.ts b/src/requestrecorder.ts new file mode 100644 index 00000000..a31de003 --- /dev/null +++ b/src/requestrecorder.ts @@ -0,0 +1,43 @@ +import { Context, Next } from 'hono'; +import { FileHandle, open } from 'fs/promises'; + +type JSONRequest = { + input: string + init: RequestInit +} + +export class RequestRecorder { + private constructor(private readonly file: FileHandle) {} + + async recordRequest(req: Request): Promise { + try { + const json = await this.requestToJSON(req); + this.file.appendFile(JSON.stringify(json) + '\n'); + } catch (err) { + console.error('Could not write JSON'); + } + } + + async honoMiddleware(ctx: Context, next: Next) { + this.recordRequest(ctx.req.raw); + await next(); + } + + async requestToJSON(request: Request): Promise { + const req = request.clone(); + const body = await new Response(req.body).text(); + return { + input: req.url, + init: { + body: body, + headers: Object.fromEntries(req.headers.entries()), + method: req.method + } + }; + } + + static async create(path: string): Promise { + const file = await open(path, 'a'); + return new RequestRecorder(file); + } +}