Implemented comments likes and refactored some routes

This commit is contained in:
Hackntosh 2023-07-28 10:18:22 -03:00
parent d428991539
commit fcb4a83787
37 changed files with 326 additions and 84 deletions

View file

@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "Like" ADD COLUMN "commentId" TEXT;
-- AddForeignKey
ALTER TABLE "Like" ADD CONSTRAINT "Like_commentId_fkey" FOREIGN KEY ("commentId") REFERENCES "Comments"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View file

@ -0,0 +1,36 @@
/*
Warnings:
- You are about to drop the column `commentId` on the `Like` table. All the data in the column will be lost.
- You are about to drop the column `postId` on the `Like` table. All the data in the column will be lost.
- A unique constraint covering the columns `[userId,targetType,targetId]` on the table `Like` will be added. If there are existing duplicate values, this will fail.
- Added the required column `targetId` to the `Like` table without a default value. This is not possible if the table is not empty.
- Added the required column `targetType` to the `Like` table without a default value. This is not possible if the table is not empty.
*/
-- DropForeignKey
ALTER TABLE "Like" DROP CONSTRAINT "Like_commentId_fkey";
-- DropForeignKey
ALTER TABLE "Like" DROP CONSTRAINT "Like_postId_fkey";
-- DropForeignKey
ALTER TABLE "Like" DROP CONSTRAINT "Like_userId_fkey";
-- AlterTable
ALTER TABLE "Like" DROP COLUMN "commentId",
DROP COLUMN "postId",
ADD COLUMN "targetId" TEXT NOT NULL,
ADD COLUMN "targetType" TEXT NOT NULL;
-- CreateIndex
CREATE UNIQUE INDEX "Like_userId_targetType_targetId_key" ON "Like"("userId", "targetType", "targetId");
-- AddForeignKey
ALTER TABLE "Like" ADD CONSTRAINT "Like_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Like" ADD CONSTRAINT "PostLikes" FOREIGN KEY ("targetId") REFERENCES "Post"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Like" ADD CONSTRAINT "CommentLikes" FOREIGN KEY ("targetId") REFERENCES "Comments"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View file

@ -0,0 +1,59 @@
/*
Warnings:
- The values [FRIEND] on the enum `NotificationType` will be removed. If these variants are still used in the database, this will fail.
- You are about to drop the `Like` table. If the table is not empty, all the data it contains will be lost.
*/
-- AlterEnum
BEGIN;
CREATE TYPE "NotificationType_new" AS ENUM ('WARNING', 'INFO');
ALTER TABLE "Notifications" ALTER COLUMN "type" TYPE "NotificationType_new" USING ("type"::text::"NotificationType_new");
ALTER TYPE "NotificationType" RENAME TO "NotificationType_old";
ALTER TYPE "NotificationType_new" RENAME TO "NotificationType";
DROP TYPE "NotificationType_old";
COMMIT;
-- DropForeignKey
ALTER TABLE "Like" DROP CONSTRAINT "CommentLikes";
-- DropForeignKey
ALTER TABLE "Like" DROP CONSTRAINT "Like_userId_fkey";
-- DropForeignKey
ALTER TABLE "Like" DROP CONSTRAINT "PostLikes";
-- DropTable
DROP TABLE "Like";
-- CreateTable
CREATE TABLE "PostLike" (
"id" TEXT NOT NULL,
"postId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "PostLike_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "CommentLike" (
"id" TEXT NOT NULL,
"commentId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "CommentLike_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "PostLike" ADD CONSTRAINT "PostLike_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PostLike" ADD CONSTRAINT "PostLike_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CommentLike" ADD CONSTRAINT "CommentLike_commentId_fkey" FOREIGN KEY ("commentId") REFERENCES "Comments"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CommentLike" ADD CONSTRAINT "CommentLike_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View file

@ -15,7 +15,8 @@ model User {
password String password String
posts Post[] posts Post[]
profileImage String? profileImage String?
likedPosts Like[] likedPosts PostLike[]
likedComments CommentLike[]
followers Follows[] @relation("following") followers Follows[] @relation("following")
following Follows[] @relation("follower") following Follows[] @relation("follower")
postComments Comments[] postComments Comments[]
@ -29,13 +30,13 @@ model Post {
content String content String
authorId String authorId String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade) author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
likes Like[] likes PostLike[]
comments Comments[] comments Comments[]
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
} }
model Like { model PostLike {
id String @id @default(uuid()) id String @id @default(uuid())
postId String postId String
post Post @relation(fields: [postId], references: [id], onDelete: Cascade) post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
@ -44,6 +45,15 @@ model Like {
createdAt DateTime @default(now()) createdAt DateTime @default(now())
} }
model CommentLike {
id String @id @default(uuid())
commentId String
comment Comments @relation(fields: [commentId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
}
model Follows { model Follows {
follower User @relation("follower", fields: [followerId], references: [id], onDelete: Cascade) follower User @relation("follower", fields: [followerId], references: [id], onDelete: Cascade)
followerId String followerId String
@ -60,6 +70,7 @@ model Comments {
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
postId String postId String
post Post @relation(fields: [postId], references: [id], onDelete: Cascade) post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
likes CommentLike[]
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt @default(now()) updatedAt DateTime @updatedAt @default(now())
} }
@ -77,5 +88,5 @@ model Notifications {
enum NotificationType { enum NotificationType {
WARNING WARNING
FRIEND INFO
} }

View file

@ -5,7 +5,8 @@ import { Router } from 'express'
// Controllers // Controllers
import commentCreateController from './comments/create' import commentCreateController from './comments/create'
import commentDeleteController from './comments/delete' import commentDeleteController from './comments/delete'
import commentFetchController from './comments/fetch' import commentFetchController from './comments/fetch-info'
import commentFetchLikesController from './comments/fetch-likes'
import commentUpdateController from './comments/update' import commentUpdateController from './comments/update'
// Middlewares // Middlewares
@ -18,5 +19,6 @@ commentsRouter.post('/create', ensureAuthenticated, commentCreateController)
commentsRouter.post('/delete', ensureAuthenticated, commentDeleteController) commentsRouter.post('/delete', ensureAuthenticated, commentDeleteController)
commentsRouter.get('/info', commentFetchController) commentsRouter.get('/info', commentFetchController)
commentsRouter.put('/update', ensureAuthenticated, commentUpdateController) commentsRouter.put('/update', ensureAuthenticated, commentUpdateController)
commentsRouter.get('/fetch-likes', commentFetchLikesController)
export default commentsRouter export default commentsRouter

View file

@ -1,4 +1,4 @@
import { comment } from '../../services' import comment from '../../services/comments/index'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,4 +1,4 @@
import { comment } from '../../services' import comment from '../../services/comments/index'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'
@ -7,7 +7,7 @@ async function commentDeleteController (req: Request, res: Response): Promise<vo
const id = req.user?.id ?? '' const id = req.user?.id ?? ''
if (commentId === undefined) { if (commentId === undefined) {
return badRequest(res, 'Expected post id') return badRequest(res, 'Expected comment id')
} }
const result = await comment.delete(commentId, id) const result = await comment.delete(commentId, id)

View file

@ -1,4 +1,4 @@
import { comment } from '../../services' import comment from '../../services/comments/index'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'
@ -6,7 +6,7 @@ async function commentFetchController (req: Request, res: Response): Promise<voi
const commentId = req.query.id as string const commentId = req.query.id as string
if (commentId === undefined) { if (commentId === undefined) {
return badRequest(res, 'Expected post id') return badRequest(res, 'Expected comment id')
} }
const result = await comment.fetch(commentId) const result = await comment.fetch(commentId)

View file

@ -0,0 +1,21 @@
import comment from '../../services/comments/index'
import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors'
async function commentFetchLikesController (req: Request, res: Response): Promise<void> {
const commentId = req.query.id as string
if (commentId === undefined) {
return badRequest(res, 'Expected comment id')
}
const result = await comment.fetchLikes(commentId)
if (result instanceof Error) {
return badRequest(res, result.message)
}
res.json(result)
}
export default commentFetchLikesController

View file

@ -1,4 +1,4 @@
import { comment } from '../../services' import comment from '../../services/comments/index'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,4 +1,4 @@
import { post } from '../../services/index' import post from '../../services/posts/index'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,4 +1,4 @@
import { post } from '../../services/index' import post from '../../services/posts/index'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,4 +1,4 @@
import { post } from '../../services/index' import post from '../../services/posts/index'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,4 +1,4 @@
import { post } from '../../services/index' import post from '../../services/posts/index'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,4 +1,4 @@
import { post } from '../../services/index' import post from '../../services/posts/index'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -8,6 +8,7 @@ import userDeleteController from './users/delete'
import userFollowController from './users/follow-user' import userFollowController from './users/follow-user'
import userFetchInfoController from './users/fetch-info' import userFetchInfoController from './users/fetch-info'
import userFetchPostsController from './users/fetch-posts' import userFetchPostsController from './users/fetch-posts'
import userLikeCommentController from './users/like-comment'
import userLikePostController from './users/like-post' import userLikePostController from './users/like-post'
import userSearchController from './users/search-user' import userSearchController from './users/search-user'
import userSignupController from './users/signup' import userSignupController from './users/signup'
@ -31,5 +32,6 @@ usersRouter.post('/like-post', ensureAuthenticated, userLikePostController)
usersRouter.post('/follow-user', ensureAuthenticated, userFollowController) usersRouter.post('/follow-user', ensureAuthenticated, userFollowController)
usersRouter.get('/fetch-posts', userFetchPostsController) usersRouter.get('/fetch-posts', userFetchPostsController)
usersRouter.get('/search', userSearchController) usersRouter.get('/search', userSearchController)
usersRouter.post('/like-comment', ensureAuthenticated, userLikeCommentController)
export default usersRouter export default usersRouter

View file

@ -1,4 +1,4 @@
import { user } from '../../services/index' import user from '../../services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,4 +1,4 @@
import { user } from '../../services' import user from '../../services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,4 +1,4 @@
import { user } from '../../services' import user from '../../services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,4 +1,4 @@
import { user } from '../../services' import user from '../../services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,4 +1,4 @@
import { user } from '../../services' import user from '../../services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -0,0 +1,18 @@
import user from '../../services/users'
import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors'
async function userLikeCommentController (req: Request, res: Response): Promise<void> {
const userId = req.user?.id ?? ''
const { commentId, postId } = req.body
const result = await user.likeComment(postId, commentId, userId)
if (result instanceof Error) {
return badRequest(res, result.message)
}
res.json(result)
}
export default userLikeCommentController

View file

@ -1,4 +1,4 @@
import { user } from '../../services' import user from '../../services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,4 +1,4 @@
import { user } from '../../services' import user from '../../services/users'
import { Request, Response } from 'express' import { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,4 +1,4 @@
import { user } from '../../services' import user from '../../services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,4 +1,4 @@
import { user } from '../../services' import user from '../../services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/restrict-template-expressions */
import { user } from '../../services/index' import user from '../../services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors' import { badRequest } from '../../lib/http-errors'

View file

@ -18,6 +18,10 @@ function uploadImage (req: Request, res: Response, next: NextFunction) {
return badRequest(res, cb.message) return badRequest(res, cb.message)
} }
if (req.file === undefined) {
return badRequest(res, 'Expected file')
}
// @ts-expect-error property `key` does not exists in types // @ts-expect-error property `key` does not exists in types
await compressImage(req.file?.key) await compressImage(req.file?.key)

View file

@ -0,0 +1,26 @@
import prisma from '../../clients/prisma-client'
async function commentFetchLikesService (id: string): Promise<Object | Error> {
const post = await prisma.commentLike.findMany({
where: {
commentId: id
},
select: {
user: {
select: {
displayName: true,
username: true,
profileImage: true
}
}
}
})
if (post === null) {
return new Error('Comment not found')
}
return post
}
export default commentFetchLikesService

View file

@ -0,0 +1,15 @@
import commentCreateService from './create'
import commentDeleteService from './delete'
import commentFetchService from './fetch-info'
import commentFetchLikesService from './fetch-likes'
import commentUpdateService from './update'
const comment = {
create: commentCreateService,
delete: commentDeleteService,
fetch: commentFetchService,
fetchLikes: commentFetchLikesService,
update: commentUpdateService
}
export default comment

View file

@ -1,54 +0,0 @@
import userAuthService from './users/auth'
import userDeleteService from './users/delete'
import userFollowService from './users/follow-user'
import userFetchPostsService from './users/fetch-posts'
import userFetchInfoService from './users/fetch-info'
import userLikePostService from './users/like-post'
import userSearchService from './users/search-user'
import userSignupService from './users/signup'
import userUpdateService from './users/update'
import userUploadPictureService from './users/upload-picture'
import postCreateService from './posts/create'
import postDeleteService from './posts/delete'
import postFetchInfoService from './posts/fetch-info'
import postFetchLikesService from './posts/fetch-likes'
import postUpdateService from './posts/update'
import commentCreateService from './comments/create'
import commentDeleteService from './comments/delete'
import commentFetchService from './comments/fetch'
import commentUpdateService from './comments/update'
// User services
const user = {
auth: userAuthService,
delete: userDeleteService,
fetchInfo: userFetchInfoService,
signup: userSignupService,
update: userUpdateService,
uploadPicture: userUploadPictureService,
likePost: userLikePostService,
follow: userFollowService,
fetchPosts: userFetchPostsService,
searchUser: userSearchService
}
// Post services
const post = {
create: postCreateService,
delete: postDeleteService,
info: postFetchInfoService,
update: postUpdateService,
fetchLikes: postFetchLikesService
}
// Comment services
const comment = {
create: commentCreateService,
delete: commentDeleteService,
fetch: commentFetchService,
update: commentUpdateService
}
export { user, post, comment }

View file

@ -1,7 +1,7 @@
import prisma from '../../clients/prisma-client' import prisma from '../../clients/prisma-client'
async function postFetchLikesService (id: string): Promise<Object | Error> { async function postFetchLikesService (id: string): Promise<Object | Error> {
const post = await prisma.like.findMany({ const post = await prisma.postLike.findMany({
where: { where: {
postId: id postId: id
}, },

View file

@ -0,0 +1,15 @@
import postCreateService from './create'
import postDeleteService from './delete'
import postFetchInfoService from './fetch-info'
import postFetchLikesService from './fetch-likes'
import postUpdateService from './update'
const post = {
create: postCreateService,
delete: postDeleteService,
fetchLikes: postFetchLikesService,
info: postFetchInfoService,
update: postUpdateService
}
export default post

View file

@ -0,0 +1,27 @@
import userAuthService from './auth'
import userDeleteService from './delete'
import userFollowService from './follow-user'
import userFetchPostsService from './fetch-posts'
import userFetchInfoService from './fetch-info'
import userLikeCommentService from './like-comment'
import userLikePostService from './like-post'
import userSearchService from './search-user'
import userSignupService from './signup'
import userUpdateService from './update'
import userUploadPictureService from './upload-picture'
const user = {
auth: userAuthService,
delete: userDeleteService,
fetchInfo: userFetchInfoService,
fetchPosts: userFetchPostsService,
follow: userFollowService,
likeComment: userLikeCommentService,
likePost: userLikePostService,
searchUser: userSearchService,
signup: userSignupService,
update: userUpdateService,
uploadPicture: userUploadPictureService
}
export default user

View file

@ -0,0 +1,55 @@
import prisma from '../../clients/prisma-client'
async function userLikeCommentService (postId: string, commentId: string, userId: string): Promise<Object | Error> {
if (commentId === undefined || userId === undefined) {
return new Error('Missing fields')
}
const comment = await prisma.comments.findFirst({
where: {
id: commentId
}
})
if (comment === null) {
return new Error('Comment not found')
}
const user = await prisma.user.findFirst({
where: {
id: userId
}
})
if (user === null) {
return new Error('User not found')
}
const alreadyLiked = await prisma.commentLike.findFirst({
where: {
commentId: comment.id,
userId: user.id
}
})
if (alreadyLiked !== null) {
await prisma.commentLike.deleteMany({
where: {
commentId: comment.id,
userId: user.id
}
})
return {}
}
const like = await prisma.commentLike.create({
data: {
commentId: comment.id,
userId: user.id
}
})
return like
}
export default userLikeCommentService

View file

@ -25,7 +25,7 @@ async function userLikePostService (postId: string, userId: string): Promise<Obj
return new Error('User not found') return new Error('User not found')
} }
const alreadyLiked = await prisma.like.findFirst({ const alreadyLiked = await prisma.postLike.findFirst({
where: { where: {
postId: post.id, postId: post.id,
userId: user.id userId: user.id
@ -33,7 +33,7 @@ async function userLikePostService (postId: string, userId: string): Promise<Obj
}) })
if (alreadyLiked !== null) { if (alreadyLiked !== null) {
await prisma.like.deleteMany({ await prisma.postLike.deleteMany({
where: { where: {
postId: post.id, postId: post.id,
userId: user.id userId: user.id
@ -42,7 +42,7 @@ async function userLikePostService (postId: string, userId: string): Promise<Obj
return {} return {}
} }
const like = await prisma.like.create({ const like = await prisma.postLike.create({
data: { data: {
postId: post.id, postId: post.id,
userId: user.id userId: user.id