├── .babelrc.js ├── .eslintrc ├── .eslintrc.json ├── .gitignore ├── .prettierrc ├── .prettierrc.json ├── Procfile ├── README.md ├── auteur ├── matcha.pgsql ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── server ├── config │ ├── awsConfig.js │ ├── database.js │ └── mailerConfig.js ├── index.js ├── mailer │ ├── sendForgotPasswordEmail.js │ ├── sendLikeEmail.js │ └── sendSigninEmail.js ├── rest │ ├── components(C-M-R) │ │ ├── auth │ │ │ ├── controller.js │ │ │ └── routes.js │ │ ├── block │ │ │ ├── controller.js │ │ │ ├── model.js │ │ │ └── routes.js │ │ ├── chatroom │ │ │ ├── controller.js │ │ │ ├── model.js │ │ │ └── routes.js │ │ ├── gender │ │ │ ├── controller.js │ │ │ ├── model.js │ │ │ └── routes.js │ │ ├── images │ │ │ ├── controller.js │ │ │ └── routes.js │ │ ├── interests │ │ │ ├── controller.js │ │ │ ├── model.js │ │ │ └── routes.js │ │ ├── like │ │ │ ├── controller.js │ │ │ ├── model.js │ │ │ └── routes.js │ │ ├── match │ │ │ ├── controller.js │ │ │ ├── model.js │ │ │ └── routes.js │ │ ├── notification │ │ │ ├── controller.js │ │ │ ├── model.js │ │ │ └── routes.js │ │ ├── report │ │ │ ├── controller.js │ │ │ ├── model.js │ │ │ └── routes.js │ │ ├── sexualOrientation │ │ │ ├── controller.js │ │ │ ├── model.js │ │ │ └── routes.js │ │ ├── user │ │ │ ├── controller.js │ │ │ ├── model.js │ │ │ ├── routes.js │ │ │ ├── seed.js │ │ │ ├── users.csv │ │ │ └── utils.js │ │ ├── userValidation │ │ │ ├── controller.js │ │ │ ├── model.js │ │ │ └── routes.js │ │ └── visit │ │ │ ├── controller.js │ │ │ ├── model.js │ │ │ └── routes.js │ └── middleware │ │ └── jwt.js └── socket │ ├── connectedUsers.js │ ├── disconnection.js │ ├── joinChatroom.js │ ├── newConnection.js │ ├── newMessage.js │ ├── newNotification.js │ └── socket.js ├── src ├── assets │ └── images │ │ ├── heart-bg.jpg │ │ ├── heart-confetti-background-1.png │ │ ├── home-bg-1.jpg │ │ ├── home-bg-2.jpg │ │ ├── home-bg-3.jpg │ │ ├── logo.svg │ │ ├── mamie.jpeg │ │ ├── pink-bg-1.jpg │ │ ├── pink-bg-2.jpg │ │ └── pink-bg-3.jpg ├── components │ ├── ResetforgotPassword │ │ ├── index.js │ │ ├── resetforgotpassword-container.js │ │ └── resetforgotpassword-view.js │ ├── app │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── AuthContext.js │ │ ├── NotLoggedRoute.js │ │ ├── SecureRoute.js │ │ └── index.js │ ├── auth │ │ ├── AuthContainer.js │ │ └── index.js │ ├── chat │ │ ├── chat-container.js │ │ ├── chat-view.js │ │ └── index.js │ ├── chatroom │ │ ├── chatroom-container.js │ │ ├── chatroom-view.js │ │ └── index.js │ ├── forgotpassword │ │ ├── forgotpassword-container.js │ │ ├── forgotpassword-view.js │ │ └── index.js │ ├── home │ │ ├── home-container.js │ │ ├── home-view.js │ │ └── index.js │ ├── like │ │ ├── index.js │ │ ├── like-container.js │ │ └── like-view.js │ ├── login │ │ ├── index.js │ │ ├── login-container.js │ │ └── login-view.js │ ├── nav │ │ ├── components │ │ │ └── notificationDrawer.js │ │ ├── index.js │ │ ├── nav-container.js │ │ └── nav-view.js │ ├── profile │ │ ├── components │ │ │ ├── cropper │ │ │ │ ├── cropImage.js │ │ │ │ └── cropper.js │ │ │ ├── current-pictures.js │ │ │ ├── formCheckBox.js │ │ │ ├── inputTextShort.js │ │ │ ├── location │ │ │ │ ├── address-autocomplete.js │ │ │ │ ├── cityGuess.js │ │ │ │ └── map.js │ │ │ ├── modal.js │ │ │ ├── tabPanelProfileAbout.js │ │ │ ├── tabPanelProfileParameters.js │ │ │ └── upperBoxProfile.js │ │ ├── index.js │ │ ├── profile-container.js │ │ └── profile-view.js │ ├── profileshow │ │ ├── components │ │ │ ├── chipsList.js │ │ │ └── loggedDot.js │ │ ├── index.js │ │ ├── profileshow-container.js │ │ └── profileshow-view.js │ ├── search │ │ ├── components │ │ │ └── search-filters.js │ │ ├── index.js │ │ ├── search-container.js │ │ └── search-view.js │ ├── shared │ │ ├── components │ │ │ └── media-card.js │ │ ├── profiles-grid.js │ │ └── title.js │ ├── signup │ │ ├── index.js │ │ ├── signup-container.js │ │ └── signup-view.js │ ├── suggestions │ │ ├── components │ │ │ └── suggestions-filters.js │ │ ├── index.js │ │ ├── suggestions-container.js │ │ └── suggestions-view.js │ ├── toaster │ │ ├── index.js │ │ ├── toaster-container.js │ │ └── toaster-view.js │ ├── uservalidation │ │ ├── index.js │ │ └── uservalidation-view.js │ └── visit │ │ ├── index.js │ │ ├── visit-container.js │ │ └── visit-view.js ├── index.css ├── index.js └── serviceWorker.js └── todo.MD /.babelrc.js: -------------------------------------------------------------------------------- 1 | const plugins = [ 2 | [ 3 | 'babel-plugin-transform-imports', 4 | { 5 | '@material-ui/core': { 6 | // Use "transform: '@material-ui/core/${member}'," if your bundler does not support ES modules 7 | transform: '@material-ui/core/esm/${member}', 8 | preventFullImport: true, 9 | }, 10 | '@material-ui/icons': { 11 | // Use "transform: '@material-ui/icons/${member}'," if your bundler does not support ES modules 12 | transform: '@material-ui/icons/esm/${member}', 13 | preventFullImport: true, 14 | }, 15 | '@lodash': { 16 | transform: '@lodash/${member}', 17 | preventFullImport: true, 18 | }, 19 | }, 20 | ], 21 | ]; 22 | 23 | module.exports = { plugins }; 24 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [], 3 | // "extends": [ 4 | // "airbnb", 5 | // "prettier" 6 | // ], 7 | "plugins": ["prettier"], 8 | "rules": { 9 | "prettier/prettier": ["error"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb", "plugin:prettier/recommended", "prettier/react"], 3 | "env": { 4 | "browser": true, 5 | "commonjs": true, 6 | "es6": true, 7 | "jest": true, 8 | "node": true 9 | }, 10 | "rules": { 11 | "no-console": "off", 12 | "class-methods-use-this": "off", 13 | "react/prop-types": 0, 14 | "jsx-a11y/href-no-hash": ["off"], 15 | "react/jsx-filename-extension": ["warn", { "extensions": [".js", ".jsx"] }], 16 | "react-hooks/exhaustive-deps": 0, 17 | "max-len": [ 18 | "warn", 19 | { 20 | "code": 80, 21 | "tabWidth": 2, 22 | "comments": 80, 23 | "ignoreComments": false, 24 | "ignoreTrailingComments": true, 25 | "ignoreUrls": true, 26 | "ignoreStrings": true, 27 | "ignoreTemplateLiterals": true, 28 | "ignoreRegExpLiterals": true 29 | } 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.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 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | 26 | server/config/db-log.js 27 | .env 28 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "trailingComma": "all", 4 | "tabWidth": 2, 5 | "semi": true, 6 | "singleQuote": true 7 | } 8 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "singleQuote": true, 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: npm run prodclient 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 💘 Dating app 2 | ### Built with Node, React, Postgres, Socket.io & Material-UI 3 | 4 | ## The project 5 | 6 | Matcha is a dating app with the following features: 7 | * 🌈 non-binary gender & sexual orientation choices 8 | * 💘 matching algorithm based on preferences, distance, common interests and popularity rates 9 | * 🛎 real-time notifications 10 | * 💌 chat 11 | * 🚫 possibility to block or report a user 12 | 13 | ## How to quickly try it? 14 | 15 | 👉 The project is hosted on Heroku: https://maatcha.herokuapp.com/ 16 | 17 | 💡 If you don't want to go through the sign-up process, here are a few login credentials you can use: 18 | ``` 19 | * maëlle16675 // xLDUD5AOSsLL2rs 20 | * romain81397 // K1XZ_qxOGYsQnr1 21 | * noémie36222 // ac3i707y193nqWs 22 | ``` 23 | 24 | ## The stack 25 | ### Back 26 | * [Node](https://nodejs.org/en/) 27 | * [Express](https://expressjs.com/) 28 | * [Postgres](https://www.postgresql.org/) 29 | * [Socket.io](https://socket.io/) : real-time engine (chat + notifications) 30 | * [AWS](https://aws.amazon.com/fr/s3/) : images hosting 31 | 32 | ### Front 33 | * [React](https://reactjs.org/) 34 | * [Material-Ui](https://material-ui.com/) : React UI framework 35 | 36 | ### API we used 37 | * [Google maps](https://developers.google.com/maps/documentation/javascript/tutorial) 38 | * [Mailjet](https://www.mailjet.com/) 39 | * [Faker](https://github.com/marak/Faker.js/) : to generate fake profiles for the seed 40 | 41 | ## What it looks like 42 | 43 | [![Dashboard](https://iili.io/Jfl9Xn.png)](https://freeimage.host/i/capture-decran-2020-03-30-123639.Jfl9Xn) 44 | [![Profile](https://iili.io/Jfl3g4.png)](https://freeimage.host/i/capture-decran-2020-03-30-132035.Jfl3g4) 45 | [![Search-matches](https://iili.io/JflHss.png)](https://freeimage.host/i/capture-decran-2020-03-30-130950.JflHss) 46 | [![Chatroom](https://iili.io/JfldqG.png)](https://freeimage.host/i/capture-decran-2020-03-30-131939.JfldqG) 47 | [![Notifications](https://iili.io/Jfl21f.png)](https://freeimage.host/i/capture-decran-2020-03-30-131952.Jfl21f) 48 | 49 | ## How we've been working 50 | * 🗓 Planning the project and user stories on Trello : [The Project Board](https://trello.com/b/RLNAgAuw/matcha-launch) 51 | * 🗄 Designing the database on dbdiagram.io 52 | [![Database-diagram](https://iili.io/JfcVO7.png)](https://freeimage.host/i/capture-decran-2020-03-30-121118.JfcVO7) 53 | * 🎨 Designing simple wireframes on Figma : [The Wireframes](https://www.figma.com/file/daD5AHhiB3XmfUPdi4PhsS/Matcha?node-id=0%3A1) 54 | 55 | ## Credits 56 | 57 | 👨🏻‍💻👩🏻‍💻Built and designed by [@yann120](https://github.com/yann120) & [@Segolene-Alquier](https://github.com/Segolene-Alquier/) 58 | -------------------------------------------------------------------------------- /auteur: -------------------------------------------------------------------------------- 1 | salquier 2 | ypetitje -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "matcha", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@babel/runtime": "^7.8.4", 7 | "@date-io/date-fns": "^1.3.13", 8 | "@material-ui/core": "^4.9.3", 9 | "@material-ui/icons": "^4.9.1", 10 | "@material-ui/lab": "^4.0.0-alpha.43", 11 | "@material-ui/pickers": "^3.2.10", 12 | "aws-sdk": "^2.624.0", 13 | "axios": "^0.19.2", 14 | "bcrypt": "^4.0.0", 15 | "bluebird": "^3.7.2", 16 | "body-parser": "^1.19.0", 17 | "bootstrap": "^4.4.1", 18 | "concurrently": "^5.1.0", 19 | "date-fns": "^2.9.0", 20 | "dotenv": "^8.2.0", 21 | "eslint-plugin-flowtype": "^4.6.0", 22 | "express": "^4.17.1", 23 | "express-jwt": "^5.3.1", 24 | "faker": "^4.1.0", 25 | "file-type": "^12.4.2", 26 | "fs": "0.0.1-security", 27 | "geolib": "^3.2.1", 28 | "google-map-react": "^1.1.6", 29 | "jquery": "^3.4.1", 30 | "jsonwebtoken": "^8.5.1", 31 | "jwt-decode": "^2.2.0", 32 | "lodash": "^4.17.15", 33 | "moment": "^2.24.0", 34 | "multiparty": "^4.2.1", 35 | "node-mailjet": "^3.3.1", 36 | "objects-to-csv": "^1.3.6", 37 | "pg-native": "^3.0.0", 38 | "pg-promise": "^9.3.6", 39 | "prop-types": "^15.7.2", 40 | "query-string": "^6.11.0", 41 | "react": "^16.12.0", 42 | "react-bootstrap": "^1.0.0-beta.16", 43 | "react-dev-utils": "^9.1.0", 44 | "react-dom": "^16.12.0", 45 | "react-easy-crop": "^1.17.1", 46 | "react-google-places-autocomplete": "^1.6.2", 47 | "react-infinite-scroll-component": "^5.0.4", 48 | "react-router-dom": "^5.1.2", 49 | "react-scripts": "^3.4.0", 50 | "react-swipeable-views": "^0.13.9", 51 | "react-toastify": "^5.5.0", 52 | "socket.io": "^2.3.0", 53 | "use-debounce": "^3.3.0", 54 | "uuid-token-generator": "^1.0.0" 55 | }, 56 | "scripts": { 57 | "client-install": "npm install", 58 | "start": "npm run dev", 59 | "client": "react-scripts start", 60 | "server": "node ./server/index.js", 61 | "serverlocal": "npx nodemon ./server/index.js --ignore './src/' localhost 3001", 62 | "prodclient": "npx serve -s build", 63 | "build": "react-scripts build", 64 | "dev": "concurrently \"npm run serverlocal\" \"npm run client\"", 65 | "heroku-postbuild": "NPM_CONFIG_PRODUCTION=false npm install && npm run build", 66 | "test": "react-scripts test", 67 | "eject": "react-scripts eject" 68 | }, 69 | "eslintConfig": { 70 | "extends": "react-app" 71 | }, 72 | "browserslist": { 73 | "production": [ 74 | ">0.2%", 75 | "not dead", 76 | "not op_mini all" 77 | ], 78 | "development": [ 79 | "last 1 chrome version", 80 | "last 1 firefox version", 81 | "last 1 safari version" 82 | ] 83 | }, 84 | "devDependencies": { 85 | "babel-eslint": "^10.0.3", 86 | "babel-plugin-transform-imports": "^2.0.0", 87 | "eslint": "^6.8.0", 88 | "eslint-config-airbnb": "^18.0.1", 89 | "eslint-config-prettier": "^6.10.0", 90 | "eslint-plugin-import": "^2.20.1", 91 | "eslint-plugin-jsx-a11y": "^6.2.3", 92 | "eslint-plugin-prettier": "^3.1.2", 93 | "eslint-plugin-react": "^7.18.3", 94 | "nodemon": "^1.19.4", 95 | "prettier": "^1.19.1" 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elitedev201/React-Dating-app/11b2954e5de0d2504c41abaafbf12b417e1a6f32/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | Matcha 14 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elitedev201/React-Dating-app/11b2954e5de0d2504c41abaafbf12b417e1a6f32/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elitedev201/React-Dating-app/11b2954e5de0d2504c41abaafbf12b417e1a6f32/public/logo512.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /server/config/awsConfig.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | const bluebird = require('bluebird'); 3 | 4 | AWS.config.update({ 5 | accessKeyId: process.env.AWS_ACCESS_KEY_ID, 6 | secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, 7 | sessionToken: process.env.AWS_SESSION_TOKEN, 8 | }); 9 | 10 | AWS.config.setPromisesDependency(bluebird); 11 | 12 | const s3 = new AWS.S3(); 13 | 14 | module.exports.s3 = s3; 15 | -------------------------------------------------------------------------------- /server/config/database.js: -------------------------------------------------------------------------------- 1 | const pgp = require('pg-promise')({ 2 | // Initialization Options 3 | }); 4 | 5 | let cn; 6 | if (process.env.ENVIRONMENT === 'production' && process.env.DATABASE_URL) { 7 | cn = process.env.DATABASE_URL; 8 | } else { 9 | cn = { 10 | host: process.env.DB_HOST, 11 | port: 5432, 12 | database: 'matcha', 13 | user: process.env.DB_USER, 14 | password: process.env.DB_PASSWORD, 15 | currentSchema: 'public', 16 | }; 17 | } 18 | const db = pgp(cn); 19 | 20 | module.exports = { db, pgp }; 21 | -------------------------------------------------------------------------------- /server/config/mailerConfig.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/order 2 | const mailjet = require('node-mailjet').connect( 3 | process.env.MAILJET_API_KEY, 4 | process.env.MAILJET_API_SECRET, 5 | ); 6 | 7 | module.exports.mailjet = mailjet; 8 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | require('dotenv').config(); 4 | 5 | const port = process.env.PORT || 3001; 6 | const app = express(); 7 | 8 | app.use(bodyParser.json()); 9 | app.use(bodyParser.urlencoded({ extended: true })); 10 | app.use((req, res, next) => { 11 | const allowedOrigins = [ 12 | 'http://localhost:3000', 13 | 'http://ypetitjean.fr:3000', 14 | 'https://maatcha.herokuapp.com', 15 | 'http://maatcha.herokuapp.com', 16 | ]; 17 | const { origin } = req.headers; 18 | if (allowedOrigins.indexOf(origin) > -1) { 19 | res.header('Access-Control-Allow-Origin', origin); 20 | res.header( 21 | 'Access-Control-Allow-Methods', 22 | 'PUT, POST, GET, DELETE, OPTIONS', 23 | ); 24 | res.header('Access-Control-Allow-Credentials'); 25 | } 26 | res.header( 27 | 'Access-Control-Allow-Headers', 28 | 'Origin, X-Requested-With, Content-Type, Accept, x-access-token, Authorization', 29 | ); 30 | next(); 31 | }); 32 | 33 | app.use('/users', require('./rest/components(C-M-R)/user/routes')); 34 | app.use('/genders', require('./rest/components(C-M-R)/gender/routes')); 35 | app.use('/interests', require('./rest/components(C-M-R)/interests/routes')); 36 | app.use('/auth', require('./rest/components(C-M-R)/auth/routes')); 37 | app.use('/visits', require('./rest/components(C-M-R)/visit/routes')); 38 | app.use('/matchs', require('./rest/components(C-M-R)/match/routes')); 39 | app.use('/likes', require('./rest/components(C-M-R)/like/routes')); 40 | app.use('/block', require('./rest/components(C-M-R)/block/routes')); 41 | app.use('/report', require('./rest/components(C-M-R)/report/routes')); 42 | app.use('/chat', require('./rest/components(C-M-R)/chatroom/routes')); 43 | app.use( 44 | '/notification', 45 | require('./rest/components(C-M-R)/notification/routes'), 46 | ); 47 | app.use( 48 | '/validation', 49 | require('./rest/components(C-M-R)/userValidation/routes'), 50 | ); 51 | app.use('/images', require('./rest/components(C-M-R)/images/routes')); 52 | 53 | const server = require('http').Server(app); 54 | global.io = require('socket.io')(server); 55 | require('./socket/socket')(); 56 | 57 | server.listen(port, () => { 58 | console.log(`Matcha is listening on port ${port}!`); 59 | }); 60 | -------------------------------------------------------------------------------- /server/mailer/sendForgotPasswordEmail.js: -------------------------------------------------------------------------------- 1 | const { mailjet } = require('../config/mailerConfig'); 2 | 3 | const sendForgotPasswordEmail = async (email, firstname, token) => { 4 | const request = mailjet.post('send', { version: 'v3.1' }).request({ 5 | Messages: [ 6 | { 7 | From: { 8 | Email: 'yann.petitjean06@gmail.com', 9 | Name: 'Matcha', 10 | }, 11 | To: [ 12 | { 13 | Email: email, 14 | Name: firstname, 15 | }, 16 | ], 17 | TemplateID: 1091114, 18 | TemplateLanguage: true, 19 | Subject: 'Matcha - Mot de passe oublié', 20 | Variables: { 21 | firstname, 22 | COMFIRMATION_TOKEN: token, 23 | }, 24 | }, 25 | ], 26 | }); 27 | 28 | await request.catch(err => { 29 | if (process.env.VERBOSE === 'true') console.log(err); 30 | }); 31 | }; 32 | 33 | module.exports.sendForgotPasswordEmail = sendForgotPasswordEmail; 34 | -------------------------------------------------------------------------------- /server/mailer/sendLikeEmail.js: -------------------------------------------------------------------------------- 1 | const { mailjet } = require('../config/mailerConfig'); 2 | const User = require('./../rest/components(C-M-R)/user/model'); 3 | 4 | const user = new User(); 5 | 6 | const sendLikeEmail = async (likedUserId, likingUserId) => { 7 | const [{ firstname, email }] = await user.getByFiltered('id', likedUserId, [ 8 | 'firstname', 9 | 'email', 10 | ]); 11 | const [{ username: likingUser }] = await user.getByFiltered( 12 | 'id', 13 | likingUserId, 14 | ['username'], 15 | ); 16 | const request = mailjet.post('send', { version: 'v3.1' }).request({ 17 | Messages: [ 18 | { 19 | From: { 20 | Email: 'yann.petitjean06@gmail.com', 21 | Name: 'Matcha', 22 | }, 23 | To: [ 24 | { 25 | Email: email, 26 | Name: firstname, 27 | }, 28 | ], 29 | TemplateID: 1197417, 30 | TemplateLanguage: true, 31 | Subject: 'Someone likes you on Matcha 🔥', 32 | Variables: { 33 | firstname, 34 | likinguser: likingUser, 35 | }, 36 | }, 37 | ], 38 | }); 39 | 40 | await request.catch(err => { 41 | if (process.env.VERBOSE === 'true') console.log(err); 42 | }); 43 | }; 44 | 45 | module.exports.sendLikeEmail = sendLikeEmail; 46 | -------------------------------------------------------------------------------- /server/mailer/sendSigninEmail.js: -------------------------------------------------------------------------------- 1 | const { mailjet } = require('../config/mailerConfig'); 2 | 3 | const sendSigninEmail = async (email, firstname, token) => { 4 | const request = mailjet.post('send', { version: 'v3.1' }).request({ 5 | Messages: [ 6 | { 7 | From: { 8 | Email: 'yann.petitjean06@gmail.com', 9 | Name: 'Matcha', 10 | }, 11 | To: [ 12 | { 13 | Email: email, 14 | Name: firstname, 15 | }, 16 | ], 17 | TemplateID: 1085893, 18 | TemplateLanguage: true, 19 | Subject: 'Bienvenue chez Matcha', 20 | Variables: { 21 | firstname, 22 | COMFIRMATION_TOKEN: token, 23 | }, 24 | }, 25 | ], 26 | }); 27 | 28 | await request.catch(err => { 29 | if (process.env.VERBOSE === 'true') console.log(err); 30 | }); 31 | }; 32 | 33 | module.exports.sendSigninEmail = sendSigninEmail; 34 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/auth/controller.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const bcrypt = require('bcrypt'); 3 | const User = require('../user/model'); 4 | 5 | const user = new User(); 6 | 7 | const secret = 'mignon4ever'; 8 | 9 | async function booleanToken(request, response) { 10 | let token = 11 | request.headers['x-access-token'] || request.headers.authorization; 12 | if (token) { 13 | if (token.startsWith('Bearer ')) { 14 | token = token.slice(7, token.length); 15 | } 16 | jwt.verify(token, secret, (err, decoded) => { 17 | if (err) { 18 | return response.json({ 19 | success: false, 20 | message: 'Token is not valid', 21 | }); 22 | } 23 | request.decoded = decoded; 24 | return request.decoded; 25 | }); 26 | const userId = request.decoded.userid; 27 | const { lon, lat } = request.query; 28 | if (lon && lat) { 29 | const location = [parseFloat(lon), parseFloat(lat)]; 30 | user.updateById(userId, { location }); 31 | } 32 | const userData = await user.getByFiltered('id', userId, [ 33 | 'id', 34 | 'firstname', 35 | 'surname', 36 | 'username', 37 | 'email', 38 | 'location', 39 | 'birthDate', 40 | 'popularityRate', 41 | 'gender', 42 | 'sexualOrientation', 43 | 'description', 44 | 'interests', 45 | 'images', 46 | 'profilePicture', 47 | 'notificationMail', 48 | 'notificationPush', 49 | ]); 50 | 51 | return response.json({ 52 | success: true, 53 | message: 'Token is valid', 54 | data: userData[0], 55 | }); 56 | } 57 | return response.json({ 58 | success: false, 59 | message: 'Auth token not supplied', 60 | }); 61 | } 62 | 63 | async function login(request, response) { 64 | const { username, password, lon, lat } = request.body; 65 | 66 | if (process.env.VERBOSE === 'true') 67 | console.log('User submitted: ', username, password); 68 | try { 69 | const query = await user.getBy('username', username); 70 | if (query.length <= 0) { 71 | if (process.env.VERBOSE === 'true') 72 | console.log(username, " doesn't exist"); 73 | response.status(200).json({ 74 | success: false, 75 | token: null, 76 | err: `${username} doesn't exist`, 77 | }); 78 | return; 79 | } 80 | const [visitor] = query; 81 | if (visitor.suspended) { 82 | response.status(200).json({ 83 | success: false, 84 | token: null, 85 | err: 'Your account has been suspended', 86 | }); 87 | return; 88 | } 89 | if (process.env.VERBOSE === 'true') 90 | console.log('compare : ', visitor.password, password); 91 | if (bcrypt.compareSync(password, visitor.password)) { 92 | if (visitor.validated) { 93 | const token = jwt.sign({ userid: visitor.id }, 'mignon4ever', { 94 | expiresIn: '1d', 95 | }); 96 | if (lon && lat) { 97 | const location = [parseFloat(lon), parseFloat(lat)]; 98 | user.updateById(visitor.id, { location, lastConnection: 'now()' }); 99 | } else { 100 | await user.updateById(visitor.id, { 101 | lastConnection: 'now()', 102 | }); 103 | } 104 | response.json({ 105 | success: true, 106 | token, 107 | err: null, 108 | }); 109 | } else { 110 | response.status(200).json({ 111 | success: false, 112 | token: null, 113 | err: 'The user is not validated', 114 | }); 115 | } 116 | } else { 117 | if (process.env.VERBOSE === 'true') 118 | console.log('The login and password do not match!'); 119 | response.status(200).json({ 120 | success: false, 121 | token: null, 122 | err: 'The login and password do not match!', 123 | }); 124 | } 125 | } catch (err) { 126 | if (process.env.VERBOSE === 'true') console.log(err); 127 | response.status(206).send(err); 128 | } 129 | } 130 | 131 | async function logout(request, response) { 132 | try { 133 | // let call = await gender.getAll() 134 | // response.status(200).json(call) 135 | } catch (err) { 136 | if (process.env.VERBOSE === 'true') console.log(err); 137 | response.status(206).send(err); 138 | } 139 | } 140 | 141 | module.exports.login = login; 142 | module.exports.logout = logout; 143 | module.exports.booleanToken = booleanToken; 144 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/auth/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const router = express.Router(); 4 | const { login, logout, booleanToken } = require('./controller'); 5 | 6 | // login user 7 | router.post('/login', login); 8 | 9 | // logout user 10 | router.post('/logout', logout); 11 | 12 | // check token 13 | router.get('/checkToken', booleanToken); 14 | 15 | module.exports = router; 16 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/block/controller.js: -------------------------------------------------------------------------------- 1 | const Block = require('./model'); 2 | 3 | const blocks = new Block(); 4 | 5 | async function blockUnblockUserId(request, response) { 6 | const blockingUser = request.decoded.userid; 7 | const blockedUser = parseInt(request.params.id, 10); 8 | if (blockedUser === blockingUser) { 9 | return response 10 | .status(200) 11 | .json({ success: false, error: 'You can not block yourself!' }); 12 | } 13 | try { 14 | const alreadyBlocked = await blocks.exists(blockingUser, blockedUser); 15 | let query; 16 | if (alreadyBlocked) { 17 | query = await blocks.delete(blockingUser, blockedUser); 18 | } else { 19 | query = await blocks.create(blockingUser, blockedUser); 20 | } 21 | return response.status(200).json(query); 22 | } catch (err) { 23 | if (process.env.VERBOSE === 'true') console.log(err); 24 | return response.status(206).send(err); 25 | } 26 | } 27 | 28 | module.exports.blockUnblockUserId = blockUnblockUserId; 29 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/block/model.js: -------------------------------------------------------------------------------- 1 | const { db } = require('../../../config/database'); 2 | 3 | class Block { 4 | isValidType(type) { 5 | const authorizedTypes = ['id', 'blockingUser', 'blockedUser']; 6 | return authorizedTypes.some(authorizedType => { 7 | return type === authorizedType; 8 | }); 9 | } 10 | 11 | async create(blockingUserId, blockedUserId) { 12 | try { 13 | if (process.env.VERBOSE === 'true') 14 | console.log( 15 | `INSERT INTO public."Block" (blockingUser, blockedUser) VALUES (${blockingUserId}, ${blockedUserId} RETURNING id)`, 16 | ); 17 | return await db 18 | .any( 19 | 'INSERT INTO public."Block" ("blockingUser", "blockedUser") VALUES ($1, $2) RETURNING id', 20 | [blockingUserId, blockedUserId], 21 | ) 22 | .then(data => { 23 | return { 24 | success: true, 25 | created: true, 26 | id: data[0].id, 27 | }; 28 | }); 29 | } catch (err) { 30 | if (process.env.VERBOSE === 'true') 31 | console.log(err, 'in model Block.create()'); 32 | return { created: false, error: err }; 33 | } 34 | } 35 | 36 | async exists(blockingUser, blockedUser) { 37 | try { 38 | if (process.env.VERBOSE === 'true') 39 | console.log( 40 | `SELECT exists(SELECT from public."Block" WHERE "blockingUser" = ${blockingUser} AND "blockedUser" = ${blockedUser})`, 41 | ); 42 | const result = await db.any( 43 | `SELECT exists(SELECT from public."Block" WHERE "blockingUser" = $1 AND "blockedUser" = $2);`, 44 | [blockingUser, blockedUser], 45 | ); 46 | return result[0].exists; 47 | } catch (err) { 48 | if (process.env.VERBOSE === 'true') 49 | console.log(err, 'in model Block.exists()'); 50 | return null; 51 | } 52 | } 53 | 54 | async delete(blockingUser, blockedUser) { 55 | try { 56 | if (process.env.VERBOSE === 'true') 57 | console.log( 58 | `DELETE FROM public."Block" WHERE "blockingUser" = ${blockingUser} AND "blockedUser" = ${blockedUser}`, 59 | ); 60 | await db.any( 61 | 'DELETE FROM public."Block" WHERE "blockingUser" = $1 AND "blockedUser" = $2 ', 62 | [blockingUser, blockedUser], 63 | ); 64 | return { success: true, deleted: true }; 65 | } catch (err) { 66 | if (process.env.VERBOSE === 'true') 67 | console.log(err, 'in model User.delete()'); 68 | return { deleted: false, error: err }; 69 | } 70 | } 71 | } 72 | 73 | module.exports = Block; 74 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/block/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { checkToken } = require('../../middleware/jwt'); 3 | 4 | const router = express.Router(); 5 | const { 6 | // getLikesFromCurrentUser, 7 | blockUnblockUserId, 8 | } = require('./controller'); 9 | 10 | // get the list of blocked users from the current user 11 | // router.get('/', checkToken, getLikesFromCurrentUser); 12 | router.post('/block-unblock/:id', checkToken, blockUnblockUserId); 13 | module.exports = router; 14 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/chatroom/controller.js: -------------------------------------------------------------------------------- 1 | const Chat = require('./model'); 2 | const Match = require('../match/model'); 3 | const User = require('../user/model'); 4 | 5 | const messages = new Chat(); 6 | const match = new Match(); 7 | const user = new User(); 8 | 9 | async function getMatchsFromCurrentUser(request, response) { 10 | try { 11 | const id = request.decoded.userid; 12 | const call = await messages.getBy(['user1', 'user2'], id); 13 | response.status(200).json(call); 14 | } catch (err) { 15 | if (process.env.VERBOSE === 'true') console.log(err); 16 | response.status(206).send(err); 17 | } 18 | } 19 | 20 | async function getMessagesFromMatchId(request, response) { 21 | try { 22 | const { id: matchId } = request.params; 23 | const userId = request.decoded.userid; 24 | let otherUserId; 25 | if (await messages.userCanAccessMatch(matchId, userId)) { 26 | let call = await messages.getAll(matchId); 27 | const usersIds = await match.getUsersFromMatchId(matchId); 28 | if (usersIds[0] === userId) { 29 | [, otherUserId] = usersIds; 30 | } 31 | if (usersIds[1] === userId) { 32 | [otherUserId] = usersIds; 33 | } 34 | const otherUserInfo = await user.getByFiltered('id', otherUserId, [ 35 | 'profilePicture', 36 | 'firstname', 37 | 'username', 38 | ]); 39 | call = { 40 | messages: call, 41 | profilePicture: otherUserInfo[0].profilePicture, 42 | firstname: otherUserInfo[0].firstname, 43 | username: otherUserInfo[0].username, 44 | }; 45 | if (process.env.VERBOSE === 'true') console.log('call', call); 46 | messages.updateRead(matchId, userId); 47 | response.status(200).json(call); 48 | } else { 49 | response.status(200).json({ 50 | success: false, 51 | message: "You don't have access to that chatroom, nice try!", 52 | }); 53 | } 54 | } catch (err) { 55 | if (process.env.VERBOSE === 'true') console.log(err); 56 | response.status(206).send(err); 57 | } 58 | } 59 | 60 | async function numberOfUnreadMessages(request, response) { 61 | const id = request.decoded.userid; 62 | try { 63 | const call = await messages.numberUnread(id); 64 | response.status(200).json(call); 65 | } catch (err) { 66 | if (process.env.VERBOSE === 'true') console.log(err); 67 | response.status(206).send(err); 68 | } 69 | } 70 | 71 | module.exports.getMatchsFromCurrentUser = getMatchsFromCurrentUser; 72 | module.exports.getMessagesFromMatchId = getMessagesFromMatchId; 73 | module.exports.numberOfUnreadMessages = numberOfUnreadMessages; 74 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/chatroom/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { checkToken } = require('../../middleware/jwt'); 3 | 4 | const router = express.Router(); 5 | const { 6 | getMatchsFromCurrentUser, 7 | getMessagesFromMatchId, 8 | numberOfUnreadMessages, 9 | } = require('./controller'); 10 | 11 | // list of all Matchs from current user - Chat 12 | router.get('/', checkToken, getMatchsFromCurrentUser); 13 | // get number of unread messages by user id - Chat 14 | router.get('/total', checkToken, numberOfUnreadMessages); 15 | // get all messages by match id - Chat 16 | router.get('/:id', checkToken, getMessagesFromMatchId); 17 | 18 | module.exports = router; 19 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/gender/controller.js: -------------------------------------------------------------------------------- 1 | const Gender = require('./model'); 2 | 3 | const gender = new Gender(); 4 | 5 | async function getGenders(request, response) { 6 | try { 7 | const call = await gender.getAll(); 8 | response.status(200).json(call); 9 | } catch (err) { 10 | if (process.env.VERBOSE === 'true') console.log(err); 11 | response.status(206).send(err); 12 | } 13 | } 14 | 15 | async function getGenderById(request, response) { 16 | const id = parseInt(request.params.id, 10); 17 | try { 18 | const call = await gender.getBy('id', id); 19 | response.status(200).json(call); 20 | } catch (err) { 21 | if (process.env.VERBOSE === 'true') console.log(err); 22 | response.status(206).send(err); 23 | } 24 | } 25 | 26 | module.exports.getGenders = getGenders; 27 | module.exports.getGenderById = getGenderById; 28 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/gender/model.js: -------------------------------------------------------------------------------- 1 | const { db } = require('../../../config/database'); 2 | 3 | class Gender { 4 | isValidType(type) { 5 | const authorizedTypes = ['id', 'name']; 6 | return authorizedTypes.some(authorizedType => { 7 | return type === authorizedType; 8 | }); 9 | } 10 | 11 | async getBy(type, value) { 12 | try { 13 | if (!this.isValidType(type)) { 14 | if (process.env.VERBOSE === 'true') 15 | console.log(`Gender.getBy(): ${type} is not an authorized type`); 16 | return null; 17 | } 18 | if (process.env.VERBOSE === 'true') 19 | console.log(`SELECT * FROM public."Gender" WHERE ${type} = ${value}`); 20 | const result = await db.any( 21 | `SELECT * FROM public."Gender" WHERE $1:name = $2`, 22 | [type, value], 23 | ); 24 | return result; 25 | } catch (err) { 26 | if (process.env.VERBOSE === 'true') 27 | console.log(err, 'in model Gender.getBy()'); 28 | return null; 29 | } 30 | } 31 | 32 | async getAll() { 33 | try { 34 | if (process.env.VERBOSE === 'true') 35 | console.log('SELECT * FROM public."Gender"'); 36 | const result = await db.any('SELECT * FROM public."Gender"'); 37 | return result; 38 | } catch (err) { 39 | if (process.env.VERBOSE === 'true') 40 | console.log(err, 'in model Gender.getAll()'); 41 | return null; 42 | } 43 | } 44 | 45 | async exists(type, value) { 46 | try { 47 | if (!value) return false; 48 | if (!this.isValidType(type)) { 49 | if (process.env.VERBOSE === 'true') 50 | console.log(`Gender.exists(): ${type} is not an authorized type`); 51 | return null; 52 | } 53 | if (process.env.VERBOSE === 'true') 54 | console.log( 55 | `SELECT exists(SELECT from public."Gender" WHERE ${type} = ${value})`, 56 | ); 57 | const result = await db.any( 58 | `SELECT exists(SELECT from public."Gender" WHERE $1:name = $2);`, 59 | [type, value], 60 | ); 61 | return result[0].exists; 62 | } catch (err) { 63 | if (process.env.VERBOSE === 'true') 64 | console.log(err, 'in model Gender.exists()'); 65 | return null; 66 | } 67 | } 68 | } 69 | 70 | module.exports = Gender; 71 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/gender/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const router = express.Router(); 4 | const { getGenders, getGenderById } = require('./controller'); 5 | 6 | // list of all Genders - Gender 7 | router.get('/', getGenders); 8 | // get Gender by id - Gender 9 | router.get('/:id', getGenderById); 10 | 11 | module.exports = router; 12 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/images/controller.js: -------------------------------------------------------------------------------- 1 | const multiparty = require('multiparty'); 2 | const fs = require('fs'); 3 | const fileType = require('file-type'); 4 | const { s3 } = require('./../../../config/awsConfig'); 5 | const User = require('../user/model'); 6 | 7 | const user = new User(); 8 | 9 | const uploadFile = (buffer, name, type) => { 10 | const params = { 11 | ACL: 'public-read', 12 | Body: buffer, 13 | Bucket: process.env.S3_BUCKET, 14 | ContentType: type.mime, 15 | Key: `${name}.${type.ext}`, 16 | }; 17 | return s3.upload(params).promise(); 18 | }; 19 | 20 | const deleteFile = url => { 21 | const name = url.match(/[^\/]+\/[^\/]+\/[^\/]+$/)[0]; 22 | const params = { 23 | Bucket: process.env.S3_BUCKET, 24 | Key: name, 25 | }; 26 | return s3.deleteObject(params).promise(); 27 | }; 28 | 29 | async function uploadImage(request, response) { 30 | const id = request.decoded.userid; 31 | try { 32 | const length = await user.getByFiltered('id', id, ['images']).then(data => { 33 | return data[0].images.length; 34 | }); 35 | if (length >= 5) { 36 | return response.status(400).send('A user can only upload 5 pictures'); 37 | } 38 | } catch (error) { 39 | if (process.env.VERBOSE === 'true') console.log(error); 40 | return response.status(400).send(error); 41 | } 42 | const form = new multiparty.Form(); 43 | form.parse(request, async (error, fields, files) => { 44 | if (error) throw new Error(error); 45 | try { 46 | const { path } = files.file[0]; 47 | const buffer = fs.readFileSync(path); 48 | const type = fileType(buffer); 49 | const timestamp = Date.now().toString(); 50 | const fileName = `${process.env.ENVIRONMENT}/${id}/${timestamp}`; 51 | const data = await uploadFile(buffer, fileName, type); 52 | await user.addElementToArrayById(id, 'images', data.Location); 53 | await user.updateProfilePictureIfNotExist(id); 54 | return response.status(200).send(data); 55 | } catch (error) { 56 | if (process.env.VERBOSE === 'true') console.log(error); 57 | return response.status(400).send(error); 58 | } 59 | }); 60 | } 61 | 62 | async function deleteImage(request, response) { 63 | const id = request.decoded.userid; 64 | const { url } = request.body; 65 | try { 66 | await user.deleteElementToArrayById(id, 'images', url); 67 | await user.updateProfilePictureIfNotExist(id); 68 | if (!url.includes('generated.photos')) { 69 | await deleteFile(url); 70 | } 71 | return response.status(200).send({ success: true }); 72 | } catch (error) { 73 | if (process.env.VERBOSE === 'true') console.log(error); 74 | return response.status(400).send({ success: false, error }); 75 | } 76 | } 77 | 78 | module.exports.uploadImage = uploadImage; 79 | module.exports.deleteImage = deleteImage; 80 | module.exports.deleteFile = deleteFile; 81 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/images/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { checkToken } = require('../../middleware/jwt'); 3 | 4 | const router = express.Router(); 5 | const { uploadImage, deleteImage } = require('./controller'); 6 | 7 | // Upload an image 8 | router.post('/upload', checkToken, uploadImage); 9 | router.post('/delete', checkToken, deleteImage); 10 | 11 | module.exports = router; 12 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/interests/controller.js: -------------------------------------------------------------------------------- 1 | const Interest = require('./model'); 2 | 3 | const interests = new Interest(); 4 | 5 | async function getInterests(request, response) { 6 | try { 7 | const call = await interests.getAll(); 8 | response.status(200).json(call); 9 | } catch (err) { 10 | if (process.env.VERBOSE === 'true') console.log(err); 11 | response.status(206).send(err); 12 | } 13 | } 14 | 15 | async function getInterestById(request, response) { 16 | const id = parseInt(request.params.id, 10); 17 | try { 18 | const call = await interests.getBy('id', id); 19 | response.status(200).json(call); 20 | } catch (err) { 21 | if (process.env.VERBOSE === 'true') console.log(err); 22 | response.status(206).send(err); 23 | } 24 | } 25 | 26 | module.exports.getInterests = getInterests; 27 | module.exports.getInterestById = getInterestById; 28 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/interests/model.js: -------------------------------------------------------------------------------- 1 | const { db } = require('../../../config/database'); 2 | 3 | class Interest { 4 | isValidType(type) { 5 | const authorizedTypes = ['id', 'name']; 6 | return authorizedTypes.some(authorizedType => { 7 | return type === authorizedType; 8 | }); 9 | } 10 | 11 | async getBy(type, value) { 12 | try { 13 | if (!this.isValidType(type)) { 14 | if (process.env.VERBOSE === 'true') 15 | console.log(`Interest.getBy(): ${type} is not an authorized type`); 16 | return null; 17 | } 18 | if (process.env.VERBOSE === 'true') 19 | console.log(`SELECT * FROM public."Interest" WHERE ${type} = ${value}`); 20 | const result = await db.any( 21 | `SELECT * FROM public."Interest" WHERE $1:name = $2`, 22 | [type, value], 23 | ); 24 | return result; 25 | } catch (err) { 26 | if (process.env.VERBOSE === 'true') 27 | console.log(err, 'in model Interest.getBy()'); 28 | return null; 29 | } 30 | } 31 | 32 | async getAll() { 33 | try { 34 | if (process.env.VERBOSE === 'true') 35 | console.log('SELECT * FROM public."Interest"'); 36 | const result = await db.any('SELECT * FROM public."Interest"'); 37 | return result; 38 | } catch (err) { 39 | if (process.env.VERBOSE === 'true') 40 | console.log(err, 'in model Interest.getAll()'); 41 | return null; 42 | } 43 | } 44 | 45 | async exists(type, value) { 46 | try { 47 | if (!value) return false; 48 | if (!this.isValidType(type)) { 49 | if (process.env.VERBOSE === 'true') 50 | console.log(`Interest.exists(): ${type} is not an authorized type`); 51 | return null; 52 | } 53 | if (process.env.VERBOSE === 'true') 54 | console.log( 55 | `SELECT exists(SELECT from public."Interest" WHERE ${type} = ${value})`, 56 | ); 57 | const result = await db.none( 58 | `SELECT exists(SELECT from public."Interest" WHERE id = ALL($2));`, 59 | [value], 60 | ); 61 | return result[0].exists; 62 | } catch (err) { 63 | if (process.env.VERBOSE === 'true') 64 | console.log(err, 'in model Interest.exists()'); 65 | return null; 66 | } 67 | } 68 | } 69 | 70 | module.exports = Interest; 71 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/interests/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const router = express.Router(); 4 | const { getInterests, getInterestById } = require('./controller'); 5 | 6 | // list of all Interests - Interest 7 | router.get('/', getInterests); 8 | // get Interest by id - Interest 9 | router.get('/:id', getInterestById); 10 | 11 | module.exports = router; 12 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/like/controller.js: -------------------------------------------------------------------------------- 1 | const Like = require('./model'); 2 | const Match = require('./../match/model'); 3 | const Block = require('./../block/model'); 4 | const User = require('./../user/model'); 5 | const Chat = require('./../chatroom/model'); 6 | const _ = require('lodash'); 7 | 8 | const { sendLikeEmail } = require('../../../mailer/sendLikeEmail'); 9 | const { isConnected } = require('./../../../socket/newConnection'); 10 | const newNotification = require('../../../socket/newNotification'); 11 | 12 | const likes = new Like(); 13 | const block = new Block(); 14 | const matchs = new Match(); 15 | const user = new User(); 16 | const chat = new Chat(); 17 | 18 | async function getLikesFromCurrentUser(request, response) { 19 | const id = request.decoded.userid; 20 | try { 21 | let call = await likes.getBy('likedUser', id); 22 | call = _.map(call, like => { 23 | return { ...like, connected: isConnected(like.likingUser) }; 24 | }); 25 | response.status(200).json(call); 26 | } catch (err) { 27 | if (process.env.VERBOSE === 'true') console.log(err); 28 | response.status(206).send(err); 29 | } 30 | } 31 | 32 | async function likeUnlikeUserId(request, response) { 33 | const likingUser = request.decoded.userid; 34 | const likedUser = parseInt(request.params.id, 10); 35 | if (likedUser === likingUser) { 36 | return response 37 | .status(200) 38 | .json({ success: false, error: 'You can not like yourself!' }); 39 | } 40 | if (await block.exists(likedUser, likingUser)) { 41 | return response.status(200).json({ 42 | success: false, 43 | blocked: true, 44 | message: 'You have been blocked by this user!', 45 | }); 46 | } 47 | try { 48 | const alreadyLiked = await likes.exists(likingUser, likedUser); 49 | let query; 50 | if (alreadyLiked) { 51 | query = await likes.delete(likingUser, likedUser); 52 | if (query.unmatch) { 53 | const matchId = await matchs.getMatchId(likingUser, likedUser); 54 | newNotification(likedUser, likingUser, 'unmatch'); 55 | await chat.delete(matchId); 56 | matchs.delete(likingUser, likedUser); 57 | } 58 | } else { 59 | query = await likes.create(likingUser, likedUser); 60 | if (query.match) { 61 | const matchQuery = await matchs.create(likingUser, likedUser); 62 | newNotification(likedUser, likingUser, 'match'); 63 | query.matchId = matchQuery.id; 64 | } else { 65 | newNotification(likedUser, likingUser, 'like'); 66 | sendLikeEmail(likedUser, likingUser); 67 | } 68 | } 69 | user.updatePopularityRate(likedUser); 70 | response.status(200).json(query); 71 | } catch (err) { 72 | if (process.env.VERBOSE === 'true') console.log(err); 73 | response.status(206).send(err); 74 | } 75 | } 76 | 77 | module.exports.getLikesFromCurrentUser = getLikesFromCurrentUser; 78 | module.exports.likeUnlikeUserId = likeUnlikeUserId; 79 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/like/model.js: -------------------------------------------------------------------------------- 1 | const { db } = require('../../../config/database'); 2 | 3 | class Like { 4 | isValidType(type) { 5 | const authorizedTypes = ['id', 'likingUser', 'likedUser']; 6 | return authorizedTypes.some(authorizedType => { 7 | return type === authorizedType; 8 | }); 9 | } 10 | 11 | async create(likingUserId, likedUserId) { 12 | try { 13 | if (process.env.VERBOSE === 'true') 14 | console.log( 15 | `INSERT INTO public."Like" (likingUser, likedUser, date) VALUES (${likingUserId}, ${likedUserId}, Now() RETURNING id)`, 16 | ); 17 | return await db 18 | .any( 19 | 'INSERT INTO public."Like" ("likingUser", "likedUser", date) VALUES ($1, $2, NOW()) RETURNING id, EXISTS(SELECT * FROM public."Like" WHERE "likingUser" = $2 AND "likedUser" = $1) AS match', 20 | [likingUserId, likedUserId], 21 | ) 22 | .then(data => { 23 | return { 24 | success: true, 25 | created: true, 26 | id: data[0].id, 27 | match: data[0].match, 28 | }; 29 | }); 30 | } catch (err) { 31 | if (process.env.VERBOSE === 'true') 32 | console.log(err, 'in model Like.create()'); 33 | return { created: false, error: err }; 34 | } 35 | } 36 | 37 | async getBy(type, value) { 38 | try { 39 | if (!this.isValidType(type)) { 40 | if (process.env.VERBOSE === 'true') 41 | console.log(`Like.getBy(): ${type} is not an authorized type`); 42 | return null; 43 | } 44 | if (process.env.VERBOSE === 'true') 45 | console.log( 46 | `SELECT firstname, username, birthDate, location, popularityRate, profilePicture, date 47 | FROM public."Like" 48 | WHERE ${type} = ${value}`, 49 | ); 50 | const result = await db.any( 51 | `SELECT firstname, username, "birthDate", location, "popularityRate", "profilePicture", "likingUser" AS visitor, "likedUser" AS visited, date, 52 | EXISTS(SELECT * FROM public."Like" AS secondlike WHERE secondlike."likingUser" = $2 AND secondlike."likedUser" = "Like"."likingUser") AS liking 53 | FROM public."Like" , public."User" 54 | WHERE $1:name = $2 AND "Like"."likingUser" = "User".id 55 | AND NOT EXISTS ( 56 | SELECT * 57 | FROM public."Block" 58 | WHERE "blockedUser" = $2 59 | AND "blockingUser" = "likingUser" 60 | ) 61 | ORDER BY date DESC`, 62 | [type, value], 63 | ); 64 | result.forEach(element => { 65 | element.match = element.liking; 66 | }); 67 | return result; 68 | } catch (err) { 69 | if (process.env.VERBOSE === 'true') 70 | console.log(err, 'in model Like.getBy()'); 71 | return null; 72 | } 73 | } 74 | 75 | async getAll() { 76 | try { 77 | if (process.env.VERBOSE === 'true') 78 | console.log('SELECT * FROM public."Like"'); 79 | const result = await db.any('SELECT * FROM public."Like"'); 80 | return result; 81 | } catch (err) { 82 | if (process.env.VERBOSE === 'true') 83 | console.log(err, 'in model Like.getAll()'); 84 | return null; 85 | } 86 | } 87 | 88 | async exists(likingUser, likedUser) { 89 | try { 90 | if (process.env.VERBOSE === 'true') 91 | console.log( 92 | `SELECT exists(SELECT from public."Like" WHERE "likingUser" = ${likingUser} AND "likedUser" = ${likedUser})`, 93 | ); 94 | const result = await db.any( 95 | `SELECT exists(SELECT from public."Like" WHERE "likingUser" = $1 AND "likedUser" = $2);`, 96 | [likingUser, likedUser], 97 | ); 98 | return result[0].exists; 99 | } catch (err) { 100 | if (process.env.VERBOSE === 'true') 101 | console.log(err, 'in model Like.exists()'); 102 | return null; 103 | } 104 | } 105 | 106 | async relationship(visitorUser, visitedUser) { 107 | try { 108 | if (process.env.VERBOSE === 'true') 109 | console.log( 110 | `SELECT exists(SELECT from public."Like" WHERE "likingUser" = $1 AND "likedUser" = $2) AS visitorlikevisited, exists(SELECT from public."Like" WHERE "likedUser" = $1 AND "likingUser" = $2) AS visitedlikevisitor`, 111 | ); 112 | const result = await db.any( 113 | `SELECT exists(SELECT from public."Like" WHERE "likingUser" = $1 AND "likedUser" = $2) AS visitorlikevisited, exists(SELECT from public."Like" WHERE "likedUser" = $1 AND "likingUser" = $2) AS visitedlikevisitor`, 114 | [visitorUser, visitedUser], 115 | ); 116 | result[0].match = 117 | result[0].visitorlikevisited && result[0].visitedlikevisitor; 118 | return result[0]; 119 | } catch (err) { 120 | if (process.env.VERBOSE === 'true') 121 | console.log(err, 'in model Like.exists()'); 122 | return null; 123 | } 124 | } 125 | 126 | async delete(likingUser, likedUser) { 127 | try { 128 | if (process.env.VERBOSE === 'true') 129 | console.log( 130 | `DELETE FROM public."Like" WHERE "likingUser" = ${likingUser} AND "likedUser" = ${likedUser}`, 131 | ); 132 | const result = await db.any( 133 | 'DELETE FROM public."Like" WHERE "likingUser" = $1 AND "likedUser" = $2 RETURNING EXISTS(SELECT from public."Like" WHERE "likedUser" = $1 AND "likingUser" = $2) AS unmatch', 134 | [likingUser, likedUser], 135 | ); 136 | if (process.env.VERBOSE === 'true') console.log(result[0].unmatch); 137 | return { success: true, deleted: true, unmatch: result[0].unmatch }; 138 | } catch (err) { 139 | if (process.env.VERBOSE === 'true') 140 | console.log(err, 'in model User.delete()'); 141 | return { deleted: false, error: err }; 142 | } 143 | } 144 | } 145 | 146 | module.exports = Like; 147 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/like/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { checkToken } = require('../../middleware/jwt'); 3 | 4 | const router = express.Router(); 5 | const { 6 | // getLikesByLikingUserId, 7 | // getLikesByLikedUserId, 8 | getLikesFromCurrentUser, 9 | likeUnlikeUserId, 10 | } = require('./controller'); 11 | 12 | // get the list of Like from the current user 13 | router.get('/', checkToken, getLikesFromCurrentUser); 14 | router.get('/like-unlike/:id', checkToken, likeUnlikeUserId); 15 | // router.get('/:id', checkToken, getLikesByLikedUserId); 16 | // router.get('/:id', checkToken, getLikesByLikingUserId); 17 | module.exports = router; 18 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/match/controller.js: -------------------------------------------------------------------------------- 1 | const Match = require('./model'); 2 | 3 | const matchs = new Match(); 4 | 5 | async function getMatchsFromCurrentUser(request, response) { 6 | try { 7 | const id = request.decoded.userid; 8 | const call = await matchs.getBy(['user1', 'user2'], id); 9 | response.status(200).json(call); 10 | } catch (err) { 11 | if (process.env.VERBOSE === 'true') console.log(err); 12 | response.status(206).send(err); 13 | } 14 | } 15 | 16 | async function getMatchById(request, response) { 17 | const id = parseInt(request.params.id, 10); 18 | try { 19 | const call = await matchs.getBy('id', id); 20 | response.status(200).json(call); 21 | } catch (err) { 22 | if (process.env.VERBOSE === 'true') console.log(err); 23 | response.status(206).send(err); 24 | } 25 | } 26 | 27 | module.exports.getMatchsFromCurrentUser = getMatchsFromCurrentUser; 28 | module.exports.getMatchById = getMatchById; 29 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/match/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { checkToken } = require('../../middleware/jwt'); 3 | 4 | const router = express.Router(); 5 | const { getMatchsFromCurrentUser, getMatchById } = require('./controller'); 6 | 7 | // list of all Matchs from current user - Match 8 | router.get('/', checkToken, getMatchsFromCurrentUser); 9 | // get Match by id - Match 10 | router.get('/:id', checkToken, getMatchById); 11 | 12 | module.exports = router; 13 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/notification/controller.js: -------------------------------------------------------------------------------- 1 | const Notification = require('./model'); 2 | const Match = require('./../match/model'); 3 | const Block = require('./../block/model'); 4 | const User = require('./../user/model'); 5 | const Chat = require('./../chatroom/model'); 6 | // const { sendNotificationEmail } = require('../../../mailer/sendNotificationEmail'); 7 | 8 | const notifications = new Notification(); 9 | const block = new Block(); 10 | const matchs = new Match(); 11 | const user = new User(); 12 | const chat = new Chat(); 13 | 14 | async function getNotificationsFromCurrentUser(request, response) { 15 | const id = request.decoded.userid; 16 | try { 17 | const call = await notifications.getBy('recipient', id); 18 | notifications.updateRead(id); 19 | response.status(200).json(call); 20 | } catch (err) { 21 | if (process.env.VERBOSE === 'true') console.log(err); 22 | response.status(206).send(err); 23 | } 24 | } 25 | 26 | async function numberOfUnreadNotifications(request, response) { 27 | const id = request.decoded.userid; 28 | try { 29 | const call = await notifications.numberUnread(id); 30 | response.status(200).json(call); 31 | } catch (err) { 32 | if (process.env.VERBOSE === 'true') console.log(err); 33 | response.status(206).send(err); 34 | } 35 | } 36 | 37 | module.exports.getNotificationsFromCurrentUser = getNotificationsFromCurrentUser; 38 | module.exports.numberOfUnreadNotifications = numberOfUnreadNotifications; 39 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/notification/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { checkToken } = require('../../middleware/jwt'); 3 | 4 | const router = express.Router(); 5 | const { 6 | getNotificationsFromCurrentUser, 7 | numberOfUnreadNotifications, 8 | } = require('./controller'); 9 | 10 | // get number of notifications unread from the current user 11 | router.get('/total', checkToken, numberOfUnreadNotifications); 12 | // get the list of Notification from the current user 13 | router.get('/', checkToken, getNotificationsFromCurrentUser); 14 | 15 | module.exports = router; 16 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/report/controller.js: -------------------------------------------------------------------------------- 1 | const Report = require('./model'); 2 | const User = require('../user/model'); 3 | 4 | const reports = new Report(); 5 | const user = new User(); 6 | 7 | async function reportUserId(request, response) { 8 | const reportingUser = request.decoded.userid; 9 | const reportedUser = parseInt(request.params.id, 10); 10 | if (reportedUser === reportingUser) { 11 | return response 12 | .status(200) 13 | .json({ success: false, error: 'You can not report yourself!' }); 14 | } 15 | try { 16 | const alreadyReported = await reports.exists(reportingUser, reportedUser); 17 | let query; 18 | if (alreadyReported) { 19 | return response 20 | .status(200) 21 | .json({ created: false, message: 'You already reported this User' }); 22 | } 23 | query = await reports.create(reportingUser, reportedUser); 24 | if (parseInt(query.nbOfReports, 10) >= 2) { 25 | user.updateById(reportedUser, { suspended: true }); 26 | } 27 | return response.status(200).json(query); 28 | } catch (err) { 29 | if (process.env.VERBOSE === 'true') console.log(err); 30 | response.status(206).send(err); 31 | } 32 | } 33 | 34 | module.exports.reportUserId = reportUserId; 35 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/report/model.js: -------------------------------------------------------------------------------- 1 | const { db } = require('../../../config/database'); 2 | 3 | class Report { 4 | isValidType(type) { 5 | const authorizedTypes = ['id', 'reportingUser', 'reportedUser']; 6 | return authorizedTypes.some(authorizedType => { 7 | return type === authorizedType; 8 | }); 9 | } 10 | 11 | async create(reportingUserId, reportedUserId) { 12 | try { 13 | if (process.env.VERBOSE === 'true') 14 | console.log( 15 | `INSERT INTO public."Report" (reportingUser, reportedUser) VALUES (${reportingUserId}, ${reportedUserId} RETURNING id)`, 16 | ); 17 | return await db 18 | .any( 19 | 'INSERT INTO public."Report" ("reportingUser", "reportedUser") VALUES ($1, $2) RETURNING id, (SELECT COUNT(*) as nbofreports FROM Public."Report" WHERE "reportedUser" = $2)', 20 | [reportingUserId, reportedUserId], 21 | ) 22 | .then(data => { 23 | return { 24 | success: true, 25 | created: true, 26 | id: data[0].id, 27 | nbOfReports: data[0].nbofreports, 28 | }; 29 | }); 30 | } catch (err) { 31 | if (process.env.VERBOSE === 'true') 32 | console.log(err, 'in model Report.create()'); 33 | return { created: false, error: err }; 34 | } 35 | } 36 | 37 | async exists(reportingUser, reportedUser) { 38 | try { 39 | if (process.env.VERBOSE === 'true') 40 | console.log( 41 | `SELECT exists(SELECT from public."Report" WHERE "reportingUser" = ${reportingUser} AND "reportedUser" = ${reportedUser})`, 42 | ); 43 | const result = await db.any( 44 | `SELECT exists(SELECT from public."Report" WHERE "reportingUser" = $1 AND "reportedUser" = $2);`, 45 | [reportingUser, reportedUser], 46 | ); 47 | return result[0].exists; 48 | } catch (err) { 49 | if (process.env.VERBOSE === 'true') 50 | console.log(err, 'in model Report.exists()'); 51 | return null; 52 | } 53 | } 54 | } 55 | 56 | module.exports = Report; 57 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/report/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { checkToken } = require('../../middleware/jwt'); 3 | 4 | const router = express.Router(); 5 | const { reportUserId } = require('./controller'); 6 | 7 | // get the list of blocked users from the current user 8 | // router.get('/', checkToken, getLikesFromCurrentUser); 9 | router.post('/:id', checkToken, reportUserId); 10 | module.exports = router; 11 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/sexualOrientation/controller.js: -------------------------------------------------------------------------------- 1 | const SexualOrientation = require('./model'); 2 | 3 | const sexualOrientation = new SexualOrientation(); 4 | 5 | async function getSexualOrientations(request, response) { 6 | try { 7 | const call = await sexualOrientation.getAll(); 8 | response.status(200).json(call); 9 | } catch (err) { 10 | if (process.env.VERBOSE === 'true') console.log(err); 11 | response.status(206).send(err); 12 | } 13 | } 14 | 15 | async function getSexualOrientationById(request, response) { 16 | const id = parseInt(request.params.id, 10); 17 | try { 18 | const call = await sexualOrientation.getBy('id', id); 19 | response.status(200).json(call); 20 | } catch (err) { 21 | if (process.env.VERBOSE === 'true') console.log(err); 22 | response.status(206).send(err); 23 | } 24 | } 25 | 26 | module.exports.getSexualOrientations = getSexualOrientations; 27 | module.exports.getSexualOrientationById = getSexualOrientationById; 28 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/sexualOrientation/model.js: -------------------------------------------------------------------------------- 1 | const { db } = require('../../../config/database'); 2 | 3 | class SexualOrientation { 4 | isValidType(type) { 5 | const authorizedTypes = ['id', 'name']; 6 | return authorizedTypes.some(authorizedType => { 7 | return type === authorizedType; 8 | }); 9 | } 10 | 11 | async getBy(type, value) { 12 | try { 13 | if (!this.isValidType(type)) { 14 | if (process.env.VERBOSE === 'true') 15 | console.log( 16 | `SexualOrientation.getBy(): ${type} is not an authorized type`, 17 | ); 18 | return null; 19 | } 20 | if (process.env.VERBOSE === 'true') 21 | console.log( 22 | `SELECT * FROM public."SexualOrientation" WHERE ${type} = ${value}`, 23 | ); 24 | const result = await db.any( 25 | `SELECT * FROM public."Gender" WHERE $1:name = $2`, 26 | [type, value], 27 | ); 28 | return result; 29 | } catch (err) { 30 | if (process.env.VERBOSE === 'true') 31 | console.log(err, 'in model Gender.getBy()'); 32 | return null; 33 | } 34 | } 35 | 36 | async getAll() { 37 | try { 38 | if (process.env.VERBOSE === 'true') 39 | console.log('SELECT * FROM public."Gender"'); 40 | const result = await db.any('SELECT * FROM public."Gender"'); 41 | return result; 42 | } catch (err) { 43 | if (process.env.VERBOSE === 'true') 44 | console.log(err, 'in model Gender.getAll()'); 45 | return null; 46 | } 47 | } 48 | 49 | async exists(type, value) { 50 | try { 51 | if (!value) return false; 52 | if (!this.isValidType(type)) { 53 | if (process.env.VERBOSE === 'true') 54 | console.log(`Gender.exists(): ${type} is not an authorized type`); 55 | return null; 56 | } 57 | if (process.env.VERBOSE === 'true') 58 | console.log( 59 | `SELECT exists(SELECT from public."Gender" WHERE ${type} = ${value})`, 60 | ); 61 | const result = await db.any( 62 | `SELECT exists(SELECT from public."Gender" WHERE $1:name = $2);`, 63 | [type, value], 64 | ); 65 | return result[0].exists; 66 | } catch (err) { 67 | if (process.env.VERBOSE === 'true') 68 | console.log(err, 'in model Gender.exists()'); 69 | return null; 70 | } 71 | } 72 | } 73 | 74 | module.exports = SexualOrientation; 75 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/sexualOrientation/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const router = express.Router(); 4 | const { 5 | getSexualOrientations, 6 | getSexualOrientationById, 7 | } = require('./controller'); 8 | 9 | // list of all SexualOrientations - SexualOrientation 10 | router.get('/', getSexualOrientations); 11 | // get SexualOrientation by id - SexualOrientation 12 | router.get('/:id', getSexualOrientationById); 13 | 14 | module.exports = router; 15 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/user/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { checkToken } = require('../../middleware/jwt'); 3 | 4 | const router = express.Router(); 5 | const { 6 | getUserById, 7 | getUserByUsername, 8 | getUsers, 9 | usernameExists, 10 | emailExists, 11 | createUser, 12 | updateUser, 13 | deleteUser, 14 | getMyUserInfo, 15 | search, 16 | suggestions, 17 | } = require('./controller'); 18 | 19 | // list of all users - user 20 | router.get('/', checkToken, getUsers); 21 | // suggestions from the user 22 | router.post('/suggestions', checkToken, suggestions); 23 | // search for a user 24 | router.post('/search', checkToken, search); 25 | // username already exists ? - user 26 | router.get('/verification/username', usernameExists); 27 | // email already exists ? - user 28 | router.get('/verification/email', emailExists); 29 | // get profile of visited User 30 | router.get('/profile/:username', checkToken, getUserByUsername); 31 | // get my profile info 32 | router.get('/profile', checkToken, getMyUserInfo); 33 | // get user by id - user 34 | router.get('/:id', checkToken, getUserById); 35 | // create user - user 36 | router.post('/', createUser); 37 | // update user - user 38 | router.put('/', checkToken, updateUser); 39 | // delete user - user 40 | router.delete('/', checkToken, deleteUser); 41 | 42 | module.exports = router; 43 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/user/seed.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config({ path: './../../../../.env' }); 2 | const faker = require('faker/locale/fr'); 3 | const _ = require('lodash'); 4 | const User = require('./model'); 5 | const Interest = require('./../interests/model'); 6 | 7 | const user = new User(); 8 | const interest = new Interest(); 9 | const axios = require('axios'); 10 | const ObjectsToCsv = require('objects-to-csv'); 11 | 12 | let interestsList = []; 13 | 14 | const createFakeUser = async () => { 15 | const firstName = faker.name.firstName(); 16 | const surname = faker.name.lastName(); 17 | const userName = firstName.toLowerCase() + faker.random.number(); 18 | const password = faker.internet.password(); 19 | const email = `${userName}@growth-tools.tk`; 20 | 21 | console.log(firstName, surname, userName, password, email); 22 | const newUser = { 23 | ...(await user.create({ 24 | firstname: firstName, 25 | surname, 26 | username: userName, 27 | password, 28 | email, 29 | })), 30 | userName, 31 | password, 32 | email, 33 | }; 34 | const csv = new ObjectsToCsv([newUser]); 35 | await csv.toDisk('./users.csv', { append: true }); 36 | return newUser; 37 | }; 38 | 39 | function randomInteger(min, max) { 40 | return Math.floor(Math.random() * (max - min + 1)) + min; 41 | } 42 | function randomArrayInt(min, max) { 43 | const array = []; 44 | array[0] = randomInteger(min, max); 45 | array[1] = randomInteger(min, max); 46 | array[2] = randomInteger(min, max); 47 | return _.sortBy(_.uniq(array)); 48 | } 49 | 50 | function randomArrayOfInterests(nbOfElements) { 51 | const array = []; 52 | for (let i = 0; i < nbOfElements; i++) { 53 | array[i] = interestsList[Math.floor(Math.random() * interestsList.length)]; 54 | } 55 | return _.sortBy(_.uniq(array)); 56 | } 57 | 58 | const generateFakeImages = () => { 59 | return axios 60 | .get( 61 | 'https://api.generated.photos/api/v1/faces?api_key=0c_nmVH48EoxfDeTmn_-3Q&per_page=5&order_by=random', 62 | ) 63 | .then(res => { 64 | return res.data.faces.map(face => { 65 | return face.urls[4]['512']; 66 | }); 67 | }); 68 | }; 69 | 70 | const updateFakeUser = async userId => { 71 | const infos = {}; 72 | infos.validated = true; 73 | infos.description = faker.lorem.paragraphs(); 74 | infos.location = []; 75 | // latitude : entre 48.60 et 48.99 76 | infos.location[0] = parseFloat(`48.${randomInteger(60, 99)}`); 77 | // longitude : entre 2.30 et 2.60 78 | infos.location[1] = parseFloat(`2.${randomInteger(30, 60)}`); 79 | const now = new Date(); 80 | infos.lastConnection = now.toISOString(); 81 | infos.popularityRate = randomInteger(20, 90); 82 | infos.birthDate = faker.date.between('1940-01-01', '2001-12-31'); 83 | infos.gender = randomArrayInt(1, 7); 84 | infos.sexualOrientation = randomArrayInt(1, 7); 85 | infos.interests = randomArrayOfInterests(10); 86 | infos.images = await generateFakeImages(); 87 | infos.profilePicture = infos.images[0]; 88 | await user.updateById(userId, infos); 89 | }; 90 | 91 | const generateProfiles = async nbOfProfiles => { 92 | interestsList = await interest.getAll().then(list => { 93 | return list.map(element => element.name); 94 | }); 95 | let profiles = []; 96 | for (let i = 0; i < nbOfProfiles; i++) { 97 | let userId = 0; 98 | const newUser = await createFakeUser(); 99 | if (newUser.created) { 100 | userId = newUser.id; 101 | await updateFakeUser(userId); 102 | profiles.push( 103 | `new user created with id: ${userId} , username: ${newUser.userName} , password: ${newUser.password}`, 104 | ); 105 | } 106 | } 107 | profiles.forEach(profile => console.log(profile)); 108 | }; 109 | 110 | generateProfiles(1); 111 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/userValidation/controller.js: -------------------------------------------------------------------------------- 1 | const UserValidation = require('./model'); 2 | const { 3 | sendForgotPasswordEmail, 4 | } = require('../../../mailer/sendForgotPasswordEmail'); 5 | const User = require('../user/model'); 6 | const bcrypt = require('bcrypt'); 7 | 8 | const user = new User(); 9 | const uv = new UserValidation(); 10 | 11 | async function verifyConfirmationToken(request, response) { 12 | try { 13 | const call = await uv.verifyConfirmationToken({ 14 | token: request.params.token, 15 | }); 16 | response.status(200).json(call); 17 | } catch (err) { 18 | if (process.env.VERBOSE === 'true') console.log(err); 19 | response.status(206).send(err); 20 | } 21 | } 22 | 23 | async function verifyForgotPasswordToken(request, response) { 24 | try { 25 | const call = await uv.verifyForgotPasswordToken({ 26 | token: request.params.token, 27 | }); 28 | response.status(200).json(call); 29 | } catch (err) { 30 | if (process.env.VERBOSE === 'true') console.log(err); 31 | response.status(206).send(err); 32 | } 33 | } 34 | 35 | async function forgotPassword(request, response) { 36 | const { email } = request.body; 37 | try { 38 | const userRequest = await user.getBy('email', email); 39 | if (userRequest.length) { 40 | const { id, firstname } = userRequest[0]; 41 | if (process.env.VERBOSE === 'true') console.log(id); 42 | const call = await uv.create({ userId: id, type: 'resetPassword' }); 43 | if (call.created) { 44 | sendForgotPasswordEmail(email, firstname, call.token); 45 | response.status(206).send({ success: true }); 46 | } else { 47 | response.status(206).send({ success: false, err: call }); 48 | } 49 | } else { 50 | response.status(200).json({ 51 | success: false, 52 | err: "This email doesn't exist", 53 | }); 54 | } 55 | } catch (err) { 56 | if (process.env.VERBOSE === 'true') console.log(err); 57 | response.status(206).send(err); 58 | } 59 | } 60 | 61 | async function forgotPasswordUpdate(request, response) { 62 | const { password } = request.body; 63 | try { 64 | const call = await uv.verifyForgotPasswordToken({ 65 | token: request.params.token, 66 | }); 67 | if (process.env.VERBOSE === 'true') console.log(call); 68 | const { success, userId } = call; 69 | if (success) { 70 | const hashedPassword = bcrypt.hashSync(password, 10); 71 | await user.updateById(userId, { password: hashedPassword }); 72 | uv.delete({ userId }); 73 | response.status(206).send({ success: true }); 74 | } else { 75 | response.status(206).send({ success: false, err: call.error }); 76 | } 77 | } catch (err) { 78 | if (process.env.VERBOSE === 'true') console.log(err); 79 | response.status(206).send(err); 80 | } 81 | } 82 | 83 | module.exports.verifyConfirmationToken = verifyConfirmationToken; 84 | module.exports.forgotPassword = forgotPassword; 85 | module.exports.verifyForgotPasswordToken = verifyForgotPasswordToken; 86 | module.exports.forgotPasswordUpdate = forgotPasswordUpdate; 87 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/userValidation/model.js: -------------------------------------------------------------------------------- 1 | const TokenGenerator = require('uuid-token-generator'); 2 | const { db } = require('../../../config/database'); 3 | const User = require('../user/model'); 4 | 5 | const user = new User(); 6 | 7 | const tokgen = new TokenGenerator(256, TokenGenerator.BASE62); 8 | 9 | class UserValidation { 10 | async create({ userId, type }) { 11 | if (type !== 'validationKey' && type !== 'resetPassword') { 12 | if (process.env.VERBOSE === 'true') 13 | console.log('The type is not valid in model UserValidation.create()'); 14 | return { 15 | created: false, 16 | error: 'The type is not valid in model UserValidation.create()', 17 | }; 18 | } 19 | try { 20 | const token = tokgen.generate(); 21 | if (process.env.VERBOSE === 'true') 22 | console.log( 23 | `INSERT INTO public."UserValidation" (userId, ${type}) VALUES (${userId}, ${token})`, 24 | ); 25 | // await db.any( 26 | // 'INSERT INTO public."UserValidation" ("userId", $1:name) VALUES ($2, $3)', 27 | // [type, userId, token], 28 | // ); 29 | await db.any( 30 | 'UPDATE public."UserValidation" SET $1:name=$3 WHERE "userId"=$2; INSERT INTO public."UserValidation" ("userId", $1:name) SELECT $2, $3 WHERE NOT EXISTS (SELECT 1 FROM public."UserValidation" WHERE "userId"=$2)', 31 | [type, userId, token], 32 | ); 33 | return { created: true, token }; 34 | } catch (err) { 35 | if (process.env.VERBOSE === 'true') 36 | console.log(err, 'in model UserValidation.create()'); 37 | return { created: false, error: err }; 38 | } 39 | } 40 | 41 | async verifyConfirmationToken({ token }) { 42 | if (token === undefined) { 43 | if (process.env.VERBOSE === 'true') 44 | console.log('The token is not defined'); 45 | return { 46 | success: false, 47 | error: 'The token is not defined', 48 | }; 49 | } 50 | try { 51 | return db 52 | .one( 53 | 'DELETE FROM public."UserValidation" WHERE "validationKey" = $1 RETURNING "userId"', 54 | token, 55 | ) 56 | .then(({ userId }) => { 57 | user.updateById(userId, { validated: true }); 58 | return { 59 | success: true, 60 | error: 'The account is now validated!', 61 | }; 62 | }) 63 | .catch(error => { 64 | if (error.received === 0) { 65 | return { 66 | success: false, 67 | error: 'The confirmation link is not valid', 68 | }; 69 | } 70 | if (process.env.VERBOSE === 'true') 71 | console.log(error, 'in model UserValidation.create()'); 72 | return { 73 | success: false, 74 | error, 75 | }; 76 | }); 77 | } catch (err) { 78 | if (process.env.VERBOSE === 'true') 79 | console.log(err, 'in model UserValidation.create()'); 80 | return { success: false, error: err }; 81 | } 82 | } 83 | 84 | async verifyForgotPasswordToken({ token }) { 85 | if (token === undefined) { 86 | if (process.env.VERBOSE === 'true') 87 | console.log('The token is not defined'); 88 | return { 89 | success: false, 90 | error: 'The token is not defined', 91 | }; 92 | } 93 | try { 94 | return db 95 | .one( 96 | 'SELECT * FROM public."UserValidation" WHERE "resetPassword" = $1', 97 | token, 98 | ) 99 | .then(data => { 100 | return { 101 | success: true, 102 | userId: data.userId, 103 | }; 104 | }) 105 | .catch(error => { 106 | if (error.received === 0) { 107 | return { 108 | success: false, 109 | error: 'The confirmation link is not valid', 110 | }; 111 | } 112 | if (process.env.VERBOSE === 'true') 113 | console.log(error, 'in model UserValidation.create()'); 114 | return { 115 | success: false, 116 | error, 117 | }; 118 | }); 119 | } catch (err) { 120 | if (process.env.VERBOSE === 'true') 121 | console.log(err, 'in model UserValidation.create()'); 122 | return { success: false, error: err }; 123 | } 124 | } 125 | 126 | async delete({ userId }) { 127 | try { 128 | db.any('DELETE FROM public."UserValidation" WHERE "userId" = $1', userId); 129 | return; 130 | } catch (error) { 131 | if (process.env.VERBOSE === 'true') 132 | console.log(error, 'in model UserValidation.delete()'); 133 | return { success: false, error }; 134 | } 135 | } 136 | } 137 | module.exports = UserValidation; 138 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/userValidation/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const router = express.Router(); 4 | const { 5 | verifyConfirmationToken, 6 | verifyForgotPasswordToken, 7 | forgotPassword, 8 | forgotPasswordUpdate, 9 | } = require('./controller'); 10 | 11 | // confirm new account with token 12 | router.get('/newaccount/:token', verifyConfirmationToken); 13 | router.get('/forgotpassword/:token', verifyForgotPasswordToken); 14 | router.post('/forgotpasswordcreate', forgotPassword); 15 | router.post('/forgotpasswordupdate/:token', forgotPasswordUpdate); 16 | 17 | module.exports = router; 18 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/visit/controller.js: -------------------------------------------------------------------------------- 1 | const Visit = require('./model'); 2 | const _ = require('lodash'); 3 | const { isConnected } = require('./../../../socket/newConnection'); 4 | const visits = new Visit(); 5 | 6 | async function getVisits(request, response) { 7 | try { 8 | const call = await visits.getAll(); 9 | response.status(200).json(call); 10 | } catch (err) { 11 | if (process.env.VERBOSE === 'true') console.log(err); 12 | response.status(206).send(err); 13 | } 14 | } 15 | 16 | async function getVisitsFromCurrentUser(request, response) { 17 | const id = request.decoded.userid; 18 | try { 19 | let call = await visits.getBy('visited', id); 20 | call = _.uniqBy(call, 'visitor'); 21 | call = _.map(call, visit => { 22 | return { ...visit, connected: isConnected(visit.visitor) }; 23 | }); 24 | response.status(200).json(call); 25 | } catch (err) { 26 | if (process.env.VERBOSE === 'true') console.log(err); 27 | response.status(206).send(err); 28 | } 29 | } 30 | 31 | module.exports.getVisits = getVisits; 32 | module.exports.getVisitsFromCurrentUser = getVisitsFromCurrentUser; 33 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/visit/model.js: -------------------------------------------------------------------------------- 1 | const { db } = require('../../../config/database'); 2 | 3 | class Visit { 4 | isValidType(type) { 5 | const authorizedTypes = ['id', 'visitor', 'visited']; 6 | return authorizedTypes.some(authorizedType => { 7 | return type === authorizedType; 8 | }); 9 | } 10 | 11 | async create(visitorId, visitedId) { 12 | try { 13 | if (process.env.VERBOSE === 'true') 14 | console.log( 15 | `INSERT INTO public."Visit" (visitor, visited, date) VALUES (${visitorId}, ${visitedId} RETURNING id)`, 16 | ); 17 | return await db 18 | .any( 19 | 'INSERT INTO public."Visit" (visitor, visited, date) VALUES ($1, $2, NOW()) RETURNING id', 20 | [visitorId, visitedId], 21 | ) 22 | .then(data => { 23 | return { created: true, id: data[0].id }; 24 | }); 25 | } catch (err) { 26 | if (process.env.VERBOSE === 'true') 27 | console.log(err, 'in model Visit.create()'); 28 | return { created: false, error: err }; 29 | } 30 | } 31 | 32 | async getBy(type, value) { 33 | try { 34 | if (!this.isValidType(type)) { 35 | if (process.env.VERBOSE === 'true') 36 | console.log(`Visit.getBy(): ${type} is not an authorized type`); 37 | return null; 38 | } 39 | if (process.env.VERBOSE === 'true') 40 | console.log( 41 | `SELECT firstname, username, birthDate, location, popularityRate, profilePicture, date FROM public."Visit" WHERE ${type} = ${value}`, 42 | ); 43 | const result = await db.any( 44 | `SELECT firstname, username, "birthDate", location, "popularityRate", "profilePicture", date, visitor, 45 | EXISTS(SELECT * FROM public."Like" WHERE "likingUser" = $2 AND "likedUser" = visitor) AS liking, 46 | EXISTS(SELECT * FROM public."Like" WHERE "likedUser" = $2 AND "likingUser" = visitor) AS liked 47 | FROM public."Visit", public."User" 48 | WHERE $1:name = $2 49 | AND "Visit".visitor = "User".id 50 | AND NOT EXISTS ( 51 | SELECT * 52 | FROM public."Block" 53 | WHERE "blockedUser" = $2 54 | AND "blockingUser" = visitor 55 | ) 56 | ORDER BY date DESC`, 57 | [type, value], 58 | ); 59 | result.forEach(element => { 60 | element.match = element.liked && element.liking; 61 | }); 62 | return result; 63 | } catch (err) { 64 | if (process.env.VERBOSE === 'true') 65 | console.log(err, 'in model Visit.getBy()'); 66 | return null; 67 | } 68 | } 69 | 70 | async getAll() { 71 | try { 72 | if (process.env.VERBOSE === 'true') 73 | console.log('SELECT * FROM public."Visit"'); 74 | const result = await db.any('SELECT * FROM public."Visit"'); 75 | return result; 76 | } catch (err) { 77 | if (process.env.VERBOSE === 'true') 78 | console.log(err, 'in model Visit.getAll()'); 79 | return null; 80 | } 81 | } 82 | 83 | async exists(type, value) { 84 | try { 85 | if (!value) return false; 86 | if (!this.isValidType(type)) { 87 | if (process.env.VERBOSE === 'true') 88 | console.log(`Visit.exists(): ${type} is not an authorized type`); 89 | return null; 90 | } 91 | if (process.env.VERBOSE === 'true') 92 | console.log( 93 | `SELECT exists(SELECT from public."Visit" WHERE ${type} = ${value})`, 94 | ); 95 | const result = await db.none( 96 | `SELECT exists(SELECT from public."Visit" WHERE id = ALL($2));`, 97 | [value], 98 | ); 99 | return result[0].exists; 100 | } catch (err) { 101 | if (process.env.VERBOSE === 'true') 102 | console.log(err, 'in model Visit.exists()'); 103 | return null; 104 | } 105 | } 106 | } 107 | 108 | module.exports = Visit; 109 | -------------------------------------------------------------------------------- /server/rest/components(C-M-R)/visit/routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { checkToken } = require('../../middleware/jwt'); 3 | 4 | const router = express.Router(); 5 | const { getVisits, getVisitsFromCurrentUser } = require('./controller'); 6 | 7 | // get the list of visit from the current user 8 | router.get('/', checkToken, getVisitsFromCurrentUser); 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /server/rest/middleware/jwt.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | // var bcrypt = require('bcrypt'); 3 | const secret = 'mignon4ever'; 4 | 5 | async function checkToken(request, response, next) { 6 | let token = 7 | request.headers['x-access-token'] || request.headers.authorization; 8 | if (token) { 9 | if (token.startsWith('Bearer ')) { 10 | token = token.slice(7, token.length); 11 | } 12 | jwt.verify(token, secret, (err, decoded) => { 13 | if (err) { 14 | return response.json({ 15 | success: false, 16 | message: 'Token is not valid', 17 | }); 18 | } 19 | request.decoded = decoded; 20 | next(); 21 | return request.decoded; 22 | }); 23 | } else { 24 | return response.json({ 25 | success: false, 26 | message: 'Auth token not supplied', 27 | }); 28 | } 29 | return null; 30 | } 31 | 32 | async function checkTokenSocket(token) { 33 | if (token) { 34 | if (token.startsWith('Bearer ')) { 35 | token = token.slice(7, token.length); 36 | } 37 | return jwt.verify(token, secret, (err, decoded) => { 38 | if (err) { 39 | return false; 40 | } 41 | return decoded; 42 | }); 43 | } 44 | } 45 | 46 | module.exports.checkToken = checkToken; 47 | module.exports.checkTokenSocket = checkTokenSocket; 48 | -------------------------------------------------------------------------------- /server/socket/connectedUsers.js: -------------------------------------------------------------------------------- 1 | let connectedUsers = {}; 2 | 3 | const getConnectedUsers = () => { 4 | return connectedUsers; 5 | }; 6 | 7 | const setConnectedUsers = value => { 8 | connectedUsers = value; 9 | return connectedUsers; 10 | }; 11 | 12 | module.exports.getConnectedUsers = getConnectedUsers; 13 | module.exports.setConnectedUsers = setConnectedUsers; 14 | -------------------------------------------------------------------------------- /server/socket/disconnection.js: -------------------------------------------------------------------------------- 1 | const { setConnectedUsers } = require('./connectedUsers'); 2 | 3 | const disconnection = (io, connectedUsers, socket) => { 4 | if (process.env.VERBOSE === 'true') 5 | console.log('user disconnected', connectedUsers[socket.id]); 6 | socket.removeAllListeners('chat message'); 7 | socket.removeAllListeners('joinchatroom'); 8 | socket.removeAllListeners('disconnect'); 9 | // io.removeAllListeners('connection'); 10 | delete connectedUsers[socket.id]; 11 | setConnectedUsers(connectedUsers); 12 | }; 13 | 14 | module.exports = disconnection; 15 | -------------------------------------------------------------------------------- /server/socket/joinChatroom.js: -------------------------------------------------------------------------------- 1 | const Chat = require('./../rest/components(C-M-R)/chatroom/model'); 2 | const chat = new Chat(); 3 | 4 | const joinChatroom = async (matchId, userId, socket) => { 5 | if (await chat.userCanAccessMatch(matchId, userId)) { 6 | socket.join(matchId); 7 | } else { 8 | if (process.env.VERBOSE === 'true') 9 | console.log( 10 | `The user ${userId} cannot access this chatroom because it doesn't belong to match ${matchId}`, 11 | ); 12 | } 13 | }; 14 | 15 | module.exports = joinChatroom; 16 | -------------------------------------------------------------------------------- /server/socket/newConnection.js: -------------------------------------------------------------------------------- 1 | const { checkTokenSocket } = require('./../rest/middleware/jwt'); 2 | const _ = require('lodash'); 3 | const { getConnectedUsers, setConnectedUsers } = require('./connectedUsers'); 4 | 5 | const connectedUsersId = () => { 6 | const connectedUsers = getConnectedUsers(); 7 | return _.uniq(_.values(connectedUsers)); 8 | }; 9 | 10 | const isConnected = id => { 11 | return _.includes(connectedUsersId(), id); 12 | }; 13 | 14 | const newConnection = async (io, socket) => { 15 | const userConnected = await checkTokenSocket(socket.handshake.query.token); 16 | if (userConnected) { 17 | const connectedUsers = getConnectedUsers(); 18 | connectedUsers[socket.id] = userConnected.userid; 19 | setConnectedUsers(connectedUsers); 20 | } 21 | }; 22 | 23 | module.exports.newConnection = newConnection; 24 | module.exports.connectedUsersId = connectedUsersId; 25 | module.exports.isConnected = isConnected; 26 | -------------------------------------------------------------------------------- /server/socket/newMessage.js: -------------------------------------------------------------------------------- 1 | const Chat = require('./../rest/components(C-M-R)/chatroom/model'); 2 | const Match = require('./../rest/components(C-M-R)/match/model'); 3 | const Block = require('./../rest/components(C-M-R)/block/model'); 4 | const newNotification = require('./newNotification'); 5 | 6 | const chat = new Chat(); 7 | const match = new Match(); 8 | const block = new Block(); 9 | 10 | const newMessage = async (msg, matchId, userId, socket, io) => { 11 | if (await chat.canAccessChat(matchId)) { 12 | chat.create(matchId, userId, msg).then(async response => { 13 | io.to(matchId).emit('chat message', response); 14 | match.updateLastMessage(matchId, response.id); 15 | const matchUsers = await match.getUsersFromMatchId(matchId); 16 | let recipient; 17 | if (matchUsers[0] === userId) { 18 | [, recipient] = matchUsers; 19 | } else { 20 | [recipient] = matchUsers; 21 | } 22 | newNotification(recipient, userId, 'message'); 23 | }); 24 | } else { 25 | socket.emit('redirect', 'cant_access_chat'); 26 | } 27 | }; 28 | 29 | module.exports = newMessage; 30 | -------------------------------------------------------------------------------- /server/socket/newNotification.js: -------------------------------------------------------------------------------- 1 | const { getConnectedUsers, setConnectedUsers } = require('./connectedUsers'); 2 | const _ = require('lodash'); 3 | const Notification = require('./../rest/components(C-M-R)/notification/model'); 4 | const notification = new Notification(); 5 | 6 | const newNotification = (recipient, sender, type) => { 7 | const connectedUsers = getConnectedUsers(); 8 | const recipientSocketIds = _.keys( 9 | _.pickBy(connectedUsers, user => { 10 | return user === recipient; 11 | }), 12 | ); 13 | if (process.env.VERBOSE === 'true') 14 | console.log('recipientSocketIds', recipientSocketIds); 15 | notification.create(recipient, sender, type); 16 | recipientSocketIds.forEach(socketId => { 17 | io.to(socketId).emit('new notification', sender, type); 18 | }); 19 | }; 20 | 21 | module.exports = newNotification; 22 | -------------------------------------------------------------------------------- /server/socket/socket.js: -------------------------------------------------------------------------------- 1 | const { newConnection } = require('./newConnection'); 2 | const { getConnectedUsers, setConnectedUsers } = require('./connectedUsers'); 3 | const disconnection = require('./disconnection'); 4 | const newMessage = require('./newMessage'); 5 | const joinChatroom = require('./joinChatroom'); 6 | 7 | const socketRouter = () => { 8 | io.on('connection', async socket => { 9 | newConnection(io, socket, getConnectedUsers()); 10 | socket.on('joinchatroom', function(match) { 11 | joinChatroom(match, getConnectedUsers()[socket.id], socket); 12 | }); 13 | socket.on('error', function(err) { 14 | if (process.env.VERBOSE === 'true') console.log(err.stack); 15 | }); 16 | socket.on('disconnect', function() { 17 | disconnection(io, getConnectedUsers(), socket); 18 | }); 19 | 20 | socket.on('chat message', function(msg, match) { 21 | if (process.env.VERBOSE === 'true') 22 | console.log('new msg', msg, match, getConnectedUsers()[socket.id]); 23 | newMessage(msg, match, getConnectedUsers()[socket.id], socket, io); 24 | }); 25 | }); 26 | }; 27 | 28 | module.exports = socketRouter; 29 | -------------------------------------------------------------------------------- /src/assets/images/heart-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elitedev201/React-Dating-app/11b2954e5de0d2504c41abaafbf12b417e1a6f32/src/assets/images/heart-bg.jpg -------------------------------------------------------------------------------- /src/assets/images/heart-confetti-background-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elitedev201/React-Dating-app/11b2954e5de0d2504c41abaafbf12b417e1a6f32/src/assets/images/heart-confetti-background-1.png -------------------------------------------------------------------------------- /src/assets/images/home-bg-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elitedev201/React-Dating-app/11b2954e5de0d2504c41abaafbf12b417e1a6f32/src/assets/images/home-bg-1.jpg -------------------------------------------------------------------------------- /src/assets/images/home-bg-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elitedev201/React-Dating-app/11b2954e5de0d2504c41abaafbf12b417e1a6f32/src/assets/images/home-bg-2.jpg -------------------------------------------------------------------------------- /src/assets/images/home-bg-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elitedev201/React-Dating-app/11b2954e5de0d2504c41abaafbf12b417e1a6f32/src/assets/images/home-bg-3.jpg -------------------------------------------------------------------------------- /src/assets/images/mamie.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elitedev201/React-Dating-app/11b2954e5de0d2504c41abaafbf12b417e1a6f32/src/assets/images/mamie.jpeg -------------------------------------------------------------------------------- /src/assets/images/pink-bg-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elitedev201/React-Dating-app/11b2954e5de0d2504c41abaafbf12b417e1a6f32/src/assets/images/pink-bg-1.jpg -------------------------------------------------------------------------------- /src/assets/images/pink-bg-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elitedev201/React-Dating-app/11b2954e5de0d2504c41abaafbf12b417e1a6f32/src/assets/images/pink-bg-2.jpg -------------------------------------------------------------------------------- /src/assets/images/pink-bg-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elitedev201/React-Dating-app/11b2954e5de0d2504c41abaafbf12b417e1a6f32/src/assets/images/pink-bg-3.jpg -------------------------------------------------------------------------------- /src/components/ResetforgotPassword/index.js: -------------------------------------------------------------------------------- 1 | import ResetForgotPassword from './resetforgotpassword-view'; 2 | 3 | export default ResetForgotPassword; 4 | -------------------------------------------------------------------------------- /src/components/ResetforgotPassword/resetforgotpassword-container.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import axios from 'axios'; 3 | import { toast } from 'react-toastify'; 4 | 5 | const usePasswordForm = (callback, token) => { 6 | const [inputs, setInputs] = useState({ 7 | password1: '', 8 | password2: '', 9 | }); 10 | const { password1, password2 } = inputs; 11 | 12 | const handleSubmit = event => { 13 | if (event) { 14 | event.preventDefault(); 15 | if (password1 !== password2) { 16 | toast.error("The passwords doesn't match"); 17 | return; 18 | } 19 | axios 20 | .post( 21 | `${process.env.REACT_APP_PUBLIC_API_URL}/validation/forgotpasswordupdate/${token}`, 22 | { 23 | password: password1, 24 | }, 25 | { 26 | headers: { 27 | 'Content-type': 'application/json; charset=UTF-8', 28 | }, 29 | }, 30 | ) 31 | .then(({ data }) => { 32 | if (data.success === true) { 33 | callback(true); 34 | } else { 35 | toast.error(data.err); 36 | } 37 | }); 38 | } 39 | }; 40 | 41 | const handleInputChange = event => { 42 | event.persist(); 43 | const newInput = { 44 | ...inputs, 45 | [event.target.name]: event.target.value, 46 | }; 47 | setInputs(newInput); 48 | }; 49 | return { 50 | handleSubmit, 51 | handleInputChange, 52 | inputs, 53 | }; 54 | }; 55 | 56 | export default usePasswordForm; 57 | -------------------------------------------------------------------------------- /src/components/ResetforgotPassword/resetforgotpassword-view.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import React, { useState } from 'react'; 3 | import PropTypes from 'prop-types'; 4 | import Avatar from '@material-ui/core/Avatar'; 5 | import LockOutlinedIcon from '@material-ui/icons/LockOutlined'; 6 | import TextField from '@material-ui/core/TextField'; 7 | import Button from '@material-ui/core/Button'; 8 | import Container from '@material-ui/core/Container'; 9 | import Typography from '@material-ui/core/Typography'; 10 | import { makeStyles } from '@material-ui/core/styles'; 11 | import usePasswordForm from './resetforgotpassword-container'; 12 | 13 | const useStyles = makeStyles(theme => ({ 14 | '@global': { 15 | body: { 16 | backgroundColor: theme.palette.common.white, 17 | }, 18 | }, 19 | paper: { 20 | marginTop: theme.spacing(8), 21 | display: 'flex', 22 | flexDirection: 'column', 23 | alignItems: 'center', 24 | }, 25 | avatar: { 26 | margin: theme.spacing(1), 27 | backgroundColor: theme.palette.secondary.main, 28 | }, 29 | form: { 30 | width: '100%', 31 | marginTop: theme.spacing(1), 32 | }, 33 | submit: { 34 | margin: theme.spacing(3, 0, 2), 35 | }, 36 | })); 37 | 38 | const ResetForgotPassword = ({ computedMatch }) => { 39 | const classes = useStyles(); 40 | const callback = success => { 41 | if (success) { 42 | window.location = '/?message=reset_password_success'; 43 | } 44 | }; 45 | const { token } = computedMatch.params; 46 | const { inputs, handleInputChange, handleSubmit } = usePasswordForm( 47 | callback, 48 | token, 49 | ); 50 | const [validToken, setValidToken] = useState(false); 51 | if (!validToken) { 52 | axios 53 | .get( 54 | `${process.env.REACT_APP_PUBLIC_API_URL}/validation/forgotpassword/${token}`, 55 | { 56 | headers: { 57 | 'Content-type': 'application/json; charset=UTF-8', 58 | }, 59 | }, 60 | ) 61 | .then(data => { 62 | if (data.data.success) { 63 | setValidToken(true); 64 | } else { 65 | window.location = '/?message=user_not_validated'; 66 | } 67 | }); 68 | } 69 | if (validToken === true) { 70 | return ( 71 | 72 |
73 | 74 | 75 | 76 | 77 | Change your password 78 | 79 |
80 | 94 | 108 | 117 | 118 |
119 |
120 | ); 121 | } 122 | return

Chargement en cours

; 123 | }; 124 | ResetForgotPassword.propTypes = { 125 | // eslint-disable-next-line react/forbid-prop-types 126 | computedMatch: PropTypes.object.isRequired, 127 | }; 128 | 129 | export default ResetForgotPassword; 130 | -------------------------------------------------------------------------------- /src/components/app/App.css: -------------------------------------------------------------------------------- 1 | /* .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | } 8 | 9 | .App-header { 10 | background-color: #282c34; 11 | min-height: 100vh; 12 | display: flex; 13 | flex-direction: column; 14 | align-items: center; 15 | justify-content: center; 16 | font-size: calc(10px + 2vmin); 17 | color: white; 18 | } 19 | 20 | .App-link { 21 | color: #09d3ac; 22 | } */ 23 | /* 24 | body { 25 | font-family: 'roboto'; 26 | margin: 0px; 27 | } */ 28 | -------------------------------------------------------------------------------- /src/components/app/App.js: -------------------------------------------------------------------------------- 1 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; 2 | import { ToastContainer } from 'react-toastify'; 3 | import React from 'react'; 4 | import Signup from '../signup'; 5 | import './App.css'; 6 | import Login from '../login'; 7 | import Profile from '../profile'; 8 | import ProfileShow from '../profileshow'; 9 | import Search from '../search'; 10 | import Suggestions from '../suggestions'; 11 | import Visit from '../visit'; 12 | import Like from '../like'; 13 | import Home from '../home'; 14 | import { AuthProvider } from './AuthContext'; 15 | import SecureRoute from './SecureRoute'; 16 | import NotLoggedRoute from './NotLoggedRoute'; 17 | import 'react-toastify/dist/ReactToastify.css'; 18 | import Nav from '../nav'; 19 | import Toaster from '../toaster'; 20 | import UserValidation from '../uservalidation'; 21 | import ResetForgotPassword from '../ResetforgotPassword'; 22 | import ForgotPassword from '../forgotpassword'; 23 | import Chat from '../chat'; 24 | import ChatRoom from '../chatroom'; 25 | 26 | function App() { 27 | return ( 28 | 29 | 30 |