├── frontend ├── .env.example ├── src │ ├── context │ │ ├── index.js │ │ └── AuthProvider.jsx │ ├── api │ │ ├── index.js │ │ ├── axios.js │ │ └── AccountApi.js │ ├── models │ │ ├── index.js │ │ ├── gender.js │ │ └── filter.js │ ├── assets │ │ ├── map.jpg │ │ ├── moving_out.jpg │ │ ├── roommates.jpg │ │ └── dallas_skyline.jpg │ ├── components │ │ ├── domain │ │ │ ├── Account │ │ │ │ ├── index.js │ │ │ │ ├── Login.jsx │ │ │ │ └── Registration.jsx │ │ │ ├── Dashboard │ │ │ │ ├── index.js │ │ │ │ ├── ProfileExplorer.jsx │ │ │ │ ├── ResultList.jsx │ │ │ │ └── ProfileSearch.jsx │ │ │ ├── index.js │ │ │ ├── Profile │ │ │ │ ├── index.js │ │ │ │ ├── AvatarUpload.jsx │ │ │ │ ├── RoommateList.jsx │ │ │ │ ├── RequestList.jsx │ │ │ │ ├── ProfileDetails.jsx │ │ │ │ └── ProfileEditor.jsx │ │ │ └── Routing │ │ │ │ ├── index.js │ │ │ │ ├── RequireAuth.jsx │ │ │ │ ├── Missing.jsx │ │ │ │ ├── PersistentLogin.jsx │ │ │ │ ├── Router.jsx │ │ │ │ ├── NavBar.jsx │ │ │ │ └── LandingPage.jsx │ │ ├── common │ │ │ ├── index.js │ │ │ ├── Footer.jsx │ │ │ ├── TextAreaField.jsx │ │ │ ├── CheckBoxField.jsx │ │ │ ├── SearchField.jsx │ │ │ ├── TextField.jsx │ │ │ ├── PlainNavBar.jsx │ │ │ ├── SelectField.jsx │ │ │ ├── CheckBoxDropdown.jsx │ │ │ └── CredentialsField.jsx │ │ └── App.jsx │ ├── hooks │ │ ├── index.js │ │ ├── useAuth.jsx │ │ ├── useLogout.jsx │ │ ├── useRefreshToken.jsx │ │ └── useAxiosPrivate.jsx │ ├── index.js │ ├── tests │ │ └── App.test.js │ └── styles │ │ └── styles.css ├── public │ ├── favicon.ico │ ├── logo192.png │ ├── images │ │ ├── default.jpg │ │ └── branding.png │ ├── manifest.json │ └── index.html ├── .gitignore └── package.json ├── backend ├── .dockerignore ├── config │ ├── allowedOrigins.js │ ├── knex.js │ └── corsOptions.js ├── Dockerfile ├── middleware │ ├── errorHandler.js │ ├── credentials.js │ ├── createModels.js │ ├── verifyJWT.js │ └── logEvents.js ├── .gitignore ├── controllers │ ├── logoutController.js │ ├── authController.js │ └── refreshTokenController.js ├── knexfile.js ├── .env.example ├── migrations │ ├── 20221120224806_add_requests_table.js │ ├── 20221101182132_add_users_table.js │ ├── 20221110205801_add_preferences_table.js │ └── 20221124033752_add_house_table.js ├── routes │ ├── roommate.js │ ├── register.js │ ├── avatar.js │ ├── session.js │ ├── request.js │ └── user.js ├── package.json ├── index.js ├── models │ ├── request.js │ ├── avatar.js │ ├── user.js │ └── roommate.js └── tests │ └── api.test.js ├── .github └── workflows │ ├── build.yml │ └── deploy.yml ├── README.md └── LICENSE /frontend/.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_API_URL= -------------------------------------------------------------------------------- /backend/.dockerignore: -------------------------------------------------------------------------------- 1 | /usr/src/app/node-modules/ -------------------------------------------------------------------------------- /frontend/src/context/index.js: -------------------------------------------------------------------------------- 1 | export * from "./AuthProvider"; 2 | -------------------------------------------------------------------------------- /frontend/src/api/index.js: -------------------------------------------------------------------------------- 1 | export * from "./axios"; 2 | export * from "./AccountApi"; 3 | -------------------------------------------------------------------------------- /frontend/src/models/index.js: -------------------------------------------------------------------------------- 1 | export * from "./gender"; 2 | export * from "./filter"; 3 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkwat/mate-match/HEAD/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkwat/mate-match/HEAD/frontend/public/logo192.png -------------------------------------------------------------------------------- /frontend/src/assets/map.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkwat/mate-match/HEAD/frontend/src/assets/map.jpg -------------------------------------------------------------------------------- /frontend/src/components/domain/Account/index.js: -------------------------------------------------------------------------------- 1 | export * from "./Registration"; 2 | export * from "./Login"; 3 | -------------------------------------------------------------------------------- /frontend/public/images/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkwat/mate-match/HEAD/frontend/public/images/default.jpg -------------------------------------------------------------------------------- /frontend/src/assets/moving_out.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkwat/mate-match/HEAD/frontend/src/assets/moving_out.jpg -------------------------------------------------------------------------------- /frontend/src/assets/roommates.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkwat/mate-match/HEAD/frontend/src/assets/roommates.jpg -------------------------------------------------------------------------------- /frontend/public/images/branding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkwat/mate-match/HEAD/frontend/public/images/branding.png -------------------------------------------------------------------------------- /frontend/src/assets/dallas_skyline.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirkwat/mate-match/HEAD/frontend/src/assets/dallas_skyline.jpg -------------------------------------------------------------------------------- /frontend/src/models/gender.js: -------------------------------------------------------------------------------- 1 | export class Gender { 2 | constructor(id, display) { 3 | this.id = id; 4 | this.display = display; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/components/domain/Dashboard/index.js: -------------------------------------------------------------------------------- 1 | export * from "./ProfileExplorer"; 2 | export * from "./ProfileSearch"; 3 | export * from "./ResultList"; 4 | -------------------------------------------------------------------------------- /backend/config/allowedOrigins.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | 3 | const allowedOrigins = [process.env.CLIENT_URL]; 4 | 5 | module.exports = allowedOrigins; 6 | -------------------------------------------------------------------------------- /frontend/src/components/domain/index.js: -------------------------------------------------------------------------------- 1 | export * from "./Account"; 2 | export * from "./Profile"; 3 | export * from "./Dashboard"; 4 | export * from "./Routing"; 5 | -------------------------------------------------------------------------------- /frontend/src/hooks/index.js: -------------------------------------------------------------------------------- 1 | export * from "./useAuth"; 2 | export * from "./useLogout"; 3 | export * from "./useAxiosPrivate"; 4 | export * from "./useRefreshToken"; 5 | -------------------------------------------------------------------------------- /frontend/src/components/domain/Profile/index.js: -------------------------------------------------------------------------------- 1 | export * from "./ProfileDetails"; 2 | export * from "./ProfileEditor"; 3 | export * from "./RoommateList"; 4 | export * from "./RequestList"; 5 | -------------------------------------------------------------------------------- /frontend/src/models/filter.js: -------------------------------------------------------------------------------- 1 | export class Filter { 2 | constructor(id, display, value) { 3 | this.id = id; 4 | this.display = display; 5 | this.value = value; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package.json . 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | EXPOSE 8000 12 | 13 | CMD ["npm", "start"] -------------------------------------------------------------------------------- /backend/config/knex.js: -------------------------------------------------------------------------------- 1 | const env = require("dotenv"); 2 | const knexConfig = require("../knexfile"); 3 | const knex = require("knex"); 4 | 5 | module.exports = knex(knexConfig.development); 6 | -------------------------------------------------------------------------------- /frontend/src/hooks/useAuth.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { AuthContext } from "../context"; 3 | 4 | export const useAuth = () => { 5 | return useContext(AuthContext); 6 | }; 7 | -------------------------------------------------------------------------------- /frontend/src/components/domain/Routing/index.js: -------------------------------------------------------------------------------- 1 | export * from "./Router"; 2 | export * from "./RequireAuth"; 3 | export * from "./PersistentLogin"; 4 | export * from "./NavBar"; 5 | export * from "./LandingPage"; 6 | export * from "./Missing"; 7 | -------------------------------------------------------------------------------- /backend/middleware/errorHandler.js: -------------------------------------------------------------------------------- 1 | const { logEvents } = require("./logEvents"); 2 | 3 | const errorHandler = (err, req, res, next) => { 4 | logEvents(`${err.name}: ${err.message}`, "errLog.txt"); 5 | console.error(err.stack); 6 | res.status(500).send(err.message); 7 | }; 8 | 9 | module.exports = errorHandler; 10 | -------------------------------------------------------------------------------- /frontend/src/api/axios.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export default axios.create({ 4 | baseURL: process.env.REACT_APP_API_URL, 5 | }); 6 | 7 | export const axiosPrivate = axios.create({ 8 | baseURL: process.env.REACT_APP_API_URL, 9 | headers: { "Content-Type": "application/json" }, 10 | withCredentials: true, 11 | }); 12 | -------------------------------------------------------------------------------- /frontend/src/components/common/index.js: -------------------------------------------------------------------------------- 1 | export * from "./CheckBoxField"; 2 | export * from "./SelectField"; 3 | export * from "./TextAreaField"; 4 | export * from "./TextField"; 5 | export * from "./SearchField"; 6 | export * from "./CheckBoxDropdown"; 7 | export * from "./CredentialsField"; 8 | export * from "./Footer"; 9 | export * from "./PlainNavBar"; 10 | -------------------------------------------------------------------------------- /backend/middleware/credentials.js: -------------------------------------------------------------------------------- 1 | const allowedOrigins = require("../config/allowedOrigins"); 2 | 3 | const credentials = (req, res, next) => { 4 | const origin = req.headers.origin; 5 | if (allowedOrigins.includes(origin)) { 6 | res.header("Access-Control-Allow-Credentials", true); 7 | } 8 | next(); 9 | }; 10 | 11 | module.exports = credentials; 12 | -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { App } from "./components/App"; 4 | 5 | import "bootstrap"; 6 | import "./styles/bootstrap.min.css"; 7 | import "./styles/styles.css"; 8 | 9 | const root = ReactDOM.createRoot(document.getElementById("root")); 10 | root.render( 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /frontend/src/context/AuthProvider.jsx: -------------------------------------------------------------------------------- 1 | import { createContext, useState } from "react"; 2 | 3 | export const AuthContext = createContext({}); 4 | 5 | export const AuthProvider = ({ children }) => { 6 | const [auth, setAuth] = useState({}); 7 | 8 | return ( 9 | <> 10 | 11 | {children} 12 | 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /frontend/.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 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | /logs 3 | 4 | # dependencies 5 | /node_modules 6 | /.pnp 7 | .pnp.js 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .DS_Store 17 | .env 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* -------------------------------------------------------------------------------- /backend/controllers/logoutController.js: -------------------------------------------------------------------------------- 1 | const User = require("../models/user"); 2 | 3 | const handleLogout = async (cookies) => { 4 | const refreshToken = cookies.jwt; 5 | 6 | const users = await User.findUserByRT(refreshToken); 7 | if (users.length === 0) return "invalid refreshtoken"; 8 | 9 | await User.createRefreshToken(users[0].email, null); 10 | 11 | return "logged out"; 12 | }; 13 | 14 | module.exports = { handleLogout }; 15 | -------------------------------------------------------------------------------- /backend/knexfile.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | module.exports = { 3 | development: { 4 | client: "mysql", 5 | debug: true, 6 | connection: { 7 | host: process.env.MYSQL_CLOUD_HOST, 8 | port: process.env.MYSQL_PORT, 9 | user: process.env.MYSQL_CLOUD_USER, 10 | password: process.env.MYSQL_CLOUD_PASS, 11 | insecureAuth: true, 12 | database: process.env.MYSQL_DB, 13 | }, 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /frontend/src/tests/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from "@testing-library/react"; 2 | 3 | import { App } from "../components/App"; 4 | 5 | describe("App", () => { 6 | it("landing page should have create account button", () => { 7 | render(); 8 | 9 | const createAccountButton = screen.getByTestId("create-account-button"); 10 | 11 | expect(createAccountButton.innerHTML).toBe("Create your Account"); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /frontend/src/hooks/useLogout.jsx: -------------------------------------------------------------------------------- 1 | import axios from "../api/axios"; 2 | import { useAuth } from "./useAuth"; 3 | 4 | export const useLogout = () => { 5 | const { setAuth } = useAuth(); 6 | 7 | const logout = async () => { 8 | setAuth({}); 9 | try { 10 | await axios("/session/logout", { 11 | withCredentials: true, 12 | }); 13 | } catch (err) { 14 | console.error(err); 15 | } 16 | }; 17 | return logout; 18 | }; 19 | -------------------------------------------------------------------------------- /backend/.env.example: -------------------------------------------------------------------------------- 1 | CLIENT_URL= 2 | 3 | ACCESS_TOKEN_SECRET= 4 | REFRESH_TOKEN_SECRET= 5 | 6 | MYSQL_DB= 7 | MYSQL_PORT= 8 | MYSQL_CLOUD_USER= 9 | MYSQL_CLOUD_PASS= 10 | MYSQL_CLOUD_HOST= 11 | 12 | BUCKET_NAME= 13 | BUCKET_REGION= 14 | BUCKET_ACCESS_KEY= 15 | BUCKET_SECRET_ACCESS_KEY= 16 | 17 | CLOUDFRONT_DISTRIBUTION_ID= 18 | CLOUDFRONT_URL= 19 | CLOUDFRONT_KEY_PAIR_ID= 20 | 21 | SECRET_REGION= 22 | SECRET_SIGNING_KEY= 23 | 24 | TEST_USERNAME= 25 | TEST_PASSWORD= -------------------------------------------------------------------------------- /frontend/src/components/common/Footer.jsx: -------------------------------------------------------------------------------- 1 | export const Footer = () => { 2 | return ( 3 | <> 4 | 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /backend/middleware/createModels.js: -------------------------------------------------------------------------------- 1 | const User = require("../models/user"); 2 | const Request = require("../models/request"); 3 | const Roommate = require("../models/roommate"); 4 | const Avatar = require("../models/avatar"); 5 | 6 | const createModels = async (req, res, next) => { 7 | req.models = { 8 | user: User, 9 | request: Request, 10 | roommate: Roommate, 11 | avatar: Avatar, 12 | }; 13 | 14 | next(); 15 | }; 16 | 17 | module.exports = createModels; 18 | -------------------------------------------------------------------------------- /frontend/src/components/common/TextAreaField.jsx: -------------------------------------------------------------------------------- 1 | export const TextAreaField = ({ label, value, setValue, rowNum }) => ( 2 | <> 3 |
4 | 5 |