From 00072cf2b6b0f2f7b63e34f7fb8dbc832e4a00ee Mon Sep 17 00:00:00 2001 From: Aarvayn Date: Mon, 20 Apr 2026 19:49:16 +0530 Subject: [PATCH] feat: add bug CRUD routes, controller and model --- core-services/.gitignore | 2 + core-services/Models/bug.js | 90 ++++++++++++++++++++ core-services/config/dbConnection.js | 21 +++++ core-services/constants.js | 8 ++ core-services/controllers/bugController.js | 96 ++++++++++++++++++++++ core-services/middleware/errorHandler.js | 34 ++++++++ core-services/package-lock.json | 70 ++++++---------- core-services/package.json | 22 +++-- core-services/routes/bugsRoutes.js | 15 ++++ core-services/server.js | 18 ++++ 10 files changed, 319 insertions(+), 57 deletions(-) create mode 100644 core-services/.gitignore create mode 100644 core-services/Models/bug.js create mode 100644 core-services/config/dbConnection.js create mode 100644 core-services/constants.js create mode 100644 core-services/controllers/bugController.js create mode 100644 core-services/middleware/errorHandler.js create mode 100644 core-services/routes/bugsRoutes.js create mode 100644 core-services/server.js diff --git a/core-services/.gitignore b/core-services/.gitignore new file mode 100644 index 0000000..37d7e73 --- /dev/null +++ b/core-services/.gitignore @@ -0,0 +1,2 @@ +node_modules +.env diff --git a/core-services/Models/bug.js b/core-services/Models/bug.js new file mode 100644 index 0000000..76cdece --- /dev/null +++ b/core-services/Models/bug.js @@ -0,0 +1,90 @@ +const mongoose = require('mongoose'); + +const bugSchema = new mongoose.Schema( + { + title: + { + type:String, + required:true, + trim:true + }, + description: + { + type: String, + required: true + }, + techStack: + [{ + type: String, + lowercase: true, + trim: true + }], + tags: + [{ + type: String, + lowercase: true, + trim: true + }], + difficulty: + { + type: String, + enum: ["EASY", "MEDIUM", "HARD"], + index: true + }, + reward: + { + type: Number, + required: true, + min: 1 + }, + status: + { + type: String, + enum: ["OPEN", "SOLVED", "CLOSED"], + default: "OPEN", + index: true + }, + ownerId: + { + type: mongoose.Schema.Types.ObjectId, + ref: "User", + index: true + }, + acceptedSolutionId: + { + type: mongoose.Schema.Types.ObjectId, + ref: "Solution" + }, + solutionCount: + { + type: Number, + default: 0 + }, + github: + { + repoUrl: String, + filePath: String, + issueUrl: String + }, + deadline: Date, + isRewardLocked: + { + type: Boolean, + default: true + }, + isAbusive: + { + type: Boolean, + default: false + }, + refundProcessed: + { + type: Boolean, + default: false + } +}, +{ + timestamps: true +} +); +module.exports = mongoose.model('Bug',bugSchema); \ No newline at end of file diff --git a/core-services/config/dbConnection.js b/core-services/config/dbConnection.js new file mode 100644 index 0000000..8ab7897 --- /dev/null +++ b/core-services/config/dbConnection.js @@ -0,0 +1,21 @@ +const mongoose = require('mongoose'); + +const connectDb = async() => +{ + try + { + const connect = await mongoose.connect(process.env.CONNECTION_STRING); + console.log( + 'Database connected:', + connect.connection.host, + connect.connection.name + ); + } + catch (err) + { + console.log(err); + process.exit(1); + } +}; + +module.exports = connectDb; \ No newline at end of file diff --git a/core-services/constants.js b/core-services/constants.js new file mode 100644 index 0000000..509c6d4 --- /dev/null +++ b/core-services/constants.js @@ -0,0 +1,8 @@ +exports.constants = +{ + VALIDATION_ERROR:400, + NOT_FOUND:404, + UNAUTHORIZED:401, + FORBIDDEN:403, + SERVER_ERROR:500 +}; \ No newline at end of file diff --git a/core-services/controllers/bugController.js b/core-services/controllers/bugController.js new file mode 100644 index 0000000..90ad8f9 --- /dev/null +++ b/core-services/controllers/bugController.js @@ -0,0 +1,96 @@ +const asyncHandler = require('express-async-handler'); +const Bug = require('../Models/bug'); + +//@desc Get all Bugs.. +//@route Get /api/bugs +//@access public + +const getBugs = asyncHandler(async (req,res) => +{ + const bugs = await Bug.find(); + res.status(200).json(bugs); +}); + +//@desc Create new Bug.. +//@route POST /api/bugs +//@access public + +const createBug = asyncHandler(async (req, res) => +{ + console.log('The request body is: ',req.body); + const {title,description,deadline,reward} = req.body; + if(!title || !description || !deadline || !reward) + { + res.status(400); + throw new Error('All fields are mandatory') + } + const bug = await Bug.create({ + title, + description, + deadline, + reward + }); + res.status(201).json(bug); +}); + +//@desc Get Bug by id.. +//@route Get /api/bugs/:id +//@access public + +const getBug = asyncHandler(async (req, res) => +{ + const bug = await Bug.findById(req.params.id); + if(!bug) + { + res.status(404); + throw new Error('Bug not found'); + } + res.status(200).json(bug); +}); + +//@desc Update Bug by id.. +//@route PUT /api/bugs/:id +//@access public + +const updateBug = asyncHandler(async(req, res) => +{ + const bug = await Bug.findById(req.params.id); + if(!bug) + { + res.status(404); + throw new Error('Bug not found'); + } + const updatedBug = await Bug.findByIdAndUpdate( + req.params.id, + req.body, + {new: true} + ); + + res.status(200).json(updatedBug); +}); + +//@desc Delete Bug.. +//@route DELETE /api/bugs/:id +//@access public + +const deleteBugs = asyncHandler(async (req, res) => +{ + const bug = await Bug.findById(req.params.id); + if(!bug) + { + res.status(404); + throw new Error('Bug not found'); + } + + await bug.deleteOne(); + res.status(200).json(bug); +}); + +module.exports = +{ + getBugs, + createBug, + getBug, + updateBug, + deleteBugs +}; \ No newline at end of file diff --git a/core-services/middleware/errorHandler.js b/core-services/middleware/errorHandler.js new file mode 100644 index 0000000..81e90e3 --- /dev/null +++ b/core-services/middleware/errorHandler.js @@ -0,0 +1,34 @@ +const{constants} = require('../constants'); +const errorHandler = (err,req,res,next) => +{ + const statusCode = res.statusCode ? res.statusCode: 500; + + switch (statusCode) + { + case constants.VALIDATION_ERROR: + res.json({title: "Validation Failed",message: err.message,stackTrace: err.stack}); + break; + + case constants.NOT_FOUND: + res.json({title: "Not Found",message: err.message,stackTrace: err.stack}); + break; + + case constants.UNAUTHORIZED: + res.json({title: "Not Authorized",message: err.message,stackTrace: err.stack}); + break; + + case constants.FORBIDDEN: + res.json({title: "Forbidden",message: err.message,stackTrace: err.stack}); + break; + + case constants.SERVER_ERROR: + res.json({title: "Server Error",message: err.message,stackTrace: err.stack}); + break; + + default: + console.log('No Error'); + break; + } +}; + +module.exports = errorHandler; \ No newline at end of file diff --git a/core-services/package-lock.json b/core-services/package-lock.json index 33cb959..bae162a 100644 --- a/core-services/package-lock.json +++ b/core-services/package-lock.json @@ -1,18 +1,18 @@ { - "name": "core-services", + "name": "bug-graveyard-backend-api", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "core-services", + "name": "bug-graveyard-backend-api", "version": "1.0.0", - "license": "ISC", + "license": "MIT", "dependencies": { - "cors": "^2.8.6", - "dotenv": "^17.3.1", + "dotenv": "^17.4.0", "express": "^5.2.1", - "mongoose": "^9.3.3" + "express-async-handler": "^1.2.0", + "mongoose": "^9.4.1" }, "devDependencies": { "nodemon": "^3.1.14" @@ -254,23 +254,6 @@ "node": ">=6.6.0" } }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -298,9 +281,9 @@ } }, "node_modules/dotenv": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", - "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.0.tgz", + "integrity": "sha512-kCKF62fwtzwYm0IGBNjRUjtJgMfGapII+FslMHIjMR5KTnwEmBmWLDRSnc3XSNP8bNy34tekgQyDT0hr7pERRQ==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -426,6 +409,12 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-async-handler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/express-async-handler/-/express-async-handler-1.2.0.tgz", + "integrity": "sha512-rCSVtPXRmQSW8rmik/AIb2P0op6l7r1fMW538yyvTMltCO4xQEWMmobfrIxN2V1/mVrgxB8Az3reYF6yUZw37w==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -779,13 +768,13 @@ } }, "node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.5" }, "engines": { "node": "18 || 20 || >=22" @@ -854,9 +843,9 @@ } }, "node_modules/mongoose": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.3.3.tgz", - "integrity": "sha512-sfv5LOIPWeN5o/281kp4Rx9ZnuXb0g8CtvBTi7trYQs2PYYx8LWXegXxG3ar7VEns1o+d4h9LI/Dtc7dTTyYmA==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.4.1.tgz", + "integrity": "sha512-4rFBWa+/wdBQSfvnOPJBpiSG6UCEbhSQh865dEdaH9Y8WfHBUC+I2XT28dp0IBIGrEwmh+gzrgZgea5PbmrHWA==", "license": "MIT", "dependencies": { "kareem": "3.2.0", @@ -946,15 +935,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -998,9 +978,9 @@ } }, "node_modules/path-to-regexp": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.0.tgz", - "integrity": "sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", "funding": { "type": "opencollective", diff --git a/core-services/package.json b/core-services/package.json index 2ac36f1..5e0c1ba 100644 --- a/core-services/package.json +++ b/core-services/package.json @@ -1,22 +1,20 @@ { - "name": "core-services", + "name": "bug-graveyard-backend-api", "version": "1.0.0", - "description": "", - "main": "index.js", + "description": "This is express project for Bug-Graveyard backend api", + "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "dev": "nodemon src/server.js", - "start": "node src/server.js" + "start": "node server.js", + "dev": "nodemon server.js" }, - "keywords": [], - "author": "", - "license": "ISC", - "type": "commonjs", + "author": "Atharva Javheri", + "license": "MIT", "dependencies": { - "cors": "^2.8.6", - "dotenv": "^17.3.1", + "dotenv": "^17.4.0", "express": "^5.2.1", - "mongoose": "^9.3.3" + "express-async-handler": "^1.2.0", + "mongoose": "^9.4.1" }, "devDependencies": { "nodemon": "^3.1.14" diff --git a/core-services/routes/bugsRoutes.js b/core-services/routes/bugsRoutes.js new file mode 100644 index 0000000..a5c6507 --- /dev/null +++ b/core-services/routes/bugsRoutes.js @@ -0,0 +1,15 @@ +const express = require('express'); +const router = express.Router(); +const {getBugs,createBug,getBug,updateBug,deleteBugs} = require('../controllers/bugController'); + +router.route('/').get(getBugs); + +router.route('/').post(createBug); + +router.route('/:id').get(getBug); + +router.route('/:id').put(updateBug); + +router.route('/:id').delete(deleteBugs); + +module.exports = router; \ No newline at end of file diff --git a/core-services/server.js b/core-services/server.js new file mode 100644 index 0000000..6f2df5f --- /dev/null +++ b/core-services/server.js @@ -0,0 +1,18 @@ +const express = require('express'); +const errorHandler = require('./middleware/errorHandler'); +const connectDb = require('./config/dbConnection'); +const dotenv = require('dotenv').config(); +const router = require("./routes/bugsRoutes") + +connectDb(); +const app = express(); + +const port = process.env.PORT || 5000; + +app.use(express.json()); +app.use('/api/bugs',router); +app.use(errorHandler); + +app.listen(port, () => { + console.log('server running on port', port); +}); \ No newline at end of file