From 6dfae4fa20dfda8e35df7b3b4268482df79bf645 Mon Sep 17 00:00:00 2001 From: KagChi Date: Sat, 3 Sep 2022 14:46:13 +0700 Subject: [PATCH 1/9] chore: initial redis cache --- README.md | 6 ++++- package.json | 5 ++-- src/Entities/Track.ts | 17 +++++++++++++ src/app.cache.service.ts | 18 +++++++++++++ src/app.controller.ts | 55 +++++++++++++++++++++++++++++++++++++--- src/app.module.ts | 3 ++- tsconfig.json | 3 ++- 7 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 src/Entities/Track.ts create mode 100644 src/app.cache.service.ts diff --git a/README.md b/README.md index 3bb8f99..f4c81df 100644 --- a/README.md +++ b/README.md @@ -17,4 +17,8 @@ - Multiple Lavalink nodes support - Configure-able response timeout - Vercel Serverless support -- Use preferred custom node using `x-node-name` headers \ No newline at end of file +- Use preferred custom node using `x-node-name` headers + +# Redis Caching (NEW) +- Tracks cache are guaranteed not to be bypassed +- Redis must be using `RedisSearch` and `RedisJSON` \ No newline at end of file diff --git a/package.json b/package.json index b1197b4..5a32e03 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,9 @@ "name": "nezly", "version": "1.0.0", "description": "A REST Proxy container for the Lavalink REST API.", - "main": "api/index.js", + "main": "dist/api/index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "build": "rimraf dist && tsc", "lint": "eslint **/*.ts", "lint:fix": "eslint **/*.ts --fix" }, @@ -53,6 +53,7 @@ "dotenv": "^16.0.1", "express": "^4.18.1", "lavalink-api-types": "^1.1.2", + "redis-om": "^0.3.6", "reflect-metadata": "^0.1.13", "rxjs": "^7.5.6", "undici": "^5.10.0" diff --git a/src/Entities/Track.ts b/src/Entities/Track.ts new file mode 100644 index 0000000..9ce9451 --- /dev/null +++ b/src/Entities/Track.ts @@ -0,0 +1,17 @@ +import { Entity, Schema } from "redis-om"; + +export class Track extends Entity { } + +export const TrackSchema = new Schema(Track, { + track: { type: "string" }, + identifier: { type: "string" }, + isSeekable: { type: "boolean" }, + author: { type: "string" }, + length: { type: "number" }, + isStream: { type: "boolean" }, + position: { type: "number" }, + title: { type: "text" }, + uri: { type: "string" }, + sourceName: { type: "string" }, + artworkUrl: { type: "string" } +}); diff --git a/src/app.cache.service.ts b/src/app.cache.service.ts new file mode 100644 index 0000000..6799e61 --- /dev/null +++ b/src/app.cache.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from "@nestjs/common"; +import { Result } from "@sapphire/result"; +import { Client, Repository } from "redis-om"; +import { TrackSchema, Track } from "./Entities/Track"; + +@Injectable() +export class AppCacheService { + public client = new Client(); + public constructor( + ) { + void Result.fromAsync(() => this.client.open(process.env.REDIS_URL)); + void Result.fromAsync(() => this.client.fetchRepository(TrackSchema).createIndex()); + } + + public trackRepository(): Repository { + return this.client.fetchRepository(TrackSchema); + } +} diff --git a/src/app.controller.ts b/src/app.controller.ts index a130a46..ac0360c 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,3 +1,6 @@ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ +/* eslint-disable no-nested-ternary */ +/* eslint-disable max-len */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { Body, Controller, Get, Post, Query, Req, Res } from "@nestjs/common"; import { Response, Request } from "express"; @@ -6,11 +9,15 @@ import { AppNodeService } from "./app.node.service"; import { REST } from "@kirishima/rest"; import { Result } from "@sapphire/result"; import { Time } from "@sapphire/time-utilities"; +import { AppCacheService } from "./app.cache.service"; @Controller() export class AppController { - public constructor(private readonly appNodeService: AppNodeService) {} + public constructor( + private readonly appNodeService: AppNodeService, + private readonly appCacheService: AppCacheService + ) {} @Get() public getIndex(@Res() res: Response): Response { @@ -28,18 +35,50 @@ export class AppController { try { if (req.headers.authorization !== process.env.AUTHORIZATION) return res.sendStatus(401); if (!identifier || (resolveAttempt && resolveAttempt > 3)) return res.json({ playlistInfo: {}, loadType: LoadTypeEnum.NO_MATCHES, tracks: [] }); - const node = this.appNodeService.getLavalinkNode(req.headers["x-node-name"] as string, excludeNode); - const nodeRest = new REST(node.secure ? `https://${node.host}` : `http://${node.host}`) - .setAuthorization(node.auth); const source = identifier.split(":")[0]; const query = identifier.split(":")[1]; + const cachedTracks = await this.appCacheService.trackRepository() + .search() + .where("title") + .matches(query) + .and("sourceName") + .equalTo("youtube") + .return.all(); + + if (cachedTracks.length) { + return res.json({ + // TODO: rework this + loadType: LoadTypeEnum.SEARCH_RESULT, + tracks: cachedTracks.map(x => ({ + info: { + identifier: x.toJSON().identifier, + isSeekable: x.toJSON().isSeekable, + author: x.toJSON().author, + length: x.toJSON().length, + isStream: x.toJSON().isStream, + position: x.toJSON().position, + title: x.toJSON().title, + uri: x.toJSON().uri, + sourceName: x.toJSON().sourceName, + artworkUrl: x.toJSON().artworkUrl + }, + track: x.toJSON().track + })) + }); + } + + const node = this.appNodeService.getLavalinkNode(req.headers["x-node-name"] as string, excludeNode); + const nodeRest = new REST(node.secure ? `https://${node.host}` : `http://${node.host}`) + .setAuthorization(node.auth); + const timeout = setTimeout(() => Result.fromAsync(this.getLoadTracks(res, req, identifier, node.name)), Time.Second * Number(process.env.TIMEOUT_SECONDS ?? 3)); const result = await nodeRest.loadTracks(source ? { source, query } : identifier); clearTimeout(timeout); if (!result.tracks.length) return await this.getLoadTracks(res, req, identifier, node.name, (resolveAttempt ?? 0) + 1); + for (const track of result.tracks) await this.appCacheService.trackRepository().createAndSave({ track: track.track, ...track.info }); return res.json(result); } catch (e) { return res.status(500).json({ status: 500, message: e.message }); @@ -91,4 +130,12 @@ export class AppController { return res.status(500).json({ status: 500, message: e.message }); } } + + public parseUrl(rawUrl: string): string | null { + try { + return rawUrl.split(":").slice(1, 3).join(":"); + } catch { + return null; + } + } } diff --git a/src/app.module.ts b/src/app.module.ts index 36deb27..5388f7a 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,11 +1,12 @@ import { Module } from "@nestjs/common"; import "dotenv/config"; +import { AppCacheService } from "./app.cache.service"; import { AppController } from "./app.controller"; import { AppNodeService } from "./app.node.service"; @Module({ controllers: [AppController], - providers: [AppNodeService], + providers: [AppNodeService, AppCacheService], imports: [] }) diff --git a/tsconfig.json b/tsconfig.json index 898cc27..5fb0b41 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,8 +11,9 @@ "sourceMap": true, "baseUrl": "./", "incremental": true, + "outDir": "./dist", "skipLibCheck": true }, "include": ["./**/**.ts"], - "exclude": ["node_modules"] + "exclude": ["node_modules", "dist"] } From 6070ac94b980d45d78bba922cfbdf4bf38c96dac Mon Sep 17 00:00:00 2001 From: KagChi Date: Sat, 3 Sep 2022 14:49:20 +0700 Subject: [PATCH 2/9] chore: rename build scripts --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5a32e03..9146faf 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "A REST Proxy container for the Lavalink REST API.", "main": "dist/api/index.js", "scripts": { - "build": "rimraf dist && tsc", + "compile": "rimraf dist && tsc", "lint": "eslint **/*.ts", "lint:fix": "eslint **/*.ts --fix" }, From 11cbab3908c2ffbe7559d2201cd839513ac92a05 Mon Sep 17 00:00:00 2001 From: KagChi Date: Sat, 3 Sep 2022 15:47:43 +0700 Subject: [PATCH 3/9] feat: decode caching --- src/app.controller.ts | 97 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 20 deletions(-) diff --git a/src/app.controller.ts b/src/app.controller.ts index ac0360c..e1357ca 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -4,7 +4,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { Body, Controller, Get, Post, Query, Req, Res } from "@nestjs/common"; import { Response, Request } from "express"; -import { LoadTypeEnum } from "lavalink-api-types"; +import { LavalinkTrack, LoadTypeEnum } from "lavalink-api-types"; import { AppNodeService } from "./app.node.service"; import { REST } from "@kirishima/rest"; import { Result } from "@sapphire/result"; @@ -48,25 +48,27 @@ export class AppController { .return.all(); if (cachedTracks.length) { - return res.json({ - // TODO: rework this - loadType: LoadTypeEnum.SEARCH_RESULT, - tracks: cachedTracks.map(x => ({ - info: { - identifier: x.toJSON().identifier, - isSeekable: x.toJSON().isSeekable, - author: x.toJSON().author, - length: x.toJSON().length, - isStream: x.toJSON().isStream, - position: x.toJSON().position, - title: x.toJSON().title, - uri: x.toJSON().uri, - sourceName: x.toJSON().sourceName, - artworkUrl: x.toJSON().artworkUrl - }, - track: x.toJSON().track - })) - }); + return res + .header({ "x-cache-hits": true }) + .json({ + // TODO: rework this + loadType: LoadTypeEnum.SEARCH_RESULT, + tracks: cachedTracks.map(x => ({ + info: { + identifier: x.toJSON().identifier, + isSeekable: x.toJSON().isSeekable, + author: x.toJSON().author, + length: x.toJSON().length, + isStream: x.toJSON().isStream, + position: x.toJSON().position, + title: x.toJSON().title, + uri: x.toJSON().uri, + sourceName: x.toJSON().sourceName, + artworkUrl: x.toJSON().artworkUrl + }, + track: x.toJSON().track + })) + }); } const node = this.appNodeService.getLavalinkNode(req.headers["x-node-name"] as string, excludeNode); @@ -94,6 +96,30 @@ export class AppController { ): Promise { try { if (req.headers.authorization !== process.env.AUTHORIZATION) return res.sendStatus(401); + + const cachedTrack = await this.appCacheService.trackRepository() + .search() + .where("track") + .equalTo(track) + .return.first(); + + if (cachedTrack) { + return res + .header({ "x-cache-hits": true }) + .json({ + identifier: cachedTrack.toJSON().identifier, + isSeekable: cachedTrack.toJSON().isSeekable, + author: cachedTrack.toJSON().author, + length: cachedTrack.toJSON().length, + isStream: cachedTrack.toJSON().isStream, + position: cachedTrack.toJSON().position, + title: cachedTrack.toJSON().title, + uri: cachedTrack.toJSON().uri, + sourceName: cachedTrack.toJSON().sourceName, + artworkUrl: cachedTrack.toJSON().artworkUrl + }); + } + const node = this.appNodeService.getLavalinkNode(req.headers["x-node-name"] as string, excludeNode); const nodeRest = new REST(node.secure ? `https://${node.host}` : `http://${node.host}`) .setAuthorization(node.auth); @@ -117,6 +143,37 @@ export class AppController { ): Promise { try { if (req.headers.authorization !== process.env.AUTHORIZATION) return res.sendStatus(401); + const results: any[] = []; + + for (const track of tracks) { + const cachedTrack = await this.appCacheService.trackRepository() + .search() + .where("track") + .equalTo(track) + .return.first(); + + if (cachedTrack) { + results.push({ + identifier: cachedTrack.toJSON().identifier, + isSeekable: cachedTrack.toJSON().isSeekable, + author: cachedTrack.toJSON().author, + length: cachedTrack.toJSON().length, + isStream: cachedTrack.toJSON().isStream, + position: cachedTrack.toJSON().position, + title: cachedTrack.toJSON().title, + uri: cachedTrack.toJSON().uri, + sourceName: cachedTrack.toJSON().sourceName, + artworkUrl: cachedTrack.toJSON().artworkUrl + }); + } + } + + if (results.length) { + return res + .header({ "x-cache-hits": true }) + .json(results); + } + const node = this.appNodeService.getLavalinkNode(req.headers["x-node-name"] as string, excludeNode); const nodeRest = new REST(node.secure ? `https://${node.host}` : `http://${node.host}`) .setAuthorization(node.auth); From 10600cc1fac775067f162b7bc65634a6ee663e70 Mon Sep 17 00:00:00 2001 From: KagChi Date: Sat, 3 Sep 2022 15:49:05 +0700 Subject: [PATCH 4/9] fix: remove unused import --- src/app.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app.controller.ts b/src/app.controller.ts index e1357ca..0aa114e 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -4,7 +4,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { Body, Controller, Get, Post, Query, Req, Res } from "@nestjs/common"; import { Response, Request } from "express"; -import { LavalinkTrack, LoadTypeEnum } from "lavalink-api-types"; +import { LoadTypeEnum } from "lavalink-api-types"; import { AppNodeService } from "./app.node.service"; import { REST } from "@kirishima/rest"; import { Result } from "@sapphire/result"; From 8cb9378cd49c3a44a9cef2bbed81208659538fce Mon Sep 17 00:00:00 2001 From: KagChi Date: Sat, 3 Sep 2022 15:52:38 +0700 Subject: [PATCH 5/9] feat: save not cached tracks --- src/app.controller.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/app.controller.ts b/src/app.controller.ts index 0aa114e..e47f000 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -125,10 +125,12 @@ export class AppController { .setAuthorization(node.auth); const timeout = setTimeout(() => Result.fromAsync(this.getDecodeTrack(res, req, track, node.name)), Time.Second * Number(process.env.TIMEOUT_SECONDS ?? 3)); - const result = await nodeRest.decodeTracks([track]); + const results = await nodeRest.decodeTracks([track]); clearTimeout(timeout); - return res.json(result[0].info); + for (const lavalinkTrack of results) await this.appCacheService.trackRepository().createAndSave({ track, ...lavalinkTrack.info }); + + return res.json(results[0].info); } catch (e) { return res.status(500).json({ status: 500, message: e.message }); } @@ -179,10 +181,12 @@ export class AppController { .setAuthorization(node.auth); const timeout = setTimeout(() => Result.fromAsync(this.postDecodeTracks(res, req, tracks, node.name)), Time.Second * Number(process.env.TIMEOUT_SECONDS ?? 3)); - const result = await nodeRest.decodeTracks(tracks); + const decodeResults = await nodeRest.decodeTracks(tracks); clearTimeout(timeout); - return res.json(result.map(x => x.info)); + for (const track of decodeResults) await this.appCacheService.trackRepository().createAndSave({ track: track.track, ...track.info }); + + return res.json(decodeResults.map(x => x.info)); } catch (e) { return res.status(500).json({ status: 500, message: e.message }); } From 399591dc846eaca72d8304578780956792ac0f15 Mon Sep 17 00:00:00 2001 From: KagChi Date: Sat, 3 Sep 2022 15:58:16 +0700 Subject: [PATCH 6/9] fix: properly assign header --- src/app.controller.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app.controller.ts b/src/app.controller.ts index e47f000..fd7ec34 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -49,7 +49,7 @@ export class AppController { if (cachedTracks.length) { return res - .header({ "x-cache-hits": true }) + .header({ "X-Cache-Hits": true }) .json({ // TODO: rework this loadType: LoadTypeEnum.SEARCH_RESULT, @@ -105,7 +105,7 @@ export class AppController { if (cachedTrack) { return res - .header({ "x-cache-hits": true }) + .header({ "X-Cache-Hits": true }) .json({ identifier: cachedTrack.toJSON().identifier, isSeekable: cachedTrack.toJSON().isSeekable, @@ -172,7 +172,7 @@ export class AppController { if (results.length) { return res - .header({ "x-cache-hits": true }) + .header({ "X-Cache-Hits": true }) .json(results); } From 2e63efff5a79a967861f4397a8bb50c613e5ead9 Mon Sep 17 00:00:00 2001 From: KagChi Date: Sat, 3 Sep 2022 16:34:20 +0700 Subject: [PATCH 7/9] feat: add playlist schema --- src/Entities/Playlist.ts | 10 ++++++++++ src/Entities/Track.ts | 2 +- src/app.cache.service.ts | 8 +++++++- src/app.controller.ts | 12 ++++++------ 4 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 src/Entities/Playlist.ts diff --git a/src/Entities/Playlist.ts b/src/Entities/Playlist.ts new file mode 100644 index 0000000..1f655ac --- /dev/null +++ b/src/Entities/Playlist.ts @@ -0,0 +1,10 @@ +import { Entity, Schema } from "redis-om"; + +export class Playlist extends Entity { } + +export const PlaylistSchema = new Schema(Playlist, { + playlistName: { type: "string" }, + playlistUrl: { type: "string" }, + playlistSelectedTrack: { type: "number" }, + tracks: { type: "string[]" } +}); diff --git a/src/Entities/Track.ts b/src/Entities/Track.ts index 9ce9451..75c9a27 100644 --- a/src/Entities/Track.ts +++ b/src/Entities/Track.ts @@ -11,7 +11,7 @@ export const TrackSchema = new Schema(Track, { isStream: { type: "boolean" }, position: { type: "number" }, title: { type: "text" }, - uri: { type: "string" }, + uri: { type: "text" }, sourceName: { type: "string" }, artworkUrl: { type: "string" } }); diff --git a/src/app.cache.service.ts b/src/app.cache.service.ts index 6799e61..3b0a082 100644 --- a/src/app.cache.service.ts +++ b/src/app.cache.service.ts @@ -1,6 +1,7 @@ import { Injectable } from "@nestjs/common"; import { Result } from "@sapphire/result"; import { Client, Repository } from "redis-om"; +import { PlaylistSchema } from "./Entities/Playlist"; import { TrackSchema, Track } from "./Entities/Track"; @Injectable() @@ -10,9 +11,14 @@ export class AppCacheService { ) { void Result.fromAsync(() => this.client.open(process.env.REDIS_URL)); void Result.fromAsync(() => this.client.fetchRepository(TrackSchema).createIndex()); + void Result.fromAsync(() => this.client.fetchRepository(PlaylistSchema).createIndex()); } - public trackRepository(): Repository { + public getTrackRepository(): Repository { return this.client.fetchRepository(TrackSchema); } + + public getPlaylistTrackRepository(): Repository { + return this.client.fetchRepository(PlaylistSchema); + } } diff --git a/src/app.controller.ts b/src/app.controller.ts index fd7ec34..5ec285a 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -39,7 +39,7 @@ export class AppController { const source = identifier.split(":")[0]; const query = identifier.split(":")[1]; - const cachedTracks = await this.appCacheService.trackRepository() + const cachedTracks = await this.appCacheService.getTrackRepository() .search() .where("title") .matches(query) @@ -80,7 +80,7 @@ export class AppController { clearTimeout(timeout); if (!result.tracks.length) return await this.getLoadTracks(res, req, identifier, node.name, (resolveAttempt ?? 0) + 1); - for (const track of result.tracks) await this.appCacheService.trackRepository().createAndSave({ track: track.track, ...track.info }); + for (const track of result.tracks) await this.appCacheService.getTrackRepository().createAndSave({ track: track.track, ...track.info }); return res.json(result); } catch (e) { return res.status(500).json({ status: 500, message: e.message }); @@ -97,7 +97,7 @@ export class AppController { try { if (req.headers.authorization !== process.env.AUTHORIZATION) return res.sendStatus(401); - const cachedTrack = await this.appCacheService.trackRepository() + const cachedTrack = await this.appCacheService.getTrackRepository() .search() .where("track") .equalTo(track) @@ -128,7 +128,7 @@ export class AppController { const results = await nodeRest.decodeTracks([track]); clearTimeout(timeout); - for (const lavalinkTrack of results) await this.appCacheService.trackRepository().createAndSave({ track, ...lavalinkTrack.info }); + for (const lavalinkTrack of results) await this.appCacheService.getTrackRepository().createAndSave({ track, ...lavalinkTrack.info }); return res.json(results[0].info); } catch (e) { @@ -148,7 +148,7 @@ export class AppController { const results: any[] = []; for (const track of tracks) { - const cachedTrack = await this.appCacheService.trackRepository() + const cachedTrack = await this.appCacheService.getTrackRepository() .search() .where("track") .equalTo(track) @@ -184,7 +184,7 @@ export class AppController { const decodeResults = await nodeRest.decodeTracks(tracks); clearTimeout(timeout); - for (const track of decodeResults) await this.appCacheService.trackRepository().createAndSave({ track: track.track, ...track.info }); + for (const track of decodeResults) await this.appCacheService.getTrackRepository().createAndSave({ track: track.track, ...track.info }); return res.json(decodeResults.map(x => x.info)); } catch (e) { From aca5dc8427c192be998acdc08d2165751b487235 Mon Sep 17 00:00:00 2001 From: KagChi Date: Sat, 3 Sep 2022 16:40:32 +0700 Subject: [PATCH 8/9] fix: redis cache is optional --- src/app.controller.ts | 46 ++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/app.controller.ts b/src/app.controller.ts index 5ec285a..216165a 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -39,13 +39,15 @@ export class AppController { const source = identifier.split(":")[0]; const query = identifier.split(":")[1]; - const cachedTracks = await this.appCacheService.getTrackRepository() - .search() - .where("title") - .matches(query) - .and("sourceName") - .equalTo("youtube") - .return.all(); + const cachedTracks = this.appCacheService.client.isOpen() + ? await this.appCacheService.getTrackRepository() + .search() + .where("title") + .matches(query) + .and("sourceName") + .equalTo("youtube") + .return.all() + : []; if (cachedTracks.length) { return res @@ -80,7 +82,7 @@ export class AppController { clearTimeout(timeout); if (!result.tracks.length) return await this.getLoadTracks(res, req, identifier, node.name, (resolveAttempt ?? 0) + 1); - for (const track of result.tracks) await this.appCacheService.getTrackRepository().createAndSave({ track: track.track, ...track.info }); + if (this.appCacheService.client.isOpen()) for (const track of result.tracks) await this.appCacheService.getTrackRepository().createAndSave({ track: track.track, ...track.info }); return res.json(result); } catch (e) { return res.status(500).json({ status: 500, message: e.message }); @@ -97,11 +99,13 @@ export class AppController { try { if (req.headers.authorization !== process.env.AUTHORIZATION) return res.sendStatus(401); - const cachedTrack = await this.appCacheService.getTrackRepository() - .search() - .where("track") - .equalTo(track) - .return.first(); + const cachedTrack = this.appCacheService.client.isOpen() + ? await this.appCacheService.getTrackRepository() + .search() + .where("track") + .equalTo(track) + .return.first() + : null; if (cachedTrack) { return res @@ -128,7 +132,7 @@ export class AppController { const results = await nodeRest.decodeTracks([track]); clearTimeout(timeout); - for (const lavalinkTrack of results) await this.appCacheService.getTrackRepository().createAndSave({ track, ...lavalinkTrack.info }); + if (this.appCacheService.client.isOpen()) for (const lavalinkTrack of results) await this.appCacheService.getTrackRepository().createAndSave({ track, ...lavalinkTrack.info }); return res.json(results[0].info); } catch (e) { @@ -148,11 +152,13 @@ export class AppController { const results: any[] = []; for (const track of tracks) { - const cachedTrack = await this.appCacheService.getTrackRepository() - .search() - .where("track") - .equalTo(track) - .return.first(); + const cachedTrack = this.appCacheService.client.isOpen() + ? await this.appCacheService.getTrackRepository() + .search() + .where("track") + .equalTo(track) + .return.first() + : null; if (cachedTrack) { results.push({ @@ -184,7 +190,7 @@ export class AppController { const decodeResults = await nodeRest.decodeTracks(tracks); clearTimeout(timeout); - for (const track of decodeResults) await this.appCacheService.getTrackRepository().createAndSave({ track: track.track, ...track.info }); + if (this.appCacheService.client.isOpen()) for (const track of decodeResults) await this.appCacheService.getTrackRepository().createAndSave({ track: track.track, ...track.info }); return res.json(decodeResults.map(x => x.info)); } catch (e) { From 06809135cb387f07526ca815033e9215385db106 Mon Sep 17 00:00:00 2001 From: KagChi Date: Sat, 3 Sep 2022 16:48:42 +0700 Subject: [PATCH 9/9] fix: only connects if redis present --- src/app.cache.service.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/app.cache.service.ts b/src/app.cache.service.ts index 3b0a082..86047c7 100644 --- a/src/app.cache.service.ts +++ b/src/app.cache.service.ts @@ -9,9 +9,11 @@ export class AppCacheService { public client = new Client(); public constructor( ) { - void Result.fromAsync(() => this.client.open(process.env.REDIS_URL)); - void Result.fromAsync(() => this.client.fetchRepository(TrackSchema).createIndex()); - void Result.fromAsync(() => this.client.fetchRepository(PlaylistSchema).createIndex()); + if (process.env.REDIS_URL) { + void Result.fromAsync(() => this.client.open(process.env.REDIS_URL)); + void Result.fromAsync(() => this.client.fetchRepository(TrackSchema).createIndex()); + void Result.fromAsync(() => this.client.fetchRepository(PlaylistSchema).createIndex()); + } } public getTrackRepository(): Repository {