diff --git a/package.json b/package.json index 8ee6c7e..34a957f 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "ejs": "^3.1.9", "express": "~4.18.2", "express-actuator": "^1.8.4", + "redis": "^4.6.7", "winston": "^3.9.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3e9b456..1f06699 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,8 @@ -lockfileVersion: '6.0' +lockfileVersion: '6.1' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false dependencies: ejs: @@ -10,6 +14,9 @@ dependencies: express-actuator: specifier: ^1.8.4 version: 1.8.4 + redis: + specifier: ^4.6.7 + version: 4.6.7 winston: specifier: ^3.9.0 version: 3.9.0 @@ -142,6 +149,55 @@ packages: fastq: 1.15.0 dev: true + /@redis/bloom@1.2.0(@redis/client@1.5.8): + resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.8 + dev: false + + /@redis/client@1.5.8: + resolution: {integrity: sha512-xzElwHIO6rBAqzPeVnCzgvrnBEcFL1P0w8P65VNLRkdVW8rOE58f52hdj0BDgmsdOm4f1EoXPZtH4Fh7M/qUpw==} + engines: {node: '>=14'} + dependencies: + cluster-key-slot: 1.1.2 + generic-pool: 3.9.0 + yallist: 4.0.0 + dev: false + + /@redis/graph@1.1.0(@redis/client@1.5.8): + resolution: {integrity: sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.8 + dev: false + + /@redis/json@1.0.4(@redis/client@1.5.8): + resolution: {integrity: sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.8 + dev: false + + /@redis/search@1.1.3(@redis/client@1.5.8): + resolution: {integrity: sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.8 + dev: false + + /@redis/time-series@1.0.4(@redis/client@1.5.8): + resolution: {integrity: sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.8 + dev: false + /@tsconfig/recommended@1.0.2: resolution: {integrity: sha512-dbHBtbWBOjq0/otpopAE02NT2Cm05Qe2JsEKeCf/wjSYbI2hz8nCqnpnOJWHATgjDz4fd3dchs3Wy1gQGjfN6w==} dev: true @@ -516,6 +572,11 @@ packages: fsevents: 2.3.2 dev: true + /cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + dev: false + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -972,6 +1033,11 @@ packages: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: false + /generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + dev: false + /get-intrinsic@1.2.1: resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} dependencies: @@ -1506,6 +1572,17 @@ packages: picomatch: 2.3.1 dev: true + /redis@4.6.7: + resolution: {integrity: sha512-KrkuNJNpCwRm5vFJh0tteMxW8SaUzkm5fBH7eL5hd/D0fAkzvapxbfGPP/r+4JAXdQuX7nebsBkBqA2RHB7Usw==} + dependencies: + '@redis/bloom': 1.2.0(@redis/client@1.5.8) + '@redis/client': 1.5.8 + '@redis/graph': 1.1.0(@redis/client@1.5.8) + '@redis/json': 1.0.4(@redis/client@1.5.8) + '@redis/search': 1.1.3(@redis/client@1.5.8) + '@redis/time-series': 1.0.4(@redis/client@1.5.8) + dev: false + /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -1820,7 +1897,6 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 0000000..8ed147d --- /dev/null +++ b/src/api.ts @@ -0,0 +1,39 @@ +import { Router } from "express"; +import { redisClient } from "./app"; + +const router = Router(); + +router.get("/", async (req, res, next) => { + const result = await redisClient.json.get("ldn"); + + if (result == null || typeof (result) != "object") { + return res.sendStatus(404); + } + + return res.send(result); +}); + +router.get("/public_games", async (req, res, next) => { + let gameFilter = ""; + + if (req.query.titleid != null && (req.query.titleid as string)?.length > 0) { + gameFilter = req.query.titleid as string; + } + + const results = await redisClient.json.get("games"); + + if (results == null || typeof (results) != "object") { + return res.sendStatus(404); + } + + const games = Object.entries(results).map(([_, game]) => game); + + if (gameFilter.length > 0) { + return res.send(games.filter((game) => game.title_id === gameFilter)); + } + + return res.send(games); +}); + + +export default router; diff --git a/src/app.ts b/src/app.ts index 1862fe7..d4c1f19 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,6 +1,8 @@ import express from "express"; import actuator from "express-actuator"; +import { createClient } from 'redis'; import winston from "winston"; +import apiRouter from "./api"; import { errorLogger, requestLogger } from "./middleware"; // Init logger @@ -18,6 +20,14 @@ export const logger = loggerInstance.child({ source: "Node", }); +// Init Redis client +export const redisClient = createClient({ + url: process.env.REDIS_URL, + readonly: true, +}); + +redisClient.on("error", (err: Error) => winston.error("An error occurred.", { source: "Redis client", error: err })); + // Init express server export const app = express(); app.set("view engine", "ejs"); @@ -36,6 +46,7 @@ app.use(requestLogger); // Set up routes app.use(express.static("public")); +app.use("/api", apiRouter); // Error-handling app.use(errorLogger);