From f61d71ac8cda6455238faa976ef221525ab5ed34 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Thu, 20 Mar 2025 20:43:05 -0400 Subject: refactor mastodon API and preserve remote user agent for requests --- .../server/api/mastodon/MastodonClientService.ts | 69 ++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 packages/backend/src/server/api/mastodon/MastodonClientService.ts (limited to 'packages/backend/src/server/api/mastodon/MastodonClientService.ts') diff --git a/packages/backend/src/server/api/mastodon/MastodonClientService.ts b/packages/backend/src/server/api/mastodon/MastodonClientService.ts new file mode 100644 index 0000000000..82f9b7bfa9 --- /dev/null +++ b/packages/backend/src/server/api/mastodon/MastodonClientService.ts @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { megalodon, MegalodonInterface } from 'megalodon'; +import { Injectable } from '@nestjs/common'; +import { MiLocalUser } from '@/models/User.js'; +import { AuthenticateService } from '@/server/api/AuthenticateService.js'; +import type { FastifyRequest } from 'fastify'; + +@Injectable() +export class MastodonClientService { + constructor( + private readonly authenticateService: AuthenticateService, + ) {} + + /** + * Gets the authenticated user and API client for a request. + */ + public async getAuthClient(request: FastifyRequest, accessToken?: string | null): Promise<{ client: MegalodonInterface, me: MiLocalUser | null }> { + const authorization = request.headers.authorization; + accessToken = accessToken !== undefined ? accessToken : getAccessToken(authorization); + + const me = await this.getAuth(request, accessToken); + const client = this.getClient(request, accessToken); + + return { client, me }; + } + + /** + * Gets the authenticated client user for a request. + */ + public async getAuth(request: FastifyRequest, accessToken?: string | null): Promise { + const authorization = request.headers.authorization; + accessToken = accessToken !== undefined ? accessToken : getAccessToken(authorization); + const [me] = await this.authenticateService.authenticate(accessToken); + return me; + } + + /** + * Creates an authenticated API client for a request. + */ + public getClient(request: FastifyRequest, accessToken?: string | null): MegalodonInterface { + const authorization = request.headers.authorization; + accessToken = accessToken !== undefined ? accessToken : getAccessToken(authorization); + + // TODO pass agent? + const baseUrl = this.getBaseUrl(request); + const userAgent = request.headers['user-agent']; + return megalodon('misskey', baseUrl, accessToken, userAgent); + } + + /** + * Gets the base URL (origin) of the incoming request + */ + public getBaseUrl(request: FastifyRequest): string { + return `${request.protocol}://${request.host}`; + } +} + +/** + * Extracts the first access token from an authorization header + * Returns null if none were found. + */ +function getAccessToken(authorization: string | undefined): string | null { + const accessTokenArr = authorization?.split(' ') ?? [null]; + return accessTokenArr[accessTokenArr.length - 1]; +} -- cgit v1.2.3-freya