broll-express 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +6 -0
- package/LICENSE +21 -0
- package/package.json +32 -0
- package/src/app.js +32 -0
- package/src/config/mongoConfig.js +9 -0
- package/src/config/swaggerConfig.js +27 -0
- package/src/controllers/auth.controller.js +22 -0
- package/src/controllers/signup.controller.js +31 -0
- package/src/library/auth.lib.js +4 -0
- package/src/library/error.lib.js +11 -0
- package/src/middlewares/errorHandler.middleware.js +19 -0
- package/src/middlewares/payloadValidation.middleware.js +19 -0
- package/src/models/auth.models.js +63 -0
- package/src/models/common.schema.js +10 -0
- package/src/models/user.models.js +30 -0
- package/src/routes/auth.routes.js +30 -0
- package/src/routes/business.routes.js +5 -0
- package/src/routes/public.routes.js +2 -0
- package/src/routes/user.routes.js +0 -0
- package/src/server.js +13 -0
- package/src/validations/validations.schema.js +53 -0
- package/start.js +11 -0
package/.babelrc
ADDED
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,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,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,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;
|
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