feat: pipe external urls through iframely
This commit is contained in:
@@ -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=
|
||||
|
||||
5
plugins/iframely/plugin.json
Normal file
5
plugins/iframely/plugin.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Iframely",
|
||||
"description": "Integrate Iframely to enable unfurling of arbitrary urls",
|
||||
"requiredEnvVars": ["IFRAMELY_URL", "IFRAMELY_API_KEY"]
|
||||
}
|
||||
3
plugins/iframely/server/.babelrc
Normal file
3
plugins/iframely/server/.babelrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../../server/.babelrc"
|
||||
}
|
||||
24
plugins/iframely/server/iframely.ts
Normal file
24
plugins/iframely/server/iframely.ts
Normal 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;
|
||||
3
plugins/iframely/server/unfurl.ts
Normal file
3
plugins/iframely/server/unfurl.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import Iframely from "./iframely";
|
||||
|
||||
export const unfurl = async (url: string) => Iframely.get(url);
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -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
24
server/utils/unfurl.ts
Normal 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"];
|
||||
Reference in New Issue
Block a user