diff --git a/sports-matcher/client/package.json b/sports-matcher/client/package.json index 1e0beaf..1e67687 100644 --- a/sports-matcher/client/package.json +++ b/sports-matcher/client/package.json @@ -16,7 +16,7 @@ "web-vitals": "^2.1.4" }, "scripts": { - "start": "NODE_ENV=development API_HOST=http://localhost:5000 react-scripts start", + "start": "NODE_ENV='development' REACT_APP_API_HOST='http://localhost:5000' react-scripts start", "build": "../scripts/build.py", "test": "react-scripts test", "eject": "react-scripts eject" diff --git a/sports-matcher/client/public/images/carousel/basketball_normalized.jpg b/sports-matcher/client/public/images/carousel/basketball_normalized.jpg new file mode 100644 index 0000000..16b577e Binary files /dev/null and b/sports-matcher/client/public/images/carousel/basketball_normalized.jpg differ diff --git a/sports-matcher/client/public/images/carousel/rentals_normalized.jpg b/sports-matcher/client/public/images/carousel/rentals_normalized.jpg new file mode 100644 index 0000000..36ce398 Binary files /dev/null and b/sports-matcher/client/public/images/carousel/rentals_normalized.jpg differ diff --git a/sports-matcher/client/public/images/carousel/schedule_normalized.jpg b/sports-matcher/client/public/images/carousel/schedule_normalized.jpg new file mode 100644 index 0000000..20e5bbd Binary files /dev/null and b/sports-matcher/client/public/images/carousel/schedule_normalized.jpg differ diff --git a/sports-matcher/client/public/images/carousel/tennis_normalized.jpg b/sports-matcher/client/public/images/carousel/tennis_normalized.jpg new file mode 100644 index 0000000..957a623 Binary files /dev/null and b/sports-matcher/client/public/images/carousel/tennis_normalized.jpg differ diff --git a/sports-matcher/client/public/images/carousel/volleyball_normalized.jpg b/sports-matcher/client/public/images/carousel/volleyball_normalized.jpg new file mode 100644 index 0000000..800afc8 Binary files /dev/null and b/sports-matcher/client/public/images/carousel/volleyball_normalized.jpg differ diff --git a/sports-matcher/client/src/components/GameInfoCardDisplay.js b/sports-matcher/client/src/components/GameInfoCardDisplay.js deleted file mode 100644 index 29dae92..0000000 --- a/sports-matcher/client/src/components/GameInfoCardDisplay.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import propTypes from "prop-types"; -import GameInfoCard from "./GameInfoCard"; - -export default class GameInfoCardDisplay extends React.Component { - constructor(props) { - super(props); - - } - render() { - return ( -
- {this.props.recommendedMatches.map((match) => )} -
- ); - } -} - -GameInfoCardDisplay.propTypes = { - recommendedMatches: propTypes.array, -}; \ No newline at end of file diff --git a/sports-matcher/client/src/components/HomeCarousel.js b/sports-matcher/client/src/components/HomeCarousel.js index 4f667a8..b5a9bbf 100644 --- a/sports-matcher/client/src/components/HomeCarousel.js +++ b/sports-matcher/client/src/components/HomeCarousel.js @@ -1,18 +1,17 @@ import React from "react"; import { Carousel } from "react-bootstrap"; -import "../styles/HomeCarousel.css"; -export default class HomeCarousel extends React.Component{ +export default class HomeCarousel extends React.Component { constructor(props) { super(props); } render() { return ( - + Connect Slide
@@ -23,9 +22,9 @@ export default class HomeCarousel extends React.Component{ Schedule Slide
@@ -36,9 +35,9 @@ export default class HomeCarousel extends React.Component{ Rent Slide
diff --git a/sports-matcher/client/src/components/GameInfoCard.js b/sports-matcher/client/src/components/MatchInfoCard.js similarity index 65% rename from sports-matcher/client/src/components/GameInfoCard.js rename to sports-matcher/client/src/components/MatchInfoCard.js index 99dccf2..f99832f 100644 --- a/sports-matcher/client/src/components/GameInfoCard.js +++ b/sports-matcher/client/src/components/MatchInfoCard.js @@ -2,14 +2,15 @@ import React from "react"; import { Button, Card } from "react-bootstrap"; import propTypes from "prop-types"; import { grammaticalListString } from "../utils/strings"; -export default class GameInfoCard extends React.Component { +export default class MatchInfoCard extends React.Component { constructor(props) { super(props); } getParticipants() { let participants = []; - this.props.match.registeredUsers.array.forEach(user => { + console.log(this.props); + this.props.match.participants.forEach(user => { participants.push(user.firstName); }); return participants; @@ -19,10 +20,10 @@ export default class GameInfoCard extends React.Component { return ( - {this.props.match.sport} - {this.props.match.sport} + {this.props.match.sport.name} + {this.props.match.title} - Join {grammaticalListString(this.getParticipants(), 4)} to play a few matches of {this.props.match.sport} at {this.props.match.location} on {this.props.match.dateTime.toLocaleDateString("en-US")}! + Join {grammaticalListString(this.getParticipants(), 4)} to play a few matches of {this.props.match.sport.name} at {this.props.match.location.toString()} on {new Date(this.props.match.when).toLocaleDateString("en-US")}! @@ -31,6 +32,6 @@ export default class GameInfoCard extends React.Component { } } -GameInfoCard.propTypes = { +MatchInfoCard.propTypes = { match: propTypes.object, }; \ No newline at end of file diff --git a/sports-matcher/client/src/components/MatchInfoCardDisplay.js b/sports-matcher/client/src/components/MatchInfoCardDisplay.js new file mode 100644 index 0000000..3dd3f7b --- /dev/null +++ b/sports-matcher/client/src/components/MatchInfoCardDisplay.js @@ -0,0 +1,24 @@ +import React from "react"; +import propTypes from "prop-types"; +import MatchInfoCard from "./MatchInfoCard"; + +export default class MatchInfoCardDisplay extends React.Component { + constructor(props) { + super(props); + } + render() { + let matches = null; + if (this.props.recommendedmatches.length > 0) { + matches = this.props.recommendedmatches.map((match) => ); + } + return ( +
+ {matches} +
+ ); + } +} + +MatchInfoCardDisplay.propTypes = { + recommendedmatches: propTypes.array, +}; \ No newline at end of file diff --git a/sports-matcher/client/src/index.js b/sports-matcher/client/src/index.js index 5620a3e..1cdb2e4 100644 --- a/sports-matcher/client/src/index.js +++ b/sports-matcher/client/src/index.js @@ -4,6 +4,9 @@ import Layout from "./Layout"; import reportWebVitals from "./reportWebVitals"; import { BrowserRouter } from "react-router-dom"; import "bootstrap/dist/css/bootstrap.min.css"; // This could be optimized by importing individual css components. + +console.log(process.env); + ReactDOM.render( diff --git a/sports-matcher/client/src/pages/Welcome.js b/sports-matcher/client/src/pages/Welcome.js index e1484c7..56562c2 100644 --- a/sports-matcher/client/src/pages/Welcome.js +++ b/sports-matcher/client/src/pages/Welcome.js @@ -1,20 +1,31 @@ import React from "react"; import { apiClient } from "../utils/httpClients"; import HomeCarousel from "../components/HomeCarousel"; +import MatchInfoCardDisplay from "../components/MatchInfoCardDisplay"; export default class Welcome extends React.Component { constructor(props) { super(props); - this.recentMatchesRequest = apiClient.get("/match/recent/15"); + this.state = { + displayedMatches: [], + }; + } + + async componentDidMount() { + await this.latestMatches(); + + } + + async latestMatches() { + let recentMatchesRes = await apiClient.get("/match/recent/15"); + if (recentMatchesRes.status === 200) { + this.setState({ displayedMatches: recentMatchesRes.data.recent }); + } } render() { return (
-
- {/*

Sports Matcher

-

The best place to find a local match for a good game of your favourite sport!

*/} - -
+

Why?

Because you want to play the sports you love while meeting new friends!

@@ -23,6 +34,7 @@ export default class Welcome extends React.Component {

Available Matches

+
); diff --git a/sports-matcher/client/src/styles/HomeCarousel.css b/sports-matcher/client/src/styles/HomeCarousel.css deleted file mode 100644 index ecedbf6..0000000 --- a/sports-matcher/client/src/styles/HomeCarousel.css +++ /dev/null @@ -1,15 +0,0 @@ -.captionStyle { - background-color: seashell; - color: black; - outline: 1px solid black; -} - -.carousel-control-next, -.carousel-control-prev /*, .carousel-indicators */ { - filter: invert(100%); -} - -.carousel-indicators button { - filter: invert(100%); -} - diff --git a/sports-matcher/client/src/styles/extra.css b/sports-matcher/client/src/styles/extra.css index 8aaad9d..8482f5f 100644 --- a/sports-matcher/client/src/styles/extra.css +++ b/sports-matcher/client/src/styles/extra.css @@ -1,19 +1,5 @@ -.jumbotron { - width: 100%; - padding-left: 1.5rem; - padding-right: 1.5rem; - padding-top: 12rem; - padding-bottom: 1rem; - text-align: center; - background-size: cover; - background-color: black; - color: white; -} - -.jumbotron h1 { - font-size: 1.5rem; -} - .horizontal-scroller { overflow-x: scroll; + padding-top: 1rem; + padding-bottom: 1rem; } diff --git a/sports-matcher/client/src/utils/httpClients.js b/sports-matcher/client/src/utils/httpClients.js index a1d3187..e512953 100644 --- a/sports-matcher/client/src/utils/httpClients.js +++ b/sports-matcher/client/src/utils/httpClients.js @@ -1,6 +1,6 @@ import axios from "axios"; export const apiClient = axios.create({ - baseURL: process.env.API_HOST, + baseURL: process.env.REACT_APP_API_HOST, timeout: 5000, }); \ No newline at end of file diff --git a/sports-matcher/client/src/utils/strings.js b/sports-matcher/client/src/utils/strings.js index 14f18e1..9a47ab9 100644 --- a/sports-matcher/client/src/utils/strings.js +++ b/sports-matcher/client/src/utils/strings.js @@ -10,7 +10,9 @@ export function grammaticalListString(items, max) { return; } built += item; - built += ", "; + if (index < items.length - 1) { + built += ", "; + } if (index == max - 1) { built += "and "; } diff --git a/sports-matcher/scripts/start_mongo.bat b/sports-matcher/scripts/start_mongo.bat new file mode 100644 index 0000000..5dcea4f --- /dev/null +++ b/sports-matcher/scripts/start_mongo.bat @@ -0,0 +1 @@ +mongod --dbpath ./server/mongo-data \ No newline at end of file diff --git a/sports-matcher/scripts/start_mongo.sh b/sports-matcher/scripts/start_mongo.sh new file mode 100755 index 0000000..0d12fdc --- /dev/null +++ b/sports-matcher/scripts/start_mongo.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +mongod --dbpath ./server/mongo-data \ No newline at end of file diff --git a/sports-matcher/server/controllers/matchController.js b/sports-matcher/server/controllers/matchController.js index 8619e20..0ec8ed0 100644 --- a/sports-matcher/server/controllers/matchController.js +++ b/sports-matcher/server/controllers/matchController.js @@ -1,5 +1,5 @@ import express from "express"; -import { authenticationGuard } from "../middleware/authority.js"; +import { requireAuthenticated } from "../middleware/authority.js"; import { needDatabase } from "../middleware/database.js"; import matchModel from "../schemas/matchModel.js"; import sportModel from "../schemas/sportModel.js"; @@ -18,7 +18,7 @@ MatchController.get("/search/:sport", needDatabase, async (req, res) => { if (req.query.beforeDate) query.where("when").lte(req.query.beforeDate); let queryResults = await query; - res.send({ queryResults }); + res.send({ results: queryResults }); } catch (error) { console.error(error); res.status(500).send("Internal server error."); @@ -26,27 +26,43 @@ MatchController.get("/search/:sport", needDatabase, async (req, res) => { }); MatchController.get("/recent/:limit?", needDatabase, async (req, res) => { - let limit = req.params.limit; - if (!req.params.limit) limit = 10; - if (isNaN(limit)) { - res.status(400).send("Limit parameter not a number."); - return; - } - if (limit > 50) { - res.status(400).send("Limit greater than maximum limit of 50."); - return; - } try { - const recent = await matchModel.find().where("publicity").gte(2).limit(limit).sort({ createDate: -1 }); + let user = null; + if (req.session.userId) { + user = await userModel.findById(req.session.userId); + } + let limit = parseInt(req.params.limit); + if (!req.params.limit) limit = 10; + if (isNaN(limit)) { + console.log(typeof (limit)); + res.status(400).send("Limit parameter is not a number."); + return; + } + if (isNaN(limit)) { + res.status(400).send("Limit parameter not a number."); + return; + } + if (limit > 50) { + res.status(400).send("Limit greater than maximum limit of 50."); + return; + } + let recent = null; + if (user) { + await user.populate("participatingMatches"); + recent = user.participatingMatches.slice(-limit); + } else { + recent = await matchModel.find().where("publicity").gte(2).limit(limit).sort({ createDate: -1 }); + } + await recent.populate("members.$"); // Populates all references. res.status(200).send({ recent: recent }); - } catch (err) { - console.error(err); + } catch (error) { + console.error(error); res.status(500).send("Internal server error."); // TODO: Check and improve error handling. } }); -MatchController.post("/", needDatabase, authenticationGuard, async (req, res) => { +MatchController.post("/", needDatabase, requireAuthenticated, async (req, res) => { try { const userId = req.session.userId; const user = await userModel.findById(userId); @@ -64,7 +80,7 @@ MatchController.post("/", needDatabase, authenticationGuard, async (req, res) => user.createdMatches.push(match._id); user.participatingMatches.push(match._id); await user.save(); - res.status(201).send(match); + res.status(201).send({ createdMatch: match }); } catch (error) { console.error(error); res.status(500).send("Internal server error."); @@ -72,110 +88,129 @@ MatchController.post("/", needDatabase, authenticationGuard, async (req, res) => } }); -MatchController.patch("/:id", needDatabase, authenticationGuard, async (req, res) => { - const match = await matchModel.findById(req.params.id); - if (!match) { - res.status(400).send("Invalid match ID provided."); - return; - } - - if (req.user._id !== match.creator && req.user.accessLevel < 3) { - res.status(401).send("Not authorized."); - return; - } - - if (req.body._id) { - res.status(400).send("Cannot change ID of match."); - return; - } - - if (req.body.creator) { - res.status(400).send("Cannot change creator of match."); - return; - } - - await match.updateOne(req.body); - - res.status(200).send(match); -}); - -MatchController.delete("/:id", needDatabase, authenticationGuard, async (req, res) => { - const match = await matchModel.findById(req.params.id); - if (!match) { - res.status(400).send("Invalid match ID provided."); - return; - } - - if (req.user._id !== match.creator && req.user.accessLevel < 3) { - res.status(401).send("Not authorized."); - return; - } - await match.deleteOne(); -}); - -MatchController.get("/:matchId", needDatabase, async (req, res) => { - if (!req.params.matchId) { - res.status(404).send("Id must be provided to retrieve match"); - return; - } +MatchController.patch("/:id", needDatabase, requireAuthenticated, async (req, res) => { try { - const match = await matchModel.findById(req.params.matchId); + const match = await matchModel.findById(req.params.id); + if (!match) { + res.status(400).send("Invalid match ID provided."); + return; + } + + if (req.user._id !== match.creator && req.user.accessLevel < 3) { + res.status(401).send("Not authorized."); + return; + } + + if (req.body._id) { + res.status(400).send("Cannot change ID of match."); + return; + } + + if (req.body.creator) { + res.status(400).send("Cannot change creator of match."); + return; + } + await match.updateOne(req.body); + res.status(200).send({ updatedMatch: match }); + + } catch (error) { + res.status(200).send("Internal server error."); + } +}); + +MatchController.delete("/:id", needDatabase, requireAuthenticated, async (req, res) => { + try { + const match = await matchModel.findById(req.params.id); + if (!match) { + res.status(400).send("Invalid match ID provided."); + return; + } + if (req.user._id !== match.creator && req.user.accessLevel < 3) { + res.status(401).send("Not authorized."); + return; + } + await match.deleteOne(); + res.status(200).send("Deleted."); + } catch (error) { + console.error(error); + res.status(500).send("Internal server error"); + } +}); + +MatchController.get("/:id", needDatabase, async (req, res) => { + try { + if (!req.params.id) { + res.status(404).send("Id must be provided to retrieve match"); + return; + } + const match = await matchModel.findById(req.params.id).populate("sport"); if (match) { - res.status(200).send(match); + res.status(200).send({ match: match }); } else { - res.status(404).send("Could not find match with ID: " + req.params.matchId); + res.status(404).send("Could not find match with ID: " + req.params.id); } } catch (error) { + console.error(error); res.status(500).send("Internal server error."); - // TODO: Develop the error handling. + // TODO: Improve the error handling. } }); -MatchController.get("/join/:id", needDatabase, authenticationGuard, async (req, res) => { - const match = await matchModel.findById(req.params.id); - const user = req.user; - if (!match) { - res.status(400).send("Invalid match ID provided."); - return; +MatchController.get("/join/:id", needDatabase, requireAuthenticated, async (req, res) => { + try { + const match = await matchModel.findById(req.params.id); + const user = req.user; + if (!match) { + res.status(400).send("Invalid match ID provided."); + return; + } + + if (user.participatingMatches.includes(match._id)) { + res.status(400).send("Already participating in match."); + return; + } + + match.participants.push(user._id); + user.participatingMatches.push(match._id); + + await match.save(); + await user.save(); + + res.status(200).send("Joined."); + } catch (error) { + console.error(error); + res.status(500).send("Internal server error."); } - - if (user.participatingMatches.includes(match._id)) { - res.status(400).send("Already participating in match."); - return; - } - - match.participants.push(user._id); - user.participatingMatches.push(match._id); - - await match.save(); - await user.save(); - - res.status(200).send("Joined."); }); -MatchController.get("/leave/:id", needDatabase, authenticationGuard, async (req, res) => { - const match = await matchModel.findById(req.params.id); - const user = req.user; +MatchController.get("/leave/:id", needDatabase, requireAuthenticated, async (req, res) => { + try { + const match = await matchModel.findById(req.params.id); + const user = req.user; - if (!match) { - res.status(400).send("Invalid match ID provided."); - return; + if (!match) { + res.status(400).send("Invalid match ID provided."); + return; + } + + if (!user.participatingMatches.includes(match._id)) { + res.status(400).send("Not part of match."); + return; + } + + const userIndex = match.participants.indexOf(user._id); + match.participants.splice(userIndex, 1); + await match.save(); + + const matchIndex = user.participatingMatches.indexOf(match._id); + user.participatingMatches.splice(matchIndex, 1); + await user.save(); + + res.status(200).send("Left match."); + } catch (error) { + console.error(error); + res.status(500).send("Internal server error."); } - - if (!user.participatingMatches.includes(match._id)) { - res.status(400).send("Not part of match."); - return; - } - - const userIndex = match.participants.indexOf(user._id); - match.participants.splice(userIndex, 1); - await match.save(); - - const matchIndex = user.participatingMatches.indexOf(match._id); - user.participatingMatches.splice(matchIndex, 1); - await user.save(); - - res.status(200).send("Left match."); }); export default MatchController; \ No newline at end of file diff --git a/sports-matcher/server/controllers/rentalController.js b/sports-matcher/server/controllers/rentalController.js new file mode 100644 index 0000000..616c09b --- /dev/null +++ b/sports-matcher/server/controllers/rentalController.js @@ -0,0 +1,116 @@ +import express from "express"; +import { requireAuthenticated } from "../middleware/authority.js"; +import { needDatabase } from "../middleware/database.js"; +import rentalModel from "../schemas/rentalModel.js"; +import userModel from "../schemas/userModel.js"; +const rentalController = express.Router(); + + +rentalController.post("/", needDatabase, requireAuthenticated, async (req, res) => { + try { + const user = req.user; + req.body.createDate = undefined; + req.body.creator = user._id; + const rental = new rentalModel(req.body); + await rental.save(); + res.status(201).send({ createdRental: rental }); + } catch (error) { + console.error(error); + res.status(500).send("Internal server error."); + } +}); + +rentalController.get("/:id", needDatabase, async (req, res) => { + try { + const rental = await rentalModel.findById(req.params.id).populate("creator"); + res.status(200).send({ rental: rental }); + } catch (error) { + console.error(error); + res.status(500).send("Internal server error"); + } +}); + +rentalController.get("/recent/:limit?", needDatabase, async (req, res) => { + try { + let user = null; + if (req.session.userId) { + user = await userModel.findById(req.session.userId); + } + let limit = parseInt(req.params.limit); + if (!req.params.limit) limit = 10; + if (isNaN(limit)) { + console.log(typeof (limit)); + res.status(400).send("Limit parameter is not a number."); + return; + } + if (isNaN(limit)) { + res.status(400).send("Limit parameter not a number."); + return; + } + if (limit > 50) { + res.status(400).send("Limit greater than maximum limit of 50."); + return; + } + let recent = null; + if (user) { + await user.populate("createdRentals"); + recent = user.createdRentals.slice(-limit); + } else { + recent = await rentalModel.find().limit(limit).sort({ createDate: -1 }); + } + await recent.populate("members.$"); + res.status(200).send({ recent: recent }); + } catch (error) { + console.error(error); + res.status(500).send("Internal server error."); + } +}); + +rentalController.patch("/:id", needDatabase, requireAuthenticated, async (req, res) => { + try { + const rental = await rentalModel.findById(req.params.id); + if (!rental) { + res.status(400).send("Invalid rental ID provided."); + return; + } + if (req.body._id) { + res.status(400).send("Cannot change ID of rental."); + return; + } + if (req.body.creator) { + res.status(400).send("Cannot change creator of rental."); + return; + } + if (req.user._id !== rental.creator && req.user.accessLevel < 3) { + res.status(401).send("Not authorized."); + return; + } + await rental.updateOne(req.body); + res.status(200).send({ updated: rental }); + } catch (error) { + console.error(error); + res.status(500).send("Internal server error."); + } +}); + +rentalController.delete("/:id", needDatabase, requireAuthenticated, async (req, res) => { + try { + const rental = await rentalModel.findById(req.params.id); + if (!rental) { + res.status(400).send("Invalid match ID provided."); + return; + } + + if (req.user._id !== rental.creator && req.user.accessLevel < 3) { + res.status(401).send("Not authorized."); + return; + } + await rental.deleteOne(); + res.status(200).send("Deleted."); + } catch (error) { + console.error(error); + res.status(500).send("Internal server error"); + } +}); + +export default rentalController; \ No newline at end of file diff --git a/sports-matcher/server/controllers/sportController.js b/sports-matcher/server/controllers/sportController.js index 40be0c1..0d7b667 100644 --- a/sports-matcher/server/controllers/sportController.js +++ b/sports-matcher/server/controllers/sportController.js @@ -1,12 +1,12 @@ import express from "express"; -import { authenticationGuard } from "../middleware/authority.js"; +import { requireAuthenticated } from "../middleware/authority.js"; import { needDatabase } from "../middleware/database.js"; import sportModel from "../schemas/sportModel.js"; import userModel from "../schemas/userModel.js"; const SportController = express.Router(); -SportController.post("/", needDatabase, authenticationGuard, async (req, res) => { +SportController.post("/", needDatabase, requireAuthenticated, async (req, res) => { const user = await userModel.findById(req.session.userId); try { if (user.accessLevel <= 2) { diff --git a/sports-matcher/server/controllers/userController.js b/sports-matcher/server/controllers/userController.js index 4ca3888..520b788 100644 --- a/sports-matcher/server/controllers/userController.js +++ b/sports-matcher/server/controllers/userController.js @@ -1,5 +1,5 @@ import express from "express"; -import { authenticationGuard } from "../middleware/authority.js"; +import { requireAuthenticated } from "../middleware/authority.js"; import { needDatabase } from "../middleware/database.js"; import userModel from "../schemas/userModel.js"; import User from "../schemas/userModel.js"; @@ -34,7 +34,7 @@ UserController.post("/login", needDatabase, async (req, res) => { } }); -UserController.get("/logout", authenticationGuard, (req, res) => { +UserController.get("/logout", requireAuthenticated, (req, res) => { req.session.destroy((err) => { if (err) { console.error(err); @@ -50,7 +50,7 @@ UserController.get("/logout", authenticationGuard, (req, res) => { }); }); -UserController.get("/:id?", needDatabase, authenticationGuard, async (req, res) => { +UserController.get("/:id?", needDatabase, requireAuthenticated, async (req, res) => { let user = null; if (req.params.id) { if (req.user.accessLevel > 2) { @@ -66,7 +66,7 @@ UserController.get("/:id?", needDatabase, authenticationGuard, async (req, res) res.status(200).send(user); }); -UserController.patch("/:id?", needDatabase, authenticationGuard, async (req, res) => { +UserController.patch("/:id?", needDatabase, requireAuthenticated, async (req, res) => { let user = null; if (req.params.id) { if (req.user.accessLevel > 2) { @@ -114,7 +114,7 @@ UserController.patch("/:id?", needDatabase, authenticationGuard, async (req, res /* TODO: Implement middleware for removing users. -UserController.delete("/:id?", needDatabase, authenticationGuard, async (req, res) => { +UserController.delete("/:id?", needDatabase, requireAuthenticated, async (req, res) => { let user = null; if (req.params.id) { if (req.user.accessLevel > 2) { diff --git a/sports-matcher/server/middleware/authority.js b/sports-matcher/server/middleware/authority.js index bcab71e..9c09f66 100644 --- a/sports-matcher/server/middleware/authority.js +++ b/sports-matcher/server/middleware/authority.js @@ -17,7 +17,7 @@ if (process.env.NODE_ENV === "production") { } export const userSession = session(sessionConf); -export async function authenticationGuard(req, res, next) { +export async function requireAuthenticated(req, res, next) { if (req.session.userId) { req.user = await userModel.findById(req.session.userId); next(); @@ -26,7 +26,3 @@ export async function authenticationGuard(req, res, next) { return; } } - -// TODO: Authentication -// TODO: Identity -// TODO: Authority \ No newline at end of file diff --git a/sports-matcher/server/schemas/modelNameRegister.js b/sports-matcher/server/schemas/modelNameRegister.js index 3c3b6e6..decc2b8 100644 --- a/sports-matcher/server/schemas/modelNameRegister.js +++ b/sports-matcher/server/schemas/modelNameRegister.js @@ -1,5 +1,6 @@ export default { Match: "match", User: "user", - Sport: "sport" + Sport: "sport", + Rental: "rental", }; \ No newline at end of file diff --git a/sports-matcher/server/schemas/rentalModel.js b/sports-matcher/server/schemas/rentalModel.js new file mode 100644 index 0000000..31e2d24 --- /dev/null +++ b/sports-matcher/server/schemas/rentalModel.js @@ -0,0 +1,23 @@ +import mongoose from "mongoose"; +import modelNameRegister from "./modelNameRegister.js"; + +const Types = mongoose.Schema.Types; + +const rentalSchema = new mongoose.Schema({ + title: { type: String, required: true, trim: true }, + rate: { type: String, required: true, trim: true }, + description: { type: String, required: true }, + contact: { type: String, required: true }, + createDate: { type: Date, required: true, default: Date.now }, + creator: { type: Types.ObjectId, ref: modelNameRegister.User } +}); + +rentalSchema.pre("remove", async function (next) { + const rental = this; + const rentalInd = rental.creator.createdRentals.indexOf(rental._id); + rental.creator.createdRentals.splice(rentalInd, 1); + await rental.save(); + next(); +}); + +export default mongoose.model(modelNameRegister.Rental, rentalSchema); \ No newline at end of file diff --git a/sports-matcher/server/schemas/userModel.js b/sports-matcher/server/schemas/userModel.js index 4c21b66..28a2be5 100644 --- a/sports-matcher/server/schemas/userModel.js +++ b/sports-matcher/server/schemas/userModel.js @@ -29,6 +29,7 @@ const userSchema = new mongoose.Schema({ }, createdMatches: { type: [{ type: Types.ObjectId, ref: modelNameRegister.Match }], required: true, default: [] }, participatingMatches: { type: [{ type: Types.ObjectId, ref: modelNameRegister.Match }], required: true, default: [] }, + createdRentals: { type: [{ type: Types.ObjectId, ref: modelNameRegister.Rental }], required: true, default: [] }, emailPublicity: { type: Number, required: true, default: 0 }, bioPublicity: { type: Boolean, required: true, default: false }, phonePublicity: { type: Boolean, required: true, default: false }, diff --git a/sports-matcher/server/server.js b/sports-matcher/server/server.js index 08dbae4..dcb9da6 100644 --- a/sports-matcher/server/server.js +++ b/sports-matcher/server/server.js @@ -7,6 +7,7 @@ import SportController from "./controllers/sportController.js"; import { userSession } from "./middleware/authority.js"; import { mongooseDbName, mongoURI } from "./database/mongoose.js"; import cors from "cors"; +import rentalController from "./controllers/rentalController.js"; const server = express(); const port = process.env.PORT || 5000; @@ -40,7 +41,7 @@ server.use(userSession); server.use("/user", UserController); server.use("/match", MatchController); server.use("/sport", SportController); - +server.use("/rental", rentalController); server.listen(port, () => { console.log(`Server listening on port ${port}.`);