mirror of
https://iceshrimp.dev/iceshrimp/iceshrimp
synced 2024-12-01 00:08:10 +09:00
[mastodon-client] Refactor api router init
This commit is contained in:
parent
758f995dac
commit
366311a8b1
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
78
packages/backend/src/server/api/mastodon/endpoints/media.ts
Normal file
78
packages/backend/src/server/api/mastodon/endpoints/media.ts
Normal 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;
|
||||
}
|
||||
});
|
||||
}
|
@ -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;
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
54
packages/backend/src/server/api/mastodon/index.ts
Normal file
54
packages/backend/src/server/api/mastodon/index.ts
Normal 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);
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user