blob: d7c5d425eea43b905a2b69eee4f2153a6e60709f [file] [log] [blame]
/*
* Copyright (C) 2022 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
import { Router } from 'express';
import asyncHandler from 'express-async-handler';
import { HttpStatusCode } from 'jami-web-common';
import { getLinkPreview } from 'link-preview-js';
import * as linkify from 'linkifyjs';
import { authenticateToken } from '../middleware/auth.js';
export const linkPreviewRouter = Router();
// Result of getLinkPreview from link-preview-js
type LinkPreviewJs = {
url: string;
title: string;
siteName: string | undefined;
description: string | undefined;
mediaType: string;
contentType: string | undefined;
images: string[];
videos: {
url: string | undefined;
secureUrl: string | null | undefined;
type: string | null | undefined;
width: string | undefined;
height: string | undefined;
}[];
favicons: string[];
};
linkPreviewRouter.use(authenticateToken);
const linkPreviewOptions = {
// Allowing redirection from http to https
// Code from doc: https://github.com/ospfranco/link-preview-js#redirections
followRedirects: 'manual',
handleRedirects: (baseURL: string, forwardedURL: string) => {
const urlObj = new URL(baseURL);
const forwardedURLObj = new URL(forwardedURL);
if (
forwardedURLObj.hostname === urlObj.hostname ||
forwardedURLObj.hostname === 'www.' + urlObj.hostname ||
'www.' + forwardedURLObj.hostname === urlObj.hostname
) {
return true;
} else {
return false;
}
},
} as const;
linkPreviewRouter.get(
'/',
asyncHandler(async (req, res) => {
const url = req.query.url;
if (typeof url !== 'string') {
res.status(HttpStatusCode.BadRequest).send('Invalid query parameters');
return;
}
// Add 'http' or 'https' if absent. This is required by getLinkPreview
const sanitizedUrl = linkify.find(url)[0]?.href;
if (!sanitizedUrl) {
res.status(HttpStatusCode.BadRequest).send('Invalid url');
return;
}
try {
const detailedLinkPreview = (await getLinkPreview(sanitizedUrl, linkPreviewOptions)) as LinkPreviewJs;
const linkPreview = {
title: detailedLinkPreview.title,
description: detailedLinkPreview.description,
// We might eventualy want to compare the images in order to select the best fit
// https://andrejgajdos.com/how-to-create-a-link-preview/
image: detailedLinkPreview.images[0] ?? detailedLinkPreview.favicons[0],
};
res.json(linkPreview).end();
} catch (e) {
res.status(HttpStatusCode.NotFound).send('Could not access url');
}
})
);