broll-express 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
package/.babelrc ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "presets": [
3
+ "@babel/preset-env"
4
+ ]
5
+ }
6
+
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Ananda krishnan g r
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "broll-express",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "type": "module",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1",
8
+ "dev": " env-cmd -f .env.development node --no-warnings --watch --loader @babel/register ./start.js"
9
+ },
10
+ "author": "anandakrishnangr",
11
+ "license": "ISC",
12
+ "description": "",
13
+ "dependencies": {
14
+ "babel-node": "^0.0.1-security",
15
+ "bcryptjs": "^2.4.3",
16
+ "body-parser": "^1.20.3",
17
+ "cors": "^2.8.5",
18
+ "env-cmd": "^10.1.0",
19
+ "express": "^4.21.1",
20
+ "joi": "^17.13.3",
21
+ "jsonwebtoken": "^9.0.2",
22
+ "mongoose": "^8.7.1",
23
+ "swagger-jsdoc": "^6.2.8",
24
+ "swagger-ui-express": "^5.0.1"
25
+ },
26
+ "devDependencies": {
27
+ "@babel/core": "^7.25.8",
28
+ "@babel/node": "^7.25.7",
29
+ "@babel/preset-env": "^7.25.8",
30
+ "@babel/register": "^7.25.7"
31
+ }
32
+ }
package/src/app.js ADDED
@@ -0,0 +1,32 @@
1
+ import express from 'express';
2
+ import { connectMongoose } from './config/mongoConfig.js'
3
+ import bodyParser from 'body-parser';
4
+ import { createUser } from './controllers/signup.controller.js';
5
+ import authRoute from './routes/auth.routes.js'
6
+ import { validateRequest } from './middlewares/payloadValidation.middleware.js';
7
+ import cors from 'cors'
8
+ import { swaggerSpecs } from "./config/swaggerConfig.js";
9
+ import swaggerUi from "swagger-ui-express"
10
+
11
+ const app = express();
12
+ connectMongoose()
13
+ // Middleware to parse JSON requests
14
+ app.use(cors());
15
+ app.use(express.json());
16
+ app.use(bodyParser.json({
17
+ limit: '5mb'
18
+ }));
19
+ app.use(bodyParser.urlencoded({ extended: false }))
20
+ app.use(bodyParser.json())
21
+ app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpecs));
22
+
23
+ app.use(validateRequest)
24
+ app.get('/', (req, res) => {
25
+ res.send('Welcome to the Demo App!');
26
+ });
27
+
28
+ app.post('/test', createUser)
29
+ // app.post('/business',)
30
+ app.use("/auth", authRoute)
31
+
32
+ export default app // Export the app for use in server.js
@@ -0,0 +1,9 @@
1
+ import { default as mongoose } from "mongoose";
2
+
3
+ export const connectMongoose =async () => {
4
+ return mongoose.connect("").then((res) => {
5
+ console.log("connected")
6
+ }).catch((err) => {
7
+ console.log("err")
8
+ })
9
+ }
@@ -0,0 +1,27 @@
1
+ import swaggerJsdoc from 'swagger-jsdoc'
2
+ import { fileURLToPath } from 'url';
3
+ import packageDetails from '../../package.json' assert {type: 'json'}
4
+ import path from 'path';
5
+ const PORT = process.env.PORT
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const options = {
9
+ definition: {
10
+ openapi: "3.0.0",
11
+ info: {
12
+ title: "Ecom API with Swagger",
13
+ version: packageDetails.version,
14
+ description: "A simple CRUD API application made with Express and documented with Swagger",
15
+ },
16
+ servers: [
17
+ {
18
+ url: `http://localhost:${PORT}`, // Replace with your server URL
19
+ },
20
+ ],
21
+ },
22
+ apis: [path.join(__dirname, "../routes/*.js")],
23
+ };
24
+
25
+ export const swaggerSpecs = swaggerJsdoc(options);
26
+
27
+
@@ -0,0 +1,22 @@
1
+ import Login from "../models/auth.models.js"
2
+
3
+ export const register = async (req, res, next) => {
4
+ try {
5
+ let { password, email } = req.body
6
+ await Login.create({ password, email })
7
+ res.send(200)
8
+ } catch (error) {
9
+ next(error)
10
+ }
11
+ }
12
+
13
+ export const login = async (req, res, next) => {
14
+ try {
15
+ let { password, email } = req.body
16
+ await Login.isUserAuthenticated(email, password)
17
+ res.send(200)
18
+ } catch (error) {
19
+ console.log(error)
20
+ next(error)
21
+ }
22
+ }
@@ -0,0 +1,31 @@
1
+ import mongoose from "mongoose"
2
+ import user from "../models/user.models.js";
3
+
4
+
5
+ export const createUser = async(req, res) => {
6
+ const session = await mongoose.startSession();
7
+
8
+ try {
9
+ session.startTransaction();
10
+
11
+ // Create a new address
12
+ const newUser = await user.create([{
13
+ firstName:"ananda",
14
+ lastName:"krishnan",
15
+ gender:"male"
16
+ }], { session });
17
+
18
+ // Commit the transaction
19
+ throw Error
20
+ await session.commitTransaction();
21
+ console.log('Transaction committed:', newUser);
22
+ } catch (error) {
23
+ // If an error occurs, abort the transaction
24
+ await session.abortTransaction();
25
+ console.error('Transaction aborted due to error:', error);
26
+ } finally {
27
+ session.endSession();
28
+ }
29
+
30
+
31
+ }
@@ -0,0 +1,4 @@
1
+ import bcryptjs from 'bcryptjs'
2
+
3
+ export const hashPassword = async (password) => bcryptjs.hash(password, await bcryptjs.genSalt(10))
4
+ export const comparePassword = async (password, hash) => bcryptjs.compare(password, hash)
@@ -0,0 +1,11 @@
1
+ class AppError extends Error {
2
+ constructor({ message, data, statusCode }) {
3
+ super(data);
4
+ this.data = data
5
+ this.message = message
6
+ this.statusCode = statusCode;
7
+ this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
8
+ this.isOperational = true; // Flag to indicate it's an expected error
9
+ }
10
+ }
11
+ export default AppError
@@ -0,0 +1,19 @@
1
+ function globalErrorHandler(err, req, res, next) {
2
+ let statusCode = err.statusCode || 500;
3
+ let message = err.message || 'Internal Server Error';
4
+ let data = err.data || {};
5
+ console.log(err.name)
6
+ if (err.name === 'ValidationError') {
7
+ message = Object.values(err.errors).map(err => err.message);
8
+ console.error('Validation Errors:', message);
9
+ } else {
10
+
11
+ }
12
+ res.status(statusCode).json({
13
+ status: err.status || 'error',
14
+ message,
15
+ data
16
+ });
17
+ }
18
+
19
+ export default globalErrorHandler
@@ -0,0 +1,19 @@
1
+ import { schemaLookup } from "../validations/validations.schema.js";
2
+
3
+ export const validateRequest = (req, res, next) => {
4
+ const { method, path } = req;
5
+ console.log(path, method)
6
+
7
+ const schema = schemaLookup[`${method}:${path}`] || Object.keys(schemaLookup).find(key => {
8
+ const regex = new RegExp(`^${key.split(':')[1].replace(':id', '\\d+')}$`);
9
+ return regex.test(path) && key.startsWith(method);
10
+ });
11
+ if (schema) {
12
+ const { error } = schema.validate(req.body);
13
+ if (error) {
14
+ return res.status(400).json({ message: error.details[0].message });
15
+ }
16
+ }
17
+ next(); // Proceed to the next middleware or route handler
18
+ };
19
+
@@ -0,0 +1,63 @@
1
+ import mongoose from "mongoose";
2
+ import { VerificationDetailsSchema } from "./common.schema.js";
3
+ import { comparePassword, hashPassword } from "../library/auth.lib.js";
4
+ import AppError from "../library/error.lib.js";
5
+
6
+ const LoginSchema = mongoose.Schema({
7
+ email: {
8
+ type: String,
9
+ unique: true,
10
+ trim: true,
11
+ required: [true, 'Email is required'],
12
+ match: [/.+@.+\..+/, 'Please enter a valid email address'],
13
+ lowercase: true,
14
+ },
15
+ phone: {
16
+ type: Number,
17
+ trim: true,
18
+ required: false,
19
+ min: 10
20
+ },
21
+ password: {
22
+ type: String,
23
+ trim: true,
24
+ required: [true, 'Password is required'],
25
+ minlength: [6, 'Password must be at least 6 characters long'],
26
+ },
27
+ email_verification_Details: VerificationDetailsSchema,
28
+ phone_verification_Details: VerificationDetailsSchema
29
+ }, {
30
+ timestamps: true
31
+ });
32
+
33
+ LoginSchema.pre('save', async function (next) {
34
+ this.password = await hashPassword(this.password)
35
+ next();
36
+ });
37
+
38
+ LoginSchema.post(['find', 'findOne'], async function (docs) {
39
+ const _includePassword = this.options.includePassword
40
+ if (_includePassword) {
41
+ return docs
42
+ }
43
+ if (Array.isArray(docs)) {
44
+ docs.forEach(doc => {
45
+ doc.password = undefined; // Remove password from each document
46
+ });
47
+ } else if (docs) {
48
+ docs.password = undefined; // Remove password from the single document
49
+ }
50
+ });
51
+
52
+ LoginSchema.statics.isUserAuthenticated = async function (email, password) {
53
+ let user = await this.findOne({ email }).setOptions({ includePassword: true });
54
+ if (!user) {
55
+ throw new AppError({ message: 'User Not Found !', statusCode: 401 })
56
+ }
57
+ const isMatch = await comparePassword(password, user.password)
58
+ if (!isMatch) throw new AppError({ message: 'Incorrect password', statusCode: 400 })
59
+ return isMatch
60
+ }
61
+
62
+ const Login = mongoose.model("Loginsf", LoginSchema);
63
+ export default Login;
@@ -0,0 +1,10 @@
1
+ import mongoose from "mongoose";
2
+
3
+ export const VerificationDetailsSchema = mongoose.Schema({
4
+ status: {
5
+ type: Boolean,
6
+ default: false,
7
+ },
8
+ }, {
9
+ timestamps: true
10
+ });
@@ -0,0 +1,30 @@
1
+ import mongoose from "mongoose";
2
+
3
+ const userSchema = mongoose.Schema({
4
+ firstName:{
5
+ type:String,
6
+ required:true,
7
+ trim:true
8
+ },
9
+ lastName:{
10
+ type:String,
11
+ trim:true
12
+ },
13
+ LastUpdatedAt:{
14
+ type: Date,
15
+ default: Date.now,
16
+ },
17
+ createdAt: {
18
+ type: Date,
19
+ default: Date.now,
20
+ },
21
+ gender: {
22
+ type: String,
23
+ enum: ['male', 'female'], // Enum values for gender
24
+ required: true,
25
+ },
26
+ })
27
+
28
+
29
+ const user = mongoose.model('customerUser', userSchema);
30
+ export default user;
@@ -0,0 +1,30 @@
1
+ import express from 'express';
2
+ import { register, login } from '../controllers/auth.controller.js';
3
+
4
+ const router = express.Router();
5
+
6
+ /**
7
+ * @swagger
8
+ * /auth/register:
9
+ * post:
10
+ * summary: Register a new user
11
+ * responses:
12
+ * 200:
13
+ * description: User registered successfully.
14
+ * 400:
15
+ * description: Bad request
16
+ */
17
+ router.post('/register', register);
18
+
19
+ /**
20
+ * @swagger
21
+ * /auth/login:
22
+ * post:
23
+ * summary: User login
24
+ * responses:
25
+ * 200:
26
+ * description: User logged in successfully.
27
+ */
28
+ router.post('/login', login);
29
+
30
+ export default router;
@@ -0,0 +1,5 @@
1
+ import express from 'express'
2
+
3
+ export const app = express()
4
+
5
+ app.post('/',login)
@@ -0,0 +1,2 @@
1
+
2
+
File without changes
package/src/server.js ADDED
@@ -0,0 +1,13 @@
1
+ import app from "./app.js"
2
+
3
+ import globalErrorHandler from "./middlewares/errorHandler.middleware.js";
4
+ const PORT = process.env.PORT || 3000;
5
+ const ENV = process.env.NODE_ENV
6
+
7
+
8
+ app.use(globalErrorHandler)
9
+
10
+
11
+ app.listen(PORT, () => {
12
+ console.log(`Server is running on http://localhost:${PORT} at ${ENV}`);
13
+ });
@@ -0,0 +1,53 @@
1
+ import Joi from 'joi';
2
+
3
+ // Define your schemas (same as before)
4
+ const userSchema = Joi.object({
5
+ email: Joi.string()
6
+ .email()
7
+ .optional()
8
+ .messages({
9
+ 'string.email': 'Please enter a valid email address',
10
+ }),
11
+ phone: Joi.number()
12
+ .optional()
13
+ .min(1000000000)
14
+ .max(9999999999)
15
+ .messages({
16
+ 'number.min': 'Phone number must be at least 10 digits',
17
+ 'number.max': 'Phone number must be at most 10 digits',
18
+ }),
19
+ password: Joi.string()
20
+ .min(6)
21
+ .optional()
22
+ .pattern(/^[a-zA-Z0-9]{3,30}$/)
23
+ .messages({
24
+ 'string.min': 'Password must be at least 6 characters long',
25
+ 'string.pattern.base': 'Password can only contain alphanumeric characters',
26
+ }),
27
+ }).xor('email', 'phone')
28
+ .messages({
29
+ 'object.xor': 'Either email or phone number must be provided.',
30
+ });
31
+
32
+ // Schema configuration mapping
33
+ const schemaConfig = [
34
+ {
35
+ methods: ['POST', 'PUT'],
36
+ paths: ['/auth/login', '/users/:id'],
37
+ schema: userSchema,
38
+ },
39
+ // Add more route configurations as needed
40
+ ];
41
+
42
+ // Preprocess the schema configuration into a lookup map
43
+ const schemaLookup = {};
44
+
45
+ schemaConfig.forEach(route => {
46
+ route.methods.forEach(method => {
47
+ route.paths.forEach(path => {
48
+ schemaLookup[`${method}:${path}`] = route.schema;
49
+ });
50
+ });
51
+ });
52
+
53
+ export { schemaLookup, userSchema };
package/start.js ADDED
@@ -0,0 +1,11 @@
1
+ // start.mjs
2
+ import register from '@babel/register';
3
+
4
+ register({
5
+ extensions: ['.js', '.mjs'], // Specify the extensions you want Babel to transpile
6
+ });
7
+
8
+ import('./src/server.js').catch(err => {
9
+ console.error(err);
10
+ process.exit(1);
11
+ });