diff --git a/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts b/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts index 9212a1fcf..0c74bfb50 100644 --- a/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts +++ b/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UsersRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; -import megalodon, { Entity, MegalodonInterface } from "megalodon"; +import megalodon, { Entity, MegalodonInterface } from 'megalodon'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; import { convertId, IdConvertType as IdType, convertAccount, convertAnnouncement, convertFilter, convertAttachment, convertFeaturedTag, convertList } from './converters.js'; import { IsNull } from 'typeorm'; @@ -11,16 +11,12 @@ 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 } from './endpoints/auth.js'; -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 { apiAuthMastodon, apiAccountMastodon, apiFilterMastodon, apiNotifyMastodon, apiSearchMastodon } from './endpoints.js'; const staticAssets = fileURLToPath(new URL('../../../../assets/', import.meta.url)); export function getClient(BASE_URL: string, authorization: string | undefined): MegalodonInterface { - const accessTokenArr = authorization?.split(" ") ?? [null]; + const accessTokenArr = authorization?.split(' ') ?? [null]; const accessToken = accessTokenArr[accessTokenArr.length - 1]; const generator = (megalodon as any).default; const client = generator('misskey', BASE_URL, accessToken) as MegalodonInterface; @@ -49,7 +45,7 @@ export class MastodonApiServerService { fastify.register(multer.contentParser); - fastify.get("/v1/custom_emojis", async (_request, reply) => { + fastify.get('/v1/custom_emojis', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -62,7 +58,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/instance", async (_request, reply) => { + fastify.get('/v1/instance', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt @@ -76,7 +72,7 @@ export class MastodonApiServerService { isDeleted: false, isSuspended: false, }, - order: { id: "ASC" }, + order: { id: 'ASC' }, }); const contact = admin == null ? null : convertAccount((await client.getAccount(admin.id)).data); reply.send(await getInstance(data.data, contact, this.config, await this.metaService.fetch())); @@ -86,7 +82,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/announcements", async (_request, reply) => { + fastify.get('/v1/announcements', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -99,7 +95,7 @@ export class MastodonApiServerService { } }); - fastify.post<{ Body: { id: string } }>("/v1/announcements/:id/dismiss", async (_request, reply) => { + fastify.post<{ Body: { id: string } }>('/v1/announcements/:id/dismiss', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -115,14 +111,14 @@ export class MastodonApiServerService { }, ); - fastify.post("/v1/media", { preHandler: upload.single('file') }, async (_request, reply) => { + fastify.post('/v1/media', { preHandler: upload.single('file') }, async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const multipartData = await _request.file; if (!multipartData) { - reply.code(401).send({ error: "No image" }); + reply.code(401).send({ error: 'No image' }); return; } const data = await client.uploadMedia(multipartData); @@ -133,14 +129,14 @@ export class MastodonApiServerService { } }); - fastify.post("/v2/media", { preHandler: upload.single('file') }, async (_request, reply) => { + fastify.post('/v2/media', { preHandler: upload.single('file') }, async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { const multipartData = await _request.file; if (!multipartData) { - reply.code(401).send({ error: "No image" }); + reply.code(401).send({ error: 'No image' }); return; } const data = await client.uploadMedia(multipartData, _request.body!); @@ -151,7 +147,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/filters", async (_request, reply) => { + fastify.get('/v1/filters', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt @@ -165,7 +161,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/trends", async (_request, reply) => { + fastify.get('/v1/trends', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt @@ -179,9 +175,9 @@ export class MastodonApiServerService { } }); - fastify.post("/v1/apps", async (_request, reply) => { + fastify.post('/v1/apps', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; - const client = getClient(BASE_URL, ""); // we are using this here, because in private mode some info isnt + const client = getClient(BASE_URL, ''); // we are using this here, because in private mode some info isnt // displayed without being logged in try { const data = await apiAuthMastodon(_request, client); @@ -192,7 +188,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/preferences", async (_request, reply) => { + fastify.get('/v1/preferences', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt @@ -207,7 +203,7 @@ export class MastodonApiServerService { }); //#region Accounts - fastify.get("/v1/accounts/verify_credentials", async (_request, reply) => { + fastify.get('/v1/accounts/verify_credentials', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt @@ -221,7 +217,7 @@ export class MastodonApiServerService { } }); - fastify.patch("/v1/accounts/update_credentials", async (_request, reply) => { + fastify.patch('/v1/accounts/update_credentials', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt @@ -235,7 +231,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/accounts/lookup", async (_request, reply) => { + fastify.get('/v1/accounts/lookup', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt @@ -249,15 +245,15 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/accounts/relationships", async (_request, reply) => { + fastify.get('/v1/accounts/relationships', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt // displayed without being logged in let users; try { - let ids = _request.query ? (_request.query as any)["id[]"] : null; - if (typeof ids === "string") { + let ids = _request.query ? (_request.query as any)['id[]'] : null; + if (typeof ids === 'string') { ids = [ids]; } users = ids; @@ -272,7 +268,7 @@ export class MastodonApiServerService { } }); - fastify.get<{ Params: { id: string } }>("/v1/accounts/:id", async (_request, reply) => { + fastify.get<{ Params: { id: string } }>('/v1/accounts/:id', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -287,7 +283,7 @@ export class MastodonApiServerService { } }); - fastify.get<{ Params: { id: string } }>("/v1/accounts/:id/statuses", async (_request, reply) => { + fastify.get<{ Params: { id: string } }>('/v1/accounts/:id/statuses', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -301,7 +297,7 @@ export class MastodonApiServerService { } }); - fastify.get<{ Params: { id: string } }>("/v1/accounts/:id/featured_tags", async (_request, reply) => { + fastify.get<{ Params: { id: string } }>('/v1/accounts/:id/featured_tags', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -315,7 +311,7 @@ export class MastodonApiServerService { } }); - fastify.get<{ Params: { id: string } }>("/v1/accounts/:id/followers", async (_request, reply) => { + fastify.get<{ Params: { id: string } }>('/v1/accounts/:id/followers', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -329,7 +325,7 @@ export class MastodonApiServerService { } }); - fastify.get<{ Params: { id: string } }>("/v1/accounts/:id/following", async (_request, reply) => { + fastify.get<{ Params: { id: string } }>('/v1/accounts/:id/following', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -343,7 +339,7 @@ export class MastodonApiServerService { } }); - fastify.get<{ Params: { id: string } }>("/v1/accounts/:id/lists", async (_request, reply) => { + fastify.get<{ Params: { id: string } }>('/v1/accounts/:id/lists', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -357,7 +353,7 @@ export class MastodonApiServerService { } }); - fastify.post<{ Params: { id: string } }>("/v1/accounts/:id/follow", async (_request, reply) => { + fastify.post<{ Params: { id: string } }>('/v1/accounts/:id/follow', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -371,7 +367,7 @@ export class MastodonApiServerService { } }); - fastify.post<{ Params: { id: string } }>("/v1/accounts/:id/unfollow", async (_request, reply) => { + fastify.post<{ Params: { id: string } }>('/v1/accounts/:id/unfollow', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -385,7 +381,7 @@ export class MastodonApiServerService { } }); - fastify.post<{ Params: { id: string } }>("/v1/accounts/:id/block", async (_request, reply) => { + fastify.post<{ Params: { id: string } }>('/v1/accounts/:id/block', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -399,7 +395,7 @@ export class MastodonApiServerService { } }); - fastify.post<{ Params: { id: string } }>("/v1/accounts/:id/unblock", async (_request, reply) => { + fastify.post<{ Params: { id: string } }>('/v1/accounts/:id/unblock', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -413,7 +409,7 @@ export class MastodonApiServerService { } }); - fastify.post<{ Params: { id: string } }>("/v1/accounts/:id/mute", async (_request, reply) => { + fastify.post<{ Params: { id: string } }>('/v1/accounts/:id/mute', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -427,7 +423,7 @@ export class MastodonApiServerService { } }); - fastify.post<{ Params: { id: string } }>("/v1/accounts/:id/unmute", async (_request, reply) => { + fastify.post<{ Params: { id: string } }>('/v1/accounts/:id/unmute', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -441,7 +437,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/followed_tags", async (_request, reply) => { + fastify.get('/v1/followed_tags', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -455,7 +451,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/bookmarks", async (_request, reply) => { + fastify.get('/v1/bookmarks', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -469,7 +465,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/favourites", async (_request, reply) => { + fastify.get('/v1/favourites', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -483,7 +479,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/mutes", async (_request, reply) => { + fastify.get('/v1/mutes', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -497,7 +493,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/blocks", async (_request, reply) => { + fastify.get('/v1/blocks', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -511,7 +507,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/follow_requests", async (_request, reply) => { + fastify.get('/v1/follow_requests', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -525,7 +521,7 @@ export class MastodonApiServerService { } }); - fastify.post<{ Params: { id: string } }>("/v1/follow_requests/:id/authorize", async (_request, reply) => { + fastify.post<{ Params: { id: string } }>('/v1/follow_requests/:id/authorize', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -539,7 +535,7 @@ export class MastodonApiServerService { } }); - fastify.post<{ Params: { id: string } }>("/v1/follow_requests/:id/reject", async (_request, reply) => { + fastify.post<{ Params: { id: string } }>('/v1/follow_requests/:id/reject', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -555,7 +551,7 @@ export class MastodonApiServerService { //#endregion //#region Search - fastify.get("/v1/search", async (_request, reply) => { + fastify.get('/v1/search', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -569,7 +565,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v2/search", async (_request, reply) => { + fastify.get('/v2/search', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -583,7 +579,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v1/trends/statuses", async (_request, reply) => { + fastify.get('/v1/trends/statuses', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -597,7 +593,7 @@ export class MastodonApiServerService { } }); - fastify.get("/v2/suggestions", async (_request, reply) => { + fastify.get('/v2/suggestions', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -613,7 +609,7 @@ export class MastodonApiServerService { //#endregion //#region Notifications - fastify.get("/v1/notifications", async (_request, reply) => { + fastify.get('/v1/notifications', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -627,7 +623,7 @@ export class MastodonApiServerService { } }); - fastify.get<{ Params: { id: string } }>("/v1/notification/:id", async (_request, reply) => { + fastify.get<{ Params: { id: string } }>('/v1/notification/:id', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -641,7 +637,7 @@ export class MastodonApiServerService { } }); - fastify.post<{ Params: { id: string } }>("/v1/notification/:id/dismiss", async (_request, reply) => { + fastify.post<{ Params: { id: string } }>('/v1/notification/:id/dismiss', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -655,7 +651,7 @@ export class MastodonApiServerService { } }); - fastify.post("/v1/notifications/clear", async (_request, reply) => { + fastify.post('/v1/notifications/clear', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -671,7 +667,7 @@ export class MastodonApiServerService { //#endregion //#region Filters - fastify.get<{ Params: { id: string } }>("/v1/filters/:id", async (_request, reply) => { + fastify.get<{ Params: { id: string } }>('/v1/filters/:id', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -685,7 +681,7 @@ export class MastodonApiServerService { } }); - fastify.post("/v1/filters", async (_request, reply) => { + fastify.post('/v1/filters', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -699,7 +695,7 @@ export class MastodonApiServerService { } }); - fastify.post<{ Params: { id: string } }>("/v1/filters/:id", async (_request, reply) => { + fastify.post<{ Params: { id: string } }>('/v1/filters/:id', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -713,7 +709,7 @@ export class MastodonApiServerService { } }); - fastify.delete<{ Params: { id: string } }>("/v1/filters/:id", async (_request, reply) => { + fastify.delete<{ Params: { id: string } }>('/v1/filters/:id', async (_request, reply) => { const BASE_URL = `${_request.protocol}://${_request.hostname}`; const accessTokens = _request.headers.authorization; const client = getClient(BASE_URL, accessTokens); diff --git a/packages/backend/src/server/api/mastodon/converters.ts b/packages/backend/src/server/api/mastodon/converters.ts index e6244455e..5d77a3e3f 100644 --- a/packages/backend/src/server/api/mastodon/converters.ts +++ b/packages/backend/src/server/api/mastodon/converters.ts @@ -1,6 +1,6 @@ -import { Entity } from "megalodon"; +import { Entity } from 'megalodon'; -const CHAR_COLLECTION: string = "0123456789abcdefghijklmnopqrstuvwxyz"; +const CHAR_COLLECTION: string = '0123456789abcdefghijklmnopqrstuvwxyz'; export enum IdConvertType { MastodonId, @@ -26,7 +26,7 @@ export function convertId(in_id: string, id_convert_type: IdConvertType): string outStr = charFromNum(remainder) + outStr; input /= BigInt(36); } - let ReversedoutStr = outStr.split("").reduce((acc, char) => char + acc, ""); + let ReversedoutStr = outStr.split('').reduce((acc, char) => char + acc, ''); return ReversedoutStr; default: diff --git a/packages/backend/src/server/api/mastodon/endpoints.ts b/packages/backend/src/server/api/mastodon/endpoints.ts new file mode 100644 index 000000000..d5528ade5 --- /dev/null +++ b/packages/backend/src/server/api/mastodon/endpoints.ts @@ -0,0 +1,13 @@ +import { apiAuthMastodon } from './endpoints/auth.js'; +import { apiAccountMastodon } from './endpoints/account.js'; +import { apiSearchMastodon } from './endpoints/search.js'; +import { apiNotifyMastodon } from './endpoints/notifications.js'; +import { apiFilterMastodon } from './endpoints/filter.js'; + +export { + apiAccountMastodon, + apiAuthMastodon, + apiSearchMastodon, + apiNotifyMastodon, + apiFilterMastodon +} diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index a9580aeed..21e1fa9fc 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -1,11 +1,10 @@ -import { FindOptionsWhere, IsNull } from "typeorm"; -import type { MegalodonInterface } from "megalodon"; +import type { MegalodonInterface } from 'megalodon'; import type { FastifyRequest } from 'fastify'; -import { argsToBools, convertTimelinesArgsId, limitToInt } from "./timeline.js"; -import { convertId, IdConvertType as IdType, convertAccount, convertFeaturedTag, convertList, convertRelationship, convertStatus } from '../converters.js'; +import { argsToBools, convertTimelinesArgsId, limitToInt } from './timeline.js'; +import { convertId, IdConvertType as IdType, convertAccount, convertRelationship, convertStatus } from '../converters.js'; const relationshipModel = { - id: "", + id: '', following: false, followed_by: false, delivery_following: false, @@ -18,7 +17,7 @@ const relationshipModel = { showing_reblogs: false, endorsed: false, notifying: false, - note: "", + note: '', }; export class apiAccountMastodon { @@ -39,16 +38,16 @@ export class apiAccountMastodon { acct.id = convertId(acct.id, IdType.MastodonId); acct.display_name = acct.display_name || acct.username; acct.url = `${this.BASE_URL}/@${acct.url}`; - acct.note = acct.note || ""; + acct.note = acct.note || ''; acct.avatar_static = acct.avatar; - acct.header = acct.header || "/static-assets/transparent.png"; - acct.header_static = acct.header || "/static-assets/transparent.png"; + acct.header = acct.header || '/static-assets/transparent.png'; + acct.header_static = acct.header || '/static-assets/transparent.png'; acct.source = { note: acct.note, fields: acct.fields, - privacy: "", + privacy: '', sensitive: false, - language: "", + language: '', }; console.log(acct); return acct; @@ -72,7 +71,7 @@ export class apiAccountMastodon { public async lookup() { try { - const data = await this.client.search((this.request.query as any).acct, { type: "accounts" }); + const data = await this.client.search((this.request.query as any).acct, { type: 'accounts' }); return convertAccount(data.data.accounts[0]); } catch (e: any) { console.error(e); @@ -83,7 +82,7 @@ export class apiAccountMastodon { public async getRelationships(users: [string]) { try { - relationshipModel.id = users?.toString() || "1"; + relationshipModel.id = users?.toString() || '1'; if (!users) { return [relationshipModel]; diff --git a/packages/backend/src/server/api/mastodon/endpoints/auth.ts b/packages/backend/src/server/api/mastodon/endpoints/auth.ts index 48b5ec55c..27664ae1f 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/auth.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/auth.ts @@ -1,49 +1,49 @@ -import type { MegalodonInterface } from "megalodon"; +import type { MegalodonInterface } from 'megalodon'; import type { FastifyRequest } from 'fastify'; const readScope = [ - "read:account", - "read:drive", - "read:blocks", - "read:favorites", - "read:following", - "read:messaging", - "read:mutes", - "read:notifications", - "read:reactions", - "read:pages", - "read:page-likes", - "read:user-groups", - "read:channels", - "read:gallery", - "read:gallery-likes", + 'read:account', + 'read:drive', + 'read:blocks', + 'read:favorites', + 'read:following', + 'read:messaging', + 'read:mutes', + 'read:notifications', + 'read:reactions', + 'read:pages', + 'read:page-likes', + 'read:user-groups', + 'read:channels', + 'read:gallery', + 'read:gallery-likes', ]; const writeScope = [ - "write:account", - "write:drive", - "write:blocks", - "write:favorites", - "write:following", - "write:messaging", - "write:mutes", - "write:notes", - "write:notifications", - "write:reactions", - "write:votes", - "write:pages", - "write:page-likes", - "write:user-groups", - "write:channels", - "write:gallery", - "write:gallery-likes", + 'write:account', + 'write:drive', + 'write:blocks', + 'write:favorites', + 'write:following', + 'write:messaging', + 'write:mutes', + 'write:notes', + 'write:notifications', + 'write:reactions', + 'write:votes', + 'write:pages', + 'write:page-likes', + 'write:user-groups', + 'write:channels', + 'write:gallery', + 'write:gallery-likes', ]; export async function apiAuthMastodon(request: FastifyRequest, client: MegalodonInterface) { const body: any = request.body || request.query; try { let scope = body.scopes; - if (typeof scope === "string") scope = scope.split(" "); + if (typeof scope === 'string') scope = scope.split(' '); const pushScope = new Set(); for (const s of scope) { if (s.match(/^read/)) for (const r of readScope) pushScope.add(r); @@ -62,7 +62,7 @@ export async function apiAuthMastodon(request: FastifyRequest, client: Megalodon name: appData.name, website: body.website, redirect_uri: red, - client_id: Buffer.from(appData.url || "").toString("base64"), + client_id: Buffer.from(appData.url || '').toString('base64'), client_secret: appData.clientSecret, }; diff --git a/packages/backend/src/server/api/mastodon/endpoints/filter.ts b/packages/backend/src/server/api/mastodon/endpoints/filter.ts index 221090f28..175f2d519 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/filter.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/filter.ts @@ -1,6 +1,5 @@ -import type { MegalodonInterface } from "megalodon"; +import type { MegalodonInterface } from 'megalodon'; import type { FastifyRequest } from 'fastify'; -import { convertTimelinesArgsId } from "./timeline.js"; import { IdConvertType as IdType, convertId, convertFilter } from '../converters.js'; export class apiFilterMastodon { diff --git a/packages/backend/src/server/api/mastodon/endpoints/meta.ts b/packages/backend/src/server/api/mastodon/endpoints/meta.ts index a37742a06..28ea6c08f 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/meta.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/meta.ts @@ -1,7 +1,7 @@ -import { Entity } from "megalodon"; -import { MAX_NOTE_TEXT_LENGTH, FILE_TYPE_BROWSERSAFE } from "@/const.js"; +import { Entity } from 'megalodon'; +import { MAX_NOTE_TEXT_LENGTH, FILE_TYPE_BROWSERSAFE } from '@/const.js'; import type { Config } from '@/config.js'; -import type { MiMeta } from "@/models/Meta.js"; +import type { MiMeta } from '@/models/Meta.js'; export async function getInstance( response: Entity.Instance, @@ -11,13 +11,13 @@ export async function getInstance( ) { return { uri: config.url, - title: meta.name || "Sharkey", + title: meta.name || 'Sharkey', short_description: - meta.description?.substring(0, 50) || "See real server website", + meta.description?.substring(0, 50) || 'See real server website', description: meta.description || "This is a vanilla Sharkey Instance. It doesn't seem to have a description.", - email: response.email || "", + email: response.email || '', version: `3.0.0 (compatible; Sharkey ${config.version})`, urls: response.urls, stats: { @@ -25,7 +25,7 @@ export async function getInstance( status_count: response.stats.status_count, domain_count: response.stats.domain_count, }, - thumbnail: meta.backgroundImageUrl || "/static-assets/transparent.png", + thumbnail: meta.backgroundImageUrl || '/static-assets/transparent.png', languages: meta.langs, registrations: !meta.disableRegistration || response.registrations, approval_required: !response.registrations, diff --git a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts b/packages/backend/src/server/api/mastodon/endpoints/notifications.ts index 4e8c314a5..667379e8b 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/notifications.ts @@ -1,10 +1,10 @@ -import type { MegalodonInterface } from "megalodon"; +import type { MegalodonInterface } from 'megalodon'; import type { FastifyRequest } from 'fastify'; -import { convertTimelinesArgsId } from "./timeline.js"; +import { convertTimelinesArgsId } from './timeline.js'; import { IdConvertType as IdType, convertId, convertNotification } from '../converters.js'; function toLimitToInt(q: any) { - if (q.limit) if (typeof q.limit === "string") q.limit = parseInt(q.limit, 10); + if (q.limit) if (typeof q.limit === 'string') q.limit = parseInt(q.limit, 10); return q; } @@ -23,8 +23,8 @@ export class apiNotifyMastodon { const notifs = data.data; const processed = notifs.map((n) => { n = convertNotification(n); - if (n.type !== "follow" && n.type !== "follow_request") { - if (n.type === "reaction") n.type = "favourite"; + if (n.type !== 'follow' && n.type !== 'follow_request') { + if (n.type === 'reaction') n.type = 'favourite'; return n; } else { return n; @@ -41,7 +41,7 @@ export class apiNotifyMastodon { try { const data = await this.client.getNotification( convertId((this.request.params as any).id, IdType.SharkeyId) ); const notif = convertNotification(data.data); - if (notif.type !== "follow" && notif.type !== "follow_request" && notif.type === "reaction") notif.type = "favourite"; + if (notif.type !== 'follow' && notif.type !== 'follow_request' && notif.type === 'reaction') notif.type = 'favourite'; return notif; } catch (e: any) { console.error(e); diff --git a/packages/backend/src/server/api/mastodon/endpoints/search.ts b/packages/backend/src/server/api/mastodon/endpoints/search.ts index d55831640..6b36582b9 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/search.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/search.ts @@ -1,7 +1,7 @@ -import type { MegalodonInterface } from "megalodon"; -import { Converter } from "megalodon"; +import type { MegalodonInterface } from 'megalodon'; +import { Converter } from 'megalodon'; import type { FastifyRequest } from 'fastify'; -import { convertTimelinesArgsId, limitToInt } from "./timeline.js"; +import { convertTimelinesArgsId, limitToInt } from './timeline.js'; import { convertAccount, convertStatus } from '../converters.js'; async function getHighlight( diff --git a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts index 4d4cb2c41..1d2812c37 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts @@ -1,19 +1,19 @@ import { convertId, IdConvertType as IdType, convertAccount, convertConversation, convertList, convertStatus } from '../converters.js'; -import { ParsedUrlQuery } from "querystring"; +import { ParsedUrlQuery } from 'querystring'; export function limitToInt(q: ParsedUrlQuery) { let object: any = q; if (q.limit) - if (typeof q.limit === "string") object.limit = parseInt(q.limit, 10); + if (typeof q.limit === 'string') object.limit = parseInt(q.limit, 10); if (q.offset) - if (typeof q.offset === "string") object.offset = parseInt(q.offset, 10); + if (typeof q.offset === 'string') object.offset = parseInt(q.offset, 10); return object; } export function argsToBools(q: ParsedUrlQuery) { // Values taken from https://docs.joinmastodon.org/client/intro/#boolean const toBoolean = (value: string) => - !["0", "f", "F", "false", "FALSE", "off", "OFF"].includes(value); + !['0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].includes(value); // Keys taken from: // - https://docs.joinmastodon.org/methods/accounts/#statuses @@ -21,27 +21,48 @@ export function argsToBools(q: ParsedUrlQuery) { // - https://docs.joinmastodon.org/methods/timelines/#tag let object: any = q; if (q.only_media) - if (typeof q.only_media === "string") + if (typeof q.only_media === 'string') object.only_media = toBoolean(q.only_media); if (q.exclude_replies) - if (typeof q.exclude_replies === "string") + if (typeof q.exclude_replies === 'string') object.exclude_replies = toBoolean(q.exclude_replies); if (q.exclude_reblogs) - if (typeof q.exclude_reblogs === "string") + if (typeof q.exclude_reblogs === 'string') object.exclude_reblogs = toBoolean(q.exclude_reblogs); if (q.pinned) - if (typeof q.pinned === "string") object.pinned = toBoolean(q.pinned); + if (typeof q.pinned === 'string') object.pinned = toBoolean(q.pinned); if (q.local) - if (typeof q.local === "string") object.local = toBoolean(q.local); + if (typeof q.local === 'string') object.local = toBoolean(q.local); return q; } export function convertTimelinesArgsId(q: ParsedUrlQuery) { - if (typeof q.min_id === "string") + if (typeof q.min_id === 'string') q.min_id = convertId(q.min_id, IdType.SharkeyId); - if (typeof q.max_id === "string") + if (typeof q.max_id === 'string') q.max_id = convertId(q.max_id, IdType.SharkeyId); - if (typeof q.since_id === "string") + if (typeof q.since_id === 'string') q.since_id = convertId(q.since_id, IdType.SharkeyId); return q; +} + +function escapeHTML(str: string) { + if (!str) { + return ''; + } + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/'/g, '"') + .replace(/'/g, '''); +} + +function nl2br(str: string) { + if (!str) { + return ''; + } + str = str.replace(/\r\n/g, '
'); + str = str.replace(/(\n|\r)/g, '
'); + return str; } \ No newline at end of file