moved OpenGraph & oEmbed into main file; removed express-useragent

pull/20/head
tycrek 3 years ago
parent ba8b9751ce
commit cd1954f9ad
No known key found for this signature in database
GPG Key ID: 25D74F3943625263

@ -20,12 +20,11 @@ const fetch = require('node-fetch');
const marked = require('marked');
const { DateTime } = require('luxon');
const { WebhookClient, MessageEmbed } = require('discord.js');
const OpenGraph = require('./ogp');
const Thumbnail = require('./thumbnails');
const Vibrant = require('./vibrant');
const Hash = require('./hash');
const { uploadLocal, uploadS3, deleteS3 } = require('./storage');
const { path, saveData, log, verify, getTrueHttp, getTrueDomain, renameFile, generateToken, generateId, formatBytes, formatTimestamp, arrayEquals, getS3url, getDirectUrl, getResourceColor, downloadTempS3, sanitize } = require('./utils');
const { path, saveData, log, verify, getTrueHttp, getTrueDomain, renameFile, generateToken, generateId, formatBytes, formatTimestamp, arrayEquals, getS3url, getDirectUrl, getSafeExt, getResourceColor, downloadTempS3, sanitize, replaceholder } = require('./utils');
const { CODE_NO_CONTENT, CODE_BAD_REQUEST, CODE_UNAUTHORIZED, CODE_NOT_FOUND } = require('./MagicNumbers.json');
//#endregion
@ -240,27 +239,30 @@ function startup() {
app.get('/:resourceId', (req, res) => {
const { resourceId } = req.ass;
const fileData = data[resourceId];
const requiredItems = {
randomId: fileData.randomId,
originalname: escape(fileData.originalname),
mimetype: fileData.mimetype,
size: fileData.size,
timestamp: fileData.timestamp,
opengraph: fileData.opengraph,
vibrant: fileData.vibrant,
};
// If the client is a social bot (such as Discord or Instagram), send an Open Graph embed
if (req.useragent.isBot) res.type('html').send(new OpenGraph(getTrueHttp(), getTrueDomain(), resourceId, requiredItems).build());
else res.render('view', {
title: requiredItems.originalname,
const isVideo = fileData.mimetype.includes('video');
// Build OpenGraph meta tags
const og = fileData.opengraph, ogs = [''];
og.title && (ogs.push(`<meta property="og:title" content="${og.title}">`));
og.description && (ogs.push(`<meta property="og:description" content="${og.description}">`));
og.author && (ogs.push(`<meta property="og:site_name" content="${og.author}">`));
og.color && (ogs.push(`<meta name="theme-color" content="${getResourceColor(og.color, fileData.vibrant)}">`));
!isVideo && (ogs.push(`<meta name="twitter:card" content="summary_large_image">`));
// Send the view to the client
res.render('view', {
isVideo,
title: escape(fileData.originalname),
uploader: users[fileData.token].username,
timestamp: formatTimestamp(fileData.timestamp),
size: formatBytes(fileData.size),
color: getResourceColor(fileData.opengraph.color || null, fileData.vibrant),
isVideo: fileData.mimetype.includes('video'),
resourceAttr: { src: getDirectUrl(resourceId) },
discordUrl: `${getDirectUrl(resourceId)}${getSafeExt(fileData.mimetype)}`,
oembedUrl: `${getTrueHttp()}${getTrueDomain()}/${resourceId}/oembed.json`,
ogtype: isVideo ? 'video.other' : 'image',
urlType: `og:${isVideo ? 'video' : 'image'}`,
opengraph: replaceholder(ogs.join('\n'), fileData)
});
});
@ -305,10 +307,10 @@ function startup() {
res.type('json').send({
version: '1.0',
type: mimetype.includes('video') ? 'video' : 'photo',
author_name: opengraph.author,
author_url: opengraph.authorUrl,
provider_name: opengraph.provider,
provider_url: opengraph.providerUrl
provider_url: opengraph.providerUrl,
author_name: replaceholder(opengraph.author || '', data[resourceId]),
provider_name: replaceholder(opengraph.provider || '', data[resourceId])
});
});

@ -1,90 +0,0 @@
const Mustache = require('mustache');
const { homepage, version } = require('./package.json');
const { formatBytes, getDirectUrl, getResourceColor, formatTimestamp, getSafeExt } = require('./utils');
// https://ogp.me/
class OpenGraph {
http;
domain;
resourceId;
randomId;
filename;
type;
size;
timestamp;
vibrant;
title;
description;
author;
color;
constructor(http, domain, resourceId, { randomId, originalname, mimetype, size, timestamp, opengraph, vibrant }) {
this.http = http;
this.domain = domain;
this.resourceId = resourceId;
this.randomId = randomId;
this.type = mimetype;
this.filename = originalname;
this.size = size;
this.timestamp = timestamp;
this.vibrant = vibrant;
this.title = opengraph.title || '';
this.description = opengraph.description || '';
this.author = opengraph.author || '';
this.color = opengraph.color || '';
}
build() {
let resourceUrl = getDirectUrl(this.resourceId) + getSafeExt(this.type);
return Mustache.render(html, {
homepage,
version,
http: this.http,
domain: this.domain,
resourceId: this.resourceId,
resourceUrl,
media: `<${this.type.includes('video') ? 'video' : 'img'} src="${resourceUrl}" style="height: 50vh;">`,
ogtype: this.type.includes('video') ? 'video.other' : 'image',
type: this.type.includes('video') ? 'video' : 'image',
title: (this.title.length != 0) ? `<meta property="og:title" content="${this.title}">` : '',
description: (this.description.length != 0) ? `<meta property="og:description" content="${this.description}">` : '',
site: (this.author.length != 0) ? `<meta property="og:site_name" content="${this.author}">` : '',
color: (this.color.length != 0) ? `<meta name="theme-color" content="${getResourceColor(this.color, this.vibrant)}">` : '',
card: !this.type.includes('video') ? `<meta name="twitter:card" content="summary_large_image">` : '',
})
.replace(new RegExp('&size', 'g'), formatBytes(this.size))
.replace(new RegExp('&filename', 'g'), this.filename)
.replace(new RegExp('&timestamp', 'g'), formatTimestamp(this.timestamp));
}
}
const html = `
<html>
<head>
<title>ass</title>
<!-- Open Graph (https://ogp.me/) -->
<meta property="og:type" content="{{{ogtype}}}">
<meta property="og:{{{type}}}" content="{{{resourceUrl}}}">
{{{title}}}
{{{description}}}
{{{site}}}
{{{color}}}
{{{card}}}
<!-- oEmbed (https://oembed.com/) -->
<link rel="alternate" type="application/json+oembed" href="{{{http}}}{{{domain}}}/{{{resourceId}}}/oembed.json" title="oEmbed">
</head>
<body>
Open Graph response for <a href="{{{homepage}}}" target="_blank">ass</a> {{{version}}}
<br>
{{{media}}}
</body>
</html>
`;
module.exports = OpenGraph;

14
package-lock.json generated

@ -16,7 +16,6 @@
"escape-html": "^1.0.3",
"express": "^4.17.1",
"express-rate-limit": "^5.2.6",
"express-useragent": "^1.0.15",
"ffmpeg-static": "^4.3.0",
"fs-extra": "^9.1.0",
"helmet": "^4.6.0",
@ -1278,14 +1277,6 @@
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.2.6.tgz",
"integrity": "sha512-nE96xaxGfxiS5jP3tD3kIW1Jg9yQgX0rXCs3rCkZtmbWHEGyotwaezkLj7bnB41Z0uaOLM8W4AX6qHao4IZ2YA=="
},
"node_modules/express-useragent": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/express-useragent/-/express-useragent-1.0.15.tgz",
"integrity": "sha512-eq5xMiYCYwFPoekffMjvEIk+NWdlQY9Y38OsTyl13IvA728vKT+q/CSERYWzcw93HGBJcIqMIsZC5CZGARPVdg==",
"engines": {
"node": ">=4.5"
}
},
"node_modules/eyes": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
@ -3868,11 +3859,6 @@
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.2.6.tgz",
"integrity": "sha512-nE96xaxGfxiS5jP3tD3kIW1Jg9yQgX0rXCs3rCkZtmbWHEGyotwaezkLj7bnB41Z0uaOLM8W4AX6qHao4IZ2YA=="
},
"express-useragent": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/express-useragent/-/express-useragent-1.0.15.tgz",
"integrity": "sha512-eq5xMiYCYwFPoekffMjvEIk+NWdlQY9Y38OsTyl13IvA728vKT+q/CSERYWzcw93HGBJcIqMIsZC5CZGARPVdg=="
},
"eyes": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",

@ -32,7 +32,6 @@
"escape-html": "^1.0.3",
"express": "^4.17.1",
"express-rate-limit": "^5.2.6",
"express-useragent": "^1.0.15",
"ffmpeg-static": "^4.3.0",
"fs-extra": "^9.1.0",
"helmet": "^4.6.0",

@ -48,6 +48,20 @@ function formatTimestamp(timestamp) {
return DateTime.fromMillis(timestamp).toLocaleString(DateTime.DATETIME_MED);
}
function formatBytes(bytes, decimals = 2) { // skipcq: JS-0074
if (bytes === 0) return '0 Bytes';
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(KILOBYTES));
return parseFloat((bytes / Math.pow(KILOBYTES, i)).toFixed(decimals < 0 ? 0 : decimals)).toString().concat(` ${sizes[i]}`);
}
function replaceholder(data, { size, timestamp, originalname }) {
return data
.replace(new RegExp('&size', 'g'), formatBytes(size))
.replace(new RegExp('&filename', 'g'), originalname)
.replace(new RegExp('&timestamp', 'g'), formatTimestamp(timestamp));
}
const idModes = {
zws: 'zws', // Zero-width spaces (see: https://zws.im/)
og: 'original', // Use original uploaded filename
@ -68,6 +82,8 @@ module.exports = {
getDirectUrl,
getResourceColor,
formatTimestamp,
formatBytes,
replaceholder,
getSafeExt,
randomHexColour,
sanitize,
@ -86,12 +102,6 @@ module.exports = {
}),
generateToken: () => token(),
generateId: (mode, length, gfyLength, originalName) => (GENERATORS.has(mode) ? GENERATORS.get(mode)({ length, gfyLength }) : originalName),
formatBytes: (bytes, decimals = 2) => { // skipcq: JS-0074
if (bytes === 0) return '0 Bytes';
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(KILOBYTES));
return parseFloat((bytes / Math.pow(KILOBYTES, i)).toFixed(decimals < 0 ? 0 : decimals)).toString().concat(` ${sizes[i]}`);
},
arrayEquals: (arr1, arr2) => arr1.length === arr2.length && arr1.slice().sort().every((value, index) => value === arr2.slice().sort()[index]),
downloadTempS3: (file) => new Promise((resolve, reject) =>
fetch(getS3url(file.randomId, file.mimetype))

@ -8,6 +8,16 @@ html
style: include css/img.css
style.
img, video { border-color: #{color}; }
// Open Graph (https://ogp.me/)
meta(property='og:type' content=ogtype)
meta(property=urlType content=discordUrl)
.
!{opengraph}
// oEmbed (https://oembed.com/)
link(href=oembedUrl title='oEmbed' rel='alternate' type='application/json+oembed')
body
#container
#content

Loading…
Cancel
Save