From acfc88389a3351fc95c5b1ef837241b51fa7c9c1 Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Thu, 30 Mar 2023 19:10:03 -0700 Subject: [PATCH] formatting --- .../koa-remove-trailing-slashes/index.d.ts | 2 +- packages/backend/src/db/sonic.ts | 52 +++-- packages/backend/src/misc/post.ts | 7 +- packages/backend/src/queue/index.ts | 12 +- .../processors/background/index-all-notes.ts | 33 ++-- .../src/queue/processors/background/index.ts | 5 +- .../src/queue/processors/db/import-posts.ts | 4 +- .../src/queue/processors/webhook-deliver.ts | 2 +- .../src/remote/activitypub/models/person.ts | 54 ++++-- .../api/endpoints/drive/files/update.ts | 6 +- .../backend/src/server/api/endpoints/emoji.ts | 3 +- .../endpoints/messaging/messages/create.ts | 2 +- .../src/server/api/endpoints/notes/search.ts | 4 +- packages/backend/src/server/api/index.ts | 10 +- .../server/api/mastodon/endpoints/account.ts | 124 ++++++++---- .../src/server/api/mastodon/endpoints/auth.ts | 6 +- .../src/server/api/mastodon/endpoints/meta.ts | 15 +- .../server/api/mastodon/endpoints/search.ts | 12 +- .../server/api/mastodon/endpoints/status.ts | 182 +++++++++--------- .../server/api/mastodon/endpoints/timeline.ts | 58 +++--- .../backend/src/server/api/private/signin.ts | 6 +- .../backend/src/server/api/stream/index.ts | 2 +- packages/backend/src/server/index.ts | 28 +-- packages/client/src/router.ts | 8 +- packages/client/src/scripts/get-user-menu.ts | 4 +- packages/client/src/scripts/katex-macro.ts | 176 +++++++++-------- packages/client/src/scripts/preprocess.ts | 12 +- 27 files changed, 463 insertions(+), 366 deletions(-) diff --git a/packages/backend/src/@types/koa-remove-trailing-slashes/index.d.ts b/packages/backend/src/@types/koa-remove-trailing-slashes/index.d.ts index eda0cf142..429d1d53e 100644 --- a/packages/backend/src/@types/koa-remove-trailing-slashes/index.d.ts +++ b/packages/backend/src/@types/koa-remove-trailing-slashes/index.d.ts @@ -1 +1 @@ -declare module 'koa-remove-trailing-slashes'; +declare module "koa-remove-trailing-slashes"; diff --git a/packages/backend/src/db/sonic.ts b/packages/backend/src/db/sonic.ts index 6c4d28f70..590c47924 100644 --- a/packages/backend/src/db/sonic.ts +++ b/packages/backend/src/db/sonic.ts @@ -7,32 +7,26 @@ const logger = dbLogger.createSubLogger("sonic", "gray", false); logger.info("Connecting to Sonic"); -const handlers = (type: string): SonicChannel.Handlers => ( - { - connected: () => { - logger.succ(`Connected to Sonic ${type}`); - }, - disconnected: (error) => { - logger.warn(`Disconnected from Sonic ${type}, error: ${error}`); - }, - error: (error) => { - logger.warn(`Sonic ${type} error: ${error}`); - }, - retrying: () => { - logger.info(`Sonic ${type} retrying`); - }, - timeout: () => { - logger.warn(`Sonic ${type} timeout`); - }, - } -) +const handlers = (type: string): SonicChannel.Handlers => ({ + connected: () => { + logger.succ(`Connected to Sonic ${type}`); + }, + disconnected: (error) => { + logger.warn(`Disconnected from Sonic ${type}, error: ${error}`); + }, + error: (error) => { + logger.warn(`Sonic ${type} error: ${error}`); + }, + retrying: () => { + logger.info(`Sonic ${type} retrying`); + }, + timeout: () => { + logger.warn(`Sonic ${type} timeout`); + }, +}); const hasConfig = - config.sonic - && ( config.sonic.host - || config.sonic.port - || config.sonic.auth - ) + config.sonic && (config.sonic.host || config.sonic.port || config.sonic.auth); const host = hasConfig ? config.sonic.host ?? "localhost" : ""; const port = hasConfig ? config.sonic.port ?? 1491 : 0; @@ -42,10 +36,14 @@ const bucket = hasConfig ? config.sonic.bucket ?? "default" : ""; export default hasConfig ? { - search: new SonicChannel.Search({host, port, auth}).connect(handlers("search")), - ingest: new SonicChannel.Ingest({host, port, auth}).connect(handlers("ingest")), + search: new SonicChannel.Search({ host, port, auth }).connect( + handlers("search"), + ), + ingest: new SonicChannel.Ingest({ host, port, auth }).connect( + handlers("ingest"), + ), collection, bucket, - } + } : null; diff --git a/packages/backend/src/misc/post.ts b/packages/backend/src/misc/post.ts index e14aa3444..90f4f7528 100644 --- a/packages/backend/src/misc/post.ts +++ b/packages/backend/src/misc/post.ts @@ -6,7 +6,12 @@ export type Post = { }; export function parse(acct: any): Post { - return { text: acct.text, cw: acct.cw, localOnly: acct.localOnly, createdAt: new Date(acct.createdAt) }; + return { + text: acct.text, + cw: acct.cw, + localOnly: acct.localOnly, + createdAt: new Date(acct.createdAt), + }; } export function toJson(acct: Post): string { diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index 1327853d0..035556e48 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -440,14 +440,10 @@ export function createCleanRemoteFilesJob() { } export function createIndexAllNotesJob(data = {}) { - return backgroundQueue.add( - "indexAllNotes", - data, - { - removeOnComplete: true, - removeOnFail: true, - }, - ); + return backgroundQueue.add("indexAllNotes", data, { + removeOnComplete: true, + removeOnFail: true, + }); } export function webhookDeliver( diff --git a/packages/backend/src/queue/processors/background/index-all-notes.ts b/packages/backend/src/queue/processors/background/index-all-notes.ts index f03275886..03219199d 100644 --- a/packages/backend/src/queue/processors/background/index-all-notes.ts +++ b/packages/backend/src/queue/processors/background/index-all-notes.ts @@ -3,26 +3,30 @@ import type Bull from "bull"; import { queueLogger } from "../../logger.js"; import { Notes } from "@/models/index.js"; import { MoreThan } from "typeorm"; -import { index } from "@/services/note/create.js" +import { index } from "@/services/note/create.js"; import { Note } from "@/models/entities/note.js"; const logger = queueLogger.createSubLogger("index-all-notes"); export default async function indexAllNotes( job: Bull.Job>, - done: ()=>void, + done: () => void, ): Promise { logger.info("Indexing all notes..."); - let cursor: string|null = job.data.cursor as string ?? null; - let indexedCount: number = job.data.indexedCount as number ?? 0; - let total: number = job.data.total as number ?? 0; + let cursor: string | null = (job.data.cursor as string) ?? null; + let indexedCount: number = (job.data.indexedCount as number) ?? 0; + let total: number = (job.data.total as number) ?? 0; let running = true; const take = 50000; const batch = 100; while (running) { - logger.info(`Querying for ${take} notes ${indexedCount}/${total ? total : '?'} at ${cursor}`); + logger.info( + `Querying for ${take} notes ${indexedCount}/${ + total ? total : "?" + } at ${cursor}`, + ); let notes: Note[] = []; try { @@ -49,22 +53,21 @@ export default async function indexAllNotes( try { const count = await Notes.count(); total = count; - job.update({ indexedCount, cursor, total }) - } catch (e) { - } + job.update({ indexedCount, cursor, total }); + } catch (e) {} for (let i = 0; i < notes.length; i += batch) { const chunk = notes.slice(i, i + batch); - await Promise.all(chunk.map(note => index(note))); + await Promise.all(chunk.map((note) => index(note))); indexedCount += chunk.length; - const pct = (indexedCount / total)*100; - job.update({ indexedCount, cursor, total }) - job.progress(+(pct.toFixed(1))); - logger.info(`Indexed notes ${indexedCount}/${total ? total : '?'}`); + const pct = (indexedCount / total) * 100; + job.update({ indexedCount, cursor, total }); + job.progress(+pct.toFixed(1)); + logger.info(`Indexed notes ${indexedCount}/${total ? total : "?"}`); } cursor = notes[notes.length - 1].id; - job.update({ indexedCount, cursor, total }) + job.update({ indexedCount, cursor, total }); if (notes.length < take) { running = false; diff --git a/packages/backend/src/queue/processors/background/index.ts b/packages/backend/src/queue/processors/background/index.ts index cf96b67ef..6674f954b 100644 --- a/packages/backend/src/queue/processors/background/index.ts +++ b/packages/backend/src/queue/processors/background/index.ts @@ -3,10 +3,7 @@ import indexAllNotes from "./index-all-notes.js"; const jobs = { indexAllNotes, -} as Record< - string, - Bull.ProcessCallbackFunction> ->; +} as Record>>; export default function (q: Bull.Queue) { for (const [k, v] of Object.entries(jobs)) { diff --git a/packages/backend/src/queue/processors/db/import-posts.ts b/packages/backend/src/queue/processors/db/import-posts.ts index 561aeaa7a..20ef3a518 100644 --- a/packages/backend/src/queue/processors/db/import-posts.ts +++ b/packages/backend/src/queue/processors/db/import-posts.ts @@ -88,7 +88,7 @@ export async function importPosts( continue; } if (job.data.signatureCheck) { - if(!post.signature) { + if (!post.signature) { continue; } } @@ -99,7 +99,7 @@ export async function importPosts( continue; } logger.info(`Posting[${linenum}] ...`); - + const note = await create(user, { createdAt: new Date(post.object.published), files: undefined, diff --git a/packages/backend/src/queue/processors/webhook-deliver.ts b/packages/backend/src/queue/processors/webhook-deliver.ts index 2edf4f696..0a54ae7d8 100644 --- a/packages/backend/src/queue/processors/webhook-deliver.ts +++ b/packages/backend/src/queue/processors/webhook-deliver.ts @@ -20,7 +20,7 @@ export default async (job: Bull.Job) => { "X-Calckey-Host": config.host, "X-Calckey-Hook-Id": job.data.webhookId, "X-Calckey-Hook-Secret": job.data.secret, - 'Content-Type': 'application/json' + "Content-Type": "application/json", }, body: JSON.stringify({ hookId: job.data.webhookId, diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index f4f792df1..877f5f332 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -205,7 +205,9 @@ export async function createPerson( if (typeof person.followers === "string") { try { - let data = await fetch(person.followers, { headers: { "Accept": "application/json" } }); + let data = await fetch(person.followers, { + headers: { Accept: "application/json" }, + }); let json_data = JSON.parse(await data.text()); followersCount = json_data.totalItems; @@ -218,7 +220,9 @@ export async function createPerson( if (typeof person.following === "string") { try { - let data = await fetch(person.following, { headers: { "Accept": "application/json" } }); + let data = await fetch(person.following, { + headers: { Accept: "application/json" }, + }); let json_data = JSON.parse(await data.text()); followingCount = json_data.totalItems; @@ -227,7 +231,6 @@ export async function createPerson( } } - // Create user let user: IRemoteUser; try { @@ -255,14 +258,20 @@ export async function createPerson( followersUri: person.followers ? getApId(person.followers) : undefined, - followersCount: followersCount !== undefined - ? followersCount - : person.followers && typeof person.followers !== "string" && isCollectionOrOrderedCollection(person.followers) + followersCount: + followersCount !== undefined + ? followersCount + : person.followers && + typeof person.followers !== "string" && + isCollectionOrOrderedCollection(person.followers) ? person.followers.totalItems : undefined, - followingCount: followingCount !== undefined - ? followingCount - : person.following && typeof person.following !== "string" && isCollectionOrOrderedCollection(person.following) + followingCount: + followingCount !== undefined + ? followingCount + : person.following && + typeof person.following !== "string" && + isCollectionOrOrderedCollection(person.following) ? person.following.totalItems : undefined, featured: person.featured ? getApId(person.featured) : undefined, @@ -440,7 +449,9 @@ export async function updatePerson( if (typeof person.followers === "string") { try { - let data = await fetch(person.followers, { headers: { "Accept": "application/json" } } ); + let data = await fetch(person.followers, { + headers: { Accept: "application/json" }, + }); let json_data = JSON.parse(await data.text()); followersCount = json_data.totalItems; @@ -449,12 +460,13 @@ export async function updatePerson( } } - let followingCount: number | undefined; if (typeof person.following === "string") { try { - let data = await fetch(person.following, { headers: { "Accept": "application/json" } } ); + let data = await fetch(person.following, { + headers: { Accept: "application/json" }, + }); let json_data = JSON.parse(await data.text()); followingCount = json_data.totalItems; @@ -470,14 +482,20 @@ export async function updatePerson( person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), followersUri: person.followers ? getApId(person.followers) : undefined, - followersCount: followersCount !== undefined - ? followersCount - : person.followers && typeof person.followers !== "string" && isCollectionOrOrderedCollection(person.followers) + followersCount: + followersCount !== undefined + ? followersCount + : person.followers && + typeof person.followers !== "string" && + isCollectionOrOrderedCollection(person.followers) ? person.followers.totalItems : undefined, - followingCount: followingCount !== undefined - ? followingCount - : person.following && typeof person.following !== "string" && isCollectionOrOrderedCollection(person.following) + followingCount: + followingCount !== undefined + ? followingCount + : person.following && + typeof person.following !== "string" && + isCollectionOrOrderedCollection(person.following) ? person.following.totalItems : undefined, featured: person.featured, diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index e681f1294..e833fddb5 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -54,7 +54,11 @@ export const paramDef = { folderId: { type: "string", format: "misskey:id", nullable: true }, name: { type: "string" }, isSensitive: { type: "boolean" }, - comment: { type: "string", nullable: true, maxLength: DB_MAX_IMAGE_COMMENT_LENGTH }, + comment: { + type: "string", + nullable: true, + maxLength: DB_MAX_IMAGE_COMMENT_LENGTH, + }, }, required: ["fileId"], } as const; diff --git a/packages/backend/src/server/api/endpoints/emoji.ts b/packages/backend/src/server/api/endpoints/emoji.ts index 5d3c77e5e..ddfad7737 100644 --- a/packages/backend/src/server/api/endpoints/emoji.ts +++ b/packages/backend/src/server/api/endpoints/emoji.ts @@ -11,7 +11,8 @@ export const meta = { res: { type: "object", - optional: false, nullable: false, + optional: false, + nullable: false, ref: "Emoji", }, } as const; diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts index 03d762e97..ed9ae16df 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts @@ -151,7 +151,7 @@ export default define(meta, paramDef, async (ps, user) => { } // テキストが無いかつ添付ファイルも無かったらエラー - if ((ps.text == null || ps.text.trim() === '') && file == null) { + if ((ps.text == null || ps.text.trim() === "") && file == null) { throw new ApiError(meta.errors.contentRequired); } diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 5e431d4f7..8f563c384 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -139,7 +139,7 @@ export default define(meta, paramDef, async (ps, me) => { }) .map((key) => key.id); - ids.push(...res); + ids.push(...res); } // Sort all the results by note id DESC (newest first) @@ -160,7 +160,7 @@ export default define(meta, paramDef, async (ps, me) => { }); // The notes are checked for visibility and muted/blocked users when packed - found.push(...await Notes.packMany(notes, me)); + found.push(...(await Notes.packMany(notes, me))); start += chunkSize; } diff --git a/packages/backend/src/server/api/index.ts b/packages/backend/src/server/api/index.ts index a0833eb3e..529f09bd5 100644 --- a/packages/backend/src/server/api/index.ts +++ b/packages/backend/src/server/api/index.ts @@ -7,7 +7,10 @@ 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 { + apiMastodonCompatible, + getClient, +} from "./mastodon/ApiMastodonCompatibleService.js"; import { Instances, AccessTokens, Users } from "@/models/index.js"; import config from "@/config/index.js"; import fs from "fs"; @@ -21,10 +24,10 @@ 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, IdConvertType as IdType } from "native-utils" +import { convertId, IdConvertType as IdType } from "native-utils"; // re-export native rust id conversion (function and enum) -export { IdType, convertId }; +export { IdType, convertId }; // Init app const app = new Koa(); @@ -74,7 +77,6 @@ mastoRouter.use( }), ); - mastoFileRouter.post("/v1/media", upload.single("file"), async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index fa218305c..70bdb74f3 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -77,7 +77,10 @@ export function apiAccountMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.search((ctx.request.query as any).acct, 'accounts'); + const data = await client.search( + (ctx.request.query as any).acct, + "accounts", + ); let resp = data.data.accounts[0]; resp.id = convertId(resp.id, IdType.MastodonId); ctx.body = resp; @@ -88,26 +91,23 @@ export function apiAccountMastodon(router: Router): void { ctx.body = e.response.data; } }); - router.get<{ Params: { id: string } }>( - "/v1/accounts/:id", - async (ctx) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const calcId = convertId(ctx.params.id, IdType.CalckeyId); - const data = await client.getAccount(calcId); - let resp = data.data; - resp.id = convertId(resp.id, IdType.MastodonId); - ctx.body = resp; - } catch (e: any) { - console.error(e); - console.error(e.response.data); - ctx.status = 401; - ctx.body = e.response.data; - } - }, - ); + router.get<{ Params: { id: string } }>("/v1/accounts/:id", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const calcId = convertId(ctx.params.id, IdType.CalckeyId); + const data = await client.getAccount(calcId); + let resp = data.data; + resp.id = convertId(resp.id, IdType.MastodonId); + ctx.body = resp; + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); router.get<{ Params: { id: string } }>( "/v1/accounts/:id/statuses", async (ctx) => { @@ -122,11 +122,19 @@ export function apiAccountMastodon(router: Router): void { let resp = data.data; for (let statIdx = 0; statIdx < resp.length; statIdx++) { resp[statIdx].id = convertId(resp[statIdx].id, IdType.MastodonId); - resp[statIdx].in_reply_to_account_id = resp[statIdx].in_reply_to_account_id ? convertId(resp[statIdx].in_reply_to_account_id, IdType.MastodonId) : null; - resp[statIdx].in_reply_to_id = resp[statIdx].in_reply_to_id ? convertId(resp[statIdx].in_reply_to_id, IdType.MastodonId) : null; - let mentions = resp[statIdx].mentions + resp[statIdx].in_reply_to_account_id = resp[statIdx] + .in_reply_to_account_id + ? convertId(resp[statIdx].in_reply_to_account_id, IdType.MastodonId) + : null; + resp[statIdx].in_reply_to_id = resp[statIdx].in_reply_to_id + ? convertId(resp[statIdx].in_reply_to_id, IdType.MastodonId) + : null; + let mentions = resp[statIdx].mentions; for (let mtnIdx = 0; mtnIdx < mentions.length; mtnIdx++) { - resp[statIdx].mentions[mtnIdx].id = convertId(mentions[mtnIdx].id, IdType.MastodonId); + resp[statIdx].mentions[mtnIdx].id = convertId( + mentions[mtnIdx].id, + IdType.MastodonId, + ); } } ctx.body = resp; @@ -210,7 +218,9 @@ export function apiAccountMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.followAccount(convertId(ctx.params.id, IdType.CalckeyId)); + const data = await client.followAccount( + convertId(ctx.params.id, IdType.CalckeyId), + ); let acct = data.data; acct.following = true; acct.id = convertId(acct.id, IdType.MastodonId); @@ -230,7 +240,9 @@ export function apiAccountMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.unfollowAccount(convertId(ctx.params.id, IdType.CalckeyId)); + const data = await client.unfollowAccount( + convertId(ctx.params.id, IdType.CalckeyId), + ); let acct = data.data; acct.id = convertId(acct.id, IdType.MastodonId); acct.following = false; @@ -250,7 +262,9 @@ export function apiAccountMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.blockAccount(convertId(ctx.params.id, IdType.CalckeyId)); + const data = await client.blockAccount( + convertId(ctx.params.id, IdType.CalckeyId), + ); let resp = data.data; resp.id = convertId(resp.id, IdType.MastodonId); ctx.body = resp; @@ -269,7 +283,9 @@ export function apiAccountMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.unblockAccount(convertId(ctx.params.id, IdType.MastodonId)); + const data = await client.unblockAccount( + convertId(ctx.params.id, IdType.MastodonId), + ); let resp = data.data; resp.id = convertId(resp.id, IdType.MastodonId); ctx.body = resp; @@ -310,7 +326,9 @@ export function apiAccountMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.unmuteAccount(convertId(ctx.params.id, IdType.CalckeyId)); + const data = await client.unmuteAccount( + convertId(ctx.params.id, IdType.CalckeyId), + ); let resp = data.data; resp.id = convertId(resp.id, IdType.MastodonId); ctx.body = resp; @@ -344,7 +362,7 @@ export function apiAccountMastodon(router: Router): void { for (let i = 0; i < ids.length; i++) { reqIds.push(convertId(ids[i], IdType.CalckeyId)); } - + const data = await client.getRelationships(reqIds); let resp = data.data; for (let acctIdx = 0; acctIdx < resp.length; acctIdx++) { @@ -365,15 +383,25 @@ export function apiAccountMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = (await client.getBookmarks(limitToInt(ctx.query as any))) as any; + const data = (await client.getBookmarks( + limitToInt(ctx.query as any), + )) as any; let resp = data.data; for (let statIdx = 0; statIdx < resp.length; statIdx++) { resp[statIdx].id = convertId(resp[statIdx].id, IdType.MastodonId); - resp[statIdx].in_reply_to_account_id = resp[statIdx].in_reply_to_account_id ? convertId(resp[statIdx].in_reply_to_account_id, IdType.MastodonId) : null; - resp[statIdx].in_reply_to_id = resp[statIdx].in_reply_to_id ? convertId(resp[statIdx].in_reply_to_id, IdType.MastodonId) : null; - let mentions = resp[statIdx].mentions + resp[statIdx].in_reply_to_account_id = resp[statIdx] + .in_reply_to_account_id + ? convertId(resp[statIdx].in_reply_to_account_id, IdType.MastodonId) + : null; + resp[statIdx].in_reply_to_id = resp[statIdx].in_reply_to_id + ? convertId(resp[statIdx].in_reply_to_id, IdType.MastodonId) + : null; + let mentions = resp[statIdx].mentions; for (let mtnIdx = 0; mtnIdx < mentions.length; mtnIdx++) { - resp[statIdx].mentions[mtnIdx].id = convertId(mentions[mtnIdx].id, IdType.MastodonId); + resp[statIdx].mentions[mtnIdx].id = convertId( + mentions[mtnIdx].id, + IdType.MastodonId, + ); } } ctx.body = resp; @@ -393,11 +421,19 @@ export function apiAccountMastodon(router: Router): void { let resp = data.data; for (let statIdx = 0; statIdx < resp.length; statIdx++) { resp[statIdx].id = convertId(resp[statIdx].id, IdType.MastodonId); - resp[statIdx].in_reply_to_account_id = resp[statIdx].in_reply_to_account_id ? convertId(resp[statIdx].in_reply_to_account_id, IdType.MastodonId) : null; - resp[statIdx].in_reply_to_id = resp[statIdx].in_reply_to_id ? convertId(resp[statIdx].in_reply_to_id, IdType.MastodonId) : null; - let mentions = resp[statIdx].mentions + resp[statIdx].in_reply_to_account_id = resp[statIdx] + .in_reply_to_account_id + ? convertId(resp[statIdx].in_reply_to_account_id, IdType.MastodonId) + : null; + resp[statIdx].in_reply_to_id = resp[statIdx].in_reply_to_id + ? convertId(resp[statIdx].in_reply_to_id, IdType.MastodonId) + : null; + let mentions = resp[statIdx].mentions; for (let mtnIdx = 0; mtnIdx < mentions.length; mtnIdx++) { - resp[statIdx].mentions[mtnIdx].id = convertId(mentions[mtnIdx].id, IdType.MastodonId); + resp[statIdx].mentions[mtnIdx].id = convertId( + mentions[mtnIdx].id, + IdType.MastodonId, + ); } } ctx.body = resp; @@ -471,7 +507,9 @@ export function apiAccountMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.acceptFollowRequest(convertId(ctx.params.id, IdType.CalckeyId)); + const data = await client.acceptFollowRequest( + convertId(ctx.params.id, IdType.CalckeyId), + ); let resp = data.data; resp.id = convertId(resp.id, IdType.MastodonId); ctx.body = resp; @@ -490,7 +528,9 @@ export function apiAccountMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.rejectFollowRequest(convertId(ctx.params.id, IdType.CalckeyId)); + const data = await client.rejectFollowRequest( + convertId(ctx.params.id, IdType.CalckeyId), + ); let resp = data.data; resp.id = convertId(resp.id, IdType.MastodonId); ctx.body = resp; diff --git a/packages/backend/src/server/api/mastodon/endpoints/auth.ts b/packages/backend/src/server/api/mastodon/endpoints/auth.ts index f1c54be0a..e2cfc47af 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/auth.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/auth.ts @@ -44,7 +44,7 @@ const writeScope = [ export function apiAuthMastodon(router: Router): void { router.post("/v1/apps", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; - const client = getClient(BASE_URL, ''); + const client = getClient(BASE_URL, ""); const body: any = ctx.request.body || ctx.request.query; try { let scope = body.scopes; @@ -68,9 +68,9 @@ export function apiAuthMastodon(router: Router): void { website: body.website, redirect_uri: red, client_id: Buffer.from(appData.url || "").toString("base64"), - client_secret: appData.clientSecret + client_secret: appData.clientSecret, }; - console.log(returns) + console.log(returns); ctx.body = returns; } catch (e: any) { console.error(e); diff --git a/packages/backend/src/server/api/mastodon/endpoints/meta.ts b/packages/backend/src/server/api/mastodon/endpoints/meta.ts index e5e0f2622..d362d1b9e 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/meta.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/meta.ts @@ -11,17 +11,20 @@ export async function getInstance(response: Entity.Instance) { return { uri: response.uri, title: response.title || "Calckey", - short_description: response.description.substring(0, 50) || "See real server website", - description: response.description || "This is a vanilla Calckey Instance. It doesnt seem to have a description. BTW you are using the Mastodon api to access this server :)", + short_description: + response.description.substring(0, 50) || "See real server website", + description: + response.description || + "This is a vanilla Calckey Instance. It doesnt seem to have a description. BTW you are using the Mastodon api to access this server :)", email: response.email || "", version: "3.0.0 compatible (3.5+ Calckey)", //I hope this version string is correct, we will need to test it. urls: response.urls, stats: { - user_count: (await totalUsers), - status_count: (await totalStatuses), - domain_count: response.stats.domain_count + user_count: await totalUsers, + status_count: await totalStatuses, + domain_count: response.stats.domain_count, }, - thumbnail: response.thumbnail || 'https://http.cat/404', + thumbnail: response.thumbnail || "https://http.cat/404", languages: meta.langs, registrations: !meta.disableRegistration || response.registrations, approval_required: !response.registrations, diff --git a/packages/backend/src/server/api/mastodon/endpoints/search.ts b/packages/backend/src/server/api/mastodon/endpoints/search.ts index b610e784d..e4990811a 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/search.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/search.ts @@ -44,7 +44,7 @@ export function apiSearchMastodon(router: Router): void { } } catch (e: any) { console.error(e); - ctx.status = (401); + ctx.status = 401; ctx.body = e.response.data; } }); @@ -52,11 +52,15 @@ export function apiSearchMastodon(router: Router): void { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const accessTokens = ctx.headers.authorization; try { - const data = await getHighlight(BASE_URL, ctx.request.hostname, accessTokens); + const data = await getHighlight( + BASE_URL, + ctx.request.hostname, + accessTokens, + ); ctx.body = data; } catch (e: any) { console.error(e); - ctx.status = (401); + ctx.status = 401; ctx.body = e.response.data; } }); @@ -75,7 +79,7 @@ export function apiSearchMastodon(router: Router): void { ctx.body = data; } catch (e: any) { console.error(e); - ctx.status = (401); + ctx.status = 401; ctx.body = e.response.data; } }); diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts index 6494ad5a6..fcfbd6aaa 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/status.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/status.ts @@ -2,13 +2,13 @@ import Router from "@koa/router"; import { getClient } from "../ApiMastodonCompatibleService.js"; import { emojiRegexAtStartToEnd } from "@/misc/emoji-regex.js"; import axios from "axios"; -import querystring from 'node:querystring' -import qs from 'qs' +import querystring from "node:querystring"; +import qs from "qs"; import { limitToInt } from "./timeline.js"; function normalizeQuery(data: any) { - const str = querystring.stringify(data); - return qs.parse(str); + const str = querystring.stringify(data); + return qs.parse(str); } export function apiStatusMastodon(router: Router): void { @@ -18,11 +18,14 @@ export function apiStatusMastodon(router: Router): void { const client = getClient(BASE_URL, accessTokens); try { let body: any = ctx.request.body; - if ((!body.poll && body['poll[options][]']) || (!body.media_ids && body['media_ids[]'])) { - body = normalizeQuery(body) + if ( + (!body.poll && body["poll[options][]"]) || + (!body.media_ids && body["media_ids[]"]) + ) { + body = normalizeQuery(body); } const text = body.status; - const removed = text.replace(/@\S+/g, "").replace(/\s|​/g, '') + const removed = text.replace(/@\S+/g, "").replace(/\s|​/g, ""); const isDefaultEmoji = emojiRegexAtStartToEnd.test(removed); const isCustomEmoji = /^:[a-zA-Z0-9@_]+:$/.test(removed); if ((body.in_reply_to_id && isDefaultEmoji) || isCustomEmoji) { @@ -47,8 +50,9 @@ export function apiStatusMastodon(router: Router): void { } if (!body.media_ids) body.media_ids = undefined; if (body.media_ids && !body.media_ids.length) body.media_ids = undefined; - const { sensitive } = body - body.sensitive = typeof sensitive === 'string' ? sensitive === 'true' : sensitive + const { sensitive } = body; + body.sensitive = + typeof sensitive === "string" ? sensitive === "true" : sensitive; const data = await client.postStatus(text, body); ctx.body = data.data; } catch (e: any) { @@ -57,38 +61,32 @@ export function apiStatusMastodon(router: Router): void { ctx.body = e.response.data; } }); - router.get<{ Params: { id: string } }>( - "/v1/statuses/: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.getStatus(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e); - ctx.status = 401; - ctx.body = e.response.data; - } - }, - ); - router.delete<{ Params: { id: string } }>( - "/v1/statuses/: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.deleteStatus(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e.response.data, request.params.id); - ctx.status = 401; - ctx.body = e.response.data; - } - }, - ); + router.get<{ Params: { id: string } }>("/v1/statuses/: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.getStatus(ctx.params.id); + ctx.body = data.data; + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.delete<{ Params: { id: string } }>("/v1/statuses/: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.deleteStatus(ctx.params.id); + ctx.body = data.data; + } catch (e: any) { + console.error(e.response.data, request.params.id); + ctx.status = 401; + ctx.body = e.response.data; + } + }); interface IReaction { id: string; createdAt: string; @@ -103,12 +101,15 @@ export function apiStatusMastodon(router: Router): void { const client = getClient(BASE_URL, accessTokens); try { const id = ctx.params.id; - const data = await client.getStatusContext(id, limitToInt(ctx.query as any)); + const data = await client.getStatusContext( + id, + limitToInt(ctx.query as any), + ); const status = await client.getStatus(id); let reqInstance = axios.create({ headers: { - Authorization : ctx.headers.authorization - } + Authorization: ctx.headers.authorization, + }, }); const reactionsAxios = await reqInstance.get( `${BASE_URL}/api/notes/reactions?noteId=${id}`, @@ -296,57 +297,48 @@ 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(ctx.params.id); - ctx.body = 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( - ctx.params.id, - ctx.request.body as any, - ); - ctx.body = 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; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getPoll(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e); - ctx.status = 401; - ctx.body = e.response.data; - } - }, - ); + 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(ctx.params.id); + ctx.body = 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( + ctx.params.id, + ctx.request.body as any, + ); + ctx.body = 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; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getPoll(ctx.params.id); + ctx.body = data.data; + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }); router.post<{ Params: { id: string } }>( "/v1/polls/:id/votes", async (ctx) => { diff --git a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts index 0393a206c..ce3a4dc95 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts @@ -16,7 +16,8 @@ export function limitToInt(q: ParsedUrlQuery) { 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); + const toBoolean = (value: string) => + !["0", "f", "F", "false", "FALSE", "off", "OFF"].includes(value); let object: any = q; if (q.only_media) @@ -35,26 +36,26 @@ export function toTextWithReaction(status: Entity.Status[], host: string) { if (!t.emoji_reactions) return t; if (t.reblog) t.reblog = toTextWithReaction([t.reblog], host)[0]; const reactions = t.emoji_reactions.map((r) => { - const emojiNotation = r.url ? `:${r.name.replace('@.', '')}:` : r.name - return `${emojiNotation} (${r.count}${r.me ? `* ` : ''})` + const emojiNotation = r.url ? `:${r.name.replace("@.", "")}:` : r.name; + return `${emojiNotation} (${r.count}${r.me ? `* ` : ""})`; }); const reaction = t.emoji_reactions as Entity.Reaction[]; - const emoji = t.emojis || [] + const emoji = t.emojis || []; for (const r of reaction) { - if (!r.url) continue - emoji.push({ - 'shortcode': r.name, - 'url': r.url, - 'static_url': r.url, - 'visible_in_picker': true, - category: "" - },) + if (!r.url) continue; + emoji.push({ + shortcode: r.name, + url: r.url, + static_url: r.url, + visible_in_picker: true, + category: "", + }); } const isMe = reaction.findIndex((r) => r.me) > -1; const total = reaction.reduce((sum, reaction) => sum + reaction.count, 0); t.favourited = isMe; t.favourites_count = total; - t.emojis = emoji + t.emojis = emoji; t.content = `

${autoLinker(t.content, host)}

${reactions.join( ", ", )}

`; @@ -126,23 +127,20 @@ export function apiTimelineMastodon(router: Router): void { } }, ); - router.get( - "/v1/timelines/home", - async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getHomeTimeline(limitToInt(ctx.query)); - ctx.body = toTextWithReaction(data.data, ctx.hostname); - } catch (e: any) { - console.error(e); - console.error(e.response.data); - ctx.status = 401; - ctx.body = e.response.data; - } - }, - ); + router.get("/v1/timelines/home", async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getHomeTimeline(limitToInt(ctx.query)); + ctx.body = toTextWithReaction(data.data, ctx.hostname); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); router.get<{ Params: { listId: string } }>( "/v1/timelines/list/:listId", async (ctx, reply) => { diff --git a/packages/backend/src/server/api/private/signin.ts b/packages/backend/src/server/api/private/signin.ts index c8cd5fb2c..7c4b511a2 100644 --- a/packages/backend/src/server/api/private/signin.ts +++ b/packages/backend/src/server/api/private/signin.ts @@ -12,7 +12,11 @@ import { } from "@/models/index.js"; import type { ILocalUser } from "@/models/entities/user.js"; import { genId } from "@/misc/gen-id.js"; -import { comparePassword, hashPassword, isOldAlgorithm } from '@/misc/password.js'; +import { + comparePassword, + hashPassword, + isOldAlgorithm, +} from "@/misc/password.js"; import { verifyLogin, hash } from "../2fa.js"; import { randomBytes } from "node:crypto"; import { IsNull } from "typeorm"; diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts index f285b2f5b..061a17be2 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/index.ts @@ -414,7 +414,7 @@ export default class Connection { const client = getClient(this.host, this.accessToken); client.getStatus(payload.id).then((data) => { const newPost = toTextWithReaction([data.data], this.host); - const targetPost = newPost[0] + const targetPost = newPost[0]; for (const stream of this.currentSubscribe) { this.wsConnection.send( JSON.stringify({ diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts index 3f7f7c03c..be1f71a7a 100644 --- a/packages/backend/src/server/index.ts +++ b/packages/backend/src/server/index.ts @@ -31,7 +31,7 @@ import webServer from "./web/index.js"; import { initializeStreamingServer } from "./api/streaming.js"; import { koaBody } from "koa-body"; import removeTrailingSlash from "koa-remove-trailing-slashes"; -import {v4 as uuid} from "uuid"; +import { v4 as uuid } from "uuid"; export const serverLogger = new Logger("server", "gray", false); @@ -162,19 +162,19 @@ mastoRouter.get("/oauth/authorize", async (ctx) => { const { client_id, state, redirect_uri } = ctx.request.query; console.log(ctx.request.req); let param = "mastodon=true"; - if (state) - param += `&state=${state}`; - if (redirect_uri) - param += `&redirect_uri=${redirect_uri}`; - const client = client_id? client_id : ""; - ctx.redirect(`${Buffer.from(client.toString(), 'base64').toString()}?${param}`); + if (state) param += `&state=${state}`; + if (redirect_uri) param += `&redirect_uri=${redirect_uri}`; + const client = client_id ? client_id : ""; + ctx.redirect( + `${Buffer.from(client.toString(), "base64").toString()}?${param}`, + ); }); mastoRouter.post("/oauth/token", async (ctx) => { const body: any = ctx.request.body || ctx.request.query; - console.log('token-request', body); - console.log('token-query', ctx.request.query); - if (body.grant_type === 'client_credentials') { + console.log("token-request", body); + console.log("token-query", ctx.request.query); + if (body.grant_type === "client_credentials") { const ret = { access_token: uuid(), token_type: "Bearer", @@ -197,8 +197,8 @@ mastoRouter.post("/oauth/token", async (ctx) => { // return; //} //token = `${m[1]}-${m[2]}-${m[3]}-${m[4]}-${m[5]}` - console.log(body.code, token) - token = body.code + console.log(body.code, token); + token = body.code; } if (client_id instanceof Array) { client_id = client_id.toString(); @@ -214,10 +214,10 @@ mastoRouter.post("/oauth/token", async (ctx) => { const ret = { access_token: atData.accessToken, token_type: "Bearer", - scope: body.scope || 'read write follow push', + scope: body.scope || "read write follow push", created_at: Math.floor(new Date().getTime() / 1000), }; - console.log('token-response', ret) + console.log("token-response", ret); ctx.body = ret; } catch (err: any) { console.error(err); diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts index 3529032b2..8276e891c 100644 --- a/packages/client/src/router.ts +++ b/packages/client/src/router.ts @@ -136,7 +136,9 @@ export const routes = [ { path: "/custom-katex-macro", name: "custom-katex-macro", - component: page(() => import("./pages/settings/custom-katex-macro.vue")), + component: page( + () => import("./pages/settings/custom-katex-macro.vue"), + ), }, { path: "/account-info", @@ -243,7 +245,9 @@ export const routes = [ { path: "/custom-katex-macro", name: "general", - component: page(() => import("./pages/settings/custom-katex-macro.vue")), + component: page( + () => import("./pages/settings/custom-katex-macro.vue"), + ), }, { path: "/accounts", diff --git a/packages/client/src/scripts/get-user-menu.ts b/packages/client/src/scripts/get-user-menu.ts index d5ec5e0d5..44dbac961 100644 --- a/packages/client/src/scripts/get-user-menu.ts +++ b/packages/client/src/scripts/get-user-menu.ts @@ -262,7 +262,9 @@ export function getUserMenu(user, router: Router = mainRouter) { menu = menu.concat([ null, { - icon: user.isMuted ? "ph-eye ph-bold ph-lg" : "ph-eye-slash ph-bold ph-lg", + icon: user.isMuted + ? "ph-eye ph-bold ph-lg" + : "ph-eye-slash ph-bold ph-lg", text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute, hidden: user.isBlocking === true, action: toggleMute, diff --git a/packages/client/src/scripts/katex-macro.ts b/packages/client/src/scripts/katex-macro.ts index bede1c5f6..4368095be 100644 --- a/packages/client/src/scripts/katex-macro.ts +++ b/packages/client/src/scripts/katex-macro.ts @@ -1,19 +1,17 @@ type KaTeXMacro = { - args: number; - rule: (string | number)[]; + args: number; + rule: (string | number)[]; }; function parseSingleKaTeXMacro(src: string): [string, KaTeXMacro] { const invalid: [string, KaTeXMacro] = ["", { args: 0, rule: [] }]; const skipSpaces = (pos: number): number => { - while (src[pos] === " ") - ++pos; + while (src[pos] === " ") ++pos; return pos; }; - if (!src.startsWith("\\newcommand") || src.slice(-1) !== "}") - return invalid; + if (!src.startsWith("\\newcommand") || src.slice(-1) !== "}") return invalid; // current index we are checking (= "\\newcommand".length) let currentPos: number = 11; @@ -21,28 +19,21 @@ function parseSingleKaTeXMacro(src: string): [string, KaTeXMacro] { // parse {\name}, (\name), or [\name] let bracket: string; - if (src[currentPos] === "{") - bracket = "{}"; - else if (src[currentPos] === "(") - bracket = "()"; - else if (src[currentPos] === "[") - bracket = "[]"; - else - return invalid; + if (src[currentPos] === "{") bracket = "{}"; + else if (src[currentPos] === "(") bracket = "()"; + else if (src[currentPos] === "[") bracket = "[]"; + else return invalid; ++currentPos; currentPos = skipSpaces(currentPos); - if (src[currentPos] !== "\\") - return invalid; + if (src[currentPos] !== "\\") return invalid; const closeNameBracketPos: number = src.indexOf(bracket[1], currentPos); - if (closeNameBracketPos === -1) - return invalid; + if (closeNameBracketPos === -1) return invalid; const name: string = src.slice(currentPos + 1, closeNameBracketPos).trim(); - if (!/^[a-zA-Z]+$/.test(name)) - return invalid; + if (!/^[a-zA-Z]+$/.test(name)) return invalid; currentPos = skipSpaces(closeNameBracketPos + 1); @@ -54,8 +45,7 @@ function parseSingleKaTeXMacro(src: string): [string, KaTeXMacro] { macro.args = Number(src.slice(currentPos + 1, closeArgsBracketPos).trim()); currentPos = closeArgsBracketPos + 1; - if (Number.isNaN(macro.args) || macro.args < 0) - return invalid; + if (Number.isNaN(macro.args) || macro.args < 0) return invalid; } else if (src[currentPos] === "{") { macro.args = 0; } else { @@ -65,8 +55,7 @@ function parseSingleKaTeXMacro(src: string): [string, KaTeXMacro] { currentPos = skipSpaces(currentPos); // parse {rule} - if (src[currentPos] !== "{") - return invalid; + if (src[currentPos] !== "{") return invalid; ++currentPos; currentPos = skipSpaces(currentPos); @@ -94,8 +83,11 @@ function parseSingleKaTeXMacro(src: string): [string, KaTeXMacro] { break; } - const argIndexEndPos = src.slice(numbersignPos + 1).search(/[^\d]/) + numbersignPos; - const argIndex: number = Number(src.slice(numbersignPos + 1, argIndexEndPos + 1)); + const argIndexEndPos = + src.slice(numbersignPos + 1).search(/[^\d]/) + numbersignPos; + const argIndex: number = Number( + src.slice(numbersignPos + 1, argIndexEndPos + 1), + ); if (Number.isNaN(argIndex) || argIndex < 1 || macro.args < argIndex) return invalid; @@ -107,10 +99,8 @@ function parseSingleKaTeXMacro(src: string): [string, KaTeXMacro] { currentPos = argIndexEndPos + 1; } - if (macro.args === 0) - return [name, macro]; - else - return [name + bracket[0], macro]; + if (macro.args === 0) return [name, macro]; + else return [name + bracket[0], macro]; } export function parseKaTeXMacros(src: string): string { @@ -118,8 +108,7 @@ export function parseKaTeXMacros(src: string): string { for (const s of src.split("\n")) { const [name, macro]: [string, KaTeXMacro] = parseSingleKaTeXMacro(s.trim()); - if (name !== "") - result[name] = macro; + if (name !== "") result[name] = macro; } return JSON.stringify(result); @@ -127,11 +116,22 @@ export function parseKaTeXMacros(src: string): string { // returns [expanded text, whether something is expanded, how many times we can expand more] // the boolean value is used for multi-pass expansions (macros can expand to other macros) -function expandKaTeXMacroOnce(src: string, macros: { [name: string]: KaTeXMacro }, maxNumberOfExpansions: number) - : [string, boolean, number] { +function expandKaTeXMacroOnce( + src: string, + macros: { [name: string]: KaTeXMacro }, + maxNumberOfExpansions: number, +): [string, boolean, number] { const bracketKinds = 3; - const openBracketId: { [bracket: string]: number } = {"(": 0, "{": 1, "[": 2}; - const closeBracketId: { [bracket: string]: number } = {")": 0, "}": 1, "]": 2}; + const openBracketId: { [bracket: string]: number } = { + "(": 0, + "{": 1, + "[": 2, + }; + const closeBracketId: { [bracket: string]: number } = { + ")": 0, + "}": 1, + "]": 2, + }; const openBracketFromId = ["(", "{", "["]; const closeBracketFromId = [")", "}", "]"]; @@ -142,21 +142,29 @@ function expandKaTeXMacroOnce(src: string, macros: { [name: string]: KaTeXMacro let result: BracketMapping = {}; const n = src.length; - let depths = new Array(bracketKinds).fill(0); // current bracket depth for "()", "{}", and "[]" - let buffer = Array.from(Array(bracketKinds), () => Array(n)); + let depths = new Array(bracketKinds).fill(0); // current bracket depth for "()", "{}", and "[]" + let buffer = Array.from(Array(bracketKinds), () => + Array(n), + ); let isEscaped = false; for (let i = 0; i < n; ++i) { - if (!isEscaped && src[i] === "\\" && i + 1 < n && ["{", "}", "\\"].includes(src[i+1])) { + if ( + !isEscaped && + src[i] === "\\" && + i + 1 < n && + ["{", "}", "\\"].includes(src[i + 1]) + ) { isEscaped = true; continue; } - if (isEscaped - || (src[i] !== "\\" - && !openBracketFromId.includes(src[i]) - && !closeBracketFromId.includes(src[i]))) - { + if ( + isEscaped || + (src[i] !== "\\" && + !openBracketFromId.includes(src[i]) && + !closeBracketFromId.includes(src[i])) + ) { isEscaped = false; continue; } @@ -178,27 +186,29 @@ function expandKaTeXMacroOnce(src: string, macros: { [name: string]: KaTeXMacro return result; })(); - function expandSingleKaTeXMacro(expandedArgs: string[], macroName: string): string { + function expandSingleKaTeXMacro( + expandedArgs: string[], + macroName: string, + ): string { let result = ""; for (const block of macros[macroName].rule) { - if (typeof block === "string") - result += block; - else - result += expandedArgs[block - 1]; + if (typeof block === "string") result += block; + else result += expandedArgs[block - 1]; } return result; } // only expand src.slice(beginPos, endPos) - function expandKaTeXMacroImpl(beginPos: number, endPos: number): [string, boolean] { - if (endPos <= beginPos) - return ["", false]; + function expandKaTeXMacroImpl( + beginPos: number, + endPos: number, + ): [string, boolean] { + if (endPos <= beginPos) return ["", false]; const raw: string = src.slice(beginPos, endPos); - const fallback: string = raw; // returned for invalid inputs or too many expansions + const fallback: string = raw; // returned for invalid inputs or too many expansions - if (maxNumberOfExpansions <= 0) - return [fallback, false]; + if (maxNumberOfExpansions <= 0) return [fallback, false]; --maxNumberOfExpansions; // search for a custom macro @@ -218,14 +228,13 @@ function expandKaTeXMacroOnce(src: string, macros: { [name: string]: KaTeXMacro checkedPos = src.indexOf("\\", checkedPos + 1); // there is no macro to expand - if (checkedPos === -1) - return [raw, false]; + if (checkedPos === -1) return [raw, false]; // is it a custom macro? - let nonAlphaPos = src.slice(checkedPos + 1).search(/[^A-Za-z]/) + checkedPos + 1; + let nonAlphaPos = + src.slice(checkedPos + 1).search(/[^A-Za-z]/) + checkedPos + 1; - if (nonAlphaPos === checkedPos) - nonAlphaPos = endPos; + if (nonAlphaPos === checkedPos) nonAlphaPos = endPos; let macroNameCandidate = src.slice(checkedPos + 1, nonAlphaPos); if (macros.hasOwnProperty(macroNameCandidate)) { @@ -239,19 +248,17 @@ function expandKaTeXMacroOnce(src: string, macros: { [name: string]: KaTeXMacro let nextOpenBracketPos = endPos; for (let i = 0; i < bracketKinds; ++i) { const pos = src.indexOf(openBracketFromId[i], checkedPos + 1); - if (pos !== -1 && pos < nextOpenBracketPos) - nextOpenBracketPos = pos; + if (pos !== -1 && pos < nextOpenBracketPos) nextOpenBracketPos = pos; } - if (nextOpenBracketPos === endPos) - continue; // there is no open bracket + if (nextOpenBracketPos === endPos) continue; // there is no open bracket macroNameCandidate += src[nextOpenBracketPos]; if (macros.hasOwnProperty(macroNameCandidate)) { macroBackslashPos = checkedPos; macroArgBeginPos = nextOpenBracketPos; - macroArgEndPos = nextOpenBracketPos; // to search the first arg from here + macroArgEndPos = nextOpenBracketPos; // to search the first arg from here macroName = macroNameCandidate; break; } @@ -265,31 +272,46 @@ function expandKaTeXMacroOnce(src: string, macros: { [name: string]: KaTeXMacro for (let i = 0; i < numArgs; ++i) { // find the first open bracket after what we've searched const nextOpenBracketPos = src.indexOf(openBracket, macroArgEndPos); - if (nextOpenBracketPos === -1) - return [fallback, false]; // not enough arguments are provided - if (!bracketMapping[nextOpenBracketPos]) - return [fallback, false]; // found open bracket doesn't correspond to any close bracket + if (nextOpenBracketPos === -1) return [fallback, false]; // not enough arguments are provided + if (!bracketMapping[nextOpenBracketPos]) return [fallback, false]; // found open bracket doesn't correspond to any close bracket macroArgEndPos = bracketMapping[nextOpenBracketPos]; - expandedArgs[i] = expandKaTeXMacroImpl(nextOpenBracketPos + 1, macroArgEndPos)[0]; + expandedArgs[i] = expandKaTeXMacroImpl( + nextOpenBracketPos + 1, + macroArgEndPos, + )[0]; } - return [src.slice(beginPos, macroBackslashPos) - + expandSingleKaTeXMacro(expandedArgs, macroName) - + expandKaTeXMacroImpl(macroArgEndPos + 1, endPos)[0], true]; + return [ + src.slice(beginPos, macroBackslashPos) + + expandSingleKaTeXMacro(expandedArgs, macroName) + + expandKaTeXMacroImpl(macroArgEndPos + 1, endPos)[0], + true, + ]; } - const [expandedText, expandedFlag]: [string, boolean] = expandKaTeXMacroImpl(0, src.length); + const [expandedText, expandedFlag]: [string, boolean] = expandKaTeXMacroImpl( + 0, + src.length, + ); return [expandedText, expandedFlag, maxNumberOfExpansions]; } -export function expandKaTeXMacro(src: string, macrosAsJSONString: string, maxNumberOfExpansions: number): string { +export function expandKaTeXMacro( + src: string, + macrosAsJSONString: string, + maxNumberOfExpansions: number, +): string { const macros = JSON.parse(macrosAsJSONString); let expandMore = true; - while (expandMore && (0 < maxNumberOfExpansions)) - [src, expandMore, maxNumberOfExpansions] = expandKaTeXMacroOnce(src, macros, maxNumberOfExpansions); + while (expandMore && 0 < maxNumberOfExpansions) + [src, expandMore, maxNumberOfExpansions] = expandKaTeXMacroOnce( + src, + macros, + maxNumberOfExpansions, + ); return src; } diff --git a/packages/client/src/scripts/preprocess.ts b/packages/client/src/scripts/preprocess.ts index da163b261..438a44d30 100644 --- a/packages/client/src/scripts/preprocess.ts +++ b/packages/client/src/scripts/preprocess.ts @@ -4,15 +4,19 @@ import { expandKaTeXMacro } from "@/scripts/katex-macro"; export function preprocess(text: string): string { if (defaultStore.state.enableCustomKaTeXMacro) { - const parsedKaTeXMacro = localStorage.getItem("customKaTeXMacroParsed") ?? "{}"; - const maxNumberOfExpansions = 200; // to prevent infinite expansion loops + const parsedKaTeXMacro = + localStorage.getItem("customKaTeXMacroParsed") ?? "{}"; + const maxNumberOfExpansions = 200; // to prevent infinite expansion loops let nodes = mfm.parse(text); for (let node of nodes) { if (node["type"] === "mathInline" || node["type"] === "mathBlock") { - node["props"]["formula"] - = expandKaTeXMacro(node["props"]["formula"], parsedKaTeXMacro, maxNumberOfExpansions); + node["props"]["formula"] = expandKaTeXMacro( + node["props"]["formula"], + parsedKaTeXMacro, + maxNumberOfExpansions, + ); } }