Backend not working on Heroku after deployment - node.js

My backend is not running after deploying to Heroku. I receive the following axios error in the console.
enter image description here
When I run the app locally (with the code unchanged), I receive no errors. Right now, I can only see the front end home page. I cannot access anywhere else.
I have looked into solutions other people have provided. Most of them were about ensuring that the PORT that Heroku assigns was being used. down the bottom, I believe I have adjust the app.listen correctly. Currently this is what I have in my index.js:
index.js
const express = require('express')
const {MongoClient} = require('mongodb')
const {v4: uuidv4} = require('uuid')
const jwt = require('jsonwebtoken')
const cors = require('cors')
const bcrypt = require('bcrypt')
require('dotenv').config()
const uri = process.env.MONGODB_URL
const app = express()
app.use(cors())
app.use(express.json())
// const PORT = process.env.PORT || 8000
//----------------------------------- SIGNUP -----------------------------------
app.post('/signup', async (req, res) => {
const client = new MongoClient(uri)
const {email, password} = req.body
const generateduserId = uuidv4()
const hashedPassword = await bcrypt.hash(password, 10) //learn more about hashed passwords
try {
await client.connect()
const database = client.db('app-data')
const users = database.collection('users')
//check user exists
const existingUser = await users.findOne({email})
if (existingUser) {
return res.status(409).send('user already exists, please login')
}
const sanitizedEmail = email.toLowerCase()
//configuring data before adding to database
const data = {
user_id: generateduserId,
email: sanitizedEmail,
hashed_password: hashedPassword
}
const insertedUser = await users.insertOne(data) //inserts user (data) to database
//insertedUser and sanitizedEmail are required values to generate a token
const token = jwt.sign(insertedUser, sanitizedEmail, {
expiresIn: 60 * 24, //expires in 24 hours
})
res.status(201).json({token, userId: generateduserId})
} catch (error) {
console.log(error)
}
})
//----------------------------------- LOGIN -----------------------------------
app.post('/login', async (req, res) => {
const client = new MongoClient(uri)
const {email, password} = req.body
try {
await client.connect()
const database = client.db('app-data')
const users = database.collection('users')
const user = await users.findOne({email})
const correctPassword = await bcrypt.compare(password, user.hashed_password)
if (user && correctPassword) {
const token = jwt.sign(user, email, {
expiresIn: 60 * 24, //expires in 24 hours
})
res.status(201).json({token, userId: user.user_id})
} else {
res.status(400).send('invalid credentials')
}
} catch (error) {
console.log(error)
}
})
//----------------------------------- UPDATING -----------------------------------
app.put('/user', async (req, res) => {
const client = new MongoClient(uri)
const formData = req.body.formData
try {
await client.connect()
const database = client.db('app-data')
const users = database.collection('users')
const query = {user_id: formData.user_id}
const updateDocument = {
$set: {
first_name: formData.first_name,
dob_day: formData.dob_day,
dob_month: formData.dob_month,
dob_year: formData.dob_year,
show_gender: formData.show_gender,
gender_identity: formData.gender_identity,
gender_interest: formData.gender_interest,
url: formData.url,
about: formData.about,
matches: formData.matches
},
}
const insertedUser = await users.updateOne(query, updateDocument)
res.send(insertedUser)
} finally {
await client.close() //ensures client will close when finished or error occurs
}
})
//----------------------------------- DASHBOARD -----------------------------------
app.get('/user', async (req, res) => {
const client = new MongoClient(uri)
const userId = req.query.userId
try {
await client.connect()
const database = client.db('app-data')
const users = database.collection('users')
const query = {user_id: userId}
const user = await users.findOne(query)
res.send(user)
} finally {
await client.close()
}
})
//ONLY DISPLAY THE GENDER THAT USER WANTS
app.get('/gendered-users', async (req, res) => {
const client = new MongoClient(uri)
const gender = req.query.gender
try {
await client.connect()
const database = client.db('app-data')
const users = database.collection('users')
const query = { gender_identity: {$eq: gender} }
const foundUsers = await users.find(query).toArray() //return users with genders are interested in
res.send(foundUsers)
} finally {
await client.close() //ensures client will close when finished or error occurs
}
})
//ADD MATCHES TO USER DATABASE
app.put('/addmatch', async (req, res) => {
const client = new MongoClient(uri)
const {userId, matchedUserId} = req.body
try {
await client.connect()
const database = client.db('app-data')
const users = database.collection('users')
const query = {user_id: userId}
const firstUpdateDocument = {
$push: {matches: {user_id: matchedUserId}}
}
const firstMatchesUpload = await users.updateOne(query, firstUpdateDocument)
const userMatches = await users.findOne({user_id: userId})
//remove duplicates
const jsonObj = userMatches.matches.map(JSON.stringify)
const uniqueSet = new Set(jsonObj)
const uniqueArray = Array.from(uniqueSet).map(JSON.parse)
const user = await users.updateOne(query, {$set: {matches: uniqueArray}})
res.send(user)
} finally {
await client.close()
}
})
//SHOW MATCHED USERS IN MATCHES DISPLAY
app.get('/users', async (req, res) => {
const client = new MongoClient(uri)
const userIds = JSON.parse(req.query.userIds)
try {
await client.connect()
const database = client.db('app-data')
const users = database.collection('users')
const pipeline = [
{
'$match': {
'user_id': {
'$in': userIds
}
}
}
]
const foundUsers = await users.aggregate(pipeline).toArray()
res.send(foundUsers)
} finally {
await client.close()
}
})
//----------------------------------- CHAT -----------------------------------
app.get('/messages', async (req, res) => {
const client = new MongoClient(uri)
const {userId, correspondingUserId} = req.query
try {
await client.connect()
const database = client.db('app-data')
const messages = database.collection('messages')
const query = {
from_userId: userId, to_userId: correspondingUserId
}
const foundMessages = await messages.find(query).toArray()
res.send(foundMessages)
} finally {
await client.close()
}
})
app.post('/message', async (req, res) => {
const client = new MongoClient(uri)
const message = req.body.message
try {
await client.connect()
const database = client.db('app-data')
const messages = database.collection('messages')
const insertedMessage = await messages.insertOne(message)
res.send(insertedMessage)
} finally {
await client.close()
}
})
app.use(express.static(__dirname + '/client/build'))
app.get('*', (req, res) => {
res.sendFile(__dirname + '/client/build/index.html')
})
app.listen(process.env.PORT || 8000)
According to this: https://coursework.vschool.io/deploying-mern-app-to-heroku/
the app.listen should come after the app.get('*'...), however this and other configurations have not worked.
AuthModal.js (where the error occurs):
import {useState} from 'react'
import axios from 'axios'
import {useNavigate} from 'react-router-dom'
import {useCookies} from'react-cookie'
const AuthModal = ({setShowModal, isSignUp}) => {
const [email, setEmail] = useState(null)
const [password, setPassword] = useState(null)
const [confirmPassword, setConfirmPassword] = useState(null)
const [error, setError] = useState(null)
const [cookies, setCookie, removeCookie] = useCookies(['user'])
let navigate = useNavigate()
isSignUp ? console.log(email, password, confirmPassword) : console.log(email, password)
const handleClick = () => {
setShowModal(false)
}
const handleSubmit = async (e) => {
e.preventDefault() //prevents page from refreshing
try {
if (isSignUp && (password !== confirmPassword)) {
setError('Passwords do not match')
return
}
// posts the email and password to the signup in backend or to login depending
const response = await axios.post(`http://localhost:8000/${isSignUp ? 'signup': 'login'}`, {email, password})
console.log('response', response)
//these are the parameters returned from the jsonwebtoken response status code
setCookie('AuthToken', response.data.token)
setCookie('UserId', response.data.userId)
const success = response.status === 201
if (success && isSignUp) navigate ('/onboarding') //if post request successful and sigin up, navigate to onboarding page
if (success && !isSignUp) navigate ('/dashboard') //if post request successful and loggin in, navigate to dashboard page
window.location.reload() //make sure auth token is read by /onboarding and /dashboard
} catch (error) {
console.log(error)
}
}
return (
<div className="auth_modal">
<div className='close_icon' onClick={handleClick}>✕</div>
<h2>{isSignUp ? 'CREATE ACCOUNT' : 'LOG IN'}</h2>
<p>By Logging In you agree to our T&Cs</p>
<form onSubmit={handleSubmit}>
<input
type='email'
id='email'
name='email'
placeholder='email'
required={true}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type='password'
id='password'
name='password'
placeholder='password'
required={true}
onChange={(e) => setPassword(e.target.value)}
/>
{isSignUp && <input
type='password'
id='password-check'
name='password-check'
placeholder='confirm password'
required={true}
onChange={(e) => setConfirmPassword(e.target.value)}
/> }
<input className='secondary_button' type='submit' />
<p>{error}</p>
</form>
<hr/>
<h2>GET THE APP</h2>
</div>
)
}
export default AuthModal
package.json (backend):
{
"name": "server",
"version": "1.0.0",
"engines": {
"node": "18.9.1"
},
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"heroku-postbuild": "cd client && npm i && npm run build"
},
"author": "",
"license": "ISC",
"dependencies": {
"bcrypt": "^5.1.0",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.0",
"mongodb": "^4.13.0",
"nodemon": "^2.0.20",
"uuid": "^9.0.0"
}
}
I have also added the config vars through the heroku dashboard (being the mongodb url).

You should update this line on the authModal.js
const response = await axios.post(`http://localhost:8000/${isSignUp ? 'signup': 'login'}`, {email, password})
with
const response = await axios.post(`/${isSignUp ? 'signup': 'login'}`, {email, password})
Make sure that this file is handle on the backend side.

Related

Cookies doesn't show up in my application ReactJS

Hello i'm trying to code auth for my app i'm using json web token the problem is when i send post request using postman i can see the cookie and access token in headers but in my application i can't see anything in my localstorage&cookies
Here is my code
authContext.js
import axios from "axios";
import { createContext, useEffect, useState } from "react";
export const AuthContext = createContext();
export const AuthContextProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(
JSON.parse(localStorage.getItem("user")) || null
);
const login = async (inputs) => {
const res = await axios.post("http://localhost:8800/api/auth/login", inputs, {
withCredentials: true,
});
setCurrentUser(res.data)
};
useEffect(() => {
localStorage.setItem("user", JSON.stringify(currentUser));
console.log(currentUser);
}, [currentUser]);
return (
<AuthContext.Provider value={{ currentUser, login }}>
{children}
</AuthContext.Provider>
);
};
in login.jsx
const [inputs, setInputs] = useState({
username: "",
password: "",
});
const [err, setErr] = useState(null);
const navigate = useNavigate()
const handleChange = (e) => {
setInputs((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
const login = useContext(AuthContext);
const handleLogin = async (e) => {
e.preventDefault();
try {
await login(inputs);
navigate("/")
} catch (err) {
setErr(err.response.data);
}
};
console.log(err);
console.log(inputs);
I'm trying to solve the problem because i'm trying to create a basic social app i need accessToken to display posts in my feed easily

Showing only Post That user Posted in Reactjs with Server Side is Node Js, Mongodb

What I want?
I want to add post that only login user post. This post only showing My Item component page. here is my all code. When I login and try to add post and then check my item component page. this page showing all of the post. app.get and Server url '/product' it's collect ```email object'''
Thank you.
async function run() {
try {
await client.connect();
const productCollection = client.db("data").collection("product");
app.get("/product", async (req, res) => {
const query = {};
const cursor = productCollection.find(query);
const products = await cursor.toArray();
res.send(products);
});
app.get("/product", async (req, res) => {
const email = req.query.email;
const query = {email: email};
const cursor = productCollection.find(query);
const products = await cursor.toArray();
res.send(products);
});
app.get("/product/:id", async (req, res) => {
const id = req.params.id;
const query = { _id: ObjectId(id) };
const product = await productCollection.findOne(query);
res.send(product);
});
app.post("/product", async (req, res) => {
const newProduct = req.body;
const result = await productCollection.insertOne(newProduct);
res.send(result);
});
// DELETE
app.delete("/product/:id", async (req, res) => {
const id = req.params.id;
const query = { _id: ObjectId(id) };
const result = await productCollection.deleteOne(query);
res.send(result);
});
app.put('/update-quantity/:id', async (req, res) => {
const id = req.params.id;
const updatedInventoryInfo = req.body;
const filter = { _id: ObjectId(id) };
const options = { upsert: true };
const updatedDoc = {
$set: {
quantity: updatedInventoryInfo.quantity,
sold: updatedInventoryInfo.sold
}
}
const result = await productCollection.updateOne(filter, updatedDoc, options);
res.send(result);
})
} finally {
}
}
Client Side Code React Js
import axios from "axios";
import React, { useEffect } from "react";
import { Button, Col, Container, Row } from "react-bootstrap";
import { useAuthState } from "react-firebase-hooks/auth";
import { useNavigate } from "react-router-dom";
import auth from "../../firebase.init";
import useProduct from "../../Hooks/useProduct";
const MyItem = () => {
const [user] = useAuthState(auth);
const [products, setProducts] = useProduct();
const navigate = useNavigate();
useEffect(() => {
const getItems = async () => {
const email = user.email;
console.log(email);
const url = `http://localhost:5000/product?email=${email}`;
const { data } = await axios.get(url);
setProducts(data);
};
getItems();
}, [user]);
const handelDelete = (id) => {
const process = window.confirm(
"Are you sure you want to delete this item?"
);
if (process) {
const url = `http://localhost:5000/product/${id}`;
fetch(url, {
method: "DELETE",
})
.then((res) => res.json())
.then((result) => {
console.log(result);
setProducts(products.filter((product) => product._id !== id));
});
}
};
const navigateToProductDetails = (id) => {
navigate(`/product/${id}`);
};
return (
<>
<Container>
<Row>
{products.map((product, index) => {
return (
<Col key={index} md={4}>
<div className="product-aria">
<img src={product.img} alt="" />
<div>
<h1>{product.name}</h1>
<p>Details:{product.content}</p>
<div className="d-flex gap-3 flex-wrap justify-content-center">
<Button
onClick={() => navigateToProductDetails(product._id)}
variant="primary"
>
Manage
</Button>
<Button
onClick={() => handelDelete(product._id)}
variant="danger"
>
Delete
</Button>
</div>
</div>
</div>
</Col>
);
})}
</Row>
</Container>
</>
);
};
export default MyItem;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Node Js
Your Are using same api url /products. So I Change this url and its work fine now.
app.get("/products", async (req, res) => {
const query = {};
const cursor = productCollection.find(query);
const products = await cursor.toArray();
res.send(products);
});
app.get("/product", async (req, res) => {
const email = req.query.email;
const query = {email: email};
const cursor = await productCollection.find(query).toArray();
res.send(cursor);
});
app.get("/product/:id", async (req, res) => {
const id = req.params.id;
const query = { _id: ObjectId(id) };
const product = await productCollection.findOne(query);
res.send(product);
});
app.post("/products", async (req, res) => {
const newProduct = req.body;
const result = await productCollection.insertOne(newProduct);
res.send(result);
});
I think you are using a custom hook. useProduct() I just comment it and use useState() hook and change the dependancy useEffect hook user to email. Here is my code.
const [user] = useAuthState(auth);
const { email } = user;
console.log(email);
// const [products, setProducts] = useProduct();
const [products, setProducts] = useState([]);
const navigate = useNavigate();
useEffect(() => {
const getItems = async () => {
// console.log(email);
const url = `http://localhost:5000/product?email=${email}`;
const { data } = await axios.get(url);
setProducts(data);
};
getItems();
}, [email]);

401 error in axios post request to local server

Context
I'm building a simple web application using the MERN stack for practice. In the app, logged-in users should be able to add a new blog to the site. However, for some reason my axios post request to the backend is failing and I'm receiving a 401 error. I'm using jsonwebtoken to handle the authentication. Submitting a POST request via Insomnia works fine so I don't believe it's an issue with my endpoint. I'm running backend server locally on my machine on port 3003 and have set up a proxy so there's no issues with cors. This works fine as the blogs from the backend are displays on the frontend once a user has logged in.
I've also checked the headers and can confirm that logged-in users have a valid bearer token.
What could be causing the issue?
Frontend
I can't post any images but here's a link to the frontend view:
https://imgur.com/a/DdUlfg9
App.js
import React, { useState, useEffect } from 'react'
import Blog from './components/Blog'
import blogService from './services/blogs'
import loginService from './services/login'
import LoginForm from './components/loginForm'
import BlogForm from './components/blogForm'
const App = () => {
const [blogs, setBlogs] = useState([])
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [user, setUser] = useState(null)
const [errorMessage, setErrorMessage] = useState(null)
const [newBlog, setNewBlog] = useState({
title: '',
author: '',
url: ''
})
useEffect(() => {
blogService.getAll().then(blogs =>
setBlogs( blogs )
)
}, [])
useEffect(() => {
const loggedInUser = window.localStorage.getItem("loggedBlogUser")
if(loggedInUser){
const user = JSON.parse(loggedInUser)
setUser(user)
}
},[])
const handleLogin = async (event) => {
event.preventDefault()
try {
const user = await loginService.login({
username, password
})
window.localStorage.setItem(
'loggedBlogUser', JSON.stringify(user)
)
blogService.setToken(user.token)
setUser(user)
setUsername('')
setPassword('')
} catch (exception){
setErrorMessage('Wrong credentials')
setTimeout(() => {
setErrorMessage(null)
}, 5000)
}
}
const handleLogout = async (event) => {
event.preventDefault()
if(user){
window.localStorage.removeItem("loggedBlogUser")
setUser(null)
}
}
const handleBlogField = (event) => {
event.preventDefault()
const {name, value} = event.target
console.log(newBlog.title)
setNewBlog(prevBlog => ({
...prevBlog,
[name] : value
}))
}
const addBlog = async (event) => {
event.preventDefault()
try {
const blog = await blogService.create(newBlog)
console.log("POST REQUEST: ",newBlog)
console.log('lets geddit')
setBlogs(blogs.concat(blog))
} catch (exception){
setErrorMessage('Uh oh, try again :[')
setTimeout(() => {
setErrorMessage(null)
}, 5000)
}
}
if(user === null){
return(
<>
{errorMessage}
<h2>Log into application</h2>
<LoginForm handleLogin={handleLogin} setUsername={setUsername} setPassword={setPassword} username={username} password={password}/>
</>
)
}
return (
<div>
<h2>blogs</h2>
{user &&
<div>
<h3>{user.username} logged in</h3>
<button onClick={handleLogout}>Logout</button>
</div>
}
<BlogForm handleSubmission={addBlog} newBlog={newBlog} handleBlogField={setNewBlog}/>
{/* <BlogForm addBlog={addBlog} title={newBlog.title} setTitle={setTitle} setAuthor={setAuthor} author={newBlog.author} url={newBlog.url} setUrl={setUrl}/> */}
{blogs.map(blog =>
<Blog key={blog.id} blog={blog} />
)}
</div>
)
}
export default App
Blogs.js
import axios from 'axios'
const baseUrl = '/api/blogs'
let token = null
//let config
const setToken = (newToken) => {
token = `bearer ${newToken}`
}
const getAll = async () => {
const response = await axios.get(baseUrl)
return response.data
}
const create = async (newObject) => {
const config = {
headers: {
Authorization: token
}
}
const response = await axios.post(baseUrl, newObject, config)
console.log(`RESPONSE: ${newObject}`)
return response.data
}
const blogService = {
getAll, setToken, create
}
export default blogService
Have you configured CORS?, in order to accept your localhost requests?

Why is the response (payment Intent) after completing the payment undefined?

myReactApp/functions/index.js
const functions = require("firebase-functions");
const express = require("express");
const cors = require("cors");
const stripe = require('stripe')
('sk_test_**********');
// API
// App config
const app = express();
// Middlewares
app.use( cors({origin:true}) );
app.use(express.json());
// API routes
app.get('/', (request, respond) => respond.status(200).send("page is working") );
app.post('/payment/create', async (request, response) => {
const total = request.query.total;//get the value of total from URL using query
console.log('payment request recived >>>' , total);
const paymentIntent = await stripe.paymentIntents.create({
amount: total,
currency: 'USD'
})
response.status(201).send({
clientSecret : paymentIntent.client_secret,
})
})
//http://localhost:5001/clone-21937/us-central1/api
// Listen command
exports.api = functions.https.onRequest(app);
myReactApp/src/payment.js
import React, { useEffect, useState } from 'react';
import Orders from './Orders';
import CheckoutProduct from './CheckoutProduct';
import CurrencyFormat from 'react-currency-format';
import { CardElement, useElements, useStripe } from '#stripe/react-stripe-js';
import { collection, addDoc, setDoc } from 'firebase/firestore/lite'
import { Link, useNavigate } from 'react-router-dom';
import { useStateValue } from './StateProvider';
import { db } from './firebase';
import axios from './axios';
const Payment = () => {
const stripe = useStripe();
const elements = useElements();
const navigate = useNavigate();
const [{ basket, subtotal, user }, dispatch] = useStateValue();
const [succeeded, setSucceeded] = useState(false);
const [processing, setProcessing] = useState('');
const [error, setError] = useState(null);
const [disabled, setDisabled] = useState(true);
const [clientSecret, setClientSecret] = useState(true);
useEffect(() => {
const getClientSecret = async () => {
const response = await axios({
method: 'POST',
url: `/payment/create?total=${subtotal * 100}`
});
//clientSecret is the amount to be paid
//fetching the clientSecret send from response (Index.js)
setClientSecret(response.data.clientSecret);
}
getClientSecret();
}, [basket]);
//handle submitting the form
const handleSubmit = async (event) => {
event.preventDefault();
setProcessing(true);
const payload = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: elements.getElement(CardElement)
}
}).then( ({paymentIntent}) => {
console.log(paymentIntent);//undefined
try {
addDoc(
collection(db,'users', user?.uid, 'orders'),{
basket:basket
}
)
}
catch (error) {
alert(error.message);
}
//empty the basket after order
dispatch({
type: 'EMPTY_BASKET'
})
setSucceeded(true);
setProcessing(false);
setError(null);
// navigate('/orders');
})
}
const handleChange = (event) => {
setDisabled(event.empty);
setError(event.error ? event.error.message : '');
}
return (
<div className="payment_sectionTransaction">
<form onSubmit={handleSubmit}>
<CardElement className='payment_cardElement' onChange={handleChange}/>
<CurrencyFormat
value={subtotal}
prefix={'$'}
decimalScale={2}
thousandSeparator={true}
displayType={'text'}
renderText={(value) => {
return <>
<p><strong>Amount: {value}</strong></p>
</>
}} />
<button type='submit' disabled={processing || succeeded || disabled} >
{processing ? 'Processing..' : 'Confirm Order'}
</button>
</form>
</div>
)
}
export default Payment
After payment I am trying to get the response-paymentIntent but it shows to be undefined.
I can see the payment in the stripe dashboard so may be payment sections is all good but response after the payment is not good.
I also get this error in console after payment get done:
POST http://localhost:5001/clone-21937/us-central1/api/payment/create?total=0 net::ERR_FAILED 200
getClientSecret is async function. You must write code look like
const result = await getClientSecret();

App does not reload, authentication fails despite JWT token remaining in local storage react/node.js

I have had success implementing the application in its entirety but the only problem I am having deals with authorization when reloading the page. I have set a "token" key in localStorage but it fails to retrieve this key or token (presumably) on reload. I can clearly see it is defined in Chrome's Inspect but when I try to console.log or use that localStorage variable in anyway after reloading it is reads undefined. I can still see the token visible in Chrome's Application Storage and I am not sure how this is possible. The app works properly in development but when deployed to heroku or when express is used to deliver the static file it also stops working and this behavior of returning undefined keeps happening always returning "res.status(403).json("Not Authorized (authorization catch)")"
I have gone through many documents in express and jwt along with many SO solutions. It seems most people lose their token but that is not the case here.
My server code looks like:
const express= require("express")
const app = express()
const cors = require("cors")
const path = require('path')
// middleware
app.use(express.json())
app.use(cors())
// ROUTES
// register and login
app.use("/auth", require("./routes/jwtAuth"))
app.use("/dashboard", require("./routes/dashboard"))
app.use("/", express.static(path.join(__dirname, 'client/build')))
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client/build/index.html"));
});
const PORT = process.env.PORT || 5000
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`)
})
And the page that I would like to reload includes:
import React, { Fragment, useState, useEffect } from 'react'
//components
import InputConnection from './connectionlist/InputConnection'
import ListConnections from './connectionlist/ListConnections'
import LogoutBtn from './LogoutBtn'
import ReportingLayout from './reporting/Layout/ReportingLayout'
const Dashboard = ({ setAuth }) => {
const [name, setName] = useState("")
const [allConnections, setAllConnections] = useState([])
const [connectionsChange, setConnectionsChange] = useState(false)
const auth = setAuth
const getName = async () => {
try {
const response = await fetch("/dashboard/", {
method:"GET",
headers:{ token: localStorage.token }
})
const parseData = await response.json()
// console.log(parseData)
if (parseData.admin === 'lead') {
setName("Lead School Counselor")
setAllConnections(parseData.results)
}else{
setName(parseData[0].user_name)
setAllConnections(parseData)
}
} catch (error) {
}
}
useEffect(() => {
getName()
setConnectionsChange(false)
}, [connectionsChange])
if(name === "Lead School Counselor" ){
return(
<div>
<ReportingLayout auth={auth} allConnections={ allConnections } />
</div>
)
}else{
return(
<Fragment>
<div className="container">
<div className='btn-group '>
<LogoutBtn setAuth = {setAuth}/>
</div>
<h1 className="d-flex rm-3" > Welcome {name}, </h1>
<InputConnection setConnectionsChange={setConnectionsChange}/>
<ListConnections allConnections={ allConnections } setConnectionsChange=
{setConnectionsChange}/>
</div>
</Fragment>
)
}
}
export default Dashboard;
This is where the code fails. It is a middleware that deals with the authorization and prinst the error message from step 1:
const jwt = require("jsonwebtoken")
require("dotenv").config()
module.exports = async (req, res, next) => {
try {
// step 1 destructure
const jwtToken = req.header("token")
if(!jwtToken){
return res.status(403).json("Not Authorized (authorization not jwt Token)")
}
// step 2 check if the token is valid
const payload = jwt.verify(jwtToken, process.env.jwtSecret)
// step 3 gives access as req.user
req.user = payload.user
next()
} catch (err) {
console.error(err.message)
return res.status(403).json("Not Authorized (authorization catch)")
}
}
App.js:
import React, { Fragment, useState, useEffect } from 'react';
import './App.css';
import {BrowserRouter as Router, Switch, Route, Redirect} from 'react-
router-dom'
// components
import Dashboard from './components/dashboard/Dashboard'
import Login from './components/Login'
import Register from './components/Register'
import Landing from './components/Landing'
//toastify
import "react-toastify/dist/ReactToastify.css";
import { toast } from "react-toastify";
toast.configure()
function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false)
const setAuth = (boolean) => {
setIsAuthenticated(boolean)
}
async function isAuth(){
try {
const response = await fetch("/auth/is-verified", {
method:"GET",
headers:{token: localStorage.getItem("token") }
app.s })
const parseRes = await response.json()
console.log(`this message is ${parseRes}`)
parseRes === true ? setIsAuthenticated(true): setIsAuthenticated(false)
} catch (err) {
console.error(err.message)
}
}
useEffect(() => {
isAuth()
})
return (
<Fragment>
<Router>
<div>
<Switch>
<Route exact path="/landing" render={props => !isAuthenticated
? <Landing {...props} /> : <Redirect to='/dashboard'/>} />
<Route exact path="/register" render={props => !isAuthenticated ?
<Register {...props} setAuth ={setAuth} /> : <Redirect to='/login'/>} />
<Route exact path="/login" render={props => !isAuthenticated ?
<Login {...props} setAuth ={setAuth} auth={isAuthenticated}/> : <Redirect
to='/dashboard'/>} />
<Route exact path="/dashboard" render={props => isAuthenticated ?
<Dashboard {...props} setAuth ={setAuth} /> : <Redirect to='/login'/>} />
</Switch>
</div>
</Router>
</Fragment>
);
}
export default App;
here is the dashboard.js db and connections:
const router = require("express").Router()
const pool = require("../db")
const authorization = require("../middleware/authorization")
// all connections and name
router.get("/", authorization, async (req, res) => {
try {
res.json(req.user.name)
if(req.user.name === 'lead'){
const lead = await pool.query("SELECT * FROM connections LEFT JOIN
users ON users.user_id = connections.user_id")
... A bunch of sql queries here that work fine...
res.json({
admin: req.user.name,
results: lead.rows,
// aggregated queries
studentsEngaged: studentsEngaged.rows,
gender:gender.rows,
distinctStudents: distinctStudents.rows,
amountSep: amountSep.rows,
amountOct:amountOct.rows,
amountNov: amountNov.rows,
amountDec: amountDec.rows,
studentSessions:studentSessions.rows,
homeVisits: homeVisits.rows,
outsideAgencies: outsideAgencies.rows,
cpReferrals: cpReferrals.rows,
amountReferrals:amountReferrals.rows,
amountDischarges: amountDischarges.rows,
classroomPresentations: classroomPresentations.rows,
groupSessions: groupSessions.rows,
checkins: checkins.rows,
crisisInterventions: crisisInterventions.rows,
parentContacts: parentContacts.rows,
meetings : meetings.rows
})
}else{
const user = await pool.query("SELECT u.user_name, c.connection_id,
c.contact_type, c.contact_method, c.provision, c.connection_date,
c.student_id,
c.purpose, c.gender, c.yearGroup, c.school, c.referral_discharge,
c.cp_referral
FROM users AS u LEFT JOIN connections AS c ON u.user_id = c.user_id WHERE
u.user_id= $1", [req.user.id])
res.json(user.rows)
}
} catch (err) {
console.error(err.message)
res.status(500).json("Server Error (dashboard catch)")
}
})
// create connection
router.post("/connections", authorization, async (req, res) => {
try {
// console.log(req.body);
const { student_id, contact_type, yearGroup, school, contact_method,
gender, purpose, provision, connection_date, referral_discharge, cp_referral}
=
req.body;
const newConnection = await pool.query(
"INSERT INTO connections (user_id, student_id, user_name, contact_type,
yearGroup, school, contact_method, gender, purpose, provision,
connection_date,
referral_discharge, cp_referral) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9,
$10, $11, $12, $13) RETURNING *",
[req.user.id, student_id, req.user.name, contact_type, yearGroup, school,
contact_method, gender, purpose, provision, connection_date,
referral_discharge,
cp_referral]
);
res.json(newConnection.rows[0]);
// console.log(newConnection.rows[0])
} catch (err) {
console.error(err.message)
}
});
// update connection
router.put("/connections/:id", authorization, async (req, res) => {
try {
const { id } = req.params;
const { student_id, contact_type, yearGroup, school, contact_method,
gender, purpose, provision, connection_date, referral_discharge, cp_referral
} =
req.body;
const updateConneciton = await pool.query(
"UPDATE connections SET student_id=$1, contact_type=$2, yearGroup=$3,
school=$4, contact_method=$5, gender=$6, purpose=$7, provision=$8,
connection_date=$9, referral_discharge=$10, cp_referral=$11 WHERE
connection_id =
$12 AND user_id = $13 RETURNING *",
[student_id, contact_type, yearGroup, school, contact_method, gender,
purpose, provision, connection_date, referral_discharge, cp_referral, id,
req.user.id]
);
if (updateConneciton.rows.length === 0) {
return res.json("This connection is not yours");
}
res.json("Connection was updated");
} catch (err) {
console.error(err.message);
}
});
// delete connection
router.delete("/connections/:id", authorization, async (req, res) => {
try {
const { id } = req.params;
const deleteConnection = await pool.query(
"DELETE FROM connections WHERE connection_id = $1 AND user_id = $2
RETURNING *",
[id, req.user.id]
);
if (deleteConnection.rows.length === 0) {
return res.json("This connection is not yours");
}
res.json("Connection was deleted");
} catch (err) {
console.error(err.message);
}
}) ;
At input is appended to the headers:
import React, { Fragment, useState } from "react";
import { toast } from 'react-toastify'
const InputTodo = ({ setConnectionsChange }) => {
const [contact_type, setContactType] = useState("");
const [contact_method, setContactMethod] = useState("");
const [provision, setProvision] = useState("");
const [connection_date, setDate] = useState("");
const [student_id, setStudentID] = useState("");
const [purpose, setPurpose] = useState("");
const [gender, setGender] = useState("");
const [yearGroup, setYearGroup] = useState("");
const [school, setSchool] = useState("");
const [referral_discharge, setReferralDischarge] = useState("");
const [cp_referral, setCPReferral] = useState("");
const onSubmitForm = async e => {
e.preventDefault();
try {
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("token", localStorage.token);
const body = {
contact_type,
contact_method,
provision,
connection_date,
student_id,
purpose,
gender,
yearGroup,
school,
referral_discharge,
cp_referral
};
const response = await fetch("/dashboard/connections", {
method: "POST",
headers: myHeaders,
body: JSON.stringify(body)
});
const parseResponse = await response.json();
console.count(parseResponse);
setConnectionsChange(true);
setContactType("")
setContactMethod("")
setProvision("")
setDate("")
setStudentID("")
setPurpose("")
setGender("")
setYearGroup("")
setSchool("")
setReferralDischarge("")
setCPReferral("")
toast.success('Contact has been added', {
position: "top-center",
autoClose: 3000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
});
} catch (error) {
console.error(error.message);
}
};
return (
<Fragment> A form
</Fragment>
)
Authorization check on back end:
const router = require("express").Router()
const pool = require("../db")
const bcrypt = require("bcrypt")
const jwtGenerator = require("../utils/jwtGenerator")
const validInfo = require("../middleware/validInfo")
const authorization = require("../middleware/authorization")
// registering
router.post("/register", validInfo, async(req, res) =>{
try {
// step1 destructure
const { name, email, password } = req.body
// step2 check if the user exists
const user = await pool.query("SELECT * FROM users WHERE
user_email=$1", [email])
if(user.rows.length >0){
return res.status(401).json("User already exists; email is
already registered with the database")
}
// step3 bcrypt the user password for db
const saltRound = 10;
const salt = await bcrypt.genSalt(saltRound)
const bcryptPassword = await bcrypt.hash(password, salt)
// step4 insert the info into the db
const newUser = await pool.query("INSERT INTO users (user_name,
user_email, user_password) VALUES ($1, $2, $3) RETURNING *", [name, email,
bcryptPassword])
// step5 generate a jwt token
const token = jwtGenerator(newUser.rows[0].user_id,
newUser.rows[0].user_name)
res.json({ token })
} catch (err) {
console.error(err.message)
res.status(500).json("Server Error (register)")
}
})
// login and logout
router.post("/login", validInfo, async (req, res) => {
try {
// step1 deconstruct req.body
const { email, password } = req.body
// step 2 check if user doesnt exist and if not throw and error
const user = await pool.query("SELECT * FROM users WHERE
user_email=$1", [email])
if(user.rows.length === 0){
return res.status(401).json("User email is incorrect or does not
exist.")
}
// step 3 check if incoming pword is the same as db password
const validPassword = await bcrypt.compare(password,
user.rows[0].user_password)
if(!validPassword){
return res.status(401).json("Password is incorrect.")
}
// step4 give them a jwt token
const token = jwtGenerator(user.rows[0].user_id, user.rows[0].user_name)
res.json({ token })
} catch (err) {
console.log(err.Message)
res.statusMessage(500).json("Server Error (login)")
}
})
router.get("/is-verified", authorization, (req, res) => {
try {
res.json(true)
} catch (error) {
console.log(err.Message)
res.statusMessage(500).json("Server Error (is-verified)")
}
})
module.exports = router;
Instead of reloading, I will type in "/login" into the url and it will then redirect me (using the routes set up) to dashboard no problem. Could I just direct the user to log in on reloading and if so what is an optimal way to implement it?

Resources