├── .nvmrc ├── client ├── src │ ├── images │ │ ├── index.js │ │ ├── headerBg.jpg │ │ ├── background.png │ │ ├── defaultFilm.png │ │ ├── icoMovies.png │ │ └── defaultPerson.png │ ├── components │ │ ├── home │ │ │ ├── home.css │ │ │ ├── genreList │ │ │ │ ├── genreItem │ │ │ │ │ ├── index.js │ │ │ │ │ ├── genreItem.css │ │ │ │ │ └── genreItem.js │ │ │ │ ├── movies │ │ │ │ │ ├── moviesItem │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ ├── moviesItem.js │ │ │ │ │ │ └── moviesItem.css │ │ │ │ │ ├── index.js │ │ │ │ │ ├── movies.css │ │ │ │ │ └── movies.js │ │ │ │ ├── index.js │ │ │ │ └── genreList.js │ │ │ ├── index.js │ │ │ └── home.js │ │ ├── detailsOfFilm │ │ │ ├── detailsOfFilm.css │ │ │ ├── detailsItem │ │ │ │ ├── index.js │ │ │ │ ├── detailsItem.css │ │ │ │ └── detailsItem.js │ │ │ ├── rateFilm │ │ │ │ ├── rateItem │ │ │ │ │ ├── index.js │ │ │ │ │ └── rateItem.js │ │ │ │ ├── index.js │ │ │ │ ├── rateFilm.css │ │ │ │ └── rateFilm.js │ │ │ ├── index.js │ │ │ └── detailsOfFilm.js │ │ ├── error │ │ │ ├── index.js │ │ │ ├── error.js │ │ │ └── error.css │ │ ├── footer │ │ │ ├── index.js │ │ │ ├── footer.js │ │ │ └── footer.css │ │ ├── header │ │ │ ├── index.js │ │ │ ├── header.css │ │ │ ├── RedisLogo.js │ │ │ └── header.js │ │ ├── modal │ │ │ ├── index.js │ │ │ ├── modal.css │ │ │ └── modal.js │ │ ├── loading │ │ │ ├── index.js │ │ │ ├── loading.js │ │ │ └── loading.css │ │ ├── authorization │ │ │ ├── signIn │ │ │ │ ├── index.js │ │ │ │ └── signIn.js │ │ │ ├── signUp │ │ │ │ ├── index.js │ │ │ │ └── signUp.js │ │ │ ├── index.js │ │ │ ├── authorization.js │ │ │ └── authorization.css │ │ ├── personsPage │ │ │ ├── pageItem │ │ │ │ ├── index.js │ │ │ │ ├── pageItem.css │ │ │ │ └── pageItem.js │ │ │ ├── actorsPage │ │ │ │ ├── index.js │ │ │ │ └── actorsPage.js │ │ │ ├── directionsPage │ │ │ │ ├── index.js │ │ │ │ └── directionsPage.js │ │ │ └── index.js │ │ ├── filmsWithGenre │ │ │ ├── filmsList │ │ │ │ ├── index.js │ │ │ │ └── filmsList.js │ │ │ ├── index.js │ │ │ ├── filmsWithGenre.css │ │ │ └── filmsWithGenre.js │ │ ├── profile │ │ │ ├── profileItem │ │ │ │ ├── index.js │ │ │ │ ├── profileItem.js │ │ │ │ └── profileItem.css │ │ │ ├── index.js │ │ │ └── profile.js │ │ └── index.js │ ├── routers │ │ ├── index.js │ │ └── routers.js │ ├── config │ │ ├── index.js │ │ └── axios.config.js │ ├── layout │ │ ├── index.js │ │ └── baseLayout.js │ ├── services │ │ ├── index.js │ │ ├── genres.service.js │ │ ├── person.service.js │ │ ├── movies.service.js │ │ └── user.service.js │ ├── index.js │ ├── App.js │ └── index.css ├── README.md ├── build │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── icoMovies.png │ ├── static │ │ ├── media │ │ │ ├── headerBg.29b2a492.jpg │ │ │ ├── icoMovies.dffcbca8.png │ │ │ └── defaultPerson.141be92d.png │ │ └── js │ │ │ ├── 2.df22a81a.chunk.js.LICENSE.txt │ │ │ ├── runtime-main.0733fe2d.js │ │ │ └── runtime-main.0733fe2d.js.map │ ├── manifest.json │ ├── asset-manifest.json │ └── index.html ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── icoMovies.png │ ├── manifest.json │ └── index.html ├── .gitignore └── package.json ├── heroku.yml ├── .gitignore ├── .env.example ├── .DS_Store ├── docs ├── a.png ├── b.png ├── c.png ├── d.png ├── e.png ├── f.png └── g.png ├── images └── app_preview_image.png ├── swaggerui ├── images │ ├── expand.gif │ ├── favicon.ico │ ├── collapse.gif │ ├── throbber.gif │ ├── logo_small.png │ ├── wordnik_api.png │ ├── explorer_icons.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ └── pet_store_api.png ├── fonts │ ├── DroidSans.ttf │ └── DroidSans-Bold.ttf ├── lib │ ├── jquery.slideto.min.js │ ├── jquery.wiggle.min.js │ ├── object-assign-pollyfill.js │ ├── highlight.9.1.0.pack_extended.js │ └── jquery.ba-bbq.min.js ├── css │ ├── typography.css │ ├── reset.css │ └── style.css ├── o2c.html ├── lang │ ├── translator.js │ ├── zh-cn.js │ ├── ko-kr.js │ ├── ja.js │ ├── tr.js │ ├── pl.js │ ├── pt.js │ ├── en.js │ ├── ru.js │ ├── ca.js │ ├── geo.js │ ├── it.js │ ├── es.js │ └── fr.js └── index.html ├── Dockerfile ├── routes ├── genresRouter.js ├── index.js ├── authRouter.js ├── peopleRouter.js ├── dataRouter.js └── moviesRouter.js ├── data ├── index.js ├── genres.js └── moviesWithGenres.js ├── controllers ├── index.js ├── genres.js ├── people.js ├── data.js └── users.js ├── models ├── redis │ ├── person.js │ ├── genre.js │ ├── user.js │ └── movie.js └── actions │ ├── genres.js │ ├── people.js │ ├── users.js │ └── movies.js ├── helpers └── response.js ├── .prettierrc.js ├── app.json ├── middlewares ├── loginRequired.js └── setAuthUser.js ├── config.js ├── package.json ├── LICENSE ├── marketplace.json ├── db ├── dbUtils.js └── initialize.js └── app.js /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/erbium 2 | -------------------------------------------------------------------------------- /client/src/images/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/home/home.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # neo4j movie app frontend 2 | 3 | -------------------------------------------------------------------------------- /client/src/components/detailsOfFilm/detailsOfFilm.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/routers/index.js: -------------------------------------------------------------------------------- 1 | export * from './routers'; 2 | -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | web: Dockerfile -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | .env 4 | .git 5 | 6 | -------------------------------------------------------------------------------- /client/src/config/index.js: -------------------------------------------------------------------------------- 1 | export * from './axios.config'; 2 | -------------------------------------------------------------------------------- /client/src/layout/index.js: -------------------------------------------------------------------------------- 1 | export * from './baseLayout'; 2 | -------------------------------------------------------------------------------- /client/src/components/error/index.js: -------------------------------------------------------------------------------- 1 | export * from './error'; 2 | -------------------------------------------------------------------------------- /client/src/components/footer/index.js: -------------------------------------------------------------------------------- 1 | export * from './footer'; 2 | -------------------------------------------------------------------------------- /client/src/components/header/index.js: -------------------------------------------------------------------------------- 1 | export * from './header'; 2 | -------------------------------------------------------------------------------- /client/src/components/modal/index.js: -------------------------------------------------------------------------------- 1 | export * from './modal'; 2 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | REDIS_ENDPOINT_URL= 2 | REDIS_PASSWORD= 3 | LOG_QUERIES=0 -------------------------------------------------------------------------------- /client/src/components/loading/index.js: -------------------------------------------------------------------------------- 1 | export * from './loading'; 2 | -------------------------------------------------------------------------------- /client/src/components/authorization/signIn/index.js: -------------------------------------------------------------------------------- 1 | export * from './signIn'; 2 | -------------------------------------------------------------------------------- /client/src/components/authorization/signUp/index.js: -------------------------------------------------------------------------------- 1 | export * from './signUp'; 2 | -------------------------------------------------------------------------------- /client/src/components/personsPage/pageItem/index.js: -------------------------------------------------------------------------------- 1 | export * from './pageItem'; 2 | -------------------------------------------------------------------------------- /client/src/components/filmsWithGenre/filmsList/index.js: -------------------------------------------------------------------------------- 1 | export * from './filmsList'; 2 | -------------------------------------------------------------------------------- /client/src/components/home/genreList/genreItem/index.js: -------------------------------------------------------------------------------- 1 | export * from './genreItem'; 2 | -------------------------------------------------------------------------------- /client/src/components/personsPage/actorsPage/index.js: -------------------------------------------------------------------------------- 1 | export * from './actorsPage'; 2 | -------------------------------------------------------------------------------- /client/src/components/profile/profileItem/index.js: -------------------------------------------------------------------------------- 1 | export * from './profileItem'; 2 | -------------------------------------------------------------------------------- /client/src/components/detailsOfFilm/detailsItem/index.js: -------------------------------------------------------------------------------- 1 | export * from './detailsItem'; 2 | -------------------------------------------------------------------------------- /client/src/components/detailsOfFilm/rateFilm/rateItem/index.js: -------------------------------------------------------------------------------- 1 | export * from './rateItem'; 2 | -------------------------------------------------------------------------------- /client/src/components/home/genreList/movies/moviesItem/index.js: -------------------------------------------------------------------------------- 1 | export * from './moviesItem'; 2 | -------------------------------------------------------------------------------- /client/src/components/home/index.js: -------------------------------------------------------------------------------- 1 | export * from './home'; 2 | export * from './genreList'; 3 | -------------------------------------------------------------------------------- /client/src/components/personsPage/directionsPage/index.js: -------------------------------------------------------------------------------- 1 | export * from './directionsPage'; 2 | -------------------------------------------------------------------------------- /client/build/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/src/components/profile/index.js: -------------------------------------------------------------------------------- 1 | export * from './profile'; 2 | export * from './profileItem'; 3 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/.DS_Store -------------------------------------------------------------------------------- /docs/a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/docs/a.png -------------------------------------------------------------------------------- /docs/b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/docs/b.png -------------------------------------------------------------------------------- /docs/c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/docs/c.png -------------------------------------------------------------------------------- /docs/d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/docs/d.png -------------------------------------------------------------------------------- /docs/e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/docs/e.png -------------------------------------------------------------------------------- /docs/f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/docs/f.png -------------------------------------------------------------------------------- /docs/g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/docs/g.png -------------------------------------------------------------------------------- /client/src/components/detailsOfFilm/rateFilm/index.js: -------------------------------------------------------------------------------- 1 | export * from './rateFilm'; 2 | export * from './rateItem'; 3 | -------------------------------------------------------------------------------- /client/src/components/filmsWithGenre/index.js: -------------------------------------------------------------------------------- 1 | export * from './filmsWithGenre'; 2 | export * from './filmsList'; 3 | -------------------------------------------------------------------------------- /client/src/components/home/genreList/movies/index.js: -------------------------------------------------------------------------------- 1 | export * from './movies'; 2 | export * from './moviesItem'; 3 | -------------------------------------------------------------------------------- /client/build/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/build/favicon.ico -------------------------------------------------------------------------------- /client/build/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/build/logo192.png -------------------------------------------------------------------------------- /client/build/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/build/logo512.png -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/public/logo512.png -------------------------------------------------------------------------------- /client/src/components/authorization/index.js: -------------------------------------------------------------------------------- 1 | export * from './authorization'; 2 | export * from './signIn'; 3 | export * from './signUp'; 4 | -------------------------------------------------------------------------------- /client/src/components/detailsOfFilm/index.js: -------------------------------------------------------------------------------- 1 | export * from './detailsOfFilm'; 2 | export * from '../modal'; 3 | export * from './rateFilm'; 4 | -------------------------------------------------------------------------------- /client/src/components/filmsWithGenre/filmsWithGenre.css: -------------------------------------------------------------------------------- 1 | .genreAllFilms h3{ 2 | /* margin-left: 11%; */ 3 | font-size: 2rem; 4 | } 5 | -------------------------------------------------------------------------------- /client/src/components/home/genreList/index.js: -------------------------------------------------------------------------------- 1 | export * from './genreList'; 2 | export * from './movies'; 3 | export * from './genreItem'; 4 | -------------------------------------------------------------------------------- /client/src/config/axios.config.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const AXIOS = axios.create({ 4 | baseURL: '/api', 5 | }); 6 | -------------------------------------------------------------------------------- /client/build/icoMovies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/build/icoMovies.png -------------------------------------------------------------------------------- /client/public/icoMovies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/public/icoMovies.png -------------------------------------------------------------------------------- /client/src/components/personsPage/index.js: -------------------------------------------------------------------------------- 1 | export * from './actorsPage'; 2 | export * from './directionsPage'; 3 | export * from './pageItem'; 4 | -------------------------------------------------------------------------------- /images/app_preview_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/images/app_preview_image.png -------------------------------------------------------------------------------- /swaggerui/images/expand.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/swaggerui/images/expand.gif -------------------------------------------------------------------------------- /swaggerui/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/swaggerui/images/favicon.ico -------------------------------------------------------------------------------- /client/src/images/headerBg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/src/images/headerBg.jpg -------------------------------------------------------------------------------- /swaggerui/fonts/DroidSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/swaggerui/fonts/DroidSans.ttf -------------------------------------------------------------------------------- /swaggerui/images/collapse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/swaggerui/images/collapse.gif -------------------------------------------------------------------------------- /swaggerui/images/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/swaggerui/images/throbber.gif -------------------------------------------------------------------------------- /client/src/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/src/images/background.png -------------------------------------------------------------------------------- /client/src/images/defaultFilm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/src/images/defaultFilm.png -------------------------------------------------------------------------------- /client/src/images/icoMovies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/src/images/icoMovies.png -------------------------------------------------------------------------------- /swaggerui/images/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/swaggerui/images/logo_small.png -------------------------------------------------------------------------------- /swaggerui/images/wordnik_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/swaggerui/images/wordnik_api.png -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12-slim 2 | WORKDIR /usr/src/app 3 | COPY package*.json ./ 4 | RUN npm install --only=production 5 | COPY . ./ 6 | CMD [ "node", "app.js" ] -------------------------------------------------------------------------------- /client/src/images/defaultPerson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/src/images/defaultPerson.png -------------------------------------------------------------------------------- /swaggerui/fonts/DroidSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/swaggerui/fonts/DroidSans-Bold.ttf -------------------------------------------------------------------------------- /swaggerui/images/explorer_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/swaggerui/images/explorer_icons.png -------------------------------------------------------------------------------- /swaggerui/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/swaggerui/images/favicon-16x16.png -------------------------------------------------------------------------------- /swaggerui/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/swaggerui/images/favicon-32x32.png -------------------------------------------------------------------------------- /swaggerui/images/pet_store_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/swaggerui/images/pet_store_api.png -------------------------------------------------------------------------------- /client/src/services/index.js: -------------------------------------------------------------------------------- 1 | export * from './movies.service'; 2 | export * from './genres.service'; 3 | export * from './user.service'; 4 | export * from './person.service'; 5 | -------------------------------------------------------------------------------- /client/build/static/media/headerBg.29b2a492.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/build/static/media/headerBg.29b2a492.jpg -------------------------------------------------------------------------------- /client/build/static/media/icoMovies.dffcbca8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/build/static/media/icoMovies.dffcbca8.png -------------------------------------------------------------------------------- /client/src/components/home/genreList/movies/movies.css: -------------------------------------------------------------------------------- 1 | .film-row{ 2 | display: flex; 3 | flex-direction: row; 4 | justify-content: flex-start; 5 | flex-wrap: wrap; 6 | } 7 | -------------------------------------------------------------------------------- /client/build/static/media/defaultPerson.141be92d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/client/build/static/media/defaultPerson.141be92d.png -------------------------------------------------------------------------------- /client/src/components/footer/footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './footer.css'; 3 | 4 | export const Footer = () => { 5 | return
2021
; 6 | }; 7 | -------------------------------------------------------------------------------- /routes/genresRouter.js: -------------------------------------------------------------------------------- 1 | const {Router} = require('express'); 2 | const {genres} = require('../controllers'); 3 | 4 | const genresRouter = Router(); 5 | 6 | genresRouter.get('/', genres.list); 7 | 8 | module.exports = genresRouter; 9 | -------------------------------------------------------------------------------- /data/index.js: -------------------------------------------------------------------------------- 1 | exports.ACTORS = require('./actors'); 2 | exports.DIRECTORS_OF_FILMS = require('./directorsOfFilms'); 3 | exports.GENRES = require('./genres'); 4 | exports.MOVIES = require('./movies'); 5 | exports.MOVIES_WITH_GENRES = require('./moviesWithGenres'); 6 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moviesRouter: require('./moviesRouter'), 3 | peopleRouter: require('./peopleRouter'), 4 | authRouter: require('./authRouter'), 5 | genresRouter: require('./genresRouter'), 6 | dataRouter: require('./dataRouter'), 7 | }; 8 | -------------------------------------------------------------------------------- /controllers/index.js: -------------------------------------------------------------------------------- 1 | // convenience wrapper around all other files: 2 | exports.users = require('./users'); 3 | exports.people = require('./people'); 4 | exports.movies = require('./movies'); 5 | exports.genres = require('./genres'); 6 | exports.data = require('./data'); 7 | -------------------------------------------------------------------------------- /models/redis/person.js: -------------------------------------------------------------------------------- 1 | // extracts just the data from the query results 2 | 3 | const _ = require('lodash'); 4 | 5 | const Person = function (_node) { 6 | _.extend(this, _node.properties); 7 | this.poster_image = this.poster; 8 | }; 9 | 10 | module.exports = Person; 11 | -------------------------------------------------------------------------------- /client/src/components/home/home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './home.css'; 3 | import {GenreList} from './genreList'; 4 | 5 | export const Home = () => { 6 | return ( 7 |
8 | 9 |
10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /client/src/services/genres.service.js: -------------------------------------------------------------------------------- 1 | import {AXIOS} from '../config'; 2 | 3 | class GenresService { 4 | async getAllGenres() { 5 | const {data} = await AXIOS.get('/genres'); 6 | return data; 7 | } 8 | } 9 | 10 | export const genresService = new GenresService(); 11 | -------------------------------------------------------------------------------- /routes/authRouter.js: -------------------------------------------------------------------------------- 1 | const {Router} = require('express'); 2 | const {users} = require('../controllers'); 3 | 4 | const authRouter = Router(); 5 | 6 | authRouter.post('/register', users.register); 7 | authRouter.post('/login', users.login); 8 | 9 | module.exports = authRouter; 10 | -------------------------------------------------------------------------------- /helpers/response.js: -------------------------------------------------------------------------------- 1 | exports.writeResponse = function writeResponse(res, response, status) { 2 | res.status(status || 200).json(response); 3 | }; 4 | 5 | exports.writeError = function writeError(res, error, status) { 6 | res.status(error.status || status || 400).send(JSON.stringify(error)); 7 | }; 8 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 80, 3 | tabWidth: 2, 4 | useTabs: false, 5 | semi: true, 6 | singleQuote: true, 7 | trailingComma: 'all', 8 | bracketSpacing: false, 9 | jsxBracketSameLine: true, 10 | arrowParens: 'always', 11 | proseWrap: 'preserve', 12 | parser: 'typescript', 13 | }; 14 | -------------------------------------------------------------------------------- /routes/peopleRouter.js: -------------------------------------------------------------------------------- 1 | const {Router} = require('express'); 2 | const {people, users} = require('../controllers'); 3 | 4 | const peopleRouter = Router(); 5 | 6 | peopleRouter.get('/', people.list); 7 | peopleRouter.get('/me', users.me); 8 | peopleRouter.get('/:id', people.findById); 9 | 10 | module.exports = peopleRouter; 11 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Node.JS RedisGraph demo", 3 | "stack": "container", 4 | "env": { 5 | "REDIS_ENDPOINT_URL": { 6 | "description": "A Redis cloud endpoint URL.", 7 | "required": true 8 | }, 9 | "REDIS_PASSWORD": { 10 | "description": "A Redis password.", 11 | "required": true 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /client/src/components/error/error.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './error.css'; 3 | 4 | export const Error = () => { 5 | return ( 6 |
7 |
8 |

Error 404

9 |

Page not found

10 |
11 |
12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /middlewares/loginRequired.js: -------------------------------------------------------------------------------- 1 | const writeError = require('../helpers/response').writeResponse; 2 | 3 | module.exports = function loginRequired(req, res, next) { 4 | const authHeader = req.headers.authorization; 5 | if (!authHeader) { 6 | return writeError(res, {detail: 'no authorization provided'}, 401); 7 | } 8 | next(); 9 | }; 10 | -------------------------------------------------------------------------------- /models/redis/genre.js: -------------------------------------------------------------------------------- 1 | // extracts just the data from the query results 2 | const _ = require('lodash'); 3 | 4 | const Genre = function (_node) { 5 | _.extend(this, _node.properties); 6 | if (this.id) { 7 | this.id = this.id.toNumber(); 8 | } else { 9 | this.id = this.name; 10 | } 11 | }; 12 | 13 | module.exports = Genre; 14 | -------------------------------------------------------------------------------- /models/actions/genres.js: -------------------------------------------------------------------------------- 1 | const Genre = require('../redis/genre'); 2 | 3 | const _manyGenres = function (result) { 4 | return result._results.map((r) => new Genre(r.get('genre'))); 5 | }; 6 | 7 | const getAll = function (session) { 8 | return session.query('MATCH (genre:Genre) RETURN genre').then(_manyGenres); 9 | }; 10 | 11 | module.exports = { 12 | getAll, 13 | }; 14 | -------------------------------------------------------------------------------- /client/src/components/index.js: -------------------------------------------------------------------------------- 1 | export * from './home'; 2 | export * from './header'; 3 | export * from './footer'; 4 | export * from './detailsOfFilm'; 5 | export * from './authorization'; 6 | export * from './filmsWithGenre'; 7 | export * from './personsPage'; 8 | export * from './loading'; 9 | export * from './profile'; 10 | export * from './modal'; 11 | export * from './error'; 12 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import {App} from './App'; 5 | import {BrowserRouter as Router} from 'react-router-dom'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | 11 | 12 | , 13 | document.getElementById('root'), 14 | ); 15 | -------------------------------------------------------------------------------- /swaggerui/lib/jquery.slideto.min.js: -------------------------------------------------------------------------------- 1 | (function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery); 2 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | 13 | # misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | -------------------------------------------------------------------------------- /client/src/components/footer/footer.css: -------------------------------------------------------------------------------- 1 | .footerSection{ 2 | margin-top: 8rem; 3 | -webkit-box-shadow: 0px -11px 11px 0px rgba(0,0,0,0.75); 4 | -moz-box-shadow: 0px -11px 11px 0px rgba(0,0,0,0.75); 5 | box-shadow: 0px -11px 11px 0px rgba(0,0,0,0.75); 6 | background-image: url("../../images/headerBg.jpg"); 7 | min-height: 5rem; 8 | text-align: center; 9 | padding-top: 1rem; 10 | font-size: 0.8rem; 11 | } 12 | -------------------------------------------------------------------------------- /client/src/components/home/genreList/genreItem/genreItem.css: -------------------------------------------------------------------------------- 1 | .genreLinks:first-child{ 2 | margin-top: 2rem; 3 | } 4 | 5 | .genreLinks a{ 6 | margin-top: 2rem; 7 | /* margin-left: 11%; */ 8 | font-size: 1.8rem; 9 | text-decoration: none; 10 | color: white; 11 | } 12 | 13 | .genreLinks a span{ 14 | font-size: 1rem; 15 | } 16 | 17 | .genreLinks a:hover{ 18 | color: #989898; 19 | transition: 0.5s; 20 | } 21 | -------------------------------------------------------------------------------- /client/src/components/filmsWithGenre/filmsWithGenre.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {useParams} from 'react-router-dom'; 3 | import {FilmsList} from './filmsList'; 4 | import './filmsWithGenre.css'; 5 | 6 | export const FilmsWithGenre = () => { 7 | const {name} = useParams(); 8 | 9 | return ( 10 |
11 |

{name}

12 | 13 |
14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /models/redis/user.js: -------------------------------------------------------------------------------- 1 | // extracts just the data from the query results 2 | const _ = require('lodash'); 3 | const md5 = require('md5'); 4 | 5 | const User = function (_node) { 6 | const {username} = _node.properties; 7 | 8 | _.extend(this, { 9 | id: _node.properties.id, 10 | username, 11 | avatar: { 12 | full_size: `https://www.gravatar.com/avatar/${md5(username)}?d=retro`, 13 | }, 14 | }); 15 | }; 16 | module.exports = User; 17 | -------------------------------------------------------------------------------- /client/src/layout/baseLayout.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Header} from '../components/header'; 3 | import {Footer} from '../components/footer'; 4 | 5 | export const BaseLayout = ({children}) => { 6 | return ( 7 |
8 |
9 |
10 |
11 |
{children}
12 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /routes/dataRouter.js: -------------------------------------------------------------------------------- 1 | const {Router} = require('express'); 2 | const {data} = require('../controllers'); 3 | 4 | const dataRouter = Router(); 5 | 6 | dataRouter.post('/movies', data.setMovies); 7 | dataRouter.post('/genres', data.setGenres); 8 | dataRouter.post('/movies_genres', data.setGenresToMovies); 9 | dataRouter.post('/actors', data.setActorsWithRelationshipToMovie); 10 | dataRouter.post('/directors', data.setDirectorsWithRelationshipToMovie); 11 | 12 | module.exports = dataRouter; 13 | -------------------------------------------------------------------------------- /client/src/components/detailsOfFilm/rateFilm/rateItem/rateItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const RateItem = ({handleRate, arr}) => { 4 | return ( 5 |
6 |

Rate this film!

7 | 14 | Bad 15 | Good 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /models/redis/movie.js: -------------------------------------------------------------------------------- 1 | // extracts just the data from the query results 2 | 3 | const _ = require('lodash'); 4 | 5 | const Movie = function (_node, myRating) { 6 | _.extend(this, _node.properties); 7 | 8 | if (this.duration) { 9 | this.duration = Number(this.duration); 10 | } else if (this.runtime) { 11 | this.duration = Number(this.runtime); 12 | } 13 | 14 | if (myRating || myRating === 0) { 15 | this.my_rating = myRating; 16 | } 17 | }; 18 | module.exports = Movie; 19 | -------------------------------------------------------------------------------- /client/src/components/error/error.css: -------------------------------------------------------------------------------- 1 | .errorPage{ 2 | display: flex; 3 | justify-content: center; 4 | margin-top: 5%; 5 | margin-bottom: 20%; 6 | } 7 | .errorPage .errorWrapper{ 8 | background: white; 9 | padding: 3%; 10 | border-radius: 50%; 11 | color: black; 12 | text-align: center; 13 | } 14 | 15 | .errorPage .errorWrapper h2{ 16 | display: block; 17 | font-size: 2rem; 18 | } 19 | .errorPage .errorWrapper p{ 20 | display: block; 21 | font-size: 1.3rem; 22 | } 23 | -------------------------------------------------------------------------------- /swaggerui/css/typography.css: -------------------------------------------------------------------------------- 1 | /* Google Font's Droid Sans */ 2 | @font-face { 3 | font-family: 'Droid Sans'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: local('Droid Sans'), local('DroidSans'), url('../fonts/DroidSans.ttf'), format('truetype'); 7 | } 8 | /* Google Font's Droid Sans Bold */ 9 | @font-face { 10 | font-family: 'Droid Sans'; 11 | font-style: normal; 12 | font-weight: 700; 13 | src: local('Droid Sans Bold'), local('DroidSans-Bold'), url('../fonts/DroidSans-Bold.ttf'), format('truetype'); 14 | } 15 | -------------------------------------------------------------------------------- /swaggerui/o2c.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/genres.js: -------------------------------------------------------------------------------- 1 | exports.genres = [ 2 | {genre: 'Adventure'}, 3 | {genre: 'Animation'}, 4 | {genre: 'Children'}, 5 | {genre: 'Comedy'}, 6 | {genre: 'Fantasy'}, 7 | {genre: 'Romance'}, 8 | {genre: 'Drama'}, 9 | {genre: 'Action'}, 10 | {genre: 'Crime'}, 11 | {genre: 'Thriller'}, 12 | {genre: 'Horror'}, 13 | {genre: 'Mystery'}, 14 | {genre: 'Sci-Fi'}, 15 | {genre: 'Documentary'}, 16 | {genre: 'IMAX'}, 17 | {genre: 'War'}, 18 | {genre: 'Musical'}, 19 | {genre: 'Western'}, 20 | {genre: 'Film-Noir'}, 21 | {genre: '(no genres listed)'}, 22 | ]; 23 | -------------------------------------------------------------------------------- /client/build/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import React, {createContext, useContext} from 'react'; 2 | import {BaseLayout} from './layout'; 3 | import {Routers} from './routers'; 4 | import {useRatedFilms2, RatedFilmsContext} from './services'; 5 | 6 | export const App = () => { 7 | const { 8 | adjustRating, 9 | revalidate, 10 | ratedFilmsLoading: loading, 11 | } = useRatedFilms2(); 12 | return ( 13 |
14 | 15 | 16 | 17 | 18 | 19 |
20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /client/src/components/home/genreList/genreItem/genreItem.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import {Movies} from '../movies'; 3 | import {Link} from 'react-router-dom'; 4 | import './genreItem.css'; 5 | 6 | export const GenreItem = ({items}) => { 7 | const [styles, setStyles] = useState(true); 8 | 9 | return ( 10 |
11 | 14 | {items.name} (view more) 15 | 16 | 17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /client/src/components/loading/loading.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './loading.css'; 3 | 4 | export const Loading = () => { 5 | return ( 6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /client/src/components/personsPage/pageItem/pageItem.css: -------------------------------------------------------------------------------- 1 | .personInfoPage{ 2 | padding: 3%; 3 | } 4 | 5 | .personInfoPage .personInfoWrapper{ 6 | display: flex; 7 | flex-direction: row; 8 | } 9 | 10 | .personInfoPage .personInfoWrapper .personImage{ 11 | flex: 2; 12 | } 13 | 14 | .personInfoPage .personInfoWrapper .personInfoText{ 15 | flex: 3 16 | } 17 | .personInfoPage .personInfoWrapper .personInfoText h4{ 18 | font-size: 2rem; 19 | margin-top: 0; 20 | } 21 | .personInfoPage .personInfoWrapper .personInfoText p{ 22 | font-size: 1.2rem; 23 | } 24 | 25 | .personInfoPage h3{ 26 | margin-left: 3%; 27 | font-size: 1.5rem; 28 | margin-top: 3%; 29 | } 30 | -------------------------------------------------------------------------------- /client/src/components/detailsOfFilm/rateFilm/rateFilm.css: -------------------------------------------------------------------------------- 1 | .ratePage h4{ 2 | text-align: center; 3 | margin-top: 0; 4 | } 5 | .ratePage span{ 6 | margin-left: 2%; 7 | margin-right: 83%; 8 | font-size: 1rem; 9 | } 10 | 11 | .ratePage ul{ 12 | list-style-type: none; 13 | margin: 0; 14 | padding: 0; 15 | display: flex; 16 | justify-content: center; 17 | 18 | } 19 | 20 | .ratePage ul li{ 21 | display: inline-block; 22 | margin-right: 1%; 23 | background: black; 24 | color: white; 25 | padding: 3%; 26 | } 27 | .ratePage ul li:hover{ 28 | background: white; 29 | color: black; 30 | cursor: pointer; 31 | transition: 0.5s; 32 | } 33 | -------------------------------------------------------------------------------- /client/src/services/person.service.js: -------------------------------------------------------------------------------- 1 | import {AXIOS} from '../config'; 2 | 3 | class PersonService { 4 | async getActorById(id) { 5 | const userToken = await localStorage.getItem('token'); 6 | const {data} = await AXIOS.get(`movies/acted_in_by/${id}`, { 7 | headers: { 8 | Authorization: `Token ${userToken}`, 9 | }, 10 | }); 11 | return data; 12 | } 13 | 14 | async getDirectionById(id) { 15 | const userToken = await localStorage.getItem('token'); 16 | const {data} = await AXIOS.get(`movies/directed_by/${id}`, { 17 | headers: { 18 | Authorization: `Token ${userToken}`, 19 | }, 20 | }); 21 | return data; 22 | } 23 | } 24 | 25 | export const personService = new PersonService(); 26 | -------------------------------------------------------------------------------- /swaggerui/lib/jquery.wiggle.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | jQuery Wiggle 3 | Author: WonderGroup, Jordan Thomas 4 | URL: http://labs.wondergroup.com/demos/mini-ui/index.html 5 | License: MIT (http://en.wikipedia.org/wiki/MIT_License) 6 | */ 7 | jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('
').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);} 8 | if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});}; -------------------------------------------------------------------------------- /swaggerui/lib/object-assign-pollyfill.js: -------------------------------------------------------------------------------- 1 | if (typeof Object.assign != 'function') { 2 | (function () { 3 | Object.assign = function (target) { 4 | 'use strict'; 5 | if (target === undefined || target === null) { 6 | throw new TypeError('Cannot convert undefined or null to object'); 7 | } 8 | 9 | var output = Object(target); 10 | for (var index = 1; index < arguments.length; index++) { 11 | var source = arguments[index]; 12 | if (source !== undefined && source !== null) { 13 | for (var nextKey in source) { 14 | if (Object.prototype.hasOwnProperty.call(source, nextKey)) { 15 | output[nextKey] = source[nextKey]; 16 | } 17 | } 18 | } 19 | } 20 | return output; 21 | }; 22 | })(); 23 | } 24 | -------------------------------------------------------------------------------- /middlewares/setAuthUser.js: -------------------------------------------------------------------------------- 1 | const {writeError} = require('../helpers/response'); 2 | const Users = require('../models/actions/users'); 3 | const dbUtils = require('../db/dbUtils'); 4 | 5 | module.exports = function setAuthUser(req, res, next) { 6 | const authHeader = req.headers.authorization; 7 | if (!authHeader) { 8 | req.user = {id: null}; 9 | next(); 10 | } else { 11 | const match = authHeader.match(/^Token (\S+)/); 12 | if (!match || !match[1]) { 13 | return writeError( 14 | res, 15 | {detail: 'invalid authorization format. Follow `Token `'}, 16 | 401, 17 | ); 18 | } 19 | const token = match[1]; 20 | 21 | Users.me(dbUtils.getSession(), token) 22 | .then((user) => { 23 | req.user = user; 24 | next(); 25 | }) 26 | .catch(next); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /routes/moviesRouter.js: -------------------------------------------------------------------------------- 1 | const {Router} = require('express'); 2 | const {movies} = require('../controllers'); 3 | 4 | const moviesRouter = Router(); 5 | 6 | moviesRouter.get('/', movies.list); 7 | moviesRouter.get('/recommended', movies.getRecommendedMovies); 8 | moviesRouter.get('/rated', movies.findMoviesRatedByMe); 9 | moviesRouter.get('/:id', movies.findById); 10 | moviesRouter.get('/genre/:id', movies.findByGenre); 11 | moviesRouter.get('/daterange/:start/:end', movies.findMoviesByDateRange); 12 | moviesRouter.get('/directed_by/:id', movies.findMoviesByDirector); 13 | moviesRouter.get('/acted_in_by/:id', movies.findMoviesByActor); 14 | moviesRouter.get('/written_by/:id', movies.findMoviesByWriter); 15 | moviesRouter.post('/:id/rate', movies.rateMovie); 16 | moviesRouter.delete('/:id/rate', movies.deleteMovieRating); 17 | 18 | module.exports = moviesRouter; 19 | -------------------------------------------------------------------------------- /client/src/components/home/genreList/genreList.js: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | import {GenreItem} from './genreItem'; 3 | import {genresService} from '../../../services'; 4 | import {Loading} from '../../loading'; 5 | 6 | export const GenreList = () => { 7 | const [loading, setLoading] = useState(true); 8 | const [genres, setGenres] = useState([]); 9 | 10 | const handleGenresData = async () => { 11 | const response = await genresService.getAllGenres(); 12 | 13 | setGenres(response); 14 | setLoading(false); 15 | }; 16 | 17 | useEffect(() => { 18 | handleGenresData(); 19 | }, []); 20 | 21 | return ( 22 |
23 | {loading ? ( 24 | 25 | ) : ( 26 | genres.map((value, index) => ) 27 | )} 28 |
29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /client/src/routers/routers.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Route, Switch} from 'react-router-dom'; 3 | import { 4 | FilmsWithGenre, 5 | Home, 6 | DetailsOfFilm, 7 | Authorization, 8 | DirectionsPage, 9 | ActorsPage, 10 | Profile, 11 | Error, 12 | } from '../components'; 13 | 14 | export const Routers = () => { 15 | return ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /models/actions/people.js: -------------------------------------------------------------------------------- 1 | const User = require('../redis/user'); 2 | 3 | // return many people 4 | function _manyPeople(listOfPersons) { 5 | return listOfPersons._results.map((r) => new User(r.get('user'))); 6 | } 7 | 8 | // get a single person by id 9 | const getById = function (session, id) { 10 | const query = ['MATCH (user:User {id: $id})', 'RETURN DISTINCT user'].join( 11 | '\n', 12 | ); 13 | 14 | return session.query(query, {id}).then((result) => { 15 | if (result.hasNext()) { 16 | const record = result.next(); 17 | return new User(record.get('user')); 18 | } 19 | throw {message: 'person not found', status: 404}; 20 | }); 21 | }; 22 | 23 | // get all people 24 | const getAll = function (session) { 25 | return session 26 | .query('MATCH (user:User) RETURN user') 27 | .then((result) => _manyPeople(result)); 28 | }; 29 | 30 | module.exports = { 31 | getAll, 32 | getById, 33 | }; 34 | -------------------------------------------------------------------------------- /client/src/components/modal/modal.css: -------------------------------------------------------------------------------- 1 | .modal{ 2 | height: 100vh; 3 | width: 100vw; 4 | background-color: rgba(0,0,0,0.95); 5 | position: fixed; 6 | top: 0; 7 | left: 0; 8 | display: flex; 9 | align-items: center; 10 | justify-content: center; 11 | opacity: 0; 12 | pointer-events: none; 13 | /*transition: 0.1s;*/ 14 | } 15 | 16 | .modal.active{ 17 | opacity: 1; 18 | pointer-events: all; 19 | } 20 | 21 | .modal__content{ 22 | padding: 20px; 23 | border-radius: 12px; 24 | background-color: white; 25 | transform: scale(0.5); 26 | transition: 0.4s all; 27 | width: 50vw; 28 | height: 50vh; 29 | } 30 | 31 | .modal__content .active{ 32 | transform: scale(1); 33 | } 34 | 35 | .modal .active img{ 36 | margin-left: 90%; 37 | width: 7%; 38 | } 39 | .modal .active img:hover{ 40 | cursor: pointer; 41 | } 42 | 43 | .modal .active{ 44 | color: black; 45 | font-size: 2rem; 46 | } 47 | -------------------------------------------------------------------------------- /client/src/components/personsPage/directionsPage/directionsPage.js: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | import {useParams} from 'react-router'; 3 | import {personService} from '../../../services'; 4 | import {PageItem} from '../pageItem'; 5 | import {Loading} from '../../loading'; 6 | 7 | export const DirectionsPage = () => { 8 | const {id} = useParams(); 9 | 10 | const [direction, setDirection] = useState([]); 11 | const [loading, setLoading] = useState(true); 12 | 13 | const handleDirectionData = async () => { 14 | const response = await personService.getDirectionById(id); 15 | setDirection(response); 16 | setLoading(false); 17 | }; 18 | 19 | useEffect(() => { 20 | handleDirectionData(); 21 | }, []); 22 | 23 | return ( 24 |
25 | {loading ? ( 26 | 27 | ) : ( 28 | 29 | )} 30 |
31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /client/src/components/profile/profileItem/profileItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './profileItem.css'; 3 | import {MoviesItem} from '../../home/genreList/movies/moviesItem'; 4 | 5 | export const ProfileItem = ({user, films, handleLogOut}) => { 6 | return ( 7 |
8 |
9 |
10 | avatar 11 |
12 |
13 |

{user.username}: Profile

14 |
15 |
16 | 17 |
18 |
19 |

Rated by me:

20 |
21 | {films.map((value, index) => ( 22 | 23 | ))} 24 |
25 |
26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /client/src/services/movies.service.js: -------------------------------------------------------------------------------- 1 | import {AXIOS} from '../config'; 2 | 3 | class MoviesService { 4 | async getMoviesWithGenre(id) { 5 | const {data} = await AXIOS.get(`/movies/genre/${id}`); 6 | return data.movies; 7 | } 8 | 9 | async getMovieById(id) { 10 | const userToken = await localStorage.getItem('token'); 11 | const {data} = await AXIOS.get(`/movies/${id}`, { 12 | headers: { 13 | Authorization: `Token ${userToken}`, 14 | }, 15 | }); 16 | return data; 17 | } 18 | 19 | async rateMovie(id, number) { 20 | const userToken = await localStorage.getItem('token'); 21 | 22 | const response = AXIOS.post( 23 | `/movies/${id}/rate`, 24 | { 25 | rating: number, 26 | }, 27 | { 28 | headers: { 29 | Authorization: `Token ${userToken}`, 30 | }, 31 | }, 32 | ); 33 | 34 | return response; 35 | } 36 | } 37 | 38 | export const moviesService = new MoviesService(); 39 | -------------------------------------------------------------------------------- /client/src/components/detailsOfFilm/rateFilm/rateFilm.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import {useLocation} from 'react-router-dom'; 3 | import {moviesService} from '../../../services'; 4 | import './rateFilm.css'; 5 | import {RateItem} from './rateItem'; 6 | 7 | export const RateFilm = () => { 8 | const [success, setSuccess] = useState(false); 9 | const query = new URLSearchParams(useLocation().search); 10 | const id = query.get('id'); 11 | 12 | const handleRate = async (value) => { 13 | const response = await moviesService.rateMovie(id, value); 14 | setSuccess(true); 15 | console.log(response); 16 | setTimeout(() => window.location.reload(), 200); 17 | }; 18 | 19 | const arrRating = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 20 | 21 | return ( 22 |
23 | {success ? ( 24 |

Thank you for your rating!

25 | ) : ( 26 | 27 | )} 28 |
29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /controllers/genres.js: -------------------------------------------------------------------------------- 1 | const Genres = require('../models/actions/genres'); 2 | const {writeResponse} = require('../helpers/response'); 3 | const dbUtils = require('../db/dbUtils'); 4 | 5 | /** 6 | * @swagger 7 | * definition: 8 | * Genre: 9 | * type: object 10 | * properties: 11 | * id: 12 | * type: integer 13 | * name: 14 | * type: string 15 | */ 16 | 17 | /** 18 | * @swagger 19 | * /genres: 20 | * get: 21 | * tags: 22 | * - genres 23 | * description: Returns all genres 24 | * summary: Returns all genres 25 | * produces: 26 | * - application/json 27 | * responses: 28 | * 200: 29 | * description: A list of genres 30 | * schema: 31 | * type: array 32 | * items: 33 | * $ref: '#/definitions/Genre' 34 | */ 35 | exports.list = function (req, res, next) { 36 | Genres.getAll(dbUtils.getSession()) 37 | .then((response) => writeResponse(res, response)) 38 | .catch(next); 39 | }; 40 | -------------------------------------------------------------------------------- /client/src/components/personsPage/actorsPage/actorsPage.js: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | import {useParams} from 'react-router'; 3 | import {personService, useRatedFilms} from '../../../services'; 4 | import {PageItem} from '../pageItem'; 5 | import {Loading} from '../../loading'; 6 | 7 | export const ActorsPage = () => { 8 | const {id} = useParams(); 9 | 10 | const [actors, setActors] = useState([]); 11 | const [loading, setLoading] = useState(true); 12 | 13 | const handleActorData = async () => { 14 | const response = await personService.getActorById(id); 15 | setActors(response); 16 | setLoading(false); 17 | }; 18 | 19 | useEffect(() => { 20 | handleActorData(); 21 | }, []); 22 | 23 | const {adjustRating, loading: ratedFilmsLoading} = useRatedFilms(); 24 | 25 | return ( 26 |
27 | {loading || ratedFilmsLoading ? ( 28 | 29 | ) : ( 30 | 31 | )} 32 |
33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /client/src/components/home/genreList/movies/movies.js: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | import {moviesService, useRatedFilms} from '../../../../services'; 3 | import {MoviesItem} from './moviesItem'; 4 | import './movies.css'; 5 | import {Loading} from '../../../loading'; 6 | 7 | export const Movies = ({name, setStyles}) => { 8 | const [films, setFilms] = useState([]); 9 | 10 | const {adjustRating, loading: ratedFilmsLoading} = useRatedFilms(); 11 | 12 | const handleFilmsData = async () => { 13 | const response = await moviesService.getMoviesWithGenre(name); 14 | if (response.length === 0) { 15 | setStyles(false); 16 | return; 17 | } 18 | setFilms(response.slice(0, 4)); 19 | }; 20 | 21 | useEffect(() => { 22 | handleFilmsData(); 23 | }, []); 24 | 25 | return ratedFilmsLoading ? ( 26 | 27 | ) : ( 28 |
29 | {adjustRating(films).map((value, index) => ( 30 | 31 | ))} 32 |
33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /client/src/components/profile/profileItem/profileItem.css: -------------------------------------------------------------------------------- 1 | .profile h3{ 2 | text-align: left; 3 | margin-left: 48px; 4 | font-size: 1.3rem; 5 | } 6 | 7 | .profilePage{ 8 | display: flex; 9 | flex-direction: row; 10 | margin: 3%; 11 | } 12 | 13 | .profilePage .profileImage{ 14 | flex: 2; 15 | } 16 | .profilePage .profileImage img{ 17 | width: 150px; 18 | border-radius: 50%; 19 | margin-right: 24px; 20 | } 21 | 22 | .profileName h2 { 23 | margin-top: 0; 24 | } 25 | 26 | .profilePage .profileName{ 27 | flex: 10; 28 | } 29 | .profilePage .profileName h2{ 30 | font-size: 2rem; 31 | } 32 | 33 | .profilePage .profileName h2:first-letter{ 34 | text-transform: uppercase; 35 | } 36 | 37 | .profile .profilePage .logout button{ 38 | background: none; 39 | color: white; 40 | padding: 0.7rem 1.2rem; 41 | border: 1px solid white; 42 | } 43 | .profile .profilePage .logout button:hover{ 44 | background: white; 45 | color: black; 46 | transition: 0.5s; 47 | transform: scale(1.1); 48 | cursor: pointer; 49 | } 50 | -------------------------------------------------------------------------------- /client/src/components/filmsWithGenre/filmsList/filmsList.js: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | import {moviesService, useRatedFilms} from '../../../services'; 3 | import {MoviesItem} from '../../home/genreList/movies/moviesItem'; 4 | import {Loading} from '../../loading'; 5 | 6 | export const FilmsList = ({name}) => { 7 | const [loading, setLoading] = useState(true); 8 | const [films, setFilms] = useState([]); 9 | const {adjustRating, loading: ratedFilmsLoading} = useRatedFilms(); 10 | 11 | const handleFilmsData = async () => { 12 | const response = await moviesService.getMoviesWithGenre(name); 13 | setFilms(response); 14 | setLoading(false); 15 | }; 16 | 17 | useEffect(() => { 18 | handleFilmsData(); 19 | }, []); 20 | 21 | return ( 22 |
23 | {loading || ratedFilmsLoading ? ( 24 | 25 | ) : ( 26 | adjustRating(films).map((value, index) => ( 27 | 28 | )) 29 | )} 30 |
31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | const nconf = require('nconf'); 4 | 5 | const {REDIS_ENDPOINT_URL, REDIS_PASSWORD} = process.env; 6 | const [redisHost, redisPort] = (REDIS_ENDPOINT_URL || '127.0.0.1:6379').split( 7 | ':', 8 | ); 9 | 10 | nconf 11 | .env(['PORT', 'NODE_ENV']) 12 | .argv({ 13 | e: { 14 | alias: 'NODE_ENV', 15 | describe: 'Set production or development mode.', 16 | demand: false, 17 | default: 'development', 18 | }, 19 | p: { 20 | alias: 'PORT', 21 | describe: 'Port to run on.', 22 | demand: false, 23 | default: 4000, 24 | }, 25 | n: { 26 | alias: 'redis', 27 | describe: 'Use local or remote redis instance', 28 | demand: false, 29 | default: 'local', 30 | }, 31 | }) 32 | .defaults({ 33 | REDIS_HOST: redisHost, 34 | REDIS_PORT: +redisPort, 35 | REDIS_PASSWORD: REDIS_PASSWORD, 36 | GRAPH_NAME: process.env.GRAPH_NAME || 'MovieApp', 37 | 38 | api_path: '/api', 39 | }); 40 | 41 | module.exports = nconf; 42 | -------------------------------------------------------------------------------- /client/src/components/authorization/authorization.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import {SignIn} from './signIn'; 3 | import {SignUp} from './signUp'; 4 | import './authorization.css'; 5 | 6 | export const Authorization = () => { 7 | const [button, setButton] = useState(false); 8 | const [error, setError] = useState(''); 9 | 10 | const handleChangeState = () => { 11 | setButton(!button); 12 | }; 13 | 14 | return ( 15 |
16 |
19 | {button ? ( 20 | 25 | ) : ( 26 | 33 | )} 34 |
35 |
36 | ); 37 | }; 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redisgraph-movie-demo", 3 | "version": "0.0.20", 4 | "description": "API for a movie demo app using Neo4j", 5 | "repository": { 6 | "type": "git", 7 | "url": "tbd" 8 | }, 9 | "keywords": [ 10 | "node", 11 | "neo4j", 12 | "express", 13 | "api", 14 | "http", 15 | "rest", 16 | "swagger", 17 | "server" 18 | ], 19 | "engines": { 20 | "node": ">=0.11.x", 21 | "npm": "1.2.x" 22 | }, 23 | "dependencies": { 24 | "randomstring": "^1.1.5", 25 | "connect-history-api-fallback": "^1.6.0", 26 | "dotenv": "^8.2.0", 27 | "express": "^4.17.1", 28 | "lodash": "^4.17.20", 29 | "md5": "^2.2.1", 30 | "method-override": "^3.0.0", 31 | "nconf": "^0.10.0", 32 | "node-uuid": "^1.4.8", 33 | "redisgraph.js": "^2.2.1", 34 | "swagger-jsdoc": "^4.0.0", 35 | "swagger-ui-express": "^4.1.4" 36 | }, 37 | "devDependencies": { 38 | "nodemon": "^2.0.7" 39 | }, 40 | "license": "MIT", 41 | "scripts": { 42 | "start": "node app.js", 43 | "dev": "nodemon app.js" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 tinj 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.9", 7 | "@testing-library/react": "^11.2.5", 8 | "@testing-library/user-event": "^12.8.1", 9 | "axios": "^0.21.1", 10 | "react": "^17.0.1", 11 | "react-dom": "^17.0.1", 12 | "react-responsive": "^8.2.0", 13 | "react-router": "^5.2.0", 14 | "react-router-dom": "^5.2.0", 15 | "react-scripts": "4.0.3", 16 | "web-vitals": "^1.1.0" 17 | }, 18 | "proxy": "http://localhost:4000", 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject" 24 | }, 25 | "eslintConfig": { 26 | "extends": [ 27 | "react-app", 28 | "react-app/jest" 29 | ] 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /client/src/components/header/header.css: -------------------------------------------------------------------------------- 1 | .headerSection{ 2 | -webkit-box-shadow: 0px 11px 12px -1px rgba(0,0,0,0.75); 3 | -moz-box-shadow: 0px 11px 12px -1px rgba(0,0,0,0.75); 4 | box-shadow: 0px 11px 12px -1px rgba(0,0,0,0.75); 5 | height: 105px; 6 | background-image: url("../../images/headerBg.jpg"); 7 | } 8 | 9 | .headerSection h2,h3{ 10 | margin-top: 1.5rem; 11 | } 12 | 13 | .headerSection h3 a{ 14 | color: white; 15 | text-decoration: none; 16 | } 17 | .headerSection h2 a{ 18 | color: white; 19 | text-decoration: none; 20 | } 21 | .headerSection { 22 | display: flex; 23 | flex-direction: row; 24 | } 25 | .headerSection .headerTitle{ 26 | display: flex; 27 | align-items: center; 28 | } 29 | 30 | .headerSection .headerTitle h1{ 31 | text-transform: uppercase; 32 | } 33 | 34 | .headerSection .logo{ 35 | display: flex; 36 | align-items: center; 37 | } 38 | 39 | .headerSection .auth{ 40 | display: flex; 41 | align-items: center; 42 | } 43 | 44 | .headerSection .auth img{ 45 | border-radius: 50%; 46 | } 47 | 48 | .headerSection .logo img{ 49 | border-radius: 50%; 50 | width: 100%; 51 | } 52 | -------------------------------------------------------------------------------- /client/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | background-image: url("./images/background.png"); 9 | color: white; 10 | font-family: 'Open Sans', sans-serif; 11 | } 12 | 13 | code { 14 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 15 | monospace; 16 | } 17 | 18 | .d-flex { 19 | display: flex; 20 | } 21 | 22 | .d-block { 23 | display: block; 24 | } 25 | 26 | .align-items-center { 27 | align-items: center; 28 | } 29 | 30 | .text-white { 31 | color: #fff !important; 32 | } 33 | 34 | .text-decoration-none { 35 | text-decoration: none !important; 36 | } 37 | .m-0 { 38 | margin: 0 !important; 39 | } 40 | .mr-2 { 41 | margin-right: 24px; 42 | } 43 | 44 | .ml-3 { 45 | margin-left: 36px; 46 | } 47 | 48 | .font-size-20 { 49 | font-size: 20px; 50 | } 51 | 52 | .font-size-24 { 53 | font-size: 24px; 54 | } 55 | 56 | .cursor-pointer { 57 | cursor:pointer; 58 | } 59 | -------------------------------------------------------------------------------- /client/build/asset-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "main.css": "/static/css/main.d8fefa95.chunk.css", 4 | "main.js": "/static/js/main.371e6a8d.chunk.js", 5 | "main.js.map": "/static/js/main.371e6a8d.chunk.js.map", 6 | "runtime-main.js": "/static/js/runtime-main.0733fe2d.js", 7 | "runtime-main.js.map": "/static/js/runtime-main.0733fe2d.js.map", 8 | "static/js/2.df22a81a.chunk.js": "/static/js/2.df22a81a.chunk.js", 9 | "static/js/2.df22a81a.chunk.js.map": "/static/js/2.df22a81a.chunk.js.map", 10 | "index.html": "/index.html", 11 | "static/css/main.d8fefa95.chunk.css.map": "/static/css/main.d8fefa95.chunk.css.map", 12 | "static/js/2.df22a81a.chunk.js.LICENSE.txt": "/static/js/2.df22a81a.chunk.js.LICENSE.txt", 13 | "static/media/defaultPerson.141be92d.png": "/static/media/defaultPerson.141be92d.png", 14 | "static/media/footer.css": "/static/media/headerBg.29b2a492.jpg", 15 | "static/media/icoMovies.dffcbca8.png": "/static/media/icoMovies.dffcbca8.png" 16 | }, 17 | "entrypoints": [ 18 | "static/js/runtime-main.0733fe2d.js", 19 | "static/js/2.df22a81a.chunk.js", 20 | "static/css/main.d8fefa95.chunk.css", 21 | "static/js/main.371e6a8d.chunk.js" 22 | ] 23 | } -------------------------------------------------------------------------------- /swaggerui/lib/highlight.9.1.0.pack_extended.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function () { 4 | var configure, highlightBlock; 5 | 6 | configure = hljs.configure; 7 | // "extending" hljs.configure method 8 | hljs.configure = function _configure (options) { 9 | var size = options.highlightSizeThreshold; 10 | 11 | // added highlightSizeThreshold option to set maximum size 12 | // of processed string. Set to null if not a number 13 | hljs.highlightSizeThreshold = size === +size ? size : null; 14 | 15 | configure.call(this, options); 16 | }; 17 | 18 | highlightBlock = hljs.highlightBlock; 19 | 20 | // "extending" hljs.highlightBlock method 21 | hljs.highlightBlock = function _highlightBlock (el) { 22 | var innerHTML = el.innerHTML; 23 | var size = hljs.highlightSizeThreshold; 24 | 25 | // check if highlightSizeThreshold is not set or element innerHTML 26 | // is less than set option highlightSizeThreshold 27 | if (size == null || size > innerHTML.length) { 28 | // proceed with hljs.highlightBlock 29 | highlightBlock.call(hljs, el); 30 | } 31 | }; 32 | 33 | })(); 34 | 35 | -------------------------------------------------------------------------------- /client/src/components/profile/profile.js: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | import {ProfileItem} from './profileItem'; 3 | import {useRatedFilms, userService} from '../../services'; 4 | import {Loading} from '../loading'; 5 | 6 | export const Profile = () => { 7 | const [user, setUser] = useState(null); 8 | const [films, setFilms] = useState([]); 9 | const [loading, setLoading] = useState(true); 10 | 11 | const {adjustRating, loading: ratedFilmsLoading} = useRatedFilms(); 12 | 13 | const handleUserData = () => { 14 | const userLocal = localStorage.getItem('user'); 15 | const userParse = JSON.parse(userLocal); 16 | setUser(userParse); 17 | }; 18 | 19 | const handleUserFilms = async () => { 20 | const ratedFilms = await userService.ratedFilms(); 21 | setFilms(ratedFilms); 22 | setLoading(false); 23 | }; 24 | 25 | const handleLogOut = () => { 26 | localStorage.clear(); 27 | window.location.href = '/'; 28 | }; 29 | 30 | useEffect(() => { 31 | handleUserData(); 32 | handleUserFilms(); 33 | }, []); 34 | 35 | return ( 36 |
37 | {loading || ratedFilmsLoading ? ( 38 | 39 | ) : ( 40 | 45 | )} 46 |
47 | ); 48 | }; 49 | -------------------------------------------------------------------------------- /client/src/components/detailsOfFilm/detailsOfFilm.js: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | import {moviesService, useRatedFilms} from '../../services'; 3 | import './detailsOfFilm.css'; 4 | import {useLocation, useHistory} from 'react-router-dom'; 5 | import {DetailsItem} from './detailsItem'; 6 | import {Loading} from '../loading'; 7 | 8 | export const DetailsOfFilm = () => { 9 | const history = useHistory(); 10 | 11 | const [loading, setLoading] = useState(true); 12 | const [film, setFilm] = useState([]); 13 | 14 | const query = new URLSearchParams(useLocation().search); 15 | const id = query.get('id'); 16 | 17 | const handleGetOneMovie = async () => { 18 | const response = await moviesService.getMovieById(id); 19 | setFilm(response); 20 | setLoading(false); 21 | }; 22 | 23 | useEffect(() => { 24 | handleGetOneMovie(); 25 | }, []); 26 | 27 | const {adjustRating} = useRatedFilms(); 28 | 29 | const handleGetIdActors = (id) => { 30 | history.push(`/actors/${id}`); 31 | }; 32 | 33 | const handleGetIdDirectors = (id) => { 34 | history.push(`/directions/${id}`); 35 | }; 36 | 37 | return ( 38 |
39 | {loading ? ( 40 | 41 | ) : ( 42 | 47 | )} 48 |
49 | ); 50 | }; 51 | -------------------------------------------------------------------------------- /client/build/static/js/2.df22a81a.chunk.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* 2 | object-assign 3 | (c) Sindre Sorhus 4 | @license MIT 5 | */ 6 | 7 | /** @license React v0.20.2 8 | * scheduler.production.min.js 9 | * 10 | * Copyright (c) Facebook, Inc. and its affiliates. 11 | * 12 | * This source code is licensed under the MIT license found in the 13 | * LICENSE file in the root directory of this source tree. 14 | */ 15 | 16 | /** @license React v16.13.1 17 | * react-is.production.min.js 18 | * 19 | * Copyright (c) Facebook, Inc. and its affiliates. 20 | * 21 | * This source code is licensed under the MIT license found in the 22 | * LICENSE file in the root directory of this source tree. 23 | */ 24 | 25 | /** @license React v17.0.2 26 | * react-dom.production.min.js 27 | * 28 | * Copyright (c) Facebook, Inc. and its affiliates. 29 | * 30 | * This source code is licensed under the MIT license found in the 31 | * LICENSE file in the root directory of this source tree. 32 | */ 33 | 34 | /** @license React v17.0.2 35 | * react-jsx-runtime.production.min.js 36 | * 37 | * Copyright (c) Facebook, Inc. and its affiliates. 38 | * 39 | * This source code is licensed under the MIT license found in the 40 | * LICENSE file in the root directory of this source tree. 41 | */ 42 | 43 | /** @license React v17.0.2 44 | * react.production.min.js 45 | * 46 | * Copyright (c) Facebook, Inc. and its affiliates. 47 | * 48 | * This source code is licensed under the MIT license found in the 49 | * LICENSE file in the root directory of this source tree. 50 | */ 51 | -------------------------------------------------------------------------------- /client/build/static/js/runtime-main.0733fe2d.js: -------------------------------------------------------------------------------- 1 | !function(e){function r(r){for(var n,p,l=r[0],a=r[1],f=r[2],c=0,s=[];c { 7 | console.log(info); 8 | return ( 9 |
10 |
11 |
12 | {!info.properties.poster ? ( 13 | defaultPerson 14 | ) : ( 15 | {info.properties.name} 16 | )} 17 |
18 |
19 |

{info.properties.name}

20 |

Born year: {info.properties.born[0]}

21 | {info.properties.died ? ( 22 |

Dead in: {info.properties.died[0]}

23 | ) : null} 24 | {info.properties.bornIn ? ( 25 |

Born in: {info.properties.bornIn}

26 | ) : null} 27 | {info.properties.bio ? ( 28 |

{info.properties.bio}

29 | ) : ( 30 |

No information about this person

31 | )} 32 |
33 |
34 |

Films of this person:

35 |
36 | {films.map((value, index) => ( 37 | 38 | ))} 39 |
40 |
41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /swaggerui/lang/translator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Translator for documentation pages. 5 | * 6 | * To enable translation you should include one of language-files in your index.html 7 | * after . 8 | * For example - 9 | * 10 | * If you wish to translate some new texts you should do two things: 11 | * 1. Add a new phrase pair ("New Phrase": "New Translation") into your language file (for example lang/ru.js). It will be great if you add it in other language files too. 12 | * 2. Mark that text it templates this way New Phrase or . 13 | * The main thing here is attribute data-sw-translate. Only inner html, title-attribute and value-attribute are going to translate. 14 | * 15 | */ 16 | window.SwaggerTranslator = { 17 | 18 | _words:[], 19 | 20 | translate: function(sel) { 21 | var $this = this; 22 | sel = sel || '[data-sw-translate]'; 23 | 24 | $(sel).each(function() { 25 | $(this).html($this._tryTranslate($(this).html())); 26 | 27 | $(this).val($this._tryTranslate($(this).val())); 28 | $(this).attr('title', $this._tryTranslate($(this).attr('title'))); 29 | }); 30 | }, 31 | 32 | _tryTranslate: function(word) { 33 | return this._words[$.trim(word)] !== undefined ? this._words[$.trim(word)] : word; 34 | }, 35 | 36 | learn: function(wordsMap) { 37 | this._words = wordsMap; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /client/src/components/header/RedisLogo.js: -------------------------------------------------------------------------------- 1 | const RedisLogo = ({size = 32, color = '#fff'}) => ( 2 | 8 | 9 | 10 | ); 11 | 12 | export default RedisLogo; 13 | -------------------------------------------------------------------------------- /marketplace.json: -------------------------------------------------------------------------------- 1 | { 2 | "app_name": "Movie Database app in NodeJS", 3 | "description": "Movie Database app in NodeJS based on Redis", 4 | "type": "App", 5 | "contributed_by": "Redis", 6 | "repo_url": "https://github.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs", 7 | "preview_image_url": "https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/images/app_preview_image.png", 8 | "download_url": "https://github.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/-/archive/master/neo4j-movie-app-master.zip", 9 | "hosted_url": "", 10 | "quick_deploy": "true", 11 | "deploy_buttons": [ 12 | { 13 | "heroku": "https://heroku.com/deploy?template=https://github.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs" 14 | }, 15 | { 16 | "Google": "https://deploy.cloud.run/?git_repo=https://github.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs.git" 17 | } 18 | ], 19 | "language": [ 20 | "JavaScript" 21 | ], 22 | "redis_commands": [ 23 | "MATCH", 24 | "OPTIONAL_MATCH", 25 | "WHERE", 26 | "RETURN", 27 | "ORDER_BY", 28 | "SKIP", 29 | "LIMIT", 30 | "CREATE", 31 | "MERGE", 32 | "DELETE", 33 | "SET", 34 | "WITH", 35 | "UNION" 36 | ], 37 | "redis_features": [ 38 | "Graph" 39 | ], 40 | "app_image_urls": [ 41 | "https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/docs/a.png" 42 | ], 43 | "youtube_url": "", 44 | "special_tags": [], 45 | "verticals": [ 46 | "Others" 47 | ], 48 | "markdown": "https://raw.githubusercontent.com/redis-developer/basic-redisgraph-movie-demo-app-nodejs/master/README.md" 49 | } -------------------------------------------------------------------------------- /db/dbUtils.js: -------------------------------------------------------------------------------- 1 | const RedisGraph = require('redisgraph.js').Graph; 2 | const nconf = require('../config'); 3 | 4 | const graphName = nconf.get('GRAPH_NAME'); 5 | const host = nconf.get('REDIS_HOST'); 6 | const port = nconf.get('REDIS_PORT'); 7 | const password = nconf.get('REDIS_PASSWORD'); 8 | 9 | const graph = new RedisGraph(graphName, host, port, {password}); 10 | 11 | const session = { 12 | ...graph, 13 | query: async (...args) => { 14 | let functionName, pathName; 15 | { 16 | try { 17 | const stack = Error().stack; 18 | const [f, path] = stack.split('at').slice(1, 3).pop().trim().split(' '); 19 | try { 20 | const [, fname] = f.split('.'); 21 | functionName = fname; 22 | } catch (_) {} 23 | try { 24 | const p = path 25 | .split(')') 26 | .join('') 27 | .split('\\') 28 | .reverse() 29 | .slice(0, 3) 30 | .reverse() 31 | .reduce((a, b) => (a === '' ? b : a + '/' + b), ''); 32 | pathName = p; 33 | } catch (_) {} 34 | } catch (_) {} 35 | } 36 | let query; 37 | { 38 | const [theQuery, values] = args; 39 | if (values) { 40 | for (const [key, value] of Object.entries(values)) { 41 | const theKey = '$' + key; 42 | query = theQuery.split(theKey).join(`"${value}"`); 43 | } 44 | } else { 45 | query = theQuery; 46 | } 47 | } 48 | 49 | console.log(functionName, pathName); 50 | console.log(query + '\n'); 51 | return await graph.query(...args); 52 | }, 53 | }; 54 | 55 | exports.getSession = 56 | process.env.LOG_QUERIES === '1' ? () => session : () => graph; 57 | -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Movies Site 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /swaggerui/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */ 2 | html, 3 | body, 4 | div, 5 | span, 6 | applet, 7 | object, 8 | iframe, 9 | h1, 10 | h2, 11 | h3, 12 | h4, 13 | h5, 14 | h6, 15 | p, 16 | blockquote, 17 | pre, 18 | a, 19 | abbr, 20 | acronym, 21 | address, 22 | big, 23 | cite, 24 | code, 25 | del, 26 | dfn, 27 | em, 28 | img, 29 | ins, 30 | kbd, 31 | q, 32 | s, 33 | samp, 34 | small, 35 | strike, 36 | strong, 37 | sub, 38 | sup, 39 | tt, 40 | var, 41 | b, 42 | u, 43 | i, 44 | center, 45 | dl, 46 | dt, 47 | dd, 48 | ol, 49 | ul, 50 | li, 51 | fieldset, 52 | form, 53 | label, 54 | legend, 55 | table, 56 | caption, 57 | tbody, 58 | tfoot, 59 | thead, 60 | tr, 61 | th, 62 | td, 63 | article, 64 | aside, 65 | canvas, 66 | details, 67 | embed, 68 | figure, 69 | figcaption, 70 | footer, 71 | header, 72 | hgroup, 73 | menu, 74 | nav, 75 | output, 76 | ruby, 77 | section, 78 | summary, 79 | time, 80 | mark, 81 | audio, 82 | video { 83 | margin: 0; 84 | padding: 0; 85 | border: 0; 86 | font-size: 100%; 87 | font: inherit; 88 | vertical-align: baseline; 89 | } 90 | /* HTML5 display-role reset for older browsers */ 91 | article, 92 | aside, 93 | details, 94 | figcaption, 95 | figure, 96 | footer, 97 | header, 98 | hgroup, 99 | menu, 100 | nav, 101 | section { 102 | display: block; 103 | } 104 | body { 105 | line-height: 1; 106 | } 107 | ol, 108 | ul { 109 | list-style: none; 110 | } 111 | blockquote, 112 | q { 113 | quotes: none; 114 | } 115 | blockquote:before, 116 | blockquote:after, 117 | q:before, 118 | q:after { 119 | content: ''; 120 | content: none; 121 | } 122 | table { 123 | border-collapse: collapse; 124 | border-spacing: 0; 125 | } 126 | -------------------------------------------------------------------------------- /swaggerui/lang/zh-cn.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"警告:已过时", 6 | "Implementation Notes":"实现备注", 7 | "Response Class":"响应类", 8 | "Status":"状态", 9 | "Parameters":"参数", 10 | "Parameter":"参数", 11 | "Value":"值", 12 | "Description":"描述", 13 | "Parameter Type":"参数类型", 14 | "Data Type":"数据类型", 15 | "Response Messages":"响应消息", 16 | "HTTP Status Code":"HTTP状态码", 17 | "Reason":"原因", 18 | "Response Model":"响应模型", 19 | "Request URL":"请求URL", 20 | "Response Body":"响应体", 21 | "Response Code":"响应码", 22 | "Response Headers":"响应头", 23 | "Hide Response":"隐藏响应", 24 | "Headers":"头", 25 | "Try it out!":"试一下!", 26 | "Show/Hide":"显示/隐藏", 27 | "List Operations":"显示操作", 28 | "Expand Operations":"展开操作", 29 | "Raw":"原始", 30 | "can't parse JSON. Raw result":"无法解析JSON. 原始结果", 31 | "Model Schema":"模型架构", 32 | "Model":"模型", 33 | "apply":"应用", 34 | "Username":"用户名", 35 | "Password":"密码", 36 | "Terms of service":"服务条款", 37 | "Created by":"创建者", 38 | "See more at":"查看更多:", 39 | "Contact the developer":"联系开发者", 40 | "api version":"api版本", 41 | "Response Content Type":"响应Content Type", 42 | "fetching resource":"正在获取资源", 43 | "fetching resource list":"正在获取资源列表", 44 | "Explore":"浏览", 45 | "Show Swagger Petstore Example Apis":"显示 Swagger Petstore 示例 Apis", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"无法从服务器读取。可能没有正确设置access-control-origin。", 47 | "Please specify the protocol for":"请指定协议:", 48 | "Can't read swagger JSON from":"无法读取swagger JSON于", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"已加载资源信息。正在渲染Swagger UI", 50 | "Unable to read api":"无法读取api", 51 | "from path":"从路径", 52 | "server returned":"服务器返回" 53 | }); 54 | -------------------------------------------------------------------------------- /client/src/components/home/genreList/movies/moviesItem/moviesItem.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import './moviesItem.css'; 3 | import {useHistory, Link} from 'react-router-dom'; 4 | import {Modal} from '../../../../modal'; 5 | import defaultFilm from '../../../../../images/defaultFilm.png'; 6 | 7 | export const MoviesItem = ({items, name}) => { 8 | const history = useHistory(); 9 | 10 | const [active, setActive] = useState(false); 11 | 12 | const handleDetailsOfFilm = () => { 13 | const token = localStorage.getItem('token'); 14 | if (!token) { 15 | setActive(true); 16 | } else { 17 | history.push(`/movies/${name}?id=${items.id}`); 18 | } 19 | }; 20 | 21 | return ( 22 |
23 | {items.my_rating ? My rate: {items.my_rating} of 10 : null} 24 |
25 | {!items.poster ? ( 26 | defaultIco 27 | ) : ( 28 | {items.title} 29 | )} 30 |
31 |

Rating: {items.imdbRating} of 10

32 |
33 |
36 |
37 |

{items.runtime} min

38 |

39 | {items.year}, {items.countries[0]} 40 |

41 |
42 |
43 |
44 |

{items.title}

45 |
46 |
47 | {active ? ( 48 | 49 |

50 | Sign up to see the details of 51 | the movie 52 |

53 |
54 | ) : null} 55 |
56 |
57 | ); 58 | }; 59 | -------------------------------------------------------------------------------- /swaggerui/lang/ko-kr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"경고:폐기예정됨", 6 | "Implementation Notes":"구현 노트", 7 | "Response Class":"응답 클래스", 8 | "Status":"상태", 9 | "Parameters":"매개변수들", 10 | "Parameter":"매개변수", 11 | "Value":"값", 12 | "Description":"설명", 13 | "Parameter Type":"매개변수 타입", 14 | "Data Type":"데이터 타입", 15 | "Response Messages":"응답 메세지", 16 | "HTTP Status Code":"HTTP 상태 코드", 17 | "Reason":"원인", 18 | "Response Model":"응답 모델", 19 | "Request URL":"요청 URL", 20 | "Response Body":"응답 본문", 21 | "Response Code":"응답 코드", 22 | "Response Headers":"응답 헤더", 23 | "Hide Response":"응답 숨기기", 24 | "Headers":"헤더", 25 | "Try it out!":"써보기!", 26 | "Show/Hide":"보이기/숨기기", 27 | "List Operations":"목록 작업", 28 | "Expand Operations":"전개 작업", 29 | "Raw":"원본", 30 | "can't parse JSON. Raw result":"JSON을 파싱할수 없음. 원본결과:", 31 | "Model Schema":"모델 스키마", 32 | "Model":"모델", 33 | "apply":"적용", 34 | "Username":"사용자 이름", 35 | "Password":"암호", 36 | "Terms of service":"이용약관", 37 | "Created by":"작성자", 38 | "See more at":"추가정보:", 39 | "Contact the developer":"개발자에게 문의", 40 | "api version":"api버전", 41 | "Response Content Type":"응답Content Type", 42 | "fetching resource":"리소스 가져오기", 43 | "fetching resource list":"리소스 목록 가져오기", 44 | "Explore":"탐색", 45 | "Show Swagger Petstore Example Apis":"Swagger Petstore 예제 보기", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"서버로부터 읽어들일수 없습니다. access-control-origin 설정이 올바르지 않을수 있습니다.", 47 | "Please specify the protocol for":"다음을 위한 프로토콜을 정하세요", 48 | "Can't read swagger JSON from":"swagger JSON 을 다음으로 부터 읽을수 없습니다", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"리소스 정보 불러오기 완료. Swagger UI 랜더링", 50 | "Unable to read api":"api를 읽을 수 없습니다.", 51 | "from path":"다음 경로로 부터", 52 | "server returned":"서버 응답함." 53 | }); 54 | -------------------------------------------------------------------------------- /controllers/people.js: -------------------------------------------------------------------------------- 1 | const People = require('../models/actions/people'); 2 | const {writeResponse} = require('../helpers/response'); 3 | const dbUtils = require('../db/dbUtils'); 4 | 5 | /** 6 | * @swagger 7 | * definition: 8 | * Person: 9 | * type: object 10 | * properties: 11 | * id: 12 | * type: integer 13 | * name: 14 | * type: string 15 | * poster_image: 16 | * type: string 17 | */ 18 | 19 | /** 20 | * @swagger 21 | * /people: 22 | * get: 23 | * tags: 24 | * - people 25 | * description: Returns all people 26 | * summary: Returns all people 27 | * produces: 28 | * - application/json 29 | * responses: 30 | * 200: 31 | * description: A list of people 32 | * schema: 33 | * type: array 34 | * items: 35 | * $ref: '#/definitions/Person' 36 | */ 37 | exports.list = function (req, res, next) { 38 | People.getAll(dbUtils.getSession()) 39 | .then((response) => writeResponse(res, response)) 40 | .catch(next); 41 | }; 42 | 43 | /** 44 | * @swagger 45 | * /people/{id}: 46 | * get: 47 | * tags: 48 | * - people 49 | * description: Returns a person by id 50 | * summary: Returns a person by id 51 | * produces: 52 | * - application/json 53 | * parameters: 54 | * - name: id 55 | * description: Person id 56 | * in: path 57 | * required: true 58 | * type: integer 59 | * responses: 60 | * 200: 61 | * description: A person 62 | * schema: 63 | * $ref: '#/definitions/Person' 64 | * 400: 65 | * description: Error message(s) 66 | * 404: 67 | * description: Person not found 68 | */ 69 | exports.findById = function (req, res, next) { 70 | const {id} = req.params; 71 | if (!id) throw {message: 'Invalid id', status: 400}; 72 | 73 | People.getById(dbUtils.getSession(), id) 74 | .then((response) => { 75 | writeResponse(res, response); 76 | }) 77 | .catch(next); 78 | }; 79 | -------------------------------------------------------------------------------- /swaggerui/lang/ja.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"警告: 廃止予定", 6 | "Implementation Notes":"実装メモ", 7 | "Response Class":"レスポンスクラス", 8 | "Status":"ステータス", 9 | "Parameters":"パラメータ群", 10 | "Parameter":"パラメータ", 11 | "Value":"値", 12 | "Description":"説明", 13 | "Parameter Type":"パラメータタイプ", 14 | "Data Type":"データタイプ", 15 | "Response Messages":"レスポンスメッセージ", 16 | "HTTP Status Code":"HTTPステータスコード", 17 | "Reason":"理由", 18 | "Response Model":"レスポンスモデル", 19 | "Request URL":"リクエストURL", 20 | "Response Body":"レスポンスボディ", 21 | "Response Code":"レスポンスコード", 22 | "Response Headers":"レスポンスヘッダ", 23 | "Hide Response":"レスポンスを隠す", 24 | "Headers":"ヘッダ", 25 | "Try it out!":"実際に実行!", 26 | "Show/Hide":"表示/非表示", 27 | "List Operations":"操作一覧", 28 | "Expand Operations":"操作の展開", 29 | "Raw":"Raw", 30 | "can't parse JSON. Raw result":"JSONへ解釈できません. 未加工の結果", 31 | "Model Schema":"モデルスキーマ", 32 | "Model":"モデル", 33 | "apply":"実行", 34 | "Username":"ユーザ名", 35 | "Password":"パスワード", 36 | "Terms of service":"サービス利用規約", 37 | "Created by":"Created by", 38 | "See more at":"See more at", 39 | "Contact the developer":"開発者に連絡", 40 | "api version":"APIバージョン", 41 | "Response Content Type":"レスポンス コンテンツタイプ", 42 | "fetching resource":"リソースの取得", 43 | "fetching resource list":"リソース一覧の取得", 44 | "Explore":"Explore", 45 | "Show Swagger Petstore Example Apis":"SwaggerペットストアAPIの表示", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"サーバから読み込めません. 適切なaccess-control-origin設定を持っていない可能性があります.", 47 | "Please specify the protocol for":"プロトコルを指定してください", 48 | "Can't read swagger JSON from":"次からswagger JSONを読み込めません", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"リソース情報の読み込みが完了しました. Swagger UIを描画しています", 50 | "Unable to read api":"APIを読み込めません", 51 | "from path":"次のパスから", 52 | "server returned":"サーバからの返答" 53 | }); 54 | -------------------------------------------------------------------------------- /client/build/index.html: -------------------------------------------------------------------------------- 1 | Movies Site
-------------------------------------------------------------------------------- /client/src/components/home/genreList/movies/moviesItem/moviesItem.css: -------------------------------------------------------------------------------- 1 | .card-film{ 2 | max-width: 15%; 3 | margin: 3% 0 3% 4%; 4 | position:relative; 5 | -webkit-box-shadow: 0px 0px 18px 9px rgba(0,0,0,0.75); 6 | -moz-box-shadow: 0px 0px 18px 9px rgba(0,0,0,0.75); 7 | box-shadow: 0px 0px 18px 9px rgba(0,0,0,0.75); 8 | background-color: rgba(0,0,0,0.7); 9 | border-radius: 0.8rem; 10 | } 11 | 12 | div.card-film span{ 13 | display: block; 14 | padding-bottom: 5%; 15 | } 16 | 17 | .card-film .modalWindow{ 18 | position: absolute; 19 | z-index: 2; 20 | } 21 | 22 | .card-film .film-photo img{ 23 | width: 100%; 24 | } 25 | 26 | .card-film .film-photo .filmInfo{ 27 | position:absolute; 28 | left:0; 29 | bottom:1%; 30 | right:0; 31 | top:0; 32 | background: rgba(29, 29, 29, 0.8); 33 | display:none; 34 | color: white; 35 | } 36 | 37 | .card-film .film-photo:hover .filmInfo{ 38 | display: block; 39 | } 40 | 41 | .card-film .film-photo:hover{ 42 | transform: scale(1.05); 43 | transition-duration: 0.4s; 44 | } 45 | 46 | .card-film .film-photo:hover img{ 47 | filter: blur(1px); 48 | } 49 | 50 | .card-film{ 51 | cursor: pointer; 52 | } 53 | 54 | .card-film .film-photo .filmInfo .rating{ 55 | position: relative; 56 | background: #5b5b5b; 57 | width: 75%; 58 | height: 2%; 59 | margin-top: 1%; 60 | margin-left: 5%; 61 | border-radius: 1rem; 62 | } 63 | 64 | .card-film .film-photo .filmInfo .rating .rating-item{ 65 | width: 70%; 66 | height: 100%; 67 | background: #878787; 68 | border-radius: 1rem; 69 | } 70 | 71 | .card-film .film-photo .filmInfo p:first-child{ 72 | margin-top: 93%; 73 | } 74 | 75 | .card-film .film-photo .filmInfo p{ 76 | margin-left: 5%; 77 | font-size: 1.3rem; 78 | margin-bottom: 0; 79 | margin-top: 5%; 80 | } 81 | 82 | .card-film .film-title h4{ 83 | margin-left: 4%; 84 | font-size: 0.9rem; 85 | font-style: normal; 86 | } 87 | 88 | .card-film .modalWindow a{ 89 | color: black; 90 | text-decoration: underline; 91 | margin-left: 0; 92 | } 93 | .card-film .modalWindow h4{ 94 | text-align: center; 95 | } 96 | -------------------------------------------------------------------------------- /client/src/components/authorization/signIn/signIn.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {userService} from '../../../services'; 3 | import {useRatedFilms} from '../../../services/user.service'; 4 | 5 | export const SignIn = ({handleChangeState, error, setError}) => { 6 | const {revalidate} = useRatedFilms(); 7 | 8 | const handleSignIn = async (e) => { 9 | console.log('from handle'); 10 | e.preventDefault(); 11 | const username = e.target[0].value; 12 | const password = e.target[1].value; 13 | const response = await userService.signIn({username, password}); 14 | if (!response) { 15 | console.log('from handle1'); 16 | setError('Error, wrong password or user name...'); 17 | } else { 18 | localStorage.setItem('token', response.data.token); 19 | localStorage.setItem('user', JSON.stringify(response.data.user)); 20 | revalidate(); 21 | window.location.href = '/'; 22 | } 23 | }; 24 | 25 | return ( 26 |
27 |

Sign in

28 |
29 |
30 | Enter your name 31 |
32 | 39 |
40 | Enter your password 41 |
42 | 49 |
50 |

{error}

51 |
52 | 53 |
54 |
55 |
56 | 64 |
65 | ); 66 | }; 67 | -------------------------------------------------------------------------------- /swaggerui/lang/tr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Uyarı: Deprecated", 6 | "Implementation Notes":"Gerçekleştirim Notları", 7 | "Response Class":"Dönen Sınıf", 8 | "Status":"Statü", 9 | "Parameters":"Parametreler", 10 | "Parameter":"Parametre", 11 | "Value":"Değer", 12 | "Description":"Açıklama", 13 | "Parameter Type":"Parametre Tipi", 14 | "Data Type":"Veri Tipi", 15 | "Response Messages":"Dönüş Mesajı", 16 | "HTTP Status Code":"HTTP Statü Kodu", 17 | "Reason":"Gerekçe", 18 | "Response Model":"Dönüş Modeli", 19 | "Request URL":"İstek URL", 20 | "Response Body":"Dönüş İçeriği", 21 | "Response Code":"Dönüş Kodu", 22 | "Response Headers":"Dönüş Üst Bilgileri", 23 | "Hide Response":"Dönüşü Gizle", 24 | "Headers":"Üst Bilgiler", 25 | "Try it out!":"Dene!", 26 | "Show/Hide":"Göster/Gizle", 27 | "List Operations":"Operasyonları Listele", 28 | "Expand Operations":"Operasyonları Aç", 29 | "Raw":"Ham", 30 | "can't parse JSON. Raw result":"JSON çözümlenemiyor. Ham sonuç", 31 | "Model Schema":"Model Şema", 32 | "Model":"Model", 33 | "apply":"uygula", 34 | "Username":"Kullanıcı Adı", 35 | "Password":"Parola", 36 | "Terms of service":"Servis şartları", 37 | "Created by":"Oluşturan", 38 | "See more at":"Daha fazlası için", 39 | "Contact the developer":"Geliştirici ile İletişime Geçin", 40 | "api version":"api versiyon", 41 | "Response Content Type":"Dönüş İçerik Tipi", 42 | "fetching resource":"kaynak getiriliyor", 43 | "fetching resource list":"kaynak listesi getiriliyor", 44 | "Explore":"Keşfet", 45 | "Show Swagger Petstore Example Apis":"Swagger Petstore Örnek Api'yi Gör", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Sunucudan okuma yapılamıyor. Sunucu access-control-origin ayarlarınızı kontrol edin.", 47 | "Please specify the protocol for":"Lütfen istenen adres için protokol belirtiniz", 48 | "Can't read swagger JSON from":"Swagger JSON bu kaynaktan okunamıyor", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Kaynak baglantısı tamamlandı. Swagger UI gösterime hazırlanıyor", 50 | "Unable to read api":"api okunamadı", 51 | "from path":"yoldan", 52 | "server returned":"sunucuya dönüldü" 53 | }); 54 | -------------------------------------------------------------------------------- /client/src/components/header/header.js: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | import {Link, useHistory} from 'react-router-dom'; 3 | import './header.css'; 4 | import icoMovies from '../../images/icoMovies.png'; 5 | import RedisLogo from './RedisLogo'; 6 | 7 | export const Header = () => { 8 | const [state, setState] = useState(true); 9 | const [icoUser, setIcoUser] = useState(null); 10 | const [username, setUsername] = useState(null); 11 | 12 | const dateFromLocal = localStorage.getItem('token'); 13 | const userFromLocal = localStorage.getItem('user'); 14 | 15 | const history = useHistory(); 16 | 17 | useEffect(() => { 18 | if (dateFromLocal) { 19 | setState(false); 20 | const user = JSON.parse(userFromLocal); 21 | setUsername(user.username); 22 | setIcoUser(user.avatar); 23 | } 24 | }, [dateFromLocal]); 25 | 26 | return ( 27 |
28 |
29 | {/* */} 30 |
history.push('/')} 32 | className="d-flex align-items-center ml-3 cursor-pointer"> 33 |
34 | 35 |
36 | 40 | Redis Movie App 41 | 42 |
43 | {/* */} 44 |
45 |
46 |
47 | {state ? ( 48 |

49 | Sign up 50 |

51 | ) : ( 52 |
53 | 54 | avatar 59 | 60 |
61 | 62 | {username} 63 | 64 |
65 |
66 | )} 67 |
68 |
69 | ); 70 | }; 71 | -------------------------------------------------------------------------------- /swaggerui/lang/pl.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Uwaga: Wycofane", 6 | "Implementation Notes":"Uwagi Implementacji", 7 | "Response Class":"Klasa Odpowiedzi", 8 | "Status":"Status", 9 | "Parameters":"Parametry", 10 | "Parameter":"Parametr", 11 | "Value":"Wartość", 12 | "Description":"Opis", 13 | "Parameter Type":"Typ Parametru", 14 | "Data Type":"Typ Danych", 15 | "Response Messages":"Wiadomości Odpowiedzi", 16 | "HTTP Status Code":"Kod Statusu HTTP", 17 | "Reason":"Przyczyna", 18 | "Response Model":"Model Odpowiedzi", 19 | "Request URL":"URL Wywołania", 20 | "Response Body":"Treść Odpowiedzi", 21 | "Response Code":"Kod Odpowiedzi", 22 | "Response Headers":"Nagłówki Odpowiedzi", 23 | "Hide Response":"Ukryj Odpowiedź", 24 | "Headers":"Nagłówki", 25 | "Try it out!":"Wypróbuj!", 26 | "Show/Hide":"Pokaż/Ukryj", 27 | "List Operations":"Lista Operacji", 28 | "Expand Operations":"Rozwiń Operacje", 29 | "Raw":"Nieprzetworzone", 30 | "can't parse JSON. Raw result":"nie można przetworzyć pliku JSON. Nieprzetworzone dane", 31 | "Model Schema":"Schemat Modelu", 32 | "Model":"Model", 33 | "apply":"użyj", 34 | "Username":"Nazwa użytkownika", 35 | "Password":"Hasło", 36 | "Terms of service":"Warunki używania", 37 | "Created by":"Utworzone przez", 38 | "See more at":"Zobacz więcej na", 39 | "Contact the developer":"Kontakt z deweloperem", 40 | "api version":"wersja api", 41 | "Response Content Type":"Typ Zasobu Odpowiedzi", 42 | "fetching resource":"ładowanie zasobu", 43 | "fetching resource list":"ładowanie listy zasobów", 44 | "Explore":"Eksploruj", 45 | "Show Swagger Petstore Example Apis":"Pokaż Przykładowe Api Swagger Petstore", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Brak połączenia z serwerem. Może on nie mieć odpowiednich ustawień access-control-origin.", 47 | "Please specify the protocol for":"Proszę podać protokół dla", 48 | "Can't read swagger JSON from":"Nie można odczytać swagger JSON z", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Ukończono Ładowanie Informacji o Zasobie. Renderowanie Swagger UI", 50 | "Unable to read api":"Nie można odczytać api", 51 | "from path":"ze ścieżki", 52 | "server returned":"serwer zwrócił" 53 | }); 54 | -------------------------------------------------------------------------------- /swaggerui/lang/pt.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Aviso: Depreciado", 6 | "Implementation Notes":"Notas de Implementação", 7 | "Response Class":"Classe de resposta", 8 | "Status":"Status", 9 | "Parameters":"Parâmetros", 10 | "Parameter":"Parâmetro", 11 | "Value":"Valor", 12 | "Description":"Descrição", 13 | "Parameter Type":"Tipo de parâmetro", 14 | "Data Type":"Tipo de dados", 15 | "Response Messages":"Mensagens de resposta", 16 | "HTTP Status Code":"Código de status HTTP", 17 | "Reason":"Razão", 18 | "Response Model":"Modelo resposta", 19 | "Request URL":"URL requisição", 20 | "Response Body":"Corpo da resposta", 21 | "Response Code":"Código da resposta", 22 | "Response Headers":"Cabeçalho da resposta", 23 | "Headers":"Cabeçalhos", 24 | "Hide Response":"Esconder resposta", 25 | "Try it out!":"Tente agora!", 26 | "Show/Hide":"Mostrar/Esconder", 27 | "List Operations":"Listar operações", 28 | "Expand Operations":"Expandir operações", 29 | "Raw":"Cru", 30 | "can't parse JSON. Raw result":"Falha ao analisar JSON. Resulto cru", 31 | "Model Schema":"Modelo esquema", 32 | "Model":"Modelo", 33 | "apply":"Aplicar", 34 | "Username":"Usuário", 35 | "Password":"Senha", 36 | "Terms of service":"Termos do serviço", 37 | "Created by":"Criado por", 38 | "See more at":"Veja mais em", 39 | "Contact the developer":"Contate o desenvolvedor", 40 | "api version":"Versão api", 41 | "Response Content Type":"Tipo de conteúdo da resposta", 42 | "fetching resource":"busca recurso", 43 | "fetching resource list":"buscando lista de recursos", 44 | "Explore":"Explorar", 45 | "Show Swagger Petstore Example Apis":"Show Swagger Petstore Example Apis", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Não é possível ler do servidor. Pode não ter as apropriadas configurações access-control-origin", 47 | "Please specify the protocol for":"Por favor especifique o protocolo", 48 | "Can't read swagger JSON from":"Não é possível ler o JSON Swagger de", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Carregar informação de recurso finalizada. Renderizando Swagger UI", 50 | "Unable to read api":"Não foi possível ler api", 51 | "from path":"do caminho", 52 | "server returned":"servidor retornou" 53 | }); 54 | -------------------------------------------------------------------------------- /swaggerui/lang/en.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Warning: Deprecated", 6 | "Implementation Notes":"Implementation Notes", 7 | "Response Class":"Response Class", 8 | "Status":"Status", 9 | "Parameters":"Parameters", 10 | "Parameter":"Parameter", 11 | "Value":"Value", 12 | "Description":"Description", 13 | "Parameter Type":"Parameter Type", 14 | "Data Type":"Data Type", 15 | "Response Messages":"Response Messages", 16 | "HTTP Status Code":"HTTP Status Code", 17 | "Reason":"Reason", 18 | "Response Model":"Response Model", 19 | "Request URL":"Request URL", 20 | "Response Body":"Response Body", 21 | "Response Code":"Response Code", 22 | "Response Headers":"Response Headers", 23 | "Hide Response":"Hide Response", 24 | "Headers":"Headers", 25 | "Try it out!":"Try it out!", 26 | "Show/Hide":"Show/Hide", 27 | "List Operations":"List Operations", 28 | "Expand Operations":"Expand Operations", 29 | "Raw":"Raw", 30 | "can't parse JSON. Raw result":"can't parse JSON. Raw result", 31 | "Example Value":"Example Value", 32 | "Model Schema":"Model Schema", 33 | "Model":"Model", 34 | "Click to set as parameter value":"Click to set as parameter value", 35 | "apply":"apply", 36 | "Username":"Username", 37 | "Password":"Password", 38 | "Terms of service":"Terms of service", 39 | "Created by":"Created by", 40 | "See more at":"See more at", 41 | "Contact the developer":"Contact the developer", 42 | "api version":"api version", 43 | "Response Content Type":"Response Content Type", 44 | "Parameter content type:":"Parameter content type:", 45 | "fetching resource":"fetching resource", 46 | "fetching resource list":"fetching resource list", 47 | "Explore":"Explore", 48 | "Show Swagger Petstore Example Apis":"Show Swagger Petstore Example Apis", 49 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Can't read from server. It may not have the appropriate access-control-origin settings.", 50 | "Please specify the protocol for":"Please specify the protocol for", 51 | "Can't read swagger JSON from":"Can't read swagger JSON from", 52 | "Finished Loading Resource Information. Rendering Swagger UI":"Finished Loading Resource Information. Rendering Swagger UI", 53 | "Unable to read api":"Unable to read api", 54 | "from path":"from path", 55 | "server returned":"server returned" 56 | }); 57 | -------------------------------------------------------------------------------- /swaggerui/lang/ru.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Предупреждение: Устарело", 6 | "Implementation Notes":"Заметки", 7 | "Response Class":"Пример ответа", 8 | "Status":"Статус", 9 | "Parameters":"Параметры", 10 | "Parameter":"Параметр", 11 | "Value":"Значение", 12 | "Description":"Описание", 13 | "Parameter Type":"Тип параметра", 14 | "Data Type":"Тип данных", 15 | "HTTP Status Code":"HTTP код", 16 | "Reason":"Причина", 17 | "Response Model":"Структура ответа", 18 | "Request URL":"URL запроса", 19 | "Response Body":"Тело ответа", 20 | "Response Code":"HTTP код ответа", 21 | "Response Headers":"Заголовки ответа", 22 | "Hide Response":"Спрятать ответ", 23 | "Headers":"Заголовки", 24 | "Response Messages":"Что может прийти в ответ", 25 | "Try it out!":"Попробовать!", 26 | "Show/Hide":"Показать/Скрыть", 27 | "List Operations":"Операции кратко", 28 | "Expand Operations":"Операции подробно", 29 | "Raw":"В сыром виде", 30 | "can't parse JSON. Raw result":"Не удается распарсить ответ:", 31 | "Example Value":"Пример", 32 | "Model Schema":"Структура", 33 | "Model":"Описание", 34 | "Click to set as parameter value":"Нажмите, чтобы испльзовать в качестве значения параметра", 35 | "apply":"применить", 36 | "Username":"Имя пользователя", 37 | "Password":"Пароль", 38 | "Terms of service":"Условия использования", 39 | "Created by":"Разработано", 40 | "See more at":"Еще тут", 41 | "Contact the developer":"Связаться с разработчиком", 42 | "api version":"Версия API", 43 | "Response Content Type":"Content Type ответа", 44 | "Parameter content type:":"Content Type параметра:", 45 | "fetching resource":"Получение ресурса", 46 | "fetching resource list":"Получение ресурсов", 47 | "Explore":"Показать", 48 | "Show Swagger Petstore Example Apis":"Показать примеры АПИ", 49 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Не удается получить ответ от сервера. Возможно, проблема с настройками доступа", 50 | "Please specify the protocol for":"Пожалуйста, укажите протокол для", 51 | "Can't read swagger JSON from":"Не получается прочитать swagger json из", 52 | "Finished Loading Resource Information. Rendering Swagger UI":"Загрузка информации о ресурсах завершена. Рендерим", 53 | "Unable to read api":"Не удалось прочитать api", 54 | "from path":"по адресу", 55 | "server returned":"сервер сказал" 56 | }); 57 | -------------------------------------------------------------------------------- /swaggerui/lang/ca.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Advertència: Obsolet", 6 | "Implementation Notes":"Notes d'implementació", 7 | "Response Class":"Classe de la Resposta", 8 | "Status":"Estatus", 9 | "Parameters":"Paràmetres", 10 | "Parameter":"Paràmetre", 11 | "Value":"Valor", 12 | "Description":"Descripció", 13 | "Parameter Type":"Tipus del Paràmetre", 14 | "Data Type":"Tipus de la Dada", 15 | "Response Messages":"Missatges de la Resposta", 16 | "HTTP Status Code":"Codi d'Estatus HTTP", 17 | "Reason":"Raó", 18 | "Response Model":"Model de la Resposta", 19 | "Request URL":"URL de la Sol·licitud", 20 | "Response Body":"Cos de la Resposta", 21 | "Response Code":"Codi de la Resposta", 22 | "Response Headers":"Capçaleres de la Resposta", 23 | "Hide Response":"Amagar Resposta", 24 | "Try it out!":"Prova-ho!", 25 | "Show/Hide":"Mostrar/Amagar", 26 | "List Operations":"Llista Operacions", 27 | "Expand Operations":"Expandir Operacions", 28 | "Raw":"Cru", 29 | "can't parse JSON. Raw result":"no puc analitzar el JSON. Resultat cru", 30 | "Example Value":"Valor d'Exemple", 31 | "Model Schema":"Esquema del Model", 32 | "Model":"Model", 33 | "apply":"aplicar", 34 | "Username":"Nom d'usuari", 35 | "Password":"Contrasenya", 36 | "Terms of service":"Termes del servei", 37 | "Created by":"Creat per", 38 | "See more at":"Veure més en", 39 | "Contact the developer":"Contactar amb el desenvolupador", 40 | "api version":"versió de la api", 41 | "Response Content Type":"Tipus de Contingut de la Resposta", 42 | "fetching resource":"recollint recurs", 43 | "fetching resource list":"recollins llista de recursos", 44 | "Explore":"Explorant", 45 | "Show Swagger Petstore Example Apis":"Mostrar API d'Exemple Swagger Petstore", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"No es pot llegir del servidor. Potser no teniu la configuració de control d'accés apropiada.", 47 | "Please specify the protocol for":"Si us plau, especifiqueu el protocol per a", 48 | "Can't read swagger JSON from":"No es pot llegir el JSON de swagger des de", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Finalitzada la càrrega del recurs informatiu. Renderitzant Swagger UI", 50 | "Unable to read api":"No es pot llegir l'api", 51 | "from path":"des de la ruta", 52 | "server returned":"el servidor ha retornat" 53 | }); 54 | -------------------------------------------------------------------------------- /swaggerui/lang/geo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"ყურადღება: აღარ გამოიყენება", 6 | "Implementation Notes":"იმპლემენტაციის აღწერა", 7 | "Response Class":"რესპონს კლასი", 8 | "Status":"სტატუსი", 9 | "Parameters":"პარამეტრები", 10 | "Parameter":"პარამეტრი", 11 | "Value":"მნიშვნელობა", 12 | "Description":"აღწერა", 13 | "Parameter Type":"პარამეტრის ტიპი", 14 | "Data Type":"მონაცემის ტიპი", 15 | "Response Messages":"პასუხი", 16 | "HTTP Status Code":"HTTP სტატუსი", 17 | "Reason":"მიზეზი", 18 | "Response Model":"რესპონს მოდელი", 19 | "Request URL":"მოთხოვნის URL", 20 | "Response Body":"პასუხის სხეული", 21 | "Response Code":"პასუხის კოდი", 22 | "Response Headers":"პასუხის ჰედერები", 23 | "Hide Response":"დამალე პასუხი", 24 | "Headers":"ჰედერები", 25 | "Try it out!":"ცადე !", 26 | "Show/Hide":"გამოჩენა/დამალვა", 27 | "List Operations":"ოპერაციების სია", 28 | "Expand Operations":"ოპერაციები ვრცლად", 29 | "Raw":"ნედლი", 30 | "can't parse JSON. Raw result":"JSON-ის დამუშავება ვერ მოხერხდა. ნედლი პასუხი", 31 | "Example Value":"მაგალითი", 32 | "Model Schema":"მოდელის სტრუქტურა", 33 | "Model":"მოდელი", 34 | "Click to set as parameter value":"პარამეტრისთვის მნიშვნელობის მისანიჭებლად, დააკლიკე", 35 | "apply":"გამოყენება", 36 | "Username":"მოხმარებელი", 37 | "Password":"პაროლი", 38 | "Terms of service":"მომსახურების პირობები", 39 | "Created by":"შექმნა", 40 | "See more at":"ნახე ვრცლად", 41 | "Contact the developer":"დაუკავშირდი დეველოპერს", 42 | "api version":"api ვერსია", 43 | "Response Content Type":"პასუხის კონტენტის ტიპი", 44 | "Parameter content type:":"პარამეტრის კონტენტის ტიპი:", 45 | "fetching resource":"რესურსების მიღება", 46 | "fetching resource list":"რესურსების სიის მიღება", 47 | "Explore":"ნახვა", 48 | "Show Swagger Petstore Example Apis":"ნახე Swagger Petstore სამაგალითო Api", 49 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"სერვერთან დაკავშირება ვერ ხერხდება. შეამოწმეთ access-control-origin.", 50 | "Please specify the protocol for":"მიუთითეთ პროტოკოლი", 51 | "Can't read swagger JSON from":"swagger JSON წაკითხვა ვერ მოხერხდა", 52 | "Finished Loading Resource Information. Rendering Swagger UI":"რესურსების ჩატვირთვა სრულდება. Swagger UI რენდერდება", 53 | "Unable to read api":"api წაკითხვა ვერ მოხერხდა", 54 | "from path":"მისამართიდან", 55 | "server returned":"სერვერმა დააბრუნა" 56 | }); 57 | -------------------------------------------------------------------------------- /swaggerui/lang/it.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Attenzione: Deprecato", 6 | "Implementation Notes":"Note di implementazione", 7 | "Response Class":"Classe della risposta", 8 | "Status":"Stato", 9 | "Parameters":"Parametri", 10 | "Parameter":"Parametro", 11 | "Value":"Valore", 12 | "Description":"Descrizione", 13 | "Parameter Type":"Tipo di parametro", 14 | "Data Type":"Tipo di dato", 15 | "Response Messages":"Messaggi della risposta", 16 | "HTTP Status Code":"Codice stato HTTP", 17 | "Reason":"Motivo", 18 | "Response Model":"Modello di risposta", 19 | "Request URL":"URL della richiesta", 20 | "Response Body":"Corpo della risposta", 21 | "Response Code":"Oggetto della risposta", 22 | "Response Headers":"Intestazioni della risposta", 23 | "Hide Response":"Nascondi risposta", 24 | "Try it out!":"Provalo!", 25 | "Show/Hide":"Mostra/Nascondi", 26 | "List Operations":"Mostra operazioni", 27 | "Expand Operations":"Espandi operazioni", 28 | "Raw":"Grezzo (raw)", 29 | "can't parse JSON. Raw result":"non è possibile parsare il JSON. Risultato grezzo (raw).", 30 | "Model Schema":"Schema del modello", 31 | "Model":"Modello", 32 | "apply":"applica", 33 | "Username":"Nome utente", 34 | "Password":"Password", 35 | "Terms of service":"Condizioni del servizio", 36 | "Created by":"Creato da", 37 | "See more at":"Informazioni aggiuntive:", 38 | "Contact the developer":"Contatta lo sviluppatore", 39 | "api version":"versione api", 40 | "Response Content Type":"Tipo di contenuto (content type) della risposta", 41 | "fetching resource":"recuperando la risorsa", 42 | "fetching resource list":"recuperando lista risorse", 43 | "Explore":"Esplora", 44 | "Show Swagger Petstore Example Apis":"Mostra le api di esempio di Swagger Petstore", 45 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Non è possibile leggere dal server. Potrebbe non avere le impostazioni di controllo accesso origine (access-control-origin) appropriate.", 46 | "Please specify the protocol for":"Si prega di specificare il protocollo per", 47 | "Can't read swagger JSON from":"Impossibile leggere JSON swagger da:", 48 | "Finished Loading Resource Information. Rendering Swagger UI":"Lettura informazioni risorse termianta. Swagger UI viene mostrata", 49 | "Unable to read api":"Impossibile leggere la api", 50 | "from path":"da cartella", 51 | "server returned":"il server ha restituito" 52 | }); 53 | -------------------------------------------------------------------------------- /swaggerui/lang/es.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Advertencia: Obsoleto", 6 | "Implementation Notes":"Notas de implementación", 7 | "Response Class":"Clase de la Respuesta", 8 | "Status":"Status", 9 | "Parameters":"Parámetros", 10 | "Parameter":"Parámetro", 11 | "Value":"Valor", 12 | "Description":"Descripción", 13 | "Parameter Type":"Tipo del Parámetro", 14 | "Data Type":"Tipo del Dato", 15 | "Response Messages":"Mensajes de la Respuesta", 16 | "HTTP Status Code":"Código de Status HTTP", 17 | "Reason":"Razón", 18 | "Response Model":"Modelo de la Respuesta", 19 | "Request URL":"URL de la Solicitud", 20 | "Response Body":"Cuerpo de la Respuesta", 21 | "Response Code":"Código de la Respuesta", 22 | "Response Headers":"Encabezados de la Respuesta", 23 | "Hide Response":"Ocultar Respuesta", 24 | "Try it out!":"Pruébalo!", 25 | "Show/Hide":"Mostrar/Ocultar", 26 | "List Operations":"Listar Operaciones", 27 | "Expand Operations":"Expandir Operaciones", 28 | "Raw":"Crudo", 29 | "can't parse JSON. Raw result":"no puede parsear el JSON. Resultado crudo", 30 | "Example Value":"Valor de Ejemplo", 31 | "Model Schema":"Esquema del Modelo", 32 | "Model":"Modelo", 33 | "apply":"aplicar", 34 | "Username":"Nombre de usuario", 35 | "Password":"Contraseña", 36 | "Terms of service":"Términos de Servicio", 37 | "Created by":"Creado por", 38 | "See more at":"Ver más en", 39 | "Contact the developer":"Contactar al desarrollador", 40 | "api version":"versión de la api", 41 | "Response Content Type":"Tipo de Contenido (Content Type) de la Respuesta", 42 | "fetching resource":"buscando recurso", 43 | "fetching resource list":"buscando lista del recurso", 44 | "Explore":"Explorar", 45 | "Show Swagger Petstore Example Apis":"Mostrar Api Ejemplo de Swagger Petstore", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"No se puede leer del servidor. Tal vez no tiene la configuración de control de acceso de origen (access-control-origin) apropiado.", 47 | "Please specify the protocol for":"Por favor, especificar el protocola para", 48 | "Can't read swagger JSON from":"No se puede leer el JSON de swagger desde", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Finalizada la carga del recurso de Información. Mostrando Swagger UI", 50 | "Unable to read api":"No se puede leer la api", 51 | "from path":"desde ruta", 52 | "server returned":"el servidor retornó" 53 | }); 54 | -------------------------------------------------------------------------------- /swaggerui/lang/fr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Avertissement : Obsolète", 6 | "Implementation Notes":"Notes d'implémentation", 7 | "Response Class":"Classe de la réponse", 8 | "Status":"Statut", 9 | "Parameters":"Paramètres", 10 | "Parameter":"Paramètre", 11 | "Value":"Valeur", 12 | "Description":"Description", 13 | "Parameter Type":"Type du paramètre", 14 | "Data Type":"Type de données", 15 | "Response Messages":"Messages de la réponse", 16 | "HTTP Status Code":"Code de statut HTTP", 17 | "Reason":"Raison", 18 | "Response Model":"Modèle de réponse", 19 | "Request URL":"URL appelée", 20 | "Response Body":"Corps de la réponse", 21 | "Response Code":"Code de la réponse", 22 | "Response Headers":"En-têtes de la réponse", 23 | "Hide Response":"Cacher la réponse", 24 | "Headers":"En-têtes", 25 | "Try it out!":"Testez !", 26 | "Show/Hide":"Afficher/Masquer", 27 | "List Operations":"Liste des opérations", 28 | "Expand Operations":"Développer les opérations", 29 | "Raw":"Brut", 30 | "can't parse JSON. Raw result":"impossible de décoder le JSON. Résultat brut", 31 | "Example Value":"Exemple la valeur", 32 | "Model Schema":"Définition du modèle", 33 | "Model":"Modèle", 34 | "apply":"appliquer", 35 | "Username":"Nom d'utilisateur", 36 | "Password":"Mot de passe", 37 | "Terms of service":"Conditions de service", 38 | "Created by":"Créé par", 39 | "See more at":"Voir plus sur", 40 | "Contact the developer":"Contacter le développeur", 41 | "api version":"version de l'api", 42 | "Response Content Type":"Content Type de la réponse", 43 | "fetching resource":"récupération de la ressource", 44 | "fetching resource list":"récupération de la liste de ressources", 45 | "Explore":"Explorer", 46 | "Show Swagger Petstore Example Apis":"Montrer les Apis de l'exemple Petstore de Swagger", 47 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Impossible de lire à partir du serveur. Il se peut que les réglages access-control-origin ne soient pas appropriés.", 48 | "Please specify the protocol for":"Veuillez spécifier un protocole pour", 49 | "Can't read swagger JSON from":"Impossible de lire le JSON swagger à partir de", 50 | "Finished Loading Resource Information. Rendering Swagger UI":"Chargement des informations terminé. Affichage de Swagger UI", 51 | "Unable to read api":"Impossible de lire l'api", 52 | "from path":"à partir du chemin", 53 | "server returned":"réponse du serveur" 54 | }); 55 | -------------------------------------------------------------------------------- /client/src/components/authorization/signUp/signUp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {userService} from '../../../services'; 3 | 4 | export const SignUp = ({ 5 | handleChangeState, 6 | setButton, 7 | button, 8 | error, 9 | setError, 10 | }) => { 11 | const handleSignUp = async (e) => { 12 | setError(null); 13 | e.preventDefault(); 14 | if (e.target[1].value === e.target[2].value) { 15 | const username = e.target[0].value; 16 | const password = e.target[1].value; 17 | const response = await userService.signUp({username, password}); 18 | if (response.error) { 19 | console.log(response.error); 20 | if (response.error.username) { 21 | setError(response.error.username); 22 | } else { 23 | setError('Wrong password or user name...'); 24 | } 25 | } else { 26 | setButton(!button); 27 | } 28 | } else { 29 | setError("Passwords don't match"); 30 | } 31 | }; 32 | 33 | return ( 34 |
35 |

Create Account

36 |
37 |
38 | Enter your name 39 |
40 | 49 |
50 | Enter your password 51 |
52 | 59 |
60 | Repeat your password 61 |
62 | 69 |
70 |

{error}

71 |
72 | 73 |
74 |
75 |
76 | 84 |
85 | ); 86 | }; 87 | -------------------------------------------------------------------------------- /models/actions/users.js: -------------------------------------------------------------------------------- 1 | const uuid = require('node-uuid'); 2 | const randomstring = require('randomstring'); 3 | const crypto = require('crypto'); 4 | const User = require('../redis/user'); 5 | 6 | const register = function (session, username, password) { 7 | return session 8 | .query('MATCH (user:User {username: $username}) RETURN user', {username}) 9 | .then((results) => { 10 | if (results.hasNext()) { 11 | throw {username: 'username already in use', status: 400}; 12 | } else { 13 | return session 14 | .query( 15 | 'CREATE (user:User {id: $id, username: $username, password: $password, api_key: $api_key}) RETURN user', 16 | { 17 | id: uuid.v4(), 18 | username, 19 | password: hashPassword(username, password), 20 | api_key: randomstring.generate({ 21 | length: 20, 22 | charset: 'hex', 23 | }), 24 | }, 25 | ) 26 | .then((createdUser) => { 27 | while (createdUser.hasNext()) { 28 | const record = createdUser.next(); 29 | return new User(record.get('user')); 30 | } 31 | }); 32 | } 33 | }); 34 | }; 35 | 36 | const me = function (session, apiKey) { 37 | return session 38 | .query('MATCH (user:User {api_key: $api_key}) RETURN user', { 39 | api_key: apiKey, 40 | }) 41 | .then((foundedUser) => { 42 | if (!foundedUser.hasNext()) { 43 | throw {message: 'invalid authorization key', status: 401}; 44 | } 45 | while (foundedUser.hasNext()) { 46 | const record = foundedUser.next(); 47 | return new User(record.get('user')); 48 | } 49 | }); 50 | }; 51 | 52 | const login = function (session, username, password) { 53 | return session 54 | .query('MATCH (user:User {username: $username}) RETURN user', {username}) 55 | .then((foundedUser) => { 56 | if (!foundedUser.hasNext()) { 57 | throw {username: 'username does not exist', status: 400}; 58 | } else { 59 | while (foundedUser.hasNext()) { 60 | const record = foundedUser.next(); 61 | const dbUser = record.get('user').properties; 62 | if (dbUser.password !== hashPassword(username, password)) { 63 | throw {password: 'wrong password', status: 400}; 64 | } 65 | return {token: dbUser.api_key, user: new User(record.get('user'))}; 66 | } 67 | } 68 | }); 69 | }; 70 | 71 | function hashPassword(username, password) { 72 | const s = `${username}:${password}`; 73 | return crypto.createHash('sha256').update(s).digest('hex'); 74 | } 75 | 76 | module.exports = { 77 | register, 78 | me, 79 | login, 80 | }; 81 | -------------------------------------------------------------------------------- /client/src/components/detailsOfFilm/detailsItem/detailsItem.css: -------------------------------------------------------------------------------- 1 | .details{ 2 | display: flex; 3 | flex-direction: column; 4 | padding: 3%; 5 | } 6 | 7 | .details h3{ 8 | margin-top: 2.1rem; 9 | font-size: 1.5rem; 10 | } 11 | 12 | .details-page{ 13 | display: flex; 14 | flex-direction: row; 15 | } 16 | 17 | .details-page .detailsImg{ 18 | flex: 2; 19 | } 20 | 21 | .details-page .detailsImg img{ 22 | width: 70%; 23 | } 24 | 25 | .details-page .details-information{ 26 | flex: 3; 27 | } 28 | 29 | .details-page .details-information h2{ 30 | font-size: 2.3rem; 31 | margin-top: 0; 32 | } 33 | .details-page .details-information p{ 34 | font-size: 1.2rem; 35 | margin: 3% 0; 36 | } 37 | .details-page .details-information span{ 38 | font-size: 1.1rem; 39 | } 40 | .details-page .details-information .rateFilm button{ 41 | margin-left: 5px; 42 | margin-top: 1rem; 43 | background: none; 44 | border: 1px solid white; 45 | color: white; 46 | padding: 0.7rem 1.2rem; 47 | } 48 | .details-page .details-information .rateFilm button:hover{ 49 | background: white; 50 | color: black; 51 | transform: scale(1.1); 52 | transition: 0.5s; 53 | cursor: pointer; 54 | } 55 | 56 | .details .details-film-actors{ 57 | margin-top: 2%; 58 | display: flex; 59 | flex-direction: row; 60 | justify-content: left; 61 | } 62 | 63 | .details .details-film-actors .card-actor{ 64 | margin-right: 5%; 65 | max-width: 140px; 66 | text-align: center; 67 | font-size: 1.1rem; 68 | background: black; 69 | -webkit-box-shadow: 0px 0px 18px 9px rgba(0,0,0,0.75); 70 | -moz-box-shadow: 0px 0px 18px 9px rgba(0,0,0,0.75); 71 | box-shadow: 0px 0px 18px 9px rgba(0,0,0,0.75); 72 | } 73 | .details .details-film-actors .card-actor:hover{ 74 | cursor: pointer; 75 | } 76 | .details .details-film-actors .card-actor:hover img{ 77 | transform: scale(1.05); 78 | transition-duration: 0.4s; 79 | } 80 | .details .details-film-actors .card-actor .photoActor img{ 81 | width: 100%; 82 | height: 100%; 83 | border-radius: 5%; 84 | } 85 | 86 | .details .details-film-actors .card-actor .photoActor img:first-child{ 87 | height: 180px; 88 | object-fit: cover; 89 | width: 100%; 90 | } 91 | 92 | 93 | .details .details-page .details-information .ratingLine{ 94 | position: relative; 95 | background: #5b5b5b; 96 | width: 30%; 97 | height: 5%; 98 | margin-top: 1%; 99 | border-radius: 1rem; 100 | } 101 | .details .details-page .details-information .ratingLine .ratingLineItem{ 102 | width: 70%; 103 | height: 100%; 104 | background: #878787; 105 | border-radius: 1rem; 106 | color: #5b5b5b; 107 | } 108 | 109 | -------------------------------------------------------------------------------- /client/src/components/loading/loading.css: -------------------------------------------------------------------------------- 1 | @keyframes ldio-1y95m1hav6oj { 2 | 0 % {transform: rotate(0)} 3 | 100% {transform: rotate(360deg)} 4 | } 5 | .ldio-1y95m1hav6oj div {box-sizing: border-box!important} 6 | .ldio-1y95m1hav6oj > div { 7 | position: absolute; 8 | width: 69px; 9 | height: 69px; 10 | top: 15.5px; 11 | left: 15.5px; 12 | border-radius: 50%; 13 | border: 5px solid #000; 14 | border-color: #a2a2a4 transparent #a2a2a4 transparent; 15 | animation: ldio-1y95m1hav6oj 2.127659574468085s linear infinite; 16 | } 17 | 18 | .ldio-1y95m1hav6oj > div:nth-child(2), .ldio-1y95m1hav6oj > div:nth-child(4) { 19 | width: 57px; 20 | height: 57px; 21 | top: 21.5px; 22 | left: 21.5px; 23 | animation: ldio-1y95m1hav6oj 2.127659574468085s linear infinite reverse; 24 | } 25 | .ldio-1y95m1hav6oj > div:nth-child(2) { 26 | border-color: transparent #d2d2d2 transparent #d2d2d2 27 | } 28 | .ldio-1y95m1hav6oj > div:nth-child(3) {border-color: transparent} 29 | .ldio-1y95m1hav6oj > div:nth-child(3) div { 30 | position: absolute; 31 | width: 100%; 32 | height: 100%; 33 | transform: rotate(45deg); 34 | } 35 | .ldio-1y95m1hav6oj > div:nth-child(3) div:before, .ldio-1y95m1hav6oj > div:nth-child(3) div:after { 36 | content: ""; 37 | display: block; 38 | position: absolute; 39 | width: 5px; 40 | height: 5px; 41 | top: -5px; 42 | left: 27px; 43 | background: #a2a2a4; 44 | border-radius: 50%; 45 | box-shadow: 0 64px 0 0 #a2a2a4; 46 | } 47 | .ldio-1y95m1hav6oj > div:nth-child(3) div:after { 48 | left: -5px; 49 | top: 27px; 50 | box-shadow: 64px 0 0 0 #a2a2a4; 51 | } 52 | 53 | .ldio-1y95m1hav6oj > div:nth-child(4) {border-color: transparent;} 54 | .ldio-1y95m1hav6oj > div:nth-child(4) div { 55 | position: absolute; 56 | width: 100%; 57 | height: 100%; 58 | transform: rotate(45deg); 59 | } 60 | .ldio-1y95m1hav6oj > div:nth-child(4) div:before, .ldio-1y95m1hav6oj > div:nth-child(4) div:after { 61 | content: ""; 62 | display: block; 63 | position: absolute; 64 | width: 5px; 65 | height: 5px; 66 | top: -5px; 67 | left: 21px; 68 | background: #d2d2d2; 69 | border-radius: 50%; 70 | box-shadow: 0 52px 0 0 #d2d2d2; 71 | } 72 | .ldio-1y95m1hav6oj > div:nth-child(4) div:after { 73 | left: -5px; 74 | top: 21px; 75 | box-shadow: 52px 0 0 0 #d2d2d2; 76 | } 77 | .loadingio-spinner-double-ring-e1lvwbcki8 { 78 | width: 84px; 79 | height: 84px; 80 | display: inline-block; 81 | overflow: hidden; 82 | background: none; 83 | } 84 | .ldio-1y95m1hav6oj { 85 | width: 100%; 86 | height: 100%; 87 | position: relative; 88 | transform: translateZ(0) scale(0.84); 89 | backface-visibility: hidden; 90 | transform-origin: 0 0; /* see note above */ 91 | } 92 | .ldio-1y95m1hav6oj div {box-sizing: content-box;} 93 | 94 | .spinnerBlock{ 95 | display: flex; 96 | justify-content: center; 97 | margin-top: 50px; 98 | margin-bottom: 800px; 99 | } 100 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | const express = require('express'); 4 | const swaggerJSDoc = require('swagger-jsdoc'); 5 | const swaggerUi = require('swagger-ui-express'); 6 | const methodOverride = require('method-override'); 7 | const history = require('connect-history-api-fallback'); 8 | const nconf = require('./config'); 9 | const path = require('path'); 10 | const setAuthUser = require('./middlewares/setAuthUser'); 11 | const {writeError} = require('./helpers/response'); 12 | const {initialize} = require('./db/initialize'); 13 | const { 14 | moviesRouter, 15 | peopleRouter, 16 | authRouter, 17 | genresRouter, 18 | dataRouter, 19 | } = require('./routes'); 20 | 21 | const app = express(); 22 | 23 | const historyMiddleware = history({ 24 | verbose: false, 25 | }); 26 | app 27 | .use((req, res, next) => { 28 | if (req.path.startsWith('/api')) { 29 | next(); 30 | } else { 31 | return historyMiddleware(req, res, next); 32 | } 33 | }) 34 | .use(express.static(path.resolve(path.join(__dirname, './client/build')))); 35 | 36 | const api = express(); 37 | 38 | app.use(nconf.get('api_path'), api); 39 | 40 | const swaggerDefinition = { 41 | info: { 42 | title: 'Redis-graph Movie Demo API (Node/Express)', 43 | version: '1.0.0', 44 | description: '', 45 | }, 46 | host: 'localhost:4000', 47 | basePath: '/', 48 | }; 49 | 50 | // options for the swagger docs 51 | const options = { 52 | // import swaggerDefinitions 53 | swaggerDefinition, 54 | // path to the API docs 55 | apis: ['./controllers/*.js'], 56 | }; 57 | 58 | // initialize swagger-jsdoc 59 | const swaggerSpec = swaggerJSDoc(options); 60 | 61 | api.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); 62 | 63 | app.set('port', nconf.get('PORT')); 64 | 65 | api.use(express.json()); 66 | api.use(methodOverride()); 67 | 68 | // enable CORS 69 | api.use((_, res, next) => { 70 | res.header('Access-Control-Allow-Origin', '*'); 71 | res.header('Access-Control-Allow-Credentials', 'true'); 72 | res.header( 73 | 'Access-Control-Allow-Methods', 74 | 'GET,HEAD,OPTIONS,POST,PUT,DELETE', 75 | ); 76 | res.header( 77 | 'Access-Control-Allow-Headers', 78 | 'Origin, X-Requested-With, Content-Type, Accept, Authorization', 79 | ); 80 | next(); 81 | }); 82 | 83 | // api custom middlewares: 84 | api.use(setAuthUser); 85 | 86 | // api routes 87 | api.use('/auth', authRouter); 88 | api.use('/movies', moviesRouter); 89 | api.use('/user', peopleRouter); 90 | api.use('/genres', genresRouter); 91 | api.use('/data', dataRouter); 92 | 93 | // api error handler 94 | api.use((err, req, res, next) => { 95 | if (err && err.status) { 96 | writeError(res, err); 97 | } else next(err); 98 | }); 99 | 100 | app.listen(app.get('port'), '0.0.0.0', () => { 101 | console.log( 102 | `Express server listening on port ${app.get('port')} see docs at /docs`, 103 | ); 104 | /** We need to check the initialization state in here. */ 105 | initialize(); 106 | }); 107 | -------------------------------------------------------------------------------- /client/src/services/user.service.js: -------------------------------------------------------------------------------- 1 | import {AXIOS} from '../config'; 2 | import { 3 | useCallback, 4 | useContext, 5 | useEffect, 6 | useState, 7 | createContext, 8 | } from 'react'; 9 | 10 | class UserService { 11 | async signIn(user) { 12 | try { 13 | const response = await AXIOS.post('/auth/login', user); 14 | return response; 15 | } catch (e) { 16 | console.error(e); 17 | } 18 | } 19 | 20 | async signUp(user) { 21 | try { 22 | const response = await AXIOS.post('/auth/register', user); 23 | return response; 24 | } catch (e) { 25 | console.error(e); 26 | return {error: e.response.data}; 27 | } 28 | } 29 | 30 | async ratedFilms() { 31 | const userToken = await localStorage.getItem('token'); 32 | 33 | const {data} = await AXIOS.get('/movies/rated', { 34 | headers: { 35 | Authorization: `Token ${userToken}`, 36 | }, 37 | }); 38 | return data; 39 | } 40 | } 41 | 42 | const userService = new UserService(); 43 | 44 | export const RatedFilmsContext = createContext({ 45 | adjustRating: (films) => films, 46 | revalidate: () => {}, 47 | loading: false, 48 | }); 49 | 50 | const useRatedFilms = () => useContext(RatedFilmsContext); 51 | 52 | /** 53 | * This hook provides a helper method for adjusting the overall films rating 54 | * based on the user-specific ratings. 55 | */ 56 | const useRatedFilms2 = () => { 57 | const [ratedFilms, setRatedFilms] = useState(() => []); 58 | const [ratedFilmsLoading, setRatedFilmsLoading] = useState(true); 59 | console.log(ratedFilms); 60 | useEffect(() => { 61 | /** 62 | * The only problems comes that we don't employ caching for now 63 | * and that results in excessive API requests. 64 | */ 65 | userService 66 | .ratedFilms() 67 | .then((films) => { 68 | setRatedFilmsLoading(false); 69 | setRatedFilms(films); 70 | }) 71 | .catch((e) => { 72 | setRatedFilmsLoading(false); 73 | }); 74 | }, []); 75 | 76 | const revalidate = useCallback(() => { 77 | setRatedFilmsLoading(true); 78 | userService.ratedFilms().then((films) => { 79 | setRatedFilmsLoading(false); 80 | setRatedFilms(films); 81 | }); 82 | }, []); 83 | 84 | const findMyRating = useCallback( 85 | (filmId) => ratedFilms.filter((x) => +x.id === +filmId).pop() ?? null, 86 | [ratedFilms], 87 | ); 88 | 89 | const adjustRating = useCallback( 90 | (films) => { 91 | return films.map((film) => { 92 | const rated = findMyRating(film.id); 93 | if (rated === null) { 94 | return film; 95 | } 96 | const myRating = rated.my_rating; 97 | /** Adjust the rating based on user's personal rating */ 98 | return { 99 | ...film, 100 | imdbRating: 101 | Math.floor((film.imdbRating + (myRating - 5) * 0.2) * 10) / 10, 102 | }; 103 | }); 104 | }, 105 | [findMyRating], 106 | ); 107 | 108 | return { 109 | ratedFilms, 110 | ratedFilmsLoading, 111 | adjustRating, 112 | revalidate, 113 | }; 114 | }; 115 | 116 | export {userService, useRatedFilms, useRatedFilms2}; 117 | -------------------------------------------------------------------------------- /controllers/data.js: -------------------------------------------------------------------------------- 1 | // movies.js 2 | const {writeResponse} = require('../helpers/response'); 3 | const { 4 | ACTORS: {actors}, 5 | DIRECTORS_OF_FILMS: {directors}, 6 | GENRES: {genres}, 7 | MOVIES: {movies}, 8 | MOVIES_WITH_GENRES: {moviesWithGenres}, 9 | } = require('../data'); 10 | 11 | const { 12 | addGenres, 13 | setMovies, 14 | setDirectorsWithRelationshipToMovie, 15 | setActorsWithRelationshipToMovie, 16 | setGenresToMovies, 17 | } = require('../db/initialize'); 18 | 19 | /** 20 | * @swagger 21 | * /data/genres: 22 | * post: 23 | * tags: 24 | * - data 25 | * description: Set 20 genres 26 | * produces: 27 | * - application/json 28 | * responses: 29 | * 200: 30 | * description: "added 20 genres" 31 | */ 32 | exports.setGenres = async function (_, res, next) { 33 | try { 34 | await addGenres(); 35 | writeResponse(res, `added ${genres.length} genres`); 36 | } catch (e) { 37 | next(e); 38 | } 39 | }; 40 | /** 41 | * @swagger 42 | * /data/movies: 43 | * post: 44 | * tags: 45 | * - data 46 | * description: Set 50 movies 47 | * produces: 48 | * - application/json 49 | * responses: 50 | * 200: 51 | * description: "added 50 movies" 52 | */ 53 | exports.setMovies = async function (_, res, next) { 54 | try { 55 | await setMovies(); 56 | writeResponse(res, `added ${movies.length} movies`); 57 | } catch (e) { 58 | next(e); 59 | } 60 | }; 61 | /** 62 | * @swagger 63 | * /data/directors: 64 | * post: 65 | * tags: 66 | * - data 67 | * description: Set directors and their relations to movies 68 | * produces: 69 | * - application/json 70 | * responses: 71 | * 200: 72 | * description: "added 54 directors with relations [DIRECTED] to their movies" 73 | */ 74 | exports.setDirectorsWithRelationshipToMovie = async function (_, res, next) { 75 | try { 76 | await setDirectorsWithRelationshipToMovie(); 77 | writeResponse( 78 | res, 79 | `added ${directors.length} directors with relations [DIRECTED] to their movies`, 80 | ); 81 | } catch (e) { 82 | next(e); 83 | } 84 | }; 85 | /** 86 | * @swagger 87 | * /data/actors: 88 | * post: 89 | * tags: 90 | * - data 91 | * description: Set actors and their relations to movies 92 | * produces: 93 | * - application/json 94 | * responses: 95 | * 200: 96 | * description: "added 192 actors with relations [ACTED_IN_MOVIE] to their movies" 97 | */ 98 | exports.setActorsWithRelationshipToMovie = async function (_, res, next) { 99 | try { 100 | await setActorsWithRelationshipToMovie(); 101 | writeResponse( 102 | res, 103 | `added ${actors.length} actors with relations [ACTED_IN_MOVIE] to their movies`, 104 | ); 105 | } catch (e) { 106 | next(e); 107 | } 108 | }; 109 | /** 110 | * @swagger 111 | * /data/movies_genres: 112 | * post: 113 | * tags: 114 | * - data 115 | * description: Set genres relations to movies 116 | * produces: 117 | * - application/json 118 | * responses: 119 | * 200: 120 | * description: "added 115 relationships between movies and genres" 121 | */ 122 | exports.setGenresToMovies = async function (_, res, next) { 123 | try { 124 | await setGenresToMovies(); 125 | writeResponse( 126 | res, 127 | `added ${moviesWithGenres.length} relationships between movies and genres`, 128 | ); 129 | } catch (e) { 130 | next(e); 131 | } 132 | }; 133 | -------------------------------------------------------------------------------- /swaggerui/lib/jquery.ba-bbq.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010 3 | * http://benalman.com/projects/jquery-bbq-plugin/ 4 | * 5 | * Copyright (c) 2010 "Cowboy" Ben Alman 6 | * Dual licensed under the MIT and GPL licenses. 7 | * http://benalman.com/about/license/ 8 | */ 9 | (function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this); -------------------------------------------------------------------------------- /controllers/users.js: -------------------------------------------------------------------------------- 1 | const Users = require('../models/actions/users'); 2 | const {writeResponse} = require('../helpers/response'); 3 | const loginRequired = require('../middlewares/loginRequired'); 4 | const dbUtils = require('../db/dbUtils'); 5 | 6 | /** 7 | * @swagger 8 | * definition: 9 | * User: 10 | * type: object 11 | * properties: 12 | * id: 13 | * type: string 14 | * username: 15 | * type: string 16 | * avatar: 17 | * type: object 18 | */ 19 | 20 | /** 21 | * @swagger 22 | * /auth/register: 23 | * post: 24 | * tags: 25 | * - auth 26 | * description: Register a new user 27 | * produces: 28 | * - application/json 29 | * parameters: 30 | * - name: body 31 | * in: body 32 | * type: object 33 | * schema: 34 | * properties: 35 | * username: 36 | * type: string 37 | * password: 38 | * type: string 39 | * responses: 40 | * 201: 41 | * description: Your new user 42 | * schema: 43 | * $ref: '#/definitions/User' 44 | * 400: 45 | * description: Error message(s) 46 | */ 47 | exports.register = function (req, res, next) { 48 | const {username} = req.body; 49 | const {password} = req.body; 50 | 51 | if (!username) { 52 | throw {username: 'This field is required.', status: 400}; 53 | } 54 | if (!password) { 55 | throw {password: 'This field is required.', status: 400}; 56 | } 57 | 58 | Users.register(dbUtils.getSession(), username, password) 59 | .then((response) => writeResponse(res, response, 201)) 60 | .catch(next); 61 | }; 62 | 63 | /** 64 | * @swagger 65 | * /auth/login: 66 | * post: 67 | * tags: 68 | * - auth 69 | * description: Login 70 | * produces: 71 | * - application/json 72 | * parameters: 73 | * - name: body 74 | * in: body 75 | * type: object 76 | * schema: 77 | * properties: 78 | * username: 79 | * type: string 80 | * password: 81 | * type: string 82 | * responses: 83 | * 200: 84 | * description: succesful login 85 | * schema: 86 | * properties: 87 | * token: 88 | * type: string 89 | * 400: 90 | * description: invalid credentials 91 | */ 92 | exports.login = function (req, res, next) { 93 | const {username, password} = req.body; 94 | 95 | if (!username) { 96 | throw {username: 'This field is required.', status: 400}; 97 | } 98 | if (!password) { 99 | throw {password: 'This field is required.', status: 400}; 100 | } 101 | 102 | Users.login(dbUtils.getSession(), username, password) 103 | .then((response) => writeResponse(res, response)) 104 | .catch(next); 105 | }; 106 | 107 | /** 108 | * @swagger 109 | * /people/me: 110 | * get: 111 | * tags: 112 | * - people 113 | * description: Get your user 114 | * produces: 115 | * - application/json 116 | * parameters: 117 | * - name: Authorization 118 | * in: header 119 | * type: string 120 | * required: true 121 | * description: Token (token goes here) 122 | * responses: 123 | * 200: 124 | * description: the user 125 | * schema: 126 | * $ref: '#/definitions/User' 127 | * 401: 128 | * description: invalid / missing authentication 129 | */ 130 | exports.me = function (req, res, next) { 131 | loginRequired(req, res, () => { 132 | const authHeader = req.headers.authorization; 133 | const match = authHeader.match(/^Token (\S+)/); 134 | if (!match || !match[1]) { 135 | throw { 136 | message: 'invalid authorization format. Follow `Token `', 137 | status: 401, 138 | }; 139 | } 140 | 141 | const token = match[1]; 142 | Users.me(dbUtils.getSession(), token) 143 | .then((response) => writeResponse(res, response)) 144 | .catch(next); 145 | }); 146 | }; 147 | -------------------------------------------------------------------------------- /client/src/components/detailsOfFilm/detailsItem/detailsItem.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import './detailsItem.css'; 3 | import defaultPerson from '../../../images/defaultPerson.png'; 4 | import {Modal} from '../../modal'; 5 | import {RateFilm} from '../rateFilm'; 6 | import defaultFilm from '../../../images/defaultFilm.png'; 7 | 8 | export const DetailsItem = ({ 9 | film, 10 | handleGetIdActors, 11 | handleGetIdDirectors, 12 | }) => { 13 | const [active, setActive] = useState(false); 14 | 15 | return ( 16 |
17 |
18 |
19 | {!film.poster ? ( 20 | defaultFilm 21 | ) : ( 22 | {film.title} 23 | )} 24 |
25 |
26 |
27 |

{film.title}

28 |

Year: {film.year}.

29 |

30 | Countries:{' '} 31 | {film.countries !== undefined && 32 | film.countries.map((value, index) => 33 | index === film.countries.length - 1 34 | ? value + '.' 35 | : value + ', ', 36 | )} 37 |

38 |

39 | Genres:{' '} 40 | {film.genres !== undefined && 41 | film.genres.map((value, index) => 42 | index === film.genres.length - 1 43 | ? value.name + '.' 44 | : value.name + ', ', 45 | )} 46 |

47 |

Film duration: {film.runtime} min.

48 |

49 | Actors:{' '} 50 | {film.actors !== undefined && 51 | film.actors.map((value, index) => 52 | index === film.actors.length - 1 53 | ? value.properties.name + '.' 54 | : value.properties.name + ', ', 55 | )} 56 |

57 |

{film.tagline}

58 | Rating of film: {film.imdbRating} of 10 59 |
60 |
63 | . 64 |
65 |
66 |
67 | 68 | 69 | 70 | 71 |
72 |
73 |
74 |
75 |

Actors and founders:

76 |
77 | {film.directors !== undefined && 78 | film.directors.map((value, index) => { 79 | return ( 80 |
handleGetIdDirectors(value.tmdbId)}> 84 |
85 | {!value.poster ? ( 86 | defaultPerson 87 | ) : ( 88 | {value.name} 89 | )} 90 |
91 |
92 |

{value.name}

93 |
94 |
95 | ); 96 | })} 97 | {film.actors !== undefined && 98 | film.actors.map((value, index) => { 99 | return ( 100 |
handleGetIdActors(value.properties.tmdbId)}> 104 |
105 | {!value.properties.poster ? ( 106 | defaultPerson 107 | ) : ( 108 | {value.properties.name} 112 | )} 113 |
114 |
115 |

{value.properties.name}

116 |
117 |
118 | ); 119 | })} 120 |
121 |
122 | ); 123 | }; 124 | -------------------------------------------------------------------------------- /swaggerui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swagger UI 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 89 | 90 | 91 | 92 | 102 | 103 |
 
104 |
105 | 106 | 107 | -------------------------------------------------------------------------------- /client/src/components/modal/modal.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './modal.css'; 3 | 4 | export const Modal = ({active, setActive, children}) => { 5 | return ( 6 |
setActive(false)}> 9 |
e.stopPropagation()}> 12 | setActive(false)} 16 | /> 17 | {children} 18 |
19 |
20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /swaggerui/css/style.css: -------------------------------------------------------------------------------- 1 | .swagger-section #header a#logo { 2 | font-size: 1.5em; 3 | font-weight: bold; 4 | text-decoration: none; 5 | background: transparent url(../images/logo.png) no-repeat left center; 6 | padding: 20px 0 20px 40px; 7 | } 8 | #text-head { 9 | font-size: 80px; 10 | font-family: 'Roboto', sans-serif; 11 | color: #ffffff; 12 | float: right; 13 | margin-right: 20%; 14 | } 15 | .navbar-fixed-top .navbar-nav { 16 | height: auto; 17 | } 18 | .navbar-fixed-top .navbar-brand { 19 | height: auto; 20 | } 21 | .navbar-header { 22 | height: auto; 23 | } 24 | .navbar-inverse { 25 | background-color: #000; 26 | border-color: #000; 27 | } 28 | #navbar-brand { 29 | margin-left: 20%; 30 | } 31 | .navtext { 32 | font-size: 10px; 33 | } 34 | .h1, 35 | h1 { 36 | font-size: 60px; 37 | } 38 | .navbar-default .navbar-header .navbar-brand { 39 | color: #a2dfee; 40 | } 41 | /* tag titles */ 42 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a { 43 | color: #393939; 44 | font-family: 'Arvo', serif; 45 | font-size: 1.5em; 46 | } 47 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover { 48 | color: black; 49 | } 50 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 { 51 | color: #525252; 52 | padding-left: 0px; 53 | display: block; 54 | clear: none; 55 | float: left; 56 | font-family: 'Arvo', serif; 57 | font-weight: bold; 58 | } 59 | .navbar-default .navbar-collapse, 60 | .navbar-default .navbar-form { 61 | border-color: #0A0A0A; 62 | } 63 | .container1 { 64 | width: 1500px; 65 | margin: auto; 66 | margin-top: 0; 67 | background-image: url('../images/shield.png'); 68 | background-repeat: no-repeat; 69 | background-position: -40px -20px; 70 | margin-bottom: 210px; 71 | } 72 | .container-inner { 73 | width: 1200px; 74 | margin: auto; 75 | background-color: rgba(223, 227, 228, 0.75); 76 | padding-bottom: 40px; 77 | padding-top: 40px; 78 | border-radius: 15px; 79 | } 80 | .header-content { 81 | padding: 0; 82 | width: 1000px; 83 | } 84 | .title1 { 85 | font-size: 80px; 86 | font-family: 'Vollkorn', serif; 87 | color: #404040; 88 | text-align: center; 89 | padding-top: 40px; 90 | padding-bottom: 100px; 91 | } 92 | #icon { 93 | margin-top: -18px; 94 | } 95 | .subtext { 96 | font-size: 25px; 97 | font-style: italic; 98 | color: #08b; 99 | text-align: right; 100 | padding-right: 250px; 101 | } 102 | .bg-primary { 103 | background-color: #00468b; 104 | } 105 | .navbar-default .nav > li > a, 106 | .navbar-default .nav > li > a:focus { 107 | color: #08b; 108 | } 109 | .navbar-default .nav > li > a, 110 | .navbar-default .nav > li > a:hover { 111 | color: #08b; 112 | } 113 | .navbar-default .nav > li > a, 114 | .navbar-default .nav > li > a:focus:hover { 115 | color: #08b; 116 | } 117 | .text-faded { 118 | font-size: 25px; 119 | font-family: 'Vollkorn', serif; 120 | } 121 | .section-heading { 122 | font-family: 'Vollkorn', serif; 123 | font-size: 45px; 124 | padding-bottom: 10px; 125 | } 126 | hr { 127 | border-color: #00468b; 128 | padding-bottom: 10px; 129 | } 130 | .description { 131 | margin-top: 20px; 132 | padding-bottom: 200px; 133 | } 134 | .description li { 135 | font-family: 'Vollkorn', serif; 136 | font-size: 25px; 137 | color: #525252; 138 | margin-left: 28%; 139 | padding-top: 5px; 140 | } 141 | .gap { 142 | margin-top: 200px; 143 | } 144 | .troubleshootingtext { 145 | color: rgba(255, 255, 255, 0.7); 146 | padding-left: 30%; 147 | } 148 | .troubleshootingtext li { 149 | list-style-type: circle; 150 | font-size: 25px; 151 | padding-bottom: 5px; 152 | } 153 | .overlay { 154 | position: absolute; 155 | top: 0; 156 | left: 0; 157 | width: 100%; 158 | height: 100%; 159 | z-index: 1000; 160 | } 161 | .block.response_body.json:hover { 162 | cursor: pointer; 163 | } 164 | .backdrop { 165 | color: blue; 166 | } 167 | #myModal { 168 | height: 100%; 169 | } 170 | .modal-backdrop { 171 | bottom: 0; 172 | position: fixed; 173 | } 174 | .curl { 175 | padding: 10px; 176 | font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; 177 | font-size: 0.9em; 178 | max-height: 400px; 179 | margin-top: 5px; 180 | overflow-y: auto; 181 | background-color: #fcf6db; 182 | border: 1px solid #e5e0c6; 183 | border-radius: 4px; 184 | } 185 | .curl_title { 186 | font-size: 1.1em; 187 | margin: 0; 188 | padding: 15px 0 5px; 189 | font-family: 'Open Sans', 'Helvetica Neue', Arial, sans-serif; 190 | font-weight: 500; 191 | line-height: 1.1; 192 | } 193 | .footer { 194 | display: none; 195 | } 196 | .swagger-section .swagger-ui-wrap h2 { 197 | padding: 0; 198 | } 199 | h2 { 200 | margin: 0; 201 | margin-bottom: 5px; 202 | } 203 | .markdown p { 204 | font-size: 15px; 205 | font-family: 'Arvo', serif; 206 | } 207 | .swagger-section .swagger-ui-wrap .code { 208 | font-size: 15px; 209 | font-family: 'Arvo', serif; 210 | } 211 | .swagger-section .swagger-ui-wrap b { 212 | font-family: 'Arvo', serif; 213 | } 214 | #signin:hover { 215 | cursor: pointer; 216 | } 217 | .dropdown-menu { 218 | padding: 15px; 219 | } 220 | .navbar-right .dropdown-menu { 221 | left: 0; 222 | right: auto; 223 | } 224 | #signinbutton { 225 | width: 100%; 226 | height: 32px; 227 | font-size: 13px; 228 | font-weight: bold; 229 | color: #08b; 230 | } 231 | .navbar-default .nav > li .details { 232 | color: #000000; 233 | text-transform: none; 234 | font-size: 15px; 235 | font-weight: normal; 236 | font-family: 'Open Sans', sans-serif; 237 | font-style: italic; 238 | line-height: 20px; 239 | top: -2px; 240 | } 241 | .navbar-default .nav > li .details:hover { 242 | color: black; 243 | } 244 | #signout { 245 | width: 100%; 246 | height: 32px; 247 | font-size: 13px; 248 | font-weight: bold; 249 | color: #08b; 250 | } 251 | -------------------------------------------------------------------------------- /data/moviesWithGenres.js: -------------------------------------------------------------------------------- 1 | exports.moviesWithGenres = [ 2 | {movieTitle: 'Forrest Gump', genre: 'Romance'}, 3 | {movieTitle: 'Forrest Gump', genre: 'Drama'}, 4 | {movieTitle: 'Toy Story', genre: 'Adventure'}, 5 | {movieTitle: 'Toy Story', genre: 'Animation'}, 6 | {movieTitle: 'Toy Story', genre: 'Children'}, 7 | {movieTitle: 'Toy Story', genre: 'Comedy'}, 8 | {movieTitle: 'Toy Story', genre: 'Fantasy'}, 9 | {movieTitle: 'Jumanji', genre: 'Adventure'}, 10 | {movieTitle: 'Jumanji', genre: 'Children'}, 11 | {movieTitle: 'Jumanji', genre: 'Fantasy'}, 12 | {movieTitle: 'Grumpier Old Men', genre: 'Comedy'}, 13 | {movieTitle: 'Grumpier Old Men', genre: 'Romance'}, 14 | {movieTitle: 'Waiting to Exhale', genre: 'Romance'}, 15 | {movieTitle: 'Waiting to Exhale', genre: 'Drama'}, 16 | {movieTitle: 'Waiting to Exhale', genre: 'Comedy'}, 17 | {movieTitle: 'Father of the Bride Part II', genre: 'Comedy'}, 18 | {movieTitle: 'Heat', genre: 'Action'}, 19 | {movieTitle: 'Heat', genre: 'Crime'}, 20 | {movieTitle: 'Heat', genre: 'Thriller'}, 21 | {movieTitle: 'Sabrina', genre: 'Comedy'}, 22 | {movieTitle: 'Sabrina', genre: 'Romance'}, 23 | {movieTitle: 'Tom and Huck', genre: 'Children'}, 24 | {movieTitle: 'Tom and Huck', genre: 'Adventure'}, 25 | {movieTitle: 'Sudden Death', genre: 'Action'}, 26 | {movieTitle: 'GoldenEye', genre: 'Adventure'}, 27 | {movieTitle: 'GoldenEye', genre: 'Action'}, 28 | {movieTitle: 'GoldenEye', genre: 'Thriller'}, 29 | {movieTitle: 'American President, The', genre: 'Comedy'}, 30 | {movieTitle: 'American President, The', genre: 'Romance'}, 31 | {movieTitle: 'American President, The', genre: 'Drama'}, 32 | {movieTitle: 'Dracula: Dead and Loving It', genre: 'Comedy'}, 33 | {movieTitle: 'Dracula: Dead and Loving It', genre: 'Horror'}, 34 | {movieTitle: 'Balto', genre: 'Children'}, 35 | {movieTitle: 'Balto', genre: 'Adventure'}, 36 | {movieTitle: 'Balto', genre: 'Animation'}, 37 | {movieTitle: 'Nixon', genre: 'Drama'}, 38 | {movieTitle: 'Cutthroat Island', genre: 'Romance'}, 39 | {movieTitle: 'Cutthroat Island', genre: 'Adventure'}, 40 | {movieTitle: 'Cutthroat Island', genre: 'Action'}, 41 | {movieTitle: 'Casino', genre: 'Drama'}, 42 | {movieTitle: 'Casino', genre: 'Crime'}, 43 | {movieTitle: 'Sense and Sensibility', genre: 'Drama'}, 44 | {movieTitle: 'Sense and Sensibility', genre: 'Romance'}, 45 | {movieTitle: 'Four Rooms', genre: 'Comedy'}, 46 | {movieTitle: 'Ace Ventura: When Nature Calls', genre: 'Comedy'}, 47 | {movieTitle: 'Money Train', genre: 'Crime'}, 48 | {movieTitle: 'Money Train', genre: 'Comedy'}, 49 | {movieTitle: 'Money Train', genre: 'Thriller'}, 50 | {movieTitle: 'Money Train', genre: 'Drama'}, 51 | {movieTitle: 'Money Train', genre: 'Action'}, 52 | {movieTitle: 'Get Shorty', genre: 'Thriller'}, 53 | {movieTitle: 'Get Shorty', genre: 'Crime'}, 54 | {movieTitle: 'Get Shorty', genre: 'Comedy'}, 55 | {movieTitle: 'Copycat', genre: 'Drama'}, 56 | {movieTitle: 'Copycat', genre: 'Horror'}, 57 | {movieTitle: 'Copycat', genre: 'Mystery'}, 58 | {movieTitle: 'Copycat', genre: 'Thriller'}, 59 | {movieTitle: 'Copycat', genre: 'Crime'}, 60 | {movieTitle: 'Assassins', genre: 'Action'}, 61 | {movieTitle: 'Assassins', genre: 'Crime'}, 62 | {movieTitle: 'Assassins', genre: 'Thriller'}, 63 | {movieTitle: 'Powder', genre: 'Sci-Fi'}, 64 | {movieTitle: 'Powder', genre: 'Drama'}, 65 | {movieTitle: 'Leaving Las Vegas', genre: 'Romance'}, 66 | {movieTitle: 'Leaving Las Vegas', genre: 'Drama'}, 67 | {movieTitle: 'Othello', genre: 'Drama'}, 68 | {movieTitle: 'Now and Then', genre: 'Drama'}, 69 | {movieTitle: 'Now and Then', genre: 'Children'}, 70 | {movieTitle: 'Persuasion', genre: 'Romance'}, 71 | {movieTitle: 'Persuasion', genre: 'Drama'}, 72 | { 73 | movieTitle: 'City of Lost Children, The (Cité des enfants perdus, La)', 74 | genre: 'Adventure', 75 | }, 76 | { 77 | movieTitle: 'City of Lost Children, The (Cité des enfants perdus, La)', 78 | genre: 'Drama', 79 | }, 80 | { 81 | movieTitle: 'City of Lost Children, The (Cité des enfants perdus, La)', 82 | genre: 'Fantasy', 83 | }, 84 | { 85 | movieTitle: 'City of Lost Children, The (Cité des enfants perdus, La)', 86 | genre: 'Mystery', 87 | }, 88 | { 89 | movieTitle: 'City of Lost Children, The (Cité des enfants perdus, La)', 90 | genre: 'Sci-Fi', 91 | }, 92 | {movieTitle: 'Shanghai Triad (Yao a yao yao dao waipo qiao)', genre: 'Drama'}, 93 | {movieTitle: 'Shanghai Triad (Yao a yao yao dao waipo qiao)', genre: 'Crime'}, 94 | {movieTitle: 'Dangerous Minds', genre: 'Drama'}, 95 | {movieTitle: 'Twelve Monkeys (a.k.a. 12 Monkeys)', genre: 'Mystery'}, 96 | {movieTitle: 'Twelve Monkeys (a.k.a. 12 Monkeys)', genre: 'Sci-Fi'}, 97 | {movieTitle: 'Twelve Monkeys (a.k.a. 12 Monkeys)', genre: 'Thriller'}, 98 | {movieTitle: 'Babe', genre: 'Children'}, 99 | {movieTitle: 'Babe', genre: 'Drama'}, 100 | {movieTitle: 'Carrington', genre: 'Romance'}, 101 | {movieTitle: 'Carrington', genre: 'Drama'}, 102 | {movieTitle: 'Dead Man Walking', genre: 'Drama'}, 103 | {movieTitle: 'Dead Man Walking', genre: 'Crime'}, 104 | {movieTitle: 'Across the Sea of Time', genre: 'IMAX'}, 105 | {movieTitle: 'Across the Sea of Time', genre: 'Documentary'}, 106 | {movieTitle: 'It Takes Two', genre: 'Comedy'}, 107 | {movieTitle: 'It Takes Two', genre: 'Children'}, 108 | {movieTitle: 'Clueless', genre: 'Comedy'}, 109 | {movieTitle: 'Clueless', genre: 'Romance'}, 110 | {movieTitle: 'Cry, the Beloved Country', genre: 'Drama'}, 111 | {movieTitle: 'Richard III', genre: 'Drama'}, 112 | {movieTitle: 'Richard III', genre: 'War'}, 113 | {movieTitle: 'Dead Presidents', genre: 'Drama'}, 114 | {movieTitle: 'Dead Presidents', genre: 'Action'}, 115 | {movieTitle: 'Dead Presidents', genre: 'Crime'}, 116 | {movieTitle: 'Restoration', genre: 'Drama'}, 117 | {movieTitle: 'Mortal Kombat', genre: 'Adventure'}, 118 | {movieTitle: 'Mortal Kombat', genre: 'Action'}, 119 | {movieTitle: 'Mortal Kombat', genre: 'Fantasy'}, 120 | {movieTitle: 'To Die For', genre: 'Drama'}, 121 | {movieTitle: 'To Die For', genre: 'Thriller'}, 122 | {movieTitle: 'To Die For', genre: 'Comedy'}, 123 | {movieTitle: 'How to Make an American Quilt', genre: 'Romance'}, 124 | {movieTitle: 'How to Make an American Quilt', genre: 'Drama'}, 125 | {movieTitle: 'Seven (a.k.a. Se7en)', genre: 'Mystery'}, 126 | {movieTitle: 'Seven (a.k.a. Se7en)', genre: 'Thriller'}, 127 | {movieTitle: 'Pocahontas', genre: 'Drama'}, 128 | {movieTitle: 'Pocahontas', genre: 'Musical'}, 129 | {movieTitle: 'Pocahontas', genre: 'Animation'}, 130 | {movieTitle: 'Pocahontas', genre: 'Children'}, 131 | {movieTitle: 'Pocahontas', genre: 'Romance'}, 132 | {movieTitle: 'When Night Is Falling', genre: 'Drama'}, 133 | {movieTitle: 'When Night Is Falling', genre: 'Romance'}, 134 | ]; 135 | -------------------------------------------------------------------------------- /models/actions/movies.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const Movie = require('../redis/movie'); 3 | const Person = require('../redis/person'); 4 | const Genre = require('../redis/genre'); 5 | 6 | const _singleMovieWithDetails = function (movie) { 7 | if (movie._values.length) { 8 | const result = {}; 9 | _.extend(result, new Movie(movie.get('movie'), movie.get('my_rating'))); 10 | 11 | result.directors = _.map( 12 | movie.get('directors'), 13 | (record) => new Person(record), 14 | ); 15 | result.genres = _.map(movie.get('genres'), (record) => new Genre(record)); 16 | result.producers = _.map( 17 | movie.get('producers'), 18 | (record) => new Person(record), 19 | ); 20 | result.writers = _.map( 21 | movie.get('writers'), 22 | (record) => new Person(record), 23 | ); 24 | result.actors = _.map(movie.get('actors'), (record) => record); 25 | result.related = _.map(movie.get('related'), (record) => new Movie(record)); 26 | return result; 27 | } 28 | return null; 29 | }; 30 | 31 | /** 32 | * Query Functions 33 | */ 34 | 35 | function manyMovies(listOfMovies) { 36 | const movies = { 37 | movies: listOfMovies._results.map((r) => new Movie(r.get('movie'))), 38 | }; 39 | 40 | if (listOfMovies._results[0]) { 41 | Object.assign(movies, {actor: listOfMovies._results[0].get('actor')}); 42 | Object.assign(movies, {director: listOfMovies._results[0].get('director')}); 43 | } 44 | 45 | return movies; 46 | } 47 | 48 | // get all movies 49 | const getAll = function (session) { 50 | return session 51 | .query('MATCH (movie:Movie) RETURN movie') 52 | .then((r) => manyMovies(r)); 53 | }; 54 | 55 | // get a single movie by id 56 | const getById = function (session, movieId, userId) { 57 | if (!userId) throw {message: 'invalid authorization key', status: 401}; 58 | const query = [ 59 | 'MATCH (movie:Movie {tmdbId: $movieId})\n' + 60 | ' OPTIONAL MATCH (movie)<-[my_rated:RATED]-(me:User {id: $userId})\n' + 61 | ' OPTIONAL MATCH (movie)<-[r:ACTED_IN_MOVIE]-(a:Actor)\n' + 62 | ' OPTIONAL MATCH (movie)-[:IN_GENRE]->(genre:Genre)\n' + 63 | ' OPTIONAL MATCH (movie)<-[:DIRECTED]-(d:Director)\n' + 64 | ' WITH DISTINCT movie, my_rated, genre, d, a, r\n' + 65 | ' RETURN DISTINCT movie,\n' + 66 | ' collect(DISTINCT d) AS directors,\n' + 67 | ' collect(DISTINCT a) AS actors,\n' + 68 | ' collect(DISTINCT genre) AS genres', 69 | ].join(' '); 70 | return session 71 | .query(query, { 72 | movieId: movieId.toString(), 73 | userId: userId.toString(), 74 | }) 75 | .then((result) => { 76 | if (result.hasNext()) { 77 | return _singleMovieWithDetails(result.next()); 78 | } 79 | throw {message: 'movie not found', status: 404}; 80 | }); 81 | }; 82 | 83 | // Get by date range 84 | const getByDateRange = function (session, start, end) { 85 | const query = [ 86 | 'MATCH (movie:Movie)', 87 | 'WHERE movie.released > $start AND movie.released < $end', 88 | 'RETURN movie', 89 | ].join('\n'); 90 | 91 | return session 92 | .query(query, { 93 | start: parseInt(start || 0, 10), 94 | end: parseInt(end || 0, 10), 95 | }) 96 | .then((result) => manyMovies(result.next())); 97 | }; 98 | 99 | // Get by date range 100 | const getByActor = function (session, id) { 101 | const query = [ 102 | 'MATCH (actor:Actor {tmdbId: $id})-[:ACTED_IN_MOVIE]->(movie:Movie)', 103 | 'RETURN DISTINCT movie,actor', 104 | ].join('\n'); 105 | 106 | return session 107 | .query(query, { 108 | id, 109 | }) 110 | .then((result) => manyMovies(result)); 111 | }; 112 | 113 | // get a movie by genre 114 | const getByGenre = function (session, genreId) { 115 | const query = [ 116 | 'MATCH (movie:Movie)-[:IN_GENRE]->(genre)', 117 | 'WHERE toLower(genre.name) = toLower($genreId) OR id(genre) = toInteger($genreId)', 118 | 'RETURN movie', 119 | ].join('\n'); 120 | 121 | return session 122 | .query(query, { 123 | genreId, 124 | }) 125 | .then((result) => manyMovies(result)); 126 | }; 127 | 128 | // Get many movies directed by a person 129 | const getByDirector = function (session, personId) { 130 | const query = [ 131 | 'MATCH (director:Director {tmdbId: $personId})-[:DIRECTED]->(movie:Movie)', 132 | 'RETURN DISTINCT movie,director', 133 | ].join('\n'); 134 | 135 | return session 136 | .query(query, { 137 | personId, 138 | }) 139 | .then((result) => manyMovies(result)); 140 | }; 141 | 142 | // Get many movies written by a person 143 | const getByWriter = function (session, personId) { 144 | const query = [ 145 | 'MATCH (:Writer {tmdbId: $personId})-[:WRITER_OF]->(movie:Movie)', 146 | 'RETURN DISTINCT movie', 147 | ].join('\n'); 148 | 149 | return session 150 | .query(query, { 151 | personId, 152 | }) 153 | .then((result) => manyMovies(result)); 154 | }; 155 | 156 | const rate = function (session, movieId, userId, rating) { 157 | return session.query( 158 | 'MATCH (u:User {id: $userId}),(m:Movie {tmdbId: $movieId}) \ 159 | MERGE (u)-[r:RATED]->(m) \ 160 | SET r.rating = $rating \ 161 | RETURN m', 162 | { 163 | userId, 164 | movieId, 165 | rating: parseInt(rating, 10), 166 | }, 167 | ); 168 | }; 169 | 170 | const deleteRating = function (session, movieId, userId) { 171 | return session.query( 172 | 'MATCH (u:User {id: $userId})-[r:RATED]->(m:Movie {tmdbId: $movieId}) DELETE r', 173 | {userId, movieId}, 174 | ); 175 | }; 176 | 177 | const getRatedByUser = function (session, userId) { 178 | return session 179 | .query( 180 | 'MATCH (:User {id: $userId})-[rated:RATED]->(movie:Movie) \ 181 | RETURN DISTINCT movie, rated.rating as my_rating', 182 | {userId}, 183 | ) 184 | .then((result) => 185 | result._results.map((r) => new Movie(r.get('movie'), r.get('my_rating'))), 186 | ); 187 | }; 188 | 189 | const getRecommended = function (session, userId) { 190 | return session 191 | .query( 192 | 'MATCH (me:User {id: $userId})-[my:RATED]->(m:Movie) \ 193 | MATCH (other:User)-[their:RATED]->(m) \ 194 | WHERE me <> other \ 195 | AND abs(my.rating - their.rating) < 2 \ 196 | WITH other,m \ 197 | MATCH (other)-[otherRating:RATED]->(movie:Movie) \ 198 | WHERE movie <> m \ 199 | WITH avg(otherRating.rating) AS avgRating, movie \ 200 | RETURN movie \ 201 | ORDER BY avgRating desc \ 202 | LIMIT 25', 203 | {userId}, 204 | ) 205 | .then((result) => manyMovies(result)); 206 | }; 207 | 208 | // export exposed functions 209 | module.exports = { 210 | getAll, 211 | getById, 212 | getByDateRange, 213 | getByActor, 214 | getByGenre, 215 | getMoviesbyDirector: getByDirector, 216 | getMoviesByWriter: getByWriter, 217 | rate, 218 | deleteRating, 219 | getRatedByUser, 220 | getRecommended, 221 | }; 222 | -------------------------------------------------------------------------------- /db/initialize.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const {getSession} = require('./dbUtils'); 3 | const Genres = require('../models/actions/genres'); 4 | const { 5 | ACTORS: {actors}, 6 | DIRECTORS_OF_FILMS: {directors}, 7 | GENRES: {genres}, 8 | MOVIES: {movies}, 9 | MOVIES_WITH_GENRES: {moviesWithGenres}, 10 | } = require('../data'); 11 | 12 | const addGenres = async () => { 13 | const session = getSession(); 14 | for (const genre of genres) { 15 | /** @ts-ignore */ 16 | await session.query('create (g:Genre{name:$genre}) ', genre); 17 | } 18 | }; 19 | 20 | const setMovies = async () => { 21 | const session = getSession(); 22 | 23 | movies.forEach((movie) => { 24 | for (const field in movie) { 25 | if (movie[field].low) movie[field] = movie[field].low; 26 | } 27 | }); 28 | 29 | for (const { 30 | url, 31 | id, 32 | languages, 33 | title, 34 | countries, 35 | budget, 36 | duration, 37 | imdbId, 38 | imdbRating, 39 | imdbVotes, 40 | movieId, 41 | plot, 42 | poster, 43 | poster_image, 44 | released, 45 | revenue, 46 | runtime, 47 | tagline, 48 | tmdbId, 49 | year, 50 | } of movies) { 51 | await session.query( 52 | 'create (m:Movie {url: $url,' + 53 | 'id:$id, ' + 54 | 'languages:$languages,' + 55 | ' title:$title,' + 56 | ' countries:$countries,' + 57 | ' budget:$budget, ' + 58 | 'duration:$duration,' + 59 | ' imdbId:$imdbId, ' + 60 | 'imdbRating:$imdbRating,' + 61 | ' imdbVotes:$imdbVotes, ' + 62 | 'movieId:$movieId, ' + 63 | 'plot:$plot, ' + 64 | 'poster:$poster,' + 65 | ' poster_image:$poster_image, ' + 66 | 'released:$released, ' + 67 | 'revenue:$revenue,' + 68 | ' runtime:$runtime,' + 69 | ' tagline:$tagline, ' + 70 | 'tmdbId:$tmdbId, ' + 71 | 'year:$year})', 72 | { 73 | /** @ts-ignore */ 74 | url, 75 | id, 76 | languages, 77 | title, 78 | countries, 79 | budget, 80 | duration, 81 | imdbId, 82 | imdbRating, 83 | imdbVotes, 84 | movieId, 85 | plot, 86 | poster, 87 | poster_image, 88 | released, 89 | revenue, 90 | runtime, 91 | tagline, 92 | tmdbId, 93 | year, 94 | }, 95 | ); 96 | } 97 | }; 98 | 99 | const setDirectorsWithRelationshipToMovie = async () => { 100 | const session = getSession(); 101 | 102 | directors.forEach((director) => { 103 | const {born, died} = director.director.properties; 104 | /** @ts-ignore */ 105 | director.director.properties.born = []; 106 | if (died) { 107 | /** @ts-ignore */ 108 | director.director.properties.died = []; 109 | } 110 | 111 | for (const field in born) { 112 | if (born) { 113 | director.director.properties.born.push(born[field].low); 114 | } 115 | 116 | if (died) { 117 | director.director.properties.died.push(died[field].low); 118 | } 119 | } 120 | }); 121 | 122 | for (const { 123 | director: { 124 | properties: {bio, born, bornIn, imdbId, name, poster, tmdbId, url}, 125 | }, 126 | movieTitle, 127 | } of directors) { 128 | await session.query( 129 | 'MATCH (m:Movie) ' + 130 | 'WHERE m.title=$movieTitle ' + 131 | 'CREATE (d:Director :Person' + 132 | '{' + 133 | 'bio:$bio,' + 134 | 'born:$born,' + 135 | 'bornIn:$bornIn,' + 136 | 'imdbId:$imdbId,' + 137 | 'name:$name,' + 138 | 'poster:$poster,' + 139 | 'tmdbId:$tmdbId,' + 140 | 'url:$url' + 141 | '}' + 142 | ')-[r:DIRECTED]->(m)', 143 | { 144 | /** @ts-ignore */ 145 | bio, 146 | born, 147 | bornIn, 148 | imdbId, 149 | name, 150 | poster, 151 | tmdbId, 152 | url, 153 | movieTitle, 154 | }, 155 | ); 156 | } 157 | }; 158 | 159 | const setActorsWithRelationshipToMovie = async () => { 160 | const session = getSession(); 161 | 162 | actors.forEach((actor) => { 163 | const {born, died} = actor.actor.properties; 164 | /** @ts-ignore */ 165 | actor.actor.properties.born = []; 166 | /** @ts-ignore */ 167 | if (died) actor.actor.properties.died = []; 168 | 169 | for (const field in born) { 170 | if (born) { 171 | actor.actor.properties.born.push(born[field].low); 172 | } 173 | 174 | if (died) { 175 | actor.actor.properties.died.push(died[field].low); 176 | } 177 | } 178 | }); 179 | 180 | for (const { 181 | actor: { 182 | properties: {bio, born, bornIn, died, imdbId, name, poster, tmdbId, url}, 183 | }, 184 | movieTitle, 185 | role, 186 | } of actors) { 187 | await session.query( 188 | 'MATCH (m:Movie) ' + 189 | 'WHERE m.title=$movieTitle ' + 190 | 'CREATE (a:Actor :Person' + 191 | '{' + 192 | 'bio:$bio,' + 193 | 'born:$born,' + 194 | 'bornIn:$bornIn,' + 195 | 'died:$died,' + 196 | 'imdbId:$imdbId,' + 197 | 'name:$name,' + 198 | 'poster:$poster,' + 199 | 'tmdbId:$tmdbId,' + 200 | 'url:$url' + 201 | '}' + 202 | ')-[r:ACTED_IN_MOVIE {role: $role}]->(m)', 203 | { 204 | /** @ts-ignore */ 205 | bio, 206 | born, 207 | bornIn, 208 | died, 209 | imdbId, 210 | name, 211 | poster, 212 | tmdbId, 213 | url, 214 | movieTitle, 215 | role, 216 | }, 217 | ); 218 | } 219 | }; 220 | 221 | const setGenresToMovies = async () => { 222 | const session = getSession(); 223 | for (const {genre, movieTitle} of moviesWithGenres) { 224 | session.query( 225 | 'MATCH (g:Genre), (m:Movie)\n' + 226 | 'WHERE g.name = $genre AND m.title = $movieTitle\n' + 227 | 'CREATE (m)-[:IN_GENRE]->(g)\n', 228 | { 229 | // @ts-ignore 230 | genre, 231 | movieTitle, 232 | }, 233 | ); 234 | } 235 | }; 236 | 237 | const initialize = async () => { 238 | const genres = await Genres.getAll(getSession()); 239 | if (genres.length === 0) { 240 | console.log('Need to initialize'); 241 | await addGenres(); 242 | console.log('Added genres'); 243 | await setMovies(); 244 | console.log('Added movies'); 245 | await setDirectorsWithRelationshipToMovie(); 246 | console.log('Added relationships'); 247 | await setActorsWithRelationshipToMovie(); 248 | console.log('Added relationships2'); 249 | await setGenresToMovies(); 250 | console.log('Initialized'); 251 | } else { 252 | console.log("Doesn't need to initialize"); 253 | } 254 | }; 255 | 256 | module.exports = { 257 | initialize, 258 | addGenres, 259 | setMovies, 260 | setDirectorsWithRelationshipToMovie, 261 | setActorsWithRelationshipToMovie, 262 | setGenresToMovies, 263 | }; 264 | -------------------------------------------------------------------------------- /client/build/static/js/runtime-main.0733fe2d.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../webpack/bootstrap"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","executeModules","i","resolves","length","Object","prototype","hasOwnProperty","call","installedChunks","push","modules","parentJsonpFunction","shift","deferredModules","apply","checkDeferredModules","result","deferredModule","fulfilled","j","depId","splice","__webpack_require__","s","installedModules","1","exports","module","l","m","c","d","name","getter","o","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","p","jsonpArray","this","oldJsonpFunction","slice"],"mappings":"aACE,SAASA,EAAqBC,GAQ7B,IAPA,IAMIC,EAAUC,EANVC,EAAWH,EAAK,GAChBI,EAAcJ,EAAK,GACnBK,EAAiBL,EAAK,GAIHM,EAAI,EAAGC,EAAW,GACpCD,EAAIH,EAASK,OAAQF,IACzBJ,EAAUC,EAASG,GAChBG,OAAOC,UAAUC,eAAeC,KAAKC,EAAiBX,IAAYW,EAAgBX,IACpFK,EAASO,KAAKD,EAAgBX,GAAS,IAExCW,EAAgBX,GAAW,EAE5B,IAAID,KAAYG,EACZK,OAAOC,UAAUC,eAAeC,KAAKR,EAAaH,KACpDc,EAAQd,GAAYG,EAAYH,IAKlC,IAFGe,GAAqBA,EAAoBhB,GAEtCO,EAASC,QACdD,EAASU,OAATV,GAOD,OAHAW,EAAgBJ,KAAKK,MAAMD,EAAiBb,GAAkB,IAGvDe,IAER,SAASA,IAER,IADA,IAAIC,EACIf,EAAI,EAAGA,EAAIY,EAAgBV,OAAQF,IAAK,CAG/C,IAFA,IAAIgB,EAAiBJ,EAAgBZ,GACjCiB,GAAY,EACRC,EAAI,EAAGA,EAAIF,EAAed,OAAQgB,IAAK,CAC9C,IAAIC,EAAQH,EAAeE,GACG,IAA3BX,EAAgBY,KAAcF,GAAY,GAE3CA,IACFL,EAAgBQ,OAAOpB,IAAK,GAC5Be,EAASM,EAAoBA,EAAoBC,EAAIN,EAAe,KAItE,OAAOD,EAIR,IAAIQ,EAAmB,GAKnBhB,EAAkB,CACrBiB,EAAG,GAGAZ,EAAkB,GAGtB,SAASS,EAAoB1B,GAG5B,GAAG4B,EAAiB5B,GACnB,OAAO4B,EAAiB5B,GAAU8B,QAGnC,IAAIC,EAASH,EAAiB5B,GAAY,CACzCK,EAAGL,EACHgC,GAAG,EACHF,QAAS,IAUV,OANAhB,EAAQd,GAAUW,KAAKoB,EAAOD,QAASC,EAAQA,EAAOD,QAASJ,GAG/DK,EAAOC,GAAI,EAGJD,EAAOD,QAKfJ,EAAoBO,EAAInB,EAGxBY,EAAoBQ,EAAIN,EAGxBF,EAAoBS,EAAI,SAASL,EAASM,EAAMC,GAC3CX,EAAoBY,EAAER,EAASM,IAClC5B,OAAO+B,eAAeT,EAASM,EAAM,CAAEI,YAAY,EAAMC,IAAKJ,KAKhEX,EAAoBgB,EAAI,SAASZ,GACX,qBAAXa,QAA0BA,OAAOC,aAC1CpC,OAAO+B,eAAeT,EAASa,OAAOC,YAAa,CAAEC,MAAO,WAE7DrC,OAAO+B,eAAeT,EAAS,aAAc,CAAEe,OAAO,KAQvDnB,EAAoBoB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQnB,EAAoBmB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,kBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKzC,OAAO0C,OAAO,MAGvB,GAFAxB,EAAoBgB,EAAEO,GACtBzC,OAAO+B,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOnB,EAAoBS,EAAEc,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRvB,EAAoB2B,EAAI,SAAStB,GAChC,IAAIM,EAASN,GAAUA,EAAOiB,WAC7B,WAAwB,OAAOjB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAL,EAAoBS,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRX,EAAoBY,EAAI,SAASgB,EAAQC,GAAY,OAAO/C,OAAOC,UAAUC,eAAeC,KAAK2C,EAAQC,IAGzG7B,EAAoB8B,EAAI,IAExB,IAAIC,EAAaC,KAAK,sBAAwBA,KAAK,uBAAyB,GACxEC,EAAmBF,EAAW5C,KAAKuC,KAAKK,GAC5CA,EAAW5C,KAAOf,EAClB2D,EAAaA,EAAWG,QACxB,IAAI,IAAIvD,EAAI,EAAGA,EAAIoD,EAAWlD,OAAQF,IAAKP,EAAqB2D,EAAWpD,IAC3E,IAAIU,EAAsB4C,EAI1BxC,I","file":"static/js/runtime-main.0733fe2d.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n \t\tvar executeModules = data[2];\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t\t// add entry modules from loaded chunk to deferred list\n \t\tdeferredModules.push.apply(deferredModules, executeModules || []);\n\n \t\t// run deferred modules when all chunks ready\n \t\treturn checkDeferredModules();\n \t};\n \tfunction checkDeferredModules() {\n \t\tvar result;\n \t\tfor(var i = 0; i < deferredModules.length; i++) {\n \t\t\tvar deferredModule = deferredModules[i];\n \t\t\tvar fulfilled = true;\n \t\t\tfor(var j = 1; j < deferredModule.length; j++) {\n \t\t\t\tvar depId = deferredModule[j];\n \t\t\t\tif(installedChunks[depId] !== 0) fulfilled = false;\n \t\t\t}\n \t\t\tif(fulfilled) {\n \t\t\t\tdeferredModules.splice(i--, 1);\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = deferredModule[0]);\n \t\t\t}\n \t\t}\n\n \t\treturn result;\n \t}\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t1: 0\n \t};\n\n \tvar deferredModules = [];\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n \tvar jsonpArray = this[\"webpackJsonpmy-app\"] = this[\"webpackJsonpmy-app\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// run deferred modules from other chunks\n \tcheckDeferredModules();\n"],"sourceRoot":""} --------------------------------------------------------------------------------