diff --git a/server/routes/api/urls/urls.ts b/server/routes/api/urls/urls.ts index a6212b804..27b390a0a 100644 --- a/server/routes/api/urls/urls.ts +++ b/server/routes/api/urls/urls.ts @@ -1,7 +1,8 @@ import Router from "koa-router"; import parseDocumentSlug from "@shared/utils/parseDocumentSlug"; import parseMentionUrl from "@shared/utils/parseMentionUrl"; -import { NotFoundError } from "@server/errors"; +import { isInternalUrl } from "@shared/utils/urls"; +import { NotFoundError, ValidationError } from "@server/errors"; import auth from "@server/middlewares/authentication"; import { rateLimiter } from "@server/middlewares/rateLimiter"; import validate from "@server/middlewares/validate"; @@ -26,12 +27,16 @@ router.post( const { user: actor } = ctx.state.auth; const urlObj = new URL(url); + // Mentions if (urlObj.protocol === "mention:") { + if (!documentId) { + throw ValidationError("Document ID is required to unfurl a mention"); + } const { modelId: userId } = parseMentionUrl(url); const [user, document] = await Promise.all([ User.findByPk(userId), - Document.findByPk(documentId!, { + Document.findByPk(documentId, { userId: actor.id, }), ]); @@ -48,20 +53,25 @@ router.post( return; } - const previewDocumentId = parseDocumentSlug(url); - if (previewDocumentId) { - const document = previewDocumentId - ? await Document.findByPk(previewDocumentId, { userId: actor.id }) - : undefined; - if (!document) { - throw NotFoundError("Document does not exist"); - } - authorize(actor, "read", document); + // Internal resources + if (isInternalUrl(url)) { + const previewDocumentId = parseDocumentSlug(url); + if (previewDocumentId) { + 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); - return; + ctx.body = presentDocument(document, actor); + return; + } + return (ctx.response.status = 204); } + // External resources if (resolvers.Iframely) { const data = await resolvers.Iframely.unfurl(url); return data.error diff --git a/shared/utils/urls.ts b/shared/utils/urls.ts index d8a280ee0..b14d85f64 100644 --- a/shared/utils/urls.ts +++ b/shared/utils/urls.ts @@ -1,6 +1,6 @@ import escapeRegExp from "lodash/escapeRegExp"; import env from "../env"; -import { parseDomain } from "./domains"; +import { getBaseDomain, parseDomain } from "./domains"; /** * Prepends the CDN url to the given path (If a CDN is configured). @@ -15,10 +15,6 @@ export function cdnPath(path: string): string { /** * Returns true if the given string is a link to inside the application. * - * Important Note: If this is called server-side, it will always return false. - * The reason this is in a shared util is because it's used in an editor plugin - * which is also in the shared code - * * @param url The url to check. * @returns True if the url is internal, false otherwise. */ @@ -36,10 +32,10 @@ export function isInternalUrl(href: string) { const outline = typeof window !== "undefined" ? parseDomain(window.location.href) - : undefined; - + : parseDomain(env.URL); const domain = parseDomain(href); - return outline?.host === domain.host; + + return outline.host === domain.host || domain.host.endsWith(getBaseDomain()); } /**