fix(redis): fix logs being polluted when redis is disabled

also used streaming when redis is disabled for faster response.
This commit is contained in:
zyachel 2022-11-13 17:29:47 +05:30
parent 6f664d2164
commit 5fd0d92187
7 changed files with 148 additions and 73 deletions

View file

@ -1,15 +1,19 @@
# required fields ### required fields
# used for meta tags. e.g: 'https://libremdb.iket.me' don't add end slash. ## used for meta tags. e.g: 'https://libremdb.iket.me'. don't add end slash.
NEXT_PUBLIC_URL= NEXT_PUBLIC_URL=
# optional fields. uncomment them and add the values if you wish so. ### optional fields. modify according to your needs.
# default useragent for requesting data from imdb is 'axios/0.27.2' ## comment it out if you wish to enable nextjs stats collection. more at https://nextjs.org/telemetry
NEXT_TELEMETRY_DISABLED=1
## default useragent for requesting data from imdb is 'axios/0.27.2'
# AXIOS_USERAGENT= # AXIOS_USERAGENT=
# default accept header is 'application/json, text/plain, */*' ## default accept header is 'application/json, text/plain, */*'
# AXIOS_ACCEPT= # AXIOS_ACCEPT=
## for forcing a certain language for data we get from imdb
# AXIOS_LANGUAGE=en-US
# for docker, just set the domain to the container name, default is 'libremdb_redis' ### REDIS
## if you want to use redis to speed up the media proxy, set this to true
# USE_REDIS=true
## for docker, just set the domain to the container name, default is 'libremdb_redis'
REDIS_URL=localhost:6379 REDIS_URL=localhost:6379
# if you want to use redis to speed up the media proxy, set this to true
USE_REDIS = true

6
.gitignore vendored
View file

@ -28,11 +28,15 @@ yarn-error.log*
# typescript # typescript
*.tsbuildinfo *.tsbuildinfo
next-env.d.ts next-env.d.ts
#just dev stuff #just dev stuff
dev/* dev/*
# other lockfiles
yarn.lock yarn.lock
package-lock.json
# docker # docker
docker-compose.yml docker-compose.yml
dump.rdb

View file

@ -17,7 +17,6 @@
"cheerio": "1.0.0-rc.12", "cheerio": "1.0.0-rc.12",
"ioredis": "^5.2.3", "ioredis": "^5.2.3",
"next": "12.2.5", "next": "12.2.5",
"node-fetch": "^3.2.10",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"sharp": "^0.31.0" "sharp": "^0.31.0"

View file

@ -8,6 +8,7 @@ specifiers:
cheerio: 1.0.0-rc.12 cheerio: 1.0.0-rc.12
eslint: 8.22.0 eslint: 8.22.0
eslint-config-next: 12.2.5 eslint-config-next: 12.2.5
ioredis: ^5.2.3
next: 12.2.5 next: 12.2.5
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0 react-dom: 18.2.0
@ -18,6 +19,7 @@ specifiers:
dependencies: dependencies:
axios: 0.27.2 axios: 0.27.2
cheerio: 1.0.0-rc.12 cheerio: 1.0.0-rc.12
ioredis: 5.2.4
next: 12.2.5_ivfob5dyaiglqb5g2zdrumbbbm next: 12.2.5_ivfob5dyaiglqb5g2zdrumbbbm
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0_react@18.2.0 react-dom: 18.2.0_react@18.2.0
@ -85,6 +87,10 @@ packages:
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
dev: true dev: true
/@ioredis/commands/1.2.0:
resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==}
dev: false
/@next/env/12.2.5: /@next/env/12.2.5:
resolution: {integrity: sha512-vLPLV3cpPGjUPT3PjgRj7e3nio9t6USkuew3JE/jMeon/9Mvp1WyR18v3iwnCuX7eUAm1HmAbJHHLAbcu/EJcw==} resolution: {integrity: sha512-vLPLV3cpPGjUPT3PjgRj7e3nio9t6USkuew3JE/jMeon/9Mvp1WyR18v3iwnCuX7eUAm1HmAbJHHLAbcu/EJcw==}
dev: false dev: false
@ -561,6 +567,11 @@ packages:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
dev: false dev: false
/cluster-key-slot/1.1.2:
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
engines: {node: '>=0.10.0'}
dev: false
/color-convert/2.0.1: /color-convert/2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'} engines: {node: '>=7.0.0'}
@ -665,7 +676,6 @@ packages:
optional: true optional: true
dependencies: dependencies:
ms: 2.1.2 ms: 2.1.2
dev: true
/decompress-response/6.0.0: /decompress-response/6.0.0:
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
@ -696,6 +706,11 @@ packages:
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
dev: false dev: false
/denque/2.1.0:
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
engines: {node: '>=0.10'}
dev: false
/detect-libc/2.0.1: /detect-libc/2.0.1:
resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==} resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -1381,6 +1396,23 @@ packages:
side-channel: 1.0.4 side-channel: 1.0.4
dev: true dev: true
/ioredis/5.2.4:
resolution: {integrity: sha512-qIpuAEt32lZJQ0XyrloCRdlEdUUNGG9i0UOk6zgzK6igyudNWqEBxfH6OlbnOOoBBvr1WB02mm8fR55CnikRng==}
engines: {node: '>=12.22.0'}
dependencies:
'@ioredis/commands': 1.2.0
cluster-key-slot: 1.1.2
debug: 4.3.4
denque: 2.1.0
lodash.defaults: 4.2.0
lodash.isarguments: 3.1.0
redis-errors: 1.2.0
redis-parser: 3.0.0
standard-as-callback: 2.1.0
transitivePeerDependencies:
- supports-color
dev: false
/is-arrayish/0.3.2: /is-arrayish/0.3.2:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
dev: false dev: false
@ -1545,6 +1577,14 @@ packages:
p-locate: 5.0.0 p-locate: 5.0.0
dev: true dev: true
/lodash.defaults/4.2.0:
resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
dev: false
/lodash.isarguments/3.1.0:
resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
dev: false
/lodash.merge/4.6.2: /lodash.merge/4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
dev: true dev: true
@ -1610,7 +1650,6 @@ packages:
/ms/2.1.2: /ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true
/ms/2.1.3: /ms/2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@ -1941,6 +1980,18 @@ packages:
dependencies: dependencies:
picomatch: 2.3.1 picomatch: 2.3.1
/redis-errors/1.2.0:
resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
engines: {node: '>=4'}
dev: false
/redis-parser/3.0.0:
resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==}
engines: {node: '>=4'}
dependencies:
redis-errors: 1.2.0
dev: false
/regenerator-runtime/0.13.9: /regenerator-runtime/0.13.9:
resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==} resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==}
dev: true dev: true
@ -2093,6 +2144,10 @@ packages:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
/standard-as-callback/2.1.0:
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
dev: false
/string.prototype.matchall/4.0.7: /string.prototype.matchall/4.0.7:
resolution: {integrity: sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==} resolution: {integrity: sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==}
dependencies: dependencies:

View file

@ -1,16 +1,16 @@
import Image from 'next/future/image' import Image from 'next/future/image';
import Link from 'next/link' import Link from 'next/link';
import { NextRouter } from 'next/router' import { NextRouter } from 'next/router';
import { Media } from '../../interfaces/shared/title' import { Media } from '../../interfaces/shared/title';
import { getProxiedIMDbImgUrl, modifyIMDbImg } from '../../utils/helpers' import { getProxiedIMDbImgUrl, modifyIMDbImg } from '../../utils/helpers';
import styles from '../../styles/modules/components/title/media.module.scss' import styles from '../../styles/modules/components/title/media.module.scss';
type Props = { type Props = {
className: string className: string;
media: Media media: Media;
router: NextRouter router: NextRouter;
} };
const Media = ({ className, media, router }: Props) => { const Media = ({ className, media, router }: Props) => {
return ( return (
@ -32,8 +32,9 @@ const Media = ({ className, media, router }: Props) => {
modifyIMDbImg(media.trailer.thumbnail) modifyIMDbImg(media.trailer.thumbnail)
)} )}
className={styles.trailer__video} className={styles.trailer__video}
preload="none"
> >
{media.trailer.urls.map((source) => ( {media.trailer.urls.map(source => (
<source <source
key={source.url} key={source.url}
type={source.mimeType} type={source.mimeType}
@ -46,7 +47,7 @@ const Media = ({ className, media, router }: Props) => {
)} )}
{!!media.videos.total && {!!media.videos.total &&
media.videos.videos.map((video) => ( media.videos.videos.map(video => (
<Link href={`/video/${video.id}`} key={video.id}> <Link href={`/video/${video.id}`} key={video.id}>
<a className={styles.video}> <a className={styles.video}>
<Image <Image
@ -69,7 +70,7 @@ const Media = ({ className, media, router }: Props) => {
<section className={styles.images}> <section className={styles.images}>
<h2 className="heading heading__secondary">Images</h2> <h2 className="heading heading__secondary">Images</h2>
<div className={styles.images__container}> <div className={styles.images__container}>
{media.images.images.map((image) => ( {media.images.images.map(image => (
<figure key={image.id} className={styles.image}> <figure key={image.id} className={styles.image}>
<Image <Image
className={styles.image__img} className={styles.image__img}
@ -87,6 +88,6 @@ const Media = ({ className, media, router }: Props) => {
</section> </section>
)} )}
</div> </div>
) );
} };
export default Media export default Media;

View file

@ -1,59 +1,71 @@
import { NextApiRequest, NextApiResponse } from 'next' import { NextApiRequest, NextApiResponse } from 'next';
import redis from '../../utils/redis' import redis from '../../utils/redis';
import axiosInstance from '../../utils/axiosInstance' import axiosInstance from '../../utils/axiosInstance';
import { AxiosResponse } from 'axios'
const regex = const regex =
/^https:\/\/((m\.)?media-amazon\.com|imdb-video\.media-imdb\.com).*\.(jpg|jpeg|png|mp4|gif|webp).*$/ /^https:\/\/((m\.)?media-amazon\.com|imdb-video\.media-imdb\.com).*\.(jpg|jpeg|png|mp4|gif|webp).*$/;
export default async function handler( export default async function handler(
req: NextApiRequest, req: NextApiRequest,
res: NextApiResponse res: NextApiResponse
) { ) {
const mediaUrl = req.query.url as string | undefined try {
const mediaUrl = req.query.url as string | undefined;
// 1. returning if query is illegal
if (!mediaUrl || !regex.test(mediaUrl)) if (!mediaUrl || !regex.test(mediaUrl))
return res.status(400).json({ return res.status(400).json({
success: false, success: false,
message: 'Invalid query', message: 'Invalid query',
}) });
if (process.env.USE_REDIS === 'true') { // 2. sending streamed response if redis isn't enabled
const cachedMedia = await redis.getBuffer(mediaUrl) if (redis === null) {
const mediaRes = await axiosInstance.get(mediaUrl, {
responseType: 'stream',
});
res.setHeader('Content-Type', mediaRes.headers['content-type']);
mediaRes.data.pipe(res);
return;
}
// 3. else if resourced is cached, sending it
const cachedMedia = await redis!.getBuffer(mediaUrl);
if (cachedMedia) { if (cachedMedia) {
res.setHeader('x-cached', 'true') res.setHeader('x-cached', 'true');
res.status(302).send(cachedMedia) res.status(302).send(cachedMedia);
return return;
}
} }
let mediaRes: AxiosResponse // 4. else getting, caching and sending response
try { const mediaRes = await axiosInstance(mediaUrl, {
mediaRes = await axiosInstance(mediaUrl, { responseType: 'arraybuffer' }) responseType: 'arraybuffer',
});
const data = mediaRes.data;
// saving in redis for 30 minutes
await redis!.setex(mediaUrl, 30 * 60, Buffer.from(data));
// sending media
res.setHeader('x-cached', 'false');
res.send(data);
// sending token response on any error
} catch { } catch {
res.status(404) res.status(404);
res.json({ res.json({
success: false, success: false,
message: 'Error from IMDb', message: 'something went wrong',
}) });
return return;
} }
const data = mediaRes.data
if (process.env.USE_REDIS === 'true') {
// save in redis for 30 minutes
await redis.setex(mediaUrl, 30 * 60, Buffer.from(data))
}
// send media
res.setHeader('x-cached', 'false')
res.send(data)
} }
export const config = { export const config = {
api: { api: {
responseLimit: false, responseLimit: false,
}, },
} };

View file

@ -1,11 +1,11 @@
import Redis from 'ioredis' import Redis from 'ioredis';
const redisUrl = process.env.REDIS_URL const redisUrl = process.env.REDIS_URL;
const toUseRedis = process.env.USE_REDIS === 'true';
if (!redisUrl) { let redis: Redis | null;
throw 'Please set the REDIS_URL environment variable.'
}
const redis = new Redis(redisUrl) if (toUseRedis && redisUrl) redis = new Redis(redisUrl);
else redis = null;
export default redis export default redis;