16 | Are you sure you want to delete your account?
17 |
18 |
19 | Delete
20 |
21 |
22 | )
23 | }
24 |
25 | export default DeleteConfirmationPage
26 |
--------------------------------------------------------------------------------
/config/checkToken.js:
--------------------------------------------------------------------------------
1 | const jwt = require("jsonwebtoken")
2 |
3 | module.exports = function (req, res, next) {
4 | // Check for the token being sent in a header or as a query parameter
5 | let token = req.get("Authorization") || req.query.token
6 | if (token) {
7 | // Remove the 'Bearer ' if it was included in the token header
8 | token = token.replace("Bearer ", "")
9 | // Check if token is valid and not expired
10 | jwt.verify(token, process.env.SECRET, function (err, decoded) {
11 | // If valid token, decoded will be the token's entire payload
12 | // If invalid token, err will be set
13 | req.user = err ? null : decoded.user
14 | // If your app cares... (optional)
15 | req.exp = err ? null : new Date(decoded.exp * 1000)
16 | return next()
17 | })
18 | } else {
19 | // No token was sent
20 | req.user = null
21 | return next()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Minecraft-Server-Deployer(Work-in-Progress)
2 |
3 | ## Description
4 |
5 | This application will eventually be a website where users can launch their own minecraft dedicated servers. It is designed to connect non-tech individuals with the power of dedicated servers, without having to learn all of the technical bits. Ideally, I would like to create containerized Java minecraft servers hosted on Amazon Web Services'(AWS) Elastic Kubernetes Service (EKS). I want to develop this app so I can connect non-tech individuals to normally tech-savvy functions and so I can earn passive income while I continue to search for permanent employment.
6 |
7 | ## Technology Used
8 |
9 | bcrypt
10 | dotenv
11 | express
12 | jsonwebtoken
13 | mongoose
14 | morgan
15 | react
16 | react-dom
17 | react-router-dom
18 | react-scripts
19 | serve
20 | serve-favicon
21 | web-vitals
22 |
23 | ## Installation
24 |
25 | npm i
26 | create a .env file
27 | add environment variables in .env:
28 | DATABASE_URL=
29 | SECRET=
30 | REACT_APP_API_KEY=
31 |
32 | ## Getting started
33 |
34 | Deployed App: https://minecraft-server-deployer.onrender.com/
35 | https://trello.com/b/EZTgyPfB/final-project
36 |
--------------------------------------------------------------------------------
/src/components/NavBar.js:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom"
2 | import { logOut } from "../utilities/users-service"
3 | import styles from "./NavBar.module.css"
4 |
5 | function NavBar({ user, setUser }) {
6 | const handleLogOut = () => {
7 | logOut()
8 | setUser(null)
9 | }
10 | return (
11 |
12 |
Welcome, {user.name}
13 |
32 |
33 | )
34 | }
35 |
36 | export default NavBar
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Minecraft-Server-Deployer",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "bcrypt": "^5.1.0",
7 | "dotenv": "^16.0.3",
8 | "express": "^4.18.2",
9 | "jsonwebtoken": "^9.0.0",
10 | "mongoose": "^6.10.5",
11 | "morgan": "^1.10.0",
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0",
14 | "react-router-dom": "^6.10.0",
15 | "react-scripts": "5.0.1",
16 | "serve": "^14.2.1",
17 | "serve-favicon": "^2.5.0",
18 | "web-vitals": "^2.1.4"
19 | },
20 | "scripts": {
21 | "start": "BROWSER=none react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | },
44 | "proxy": "http://localhost:3001"
45 | }
46 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react"
2 |
3 | import { Routes, Route } from "react-router-dom"
4 |
5 | import AuthPage from "./pages/AuthPage"
6 | import EditPage from "./pages/EditPage"
7 | import NavBar from "./components/NavBar"
8 | import ProfilePage from "./pages/ProfilePage"
9 | import DeleteConfirmationPage from "./pages/DeleteConfirmationPage"
10 |
11 | import { getUser } from "./utilities/users-service"
12 |
13 | import "./App.css"
14 | import WeatherPage from "./pages/WeatherPage"
15 |
16 | function App() {
17 | const [user, setUser] = useState(getUser())
18 |
19 | return (
20 |
21 | {user ? (
22 | <>
23 |
24 |
25 | } />
26 | } />
27 | } />
28 | }
31 | />
32 |
33 | >
34 | ) : (
35 |
36 | )}
37 |
38 | )
39 | }
40 |
41 | export default App
42 |
--------------------------------------------------------------------------------
/models/user.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose")
2 |
3 | const Schema = mongoose.Schema
4 | const bcrypt = require("bcrypt")
5 |
6 | //* determines how much processing time it will take to perform the hash
7 | const SALT_ROUNDS = 6 // 6 is a reasonable value
8 |
9 | const userSchema = new Schema(
10 | {
11 | name: { type: String, required: true },
12 | email: {
13 | type: String,
14 | unique: true,
15 | trim: true,
16 | lowercase: true,
17 | required: true,
18 | },
19 | password: {
20 | type: String,
21 | trim: true,
22 | minLength: 3,
23 | required: true,
24 | },
25 | },
26 | {
27 | timestamps: true,
28 | toJSON: function (doc, ret) {
29 | delete ret.password
30 | return ret
31 | },
32 | }
33 | )
34 |
35 | //* Pre Hook
36 | userSchema.pre("save", async function (next) {
37 | // if password was NOT modified continue to the next middleware
38 | if (!this.isModified("password")) return next()
39 |
40 | // update the password with the computed hash
41 | this.password = await bcrypt.hash(this.password, SALT_ROUNDS)
42 | return next()
43 | })
44 |
45 | userSchema.pre("findOneAndUpdate", async function (next) {
46 | const docToUpdate = await this.model.findOne(this.getQuery())
47 | this.password = await bcrypt.hash(docToUpdate.password, SALT_ROUNDS)
48 | return next()
49 | })
50 |
51 | module.exports = mongoose.model("User", userSchema)
52 |
--------------------------------------------------------------------------------
/src/utilities/users-service.js:
--------------------------------------------------------------------------------
1 | import * as usersApi from "./users-api"
2 |
3 | //* Get Token
4 | export function getToken() {
5 | const token = localStorage.getItem("token")
6 | // if there is no token
7 | if (!token) return null
8 |
9 | const payload = JSON.parse(atob(token.split(".")[1]))
10 | console.log(payload)
11 |
12 | // if token is expired
13 | if (payload.exp < Date.now() / 1000) {
14 | localStorage.removeItem("token")
15 | return null
16 | }
17 |
18 | // token is valid
19 | return token
20 | }
21 |
22 | //* Get User
23 | export function getUser() {
24 | const token = getToken()
25 | return token ? JSON.parse(atob(token.split(".")[1])).user : null
26 | }
27 |
28 | //* SignUp
29 | export async function signUp(userData) {
30 | const token = await usersApi.signUp(userData)
31 | // saves token to localStorage
32 | localStorage.setItem("token", token)
33 |
34 | return getUser()
35 | }
36 |
37 | export async function EditUser(userData) {
38 | await usersApi.EditUser(userData)
39 | }
40 |
41 | export async function deleteUser() {
42 | await usersApi.deleteUser()
43 | }
44 |
45 | //* LogOut
46 | export function logOut() {
47 | localStorage.removeItem("token")
48 | }
49 |
50 | export async function login(credentials) {
51 | const token = await usersApi.login(credentials)
52 | localStorage.setItem("token", token)
53 | return getUser()
54 | }
55 |
56 | export async function checkToken() {
57 | return usersApi.checkToken().then((dateStr) => new Date(dateStr))
58 | }
59 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | require("dotenv").config()
2 | require("./config/database") // connects to db
3 | const express = require("express")
4 | const path = require("path") // node module
5 | const favicon = require("serve-favicon")
6 | const logger = require("morgan")
7 |
8 | const app = express()
9 | // development port: 3001
10 | // in production we'll a PORT number set in the environment variables
11 | // const PORT = process.env.PORT || 3001
12 | const PORT = 3001
13 |
14 | //* Config
15 | // Logger middleware
16 | app.use(logger("dev"))
17 | // JSON payload middleware (for data coming from frontend functions)
18 | app.use(express.json())
19 | // Configure both serve-favicon & static middleware
20 | // to serve from the production 'build' folder
21 | app.use(favicon(path.join(__dirname, "build", "favicon.ico")))
22 | app.use(express.static(path.join(__dirname, "build")))
23 | // checks if token was sent and sets a user data on the req (req.user)
24 | app.use(require("./config/checkToken"))
25 |
26 | // * All other routes
27 | app.use("/api/users", require("./routes/api/users"))
28 |
29 | app.get("/api/*", (req, res) => {
30 | res.status(500).json("internal error")
31 | })
32 |
33 | // Put API routes here, before the "catch all" route
34 | // The following "catch all" route (note the *) is necessary
35 | // to return the index.html on all non-AJAX requests
36 | app.get("/*", (req, res) => {
37 | res.sendFile(path.join(__dirname, "build", "index.html"))
38 | })
39 |
40 | app.listen(PORT, () => {
41 | console.log(`Server is running on port: ${PORT}`)
42 | })
43 |
--------------------------------------------------------------------------------
/src/components/LogInForm.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react"
2 | import { login } from "../utilities/users-service"
3 |
4 | function LoginForm({ setUser }) {
5 | const [credentials, setCredentials] = useState({
6 | email: "",
7 | password: "",
8 | })
9 |
10 | const [error, setError] = useState("")
11 |
12 | function handleChange(evt) {
13 | setCredentials({ ...credentials, [evt.target.name]: evt.target.value })
14 | setError("")
15 | }
16 |
17 | async function handleSubmit(evt) {
18 | // Prevent form from being submitted to the server
19 | evt.preventDefault()
20 | try {
21 | // The promise returned by the signUp service method
22 | // will resolve to the user object included in the
23 | // payload of the JSON Web Token (JWT)
24 | const user = await login(credentials)
25 | console.log(user)
26 | setUser(user)
27 | } catch {
28 | setError("Log In Failed - Try Again")
29 | }
30 | }
31 |
32 | return (
33 |