From 87d61d521a920aa2c57ef1d0f5da335a40b9f001 Mon Sep 17 00:00:00 2001 From: Ofek Itscovits Date: Thu, 12 Jun 2025 11:28:41 +0300 Subject: [PATCH 1/5] feat: add multer --- apps/api/package.json | 2 ++ apps/api/src/server.ts | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/apps/api/package.json b/apps/api/package.json index 3a989ea..93b3734 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -41,6 +41,7 @@ "ioredis": "^5.6.0", "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0", + "multer": "^2.0.1", "twilio": "^5.4.5", "zod": "^3.25.6" }, @@ -54,6 +55,7 @@ "@types/express": "^4.17.17", "@types/jsonwebtoken": "^9.0.9", "@types/morgan": "^1.9.9", + "@types/multer": "^1.4.13", "@types/node": "^22.13.5", "@types/supertest": "^6.0.2", "eslint": "^9.20.0", diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index c80aca5..a8c5935 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -1,5 +1,6 @@ import express, { type Express } from "express"; import morgan from "morgan"; +import multer from "multer"; import cors from "cors"; import cookieParser from "cookie-parser"; import rootRouter from "@repo/api/modules"; @@ -18,6 +19,14 @@ export const createServer = (): Express => { ) .use(express.urlencoded({ extended: true })) .use(express.json()) + .use( + multer({ + storage: multer.memoryStorage(), // Store files in memory + limits: { + fileSize: 10 * 1024 * 1024, // 10MB limit + }, + }).any() + ) .use(cookieParser()) .use(cors({ origin: "http://localhost:3002", credentials: true })) // .use( From b76ec7b2c4ec406306ffcb5f8833adb43a0ed7fe Mon Sep 17 00:00:00 2001 From: Ofek Itscovits Date: Thu, 12 Jun 2025 11:28:51 +0300 Subject: [PATCH 2/5] refactor: remove unused code --- apps/api/src/server.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index a8c5935..3e3aa32 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -5,7 +5,6 @@ import cors from "cors"; import cookieParser from "cookie-parser"; import rootRouter from "@repo/api/modules"; import errorHandler from "@repo/api/middlewares/error"; -import rateLimitMiddleware from "@repo/api/middlewares/rate-limit"; export const createServer = (): Express => { const app = express(); @@ -29,12 +28,6 @@ export const createServer = (): Express => { ) .use(cookieParser()) .use(cors({ origin: "http://localhost:3002", credentials: true })) - // .use( - // rateLimitMiddleware({ - // timeSpan: process.env.WINDOW_SIZE_IN_MINUTES, - // limit: process.env.MAX_NUMBER_OF_REQUESTS_PER_WINDOW_SIZE, - // }) - // ) .use("/api/v1", rootRouter.router) .all("/*splat", () => { throw new Error("You look a little lost."); From b394783556a9e23c1573ff352e731d5779b45f3e Mon Sep 17 00:00:00 2001 From: Ofek Itscovits Date: Thu, 12 Jun 2025 11:29:00 +0300 Subject: [PATCH 3/5] refactor: remove morgan logging --- apps/api/src/server.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index 3e3aa32..437005f 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -1,5 +1,4 @@ import express, { type Express } from "express"; -import morgan from "morgan"; import multer from "multer"; import cors from "cors"; import cookieParser from "cookie-parser"; @@ -10,12 +9,6 @@ export const createServer = (): Express => { const app = express(); app .disable("x-powered-by") - .use( - morgan( - `:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length]`, - { immediate: true } - ) - ) .use(express.urlencoded({ extended: true })) .use(express.json()) .use( From 1a7e61383b1126d818b1280f8add59ae27bb3d2c Mon Sep 17 00:00:00 2001 From: Ofek Itscovits Date: Thu, 12 Jun 2025 11:30:58 +0300 Subject: [PATCH 4/5] feat: add new user routes * Add new privacy settings get and patch handlers * Add new user delete route handler --- apps/api/src/modules/user/user.route.ts | 72 ++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/apps/api/src/modules/user/user.route.ts b/apps/api/src/modules/user/user.route.ts index c2d7ba1..58b045f 100644 --- a/apps/api/src/modules/user/user.route.ts +++ b/apps/api/src/modules/user/user.route.ts @@ -6,8 +6,13 @@ import { createTypiRouter, } from "@repo/typiserver"; import { db } from "@repo/database"; -import { users } from "@repo/database/schema"; +import { + refreshTokens, + userPrivacySettings, + users, +} from "@repo/database/schema"; import authMiddleware from "@repo/api/middlewares/auth"; +import { createUpdateSchema } from "drizzle-zod"; const userRouter = createTypiRouter({ "/": createTypiRoute({ @@ -50,6 +55,71 @@ const userRouter = createTypiRouter({ }); }, }), + delete: createTypiRouteHandler({ + middlewares: [authMiddleware], + handler: async (ctx) => { + const deleted = await db + .delete(users) + .where(eq(users.id, ctx.data.userId)) + .returning(); + + if (!deleted.length) return ctx.error("NOT_FOUND", "User not found."); + + await db + .delete(refreshTokens) + .where(eq(refreshTokens.userId, ctx.data.userId)); + + ctx.response.clearCookie(process.env.JWT_ACCESS_TOKEN_COOKIE_KEY); + ctx.response.clearCookie(process.env.JWT_REFRESH_TOKEN_COOKIE_KEY); + + return ctx.success({ + message: "User deleted successfully.", + }); + }, + }), + }), + "/privacy": createTypiRoute({ + get: createTypiRouteHandler({ + middlewares: [authMiddleware], + handler: async (ctx) => { + const privacySettings = await db.query.userPrivacySettings.findFirst({ + where: and(eq(users.id, ctx.data.userId)), + }); + if (!privacySettings) return ctx.error("NOT_FOUND", "User not found."); + + return ctx.success({ + privacySettings, + }); + }, + }), + patch: createTypiRouteHandler({ + input: { + body: z.object({ + privacySettings: createUpdateSchema(userPrivacySettings, { + createdAt: z.undefined(), + updatedAt: z.undefined(), + }), + }), + }, + middlewares: [authMiddleware], + handler: async (ctx) => { + const updatedPrivacySettings = await db + .update(userPrivacySettings) + .set({ + updatedAt: new Date(), + ...ctx.input.body.privacySettings, + }) + .where(eq(userPrivacySettings.userId, ctx.data.userId)) + .returning(); + + if (!updatedPrivacySettings.length) + return ctx.error("NOT_FOUND", "User not found."); + + return ctx.success({ + privacySettings: updatedPrivacySettings[0], + }); + }, + }), }), }); From 2944dc062c7febf144eb003daf2a7fb421a82a47 Mon Sep 17 00:00:00 2001 From: Ofek Itscovits Date: Thu, 12 Jun 2025 11:31:18 +0300 Subject: [PATCH 5/5] refactor: update user patch picture input to file type --- apps/api/src/modules/user/user.route.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/api/src/modules/user/user.route.ts b/apps/api/src/modules/user/user.route.ts index 58b045f..8ee12a6 100644 --- a/apps/api/src/modules/user/user.route.ts +++ b/apps/api/src/modules/user/user.route.ts @@ -32,9 +32,11 @@ const userRouter = createTypiRouter({ patch: createTypiRouteHandler({ input: { body: z.object({ - name: z.string().optional(), - description: z.string().optional(), - picture: z.string().optional(), + user: z.object({ + name: z.string().optional(), + description: z.string().optional(), + picture: z.instanceof(File).optional(), + }), }), }, middlewares: [authMiddleware], @@ -42,8 +44,9 @@ const userRouter = createTypiRouter({ const user = await db .update(users) .set({ + name: ctx.input.body.user.name, + description: ctx.input.body.user.description, updatedAt: new Date(), - ...ctx.input.body, }) .where(eq(users.id, ctx.data.userId)) .returning();