[mastodon-client] Refactor api router init

This commit is contained in:
Laura Hausmann 2023-09-29 21:45:58 +02:00
parent 758f995dac
commit 366311a8b1
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
12 changed files with 155 additions and 143 deletions

View file

@ -7,13 +7,9 @@ import Router from "@koa/router";
import multer from "@koa/multer";
import bodyParser from "koa-bodyparser";
import cors from "@koa/cors";
import {
apiMastodonCompatible,
getClient,
} from "./mastodon/ApiMastodonCompatibleService.js";
import { Instances, AccessTokens, Users } from "@/models/index.js";
import { setupMastodonApi } from "./mastodon/index.js";
import { AccessTokens, Users } from "@/models/index.js";
import config from "@/config/index.js";
import fs from "fs";
import endpoints from "./endpoints.js";
import compatibility from "./compatibility.js";
import handler from "./api-handler.js";
@ -24,9 +20,7 @@ import verifyEmail from "./private/verify-email.js";
import discord from "./service/discord.js";
import github from "./service/github.js";
import twitter from "./service/twitter.js";
import { koaBody } from "koa-body";
import { convertId, IdType } from "@/misc/convert-id.js";
import { convertAttachment } from "./mastodon/converters.js";
// re-export native rust id conversion (function and enum)
export { IdType, convertId };
@ -72,64 +66,7 @@ router.use(
}),
);
mastoRouter.use(
koaBody({
multipart: true,
urlencoded: true,
}),
);
mastoFileRouter.post("/v1/media", upload.single("file"), async (ctx) => {
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
const accessTokens = ctx.headers.authorization;
const client = getClient(BASE_URL, accessTokens);
try {
const multipartData = await ctx.file;
if (!multipartData) {
ctx.body = { error: "No image" };
ctx.status = 401;
return;
}
const data = await client.uploadMedia(multipartData);
ctx.body = convertAttachment(data.data as MastodonEntity.Attachment);
} catch (e: any) {
console.error(e);
ctx.status = 401;
ctx.body = e.response.data;
}
});
mastoFileRouter.post("/v2/media", upload.single("file"), async (ctx) => {
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
const accessTokens = ctx.headers.authorization;
const client = getClient(BASE_URL, accessTokens);
try {
const multipartData = await ctx.file;
if (!multipartData) {
ctx.body = { error: "No image" };
ctx.status = 401;
return;
}
const data = await client.uploadMedia(multipartData, ctx.request.body);
ctx.body = convertAttachment(data.data as MastodonEntity.Attachment);
} catch (e: any) {
console.error(e);
ctx.status = 401;
ctx.body = e.response.data;
}
});
mastoRouter.use(async (ctx, next) => {
if (ctx.request.query) {
if (!ctx.request.body || Object.keys(ctx.request.body).length === 0) {
ctx.request.body = ctx.request.query;
} else {
ctx.request.body = { ...ctx.request.body, ...ctx.request.query };
}
}
await next();
});
apiMastodonCompatible(mastoRouter);
setupMastodonApi(mastoRouter, mastoFileRouter, upload);
/**
* Register endpoint handlers

View file

@ -1,5 +1,5 @@
import Router from "@koa/router";
import { getClient } from "../ApiMastodonCompatibleService.js";
import { getClient } from "../index.js";
import { argsToBools, convertPaginationArgsIds, limitToInt, normalizeUrlQuery } from "./timeline.js";
import { convertId, IdType } from "../../index.js";
import { convertAccount, convertFeaturedTag, convertList, convertRelationship, convertStatus, } from "../converters.js";
@ -10,7 +10,7 @@ import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
export function apiAccountMastodon(router: Router): void {
export function setupEndpointsAccount(router: Router): void {
router.get("/v1/accounts/verify_credentials", async (ctx) => {
try {
const auth = await authenticate(ctx.headers.authorization, null);

View file

@ -39,7 +39,7 @@ const writeScope = [
"write:gallery-likes",
];
export function apiAuthMastodon(router: Router): void {
export function setupEndpointsAuth(router: Router): void {
router.post("/v1/apps", async (ctx) => {
const body: any = ctx.request.body || ctx.request.query;
try {

View file

@ -1,10 +1,10 @@
import megalodon, { MegalodonInterface } from "megalodon";
import Router from "@koa/router";
import { getClient } from "../ApiMastodonCompatibleService.js";
import { getClient } from "../index.js";
import { IdType, convertId } from "../../index.js";
import { convertFilter } from "../converters.js";
export function apiFilterMastodon(router: Router): void {
export function setupEndpointsFilter(router: Router): void {
router.get("/v1/filters", async (ctx) => {
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
const accessTokens = ctx.request.headers.authorization;

View file

@ -0,0 +1,78 @@
import Router from "@koa/router";
import { getClient } from "@/server/api/mastodon/index.js";
import { convertId, IdType } from "@/misc/convert-id.js";
import { convertAttachment } from "@/server/api/mastodon/converters.js";
import multer from "@koa/multer";
export function setupEndpointsMedia(router: Router, fileRouter: Router, upload: multer.Instance): void {
router.get<{ Params: { id: string } }>("/v1/media/:id", async (ctx) => {
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
const accessTokens = ctx.headers.authorization;
const client = getClient(BASE_URL, accessTokens);
try {
const data = await client.getMedia(
convertId(ctx.params.id, IdType.IceshrimpId),
);
ctx.body = convertAttachment(data.data);
} catch (e: any) {
console.error(e);
ctx.status = 401;
ctx.body = e.response.data;
}
});
router.put<{ Params: { id: string } }>("/v1/media/:id", async (ctx) => {
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
const accessTokens = ctx.headers.authorization;
const client = getClient(BASE_URL, accessTokens);
try {
const data = await client.updateMedia(
convertId(ctx.params.id, IdType.IceshrimpId),
ctx.request.body as any,
);
ctx.body = convertAttachment(data.data);
} catch (e: any) {
console.error(e);
ctx.status = 401;
ctx.body = e.response.data;
}
});
fileRouter.post("/v1/media", upload.single("file"), async (ctx) => {
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
const accessTokens = ctx.headers.authorization;
const client = getClient(BASE_URL, accessTokens);
try {
const multipartData = await ctx.file;
if (!multipartData) {
ctx.body = { error: "No image" };
ctx.status = 401;
return;
}
const data = await client.uploadMedia(multipartData);
ctx.body = convertAttachment(data.data as MastodonEntity.Attachment);
} catch (e: any) {
console.error(e);
ctx.status = 401;
ctx.body = e.response.data;
}
});
fileRouter.post("/v2/media", upload.single("file"), async (ctx) => {
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
const accessTokens = ctx.headers.authorization;
const client = getClient(BASE_URL, accessTokens);
try {
const multipartData = await ctx.file;
if (!multipartData) {
ctx.body = { error: "No image" };
ctx.status = 401;
return;
}
const data = await client.uploadMedia(multipartData, ctx.request.body);
ctx.body = convertAttachment(data.data as MastodonEntity.Attachment);
} catch (e: any) {
console.error(e);
ctx.status = 401;
ctx.body = e.response.data;
}
});
}

View file

@ -1,42 +1,17 @@
import Router from "@koa/router";
import megalodon, { MegalodonInterface } from "megalodon";
import { apiAuthMastodon } from "./endpoints/auth.js";
import { apiAccountMastodon } from "./endpoints/account.js";
import { apiStatusMastodon } from "./endpoints/status.js";
import { apiFilterMastodon } from "./endpoints/filter.js";
import { apiTimelineMastodon } from "./endpoints/timeline.js";
import { apiNotificationsMastodon } from "./endpoints/notifications.js";
import { apiSearchMastodon } from "./endpoints/search.js";
import { getInstance } from "./endpoints/meta.js";
import { getClient } from "@/server/api/mastodon/index.js";
import { convertId, IdType } from "@/misc/convert-id.js";
import {
convertAccount,
convertAnnouncement,
convertFilter,
} from "./converters.js";
import { convertId, IdType } from "../index.js";
convertAttachment,
convertFilter
} from "@/server/api/mastodon/converters.js";
import { Users } from "@/models/index.js";
import { getInstance } from "@/server/api/mastodon/endpoints/meta.js";
import { IsNull } from "typeorm";
export function getClient(
BASE_URL: string,
authorization: string | undefined,
): MegalodonInterface {
const accessTokenArr = authorization?.split(" ") ?? [null];
const accessToken = accessTokenArr[accessTokenArr.length - 1];
const generator = (megalodon as any).default;
const client = generator(BASE_URL, accessToken) as MegalodonInterface;
return client;
}
export function apiMastodonCompatible(router: Router): void {
apiAuthMastodon(router);
apiAccountMastodon(router);
apiStatusMastodon(router);
apiFilterMastodon(router);
apiTimelineMastodon(router);
apiNotificationsMastodon(router);
apiSearchMastodon(router);
export function setupEndpointsMisc(router: Router): void {
router.get("/v1/custom_emojis", async (ctx) => {
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
const accessTokens = ctx.request.headers.authorization;

View file

@ -7,7 +7,7 @@ import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
import { NotificationHelpers } from "@/server/api/mastodon/helpers/notification.js";
import { NotificationConverter } from "@/server/api/mastodon/converters/notification.js";
export function apiNotificationsMastodon(router: Router): void {
export function setupEndpointsNotifications(router: Router): void {
router.get("/v1/notifications", async (ctx) => {
try {
const auth = await authenticate(ctx.headers.authorization, null);

View file

@ -1,12 +1,12 @@
import megalodon, { MegalodonInterface } from "megalodon";
import Router from "@koa/router";
import { getClient } from "../ApiMastodonCompatibleService.js";
import { getClient } from "../index.js";
import axios from "axios";
import { Converter } from "megalodon";
import { convertPaginationArgsIds, limitToInt } from "./timeline.js";
import { convertAccount, convertStatus } from "../converters.js";
export function apiSearchMastodon(router: Router): void {
export function setupEndpointsSearch(router: Router): void {
router.get("/v1/search", async (ctx) => {
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
const accessTokens = ctx.request.headers.authorization;

View file

@ -1,5 +1,5 @@
import Router from "@koa/router";
import { getClient } from "../ApiMastodonCompatibleService.js";
import { getClient } from "../index.js";
import { emojiRegexAtStartToEnd } from "@/misc/emoji-regex.js";
import querystring from "node:querystring";
import qs from "qs";
@ -19,7 +19,7 @@ function normalizeQuery(data: any) {
return qs.parse(str);
}
export function apiStatusMastodon(router: Router): void {
export function setupEndpointsStatus(router: Router): void {
router.post("/v1/statuses", async (ctx) => {
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
const accessTokens = ctx.headers.authorization;
@ -643,38 +643,6 @@ export function apiStatusMastodon(router: Router): void {
}
},
);
router.get<{ Params: { id: string } }>("/v1/media/:id", async (ctx) => {
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
const accessTokens = ctx.headers.authorization;
const client = getClient(BASE_URL, accessTokens);
try {
const data = await client.getMedia(
convertId(ctx.params.id, IdType.IceshrimpId),
);
ctx.body = convertAttachment(data.data);
} catch (e: any) {
console.error(e);
ctx.status = 401;
ctx.body = e.response.data;
}
});
router.put<{ Params: { id: string } }>("/v1/media/:id", async (ctx) => {
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
const accessTokens = ctx.headers.authorization;
const client = getClient(BASE_URL, accessTokens);
try {
const data = await client.updateMedia(
convertId(ctx.params.id, IdType.IceshrimpId),
ctx.request.body as any,
);
ctx.body = convertAttachment(data.data);
} catch (e: any) {
console.error(e);
ctx.status = 401;
ctx.body = e.response.data;
}
});
router.get<{ Params: { id: string } }>("/v1/polls/:id", async (ctx) => {
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
const accessTokens = ctx.headers.authorization;

View file

@ -1,5 +1,5 @@
import Router from "@koa/router";
import { getClient } from "../ApiMastodonCompatibleService.js";
import { getClient } from "../index.js";
import { ParsedUrlQuery } from "querystring";
import {
convertAccount,
@ -66,7 +66,7 @@ export function normalizeUrlQuery(q: ParsedUrlQuery, arrayKeys: string[] = []):
return dict;
}
export function apiTimelineMastodon(router: Router): void {
export function setupEndpointsTimeline(router: Router): void {
router.get("/v1/timelines/public", async (ctx, reply) => {
try {
const auth = await authenticate(ctx.headers.authorization, null);

View file

@ -0,0 +1,54 @@
import Router from "@koa/router";
import megalodon, { MegalodonInterface } from "megalodon";
import { setupEndpointsAuth } from "./endpoints/auth.js";
import { setupEndpointsAccount } from "./endpoints/account.js";
import { setupEndpointsStatus } from "./endpoints/status.js";
import { setupEndpointsFilter } from "./endpoints/filter.js";
import { setupEndpointsTimeline } from "./endpoints/timeline.js";
import { setupEndpointsNotifications } from "./endpoints/notifications.js";
import { setupEndpointsSearch } from "./endpoints/search.js";
import { setupEndpointsMedia } from "@/server/api/mastodon/endpoints/media.js";
import { setupEndpointsMisc } from "@/server/api/mastodon/endpoints/misc.js";
import { koaBody } from "koa-body";
import multer from "@koa/multer";
export function getClient(
BASE_URL: string,
authorization: string | undefined,
): MegalodonInterface {
const accessTokenArr = authorization?.split(" ") ?? [null];
const accessToken = accessTokenArr[accessTokenArr.length - 1];
const generator = (megalodon as any).default;
const client = generator(BASE_URL, accessToken) as MegalodonInterface;
return client;
}
export function setupMastodonApi(router: Router, fileRouter: Router, upload: multer.Instance): void {
router.use(
koaBody({
multipart: true,
urlencoded: true,
}),
);
router.use(async (ctx, next) => {
if (ctx.request.query) {
if (!ctx.request.body || Object.keys(ctx.request.body).length === 0) {
ctx.request.body = ctx.request.query;
} else {
ctx.request.body = { ...ctx.request.body, ...ctx.request.query };
}
}
await next();
});
setupEndpointsAuth(router);
setupEndpointsAccount(router);
setupEndpointsStatus(router);
setupEndpointsFilter(router);
setupEndpointsTimeline(router);
setupEndpointsNotifications(router);
setupEndpointsSearch(router);
setupEndpointsMedia(router, fileRouter, upload);
setupEndpointsMisc(router);
}

View file

@ -26,7 +26,7 @@ import channels from "./channels/index.js";
import type Channel from "./channel.js";
import type { StreamEventEmitter, StreamMessages } from "./types.js";
import { Converter } from "megalodon";
import { getClient } from "../mastodon/ApiMastodonCompatibleService.js";
import { getClient } from "../mastodon/index.js";
/**
* Main stream connection