generated from Technigo/express-api-starter
-
Notifications
You must be signed in to change notification settings - Fork 52
API-backend-project #48
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
Open
julialindstrand
wants to merge
34
commits into
Technigo:master
Choose a base branch
from
julialindstrand:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
32ed946
Adds a filter for ID
julialindstrand a926445
Adds filter of thoughts with 0 likes
julialindstrand 48b2bcb
Troubleshooting
julialindstrand 1613e9b
More troubleshooting
julialindstrand 55a9c44
Adds users
julialindstrand 04d5efb
Error when deploying
julialindstrand dcd0fcc
Comments out errors
julialindstrand ccc290d
Adds bcrypt
julialindstrand dbe9041
Installes mongoose save
julialindstrand 3f0b6c0
Add mongoose dependency
julialindstrand ac8f163
Troubleshooting again
julialindstrand 66444aa
More troubleshooting
julialindstrand d4b1da0
Tried fixing with the AI of render
julialindstrand 03bcb12
Adds mongoose in user route
julialindstrand 845efa4
Adds .env
julialindstrand 895ad54
Adds dotenv
julialindstrand 9a9be37
Error message: cant find open port
julialindstrand d8c2a88
Removes consolelog
julialindstrand fe61e99
Adds .js
julialindstrand 1113758
Adds type field in json
julialindstrand d474dc5
Tries to move a curlybracket
julialindstrand 3ab1cb9
Removed an experimental const
julialindstrand 55bda57
Removes reset
julialindstrand ee39005
Closes the if-statment earlier
julialindstrand 84a90d6
Removes one /thougths
julialindstrand b415baf
Adds accesstoken
julialindstrand 9b1389d
Adds .js
julialindstrand dc0ff80
Changes the like endpoint
julialindstrand d8bb25a
Adds likes
julialindstrand dff0038
Installed cors
julialindstrand fd8e495
Adds corsfix
julialindstrand 688a4cb
Removes cors
julialindstrand 2d24f5d
Adds allow all corse
julialindstrand 1c58c75
Fixed a few of the final problems
julialindstrand File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,248 @@ | ||
| import express from "express" | ||
| import mongoose from "mongoose" | ||
| import { User } from "./userRoutes.js" | ||
|
|
||
| const router = express.Router(); | ||
|
|
||
| const authenticateUser = async (req, res, next) => { | ||
| try { | ||
| const user = await User.findOne({ | ||
| accessToken: req.header('Authorization').replace("Bearer ", ""), | ||
| }) | ||
| if (user) { | ||
| req.user = user | ||
| next() | ||
| } else { | ||
| res.status(401).json({ | ||
| message: "Authentication missing / invalid", | ||
| loggedOut: true | ||
| }) | ||
| } | ||
| } catch (error) { | ||
| res.status(500).json({ message: "Internal server error", error: error.message }) | ||
| } | ||
| } | ||
|
|
||
| // Thought schema | ||
| const thoughtSchema = new mongoose.Schema({ | ||
| message: { type: String, required: true }, | ||
| hearts: { type: Number, default: 0 }, | ||
| createdAt: { type: Date, default: Date.now }, | ||
| userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true } | ||
| }) | ||
|
|
||
| const Thought = mongoose.model('Thought', thoughtSchema) | ||
|
|
||
|
|
||
| // Show all thoughts | ||
| router.get("/thoughts", async (req, res) => { | ||
| try { | ||
| const thoughts = await Thought.find().sort({ createdAt: "desc" }) | ||
| res.json(thoughts) | ||
|
|
||
| } catch (error) { | ||
| res.status(500).json({ error: "Failed to fetch thougts" }) | ||
| } | ||
| }) | ||
|
|
||
|
|
||
| // Show thoughts with likes | ||
| router.get("/thoughts/like", async (req, res) => { | ||
| const { hearts } = req.query | ||
|
|
||
| const dbQuery = {} | ||
| if (hearts !== undefined) { | ||
| const heartsNum = Number(hearts) | ||
| if (!Number.isNaN(heartsNum)) { | ||
| dbQuery.hearts = heartsNum | ||
| } | ||
| } | ||
|
|
||
| try { | ||
| const thoughts = await Thought.find(dbQuery) | ||
| if (thoughts.length === 0) { | ||
| return res.status(404).json({ | ||
| success: false, | ||
| response: [], | ||
| message: "No thoughts match the query" | ||
| }) | ||
| } | ||
|
|
||
| return res.status(200).json({ | ||
| success: true, | ||
| response: thoughts, | ||
| message: "Thoughts retrieved" | ||
| }) | ||
| } catch (error) { | ||
| console.error(error); | ||
| return res.status(500).json({ | ||
| success: false, | ||
| response: [], | ||
| message: error | ||
| }) | ||
| } | ||
| }) | ||
|
|
||
|
|
||
| // Filter by ID | ||
| router.get("/thoughts/:id", async (req, res) => { | ||
| const id = req.params.id | ||
|
|
||
| try { | ||
| if (!mongoose.Types.ObjectId.isValid(id)) { | ||
| return res.status(400).json({ | ||
| success: false, | ||
| response: null, | ||
| message: "Invalid ID format", | ||
|
|
||
| }) | ||
| } | ||
| const thought = await Thought.findById(id) | ||
|
|
||
| if (!thought) { | ||
| return res.status(404).json({ | ||
| success: false, | ||
| response: null, | ||
| message: "Thought not found", | ||
| }) | ||
| } | ||
|
|
||
| return res.status(200).json({ | ||
| success: true, | ||
| response: thought, | ||
| message: "Success", | ||
| }) | ||
| } | ||
|
|
||
| catch (error) { | ||
| return res.status(500).json({ | ||
| success: false, | ||
| response: null, | ||
| message: error, | ||
| }) | ||
| } | ||
| } | ||
| ) | ||
|
|
||
|
|
||
| // Post | ||
| router.post("/thoughts", authenticateUser, async (req, res) => { | ||
| const { message } = req.body | ||
|
|
||
| try { | ||
| const newThought = await new Thought({ | ||
| message, userId: req.user._id, | ||
| }).save() | ||
|
|
||
| if (!newThought) { | ||
| return res | ||
| .status(400) | ||
| .json({ success: false, data: null, message: "Failed to post thought" }) | ||
| } | ||
|
|
||
| res.status(201).json({ | ||
| success: true, | ||
| data: newThought, | ||
| message: "Thought created successfully." | ||
| }) | ||
|
|
||
| } catch (error) { | ||
| console.log(error) | ||
| res.status(500).json({ | ||
| success: false, | ||
| data: null, | ||
| message: error.message || "Server error" | ||
| }) | ||
| } | ||
| }) | ||
|
|
||
|
|
||
| // Edit | ||
| router.patch('/thoughts/:id', authenticateUser, async (req, res) => { | ||
| const { id } = req.params | ||
|
|
||
| try { | ||
| const thought = await Thought.findById(id); | ||
| if (!thought) { | ||
| return res.status(404).json({ error: "Thought not found" }); | ||
| } | ||
| if (thought.userId.toString() !== req.user._id.toString()) { | ||
| return res.status(403).json({ error: "You can only edit your own thoughts" }); | ||
| } | ||
|
|
||
| thought.message = req.body.message ?? thought.message; | ||
| thought.hearts = req.body.hearts ?? thought.hearts; | ||
| await thought.save(); | ||
|
|
||
| return res.json({ success: true, thought }); | ||
|
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. return HTTP status as well. |
||
| } catch (err) { | ||
| return res.status(400).json({ error: "Invalid request", details: err.message }); | ||
| } | ||
| }) | ||
|
|
||
|
|
||
| // Like | ||
| router.post("/thoughts/:id/like", authenticateUser, async (req, res) => { | ||
| const { id } = req.params | ||
|
|
||
| if (!mongoose.Types.ObjectId.isValid(id)) { | ||
| return res | ||
| .status(400) | ||
| .json({ success: false, message: "Invalid thought ID" }) | ||
| } | ||
|
|
||
| try { | ||
| const thought = await Thought.findById(id) | ||
| if (!thought) { | ||
| return res | ||
| .status(404) | ||
| .json({ success: false, message: "Thought not found" }) | ||
| } | ||
|
|
||
| thought.hearts += 1 | ||
| await thought.save() | ||
|
|
||
| return res | ||
| .status(200) | ||
| .json({ success: true, hearts: thought.hearts, message: "Liked!" }) | ||
| } catch (err) { | ||
| console.error(err) | ||
| return res | ||
| .status(500) | ||
| .json({ success: false, message: err.message }) | ||
| } | ||
| }) | ||
|
|
||
|
|
||
| // Delete | ||
| router.delete("/thoughts/:id", authenticateUser, async (req, res) => { | ||
| const id = req.params.id; | ||
| try { | ||
| const thought = await Thought.findById(id) | ||
|
|
||
| if (!thought) { | ||
| return res.status(404).json({ | ||
| success: false, | ||
| response: [], | ||
| message: "Thought not found" | ||
| }) | ||
| } | ||
|
|
||
| await Thought.findByIdAndDelete(id) | ||
|
|
||
| res.status(200).json({ | ||
| success: true, | ||
| response: id, | ||
| message: "Thought deleted successfully" | ||
| }) | ||
|
|
||
| } catch (error) { | ||
| res.status(500).json({ | ||
| success: false, | ||
| response: null, | ||
| message: error, | ||
| }) | ||
| } | ||
| }) | ||
|
|
||
| export default router | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| import express from "express" | ||
| import bcrypt from "bcrypt" | ||
| import crypto from "crypto" | ||
| import mongoose from "mongoose" | ||
|
|
||
| const router = express.Router() | ||
|
|
||
| // User schema | ||
| const UserSchema = new mongoose.Schema({ | ||
| email: { type: String, required: true, unique: true }, | ||
| password: { type: String, required: true }, | ||
| accessToken: { | ||
| type: String, | ||
| default: () => crypto.randomBytes(128).toString("hex"), | ||
|
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. In order to use crypto, you need to import crypto from crypto:) |
||
| }, | ||
| }) | ||
|
|
||
| export const User = mongoose.model('User', UserSchema) | ||
|
|
||
|
|
||
| // New User | ||
| router.post('/users/signup', async (req, res) => { | ||
| try { | ||
| const { email, password } = req.body | ||
|
|
||
| const existingUser = await User.findOne({ | ||
| email: email.toLowerCase() | ||
| }) | ||
|
|
||
| if (existingUser) { | ||
| return res.status(400).json({ | ||
| success: false, | ||
| message: "User with this email already exists" | ||
| }) | ||
| } | ||
|
|
||
| const salt = bcrypt.genSaltSync() | ||
| const hashedPassword = bcrypt.hashSync(password, salt) | ||
| const user = new User({ email, password: hashedPassword }) | ||
|
|
||
| await user.save() | ||
|
|
||
| res.status(200).json({ | ||
| success: true, | ||
| message: "User created successfully", | ||
| response: { | ||
| email: user.email, | ||
| id: user._id, | ||
| accessToken: user.accessToken, | ||
| }, | ||
| }) | ||
| } catch (error) { | ||
| res.status(400).json({ | ||
| success: false, | ||
| message: 'Could not create user', | ||
| response: error, | ||
| }) | ||
| } | ||
| }) | ||
|
|
||
| // Log In | ||
| router.post('/users/login', async (req, res) => { | ||
| try { | ||
| const { email, password } = req.body | ||
| const user = await User.findOne({ email: email.toLowerCase() }) | ||
|
|
||
| if (user && bcrypt.compareSync(password, user.password)) { | ||
| res.json({ | ||
| success: true, | ||
| message: "Logged in successfully", | ||
| response: { | ||
| email: user.email, | ||
| id: user._id, | ||
| accessToken: user.accessToken | ||
| }, | ||
| }) | ||
| } else { | ||
| res.status(401).json({ | ||
| success: false, | ||
| message: "Wrong e-mail or password", | ||
| response: null, | ||
| }) | ||
| } | ||
| } catch (error) { | ||
| res.status(500).json({ | ||
| success: false, | ||
| message: "Something went wrong", | ||
| response: error | ||
| }) | ||
| } | ||
| }) | ||
|
|
||
| export default router | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
For Auth and schema I recommend to create separate files, so that the code will shorter and easy to follow.