generated from Technigo/express-api-starter
-
Notifications
You must be signed in to change notification settings - Fork 30
RAT API / server to HAppy HToughts #23
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
HolaCarmensita
wants to merge
49
commits into
Technigo:master
Choose a base branch
from
HolaCarmensita: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
49 commits
Select commit
Hold shift + click to select a range
12dbd85
added functions to thoughtServices
HolaCarmensita 68f644e
All thoughtServices done
HolaCarmensita e48b4be
Testade mina thoughtServices med assert WOW va grymt
HolaCarmensita 03510ad
Ändrade package.json utan module och importear data på samma sätt som…
HolaCarmensita 2bd403b
routes for get all thoughts, post createThought, post likeTHought och…
HolaCarmensita eede8e2
added a service for getting one thought, added that one and update an…
HolaCarmensita 0c3d349
Streach goal regardning 404 single‐item–hantering
HolaCarmensita d575cca
paginate implemeted
HolaCarmensita 4a7566f
sorting works
HolaCarmensita f6c0287
added link to render.com sajten
HolaCarmensita f3c9678
added dotenv
HolaCarmensita 4e6e3b6
just started to add moongoose things but realised that I want to refa…
HolaCarmensita 2746036
refactored the code to have controllers
HolaCarmensita 7c46cb5
installed mongoose
HolaCarmensita 9d9ae8a
test
HolaCarmensita 435e0ed
installed mondodb and added info to README
HolaCarmensita e02ae73
refactored getAllThoughts, getOneThought, like and unlike
HolaCarmensita 8ec062f
UpdateThought works now with mongo method
HolaCarmensita f2cccb3
Delete thought done
HolaCarmensita dcf58ce
remove thought refactored
HolaCarmensita 36f24b9
deleted mockdata filen som jag använde tidigare
HolaCarmensita 2acfdd0
test
HolaCarmensita ec745e2
deleted utils and a unesssesary import
HolaCarmensita 49456d7
changed hearts to likes
HolaCarmensita b9871e4
mongo URL
HolaCarmensita c305b8d
usermodel and first post using the userRouter to add signup
HolaCarmensita e9cea9e
post login set
HolaCarmensita da1be4b
added middleware to use to autheticate the user, not applaied on any …
HolaCarmensita a896b5a
using bearer token and the middleware works great on addThought, yey
HolaCarmensita 67e9806
middleware on put and delete thought and controllers refactored
HolaCarmensita 50e7fea
dont remeber what I did, forgot to commit. I think it was the adding …
HolaCarmensita ca69441
toggle like works
HolaCarmensita 4c739a5
refactored the userRoute to using userController for easier readning
HolaCarmensita 4f6aa9f
populate insted of just ID
HolaCarmensita a01c6f4
Having trouble to deploy to render.com
HolaCarmensita 7e5ca47
changed the dependencis
HolaCarmensita 2d1aec7
trying things out to make this render.com work
HolaCarmensita bf7667f
trying everything to make this build work on render
HolaCarmensita aaa66f4
forgot to savepacjege.json
HolaCarmensita 32169d1
installed npm install --save-dev @babel/cli
HolaCarmensita 88a385a
Update README.md
HolaCarmensita 4280368
Update README.md
HolaCarmensita 53f5962
added npx
HolaCarmensita f270f10
Revert "added npx"
HolaCarmensita e8b90a2
Merge remote-tracking branch 'origin/HEAD'
HolaCarmensita ce4510e
npx
HolaCarmensita ff6ddee
delited modules
HolaCarmensita 06d26a8
GAAAH
HolaCarmensita 690c056
delited babel
HolaCarmensita 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,7 @@ | ||
# Project API | ||
# 🧠 Projekt: Happy Thoughts API | ||
|
||
This project includes the packages and babel setup for an express server, and is just meant to make things a little simpler to get up and running with. | ||
[Hola-Happy-Server](https://hola-happy-server.onrender.com) | ||
|
||
## Getting started | ||
[Hola-Happy-App](https://holahappythoughts.netlify.app/) | ||
|
||
Install dependencies with `npm install`, then start the server by running `npm run dev` | ||
|
||
## View it live | ||
|
||
Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. |
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,186 @@ | ||
import { Thought } from '../models/Thoughts.js'; | ||
import mongoose from 'mongoose'; | ||
|
||
export const listAllThoughts = async (req, res) => { | ||
const sortBy = req.query.sortBy || 'createdAt'; | ||
const sortDir = req.query.sortDir === 'ascending' ? 1 : -1; | ||
|
||
try { | ||
const thoughts = await Thought.find() | ||
.sort({ [sortBy]: sortDir }) | ||
.populate('createdBy', '_id email'); | ||
res.json(thoughts); | ||
} catch (error) { | ||
console.error('Mongoose error on listAllThoughts:', error); | ||
res.status(500).json({ error: 'Could not fetch thoughts from database' }); | ||
} | ||
}; | ||
|
||
export const getOneThought = async (req, res) => { | ||
const { id } = req.params; | ||
|
||
try { | ||
let thought = await Thought.findById(id); | ||
|
||
if (!thought) { | ||
return res.status(404).json({ | ||
error: 'Thought not found', | ||
requestedId: id, | ||
}); | ||
} | ||
thought = await thought.populate('createdBy', '_id email'); | ||
res.json(thought); | ||
} catch (error) { | ||
console.error('Mongoose error on getOneThought:', error); | ||
res.status(400).json({ error: 'Invalid ID format or other error' }); | ||
} | ||
}; | ||
|
||
export const addThought = async (req, res) => { | ||
const { message } = req.body; | ||
|
||
// Validate message length | ||
if (!message || message.length < 4 || message.length > 140) { | ||
return res.status(400).json({ | ||
error: 'Message is required and must be between 5 and 140 characters', | ||
}); | ||
} | ||
|
||
try { | ||
const newThought = await Thought.create({ | ||
message, | ||
createdBy: req.user._id, | ||
// likes and createdAt will be set by defaults in the model | ||
}); | ||
|
||
const populatedThought = await newThought.populate( | ||
'createdBy', | ||
'_id email' | ||
); | ||
res.status(201).json(populatedThought); | ||
} catch (error) { | ||
console.error('Mongoose error on addThought:', error); | ||
if (error.name === 'ValidationError') { | ||
res | ||
.status(400) | ||
.json({ error: 'Validation failed', details: error.errors }); | ||
} else { | ||
res.status(400).json({ error: 'Could not add your thought' }); | ||
} | ||
} | ||
}; | ||
|
||
export const likeThought = async (req, res) => { | ||
const { id } = req.params; | ||
const userId = req.user?._id; | ||
|
||
try { | ||
const thought = await Thought.findById(id); | ||
|
||
if (!thought) { | ||
return res.status(404).json({ error: 'Thought not found' }); | ||
} | ||
|
||
if (userId) { | ||
const userIdStr = userId.toString(); | ||
// Convert likedBy to array of strings for logic | ||
let likedByStrArr = thought.likedBy.map((id) => id.toString()); | ||
console.log('Before toggle:', { userIdStr, likedBy: likedByStrArr }); | ||
const hasLiked = likedByStrArr.includes(userIdStr); | ||
|
||
if (hasLiked) { | ||
likedByStrArr = likedByStrArr.filter((id) => id !== userIdStr); | ||
console.log('User unliked. After removal:', likedByStrArr); | ||
} else { | ||
likedByStrArr.push(userIdStr); | ||
console.log('User liked. After addition:', likedByStrArr); | ||
} | ||
thought.likes = likedByStrArr.length; | ||
console.log('Final likedBy and likes:', { | ||
likedBy: likedByStrArr, | ||
likes: thought.likes, | ||
}); | ||
// Convert likedBy back to ObjectIds before saving | ||
thought.likedBy = likedByStrArr.map( | ||
(id) => new mongoose.Types.ObjectId(id) | ||
); | ||
|
||
const updatedThought = await thought.save(); | ||
const populatedThought = await updatedThought.populate( | ||
'createdBy', | ||
'_id email' | ||
); | ||
return res.status(200).json(populatedThought); | ||
} | ||
|
||
// Guests should not be able to like | ||
return res.status(401).json({ error: 'Authentication required to like' }); | ||
} catch (error) { | ||
console.error('Error in likeThought:', error); | ||
res | ||
.status(500) | ||
.json({ error: 'Could not toggle like', details: error.message }); | ||
} | ||
}; | ||
|
||
export const updateThought = async (req, res) => { | ||
const { id } = req.params; | ||
const { message } = req.body; | ||
|
||
try { | ||
const thought = await Thought.findById(id); | ||
|
||
if (!thought) { | ||
return res.status(404).json({ error: 'Thought not found' }); | ||
} | ||
|
||
// Kontrollera att den inloggade användaren äger tanken | ||
if (thought.createdBy.toString() !== req.user._id.toString()) { | ||
return res | ||
.status(403) | ||
.json({ error: 'You are not allowed to update this thought' }); | ||
} | ||
|
||
thought.message = message; | ||
const updatedThought = await thought.save(); | ||
const populatedThought = await updatedThought.populate( | ||
'createdBy', | ||
'_id email' | ||
); | ||
res.status(200).json(populatedThought); | ||
} catch (err) { | ||
res | ||
.status(500) | ||
.json({ error: 'Could not update thought', details: err.message }); | ||
} | ||
}; | ||
|
||
export const removeThought = async (req, res) => { | ||
const { id } = req.params; | ||
|
||
try { | ||
// Validate if the ID is a valid MongoDB ObjectId | ||
if (!mongoose.Types.ObjectId.isValid(id)) { | ||
return res.status(400).json({ error: 'Invalid thought ID format' }); | ||
} | ||
|
||
const thought = await Thought.findById(id); | ||
|
||
if (!thought) { | ||
return res.status(404).json({ error: 'Thought not found' }); | ||
} | ||
|
||
if (thought.createdBy.toString() !== req.user._id.toString()) { | ||
return res | ||
.status(403) | ||
.json({ error: 'You are not allowed to delete this thought' }); | ||
} | ||
|
||
await thought.deleteOne(); | ||
res.status(200).json({ message: 'Thought deleted successfully' }); | ||
} catch (err) { | ||
res | ||
.status(500) | ||
.json({ error: 'Could not delete thought', details: err.message }); | ||
} | ||
}; |
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,63 @@ | ||
import User from '../models/Users.js'; | ||
import bcrypt from 'bcrypt'; | ||
import crypto from 'crypto'; | ||
|
||
export const signup = async (req, res) => { | ||
const { email, password } = req.body; | ||
|
||
try { | ||
if (!email || !password) { | ||
return res.status(400).json({ error: 'Email and password are required' }); | ||
} | ||
const existingUser = await User.findOne({ email }); | ||
if (existingUser) { | ||
return res.status(400).json({ error: 'Email already exists' }); | ||
} | ||
const salt = bcrypt.genSaltSync(); | ||
const hashedPassword = bcrypt.hashSync(password, salt); | ||
const newUser = await new User({ email, password: hashedPassword }).save(); | ||
res.status(201).json({ | ||
email: newUser.email, | ||
accessToken: newUser.accessToken, | ||
id: newUser._id, | ||
}); | ||
} catch (err) { | ||
res | ||
.status(500) | ||
.json({ error: 'Internal server error', details: err.message }); | ||
} | ||
}; | ||
|
||
export const login = async (req, res) => { | ||
const { email, password } = req.body; | ||
|
||
try { | ||
const user = await User.findOne({ email }); | ||
if (!user) { | ||
return res.status(401).json({ error: 'Invalid email or password' }); | ||
} | ||
const passwordMatch = bcrypt.compareSync(password, user.password); | ||
if (!passwordMatch) { | ||
return res.status(401).json({ error: 'Invalid email or password' }); | ||
} | ||
res.status(200).json({ | ||
email: user.email, | ||
accessToken: user.accessToken, | ||
id: user._id, | ||
}); | ||
} catch (err) { | ||
res | ||
.status(500) | ||
.json({ error: 'Internal server error', details: err.message }); | ||
} | ||
}; | ||
|
||
export const logout = async (req, res) => { | ||
try { | ||
req.user.accessToken = crypto.randomBytes(64).toString('hex'); | ||
await req.user.save(); | ||
res.status(200).json({ message: 'Logged out successfully' }); | ||
} catch (err) { | ||
res.status(500).json({ error: 'Could not log out', details: err.message }); | ||
} | ||
}; |
This file was deleted.
Oops, something went wrong.
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.
⭐