From 2784675aa4f49167edd8b7865a6597ae3c58baf3 Mon Sep 17 00:00:00 2001 From: tycrek Date: Fri, 1 Oct 2021 09:42:43 -0600 Subject: [PATCH 1/6] Switched to `discord-webhook-node`, dropping `discord.js` for webhooks (closes #74) Users will need to update their webhook headers in this update for webhooks to continue to work. --- README.md | 13 +-- package-lock.json | 207 ++++++++-------------------------------------- package.json | 2 +- routers/upload.js | 25 +++--- 4 files changed, 52 insertions(+), 195 deletions(-) diff --git a/README.md b/README.md index b19c6f1..e56a613 100755 --- a/README.md +++ b/README.md @@ -261,20 +261,11 @@ You can insert certain metadata into your embeds with these placeholders: ### Webhooks -You may use Discord webhooks as an easy way to keep track of your uploads. The first step is to [create a new Webhook]. You only need to follow the first section, **Making a Webhook**. Once you are done that, click **Copy Webhook URL**. Next, paste your URL into a text editor. Extract these two values from the URL: - -``` -https://discord.com/api/webhooks/12345678910/T0kEn0fw3Bh00K - ^^^^^^^^^^ ^^^^^^^^^^^^ - Webhook ID Webhook Token -``` - -Once you have these, add the following HTTP headers to your ShareX config: +You may use Discord webhooks as an easy way to keep track of your uploads. The first step is to [create a new Webhook]. You only need to follow the first section, **Making a Webhook**. Once you are done that, click **Copy Webhook URL**. Finally, add these headers to your custom uploader: | Header | Purpose | | ------ | ------- | -| **`X-Ass-Webhook-Client`** | The **Webhook ID** | -| **`X-Ass-Webhook-Token`** | The **Webhook Token** | +| **`X-Ass-Webhook-Url`** | The **Webhook URL** you copied | | **`X-Ass-Webhook-Username`** | (Optional) the "username" of the Webhook; can be set to whatever you want | | **`X-Ass-Webhook-Avatar`** | (Optional) URL to an image to use as the Webhook avatar. Use the **full** URL including `https://` | diff --git a/package-lock.json b/package-lock.json index f3a8aee..0f7d57f 100755 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "aws-sdk": "^2.930.0", "check-node-version": "^4.1.0", "crypto-random-string": "3.3.1", - "discord.js": "^12.5.3", + "discord-webhook-node": "^1.1.8", "escape-html": "^1.0.3", "express": "^4.17.1", "express-rate-limit": "^5.2.6", @@ -102,24 +102,6 @@ "node": ">=6.0.0" } }, - "node_modules/@discordjs/collection": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz", - "integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ==" - }, - "node_modules/@discordjs/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/@jimp/bmp": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.16.1.tgz", @@ -728,17 +710,6 @@ "@vibrant/types": "^3.2.1-alpha.1" } }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, "node_modules/accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -1522,23 +1493,13 @@ "node": ">=4.5.0" } }, - "node_modules/discord.js": { - "version": "12.5.3", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-12.5.3.tgz", - "integrity": "sha512-D3nkOa/pCkNyn6jLZnAiJApw2N9XrIsXUAdThf01i7yrEuqUmDGc7/CexVWwEcgbQR97XQ+mcnqJpmJ/92B4Aw==", - "deprecated": "no longer supported", + "node_modules/discord-webhook-node": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/discord-webhook-node/-/discord-webhook-node-1.1.8.tgz", + "integrity": "sha512-3u0rrwywwYGc6HrgYirN/9gkBYqmdpvReyQjapoXARAHi0P0fIyf3W5tS5i3U3cc7e44E+e7dIHYUeec7yWaug==", "dependencies": { - "@discordjs/collection": "^0.1.6", - "@discordjs/form-data": "^3.0.1", - "abort-controller": "^3.0.0", - "node-fetch": "^2.6.1", - "prism-media": "^1.2.9", - "setimmediate": "^1.0.5", - "tweetnacl": "^1.0.3", - "ws": "^7.4.4" - }, - "engines": { - "node": ">=12.0.0" + "form-data": "^3.0.0", + "node-fetch": "^2.6.0" } }, "node_modules/doctypes": { @@ -1611,14 +1572,6 @@ "node": ">= 0.6" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, "node_modules/events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", @@ -1761,6 +1714,19 @@ "imul": "^1.0.0" } }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -2635,31 +2601,6 @@ "node": ">=4.0.0" } }, - "node_modules/prism-media": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.2.tgz", - "integrity": "sha512-L6UsGHcT6i4wrQhFF1aPK+MNYgjRqR2tUoIqEY+CG1NqVkMjPRKzS37j9f8GiYPlD6wG9ruBj+q5Ax+bH8Ik1g==", - "peerDependencies": { - "@discordjs/opus": "^0.5.0", - "ffmpeg-static": "^4.2.7 || ^3.0.0 || ^2.4.0", - "node-opus": "^0.3.3", - "opusscript": "^0.0.8" - }, - "peerDependenciesMeta": { - "@discordjs/opus": { - "optional": true - }, - "ffmpeg-static": { - "optional": true - }, - "node-opus": { - "optional": true - }, - "opusscript": { - "optional": true - } - } - }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -3077,11 +3018,6 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, "node_modules/setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", @@ -3320,11 +3256,6 @@ "utf8-byte-length": "^1.0.1" } }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, "node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -3567,26 +3498,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "node_modules/ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/xhr": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", @@ -3740,21 +3651,6 @@ "parse-cache-control": "^1.0.1" } }, - "@discordjs/collection": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz", - "integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ==" - }, - "@discordjs/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, "@jimp/bmp": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.16.1.tgz", @@ -4230,14 +4126,6 @@ "@vibrant/types": "^3.2.1-alpha.1" } }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -4835,19 +4723,13 @@ "streamsearch": "0.1.2" } }, - "discord.js": { - "version": "12.5.3", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-12.5.3.tgz", - "integrity": "sha512-D3nkOa/pCkNyn6jLZnAiJApw2N9XrIsXUAdThf01i7yrEuqUmDGc7/CexVWwEcgbQR97XQ+mcnqJpmJ/92B4Aw==", + "discord-webhook-node": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/discord-webhook-node/-/discord-webhook-node-1.1.8.tgz", + "integrity": "sha512-3u0rrwywwYGc6HrgYirN/9gkBYqmdpvReyQjapoXARAHi0P0fIyf3W5tS5i3U3cc7e44E+e7dIHYUeec7yWaug==", "requires": { - "@discordjs/collection": "^0.1.6", - "@discordjs/form-data": "^3.0.1", - "abort-controller": "^3.0.0", - "node-fetch": "^2.6.1", - "prism-media": "^1.2.9", - "setimmediate": "^1.0.5", - "tweetnacl": "^1.0.3", - "ws": "^7.4.4" + "form-data": "^3.0.0", + "node-fetch": "^2.6.0" } }, "doctypes": { @@ -4908,11 +4790,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", @@ -5030,6 +4907,16 @@ "imul": "^1.0.0" } }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -5714,12 +5601,6 @@ "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==" }, - "prism-media": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.2.tgz", - "integrity": "sha512-L6UsGHcT6i4wrQhFF1aPK+MNYgjRqR2tUoIqEY+CG1NqVkMjPRKzS37j9f8GiYPlD6wG9ruBj+q5Ax+bH8Ik1g==", - "requires": {} - }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -6065,11 +5946,6 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", @@ -6250,11 +6126,6 @@ "utf8-byte-length": "^1.0.1" } }, - "tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -6443,12 +6314,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", - "requires": {} - }, "xhr": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", diff --git a/package.json b/package.json index e6c15c7..c9a744d 100755 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "aws-sdk": "^2.930.0", "check-node-version": "^4.1.0", "crypto-random-string": "3.3.1", - "discord.js": "^12.5.3", + "discord-webhook-node": "^1.1.8", "escape-html": "^1.0.3", "express": "^4.17.1", "express-rate-limit": "^5.2.6", diff --git a/routers/upload.js b/routers/upload.js index 65af9e7..875cbbf 100644 --- a/routers/upload.js +++ b/routers/upload.js @@ -1,7 +1,7 @@ const fs = require('fs-extra'); //const rateLimit = require('express-rate-limit'); const { DateTime } = require('luxon'); -const { WebhookClient, MessageEmbed } = require('discord.js'); +const { Webhook, MessageBuilder } = require('discord-webhook-node'); const { doUpload, processUploaded } = require('../storage'); const { maxUploadSize, resourceIdSize, gfyIdSize, resourceIdType, spaceReplace } = require('../config.json'); const { path, log, verify, getTrueHttp, getTrueDomain, generateId, formatBytes } = require('../utils'); @@ -84,25 +84,26 @@ router.post('/', (req, res, next) => { // After we have sent the user the response, also send a Webhook to Discord (if headers are present) if (req.headers['x-ass-webhook-client'] && req.headers['x-ass-webhook-token']) { - const client = req.headers['x-ass-webhook-client'] - // Build the webhook client & embed - const whc = new WebhookClient(client, req.headers['x-ass-webhook-token']); - const embed = new MessageEmbed() + // Build the webhook + const hook = new Webhook(req.headers['x-ass-webhook-url']); + hook.setUsername(req.headers['x-ass-webhook-username'] || 'ass'); + hook.setAvatar(req.headers['x-ass-webhook-avatar'] || ASS_LOGO); + + // Build the embed + const embed = new MessageBuilder() .setTitle(logInfo) .setURL(resourceUrl) .setDescription(`**Size:** \`${formatBytes(req.file.size)}\`\n**[Delete](${deleteUrl})**`) .setThumbnail(thumbnailUrl) .setColor(req.file.vibrant) - .setTimestamp(req.file.timestamp); + .setTimestamp(); // Send the embed to the webhook, then delete the client after to free resources - log.debug('Sending webhook to client', client); - whc.send(null, { - username: req.headers['x-ass-webhook-username'] || 'ass', - avatarURL: req.headers['x-ass-webhook-avatar'] || ASS_LOGO, - embeds: [embed] - }).then(() => log.debug('Webhook sent').callback(() => whc.destroy())); + log.debug('Sending embed to webhook'); + hook.send(embed) + .then(() => log.debug('Webhook sent')) + .catch((err) => log.error('Webhook error').err(err)); } // Also update the users upload count From 29f7f25f387200856bde9fcfcd46f6e548b99be2 Mon Sep 17 00:00:00 2001 From: tycrek Date: Fri, 1 Oct 2021 10:40:50 -0600 Subject: [PATCH 2/6] Added check for duplicate ID's (closes #75) --- routers/upload.js | 130 ++++++++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 56 deletions(-) diff --git a/routers/upload.js b/routers/upload.js index 875cbbf..1383fd9 100644 --- a/routers/upload.js +++ b/routers/upload.js @@ -64,62 +64,80 @@ router.post('/', (req, res, next) => { // Fix spaces in originalname req.file.originalname = req.file.originalname.replace(/\s/g, spaceReplace === '!' ? '' : spaceReplace); - // Save the file information - const resourceId = generateId(generator, resourceIdSize, req.headers['x-ass-gfycat'] || gfyIdSize, req.file.originalname); - log.debug('Saving data', data.name); - data.put(resourceId.split('.')[0], req.file).then(() => { - // Log the upload - const logInfo = `${req.file.originalname} (${req.file.mimetype}, ${formatBytes(req.file.size)})`; - log.success('File uploaded', logInfo, `uploaded by ${users[req.token] ? users[req.token].username : ''}`); - - // Build the URLs - const resourceUrl = `${getTrueHttp()}${trueDomain}/${resourceId}`; - const thumbnailUrl = `${getTrueHttp()}${trueDomain}/${resourceId}/thumbnail`; - const deleteUrl = `${getTrueHttp()}${trueDomain}/${resourceId}/delete/${req.file.deleteId}`; - - // Send the response - res.type('json').send({ resource: resourceUrl, thumbnail: thumbnailUrl, delete: deleteUrl }) - .on('finish', () => { - log.debug('Upload response sent'); - - // After we have sent the user the response, also send a Webhook to Discord (if headers are present) - if (req.headers['x-ass-webhook-client'] && req.headers['x-ass-webhook-token']) { - - // Build the webhook - const hook = new Webhook(req.headers['x-ass-webhook-url']); - hook.setUsername(req.headers['x-ass-webhook-username'] || 'ass'); - hook.setAvatar(req.headers['x-ass-webhook-avatar'] || ASS_LOGO); - - // Build the embed - const embed = new MessageBuilder() - .setTitle(logInfo) - .setURL(resourceUrl) - .setDescription(`**Size:** \`${formatBytes(req.file.size)}\`\n**[Delete](${deleteUrl})**`) - .setThumbnail(thumbnailUrl) - .setColor(req.file.vibrant) - .setTimestamp(); - - // Send the embed to the webhook, then delete the client after to free resources - log.debug('Sending embed to webhook'); - hook.send(embed) - .then(() => log.debug('Webhook sent')) - .catch((err) => log.error('Webhook error').err(err)); - } - - // Also update the users upload count - if (!users[req.token]) { - const generateUsername = () => generateId('random', 20, null); // skipcq: JS-0074 - let username = generateUsername(); - while (Object.values(users).findIndex((user) => user.username === username) !== -1) // skipcq: JS-0073 - username = generateUsername(); - users[req.token] = { username, count: 0 }; - } - users[req.token].count += 1; - fs.writeJsonSync(path('auth.json'), { users }, { spaces: 4 }); - - log.debug('Upload request flow completed', ''); - }); - }).catch(next); + // Generate a unique resource ID + let resourceId; + + // Function to call to generate a fresh ID. Used for multiple attempts in case an ID is already taken + const gen = () => generateId(generator, resourceIdSize, req.headers['x-ass-gfycat'] || gfyIdSize, req.file.originalname); + + // Called by a promise, this will recursively resolve itself until a unique ID is found + // TODO: Add max attempts in case all available ID's are taken + function genCheckId(resolve, reject) { + let uniqueId = gen(); + data.has(uniqueId) + .then((exists) => (log.debug('ID check', exists ? 'Taken' : 'Available'), exists ? genCheckId(resolve, reject) : resolve(uniqueId))) + .catch(reject); + } + + new Promise((resolve, reject) => genCheckId(resolve, reject)) + .then((uniqueId) => { + resourceId = uniqueId; + log.debug('Saving data', data.name); + }) + .then(() => data.put(resourceId.split('.')[0], req.file)) + .then(() => { + // Log the upload + const logInfo = `${req.file.originalname} (${req.file.mimetype}, ${formatBytes(req.file.size)})`; + log.success('File uploaded', logInfo, `uploaded by ${users[req.token] ? users[req.token].username : ''}`); + + // Build the URLs + const resourceUrl = `${getTrueHttp()}${trueDomain}/${resourceId}`; + const thumbnailUrl = `${getTrueHttp()}${trueDomain}/${resourceId}/thumbnail`; + const deleteUrl = `${getTrueHttp()}${trueDomain}/${resourceId}/delete/${req.file.deleteId}`; + + // Send the response + res.type('json').send({ resource: resourceUrl, thumbnail: thumbnailUrl, delete: deleteUrl }) + .on('finish', () => { + log.debug('Upload response sent'); + + // After we have sent the user the response, also send a Webhook to Discord (if headers are present) + if (req.headers['x-ass-webhook-client'] && req.headers['x-ass-webhook-token']) { + + // Build the webhook + const hook = new Webhook(req.headers['x-ass-webhook-url']); + hook.setUsername(req.headers['x-ass-webhook-username'] || 'ass'); + hook.setAvatar(req.headers['x-ass-webhook-avatar'] || ASS_LOGO); + + // Build the embed + const embed = new MessageBuilder() + .setTitle(logInfo) + .setURL(resourceUrl) + .setDescription(`**Size:** \`${formatBytes(req.file.size)}\`\n**[Delete](${deleteUrl})**`) + .setThumbnail(thumbnailUrl) + .setColor(req.file.vibrant) + .setTimestamp(); + + // Send the embed to the webhook, then delete the client after to free resources + log.debug('Sending embed to webhook'); + hook.send(embed) + .then(() => log.debug('Webhook sent')) + .catch((err) => log.error('Webhook error').err(err)); + } + + // Also update the users upload count + if (!users[req.token]) { + const generateUsername = () => generateId('random', 20, null); // skipcq: JS-0074 + let username = generateUsername(); + while (Object.values(users).findIndex((user) => user.username === username) !== -1) // skipcq: JS-0073 + username = generateUsername(); + users[req.token] = { username, count: 0 }; + } + users[req.token].count += 1; + fs.writeJsonSync(path('auth.json'), { users }, { spaces: 4 }); + + log.debug('Upload request flow completed', ''); + }); + }).catch(next); }); module.exports = router; From 31a973196de1a720c97ccd0c47f47e5af6e48423 Mon Sep 17 00:00:00 2001 From: tycrek Date: Fri, 1 Oct 2021 10:47:50 -0600 Subject: [PATCH 3/6] Fix DeepSource issues --- routers/upload.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routers/upload.js b/routers/upload.js index 1383fd9..4d17d88 100644 --- a/routers/upload.js +++ b/routers/upload.js @@ -65,7 +65,7 @@ router.post('/', (req, res, next) => { req.file.originalname = req.file.originalname.replace(/\s/g, spaceReplace === '!' ? '' : spaceReplace); // Generate a unique resource ID - let resourceId; + let resourceId = ''; // Function to call to generate a fresh ID. Used for multiple attempts in case an ID is already taken const gen = () => generateId(generator, resourceIdSize, req.headers['x-ass-gfycat'] || gfyIdSize, req.file.originalname); @@ -73,9 +73,9 @@ router.post('/', (req, res, next) => { // Called by a promise, this will recursively resolve itself until a unique ID is found // TODO: Add max attempts in case all available ID's are taken function genCheckId(resolve, reject) { - let uniqueId = gen(); + const uniqueId = gen(); data.has(uniqueId) - .then((exists) => (log.debug('ID check', exists ? 'Taken' : 'Available'), exists ? genCheckId(resolve, reject) : resolve(uniqueId))) + .then((exists) => (log.debug('ID check', exists ? 'Taken' : 'Available'), exists ? genCheckId(resolve, reject) : resolve(uniqueId))) // skipcq: JS-0090 .catch(reject); } From 3cb2ca135d9724e85c4ba2451bcb951eadc25eb7 Mon Sep 17 00:00:00 2001 From: tycrek Date: Fri, 1 Oct 2021 10:55:34 -0600 Subject: [PATCH 4/6] Added max attempts to avoid infinite hang --- routers/upload.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/routers/upload.js b/routers/upload.js index 4d17d88..6cd87c7 100644 --- a/routers/upload.js +++ b/routers/upload.js @@ -70,12 +70,21 @@ router.post('/', (req, res, next) => { // Function to call to generate a fresh ID. Used for multiple attempts in case an ID is already taken const gen = () => generateId(generator, resourceIdSize, req.headers['x-ass-gfycat'] || gfyIdSize, req.file.originalname); + // Keeps track of the number of attempts in case all ID's are taken + const attempts = { + count: 0, + max: 50 + }; + // Called by a promise, this will recursively resolve itself until a unique ID is found - // TODO: Add max attempts in case all available ID's are taken function genCheckId(resolve, reject) { const uniqueId = gen(); + attempts.count++; data.has(uniqueId) - .then((exists) => (log.debug('ID check', exists ? 'Taken' : 'Available'), exists ? genCheckId(resolve, reject) : resolve(uniqueId))) // skipcq: JS-0090 + .then((exists) => { + log.debug('ID check', exists ? 'Taken' : 'Available'); + return attempts.count - 1 >= attempts.max ? reject(new Error('No ID\'s remaining')) : exists ? genCheckId(resolve, reject) : resolve(uniqueId); + }) .catch(reject); } From 30d5ddc0cc227ed01140d3734b608a58c51cf1fb Mon Sep 17 00:00:00 2001 From: tycrek Date: Fri, 1 Oct 2021 11:41:30 -0600 Subject: [PATCH 5/6] version bump 0.8.7 --- README.md | 2 +- package-lock.json | 4 ++-- package.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e56a613..4b6399c 100755 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ Easy! Just pull the changes & run this one-liner: git pull # Rebuild the container with the new changes (uncomment the 2nd part if the update requires refreshing the config) -docker-compose up --force-recreate --build -d && docker image prune -f # docker-compose exec ass npm run setup && docker-compose restart +docker-compose up --force-recreate --build -d && docker image prune -f # && docker-compose exec ass npm run setup && docker-compose restart ``` - `--force-recreate` will force the container to rebuild diff --git a/package-lock.json b/package-lock.json index 0f7d57f..7f0dec1 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ass", - "version": "0.8.6", + "version": "0.8.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ass", - "version": "0.8.6", + "version": "0.8.7", "license": "ISC", "dependencies": { "@tycrek/express-nofavicon": "^1.0.2", diff --git a/package.json b/package.json index c9a744d..254d4b6 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ass", - "version": "0.8.6", + "version": "0.8.7", "description": "The superior self-hosted ShareX server", "main": "ass.js", "engines": { @@ -63,4 +63,4 @@ "submodule": "^1.2.1", "uuid": "^8.3.2" } -} +} \ No newline at end of file From 26219b7cf7d9d9455407add52056ade2f3695ed3 Mon Sep 17 00:00:00 2001 From: tycrek Date: Fri, 1 Oct 2021 12:00:20 -0600 Subject: [PATCH 6/6] fixed webhooks oops --- routers/upload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/upload.js b/routers/upload.js index 6cd87c7..a09cc55 100644 --- a/routers/upload.js +++ b/routers/upload.js @@ -110,7 +110,7 @@ router.post('/', (req, res, next) => { log.debug('Upload response sent'); // After we have sent the user the response, also send a Webhook to Discord (if headers are present) - if (req.headers['x-ass-webhook-client'] && req.headers['x-ass-webhook-token']) { + if (req.headers['x-ass-webhook-url']) { // Build the webhook const hook = new Webhook(req.headers['x-ass-webhook-url']);