Skip to content

Commit d3d6ece

Browse files
committed
Twitter clone solution code
1 parent 2ac838b commit d3d6ece

Some content is hidden

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

44 files changed

+15968
-0
lines changed

Fullstack/twitter-clone/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules/

Fullstack/twitter-clone/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Fullstack JavaScript Boilerplate
2+
3+
## Run Locally:
4+
5+
* `yarn install`
6+
* `yarn start`
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "api",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"start": "nodemon ./src/index.js --watch ./src",
8+
"test": "nyc mocha ./test/**/*.test.js --require=./test/test-helper.js --timeout=20000"
9+
},
10+
"keywords": [],
11+
"author": "",
12+
"license": "ISC",
13+
"dependencies": {
14+
"bcrypt": "^3.0.6",
15+
"cors": "^2.8.5",
16+
"express": "^4.17.1",
17+
"express-async-router": "^0.1.15",
18+
"express-validator": "^6.2.0",
19+
"jsonwebtoken": "^8.5.1",
20+
"mongoose": "^5.7.3",
21+
"morgan": "^1.9.1"
22+
},
23+
"devDependencies": {
24+
"chai": "^4.2.0",
25+
"chai-http": "^4.3.0",
26+
"mocha": "^6.2.1",
27+
"nodemon": "^1.19.3",
28+
"nyc": "^14.1.1"
29+
}
30+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const { AsyncRouter } = require("express-async-router");
2+
const jwt = require("jsonwebtoken");
3+
const { check, validationResult } = require("express-validator");
4+
5+
const User = require("../models/User");
6+
const handleValidationErrors = require("../helpers/handleValidationErrors");
7+
const jwtMiddleware = require("../helpers/jwtMiddleware");
8+
9+
const router = AsyncRouter();
10+
11+
const signUpValidators = [
12+
check("email").isEmail(),
13+
check("password").exists(),
14+
check("passwordConfirm").exists()
15+
];
16+
17+
const loginValidators = [
18+
check("email").isEmail(),
19+
check("password").exists()
20+
];
21+
22+
router.post(
23+
"/sign-up",
24+
[...signUpValidators, handleValidationErrors],
25+
async (req, res) => {
26+
const userExists = await User.findOne({email: req.body.email});
27+
28+
if(userExists)
29+
return res.status(400).send("E-mail already exists");
30+
if(req.body.password !== req.body.passwordConfirm)
31+
return res.status(400).send("Passwords do not match");
32+
33+
const user = await User.signUp(req.body.email, req.body.password);
34+
res.status(201).send(user.sanitize());
35+
}
36+
);
37+
38+
router.post(
39+
"/login",
40+
[...loginValidators, handleValidationErrors],
41+
async (req, res) => {
42+
const user = await User.findOne({email: req.body.email});
43+
44+
if(!user || !user.comparePassword(req.body.password))
45+
return res.status(400).send("Invalid login information");
46+
47+
const token = jwt.sign({
48+
_id: user._id,
49+
}, "CHANGEME!");
50+
51+
res.send({token});
52+
}
53+
);
54+
55+
router.get("/profile", [jwtMiddleware], async (req, res) => {
56+
const user = await User.findOne({_id: req.user._id}).populate("chrips");
57+
58+
res.send(user);
59+
});
60+
61+
module.exports = router;
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
const { AsyncRouter } = require("express-async-router");
2+
const { check, validationResult } = require("express-validator");
3+
4+
const Chirp = require("../models/Chirp");
5+
const handleValidationErrors = require("../helpers/handleValidationErrors");
6+
const jwtMiddleware = require("../helpers/jwtMiddleware");
7+
8+
const router = AsyncRouter();
9+
10+
const createValidators = [
11+
check("body").isLength({min: 3, max: 156}),
12+
];
13+
14+
const updateValidators = [
15+
check("body").isLength({min: 3, max: 156}),
16+
];
17+
18+
// List chirps
19+
router.get("/", async (req, res) => {
20+
const chirps = await Chirp.find({})
21+
.populate({
22+
path: "replies.user user"
23+
});
24+
25+
res.send(chirps);
26+
});
27+
28+
// Create a chirp
29+
router.post(
30+
"/", [
31+
...createValidators,
32+
jwtMiddleware,
33+
handleValidationErrors
34+
],
35+
async (req, res) => {
36+
const chirp = new Chirp();
37+
chirp.body = req.body.body;
38+
chirp.user = req.user;
39+
40+
await chirp.save();
41+
42+
res.status(201).send(chirp);
43+
}
44+
)
45+
46+
// Reply to a chirp
47+
router.post(
48+
"/reply/:_id", [
49+
...createValidators,
50+
jwtMiddleware,
51+
handleValidationErrors
52+
],
53+
async (req, res) => {
54+
const chirp = await Chirp.findById(req.params._id);
55+
56+
const reply = {
57+
body: req.body.body,
58+
user: req.user._id,
59+
postedAt: new Date(),
60+
}
61+
62+
chirp.replies.push(reply);
63+
await chirp.save();
64+
65+
res.status(201).send(await chirp.populate({path: "replies.user user"}).execPopulate());
66+
}
67+
)
68+
69+
// Delete a chirp
70+
router.delete(
71+
"/:_id", [
72+
...createValidators,
73+
jwtMiddleware,
74+
handleValidationErrors
75+
],
76+
async (req, res) => {
77+
const chirp = await Chirp.findById(req.params._id);
78+
79+
await chirp.remove();
80+
81+
res.send(chirp);
82+
}
83+
)
84+
85+
module.exports = router;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
const { AsyncRouter } = require("express-async-router");
2+
const { check, validationResult } = require("express-validator");
3+
4+
const User = require("../models/User");
5+
const Chirp = require("../models/Chirp");
6+
7+
const handleValidationErrors = require("../helpers/handleValidationErrors");
8+
const jwtMiddleware = require("../helpers/jwtMiddleware");
9+
10+
const router = AsyncRouter();
11+
12+
const followValidators = [
13+
check("user").exists(),
14+
]
15+
16+
// Follow a user
17+
router.post(
18+
"/follow/:_id",
19+
[...followValidators, jwtMiddleware, handleValidationErrors],
20+
async (req, res) => {
21+
const followUser = await User.findById(req.params._id);
22+
23+
if(!followUser) return res.sendStatus(404);
24+
25+
// Add followers to followingUser
26+
followUser.followers.push(req.user._id);
27+
await followUser.save();
28+
29+
// Add following to current user
30+
req.user.following.push(followUser._id);
31+
await req.user.save();
32+
33+
res.send(req.user);
34+
}
35+
);
36+
37+
// Get chirps for a user
38+
router.get("/profile/:userId", async (req, res) => {
39+
const profile = await User.findById(req.params.userId).populate({
40+
path: "chirps followers following",
41+
})
42+
43+
res.send(profile);
44+
});
45+
46+
module.exports = router;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const { validationResult } = require("express-validator");
2+
3+
const handleValidationErrors = (req, res, next) => {
4+
const errors = validationResult(req);
5+
if (!errors.isEmpty()) {
6+
return res.status(422).json({ errors: errors.array() });
7+
}
8+
9+
next();
10+
}
11+
12+
module.exports = handleValidationErrors;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const jwt = require("jsonwebtoken");
2+
const User = require("../models/User");
3+
4+
const jwtMiddleware = async (req, res, next) => {
5+
const authorization = req.header("Authorization") || "";
6+
const [type, token] = authorization.split(" ");
7+
8+
try {
9+
if(type === "Bearer" && jwt.verify(token, "CHANGEME!")) {
10+
const payload = jwt.decode(token, "CHANGEME!");
11+
const user = await User.findOne({_id: payload._id});
12+
req.user = user;
13+
next();
14+
} else {
15+
res.status(401).send("Unauthorized");
16+
}
17+
} catch(err) {
18+
res.status(401).send("Unauthorized");
19+
}
20+
}
21+
22+
module.exports = jwtMiddleware;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const { startServer } = require("./server");
2+
3+
startServer();
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const mongoose = require("mongoose");
2+
3+
const { Schema } = mongoose;
4+
const { ObjectId } = Schema.Types;
5+
6+
const chirpFields = {
7+
body: {
8+
type: String,
9+
required: true,
10+
},
11+
user: {
12+
type: ObjectId,
13+
ref: "User",
14+
required: true,
15+
},
16+
}
17+
18+
const chirpSchema = Schema({
19+
...chirpFields,
20+
replies: [{ ...chirpFields }]
21+
}, {
22+
timestamps: true,
23+
toJSON: {
24+
virtuals: true,
25+
},
26+
toObject: {
27+
virtuals: true,
28+
}
29+
});
30+
31+
const Chirp = mongoose.model("Chirp", chirpSchema);
32+
33+
module.exports = Chirp;

0 commit comments

Comments
 (0)