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