mirror of
https://github.com/hknsh/project-knedita.git
synced 2024-11-28 17:41:15 +00:00
Added morgan and winston for logging, still need to test socket io
This commit is contained in:
parent
e11771a62b
commit
14e3990d76
57 changed files with 994 additions and 214 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,3 +8,4 @@ client
|
|||
dist
|
||||
pnpm-lock.yaml
|
||||
package_backup.json
|
||||
logs/
|
690
package-lock.json
generated
690
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -36,10 +36,12 @@
|
|||
"@swc/jest": "^0.2.26",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/cors": "^2.8.13",
|
||||
"@types/dotenv": "^8.2.0",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/jsonwebtoken": "^9.0.2",
|
||||
"@types/morgan": "^1.9.4",
|
||||
"@types/multer-s3": "^3.0.0",
|
||||
"@types/node": "^20.3.1",
|
||||
"@types/supertest": "^2.0.12",
|
||||
|
@ -68,15 +70,19 @@
|
|||
"aws-sdk": "^2.1414.0",
|
||||
"bcrypt": "^5.1.0",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"express-rate-limit": "^6.7.1",
|
||||
"ioredis": "^5.3.2",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"morgan": "^1.10.0",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"multer-s3": "^3.0.1",
|
||||
"rate-limit-redis": "^3.0.2",
|
||||
"sharp": "^0.32.3",
|
||||
"validator": "^13.9.0"
|
||||
"socket.io": "^4.7.2",
|
||||
"validator": "^13.9.0",
|
||||
"winston": "^3.10.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "socketId" TEXT;
|
|
@ -22,6 +22,7 @@ model User {
|
|||
postComments Comments[]
|
||||
fromNotifications Notifications[] @relation("fromNotifications")
|
||||
toNotifications Notifications[] @relation("toNotifications")
|
||||
socketId String?
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
|
|
8
src/@types/express.d.ts
vendored
8
src/@types/express.d.ts
vendored
|
@ -1,11 +1,17 @@
|
|||
/* eslint-disable */
|
||||
import * as express from 'express'
|
||||
import jwtPayload from '../interfaces/jwt'
|
||||
import type jwtPayload from '../interfaces/jwt'
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
user: jwtPayload | undefined
|
||||
}
|
||||
namespace Multer {
|
||||
interface File {
|
||||
location: string
|
||||
key: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
18
src/app.ts
18
src/app.ts
|
@ -1,19 +1,25 @@
|
|||
import 'dotenv/config'
|
||||
|
||||
import express from 'express'
|
||||
import router from './routes'
|
||||
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()
|
||||
|
||||
// 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 }))
|
||||
app.use(router)
|
||||
app.use(morganMiddleware)
|
||||
app.use(limiter)
|
||||
app.use(router)
|
||||
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) => {
|
||||
res.status(404).json({
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { S3Client } from '@aws-sdk/client-s3'
|
||||
import logger from 'helpers/logger'
|
||||
|
||||
let s3: S3Client
|
||||
|
||||
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({
|
||||
credentials: {
|
||||
accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? '',
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import comment from 'services/comments'
|
||||
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 (
|
||||
req: Request,
|
||||
|
@ -19,11 +20,7 @@ async function commentCreateController (
|
|||
|
||||
const result = await comment.create(postId, content, id)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default commentCreateController
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import comment from 'services/comments'
|
||||
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 (
|
||||
req: Request,
|
||||
|
@ -15,11 +16,7 @@ async function commentDeleteController (
|
|||
|
||||
const result = await comment.delete(commentId, id)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default commentDeleteController
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import comment from 'services/comments'
|
||||
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 (
|
||||
req: Request,
|
||||
|
@ -14,11 +15,7 @@ async function commentFetchController (
|
|||
|
||||
const result = await comment.fetch(commentId)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default commentFetchController
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import comment from 'services/comments'
|
||||
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 (
|
||||
req: Request,
|
||||
|
@ -14,11 +15,7 @@ async function commentFetchLikesController (
|
|||
|
||||
const result = await comment.fetchLikes(commentId)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default commentFetchLikesController
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import comment from 'services/comments'
|
||||
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 (
|
||||
req: Request,
|
||||
|
@ -19,11 +20,7 @@ async function commentUpdateController (
|
|||
|
||||
const result = await comment.update(content, id, commentId)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default commentUpdateController
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import post from 'services/posts'
|
||||
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 (
|
||||
req: Request,
|
||||
|
@ -15,11 +16,7 @@ async function postCreateController (
|
|||
|
||||
const result = await post.create(content, id)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default postCreateController
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import post from 'services/posts'
|
||||
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 (
|
||||
req: Request,
|
||||
|
@ -9,13 +10,13 @@ async function postDeleteController (
|
|||
const userId = req.user?.id ?? ''
|
||||
const postId = req.body.postId
|
||||
|
||||
const result = await post.delete(postId, userId)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
if (postId === undefined) {
|
||||
badRequest(res, 'Missing post id'); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
const result = await post.delete(postId, userId)
|
||||
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default postDeleteController
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import post from 'services/posts'
|
||||
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 (
|
||||
req: Request,
|
||||
|
@ -14,11 +15,7 @@ async function postFetchInfoController (
|
|||
|
||||
const result = await post.fetch(id)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default postFetchInfoController
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import post from 'services/posts'
|
||||
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 (
|
||||
req: Request,
|
||||
|
@ -14,11 +15,7 @@ async function postFetchLikesController (
|
|||
|
||||
const result = await post.fetchLikes(id)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default postFetchLikesController
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import post from 'services/posts'
|
||||
import type { Request, Response } from 'express'
|
||||
import { badRequest } from 'lib/http-errors'
|
||||
import handleResponse from 'helpers/handle-response'
|
||||
|
||||
async function postUpdateController (
|
||||
req: Request,
|
||||
|
@ -11,11 +11,7 @@ async function postUpdateController (
|
|||
|
||||
const result = await post.update(postId, content, userId)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default postUpdateController
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
import user from 'services/users'
|
||||
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> {
|
||||
const { email, password } = req.body
|
||||
|
||||
const result = await user.auth({ email, password })
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default userAuthController
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import user from 'services/users'
|
||||
import type { Request, Response } from 'express'
|
||||
import { badRequest } from 'lib/http-errors'
|
||||
import handleResponse from 'helpers/handle-response'
|
||||
|
||||
async function userDeleteController (
|
||||
req: Request,
|
||||
|
@ -9,11 +9,7 @@ async function userDeleteController (
|
|||
const userId = req.user?.id ?? ''
|
||||
const result = await user.delete(userId)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default userDeleteController
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import user from 'services/users'
|
||||
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 (
|
||||
req: Request,
|
||||
|
@ -14,11 +15,7 @@ async function userFetchInfoController (
|
|||
|
||||
const result = await user.fetchInfo(username.toLowerCase())
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default userFetchInfoController
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import user from 'services/users'
|
||||
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 (
|
||||
req: Request,
|
||||
|
@ -14,7 +15,7 @@ async function userFetchPostsController (
|
|||
|
||||
const result = await user.fetchPosts(username)
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default userFetchPostsController
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import user from 'services/users'
|
||||
import type { Request, Response } from 'express'
|
||||
import { badRequest } from 'lib/http-errors'
|
||||
import handleResponse from 'helpers/handle-response'
|
||||
|
||||
async function userFollowController (
|
||||
req: Request,
|
||||
|
@ -11,11 +11,7 @@ async function userFollowController (
|
|||
|
||||
const result = await user.follow(userId, userToFollow)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default userFollowController
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import user from 'services/users'
|
||||
import type { Request, Response } from 'express'
|
||||
import { badRequest } from 'lib/http-errors'
|
||||
import handleResponse from 'helpers/handle-response'
|
||||
|
||||
async function userLikeCommentController (
|
||||
req: Request,
|
||||
|
@ -11,11 +11,7 @@ async function userLikeCommentController (
|
|||
|
||||
const result = await user.likeComment(commentId, userId)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default userLikeCommentController
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import user from 'services/users'
|
||||
import type { Request, Response } from 'express'
|
||||
import { badRequest } from 'lib/http-errors'
|
||||
import handleResponse from 'helpers/handle-response'
|
||||
|
||||
async function userLikePostController (
|
||||
req: Request,
|
||||
|
@ -11,11 +11,7 @@ async function userLikePostController (
|
|||
|
||||
const result = await user.likePost(postId, userId)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default userLikePostController
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import user from 'services/users'
|
||||
import type { Request, Response } from 'express'
|
||||
import { badRequest } from 'lib/http-errors'
|
||||
import { badRequest } from 'helpers/http-errors'
|
||||
|
||||
async function userSearchController (
|
||||
req: Request,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import user from 'services/users'
|
||||
import type { Request, Response } from 'express'
|
||||
import { badRequest } from 'lib/http-errors'
|
||||
import handleResponse from 'helpers/handle-response'
|
||||
|
||||
async function userSignupController (
|
||||
req: Request,
|
||||
|
@ -10,11 +10,7 @@ async function userSignupController (
|
|||
|
||||
const result = await user.signup({ username, email, password })
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default userSignupController
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import user from 'services/users'
|
||||
import type { Request, Response } from 'express'
|
||||
import { badRequest } from 'lib/http-errors'
|
||||
import handleResponse from 'helpers/handle-response'
|
||||
|
||||
async function userUpdateController (
|
||||
req: Request,
|
||||
|
@ -11,11 +11,7 @@ async function userUpdateController (
|
|||
|
||||
const result = await user.update({ id, email, displayName, username })
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default userUpdateController
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
import user from 'services/users'
|
||||
import type { Request, Response } from 'express'
|
||||
import { badRequest } from 'lib/http-errors'
|
||||
|
||||
let url
|
||||
import { badRequest } from 'helpers/http-errors'
|
||||
import handleResponse from 'helpers/handle-response'
|
||||
|
||||
async function userUploadPictureController (
|
||||
req: Request,
|
||||
|
@ -15,21 +14,17 @@ async function userUploadPictureController (
|
|||
|
||||
const userId = req.user?.id ?? ''
|
||||
|
||||
let url: string
|
||||
|
||||
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}`
|
||||
} else {
|
||||
// @ts-expect-error property `location` doesn't exists in @types/express
|
||||
url = req.file.location
|
||||
}
|
||||
|
||||
const result = await user.uploadPicture(userId, url)
|
||||
|
||||
if (result instanceof Error) {
|
||||
badRequest(res, result.message); return
|
||||
}
|
||||
|
||||
res.json(result)
|
||||
handleResponse(res, result)
|
||||
}
|
||||
|
||||
export default userUploadPictureController
|
||||
|
|
10
src/helpers/handle-response.ts
Normal file
10
src/helpers/handle-response.ts
Normal 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
52
src/helpers/logger.ts
Normal 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
|
47
src/helpers/notification.ts
Normal file
47
src/helpers/notification.ts
Normal 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')
|
||||
}
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
interface userPayload {
|
||||
interface User {
|
||||
id?: string
|
||||
displayName?: string | null
|
||||
username?: string
|
||||
email?: string
|
||||
password?: string
|
||||
profileImage?: string | null
|
||||
createdAt?: Date
|
||||
token?: string
|
||||
socketId?: string
|
||||
}
|
||||
|
||||
export default userPayload
|
||||
export default User
|
||||
|
|
|
@ -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 {}
|
||||
}
|
|
@ -2,7 +2,7 @@ import { verify } from 'jsonwebtoken'
|
|||
import prisma from 'clients/prisma-client'
|
||||
import type { Response, Request, NextFunction } from 'express'
|
||||
import type jwtPayload from 'interfaces/jwt'
|
||||
import { unauthorized } from 'lib/http-errors'
|
||||
import { unauthorized } from 'helpers/http-errors'
|
||||
|
||||
async function authenticated (
|
||||
req: Request,
|
||||
|
|
13
src/middlewares/morgan.ts
Normal file
13
src/middlewares/morgan.ts
Normal 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
|
|
@ -1,11 +1,12 @@
|
|||
import rateLimit from 'express-rate-limit'
|
||||
import RedisStore from 'rate-limit-redis'
|
||||
import redis from 'clients/redis-client'
|
||||
import logger from 'helpers/logger'
|
||||
|
||||
let maxConnections
|
||||
|
||||
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
|
||||
} else {
|
||||
maxConnections = 5
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import type { Response, Request, NextFunction } from 'express'
|
||||
import multer from 'multer'
|
||||
import multerConfig from 'config/multer'
|
||||
import compressImage from 'lib/compress-image'
|
||||
import { badRequest } from 'lib/http-errors'
|
||||
import compressImage from 'helpers/compress-image'
|
||||
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')
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// @ts-expect-error property `key` does not exists in types
|
||||
await compressImage(req.file?.key, req.body.isProfilePicture)
|
||||
|
||||
next()
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Router } from 'express'
|
||||
|
||||
// Routers
|
||||
import usersRouter from './controllers/users-router'
|
||||
import postsRouter from './controllers/posts-router'
|
||||
import commentsRouter from './controllers/comments-router'
|
||||
import usersRouter from 'controllers/users-router'
|
||||
import postsRouter from 'controllers/posts-router'
|
||||
import commentsRouter from 'controllers/comments-router'
|
||||
|
||||
const router = Router()
|
||||
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
import app from './app'
|
||||
import { createServer } from 'http'
|
||||
import logger from 'helpers/logger'
|
||||
import createSocketIOInstance from 'socket'
|
||||
|
||||
app.listen(process.env.SERVER_PORT, () => {
|
||||
console.log(`Server is running @ ${process.env.SERVER_PORT ?? ''}`)
|
||||
const server = createServer(app)
|
||||
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 ?? ''}`)
|
||||
})
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import * as bcrypt from 'bcrypt'
|
||||
import jsonwebtoken from 'jsonwebtoken'
|
||||
import prisma from 'clients/prisma-client'
|
||||
import type userPayload from 'interfaces/user'
|
||||
import type User from 'interfaces/user'
|
||||
|
||||
async function userAuthService ({
|
||||
email,
|
||||
password
|
||||
}: userPayload): Promise<Record<string, unknown> | Error> {
|
||||
}: User): Promise<Record<string, unknown> | Error> {
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
email
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as bcrypt from 'bcrypt'
|
||||
import validator from 'validator'
|
||||
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 usernameRegex = /^[a-zA-Z0-9_.]{5,15}$/
|
||||
|
@ -10,7 +10,7 @@ async function userSignupService ({
|
|||
username,
|
||||
email,
|
||||
password
|
||||
}: userPayload): Promise<Record<string, unknown> | Error> {
|
||||
}: User): Promise<Record<string, unknown> | Error> {
|
||||
if (username === undefined || email === undefined || password === undefined) {
|
||||
return new Error('Missing fields')
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type userPayload from 'interfaces/user'
|
||||
import type User from 'interfaces/user'
|
||||
import prisma from 'clients/prisma-client'
|
||||
|
||||
async function userUpdateService ({
|
||||
|
@ -6,7 +6,7 @@ async function userUpdateService ({
|
|||
email,
|
||||
displayName,
|
||||
username
|
||||
}: userPayload): Promise<Record<string, unknown> | Error> {
|
||||
}: User): Promise<Record<string, unknown> | Error> {
|
||||
const user = await prisma.user.findFirst({ where: { id } })
|
||||
|
||||
if (user === null) {
|
||||
|
|
44
src/socket/index.ts
Normal file
44
src/socket/index.ts
Normal 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
|
||||
}
|
|
@ -2,9 +2,9 @@ import app from '../../app'
|
|||
import request from 'supertest'
|
||||
import signUpNewUser from '../utils/create-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', () => {
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -2,9 +2,9 @@ import app from '../../app'
|
|||
import request from 'supertest'
|
||||
import signUpNewUser from '../utils/create-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', () => {
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -2,11 +2,11 @@ import app from '../../app'
|
|||
import request from 'supertest'
|
||||
import signUpNewUser from '../utils/create-user'
|
||||
import deleteUser from '../utils/delete-user'
|
||||
import type userPayload from '../../interfaces/user'
|
||||
import type User from '../../interfaces/user'
|
||||
|
||||
let postId: string
|
||||
|
||||
let user: userPayload
|
||||
let user: User
|
||||
|
||||
describe('POST /post/info', () => {
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -2,9 +2,9 @@ import app from '../../app'
|
|||
import request from 'supertest'
|
||||
import signUpNewUser from '../utils/create-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', () => {
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -2,9 +2,9 @@ import request from 'supertest'
|
|||
import app from '../../app'
|
||||
import deleteUser from '../utils/delete-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', () => {
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import app from '../../app'
|
||||
import request from 'supertest'
|
||||
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', () => {
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -2,9 +2,9 @@ import app from '../../app'
|
|||
import request from 'supertest'
|
||||
import deleteUser from '../utils/delete-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', () => {
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -2,9 +2,9 @@ import request from 'supertest'
|
|||
import app from '../../app'
|
||||
import deleteUser from '../utils/delete-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', () => {
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -2,9 +2,9 @@ import request from 'supertest'
|
|||
import app from '../../app'
|
||||
import signUpNewUser from '../utils/create-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', () => {
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import app from '../../app'
|
||||
import request from 'supertest'
|
||||
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
|
||||
const username = faker.internet.userName({ lastName: 'doe' }).toLowerCase()
|
||||
const email = faker.internet.email()
|
||||
|
|
|
@ -27,12 +27,11 @@
|
|||
],
|
||||
"services/*": [
|
||||
"services/*"
|
||||
]
|
||||
],
|
||||
},
|
||||
"typeRoots": [
|
||||
"src/@types/express.d.ts",
|
||||
"src/@types/global.d.ts",
|
||||
"node_modules/@types"
|
||||
"./src/@types",
|
||||
"./node_modules/@types"
|
||||
],
|
||||
/* Emit */
|
||||
"outDir": "./dist",
|
||||
|
|
Loading…
Reference in a new issue