diff --git a/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts b/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts index 0c74bfb50d..b3c4f77d1c 100644 --- a/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts +++ b/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts @@ -11,7 +11,7 @@ import type { Config } from '@/config.js'; import { getInstance } from './endpoints/meta.js'; import { MetaService } from '@/core/MetaService.js'; import multer from 'fastify-multer'; -import { apiAuthMastodon, apiAccountMastodon, apiFilterMastodon, apiNotifyMastodon, apiSearchMastodon } from './endpoints.js'; +import { apiAuthMastodon, apiAccountMastodon, apiFilterMastodon, apiNotifyMastodon, apiSearchMastodon, apiTimelineMastodon } from './endpoints.js'; const staticAssets = fileURLToPath(new URL('../../../../assets/', import.meta.url)); @@ -722,6 +722,31 @@ export class MastodonApiServerService { reply.code(401).send(e.response.data); } }); + //#endregion + + //#region Timelines + const TLEndpoint = new apiTimelineMastodon(fastify); + + // GET Endpoints + TLEndpoint.getTL(); + TLEndpoint.getHomeTl(); + TLEndpoint.getListTL(); + TLEndpoint.getTagTl(); + TLEndpoint.getConversations(); + TLEndpoint.getList(); + TLEndpoint.getLists(); + TLEndpoint.getListAccounts(); + + // POST Endpoints + TLEndpoint.createList(); + TLEndpoint.addListAccount(); + + // PUT Endpoint + TLEndpoint.updateList(); + + // DELETE Endpoints + TLEndpoint.deleteList(); + TLEndpoint.rmListAccount(); //#endregion done(); } diff --git a/packages/backend/src/server/api/mastodon/endpoints.ts b/packages/backend/src/server/api/mastodon/endpoints.ts index d5528ade51..111f9ef9c5 100644 --- a/packages/backend/src/server/api/mastodon/endpoints.ts +++ b/packages/backend/src/server/api/mastodon/endpoints.ts @@ -3,11 +3,13 @@ import { apiAccountMastodon } from './endpoints/account.js'; import { apiSearchMastodon } from './endpoints/search.js'; import { apiNotifyMastodon } from './endpoints/notifications.js'; import { apiFilterMastodon } from './endpoints/filter.js'; +import { apiTimelineMastodon } from './endpoints/timeline.js'; export { apiAccountMastodon, apiAuthMastodon, apiSearchMastodon, apiNotifyMastodon, - apiFilterMastodon + apiFilterMastodon, + apiTimelineMastodon } diff --git a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts index 1d2812c37c..ec7010ee70 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts @@ -1,5 +1,8 @@ import { convertId, IdConvertType as IdType, convertAccount, convertConversation, convertList, convertStatus } from '../converters.js'; import { ParsedUrlQuery } from 'querystring'; +import type { Entity, MegalodonInterface } from 'megalodon'; +import type { FastifyInstance } from 'fastify'; +import { getClient } from '../MastodonApiServerService.js'; export function limitToInt(q: ParsedUrlQuery) { let object: any = q; @@ -65,4 +68,250 @@ function nl2br(str: string) { str = str.replace(/\r\n/g, '
'); str = str.replace(/(\n|\r)/g, '
'); return str; +} + +export class apiTimelineMastodon { + private fastify: FastifyInstance; + + constructor(fastify: FastifyInstance) { + this.fastify = fastify; + } + + public async getTL() { + this.fastify.get('/v1/timelines/public', async (_request, reply) => { + const BASE_URL = `${_request.protocol}://${_request.hostname}`; + const accessTokens = _request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const query: any = _request.query; + const data = query.local === 'true' + ? await client.getLocalTimeline(convertTimelinesArgsId(argsToBools(limitToInt(query)))) + : await client.getPublicTimeline(convertTimelinesArgsId(argsToBools(limitToInt(query)))); + reply.send(data.data.map((status: Entity.Status) => convertStatus(status))); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + reply.code(401).send(e.response.data); + } + }); + } + + public async getHomeTl() { + this.fastify.get('/v1/timelines/home', async (_request, reply) => { + const BASE_URL = `${_request.protocol}://${_request.hostname}`; + const accessTokens = _request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const query: any = _request.query; + const data = await client.getHomeTimeline(convertTimelinesArgsId(limitToInt(query))); + reply.send(data.data.map((status: Entity.Status) => convertStatus(status))); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + reply.code(401).send(e.response.data); + } + }); + } + + public async getTagTl() { + this.fastify.get<{ Params: { hashtag: string } }>('/v1/timelines/tag/:hashtag', async (_request, reply) => { + const BASE_URL = `${_request.protocol}://${_request.hostname}`; + const accessTokens = _request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const query: any = _request.query; + const params: any = _request.params; + const data = await client.getTagTimeline(params.hashtag, convertTimelinesArgsId(limitToInt(query))); + reply.send(data.data.map((status: Entity.Status) => convertStatus(status))); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + reply.code(401).send(e.response.data); + } + }); + } + + public async getListTL() { + this.fastify.get<{ Params: { id: string } }>('/v1/timelines/list/:id', async (_request, reply) => { + const BASE_URL = `${_request.protocol}://${_request.hostname}`; + const accessTokens = _request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const query: any = _request.query; + const params: any = _request.params; + const data = await client.getListTimeline(convertId(params.id, IdType.SharkeyId), convertTimelinesArgsId(limitToInt(query))); + reply.send(data.data.map((status: Entity.Status) => convertStatus(status))); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + reply.code(401).send(e.response.data); + } + }); + } + + public async getConversations() { + this.fastify.get('/v1/conversations', async (_request, reply) => { + const BASE_URL = `${_request.protocol}://${_request.hostname}`; + const accessTokens = _request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const query: any = _request.query; + const data = await client.getConversationTimeline(convertTimelinesArgsId(limitToInt(query))); + reply.send(data.data.map((conversation: Entity.Conversation) => convertConversation(conversation))); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + reply.code(401).send(e.response.data); + } + }); + } + + public async getList(){ + this.fastify.get<{ Params: { id: string } }>('/v1/lists/:id', async (_request, reply) => { + try { + const BASE_URL = `${_request.protocol}://${_request.hostname}`; + const accessTokens = _request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + const params: any = _request.params; + const data = await client.getList(convertId(params.id, IdType.SharkeyId)); + reply.send(convertList(data.data)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + reply.code(401).send(e.response.data); + } + }); + } + + public async getLists() { + this.fastify.get('/v1/lists', async (_request, reply) => { + try { + const BASE_URL = `${_request.protocol}://${_request.hostname}`; + const accessTokens = _request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + const account = await client.verifyAccountCredentials(); + const data = await client.getLists(account.data.id); + reply.send(data.data.map((list: Entity.List) => convertList(list))); + } catch (e: any) { + console.error(e); + return e.response.data; + } + }); + } + + public async getListAccounts(){ + this.fastify.get<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (_request, reply) => { + try { + const BASE_URL = `${_request.protocol}://${_request.hostname}`; + const accessTokens = _request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + const params: any = _request.params; + const query: any = _request.query; + const data = await client.getAccountsInList( + convertId(params.id, IdType.SharkeyId), + convertTimelinesArgsId(query) + ); + reply.send(data.data.map((account: Entity.Account) => convertAccount(account))); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + reply.code(401).send(e.response.data); + } + }); + } + + public async addListAccount() { + this.fastify.post<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (_request, reply) => { + try { + const BASE_URL = `${_request.protocol}://${_request.hostname}`; + const accessTokens = _request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + const params: any = _request.params; + const query: any = _request.query; + const data = await client.addAccountsToList( + convertId(params.id, IdType.SharkeyId), + (query.accounts_id as string[]).map((id) => convertId(id, IdType.SharkeyId)) + ); + reply.send(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + reply.code(401).send(e.response.data); + } + }); + } + + public async rmListAccount() { + this.fastify.delete<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (_request, reply) => { + try { + const BASE_URL = `${_request.protocol}://${_request.hostname}`; + const accessTokens = _request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + const params: any = _request.params; + const query: any = _request.query; + const data = await client.deleteAccountsFromList( + convertId(params.id, IdType.SharkeyId), + (query.accounts_id as string[]).map((id) => convertId(id, IdType.SharkeyId)) + ); + reply.send(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + reply.code(401).send(e.response.data); + } + }); + } + + public async createList() { + this.fastify.post('/v1/lists', async (_request, reply) => { + try { + const BASE_URL = `${_request.protocol}://${_request.hostname}`; + const accessTokens = _request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + const body: any = _request.body; + const data = await client.createList(body.title); + reply.send(convertList(data.data)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + reply.code(401).send(e.response.data); + } + }); + } + + public async updateList() { + this.fastify.put<{ Params: { id: string } }>('/v1/lists/:id', async (_request, reply) => { + try { + const BASE_URL = `${_request.protocol}://${_request.hostname}`; + const accessTokens = _request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + const body: any = _request.body; + const params: any = _request.params; + const data = await client.updateList(convertId(params.id, IdType.SharkeyId), body.title); + reply.send(convertList(data.data)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + reply.code(401).send(e.response.data); + } + }); + } + + public async deleteList() { + this.fastify.delete<{ Params: { id: string } }>('/v1/lists/:id', async (_request, reply) => { + try { + const BASE_URL = `${_request.protocol}://${_request.hostname}`; + const accessTokens = _request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + const params: any = _request.params; + const data = await client.deleteList(convertId(params.id, IdType.SharkeyId)); + reply.send(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + reply.code(401).send(e.response.data); + } + }); + } + } \ No newline at end of file