Skip to content

Commit 86a4825

Browse files
authored
Add files via upload
1 parent 2eefaf9 commit 86a4825

File tree

12 files changed

+354
-0
lines changed

12 files changed

+354
-0
lines changed

server/nodemon.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"watch": ["src"],
3+
"ext": ".ts,.js",
4+
"exec": "ts-node ./src/index.ts"
5+
}

server/package.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "server",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"start": "nodemon",
8+
"test": "echo \"Error: no test specified\" && exit 1"
9+
},
10+
"keywords": [],
11+
"author": "",
12+
"license": "ISC",
13+
"devDependencies": {
14+
"@types/body-parser": "^1.19.2",
15+
"@types/compression": "^1.7.2",
16+
"@types/cookie-parser": "^1.4.3",
17+
"@types/cors": "^2.8.13",
18+
"@types/express": "^4.17.17",
19+
"@types/lodash": "^4.14.194",
20+
"@types/mongoose": "^5.11.97",
21+
"nodemon": "^2.0.22",
22+
"ts-node": "^10.9.1",
23+
"typescript": "^5.0.4"
24+
},
25+
"dependencies": {
26+
"body-parser": "^1.20.2",
27+
"compression": "^1.7.4",
28+
"cookie-parser": "^1.4.6",
29+
"cors": "^2.8.5",
30+
"crypto": "^1.0.1",
31+
"dotenv": "^16.0.3",
32+
"express": "^4.18.2",
33+
"lodash": "^4.17.21",
34+
"mongoose": "^7.0.3"
35+
}
36+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import express from "express";
2+
import * as dotenv from "dotenv";
3+
4+
import { createUser, getUserByEmail } from "../db/users";
5+
import { random, authentication } from "../helpers";
6+
7+
dotenv.config();
8+
9+
export const login = async (req: express.Request, res: express.Response) => {
10+
try {
11+
const { email, password } = req.body;
12+
13+
if (!email || !password) {
14+
return res.sendStatus(400);
15+
}
16+
17+
const user = await getUserByEmail(email).select(
18+
"+authentication.password +authentication.salt"
19+
);
20+
21+
if (!user) {
22+
return res.sendStatus(400);
23+
}
24+
25+
const expectedHash = authentication(user.authentication.salt, password);
26+
if (user.authentication.password !== expectedHash) {
27+
return res.sendStatus(403);
28+
}
29+
30+
const salt = random();
31+
user.authentication.sessionToken = authentication(
32+
salt,
33+
user._id.toString()
34+
);
35+
36+
await user.save();
37+
38+
res.cookie(
39+
process.env.SESSION_TOKEN_NAME as string,
40+
user.authentication.sessionToken,
41+
{
42+
domain: "localhost",
43+
path: "/",
44+
}
45+
);
46+
47+
return res.status(200).json(user).end();
48+
} catch (error) {
49+
console.log(error);
50+
return res.sendStatus(400);
51+
}
52+
};
53+
54+
export const register = async (req: express.Request, res: express.Response) => {
55+
try {
56+
const { email, password, username } = req.body;
57+
58+
if (!email || !password || !username) {
59+
return res.sendStatus(400);
60+
}
61+
62+
const existingUser = await getUserByEmail(email);
63+
64+
if (existingUser) {
65+
return res.sendStatus(400);
66+
}
67+
68+
const salt = random();
69+
const user = await createUser({
70+
email,
71+
username,
72+
authentication: { salt, password: authentication(salt, password) },
73+
});
74+
75+
return res.status(200).json(user).end();
76+
} catch (error) {
77+
console.log(error);
78+
return res.sendStatus(400);
79+
}
80+
};

server/src/controllers/users.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import express from "express";
2+
3+
import { getUsers, deleteUserById, getUserById } from "../db/users";
4+
5+
export const getAllUsers = async (
6+
req: express.Request,
7+
res: express.Response
8+
) => {
9+
try {
10+
const users = await getUsers();
11+
12+
return res.status(200).json(users);
13+
} catch (error) {
14+
console.log(error);
15+
return res.sendStatus(400);
16+
}
17+
};
18+
19+
export const deleteUser = async (
20+
req: express.Request,
21+
res: express.Response
22+
) => {
23+
try {
24+
const { id } = req.params;
25+
26+
const deletedUser = await deleteUserById(id);
27+
28+
return res.json(deletedUser);
29+
} catch (error) {
30+
console.log(error);
31+
return res.sendStatus(400);
32+
}
33+
};
34+
35+
export const updateUser = async (
36+
req: express.Request,
37+
res: express.Response
38+
) => {
39+
try {
40+
const { id } = req.params;
41+
const { username } = req.body;
42+
43+
if (!username) {
44+
return res.sendStatus(400);
45+
}
46+
47+
const user = await getUserById(id);
48+
49+
user.username = username;
50+
await user.save();
51+
52+
return res.status(200).json(user).end();
53+
} catch (error) {
54+
console.log(error);
55+
return res.sendStatus(400);
56+
}
57+
};

server/src/db/users.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import mongoose from "mongoose";
2+
3+
const UserSchema = new mongoose.Schema({
4+
username: { type: String, required: true },
5+
email: { type: String, required: true },
6+
authentication: {
7+
password: { type: String, required: true, select: false },
8+
salt: { type: String, select: false },
9+
sessionToken: { type: String, select: false },
10+
},
11+
});
12+
13+
export const UserModel = mongoose.model("User", UserSchema);
14+
15+
export const getUsers = () => UserModel.find();
16+
export const getUserByEmail = (email: string) => UserModel.findOne({ email });
17+
export const getUserBySessionToken = (sessionToken: string) =>
18+
UserModel.findOne({
19+
"authentication.sessionToken": sessionToken,
20+
});
21+
export const getUserById = (id: string) => UserModel.findById(id);
22+
export const createUser = (values: Record<string, any>) =>
23+
new UserModel(values).save().then((user) => user.toObject());
24+
export const deleteUserById = (id: string) =>
25+
UserModel.findByIdAndDelete({ _id: id });
26+
export const updateUserById = (id: string, values: Record<string, any>) =>
27+
UserModel.findByIdAndUpdate(id, values);

server/src/helpers/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import crypto from "crypto";
2+
import * as dotenv from "dotenv";
3+
4+
dotenv.config();
5+
6+
export const random = () => crypto.randomBytes(128).toString("base64");
7+
export const authentication = (salt: string, password: string) => {
8+
return crypto
9+
.createHmac("sha256", [salt, password].join("/"))
10+
.update(process.env.SECRET as string)
11+
.digest("hex");
12+
};

server/src/index.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import express from "express";
2+
import http from "http";
3+
import bodyParser from "body-parser";
4+
import cookieParser from "cookie-parser";
5+
import compression from "compression";
6+
import cors from "cors";
7+
import * as dotenv from "dotenv";
8+
import mongoose from "mongoose";
9+
10+
import router from "./router";
11+
12+
dotenv.config();
13+
14+
const app = express();
15+
const PORT = process.env.PORT || 8080;
16+
17+
app.use(
18+
cors({
19+
credentials: true,
20+
})
21+
);
22+
23+
app.use(compression());
24+
app.use(cookieParser());
25+
app.use(bodyParser.json());
26+
27+
const server = http.createServer(app);
28+
29+
server.listen(PORT, () => {
30+
console.log(`Server is running on port http://localhost:${PORT}/`);
31+
});
32+
33+
mongoose.Promise = Promise;
34+
mongoose.connect(process.env.MONGODB_URL);
35+
mongoose.connection.on("error", (error: Error) => console.log(error));
36+
37+
app.use("/", router());

server/src/middlewares/index.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import express from "express";
2+
import * as dotenv from "dotenv";
3+
import { get, merge } from "lodash";
4+
5+
import { getUserBySessionToken } from "../db/users";
6+
7+
dotenv.config();
8+
9+
export const isOwner = async (
10+
req: express.Request,
11+
res: express.Response,
12+
next: express.NextFunction
13+
) => {
14+
try {
15+
const { id } = req.params;
16+
const currentUserId = get(req, "identity._id") as string;
17+
18+
if (!currentUserId) {
19+
return res.sendStatus(403);
20+
}
21+
22+
if (currentUserId.toString() !== id) {
23+
return res.sendStatus(403);
24+
}
25+
26+
next();
27+
} catch (error) {
28+
console.log(error);
29+
return res.sendStatus(400);
30+
}
31+
};
32+
33+
export const isAuthenticated = async (
34+
req: express.Request,
35+
res: express.Response,
36+
next: express.NextFunction
37+
) => {
38+
try {
39+
const sessionToken = req.cookies[process.env.SESSION_TOKEN_NAME as string];
40+
41+
if (!sessionToken) {
42+
return res.sendStatus(403);
43+
}
44+
45+
const existingUser = await getUserBySessionToken(sessionToken);
46+
47+
if (!existingUser) {
48+
return res.sendStatus(403);
49+
}
50+
51+
merge(req, { identity: existingUser });
52+
53+
return next();
54+
} catch (error) {
55+
console.log(error);
56+
return res.sendStatus(400);
57+
}
58+
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import express from "express";
2+
3+
import { register, login } from "../controllers/authentication";
4+
5+
export default (router: express.Router) => {
6+
router.post("/auth/register", register);
7+
router.post("/auth/login", login);
8+
};

server/src/router/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import express from "express";
2+
3+
import authentication from "./authentication";
4+
import users from "./users";
5+
6+
const router = express.Router();
7+
8+
export default (): express.Router => {
9+
authentication(router);
10+
users(router);
11+
12+
return router;
13+
};

0 commit comments

Comments
 (0)