Created new routes, changed file names, new tables

This commit is contained in:
Hackntosh 2023-07-25 18:25:43 -03:00
parent 11d74d846b
commit d428991539
31 changed files with 550 additions and 46 deletions

View file

@ -0,0 +1,46 @@
-- DropForeignKey
ALTER TABLE "Follows" DROP CONSTRAINT "Follows_followerId_fkey";
-- DropForeignKey
ALTER TABLE "Follows" DROP CONSTRAINT "Follows_followingId_fkey";
-- DropForeignKey
ALTER TABLE "Like" DROP CONSTRAINT "Like_postId_fkey";
-- DropForeignKey
ALTER TABLE "Like" DROP CONSTRAINT "Like_userId_fkey";
-- DropForeignKey
ALTER TABLE "Post" DROP CONSTRAINT "Post_authorId_fkey";
-- CreateTable
CREATE TABLE "Comments" (
"id" TEXT NOT NULL,
"content" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"postId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Comments_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "Post" ADD CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Like" ADD CONSTRAINT "Like_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Like" ADD CONSTRAINT "Like_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Follows" ADD CONSTRAINT "Follows_followerId_fkey" FOREIGN KEY ("followerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Follows" ADD CONSTRAINT "Follows_followingId_fkey" FOREIGN KEY ("followingId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Comments" ADD CONSTRAINT "Comments_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Comments" ADD CONSTRAINT "Comments_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View file

@ -0,0 +1,20 @@
-- CreateEnum
CREATE TYPE "NotificationType" AS ENUM ('WARNING', 'FRIEND');
-- CreateTable
CREATE TABLE "Notifications" (
"id" TEXT NOT NULL,
"type" "NotificationType" NOT NULL,
"content" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"fromUserId" TEXT NOT NULL,
"toUserId" TEXT NOT NULL,
CONSTRAINT "Notifications_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "Notifications" ADD CONSTRAINT "Notifications_fromUserId_fkey" FOREIGN KEY ("fromUserId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Notifications" ADD CONSTRAINT "Notifications_toUserId_fkey" FOREIGN KEY ("toUserId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View file

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Comments" ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;

View file

@ -8,25 +8,29 @@ datasource db {
}
model User {
id String @id @default(uuid())
displayName String?
username String @unique
email String @unique
password String
posts Post[]
profileImage String?
likedPosts Like[]
followers Follows[] @relation("following")
following Follows[] @relation("follower")
createdAt DateTime @default(now())
id String @id @default(uuid())
displayName String?
username String @unique
email String @unique
password String
posts Post[]
profileImage String?
likedPosts Like[]
followers Follows[] @relation("following")
following Follows[] @relation("follower")
postComments Comments[]
fromNotifications Notifications[] @relation("fromNotifications")
toNotifications Notifications[] @relation("toNotifications")
createdAt DateTime @default(now())
}
model Post {
id String @id @default(uuid())
content String
authorId String
author User @relation(fields: [authorId], references: [id])
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
likes Like[]
comments Comments[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
@ -34,16 +38,44 @@ model Post {
model Like {
id String @id @default(uuid())
postId String
post Post @relation(fields: [postId], references: [id])
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id])
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
}
model Follows {
follower User @relation("follower", fields: [followerId], references: [id])
follower User @relation("follower", fields: [followerId], references: [id], onDelete: Cascade)
followerId String
following User @relation("following", fields: [followingId], references: [id])
following User @relation("following", fields: [followingId], references: [id], onDelete: Cascade)
followingId String
@@id([followerId, followingId])
}
model Comments {
id String @id @default(uuid())
content String
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
postId String
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt @default(now())
}
model Notifications {
id String @id @default(uuid())
type NotificationType
content String
createdAt DateTime @default(now())
fromUserId String
fromUser User? @relation(name: "fromNotifications", fields: [fromUserId], references: [id], onDelete: Cascade)
toUserId String
toUser User? @relation(name: "toNotifications", fields: [toUserId], references: [id], onDelete: Cascade)
}
enum NotificationType {
WARNING
FRIEND
}

View file

@ -7,8 +7,7 @@ import limiter from './middlewares/rate-limit'
const app = express()
// TODO: find a way to declare global variables for better refactor on test
// TODO: must pass userMock as a global
// TODO: Create a documentation page or create the client because this is getting really big
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

View file

@ -0,0 +1,22 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
import { Router } from 'express'
// Controllers
import commentCreateController from './comments/create'
import commentDeleteController from './comments/delete'
import commentFetchController from './comments/fetch'
import commentUpdateController from './comments/update'
// Middlewares
import ensureAuthenticated from '../middlewares/ensure-authenticated'
const commentsRouter = Router()
// Posts related
commentsRouter.post('/create', ensureAuthenticated, commentCreateController)
commentsRouter.post('/delete', ensureAuthenticated, commentDeleteController)
commentsRouter.get('/info', commentFetchController)
commentsRouter.put('/update', ensureAuthenticated, commentUpdateController)
export default commentsRouter

View file

@ -0,0 +1,26 @@
import { comment } from '../../services'
import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors'
async function commentCreateController (req: Request, res: Response): Promise<void> {
const { content, postId } = req.body
const id = req.user?.id ?? ''
if (postId === undefined) {
return badRequest(res, 'Expected post id')
}
if (content === undefined) {
return badRequest(res, 'Expected comment content')
}
const result = await comment.create(postId, content, id)
if (result instanceof Error) {
return badRequest(res, result.message)
}
res.json(result)
}
export default commentCreateController

View file

@ -0,0 +1,22 @@
import { comment } from '../../services'
import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors'
async function commentDeleteController (req: Request, res: Response): Promise<void> {
const { commentId } = req.body
const id = req.user?.id ?? ''
if (commentId === undefined) {
return badRequest(res, 'Expected post id')
}
const result = await comment.delete(commentId, id)
if (result instanceof Error) {
return badRequest(res, result.message)
}
res.json(result)
}
export default commentDeleteController

View file

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

View file

@ -0,0 +1,26 @@
import { comment } from '../../services'
import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors'
async function commentUpdateController (req: Request, res: Response): Promise<void> {
const { commentId, content } = req.body
const id = req.user?.id ?? ''
if (commentId === undefined) {
return badRequest(res, 'Expected comment content')
}
if (content === undefined) {
return badRequest(res, 'Expected content to update')
}
const result = await comment.update(content, id, commentId)
if (result instanceof Error) {
return badRequest(res, result.message)
}
res.json(result)
}
export default commentUpdateController

View file

@ -5,18 +5,20 @@ import { Router } from 'express'
// Controllers
import postCreateController from './posts/create'
import postDeleteController from './posts/delete'
import postInfoController from './posts/get-info'
import postFetchInfoController from './posts/fetch-info'
import postUpdateController from './posts/update'
// Middlewares
import ensureAuthenticated from '../middlewares/ensure-authenticated'
import postFetchLikesController from './posts/fetch-likes'
const postsRouter = Router()
// Posts related
postsRouter.post('/create', ensureAuthenticated, postCreateController)
postsRouter.post('/delete', ensureAuthenticated, postDeleteController)
postsRouter.get('/info', postInfoController)
postsRouter.get('/info', postFetchInfoController)
postsRouter.put('/update', ensureAuthenticated, postUpdateController)
postsRouter.get('/fetch-likes', postFetchLikesController)
export default postsRouter

View file

@ -6,6 +6,10 @@ async function postCreateController (req: Request, res: Response): Promise<void>
const { content } = req.body
const id: string = req.user?.id ?? ''
if (content === undefined) {
return badRequest(res, 'Expected post content')
}
const result = await post.create(content, id)
if (result instanceof Error) {

View file

@ -2,7 +2,7 @@ import { post } from '../../services/index'
import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors'
async function postInfoController (req: Request, res: Response): Promise<void> {
async function postFetchInfoController (req: Request, res: Response): Promise<void> {
const id = req.query.id as string
if (id === undefined) {
@ -18,4 +18,4 @@ async function postInfoController (req: Request, res: Response): Promise<void> {
res.json(result)
}
export default postInfoController
export default postFetchInfoController

View file

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

View file

@ -6,8 +6,10 @@ import { Router } from 'express'
import userAuthController from './users/auth'
import userDeleteController from './users/delete'
import userFollowController from './users/follow-user'
import userInfoController from './users/get-info'
import userFetchInfoController from './users/fetch-info'
import userFetchPostsController from './users/fetch-posts'
import userLikePostController from './users/like-post'
import userSearchController from './users/search-user'
import userSignupController from './users/signup'
import userUpdateController from './users/update'
import userUploadPictureController from './users/upload-picture'
@ -21,11 +23,13 @@ const usersRouter = Router()
// Users related
usersRouter.post('/auth', userAuthController)
usersRouter.post('/delete', ensureAuthenticated, userDeleteController)
usersRouter.get('/info', userInfoController)
usersRouter.get('/info', userFetchInfoController)
usersRouter.post('/signup', userSignupController)
usersRouter.put('/update', ensureAuthenticated, userUpdateController)
usersRouter.put('/profile-picture/upload', ensureAuthenticated, uploadFile, userUploadPictureController)
usersRouter.post('/like-post', ensureAuthenticated, userLikePostController)
usersRouter.post('/follow-user', ensureAuthenticated, userFollowController)
usersRouter.get('/fetch-posts', userFetchPostsController)
usersRouter.get('/search', userSearchController)
export default usersRouter

View file

@ -2,14 +2,14 @@ import { user } from '../../services'
import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors'
async function userInfoController (req: Request, res: Response): Promise<void> {
async function userFetchInfoController (req: Request, res: Response): Promise<void> {
const username = req.query.u as string
if (username === undefined) {
return badRequest(res, 'Missing username')
}
const result = await user.info(username.toLowerCase())
const result = await user.fetchInfo(username.toLowerCase())
if (result instanceof Error) {
return badRequest(res, result.message)
@ -18,4 +18,4 @@ async function userInfoController (req: Request, res: Response): Promise<void> {
res.json(result)
}
export default userInfoController
export default userFetchInfoController

View file

@ -0,0 +1,17 @@
import { user } from '../../services'
import type { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors'
async function userFetchPostsController (req: Request, res: Response): Promise<void> {
const username = req.query.u as string
if (username === undefined) {
return badRequest(res, 'Missing username')
}
const result = await user.fetchPosts(username)
res.json(result)
}
export default userFetchPostsController

View file

@ -0,0 +1,17 @@
import { user } from '../../services'
import { Request, Response } from 'express'
import { badRequest } from '../../lib/http-errors'
async function userSearchController (req: Request, res: Response): Promise<void> {
const username = req.query.u as string
if (username === undefined) {
return badRequest(res, 'Missing username')
}
const result = await user.searchUser(username)
res.json(result)
}
export default userSearchController

View file

@ -3,10 +3,12 @@ import { Router } from 'express'
// Routers
import usersRouter from './controllers/users-router'
import postsRouter from './controllers/posts-router'
import commentsRouter from './controllers/comments-router'
const router = Router()
router.use('/user', usersRouter)
router.use('/post', postsRouter)
router.use('/comment', commentsRouter)
export default router

View file

@ -0,0 +1,35 @@
import prisma from '../../clients/prisma-client'
async function commentCreateService (postId: string, content: string, authorId: string): Promise<Object | Error> {
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: authorId
}
})
if (user === null) {
return new Error('User not found')
}
const comment = await prisma.comments.create({
data: {
content,
postId,
userId: authorId
}
})
return comment
}
export default commentCreateService

View file

@ -0,0 +1,35 @@
import prisma from '../../clients/prisma-client'
async function commentDeleteService (commentId: string, authorId: string): Promise<Object | Error> {
const user = await prisma.user.findFirst({
where: {
id: authorId
}
})
if (user === null) {
return new Error('User not found')
}
const comment = await prisma.comments.findFirst({
where: {
id: commentId,
userId: user.id
}
})
if (comment === null) {
return new Error('Comment not found')
}
await prisma.comments.deleteMany({
where: {
id: comment.id,
userId: user.id
}
})
return {}
}
export default commentDeleteService

View file

@ -0,0 +1,31 @@
import prisma from '../../clients/prisma-client'
async function commentFetchService (commentId: string): Promise<Object | Error> {
const comment = await prisma.comments.findFirst({
where: {
id: commentId
},
select: {
id: true,
content: true,
createdAt: true,
updatedAt: true,
user: {
select: {
id: true,
displayName: true,
username: true,
profileImage: true
}
}
}
})
if (comment === null) {
return new Error('Comment not found')
}
return comment
}
export default commentFetchService

View file

@ -0,0 +1,40 @@
import prisma from '../../clients/prisma-client'
async function commentUpdateService (content: string, authorId: string, commentId: string): Promise<Object | Error> {
const comment = await prisma.comments.findFirst({
where: {
id: commentId,
userId: authorId
}
})
if (comment === null) {
return new Error('Comment does not exists')
}
const updatedComment = await prisma.comments.update({
where: {
id: comment.id,
userId: authorId
},
data: {
content
},
select: {
id: true,
content: true,
createdAt: true,
updatedAt: true,
user: {
select: {
displayName: true,
username: true,
profileImage: true
}
}
}
})
return updatedComment
}
export default commentUpdateService

View file

@ -1,35 +1,54 @@
import userAuthService from './users/auth'
import userDeleteService from './users/delete'
import userFollowService from './users/follow-user'
import userInfoService from './users/get-info'
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 postInfoService from './posts/get-info'
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,
info: userInfoService,
fetchInfo: userFetchInfoService,
signup: userSignupService,
update: userUpdateService,
uploadPicture: userUploadPictureService,
likePost: userLikePostService,
follow: userFollowService
follow: userFollowService,
fetchPosts: userFetchPostsService,
searchUser: userSearchService
}
// Post services
const post = {
create: postCreateService,
delete: postDeleteService,
info: postInfoService,
update: postUpdateService
info: postFetchInfoService,
update: postUpdateService,
fetchLikes: postFetchLikesService
}
export { user, post }
// Comment services
const comment = {
create: commentCreateService,
delete: commentDeleteService,
fetch: commentFetchService,
update: commentUpdateService
}
export { user, post, comment }

View file

@ -1,6 +1,6 @@
import prisma from '../../clients/prisma-client'
async function postInfoService (id: string): Promise<Object | Error> {
async function postFetchInfoService (id: string): Promise<Object | Error> {
const post = await prisma.post.findFirst({
where: {
id
@ -10,13 +10,13 @@ async function postInfoService (id: string): Promise<Object | Error> {
content: true,
createdAt: true,
updatedAt: true,
likes: true,
author: {
select: {
displayName: true,
username: true
}
},
likes: true
}
}
})
@ -27,4 +27,4 @@ async function postInfoService (id: string): Promise<Object | Error> {
return post
}
export default postInfoService
export default postFetchInfoService

View file

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

View file

@ -11,12 +11,6 @@ async function userDeleteService (userId: string): Promise<Object | Error> {
return new Error('Forbidden')
}
await prisma.post.deleteMany({
where: {
authorId: user.id
}
})
await prisma.user.deleteMany({
where: {
id: userId

View file

@ -1,6 +1,6 @@
import prisma from '../../clients/prisma-client'
async function userInfoService (username: string): Promise<Object> {
async function userFetchInfoService (username: string): Promise<Object | Error> {
const user = await prisma.user.findFirst({
where: {
username
@ -33,4 +33,4 @@ async function userInfoService (username: string): Promise<Object> {
return user
}
export default userInfoService
export default userFetchInfoService

View file

@ -0,0 +1,21 @@
import prisma from '../../clients/prisma-client'
async function userFetchPostsService (username: string): Promise<Object | Error> {
const posts = await prisma.post.findMany({
where: {
author: {
username
}
},
select: {
_count: true,
id: true,
content: true,
createdAt: true,
updatedAt: true
}
})
return posts
}
export default userFetchPostsService

View file

@ -0,0 +1,20 @@
import prisma from '../../clients/prisma-client'
async function userSearchService (username: string): Promise<Object | Error> {
const users = await prisma.user.findMany({
where: {
username: {
contains: username
}
},
select: {
displayName: true,
username: true,
profileImage: true
},
take: 10
})
return users
}
export default userSearchService