From a1d751e0bbe5200d674dfe3a9726817a820fc83c Mon Sep 17 00:00:00 2001 From: La macchina desiderante Date: Fri, 17 May 2024 02:24:41 +0200 Subject: [PATCH 1/7] add redis integration (WIP) --- docker-compose.yaml | 7 ++ package-lock.json | 100 +++++++++++++++++++++++++++- package.json | 4 +- src/constants/redis.ts | 4 ++ src/redis/client.ts | 38 +++++++++++ src/utils/scrape/xnxx/agent.ts | 2 +- src/utils/scrape/xnxx/gallery.ts | 12 +++- src/utils/scrape/xnxx/video.ts | 14 +++- src/utils/scrape/xvideos/gallery.ts | 24 +++++-- src/utils/scrape/xvideos/video.ts | 14 +++- 10 files changed, 206 insertions(+), 13 deletions(-) create mode 100644 src/constants/redis.ts create mode 100644 src/redis/client.ts diff --git a/docker-compose.yaml b/docker-compose.yaml index c60fbcc..7219001 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -19,3 +19,10 @@ services: ports: - "8069:3000" + + env_file: .env + + redis: + image: redis:alpine + container_name: redis + restart: unless-stopped \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9d6ffc9..a3e1695 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "react-icons": "^5.1.0", "react-image": "^4.1.0", "react-redux": "^9.1.1", + "redis": "^4.6.14", "redux-persist": "^6.0.0", "video.js": "^8.10.0", "videojs-hls-quality-selector": "^2.0.0" @@ -30,6 +31,7 @@ "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", + "@types/redis": "^4.0.11", "eslint": "^8", "eslint-config-next": "14.2.2", "sass": "^1.75.0", @@ -477,6 +479,59 @@ "node": ">=14" } }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.5.16", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.16.tgz", + "integrity": "sha512-X1a3xQ5kEMvTib5fBrHKh6Y+pXbeKXqziYuxOUo1ojQNECg4M5Etd1qqyhMap+lFUOAh8S7UYevgJHOm4A+NOg==", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz", + "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.6.tgz", + "integrity": "sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", + "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@reduxjs/toolkit": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.3.tgz", @@ -572,6 +627,16 @@ "@types/react": "*" } }, + "node_modules/@types/redis": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/@types/redis/-/redis-4.0.11.tgz", + "integrity": "sha512-bI+gth8La8Wg/QCR1+V1fhrL9+LZUSWfcqpOj2Kc80ZQ4ffbdL173vQd5wovmoV9i071FU9oP2g6etLuEwb6Rg==", + "deprecated": "This is a stub types definition. redis provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "redis": "*" + } + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", @@ -1317,6 +1382,14 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2462,6 +2535,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "engines": { + "node": ">= 4" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -4201,6 +4282,22 @@ "node": ">=8.10.0" } }, + "node_modules/redis": { + "version": "4.6.14", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.14.tgz", + "integrity": "sha512-GrNg/e33HtsQwNXL7kJT+iNFPSwE1IPmd7wzV3j4f2z0EYxZfZE7FVTmUysgAtqQQtg5NXF5SNLR9OdO/UHOfw==", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.16", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.6", + "@redis/search": "1.1.6", + "@redis/time-series": "1.0.5" + } + }, "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", @@ -5306,8 +5403,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yocto-queue": { "version": "0.1.0", diff --git a/package.json b/package.json index 014efba..30e83a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "proxyraye-next", - "version": "0.2.0", + "version": "0.3.0", "private": true, "scripts": { "dev": "next dev", @@ -23,6 +23,7 @@ "react-icons": "^5.1.0", "react-image": "^4.1.0", "react-redux": "^9.1.1", + "redis": "^4.6.14", "redux-persist": "^6.0.0", "video.js": "^8.10.0", "videojs-hls-quality-selector": "^2.0.0" @@ -31,6 +32,7 @@ "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", + "@types/redis": "^4.0.11", "eslint": "^8", "eslint-config-next": "14.2.2", "sass": "^1.75.0", diff --git a/src/constants/redis.ts b/src/constants/redis.ts new file mode 100644 index 0000000..2610310 --- /dev/null +++ b/src/constants/redis.ts @@ -0,0 +1,4 @@ +export const DEFAULT_XVIDEOS_CONTENT_EXPIRY = { EX: 60 * 60 * 24 }; +export const DEFAULT_XNXX_CONTENT_EXPIRY = { EX: 60 * 60 * 24 }; + +export const DEFAULT_RELATED_VIDEO_KEY_PATH = '/related/' \ No newline at end of file diff --git a/src/redis/client.ts b/src/redis/client.ts new file mode 100644 index 0000000..c54f034 --- /dev/null +++ b/src/redis/client.ts @@ -0,0 +1,38 @@ +import { createClient } from 'redis'; + +export const getDataFromRedis = async (key: string): Promise => { + + if (Boolean(process.env.ENABLE_REDIS)) { + + const redis = await createClient({ url: process.env.REDIS_URL }) + .on('error', err => console.log('Redis Client Error', err)) + .connect() + + const cached = await redis.get(key); + + if (cached) { + await redis.disconnect(); + + return JSON.parse(cached) + } else { + return null + } + + + } else { + return null + } +} + +export const storeDataIntoRedis = async (key: string, data: Object, expiry?: Object) => { + if (Boolean(process.env.ENABLE_REDIS)) { + const redis = await createClient({ url: process.env.REDIS_URL }) + .on('error', err => console.log('Redis Client Error', err)) + .connect() + + await redis.set(key, JSON.stringify(data), expiry); + + await redis.disconnect(); + } + +} \ No newline at end of file diff --git a/src/utils/scrape/xnxx/agent.ts b/src/utils/scrape/xnxx/agent.ts index 9006390..3082b94 100644 --- a/src/utils/scrape/xnxx/agent.ts +++ b/src/utils/scrape/xnxx/agent.ts @@ -1,5 +1,5 @@ import { FetchParams, GalleryData, VideoAgent, VideoData } from "@/meta/data"; -import { fetchXNXXGalleryData, } from "./gallery"; +import { fetchXNXXGalleryData } from "./gallery"; import { fetchXNXXVideoData } from "./video"; export class XNXXAgent implements VideoAgent { diff --git a/src/utils/scrape/xnxx/gallery.ts b/src/utils/scrape/xnxx/gallery.ts index 3f7fec1..cdb8e77 100644 --- a/src/utils/scrape/xnxx/gallery.ts +++ b/src/utils/scrape/xnxx/gallery.ts @@ -8,6 +8,8 @@ import { getXNXXQueryUrl } from './url'; import { Platforms } from '@/meta/settings'; import { XNXX_BASE_URL } from '@/constants/urls'; +import { getDataFromRedis, storeDataIntoRedis } from '@/redis/client'; +import { DEFAULT_XNXX_CONTENT_EXPIRY } from '@/constants/redis'; export const fetchXNXXGalleryData = async (params?: FetchParams): Promise => { @@ -17,9 +19,15 @@ export const fetchXNXXGalleryData = async (params?: FetchParams): Promise { + .then(async response => { const html = response.data; @@ -41,6 +49,8 @@ export const fetchXNXXGalleryData = async (params?: FetchParams): Promise { // handle errors }); diff --git a/src/utils/scrape/xnxx/video.ts b/src/utils/scrape/xnxx/video.ts index 1cf066e..d566de0 100644 --- a/src/utils/scrape/xnxx/video.ts +++ b/src/utils/scrape/xnxx/video.ts @@ -8,6 +8,8 @@ import * as cheerio from "cheerio"; import { Platforms } from '@/meta/settings'; import { findRelatedVideos, findVideoUrlInsideTagStringByFunctionNameAndExtension } from '@/utils/scrape/common/wgcz'; import { getHeaders } from '@/utils/scrape/common/headers'; +import { DEFAULT_RELATED_VIDEO_KEY_PATH, DEFAULT_XNXX_CONTENT_EXPIRY } from '@/constants/redis'; +import { getDataFromRedis, storeDataIntoRedis } from '@/redis/client'; export const fetchXNXXVideoData = async (videoId: string, params?: FetchParams): Promise<[VideoData, GalleryData[]]> => { @@ -23,9 +25,16 @@ export const fetchXNXXVideoData = async (videoId: string, params?: FetchParams): const queryUrl = `${host}${videoId}` + const cachedVideoData = await getDataFromRedis(queryUrl) + const cachedRelatedData = await getDataFromRedis(queryUrl + DEFAULT_RELATED_VIDEO_KEY_PATH) + + if (cachedVideoData) { + return [cachedVideoData as VideoData, cachedRelatedData as GalleryData[] ?? []] + } + await axios.get(queryUrl, reqHeaders) - .then(response => { + .then(async response => { const html = response.data; @@ -63,6 +72,9 @@ export const fetchXNXXVideoData = async (videoId: string, params?: FetchParams): } }) + await storeDataIntoRedis(queryUrl + DEFAULT_RELATED_VIDEO_KEY_PATH, related, DEFAULT_XNXX_CONTENT_EXPIRY); + await storeDataIntoRedis(queryUrl, data, DEFAULT_XNXX_CONTENT_EXPIRY); + }).catch((error: AxiosError) => { // handle errors }); diff --git a/src/utils/scrape/xvideos/gallery.ts b/src/utils/scrape/xvideos/gallery.ts index 2a941e0..36f2c35 100644 --- a/src/utils/scrape/xvideos/gallery.ts +++ b/src/utils/scrape/xvideos/gallery.ts @@ -6,6 +6,9 @@ import { getHeaders } from '@/utils/scrape/common/headers'; import { getXVideosQueryUrl } from './url'; import { Platforms } from '@/meta/settings'; +import { getDataFromRedis, storeDataIntoRedis } from '@/redis/client'; +import { DEFAULT_XVIDEOS_CONTENT_EXPIRY } from '@/constants/redis'; + export const fetchXVideosGalleryData = async (params?: FetchParams): Promise => { let data: GalleryData[] = []; @@ -14,22 +17,28 @@ export const fetchXVideosGalleryData = async (params?: FetchParams): Promise { + .then(async response => { const html = response.data; const $ = cheerio.load(html); - + const thumbs = $(".thumb-block"); - + thumbs.map((key, thumb) => { - + const videoUrl = $(thumb).find(".thumb a").attr("href") const imgUrl = $(thumb).find(".thumb img").attr("data-src") const text = $(thumb).find(".thumb-under a").attr("title") - + videoUrl && imgUrl && text && data.push({ videoUrl, imgUrl, @@ -38,7 +47,10 @@ export const fetchXVideosGalleryData = async (params?: FetchParams): Promise { + await storeDataIntoRedis(queryUrl, data, DEFAULT_XVIDEOS_CONTENT_EXPIRY); + + }) + .catch((error: AxiosError) => { // handle errors }); diff --git a/src/utils/scrape/xvideos/video.ts b/src/utils/scrape/xvideos/video.ts index c323be1..614dd0d 100644 --- a/src/utils/scrape/xvideos/video.ts +++ b/src/utils/scrape/xvideos/video.ts @@ -8,6 +8,8 @@ import { getHeaders } from '@/utils/scrape/common/headers'; import { Platforms } from '@/meta/settings'; import { findRelatedVideos, findVideoUrlInsideTagStringByFunctionNameAndExtension } from '@/utils/scrape/common/wgcz'; +import { getDataFromRedis, storeDataIntoRedis } from '@/redis/client'; +import { DEFAULT_RELATED_VIDEO_KEY_PATH, DEFAULT_XVIDEOS_CONTENT_EXPIRY } from '@/constants/redis'; export const fetchXvideosVideoData = async (videoId: string, params?: FetchParams): Promise<[VideoData, GalleryData[]]> => { @@ -23,9 +25,16 @@ export const fetchXvideosVideoData = async (videoId: string, params?: FetchParam const queryUrl = `${host}${videoId}` + const cachedVideoData = await getDataFromRedis(queryUrl) + const cachedRelatedData = await getDataFromRedis(queryUrl + DEFAULT_RELATED_VIDEO_KEY_PATH) + + if (cachedVideoData) { + return [cachedVideoData as VideoData, cachedRelatedData as GalleryData[] ?? []] + } + await axios.get(queryUrl, reqHeaders) - .then(response => { + .then(async response => { const html = response.data; @@ -63,6 +72,9 @@ export const fetchXvideosVideoData = async (videoId: string, params?: FetchParam } }) + await storeDataIntoRedis(queryUrl + DEFAULT_RELATED_VIDEO_KEY_PATH, related, DEFAULT_XVIDEOS_CONTENT_EXPIRY); + await storeDataIntoRedis(queryUrl, data, DEFAULT_XVIDEOS_CONTENT_EXPIRY); + }).catch((error: AxiosError) => { // handle errors }); From 4f15f44e12a2cd806bf371283af4843407730806 Mon Sep 17 00:00:00 2001 From: La macchina desiderante Date: Fri, 17 May 2024 02:25:27 +0200 Subject: [PATCH 2/7] add redis integration gitignore (WIP) --- .env.example | 8 ++++++++ .gitignore | 3 +++ 2 files changed, 11 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..6d2dae6 --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +# enable content cache (less server api calls) +ENABLE_REDIS=true + +# if cache enabled, sets redis url for Docker network (see docker-compose.yaml) +REDIS_URL='redis://redis:6379' + +# if cache enabled, set redis url for external redis +# REDIS_URL='redis://127.0.0.1:6379' \ No newline at end of file diff --git a/.gitignore b/.gitignore index fd3dbb5..f046fd6 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# local .env +.env \ No newline at end of file From 5a149ebe267d2679d153adf73c7f43bdec0e35c4 Mon Sep 17 00:00:00 2001 From: La macchina desiderante Date: Fri, 17 May 2024 11:43:07 +0200 Subject: [PATCH 3/7] fix dockerfile and package-json BREAKING issues --- Dockerfile | 4 +- package-lock.json | 343 +++++++++++++++++++--------------------------- package.json | 48 +++---- 3 files changed, 165 insertions(+), 230 deletions(-) diff --git a/Dockerfile b/Dockerfile index 560fed2..52bc14a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # PHASE 1: copy and build -FROM node:alpine AS build +FROM node:22.0.0-alpine3.19 AS build WORKDIR /app @@ -10,7 +10,7 @@ RUN rm -rf node_modules && npm install && npm run build # PHASE 2: prepare for exec -FROM node:alpine AS exec +FROM node:22.0.0-alpine3.19 AS exec WORKDIR /app diff --git a/package-lock.json b/package-lock.json index a3e1695..e25860c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,56 +1,47 @@ { "name": "proxyraye-next", - "version": "0.2.0", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "proxyraye-next", - "version": "0.2.0", + "version": "0.3.0", "dependencies": { - "@picocss/pico": "^2.0.6", - "@reduxjs/toolkit": "^2.2.3", - "axios": "^1.6.8", - "cheerio": "^1.0.0-rc.12", - "classnames": "^2.5.1", + "@picocss/pico": "2.0.6", + "@reduxjs/toolkit": "2.2.3", + "axios": "1.6.8", + "cheerio": "1.0.0-rc.12", + "classnames": "2.5.1", "next": "14.2.2", - "next-intl": "^3.11.3", - "next-nprogress-bar": "^2.3.11", - "react": "^18", - "react-cookie": "^7.1.4", - "react-dom": "^18", - "react-icons": "^5.1.0", - "react-image": "^4.1.0", - "react-redux": "^9.1.1", - "redis": "^4.6.14", - "redux-persist": "^6.0.0", - "video.js": "^8.10.0", - "videojs-hls-quality-selector": "^2.0.0" + "next-intl": "3.11.3", + "next-nprogress-bar": "2.3.11", + "react": "18.3.0", + "react-cookie": "7.1.4", + "react-dom": "18.3.0", + "react-icons": "5.1.0", + "react-image": "4.1.0", + "react-redux": "9.1.1", + "redis": "4.6.14", + "redux-persist": "6.0.0", + "video.js": "8.10.0", + "videojs-hls-quality-selector": "2.0.0" }, "devDependencies": { - "@types/node": "^20", - "@types/react": "^18", - "@types/react-dom": "^18", - "@types/redis": "^4.0.11", - "eslint": "^8", + "@types/node": "20.12.12", + "@types/react": "18.3.0", + "@types/react-dom": "18.3.0", + "@types/redis": "4.0.11", + "eslint": "8", "eslint-config-next": "14.2.2", - "sass": "^1.75.0", - "typescript": "^5" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "sass": "1.75.0", + "typescript": "5.4.5" } }, "node_modules/@babel/runtime": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", - "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -132,64 +123,30 @@ } }, "node_modules/@formatjs/fast-memoize": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", - "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", + "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", "dependencies": { - "tslib": "^2.1.0" + "tslib": "^2.4.0" } }, "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", - "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.6.tgz", + "integrity": "sha512-etVau26po9+eewJKYoiBKP6743I1br0/Ie00Pb/S/PtmYfmjTcOn2YCh2yNkSZI12h6Rg+BOgQYborXk46BvkA==", "dependencies": { - "@formatjs/ecma402-abstract": "1.11.4", - "@formatjs/icu-skeleton-parser": "1.3.6", - "tslib": "^2.1.0" - } - }, - "node_modules/@formatjs/icu-messageformat-parser/node_modules/@formatjs/ecma402-abstract": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", - "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", - "dependencies": { - "@formatjs/intl-localematcher": "0.2.25", - "tslib": "^2.1.0" - } - }, - "node_modules/@formatjs/icu-messageformat-parser/node_modules/@formatjs/intl-localematcher": { - "version": "0.2.25", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", - "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", - "dependencies": { - "tslib": "^2.1.0" + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/icu-skeleton-parser": "1.8.0", + "tslib": "^2.4.0" } }, "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", - "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.0.tgz", + "integrity": "sha512-QWLAYvM0n8hv7Nq5BEs4LKIjevpVpbGLAJgOaYzg9wABEoX1j0JO1q2/jVkO6CVlq0dbsxZCngS5aXbysYueqA==", "dependencies": { - "@formatjs/ecma402-abstract": "1.11.4", - "tslib": "^2.1.0" - } - }, - "node_modules/@formatjs/icu-skeleton-parser/node_modules/@formatjs/ecma402-abstract": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", - "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", - "dependencies": { - "@formatjs/intl-localematcher": "0.2.25", - "tslib": "^2.1.0" - } - }, - "node_modules/@formatjs/icu-skeleton-parser/node_modules/@formatjs/intl-localematcher": { - "version": "0.2.25", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", - "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", - "dependencies": { - "tslib": "^2.1.0" + "@formatjs/ecma402-abstract": "1.18.2", + "tslib": "^2.4.0" } }, "node_modules/@formatjs/intl-localematcher": { @@ -556,9 +513,9 @@ } }, "node_modules/@rushstack/eslint-patch": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.2.tgz", - "integrity": "sha512-hw437iINopmQuxWPSUEvqE56NCPsiU8N4AYtfHmJFckclktzK9YQJieD3XkDCDH4OjL+C7zgPUh73R/nrcHrqw==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz", + "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==", "dev": true }, "node_modules/@swc/counter": { @@ -596,9 +553,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", - "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -610,18 +567,18 @@ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { - "version": "18.2.79", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz", - "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.0.tgz", + "integrity": "sha512-DiUcKjzE6soLyln8NNZmyhcQjVv+WsUIFSqetMN0p8927OztKT4VTfFTqsbAi5oAGIcgOmOajlfBqyptDDjZRw==", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.2.25", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.25.tgz", - "integrity": "sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", "dev": true, "dependencies": { "@types/react": "*" @@ -1266,9 +1223,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001612", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz", - "integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==", + "version": "1.0.30001620", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz", + "integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==", "funding": [ { "type": "opencollective", @@ -1702,9 +1659,9 @@ "dev": true }, "node_modules/enhanced-resolve": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", - "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", + "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -1807,14 +1764,14 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.0.18", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz", - "integrity": "sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==", + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", + "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", "dev": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", + "es-abstract": "^1.23.3", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", @@ -2171,9 +2128,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "dev": true, "engines": { "node": ">=10" @@ -2580,9 +2537,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", - "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz", + "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", "dev": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -2674,12 +2631,13 @@ } }, "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -2848,18 +2806,18 @@ } }, "node_modules/immer": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.4.tgz", - "integrity": "sha512-cuBuGK40P/sk5IzWa9QPUaAdvPHjkk1c+xYsd9oZw+YQQEV+10G0P5uMpGctZZKnyQ+ibRO08bD25nWLmYi2pw==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" } }, "node_modules/immutable": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", - "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", "devOptional": true }, "node_modules/import-fresh": { @@ -2923,31 +2881,14 @@ } }, "node_modules/intl-messageformat": { - "version": "9.13.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", - "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", + "version": "10.5.12", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.12.tgz", + "integrity": "sha512-izl0uxhy/melhw8gP2r8pGiVieviZmM4v5Oqx3c1/R7g9cwER2smmGfSjcIsp8Y3Q53bfciL/gkxacJRx/dUvg==", "dependencies": { - "@formatjs/ecma402-abstract": "1.11.4", - "@formatjs/fast-memoize": "1.2.1", - "@formatjs/icu-messageformat-parser": "2.1.0", - "tslib": "^2.1.0" - } - }, - "node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", - "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", - "dependencies": { - "@formatjs/intl-localematcher": "0.2.25", - "tslib": "^2.1.0" - } - }, - "node_modules/intl-messageformat/node_modules/@formatjs/intl-localematcher": { - "version": "0.2.25", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", - "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", - "dependencies": { - "tslib": "^2.1.0" + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/fast-memoize": "2.2.0", + "@formatjs/icu-messageformat-parser": "2.7.6", + "tslib": "^2.4.0" } }, "node_modules/is-array-buffer": { @@ -3505,9 +3446,9 @@ } }, "node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -3608,9 +3549,9 @@ } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -3920,17 +3861,17 @@ } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -4035,16 +3976,16 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", - "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4060,9 +4001,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -4186,9 +4127,9 @@ ] }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.0.tgz", + "integrity": "sha512-RPutkJftSAldDibyrjuku7q11d3oy6wKOyPe5K1HA/HwwrXcEqBdHsLypkC2FFYjP7bPUa6gbzSBhw4sY2JcDg==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -4210,15 +4151,15 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-zaKdLBftQJnvb7FtDIpZtsAIb2MZU087RM8bRDZU8LVCCFYjPTsDZJNFUWPcVz3HFSN1n/caxi0ca4B/aaVQGQ==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.1" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.0" } }, "node_modules/react-icons": { @@ -4540,21 +4481,18 @@ } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dependencies": { "loose-envify": "^1.1.0" } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -4562,18 +4500,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -5125,21 +5051,21 @@ "integrity": "sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==" }, "node_modules/use-intl": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-3.11.3.tgz", - "integrity": "sha512-qxW2CiDWQY6ilR7AxrQPp5qiU6hWWh7Ek8uBEKGjcO7KoRuJ/4/ybIi/Z7rTXnEHHvQyCT1dotyDT9WIBGwCWg==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-3.14.0.tgz", + "integrity": "sha512-Dy3B4C7ICGDM5ZDrnNpf8bDfFbnYYjK5vNzij0kPfJNM2G0nfLWGQvhBXEyPPKZpNvnkKkFasXR8AAxSwXTAoA==", "dependencies": { "@formatjs/ecma402-abstract": "^1.11.4", - "intl-messageformat": "^9.3.18" + "intl-messageformat": "^10.5.11" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/use-sync-external-store": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.1.tgz", - "integrity": "sha512-6MCBDr76UJmRpbF8pzP27uIoTocf3tITaMJ52mccgAhMJycuh5A/RL6mDZCTwTisj0Qfeq69FtjMCUX27U78oA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", + "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } @@ -5165,7 +5091,7 @@ "videojs-vtt.js": "0.15.5" } }, - "node_modules/video.js/node_modules/videojs-contrib-quality-levels": { + "node_modules/videojs-contrib-quality-levels": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/videojs-contrib-quality-levels/-/videojs-contrib-quality-levels-4.0.0.tgz", "integrity": "sha512-u5rmd8BjLwANp7XwuQ0Q/me34bMe6zg9PQdHfTS7aXgiVRbNTb4djcmfG7aeSrkpZjg+XCLezFNenlJaCjBHKw==", @@ -5300,6 +5226,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", diff --git a/package.json b/package.json index 30e83a2..fc52740 100644 --- a/package.json +++ b/package.json @@ -9,33 +9,33 @@ "lint": "next lint" }, "dependencies": { - "@picocss/pico": "^2.0.6", - "@reduxjs/toolkit": "^2.2.3", - "axios": "^1.6.8", - "cheerio": "^1.0.0-rc.12", - "classnames": "^2.5.1", + "@picocss/pico": "2.0.6", + "@reduxjs/toolkit": "2.2.3", + "axios": "1.6.8", + "cheerio": "1.0.0-rc.12", + "classnames": "2.5.1", "next": "14.2.2", - "next-intl": "^3.11.3", - "next-nprogress-bar": "^2.3.11", - "react": "^18", - "react-cookie": "^7.1.4", - "react-dom": "^18", - "react-icons": "^5.1.0", - "react-image": "^4.1.0", - "react-redux": "^9.1.1", - "redis": "^4.6.14", - "redux-persist": "^6.0.0", - "video.js": "^8.10.0", - "videojs-hls-quality-selector": "^2.0.0" + "next-intl": "3.11.3", + "next-nprogress-bar": "2.3.11", + "react": "18.3.0", + "react-cookie": "7.1.4", + "react-dom": "18.3.0", + "react-icons": "5.1.0", + "react-image": "4.1.0", + "react-redux": "9.1.1", + "redis": "4.6.14", + "redux-persist": "6.0.0", + "video.js": "8.10.0", + "videojs-hls-quality-selector": "2.0.0" }, "devDependencies": { - "@types/node": "^20", - "@types/react": "^18", - "@types/react-dom": "^18", - "@types/redis": "^4.0.11", - "eslint": "^8", + "@types/node": "20.12.12", + "@types/react": "18.3.0", + "@types/react-dom": "18.3.0", + "@types/redis": "4.0.11", + "eslint": "8", "eslint-config-next": "14.2.2", - "sass": "^1.75.0", - "typescript": "^5" + "sass": "1.75.0", + "typescript": "5.4.5" } } From 5245125097713f1a53a0faef836ddd0970ce50e5 Mon Sep 17 00:00:00 2001 From: La macchina desiderante Date: Fri, 17 May 2024 12:02:55 +0200 Subject: [PATCH 4/7] load env variables directly from docker compose --- .dockerignore | 4 +++- docker-compose.yaml | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.dockerignore b/.dockerignore index 6a9c051..0590cba 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,4 +2,6 @@ node_modules .git .gitignore .next -package-lock.json \ No newline at end of file +package-lock.json + +.env \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 7219001..fcf76d9 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -20,8 +20,10 @@ services: ports: - "8069:3000" - env_file: .env - + environment: + - ENABLE_REDIS=true + - REDIS_URL=redis://redis:6379 + redis: image: redis:alpine container_name: redis From 15deeddd141c03ec33a7bd57befdd13f6bc111dd Mon Sep 17 00:00:00 2001 From: La macchina desiderante Date: Sat, 18 May 2024 11:50:19 +0200 Subject: [PATCH 5/7] fix caching issue with no related data --- src/utils/scrape/xnxx/video.ts | 3 ++- src/utils/scrape/xvideos/video.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utils/scrape/xnxx/video.ts b/src/utils/scrape/xnxx/video.ts index d566de0..a6a3e46 100644 --- a/src/utils/scrape/xnxx/video.ts +++ b/src/utils/scrape/xnxx/video.ts @@ -63,6 +63,8 @@ export const fetchXNXXVideoData = async (videoId: string, params?: FetchParams): }) + await storeDataIntoRedis(queryUrl, data, DEFAULT_XNXX_CONTENT_EXPIRY); + // populate related gallery scriptTags.map((idx, elem) => { const relatedVideos = findRelatedVideos($(elem).toString(), Platforms.xnxx) @@ -73,7 +75,6 @@ export const fetchXNXXVideoData = async (videoId: string, params?: FetchParams): }) await storeDataIntoRedis(queryUrl + DEFAULT_RELATED_VIDEO_KEY_PATH, related, DEFAULT_XNXX_CONTENT_EXPIRY); - await storeDataIntoRedis(queryUrl, data, DEFAULT_XNXX_CONTENT_EXPIRY); }).catch((error: AxiosError) => { // handle errors diff --git a/src/utils/scrape/xvideos/video.ts b/src/utils/scrape/xvideos/video.ts index 614dd0d..171f669 100644 --- a/src/utils/scrape/xvideos/video.ts +++ b/src/utils/scrape/xvideos/video.ts @@ -63,6 +63,8 @@ export const fetchXvideosVideoData = async (videoId: string, params?: FetchParam }) + await storeDataIntoRedis(queryUrl, data, DEFAULT_XVIDEOS_CONTENT_EXPIRY); + // populate related gallery scriptTags.map((idx, elem) => { const relatedVideos = findRelatedVideos($(elem).toString(), Platforms.xvideos) @@ -73,7 +75,6 @@ export const fetchXvideosVideoData = async (videoId: string, params?: FetchParam }) await storeDataIntoRedis(queryUrl + DEFAULT_RELATED_VIDEO_KEY_PATH, related, DEFAULT_XVIDEOS_CONTENT_EXPIRY); - await storeDataIntoRedis(queryUrl, data, DEFAULT_XVIDEOS_CONTENT_EXPIRY); }).catch((error: AxiosError) => { // handle errors From 69d587cfb38577914174efff99c98fccacb6f48f Mon Sep 17 00:00:00 2001 From: La macchina desiderante Date: Sat, 18 May 2024 12:41:16 +0200 Subject: [PATCH 6/7] update readme and .env.example --- .env.example | 4 ++-- README.md | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index 6d2dae6..f716d90 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,7 @@ ENABLE_REDIS=true # if cache enabled, sets redis url for Docker network (see docker-compose.yaml) -REDIS_URL='redis://redis:6379' +# REDIS_URL='redis://redis:6379' # if cache enabled, set redis url for external redis -# REDIS_URL='redis://127.0.0.1:6379' \ No newline at end of file +REDIS_URL='redis://127.0.0.1:6379' \ No newline at end of file diff --git a/README.md b/README.md index 0487899..57bacc1 100644 --- a/README.md +++ b/README.md @@ -18,27 +18,55 @@ Self-hosted demo can be found [here](https://proxyraye.copyriot.xyz). You can run the project on local by cloning the repo. -Project requires no configuration. +## Docker You can run it via Docker with docker-compose by opening root folder via console and running: ``` + docker-compose up -d + ``` + And head browser to `localhost:8069`. -Or you can run it outside Docker via npm (tested with NodeJS `20.11`) by opening root folder via console and running: +### Caching + +Starting from version `0.3.0` caching is enabled by default inside `docker-compose.yaml`. + +A base Redis image will be added to the network. + +However, Proxy Raye can still work without Redis by setting `ENABLE_REDIS=false` under `environment:`. + +## Node.js + +You can also run project outside Docker via npm (tested with NodeJS `20.11` and above). + +You can run the project by opening root folder via console and running: + ``` + npm install + npm run build + npm run start + ``` + And head browser to `localhost:3000`. -### WARNING: -Proxy Raye tries to avoid ip blacklisting by setting random human-request-like headers at every call. But in the long run (after several hours of continuous requests) XVideos might **temporarily blacklist** your IP address. When this happen, it will stop returning HD videos from pages. Only low quality (SD) videos will be shown. -Using a VPN can avoid such issue. +### (optional) Enable caching + +If you want to enable caching, please rename `.env.example` to `.env` file inside root folder. Inside `.env` file you will find following variables: + +``` +ENABLE_REDIS=true +REDIS_URL='redis://127.0.0.1:6379' +``` +These values assume a basic Redis instance running on local machine. If your local setup is different, or your Redis instance is somewhere else, please change `REDIS_URL` accordingly. + # Modify If you want to edit the project you can start development mode by opening root folder via console and running: From b69a7d975184dd0a1348556c017f7fa975b71348 Mon Sep 17 00:00:00 2001 From: La macchina desiderante Date: Sat, 18 May 2024 12:42:45 +0200 Subject: [PATCH 7/7] remove spaces from README --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 57bacc1..a740920 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,7 @@ You can run the project on local by cloning the repo. You can run it via Docker with docker-compose by opening root folder via console and running: ``` - docker-compose up -d - ``` And head browser to `localhost:8069`. @@ -45,13 +43,9 @@ You can also run project outside Docker via npm (tested with NodeJS `20.11` and You can run the project by opening root folder via console and running: ``` - npm install - npm run build - npm run start - ``` And head browser to `localhost:3000`.