From fe06cee8edf408aa3f92f116f80264f5614099ef Mon Sep 17 00:00:00 2001 From: Lazar Date: Sun, 7 Jun 2020 21:00:57 +0200 Subject: [PATCH 1/4] feat: add redis for caching (wip) --- server/.adonisrc.json | 3 +- server/.env.example | 6 +- .../app/Controllers/Http/TmdbsController.ts | 27 ++++++- server/config/redis.ts | 48 ++++++++++++ server/contracts/redis.ts | 12 +++ server/package-lock.json | 77 +++++++++++++++++++ server/package.json | 1 + server/tsconfig.json | 3 +- 8 files changed, 171 insertions(+), 6 deletions(-) create mode 100644 server/config/redis.ts create mode 100644 server/contracts/redis.ts diff --git a/server/.adonisrc.json b/server/.adonisrc.json index f27692b..b7410b5 100644 --- a/server/.adonisrc.json +++ b/server/.adonisrc.json @@ -16,7 +16,8 @@ ], "providers": [ "./providers/AppProvider", - "@adonisjs/core" + "@adonisjs/core", + "@adonisjs/redis" ], "metaFiles": [ ".env", diff --git a/server/.env.example b/server/.env.example index ddc09f7..cc66bb4 100644 --- a/server/.env.example +++ b/server/.env.example @@ -6,4 +6,8 @@ APP_KEY= TMDB_API_KEY= CLIENT_URL= CLIENT_BROWSER_URL= -CLIENT_WWW_BROWSER_URL= \ No newline at end of file +CLIENT_WWW_BROWSER_URL= +REDIS_CONNECTION= +REDIS_HOST= +REDIS_PORT= +REDIS_PASSWORD= \ No newline at end of file diff --git a/server/app/Controllers/Http/TmdbsController.ts b/server/app/Controllers/Http/TmdbsController.ts index 753955b..2181a7c 100644 --- a/server/app/Controllers/Http/TmdbsController.ts +++ b/server/app/Controllers/Http/TmdbsController.ts @@ -2,6 +2,7 @@ import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' import tmdbApi from '../../Services/TmdbService' import {promises as fs } from 'fs' import path from 'path' +import Redis from '@ioc:Adonis/Addons/Redis' async function handleRequest (context: HttpContextContract, axiosPromise: Promise) { try { @@ -12,13 +13,33 @@ async function handleRequest (context: HttpContextContract, axiosPromise: Promis context.response.status(err.response.status).json({ status: err.response.status, error: err.response.statusText }) } } + +async function handleRequestWithCache (context: HttpContextContract, axiosPromise: Promise, key: string, expiration: number) { + try { + const redisData = await Redis.connection().get(key) + if (redisData === null){ + try { + const { data } = await axiosPromise + await Redis.connection().setex(key, expiration, JSON.stringify(data)) + return context.response.status(200).send(data) + } catch (err){ + console.log(err.response) + return context.response.status(err.response.status).json({ status: err.response.status, error: err.response.statusText }) + } + } + return context.response.status(200).send(redisData) + } catch (err) { + return context.response.status(500).json({status: 500, error: 'Server error'}) + } +} + export default class TmdbsController { public async getTrending (context: HttpContextContract) { - return handleRequest(context, tmdbApi.getTrending()) + return handleRequestWithCache(context, tmdbApi.getTrending(), 'trending', 43200) } public async getNextInTheaters (context: HttpContextContract) { - return handleRequest(context, tmdbApi.getNextInTheaters()) + return handleRequestWithCache(context, tmdbApi.getNextInTheaters(), 'nextInTheaters', 43200) } public async getGenresList ({ response }: HttpContextContract) { @@ -31,7 +52,7 @@ export default class TmdbsController { } public async getMovieDetails (context: HttpContextContract) { - return handleRequest(context, tmdbApi.getMovieDetails(context.params.id)) + return handleRequestWithCache(context, tmdbApi.getMovieDetails(context.params.id), context.params.id, 10800) } //SEARCH diff --git a/server/config/redis.ts b/server/config/redis.ts new file mode 100644 index 0000000..0abd7c3 --- /dev/null +++ b/server/config/redis.ts @@ -0,0 +1,48 @@ +/** + * Config source: https://git.io/JemcF + * + * Feel free to let us know via PR, if you find something broken in this config + * file. + */ + +import Env from '@ioc:Adonis/Core/Env' +import { RedisConfig } from '@ioc:Adonis/Addons/Redis' + +/* +|-------------------------------------------------------------------------- +| Redis configuration +|-------------------------------------------------------------------------- +| +| Following is the configuration used by the Redis provider to connect to +| the redis server and execute redis commands. +| +| Do make sure to pre-define the connections type inside `contracts/redis.ts` +| file for AdonisJs to recognize connections. +| +| Make sure to check `contracts/redis.ts` file for defining extra connections +*/ +const redisConfig: RedisConfig = { + connection: Env.get('REDIS_CONNECTION', 'local') as 'local', + + connections: { + /* + |-------------------------------------------------------------------------- + | The default connection + |-------------------------------------------------------------------------- + | + | The main connection you want to use to execute redis commands. The same + | connection will be used by the session provider, if you rely on the + | redis driver. + | + */ + local: { + host: Env.get('REDIS_HOST', '127.0.0.1') as string, + port: Env.get('REDIS_PORT', '6379') as string, + password: Env.get('REDIS_PASSWORD', '') as string, + db: 0, + keyPrefix: '', + }, + } +} + +export default redisConfig diff --git a/server/contracts/redis.ts b/server/contracts/redis.ts new file mode 100644 index 0000000..c04c625 --- /dev/null +++ b/server/contracts/redis.ts @@ -0,0 +1,12 @@ +/** + * Contract source: https://git.io/JemcN + * + * Feel free to let us know via PR, if you find something broken in this config + * file. + */ + +declare module '@ioc:Adonis/Addons/Redis' { + interface RedisConnectionsList { + local: RedisConnectionConfig, + } +} diff --git a/server/package-lock.json b/server/package-lock.json index 190dd5c..a829d76 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -220,6 +220,16 @@ "jest-worker": "^26.0.0" } }, + "@adonisjs/redis": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@adonisjs/redis/-/redis-4.0.1.tgz", + "integrity": "sha512-JJ187UdpbgSpjm7atymC1aJbugUUcFyOWE8Vf6VAxZkjlKPayXGNcExQGJacfd+oLnPM5IFotmYW4nQxqjz6Pg==", + "requires": { + "@poppinss/utils": "^2.2.6", + "@types/ioredis": "^4.14.9", + "ioredis": "^4.16.2" + } + }, "@adonisjs/sink": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@adonisjs/sink/-/sink-3.0.2.tgz", @@ -489,6 +499,14 @@ "@types/node": "*" } }, + "@types/ioredis": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.16.4.tgz", + "integrity": "sha512-kQsBNrNWlk93CJ0RawjMt2RK+jee1o6SYKmPvpYoMu9ofW9ell/tOblnObf5cx5DtoDWjlaOn+jgF+bA+U78Lw==", + "requires": { + "@types/node": "*" + } + }, "@types/json-schema": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", @@ -1237,6 +1255,11 @@ "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", "dev": true }, + "cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" + }, "co-body": { "version": "git+https://github.com/thetutlage/co-body.git#a3b09e688fbe5b8f3f2867bffd321406cf20dd25", "from": "git+https://github.com/thetutlage/co-body.git", @@ -1530,6 +1553,11 @@ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "optional": true }, + "denque": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -2607,6 +2635,22 @@ } } }, + "ioredis": { + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.17.3.tgz", + "integrity": "sha512-iRvq4BOYzNFkDnSyhx7cmJNOi1x/HWYe+A4VXHBu4qpwJaGT1Mp+D2bVGJntH9K/Z/GeOM/Nprb8gB3bmitz1Q==", + "requires": { + "cluster-key-slot": "^1.1.0", + "debug": "^4.1.1", + "denque": "^1.1.0", + "lodash.defaults": "^4.2.0", + "lodash.flatten": "^4.4.0", + "redis-commands": "1.5.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.0.1" + } + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -2896,6 +2940,16 @@ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, "lodash.toarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", @@ -4072,6 +4126,24 @@ "esprima": "~4.0.0" } }, + "redis-commands": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz", + "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==" + }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } + }, "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", @@ -4573,6 +4645,11 @@ "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", "dev": true }, + "standard-as-callback": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz", + "integrity": "sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg==" + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", diff --git a/server/package.json b/server/package.json index b2b52c6..43b9f21 100644 --- a/server/package.json +++ b/server/package.json @@ -23,6 +23,7 @@ "@adonisjs/ace": "^6.9.3", "@adonisjs/core": "^5.0.0-preview-rc-1.6", "@adonisjs/fold": "^6.3.5", + "@adonisjs/redis": "^4.0.1", "axios": "^0.19.2", "node-schedule": "^1.3.2", "proxy-addr": "^2.0.6", diff --git a/server/tsconfig.json b/server/tsconfig.json index 2393a3e..5a1271c 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -20,7 +20,8 @@ ] }, "types": [ - "@adonisjs/core" + "@adonisjs/core", + "@adonisjs/redis" ] } } \ No newline at end of file From 40f315db1ad6e173633c8217286821aa8377654b Mon Sep 17 00:00:00 2001 From: Lazar Date: Sun, 7 Jun 2020 23:02:57 +0200 Subject: [PATCH 2/4] chore: update docker-compose file and readme --- .gitignore | 4 ++-- README.md | 3 ++- docker-compose.staging.yml | 17 ++++++++++++++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 7e3a896..af95114 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ client/.env - -docker-compose.prod.yml \ No newline at end of file +docker-compose.prod.yml +redis.conf \ No newline at end of file diff --git a/README.md b/README.md index 8c6b356..a39d788 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Moviepark is an open-source project using the TMDb API for its movie catalog. - [Adonis.js 5](https://github.com/adonisjs) - [TailwindCSS](https://github.com/tailwindcss/tailwindcss) - [Traefik 2.2](https://github.com/containous/traefik/) +- [Redis](https://redis.io/) ## Requirements - [Node.js >= 12.0.0](https://nodejs.org/en/) @@ -41,7 +42,7 @@ To check how it would look like in production with docker (don't forget to add y ## Todo - [x] Release version 1.0 Yay! -- [ ] Caching +- [x] Caching - [ ] Get streaming services for each movie with [Utelly's API](https://rapidapi.com/utelly/api/utelly) - [ ] More search filters (ex: search by company...) - [ ] Watchlist diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml index ff2919e..1754608 100644 --- a/docker-compose.staging.yml +++ b/docker-compose.staging.yml @@ -60,6 +60,10 @@ services: - TMDB_API_KEY= - CLIENT_URL= - CLIENT_BROWSER_URL= + - REDIS_CONNECTION= + - REDIS_HOST= + - REDIS_PORT= + - REDIS_PASSWORD= labels: - "traefik.enable=true" - "traefik.http.routers.moviepark-server.rule=Host(`api.localhost`)" @@ -67,8 +71,19 @@ services: restart: always depends_on: - moviepark-traefik + - moviepark-redis + networks: + - moviepark-network + moviepark-redis: + image: redis:6-alpine + container_name: moviepark-redis + volumes: + - moviepark-redisData:/data + restart: always networks: - moviepark-network networks: moviepark-network: - driver: bridge \ No newline at end of file + driver: bridge +volumes: + moviepark-redisData: \ No newline at end of file From 439207e584a5dae4fe8446199bf4ede148e47330 Mon Sep 17 00:00:00 2001 From: Lazar Date: Sun, 7 Jun 2020 23:32:04 +0200 Subject: [PATCH 3/4] feat: give 404 when NaN for movie details request --- server/app/Controllers/Http/TmdbsController.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/app/Controllers/Http/TmdbsController.ts b/server/app/Controllers/Http/TmdbsController.ts index 2181a7c..76dd129 100644 --- a/server/app/Controllers/Http/TmdbsController.ts +++ b/server/app/Controllers/Http/TmdbsController.ts @@ -52,7 +52,10 @@ export default class TmdbsController { } public async getMovieDetails (context: HttpContextContract) { - return handleRequestWithCache(context, tmdbApi.getMovieDetails(context.params.id), context.params.id, 10800) + if (!isNaN(context.params.id)) { + return handleRequestWithCache(context, tmdbApi.getMovieDetails(context.params.id), context.params.id, 10800) + } + return context.response.status(404).json({status: 404, error: 'Not Found'}) } //SEARCH From 836b448abbe827334f1abffe7bf9103c881e0dea Mon Sep 17 00:00:00 2001 From: Lazar Date: Mon, 8 Jun 2020 19:29:11 +0200 Subject: [PATCH 4/4] feat: small fixes and bump client to 1.0.4 and server to 1.1.0 --- .../components/movie/MobileMovieDetails.vue | 21 ++++++++++--------- client/package-lock.json | 2 +- client/package.json | 2 +- .../app/Controllers/Http/TmdbsController.ts | 2 +- server/package-lock.json | 2 +- server/package.json | 2 +- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/client/components/movie/MobileMovieDetails.vue b/client/components/movie/MobileMovieDetails.vue index d130e03..a130186 100644 --- a/client/components/movie/MobileMovieDetails.vue +++ b/client/components/movie/MobileMovieDetails.vue @@ -53,6 +53,17 @@ + + +

{{ movieInfos.tagline }}

{{ movieInfos.overview }}

@@ -77,16 +88,6 @@ >{{ genre.name }} - - diff --git a/client/package-lock.json b/client/package-lock.json index 897a23b..4618f77 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,6 +1,6 @@ { "name": "moviepark-client", - "version": "1.0.3", + "version": "1.0.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/client/package.json b/client/package.json index 6f3b6b1..a0b0a77 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "moviepark-client", - "version": "1.0.3", + "version": "1.0.4", "description": "A webapp made with Nuxt using the TMDB API", "author": "Lazar Pavicevic", "private": true, diff --git a/server/app/Controllers/Http/TmdbsController.ts b/server/app/Controllers/Http/TmdbsController.ts index 76dd129..fe864a4 100644 --- a/server/app/Controllers/Http/TmdbsController.ts +++ b/server/app/Controllers/Http/TmdbsController.ts @@ -55,7 +55,7 @@ export default class TmdbsController { if (!isNaN(context.params.id)) { return handleRequestWithCache(context, tmdbApi.getMovieDetails(context.params.id), context.params.id, 10800) } - return context.response.status(404).json({status: 404, error: 'Not Found'}) + return context.response.status(404).json({ status: 404, error: 'Not Found' }) } //SEARCH diff --git a/server/package-lock.json b/server/package-lock.json index a829d76..97f405b 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,6 +1,6 @@ { "name": "moviepark-server", - "version": "1.0.1", + "version": "1.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/server/package.json b/server/package.json index 43b9f21..158ef1e 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "moviepark-server", - "version": "1.0.1", + "version": "1.1.0", "author": "Lazar Pavicevic", "private": true, "scripts": {