mirror of
https://github.com/Gabi-Zar/Images-Scrapper-JS.git
synced 2026-04-17 05:36:06 +02:00
Add rate limit using .env and fix disappearing image grid after opening settings
This commit is contained in:
21
main.js
21
main.js
@@ -4,13 +4,32 @@ import { randomUUID } from "crypto";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import archiver from "archiver";
|
import archiver from "archiver";
|
||||||
import mime from "mime";
|
import mime from "mime";
|
||||||
|
import { rateLimit } from "express-rate-limit";
|
||||||
|
import "dotenv/config";
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = 3000;
|
const PORT = 3000;
|
||||||
|
const useRateLimit = process.env.USE_RATE_LIMIT === "true";
|
||||||
|
const getImagesLimiter = rateLimit({
|
||||||
|
windowMs: 60 * 1000, // 1 min
|
||||||
|
limit: 100,
|
||||||
|
standardHeaders: "draft-8",
|
||||||
|
legacyHeaders: false,
|
||||||
|
});
|
||||||
|
const downloadLimiter = rateLimit({
|
||||||
|
windowMs: 60 * 1000, // 1 min
|
||||||
|
limit: 5,
|
||||||
|
standardHeaders: "draft-8",
|
||||||
|
legacyHeaders: false,
|
||||||
|
});
|
||||||
|
|
||||||
let cachedImagesUrls = {};
|
let cachedImagesUrls = {};
|
||||||
|
|
||||||
app.use(express.static("public"));
|
app.use(express.static("public"));
|
||||||
|
if (useRateLimit) {
|
||||||
|
app.use("/api/getImagesURL", getImagesLimiter);
|
||||||
|
app.use("/api/downloadImages", downloadLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`Server launched on http://localhost:${PORT}`);
|
console.log(`Server launched on http://localhost:${PORT}`);
|
||||||
@@ -95,7 +114,7 @@ app.get("/api/downloadImages", async (req, res) => {
|
|||||||
for (let i = 0; i < imagesUrls.length; i++) {
|
for (let i = 0; i < imagesUrls.length; i++) {
|
||||||
const url = imagesUrls[i];
|
const url = imagesUrls[i];
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(url, { responseType: "arraybuffer", timeout: 5000 });
|
const response = await axios.get(url, { responseType: "stream", timeout: 5000 });
|
||||||
const contentType = response.headers["content-type"];
|
const contentType = response.headers["content-type"];
|
||||||
const extension = mime.getExtension(contentType) || url.split(".").pop();
|
const extension = mime.getExtension(contentType) || url.split(".").pop();
|
||||||
|
|
||||||
|
|||||||
41
package-lock.json
generated
41
package-lock.json
generated
@@ -12,7 +12,9 @@
|
|||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"axios": "^1.13.6",
|
"axios": "^1.13.6",
|
||||||
"cheerio": "^1.2.0",
|
"cheerio": "^1.2.0",
|
||||||
|
"dotenv": "^17.3.1",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
|
"express-rate-limit": "^8.3.1",
|
||||||
"mime": "^4.1.0"
|
"mime": "^4.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -679,6 +681,18 @@
|
|||||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "17.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz",
|
||||||
|
"integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://dotenvx.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dunder-proto": {
|
"node_modules/dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
@@ -887,6 +901,24 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/express-rate-limit": {
|
||||||
|
"version": "8.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.1.tgz",
|
||||||
|
"integrity": "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ip-address": "10.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/express-rate-limit"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"express": ">= 4.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fast-fifo": {
|
"node_modules/fast-fifo": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
|
||||||
@@ -1222,6 +1254,15 @@
|
|||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/ip-address": {
|
||||||
|
"version": "10.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
|
||||||
|
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ipaddr.js": {
|
"node_modules/ipaddr.js": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
|
|||||||
@@ -13,7 +13,9 @@
|
|||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"axios": "^1.13.6",
|
"axios": "^1.13.6",
|
||||||
"cheerio": "^1.2.0",
|
"cheerio": "^1.2.0",
|
||||||
|
"dotenv": "^17.3.1",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
|
"express-rate-limit": "^8.3.1",
|
||||||
"mime": "^4.1.0"
|
"mime": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ const starsNumber = 1000;
|
|||||||
const view = document.getElementById("view");
|
const view = document.getElementById("view");
|
||||||
const homeTemplate = document.getElementById("home-template");
|
const homeTemplate = document.getElementById("home-template");
|
||||||
const settingsTemplate = document.getElementById("settings-template");
|
const settingsTemplate = document.getElementById("settings-template");
|
||||||
let imagesUrls = [];
|
let cachedUrls = [];
|
||||||
|
let cachedQuery = "";
|
||||||
let uuid;
|
let uuid;
|
||||||
let imagesProvider = "Bing";
|
let imagesProvider = "Bing";
|
||||||
let imagesOffset = 1;
|
let imagesOffset = 1;
|
||||||
@@ -90,10 +91,14 @@ function navigate(page) {
|
|||||||
case "home":
|
case "home":
|
||||||
view.appendChild(homeTemplate.content.cloneNode(true));
|
view.appendChild(homeTemplate.content.cloneNode(true));
|
||||||
const searchForm = document.getElementById("search-form");
|
const searchForm = document.getElementById("search-form");
|
||||||
|
const searchInput = document.getElementById("search-input");
|
||||||
|
|
||||||
searchForm.addEventListener("submit", function (event) {
|
searchForm.addEventListener("submit", function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fillImagesGrid();
|
||||||
|
searchInput.value = cachedQuery;
|
||||||
break;
|
break;
|
||||||
case "settings":
|
case "settings":
|
||||||
view.appendChild(settingsTemplate.content.cloneNode(true));
|
view.appendChild(settingsTemplate.content.cloneNode(true));
|
||||||
@@ -131,7 +136,7 @@ async function getImagesURL(query, offset, count, smart) {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
uuid = data.uuid;
|
uuid = data.uuid;
|
||||||
|
|
||||||
return data.urls;
|
cachedUrls = data.urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function downloadImages(uuid) {
|
async function downloadImages(uuid) {
|
||||||
@@ -154,22 +159,17 @@ async function downloadImages(uuid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function search() {
|
async function search() {
|
||||||
const imageTemplate = document.getElementById("image-template");
|
|
||||||
const imagesDiv = document.getElementById("images-div");
|
const imagesDiv = document.getElementById("images-div");
|
||||||
const loaderDiv = document.getElementById("loader-div");
|
const loaderDiv = document.getElementById("loader-div");
|
||||||
const searchInput = document.getElementById("search-input");
|
const searchInput = document.getElementById("search-input");
|
||||||
|
cachedQuery = searchInput.value;
|
||||||
|
|
||||||
imagesDiv.replaceChildren();
|
imagesDiv.replaceChildren();
|
||||||
loaderDiv.classList.toggle("show");
|
loaderDiv.classList.toggle("show");
|
||||||
const urls = await getImagesURL(searchInput.value, imagesOffset, maxImages, smartMode);
|
await getImagesURL(cachedQuery, imagesOffset, maxImages, smartMode);
|
||||||
|
|
||||||
loaderDiv.classList.toggle("show");
|
loaderDiv.classList.toggle("show");
|
||||||
for (const url of urls) {
|
fillImagesGrid();
|
||||||
imagesUrls.push(url);
|
|
||||||
const imageTemplateCopy = imageTemplate.content.cloneNode(true);
|
|
||||||
imageTemplateCopy.getElementById("image").src = url;
|
|
||||||
imagesDiv.appendChild(imageTemplateCopy);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function download() {
|
async function download() {
|
||||||
@@ -210,3 +210,18 @@ function toggleSelectedListButton(ListMenuId, buttonId) {
|
|||||||
listButton.classList.toggle("selected");
|
listButton.classList.toggle("selected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fillImagesGrid() {
|
||||||
|
if (!cachedUrls) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageTemplate = document.getElementById("image-template");
|
||||||
|
const imagesDiv = document.getElementById("images-div");
|
||||||
|
|
||||||
|
for (const url of cachedUrls) {
|
||||||
|
const imageTemplateCopy = imageTemplate.content.cloneNode(true);
|
||||||
|
imageTemplateCopy.getElementById("image").src = url;
|
||||||
|
imagesDiv.appendChild(imageTemplateCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user