diff --git a/controller/websocket.js b/controller/websocket.js
index 927fb33..3142707 100644
--- a/controller/websocket.js
+++ b/controller/websocket.js
@@ -1,4 +1,6 @@
const fs = require('node:fs')
+const crypto = require('node:crypto')
+const { RedisRateLimiter } = require('rolling-rate-limiter')
const upload = require('../utils/upload.js')
const ytdlp = require('../utils/ytdlp.js')
@@ -13,6 +15,13 @@ const logger = require("../utils/logger.js")
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
+const limiter = new RedisRateLimiter({
+ client: redis,
+ namespace: 'autodownload:',
+ interval: 24 * 60 * 60 * 1000,
+ maxInInterval: 5
+})
+
exports.save = async (ws, req) => {
logger.info({ message: `${req.path} ${JSON.stringify(req.query)}` })
@@ -310,6 +319,13 @@ exports.addAutodownload = async (req, res) => {
if (already) {
res.status(500).send(`This channel is already being automatically downloaded...`)
} else {
+ const ipHash = crypto.createHash('sha256').update(req.headers['x-forwarded-for'] || req.connection.remoteAddress).digest('hex')
+ const isLimited = await limiter.limit(ipHash)
+
+ if (isLimited) return res.status(420).send(`Hey! You have reached the limit of 5 queued auto-download channels per day. Sadly, hard drives don't grow on trees, so rate limits are necessary. The "Save Channel" feature has no limits, so feel free to use that.
+
+Are you planning something awesome? Feel free to email me at admin[@]preservetube.com.`)
+
await prisma.autodownload.create({
data: {
channel: channelId
diff --git a/package.json b/package.json
index 41ab246..023d01a 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"ioredis": "^5.3.1",
"isomorphic-dompurify": "^1.0.0",
"node-fetch": "2",
+ "rolling-rate-limiter": "^0.4.2",
"winston": "^3.8.2"
},
"devDependencies": {
diff --git a/yarn.lock b/yarn.lock
index b4545a5..6a3d347 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -825,6 +825,14 @@ methods@~1.1.2:
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
+microtime@^3.0.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/microtime/-/microtime-3.1.1.tgz#f3ce12f4091c55fb2982c87205e2e7698afdcd0c"
+ integrity sha512-to1r7o24cDsud9IhN6/8wGmMx5R2kT0w2Xwm5okbYI3d1dk6Xv0m+Z+jg2vS9pt+ocgQHTCtgs/YuyJhySzxNg==
+ dependencies:
+ node-addon-api "^5.0.0"
+ node-gyp-build "^4.4.0"
+
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
@@ -869,6 +877,11 @@ negotiator@0.6.3:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
+node-addon-api@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762"
+ integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==
+
node-fetch@2:
version "2.6.9"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6"
@@ -883,6 +896,11 @@ node-fetch@^2.6.12:
dependencies:
whatwg-url "^5.0.0"
+node-gyp-build@^4.4.0:
+ version "4.6.1"
+ resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.1.tgz#24b6d075e5e391b8d5539d98c7fc5c210cac8a3e"
+ integrity sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==
+
nwsapi@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0"
@@ -1034,6 +1052,14 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
+rolling-rate-limiter@^0.4.2:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/rolling-rate-limiter/-/rolling-rate-limiter-0.4.2.tgz#19204ceabf4e4e421681bfe876a779d5b0d2282b"
+ integrity sha512-4lKCwwuINmfPvyfLtvAHn+SiwXisH8fmmEvTSHYAenAfKF0p6IErkUEIS+9dvWAslU+97WtX000ne26RAToo2w==
+ dependencies:
+ microtime "^3.0.0"
+ uuid "^9.0.0"
+
safe-buffer@5.2.1, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
@@ -1241,6 +1267,11 @@ uuid@3.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
+uuid@^9.0.0:
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
+ integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
+
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"