mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
feat(api): 🔒 Make all media be proxied through an internal proxy
This commit is contained in:
parent
9547cd097a
commit
ead34b818f
|
|
@ -1,3 +1,4 @@
|
|||
import { proxyUrl } from "@response";
|
||||
import type { Config } from "config-manager";
|
||||
import type { InferSelectModel } from "drizzle-orm";
|
||||
import type * as Lysand from "lysand-types";
|
||||
|
|
@ -25,9 +26,9 @@ export const attachmentToAPI = (
|
|||
return {
|
||||
id: attachment.id,
|
||||
type: type as "image" | "video" | "audio" | "unknown",
|
||||
url: attachment.url,
|
||||
remote_url: attachment.remoteUrl,
|
||||
preview_url: attachment.thumbnailUrl || attachment.url,
|
||||
url: proxyUrl(attachment.url) ?? "",
|
||||
remote_url: proxyUrl(attachment.remoteUrl),
|
||||
preview_url: proxyUrl(attachment.thumbnailUrl || attachment.url),
|
||||
text_url: null,
|
||||
meta: {
|
||||
width: attachment.width || undefined,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { proxyUrl } from "@response";
|
||||
import { type InferSelectModel, and, eq } from "drizzle-orm";
|
||||
import type * as Lysand from "lysand-types";
|
||||
import { db } from "~drizzle/db";
|
||||
|
|
@ -93,8 +94,8 @@ export const fetchEmoji = async (
|
|||
export const emojiToAPI = (emoji: EmojiWithInstance): APIEmoji => {
|
||||
return {
|
||||
shortcode: emoji.shortcode,
|
||||
static_url: emoji.url, // TODO: Add static version
|
||||
url: emoji.url,
|
||||
static_url: proxyUrl(emoji.url) ?? "", // TODO: Add static version
|
||||
url: proxyUrl(emoji.url) ?? "",
|
||||
visible_in_picker: emoji.visibleInPicker,
|
||||
category: undefined,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { sanitizedHtmlStrip } from "@sanitization";
|
||||
import {
|
||||
type InferInsertModel,
|
||||
type SQL,
|
||||
|
|
@ -45,7 +46,6 @@ import { config } from "~packages/config-manager";
|
|||
import type { Attachment as APIAttachment } from "~types/mastodon/attachment";
|
||||
import type { Status as APIStatus } from "~types/mastodon/status";
|
||||
import { User } from "./user";
|
||||
import { sanitizedHtmlStrip } from "@sanitization";
|
||||
|
||||
/**
|
||||
* Gives helpers to fetch notes from database in a nice format
|
||||
|
|
@ -494,12 +494,7 @@ export class Note {
|
|||
sensitive: data.sensitive,
|
||||
spoiler_text: data.spoilerText,
|
||||
tags: [],
|
||||
uri:
|
||||
data.uri ||
|
||||
new URL(
|
||||
`/@${data.author.username}/${data.id}`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
uri: data.uri || this.getMastoURI(),
|
||||
visibility: data.visibility as APIStatus["visibility"],
|
||||
url: data.uri || this.getMastoURI(),
|
||||
bookmarked: false,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { idValidator } from "@api";
|
||||
import { getBestContentType, urlToContentFormat } from "@content_types";
|
||||
import { addUserToMeilisearch } from "@meilisearch";
|
||||
import { proxyUrl } from "@response";
|
||||
import { type SQL, and, desc, eq, inArray } from "drizzle-orm";
|
||||
import { htmlToText } from "html-to-text";
|
||||
import type * as Lysand from "lysand-types";
|
||||
|
|
@ -367,8 +368,8 @@ export class User {
|
|||
url:
|
||||
user.uri ||
|
||||
new URL(`/@${user.username}`, config.http.base_url).toString(),
|
||||
avatar: this.getAvatarUrl(config),
|
||||
header: this.getHeaderUrl(config),
|
||||
avatar: proxyUrl(this.getAvatarUrl(config)) ?? "",
|
||||
header: proxyUrl(this.getHeaderUrl(config)) ?? "",
|
||||
locked: user.isLocked,
|
||||
created_at: new Date(user.createdAt).toISOString(),
|
||||
followers_count: user.followerCount,
|
||||
|
|
@ -382,8 +383,8 @@ export class User {
|
|||
bot: user.isBot,
|
||||
source: isOwnAccount ? user.source : undefined,
|
||||
// TODO: Add static avatar and header
|
||||
avatar_static: this.getAvatarUrl(config),
|
||||
header_static: this.getHeaderUrl(config),
|
||||
avatar_static: proxyUrl(this.getAvatarUrl(config)) ?? "",
|
||||
header_static: proxyUrl(this.getHeaderUrl(config)) ?? "",
|
||||
acct: this.getAcct(),
|
||||
// TODO: Add these fields
|
||||
limited: false,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { errorResponse, response } from "@response";
|
|||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["GET"],
|
||||
route: "/api/v1/media/:id",
|
||||
route: "/api/media/:id",
|
||||
ratelimits: {
|
||||
max: 100,
|
||||
duration: 60,
|
||||
|
|
|
|||
27
server/api/media/proxy/index.ts
Normal file
27
server/api/media/proxy/index.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { apiRoute, applyConfig } from "@api";
|
||||
import { errorResponse, response } from "@response";
|
||||
import { z } from "zod";
|
||||
|
||||
export const meta = applyConfig({
|
||||
allowedMethods: ["GET"],
|
||||
route: "/api/media/proxy",
|
||||
ratelimits: {
|
||||
max: 100,
|
||||
duration: 60,
|
||||
},
|
||||
auth: {
|
||||
required: false,
|
||||
},
|
||||
});
|
||||
|
||||
export const schema = z.object({
|
||||
url: z.string(),
|
||||
});
|
||||
|
||||
export default apiRoute<typeof meta, typeof schema>(
|
||||
async (req, matchedRoute, extraData) => {
|
||||
const { url } = extraData.parsedRequest;
|
||||
|
||||
return fetch(url);
|
||||
},
|
||||
);
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { config } from "~packages/config-manager";
|
||||
|
||||
export const response = (
|
||||
data: BodyInit | null = null,
|
||||
status = 200,
|
||||
|
|
@ -69,3 +71,12 @@ export const redirect = (url: string | URL, status = 302) => {
|
|||
Location: url.toString(),
|
||||
});
|
||||
};
|
||||
|
||||
export const proxyUrl = (url: string | null) => {
|
||||
return url
|
||||
? new URL(
|
||||
`/media/proxy?url=${encodeURIComponent(url)}`,
|
||||
config.http.base_url,
|
||||
).toString()
|
||||
: url;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import xss, { type IFilterXSSOptions } from "xss";
|
||||
import { stringifyEntitiesLight } from "stringify-entities";
|
||||
import xss, { type IFilterXSSOptions } from "xss";
|
||||
|
||||
export const sanitizedHtmlStrip = (html: string) => {
|
||||
return sanitizeHtml(html, {
|
||||
|
|
|
|||
Loading…
Reference in a new issue