✨ User listing, getting, following and unfollowing
Signed-off-by: Pau Costa <mico@micodev.es>pull/2/head
parent
7aa0ebf366
commit
28e4b5c318
|
|
@ -1,53 +1,137 @@
|
|||
import { AppDataSource } from "../data-source"
|
||||
import { NextFunction, Request, Response } from "express"
|
||||
import { User } from "../entity/User"
|
||||
import {AppError} from "../util/AppError";
|
||||
import {AppRequest} from "../util/AppRequest";
|
||||
import {Notification} from "../entity/Notification";
|
||||
import {catchAsync} from "../util/catchAsync";
|
||||
|
||||
export class UserController {
|
||||
|
||||
private userRepository = AppDataSource.getRepository(User)
|
||||
|
||||
async all(request: Request, response: Response, next: NextFunction) {
|
||||
return this.userRepository.find()
|
||||
}
|
||||
private notificationRepository = AppDataSource.getRepository(Notification)
|
||||
public getAllUsers = catchAsync(async (req: Request, res: Response, next: NextFunction) => {
|
||||
const users = await this.userRepository.find()
|
||||
|
||||
async one(request: Request, response: Response, next: NextFunction) {
|
||||
const id = parseInt(request.params.id)
|
||||
// remove sensitive fields
|
||||
users.forEach(user => {
|
||||
user.deleteSensitiveFields()
|
||||
})
|
||||
|
||||
res.status(200).send(users)
|
||||
})
|
||||
|
||||
public getUser = catchAsync( async (req: Request, res: Response, next: NextFunction) => {
|
||||
const id = req.params.id
|
||||
const parsedId = parseInt(id)
|
||||
// Check if ID is a number
|
||||
if(isNaN(parsedId)) return next(new AppError('Invalid ID', 400))
|
||||
|
||||
const user = await this.userRepository.findOne({where: {id: parsedId}, relations:{
|
||||
followed: true,
|
||||
followers: true,
|
||||
posts: true,
|
||||
comments: true,
|
||||
}})
|
||||
|
||||
|
||||
if(!user) return next(new AppError('No user found with that ID', 404))
|
||||
|
||||
// remove sensitive fields
|
||||
user.deleteSensitiveFields()
|
||||
user.followed.forEach(followedUser => followedUser.deleteSensitiveFields())
|
||||
user.followers.forEach(follower => follower.deleteSensitiveFields())
|
||||
|
||||
return res.send(user)
|
||||
})
|
||||
|
||||
public getMe = catchAsync(async (req: AppRequest, res: Response, next: NextFunction) => {
|
||||
const user = await this.userRepository.findOne({
|
||||
where: { id }
|
||||
where: {id: req.user.id},
|
||||
relations:{
|
||||
followed: true,
|
||||
followers: true,
|
||||
posts: true,
|
||||
comments: true,
|
||||
}
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return "unregistered user"
|
||||
user.followed.forEach(followedUser => followedUser.deleteSensitiveFields())
|
||||
user.followers.forEach(follower => follower.deleteSensitiveFields())
|
||||
|
||||
return res.status(200).send(user)
|
||||
})
|
||||
|
||||
public followUser = catchAsync(async (req: AppRequest, res: Response, next: NextFunction) => {
|
||||
const userToFollowId = req.params.id
|
||||
const parsedId = parseInt(userToFollowId)
|
||||
// Check if ID is a number
|
||||
if(isNaN(parsedId)) return next(new AppError('Invalid ID', 400))
|
||||
|
||||
const user = req.user
|
||||
const userToFollow = await this.userRepository.findOne({
|
||||
where: {id: parsedId},
|
||||
relations:{followed: true, followers: true, notifications: true}}
|
||||
)
|
||||
|
||||
if(!userToFollow) return next(new AppError('No user found with that ID', 404))
|
||||
|
||||
// Check if user is already following
|
||||
if(user.followed.some(followedUser => followedUser.id === userToFollow.id)){
|
||||
return next(new AppError('You are already following this user', 400))
|
||||
}
|
||||
return user
|
||||
}
|
||||
// Follow the user
|
||||
user.followed.push(userToFollow)
|
||||
await this.userRepository.save(user)
|
||||
// Add the requesting user to the followers of the user being followed
|
||||
userToFollow.followers.push(user)
|
||||
// Create a notification for the user being followed
|
||||
const followNotification = Object.assign(new Notification(),{
|
||||
seen: false,
|
||||
message: `${user.firstName} is now following you`,
|
||||
timeStamp: new Date()
|
||||
})
|
||||
userToFollow.notifications.push(
|
||||
await this.notificationRepository.save(followNotification)
|
||||
)
|
||||
await this.userRepository.save(userToFollow)
|
||||
|
||||
async save(request: Request, response: Response, next: NextFunction) {
|
||||
const { firstName, lastName, age } = request.body;
|
||||
|
||||
const user = Object.assign(new User(), {
|
||||
firstName,
|
||||
lastName,
|
||||
age
|
||||
return res.status(200).send({
|
||||
status: 'success',
|
||||
message: `You are now following ${userToFollow.firstName}`
|
||||
})
|
||||
})
|
||||
|
||||
public unfollowUser = catchAsync(async (req: AppRequest, res: Response, next: NextFunction) => {
|
||||
const userToUnfollowId = req.params.id
|
||||
const parsedId = parseInt(userToUnfollowId)
|
||||
// Check if ID is a number
|
||||
if(isNaN(parsedId)) return next(new AppError('Invalid ID', 400))
|
||||
|
||||
const user = req.user
|
||||
const userToUnfollow = await this.userRepository.findOne({
|
||||
where: {id: parsedId},
|
||||
relations: {followed: true, followers: true}
|
||||
})
|
||||
|
||||
return this.userRepository.save(user)
|
||||
}
|
||||
|
||||
async remove(request: Request, response: Response, next: NextFunction) {
|
||||
const id = parseInt(request.params.id)
|
||||
|
||||
let userToRemove = await this.userRepository.findOneBy({ id })
|
||||
|
||||
if (!userToRemove) {
|
||||
return "this user not exist"
|
||||
if(!userToUnfollow) return next(new AppError('No user found with that ID', 404))
|
||||
// Check if user is following
|
||||
if(!user.followed.some(followedUser => followedUser.id === userToUnfollow.id)){
|
||||
return next(new AppError('You are not following this user', 400))
|
||||
}
|
||||
// Unfollow the user
|
||||
user.followed = user.followed.filter(followedUser => followedUser.id !== userToUnfollow.id)
|
||||
await this.userRepository.save(user)
|
||||
|
||||
await this.userRepository.remove(userToRemove)
|
||||
userToUnfollow.followers = userToUnfollow.followers.filter(follower => follower.id !== user.id)
|
||||
await this.userRepository.save(userToUnfollow)
|
||||
|
||||
return "user has been removed"
|
||||
}
|
||||
return res.status(200).send({
|
||||
status: 'success',
|
||||
message: `You are no longer following ${userToUnfollow.firstName}`
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
|
@ -106,7 +106,10 @@ export class AuthController {
|
|||
// Verify the token
|
||||
const decoded = jwt.verify(token, this.jwt_secret) as JwtPayload
|
||||
// Check if the user still exists
|
||||
const candidateUser = await this.userRepository.findOne({where: {id: decoded.id}})
|
||||
const candidateUser = await this.userRepository.findOne({
|
||||
where: {id: decoded.id},
|
||||
relations: {followed: true, followers: true, notifications: true}
|
||||
})
|
||||
if(!candidateUser){
|
||||
return next(new AppError('The user belonging to this token no longer exists', 401))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { DataSource } from "typeorm"
|
|||
import { User } from "./entity/User"
|
||||
import {Comment} from "./entity/Comment";
|
||||
import {Post} from "./entity/Post";
|
||||
import {Notification} from "./entity/Notification";
|
||||
|
||||
export const AppDataSource = new DataSource({
|
||||
type: "mysql",
|
||||
|
|
@ -13,7 +14,7 @@ export const AppDataSource = new DataSource({
|
|||
database: process.env.MYSQL_DATABASE,
|
||||
synchronize: true,
|
||||
logging: false,
|
||||
entities: [User, Comment, Post],
|
||||
entities: [User, Comment, Post, Notification],
|
||||
migrations: [],
|
||||
subscribers: [],
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import {Column, Entity, ManyToOne, PrimaryGeneratedColumn} from "typeorm";
|
||||
import {User} from "./User";
|
||||
|
||||
@Entity()
|
||||
export class Notification {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@Column()
|
||||
message: string
|
||||
|
||||
@Column()
|
||||
timeStamp: Date
|
||||
|
||||
@Column()
|
||||
seen: boolean
|
||||
|
||||
@ManyToOne(type => User, user => user.notifications)
|
||||
belongsTo: User
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ import {Entity, PrimaryGeneratedColumn, Column, ManyToMany, OneToMany, JoinTable
|
|||
import * as bcrypt from "bcrypt"
|
||||
import {Post} from "./Post";
|
||||
import {Comment} from "./Comment";
|
||||
import {Notification} from "./Notification";
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
|
|
@ -25,12 +26,19 @@ export class User {
|
|||
@JoinTable()
|
||||
followed: User[]
|
||||
|
||||
@ManyToMany(type => User)
|
||||
@JoinTable()
|
||||
followers: User[]
|
||||
|
||||
@OneToMany(type => Post, post=> post.createdBy)
|
||||
posts: Post[]
|
||||
|
||||
@OneToMany(type => Comment, comment=> comment.createdBy)
|
||||
comments: Comment[]
|
||||
|
||||
@OneToMany(type => Notification, notification => notification.belongsTo)
|
||||
notifications: Notification[]
|
||||
|
||||
|
||||
static async hashPassword(password: string){
|
||||
return await bcrypt.hash(password, parseInt(process.env.SALT_ROUNDS))
|
||||
|
|
@ -39,4 +47,9 @@ export class User {
|
|||
async comparePassword(password: string){
|
||||
return await bcrypt.compare(password, this.password)
|
||||
}
|
||||
|
||||
public deleteSensitiveFields(){
|
||||
delete this.password
|
||||
delete this.email
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import * as bodyParser from "body-parser"
|
|||
import { AppDataSource } from "./data-source"
|
||||
import {errorHandler} from "./controller/errorController";
|
||||
import {AuthRoutes} from "./routes/authRoutes";
|
||||
import {UserRoutes} from "./routes/userRoutes";
|
||||
import {AuthController} from "./controller/authController";
|
||||
|
||||
AppDataSource.initialize().then(async () => {
|
||||
|
||||
|
|
@ -13,6 +15,11 @@ AppDataSource.initialize().then(async () => {
|
|||
// register express routes from defined application routes
|
||||
// Auth Routes
|
||||
app.use('/auth', AuthRoutes)
|
||||
// All routes after this one require authentication
|
||||
const authController = new AuthController();
|
||||
app.use(authController.protect)
|
||||
|
||||
app.use('/users', UserRoutes)
|
||||
|
||||
// setup express app here
|
||||
app.use(errorHandler)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export const AuthRoutes = Router();
|
|||
|
||||
AuthRoutes.route("/signup").post(authController.handleSignUp)
|
||||
AuthRoutes.route("/login").post(authController.handleLogin)
|
||||
AuthRoutes.route("/logout").get(authController.handleLogout)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
import {Router} from "express";
|
||||
import {UserController} from "../controller/UserController";
|
||||
|
||||
export const UserRoutes = Router();
|
||||
|
||||
const userController = new UserController()
|
||||
|
||||
UserRoutes.route("/").get(userController.getAllUsers)
|
||||
UserRoutes.route("/me").get(userController.getMe)
|
||||
UserRoutes.route("/:id").get(userController.getUser)
|
||||
UserRoutes.route("/follow/:id").post(userController.followUser)
|
||||
UserRoutes.route("/unfollow/:id").post(userController.unfollowUser)
|
||||
Loading…
Reference in New Issue