mirror of
https://github.com/hknsh/project-knedita.git
synced 2024-11-28 17:41:15 +00:00
Created like-post and follow-user routes, changed file names
This commit is contained in:
parent
b6be9d6755
commit
11d74d846b
33 changed files with 280 additions and 41 deletions
|
@ -19,8 +19,8 @@ An open-source social media.
|
||||||
- Able to choose a profile picture✅
|
- Able to choose a profile picture✅
|
||||||
- Probably gonna use LocalStack to mock Amazon S3✅
|
- Probably gonna use LocalStack to mock Amazon S3✅
|
||||||
- Image compression ✅
|
- Image compression ✅
|
||||||
- Following/unfollowing features
|
- Following/unfollowing features ✅
|
||||||
- Like posts
|
- Like posts ✅
|
||||||
- Probably pinned posts
|
- Probably pinned posts
|
||||||
- Authentication ✅
|
- Authentication ✅
|
||||||
- Add more verification (like, if the password is too short) ✅
|
- Add more verification (like, if the password is too short) ✅
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Like" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"postId" TEXT NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "Like_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Like" ADD CONSTRAINT "Like_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Like" ADD CONSTRAINT "Like_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
@ -0,0 +1,22 @@
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Follower" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"followerId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Follower_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Following" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"followingId" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "Following_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Follower" ADD CONSTRAINT "Follower_followerId_fkey" FOREIGN KEY ("followerId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Following" ADD CONSTRAINT "Following_followingId_fkey" FOREIGN KEY ("followingId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the `Follower` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
- You are about to drop the `Following` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Follower" DROP CONSTRAINT "Follower_followerId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Following" DROP CONSTRAINT "Following_followingId_fkey";
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "Follower";
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "Following";
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Follows" (
|
||||||
|
"followerId" TEXT NOT NULL,
|
||||||
|
"followingId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Follows_pkey" PRIMARY KEY ("followerId","followingId")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Follows" ADD CONSTRAINT "Follows_followerId_fkey" FOREIGN KEY ("followerId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Follows" ADD CONSTRAINT "Follows_followingId_fkey" FOREIGN KEY ("followingId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
@ -1,6 +1,3 @@
|
||||||
// This is your Prisma schema file,
|
|
||||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
||||||
|
|
||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
}
|
}
|
||||||
|
@ -18,6 +15,9 @@ model User {
|
||||||
password String
|
password String
|
||||||
posts Post[]
|
posts Post[]
|
||||||
profileImage String?
|
profileImage String?
|
||||||
|
likedPosts Like[]
|
||||||
|
followers Follows[] @relation("following")
|
||||||
|
following Follows[] @relation("follower")
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,24 @@ model Post {
|
||||||
content String
|
content String
|
||||||
authorId String
|
authorId String
|
||||||
author User @relation(fields: [authorId], references: [id])
|
author User @relation(fields: [authorId], references: [id])
|
||||||
|
likes Like[]
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Like {
|
||||||
|
id String @id @default(uuid())
|
||||||
|
postId String
|
||||||
|
post Post @relation(fields: [postId], references: [id])
|
||||||
|
userId String
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
model Follows {
|
||||||
|
follower User @relation("follower", fields: [followerId], references: [id])
|
||||||
|
followerId String
|
||||||
|
following User @relation("following", fields: [followingId], references: [id])
|
||||||
|
followingId String
|
||||||
|
|
||||||
|
@@id([followerId, followingId])
|
||||||
|
}
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
import { Router } from 'express'
|
import { Router } from 'express'
|
||||||
|
|
||||||
// Controllers
|
// Controllers
|
||||||
import postCreateController from './posts/post-create'
|
import postCreateController from './posts/create'
|
||||||
import postDeleteController from './posts/post-delete'
|
import postDeleteController from './posts/delete'
|
||||||
import postInfoController from './posts/post-info'
|
import postInfoController from './posts/get-info'
|
||||||
import postUpdateController from './posts/post-update'
|
import postUpdateController from './posts/update'
|
||||||
|
|
||||||
// Middlewares
|
// Middlewares
|
||||||
import ensureAuthenticated from '../middlewares/ensure-authenticated'
|
import ensureAuthenticated from '../middlewares/ensure-authenticated'
|
||||||
|
|
|
@ -3,12 +3,14 @@
|
||||||
import { Router } from 'express'
|
import { Router } from 'express'
|
||||||
|
|
||||||
// Controllers
|
// Controllers
|
||||||
import userAuthController from './users/user-auth'
|
import userAuthController from './users/auth'
|
||||||
import userDeleteController from './users/user-delete'
|
import userDeleteController from './users/delete'
|
||||||
import userInfoController from './users/user-info'
|
import userFollowController from './users/follow-user'
|
||||||
import userSignupController from './users/user-signup'
|
import userInfoController from './users/get-info'
|
||||||
import userUpdateController from './users/user-update'
|
import userLikePostController from './users/like-post'
|
||||||
import userUploadPictureController from './users/user-upload-picture'
|
import userSignupController from './users/signup'
|
||||||
|
import userUpdateController from './users/update'
|
||||||
|
import userUploadPictureController from './users/upload-picture'
|
||||||
|
|
||||||
// Middlewares
|
// Middlewares
|
||||||
import ensureAuthenticated from '../middlewares/ensure-authenticated'
|
import ensureAuthenticated from '../middlewares/ensure-authenticated'
|
||||||
|
@ -23,5 +25,7 @@ usersRouter.get('/info', userInfoController)
|
||||||
usersRouter.post('/signup', userSignupController)
|
usersRouter.post('/signup', userSignupController)
|
||||||
usersRouter.put('/update', ensureAuthenticated, userUpdateController)
|
usersRouter.put('/update', ensureAuthenticated, userUpdateController)
|
||||||
usersRouter.put('/profile-picture/upload', ensureAuthenticated, uploadFile, userUploadPictureController)
|
usersRouter.put('/profile-picture/upload', ensureAuthenticated, uploadFile, userUploadPictureController)
|
||||||
|
usersRouter.post('/like-post', ensureAuthenticated, userLikePostController)
|
||||||
|
usersRouter.post('/follow-user', ensureAuthenticated, userFollowController)
|
||||||
|
|
||||||
export default usersRouter
|
export default usersRouter
|
||||||
|
|
18
src/controllers/users/follow-user.ts
Normal file
18
src/controllers/users/follow-user.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { user } from '../../services'
|
||||||
|
import type { Request, Response } from 'express'
|
||||||
|
import { badRequest } from '../../lib/http-errors'
|
||||||
|
|
||||||
|
async function userFollowController (req: Request, res: Response): Promise<void> {
|
||||||
|
const userId = req.user?.id ?? ''
|
||||||
|
const { userToFollow } = req.body
|
||||||
|
|
||||||
|
const result = await user.follow(userId, userToFollow)
|
||||||
|
|
||||||
|
if (result instanceof Error) {
|
||||||
|
return badRequest(res, result.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default userFollowController
|
18
src/controllers/users/like-post.ts
Normal file
18
src/controllers/users/like-post.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { user } from '../../services'
|
||||||
|
import type { Request, Response } from 'express'
|
||||||
|
import { badRequest } from '../../lib/http-errors'
|
||||||
|
|
||||||
|
async function userLikePostController (req: Request, res: Response): Promise<void> {
|
||||||
|
const userId = req.user?.id ?? ''
|
||||||
|
const { postId } = req.body
|
||||||
|
|
||||||
|
const result = await user.likePost(postId, userId)
|
||||||
|
|
||||||
|
if (result instanceof Error) {
|
||||||
|
return badRequest(res, result.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default userLikePostController
|
|
@ -1,14 +1,16 @@
|
||||||
import userAuthService from './users/user-auth'
|
import userAuthService from './users/auth'
|
||||||
import userDeleteService from './users/user-delete'
|
import userDeleteService from './users/delete'
|
||||||
import userInfoService from './users/user-info'
|
import userFollowService from './users/follow-user'
|
||||||
import userSignupService from './users/user-signup'
|
import userInfoService from './users/get-info'
|
||||||
import userUpdateService from './users/user-update'
|
import userLikePostService from './users/like-post'
|
||||||
import userUploadPictureService from './users/user-upload-picture'
|
import userSignupService from './users/signup'
|
||||||
|
import userUpdateService from './users/update'
|
||||||
|
import userUploadPictureService from './users/upload-picture'
|
||||||
|
|
||||||
import postCreateService from './posts/post-create'
|
import postCreateService from './posts/create'
|
||||||
import postDeleteService from './posts/post-delete'
|
import postDeleteService from './posts/delete'
|
||||||
import postInfoService from './posts/post-info'
|
import postInfoService from './posts/get-info'
|
||||||
import postUpdateService from './posts/post-update'
|
import postUpdateService from './posts/update'
|
||||||
|
|
||||||
// User services
|
// User services
|
||||||
const user = {
|
const user = {
|
||||||
|
@ -17,7 +19,9 @@ const user = {
|
||||||
info: userInfoService,
|
info: userInfoService,
|
||||||
signup: userSignupService,
|
signup: userSignupService,
|
||||||
update: userUpdateService,
|
update: userUpdateService,
|
||||||
uploadPicture: userUploadPictureService
|
uploadPicture: userUploadPictureService,
|
||||||
|
likePost: userLikePostService,
|
||||||
|
follow: userFollowService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post services
|
// Post services
|
||||||
|
|
|
@ -15,7 +15,8 @@ async function postInfoService (id: string): Promise<Object | Error> {
|
||||||
displayName: true,
|
displayName: true,
|
||||||
username: true
|
username: true
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
likes: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
0
src/services/posts/get-likes.ts
Normal file
0
src/services/posts/get-likes.ts
Normal file
|
@ -10,7 +10,7 @@ async function userAuthService (email: string, password: string): Promise<Object
|
||||||
})
|
})
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
return new Error('User does not exists')
|
return new Error('Invalid email or password')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (email === undefined || password === undefined) {
|
if (email === undefined || password === undefined) {
|
55
src/services/users/follow-user.ts
Normal file
55
src/services/users/follow-user.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import prisma from '../../clients/prisma-client'
|
||||||
|
|
||||||
|
async function userFollowService (userId: string, followingUsername: string): Promise<Object | Error> {
|
||||||
|
if (userId === undefined || followingUsername === undefined) {
|
||||||
|
return new Error('Missing fields')
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await prisma.user.findFirst({
|
||||||
|
where: {
|
||||||
|
username: followingUsername
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (user === null) {
|
||||||
|
return new Error('User not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const userToFollow = await prisma.user.findFirst({
|
||||||
|
where: {
|
||||||
|
id: userId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (userToFollow === null) {
|
||||||
|
return new Error('User to follow not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const alreadyFollow = await prisma.follows.findFirst({
|
||||||
|
where: {
|
||||||
|
followerId: user.id,
|
||||||
|
followingId: userToFollow.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (alreadyFollow !== null) {
|
||||||
|
await prisma.follows.deleteMany({
|
||||||
|
where: {
|
||||||
|
followerId: user.id,
|
||||||
|
followingId: userToFollow.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const follow = await prisma.follows.create({
|
||||||
|
data: {
|
||||||
|
followerId: user.id,
|
||||||
|
followingId: userToFollow.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return follow
|
||||||
|
}
|
||||||
|
|
||||||
|
export default userFollowService
|
|
@ -17,6 +17,11 @@ async function userInfoService (username: string): Promise<Object> {
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
updatedAt: true
|
updatedAt: true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
likedPosts: {
|
||||||
|
select: {
|
||||||
|
postId: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
55
src/services/users/like-post.ts
Normal file
55
src/services/users/like-post.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import prisma from '../../clients/prisma-client'
|
||||||
|
|
||||||
|
async function userLikePostService (postId: string, userId: string): Promise<Object | Error> {
|
||||||
|
if (postId === undefined || userId === undefined) {
|
||||||
|
return new Error('Missing fields')
|
||||||
|
}
|
||||||
|
|
||||||
|
const post = await prisma.post.findFirst({
|
||||||
|
where: {
|
||||||
|
id: postId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (post === null) {
|
||||||
|
return new Error('Post not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await prisma.user.findFirst({
|
||||||
|
where: {
|
||||||
|
id: userId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (user === null) {
|
||||||
|
return new Error('User not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const alreadyLiked = await prisma.like.findFirst({
|
||||||
|
where: {
|
||||||
|
postId: post.id,
|
||||||
|
userId: user.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (alreadyLiked !== null) {
|
||||||
|
await prisma.like.deleteMany({
|
||||||
|
where: {
|
||||||
|
postId: post.id,
|
||||||
|
userId: user.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const like = await prisma.like.create({
|
||||||
|
data: {
|
||||||
|
postId: post.id,
|
||||||
|
userId: user.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return like
|
||||||
|
}
|
||||||
|
|
||||||
|
export default userLikePostService
|
|
@ -8,6 +8,9 @@ async function userUpdateService ({ id, email, displayName, username }: userPayl
|
||||||
return new Error('User not found')
|
return new Error('User not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the provided email or username is different from the current user's data
|
||||||
|
// if they are different, additional queries are made to check if the new email or username is already in use.
|
||||||
|
|
||||||
if (email !== undefined && email.trim() !== user.email) {
|
if (email !== undefined && email.trim() !== user.email) {
|
||||||
const existingUser = await prisma.user.findFirst({ where: { email } })
|
const existingUser = await prisma.user.findFirst({ where: { email } })
|
||||||
if ((existingUser != null) && existingUser.email !== user.email) {
|
if ((existingUser != null) && existingUser.email !== user.email) {
|
|
@ -15,18 +15,7 @@ async function userUploadPictureService (authorId: string, url: string): Promise
|
||||||
profileImage: url
|
profileImage: url
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
profileImage: true,
|
profileImage: true
|
||||||
displayName: true,
|
|
||||||
username: true,
|
|
||||||
createdAt: true,
|
|
||||||
posts: {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
content: true,
|
|
||||||
createdAt: true,
|
|
||||||
updatedAt: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue