Added morgan and winston for logging, still need to test socket io

This commit is contained in:
Hackntosh 2023-08-05 16:51:43 -03:00
parent e11771a62b
commit 14e3990d76
57 changed files with 994 additions and 214 deletions

1
.gitignore vendored
View file

@ -8,3 +8,4 @@ client
dist dist
pnpm-lock.yaml pnpm-lock.yaml
package_backup.json package_backup.json
logs/

690
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -36,10 +36,12 @@
"@swc/jest": "^0.2.26", "@swc/jest": "^0.2.26",
"@types/bcrypt": "^5.0.0", "@types/bcrypt": "^5.0.0",
"@types/compression": "^1.7.2", "@types/compression": "^1.7.2",
"@types/cors": "^2.8.13",
"@types/dotenv": "^8.2.0", "@types/dotenv": "^8.2.0",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/jest": "^29.5.2", "@types/jest": "^29.5.2",
"@types/jsonwebtoken": "^9.0.2", "@types/jsonwebtoken": "^9.0.2",
"@types/morgan": "^1.9.4",
"@types/multer-s3": "^3.0.0", "@types/multer-s3": "^3.0.0",
"@types/node": "^20.3.1", "@types/node": "^20.3.1",
"@types/supertest": "^2.0.12", "@types/supertest": "^2.0.12",
@ -68,15 +70,19 @@
"aws-sdk": "^2.1414.0", "aws-sdk": "^2.1414.0",
"bcrypt": "^5.1.0", "bcrypt": "^5.1.0",
"compression": "^1.7.4", "compression": "^1.7.4",
"cors": "^2.8.5",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"express": "^4.18.2", "express": "^4.18.2",
"express-rate-limit": "^6.7.1", "express-rate-limit": "^6.7.1",
"ioredis": "^5.3.2", "ioredis": "^5.3.2",
"jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.0",
"morgan": "^1.10.0",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"multer-s3": "^3.0.1", "multer-s3": "^3.0.1",
"rate-limit-redis": "^3.0.2", "rate-limit-redis": "^3.0.2",
"sharp": "^0.32.3", "sharp": "^0.32.3",
"validator": "^13.9.0" "socket.io": "^4.7.2",
"validator": "^13.9.0",
"winston": "^3.10.0"
} }
} }

View file

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "socketId" TEXT;

View file

@ -22,6 +22,7 @@ model User {
postComments Comments[] postComments Comments[]
fromNotifications Notifications[] @relation("fromNotifications") fromNotifications Notifications[] @relation("fromNotifications")
toNotifications Notifications[] @relation("toNotifications") toNotifications Notifications[] @relation("toNotifications")
socketId String?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
} }

View file

@ -1,11 +1,17 @@
/* eslint-disable */ /* eslint-disable */
import * as express from 'express' import * as express from 'express'
import jwtPayload from '../interfaces/jwt' import type jwtPayload from '../interfaces/jwt'
declare global { declare global {
namespace Express { namespace Express {
interface Request { interface Request {
user: jwtPayload | undefined user: jwtPayload | undefined
} }
namespace Multer {
interface File {
location: string
key: string
}
}
} }
} }

View file

@ -1,19 +1,25 @@
import 'dotenv/config' import 'dotenv/config'
import express from 'express'
import router from './routes'
import compression from 'compression' import compression from 'compression'
import limiter from './middlewares/rate-limit' import cors from 'cors'
import express from 'express'
import limiter from 'middlewares/rate-limit'
import morganMiddleware from 'middlewares/morgan'
import router from './routes'
const app = express() const app = express()
// TODO: Create a documentation page or create the client because this is getting really big
app.use(express.json()) app.use(express.json())
app.use(express.urlencoded({ extended: true })) app.use(express.urlencoded({ extended: true }))
app.use(router) app.use(morganMiddleware)
app.use(limiter) app.use(limiter)
app.use(router)
app.use(compression({ level: 9 })) app.use(compression({ level: 9 }))
app.use(cors({
credentials: true,
origin: '*', // TODO: Add client development (and production too) url
optionsSuccessStatus: 200
}))
app.use((_req, res) => { app.use((_req, res) => {
res.status(404).json({ res.status(404).json({

View file

@ -1,9 +1,10 @@
import { S3Client } from '@aws-sdk/client-s3' import { S3Client } from '@aws-sdk/client-s3'
import logger from 'helpers/logger'
let s3: S3Client let s3: S3Client
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
console.log('Using Localstack services instead of AWS.') logger.info('Using Localstack services instead of AWS.')
s3 = new S3Client({ s3 = new S3Client({
credentials: { credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? '', accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? '',

View file

@ -1,6 +1,7 @@
import comment from 'services/comments' import comment from 'services/comments'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import { badRequest } from 'helpers/http-errors'
import handleResponse from 'helpers/handle-response'
async function commentCreateController ( async function commentCreateController (
req: Request, req: Request,
@ -19,11 +20,7 @@ async function commentCreateController (
const result = await comment.create(postId, content, id) const result = await comment.create(postId, content, id)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default commentCreateController export default commentCreateController

View file

@ -1,6 +1,7 @@
import comment from 'services/comments' import comment from 'services/comments'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import { badRequest } from 'helpers/http-errors'
import handleResponse from 'helpers/handle-response'
async function commentDeleteController ( async function commentDeleteController (
req: Request, req: Request,
@ -15,11 +16,7 @@ async function commentDeleteController (
const result = await comment.delete(commentId, id) const result = await comment.delete(commentId, id)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default commentDeleteController export default commentDeleteController

View file

@ -1,6 +1,7 @@
import comment from 'services/comments' import comment from 'services/comments'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import { badRequest } from 'helpers/http-errors'
import handleResponse from 'helpers/handle-response'
async function commentFetchController ( async function commentFetchController (
req: Request, req: Request,
@ -14,11 +15,7 @@ async function commentFetchController (
const result = await comment.fetch(commentId) const result = await comment.fetch(commentId)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default commentFetchController export default commentFetchController

View file

@ -1,6 +1,7 @@
import comment from 'services/comments' import comment from 'services/comments'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import { badRequest } from 'helpers/http-errors'
import handleResponse from 'helpers/handle-response'
async function commentFetchLikesController ( async function commentFetchLikesController (
req: Request, req: Request,
@ -14,11 +15,7 @@ async function commentFetchLikesController (
const result = await comment.fetchLikes(commentId) const result = await comment.fetchLikes(commentId)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default commentFetchLikesController export default commentFetchLikesController

View file

@ -1,6 +1,7 @@
import comment from 'services/comments' import comment from 'services/comments'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import { badRequest } from 'helpers/http-errors'
import handleResponse from 'helpers/handle-response'
async function commentUpdateController ( async function commentUpdateController (
req: Request, req: Request,
@ -19,11 +20,7 @@ async function commentUpdateController (
const result = await comment.update(content, id, commentId) const result = await comment.update(content, id, commentId)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default commentUpdateController export default commentUpdateController

View file

@ -1,6 +1,7 @@
import post from 'services/posts' import post from 'services/posts'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import { badRequest } from 'helpers/http-errors'
import handleResponse from 'helpers/handle-response'
async function postCreateController ( async function postCreateController (
req: Request, req: Request,
@ -15,11 +16,7 @@ async function postCreateController (
const result = await post.create(content, id) const result = await post.create(content, id)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default postCreateController export default postCreateController

View file

@ -1,6 +1,7 @@
import post from 'services/posts' import post from 'services/posts'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import { badRequest } from 'helpers/http-errors'
import handleResponse from 'helpers/handle-response'
async function postDeleteController ( async function postDeleteController (
req: Request, req: Request,
@ -9,13 +10,13 @@ async function postDeleteController (
const userId = req.user?.id ?? '' const userId = req.user?.id ?? ''
const postId = req.body.postId const postId = req.body.postId
const result = await post.delete(postId, userId) if (postId === undefined) {
badRequest(res, 'Missing post id'); return
if (result instanceof Error) {
badRequest(res, result.message); return
} }
res.json(result) const result = await post.delete(postId, userId)
handleResponse(res, result)
} }
export default postDeleteController export default postDeleteController

View file

@ -1,6 +1,7 @@
import post from 'services/posts' import post from 'services/posts'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import { badRequest } from 'helpers/http-errors'
import handleResponse from 'helpers/handle-response'
async function postFetchInfoController ( async function postFetchInfoController (
req: Request, req: Request,
@ -14,11 +15,7 @@ async function postFetchInfoController (
const result = await post.fetch(id) const result = await post.fetch(id)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default postFetchInfoController export default postFetchInfoController

View file

@ -1,6 +1,7 @@
import post from 'services/posts' import post from 'services/posts'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import { badRequest } from 'helpers/http-errors'
import handleResponse from 'helpers/handle-response'
async function postFetchLikesController ( async function postFetchLikesController (
req: Request, req: Request,
@ -14,11 +15,7 @@ async function postFetchLikesController (
const result = await post.fetchLikes(id) const result = await post.fetchLikes(id)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default postFetchLikesController export default postFetchLikesController

View file

@ -1,6 +1,6 @@
import post from 'services/posts' import post from 'services/posts'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import handleResponse from 'helpers/handle-response'
async function postUpdateController ( async function postUpdateController (
req: Request, req: Request,
@ -11,11 +11,7 @@ async function postUpdateController (
const result = await post.update(postId, content, userId) const result = await post.update(postId, content, userId)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default postUpdateController export default postUpdateController

View file

@ -1,17 +1,13 @@
import user from 'services/users' import user from 'services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import handleResponse from 'helpers/handle-response'
async function userAuthController (req: Request, res: Response): Promise<void> { async function userAuthController (req: Request, res: Response): Promise<void> {
const { email, password } = req.body const { email, password } = req.body
const result = await user.auth({ email, password }) const result = await user.auth({ email, password })
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default userAuthController export default userAuthController

View file

@ -1,6 +1,6 @@
import user from 'services/users' import user from 'services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import handleResponse from 'helpers/handle-response'
async function userDeleteController ( async function userDeleteController (
req: Request, req: Request,
@ -9,11 +9,7 @@ async function userDeleteController (
const userId = req.user?.id ?? '' const userId = req.user?.id ?? ''
const result = await user.delete(userId) const result = await user.delete(userId)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default userDeleteController export default userDeleteController

View file

@ -1,6 +1,7 @@
import user from 'services/users' 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 'helpers/http-errors'
import handleResponse from 'helpers/handle-response'
async function userFetchInfoController ( async function userFetchInfoController (
req: Request, req: Request,
@ -14,11 +15,7 @@ async function userFetchInfoController (
const result = await user.fetchInfo(username.toLowerCase()) const result = await user.fetchInfo(username.toLowerCase())
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default userFetchInfoController export default userFetchInfoController

View file

@ -1,6 +1,7 @@
import user from 'services/users' 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 'helpers/http-errors'
import handleResponse from 'helpers/handle-response'
async function userFetchPostsController ( async function userFetchPostsController (
req: Request, req: Request,
@ -14,7 +15,7 @@ async function userFetchPostsController (
const result = await user.fetchPosts(username) const result = await user.fetchPosts(username)
res.json(result) handleResponse(res, result)
} }
export default userFetchPostsController export default userFetchPostsController

View file

@ -1,6 +1,6 @@
import user from 'services/users' import user from 'services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import handleResponse from 'helpers/handle-response'
async function userFollowController ( async function userFollowController (
req: Request, req: Request,
@ -11,11 +11,7 @@ async function userFollowController (
const result = await user.follow(userId, userToFollow) const result = await user.follow(userId, userToFollow)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default userFollowController export default userFollowController

View file

@ -1,6 +1,6 @@
import user from 'services/users' import user from 'services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import handleResponse from 'helpers/handle-response'
async function userLikeCommentController ( async function userLikeCommentController (
req: Request, req: Request,
@ -11,11 +11,7 @@ async function userLikeCommentController (
const result = await user.likeComment(commentId, userId) const result = await user.likeComment(commentId, userId)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default userLikeCommentController export default userLikeCommentController

View file

@ -1,6 +1,6 @@
import user from 'services/users' import user from 'services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import handleResponse from 'helpers/handle-response'
async function userLikePostController ( async function userLikePostController (
req: Request, req: Request,
@ -11,11 +11,7 @@ async function userLikePostController (
const result = await user.likePost(postId, userId) const result = await user.likePost(postId, userId)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default userLikePostController export default userLikePostController

View file

@ -1,6 +1,6 @@
import user from 'services/users' 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 'helpers/http-errors'
async function userSearchController ( async function userSearchController (
req: Request, req: Request,

View file

@ -1,6 +1,6 @@
import user from 'services/users' import user from 'services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import handleResponse from 'helpers/handle-response'
async function userSignupController ( async function userSignupController (
req: Request, req: Request,
@ -10,11 +10,7 @@ async function userSignupController (
const result = await user.signup({ username, email, password }) const result = await user.signup({ username, email, password })
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default userSignupController export default userSignupController

View file

@ -1,6 +1,6 @@
import user from 'services/users' import user from 'services/users'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
import { badRequest } from 'lib/http-errors' import handleResponse from 'helpers/handle-response'
async function userUpdateController ( async function userUpdateController (
req: Request, req: Request,
@ -11,11 +11,7 @@ async function userUpdateController (
const result = await user.update({ id, email, displayName, username }) const result = await user.update({ id, email, displayName, username })
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default userUpdateController export default userUpdateController

View file

@ -1,9 +1,8 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/restrict-template-expressions */
import user from 'services/users' 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 'helpers/http-errors'
import handleResponse from 'helpers/handle-response'
let url
async function userUploadPictureController ( async function userUploadPictureController (
req: Request, req: Request,
@ -15,21 +14,17 @@ async function userUploadPictureController (
const userId = req.user?.id ?? '' const userId = req.user?.id ?? ''
let url: string
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
// @ts-expect-error property `key` doesn't exists in @types/express
url = `http://${process.env.AWS_BUCKET ?? ''}.s3.localhost.localstack.cloud:4566/${req.file.key}` url = `http://${process.env.AWS_BUCKET ?? ''}.s3.localhost.localstack.cloud:4566/${req.file.key}`
} else { } else {
// @ts-expect-error property `location` doesn't exists in @types/express
url = req.file.location url = req.file.location
} }
const result = await user.uploadPicture(userId, url) const result = await user.uploadPicture(userId, url)
if (result instanceof Error) { handleResponse(res, result)
badRequest(res, result.message); return
}
res.json(result)
} }
export default userUploadPictureController export default userUploadPictureController

View file

@ -0,0 +1,10 @@
import { type Response } from 'express'
import { badRequest } from './http-errors'
export default function handleResponse (res: Response, result: any): void {
if (result instanceof Error) {
badRequest(res, result.message)
} else {
res.json(result)
}
}

52
src/helpers/logger.ts Normal file
View file

@ -0,0 +1,52 @@
import winston from 'winston'
const levels = {
error: 0,
warn: 1,
info: 2,
http: 3,
debug: 4
}
const level = (): string => {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/prefer-nullish-coalescing
const env = process.env.NODE_ENV || 'development'
const isDevelopment = env === 'development'
return isDevelopment ? 'debug' : 'warn'
}
const colors = {
error: 'red',
warn: 'yellow',
info: 'green',
http: 'magenta',
debug: 'white'
}
winston.addColors(colors)
const format = winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }),
winston.format.colorize({ all: true }),
winston.format.printf(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
(info) => `${info.timestamp} ${info.level}: ${info.message}`
)
)
const transports = [
new winston.transports.Console(),
new winston.transports.File({
filename: 'logs/error.log',
level: 'error'
})
]
const logger = winston.createLogger({
level: level(),
levels,
format,
transports
})
export default logger

View file

@ -0,0 +1,47 @@
import { type NotificationType } from '@prisma/client'
import prisma from 'clients/prisma-client'
export async function createNotification (
fromUserId: string,
toUserId: string,
content: string,
type: NotificationType
): Promise<Record<never, never> | Error> {
try {
await prisma.notifications.create({
data: {
type,
fromUserId,
toUserId,
content
},
include: {
fromUser: {
select: {
id: true,
displayName: true,
username: true,
profileImage: true
}
}
}
})
return {}
} catch (_) {
return new Error('Error while creating notification')
}
}
export async function countNotifications (toUserId: string): Promise<number | Error> {
try {
const count = await prisma.notifications.count({
where: {
toUserId
}
})
return count
} catch (_) {
return new Error('Error while counting user notifications')
}
}

View file

@ -1,10 +1,13 @@
interface userPayload { interface User {
id?: string id?: string
displayName?: string | null displayName?: string | null
username?: string username?: string
email?: string email?: string
password?: string password?: string
profileImage?: string | null
createdAt?: Date
token?: string token?: string
socketId?: string
} }
export default userPayload export default User

View file

@ -1,29 +0,0 @@
import { type NotificationType } from '@prisma/client'
import prisma from 'clients/prisma-client'
export default async function createNotification (
fromUserId: string,
toUserId: string,
content: string,
type: NotificationType
): Promise<Record<never, never> | Error> {
await prisma.notifications.create({
data: {
type,
fromUserId,
toUserId,
content
},
include: {
fromUser: {
select: {
id: true,
displayName: true,
username: true,
profileImage: true
}
}
}
})
return {}
}

View file

@ -2,7 +2,7 @@ import { verify } from 'jsonwebtoken'
import prisma from 'clients/prisma-client' import prisma from 'clients/prisma-client'
import type { Response, Request, NextFunction } from 'express' import type { Response, Request, NextFunction } from 'express'
import type jwtPayload from 'interfaces/jwt' import type jwtPayload from 'interfaces/jwt'
import { unauthorized } from 'lib/http-errors' import { unauthorized } from 'helpers/http-errors'
async function authenticated ( async function authenticated (
req: Request, req: Request,

13
src/middlewares/morgan.ts Normal file
View file

@ -0,0 +1,13 @@
import morgan, { type StreamOptions } from 'morgan'
import logger from 'helpers/logger'
const stream: StreamOptions = {
write: (message) => logger.http(message)
}
const morganMiddleware = morgan(
':method :url :status - :response-time ms',
{ stream }
)
export default morganMiddleware

View file

@ -1,11 +1,12 @@
import rateLimit from 'express-rate-limit' import rateLimit from 'express-rate-limit'
import RedisStore from 'rate-limit-redis' import RedisStore from 'rate-limit-redis'
import redis from 'clients/redis-client' import redis from 'clients/redis-client'
import logger from 'helpers/logger'
let maxConnections let maxConnections
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
console.log('Detected Development environment, disabling rate limiter.') logger.info('Development environment detected. Rate limit is now disabled.')
maxConnections = 0 maxConnections = 0
} else { } else {
maxConnections = 5 maxConnections = 5

View file

@ -1,10 +1,10 @@
import type { Response, Request, NextFunction } from 'express' import type { Response, Request, NextFunction } from 'express'
import multer from 'multer' import multer from 'multer'
import multerConfig from 'config/multer' import multerConfig from 'config/multer'
import compressImage from 'lib/compress-image' import compressImage from 'helpers/compress-image'
import { badRequest } from 'lib/http-errors' import { badRequest } from 'helpers/http-errors'
function uploadImage (req: Request, res: Response, next: NextFunction) { function uploadImage (req: Request, res: Response, next: NextFunction): void {
const upload = multer(multerConfig).single('image') const upload = multer(multerConfig).single('image')
upload(req, res, async (cb: multer.MulterError | Error | any) => { upload(req, res, async (cb: multer.MulterError | Error | any) => {
@ -23,7 +23,6 @@ function uploadImage (req: Request, res: Response, next: NextFunction) {
badRequest(res, 'Expected file'); return badRequest(res, 'Expected file'); return
} }
// @ts-expect-error property `key` does not exists in types
await compressImage(req.file?.key, req.body.isProfilePicture) await compressImage(req.file?.key, req.body.isProfilePicture)
next() next()

View file

@ -1,9 +1,9 @@
import { Router } from 'express' import { Router } from 'express'
// Routers // Routers
import usersRouter from './controllers/users-router' import usersRouter from 'controllers/users-router'
import postsRouter from './controllers/posts-router' import postsRouter from 'controllers/posts-router'
import commentsRouter from './controllers/comments-router' import commentsRouter from 'controllers/comments-router'
const router = Router() const router = Router()

View file

@ -1,5 +1,17 @@
import app from './app' import app from './app'
import { createServer } from 'http'
import logger from 'helpers/logger'
import createSocketIOInstance from 'socket'
app.listen(process.env.SERVER_PORT, () => { const server = createServer(app)
console.log(`Server is running @ ${process.env.SERVER_PORT ?? ''}`) const io = createSocketIOInstance(server)
app.use((req, res, next) => {
// @ts-expect-error TODO: add io type
req.io = io
next()
})
server.listen(process.env.SERVER_PORT, () => {
logger.info(`Server is running @ ${process.env.SERVER_PORT ?? ''}`)
}) })

View file

@ -1,12 +1,12 @@
import * as bcrypt from 'bcrypt' import * as bcrypt from 'bcrypt'
import jsonwebtoken from 'jsonwebtoken' import jsonwebtoken from 'jsonwebtoken'
import prisma from 'clients/prisma-client' import prisma from 'clients/prisma-client'
import type userPayload from 'interfaces/user' import type User from 'interfaces/user'
async function userAuthService ({ async function userAuthService ({
email, email,
password password
}: userPayload): Promise<Record<string, unknown> | Error> { }: User): Promise<Record<string, unknown> | Error> {
const user = await prisma.user.findFirst({ const user = await prisma.user.findFirst({
where: { where: {
email email

View file

@ -1,7 +1,7 @@
import * as bcrypt from 'bcrypt' import * as bcrypt from 'bcrypt'
import validator from 'validator' import validator from 'validator'
import prisma from 'clients/prisma-client' import prisma from 'clients/prisma-client'
import type userPayload from 'interfaces/user' import type User from 'interfaces/user'
const passwordRegex = /^(?=.*[0-9])(?=.*[!@#$%^&*_])[a-zA-Z0-9!@#$%^&*_]{8,}$/ const passwordRegex = /^(?=.*[0-9])(?=.*[!@#$%^&*_])[a-zA-Z0-9!@#$%^&*_]{8,}$/
const usernameRegex = /^[a-zA-Z0-9_.]{5,15}$/ const usernameRegex = /^[a-zA-Z0-9_.]{5,15}$/
@ -10,7 +10,7 @@ async function userSignupService ({
username, username,
email, email,
password password
}: userPayload): Promise<Record<string, unknown> | Error> { }: User): Promise<Record<string, unknown> | Error> {
if (username === undefined || email === undefined || password === undefined) { if (username === undefined || email === undefined || password === undefined) {
return new Error('Missing fields') return new Error('Missing fields')
} }

View file

@ -1,4 +1,4 @@
import type userPayload from 'interfaces/user' import type User from 'interfaces/user'
import prisma from 'clients/prisma-client' import prisma from 'clients/prisma-client'
async function userUpdateService ({ async function userUpdateService ({
@ -6,7 +6,7 @@ async function userUpdateService ({
email, email,
displayName, displayName,
username username
}: userPayload): Promise<Record<string, unknown> | Error> { }: User): Promise<Record<string, unknown> | Error> {
const user = await prisma.user.findFirst({ where: { id } }) const user = await prisma.user.findFirst({ where: { id } })
if (user === null) { if (user === null) {

44
src/socket/index.ts Normal file
View file

@ -0,0 +1,44 @@
import prisma from 'clients/prisma-client'
import logger from 'helpers/logger'
import { Server } from 'socket.io'
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export default function createSocketIOInstance (httpServer: any) {
const io = new Server(httpServer, {
cors: {
origin: process.env.CORS_ORIGIN ?? '*'
}
})
io.use(async (socket, next) => {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!socket.handshake.auth?.id) {
const error = new Error()
error.message = 'Unauthorized'
next(error)
}
await prisma.user.update({
where: {
id: socket.handshake.auth.id
},
data: {
socketId: socket.id
}
})
next()
})
/*
const handleConnection = (socket) => {
// TODO
}
*/
io.on('connection', (_) => {
logger.info('Placeholder')
})
return io
}

View file

@ -2,9 +2,9 @@ import app from '../../app'
import request from 'supertest' import request from 'supertest'
import signUpNewUser from '../utils/create-user' import signUpNewUser from '../utils/create-user'
import deleteUser from '../utils/delete-user' import deleteUser from '../utils/delete-user'
import type userPayload from '../../interfaces/user' import type User from '../../interfaces/user'
let user: userPayload let user: User
describe('POST /post/create', () => { describe('POST /post/create', () => {
beforeAll(async () => { beforeAll(async () => {

View file

@ -2,9 +2,9 @@ import app from '../../app'
import request from 'supertest' import request from 'supertest'
import signUpNewUser from '../utils/create-user' import signUpNewUser from '../utils/create-user'
import deleteUser from '../utils/delete-user' import deleteUser from '../utils/delete-user'
import type userPayload from '../../interfaces/user' import type User from '../../interfaces/user'
let user: userPayload let user: User
describe('DELETE /post/delete', () => { describe('DELETE /post/delete', () => {
beforeAll(async () => { beforeAll(async () => {

View file

@ -2,11 +2,11 @@ import app from '../../app'
import request from 'supertest' import request from 'supertest'
import signUpNewUser from '../utils/create-user' import signUpNewUser from '../utils/create-user'
import deleteUser from '../utils/delete-user' import deleteUser from '../utils/delete-user'
import type userPayload from '../../interfaces/user' import type User from '../../interfaces/user'
let postId: string let postId: string
let user: userPayload let user: User
describe('POST /post/info', () => { describe('POST /post/info', () => {
beforeAll(async () => { beforeAll(async () => {

View file

@ -2,9 +2,9 @@ import app from '../../app'
import request from 'supertest' import request from 'supertest'
import signUpNewUser from '../utils/create-user' import signUpNewUser from '../utils/create-user'
import deleteUser from '../utils/delete-user' import deleteUser from '../utils/delete-user'
import type userPayload from '../../interfaces/user' import type User from '../../interfaces/user'
let user: userPayload let user: User
describe('PUT /post/update', () => { describe('PUT /post/update', () => {
beforeAll(async () => { beforeAll(async () => {

View file

@ -2,9 +2,9 @@ import request from 'supertest'
import app from '../../app' import app from '../../app'
import deleteUser from '../utils/delete-user' import deleteUser from '../utils/delete-user'
import signUpNewUser from '../utils/create-user' import signUpNewUser from '../utils/create-user'
import type userPayload from '../../interfaces/user' import type User from '../../interfaces/user'
let user: userPayload let user: User
describe('POST /user/auth', () => { describe('POST /user/auth', () => {
beforeAll(async () => { beforeAll(async () => {

View file

@ -1,9 +1,9 @@
import app from '../../app' import app from '../../app'
import request from 'supertest' import request from 'supertest'
import signUpNewUser from '../utils/create-user' import signUpNewUser from '../utils/create-user'
import type userPayload from '../../interfaces/user' import type User from '../../interfaces/user'
let user: userPayload let user: User
describe('DELETE /user/delete', () => { describe('DELETE /user/delete', () => {
beforeAll(async () => { beforeAll(async () => {

View file

@ -2,9 +2,9 @@ import app from '../../app'
import request from 'supertest' import request from 'supertest'
import deleteUser from '../utils/delete-user' import deleteUser from '../utils/delete-user'
import signUpNewUser from '../utils/create-user' import signUpNewUser from '../utils/create-user'
import type userPayload from '../../interfaces/user' import type User from '../../interfaces/user'
let user: userPayload let user: User
describe('POST /user/info', () => { describe('POST /user/info', () => {
beforeAll(async () => { beforeAll(async () => {

View file

@ -2,9 +2,9 @@ import request from 'supertest'
import app from '../../app' import app from '../../app'
import deleteUser from '../utils/delete-user' import deleteUser from '../utils/delete-user'
import signUpNewUser from '../utils/create-user' import signUpNewUser from '../utils/create-user'
import type userPayload from '../../interfaces/user' import type User from '../../interfaces/user'
let user: userPayload let user: User
describe('POST /user/signup', () => { describe('POST /user/signup', () => {
beforeAll(async () => { beforeAll(async () => {

View file

@ -2,9 +2,9 @@ import request from 'supertest'
import app from '../../app' import app from '../../app'
import signUpNewUser from '../utils/create-user' import signUpNewUser from '../utils/create-user'
import deleteUser from '../utils/delete-user' import deleteUser from '../utils/delete-user'
import type userPayload from '../../interfaces/user' import type User from '../../interfaces/user'
let user: userPayload let user: User
describe('PUT /user/update', () => { describe('PUT /user/update', () => {
beforeAll(async () => { beforeAll(async () => {

View file

@ -1,9 +1,9 @@
import app from '../../app' import app from '../../app'
import request from 'supertest' import request from 'supertest'
import { faker } from '@faker-js/faker' import { faker } from '@faker-js/faker'
import type userPayload from '../../interfaces/user' import type User from '../../interfaces/user'
async function signUpNewUser (): Promise<userPayload> { async function signUpNewUser (): Promise<User> {
// To avoid conflicts with existing usernames or emails // To avoid conflicts with existing usernames or emails
const username = faker.internet.userName({ lastName: 'doe' }).toLowerCase() const username = faker.internet.userName({ lastName: 'doe' }).toLowerCase()
const email = faker.internet.email() const email = faker.internet.email()

View file

@ -27,12 +27,11 @@
], ],
"services/*": [ "services/*": [
"services/*" "services/*"
] ],
}, },
"typeRoots": [ "typeRoots": [
"src/@types/express.d.ts", "./src/@types",
"src/@types/global.d.ts", "./node_modules/@types"
"node_modules/@types"
], ],
/* Emit */ /* Emit */
"outDir": "./dist", "outDir": "./dist",