feat: pipe external urls through iframely

This commit is contained in:
Apoorv Mishra
2023-07-23 08:16:38 +05:30
parent 15c8a4867f
commit 43a91626b2
9 changed files with 98 additions and 13 deletions

View File

@@ -181,3 +181,7 @@ RATE_LIMITER_ENABLED=true
# Configure default throttling parameters for rate limiter
RATE_LIMITER_REQUESTS=1000
RATE_LIMITER_DURATION_WINDOW=60
# Iframely API config
IFRAMELY_URL=
IFRAMELY_API_KEY=

View File

@@ -0,0 +1,5 @@
{
"name": "Iframely",
"description": "Integrate Iframely to enable unfurling of arbitrary urls",
"requiredEnvVars": ["IFRAMELY_URL", "IFRAMELY_API_KEY"]
}

View File

@@ -0,0 +1,3 @@
{
"extends": "../../../server/.babelrc"
}

View File

@@ -0,0 +1,24 @@
import fetch from "fetch-with-proxy";
import env from "@server/env";
import { InvalidRequestError } from "@server/errors";
class Iframely {
private static apiUrl = `${env.IFRAMELY_URL}/api`;
private static apiKey = env.IFRAMELY_API_KEY;
public static async get(url: string, type = "oembed") {
try {
const res = await fetch(
`${this.apiUrl}/${type}?url=${encodeURIComponent(url)}&api_key=${
this.apiKey
}`
);
const data = await res.json();
return data;
} catch (err) {
throw InvalidRequestError(err);
}
}
}
export default Iframely;

View File

@@ -0,0 +1,3 @@
import Iframely from "./iframely";
export const unfurl = async (url: string) => Iframely.get(url);

View File

@@ -601,6 +601,24 @@ export class Environment {
this.AWS_S3_UPLOAD_MAX_SIZE
);
/**
* Iframely url
*/
@IsOptional()
@IsUrl({
require_tld: false,
allow_underscores: true,
protocols: ["http", "https"],
})
public IFRAMELY_URL = process.env.IFRAMELY_URL ?? "https://iframe.ly";
/**
* Iframely API key
*/
@IsOptional()
@CannotUseWithout("IFRAMELY_URL")
public IFRAMELY_API_KEY = this.toOptionalString(process.env.IFRAMELY_API_KEY);
/**
* The product name
*/

View File

@@ -10,6 +10,7 @@ import { authorize } from "@server/policies";
import { presentDocument, presentMention } from "@server/presenters/unfurls";
import { APIContext } from "@server/types";
import { RateLimiterStrategy } from "@server/utils/RateLimiter";
import { Iframely } from "@server/utils/unfurl";
import * as T from "./schema";
const router = new Router();
@@ -47,22 +48,21 @@ router.post(
}
const previewDocumentId = parseDocumentSlug(url);
if (!previewDocumentId) {
ctx.response.status = 204;
if (previewDocumentId) {
const document = previewDocumentId
? await Document.findByPk(previewDocumentId)
: undefined;
if (!document) {
throw NotFoundError("Document does not exist");
}
authorize(actor, "read", document);
ctx.body = presentDocument(document, actor);
return;
}
const document = previewDocumentId
? await Document.findByPk(previewDocumentId, {
userId: actor.id,
})
: undefined;
if (!document) {
throw NotFoundError("Document does not exist");
}
authorize(actor, "read", document);
ctx.body = presentDocument(document, actor);
const data = await Iframely.unfurl(url);
ctx.body = data;
}
);

View File

@@ -446,3 +446,7 @@ export type CollectionJSONExport = {
[id: string]: AttachmentJSONExport;
};
};
export type UnfurlResolver = {
unfurl: (url: string) => Promise<any>;
};

24
server/utils/unfurl.ts Normal file
View File

@@ -0,0 +1,24 @@
import path from "path";
import glob from "glob";
import { startCase } from "lodash";
import env from "@server/env";
import Logger from "@server/logging/Logger";
import { UnfurlResolver } from "@server/types";
const resolvers: Record<string, UnfurlResolver> = {};
const rootDir = env.ENVIRONMENT === "test" ? "" : "build";
glob
.sync(path.join(rootDir, "plugins/*/server/unfurl.js"))
.forEach((filePath: string) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const resolver: UnfurlResolver = require(path.join(
process.cwd(),
filePath
));
const name = startCase(filePath.split("/")[2]);
resolvers[name] = resolver;
Logger.debug("utils", `Registered unfurl resolver ${filePath}`);
});
export const Iframely = resolvers["Iframely"];