diff --git a/db.ts b/db.ts index ab2b6f5..7f5eea5 100644 --- a/db.ts +++ b/db.ts @@ -15,10 +15,10 @@ const makeConnectionWithDB = async () => { try { await sequelize.authenticate(); // if (process.env.NODE_ENV === "development") { - // await sequelize.sync({ - // force: true, - // logging: (sql) => console.log(sql), - // }); + await sequelize.sync({ + force: true, + logging: (sql) => console.log(sql), + }); // } console.log("Connection has been established successfully."); } catch (error) { diff --git a/index.ts b/index.ts index e35d0de..8562ee1 100644 --- a/index.ts +++ b/index.ts @@ -8,11 +8,13 @@ import routes from "./routes"; import { makeConnectionWithDB } from "./db"; import loadData from "./insertData"; import { loadA2ZSheet } from "./script"; +import { limiter } from "./src/utils/rateLimiter"; dotenv.config(); const app = express(); const PORT = process.env.PORT || 5000; +app.use(limiter); app.use(helmet()); app.use(xss()); diff --git a/src/schema/revision.schema.ts b/src/schema/revision.schema.ts index 670b6d2..7e8f073 100644 --- a/src/schema/revision.schema.ts +++ b/src/schema/revision.schema.ts @@ -5,7 +5,15 @@ export const userIdSchema = object({ query: object({ userId: string({ required_error: "userId is required" }) .nonempty({ message: "userId can't be empty" }) - .trim(), + .trim() + .refine( + async (userId) => { + const user = await User.findByPk(userId); + if (user) return true; + else return false; + }, + { message: "The user does not exists" } + ), }), }); diff --git a/src/schema/submission.schema.ts b/src/schema/submission.schema.ts index 0ca5f12..d148123 100644 --- a/src/schema/submission.schema.ts +++ b/src/schema/submission.schema.ts @@ -13,12 +13,28 @@ export const createSubmissionSchema = object({ required_error: "questionId is required", }) .nonempty({ message: "questionId can't be empty" }) - .trim(), + .trim() + .refine( + async (questionId) => { + const topic = await Question.findByPk(questionId); + if (topic) return true; + else return false; + }, + { message: "questionId does not exist" } + ), topicId: string({ required_error: "topicId is required", }) .nonempty({ message: "topicId can't be empty" }) - .trim(), + .trim() + .refine( + async (questionTopic) => { + const topic = await TopicUnderSubject.findByPk(questionTopic); + if (topic) return true; + else return false; + }, + { message: "topicId does not exist" } + ), completionDate: dateSchema, revisionDate: dateSchema.optional(), submittedBy: string({ @@ -34,7 +50,15 @@ export const userIdSchema = object({ query: object({ userId: string({ required_error: "userId is required" }) .nonempty({ message: "userId can't be empty" }) - .trim(), + .trim() + .refine( + async (userId) => { + const user = await User.findByPk(userId); + if (user) return true; + else return false; + }, + { message: "The user does not exists" } + ), }), }); @@ -42,20 +66,35 @@ export const userIdAndQuestionIdSchema = object({ query: object({ userId: string({ required_error: "userId is required" }) .nonempty({ message: "emauuserIdserIdil can't be empty" }) - .trim(), + .trim() + .refine( + async (userId) => { + const user = await User.findByPk(userId); + if (user) return true; + else return false; + }, + { message: "The user does not exists" } + ), questionId: string({ required_error: "questionId is required", }) .nonempty({ message: "questionId can't be empty" }) - .trim(), + .trim() + .refine( + async (questionId) => { + const topic = await Question.findByPk(questionId); + if (topic) return true; + else return false; + }, + { message: "questionId does not exist" } + ), }), }); export const idSchema = object({ body: object({ - id: string({ required_error: "id is required" }) - .nonempty({ - message: "id can't be empty", - }), + id: string({ required_error: "id is required" }).nonempty({ + message: "id can't be empty", + }), }), }); diff --git a/src/utils/rateLimiter.ts b/src/utils/rateLimiter.ts new file mode 100644 index 0000000..32b4380 --- /dev/null +++ b/src/utils/rateLimiter.ts @@ -0,0 +1,15 @@ +import rateLimiter from "express-rate-limit"; +import { Request, Response, NextFunction } from "express"; + +export const limiter = rateLimiter({ + max: 30, // limit each ip to 30 request per window(1 minute) + windowMs: 1 * 60 * 1000, // 1 minute + standardHeaders: true, + legacyHeaders: false, + handler: function (req: Request, res: Response, next: NextFunction) { + return res.status(429).json({ + error: + "You sent too many requests. Please wait for a minute then try again", + }); + }, +}); diff --git a/todo.txt b/todo.txt index 85c4b8f..8d86984 100644 --- a/todo.txt +++ b/todo.txt @@ -1,5 +1,3 @@ -change all models add id instead of uuid -work on all controllers aas models have changed do stronger validation postman collection check any missing todos