mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2024-11-22 01:47:39 -07:00
withdrawal replacements patch
This commit is contained in:
parent
50eb66e375
commit
98f3f55371
8 changed files with 110 additions and 62 deletions
|
@ -14,6 +14,7 @@ import { StatusError } from "@/misc/fetch.js";
|
|||
import { shouldSkipInstance } from "@/misc/skipped-instances.js";
|
||||
import type { DeliverJobData } from "@/queue/types.js";
|
||||
import type Bull from "bull";
|
||||
import { patchText, shouldPatchText } from "@/remote/activitypub/renderer/note.js";
|
||||
|
||||
const logger = new Logger("deliver");
|
||||
|
||||
|
@ -30,10 +31,33 @@ export default async (job: Bull.Job<DeliverJobData>) => {
|
|||
logger.debug(`delivering ${latest}`);
|
||||
}
|
||||
|
||||
let i = undefined;
|
||||
|
||||
if (
|
||||
["Create", "Update"].includes(job.data.content.type)
|
||||
&& job.data.content.object.type === "Note"
|
||||
) {
|
||||
const obj = job.data.content.object;
|
||||
const patchSrcContent = shouldPatchText(obj.source.content);
|
||||
if (patchSrcContent) {
|
||||
i = await registerOrFetchInstanceDoc(host);
|
||||
if (shouldPatchText(obj.content))
|
||||
obj.content = patchText(obj.content, i);
|
||||
if (shouldPatchText(obj._misskey_content))
|
||||
obj._misskey_content = patchText(obj._misskey_content, i);
|
||||
if (patchSrcContent)
|
||||
obj.source.content = patchText(obj.source.content, i);
|
||||
}
|
||||
}
|
||||
|
||||
await request(job.data.user, job.data.to, job.data.content);
|
||||
|
||||
// Update stats
|
||||
registerOrFetchInstanceDoc(host).then((i) => {
|
||||
(async () => {
|
||||
if (i === undefined) {
|
||||
i = await registerOrFetchInstanceDoc(host);
|
||||
}
|
||||
|
||||
// Update stats
|
||||
Instances.update(i.id, {
|
||||
latestRequestSentAt: new Date(),
|
||||
latestStatus: 200,
|
||||
|
@ -46,7 +70,7 @@ export default async (job: Bull.Job<DeliverJobData>) => {
|
|||
instanceChart.requestSent(i.host, true);
|
||||
apRequestChart.deliverSucc();
|
||||
federationChart.deliverd(i.host, true);
|
||||
});
|
||||
})();
|
||||
|
||||
return "Success";
|
||||
} catch (res) {
|
||||
|
|
|
@ -28,24 +28,24 @@ export async function hasSignature(req: IncomingMessage): Promise<string> {
|
|||
return required ? "supplied" : "unneeded";
|
||||
}
|
||||
|
||||
export async function checkFetch(req: IncomingMessage): Promise<number> {
|
||||
export async function checkFetch(req: IncomingMessage): Promise<{ status: number; host?: string }> {
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
if (req.headers.host !== config.host) return 400;
|
||||
if (req.headers.host !== config.host) return { status: 400 };
|
||||
|
||||
let signature;
|
||||
|
||||
try {
|
||||
signature = httpSignature.parseRequest(req, { headers: ["(request-target)", "host", "date"] });
|
||||
} catch (e) {
|
||||
return 401;
|
||||
return { status: 401 };
|
||||
}
|
||||
|
||||
const keyId = new URL(signature.keyId);
|
||||
const host = toPuny(keyId.hostname);
|
||||
|
||||
if (await shouldBlockInstance(host, meta)) {
|
||||
return 403;
|
||||
return { status: 403 };
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -54,13 +54,13 @@ export async function checkFetch(req: IncomingMessage): Promise<number> {
|
|||
host !== config.domain &&
|
||||
!meta.allowedHosts.includes(host)
|
||||
) {
|
||||
return 403;
|
||||
return { status: 403 };
|
||||
}
|
||||
|
||||
const keyIdLower = signature.keyId.toLowerCase();
|
||||
if (keyIdLower.startsWith("acct:")) {
|
||||
// Old keyId is no longer supported.
|
||||
return 401;
|
||||
return { status: 401 };
|
||||
}
|
||||
|
||||
const dbResolver = new DbResolver();
|
||||
|
@ -77,23 +77,23 @@ export async function checkFetch(req: IncomingMessage): Promise<number> {
|
|||
);
|
||||
} catch (e) {
|
||||
// できなければ駄目
|
||||
return 403;
|
||||
return { status: 403 };
|
||||
}
|
||||
}
|
||||
|
||||
// publicKey がなくても終了
|
||||
if (authUser?.key == null) {
|
||||
return 403;
|
||||
return { status: 403 };
|
||||
}
|
||||
|
||||
// Cannot authenticate against local user
|
||||
if (authUser.user.uri === null || authUser.user.host === null) {
|
||||
return 400;
|
||||
return { status: 400 };
|
||||
}
|
||||
|
||||
// Check if keyId hostname matches actor hostname
|
||||
if (toPuny(new URL(authUser.user.uri).hostname) !== host) {
|
||||
return 403;
|
||||
return { status: 403 };
|
||||
}
|
||||
|
||||
// HTTP-Signatureの検証
|
||||
|
@ -107,7 +107,7 @@ export async function checkFetch(req: IncomingMessage): Promise<number> {
|
|||
authUser.key = await dbResolver.refetchPublicKeyForApId(authUser.user);
|
||||
|
||||
if (authUser.key == null) {
|
||||
return 403;
|
||||
return { status: 403 };
|
||||
}
|
||||
|
||||
httpSignatureValidated = httpSignature.verifySignature(
|
||||
|
@ -117,12 +117,12 @@ export async function checkFetch(req: IncomingMessage): Promise<number> {
|
|||
}
|
||||
|
||||
if (!httpSignatureValidated) {
|
||||
return 403;
|
||||
return { status: 403 };
|
||||
}
|
||||
|
||||
return verifySignature(signature, authUser.key) ? 200 : 401;
|
||||
}
|
||||
return 200;
|
||||
return { status: 200 };
|
||||
}
|
||||
|
||||
export async function getSignatureUser(req: IncomingMessage): Promise<{
|
||||
|
|
|
@ -10,11 +10,14 @@ import renderEmoji from "./emoji.js";
|
|||
import renderMention from "./mention.js";
|
||||
import renderHashtag from "./hashtag.js";
|
||||
import renderDocument from "./document.js";
|
||||
import { Instances } from "@/models/index.js";
|
||||
import { Instance } from "@/models/entities/instance.js";
|
||||
|
||||
export default async function renderNote(
|
||||
note: Note,
|
||||
dive = true,
|
||||
isTalk = false,
|
||||
clientHost: string | undefined = undefined,
|
||||
): Promise<Record<string, unknown>> {
|
||||
const getPromisedFiles = async (ids: string[]) => {
|
||||
if (!ids || ids.length === 0) return [];
|
||||
|
@ -93,13 +96,17 @@ export default async function renderNote(
|
|||
|
||||
const files = await getPromisedFiles(note.fileIds);
|
||||
|
||||
const text = note.text ?? "";
|
||||
let text = note.text ?? "";
|
||||
let poll: Poll | null = null;
|
||||
|
||||
if (note.hasPoll) {
|
||||
poll = await Polls.findOneBy({ noteId: note.id });
|
||||
}
|
||||
|
||||
if (clientHost && shouldPatchText(text)) {
|
||||
text = patchText(text, await Instances.findOneBy({ host: clientHost }));
|
||||
}
|
||||
|
||||
let apText = text;
|
||||
|
||||
if (quote) {
|
||||
|
@ -186,3 +193,18 @@ export async function getEmojis(names: string[]): Promise<Emoji[]> {
|
|||
|
||||
return emojis.filter((emoji) => emoji != null) as Emoji[];
|
||||
}
|
||||
export function shouldPatchText(text: string): boolean {
|
||||
return text.match(/\$INSTANCE\$(?:host|softwareName|softwareVersion|name|description)\$/) !== null;
|
||||
}
|
||||
|
||||
export function patchText(
|
||||
text: string,
|
||||
target: Instance
|
||||
): string {
|
||||
text = text.replaceAll("$INSTANCE$host$", target.host);
|
||||
text = text.replaceAll("$INSTANCE$softwareName$", target.softwareName || "softwareName");
|
||||
text = text.replaceAll("$INSTANCE$softwareVersion$", target.softwareVersion || "softwareVersion");
|
||||
text = text.replaceAll("$INSTANCE$name$", target.name || "name");
|
||||
text = text.replaceAll("$INSTANCE$description$", target.description || "description");
|
||||
return text;
|
||||
}
|
||||
|
|
|
@ -110,9 +110,9 @@ router.post("/users/:user/inbox", parseJsonBodyOrFail, inbox);
|
|||
router.get("/notes/:note", async (ctx, next) => {
|
||||
if (!isActivityPubReq(ctx)) return await next();
|
||||
|
||||
const verify = await checkFetch(ctx.req);
|
||||
if (verify !== 200) {
|
||||
ctx.status = verify;
|
||||
const { status, host } = await checkFetch(ctx.req);
|
||||
if (status !== 200) {
|
||||
ctx.status = status;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -168,7 +168,7 @@ router.get("/notes/:note", async (ctx, next) => {
|
|||
serverLogger.debug("Accepting: access criteria met");
|
||||
}
|
||||
|
||||
ctx.body = renderActivity(await renderNote(note, false));
|
||||
ctx.body = renderActivity(await renderNote(note, false, false, host));
|
||||
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
|
@ -181,9 +181,9 @@ router.get("/notes/:note", async (ctx, next) => {
|
|||
|
||||
// note activity
|
||||
router.get("/notes/:note/activity", async (ctx) => {
|
||||
const verify = await checkFetch(ctx.req);
|
||||
if (verify !== 200) {
|
||||
ctx.status = verify;
|
||||
const { status, host } = await checkFetch(ctx.req);
|
||||
if (status !== 200) {
|
||||
ctx.status = status;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -199,7 +199,7 @@ router.get("/notes/:note/activity", async (ctx) => {
|
|||
return;
|
||||
}
|
||||
|
||||
ctx.body = renderActivity(await packActivity(note));
|
||||
ctx.body = renderActivity(await packActivity(note, host));
|
||||
const meta = await fetchMeta();
|
||||
if (meta.secureMode || meta.privateMode) {
|
||||
ctx.set("Cache-Control", "private, max-age=0, must-revalidate");
|
||||
|
@ -233,9 +233,9 @@ router.get("/users/:user/publickey", async (ctx) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const verify = await checkFetch(ctx.req);
|
||||
if (verify !== 200) {
|
||||
ctx.status = verify;
|
||||
const { status } = await checkFetch(ctx.req);
|
||||
if (status !== 200) {
|
||||
ctx.status = status;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -293,10 +293,9 @@ router.get("/users/:user", async (ctx, next) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const verify = await checkFetch(ctx.req);
|
||||
if (verify !== 200) {
|
||||
ctx.status = verify;
|
||||
return;
|
||||
const { status } = await checkFetch(ctx.req);
|
||||
if (status !== 200) {
|
||||
ctx.status = status;
|
||||
}
|
||||
|
||||
const userId = ctx.params.user;
|
||||
|
@ -319,9 +318,9 @@ router.get("/@:user", async (ctx, next) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const verify = await checkFetch(ctx.req);
|
||||
if (verify !== 200) {
|
||||
ctx.status = verify;
|
||||
const { status } = await checkFetch(ctx.req);
|
||||
if (status !== 200) {
|
||||
ctx.status = status;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -342,9 +341,9 @@ router.get("/actor", async (ctx, next) => {
|
|||
|
||||
// emoji
|
||||
router.get("/emojis/:emoji", async (ctx) => {
|
||||
const verify = await checkFetch(ctx.req);
|
||||
if (verify !== 200) {
|
||||
ctx.status = verify;
|
||||
const { status } = await checkFetch(ctx.req);
|
||||
if (status !== 200) {
|
||||
ctx.status = status;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -370,9 +369,9 @@ router.get("/emojis/:emoji", async (ctx) => {
|
|||
|
||||
// like
|
||||
router.get("/likes/:like", async (ctx) => {
|
||||
const verify = await checkFetch(ctx.req);
|
||||
if (verify !== 200) {
|
||||
ctx.status = verify;
|
||||
const { status } = await checkFetch(ctx.req);
|
||||
if (status !== 200) {
|
||||
ctx.status = status;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -404,9 +403,9 @@ router.get("/likes/:like", async (ctx) => {
|
|||
router.get(
|
||||
"/follows/:follower/:followee",
|
||||
async (ctx: Router.RouterContext) => {
|
||||
const verify = await checkFetch(ctx.req);
|
||||
if (verify !== 200) {
|
||||
ctx.status = verify;
|
||||
const { status } = await checkFetch(ctx.req);
|
||||
if (status !== 200) {
|
||||
ctx.status = status;
|
||||
return;
|
||||
}
|
||||
// This may be used before the follow is completed, so we do not
|
||||
|
@ -441,9 +440,9 @@ router.get(
|
|||
|
||||
// follow request
|
||||
router.get("/follows/:followRequestId", async (ctx: Router.RouterContext) => {
|
||||
const verify = await checkFetch(ctx.req);
|
||||
if (verify !== 200) {
|
||||
ctx.status = verify;
|
||||
const { status } = await checkFetch(ctx.req);
|
||||
if (status !== 200) {
|
||||
ctx.status = status
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ import { setResponseType } from "../activitypub.js";
|
|||
import type Router from "@koa/router";
|
||||
|
||||
export default async (ctx: Router.RouterContext) => {
|
||||
const verify = await checkFetch(ctx.req);
|
||||
if (verify !== 200) {
|
||||
ctx.status = verify;
|
||||
const { status } = await checkFetch(ctx.req);
|
||||
if (status !== 200) {
|
||||
ctx.status = status;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@ import type { FindOptionsWhere } from "typeorm";
|
|||
import type Router from "@koa/router";
|
||||
|
||||
export default async (ctx: Router.RouterContext) => {
|
||||
const verify = await checkFetch(ctx.req);
|
||||
if (verify !== 200) {
|
||||
ctx.status = verify;
|
||||
const { status } = await checkFetch(ctx.req);
|
||||
if (status !== 200) {
|
||||
ctx.status = status;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@ import type { FindOptionsWhere } from "typeorm";
|
|||
import type Router from "@koa/router";
|
||||
|
||||
export default async (ctx: Router.RouterContext) => {
|
||||
const verify = await checkFetch(ctx.req);
|
||||
if (verify !== 200) {
|
||||
ctx.status = verify;
|
||||
const { status } = await checkFetch(ctx.req);
|
||||
if (status !== 200) {
|
||||
ctx.status = status;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,9 +17,9 @@ import { setResponseType } from "../activitypub.js";
|
|||
import type Router from "@koa/router";
|
||||
|
||||
export default async (ctx: Router.RouterContext) => {
|
||||
const verify = await checkFetch(ctx.req);
|
||||
if (verify !== 200) {
|
||||
ctx.status = verify;
|
||||
const { status, host } = await checkFetch(ctx.req);
|
||||
if (status !== 200) {
|
||||
ctx.status = status;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ export default async (ctx: Router.RouterContext) => {
|
|||
if (sinceId) notes.reverse();
|
||||
|
||||
const activities = await Promise.all(
|
||||
notes.map((note) => packActivity(note)),
|
||||
notes.map((note) => packActivity(note, host)),
|
||||
);
|
||||
const rendered = renderOrderedCollectionPage(
|
||||
`${partOf}?${url.query({
|
||||
|
@ -129,7 +129,10 @@ export default async (ctx: Router.RouterContext) => {
|
|||
* Pack Create<Note> or Announce Activity
|
||||
* @param note Note
|
||||
*/
|
||||
export async function packActivity(note: Note): Promise<any> {
|
||||
export async function packActivity(
|
||||
note: Note,
|
||||
clientHost: string | undefined = undefined
|
||||
): Promise<any> {
|
||||
if (
|
||||
note.renoteId &&
|
||||
note.text == null &&
|
||||
|
@ -144,5 +147,5 @@ export async function packActivity(note: Note): Promise<any> {
|
|||
);
|
||||
}
|
||||
|
||||
return renderCreate(await renderNote(note, false), note);
|
||||
return renderCreate(await renderNote(note, false, false, clientHost), note);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue