first commit
This commit is contained in:
commit
9ba07f99d5
11
.env
Normal file
11
.env
Normal file
@ -0,0 +1,11 @@
|
||||
# MongoDB Url
|
||||
MONGO_URL=mongodb://localhost:27017/social_media
|
||||
# PORT
|
||||
PORT=5000
|
||||
# Environment Name. production / development
|
||||
NODE_ENV=development
|
||||
# JWT Secret
|
||||
JWT_SECRET=R4ND0M5TR1NG
|
||||
# Gmail & Passsword
|
||||
GMAIL=sajjad.t.dev@gmail.com
|
||||
GMAIL_PASSWORD=tlvq semi gizc cyvn
|
11
.env.example
Normal file
11
.env.example
Normal file
@ -0,0 +1,11 @@
|
||||
# MongoDB Url
|
||||
MONGO_URL=
|
||||
# PORT
|
||||
PORT=
|
||||
# Environment Name. production / development
|
||||
NODE_ENV=
|
||||
# JWT Secret
|
||||
JWT_SECRET=
|
||||
# Gmail & Passsword
|
||||
GMAIL=
|
||||
GMAIL_PASSWORD=
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
node_modules
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"nuxt.isNuxtApp": false
|
||||
}
|
19
config/config.js
Normal file
19
config/config.js
Normal file
@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
|
||||
module.exports.getConfig = () => {
|
||||
const config = {
|
||||
'MODE': 'Development',
|
||||
'PORT': process.env.PORT || 5000,
|
||||
'MONGO_URL': process.env.MONGO_URL,
|
||||
'UPLOAD_PATH': path.resolve(`${__dirname}/../uploads`),
|
||||
'JWT_SECRET': process.env.JWT_SECRET || 'R4ND0M5TR1NG'
|
||||
};
|
||||
|
||||
// Modify for Production
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
config.MODE = 'Production';
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
26
config/database.js
Normal file
26
config/database.js
Normal file
@ -0,0 +1,26 @@
|
||||
const mongoose = require('mongoose'),
|
||||
config = require('./config').getConfig();
|
||||
|
||||
// Mongo Connection Class
|
||||
class Connection {
|
||||
constructor() {
|
||||
const url = config.MONGO_URL || 'mongodb://localhost:27017/social_media';
|
||||
|
||||
this.connect(url).then(() => {
|
||||
console.log('✔ Database Connected');
|
||||
}).catch((err) => {
|
||||
console.error('✘ MONGODB ERROR: ', err.message);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
async connect(url) {
|
||||
try {
|
||||
await mongoose.connect(url);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Connection();
|
40
config/routes.js
Normal file
40
config/routes.js
Normal file
@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
const { HttpError } = require('../system/helpers/HttpError');
|
||||
const apiRoutes = require('../system/routes');
|
||||
const bodyParser = require('body-parser');
|
||||
module.exports.setRoutes = (app) => {
|
||||
/**
|
||||
* Application Root Route.
|
||||
* Set the Welcome message or send a static html or use a view engine.
|
||||
*/
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(bodyParser.json());
|
||||
app.use(express.json());
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Welcome to the APP');
|
||||
});
|
||||
|
||||
/**
|
||||
* API Route.
|
||||
* All the API will start with "/api/[MODULE_ROUTE]"
|
||||
*/
|
||||
app.use('/api', apiRoutes);
|
||||
|
||||
/**
|
||||
* Serving Static files from uploads directory.
|
||||
* Currently Media module is uploading files into this directory.
|
||||
*/
|
||||
app.use('/uploads', express.static(path.join(__dirname, '../uploads')));
|
||||
|
||||
/**
|
||||
* If No route matches. Send user a 404 page
|
||||
*/
|
||||
app.use('/*', (req, res) => {
|
||||
const error = new Error('Requested path does not exist.');
|
||||
|
||||
error.statusCode = 404;
|
||||
res.status(error.statusCode).json(new HttpError(error));
|
||||
});
|
||||
};
|
19
config/server.js
Normal file
19
config/server.js
Normal file
@ -0,0 +1,19 @@
|
||||
const express = require('express');
|
||||
const helmet = require('helmet'),
|
||||
server = express();
|
||||
const { setRoutes } = require('./routes');
|
||||
// For security
|
||||
server.use(helmet());
|
||||
|
||||
const cors = require('cors'),
|
||||
// Allow Origins according to your need.
|
||||
corsOptions = {
|
||||
'origin': '*'
|
||||
};
|
||||
|
||||
server.use(cors(corsOptions));
|
||||
|
||||
// Setting up Routes
|
||||
setRoutes(server);
|
||||
|
||||
module.exports = { server };
|
23
index.js
Normal file
23
index.js
Normal file
@ -0,0 +1,23 @@
|
||||
require('dotenv').config();
|
||||
// Initialize DB Connection
|
||||
require('./config/database');
|
||||
|
||||
const config = require('./config/config').getConfig(),
|
||||
PORT = config.PORT;
|
||||
|
||||
console.log('✔ Bootstrapping Application');
|
||||
console.log(`✔ Mode: ${config.MODE}`);
|
||||
console.log(`✔ Port: ${PORT}`);
|
||||
|
||||
const { server } = require('./config/server');
|
||||
|
||||
server.listen(PORT).on('error', (err) => {
|
||||
console.log('✘ Application failed to start');
|
||||
console.error('✘', err.message);
|
||||
process.exit(0);
|
||||
}).on('listening', () => {
|
||||
console.log('✔ Application Started');
|
||||
});
|
||||
|
||||
|
||||
module.exports = { server };
|
3360
package-lock.json
generated
Normal file
3360
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
package.json
Normal file
20
package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"scripts": {
|
||||
"start": "node ./bin/www",
|
||||
"watch": "nodemon ./bin/www --watch ./ --ext '*' localhost 5000"
|
||||
},
|
||||
"dependencies": {
|
||||
"auto-bind": "^5.0.1",
|
||||
"bcrypt": "^5.1.1",
|
||||
"body-parser": "^1.20.2",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"helmet": "^7.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mongoose": "^8.0.0",
|
||||
"nodemailer": "^6.9.7",
|
||||
"nodemon": "^3.0.1",
|
||||
"pluralize": "^8.0.0"
|
||||
}
|
||||
}
|
100
src/controllers/AuthController.js
Normal file
100
src/controllers/AuthController.js
Normal file
@ -0,0 +1,100 @@
|
||||
const { AuthService } = require('./../services/AuthService');
|
||||
const User = require('./../models/User');
|
||||
// const autoBind = require('auto-bind');
|
||||
const authService = new AuthService(User);
|
||||
|
||||
class AuthController {
|
||||
|
||||
constructor(service) {
|
||||
this.service = service;
|
||||
// autoBind(this);
|
||||
this.register = this.register.bind(this)
|
||||
this.verify = this.verify.bind(this)
|
||||
this.login = this.login.bind(this)
|
||||
this.forgotPassword = this.forgotPassword.bind(this)
|
||||
this.resetPassword = this.resetPassword.bind(this)
|
||||
}
|
||||
|
||||
async login(req, res, next) {
|
||||
try {
|
||||
const response = await this.service.login(req.body);
|
||||
await res.status(response.statusCode).json(response);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
|
||||
async register(req, res, next) {
|
||||
try {
|
||||
const response = await this.service.register(req.body);
|
||||
await res.status(response.statusCode).json(response);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
|
||||
async verify(req, res, next) {
|
||||
try {
|
||||
const token = this.extractToken(req);
|
||||
const response = await this.service.verify(token);
|
||||
console.log(response, 11);
|
||||
await res.status(response.statusCode).json(response);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
|
||||
async forgotPassword(req, res, next) {
|
||||
try {
|
||||
const response = await this.service.forgotPassword(req.body);
|
||||
await res.status(response.statusCode).json(response);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
};
|
||||
async resetPassword(req, res, next) {
|
||||
try {
|
||||
const token = this.extractToken(req);
|
||||
const response = await this.service.resetPassword(token, req.body);
|
||||
await res.status(response.statusCode).json(response);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
};
|
||||
|
||||
async logout(req, res, next) {
|
||||
try {
|
||||
const response = await this.service.logout(req.token);
|
||||
|
||||
await res.status(response.statusCode).json(response);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
|
||||
async checkLogin(req, res, next) {
|
||||
try {
|
||||
const token = this.extractToken(req);
|
||||
|
||||
req.user = await this.service.checkLogin(token);
|
||||
req.authorized = true;
|
||||
req.token = token;
|
||||
next();
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
|
||||
extractToken(req) {
|
||||
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
|
||||
return req.headers.authorization.split(' ')[1];
|
||||
} else if (req.query && req.query.token) {
|
||||
return req.query.token;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = new AuthController(authService);
|
19
src/models/User.js
Normal file
19
src/models/User.js
Normal file
@ -0,0 +1,19 @@
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
const userSchema = new mongoose.Schema({
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isVerified: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = mongoose.model('User', userSchema);
|
12
src/routes/auth.js
Normal file
12
src/routes/auth.js
Normal file
@ -0,0 +1,12 @@
|
||||
const AUthController = require('../controllers/AuthController');
|
||||
const express = require('express'),
|
||||
router = express.Router();
|
||||
|
||||
router.post('/login', AUthController.login);
|
||||
router.post('/verify', AUthController.verify);
|
||||
router.get('/logout', AUthController.checkLogin, AUthController.logout);
|
||||
router.post('/register', AUthController.register);
|
||||
router.post('/forgot-password' ,AUthController.forgotPassword);
|
||||
router.post('/reset-password' ,AUthController.resetPassword);
|
||||
|
||||
module.exports = router;
|
154
src/services/AuthService.js
Normal file
154
src/services/AuthService.js
Normal file
@ -0,0 +1,154 @@
|
||||
const { Transporter } = require('../../system/services/GmailService');
|
||||
const bcrypt = require('bcrypt'), SALT_WORK_FACTOR = 10;
|
||||
const jwt = require('jsonwebtoken');
|
||||
const { UserService } = require('./UserService');
|
||||
// const autoBind = require('auto-bind');
|
||||
const { HttpResponse } = require('../../system/helpers/HttpResponse');
|
||||
const { HttpError } = require('../../system/helpers/HttpError');
|
||||
|
||||
class AuthService {
|
||||
constructor(userModel) {
|
||||
this.userModel = userModel;
|
||||
this.userService = new UserService(userModel);
|
||||
// autoBind(this);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param email: String
|
||||
* @param password: String
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
async login(data) {
|
||||
try {
|
||||
const { email, password } = data;
|
||||
// Find the user by email
|
||||
let user = await this.userModel.findOne({ email });
|
||||
if (!user) return new HttpError({ message: 'Invalid email or password.', statusCode: 400 });
|
||||
|
||||
// Check if the password is correct
|
||||
const isPasswordValid = await bcrypt.compare(password, user.password);
|
||||
if (!isPasswordValid) return new HttpError({ message: 'Invalid email or password.', statusCode: 400 });
|
||||
|
||||
// Check if the user is verified
|
||||
if (!user.isVerified) return new HttpError({ message: 'Email not verified.', statusCode: 400 });
|
||||
// Generate a JWT token
|
||||
const token = jwt.sign({ userId: user._id }, 'token');
|
||||
|
||||
// serialize user to delete password
|
||||
user = new HttpResponse().filterData(JSON.parse(JSON.stringify(user)))
|
||||
return { statusCode: 200, message: 'Login successful', data: { user, token } }
|
||||
} catch (error) {
|
||||
return new HttpError({ message: 'Login failed. Please try again.', statusCode: 500 });
|
||||
};
|
||||
};
|
||||
|
||||
async register(data) {
|
||||
try {
|
||||
console.log(data);
|
||||
const { email, password } = data;
|
||||
// Check if email already exists
|
||||
const existingUser = await this.userModel.findOne({ email });
|
||||
if (existingUser) return new HttpError({ message: 'Email already exists.', statusCode: 400 });
|
||||
|
||||
// Hash the password,
|
||||
const hashedPassword = await bcrypt.hash(password, SALT_WORK_FACTOR);
|
||||
|
||||
// Create a new user
|
||||
const response = await this.userService.store({ email, password: hashedPassword });
|
||||
const { data: user } = response;
|
||||
// Generate a verification token
|
||||
const token = jwt.sign({ userId: user._id }, 'token');
|
||||
|
||||
// Send verification email
|
||||
await Transporter.sendMail({
|
||||
from: process.env.GMAIL,
|
||||
to: email,
|
||||
subject: 'Email Verification',
|
||||
text: `Please click the following link to verify your email: http://localhost:3000/verify?token=${token}`,
|
||||
});
|
||||
return { ...response, data: {} };
|
||||
} catch (error) {
|
||||
return new HttpError({ message: 'Registration failed. Please try again.', statusCode: 500 })
|
||||
};
|
||||
};
|
||||
async verify(token) {
|
||||
try {
|
||||
|
||||
// Verify the token
|
||||
const decodedToken = jwt.verify(token, 'token');
|
||||
const userId = decodedToken.userId;
|
||||
|
||||
// Find the user with the corresponding token
|
||||
const user = await this.userModel.findById(userId);
|
||||
if (!user) return new HttpError({ message: 'Invalid or expired token.', statusCode: 400 });
|
||||
|
||||
// Update the user's isVerified field
|
||||
user.isVerified = true;
|
||||
const response = await this.userService.update(userId, user);
|
||||
return { ...response, message: 'Email verification successful.' };
|
||||
} catch (error) {
|
||||
return new HttpError({ message: 'Email verification failed. Please try again.', statusCode: 500 });
|
||||
};
|
||||
};
|
||||
async forgotPassword(data) {
|
||||
const { email } = data;
|
||||
|
||||
try {
|
||||
const user = await this.userModel.findOne({ email });
|
||||
if (!user) return new HttpError({ message: 'User not found', statusCode: 404 });
|
||||
|
||||
const token = jwt.sign({ userId: user._id }, 'token');
|
||||
|
||||
const mailOptions = {
|
||||
from: process.env.GMAIL,
|
||||
to: email,
|
||||
subject: 'Password Reset Request',
|
||||
html: `<p>Dear user,</p>
|
||||
<p>You have requested to reset your password. Please click on the following link to reset your password:</p>
|
||||
<a href="http://localhost:3000/reset-password?token=${token}">Reset Password</a>
|
||||
<br>
|
||||
<p>If you did not request this, please ignore this email.</p>`,
|
||||
};
|
||||
await Transporter.sendMail(mailOptions);
|
||||
return { message: 'Password reset email sent successfully', statusCode: 200 };
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return new HttpError({ message: 'Internal server error', statusCode: 500 });
|
||||
};
|
||||
};
|
||||
async resetPassword(token, data) {
|
||||
const { new_password } = data;
|
||||
try {
|
||||
// Verify the token
|
||||
const decodedToken = jwt.verify(token, 'token');
|
||||
const userId = decodedToken.userId;
|
||||
|
||||
// Find the user with the corresponding token
|
||||
const user = await this.userModel.findById(userId);
|
||||
if (!user) return new HttpError({ message: 'Invalid or expired reset token', statusCode: 400 });
|
||||
|
||||
// Hash the password
|
||||
const hashedPassword = await bcrypt.hash(new_password, SALT_WORK_FACTOR);
|
||||
|
||||
// Create a new user
|
||||
const response = await this.userService.update(userId, { password: hashedPassword });
|
||||
return { ...response, message: 'Password updated successfully' };
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return new HttpError({ message: 'Internal server error', statusCode: 500 });
|
||||
};
|
||||
};
|
||||
|
||||
async logout(token) {
|
||||
try {
|
||||
await this.model.deleteOne({ token });
|
||||
return new HttpResponse({ 'logout': true });
|
||||
} catch (error) {
|
||||
throw error;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = { AuthService };
|
24
src/services/UserService.js
Normal file
24
src/services/UserService.js
Normal file
@ -0,0 +1,24 @@
|
||||
const { Transporter } = require('../../system/services/GmailService');
|
||||
const { Service } = require('../../system/services/Service');
|
||||
// const autoBind = require('auto-bind');
|
||||
|
||||
class UserService extends Service {
|
||||
constructor(model) {
|
||||
console.log(model);
|
||||
super(model);
|
||||
this.model = model;
|
||||
// autoBind(this);
|
||||
}
|
||||
|
||||
|
||||
async updatePassword(id, data) {
|
||||
try {
|
||||
await this.model.findByIdAndUpdate(id, data, { 'new': true });
|
||||
return { 'passwordChanged': true };
|
||||
} catch (errors) {
|
||||
throw errors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { UserService };
|
73
system/controllers/Controller.js
Normal file
73
system/controllers/Controller.js
Normal file
@ -0,0 +1,73 @@
|
||||
const autoBind = require('auto-bind');
|
||||
|
||||
class Controller {
|
||||
|
||||
/**
|
||||
* Base Controller Layer
|
||||
* @author Sunil Kumar Samanta
|
||||
* @param service
|
||||
*/
|
||||
constructor(service) {
|
||||
this.service = service;
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
async index(req, res, next) {
|
||||
try {
|
||||
const response = await this.service.index(req.query);
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
|
||||
async show(req, res, next) {
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
const response = await this.service.show(id);
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
|
||||
async store(req, res, next) {
|
||||
try {
|
||||
const response = await this.service.store(req.body);
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
|
||||
async update(req, res, next) {
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
const response = await this.service.update(id, req.body);
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
|
||||
async delete(req, res, next) {
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
const response = await this.service.delete(id);
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = { Controller };
|
48
system/helpers/HttpError.js
Normal file
48
system/helpers/HttpError.js
Normal file
@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
|
||||
class HttpError {
|
||||
error = true;
|
||||
responseTimestamp = new Date();
|
||||
|
||||
/**
|
||||
* HTTP Error Class
|
||||
* @author Sunil Kumar Samanta
|
||||
* @param error
|
||||
*/
|
||||
constructor(error) {
|
||||
if (typeof (error) === 'string') {
|
||||
this.statusCode = 500;
|
||||
this.message = error;
|
||||
this.name = 'InternalServerError';
|
||||
} else {
|
||||
if (error.name === 'ValidationError') {
|
||||
error.statusCode = 422;
|
||||
}
|
||||
|
||||
let errorName = 'InternalServerError';
|
||||
|
||||
switch (error.statusCode) {
|
||||
case 422:
|
||||
errorName = 'ValidationError';
|
||||
break;
|
||||
case 401:
|
||||
errorName = 'UnauthorizedError';
|
||||
break;
|
||||
case 403:
|
||||
errorName = 'ForbiddenError';
|
||||
break;
|
||||
case 404:
|
||||
errorName = 'NotFoundError';
|
||||
break;
|
||||
default:
|
||||
errorName = 'InternalServerError';
|
||||
}
|
||||
this.statusCode = error.statusCode ? error.statusCode : 500;
|
||||
this.message = error.message || 'Something wrong!';
|
||||
this.errors = error.errors;
|
||||
this.name = errorName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { HttpError };
|
53
system/helpers/HttpResponse.js
Normal file
53
system/helpers/HttpResponse.js
Normal file
@ -0,0 +1,53 @@
|
||||
const defaultExcludedItemsFromResponse = ['__v', 'password'];
|
||||
|
||||
class HttpResponse {
|
||||
|
||||
/**
|
||||
* @author Sunil Kumar Samanta
|
||||
* @param data : Object | Array | String
|
||||
* @param options : {totalCount: Number, statusCode: Number, deleted: Boolean}
|
||||
*/
|
||||
error = false;
|
||||
responseTimestamp = new Date();
|
||||
|
||||
constructor(data, options = { 'totalCount': 0, 'statusCode': 200, 'deleted': null }) {
|
||||
this.statusCode = options.statusCode || 200;
|
||||
let filteredData = data;
|
||||
|
||||
if (typeof (filteredData) === 'object') {
|
||||
filteredData = this.filterData(JSON.parse(JSON.stringify(filteredData)));
|
||||
}
|
||||
if (options.deleted) {
|
||||
this.deleted = options.deleted;
|
||||
}
|
||||
if (Array.isArray(filteredData)) {
|
||||
this.data = [...filteredData];
|
||||
this.totalCount = options.totalCount || undefined;
|
||||
} else if (typeof (filteredData) === 'object') {
|
||||
this.data = { ...filteredData };
|
||||
} else {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
filterData(data) {
|
||||
if (Array.isArray(data)) {
|
||||
data.map((x, index) => {
|
||||
Object.keys(x).forEach((key) => {
|
||||
if (defaultExcludedItemsFromResponse.includes(key)) {
|
||||
delete data[index][key];
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (typeof (data) === 'object') {
|
||||
Object.keys(data).forEach((key) => {
|
||||
if (defaultExcludedItemsFromResponse.includes(key)) {
|
||||
delete data[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { HttpResponse };
|
8
system/helpers/Utility.js
Normal file
8
system/helpers/Utility.js
Normal file
@ -0,0 +1,8 @@
|
||||
module.exports.slugify = (text) => {
|
||||
return text.toString().toLowerCase()
|
||||
.replace(/\s+/g, '-') // Replace spaces with -
|
||||
.replace(/[^\w\-\.]+/g, '') // Remove all non-word chars
|
||||
.replace(/\-\-+/g, '-') // Replace multiple - with single -
|
||||
.replace(/^-+/, '') // Trim - from start of text
|
||||
.replace(/-+$/, ''); // Trim - from end of text
|
||||
};
|
55
system/routes/index.js
Normal file
55
system/routes/index.js
Normal file
@ -0,0 +1,55 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const pluralize = require('pluralize');
|
||||
const path = require('path');
|
||||
|
||||
pluralize.addUncountableRule('media');
|
||||
pluralize.addUncountableRule('auth');
|
||||
|
||||
const fs = require('fs');
|
||||
const { HttpError } = require('../helpers/HttpError');
|
||||
const packageJson = require('../../package.json'),
|
||||
routesPath = path.resolve(`${__dirname}/../../src/routes`),
|
||||
PATHS = fs.readdirSync(routesPath),
|
||||
moduleMapper = [];
|
||||
|
||||
console.log('✔ Mapping routes');
|
||||
PATHS.forEach((module) => {
|
||||
if (module !== 'index.js') {
|
||||
const name = module.split('.')[0];
|
||||
|
||||
// eslint-disable-next-line global-require
|
||||
router.use(`/${pluralize.plural(name)}`, require(path.resolve(routesPath, module)));
|
||||
moduleMapper.push({
|
||||
'Module': name,
|
||||
'Route': `/${pluralize.plural(name)}`
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
console.table(moduleMapper);
|
||||
|
||||
router.get('/', (req, res) => {
|
||||
res.json({ 'status': true, 'message': `Welcome to ${packageJson.name} V ${packageJson.version}` });
|
||||
});
|
||||
|
||||
router.use('*', (req, res, next) => {
|
||||
// 404 handler
|
||||
const error = new Error('Resource not found');
|
||||
|
||||
error.statusCode = 404;
|
||||
next(error);
|
||||
});
|
||||
|
||||
router.use((err, req, res, next) => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.error(req.method, req.url, err.statusCode, err.message);
|
||||
}
|
||||
const error = new HttpError(err);
|
||||
|
||||
res.status(error.statusCode);
|
||||
res.json(error);
|
||||
next();
|
||||
});
|
||||
module.exports = router;
|
9
system/services/GmailService.js
Normal file
9
system/services/GmailService.js
Normal file
@ -0,0 +1,9 @@
|
||||
const nodemailer = require('nodemailer');
|
||||
const Transporter = nodemailer.createTransport({
|
||||
service: 'Gmail',
|
||||
auth: {
|
||||
user: process.env.GMAIL,
|
||||
pass: process.env.GMAIL_PASSWORD,
|
||||
},
|
||||
});
|
||||
module.exports = { Transporter };
|
112
system/services/Service.js
Normal file
112
system/services/Service.js
Normal file
@ -0,0 +1,112 @@
|
||||
const mongoose = require('mongoose');
|
||||
// const autoBind = require('auto-bind');
|
||||
const { HttpResponse } = require('../helpers/HttpResponse');
|
||||
|
||||
class Service {
|
||||
/**
|
||||
* Base Service Layer
|
||||
* @author Sunil Kumar Samanta
|
||||
* @param model
|
||||
*/
|
||||
constructor(model) {
|
||||
console.log(model, 32);
|
||||
this.model = model;
|
||||
|
||||
// autoBind(this);
|
||||
}
|
||||
|
||||
async index(query) {
|
||||
let { skip, limit, sortBy } = query;
|
||||
|
||||
skip = skip ? Number(skip) : 0;
|
||||
limit = limit ? Number(limit) : 10;
|
||||
sortBy = sortBy ? sortBy : { 'createdAt': -1 };
|
||||
|
||||
delete query.skip;
|
||||
delete query.limit;
|
||||
delete query.sortBy;
|
||||
|
||||
if (query._id) {
|
||||
try {
|
||||
query._id = new mongoose.mongo.ObjectId(query._id);
|
||||
} catch (error) {
|
||||
throw new Error('Not able to generate mongoose id with content');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const items = await this.model
|
||||
.find(query)
|
||||
.sort(sortBy)
|
||||
.skip(skip)
|
||||
.limit(limit),
|
||||
|
||||
total = await this.model.countDocuments(query);
|
||||
|
||||
return new HttpResponse(items, { 'totalCount': total });
|
||||
} catch (errors) {
|
||||
throw errors;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async show(id) {
|
||||
try {
|
||||
const item = await this.model.findById(id);
|
||||
|
||||
if (!item) {
|
||||
const error = new Error('Item not found');
|
||||
|
||||
error.statusCode = 404;
|
||||
throw error;
|
||||
}
|
||||
|
||||
return new HttpResponse(item);
|
||||
} catch (errors) {
|
||||
throw errors;
|
||||
}
|
||||
}
|
||||
|
||||
async store(data) {
|
||||
try {
|
||||
const item = await this.model.create(data);
|
||||
|
||||
if (item) {
|
||||
return new HttpResponse(item);
|
||||
}
|
||||
throw new Error('Something wrong happened');
|
||||
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async update(id, data) {
|
||||
try {
|
||||
const item = await this.model.findByIdAndUpdate(id, data, { 'new': true });
|
||||
|
||||
return new HttpResponse(item);
|
||||
} catch (errors) {
|
||||
throw errors;
|
||||
}
|
||||
}
|
||||
|
||||
async delete(id) {
|
||||
try {
|
||||
const item = await this.model.findByIdAndDelete(id);
|
||||
|
||||
if (!item) {
|
||||
const error = new Error('Item not found');
|
||||
|
||||
error.statusCode = 404;
|
||||
throw error;
|
||||
} else {
|
||||
return new HttpResponse(item, { 'deleted': true });
|
||||
}
|
||||
} catch (errors) {
|
||||
throw errors;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Service };
|
Loading…
Reference in New Issue
Block a user