server/utils/content_types.ts

96 lines
2.4 KiB
TypeScript
Raw Normal View History

import type { ContentFormatSchema } from "@versia/sdk/schemas";
import { config } from "@versia-server/config";
2025-01-02 04:52:30 +01:00
import { htmlToText as htmlToTextLib } from "html-to-text";
2024-04-10 04:05:02 +02:00
import { lookup } from "mime-types";
import type { z } from "zod";
export const getBestContentType = (
content?: z.infer<typeof ContentFormatSchema> | null,
): {
content: string;
format: string;
} => {
if (!content) {
return { content: "", format: "text/plain" };
}
2024-04-10 04:05:02 +02:00
const bestFormatsRanked = [
"text/x.misskeymarkdown",
"text/html",
"text/markdown",
"text/plain",
];
for (const format of bestFormatsRanked) {
if (content[format]) {
2024-04-10 04:05:02 +02:00
return { content: content[format].content, format };
}
2024-04-07 07:30:49 +02:00
}
2024-04-10 04:05:02 +02:00
return { content: "", format: "text/plain" };
};
export const urlToContentFormat = (
url: URL,
contentType?: string,
): z.infer<typeof ContentFormatSchema> | null => {
if (url.href.startsWith("https://api.dicebear.com/")) {
2024-04-10 04:05:02 +02:00
return {
"image/svg+xml": {
content: url.toString(),
remote: true,
2024-04-10 04:05:02 +02:00
},
};
2024-04-07 07:30:49 +02:00
}
2024-04-10 04:05:02 +02:00
const mimeType =
contentType ||
lookup(url.toString().replace(url.search, "")) ||
2024-04-10 04:05:02 +02:00
"application/octet-stream";
return {
[mimeType]: {
content: url.toString(),
remote: true,
2024-04-10 04:05:02 +02:00
},
};
};
export const mimeLookup = (url: URL): Promise<string> => {
const urlWithoutSearch = url.toString().replace(url.search, "");
// Strip query params from URL to get the proper file extension
const naiveLookup = lookup(urlWithoutSearch);
if (naiveLookup) {
return Promise.resolve(naiveLookup);
}
const fetchLookup = fetch(url, {
method: "HEAD",
2024-08-26 18:15:14 +02:00
// @ts-expect-error Proxy is a Bun-specific feature
proxy: config.http.proxy_address,
})
.then(
(response) =>
response.headers.get("content-type") ||
"application/octet-stream",
)
.catch(() => "application/octet-stream");
return fetchLookup;
};
export const htmlToText = (html: string): string => {
return htmlToTextLib(html, {
selectors: [
{
selector: "a",
options: {
hideLinkHrefIfSameAsText: true,
ignoreHref: true,
},
},
],
});
};