Skip to content

Commit 1276839

Browse files
committed
first commit
1 parent 1aab631 commit 1276839

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+30028
-1
lines changed

.DS_Store

6 KB
Binary file not shown.

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ npm-debug.log*
55
yarn-debug.log*
66
yarn-error.log*
77
lerna-debug.log*
8-
8+
.env
9+
*/data
910
# Diagnostic reports (https://nodejs.org/api/report.html)
1011
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
1112

backend/config/db.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import mongoose from "mongoose";
2+
3+
const connectDB = async () => {
4+
try {
5+
const conn = await mongoose.connect(process.env.MONGO_URI, {
6+
useUnifiedTopology: true,
7+
useNewUrlParser: true,
8+
useCreateIndex: true,
9+
});
10+
console.log(`MongoDB Connected: ${conn.connection.host}`.cyan.underline);
11+
} catch (error) {
12+
console.error(`Error: ${error.message}`.red.underline.bold);
13+
process.exit(1);
14+
}
15+
};
16+
17+
export default connectDB;

backend/controllers/userController.js

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import asyncHandler from "express-async-handler";
2+
import generateToken from "../utils/generateToken.js";
3+
import User from "../models/userModel.js";
4+
import { auth as firebaseAuth } from "../firebase.js";
5+
import dotenv from "dotenv";
6+
dotenv.config();
7+
8+
// @desc Auth user & get token
9+
// @route POST /api/users/login
10+
// @access Public
11+
const authUser = asyncHandler(async (req, res) => {
12+
const { email, password } = req.body;
13+
14+
try {
15+
const {
16+
user: { emailVerified },
17+
} = await firebaseAuth.signInWithEmailAndPassword(email, password);
18+
19+
const user = await User.findOne({ email });
20+
21+
if (user) {
22+
res.json({
23+
_id: user._id,
24+
firstName: user.firstName,
25+
lastName: user.lastName,
26+
email: user.email,
27+
isAdmin: user.isAdmin,
28+
token: generateToken(user._id),
29+
verified: emailVerified,
30+
});
31+
} else {
32+
res.status(404);
33+
throw new Error("User not found");
34+
}
35+
} catch (e) {
36+
res.status(404);
37+
var errorMessage = e.message;
38+
if (
39+
errorMessage ===
40+
"The password is invalid or the user does not have a password."
41+
) {
42+
errorMessage = "Invalid password";
43+
} else if (
44+
errorMessage ===
45+
"There is no user record corresponding to this identifier. The user may have been deleted."
46+
) {
47+
errorMessage = "User not found";
48+
}
49+
throw new Error(errorMessage);
50+
}
51+
});
52+
53+
// @desc Resend email notification
54+
// @route POST /api/users/login/email-verification
55+
// @access Private
56+
const sendEmailVerification = asyncHandler(async (req, res) => {
57+
const { email, password } = req.body;
58+
try {
59+
const {
60+
user: { emailVerified },
61+
} = await firebaseAuth.signInWithEmailAndPassword(email, password);
62+
await firebaseAuth.currentUser.sendEmailVerification();
63+
res.json({ status: "Email verification sent." });
64+
} catch (e) {
65+
var errorMessage = e.message;
66+
if (
67+
e.message ===
68+
"We have blocked all requests from this device due to unusual activity. Try again later."
69+
) {
70+
errorMessage = "Please wait a few minutes and check your inbox.";
71+
}
72+
console.log(errorMessage);
73+
throw new Error(errorMessage);
74+
}
75+
});
76+
77+
// @desc Resend email notification
78+
// @route POST /api/users/login/forget-password
79+
// @access Private
80+
const sendForgetPasswordEmail = asyncHandler(async (req, res) => {
81+
const { email } = req.body;
82+
try {
83+
firebaseAuth.sendPasswordResetEmail(email);
84+
res.json({ status: "Password reset email sent." });
85+
} catch (e) {
86+
var errorMessage = e.message;
87+
if (
88+
e.message ===
89+
"We have blocked all requests from this device due to unusual activity. Try again later."
90+
) {
91+
errorMessage = "Please wait a few minutes and check your inbox.";
92+
}
93+
throw new Error(errorMessage);
94+
}
95+
});
96+
97+
// @desc Create user
98+
// @route POST /api/users
99+
// @access Public
100+
const registerUser = asyncHandler(async (req, res) => {
101+
const { firstName, lastName, email, password, isAdmin } = req.body;
102+
103+
const {
104+
user: { emailVerified },
105+
} = await firebaseAuth.createUserWithEmailAndPassword(email, password);
106+
await firebaseAuth.currentUser.sendEmailVerification();
107+
108+
const userExists = await User.findOne({ email });
109+
if (userExists) {
110+
res.status(400);
111+
throw new Error("User already exists.");
112+
}
113+
114+
const user = await User.create({
115+
firstName,
116+
lastName,
117+
email,
118+
isAdmin: isAdmin || false,
119+
});
120+
121+
if (user) {
122+
res.status(201).json({
123+
_id: user._id,
124+
firstName: user.firstName,
125+
lastName: user.lastName,
126+
email: user.email,
127+
isAdmin: user.isAdmin,
128+
verified: emailVerified,
129+
token: generateToken(user._id),
130+
});
131+
} else {
132+
res.status(400);
133+
throw new Error("Invalid user data");
134+
}
135+
});
136+
137+
// @desc Get all users
138+
// @route GET /api/users
139+
// @access Private/Admin
140+
const getUsers = asyncHandler(async (req, res) => {
141+
const users = await User.find({});
142+
res.json(users);
143+
});
144+
145+
// @desc Delete a user
146+
// @route DELETE /api/users/:id
147+
// @access Private/Admin
148+
const deleteUser = asyncHandler(async (req, res) => {
149+
const user = await User.findById(req.params.id);
150+
// TODO: maybe delete user in firebase
151+
152+
if (user) {
153+
await user.remove();
154+
res.json({ message: "User removed" });
155+
} else {
156+
res.status(404);
157+
throw new Error("User not found.");
158+
}
159+
});
160+
161+
export {
162+
authUser,
163+
registerUser,
164+
getUsers,
165+
deleteUser,
166+
sendEmailVerification,
167+
sendForgetPasswordEmail,
168+
};

backend/firebase.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import firebase from "@firebase/app";
2+
import "@firebase/auth";
3+
4+
const app = firebase.default.initializeApp({
5+
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
6+
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
7+
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
8+
storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
9+
messagingSenderId: process.env.REACT_APP_FIREBASE_API_MESSAGING_SENDER_ID,
10+
appId: process.env.REACT_APP_FIREBASE_APP_ID,
11+
measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID,
12+
});
13+
14+
export const auth = app.auth();
15+
export default app;

backend/middleware/authMiddleware.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import jwt from "jsonwebtoken";
2+
import asyncHandler from "express-async-handler";
3+
import User from "../models/userModel.js";
4+
5+
// use for all user pages
6+
const protect = asyncHandler(async (req, res, next) => {
7+
let token;
8+
9+
if (
10+
req.headers.authorization &&
11+
req.headers.authorization.startsWith("Bearer")
12+
) {
13+
try {
14+
token = req.headers.authorization.split(" ")[1];
15+
16+
const decoded = jwt.verify(token, process.env.JWT_SECRET);
17+
18+
req.user = await User.findById(decoded.id).select("-password");
19+
20+
next();
21+
} catch (error) {
22+
console.error(error);
23+
res.status(401);
24+
throw new Error("Not authorized, token failed.");
25+
}
26+
}
27+
28+
if (!token) {
29+
res.status(401);
30+
throw new Error("Not authorized, no token.");
31+
}
32+
});
33+
34+
// use for all admin pages
35+
const admin = (req, res, next) => {
36+
if (req.user && req.user.isAdmin) {
37+
next();
38+
} else {
39+
res.status(401);
40+
throw new Error("Not authorized as an admin.");
41+
}
42+
};
43+
44+
export { protect, admin };

backend/middleware/errorMiddleware.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const notFound = (req, res, next) => {
2+
const error = new Error(`Not Found - ${req.originalUrl}`);
3+
next(error);
4+
};
5+
6+
const errorHandler = (err, req, res, next) => {
7+
const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
8+
res.status(statusCode);
9+
res.json({
10+
message: err.message,
11+
stack: process.env.NODE_ENV === "production" ? null : err.stack,
12+
});
13+
};
14+
15+
export { notFound, errorHandler };

backend/models/userModel.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import mongoose from "mongoose";
2+
3+
const userSchema = mongoose.Schema(
4+
{
5+
firstName: { type: String, required: true },
6+
lastName: { type: String, required: true },
7+
email: { type: String, required: true, unique: true },
8+
isAdmin: { type: Boolean, required: true, default: false },
9+
},
10+
{
11+
timestamps: true,
12+
}
13+
);
14+
15+
const User = mongoose.model("User", userSchema);
16+
export default User;

backend/routes/userRoutes.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import express from "express";
2+
import {
3+
authUser,
4+
registerUser,
5+
getUsers,
6+
deleteUser,
7+
sendEmailVerification,
8+
sendForgetPasswordEmail,
9+
} from "../controllers/userController.js";
10+
import { protect, admin } from "../middleware/authMiddleware.js";
11+
12+
const router = express.Router();
13+
router.route("/").post(registerUser).get(protect, admin, getUsers);
14+
router.post("/login", authUser);
15+
router.post("/login/email-verification", sendEmailVerification);
16+
router.post("/login/forget-password", sendForgetPasswordEmail);
17+
18+
router.route("/:id").delete(protect, admin, deleteUser);
19+
20+
export default router;

backend/seeder.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import mongoose from "mongoose";
2+
import dotenv from "dotenv";
3+
import connectDB from "./config/db.js";
4+
import users from "./data/users.js";
5+
import User from "./models/userModel.js";
6+
import colors from "colors";
7+
8+
dotenv.config();
9+
10+
connectDB();
11+
12+
const importData = async () => {
13+
try {
14+
await User.deleteMany();
15+
await User.insertMany(users);
16+
17+
console.log("Data Imported!".green.inverse);
18+
process.exit();
19+
} catch (error) {
20+
console.log(`${error}`.red.inverse);
21+
process.exit(1);
22+
}
23+
};
24+
25+
const destroyData = async () => {
26+
try {
27+
await User.deleteMany();
28+
29+
console.log("Data Destroyed!".red.inverse);
30+
process.exit();
31+
} catch (error) {
32+
console.log(`${error}`.red.inverse);
33+
process.exit(1);
34+
}
35+
};
36+
37+
if (process.argv[2] === "-d") {
38+
destroyData();
39+
} else {
40+
importData();
41+
}

backend/server.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import express from "express";
2+
import dotenv from "dotenv";
3+
import connectDB from "./config/db.js";
4+
import { notFound, errorHandler } from "./middleware/errorMiddleware.js";
5+
import colors from "colors";
6+
import userRoutes from "./routes/userRoutes.js";
7+
8+
/////////////// setup ///////////////
9+
10+
//env variables
11+
dotenv.config();
12+
//mongo db connect
13+
connectDB();
14+
/////////////// setup ///////////////
15+
16+
/////////////// api ///////////////
17+
const app = express();
18+
//lets us accept json in body
19+
app.use(express.json());
20+
21+
// return something for main API route - for dev http://localhost:5000/
22+
app.get("/", (req, res) => {
23+
res.send(`API running...`);
24+
});
25+
//routes
26+
app.use("/api/users", userRoutes);
27+
28+
//400
29+
app.use(notFound);
30+
// 500
31+
app.use(errorHandler);
32+
33+
const PORT = process.env.PORT || 5000;
34+
35+
app.listen(
36+
PORT,
37+
console.log(
38+
`Server running in ${process.env.NODE_ENV} mode on port ${PORT}`.yellow.bold
39+
)
40+
);
41+
/////////////// api ///////////////

0 commit comments

Comments
 (0)