├── .editorconfig ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── backend ├── .env.test ├── .eslintignore ├── .eslintrc.js ├── .jshintignore ├── Dockerfile ├── Dockerfile-test ├── __tests__ │ └── backend.test.js ├── app.js ├── config │ ├── config.js │ └── env.js ├── jest.config.js ├── package-lock.json ├── package.json ├── setupTests.js ├── src │ ├── app.js │ ├── functions │ │ ├── post.js │ │ ├── reservation.js │ │ └── user.js │ ├── models │ │ ├── post.js │ │ ├── reservation.js │ │ └── user.js │ └── routes │ │ └── routes.js └── stryker.conf.js ├── docker-compose.test.yml ├── docker-compose.yml ├── frontend ├── .eslintignore ├── .eslintrc.js ├── Dockerfile ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.js │ ├── __tests__ │ └── App.test.js │ ├── components │ ├── App │ │ ├── App.js │ │ └── NotFound.js │ ├── Footer │ │ └── Footer.js │ ├── Header │ │ └── Header.js │ ├── Home │ │ └── Home.js │ ├── Login │ │ └── Login.js │ └── Post │ │ ├── Create.js │ │ ├── List.js │ │ └── Post.js │ ├── config │ └── config.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── serviceWorker.js │ └── styles.css ├── nginx ├── Dockerfile └── config.conf ├── package-lock.json ├── pool.ntp.org └── test.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,css,scss,html}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | dist/ 3 | 4 | # Node stuff 5 | node_modules 6 | .npm 7 | .node_repl_history 8 | .lock-wscript 9 | logs 10 | *.log 11 | npm-debug.log* 12 | 13 | # OS stuff 14 | Desktop.ini 15 | ehthumbs.db 16 | Thumbs.db 17 | $RECYCLE.BIN/ 18 | ._* 19 | .DS_Store 20 | .Spotlight-V100 21 | .Trashes 22 | 23 | server/config/ 24 | backend/src/config/config.js 25 | backend/config/config.js 26 | frontend/src/config/config.js 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimCoderYoutube/AirbnbClone/536f302814f2676fd5973b4fba6d6a61ba8be547/CHANGELOG.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Eugene Cheung 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Airbnb Clone 2 | 3 | Airbnb clone developed on the simcoder youtube chanel. It is made using MERN and firebase for the Auth system. 4 | 5 | ## Getting Started 6 | 7 | These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. 8 | 9 | 10 | ### Prerequisites 11 | 12 | What things you need to install the software and how to install them 13 | 14 | 1. Docker-Compose 15 | 16 | 17 | > check the following [link](https://docs.docker.com/compose/install/) on how to install docker-compose 18 | 19 | ## Setting up firebase 20 | 21 | 1. Go to your firebase dashboard -> authentication -> sign-in method and enable google 22 | 23 | 2. Go to firebase dashboard -> project Settings -> add web app and get the contents of firebaseConfig pasting them onto the variable firebaseConfig in frontend/src/Login/Login.js 24 | 25 | 3. Go to your firebase dashboard -> Project Settings -> Service accounts 26 | 1. Generate new private key copy the content and paste it into backend/src/config/serviceAccountKey.json 27 | 2. Go to backend/src/functions/user.js and change the admin.initializeApp() content for the one in the service account page. 28 | 29 | ## Deployment 30 | 31 | A step by step series of examples that tell you how to get a development env running. 32 | 33 | ``` 34 | > sudo systemctl start docker (or the equivalemnt for your OS) 35 | > sudo docker-compose up --build 36 | ``` 37 | 38 | ## Authors 39 | 40 | * **SimCoder** - *Main Dev* - [Simcoder](https://simcoder.com) 41 | 42 | 43 | ## License 44 | 45 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /backend/.env.test: -------------------------------------------------------------------------------- 1 | DB_HOST=localhost 2 | DB_PORT=27017 3 | DB_URI=mongodb://mongodb 4 | DB_USER= 5 | DB_PASS= 6 | DB_NAME=test 7 | SESSION_SECRET=ldso 8 | PORT=6200 9 | NODE_OPTIONS=--max_old_space_size=4096 -------------------------------------------------------------------------------- /backend/.eslintignore: -------------------------------------------------------------------------------- 1 | *.test* -------------------------------------------------------------------------------- /backend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | }, 6 | extends: [ 7 | 'airbnb-base', 8 | ], 9 | globals: { 10 | Atomics: 'readonly', 11 | SharedArrayBuffer: 'readonly', 12 | }, 13 | parserOptions: { 14 | ecmaVersion: 2018, 15 | sourceType: 'module', 16 | }, 17 | rules: { 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /backend/.jshintignore: -------------------------------------------------------------------------------- 1 | src/ -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | # base image 2 | FROM node:12.2.0-alpine 3 | 4 | # set working directory 5 | WORKDIR /app 6 | 7 | # install and cache app dependencies 8 | COPY package*.json ./ 9 | 10 | RUN npm install --verbose 11 | 12 | COPY ./src ./src 13 | COPY ./config ./config 14 | COPY app.js ./ 15 | 16 | RUN npm install -g nodemon 17 | 18 | 19 | EXPOSE 6200 20 | 21 | # start app 22 | CMD ["npm", "start"] 23 | -------------------------------------------------------------------------------- /backend/Dockerfile-test: -------------------------------------------------------------------------------- 1 | FROM node:8 2 | # set working directory 3 | WORKDIR /app 4 | 5 | 6 | 7 | # install and cache app dependencies 8 | COPY package*.json ./ 9 | 10 | RUN npm install 11 | 12 | COPY ./src ./src 13 | COPY ./__tests__ ./__tests__ 14 | COPY .env* ./ 15 | 16 | COPY .eslintignore/ ./ 17 | COPY .eslintrc.js/ ./ 18 | COPY .jshintignore/ ./ 19 | COPY stryker.conf.js/ ./ 20 | COPY jest.config.js . 21 | COPY setupTests.js . 22 | 23 | 24 | EXPOSE 6200 25 | 26 | # start app 27 | CMD ["npm", "run", "test-docker"] -------------------------------------------------------------------------------- /backend/__tests__/backend.test.js: -------------------------------------------------------------------------------- 1 | jest.setTimeout(40000); 2 | 3 | 4 | describe("Testing backend", () => { 5 | 6 | it("Clear Everything", async () => { 7 | }); 8 | 9 | }); 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /backend/app.js: -------------------------------------------------------------------------------- 1 | //Libraries 2 | var express = require('express'); 3 | var mongoose = require('mongoose'); 4 | var bodyParser = require('body-parser'); 5 | var cors = require('cors'); 6 | var admin = require('firebase-admin'); 7 | 8 | 9 | var serviceAccount = require("./config/config.js").serviceAccount; 10 | var databaseURL = require("./config/config.js").databaseURL; 11 | 12 | admin.initializeApp({ 13 | credential: admin.credential.cert(serviceAccount), 14 | databaseURL 15 | }); 16 | 17 | //server configuration 18 | var basePath = '/'; 19 | var port = 6200; 20 | 21 | 22 | // Connection to DB 23 | mongoose.connect('mongodb://mongodb') 24 | .then(() => { 25 | console.log('Backend Started'); 26 | }) 27 | .catch(err => { 28 | console.error('Backend error:', err.stack); 29 | process.exit(1); 30 | }); 31 | 32 | 33 | var app = express(); 34 | 35 | 36 | app.use((req, res, next) => { 37 | res.header('Access-Control-Allow-Origin', '*'); 38 | next(); 39 | }); 40 | 41 | app.use(express.static('public')); 42 | app.options(cors()); 43 | app.use(bodyParser.urlencoded({extended: true})); 44 | app.use(bodyParser.json()); 45 | // Routes and Backend Functionalities 46 | var routes = require('./src/routes/routes'); 47 | app.use(basePath, routes); 48 | 49 | 50 | app.listen(port, () => { 51 | console.log('Backend running on Port: ',port); 52 | }); 53 | 54 | -------------------------------------------------------------------------------- /backend/config/config.js: -------------------------------------------------------------------------------- 1 | databaseURL = '****' 2 | serviceAccount = { 3 | "type": "****", 4 | "project_id": "****", 5 | "private_key_id": "****", 6 | "private_key": "****", 7 | "client_email": "****", 8 | "client_id": "****", 9 | "auth_uri": "****", 10 | "token_uri": "****", 11 | "auth_provider_x509_cert_url": "****", 12 | "client_x509_cert_url": "****" 13 | } 14 | 15 | exports.databaseURL = databaseURL; 16 | exports.serviceAccount = serviceAccount; 17 | -------------------------------------------------------------------------------- /backend/config/env.js: -------------------------------------------------------------------------------- 1 | require('dotenv-flow').config(); 2 | 3 | module.exports = Object.freeze({ 4 | // Database 5 | db_host: process.env.DB_HOST, 6 | db_port: process.env.DB_PORT, 7 | db_uri: process.env.DB_URI, 8 | db_user: process.env.DB_USER, 9 | db_pass: process.env.DB_PASS, 10 | db_name: process.env.DB_NAME, 11 | 12 | session_secret: process.env.SESSION_SECRET, 13 | 14 | port: process.env.PORT, 15 | }); 16 | -------------------------------------------------------------------------------- /backend/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | clearMocks: true, 3 | coverageDirectory: 'coverage', 4 | setupFilesAfterEnv: [ 5 | '/setupTests.js', 6 | ], 7 | testEnvironment: 'node', 8 | }; 9 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "nodemon", 8 | "start": "nodemon app.js", 9 | "test-react": "react-scripts test", 10 | "lint": "eslint . ", 11 | "lint:fix": "eslint . --fix", 12 | "test": "jest", 13 | "test-docker": "jest --detectOpenHandles --collectCoverage --runInBand", 14 | "test-coverage": "jest --coverage", 15 | "pre:push": "set CI=true && npm run test && npm run lint", 16 | "pre:commit": "npm run lint && npm run test" 17 | }, 18 | "husky": { 19 | "hooks": {} 20 | }, 21 | "keywords": [], 22 | "author": "", 23 | "license": "ISC", 24 | "devDependencies": { 25 | "@stryker-mutator/core": "^2.1.0", 26 | "@stryker-mutator/html-reporter": "^2.1.0", 27 | "@stryker-mutator/javascript-mutator": "^2.1.0", 28 | "@stryker-mutator/jest-runner": "^2.1.0", 29 | "babel-jest": "^24.9.0", 30 | "eslint": "^6.5.1", 31 | "eslint-config-airbnb-base": "^14.0.0", 32 | "eslint-plugin-import": "^2.18.2", 33 | "eslint-plugin-jsx-a11y": "^6.2.3", 34 | "eslint-plugin-react-hooks": "^1.7.0", 35 | "husky": "^2.7.0", 36 | "jest": "^24.9.0", 37 | "nodemon": "^2.0.2", 38 | "supertest": "^4.0.2" 39 | }, 40 | "dependencies": { 41 | "@google-cloud/storage": "^4.7.0", 42 | "@sentry/node": "^5.10.2", 43 | "bcryptjs": "^2.4.3", 44 | "body-parser": "^1.19.0", 45 | "cors": "^2.8.5", 46 | "dotenv-flow": "^3.1.0", 47 | "eslint-friendly-formatter": "^4.0.1", 48 | "eslint-plugin-html": "^5.0.5", 49 | "express": "^4.17.1", 50 | "firebase": "^7.2.2", 51 | "firebase-admin": "^8.6.1", 52 | "mongoose": "^5.9.1", 53 | "papaparse": "^5.1.0" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /backend/setupTests.js: -------------------------------------------------------------------------------- 1 | //---------------------------------- 2 | // Base file for unit test utilities 3 | //---------------------------------- 4 | 5 | // Setting up the end-to-end request testing helper methods 6 | const supertestRequest = require('supertest'); 7 | const mongoose = require('mongoose'); 8 | const app = require('./src/app.js'); 9 | 10 | // During the test the env variable must be set to test 11 | if (process.env.NODE_ENV !== 'test') { 12 | console.error('Entered test files without being in the test environment, aborting!'); 13 | process.exit(process.env.NODE_ENV); 14 | } 15 | 16 | 17 | // Occurs before all the tests, only once 18 | beforeAll(async () => { 19 | await new Promise((resolve) => { 20 | mongoose.connection.once('open', () => { 21 | resolve(); 22 | }); 23 | }); 24 | }); 25 | 26 | 27 | afterAll(async () => { 28 | await (mongoose.connection.close()); 29 | }); 30 | 31 | 32 | const request = () => supertestRequest(app); 33 | const agent = () => supertestRequest.agent(app); 34 | 35 | global.request = request; 36 | global.agent = agent; 37 | -------------------------------------------------------------------------------- /backend/src/app.js: -------------------------------------------------------------------------------- 1 | const Sentry = require('@sentry/node'); 2 | 3 | // Libraries 4 | const express = require('express'); 5 | 6 | const mongoose = require('mongoose'); 7 | 8 | const bodyParser = require('body-parser'); 9 | 10 | const cors = require('cors'); 11 | 12 | const path = require('path'); 13 | const config = require('./config/env'); 14 | 15 | 16 | // Connection to DB 17 | 18 | const options = { 19 | dbName: config.db_name, 20 | user: config.db_user, 21 | pass: config.db_pass, 22 | useNewUrlParser: true, 23 | useCreateIndex: true, 24 | socketTimeoutMS: 30000, 25 | keepAlive: true, 26 | reconnectTries: 30000, 27 | useUnifiedTopology: true, 28 | }; 29 | 30 | // server configuration 31 | const basePath = '/'; 32 | const port = process.env.PORT || config.port; 33 | 34 | const connectionUri = config.db_uri || `mongodb+srv://${config.db_host}:${config.db_port}`; 35 | mongoose.connect(connectionUri, options) 36 | .then(() => { 37 | console.log('Backend Started'); 38 | }) 39 | .catch((err) => { 40 | console.error('Backend error:', err.stack); 41 | process.exit(1); 42 | }); 43 | 44 | // App Instance 45 | const app = express(); 46 | 47 | Sentry.init({ dsn: 'https://e93f4a23ac5d4bd692591ae1acb8adf0@sentry.io/1795620' }); 48 | 49 | // The request handler must be the first middleware on the app 50 | app.use(Sentry.Handlers.requestHandler()); 51 | 52 | app.use(express.static(path.join(__dirname, '../public/react-build'))); 53 | 54 | // Routes and Backend Functionalities 55 | const routes = require('./routes/routes'); 56 | 57 | 58 | app.use(cors()); 59 | app.use(bodyParser.urlencoded({ extended: true })); 60 | app.use(bodyParser.json()); 61 | app.use(basePath, routes); 62 | 63 | // The error handler must be before any other error middleware and after all controllers 64 | app.use(Sentry.Handlers.errorHandler()); 65 | 66 | // Optional fallthrough error handler 67 | app.use((err, req, res) => { 68 | // The error id is attached to `res.sentry` to be returned 69 | // and optionally displayed to the user for support. 70 | res.statusCode = 500; 71 | // res.end(`${res.sentry}\n`); 72 | }); 73 | 74 | // Execute App 75 | if (process.env.NODE_ENV !== 'test') { 76 | app.listen(port, () => { 77 | console.log('AreYouOk Backend running on Port: ', port); 78 | }); 79 | } 80 | -------------------------------------------------------------------------------- /backend/src/functions/post.js: -------------------------------------------------------------------------------- 1 | const userModel = require('../models/user'); 2 | const postModel = require('../models/post'); 3 | const userFunc = require('./user'); 4 | 5 | module.exports = { 6 | createPost: (user, idToken, title, description, location, numberOfPeople, pricePerNight, imageUrlList) => new Promise((resolve, reject) => { 7 | console.log(idToken) 8 | userFunc.verifyAccount(user, idToken)//Verify if user is correctly logged in 9 | .then(result => { 10 | console.log({ result }) 11 | //Create and save post object 12 | const mPost = new postModel({ title, description, location, numberOfPeople, pricePerNight, imageUrlList }); 13 | mPost.save(); 14 | 15 | //find user by the id of the result and push the post the posts entry in the user document 16 | userModel.findOneAndUpdate({ _id: result.user._id }, { $push: { posts: [mPost] } }, { new: true, useFindAndModify: false }) 17 | .then(result => { 18 | console.log(result) 19 | resolve({ 20 | code: 200, 21 | success: true, 22 | message: 'Post create successfully', 23 | post: result, 24 | }) 25 | }) 26 | .catch(error => { 27 | reject(new Error(error)) 28 | }); 29 | }) 30 | .catch(error => { 31 | reject(new Error(error)) 32 | }); 33 | }), 34 | 35 | listPost: () => new Promise((resolve, reject) => { 36 | postModel.find() 37 | .then(result => { 38 | console.log(result) 39 | resolve({ 40 | code: 200, 41 | success: true, 42 | message: 'Post(s) found successfully', 43 | post_list: result, 44 | }) 45 | }) 46 | .catch(error => { 47 | reject(new Error(error)) 48 | }) 49 | }), 50 | 51 | getById: (id) => new Promise((resolve, reject) => { 52 | postModel.findById(id). 53 | populate('reservations'). 54 | exec(function (err, post) { 55 | if (err) reject(new Error(err)); 56 | resolve(post); 57 | }); 58 | }), 59 | 60 | } -------------------------------------------------------------------------------- /backend/src/functions/reservation.js: -------------------------------------------------------------------------------- 1 | const userModel = require('../models/user'); 2 | const postModel = require('../models/post'); 3 | const reservationModel = require('../models/reservation'); 4 | const userFunc = require('./user'); 5 | const postFunc = require('./post'); 6 | 7 | module.exports = { 8 | create: (id, user, idToken, dateStart, dateEnd) => new Promise((resolve, reject) => { 9 | module.exports.validate(id, dateStart, dateEnd) 10 | .then(() => { 11 | userFunc.verifyAccount(user, idToken)//Verify if user is correctly logged in 12 | .then(result => { 13 | 14 | //Create and save post object 15 | const mReservation = new reservationModel({ post: id, creator: result._id, dateStart, dateEnd }); 16 | mReservation.save(); 17 | 18 | //find user by the id of the result and push the reservation entry in the user document 19 | userModel.findByIdAndUpdate(result.user._id, { $push: { reservations: [mReservation] } }, { new: true, useFindAndModify: false }) 20 | .then(() => { 21 | //find post by the id of the result and push the reservation entry in the post document 22 | postModel.findByIdAndUpdate(id, { $push: { reservations: [mReservation] } }, { new: true, useFindAndModify: false }) 23 | .then(result => { 24 | console.log("success") 25 | resolve(result) 26 | }) 27 | .catch(error => { 28 | reject(new Error(error)) 29 | }); 30 | }) 31 | .catch(error => { 32 | reject(new Error(error)) 33 | }); 34 | }) 35 | .catch(error => { 36 | reject(new Error(error)) 37 | }); 38 | }) 39 | .catch(error => { 40 | console.log(error) 41 | reject(new Error(error)) 42 | }); 43 | }), 44 | 45 | validate: (id, dateStart, dateEnd) => new Promise((resolve, reject) => { 46 | postFunc.getById(id) 47 | .then(result => { 48 | dateStart = new Date(dateStart) 49 | dateEnd = new Date(dateEnd) 50 | 51 | for (let i = 0; i < result.reservations.length; i++) { 52 | 53 | console.log(result.reservations[i].dateStart) 54 | console.log(result.reservations[i].dateEnd) 55 | if (dateStart < result.reservations[i].dateStart && dateEnd > result.reservations[i].dateEnd) { 56 | reject(new Error("Already reserved")) 57 | } 58 | } 59 | 60 | resolve() 61 | }) 62 | .catch(error => { 63 | reject(new Error(error)) 64 | }); 65 | }), 66 | list: () => new Promise((resolve, reject) => { 67 | reservationModel.find() 68 | .then(result => { 69 | console.log(result) 70 | resolve(result) 71 | }) 72 | .catch(error => { 73 | reject(new Error(error)) 74 | }) 75 | }) 76 | } -------------------------------------------------------------------------------- /backend/src/functions/user.js: -------------------------------------------------------------------------------- 1 | var admin = require('firebase-admin'); 2 | 3 | const userModel = require('../models/user'); 4 | 5 | module.exports = { 6 | verifyAccount: function (user, idToken) { 7 | return new Promise(function (resolve, reject) { 8 | if (user === undefined) { 9 | return reject({ 10 | code: 401, 11 | success: false, 12 | message: "auth denied" 13 | }); 14 | } 15 | //Place the user info that reached this point in a string into an object 16 | userJson = JSON.parse(user); 17 | console.log(idToken) 18 | 19 | //Check if the user token is valid, this will confirm the user is correctly logged in 20 | admin.auth().verifyIdToken(idToken) 21 | .then(function (decodedToken) { 22 | userModel.findOne({ googleId: userJson.uid }).then(function (user) { 23 | 24 | console.log(user) 25 | //Check if user exists 26 | if (!user) { 27 | 28 | //Create user and save it to the databse 29 | new userModel({ 30 | name: userJson.displayName, 31 | picture: userJson.photoURL, 32 | email: userJson.email, 33 | googleId: userJson.uid 34 | }).save().then(function (error, user) { 35 | resolve({ 36 | code: 200, 37 | success: true, 38 | message: "auth confirmed: new user created", 39 | user: user 40 | }) 41 | }) 42 | } else { 43 | resolve({ 44 | code: 200, 45 | success: true, 46 | message: "auth confirmed: existing user", 47 | user: user 48 | }) 49 | } 50 | }) 51 | }).catch(function (error) { 52 | console.log(error) 53 | reject({ 54 | code: 401, 55 | success: false, 56 | message: "auth denied", 57 | error: error 58 | }) 59 | }); 60 | }); 61 | }, 62 | list: () => new Promise((resolve, reject) => { 63 | userModel.find() 64 | .then(result => { 65 | console.log(result) 66 | resolve(result) 67 | }) 68 | .catch(error => { 69 | reject(new Error(error)) 70 | }) 71 | }), 72 | }; 73 | -------------------------------------------------------------------------------- /backend/src/models/post.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const schema = mongoose.Schema 3 | mongoose.promise = Promise 4 | 5 | const postSchema = new schema({ 6 | title: { type: String, unique: false }, 7 | description: { type: String, unique: false }, 8 | location: { type: String, unique: false }, 9 | numberOfPeople: { type: Number, unique: false }, 10 | pricePerNight: { type: Number, unique: false }, 11 | creator: { type: mongoose.Schema.Types.ObjectId, ref: 'user' }, 12 | imageUrlList: [{ 13 | type: String 14 | }], 15 | reservations:[ { type: mongoose.Schema.Types.ObjectId, ref: 'reservation' }], 16 | }) 17 | 18 | const post = mongoose.model('post', postSchema) 19 | module.exports = post -------------------------------------------------------------------------------- /backend/src/models/reservation.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const schema = mongoose.Schema 3 | mongoose.promise = Promise 4 | 5 | const reservationSchema = new schema({ 6 | dateStart: {type: Date}, 7 | dateEnd: {type: Date}, 8 | creator: { type: mongoose.Schema.Types.ObjectId, ref: 'user' }, 9 | post: { type: mongoose.Schema.Types.ObjectId, ref: 'post' }, 10 | }) 11 | 12 | const reservation = mongoose.model('reservation', reservationSchema) 13 | module.exports = reservation -------------------------------------------------------------------------------- /backend/src/models/user.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const schema = mongoose.Schema 3 | mongoose.promise = Promise 4 | 5 | const userSchema = new schema({ 6 | name : {type: String, unique: false}, 7 | email : {type: String, unique: false}, 8 | googleId : {type: String, unique: true}, 9 | posts : [{type: mongoose.Schema.Types.ObjectId, ref: 'user'}], 10 | reservations:[ { type: mongoose.Schema.Types.ObjectId, ref: 'reservation' }], 11 | }) 12 | 13 | const user = mongoose.model('user', userSchema) 14 | module.exports = user -------------------------------------------------------------------------------- /backend/src/routes/routes.js: -------------------------------------------------------------------------------- 1 | const router = require('express').Router(); 2 | 3 | const userFunc = require('../functions/user') 4 | const postFunc = require('../functions/post') 5 | const reservationFunc = require('../functions/reservation') 6 | 7 | 8 | /** 9 | * Endpoint responsible for calling the verifyAccount function 10 | * and sending back the response from it 11 | */ 12 | router.route('/api/user/check').post(function (req, res) { 13 | const { user, idToken } = req.query; 14 | userFunc.verifyAccount(user, idToken) 15 | .then(function (callback) { 16 | res.json(callback); 17 | }).catch(error => { 18 | res.json(error) 19 | }); 20 | }); 21 | 22 | /** 23 | * Endpoint that lists all users 24 | */ 25 | router.route('/api/user/list').get(function (req, res) { 26 | userFunc.list() 27 | .then(function (callback) { 28 | res.json(callback); 29 | }).catch(error => { 30 | res.json(error) 31 | }); 32 | }); 33 | 34 | /** 35 | * Endpoint responsible for calling the createPost function 36 | * and sending back the response from it 37 | * 38 | * @param user - user object 39 | * @param title - title of the post 40 | * @param description - description of the post 41 | * @param location - location of the post 42 | * @param numberOfPeople - numberOfPeople of the post 43 | * @param pricePerNight - pricePerNight of the post 44 | */ 45 | router.route('/api/post/create').post(function (req, res) { 46 | const { user, idToken, title, description, location, numberOfPeople, pricePerNight, downloadUrlList } = req.query; 47 | postFunc.createPost(user, idToken, title, description, location, numberOfPeople, pricePerNight, downloadUrlList) 48 | .then(result => { 49 | res.json(result); 50 | }) 51 | .catch(error => { 52 | res.json(error); 53 | }) 54 | }); 55 | 56 | 57 | /** 58 | * Endpoint responsible for calling the listpost function 59 | * and sending back the response from it. 60 | * 61 | * It will list all the posts available according to certain params 62 | */ 63 | router.route('/api/post/list').get(function (req, res) { 64 | postFunc.listPost() 65 | .then(result => { 66 | res.json(result); 67 | }) 68 | .catch(error => { 69 | res.json(error); 70 | }) 71 | }); 72 | 73 | /** 74 | * Endpoint responsible for calling the getById function 75 | * and sending back the response from it. 76 | * 77 | * It will return the post with the id in the request available according to certain params 78 | * 79 | * @param id - post id 80 | */ 81 | router.route('/api/post/get').get(function (req, res) { 82 | const { id } = req.query; 83 | console.log(id) 84 | 85 | postFunc.getById(id) 86 | .then(result => { 87 | res.json(result); 88 | }) 89 | .catch(error => { 90 | res.json(error); 91 | }) 92 | }); 93 | 94 | 95 | /** 96 | * Endpoint responsible creating a reservation for the user 97 | * 98 | * @param id - post id 99 | * @param user - userObject 100 | * 101 | */ 102 | router.route('/api/reservation/create').post(function (req, res) { 103 | const { id, user, idToken, dateStart, dateEnd } = req.query; 104 | 105 | reservationFunc.create(id, user, idToken, dateStart, dateEnd) 106 | .then(result => { 107 | res.json(result); 108 | }) 109 | .catch(error => { 110 | console.log(error) 111 | res.json(403, error); 112 | }) 113 | }); 114 | 115 | /** 116 | * Endpoint that lists all reservations 117 | */ 118 | router.route('/api/reservation/list').get(function (req, res) { 119 | reservationFunc.list() 120 | .then(function (callback) { 121 | res.json(callback); 122 | }).catch(error => { 123 | res.json(error) 124 | }); 125 | }); 126 | 127 | 128 | 129 | module.exports = router; 130 | 131 | 132 | -------------------------------------------------------------------------------- /backend/stryker.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | mutator: "javascript", 4 | packageManager: "npm", 5 | reporters: ["html", "clear-text", "progress", "dashboard"], 6 | testRunner: "jest", 7 | transpilers: [], 8 | coverageAnalysis: "off", 9 | files: [ 10 | "**/*", 11 | "!node_modules/**/*" 12 | ], 13 | mutate: [ "__tests__/*"], 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /docker-compose.test.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | 3 | services: 4 | 5 | mongodb: 6 | image: "mongo" 7 | ports: 8 | - "27017:27017" 9 | test: 10 | container_name: test 11 | build: 12 | context: ./backend/. 13 | dockerfile: ./Dockerfile-test 14 | image: test:latest 15 | ports: 16 | - '6201:6201' 17 | environment: 18 | - NODE_ENV=test 19 | volumes: 20 | - ./backend/__tests__:/app/__tests__ 21 | - ./backend/src:/app/src 22 | - ./backend/setupTests.js:/app/setupTests.js 23 | depends_on: 24 | - mongodb 25 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | services: 4 | mongodb: 5 | image: "mongo" 6 | ports: 7 | - "27017:27017" 8 | environment: 9 | - VIRTUAL_PORT=27017 10 | 11 | backend: 12 | build: ./backend/ 13 | expose: 14 | - 6200 15 | ports: 16 | - "6200:6200" 17 | volumes: 18 | - ./backend/src:/app/src 19 | environment: 20 | - NODE_ENV=development 21 | # - VIRTUAL_HOST=api.example.com,www.api.example.com 22 | # - VIRTUAL_PORT=6200 23 | # - LETSENCRYPT_HOST=api.example.com 24 | # - LETSENCRYPT_EMAIL=example@mail.com 25 | 26 | depends_on: 27 | - mongodb 28 | - frontend 29 | 30 | 31 | frontend: 32 | build: ./frontend/ 33 | expose: 34 | - 3000 35 | ports: 36 | - "3000:3000" 37 | volumes: 38 | - ./frontend/src:/app/src 39 | - ./frontend/public:/app/public 40 | environment: 41 | - NODE_ENV=development 42 | # - VIRTUAL_HOST=example.com,www.example.com 43 | # - VIRTUAL_PORT=3000 44 | # - LETSENCRYPT_HOST=example.com 45 | # - LETSENCRYPT_EMAIL=example@mail.com 46 | 47 | 48 | # nginx-proxy: 49 | # image: jwilder/nginx-proxy 50 | # ports: 51 | # - "80:80" 52 | # - "443:443" 53 | # volumes: 54 | # - "/etc/nginx/vhost.d" 55 | # - "/usr/share/nginx/html" 56 | # - "/var/run/docker.sock:/tmp/docker.sock:ro" 57 | # - "/etc/nginx/certs" 58 | 59 | # letsencrypt-nginx-proxy-companion: 60 | # image: jrcs/letsencrypt-nginx-proxy-companion 61 | # volumes: 62 | # - "/var/run/docker.sock:/var/run/docker.sock:ro" 63 | # volumes_from: 64 | # - "nginx-proxy" 65 | -------------------------------------------------------------------------------- /frontend/.eslintignore: -------------------------------------------------------------------------------- 1 | src/serviceWorker.js 2 | -------------------------------------------------------------------------------- /frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | jest:true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'airbnb', 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | }, 14 | parserOptions: { 15 | ecmaFeatures: { 16 | jsx: true, 17 | }, 18 | ecmaVersion: 2018, 19 | sourceType: 'module', 20 | }, 21 | plugins: [ 22 | 'react', 23 | ], 24 | rules: { 25 | "react/jsx-filename-extension":"off", 26 | }, 27 | 28 | }; 29 | -------------------------------------------------------------------------------- /frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | # base image 2 | FROM node:12.2.0-alpine 3 | 4 | # set working directory 5 | WORKDIR /app 6 | 7 | # install and cache app dependencies 8 | COPY package*.json ./ 9 | 10 | RUN npm install 11 | 12 | 13 | COPY ./src ./src 14 | COPY ./public ./public 15 | 16 | # start app 17 | CMD ["npm", "start"] 18 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `yarn start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `yarn test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `yarn build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `yarn eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `yarn build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /frontend/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@babel/preset-env', '@babel/preset-react'], 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.9.13", 7 | "axios": "^0.19.0", 8 | "bootstrap": "^4.3.1", 9 | "eslint-friendly-formatter": "^4.0.1", 10 | "eslint-plugin-html": "^5.0.5", 11 | "firebase": "^7.2.2", 12 | "firebase-admin": "^8.6.1", 13 | "mongoose": "^5.0.11", 14 | "postcss-loader": "^2.1.3", 15 | "react": "^16.2.0", 16 | "react-calendar": "^3.0.1", 17 | "react-dom": "^16.2.0", 18 | "react-firebaseui": "^4.0.0", 19 | "react-router-dom": "^5.1.2", 20 | "react-scripts": "3.2.0" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build": "react-scripts build", 25 | "test-react": "react-scripts test", 26 | "eject": "react-scripts eject", 27 | "lint": "eslint . ", 28 | "lint-fix": "eslint . --fix", 29 | "pre:push": "set CI=true && npm run test && npm run lint", 30 | "pre:commit": "npm run lint && npm run test", 31 | "test": "jest " 32 | }, 33 | "husky": { 34 | "hooks": {} 35 | }, 36 | "eslintConfig": { 37 | "extends": "react-app" 38 | }, 39 | "browserslist": { 40 | "production": [ 41 | ">0.2%", 42 | "not dead", 43 | "not op_mini all" 44 | ], 45 | "development": [ 46 | "last 1 chrome version", 47 | "last 1 firefox version", 48 | "last 1 safari version" 49 | ] 50 | }, 51 | "devDependencies": { 52 | "babel-jest": "^24.9.0", 53 | "enzyme": "^3.10.0", 54 | "enzyme-adapter-react-16": "^1.15.1", 55 | "enzyme-to-json": "^3.4.2", 56 | "eslint": "^6.5.1", 57 | "eslint-config-airbnb": "^18.0.1", 58 | "eslint-plugin-import": "^2.18.2", 59 | "eslint-plugin-jsx-a11y": "^6.2.3", 60 | "eslint-plugin-react": "^7.16.0", 61 | "eslint-plugin-react-hooks": "^1.7.0", 62 | "husky": "^3.0.9", 63 | "jest": "^24.9.0", 64 | "react-test-renderer": "^16.10.2" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimCoderYoutube/AirbnbClone/536f302814f2676fd5973b4fba6d6a61ba8be547/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | SimCoder - Airbnb 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimCoderYoutube/AirbnbClone/536f302814f2676fd5973b4fba6d6a61ba8be547/frontend/public/logo192.png -------------------------------------------------------------------------------- /frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SimCoderYoutube/AirbnbClone/536f302814f2676fd5973b4fba6d6a61ba8be547/frontend/public/logo512.png -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Simcoder_Airbnb", 3 | "name": "SimCoder - Airbnb Clone", 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 | -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /frontend/src/App.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { Component } from 'react' 3 | 4 | import { BrowserRouter as Router, Route } from 'react-router-dom'; 5 | import 'bootstrap/dist/css/bootstrap.min.css'; 6 | import './styles.css'; 7 | 8 | 9 | import Home from "./components/Home/Home"; 10 | import Create from "./components/Post/Create"; 11 | import List from "./components/Post/List"; 12 | import Post from "./components/Post/Post"; 13 | import Login from "./components/Login/Login"; 14 | import Header from './components/Header/Header'; 15 | import Footer from './components/Footer/Footer'; 16 | import firebase from 'firebase'; 17 | import axios from 'axios' 18 | 19 | export class App extends Component { 20 | 21 | componentDidMount() { 22 | let that = this 23 | firebase.auth().onAuthStateChanged(user => { 24 | 25 | if (user != null) { 26 | user.getIdToken(true).then(function (idToken) { 27 | axios.post('http://127.0.0.1:6200/api/user/check', null, { params: { user, idToken } }) 28 | .then(res => { 29 | console.log(res) 30 | that.forceUpdate() 31 | }) 32 | .catch(error => { 33 | that.forceUpdate() 34 | console.log(error) 35 | }) 36 | }).catch((error) => { 37 | that.forceUpdate() 38 | console.log(error) 39 | }) 40 | } 41 | 42 | 43 | 44 | }) 45 | } 46 | render() { 47 | return ( 48 | <> 49 | 50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 |