diff --git a/ass.js b/ass.js index 9856e5a..ecaee33 100755 --- a/ass.js +++ b/ass.js @@ -25,7 +25,7 @@ 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, arrayEquals, getS3url, downloadTempS3, sanitize } = require('./utils'); +const { path, saveData, log, verify, getTrueHttp, getTrueDomain, renameFile, generateToken, generateId, formatBytes, formatTimestamp, arrayEquals, getS3url, getDirectUrl, getResourceColor, downloadTempS3, sanitize } = require('./utils'); const { CODE_NO_CONTENT, CODE_BAD_REQUEST, CODE_UNAUTHORIZED, CODE_NOT_FOUND } = require('./MagicNumbers.json'); //#endregion @@ -252,8 +252,16 @@ function startup() { }; // If the client is a social bot (such as Discord or Instagram), send an Open Graph embed - if (req.useragent.isBot) return res.type('html').send(new OpenGraph(getTrueHttp(), getTrueDomain(), resourceId, requiredItems).build()); - else res.redirect(`${req.url}/direct`); + if (req.useragent.isBot) res.type('html').send(new OpenGraph(getTrueHttp(), getTrueDomain(), resourceId, requiredItems).build()); + else res.render('view', { + title: requiredItems.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) }, + }); }); // Direct resource diff --git a/ogp.js b/ogp.js index 6cd47a6..4639c6b 100644 --- a/ogp.js +++ b/ogp.js @@ -1,8 +1,6 @@ const Mustache = require('mustache'); -const DateTime = require('luxon').DateTime; const { homepage, version } = require('./package.json'); -const { s3enabled } = require('./config.json'); -const { formatBytes, randomHexColour, getS3url, getSafeExt } = require('./utils'); +const { formatBytes, getDirectUrl, getResourceColor, formatTimestamp, getSafeExt } = require('./utils'); // https://ogp.me/ class OpenGraph { @@ -41,7 +39,7 @@ class OpenGraph { } build() { - let resourceUrl = !s3enabled ? (this.http + this.domain + '/' + this.resourceId + '/direct' + getSafeExt(this.type)) : getS3url(this.randomId, this.type); + let resourceUrl = getDirectUrl(this.resourceId) + getSafeExt(this.type); return Mustache.render(html, { homepage, version, @@ -57,16 +55,12 @@ class OpenGraph { title: (this.title.length != 0) ? `` : '', description: (this.description.length != 0) ? `` : '', site: (this.author.length != 0) ? `` : '', - color: (this.color.length != 0) ? `` : '', + color: (this.color.length != 0) ? `` : '', card: !this.type.includes('video') ? `` : '', }) .replace(new RegExp('&size', 'g'), formatBytes(this.size)) .replace(new RegExp('&filename', 'g'), this.filename) - .replace(new RegExp('×tamp', 'g'), DateTime.fromMillis(this.timestamp).toLocaleString(DateTime.DATETIME_MED)); - } - - getBuildColor() { - return this.color === '&random' ? randomHexColour() : this.color === '&vibrant' ? this.vibrant : this.color; + .replace(new RegExp('×tamp', 'g'), formatTimestamp(this.timestamp)); } } diff --git a/utils.js b/utils.js index 04c12e4..d12b171 100755 --- a/utils.js +++ b/utils.js @@ -1,7 +1,8 @@ const fs = require('fs-extra'); const Path = require('path'); const fetch = require('node-fetch'); -const sanitize = require("sanitize-filename"); +const sanitize = require('sanitize-filename'); +const { DateTime } = require('luxon'); const token = require('./generators/token'); const zwsGen = require('./generators/zws'); const randomGen = require('./generators/random'); @@ -27,6 +28,10 @@ function getS3url(s3key, type) { return `https://${s3bucket}.${s3endpoint}/${s3key}${getSafeExt(type)}`; } +function getDirectUrl(resourceId) { + return getTrueHttp() + getTrueDomain() + `/${resourceId}/direct`; +} + function randomHexColour() { // From: https://www.geeksforgeeks.org/javascript-generate-random-hex-codes-color/ const letters = "0123456789ABCDEF"; let colour = '#'; @@ -35,6 +40,14 @@ function randomHexColour() { // From: https://www.geeksforgeeks.org/javascript-g return colour; } +function getResourceColor(colorValue, vibrantValue) { + return colorValue === '&random' ? randomHexColour() : colorValue === '&vibrant' ? vibrantValue : colorValue; +} + +function formatTimestamp(timestamp) { + return DateTime.fromMillis(timestamp).toLocaleString(DateTime.DATETIME_MED); +} + const idModes = { zws: 'zws', // Zero-width spaces (see: https://zws.im/) og: 'original', // Use original uploaded filename @@ -51,7 +64,13 @@ module.exports = { path, getTrueHttp, getTrueDomain, + getS3url, + getDirectUrl, + getResourceColor, + formatTimestamp, + getSafeExt, randomHexColour, + sanitize, log: console.log, saveData: (data) => fs.writeJsonSync(Path.join(__dirname, 'data.json'), data, { spaces: 4 }), verify: (req, users) => req.headers.authorization && Object.prototype.hasOwnProperty.call(users, req.headers.authorization), @@ -78,7 +97,4 @@ module.exports = { fetch(getS3url(file.randomId, file.mimetype)) .then((f2) => f2.body.pipe(fs.createWriteStream(Path.join(__dirname, diskFilePath, sanitize(file.originalname))).on('close', () => resolve()))) .catch(reject)), - getS3url, - getSafeExt, - sanitize } diff --git a/views/css/img.css b/views/css/img.css new file mode 100644 index 0000000..9f741ab --- /dev/null +++ b/views/css/img.css @@ -0,0 +1,5 @@ +img, video { + border-left-width: 4px; + border-left-style: solid; + border-radius: 4px; +} diff --git a/views/css/view.css b/views/css/view.css new file mode 100644 index 0000000..13668c1 --- /dev/null +++ b/views/css/view.css @@ -0,0 +1,29 @@ +#container { + display: flex; + + justify-content: center; + align-items: center; + text-align: center; + + width: 100%; + height: 100%; +} + +#content { + background-color: #151515; + border-radius: 24px; +} + +#footer { + font-size-adjust: 0.3; + margin-left: 16px; + margin-right: 16px; +} + +img, video { + max-height: 50vh; +} + +figcaption strong { + font-weight: bold; +} diff --git a/views/head.pug b/views/head.pug index 7affe9b..b878b54 100644 --- a/views/head.pug +++ b/views/head.pug @@ -1,18 +1,16 @@ meta(name='viewport' content='width=device-width, initial-scale=1.0, viewport-fit=cover') +//- Markdown stylesheet & Highlight.js theme style: include css/modest.css style: include css/dracula.highlight.js.css +//- DarkReader for easy dark modes & Highlight.js for codeblocks script(src='https://cdn.jsdelivr.net/npm/darkreader@4/darkreader.min.js') script(src='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.7.1/build/highlight.min.js') script. // Dark mode with Dark Reader DarkReader.setFetchMethod(window.fetch); - DarkReader.enable({ - brightness: 100, - contrast: 100, - sepia: 0 - }); + DarkReader.enable({}, { css: '#content { background-color: #121212 !important; }' }); // Fix the card colour // Highlight code blocks hljs.highlightAll(); diff --git a/views/view.pug b/views/view.pug new file mode 100644 index 0000000..14c4193 --- /dev/null +++ b/views/view.pug @@ -0,0 +1,25 @@ +html + head + title!=title + include head + style: include css/font.css + style: include css/view.css + if color !== null + style: include css/img.css + style. + img, video { border-color: #{color}; } + body + #container + #content + h4!=title + figure + if isVideo + video#media(controls loop muted playsinline preload='metadata')&attributes(resourceAttr) + else + img#media(decoding='async')&attributes(resourceAttr) + figcaption + br + span Uploaded by #[strong!=uploader] + br + small #{timestamp} (#{size}) + #footer: p Image hosted by #[a(href='https://github.com/tycrek/ass' target='_blank'): strong ass], the superior self-hosted ShareX server