-
Notifications
You must be signed in to change notification settings - Fork 31
API-backend-BiankaR #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
4ffef48
878b1a8
6f36df9
c219fef
4cbcffb
db2663f
220fe2e
ef7a9f5
83d0416
39e775d
e9ec654
4707209
dd28ad0
95075fd
caeb78f
fcf4ff3
ff9bd4e
841e7e3
c476392
78e1229
97a6ed8
dc500ed
e76e511
3207134
a7d145e
a0b97df
502c2d9
58780b6
b55db29
5629d8b
2c907f2
7977400
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,11 @@ | ||
| node_modules | ||
| .DS_Store | ||
| .env | ||
| .env* | ||
| .env.dev | ||
| .env.prod | ||
| .env.local | ||
| .env.development.local | ||
| .env.test.local | ||
| .env.production.local | ||
| package-lock.json | ||
| package-lock.json | ||
| todo.md |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| [ | ||
| { | ||
| "_id": "682bab8c12155b00101732ce", | ||
| "message": "Berlin baby", | ||
| "hearts": 37, | ||
| "createdAt": "2025-05-19T22:07:08.999Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682e53cc4fddf50010bbe739", | ||
| "message": "My family!", | ||
| "hearts": 0, | ||
| "createdAt": "2025-05-22T22:29:32.232Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682e4f844fddf50010bbe738", | ||
| "message": "The smell of coffee in the morning....", | ||
| "hearts": 23, | ||
| "createdAt": "2025-05-22T22:11:16.075Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682e48bf4fddf50010bbe737", | ||
| "message": "Newly washed bedlinen, kids that sleeps through the night.. FINGERS CROSSED 🤞🏼\n", | ||
| "hearts": 6, | ||
| "createdAt": "2025-05-21T21:42:23.862Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682e45804fddf50010bbe736", | ||
| "message": "I am happy that I feel healthy and have energy again", | ||
| "hearts": 13, | ||
| "createdAt": "2025-05-21T21:28:32.196Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682e23fecf615800105107aa", | ||
| "message": "cold beer", | ||
| "hearts": 2, | ||
| "createdAt": "2025-05-21T19:05:34.113Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682e22aecf615800105107a9", | ||
| "message": "My friend is visiting this weekend! <3", | ||
| "hearts": 6, | ||
| "createdAt": "2025-05-21T18:59:58.121Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682cec1b17487d0010a298b6", | ||
| "message": "A god joke: \nWhy did the scarecrow win an award?\nBecause he was outstanding in his field!", | ||
| "hearts": 12, | ||
| "createdAt": "2025-05-20T20:54:51.082Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682cebbe17487d0010a298b5", | ||
| "message": "Tacos and tequila🌮🍹", | ||
| "hearts": 2, | ||
| "createdAt": "2025-05-19T20:53:18.899Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682ceb5617487d0010a298b4", | ||
| "message": "Netflix and late night ice-cream🍦", | ||
| "hearts": 1, | ||
| "createdAt": "2025-05-18T20:51:34.494Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682c99ba3bff2d0010f5d44e", | ||
| "message": "Summer is coming...", | ||
| "hearts": 2, | ||
| "createdAt": "2025-05-20T15:03:22.379Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682c706c951f7a0017130024", | ||
| "message": "Exercise? I thought you said extra fries! 🍟😂", | ||
| "hearts": 14, | ||
| "createdAt": "2025-05-20T12:07:08.185Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682c6fe1951f7a0017130023", | ||
| "message": "I’m on a seafood diet. I see food, and I eat it.", | ||
| "hearts": 4, | ||
| "createdAt": "2025-05-20T12:04:49.978Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682c6f0e951f7a0017130022", | ||
| "message": "Cute monkeys🐒", | ||
| "hearts": 2, | ||
| "createdAt": "2025-05-20T12:01:18.308Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682c6e65951f7a0017130021", | ||
| "message": "The weather is nice!", | ||
| "hearts": 0, | ||
| "createdAt": "2025-05-20T11:58:29.662Z", | ||
| "__v": 0 | ||
| }, | ||
| { | ||
| "_id": "682bfdb4270ca300105af221", | ||
| "message": "good vibes and good things", | ||
| "hearts": 3, | ||
| "createdAt": "2025-05-20T03:57:40.322Z", | ||
| "__v": 0 | ||
| } | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import mongoose from "mongoose"; | ||
|
|
||
| const thoughtSchema = new mongoose.Schema({ | ||
| message: { | ||
| required: true, | ||
| type: String, | ||
| minlength: 5, | ||
| maxlength: 140}, | ||
| hearts: { | ||
| type: Number, | ||
| default: 0}, | ||
| createdAt: { | ||
| type: Date, | ||
| default: Date.now | ||
| }, | ||
| createdBy: { | ||
| type: mongoose.Schema.Types.ObjectId, | ||
| ref: "User", | ||
| default: null, | ||
| } | ||
| }) | ||
|
|
||
| export const Thought = mongoose.model("Thought", thoughtSchema) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import crypto from "crypto" | ||
| import mongoose from "mongoose" | ||
|
|
||
| const userSchema = new mongoose.Schema({ | ||
| username: { | ||
| type: String, | ||
| unique: true, | ||
| required: true, | ||
| minlength: 2, | ||
| trim: true, | ||
| }, | ||
| email: { | ||
| type: String, | ||
| unique: true, | ||
| sparse: true, | ||
| lowercase: true, | ||
| trim: true, | ||
| validate: { | ||
| validator: (email) => { | ||
| return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) | ||
| }, | ||
|
Comment on lines
+18
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⭐ |
||
| message: props => `${props.value} is not a valid email address!` | ||
| } | ||
|
Comment on lines
+12
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice use of a custom message and validator for the email field! |
||
| }, | ||
| password: { | ||
| type: String, | ||
| required: true, | ||
| minlength: 4, | ||
| }, | ||
| accessToken: { | ||
| type: String, | ||
| default: () => crypto.randomBytes(128).toString("hex"), | ||
| } | ||
| }) | ||
|
|
||
| export const User = mongoose.model("User", userSchema) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,11 +9,17 @@ | |
| "author": "", | ||
| "license": "ISC", | ||
| "dependencies": { | ||
| "@babel/core": "^7.17.9", | ||
| "@babel/node": "^7.16.8", | ||
| "@babel/preset-env": "^7.16.11", | ||
| "@babel/core": "^7.27.3", | ||
| "@babel/node": "^7.27.1", | ||
| "@babel/preset-env": "^7.27.2", | ||
| "bcrypt-nodejs": "^0.0.3", | ||
| "bcryptjs": "^3.0.2", | ||
|
Comment on lines
+15
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like you're only using bcryptjs — maybe remove bcrypt-nodejs to keep things clean? |
||
| "cors": "^2.8.5", | ||
| "express": "^4.17.3", | ||
| "nodemon": "^3.0.1" | ||
| "dotenv": "^16.5.0", | ||
| "express": "^4.21.2", | ||
| "express-list-endpoints": "^7.1.1", | ||
| "mongodb": "^6.17.0", | ||
| "mongoose": "^8.15.1", | ||
| "nodemon": "^3.1.10" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,3 @@ | ||
| Please include your Render link here. | ||
| Please include your Render link here: | ||
|
|
||
| https://js-project-api-b8sd.onrender.com |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| import bcrypt from "bcryptjs" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great structure and very clear authentication! |
||
| import { Router } from "express" | ||
|
|
||
| import { User } from "../models/User" | ||
|
|
||
| const authRouter = Router() | ||
|
|
||
| // REGISTRATION ENDPOINT - ASSIGN ENCRYPTED TOKEN (CREATE) | ||
| authRouter.post("/register", async (req, res) => { | ||
| try { | ||
| const { username, email, password } = req.body | ||
| if (!username || !password) { | ||
| return res.status(400).json({ error: "username and password are required"}) | ||
| } | ||
|
|
||
| const salt = bcrypt.genSaltSync() | ||
| const user = new User({ | ||
| username, | ||
| email: email || null , | ||
| password: bcrypt.hashSync(password, salt) }) | ||
|
|
||
| await user.save() | ||
|
|
||
| res.status(200).json({ | ||
| message: "Signup success", | ||
| success: true, | ||
| userid: user._id, | ||
| username: user.username, | ||
| accessToken: user.accessToken}) | ||
|
|
||
| } catch(err) { | ||
| if (err.code === 11000) { | ||
| const field = Object.keys(err.keyPattern)[0] | ||
| return res.status(409).json({ | ||
| success: false, | ||
| message: `A user with that ${field} already exists.`, | ||
| }) | ||
| } | ||
| if (err.errors) { | ||
| const validationErrors = Object.values(err.errors).map(e => e.message) | ||
| return res.status(400).json({ | ||
| success: false, | ||
| message: "Invalid input", | ||
| errors: validationErrors | ||
| }) | ||
| } | ||
| console.error(err) | ||
| res.status(500).json({ | ||
| success: false, | ||
| message: "Unexpected server error." | ||
| }) | ||
| } | ||
| }) | ||
|
|
||
| // LOGIN ENDPOINT (FINDS USER) | ||
| authRouter.post("/login", async (req, res) => { | ||
| try { | ||
| const { username, password } = req.body | ||
|
|
||
| const user = await User.findOne({ username: req.body.username }) | ||
| if (!user) { | ||
| return res.status(404).json({ | ||
| success: false, | ||
| message: "User does not exist"}) | ||
| } | ||
|
|
||
| if (user && bcrypt.compareSync(req.body.password, user.password)) { | ||
| res.status(201).json({ | ||
| success: true, | ||
| message: "User successfully logged in", | ||
| userId: user._id, | ||
| username: user.username, | ||
| accessToken: user.accessToken | ||
| }) | ||
| } else { | ||
| res.status(401).json({ | ||
| success: false, | ||
| message: "Invalid password" | ||
| }) | ||
|
|
||
| }} catch(err) { | ||
| res.status(400).json({ | ||
| success: false, | ||
| notFound: true}) | ||
| } | ||
| }) | ||
|
|
||
| // MIDDLEWARE TO AUTH | ||
| export const authenticateUser = async (req, res, next) => { | ||
| try { | ||
|
|
||
| const token = req.header("Authorization") | ||
| if (!token) { | ||
| return res.status(401).json({ | ||
| success: false, | ||
| message: "Access token missing. Please login to continue." | ||
| }) | ||
| } | ||
|
|
||
| const user = await User.findOne({ accessToken: token }) | ||
| if(!user) { | ||
| return res.status(401).json({ | ||
| success: false, | ||
| message: "Invalid token. Please login in again.", | ||
| loggedOut: true | ||
| }) | ||
| } | ||
| req.user = user | ||
| next() | ||
| } catch (err) { | ||
| console.error("Authentication error", err) | ||
| res.status(500).json({ | ||
| success: false, | ||
| message: "Server error during authentication." | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| export default authRouter | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the db is seeded you dont really need to keep this json. It's okay if you want to keep it but not really necessary