Skip to content

Commit 8f23aea

Browse files
committed
Resolved password field conflict
2 parents 4c3d7f5 + 5dde58d commit 8f23aea

9 files changed

Lines changed: 75 additions & 41 deletions

File tree

src/app.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// src/app.ts
21
import express from "express";
32
import cors from "cors";
43
import multer from "multer";
@@ -17,39 +16,40 @@ export const supabase = createClient(
1716

1817
const app = express();
1918

20-
// 1) Enable CORS for your domains
19+
2120
app.use(
2221
cors({
23-
origin: config.ALLOWED_ORIGINS.split(","), // e.g. 'https://club.example.com'
22+
origin: config.ALLOWED_ORIGINS || "*",
2423
methods: ["GET", "POST", "PATCH", "DELETE", "OPTIONS"],
2524
credentials: true,
2625
}),
2726
);
2827

29-
// 2) Parse JSON and form data
28+
3029
app.use(json());
3130
app.use(urlencoded({ extended: true }));
3231

33-
// 3) Handle file uploads (in-memory)
32+
3433
const upload = multer({ storage: multer.memoryStorage(),
3534
limits: { fileSize: 2 * 1024 * 1024 }
3635
});
3736

38-
// 4) Mount your routes, injecting `upload` middleware where needed
39-
// For endpoints that accept file uploads, you can do e.g.:
40-
// router.post('/members/:memberId/photo', upload.single('photo'), ...)
37+
38+
app.use("/health",(req,res)=>{
39+
res.status(200).json({ message: "OK" });
40+
})
4141

4242
app.use("/api/v1", routes(upload, supabase));
4343

44-
// 5) 404 handler
44+
// 404 handler
4545
app.use((req, res) => {
4646
res.status(404).json({ message: "Not Found" });
4747
});
4848

49-
// 6) Global error handler
49+
50+
// Global error handler
5051
app.use(errorHandler);
5152

52-
// 7) do 'npm run apidoc to generate the documentation, I have added it in the scripts
53-
// then you can go to localhost:3000/docs to see the docs
53+
// Serve API documentation
5454
app.use("/docs", express.static(path.join(__dirname, "..", "docs/apidoc")));
5555
export default app;

src/config/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ export default {
44
DIRECT_URL: process.env.DIRECT_URL!,
55
SUPABASE_URL: process.env.SUPABASE_URL!,
66
SUPABASE_SERVICE_ROLE_KEY: process.env.SUPABASE_SERVICE_ROLE_KEY!,
7-
ALLOWED_ORIGINS: process.env.ALLOWED_ORIGINS || "*",
7+
ALLOWED_ORIGINS: process.env.ALLOWED_ORIGINS,
88
};

src/controllers/interview.controller.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,27 @@ export const updateInterviewById = async (req: Request, res: Response) => {
103103

104104
export const deleteInterviewById = async (req: Request, res: Response) => {
105105
const interviewId = parseInt(req.params.id);
106+
const { memberId } = req.body;
106107

107108
if (!interviewId) {
108109
throw new ApiError("Invalid interview ID", 400);
109110
}
110111

112+
if (!memberId) {
113+
throw new ApiError("Member ID is required for verification", 400);
114+
}
115+
111116
const existingInterview = await interviewService.getInterviewById(interviewId);
112117

113118
if (!existingInterview) {
114119
throw new ApiError("Interview experience not found", 404);
115120
}
116121

122+
123+
if (existingInterview.memberId !== memberId) {
124+
throw new ApiError("You are not authorized to delete this interview", 403);
125+
}
126+
117127
await interviewService.deleteInterviewById(interviewId);
118128

119129
res.status(200).json({
@@ -122,4 +132,3 @@ export const deleteInterviewById = async (req: Request, res: Response) => {
122132
});
123133
};
124134

125-

src/routes/achievements.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export function parseCreateAchievementData(req: Request, res: Response, next: Ne
2020

2121
export default function acheivementsRouter(upload: Multer, supabase: SupabaseClient) {
2222
const router = express.Router();
23-
23+
2424

2525
/**
2626
* @api {get} /achievements Get all achievements

src/routes/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export default function routes(upload: Multer, supabase: SupabaseClient) {
1717

1818
router.use('/achievements' ,acheivementsRouter(upload, supabase));
1919

20-
router.use('/interviews', interviewRouter(upload, supabase));
20+
router.use('/interviews', interviewRouter());
2121

2222
router.use('/topics',topicRouter());
2323

src/routes/interviews.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
import express from 'express';
22
import * as interviewCtrl from '../controllers/interview.controller';
3-
import { supabase } from '../app';
4-
import { Multer } from 'multer';
5-
import { NextFunction } from 'express-serve-static-core';
6-
import { SupabaseClient } from '@supabase/supabase-js';
73

84

9-
10-
export default function interviewRouter(upload: Multer, supabase: SupabaseClient) {
5+
export default function interviewRouter() {
116
const router = express.Router();
127

138
/**
@@ -106,7 +101,7 @@ export default function interviewRouter(upload: Multer, supabase: SupabaseClient
106101
* @apiParam (Path Params) {Number} id Interview ID to delete
107102
*
108103
* @apiSuccess {String} message Deletion confirmation
109-
*
104+
* @apiBody (Request Body) {UUID} memberId Member ID of the owner
110105
* @apiError (400) BadRequest Invalid interview ID
111106
* @apiError (404) NotFound Interview not found
112107
* @apiError (500) InternalServerError Internal server error

src/types/members.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ declare global {
1717
codeforces?: string;
1818
password?: string
1919
}
20-
}
20+
}

src/utils/apiError.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// src/utils/apiError.ts
21
import { Request, Response, NextFunction } from "express";
32

43
/**
@@ -17,9 +16,7 @@ export class ApiError extends Error {
1716
}
1817
}
1918

20-
/**
21-
* Standard structure for API error responses
22-
*/
19+
2320
interface ErrorResponse {
2421
error: boolean;
2522
message: string;

tests/Interview.test.ts

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -309,11 +309,14 @@ describe('updateInterviewById', () => {
309309

310310

311311
describe('deleteInterviewById', () => {
312+
afterEach(() => {
313+
jest.restoreAllMocks();
314+
});
315+
312316
it('should return 200 and success message', async () => {
313317
const req: any = {
314-
params: {
315-
id: '1',
316-
},
318+
params: { id: '1' },
319+
body: { memberId: 'user_123' },
317320
};
318321

319322
const res: any = {
@@ -331,13 +334,8 @@ describe('deleteInterviewById', () => {
331334
memberId: 'user_123',
332335
};
333336

334-
jest
335-
.spyOn(interviewService, 'getInterviewById')
336-
.mockResolvedValue(mockInterview);
337-
338-
jest
339-
.spyOn(interviewService, 'deleteInterviewById')
340-
.mockResolvedValue();
337+
jest.spyOn(interviewService, 'getInterviewById').mockResolvedValue(mockInterview);
338+
jest.spyOn(interviewService, 'deleteInterviewById').mockResolvedValue();
341339

342340
await deleteInterviewById(req, res);
343341

@@ -351,22 +349,57 @@ describe('deleteInterviewById', () => {
351349
it('should throw 400 for invalid interview ID', async () => {
352350
const req: any = {
353351
params: { id: 'invalid' },
352+
body: { memberId: 'user_123' },
354353
};
355354

356355
const res: any = {};
356+
357+
await expect(deleteInterviewById(req, res)).rejects.toThrow(ApiError);
358+
});
359+
360+
it('should throw 400 if memberId is missing', async () => {
361+
const req: any = {
362+
params: { id: '1' },
363+
body: {},
364+
};
365+
366+
const res: any = {};
367+
357368
await expect(deleteInterviewById(req, res)).rejects.toThrow(ApiError);
358369
});
359370

360371
it('should throw 404 if interview not found', async () => {
361372
const req: any = {
362373
params: { id: '999' },
374+
body: { memberId: 'user_123' },
363375
};
364376

365377
const res: any = {};
366378

367-
jest
368-
.spyOn(interviewService, 'getInterviewById')
369-
.mockResolvedValue(null);
379+
jest.spyOn(interviewService, 'getInterviewById').mockResolvedValue(null);
380+
381+
await expect(deleteInterviewById(req, res)).rejects.toThrow(ApiError);
382+
});
383+
384+
it('should throw 403 if memberId does not match', async () => {
385+
const req: any = {
386+
params: { id: '1' },
387+
body: { memberId: 'wrong_user' },
388+
};
389+
390+
const res: any = {};
391+
392+
const mockInterview = {
393+
id: 1,
394+
company: 'Google',
395+
role: 'SDE',
396+
verdict: Verdict.Selected,
397+
content: 'Nice',
398+
isAnonymous: false,
399+
memberId: 'user_123',
400+
};
401+
402+
jest.spyOn(interviewService, 'getInterviewById').mockResolvedValue(mockInterview);
370403

371404
await expect(deleteInterviewById(req, res)).rejects.toThrow(ApiError);
372405
});

0 commit comments

Comments
 (0)